1 /* This file is part of the KDE project
2
3 Copyright (C) 2013 Inge Wallin <inge@lysator.liu.se>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19 */
20
21
22 // Own
23 #include "OdfTextReaderDocxBackend.h"
24
25 // Qt
26 #include <QtGlobal>
27
28 // Calligra
29 #include <KoXmlWriter.h>
30 #include <KoOdfStyle.h>
31 #include <KoOdfStyleManager.h>
32 #include <KoOdfStyleProperties.h>
33
34 // This filter
35 #include "OdfReaderDocxContext.h"
36 #include "DocxStyleHelper.h"
37 #include "DocxExportDebug.h"
38
39 #if 0
40 #define DEBUG_BACKEND() \
41 debugDocx << (reader.isStartElement() ? "start": (reader.isEndElement() ? "end" : "other")) \
42 << reader.qualifiedName().toString()
43 #else
44 #define DEBUG_BACKEND() \
45 //NOTHING
46 #endif
47
48
49 // ================================================================
50 // class OdfTextReaderDocxBackend
51
52
OdfTextReaderDocxBackend()53 OdfTextReaderDocxBackend::OdfTextReaderDocxBackend()
54 : OdfTextReaderBackend()
55 , m_insideSpanLevel(0)
56 , m_currentOutlineLevel(-1)
57 , m_commentIndex(0)
58 , m_writeComment(false)
59 , m_insideComment(false)
60 , m_insideDcCreator(false)
61 , m_insideDcDate(false)
62 , m_currentParagraphTextProperties(0)
63 {
64 }
65
~OdfTextReaderDocxBackend()66 OdfTextReaderDocxBackend::~OdfTextReaderDocxBackend()
67 {
68 }
69
70
71 // ----------------------------------------------------------------
72 // Text level functions: paragraphs, headings, sections, frames, objects, etc
73
elementOfficeAnnotation(KoXmlStreamReader & reader,OdfReaderContext * context)74 void OdfTextReaderDocxBackend::elementOfficeAnnotation(KoXmlStreamReader &reader, OdfReaderContext *context)
75 {
76 DEBUG_BACKEND();
77 OdfReaderDocxContext *docxContext = dynamic_cast<OdfReaderDocxContext*>(context);
78 if (!docxContext) {
79 return;
80 }
81 KoXmlWriter *writer = docxContext->m_commentsWriter;
82
83 if (reader.isStartElement()) {
84 m_writeComment = true;
85 m_insideComment = true;
86 writer->startElement("w:comment");
87 writer->addAttribute("w:id", m_commentIndex);
88 }
89 else {
90 writer->endElement(); // w:comment
91 m_insideComment = false;
92 }
93 Q_UNUSED(reader);
94 }
95
elementDcCreator(KoXmlStreamReader & reader,OdfReaderContext * context)96 void OdfTextReaderDocxBackend::elementDcCreator(KoXmlStreamReader &reader, OdfReaderContext *context)
97 {
98 DEBUG_BACKEND();
99 Q_UNUSED(reader);
100 Q_UNUSED(context);
101 if (reader.isStartElement()) {
102 m_insideDcCreator = true;
103 }
104 else {
105 m_insideDcCreator = false;
106 }
107 }
108
elementDcDate(KoXmlStreamReader & reader,OdfReaderContext * context)109 void OdfTextReaderDocxBackend::elementDcDate(KoXmlStreamReader &reader, OdfReaderContext *context)
110 {
111 DEBUG_BACKEND();
112 Q_UNUSED(reader);
113 Q_UNUSED(context);
114 if (reader.isStartElement()) {
115 m_insideDcDate = true;
116 }
117 else {
118 m_insideDcDate = false;
119 }
120 }
121
elementTextH(KoXmlStreamReader & reader,OdfReaderContext * context)122 void OdfTextReaderDocxBackend::elementTextH(KoXmlStreamReader &reader, OdfReaderContext *context)
123 {
124 DEBUG_BACKEND();
125 KoXmlStreamAttributes attributes = reader.attributes();
126 m_currentOutlineLevel = attributes.value("text:outline-level").toString().toInt();
127 elementTextP(reader, context);
128 }
129
elementTextP(KoXmlStreamReader & reader,OdfReaderContext * context)130 void OdfTextReaderDocxBackend::elementTextP(KoXmlStreamReader &reader, OdfReaderContext *context)
131 {
132 DEBUG_BACKEND();
133 OdfReaderDocxContext *docxContext = dynamic_cast<OdfReaderDocxContext*>(context);
134 if (!docxContext) {
135 return;
136 }
137
138 m_currentParagraphTextProperties = 0;
139 m_currentParagraphParent.clear();
140
141 KoXmlWriter *writer = docxContext->m_documentWriter;
142 if (m_insideComment) {
143 writer = docxContext->m_commentsWriter;
144 }
145 if (reader.isStartElement()) {
146 writer->startElement("w:p");
147 // FIXME: Add paragraph attributes here
148 writer->startElement("w:pPr");
149 if (m_currentOutlineLevel >= 0) {
150 writer->startElement("w:outlineLvl");
151 writer->addAttribute("w:val", m_currentOutlineLevel);
152 writer->endElement(); // w:outlineLvl
153 }
154 KoXmlStreamAttributes attributes = reader.attributes();
155 QString textStyle = attributes.value("text:style-name").toString();
156 if (!textStyle.isEmpty()) {
157 KoOdfStyle *style = docxContext->styleManager()->style(textStyle, "paragraph");
158 KoOdfStyleProperties *parProperties = style->properties("style:paragraph-properties");
159 m_currentParagraphTextProperties = style->properties("style:text-properties");
160 m_currentParagraphParent = style->parent();
161 if (!m_currentParagraphParent.isEmpty()) {
162 writer->startElement("w:pStyle");
163 writer->addAttribute("w:val", m_currentParagraphParent);
164 writer->endElement(); // w:pStyle
165 }
166 DocxStyleHelper::handleParagraphStyles(parProperties, writer);
167 writer->startElement("w:rPr");
168 DocxStyleHelper::handleTextStyles(m_currentParagraphTextProperties, writer);
169 writer->endElement(); // w:rPr
170 }
171 // FIXME: Add paragraph properties (styling) here
172 writer->endElement(); // w:pPr
173 }
174 else {
175 writer->endElement(); // w:p
176 }
177 }
178
179
180 // ----------------------------------------------------------------
181 // Paragraph level functions: spans, annotations, notes, text content itself, etc.
182
183
elementTextSpan(KoXmlStreamReader & reader,OdfReaderContext * context)184 void OdfTextReaderDocxBackend::elementTextSpan(KoXmlStreamReader &reader, OdfReaderContext *context)
185 {
186 DEBUG_BACKEND();
187 OdfReaderDocxContext *docxContext = dynamic_cast<OdfReaderDocxContext*>(context);
188 if (!docxContext) {
189 return;
190 }
191
192 if (reader.isStartElement()) {
193 startRun(reader, docxContext);
194 ++m_insideSpanLevel;
195 }
196 else {
197 endRun(docxContext);
198 --m_insideSpanLevel;
199 }
200 }
201
elementTextS(KoXmlStreamReader & reader,OdfReaderContext * context)202 void OdfTextReaderDocxBackend::elementTextS(KoXmlStreamReader &reader, OdfReaderContext *context)
203 {
204 DEBUG_BACKEND();
205 if (!reader.isStartElement())
206 return;
207
208 OdfReaderDocxContext *docxContext = dynamic_cast<OdfReaderDocxContext*>(context);
209 if (!docxContext) {
210 return;
211 }
212
213 #if 0
214 QString dummy = element.attribute("text:c", "1");
215 bool ok;
216 int numSpaces = dummy.toUInt(&ok);
217 if (!ok)
218 numSpaces = 1;
219
220 // At the end of a paragraph, output two newlines.
221 docxContext->outStream << "\n\n";
222 #endif
223 }
224
225
characterData(KoXmlStreamReader & reader,OdfReaderContext * context)226 void OdfTextReaderDocxBackend::characterData(KoXmlStreamReader &reader, OdfReaderContext *context)
227 {
228 DEBUG_BACKEND();
229 OdfReaderDocxContext *docxContext = dynamic_cast<OdfReaderDocxContext*>(context);
230 if (!docxContext) {
231 return;
232 }
233 //debugDocx << reader.text().toString();
234
235 if (m_insideDcCreator) {
236 KoXmlWriter *commentsWriter = docxContext->m_commentsWriter;
237 commentsWriter->addAttribute("w:author", reader.text().toString());
238 }
239 else if (m_insideDcDate) {
240 //KoXmlWriter *commentsWriter = docxContext->m_commentsWriter;
241 // todo, convert the date and add as attribute
242 }
243 else {
244 // In docx, a text always has to be inside a run (w:r). This is
245 // created when a text:span is encountered in odf but text nodes
246 // can exist also without a text:span surrounding it.
247 KoXmlWriter *writer = docxContext->m_documentWriter;
248 if (m_insideComment) {
249 writer = docxContext->m_commentsWriter;
250 }
251
252 if (m_insideSpanLevel == 0) {
253 startRun(reader, docxContext);
254 }
255
256 writer->startElement("w:t");
257 writer->addTextNode(reader.text().toString());
258 writer->endElement(); // w:t
259
260 if (m_insideSpanLevel == 0) {
261 endRun(docxContext);
262 }
263 }
264 }
265
266
267 // ----------------------------------------------------------------
268 // Private functions
269
270
startRun(const KoXmlStreamReader & reader,OdfReaderDocxContext * docxContext)271 void OdfTextReaderDocxBackend::startRun(const KoXmlStreamReader &reader, OdfReaderDocxContext *docxContext)
272 {
273 KoXmlWriter *writer = docxContext->m_documentWriter;
274 if (m_insideComment) {
275 writer = docxContext->m_commentsWriter;
276 }
277
278 if (m_writeComment && !m_insideComment) {
279 writer->startElement("w:commentRangeStart");
280 writer->addAttribute("w:id", m_commentIndex);
281 writer->endElement(); // w:commentRangeStart
282 }
283 writer->startElement("w:r");
284 writer->startElement("w:rPr");
285 KoXmlStreamAttributes attributes = reader.attributes();
286 KoOdfStyleProperties properties;
287 if (!m_currentParagraphParent.isEmpty()) {
288 DocxStyleHelper::inheritTextStyles(&properties, m_currentParagraphParent, docxContext->styleManager());
289 }
290 if (m_currentParagraphTextProperties != 0) {
291 properties.copyPropertiesFrom(*m_currentParagraphTextProperties);
292 }
293
294 QString textStyle = attributes.value("text:style-name").toString();
295 if (!textStyle.isEmpty()) {
296 KoOdfStyle *style = docxContext->styleManager()->style(textStyle, "text");
297 KoOdfStyleProperties *textProperties = style->properties("style:text-properties");
298 if (textProperties != 0) {
299 properties.copyPropertiesFrom(*textProperties);
300 }
301 QString parent = style->parent();
302 if (!parent.isEmpty()) {
303 writer->startElement("w:rStyle");
304 writer->addAttribute("w:val", parent);
305 writer->endElement(); // w:rStyle
306 }
307 }
308 DocxStyleHelper::handleTextStyles(&properties, writer);
309
310 writer->endElement(); // w:rPr
311 }
312
endRun(OdfReaderDocxContext * docxContext)313 void OdfTextReaderDocxBackend::endRun(OdfReaderDocxContext *docxContext)
314 {
315 // FIXME: More here?
316 KoXmlWriter *writer = docxContext->m_documentWriter;
317 if (m_insideComment) {
318 writer = docxContext->m_commentsWriter;
319 }
320 writer->endElement(); // w:r
321
322 if (m_writeComment && !m_insideComment) {
323 writer->startElement("w:commentRangeEnd");
324 writer->addAttribute("w:id", m_commentIndex);
325 writer->endElement(); // w:commentRangeEnd
326 writer->startElement("w:r");
327 writer->startElement("w:commentReference");
328 writer->addAttribute("w:id", m_commentIndex);
329 writer->endElement(); // w:commentReference
330 writer->endElement(); // w:r
331 ++m_commentIndex;
332 m_writeComment = false;
333 }
334 }
335