[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [calligra] /: Add api for writing ODF that is generated from the ODF RNG file.
From: Jos van den Oever <jos () vandenoever ! info>
Date: 2013-08-04 17:41:20
Message-ID: E1V62Iy-0006d8-U9 () scm ! kde ! org
[Download RAW message or body]
Git commit 1cf17b47013c7353d5ccd59663145e46bc2f0750 by Jos van den Oever.
Committed on 04/08/2013 at 17:39.
Pushed by vandenoever into branch 'master'.
Add api for writing ODF that is generated from the ODF RNG file.
Two years ago I wrote an initial version of this patch and a detailed discussion on \
the mailing list [1] followed. The main objections to the patch have been dealt with \
(see below). Most of this new version was written at Akademy in Bilbao.
Very short summary of the patch:
This patch should help everybody, young and old, with coding C++ for writing ODF and \
make errors easier to catch. The OpenDocument Format specification is published with \
a Relax NG file that specifies the XML format. This file can be used to check if ODF \
files are valid. It can also be used to generate a C++ API headers. This is what this \
patch does.
Example:
Instead of writing:
==
xmlWriter->startElement("text:p");
xmlWriter->addAttribute("text:style-name", "italic");
xmlWriter->startElement("text:p");
xmlWriter->addAttribute("text:style-name", "bold");
xmlWriter->addTextNode("Hello World!");
xmlWriter->endElement();
xmlWriter->endElement();
==
you can write:
==
text_p p(xmlWriter);
p.set_text_style_name("italic");
text_span span(p.add_text_span());
span.set_text_style_name("italic");
span.addTextNode("Hello World!");
==
Some advantages:
- autocompletion when coding: faster coding
- tag and attribute names are not strings but class and function names: less errors
- nesting is checked by the compiler
- you write to elements (span, p), not xmlwriter: easier to read
- required attributes are part of the element constructor
Implementation considerations:
- Calligra is large, so the generated code mixes well with the use of KoXmlWriter \
and porting can be done in small steps.
- class and function names are similar to the xml tags with ':' and '-' replaced by \
'_'.
- stack based: no heap allocations
- only header files: all code will inline and have low impact on runtime
- modular: one header file per namespace to reduce compile overhead
- code generator is Qt code, part of Calligra and runs as a build step
Not in this patch (and places where you can help in the future):
- generate enumerations based on Relax NG
- check data type for attributes (you can still write "hello" to an integer \
attribute)
- complete port of Calligra to the generated code
- improved speed by using static QString instead of const char*
Provided solutions to previously raised issues:
- modular headers to reduce compile overhead
- function "end()" to optionally close an element before it goes out of scope
- use structure of Relax NG file to reduce header sizes by inheritance from common \
groups
- provide most KoXmlWriter functionality safely through the element instances
- closing elements is now automatic at a slight runtime overhead
M +1 -0 devtools/CMakeLists.txt
A +4 -0 devtools/rng2cpp/CMakeLists.txt
A +1389 -0 devtools/rng2cpp/rng2cpp.cpp [License: LGPL (v2+)]
M +2 -1 filters/libmso/CMakeLists.txt
M +24 -43 filters/libmso/shapes.cpp
M +54 -56 filters/libmso/shapes2.cpp
M +1 -0 filters/sheets/excel/import/CMakeLists.txt
M +322 -384 filters/sheets/excel/import/excelimporttoods.cc
M +361 -271 filters/stage/powerpoint/PptToOdp.cpp
M +13 -2 filters/stage/powerpoint/PptToOdp.h
M +15 -24 libs/kotext/KoInlineNote.cpp
M +2 -0 libs/odf/CMakeLists.txt
A +10 -0 libs/odf/writeodf/CMakeLists.txt
A +16 -0 libs/odf/writeodf/README.txt
A +59 -0 libs/odf/writeodf/helpers.h [License: UNKNOWN] *
A +101 -0 libs/odf/writeodf/odfwriter.h [License: UNKNOWN] *
The files marked with a * at the end have a non valid license. Please read: \
http://techbase.kde.org/Policies/Licensing_Policy and use the headers which are \
listed at that page.
http://commits.kde.org/calligra/1cf17b47013c7353d5ccd59663145e46bc2f0750
diff --git a/devtools/CMakeLists.txt b/devtools/CMakeLists.txt
index 15008fb..f0c4859 100644
--- a/devtools/CMakeLists.txt
+++ b/devtools/CMakeLists.txt
@@ -7,3 +7,4 @@ if (SHOULD_BUILD_DEVTOOLS)
add_subdirectory(scripts)
endif (SHOULD_BUILD_DEVTOOLS)
+add_subdirectory(rng2cpp)
diff --git a/devtools/rng2cpp/CMakeLists.txt b/devtools/rng2cpp/CMakeLists.txt
new file mode 100644
index 0000000..adf9e10
--- /dev/null
+++ b/devtools/rng2cpp/CMakeLists.txt
@@ -0,0 +1,4 @@
+find_package(Qt4 REQUIRED QtCore QtXml)
+include(${QT_USE_FILE})
+add_executable(rng2cpp rng2cpp.cpp)
+target_link_libraries(rng2cpp ${QT_LIBRARIES})
diff --git a/devtools/rng2cpp/rng2cpp.cpp b/devtools/rng2cpp/rng2cpp.cpp
new file mode 100644
index 0000000..4850c8b
--- /dev/null
+++ b/devtools/rng2cpp/rng2cpp.cpp
@@ -0,0 +1,1389 @@
+/* This file is part of the KDE project
+ Copyright (C) 2013 Jos van den Oever <jos@vandenoever.info>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+#include <QFile>
+#include <QDebug>
+#include <QDomDocument>
+#include <QMap>
+#include <QtGlobal>
+#include <QSharedPointer>
+#include <QStringList>
+
+#define assert(cond, what) (Q_ASSERT_X(cond,"",(const \
char*)QString(what).toAscii())) +
+static const QString ns = "writeodf";
+
+class RNGItem;
+//typedef QSharedPointer<RNGItem> RNGItemPtr;
+typedef RNGItem* RNGItemPtr;
+typedef QSet<RNGItemPtr> RNGItems;
+typedef QVector<RNGItemPtr> RNGItemList;
+
+/**
+ * Helper class for writing fatal messages of the form fatal() << "error!";
+ */
+class fatal
+{
+private:
+ QString msg;
+public:
+ fatal() {}
+ ~fatal()
+ {
+ qFatal("%s", (const char*)msg.toAscii());
+ }
+ template<class T>
+ fatal& operator<<(T s)
+ {
+ msg += s;
+ return *this;
+ }
+};
+
+/**
+ * RNG Datatype has either a constant value or a type.
+ */
+class Datatype
+{
+public:
+ /**
+ * Standard comparator for Datatype.
+ */
+ bool operator==(const Datatype& a) const {
+ return constant == a.constant
+ && type == a.type;
+ }
+ /**
+ * Standard comparator for Datatype.
+ */
+ bool operator!=(const Datatype& a) const {
+ return !(*this == a);
+ }
+ QString constant;
+ QString type;
+};
+
+/**
+ * Define a qHash so Datatype can be put into a QSet.
+ */
+uint qHash(const Datatype& d)
+{
+ return qHash(d.constant) ^ qHash(d.type);
+}
+
+/**
+ * @brief The RNGItem class
+ * Generic class that describes any of a number of concepts from an RNG file.
+ * Any <define/>, <element/> or <attribute/> class is parsed into an instance
+ * of RNGItem.
+ */
+class RNGItem
+{
+public:
+ enum ItemType { Define, Start, Element, Attribute };
+private:
+ RNGItem(const RNGItem&);
+ void operator=(const RNGItem&);
+protected:
+ /**
+ * @brief type
+ */
+ const ItemType m_type;
+ const QString m_name;
+ /**
+ * Internal constructor.
+ */
+ RNGItem(ItemType type, const QString &name = QString()) :m_type(type),
+ m_name(name), mixedContent(false)
+ {
+ if (type != Start && name.isEmpty()) {
+ fatal() << "name is empty";
+ }
+ }
+public:
+ /**
+ * true if this item may contain text nodes
+ */
+ bool mixedContent;
+ /**
+ * name attribute of the <define/> element
+ */
+ const QString& name() const { return m_name; }
+ /**
+ * transformed name that is used in generated C++ code
+ */
+ QString cppName;
+ /**
+ * items that are allowed to be used in this item
+ */
+ RNGItems allowedItems;
+ /**
+ * items that must to be used in this item
+ */
+ RNGItems requiredItems;
+ /**
+ * names of items that are used in this item
+ * This list is resolved into allowedItems after parsing.
+ */
+ QSet<QString> referencedDeclares;
+ /**
+ * names of items that must be used in this item
+ * This list is resolved into allowedItems after parsing.
+ */
+ QSet<QString> requiredReferencedDeclares;
+ /**
+ * Collection of possible datatypes for this item.
+ */
+ QSet<Datatype> datatype;
+ /**
+ * true if this is item corresponds to a <element/>
+ */
+ bool isElement() const { return m_type == Element; }
+ /**
+ * true if this is item corresponds to a <attribute/>
+ */
+ bool isAttribute() const { return m_type == Attribute; }
+ /**
+ * Return a string value if this item can only contain a single constant value.
+ * For example "1.2" is the only allowed, but required value for the
+ * office:version attribute.
+ */
+ QString singleConstant() const
+ {
+ return datatype.size() == 1 ?datatype.constBegin()->constant :QString();
+ }
+ /**
+ * Return a string with the datatype if only one datatype is possible for
+ * this item.
+ */
+ QString singleType() const
+ {
+ return datatype.size() == 1 ?datatype.constBegin()->type :QString();
+ }
+ /**
+ * true if this is item corresponds to a <define/>
+ */
+ bool isDefine() const
+ {
+ bool isdefine = m_type == Define || m_type == Start;
+ assert(!isdefine || defineName.length(), elementName + " " + attributeName);
+ return isdefine;
+ }
+ /**
+ * true if this is item corresponds to a <start/>
+ */
+ bool isStart() const
+ {
+ return m_type == Start;
+ }
+ bool operator==(const RNGItem& a) const;
+};
+
+/**
+ * Specialization of RNGItem that is an element.
+ */
+class Element : public RNGItem
+{
+public:
+ Element(const QString& name) :RNGItem(RNGItem::Element, name)
+ {
+ }
+};
+
+/**
+ * Specialization of RNGItem that is an attribute.
+ */
+class Attribute : public RNGItem
+{
+public:
+ Attribute(const QString& name) :RNGItem(RNGItem::Attribute, name)
+ {
+ }
+};
+
+/**
+ * Specialization of RNGItem that is a a define.
+ */
+class Start : public RNGItem
+{
+public:
+ Start() :RNGItem(RNGItem::Start)
+ {
+ }
+};
+
+/**
+ * Specialization of RNGItem that is a a define.
+ */
+class Define : public RNGItem
+{
+public:
+ Define(const QString& name) :RNGItem(RNGItem::Define, name)
+ {
+ }
+};
+
+/**
+ * Simple helper class for collecting information about whether an RNGItem
+ * may contain a mix of text nodes and elements.
+ */
+class MixedCheck
+{
+public:
+ int elementCount;
+ bool mixed;
+ MixedCheck() :elementCount(0), mixed(false) {}
+};
+
+/**
+ * Determine if the RNGItem item may contain a mix of text nodes and elements.
+ * @param item item to be investigated
+ * @param seen items that were already seen, needed to avoid infinite recursion
+ * @param mc data that is being collected
+ */
+void isMixed(const RNGItemPtr& item, RNGItems& seen, MixedCheck& mc)
+{
+ if (item->isAttribute() || seen.contains(item)) {
+ return;
+ }
+ seen.insert(item);
+ mc.mixed = mc.mixed || item->mixedContent;
+ RNGItems::ConstIterator i = item->allowedItems.constBegin();
+ RNGItems::ConstIterator end = item->allowedItems.constEnd();
+ while (i != end) {
+ if ((*i)->isDefine()) {
+ isMixed(*i, seen, mc);
+ } else if ((*i)->isElement()) {
+ ++mc.elementCount;
+ }
+ ++i;
+ }
+}
+
+/**
+ * Determine if the RNGItem item may contain a mix of text nodes and elements.
+ * This function call a helper function that inspects the item recursively.
+ * @param item item to be investigated
+ * @return true if item may contain a mix of text nodes and elements
+ */
+bool isMixed(const RNGItemPtr& item)
+{
+ RNGItems seen;
+ MixedCheck mc;
+ isMixed(item, seen, mc);
+ return mc.mixed || mc.elementCount == 0;
+}
+
+/**
+ * Merge item b in to item a.
+ */
+void merge(RNGItemPtr& a, const RNGItemPtr& b)
+{
+ if (b->mixedContent) {
+ a->mixedContent = true;
+ }
+ assert(a->allowedItems.contains(b), "");
+ if (a->requiredItems.contains(b)) {
+ foreach(RNGItemPtr i, b->requiredItems) {
+ a->requiredItems.insert(i);
+ }
+ a->requiredItems.remove(b);
+ }
+ foreach(RNGItemPtr i, b->allowedItems) {
+ a->allowedItems.insert(i);
+ }
+ a->allowedItems.remove(b);
+}
+
+/**
+ * Sort function to sort the items in a nice way.
+ * <define/> items (including <start/> go first.
+ * <element/> items come next.
+ * <attribute/> items go last.
+ * Items of similar type are compared by their names.
+ */
+bool rngItemPtrLessThan(const RNGItemPtr &a, const RNGItemPtr &b)
+{
+ if (a->isDefine()) {
+ if (b->isDefine()) {
+ return a->name() < b->name();
+ }
+ return true;
+ }
+ if (b->isDefine()) {
+ return false;
+ }
+ if (a->isElement()) {
+ if (b->isElement()) {
+ if (a->name() == b->name()) {
+ // cppname maybe different, it can have e.g. a number appended
+ return a->cppName < b->cppName;
+ }
+ return a->name() < b->name();
+ }
+ return true;
+ }
+ if (b->isElement()) {
+ return false;
+ }
+ if (a->name() == b->name()) {
+ return a->cppName < b->cppName;
+ }
+ return a->name() < b->name();
+}
+
+/**
+ * Class that has a separate open header file for each namespace and each
+ * combination of namespaces.
+ * This object is passed around the code generating functions instead of
+ * a single output stream for a single header file.
+ */
+class Files
+{
+ /**
+ * List of open files.
+ */
+ QMap<QString,QMap<QString,QTextStream*> > files;
+ /**
+ * Directory into which to write all the header files.
+ */
+ const QString outdir;
+public:
+ Files(const QString& outdir_) :outdir(outdir_) {}
+ /**
+ * Close all open files after writing the closing '#endif'
+ */
+ ~Files() {
+ typedef const QMap<QString,QTextStream*> map;
+ foreach (map& m, files) {
+ foreach (QTextStream* out, m) {
+ *out << "#endif\n";
+ out->device()->close();
+ delete out->device();
+ delete out;
+ }
+ }
+ }
+ QTextStream& getFile(const QString& tag, const QString& tag2);
+ void closeNamespace();
+};
+
+/**
+ * Create a map that maps each Relax NG type to a Qt/C++ type.
+ */
+QMap<QString, QString> createTypeMap()
+{
+ QMap<QString, QString> map;
+ map.insert("string", "const QString&");
+ map.insert("date", "const QDate&");
+ map.insert("time", "const QTime&");
+ map.insert("dateTime", "const QDateTime&");
+ map.insert("duration", "qint64");
+ map.insert("integer", "qint64");
+ map.insert("nonNegativeInteger", "quint64");
+ map.insert("positiveInteger", "quint64");
+ map.insert("double", "double");
+ map.insert("anyURI", "const QUrl&");
+ map.insert("base64Binary", "const QByteArray&");
+ map.insert("ID", "const QString&");
+ map.insert("IDREF", "const QString&");
+ map.insert("IDREFS", "const QStringList&");
+ map.insert("NCName", "const QString&");
+ map.insert("language", "const QString&");
+ map.insert("token", "const QString&");
+ map.insert("QName", "const QString&");
+ map.insert("decimal", "double");
+ return map;
+}
+
+/**
+ * Return a Qt/C++ type for each Relax NG type.
+ */
+QString mapType(const QString& type)
+{
+ static const QMap<QString, QString> map = createTypeMap();
+ if (!map.contains(type)) {
+ fatal() << "Unknown data type " << type;
+ }
+ return map.value(type);
+}
+
+/**
+ * see below
+ */
+void parseContent(QDomElement content, RNGItem& item, bool required);
+
+/**
+ * Get a list of names for the attribute or element.
+ */
+QStringList getNames(QDomElement e)
+{
+ QStringList names;
+ QString name = e.attribute("name");
+ if (name.isEmpty()) {
+ QDomElement ce = e.firstChildElement();
+ if (ce.localName() == "choice") {
+ ce = ce.firstChildElement();
+ while (!ce.isNull()) {
+ if (ce.localName() == "name") {
+ names << ce.text();
+ } else {
+ fatal() << "Found element without comprehensible name.";
+ }
+ ce = ce.nextSiblingElement();
+ }
+ } else if (ce.localName() != "anyName") {
+ fatal() << "Found element without comprehensible name.";
+ }
+ } else {
+ names << name;
+ }
+ return names;
+}
+
+/**
+ * Parse an <element/> element.
+ */
+void parseElement(QDomElement e, RNGItem& parent, bool required)
+{
+ QStringList names = getNames(e);
+ foreach (const QString& name, names) {
+ RNGItemPtr element = RNGItemPtr(new Element(name));
+ parseContent(e, *element, true);
+ parent.allowedItems.insert(element);
+ if (required) {
+ parent.requiredItems.insert(element);
+ }
+ }
+}
+
+/**
+ * Parse an <attribute/> element.
+ */
+void parseAttribute(QDomElement e, RNGItem& parent, bool required)
+{
+ QStringList names = getNames(e);
+ foreach (const QString& name, names) {
+ RNGItemPtr attribute = RNGItemPtr(new Attribute(name));
+ parseContent(e, *attribute, true);
+ parent.allowedItems.insert(attribute);
+ if (required) {
+ parent.requiredItems.insert(attribute);
+ }
+ }
+}
+
+/**
+ * Parse the contents of any Relax NG element.
+ */
+void parseContent(QDomElement content, RNGItem& item, bool required)
+{
+ QDomElement e = content.firstChildElement();
+ while (!e.isNull()) {
+ QString type = e.localName();
+ QString name = e.attribute("name");
+ if (type == "interleave" || type == "oneOrMore" || type == "group") {
+ parseContent(e, item, required);
+ } else if (type == "optional" || type == "choice"
+ || type == "zeroOrMore") {
+ parseContent(e, item, false);
+ } else if (type == "ref") {
+ item.referencedDeclares.insert(name);
+ if (required) {
+ item.requiredReferencedDeclares.insert(name);
+ }
+ } else if (type == "empty") {
+ } else if (type == "data") {
+ Datatype d;
+ d.type = mapType(e.attribute("type"));
+ item.datatype.insert(d);
+ } else if (type == "list") {
+ } else if (type == "description") {
+ } else if (type == "attribute") {
+ parseAttribute(e, item, required);
+ } else if (type == "element") {
+ parseElement(e, item, required);
+ } else if (type == "text") {
+ item.mixedContent = true;
+ } else if (type == "value") {
+ Datatype d;
+ d.constant = e.text();
+ item.datatype.insert(d);
+ } else if (type == "name") {
+ } else if (type == "anyName") {
+ } else if (type == "mixed") {
+ } else {
+ fatal() << "Unknown element " << type;
+ }
+ e = e.nextSiblingElement();
+ }
+}
+
+/**
+ * Parse the contents of a <define/> or <start/> element.
+ */
+RNGItemPtr parseDefine(QDomElement defineElement, RNGItems& items, bool isstart)
+{
+ RNGItemPtr item;
+ if (isstart) {
+ item = RNGItemPtr(new Start());
+ } else {
+ item = RNGItemPtr(new Define(defineElement.attribute("name")));
+ }
+ parseContent(defineElement, *item, true);
+ items.insert(item);
+ return item;
+}
+
+/**
+ * Parse all top level Relax NG elements.
+ */
+RNGItemPtr getDefines(QDomElement e, RNGItems& items)
+{
+ RNGItemPtr start = RNGItemPtr(0);
+ e = e.firstChildElement();
+ while (!e.isNull()) {
+ if (e.localName() == "define") {
+ parseDefine(e, items, false);
+ } else if (e.localName() == "start") {
+ assert(!start, "Multiple start elements.");
+ start = parseDefine(e, items, true);
+ } else {
+ fatal() << "Unknown element " << e.localName();
+ }
+ e = e.nextSiblingElement();
+ }
+ return start;
+}
+
+/**
+ * Load an XML from disk into a DOMDocument instance.
+ */
+QDomDocument loadDOM(QString url)
+{
+ QFile f(url);
+ f.open(QIODevice::ReadOnly);
+ QByteArray data = f.readAll();
+ f.close();
+
+ QDomDocument dom;
+ QString err;
+ if (!dom.setContent(data, true, &err)) {
+ qFatal("%s", err.ascii());
+ }
+ return dom;
+}
+
+/**
+ * Look through a set of RNGitems to find one that is the same.
+ * This can be used after parsing to find definitions that are the same.
+ * Such deduplication can reduce the size of the generated code.
+ */
+RNGItemPtr findEqualItem(const RNGItemPtr&i, const RNGItems& items)
+{
+ foreach (const RNGItemPtr& j, items) {
+ if (*i == *j) {
+ return j;
+ }
+ }
+ return RNGItemPtr();
+}
+
+/**
+ * Compare two RNGItem instances.
+ */
+bool RNGItem::operator==(const RNGItem& a) const
+{
+ bool unequal = m_type != a.m_type
+ || m_name != a.m_name
+ || mixedContent != a.mixedContent
+ || cppName != a.cppName
+ || referencedDeclares != a.referencedDeclares
+ || requiredReferencedDeclares != a.requiredReferencedDeclares
+ || allowedItems.size() != a.allowedItems.size()
+ || requiredItems.size() != a.requiredItems.size()
+ || datatype != a.datatype;
+ if (unequal) {
+ return false;
+ }
+ foreach (const RNGItemPtr& i, allowedItems) {
+ RNGItemPtr j = findEqualItem(i, a.allowedItems);
+ if (!j) {
+ return false;
+ }
+ }
+ foreach (const RNGItemPtr& i, requiredItems) {
+ RNGItemPtr j = findEqualItem(i, a.requiredItems);
+ if (!j) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Move all member items in the global list.
+ * If there is already a global member that is equal, use that in the item.
+ */
+void collect(RNGItem& item, RNGItems& collected)
+{
+ typedef QPair<RNGItemPtr,RNGItemPtr> Pair;
+ QList<Pair> toSwap;
+ foreach (const RNGItemPtr& i, item.allowedItems) {
+ RNGItemPtr j = findEqualItem(i, collected);
+ if (!j) {
+ collected.insert(i);
+ collect(*i, collected);
+ } else if (i != j) {
+ toSwap.append(qMakePair(i, j));
+ }
+ }
+ foreach (const Pair& i, toSwap) {
+ RNGItemPtr toRemove = i.first;
+ RNGItemPtr toAdd = i.second;
+ if (item.requiredItems.contains(toRemove)) {
+ item.requiredItems.remove(toRemove);
+ item.requiredItems.insert(toAdd);
+ }
+ item.allowedItems.remove(toRemove);
+ item.allowedItems.insert(toAdd);
+ }
+}
+
+/**
+ * Move all member items in the global list.
+ * If there is already a global member that is equal, use that in the item.
+ */
+void collect(const RNGItems& items, RNGItems& collected)
+{
+ foreach (const RNGItemPtr& item, items) {
+ collect(*item, collected);
+ }
+}
+
+/**
+ * Count how often a particular item is used by other items or itself.
+ */
+void countUsage(RNGItem& item, QMap<RNGItemPtr,int>& usageCount)
+{
+ foreach (const RNGItemPtr& i, item.allowedItems) {
+ if (usageCount.contains(i)) {
+ usageCount[i]++;
+ } else {
+ usageCount[i] = 1;
+ }
+ }
+}
+
+/**
+ * Remove items that are not used and merge items that are used in only one
+ * place into their parent if possible.
+ * This reduces the number of classes in the generated headers.
+ */
+int reduce(RNGItems& items)
+{
+ QMap<RNGItemPtr,int> usageCount;
+ foreach (const RNGItemPtr& item, items) {
+ countUsage(*item, usageCount);
+ }
+ RNGItems toRemove;
+ foreach (RNGItemPtr item, items) {
+ if (usageCount[item] <= 1 && !item->isStart() && item->isDefine()) {
+ RNGItemPtr user = RNGItemPtr(0);
+ foreach (const RNGItemPtr& i, items) {
+ if (i->allowedItems.contains(item)) {
+ assert(!user, "");
+ user = i;
+ }
+ }
+ if (user) {
+ merge(user, item);
+ }
+ toRemove.insert(item);
+ break;
+ }
+ }
+ foreach (const RNGItemPtr& item, toRemove) {
+ items.remove(item);
+ }
+ return toRemove.size();
+}
+
+/**
+ * Collect items that are contained in other items into a list with
+ * all the items.
+ * Relax NG is a hierarchical file format and this function creates a flat list
+ * with all items.
+ */
+int expand(RNGItems& items)
+{
+ RNGItems toAdd;
+ foreach (RNGItemPtr item, items) {
+ foreach (const RNGItemPtr& i, item->allowedItems) {
+ if (!items.contains(i)) {
+ toAdd.insert(i);
+ }
+ }
+ }
+ foreach (RNGItemPtr item, toAdd) {
+ items.insert(item);
+ }
+ return toAdd.size();
+}
+
+/**
+ * Find the <define/> item by name.
+ */
+RNGItemPtr getDefine(const QString& name, const RNGItems& items)
+{
+ RNGItemPtr item = RNGItemPtr(0);
+ foreach (RNGItemPtr i, items) {
+ if (i->name() == name) {
+ assert(!item, "Doubly defined element " + name + ".");
+ item = i;
+ }
+ }
+ assert(item, "Define not found " + name);
+ return item;
+}
+
+/**
+ * Resolve all <define/> references.
+ * After parsing, the <ref/> instances should be replaced by the actual
+ * items.
+ */
+void resolveDefines(RNGItemPtr start, const RNGItems& items, RNGItems& resolved)
+{
+ if (resolved.contains(start)) {
+ return;
+ }
+ resolved.insert(start);
+ foreach (const QString& name, start->referencedDeclares) {
+ RNGItemPtr i = getDefine(name, items);
+ if (start->requiredReferencedDeclares.contains(name)) {
+ start->requiredItems.insert(i);
+ }
+ start->allowedItems.insert(i);
+ }
+ start->referencedDeclares.clear();
+ start->requiredReferencedDeclares.clear();
+
+ foreach (RNGItemPtr item, start->allowedItems) {
+ resolveDefines(item, items, resolved);
+ }
+}
+
+/**
+ * Create a C++ name from the item name.
+ */
+QString makeCppName(const RNGItemPtr&item)
+{
+ QString name;
+ if (item->isElement() || item->isAttribute()) {
+ name = item->name();
+ } else {
+ name = "group_" + item->name();
+ }
+ name.replace(':', '_');
+ name.replace('-', '_');
+ return name;
+}
+
+/**
+ * Create a new name from the item name.
+ * The new name will not clash with the names from takenNames.
+ */
+QString makeUniqueCppName(const RNGItemPtr&item, QSet<QString>& takenNames)
+{
+ QString n = makeCppName(item);
+ QString name = n;
+ int i = 0;
+ while (!name.isEmpty() && takenNames.contains(name)) {
+ name = n + "_" + QString::number(++i);
+ }
+ takenNames.insert(name);
+ return name;
+}
+
+/**
+ * Create all the C++ names corresponding with the Relax NG items.
+ */
+void makeCppNames(RNGItemList& items)
+{
+ QSet<QString> cppnames;
+ // handle elements first so they have the nicest names
+ foreach (RNGItemPtr item, items) {
+ if (item->isElement()) {
+ item->cppName = makeUniqueCppName(item, cppnames);
+ }
+ }
+ // next handle the attributes
+ foreach (RNGItemPtr item, items) {
+ if (item->isAttribute()) {
+ item->cppName = makeUniqueCppName(item, cppnames);
+ }
+ }
+ // give the remaining declares names
+ foreach (RNGItemPtr item, items) {
+ if (item->isDefine()) {
+ item->cppName = makeUniqueCppName(item, cppnames);
+ }
+ }
+}
+
+/**
+ * Find all the items that are used in this item but are not element or
+ * attributes.
+ * These items will be base classes to a class that corresponds to an element.
+ */
+RNGItemList getBasesList(RNGItemPtr item)
+{
+ RNGItems list;
+ RNGItems antilist;
+ foreach (RNGItemPtr i, item->allowedItems) {
+ if (i->isDefine()) {
+ list.insert(i);
+ foreach (RNGItemPtr j, i->allowedItems) {
+ if (j->isDefine() && j != i) {
+ antilist.insert(j);
+ }
+ }
+ }
+ }
+ list.subtract(antilist);
+ RNGItemList l = list.toList().toVector();
+ qStableSort(l.begin(), l.end(), rngItemPtrLessThan);
+ return l;
+}
+
+/**
+ * Sort items in the set.
+ * This is helpful in making the output reproducible.
+ */
+RNGItemList list(const RNGItems& items)
+{
+ RNGItemList list = items.toList().toVector();
+ qStableSort(list.begin(), list.end(), rngItemPtrLessThan);
+ return list;
+}
+
+/**
+ * Collect the data types of the attribute item.
+ */
+void resolveType(const RNGItemPtr& item, QSet<Datatype>& type)
+{
+ type.unite(item->datatype);
+ foreach (const RNGItemPtr& i, item->allowedItems) {
+ resolveType(i, type);
+ }
+}
+
+/**
+ * Collect the data types of the attributes.
+ */
+void resolveAttributeDataTypes(const RNGItems& items)
+{
+ foreach (const RNGItemPtr& i, items) {
+ if (i->isAttribute()) {
+ resolveType(i, i->datatype);
+ }
+ }
+}
+
+/**
+ * Create a ordered list of items.
+ * The order is such that dependencies of an item precede the item in the list.
+ */
+void addInOrder(RNGItemList& undefined, RNGItemList& defined)
+{
+ int last = -1;
+ while (last != undefined.size()) {
+ last = undefined.size();
+ for (int i = 0; i < undefined.size(); ++i) {
+ const RNGItemPtr& ii = undefined[i];
+ bool missingDependency = false;
+ foreach (const RNGItemPtr& j, list(ii->allowedItems)) {
+ if (j->isDefine() && !defined.contains(j) && j != ii) {
+ if (undefined.contains(j)) {
+ missingDependency = true;
+ } else if (j->name().isEmpty()) {
+ ii->allowedItems.remove(j);
+ ii->requiredItems.remove(j);
+ }
+ }
+ }
+ if (!missingDependency) {
+ defined.append(ii);
+ undefined.remove(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+ if (undefined.size()) {
+ fatal() << undefined.size() << " missing dependencies";
+ undefined.clear();
+ }
+ // Q_ASSERT(undefined.size() == 0);
+}
+
+/**
+ * Helper structure to collect required arguments.
+ */
+struct RequiredArgsList
+{
+ int length;
+ QString args;
+ QString vals;
+};
+
+/**
+ * Write lists of required arguments that can be used in generated code.
+ * This list only covers the required attributes, not required elements.
+ */
+RequiredArgsList makeRequiredArgsList(const RNGItemPtr& item)
+{
+ RequiredArgsList r;
+ r.length = 0;
+ foreach (RNGItemPtr i, list(item->requiredItems)) {
+ if (i->isAttribute() && i->singleConstant().isNull()) {
+ QString name = makeCppName(i);
+ QString type = i->singleType();
+ if (type.isNull()) {
+ type = "const QString&";
+ }
+ r.args += type + " " + name + ", ";
+ r.vals += ", " + name;
+ ++r.length;
+ }
+ }
+ r.args = r.args.left(r.args.length() - 2);
+ return r;
+}
+
+/**
+ * Recursively find the items that are required for the given item.
+ */
+RNGItemList getAllRequiredAttributes(const RNGItemPtr& item, RNGItemList& list, int \
depth = 0) +{
+ if (depth > 10) {
+ return list;
+ }
+ foreach (RNGItemPtr i, item->allowedItems) {
+ if (item->requiredItems.contains(i)) {
+ if (i->isAttribute() && i->singleConstant().isNull()) {
+ list.append(i);
+ } else if (i->isDefine()) {
+ getAllRequiredAttributes(i, list, depth + 1);
+ }
+ }
+ }
+ return list;
+}
+
+/**
+ * Write full lists of required arguments that can be used in generated code.
+ */
+RequiredArgsList makeFullRequiredArgsList(const RNGItemPtr& item)
+{
+ RequiredArgsList r;
+ r.length = 0;
+ RNGItemList list;
+ getAllRequiredAttributes(item, list);
+ qStableSort(list.begin(), list.end(), rngItemPtrLessThan);
+ foreach (RNGItemPtr i, list) {
+ QString name = makeCppName(i);
+ QString type = i->singleType();
+ if (type.isNull()) {
+ type = "const QString&";
+ }
+ r.args += type + " " + name + ", ";
+ r.vals += ", " + name;
+ ++r.length;
+ }
+ r.args = r.args.left(r.args.length() - 2);
+ return r;
+}
+
+/**
+ * Write C++ code to set the required attribute values.
+ */
+void setRequiredAttributes(QTextStream& out, const RNGItemPtr& item)
+{
+ QString o;
+ if (!item->isElement()) {
+ o = "xml.";
+ }
+ foreach (RNGItemPtr i, list(item->requiredItems)) {
+ if (i->isAttribute()) {
+ out << " " << o << "addAttribute(\"" + i->name() + "\", ";
+ QString constant = i->singleConstant();
+ if (constant.isNull()) {
+ out << makeCppName(i);
+ } else {
+ out << "\"" << constant << "\"";
+ }
+ out << ");\n";
+ }
+ }
+}
+
+/**
+ * Write the class definition for a class that corresponds to an xml element.
+ */
+void defineElement(QTextStream& out, const RNGItemPtr& item)
+{
+ RNGItemList bases = getBasesList(item);
+ out << "class " << item->cppName << " : public OdfWriter";
+ RNGItemList::const_iterator i = bases.begin();
+ while (i != bases.end()) {
+ out << ", public " << (*i)->cppName;
+ ++i;
+ }
+ out << " {\n";
+ out << "public:" << "\n";
+ RequiredArgsList r = makeFullRequiredArgsList(item);
+ if (r.args.length()) {
+ r.args = ", " + r.args;
+ }
+ out << " " << item->cppName << "(OdfWriter* x" << r.args
+ << ") :OdfWriter(x, \"" << item->name() << "\", "
+ << (isMixed(item) ?"false" :"true") << ")";
+ i = bases.begin();
+ while (i != bases.end()) {
+ RequiredArgsList r;
+ if (item->requiredItems.contains(*i)) {
+ r = makeFullRequiredArgsList(*i);
+ }
+ out << ", " << (*i)->cppName << "(*static_cast<OdfWriter*>(this)" << r.vals \
<< ")"; + ++i;
+ }
+ out << " {\n";
+ setRequiredAttributes(out, item);
+ out << " }\n";
+ out << " " << item->cppName << "(KoXmlWriter* x" << r.args
+ << ") :OdfWriter(x, \"" << item->name() << "\", "
+ << (isMixed(item) ?"false" :"true") << ")";
+ i = bases.begin();
+ while (i != bases.end()) {
+ RequiredArgsList r;
+ if (item->requiredItems.contains(*i)) {
+ r = makeFullRequiredArgsList(*i);
+ }
+ out << ", " << (*i)->cppName << "(*static_cast<OdfWriter*>(this)" << r.vals \
<< ")"; + ++i;
+ }
+ out << " {\n";
+ setRequiredAttributes(out, item);
+ out << " }\n";
+ QSet<QString> doneA;
+ QSet<QString> doneE;
+ foreach (RNGItemPtr i, list(item->allowedItems)) {
+ QString name = makeCppName(i);
+ if (i->isAttribute() && !item->requiredItems.contains(i) && \
!doneA.contains(name)) { + QString type = i->singleType();
+ type = (type.isNull()) ?"const QString&" :type;
+ out << " template<class T>\n";
+ out << " void set_" << name << "(const T& value) {\n";
+ out << " addAttribute(\"" + i->name() + "\", value);\n";
+ out << " }\n";
+ doneA.insert(name);
+ } else if (i->isElement() && !doneE.contains(name)) {
+ RequiredArgsList r = makeFullRequiredArgsList(i);
+ out << " " << i->cppName << " add_" << name << "(" + r.args + ");\n";
+ doneE.insert(name);
+ }
+ }
+ if (isMixed(item)) {
+ out << " void addTextNode(const QString& data) {\n";
+ out << " OdfWriter::addTextNode(data);\n";
+ out << " }\n";
+ }
+ out << "};\n";
+}
+
+/**
+ * Write the class definition for a class that corresponds to a Relax NG group.
+ * These groups are bases to classes that correspond to elements.
+ */
+void defineGroup(QTextStream& out, const RNGItemPtr& item)
+{
+ RNGItemList bases = getBasesList(item);
+ out << "class " << item->cppName;
+ if (bases.size()) {
+ RNGItemList::const_iterator i = bases.begin();
+ out << " : public " << (*i)->cppName;
+ while (++i != bases.end()) {
+ out << ", public " << (*i)->cppName;
+ }
+ }
+ out << " {\n";
+ out << "private:\n";
+ out << " OdfWriter& xml;\n";
+ out << "public:\n";
+ RequiredArgsList r = makeFullRequiredArgsList(item);
+ if (r.args.length()) {
+ r.args = ", " + r.args;
+ }
+ out << " " << item->cppName << "(OdfWriter& x" + r.args + ") :";
+ foreach (const RNGItemPtr& i, bases) {
+ RequiredArgsList r;
+ if (item->requiredItems.contains(i)) {
+ r = makeFullRequiredArgsList(i);
+ }
+ out << i->cppName << "(x" + r.vals + "), ";
+ }
+ out << "xml(x) {\n";
+ setRequiredAttributes(out, item);
+ out << " }\n";
+ if (r.length) {
+ out << " " << item->cppName << "(OdfWriter& x) :";
+ foreach (const RNGItemPtr& i, bases) {
+ out << i->cppName << "(x), ";
+ }
+ out << "xml(x) {}\n";
+ }
+ QSet<QString> done;
+ foreach (RNGItemPtr i, list(item->allowedItems)) {
+ QString name = makeCppName(i);
+ // also allow setting of required elements, because the might need to be
+ // set in elements where the group is optional
+ // && !item->requiredItems.contains(i)
+ if (i->isAttribute() && !done.contains(name)) {
+ QString type = i->singleType();
+ type = (type.isNull()) ?"const QString&" :type;
+ out << " template<class T>\n";
+ out << " void set_" << name << "(const T& value) {\n";
+ out << " xml.addAttribute(\"" + i->name() + "\", value);\n";
+ out << " }\n";
+ done.insert(name);
+ } else if (i->isElement()) {
+ RequiredArgsList r = makeFullRequiredArgsList(i);
+ out << " " << i->cppName << " add_" << name << "(" + r.args + ");\n";
+ }
+ }
+ if (isMixed(item)) {
+ out << " void addTextNode(const QString& data) {\n";
+ out << " xml.addTextNode(data);\n";
+ out << " }\n";
+ }
+ out << "};\n";
+}
+
+/**
+ * Write the definition for a member function to add an element to another
+ * element.
+ */
+void writeAdderDefinition(const RNGItemPtr& item, const RNGItemPtr& i, QTextStream& \
out) +{
+ QString name = makeCppName(i);
+ RequiredArgsList r = makeFullRequiredArgsList(i);
+ out << "inline ";
+ if (!ns.isEmpty()) {
+ out << ns << "::";
+ }
+ out << i->cppName << "\n";
+ if (!ns.isEmpty()) {
+ out << ns << "::";
+ }
+ out << item->cppName << "::add_" << name << "(";
+ out << r.args << ") {\n";
+ out << " return " << ns << "::" << i->cppName << "(";
+ if (item->isElement()) {
+ out << "this";
+ } else {
+ out << "&xml";
+ }
+ out << r.vals << ");\n";
+ out << "}\n";
+}
+
+/**
+ * Write the definitions for member functions to add elements to other
+ * element.
+ */
+void writeAdderDefinitions(const RNGItemPtr& item, Files& files)
+{
+ QSet<QString> done;
+ foreach (RNGItemPtr i, list(item->allowedItems)) {
+ QString name = makeCppName(i);
+ if (i->isElement() && !done.contains(name)) {
+ QString tag1 = (item->isElement()) ?item->name() :QString();
+ QTextStream& out = files.getFile(tag1, i->name());
+ writeAdderDefinition(item, i, out);
+ done.insert(name);
+ }
+ }
+}
+
+/**
+ * Write the definitions for member functions to add elements to other
+ * element.
+ */
+void writeAdderDefinitions(const RNGItemList& items, Files& files)
+{
+ foreach (RNGItemPtr item, items) {
+ writeAdderDefinitions(item, files);
+ }
+}
+
+/**
+ * Retrieve the namespace prefix from the tag name.
+ */
+QString getPrefix(const QString& tag)
+{
+ QString prefix = tag.left(tag.indexOf(":"));
+ if (prefix.isNull()) {
+ prefix = "";
+ }
+ return prefix;
+}
+
+/**
+ * Get the stream for the combination of the two tags.
+ * For tag1 = "office:text" and tag2 = "text:p", this returns a stream to a file
+ * "writeodfofficetext.h".
+ */
+QTextStream& Files::getFile(const QString& tag1, const QString& tag2 = QString())
+{
+ // each tag can result in either no prefix or a prefix
+ // if one if the prefixes is empty and the other is not, then the first
+ // prefix is given a value
+ QString prefix = getPrefix(tag1);
+ QString prefix2 = getPrefix(tag2);
+ if (prefix.isEmpty() || prefix == prefix2) {
+ prefix = prefix2;
+ prefix2 = "";
+ }
+ if (files.contains(prefix) && files[prefix].contains(prefix2)) {
+ return *files[prefix][prefix2];
+ }
+ QString name = "writeodf" + prefix + prefix2 + ".h";
+ QString path = outdir + "/" + name;
+ QFile* file = new QFile(path);
+ if (!file->open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
+ fatal() << file->errorString();
+ }
+ QTextStream* out = new QTextStream(file);
+ name = name.replace(".", "_").toUpper();
+
+ *out << "#ifndef " + name + "\n";
+ *out << "#define " + name + "\n";
+ if (name == "WRITEODF_H") {
+ *out << "#include \"writeodf/odfwriter.h\"\n";
+ } else {
+ *out << "#include \"writeodf.h\"\n";
+ }
+ if (!prefix2.isEmpty() && prefix2 != prefix) {
+ *out << "#include \"writeodf" + prefix + ".h\"\n";
+ *out << "#include \"writeodf" + prefix2 + ".h\"\n";
+ } else {
+ *out << "namespace " << ns << " {\n";
+ }
+ files[prefix][prefix2] = out;
+ return *out;
+}
+
+/**
+ * Close the namespace if it was opened previously.
+ */
+void Files::closeNamespace()
+{
+ typedef const QMap<QString,QTextStream*> map;
+ foreach (map& m, files) {
+ map::ConstIterator i = m.begin();
+ while (i != m.end()) {
+ if (!i.key().isNull() && !ns.isEmpty()) {
+ *i.value() << "}\n";
+ }
+ ++i;
+ }
+ }
+}
+
+/**
+ * Write the header files.
+ */
+void write(const RNGItemList& items, QString outdir)
+{
+ Files files(outdir);
+
+ QTextStream& out = files.getFile("", "");
+ RNGItemList undefined = items;
+ RNGItemList defined;
+ addInOrder(undefined, defined);
+ // declare all classes
+ foreach (RNGItemPtr item, defined) {
+ if (!item->isAttribute()) {
+ out << "class " << item->cppName << ";\n";
+ }
+ }
+ foreach (RNGItemPtr item, defined) {
+ if (item->isElement()) {
+ defineElement(files.getFile(item->name()), item);
+ } else if (item->isDefine()) {
+ defineGroup(out, item);
+ }
+ }
+ files.closeNamespace();
+ writeAdderDefinitions(defined, files);
+}
+
+/**
+ * Convert the given rng file to a collection of header files.
+ */
+void convert(const QString& rngfile, const QString& outdir)
+{
+ QDomDocument dom = loadDOM(rngfile);
+ RNGItems items;
+ RNGItemPtr start = getDefines(dom.documentElement(), items);
+ RNGItems collected;
+ //qDebug() << "define " << items.size();
+ //collect(items, collected);
+ collected = items;
+ //qDebug() << "collect " << collected.size();
+ RNGItems resolved;
+ resolveDefines(start, collected, resolved);
+ //qDebug() << "resolve " << resolved.size();
+ //while (expand(resolved)) {}
+ resolved.remove(start);
+ //qDebug() << "expand " << resolved.size();
+ resolveAttributeDataTypes(resolved);
+ while (reduce(resolved)) {}
+ //qDebug() << "reduce " << resolved.size();
+ RNGItemList list = resolved.toList().toVector();
+ //qDebug() << "filteredItems " << list.size();
+ qStableSort(list.begin(), list.end(), rngItemPtrLessThan);
+ makeCppNames(list);
+ write(list, outdir);
+ //qDebug() << list.size();
+}
+
+int main(int argc, char *argv[])
+{
+ QString rngfile;
+ QString outdir;
+ if (argc != 3) {
+ fatal() << "Usage " << argv[0] << " rngfile outputdir";
+ } else {
+ rngfile = argv[1];
+ outdir = argv[2];
+ }
+ convert(rngfile, outdir);
+ return 0;
+}
diff --git a/filters/libmso/CMakeLists.txt b/filters/libmso/CMakeLists.txt
index 6bc145f..b7bdd99 100644
--- a/filters/libmso/CMakeLists.txt
+++ b/filters/libmso/CMakeLists.txt
@@ -1,4 +1,5 @@
-include_directories( ${QT_INCLUDES} ${KDE4_INCLUDES} ../../libs/odf)
+include_directories( ${QT_INCLUDES} ${KDE4_INCLUDES}
+ ${KOODF_INCLUDES})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}")
if(NOT MSVC AND NOT (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel"))
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "SunPro")
diff --git a/filters/libmso/shapes.cpp b/filters/libmso/shapes.cpp
index 073e061..b5a6d99 100644
--- a/filters/libmso/shapes.cpp
+++ b/filters/libmso/shapes.cpp
@@ -32,6 +32,7 @@
#include "drawstyle.h"
#include "msodraw.h"
#include "generated/leinputstream.h"
+#include "writeodf/writeodfdraw.h"
#include <KoXmlWriter.h>
#include <kdebug.h>
@@ -43,17 +44,7 @@
using namespace MSO;
-
-namespace
-{
-void equation(Writer& out, const char* name, const char* formula)
-{
- out.xml.startElement("draw:equation");
- out.xml.addAttribute("draw:name", name);
- out.xml.addAttribute("draw:formula", formula);
- out.xml.endElement();
-}
-}
+using namespace writeodf;
qint16
ODrawToOdf::normalizeRotation(qreal rotation)
@@ -125,27 +116,23 @@ void ODrawToOdf::processRectangle(const OfficeArtSpContainer& \
o, Writer& out) // see bug https://bugs.kde.org/show_bug.cgi?id=285577
processPictureFrame(o, out);
} else {
- out.xml.startElement("draw:custom-shape");
+ draw_custom_shape rect(&out.xml);
processStyleAndText(o, out);
- out.xml.startElement("draw:enhanced-geometry");
- out.xml.addAttribute("svg:viewBox", "0 0 21600 21600");
- out.xml.addAttribute("draw:enhanced-path", "M 0 0 L 21600 0 21600 21600 \
0 21600 0 0 Z N");
- out.xml.addAttribute("draw:type", "rectangle");
+ draw_enhanced_geometry eg(rect.add_draw_enhanced_geometry());
+ eg.set_svg_viewBox("0 0 21600 21600");
+ eg.set_draw_enhanced_path("M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z \
N"); + eg.set_draw_type("rectangle");
setShapeMirroring(o, out);
- out.xml.endElement(); // draw:enhanced-geometry
- out.xml.endElement(); // draw:custom-shape
}
}
}
void ODrawToOdf::processTextBox(const OfficeArtSpContainer& o, Writer& out)
{
- out.xml.startElement("draw:frame");
+ draw_frame frame(&out.xml);
processStyle(o, out);
- out.xml.startElement("draw:text-box");
+ draw_text_box text(frame.add_draw_text_box());
processText(o, out);
- out.xml.endElement(); // draw:text-box
- out.xml.endElement(); // draw:frame
}
void ODrawToOdf::processLine(const OfficeArtSpContainer& o, Writer& out)
@@ -164,16 +151,14 @@ void ODrawToOdf::processLine(const OfficeArtSpContainer& o, \
Writer& out) qSwap(x1, x2);
}
- out.xml.startElement("draw:line");
- out.xml.addAttribute("svg:y1", client->formatPos(out.vOffset(y1)));
- out.xml.addAttribute("svg:y2", client->formatPos(out.vOffset(y2)));
- out.xml.addAttribute("svg:x1", client->formatPos(out.hOffset(x1)));
- out.xml.addAttribute("svg:x2", client->formatPos(out.hOffset(x2)));
+ draw_line line(&out.xml,
+ client->formatPos(out.hOffset(x1)),
+ client->formatPos(out.hOffset(x2)),
+ client->formatPos(out.vOffset(y1)),
+ client->formatPos(out.vOffset(y2)));
addGraphicStyleToDrawElement(out, o);
- out.xml.addAttribute("draw:layer", "layout");
+ line.set_draw_layer("layout");
processText(o, out);
-
- out.xml.endElement();
}
void ODrawToOdf::drawStraightConnector1(qreal l, qreal t, qreal r, qreal b, Writer& \
out, QPainterPath &shapePath) const @@ -385,6 +370,7 @@ void \
ODrawToOdf::processConnector(const OfficeArtSpContainer& o, Writer& out, Pa
m.translate( shapeRect.center().x(), shapeRect.center().y() );
+ // the viewbox should be set, where is this done for draw:connector?
out.xml.startElement("draw:connector");
addGraphicStyleToDrawElement(out, o);
out.xml.addAttribute("draw:layer", "layout");
@@ -417,7 +403,7 @@ void ODrawToOdf::processPictureFrame(const OfficeArtSpContainer& \
o, Writer& out) // A value of 0x00000000 MUST be ignored. [MS-ODRAW] — v20101219
if (!ds.pib()) return;
- out.xml.startElement("draw:frame");
+ draw_frame frame(&out.xml);
processStyle(o, out);
//NOTE: OfficeArtClienData might contain additional information
@@ -429,26 +415,21 @@ void ODrawToOdf::processPictureFrame(const \
OfficeArtSpContainer& o, Writer& out) }
// if the image cannot be found, just place an empty frame
if (url.isEmpty()) {
- out.xml.endElement(); //draw:frame
return;
}
- out.xml.startElement("draw:image");
- out.xml.addAttribute("xlink:href", url);
- out.xml.addAttribute("xlink:type", "simple");
- out.xml.addAttribute("xlink:show", "embed");
- out.xml.addAttribute("xlink:actuate", "onLoad");
- out.xml.endElement(); // image
- out.xml.endElement(); // frame
+ draw_image image(frame.add_draw_image());
+ image.set_xlink_href(url);
+ image.set_xlink_type("simple");
+ image.set_xlink_show("embed");
+ image.set_xlink_actuate("onLoad");
}
void ODrawToOdf::processNotPrimitive(const MSO::OfficeArtSpContainer& o, Writer& \
out) {
- out.xml.startElement("draw:custom-shape");
+ draw_custom_shape shape(&out.xml);
processStyleAndText(o, out);
- out.xml.startElement("draw:enhanced-geometry");
+ draw_enhanced_geometry eg(shape.add_draw_enhanced_geometry());
setEnhancedGeometry(o, out);
- out.xml.endElement(); //draw:enhanced-geometry
- out.xml.endElement(); //draw:custom-shape
}
diff --git a/filters/libmso/shapes2.cpp b/filters/libmso/shapes2.cpp
index 0f0b906..6146f78 100644
--- a/filters/libmso/shapes2.cpp
+++ b/filters/libmso/shapes2.cpp
@@ -19,18 +19,24 @@
#include <KoXmlWriter.h>
#include "drawstyle.h"
+#include "writeodf/writeodfdraw.h"
using namespace MSO;
+using namespace writeodf;
namespace
{
void equation(Writer& out, const char* name, const char* formula)
{
- out.xml.startElement("draw:equation");
- out.xml.addAttribute("draw:name", name);
- out.xml.addAttribute("draw:formula", formula);
- out.xml.endElement();
-
+ draw_equation eq(&out.xml);
+ eq.set_draw_name(name);
+ eq.set_draw_formula(formula);
+}
+void equation(draw_enhanced_geometry& eg, const char* name, const char* formula)
+{
+ draw_equation eq(eg.add_draw_equation());
+ eq.set_draw_name(name);
+ eq.set_draw_formula(formula);
}
}
@@ -51,67 +57,59 @@ void equation(Writer& out, const char* name, const char* formula)
void ODrawToOdf::processParallelogram(const MSO::OfficeArtSpContainer& o, Writer& \
out) {
- out.xml.startElement("draw:custom-shape");
+ draw_custom_shape shape(&out.xml);
processStyleAndText(o, out);
- out.xml.startElement("draw:enhanced-geometry");
- out.xml.addAttribute("draw:glue-points", "?f6 0 10800 ?f8 ?f11 10800 ?f9 21600 \
10800 ?f10 ?f5 10800"); + draw_enhanced_geometry \
eg(shape.add_draw_enhanced_geometry()); + eg.set_draw_glue_points("?f6 0 10800 ?f8 \
?f11 10800 ?f9 21600 10800 ?f10 ?f5 10800"); processModifiers(o, out, QList<int>() \
<< 5400);
- out.xml.addAttribute("svg:viewBox", "0 0 21600 21600");
- out.xml.addAttribute("draw:enhanced-path", "M ?f0 0 L 21600 0 ?f1 21600 0 21600 \
Z N");
- out.xml.addAttribute("draw:type", "parallelogram");
- out.xml.addAttribute("draw:text-areas", "?f3 ?f3 ?f4 ?f4");
- setShapeMirroring(o, out);
- equation(out,"f0","$0 ");
- equation(out,"f1","21600-$0 ");
- equation(out,"f2","$0 *10/24");
- equation(out,"f3","?f2 +1750");
- equation(out,"f4","21600-?f3 ");
- equation(out,"f5","?f0 /2");
- equation(out,"f6","10800+?f5 ");
- equation(out,"f7","?f0 -10800");
- equation(out,"f8","if(?f7 ,?f13 ,0)");
- equation(out,"f9","10800-?f5 ");
- equation(out,"f10","if(?f7 ,?f12 ,21600)");
- equation(out,"f11","21600-?f5 ");
- equation(out,"f12","21600*10800/?f0 ");
- equation(out,"f13","21600-?f12 ");
- out.xml.startElement("draw:handle");
- out.xml.addAttribute("draw:handle-position", "$0 top");
- out.xml.addAttribute("draw:handle-range-x-maximum", "21600");
- out.xml.addAttribute("draw:handle-range-x-minimum", "0");
- out.xml.endElement(); // draw:handle
- out.xml.endElement(); // enhanced geometry
- out.xml.endElement(); // custom shape
+ eg.set_svg_viewBox("0 0 21600 21600");
+ eg.set_draw_enhanced_path("M ?f0 0 L 21600 0 ?f1 21600 0 21600 Z N");
+ eg.set_draw_type("parallelogram");
+ eg.set_draw_text_areas("?f3 ?f3 ?f4 ?f4");
+ setShapeMirroring(o, out);
+ equation(eg,"f0","$0 ");
+ equation(eg,"f1","21600-$0 ");
+ equation(eg,"f2","$0 *10/24");
+ equation(eg,"f3","?f2 +1750");
+ equation(eg,"f4","21600-?f3 ");
+ equation(eg,"f5","?f0 /2");
+ equation(eg,"f6","10800+?f5 ");
+ equation(eg,"f7","?f0 -10800");
+ equation(eg,"f8","if(?f7 ,?f13 ,0)");
+ equation(eg,"f9","10800-?f5 ");
+ equation(eg,"f10","if(?f7 ,?f12 ,21600)");
+ equation(eg,"f11","21600-?f5 ");
+ equation(eg,"f12","21600*10800/?f0 ");
+ equation(eg,"f13","21600-?f12 ");
+ draw_handle handle(eg.add_draw_handle("$0 top"));
+ handle.set_draw_handle_radius_range_maximum("21000");
+ handle.set_draw_handle_radius_range_minimum("0");
}
void ODrawToOdf::processTrapezoid(const MSO::OfficeArtSpContainer& o, Writer& out) {
- out.xml.startElement("draw:custom-shape");
+ draw_custom_shape shape(&out.xml);
processStyleAndText(o, out);
- out.xml.startElement("draw:enhanced-geometry");
- out.xml.addAttribute("draw:glue-points", "?f6 10800 10800 21600 ?f5 10800 10800 \
0"); + draw_enhanced_geometry eg(shape.add_draw_enhanced_geometry());
+ eg.set_draw_glue_points("?f6 10800 10800 21600 ?f5 10800 10800 0");
processModifiers(o, out, QList<int>() << 5400);
- out.xml.addAttribute("svg:viewBox", "0 0 21600 21600");
- out.xml.addAttribute("draw:enhanced-path", "M 0 0 L 21600 0 ?f0 21600 ?f1 21600 \
Z N");
- out.xml.addAttribute("draw:type", "trapezoid");
- out.xml.addAttribute("draw:text-areas", "?f3 ?f3 ?f4 ?f4");
- setShapeMirroring(o, out);
- equation(out,"f0","21600-$0 ");
- equation(out,"f1","$0 ");
- equation(out,"f2","$0 *10/18");
- equation(out,"f3","?f2 +1750");
- equation(out,"f4","21600-?f3 ");
- equation(out,"f5","$0 /2");
- equation(out,"f6","21600-?f5 ");
- out.xml.startElement("draw:handle");
- out.xml.addAttribute("draw:handle-position", "$0 bottom");
- out.xml.addAttribute("draw:handle-range-x-maximum", "10800");
- out.xml.addAttribute("draw:handle-range-x-minimum", "0");
- out.xml.endElement(); // draw:handle
- out.xml.endElement(); // enhanced geometry
- out.xml.endElement(); // custom shape
+ eg.set_svg_viewBox("0 0 21600 21600");
+ eg.set_draw_enhanced_path("M 0 0 L 21600 0 ?f0 21600 ?f1 21600 Z N");
+ eg.set_draw_type("trapezoid");
+ eg.set_draw_text_areas("?f3 ?f3 ?f4 ?f4");
+ setShapeMirroring(o, out);
+ equation(eg,"f0","21600-$0 ");
+ equation(eg,"f1","$0 ");
+ equation(eg,"f2","$0 *10/18");
+ equation(eg,"f3","?f2 +1750");
+ equation(eg,"f4","21600-?f3 ");
+ equation(eg,"f5","$0 /2");
+ equation(eg,"f6","21600-?f5 ");
+ draw_handle handle(eg.add_draw_handle("$0 bottom"));
+ handle.set_draw_handle_radius_range_maximum("10000");
+ handle.set_draw_handle_radius_range_minimum("0");
}
diff --git a/filters/sheets/excel/import/CMakeLists.txt \
b/filters/sheets/excel/import/CMakeLists.txt index 2466218..225032c 100644
--- a/filters/sheets/excel/import/CMakeLists.txt
+++ b/filters/sheets/excel/import/CMakeLists.txt
@@ -3,6 +3,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../sidewinder
${CMAKE_BINARY_DIR}/filters/
${KOMAIN_INCLUDES}
+ ${KOODF_INCLUDES}
${CMAKE_SOURCE_DIR}/filters/sheets/xlsx
${CMAKE_SOURCE_DIR}/filters/libmso
${CMAKE_SOURCE_DIR}/filters/libmsooxml
diff --git a/filters/sheets/excel/import/excelimporttoods.cc \
b/filters/sheets/excel/import/excelimporttoods.cc index de788d4..53ee4f0 100644
--- a/filters/sheets/excel/import/excelimporttoods.cc
+++ b/filters/sheets/excel/import/excelimporttoods.cc
@@ -50,6 +50,13 @@
#include <iostream>
#include "ODrawClient.h"
#include "ImportUtils.h"
+#include "writeodf/writeodfofficedc.h"
+#include "writeodf/writeodfofficemeta.h"
+#include "writeodf/writeodfofficestyle.h"
+#include "writeodf/writeodfofficetable.h"
+#include "writeodf/writeodftext.h"
+#include "writeodf/writeodfnumber.h"
+#include "writeodf/helpers.h"
K_PLUGIN_FACTORY(ExcelImportFactory, registerPlugin<ExcelImport>();)
K_EXPORT_PLUGIN(ExcelImportFactory("calligrafilters"))
@@ -58,6 +65,8 @@ K_EXPORT_PLUGIN(ExcelImportFactory("calligrafilters"))
#define UNICODE_GBP 0x00A3
#define UNICODE_JPY 0x00A5
+using namespace writeodf;
+
namespace Swinder
{
// qHash function to support hashing by Swinder::FormatFont instances.
@@ -114,6 +123,14 @@ public:
QHash<Cell*, QByteArray> cellShapes;
QHash<Sheet*, QByteArray> sheetShapes;
+ struct CellValue {
+ Value value;
+ QString str;
+ QString linkName;
+ QString linkLocation;
+ Hyperlink link;
+ };
+
QHash<Row*,int> rowsRepeatedHash;
int rowsRepeated(Row* row, int rowIndex);
@@ -130,17 +147,20 @@ public:
int rowFormatIndex;
int cellFormatIndex;
- void processWorkbookForBody(KoOdfWriteStore* store, Workbook* workbook, \
KoXmlWriter* xmlWriter); + void processWorkbookForBody(Workbook* workbook, \
KoXmlWriter* xmlWriter, office_body& body);
void processWorkbookForStyle(Workbook* workbook, KoXmlWriter* xmlWriter);
- void processSheetForBody(KoOdfWriteStore* store, Sheet* sheet, KoXmlWriter* \
xmlWriter); + void processSheetForBody(Sheet* sheet, KoXmlWriter* xmlWriter, \
office_spreadsheet& spreadsheet); void processSheetForStyle(Sheet* sheet, \
KoXmlWriter* xmlWriter); void processSheetForHeaderFooter(Sheet* sheet, KoXmlWriter* \
writer);
- void processHeaderFooterStyle(const QString& text, KoXmlWriter* xmlWriter);
- void processColumnForBody(Sheet* sheet, int columnIndex, KoXmlWriter* xmlWriter, \
unsigned& outlineLevel); + void processHeaderFooterStyle(const QString& text, \
text_p& p); + void processColumnForBody(Sheet* sheet, int columnIndex, \
group_table_columns_and_groups& table, unsigned& outlineLevel);
void processColumnForStyle(Sheet* sheet, int columnIndex, KoXmlWriter* \
xmlWriter);
- int processRowForBody(KoOdfWriteStore* store, Sheet* sheet, int rowIndex, \
KoXmlWriter* xmlWriter, unsigned& outlineLevel); + int processRowForBody(Sheet* \
sheet, int rowIndex, KoXmlWriter* xmlWriter, group_table_rows_and_groups& table, \
unsigned& outlineLevel);
int processRowForStyle(Sheet* sheet, int rowIndex, KoXmlWriter* xmlWriter);
- void processCellForBody(KoOdfWriteStore* store, Cell* cell, int rowsRepeat, \
KoXmlWriter* xmlWriter); + void processCellForBody(Cell* cell, KoXmlWriter* \
xmlWriter, table_table_row& row); + void processCellAttributesForBody(Cell* cell, \
group_table_table_cell_attlist& c, CellValue& cellValue); + void \
processCellText(Cell* cell, group_paragraph_content& content, CellValue& cellValue); \
+ void processCellContentForBody(Cell* cell, KoXmlWriter* xmlWriter, \
group_table_table_cell_content& c, CellValue& cellValue); void \
processCellForStyle(Cell* cell, KoXmlWriter* xmlWriter);
QString processCellFormat(const Format* format, const QString& formula = \
QString());
QString processRowFormat(Format* format, const QString& breakBefore = QString(), \
int rowRepeat = 1, double rowHeight = -1); @@ -339,17 +359,12 @@ bool \
ExcelImport::Private::createContent(KoOdfWriteStore* store) }
// FIXME this is dummy and hardcoded, replace with real font names
- contentWriter->startElement("office:font-face-decls");
- contentWriter->startElement("style:font-face");
- contentWriter->addAttribute("style:name", "Arial");
- contentWriter->addAttribute("svg:font-family", "Arial");
- contentWriter->endElement(); // style:font-face
- contentWriter->startElement("style:font-face");
- contentWriter->addAttribute("style:name", "Times New Roman");
- contentWriter->addAttribute("svg:font-family", "'Times New Roman'");
- contentWriter->endElement(); // style:font-face
- contentWriter->endElement(); // office:font-face-decls
-
+ office_font_face_decls decls(contentWriter);
+ style_font_face font(decls.add_style_font_face("Arial"));
+ font.set_svg_font_family("Arial");
+ style_font_face font2(decls.add_style_font_face("Times New Roman"));
+ font2.set_svg_font_family("'Times New Roman'");
+ decls.end();
defaultColumnStyleIndex = 0;
// office:automatic-styles
@@ -364,9 +379,9 @@ bool ExcelImport::Private::createContent(KoOdfWriteStore* store)
// office:body
- bodyWriter->startElement("office:body");
- processWorkbookForBody(store, workbook, bodyWriter);
- bodyWriter->endElement(); // office:body
+ office_body body(bodyWriter);
+ processWorkbookForBody(workbook, bodyWriter, body);
+ body.end();
return store->closeContentWriter();
}
@@ -383,29 +398,28 @@ bool ExcelImport::Private::createStyles(KoStore* store, \
KoXmlWriter* manifestWri KoXmlWriter* stylesWriter = new KoXmlWriter(&dev);
stylesWriter->startDocument("office:document-styles");
- stylesWriter->startElement("office:document-styles");
- stylesWriter->addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0");
- stylesWriter->addAttribute("xmlns:style", \
"urn:oasis:names:tc:opendocument:xmlns:style:1.0");
- stylesWriter->addAttribute("xmlns:text", \
"urn:oasis:names:tc:opendocument:xmlns:text:1.0");
- stylesWriter->addAttribute("xmlns:table", \
"urn:oasis:names:tc:opendocument:xmlns:table:1.0");
- stylesWriter->addAttribute("xmlns:draw", \
"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
- stylesWriter->addAttribute("xmlns:fo", \
"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");
- stylesWriter->addAttribute("xmlns:svg", \
"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");
- stylesWriter->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
- stylesWriter->addAttribute("xmlns:chart", \
"urn:oasis:names:tc:opendocument:xmlns:chart:1.0");
- stylesWriter->addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
- stylesWriter->addAttribute("xmlns:meta", \
"urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
- stylesWriter->addAttribute("xmlns:number", \
"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0");
- //stylesWriter->addAttribute("xmlns:dr3d", \
"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0");
- stylesWriter->addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML");
- stylesWriter->addAttribute("xmlns:of", \
"urn:oasis:names:tc:opendocument:xmlns:of:1.2");
- stylesWriter->addAttribute("office:version", "1.2");
+ office_document_styles styles(stylesWriter);
+ styles.addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0"); + \
styles.addAttribute("xmlns:style", \
"urn:oasis:names:tc:opendocument:xmlns:style:1.0"); + \
styles.addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0"); \
+ styles.addAttribute("xmlns:table", \
"urn:oasis:names:tc:opendocument:xmlns:table:1.0"); + \
styles.addAttribute("xmlns:draw", \
"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"); + \
styles.addAttribute("xmlns:fo", \
"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"); + \
styles.addAttribute("xmlns:svg", \
"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"); + \
styles.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); + \
styles.addAttribute("xmlns:chart", \
"urn:oasis:names:tc:opendocument:xmlns:chart:1.0"); + \
styles.addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"); + \
styles.addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"); \
+ styles.addAttribute("xmlns:number", \
"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"); + \
//styles.addAttribute("xmlns:dr3d", \
"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"); + \
styles.addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML"); + \
styles.addAttribute("xmlns:of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2");
mainStyles->saveOdfStyles(KoGenStyles::MasterStyles, stylesWriter);
mainStyles->saveOdfStyles(KoGenStyles::DocumentStyles, stylesWriter); // \
office:style
mainStyles->saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, stylesWriter); \
// office:automatic-styles
- stylesWriter->endElement(); // office:document-styles
+ styles.end();
stylesWriter->endDocument();
delete stylesWriter;
@@ -421,65 +435,58 @@ bool ExcelImport::Private::createMeta(KoOdfWriteStore* store)
KoStoreDevice dev(store->store());
KoXmlWriter* metaWriter = new KoXmlWriter(&dev);
metaWriter->startDocument("office:document-meta");
- metaWriter->startElement("office:document-meta");
- metaWriter->addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0");
- metaWriter->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
- metaWriter->addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
- metaWriter->addAttribute("xmlns:meta", \
"urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
- metaWriter->startElement("office:meta");
+
+ office_document_meta metadoc(metaWriter);
+ metadoc.addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0"); + \
metadoc.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); + \
metadoc.addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"); + \
metadoc.addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"); \
+ office_meta meta(metadoc.add_office_meta());
if (workbook->hasProperty(Workbook::PIDSI_TITLE)) {
- metaWriter->startElement("dc:title");
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_TITLE).toString());
- metaWriter->endElement();
+ meta.add_dc_title().addTextNode(
+ workbook->property(Workbook::PIDSI_TITLE).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_SUBJECT)) {
- metaWriter->startElement("dc:subject", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_SUBJECT).toString());
- metaWriter->endElement();
+ meta.add_dc_subject().addTextNode(
+ workbook->property(Workbook::PIDSI_SUBJECT).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_AUTHOR)) {
- metaWriter->startElement("dc:creator", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_AUTHOR).toString());
- metaWriter->endElement();
+ meta.add_dc_creator().addTextNode(
+ workbook->property(Workbook::PIDSI_AUTHOR).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_KEYWORDS)) {
- metaWriter->startElement("meta:keyword", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_KEYWORDS).toString());
- metaWriter->endElement();
+ meta.add_meta_keyword().addTextNode(
+ workbook->property(Workbook::PIDSI_KEYWORDS).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_COMMENTS)) {
- metaWriter->startElement("meta:comments", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_COMMENTS).toString());
- metaWriter->endElement();
+ meta_user_defined c(meta.add_meta_user_defined("comments"));
+ c.set_meta_value_type("string");
+ c.addTextNode(
+ workbook->property(Workbook::PIDSI_COMMENTS).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_REVNUMBER)) {
- metaWriter->startElement("meta:editing-cycles", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_REVNUMBER).toString());
- metaWriter->endElement();
+ meta.add_meta_editing_cycles().addTextNode(
+ workbook->property(Workbook::PIDSI_REVNUMBER).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_LASTPRINTED_DTM)) {
- metaWriter->startElement("dc:print-date", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_LASTPRINTED_DTM).toString());
- metaWriter->endElement();
+ meta.add_meta_print_date().addTextNode(
+ workbook->property(Workbook::PIDSI_LASTPRINTED_DTM).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_CREATE_DTM)) {
- metaWriter->startElement("meta:creation-date", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_CREATE_DTM).toString());
- metaWriter->endElement();
+ meta.add_meta_creation_date().addTextNode(
+ workbook->property(Workbook::PIDSI_CREATE_DTM).toString());
}
if (workbook->hasProperty(Workbook::PIDSI_LASTSAVED_DTM)) {
- metaWriter->startElement("dc:date", false);
- metaWriter->addTextNode(workbook->property(Workbook::PIDSI_LASTSAVED_DTM).toString());
- metaWriter->endElement();
+ meta.add_dc_date().addTextNode(
+ workbook->property(Workbook::PIDSI_LASTSAVED_DTM).toString());
}
//if( workbook->hasProperty( Workbook::PIDSI_TEMPLATE ) ) \
metaWriter->addAttribute( "dc:", workbook->property( Workbook::PIDSI_TEMPLATE \
).toString() );
//if( workbook->hasProperty( Workbook::PIDSI_LASTAUTHOR ) ) \
metaWriter->addAttribute( "dc:", workbook->property( Workbook::PIDSI_LASTAUTHOR \
).toString() );
//if( workbook->hasProperty( Workbook::PIDSI_EDITTIME ) ) \
metaWriter->addAttribute( "dc:date", workbook->property( Workbook::PIDSI_EDITTIME \
).toString() );
- metaWriter->endElement(); // office:meta
- metaWriter->endElement(); // office:document-meta
+ metadoc.end();
metaWriter->endDocument();
delete metaWriter;
@@ -494,63 +501,54 @@ bool ExcelImport::Private::createSettings(KoOdfWriteStore* \
store)
KoStoreDevice dev(store->store());
KoXmlWriter* settingsWriter = KoOdfWriteStore::createOasisXmlWriter(&dev, \
"office:document-settings");
- settingsWriter->startElement("office:settings");
- settingsWriter->startElement("config:config-item-set");
- settingsWriter->addAttribute("config:name", "view-settings");
+ {
+ office_settings settings(settingsWriter);
+ config_config_item_set \
set(settings.add_config_config_item_set("view-settings"));
// units...
// settings
- settingsWriter->startElement("config:config-item-map-indexed");
- settingsWriter->addAttribute("config:name", "Views");
- settingsWriter->startElement("config:config-item-map-entry");
- settingsWriter->addConfigItem("ViewId", QString::fromLatin1("View1"));
+ config_config_item_map_indexed \
map(set.add_config_config_item_map_indexed("Views")); + \
config_config_item_map_entry entry(map.add_config_config_item_map_entry()); +
+ addConfigItem(entry, "ViewId", QString::fromLatin1("View1"));
if(Sheet *sheet = workbook->sheet(workbook->activeTab()))
- settingsWriter->addConfigItem("ActiveTable", sheet->name());
+ addConfigItem(entry, "ActiveTable", sheet->name());
- settingsWriter->startElement("config:config-item-map-named");
- settingsWriter->addAttribute("config:name", "Tables");
+ config_config_item_map_named \
named(entry.add_config_config_item_map_named("Tables")); for(uint i = 0; i < \
workbook->sheetCount(); ++i) { Sheet* sheet = workbook->sheet(i);
- settingsWriter->startElement("config:config-item-map-entry");
- settingsWriter->addAttribute("config:name", sheet->name());
+ config_config_item_map_entry \
entry(named.add_config_config_item_map_entry()); + \
entry.set_config_name(sheet->name()); QPoint point = sheet->firstVisibleCell();
- settingsWriter->addConfigItem("CursorPositionX", point.x());
- settingsWriter->addConfigItem("CursorPositionY", point.y());
+ addConfigItem(entry, "CursorPositionX", point.x());
+ addConfigItem(entry, "CursorPositionY", point.y());
//TODO how should we replace these settings?
// settingsWriter->addConfigItem("xOffset", columnWidth(sheet,point.x()));
// settingsWriter->addConfigItem("yOffset", rowHeight(sheet,point.y()));
- settingsWriter->addConfigItem("ShowZeroValues", sheet->showZeroValues());
- settingsWriter->addConfigItem("ShowGrid", sheet->showGrid());
- settingsWriter->addConfigItem("FirstLetterUpper", false);
- settingsWriter->addConfigItem("ShowFormulaIndicator", false);
- settingsWriter->addConfigItem("ShowCommentIndicator", true);
- settingsWriter->addConfigItem("ShowPageOutline", \
sheet->isPageBreakViewEnabled()); // best match kspread provides
- settingsWriter->addConfigItem("lcmode", false);
- settingsWriter->addConfigItem("autoCalc", sheet->autoCalc());
- settingsWriter->addConfigItem("ShowColumnNumber", false);
- settingsWriter->endElement();
- }
- settingsWriter->endElement(); // config:config-item-map-named
-
- settingsWriter->endElement(); // config:config-item-map-entry
- settingsWriter->endElement(); // config:config-item-map-indexed
- settingsWriter->endElement(); // config:config-item-set
-
- settingsWriter->endElement(); // office:settings
- settingsWriter->endElement(); // Root:element
+ addConfigItem(entry, "ShowZeroValues", sheet->showZeroValues());
+ addConfigItem(entry, "ShowGrid", sheet->showGrid());
+ addConfigItem(entry, "FirstLetterUpper", false);
+ addConfigItem(entry, "ShowFormulaIndicator", false);
+ addConfigItem(entry, "ShowCommentIndicator", true);
+ addConfigItem(entry, "ShowPageOutline", sheet->isPageBreakViewEnabled()); // \
best match kspread provides + addConfigItem(entry, "lcmode", false);
+ addConfigItem(entry, "autoCalc", sheet->autoCalc());
+ addConfigItem(entry, "ShowColumnNumber", false);
+ }
+ } // end of block closes all elements
settingsWriter->endDocument();
delete settingsWriter;
return store->store()->close();
}
// Processes the workbook content. The workbook is the top-level element for \
content.
-void ExcelImport::Private::processWorkbookForBody(KoOdfWriteStore* store, Workbook* \
workbook, KoXmlWriter* xmlWriter) +void \
ExcelImport::Private::processWorkbookForBody(Workbook* workbook, KoXmlWriter* \
xmlWriter, office_body& body) {
if (!workbook) return;
if (!xmlWriter) return;
- xmlWriter->startElement("office:spreadsheet");
+ office_spreadsheet spreadsheet(body.add_office_spreadsheet());
// count the number of rows in total to provide a good progress value
rowsCountTotal = rowsCountDone = 0;
@@ -562,49 +560,37 @@ void \
ExcelImport::Private::processWorkbookForBody(KoOdfWriteStore* store, Workbo // now \
start the whole work for (unsigned i = 0; i < workbook->sheetCount(); i++) {
Sheet* sheet = workbook->sheet(i);
- processSheetForBody(store, sheet, xmlWriter);
+ processSheetForBody(sheet, xmlWriter, spreadsheet);
}
std::map<std::pair<unsigned, QString>, QString> &namedAreas = \
workbook->namedAreas(); if(namedAreas.size() > 0) {
- xmlWriter->startElement("table:named-expressions");
+ table_named_expressions exprs(spreadsheet.add_table_named_expressions());
for(std::map<std::pair<unsigned, QString>, QString>::iterator it = \
namedAreas.begin(); it != namedAreas.end(); ++it) {
- xmlWriter->startElement("table:named-range");
- xmlWriter->addAttribute("table:name", it->first.second ); // e.g. "My \
Named Range" QString range = it->second;
if(range.startsWith(QLatin1Char('[')) && \
range.endsWith(QLatin1Char(']'))) { range.remove(0, 1).chop(1);
}
- xmlWriter->addAttribute("table:cell-range-address", range); // e.g. \
"$Sheet1.$B$2:.$B$3"
- xmlWriter->endElement();//[Sheet1.$B$2:$B$3]
+ table_named_range(exprs.add_table_named_range(range, it->first.second));
}
- xmlWriter->endElement();
}
- bool openedDBRanges = false;
+ table_database_ranges ranges(spreadsheet.add_table_database_ranges());
int rangeId = 1;
for (unsigned i = 0; i < workbook->sheetCount(); i++) {
QList<QRect> filters = workbook->filterRanges(i);
QString sheetName = workbook->sheet(i)->name();
if (filters.size()) {
- if (!openedDBRanges) xmlWriter->startElement("table:database-ranges");
- openedDBRanges = true;
-
foreach (const QRect& filter, filters) {
QString sRange(encodeAddress(sheetName, filter.left(), \
filter.top())); sRange.append(":");
sRange.append(encodeAddress(sheetName, filter.right(), \
workbook->sheet(i)->maxRow()));
- xmlWriter->startElement("table:database-range");
- xmlWriter->addAttribute("table:name", \
QString("excel-database-%1").arg(rangeId++));
- xmlWriter->addAttribute("table:display-filter-buttons", "true");
- xmlWriter->addAttribute("table:target-range-address", sRange);
- xmlWriter->endElement(); // table:database-range
+ table_database_range range(ranges.add_table_database_range(sRange));
+ range.set_table_name(QString("excel-database-%1").arg(rangeId++));
+ range.set_table_display_filter_buttons("true");
}
}
}
- if (openedDBRanges) xmlWriter->endElement(); // table:database-ranges
-
- xmlWriter->endElement(); // office:spreadsheet
}
// Processes the workbook styles. The workbook is the top-level element for content.
@@ -625,23 +611,22 @@ void ExcelImport::Private::processWorkbookForStyle(Workbook* \
workbook, KoXmlWrit KoXmlWriter writer(&buf);
//Hardcoded page-layout
- writer.startElement("style:header-style");
- writer.startElement("style:header-footer-properties");
- writer.addAttribute("fo:min-height", "20pt");
- writer.addAttribute("fo:margin-left", "0pt");
- writer.addAttribute("fo:margin-right", "0pt");
- writer.addAttribute("fo:margin-bottom", "10pt");
- writer.endElement();
- writer.endElement();
-
- writer.startElement("style:footer-style");
- writer.startElement("style:header-footer-properties");
- writer.addAttribute("fo:min-height", "20pt");
- writer.addAttribute("fo:margin-left", "0pt");
- writer.addAttribute("fo:margin-right", "0pt");
- writer.addAttribute("fo:margin-top", "10pt");
- writer.endElement();
- writer.endElement();
+ style_header_style header(&writer);
+ style_header_footer_properties hf(header.add_style_header_footer_properties());
+ hf.set_fo_min_height("20pt");
+ hf.set_fo_margin_left("0pt");
+ hf.set_fo_margin_right("0pt");
+ hf.set_fo_margin_bottom("10pt");
+ header.end();
+
+ style_footer_style footer(&writer);
+ style_header_footer_properties hf2(footer.add_style_header_footer_properties());
+ hf2.set_fo_min_height("20pt");
+ hf2.set_fo_margin_left("0pt");
+ hf2.set_fo_margin_right("0pt");
+ hf2.set_fo_margin_top("10pt");
+ footer.end();
+
QString pageLyt = QString::fromUtf8(buf.buffer(), buf.buffer().size());
buf.close();
buf.setData("", 0);
@@ -668,16 +653,16 @@ void ExcelImport::Private::processWorkbookForStyle(Workbook* \
workbook, KoXmlWrit }
// Processes a sheet.
-void ExcelImport::Private::processSheetForBody(KoOdfWriteStore* store, Sheet* sheet, \
KoXmlWriter* xmlWriter) +void ExcelImport::Private::processSheetForBody(Sheet* sheet, \
KoXmlWriter* xmlWriter, office_spreadsheet& spreadsheet) {
if (!sheet) return;
if (!xmlWriter) return;
- xmlWriter->startElement("table:table");
+ table_table table(spreadsheet.add_table_table());
- xmlWriter->addAttribute("table:name", sheet->name());
- xmlWriter->addAttribute("table:print", "false");
- xmlWriter->addAttribute("table:style-name", sheetStyles[sheetFormatIndex]);
+ table.set_table_name(sheet->name());
+ table.set_table_print("false");
+ table.set_table_style_name(sheetStyles[sheetFormatIndex]);
++sheetFormatIndex;
if(sheet->password() != 0) {
@@ -687,52 +672,39 @@ void ExcelImport::Private::processSheetForBody(KoOdfWriteStore* \
store, Sheet* sh }
if (!sheet->drawObjects().isEmpty()) {
- xmlWriter->startElement("table:shapes");
- xmlWriter->addCompleteElement(sheetShapes[sheet]);
- xmlWriter->endElement(); // table:shapes
+ table_shapes shapes(table.add_table_shapes());
+ shapes.addCompleteElement(sheetShapes[sheet]);
}
const unsigned columnCount = qMin(maximalColumnCount, sheet->maxColumn());
unsigned outlineLevel = 0;
for (unsigned i = 0; i <= columnCount; ++i) {
- processColumnForBody(sheet, i, xmlWriter, outlineLevel);
- }
- while (outlineLevel > 0) {
- xmlWriter->endElement(); // table:table-column-group
- outlineLevel--;
+ processColumnForBody(sheet, i, table, outlineLevel);
}
// in odf default-cell-style's only apply to cells/rows/columns that are present \
in the file while in Excel
// row/column styles should apply to all cells in that row/column. So, try to \
fake that behavior by writing
// a number-columns-repeated to apply the styles/formattings to "all" columns.
if (columnCount < maximalColumnCount-1) {
- xmlWriter->startElement("table:table-column");
- xmlWriter->addAttribute("table:style-name", \
defaultColumnStyles[defaultColumnStyleIndex]);
- xmlWriter->addAttribute("table:number-columns-repeated", maximalColumnCount \
- 1 - columnCount);
- xmlWriter->endElement();
+ table_table_column column(table.add_table_table_column());
+ column.set_table_style_name(defaultColumnStyles[defaultColumnStyleIndex]);
+ column.set_table_number_columns_repeated(maximalColumnCount - 1 - \
columnCount); }
// add rows
+ outlineLevel = 0;
const unsigned rowCount = qMin(maximalRowCount, sheet->maxRow());
for (unsigned i = 0; i <= rowCount;) {
- i += processRowForBody(store, sheet, i, xmlWriter, outlineLevel);
- }
- while (outlineLevel > 0) {
- xmlWriter->endElement(); // table:table-row-group
- outlineLevel--;
+ i += processRowForBody(sheet, i, xmlWriter, table, outlineLevel);
}
// same we did above with columns is also needed for rows.
if(rowCount < maximalRowCount-1) {
- xmlWriter->startElement("table:table-row");
- xmlWriter->addAttribute("table:number-rows-repeated", maximalRowCount - 1 - \
rowCount);
- xmlWriter->startElement("table:table-cell");
- xmlWriter->endElement();
- xmlWriter->endElement();
+ table_table_row row(table.add_table_table_row());
+ row.set_table_number_rows_repeated(maximalRowCount - 1 - rowCount);
+ row.add_table_table_cell();
}
-
- xmlWriter->endElement(); // table:table
++defaultColumnStyleIndex;
}
@@ -814,99 +786,73 @@ void ExcelImport::Private::processSheetForHeaderFooter(Sheet* \
sheet, KoXmlWriter if (!sheet) return;
if (!xmlWriter) return;
- xmlWriter->startElement("style:header");
+ style_header header(xmlWriter);
if (!sheet->leftHeader().isEmpty()) {
- xmlWriter->startElement("style:region-left");
- xmlWriter->startElement("text:p");
- processHeaderFooterStyle(sheet->leftHeader(), xmlWriter);
- xmlWriter->endElement();
- xmlWriter->endElement();
+ style_region_left left(header.add_style_region_left());
+ text_p p(left.add_text_p());
+ processHeaderFooterStyle(sheet->leftHeader(), p);
}
if (!sheet->centerHeader().isEmpty()) {
- xmlWriter->startElement("style:region-center");
- xmlWriter->startElement("text:p");
- processHeaderFooterStyle(sheet->centerHeader(), xmlWriter);
- xmlWriter->endElement();
- xmlWriter->endElement();
+ style_region_center center(header.add_style_region_center());
+ text_p p(center.add_text_p());
+ processHeaderFooterStyle(sheet->centerHeader(), p);
}
if (!sheet->rightHeader().isEmpty()) {
- xmlWriter->startElement("style:region-right");
- xmlWriter->startElement("text:p");
- processHeaderFooterStyle(sheet->rightHeader(), xmlWriter);
- xmlWriter->endElement();
- xmlWriter->endElement();
+ style_region_right right(header.add_style_region_right());
+ text_p p(right.add_text_p());
+ processHeaderFooterStyle(sheet->rightHeader(), p);
}
- xmlWriter->endElement();
+ header.end();
- xmlWriter->startElement("style:footer");
+ style_footer footer(xmlWriter);
if (!sheet->leftFooter().isEmpty()) {
- xmlWriter->startElement("style:region-left");
- xmlWriter->startElement("text:p");
- processHeaderFooterStyle(sheet->leftFooter(), xmlWriter);
- xmlWriter->endElement();
- xmlWriter->endElement();
+ style_region_left left(footer.add_style_region_left());
+ text_p p(left.add_text_p());
+ processHeaderFooterStyle(sheet->leftFooter(), p);
}
if (!sheet->centerFooter().isEmpty()) {
- xmlWriter->startElement("style:region-center");
- xmlWriter->startElement("text:p");
- processHeaderFooterStyle(sheet->centerFooter(), xmlWriter);
- xmlWriter->endElement();
- xmlWriter->endElement();
+ style_region_center center(footer.add_style_region_center());
+ text_p p(center.add_text_p());
+ processHeaderFooterStyle(sheet->centerFooter(), p);
}
if (!sheet->rightFooter().isEmpty()) {
- xmlWriter->startElement("style:region-right");
- xmlWriter->startElement("text:p");
- processHeaderFooterStyle(sheet->rightFooter(), xmlWriter);
- xmlWriter->endElement();
- xmlWriter->endElement();
+ style_region_right right(footer.add_style_region_right());
+ text_p p(right.add_text_p());
+ processHeaderFooterStyle(sheet->rightFooter(), p);
}
- xmlWriter->endElement();
}
// Processes the styles of a headers and footers for a sheet.
-void ExcelImport::Private::processHeaderFooterStyle(const QString& text, \
KoXmlWriter* xmlWriter) +void ExcelImport::Private::processHeaderFooterStyle(const \
QString& text, text_p& p) {
- QString content;
bool skipUnsupported = false;
int lastPos;
int pos = text.indexOf('&');
int len = text.length();
if ((pos < 0) && (text.length() > 0)) // If ther is no &
- xmlWriter->addTextNode(text);
+ p.addTextNode(text);
else if (pos > 0) // Some text and '&'
- xmlWriter->addTextNode(text.mid(0, pos - 1));
+ p.addTextNode(text.mid(0, pos - 1));
while (pos >= 0) {
switch (text[pos + 1].unicode()) {
case 'D':
- xmlWriter->startElement("text:date");
- xmlWriter->addTextNode(QDate::currentDate().toString("DD/MM/YYYY"));
- xmlWriter->endElement();
+ p.add_text_date().addTextNode(QDate::currentDate().toString("DD/MM/YYYY"));
break;
case 'T':
- xmlWriter->startElement("text:time");
- xmlWriter->addTextNode(QTime::currentTime().toString("HH:MM:SS"));
- xmlWriter->endElement();
+ p.add_text_time().addTextNode(QTime::currentTime().toString("HH:MM:SS"));
break;
case 'P':
- xmlWriter->startElement("text:page-number");
- xmlWriter->addTextNode("1");
- xmlWriter->endElement();
+ p.add_text_page_number().addTextNode("1");
break;
case 'N':
- xmlWriter->startElement("text:page-count");
- xmlWriter->addTextNode("999");
- xmlWriter->endElement();
+ p.add_text_page_count().addTextNode("999");
break;
case 'F':
- xmlWriter->startElement("text:title");
- xmlWriter->addTextNode("???");
- xmlWriter->endElement();
+ p.add_text_title().addTextNode("???");
break;
case 'A':
- xmlWriter->startElement("text:sheet-name");
- xmlWriter->addTextNode("???");
- xmlWriter->endElement();
+ p.add_text_sheet_name().addTextNode("???");
break;
case '\"':
default:
@@ -916,38 +862,34 @@ void ExcelImport::Private::processHeaderFooterStyle(const \
QString& text, KoXmlWr lastPos = pos;
pos = text.indexOf('&', lastPos + 1);
if (!skipUnsupported && (pos > (lastPos + 1)))
- xmlWriter->addTextNode(text.mid(lastPos + 2, (pos - lastPos - 2)));
+ p.addTextNode(text.mid(lastPos + 2, (pos - lastPos - 2)));
else if (!skipUnsupported && (pos < 0)) //Remaining text
- xmlWriter->addTextNode(text.mid(lastPos + 2, len - (lastPos + 2)));
+ p.addTextNode(text.mid(lastPos + 2, len - (lastPos + 2)));
else
skipUnsupported = false;
}
}
// Processes a column in a sheet.
-void ExcelImport::Private::processColumnForBody(Sheet* sheet, int columnIndex, \
KoXmlWriter* xmlWriter, unsigned& outlineLevel) +void \
ExcelImport::Private::processColumnForBody(Sheet* sheet, int columnIndex, \
group_table_columns_and_groups& table, unsigned& outlineLevel) {
Column* column = sheet->column(columnIndex, false);
- if (!xmlWriter) return;
-
unsigned newOutlineLevel = column ? column->outlineLevel() : 0;
- while (newOutlineLevel > outlineLevel) {
- xmlWriter->startElement("table:table-column-group");
+ if (newOutlineLevel > outlineLevel) {
+ table_table_column_group group(table.add_table_table_column_group());
outlineLevel++;
if (outlineLevel == newOutlineLevel && column->collapsed())
- xmlWriter->addAttribute("table:display", "false");
- }
- while (newOutlineLevel < outlineLevel) {
- xmlWriter->endElement(); // table:table-column-group
+ group.set_table_display("false");
+ processColumnForBody(sheet, columnIndex, group, outlineLevel);
outlineLevel--;
+ return;
}
if (!column) {
- xmlWriter->startElement("table:table-column");
+ table_table_column column(table.add_table_table_column());
Q_ASSERT(defaultColumnStyleIndex < defaultColumnStyles.count());
- xmlWriter->addAttribute("table:style-name", \
defaultColumnStyles[defaultColumnStyleIndex] );
- xmlWriter->endElement();
+ column.set_table_style_name(defaultColumnStyles[defaultColumnStyleIndex] );
return;
}
Q_ASSERT(columnFormatIndex < colStyles.count());
@@ -956,12 +898,11 @@ void ExcelImport::Private::processColumnForBody(Sheet* sheet, \
int columnIndex, K const QString defaultStyleName = \
colCellStyles[columnFormatIndex]; columnFormatIndex++;
- xmlWriter->startElement("table:table-column");
- xmlWriter->addAttribute("table:default-cell-style-name", defaultStyleName);
- xmlWriter->addAttribute("table:visibility", column->visible() ? "visible" : \
"collapse");
- //xmlWriter->addAttribute("table:number-columns-repeated", );
- xmlWriter->addAttribute("table:style-name", styleName);
- xmlWriter->endElement(); // table:table-column
+ table_table_column c(table.add_table_table_column());
+ c.set_table_default_cell_style_name(defaultStyleName);
+ c.set_table_visibility(column->visible() ? "visible" : "collapse");
+ //c.set_table_number_columns_repeated( );
+ c.set_table_style_name(styleName);
}
// Processes the style of a column in a sheet.
@@ -985,31 +926,26 @@ void ExcelImport::Private::processColumnForStyle(Sheet* sheet, \
int columnIndex, }
// Processes a row in a sheet.
-int ExcelImport::Private::processRowForBody(KoOdfWriteStore* store, Sheet* sheet, \
int rowIndex, KoXmlWriter* xmlWriter, unsigned& outlineLevel) +int \
ExcelImport::Private::processRowForBody(Sheet* sheet, int rowIndex, KoXmlWriter* \
xmlWriter, group_table_rows_and_groups& table, unsigned& outlineLevel) {
int repeat = 1;
- if (!xmlWriter) return repeat;
Row *row = sheet->row(rowIndex, false);
unsigned newOutlineLevel = row ? row->outlineLevel() : 0;
- while (newOutlineLevel > outlineLevel) {
- xmlWriter->startElement("table:table-row-group");
+ if (newOutlineLevel > outlineLevel) {
+ table_table_row_group group(table.add_table_table_row_group());
outlineLevel++;
if (outlineLevel == newOutlineLevel && row->collapsed())
- xmlWriter->addAttribute("table:display", "false");
- }
- while (newOutlineLevel < outlineLevel) {
- xmlWriter->endElement(); // table:table-row-group
+ group.set_table_display("false");
+ processRowForBody(sheet, rowIndex, xmlWriter, group, outlineLevel);
outlineLevel--;
+ return repeat;
}
-
if (!row) {
- xmlWriter->startElement("table:table-row");
- xmlWriter->startElement("table:table-cell");
- xmlWriter->endElement();
- xmlWriter->endElement();
+ table_table_row row(table.add_table_table_row());
+ row.add_table_table_cell();
return repeat;
}
if (!row->sheet()) return repeat;
@@ -1019,12 +955,12 @@ int ExcelImport::Private::processRowForBody(KoOdfWriteStore* \
store, Sheet* sheet
repeat = rowsRepeated(row, rowIndex);
- xmlWriter->startElement("table:table-row");
- xmlWriter->addAttribute("table:visibility", row->visible() ? "visible" : \
"collapse");
- xmlWriter->addAttribute("table:style-name", styleName);
+ table_table_row r(table.add_table_table_row());
+ r.set_table_visibility(row->visible() ? "visible" : "collapse");
+ r.set_table_style_name(styleName);
if(repeat > 1)
- xmlWriter->addAttribute("table:number-rows-repeated", repeat);
+ r.set_table_number_rows_repeated(repeat);
// find the column of the rightmost cell (if any)
const int lastCol = row->sheet()->maxCellsInRow(rowIndex);
@@ -1032,16 +968,14 @@ int ExcelImport::Private::processRowForBody(KoOdfWriteStore* \
store, Sheet* sheet do {
Cell* cell = row->sheet()->cell(i, row->index(), false);
if (cell) {
- processCellForBody(store, cell, repeat, xmlWriter);
+ processCellForBody(cell, xmlWriter, r);
i += cell->columnRepeat();
} else { // empty cell
- xmlWriter->startElement("table:table-cell");
- xmlWriter->endElement();
+ r.add_table_table_cell();
++i;
}
} while(i <= lastCol);
- xmlWriter->endElement(); // table:table-row
addProgress(repeat);
return repeat;
}
@@ -1234,151 +1168,158 @@ QString currencyValue(const QString &value)
}
// Processes a cell within a sheet.
-void ExcelImport::Private::processCellForBody(KoOdfWriteStore* store, Cell* cell, \
int rowsRepeat, KoXmlWriter* xmlWriter) +void \
ExcelImport::Private::processCellForBody(Cell* cell, KoXmlWriter* xmlWriter, \
table_table_row& row) {
- Q_UNUSED(store);
- Q_UNUSED(rowsRepeat);
-
- if (!cell) return;
- if (!xmlWriter) return;
-
- if (cell->isCovered())
- xmlWriter->startElement("table:covered-table-cell");
- else
- xmlWriter->startElement("table:table-cell");
+ CellValue cellValue;
+ if (cell->isCovered()) {
+ table_covered_table_cell c(row.add_table_covered_table_cell());
+ processCellAttributesForBody(cell, c, cellValue);
+ processCellContentForBody(cell, xmlWriter, c, cellValue);
+ } else {
+ table_table_cell c(row.add_table_table_cell());
+ if (cell->columnSpan() > 1)
+ c.set_table_number_columns_spanned(cell->columnSpan());
+ if (cell->rowSpan() > 1)
+ c.set_table_number_rows_spanned(cell->rowSpan());
+ processCellAttributesForBody(cell, c, cellValue);
+ processCellContentForBody(cell, xmlWriter, c, cellValue);
+ }
+}
+void ExcelImport::Private::processCellAttributesForBody(Cell* cell, \
group_table_table_cell_attlist& c, CellValue& cellValue) +{
Q_ASSERT(cellFormatIndex >= 0 && cellFormatIndex < cellStyles.count());
- xmlWriter->addAttribute("table:style-name", cellStyles[cellFormatIndex]);
+ c.set_table_style_name(cellStyles[cellFormatIndex]);
cellFormatIndex++;
- if (cell->columnSpan() > 1)
- xmlWriter->addAttribute("table:number-columns-spanned", cell->columnSpan());
- if (cell->rowSpan() > 1)
- xmlWriter->addAttribute("table:number-rows-spanned", cell->rowSpan());
if (cell->columnRepeat() > 1)
- xmlWriter->addAttribute("table:number-columns-repeated", \
cell->columnRepeat()); + \
c.set_table_number_columns_repeated(cell->columnRepeat());
const QString formula = cellFormula(cell);
if (!formula.isEmpty())
- xmlWriter->addAttribute("table:formula", formula);
+ c.set_table_formula(formula);
- Value value = cell->value();
+ cellValue.value = cell->value();
+ const Value& value = cellValue.value;
if (value.isBoolean()) {
- xmlWriter->addAttribute("office:value-type", "boolean");
- xmlWriter->addAttribute("office:boolean-value", value.asBoolean() ? "true" : \
"false"); + c.set_office_value_type("boolean");
+ c.set_office_boolean_value(value.asBoolean() ? "true" : "false");
} else if (value.isFloat() || value.isInteger()) {
const QString valueFormat = cell->format().valueFormat();
if (isPercentageFormat(valueFormat)) {
- xmlWriter->addAttribute("office:value-type", "percentage");
- xmlWriter->addAttribute("office:value", value.asFloat());
+ c.set_office_value_type("percentage");
+ c.set_office_value(value.asFloat());
} else if (isDateFormat(valueFormat)) {
const QString dateValue = convertDate(value.asFloat(), valueFormat);
- xmlWriter->addAttribute("office:value-type", "date");
- xmlWriter->addAttribute("office:date-value", dateValue);
+ c.set_office_value_type("date");
+ c.set_office_date_value(dateValue);
} else if (value.asFloat() < 1.0 && isTimeFormat(valueFormat)) {
const QString timeValue = convertTime(value.asFloat(), valueFormat);
- xmlWriter->addAttribute("office:value-type", "time");
- xmlWriter->addAttribute("office:time-value", timeValue);
+ c.set_office_value_type("time");
+ c.set_office_time_value(timeValue);
} else if (isFractionFormat(valueFormat)) {
const QString fractionValue = convertFraction(value.asFloat(), \
valueFormat);
- xmlWriter->addAttribute("office:value-type", "float");
- xmlWriter->addAttribute("office:value", fractionValue);
+ c.set_office_value_type("float");
+ c.set_office_value(fractionValue);
} else { // fallback is the generic float format
- xmlWriter->addAttribute("office:value-type", "float");
- xmlWriter->addAttribute("office:value", value.asFloat());
+ c.set_office_value_type("float");
+ c.set_office_value(value.asFloat());
}
} else if (value.isText() || value.isError()) {
- QString str = value.asString();
- QString linkName, linkLocation;
-
- Hyperlink link = cell->hyperlink();
- if (link.isValid) {
- linkLocation = link.location;
- if(!linkLocation.isEmpty()) {
- linkName = link.displayName.trimmed();
- if(linkName.isEmpty())
- linkName = str;
- str.clear(); // at Excel cells with links don't have additional text \
content + cellValue.str = value.asString();
+
+ cellValue.link = cell->hyperlink();
+ if (cellValue.link.isValid) {
+ cellValue.linkLocation = cellValue.link.location;
+ if(!cellValue.linkLocation.isEmpty()) {
+ cellValue.linkName = cellValue.link.displayName.trimmed();
+ if(cellValue.linkName.isEmpty())
+ cellValue.linkName = cellValue.str;
+ cellValue.str.clear(); // at Excel cells with links don't have \
additional text content }
}
- if (linkLocation.isEmpty() && value.isString()) {
- xmlWriter->addAttribute("office:value-type", "string");
- if (!(cell->format().font().subscript() || \
cell->format().font().superscript()))
- xmlWriter->addAttribute("office:string-value", str);
+ if (cellValue.linkLocation.isEmpty() && value.isString()) {
+ c.set_office_value_type("string");
+ if (!(cell->format().font().subscript() || \
cell->format().font().superscript())) + \
c.set_office_string_value(cellValue.str); + }
+ }
+}
+
+void ExcelImport::Private::processCellText(Cell* cell, group_paragraph_content& \
content, CellValue& cellValue) +{
+ const QString& str = cellValue.str;
+ if (cellValue.value.isString()) {
+ content.addTextNode(str);
+ } else {
+ // rich text
+ std::map<unsigned, FormatFont> formatRuns = cellValue.value.formatRuns();
+
+ // add sentinel to list of format runs
+ formatRuns[str.length()] = cell->format().font();
+
+ unsigned index = 0;
+ QString style;
+ for (std::map<unsigned, FormatFont>::iterator it = formatRuns.begin(); it != \
formatRuns.end(); ++it) { + if (it->first > index) {
+ if (!style.isEmpty()) {
+ text_span span(content.add_text_span());
+ span.set_text_style_name(style);
+ span.addTextNode(str.mid(index, it->first - index));
+ } else {
+ content.addTextNode(str.mid(index, it->first - index));
+ }
+ }
+
+ index = it->first;
+
+ if (it->second == cell->format().font())
+ style.clear();
+ else {
+ style = fontStyles.value(it->second);
+ }
}
+ }
+}
- xmlWriter->startElement("text:p", false);
+void ExcelImport::Private::processCellContentForBody(Cell* cell,
+ KoXmlWriter* xmlWriter, group_table_table_cell_content& c,
+ CellValue& cellValue)
+{
+ if (cellValue.value.isText() || cellValue.value.isError()) {
+ text_p p(c.add_text_p());
- if(!str.isEmpty()) {
+ if(!cellValue.str.isEmpty()) {
if (cell->format().font().subscript() || \
cell->format().font().superscript()) {
- xmlWriter->startElement("text:span");
+ text_span span(p.add_text_span());
if (cell->format().font().subscript())
- xmlWriter->addAttribute("text:style-name", subScriptStyle);
+ span.set_text_style_name(subScriptStyle);
else
- xmlWriter->addAttribute("text:style-name", superScriptStyle);
- }
-
- if (value.isString()) {
- xmlWriter->addTextNode(str);
+ span.set_text_style_name(superScriptStyle);
+ processCellText(cell, span, cellValue);
} else {
- // rich text
- std::map<unsigned, FormatFont> formatRuns = value.formatRuns();
-
- // add sentinel to list of format runs
- formatRuns[str.length()] = cell->format().font();
-
- unsigned index = 0;
- QString style;
- for (std::map<unsigned, FormatFont>::iterator it = \
formatRuns.begin(); it != formatRuns.end(); ++it) {
- if (!style.isEmpty() && it->first > index) {
- xmlWriter->startElement("text:span");
- xmlWriter->addAttribute("text:style-name", style);
- }
- if (it->first > index)
- xmlWriter->addTextNode(str.mid(index, it->first - index));
- if (!style.isEmpty() && it->first > index) {
- xmlWriter->endElement(); // text:span
- }
-
- index = it->first;
-
- if (it->second == cell->format().font())
- style.clear();
- else {
- style = fontStyles.value(it->second);
- }
- }
+ processCellText(cell, p, cellValue);
}
-
- if (cell->format().font().subscript() || \
cell->format().font().superscript())
- xmlWriter->endElement(); // text:span
}
- if (!linkName.isEmpty()) {
- xmlWriter->startElement("text:a");
- xmlWriter->addAttribute("xlink:href", linkLocation);
- const QString targetFrameName = link.targetFrameName;
+ if (!cellValue.linkName.isEmpty()) {
+ text_a a(p.add_text_a(cellValue.linkLocation));
+ const QString targetFrameName = cellValue.link.targetFrameName;
if (! targetFrameName.isEmpty())
- xmlWriter->addAttribute("office:target-frame-name", \
targetFrameName);
- xmlWriter->addTextNode(linkName);
- xmlWriter->endElement(); // text:a
+ a.set_office_target_frame_name(targetFrameName);
+ a.addTextNode(cellValue.linkName);
}
-
- xmlWriter->endElement(); // text:p
}
const QString note = cell->note();
if (! note.isEmpty()) {
- xmlWriter->startElement("office:annotation");
- //xmlWriter->startElement("dc:creator");
- //xmlWriter->addTextNode(authorName); //TODO
- //xmlWriter->endElement(); // dc:creator
- xmlWriter->startElement("text:p");
- xmlWriter->addTextNode(note);
- xmlWriter->endElement(); // text:p
- xmlWriter->endElement(); // office:annotation
+ office_annotation annotation(c.add_office_annotation());
+ //dc_creator creator(annotation.add_dc_creator());
+ //creator.addTextNode(authorName); //TODO
+ text_p p(annotation.add_text_p());
+ p.addTextNode(note);
}
@@ -1420,9 +1361,6 @@ void ExcelImport::Private::processCellForBody(KoOdfWriteStore* \
store, Cell* cell if (!cell->drawObjects().isEmpty()) {
xmlWriter->addCompleteElement(cellShapes[cell].data());
}
-
-
- xmlWriter->endElement(); // table:[covered-]table-cell
}
void ExcelImport::Private::processCharts(KoXmlWriter* manifestWriter)
@@ -1498,9 +1436,9 @@ QString ExcelImport::Private::processCellFormat(const Format* \
format, const QStr QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
KoXmlWriter xmlWriter(&buffer); // TODO pass indentation level
- xmlWriter.startElement("number:number");
- xmlWriter.addAttribute("number:decimal-places", key.decimalCount);
- xmlWriter.endElement(); // number:number
+ number_number number(&xmlWriter);
+ number.set_number_decimal_places(key.decimalCount);
+ number.end();
QString elementContents = QString::fromUtf8(buffer.buffer(), \
buffer.buffer().size()); style.addChildElement("number", elementContents);
refName = styles->insert(style, "N");
@@ -1824,12 +1762,12 @@ void ExcelImport::Private::processSheetBackground(Sheet* \
sheet, KoGenStyle& styl KoXmlWriter writer(&buffer);
//TODO add the manifest entry
- writer.startElement("style:background-image");
- writer.addAttribute("xlink:href", sheet->backgroundImage());
- writer.addAttribute("xlink:type", "simple");
- writer.addAttribute("xlink:show", "embed");
- writer.addAttribute("xlink:actuate", "onLoad");
- writer.endElement();
+ style_background_image bg(&writer);
+ bg.set_xlink_href(sheet->backgroundImage());
+ bg.set_xlink_type("simple");
+ bg.set_xlink_show("embed");
+ bg.set_xlink_actuate("onLoad");
+ bg.end();
buffer.close();
style.addChildElement("style:background-image", \
QString::fromUtf8(buffer.buffer(), buffer.buffer().size()));
diff --git a/filters/stage/powerpoint/PptToOdp.cpp \
b/filters/stage/powerpoint/PptToOdp.cpp index 425ac33..0535491 100644
--- a/filters/stage/powerpoint/PptToOdp.cpp
+++ b/filters/stage/powerpoint/PptToOdp.cpp
@@ -18,7 +18,7 @@
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
+ Boston, MA 02110-1301, USA.
*/
#include "PptToOdp.h"
@@ -35,6 +35,12 @@
#include <KoOdf.h>
#include <KoOdfWriteStore.h>
#include <KoXmlWriter.h>
+#include <writeodf/writeodftext.h>
+#include <writeodf/writeodfstyle.h>
+#include <writeodf/writeodfpresentation.h>
+#include <writeodf/writeodfofficemeta.h>
+#include <writeodf/writeodfofficedc.h>
+#include <writeodf/writeodfdraw.h>
#include <QTime>
#include <QBuffer>
@@ -47,6 +53,36 @@
#define FONTSIZE_MAX 4000 //according to MS-PPT
using namespace MSO;
+using namespace writeodf;
+
+/**
+ * This class represents an opened <text:list> tag with an optionally opened
+ * <text:list-item> tag.
+ * Usually, writeodf::text_list instances are on the stack, but in this
+ * class they are on heap. The TextListTag manages the allocation and
+ * deallocation of these instances.
+ */
+class PptToOdp::TextListTag
+{
+public:
+ QString style;
+ QSharedPointer<text_list> list;
+ QSharedPointer<text_list_item> item;
+ TextListTag() {}
+ TextListTag(const QString& style_, KoXmlWriter& out) :style(style_),
+ list(new text_list(&out))
+ {
+ }
+ TextListTag(const QString& style_, text_list_item& item) :style(style_),
+ list(new text_list(item.add_text_list()))
+ {
+ }
+ text_list_item& add_text_list_item()
+ {
+ item = QSharedPointer<text_list_item>(new \
text_list_item(list->add_text_list_item())); + return *item;
+ }
+};
namespace
{
@@ -1641,6 +1677,95 @@ QString bulletSizeToSizeString(qint16 value)
return ret;
}
} //namespace
+void PptToOdp::defineListStyleProperties(KoXmlWriter& out, bool imageBullet, const \
QString& bulletSize, + const PptTextPFRun& pf) {
+ style_list_level_properties list_level_properties(&out);
+
+ if (imageBullet) {
+ QString pictureSize = bulletSize;
+ if (pictureSize.endsWith(QLatin1Char('%'))) {
+ pictureSize.chop(1);
+ bool ok = false;
+ qreal size = pictureSize.toDouble(&ok);
+ if (!ok) {
+ qDebug() << "defineBulletStyle: error converting" << pictureSize << \
"to double"; + }
+ size = m_firstChunkFontSize * size / 100.0;
+ pictureSize = pt(size);
+ }
+
+ // fo:text-align
+ // fo:height
+ list_level_properties.set_fo_height(pictureSize);
+ // fo:width
+ list_level_properties.set_fo_width(pictureSize);
+ // style:font-name
+ // style:vertical-pos
+ list_level_properties.set_style_vertical_pos("middle");
+ // style:vertical-rel
+ list_level_properties.set_style_vertical_pos("line");
+ // svg:x
+ // svg:y
+ }
+ quint16 indent = pf.indent();
+ // text:min-label-distance
+ // text:min-label-width
+ list_level_properties.set_text_min_label_width(pptMasterUnitToCm(pf.leftMargin() \
- indent)); + // text:space-before
+ list_level_properties.set_text_space_before(pptMasterUnitToCm(indent));
+}
+
+void PptToOdp::defineListStyleTextProperties(KoXmlWriter& out, const QString& \
bulletSize, + const PptTextPFRun& pf) {
+
+ //---------------------------------------------
+ // text-properties
+ //---------------------------------------------
+
+ KoGenStyle ts(KoGenStyle::TextStyle);
+ const KoGenStyle::PropertyType text = KoGenStyle::TextType;
+
+ //bulletSize already processed
+ ts.addProperty("fo:font-size", bulletSize, text);
+
+ //default value doesn't make sense
+ QColor color;
+ if (pf.fBulletHasColor()) {
+ color = toQColor(pf.bulletColor());
+ if (color.isValid()) {
+ ts.addProperty("fo:color", color.name(), text);
+ }
+ }
+
+ const MSO::FontEntityAtom* font = 0;
+
+ //MSPowerPoint: UI does NOT enable to change font of a
+ //numbered lists label.
+ if (pf.fBulletHasFont() && !pf.fBulletHasAutoNumber()) {
+ font = getFont(pf.bulletFontRef());
+ }
+
+ //A list label should NOT inherit a symbol font.
+ if (!font && m_firstChunkSymbolAtStart) {
+ font = getFont(m_firstChunkFontRef);
+ }
+
+ if (font) {
+ QString family = QString::fromUtf16(font->lfFaceName.data(), \
font->lfFaceName.size()); + ts.addProperty("fo:font-family", family, text);
+ }
+
+ //MSPowerPoint: A label does NOT inherit Underline from
+ //text-properties of the 1st text chunk. A bullet does NOT
+ //inherit properties in {Italics, Bold}.
+ if (!pf.fBulletHasAutoNumber()) {
+ ts.addProperty("fo:font-style", "normal");
+ ts.addProperty("fo:font-weight", "normal");
+ }
+ ts.addProperty("style:text-underline-style", "none");
+
+ ts.writeStyleProperties(&out, text);
+}
void PptToOdp::defineListStyle(KoGenStyle& style, const quint16 depth,
const ListStyleInput& i)
@@ -1662,9 +1787,10 @@ void PptToOdp::defineListStyle(KoGenStyle& style, const \
quint16 depth,
if (imageBullet) {
elementName = "text:list-level-style-image";
- out.startElement("text:list-level-style-image");
- out.addAttribute("xlink:href", \
bulletPictureNames.value(i.pf.bulletBlipRef()));
- out.addAttribute("xlink:type", "simple");
+ text_list_level_style_image image(&out, depth + 1);
+ image.set_xlink_href(bulletPictureNames.value(i.pf.bulletBlipRef()));
+ image.set_xlink_type("simple");
+ defineListStyleProperties(out, imageBullet, bulletSize, i.pf);
}
else if (i.pf.fBulletHasAutoNumber() || i.pf.fHasBullet()) {
@@ -1675,119 +1801,38 @@ void PptToOdp::defineListStyle(KoGenStyle& style, const \
quint16 depth, // we assume it's a numbered list
if (i.pf.fBulletHasAutoNumber() || i.pf.bulletChar() == 0) {
elementName = "text:list-level-style-number";
- out.startElement("text:list-level-style-number");
+ text_list_level_style_number number(&out, depth + 1);
if (!numFormat.isNull()) {
- out.addAttribute("style:num-format", numFormat);
+ number.set_style_num_format(numFormat);
}
// style:display-levels
- out.addAttribute("text:start-value", i.pf.startNum());
+ number.set_text_start_value(i.pf.startNum());
if (!numPrefix.isNull()) {
- out.addAttribute("style:num-prefix", numPrefix);
+ number.set_style_num_prefix(numPrefix);
}
if (!numSuffix.isNull()) {
- out.addAttribute("style:num-suffix", numSuffix);
+ number.set_style_num_suffix(numSuffix);
}
+ defineListStyleProperties(out, imageBullet, bulletSize, i.pf);
+ defineListStyleTextProperties(out, bulletSize, i.pf);
} else {
elementName = "text:list-level-style-bullet";
- out.startElement("text:list-level-style-bullet");
- out.addAttribute("text:bullet-char", getBulletChar(i.pf));
+ text_list_level_style_bullet bullet(&out, getBulletChar(i.pf), depth + \
1); + defineListStyleProperties(out, imageBullet, bulletSize, i.pf);
+ defineListStyleTextProperties(out, bulletSize, i.pf);
// text:bullet-relative-size
}
}
//no bullet exists (i.pf.fHasBullet() == false)
else {
elementName = "text:list-level-style-number";
- out.startElement("text:list-level-style-number");
- out.addAttribute("style:num-format", "");
+ text_list_level_style_number number(&out, depth + 1);
+ number.set_style_num_format("");
+ defineListStyleProperties(out, imageBullet, bulletSize, i.pf);
+ defineListStyleTextProperties(out, bulletSize, i.pf);
}
- out.addAttribute("text:level", depth + 1);
- out.startElement("style:list-level-properties");
-
- if (imageBullet) {
- QString pictureSize = bulletSize;
- if (pictureSize.endsWith(QLatin1Char('%'))) {
- pictureSize.chop(1);
- bool ok = false;
- qreal size = pictureSize.toDouble(&ok);
- if (!ok) {
- qDebug() << "defineBulletStyle: error converting" << pictureSize << \
"to double";
- }
- size = m_firstChunkFontSize * size / 100.0;
- pictureSize = pt(size);
- }
- // fo:text-align
- // fo:height
- out.addAttribute("fo:height", pictureSize);
- // fo:width
- out.addAttribute("fo:width", pictureSize);
- // style:font-name
- // style:vertical-pos
- out.addAttribute("style:vertical-pos", "middle");
- // style:vertical-rel
- out.addAttribute("style:vertical-rel", "line");
- // svg:x
- // svg:y
- }
- quint16 indent = i.pf.indent();
- // text:min-label-distance
- // text:min-label-width
- out.addAttribute("text:min-label-width", pptMasterUnitToCm(i.pf.leftMargin() - \
indent));
- // text:space-before
- out.addAttribute("text:space-before", pptMasterUnitToCm(indent));
- out.endElement(); // style:list-level-properties
-
- //---------------------------------------------
- // text-properties
- //---------------------------------------------
-
- if (!imageBullet) {
- KoGenStyle ts(KoGenStyle::TextStyle);
- const KoGenStyle::PropertyType text = KoGenStyle::TextType;
-
- //bulletSize already processed
- ts.addProperty("fo:font-size", bulletSize, text);
-
- //default value doesn't make sense
- QColor color;
- if (i.pf.fBulletHasColor()) {
- color = toQColor(i.pf.bulletColor());
- if (color.isValid()) {
- ts.addProperty("fo:color", color.name(), text);
- }
- }
-
- const MSO::FontEntityAtom* font = 0;
-
- //MSPowerPoint: UI does NOT enable to change font of a
- //numbered lists label.
- if (i.pf.fBulletHasFont() && !i.pf.fBulletHasAutoNumber()) {
- font = getFont(i.pf.bulletFontRef());
- }
-
- //A list label should NOT inherit a symbol font.
- if (!font && m_firstChunkSymbolAtStart) {
- font = getFont(m_firstChunkFontRef);
- }
-
- if (font) {
- QString family = QString::fromUtf16(font->lfFaceName.data(), \
font->lfFaceName.size());
- ts.addProperty("fo:font-family", family, text);
- }
-
- //MSPowerPoint: A label does NOT inherit Underline from
- //text-properties of the 1st text chunk. A bullet does NOT
- //inherit properties in {Italics, Bold}.
- if (!i.pf.fBulletHasAutoNumber()) {
- ts.addProperty("fo:font-style", "normal");
- ts.addProperty("fo:font-weight", "normal");
- }
- ts.addProperty("style:text-underline-style", "none");
-
- ts.writeStyleProperties(&out, text);
- }
- out.endElement(); // text:list-level-style-*
// serialize the text:list-style element into the properties
QString contents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
style.addChildElement(elementName, contents);
@@ -2134,10 +2179,9 @@ void PptToOdp::createMainStyles(KoGenStyles& styles)
KoXmlWriter writer(¬esBuffer);
Writer out(writer, styles, true);
- writer.startElement("presentation:notes");
- writer.addAttribute("style:page-layout-name", notesPageLayoutName);
- writer.addAttribute("draw:style-name",
- drawingPageStyles[p->notesMaster]);
+ presentation_notes notes(&out.xml);
+ notes.set_style_page_layout_name(notesPageLayoutName);
+ notes.set_draw_style_name(drawingPageStyles[p->notesMaster]);
m_currentMaster = 0;
if (p->notesMaster->drawing.OfficeArtDg.groupShape) {
@@ -2145,7 +2189,6 @@ void PptToOdp::createMainStyles(KoGenStyles& styles)
drawclient.setDrawClientData(0, 0, p->notesMaster, 0);
odrawtoodf.processGroupShape(spgr, out);
}
- writer.endElement();
}
m_processingMasters = true;
@@ -2225,27 +2268,24 @@ QByteArray PptToOdp::createContent(KoGenStyles& styles)
KoXmlWriter contentWriter(&contentBuffer);
contentWriter.startDocument("office:document-content");
- contentWriter.startElement("office:document-content");
- contentWriter.addAttribute("xmlns:fo", \
"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");
- contentWriter.addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0");
- contentWriter.addAttribute("xmlns:style", \
"urn:oasis:names:tc:opendocument:xmlns:style:1.0");
- contentWriter.addAttribute("xmlns:text", \
"urn:oasis:names:tc:opendocument:xmlns:text:1.0");
- contentWriter.addAttribute("xmlns:draw", \
"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
- contentWriter.addAttribute("xmlns:presentation", \
"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0");
- contentWriter.addAttribute("xmlns:svg", \
"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");
- contentWriter.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
- contentWriter.addAttribute("office:version", "1.2");
+ {
+ office_document_content content(&contentWriter);
+ content.addAttribute("xmlns:fo", \
"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"); + \
content.addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0"); + \
content.addAttribute("xmlns:style", \
"urn:oasis:names:tc:opendocument:xmlns:style:1.0"); + \
content.addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0"); \
+ content.addAttribute("xmlns:draw", \
"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"); + \
content.addAttribute("xmlns:presentation", \
"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"); + \
content.addAttribute("xmlns:svg", \
"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"); + \
content.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
// office:automatic-styles
styles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, &contentWriter);
- // office:body
- contentWriter.startElement("office:body");
- contentWriter.startElement("office:presentation");
- contentWriter.addCompleteElement(&presentationBuffer);
- contentWriter.endElement(); // office:presentation
- contentWriter.endElement(); // office:body
- contentWriter.endElement(); // office:document-content
+ office_body body(content.add_office_body());
+ office_presentation presentation(body.add_office_presentation());
+ presentation.addCompleteElement(&presentationBuffer);
+ }
contentWriter.endDocument();
return contentData;
}
@@ -2258,52 +2298,44 @@ QByteArray PptToOdp::createMeta()
KoXmlWriter metaWriter(&buff);
metaWriter.startDocument("office:document-meta");
- metaWriter.startElement("office:document-meta");
- metaWriter.addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0");
- metaWriter.addAttribute("xmlns:meta", \
"urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
- metaWriter.addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
- metaWriter.addAttribute("office:version", "1.2");
- metaWriter.startElement("office:meta");
-
- const char *p_str = 0;
+ {
+ office_document_meta document_meta(&metaWriter);
+ document_meta.addAttribute("xmlns:office", \
"urn:oasis:names:tc:opendocument:xmlns:office:1.0"); + \
document_meta.addAttribute("xmlns:meta", \
"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"); + \
document_meta.addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"); + \
office_meta meta(document_meta.add_office_meta()); +
const MSO::PropertySet &ps = p->summaryInfo.propertySet.propertySet1;
for (uint i = 0; i < ps.numProperties; i++) {
- switch (ps.propertyIdentifierAndOffset.at(i).propertyIdentifier) {
- case PIDSI_TITLE:
- p_str = "dc:title";
- break;
- case PIDSI_SUBJECT:
- p_str = "dc:subject";
- break;
- case PIDSI_AUTHOR:
- p_str = "meta:initial-creator";
- break;
- case PIDSI_KEYWORDS:
- p_str = "meta:keyword";
- break;
- case PIDSI_COMMENTS:
- p_str = "dc:description";
- break;
- case PIDSI_LASTAUTHOR:
- p_str = "dc:creator";
- break;
- default:
- break;
- }
- if (p_str) {
- if (ps.property.at(i).vt_lpstr) {
- metaWriter.startElement(p_str);
- metaWriter.addTextNode(ps.property.at(i).vt_lpstr->characters);
- metaWriter.endElement();
+ const QSharedPointer<CodePageString>& vt_lpstr = ps.property.at(i).vt_lpstr;
+ if (vt_lpstr) {
+ switch (ps.propertyIdentifierAndOffset.at(i).propertyIdentifier) {
+ case PIDSI_TITLE:
+ meta.add_dc_title().addTextNode(vt_lpstr->characters);
+ break;
+ case PIDSI_SUBJECT:
+ meta.add_dc_subject().addTextNode(vt_lpstr->characters);
+ break;
+ case PIDSI_AUTHOR:
+ meta.add_meta_initial_creator().addTextNode(vt_lpstr->characters);
+ break;
+ case PIDSI_KEYWORDS:
+ meta.add_meta_keyword().addTextNode(vt_lpstr->characters);
+ break;
+ case PIDSI_COMMENTS:
+ meta.add_dc_description().addTextNode(vt_lpstr->characters);
+ break;
+ case PIDSI_LASTAUTHOR:
+ meta.add_dc_creator().addTextNode(vt_lpstr->characters);
+ break;
+ default:
+ break;
}
- p_str = 0;
}
}
-
- metaWriter.endElement(); // office:meta
- metaWriter.endElement(); // office:document-meta
-
+ }
+ metaWriter.endDocument();
return metaData;
}
@@ -2362,7 +2394,7 @@ const TextPFRun *findTextPFRun(const StyleTextPropAtom& style, \
unsigned int pos) }
void
-writeMeta(const TextContainerMeta& m, bool master, KoXmlWriter& out)
+writeMeta(const TextContainerMeta& m, bool master, text_meta& meta)
{
const SlideNumberMCAtom* a = m.meta.get<SlideNumberMCAtom>();
const DateTimeMCAtom* b = m.meta.get<DateTimeMCAtom>();
@@ -2370,37 +2402,30 @@ writeMeta(const TextContainerMeta& m, bool master, \
KoXmlWriter& out) const HeaderMCAtom* d = m.meta.get<HeaderMCAtom>();
const FooterMCAtom* e = m.meta.get<FooterMCAtom>();
const RTFDateTimeMCAtom* f = m.meta.get<RTFDateTimeMCAtom>();
- out.startElement("text:meta");
if (a) {
- out.startElement("text:page-number");
- out.endElement();
+ meta.add_text_page_number();
}
if (b) {
// TODO: datetime format
- out.startElement("text:time");
- out.endElement();
+ meta.add_text_time();
}
if (c) {
// TODO: datetime format
if (master) {
- out.startElement("presentation:date-time");
+ meta.add_presentation_date_time();
} else {
- out.startElement("text:date");
+ meta.add_text_date();
}
- out.endElement();
}
if (d) {
- out.startElement("presentation:header");
- out.endElement();
+ meta.add_presentation_header();
}
if (e) {
- out.startElement("presentation:footer");
- out.endElement();
+ meta.add_presentation_footer();
}
if (f) {
// TODO
}
- out.endElement();
}
template <class T>
@@ -2418,63 +2443,137 @@ int getMeta(const TextContainerMeta& m, const \
TextContainerMeta*& meta, return end;
}
-/**
-* @brief Write text deindentations the specified amount. Actually it just
-* closes elements.
-*
-* @param xmlWriter XML writer to write closing tags
-* @param count how many lists and list items to leave open
-* @param levels the list of levels to remove from
-*/
-void writeTextObjectDeIndent(KoXmlWriter& xmlWriter, const int count,
- QStack<QString>& levels)
-{
- while (levels.size() > count) {
- xmlWriter.endElement(); //text:list-item
- xmlWriter.endElement(); //text:list
- levels.pop();
- }
-}
-
void PptToOdp::addListElement(KoXmlWriter& out, const QString& listStyle,
- QStack<QString>& levels, quint16 level,
+ ListStack& levels, quint16 level,
const PptTextPFRun &pf)
{
- levels.push(listStyle);
- out.startElement("text:list");
+ levels.push(TextListTag(listStyle, out));
+ text_list& list = *levels.last().list;
if (!listStyle.isEmpty()) {
- out.addAttribute("text:style-name", listStyle);
+ list.set_text_style_name(listStyle);
} else {
qDebug() << "Warning: list style name not provided!";
}
if (pf.fBulletHasAutoNumber()) {
QString xmlId = QString("lvl%1").arg(level);
xmlId.append(QString("_%1").arg(qrand()));
- out.addAttribute("xml:id", xmlId);
+ list.set_xml_id(xmlId);
if (m_continueListNumbering.contains(level) &&
- m_continueListNumbering[level]) {
- out.addAttribute("text:continue-list", m_lvlXmlIdMap[level]);
+ m_continueListNumbering[level]) {
+ list.set_text_continue_list(m_lvlXmlIdMap[level]);
}
m_lvlXmlIdMap[level] = xmlId;
}
- out.startElement("text:list-item");
+
+ text_list_item& item = levels.last().add_text_list_item();
if (pf.fBulletHasAutoNumber()) {
if (m_continueListNumbering.contains(level) &&
(m_continueListNumbering[level] == false)) {
- out.addAttribute("text:start-value", pf.startNum());
+ item.set_text_start_value(pf.startNum());
}
m_continueListNumbering[level] = true;
}
// add styleless levels to get the right level of indentation
while (levels.size() < level) {
- out.startElement("text:list");
- out.startElement("text:list-item");
- levels.push("");
+ levels.push(TextListTag("", *levels.last().item));
+ levels.last().add_text_list_item();
+ }
+}
+template <typename T>
+void
+addTab(T& e, int ref) {
+ text_tab tab = e.add_text_tab();
+ if (ref >= 0)
+ tab.set_text_tab_ref(ref);
+}
+
+void addTextSpan(group_paragraph_content& content, const QString& text, const \
QMap<int, int>& tabCache) +{
+ int len = text.length();
+ int nrSpaces = 0; // number of consecutive spaces
+ bool leadingSpace = false;
+ QString str;
+ str.reserve(len);
+
+ // Accumulate chars either in str or in nrSpaces (for spaces).
+ // Flush str when writing a subelement (for spaces or for another reason)
+ // Flush nrSpaces when encountering two or more consecutive spaces
+ for (int i = 0; i < len ; ++i) {
+ QChar ch = text[i];
+ ushort unicode = ch.unicode();
+ if (unicode == ' ') {
+ if (i == 0)
+ leadingSpace = true;
+ ++nrSpaces;
+ } else {
+ if (nrSpaces > 0) {
+ // For the first space we use ' '.
+ // "it is good practice to use (text:s) for the second and all \
following SPACE + // characters in a sequence." (per the ODF spec)
+ // however, per the HTML spec, "authors should not rely on user \
agents to render + // white space immediately after a start tag or \
immediately before an end tag" + // (and both we and OO.o ignore \
leading spaces in <text:p> or <text:h> elements...) + if \
(!leadingSpace) { + str += ' ';
+ --nrSpaces;
+ }
+ if (nrSpaces > 0) { // there are more spaces
+ if (!str.isEmpty())
+ content.addTextNode(str);
+ str.clear();
+ text_s s = content.add_text_s();
+ if (nrSpaces > 1) // it's 1 by default
+ s.set_text_c(nrSpaces);
+ }
+ }
+ nrSpaces = 0;
+ leadingSpace = false;
+
+ switch (unicode) {
+ case '\t':
+ if (!str.isEmpty())
+ content.addTextNode(str);
+ str.clear();
+ addTab(content, tabCache.contains(i) ?tabCache[i] + 1 :-1);
+ break;
+ // gracefully handle \f form feed in text input.
+ // otherwise the xml will not be valid.
+ // \f can be added e.g. in ascii import filter.
+ case '\f':
+ case '\n':
+ case QChar::LineSeparator:
+ if (!str.isEmpty())
+ content.addTextNode(str);
+ str.clear();
+ content.add_text_line_break();
+ break;
+ default:
+ // don't add stuff that is not allowed in xml. The stuff we need we \
have already handled above + if (ch.unicode() >= 0x20) {
+ str += text[i];
+ }
+ break;
+ }
+ }
+ }
+ // either we still have text in str or we have spaces in nrSpaces
+ if (!str.isEmpty()) {
+ content.addTextNode(str);
+ }
+ if (nrSpaces > 0) { // there are more spaces
+ text_s s = content.add_text_s();
+ if (nrSpaces > 1) // it's 1 by default
+ s.set_text_c(nrSpaces);
}
}
+void addTextSpan(group_paragraph_content& e, const QString& text)
+{
+ QMap<int, int> tabCache;
+ addTextSpan(e, text, tabCache);
+}
int PptToOdp::processTextSpan(Writer& out, PptTextCFRun& cf, const \
MSO::TextContainer* tc,
const QString& text, const int start, int end, \
quint16* p_fs) @@ -2615,8 +2714,8 @@ int PptToOdp::processTextSpan(Writer& out, \
PptTextCFRun& cf, const MSO::TextCont KoGenStyle style(KoGenStyle::TextAutoStyle, \
"text"); style.setAutoStyleInStylesDotXml(out.stylesxml);
defineTextProperties(style, cf, 0, 0, si, isSymbol);
- out.xml.startElement("text:span", false);
- out.xml.addAttribute("text:style-name", out.styles.insert(style));
+ text_span span(&out.xml);
+ span.set_text_style_name(out.styles.insert(style));
// [MS-PPT]: exHyperlinkIdRef must be ignored unless action is in
// {II_JumpAction, II_HyperlinkAction, II_CustomShowAction (0x7)}
@@ -2635,27 +2734,22 @@ int PptToOdp::processTextSpan(Writer& out, PptTextCFRun& cf, \
const MSO::TextCont }
}
+ QString href;
if (mouseclick) {
- out.xml.startElement("text:a", false);
QPair<QString, QString> link = findHyperlink(
mouseclick->interactive.interactiveInfoAtom.exHyperlinkIdRef);
if (!link.second.isEmpty()) { // target
- out.xml.addAttribute("xlink:href", link.second);
- out.xml.addAttribute("xlink:type", "simple");
+ href = link.second;
} else if (!link.first.isEmpty()) {
- out.xml.addAttribute("xlink:href", link.first);
- out.xml.addAttribute("xlink:type", "simple");
+ href = link.first;
}
} else if (mouseover) {
- out.xml.startElement("text:a", false);
QPair<QString, QString> link = findHyperlink(
mouseover->interactive.interactiveInfoAtom.exHyperlinkIdRef);
if (!link.second.isEmpty()) { // target
- out.xml.addAttribute("xlink:href", link.second);
- out.xml.addAttribute("xlink:type", "simple");
+ href = link.second;
} else if (!link.first.isEmpty()) {
- out.xml.addAttribute("xlink:href", link.first);
- out.xml.addAttribute("xlink:type", "simple");
+ href = link.first;
}
} else {
// count - specifies the number of characters of the
@@ -2670,18 +2764,25 @@ int PptToOdp::processTextSpan(Writer& out, PptTextCFRun& cf, \
const MSO::TextCont }
if (meta) {
- writeMeta(*meta, m_processingMasters, out.xml);
+ if (!href.isNull()) {
+ text_a a(span.add_text_a(href));
+ text_meta m(a.add_text_meta());
+ writeMeta(*meta, m_processingMasters, m);
+ } else {
+ text_meta m(span.add_text_meta());
+ writeMeta(*meta, m_processingMasters, m);
+ }
} else {
int len = end - start;
const QString txt = text.mid(start, len).replace('\r', '\n').replace('\v', \
'\n');
- out.xml.addTextSpan(txt);
- }
-
- if (mouseclick || mouseover) {
- out.xml.endElement(); //text:a
+ if (!href.isNull()) {
+ text_a a(span.add_text_a(href));
+ addTextSpan(a, txt);
+ } else {
+ addTextSpan(span, txt);
+ }
}
- out.xml.endElement(); //text:span
return end;
} //end processTextSpan()
@@ -2719,7 +2820,7 @@ QString PptToOdp::defineAutoListStyle(Writer& out, const \
PptTextPFRun& pf, const
void
PptToOdp::processParagraph(Writer& out,
- QStack<QString>& levels,
+ ListStack& levels,
const MSO::OfficeArtClientData* clientData,
const MSO::TextContainer* tc,
const MSO::TextRuler* tr,
@@ -2795,10 +2896,10 @@ PptToOdp::processParagraph(Writer& out,
}
QString listStyle = defineAutoListStyle(out, pf, cf);
- //check if we have the corresponding style for this level, if not then
- //close the list and create a new one (K.I.S.S.)
- if (!levels.isEmpty() && (levels.first() != listStyle)) {
- writeTextObjectDeIndent(out.xml, 0, levels);
+ //check if we have the corresponding style for this level, if not then
+ //close the list and create a new one (K.I.S.S.)
+ if (!levels.isEmpty() && (levels.first().style != listStyle)) {
+ levels.clear();
}
if (!pf.fBulletHasAutoNumber()) {
QList<quint16> levels = m_continueListNumbering.keys();
@@ -2820,18 +2921,16 @@ PptToOdp::processParagraph(Writer& out,
if (levels.isEmpty()) {
addListElement(out.xml, listStyle, levels, depth, pf);
} else {
- out.xml.endElement(); //text:list-item
- out.xml.startElement("text:list-item");
+ levels.last().add_text_list_item();
}
m_previousListLevel = depth;
} else {
- writeTextObjectDeIndent(out.xml, 0, levels);
+ levels.clear();
m_continueListNumbering.clear();
m_lvlXmlIdMap.clear();
m_previousListLevel = 0;
}
- out.xml.startElement("text:p");
KoGenStyle style(KoGenStyle::ParagraphAutoStyle, "paragraph");
style.setAutoStyleInStylesDotXml(out.stylesxml);
defineParagraphProperties(style, pf, min_fontsize);
@@ -2839,9 +2938,15 @@ PptToOdp::processParagraph(Writer& out,
if (start == end) {
defineTextProperties(style, cf, 0, 0, 0);
}
- out.xml.addAttribute("text:style-name", out.styles.insert(style));
- out.xml.addCompleteElement(&spans_buf);
- out.xml.endElement(); //text:p
+ if (levels.isEmpty()) {
+ text_p p(&out.xml);
+ p.set_text_style_name(out.styles.insert(style));
+ p.addCompleteElement(&spans_buf);
+ } else {
+ text_p p(levels.last().item->add_text_p());
+ p.set_text_style_name(out.styles.insert(style));
+ p.addCompleteElement(&spans_buf);
+ }
} //end processParagraph()
int PptToOdp::processTextForBody(Writer& out, const MSO::OfficeArtClientData* \
clientData, @@ -2915,7 +3020,7 @@ int PptToOdp::processTextForBody(Writer& out, const \
MSO::OfficeArtClientData* cl static const QRegExp lineend("[\v\r]");
qint32 pos = 0, end = 0;
- QStack<QString> levels;
+ ListStack levels;
levels.reserve(5);
// loop over all the '\r' delimited lines
@@ -2927,8 +3032,6 @@ int PptToOdp::processTextForBody(Writer& out, const \
MSO::OfficeArtClientData* cl pos = end + 1;
}
- // close all open text:list elements
- writeTextObjectDeIndent(out.xml, 0, levels);
return 0;
} //end processTextForBody()
@@ -2963,17 +3066,17 @@ void PptToOdp::processSlideForBody(unsigned slideNo, Writer& \
out) nameStr.remove('\r');
nameStr.remove('\v');
- out.xml.startElement("draw:page");
QString value = masterNames.value(master);
- if (!value.isEmpty()) {
- out.xml.addAttribute("draw:master-page-name", value);
+ if (value.isEmpty()) {
+ value = "unknown";
}
- out.xml.addAttribute("draw:name", nameStr);
+ draw_page page(&out.xml, value);
+ page.set_draw_name(nameStr);
value = drawingPageStyles[slide];
if (!value.isEmpty()) {
- out.xml.addAttribute("draw:style-name", value);
+ page.set_draw_style_name(value);
}
- //xmlWriter.addAttribute("presentation:presentation-page-layout-name", "AL1T0");
+ //page.set_presentation_presentation_page_layout_name("AL1T0");
const HeadersFootersAtom* headerFooterAtom = 0;
if (master->anon.is<MainMasterContainer>()) {
@@ -2991,16 +3094,16 @@ void PptToOdp::processSlideForBody(unsigned slideNo, Writer& \
out) headerFooterAtom = &getSlideHF()->hfAtom;
}
if (!usedDateTimeDeclaration.value(slideNo).isEmpty()) {
- out.xml.addAttribute("presentation:use-date-time-name",
- usedDateTimeDeclaration[slideNo]);
+ page.set_presentation_use_date_time_name(
+ usedDateTimeDeclaration[slideNo]);
}
if (!usedHeaderDeclaration.value(slideNo).isEmpty()) {
if (!usedHeaderDeclaration[slideNo].isEmpty())
- out.xml.addAttribute("presentation:use-header-name", \
usedHeaderDeclaration[slideNo]); + \
page.set_presentation_use_header_name(usedHeaderDeclaration[slideNo]); }
if (!usedFooterDeclaration.value(slideNo).isEmpty()) {
if (!usedFooterDeclaration[slideNo].isEmpty())
- out.xml.addAttribute("presentation:use-footer-name", \
usedFooterDeclaration[slideNo]); + \
page.set_presentation_use_footer_name(usedFooterDeclaration[slideNo]); }
m_currentSlideTexts = &p->documentContainer->slideList->rgChildRec[slideNo];
@@ -3029,18 +3132,15 @@ void PptToOdp::processSlideForBody(unsigned slideNo, Writer& \
out) const NotesContainer* nc = p->notes[slideNo];
if (nc && nc->drawing.OfficeArtDg.groupShape) {
m_currentSlideTexts = 0;
- out.xml.startElement("presentation:notes");
+ presentation_notes notes(page.add_presentation_notes());
value = drawingPageStyles[nc];
if (!value.isEmpty()) {
- out.xml.addAttribute("draw:style-name", value);
+ notes.set_draw_style_name(value);
}
const OfficeArtSpgrContainer& spgr = \
*(nc->drawing.OfficeArtDg.groupShape).data();
drawclient.setDrawClientData(0, 0, p->notesMaster, nc, m_currentSlideTexts);
odrawtoodf.processGroupShape(spgr, out);
- out.xml.endElement();
}
-
- out.xml.endElement(); // draw:page
} //end processSlideForBody()
QString PptToOdp::processParaSpacing(const int value,
@@ -3506,42 +3606,32 @@ void PptToOdp::processDeclaration(KoXmlWriter* xmlWriter)
QList<QPair<QString, QString> >items = declaration.values(DateTime);
for( int i = items.size()-1; i >= 0; --i) {
QPair<QString, QString > item = items.at(i);
- xmlWriter->startElement("presentation:date-time-decl");
- xmlWriter->addAttribute("presentation:name", item.first);
- xmlWriter->addAttribute("presentation:source", "current-date");
+ presentation_date_time_decl(xmlWriter, item.first, "current-date");
//xmlWrite->addAttribute("style:data-style-name", "Dt1");
- xmlWriter->endElement(); // presentation:date-time-decl
}
} else if (slideHF->hfAtom.fHasUserDate) {
QList<QPair<QString, QString> >items = declaration.values(DateTime);
for( int i = 0; i < items.size(); ++i) {
QPair<QString, QString > item = items.at(i);
- xmlWriter->startElement("presentation:date-time-decl");
- xmlWriter->addAttribute("presentation:name", item.first);
- xmlWriter->addAttribute("presentation:source", "fixed");
- xmlWriter->addTextNode(item.second);
+ presentation_date_time_decl d(xmlWriter, item.first, "fixed");
+ d.addTextNode(item.second);
//Future - Add Fixed date data here
- xmlWriter->endElement(); //presentation:date-time-decl
}
}
if (headerAtom && slideHF->hfAtom.fHasHeader) {
QList< QPair < QString, QString > > items = declaration.values(Header);
for( int i = items.size()-1; i >= 0; --i) {
QPair<QString, QString > item = items.value(i);
- xmlWriter->startElement("presentation:header-decl");
- xmlWriter->addAttribute("presentation:name", item.first);
- xmlWriter->addTextNode(item.second);
- xmlWriter->endElement(); //presentation:header-decl
+ presentation_header_decl hd(xmlWriter, item.first);
+ hd.addTextNode(item.second);
}
}
if (footerAtom && slideHF->hfAtom.fHasFooter) {
QList< QPair < QString, QString > > items = declaration.values(Footer);
for( int i = items.size()-1 ; i >= 0; --i) {
QPair<QString, QString > item = items.at(i);
- xmlWriter->startElement("presentation:footer-decl");
- xmlWriter->addAttribute("presentation:name", item.first);
- xmlWriter->addTextNode(item.second);
- xmlWriter->endElement(); //presentation:footer-decl
+ presentation_footer_decl fd(xmlWriter, item.first);
+ fd.addTextNode(item.second);
}
}
}
diff --git a/filters/stage/powerpoint/PptToOdp.h \
b/filters/stage/powerpoint/PptToOdp.h index 8d85c1f..fce083b 100644
--- a/filters/stage/powerpoint/PptToOdp.h
+++ b/filters/stage/powerpoint/PptToOdp.h
@@ -96,6 +96,9 @@ public:
* @return path
*/
QString getPicturePath(const quint32 pib) const;
+
+ class TextListTag;
+ typedef QStack<TextListTag> ListStack;
private:
/**
@@ -310,6 +313,14 @@ private:
const quint16 indentLevel,
const ListStyleInput& info);
+ void defineListStyleProperties(KoXmlWriter& out, bool imageBullet,
+ const QString& bulletSize,
+ const PptTextPFRun& pf);
+
+ void defineListStyleTextProperties(KoXmlWriter& out_,
+ const QString& bulletSize,
+ const PptTextPFRun& pf);
+
/**
* TODO:
* @param
@@ -368,7 +379,7 @@ private:
*/
void addListElement(KoXmlWriter& out,
const QString& listStyle,
- QStack<QString>& levels,
+ ListStack& levels,
quint16 level,
const PptTextPFRun &pf);
@@ -411,7 +422,7 @@ private:
* @param end specifies end of the paragraph in text
*/
void processParagraph(Writer& out,
- QStack<QString>& levels,
+ ListStack& levels,
const MSO::OfficeArtClientData* cd,
const MSO::TextContainer* tc,
const MSO::TextRuler* tr,
diff --git a/libs/kotext/KoInlineNote.cpp b/libs/kotext/KoInlineNote.cpp
index 6faa9a9..4f7024b 100644
--- a/libs/kotext/KoInlineNote.cpp
+++ b/libs/kotext/KoInlineNote.cpp
@@ -32,6 +32,9 @@
#include <KoStyleManager.h>
#include <KoElementReference.h>
#include <kdebug.h>
+#include <writeodf/writeodftext.h>
+#include <writeodf/writeodfoffice.h>
+#include <writeodf/writeodfdc.h>
#include <QTextDocument>
#include <QTextFrame>
@@ -43,6 +46,8 @@
#include <QDateTime>
#include <QWeakPointer>
+using namespace writeodf;
+
class KoInlineNote::Private
{
public:
@@ -239,44 +244,30 @@ void KoInlineNote::saveOdf(KoShapeSavingContext & context)
KoXmlWriter *writer = &context.xmlWriter();
if (d->type == Footnote || d->type == Endnote) {
- writer->startElement("text:note", false);
- if (d->type == Footnote) {
- writer->addAttribute("text:note-class", "footnote");
- } else {
- writer->addAttribute("text:note-class", "endnote");
- }
-
- writer->startElement("text:note-citation", false);
+ text_note note(writer, (d->type == Footnote) ?"footnote" :"endnote");
+ text_note_citation cite(note.add_text_note_citation());
if (!autoNumbering()) {
- writer->addAttribute("text:label", d->label);
+ cite.set_text_label(d->label);
}
- writer->addTextNode(d->label);
- writer->endElement();
+ cite.addTextNode(d->label);
- writer->startElement("text:note-body", false);
+ text_note_body body(note.add_text_note_body());
KoTextWriter textWriter(context);
textWriter.write(d->document, d->textFrame->firstPosition(), \
d->textFrame->lastPosition());
- writer->endElement();
-
- writer->endElement();
}
else if (d->type == Annotation) {
- writer->startElement("office:annotation");
+ office_annotation annotation(writer);
if (!d->author.isEmpty()) {
- writer->startElement("dc:creator");
- writer->addTextNode(d->author);
- writer->endElement();
+ dc_creator creator(annotation.add_dc_creator());
+ creator.addTextNode(d->author);
}
if (d->date.isValid()) {
- writer->startElement("dc:date");
- writer->addTextSpan(d->date.toString(Qt::ISODate));
- writer->endElement();
+ dc_date date(annotation.add_dc_date());
+ date.addTextNode(d->date.toString(Qt::ISODate));
}
KoTextWriter textWriter(context);
textWriter.write(d->document, \
d->textFrame->firstPosition(),d->textFrame->lastPosition());
-
- writer->endElement();
}
}
diff --git a/libs/odf/CMakeLists.txt b/libs/odf/CMakeLists.txt
index 8549ace..ad11606 100644
--- a/libs/odf/CMakeLists.txt
+++ b/libs/odf/CMakeLists.txt
@@ -1,4 +1,5 @@
add_subdirectory( tests )
+add_subdirectory( writeodf )
include_directories( ${KOODF_INCLUDES} )
@@ -53,6 +54,7 @@ set(koodf_LIB_SRCS
)
kde4_add_library(koodf SHARED ${koodf_LIB_SRCS})
+add_dependencies(koodf writeodf.h-target)
target_link_libraries(koodf ${KDE4_KIO_LIBS} ${QT_QTXML_LIBRARY})
target_link_libraries(koodf LINK_INTERFACE_LIBRARIES ${KDE4_KIO_LIBS})
diff --git a/libs/odf/writeodf/CMakeLists.txt b/libs/odf/writeodf/CMakeLists.txt
new file mode 100644
index 0000000..51fae01
--- /dev/null
+++ b/libs/odf/writeodf/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(RNGFILE ${CMAKE_SOURCE_DIR}/devtools/scripts/OpenDocument-v1.2-cs01-schema-calligra.rng)
+set(RNGHEADERDIR ${CMAKE_BINARY_DIR}/libs/odf/writeodf)
+add_custom_command(
+ OUTPUT ${RNGHEADERDIR}/writeodf.h
+ COMMAND ${CMAKE_BINARY_DIR}/devtools/rng2cpp/rng2cpp
+ ARGS ${RNGFILE} ${RNGHEADERDIR}
+ DEPENDS rng2cpp ${RNGFILE}
+ WORKING_DIRECTORY ${RNGHEADERDIR}
+)
+add_custom_target(writeodf.h-target DEPENDS writeodf.h)
diff --git a/libs/odf/writeodf/README.txt b/libs/odf/writeodf/README.txt
new file mode 100644
index 0000000..7aacc60
--- /dev/null
+++ b/libs/odf/writeodf/README.txt
@@ -0,0 +1,16 @@
+Generated classes in the writeodf namespace.
+
+The program rng2cpp compiles a given Relax NG (.rng) file into C++ headers. In \
Calligra, this is used in combination with the RNG for OpenDocument Format. +
+The generated code has an API with class names that resemble the names of the ODF \
tags. <text:h/> becomes writeodf::text_h, <office:automatic-styles> becomes \
writeodf::office_automatic_styles. +
+The generated code has advantages of directly using KoXMLWriter.
+ - function names instead of strings gives autocompletion and catches typing errors \
at compile time. + - since elements are added into other elements, the nesting is \
checked at compile time. + - elements are automatically closed when they go out of \
scope (but end() can be called to close them sooner) + - elements are automatically \
closed if another item (text or element or other) is added to its parent +
+Future improvements:
+ - also generate code for reading elements
+ - also generate code from OOXML Relax NG files
+ - check data types (bool, int, string) at compile time
diff --git a/libs/odf/writeodf/helpers.h b/libs/odf/writeodf/helpers.h
new file mode 100644
index 0000000..f546f20
--- /dev/null
+++ b/libs/odf/writeodf/helpers.h
@@ -0,0 +1,59 @@
+#ifndef WRITEODF_ODFWRITER_H
+#define WRITEODF_ODFWRITER_H
+
+#include "writeodf/writeodfconfig.h"
+
+namespace writeodf {
+
+template <class T>
+void addConfigItem(T& config, const QString & configName, const QString& value)
+{
+ config_config_item item(config.add_config_config_item(configName, "string"));
+ item.addTextNode(value);
+}
+
+template <class T>
+void addConfigItem(T& config, const QString & configName, bool value)
+{
+ config_config_item item(config.add_config_config_item(configName, "boolean"));
+ item.addTextNode(value ? "true" : "false");
+}
+
+template <class T>
+void addConfigItem(T& config, const QString & configName, int value)
+{
+ config_config_item item(config.add_config_config_item(configName, "int"));
+ item.addTextNode(QString::number(value));
+}
+
+template <class T>
+void addConfigItem(T& config, const QString & configName, double value)
+{
+ config_config_item item(config.add_config_config_item(configName, "double"));
+ item.addTextNode(QString::number(value));
+}
+
+template <class T>
+void addConfigItem(T& config, const QString & configName, float value)
+{
+ config_config_item item(config.add_config_config_item(configName, "double"));
+ item.addTextNode(QString::number(value));
+}
+
+template <class T>
+void addConfigItem(T& config, const QString & configName, long value)
+{
+ config_config_item item(config.add_config_config_item(configName, "long"));
+ item.addTextNode(QString::number(value));
+}
+
+template <class T>
+void addConfigItem(T& config, const QString & configName, short value)
+{
+ config_config_item item(config.add_config_config_item(configName, "short"));
+ item.addTextNode(QString::number(value));
+}
+
+}
+
+#endif
diff --git a/libs/odf/writeodf/odfwriter.h b/libs/odf/writeodf/odfwriter.h
new file mode 100644
index 0000000..22bc240
--- /dev/null
+++ b/libs/odf/writeodf/odfwriter.h
@@ -0,0 +1,101 @@
+#ifndef ODFWRITER_H
+#define ODFWRITER_H
+
+#include <KoXmlWriter.h>
+#include <QtCore/QUrl>
+#include <QtCore/QDate>
+#include <QtCore/QStringList>
+
+class OdfWriter {
+private:
+ OdfWriter* child;
+ OdfWriter* parent;
+ void operator=(const OdfWriter&);
+protected:
+ mutable KoXmlWriter* xml;
+ OdfWriter(KoXmlWriter* xml_, const char* tag, bool indent) :child(0), parent(0), \
xml(xml_) { + xml->startElement(tag, indent);
+ }
+ OdfWriter(OdfWriter* p, const char* tag, bool indent) :child(0), parent(p), \
xml(parent->xml) { + if (parent->child) {
+ parent->child->end();
+ }
+ parent->child = this;
+ xml->startElement(tag, indent);
+ }
+ ~OdfWriter() {
+ end();
+ }
+ void endChild() {
+ if (child) {
+ child->parent = 0;
+ child->end();
+ child = 0;
+ }
+ }
+ // ideally, the copy constructor would never be called
+ OdfWriter(const OdfWriter&o) :child(o.child), parent(o.parent), xml(o.xml) {
+ // disable o and make the parent refer to this new copy
+ o.xml = 0;
+ if (parent && parent->child == &o) {
+ parent->child = this;
+ }
+ }
+public:
+ void end() {
+ if (xml) {
+ endChild();
+ xml->endElement();
+ if (parent) {
+ parent->child = 0;
+ }
+ xml = 0;
+ }
+ }
+ void addTextNode(const QString& str) {
+ endChild();
+ xml->addTextNode(str);
+ }
+ void addAttribute(const char* name, const char* value) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, value);
+ }
+ void addAttribute(const char* name, const QString& value) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, value);
+ }
+ void addAttribute(const char* name, quint64 value) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, QString::number(value));
+ }
+ void addAttribute(const char* name, const QUrl& value) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, value.toString());
+ }
+ void addAttribute(const char* name, const QDate& value) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, value.toString(Qt::ISODate));
+ }
+ void addAttribute(const char* name, const QTime& value) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, value.toString(Qt::ISODate));
+ }
+ void addAttribute(const char* name, const QDateTime& value) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, value.toString(Qt::ISODate));
+ }
+ void addAttribute(const char* name, const QStringList& /*value*/) {
+ Q_ASSERT(!child);
+ xml->addAttribute(name, "");
+ }
+ void addProcessingInstruction(const char* cstr) {
+ endChild();
+ xml->addProcessingInstruction(cstr);
+ }
+ template <class T>
+ void addCompleteElement(T cstr) {
+ endChild();
+ xml->addCompleteElement(cstr);
+ }
+};
+#endif
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic