1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QTextDocument>
31#include <QTextCursor>
32#include <QTextBlock>
33#include <QTextList>
34#include <QTextTable>
35#include <QBuffer>
36#include <QDebug>
37
38#include <private/qtextodfwriter_p.h>
39
40class tst_QTextOdfWriter : public QObject
41{
42 Q_OBJECT
43public slots:
44 void init();
45 void cleanup();
46
47private slots:
48 void testWriteParagraph_data();
49 void testWriteParagraph();
50 void testWriteStyle1_data();
51 void testWriteStyle1();
52 void testWriteStyle2();
53 void testWriteList();
54 void testWriteList2();
55 void createArchive();
56 void testWriteAll();
57 void testWriteSection();
58 void testWriteTable();
59 void testWriteFrameFormat();
60
61private:
62 /// closes the document and returns the part of the XML stream that the test wrote
63 QString getContentFromXml();
64
65private:
66 QTextDocument *document;
67 QXmlStreamWriter *xmlWriter;
68 QTextOdfWriter *odfWriter;
69 QBuffer *buffer;
70};
71
72void tst_QTextOdfWriter::init()
73{
74 document = new QTextDocument();
75 odfWriter = new QTextOdfWriter(*document, 0);
76
77 buffer = new QBuffer();
78 buffer->open(openMode: QIODevice::WriteOnly);
79 xmlWriter = new QXmlStreamWriter(buffer);
80 xmlWriter->writeNamespace(namespaceUri: odfWriter->officeNS, prefix: "office");
81 xmlWriter->writeNamespace(namespaceUri: odfWriter->textNS, prefix: "text");
82 xmlWriter->writeNamespace(namespaceUri: odfWriter->styleNS, prefix: "style");
83 xmlWriter->writeNamespace(namespaceUri: odfWriter->foNS, prefix: "fo");
84 xmlWriter->writeNamespace(namespaceUri: odfWriter->tableNS, prefix: "table");
85 xmlWriter->writeStartDocument();
86 xmlWriter->writeStartElement(qualifiedName: "dummy");
87}
88
89void tst_QTextOdfWriter::cleanup()
90{
91 delete document;
92 delete odfWriter;
93 delete xmlWriter;
94 delete buffer;
95}
96
97QString tst_QTextOdfWriter::getContentFromXml()
98{
99 xmlWriter->writeEndDocument();
100 buffer->close();
101 QString stringContent = QString::fromUtf8(str: buffer->data());
102 QString ret;
103 int index = stringContent.indexOf(s: "<dummy");
104 if (index > 0) {
105 index = stringContent.indexOf(c: '>', from: index);
106 if (index > 0)
107 ret = stringContent.mid(position: index+1, n: stringContent.length() - index - 10);
108 }
109 return ret;
110}
111
112void tst_QTextOdfWriter::testWriteParagraph_data()
113{
114 QTest::addColumn<QString>(name: "input");
115 QTest::addColumn<QString>(name: "xml");
116
117 QTest::newRow(dataTag: "empty") << "" <<
118 "<text:p text:style-name=\"p1\"/>";
119 QTest::newRow(dataTag: "spaces") << "foobar word" <<
120 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foobar <text:s text:c=\"2\"/>word</text:span></text:p>";
121 QTest::newRow(dataTag: "starting spaces") << " starting spaces" <<
122 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\"><text:s text:c=\"2\"/>starting spaces</text:span></text:p>";
123 QTest::newRow(dataTag: "trailing spaces") << "trailing spaces " <<
124 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">trailing spaces <text:s/></text:span></text:p>";
125 QTest::newRow(dataTag: "tab") << "word\ttab x" <<
126 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">word<text:tab/>tab x</text:span></text:p>";
127 QTest::newRow(dataTag: "tab2") << "word\t\ttab\tx" <<
128 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">word<text:tab/><text:tab/>tab<text:tab/>x</text:span></text:p>";
129 QTest::newRow(dataTag: "misc") << "foobar word\ttab x" <<
130 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foobar <text:s text:c=\"2\"/>word<text:tab/>tab x</text:span></text:p>";
131 QTest::newRow(dataTag: "misc2") << "\t \tFoo" <<
132 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\"><text:tab/> <text:s text:c=\"4\"/><text:tab/>Foo</text:span></text:p>";
133 QTest::newRow(dataTag: "linefeed") << (QStringLiteral("line1") + QChar(0x2028) + QStringLiteral("line2")) <<
134 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">line1<text:tab/><text:line-break/>line2</text:span></text:p>";
135 QTest::newRow(dataTag: "spaces") << "The quick brown fox jumped over the lazy dog" <<
136 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">The quick brown fox jumped over the lazy dog</text:span></text:p>";
137}
138
139void tst_QTextOdfWriter::testWriteParagraph()
140{
141 QFETCH(QString, input);
142 QFETCH(QString, xml);
143
144 QTextCursor cursor(document);
145 cursor.insertText(text: input);
146
147 odfWriter->writeBlock(writer&: *xmlWriter, block: document->begin());
148 QCOMPARE( getContentFromXml(), xml);
149}
150
151void tst_QTextOdfWriter::testWriteStyle1_data()
152{
153 QTest::addColumn<QString>(name: "htmlInput");
154 QTest::addColumn<int>(name: "cursorPosition");
155 QTest::addColumn<QString>(name: "xml");
156
157 QString text1 = "Normal<b>bold</b><i>italic</i><b><i>Bold/Italic</i></b>";
158 QTest::newRow(dataTag: "normal") << text1 << 2 <<
159 "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-family=\"Sans\"/></style:style>";
160 QTest::newRow(dataTag: "bold") << text1 << 10 <<
161 "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-weight=\"bold\" fo:font-family=\"Sans\"/></style:style>";
162 QTest::newRow(dataTag: "italic") << text1 << 14 <<
163 "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-style=\"italic\" fo:font-family=\"Sans\"/></style:style>";
164 QTest::newRow(dataTag: "bold+italic") << text1 << 25 <<
165 "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-style=\"italic\" fo:font-weight=\"bold\" fo:font-family=\"Sans\"/></style:style>";
166 QString colorText = "<span style=\"color: #00FF00; background-color: #FF0000;\"> Color Text </span>";
167 QTest::newRow(dataTag: "green/red") << colorText << 3 <<
168 "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-family=\"Sans\" fo:color=\"#00ff00\" fo:background-color=\"#ff0000\"/></style:style>";
169
170}
171
172void tst_QTextOdfWriter::testWriteStyle1()
173{
174 QFETCH(QString, htmlInput);
175 QFETCH(int, cursorPosition);
176 QFETCH(QString, xml);
177 document->setHtml(htmlInput);
178
179 QTextCursor cursor(document);
180 cursor.setPosition(pos: cursorPosition);
181 odfWriter->writeCharacterFormat(writer&: *xmlWriter, format: cursor.charFormat(), formatIndex: 4);
182 QCOMPARE( getContentFromXml(), xml);
183}
184
185void tst_QTextOdfWriter::testWriteStyle2()
186{
187 QTextBlockFormat bf; // = cursor.blockFormat();
188 QList<QTextOption::Tab> tabs;
189 QTextOption::Tab tab1(40, QTextOption::RightTab);
190 tabs << tab1;
191 QTextOption::Tab tab2(80, QTextOption::DelimiterTab, 'o');
192 tabs << tab2;
193 bf.setTabPositions(tabs);
194
195 odfWriter->writeBlockFormat(writer&: *xmlWriter, format: bf, formatIndex: 1);
196 QString xml = QString::fromLatin1(
197 str: "<style:style style:name=\"p1\" style:family=\"paragraph\">"
198 "<style:paragraph-properties>"
199 "<style:tab-stops>"
200 "<style:tab-stop style:position=\"30pt\" style:type=\"right\"/>"
201 "<style:tab-stop style:position=\"60pt\" style:type=\"char\" style:char=\"o\"/>"
202 "</style:tab-stops>"
203 "</style:paragraph-properties>"
204 "</style:style>");
205 QCOMPARE(getContentFromXml(), xml);
206}
207
208void tst_QTextOdfWriter::testWriteList()
209{
210 QTextCursor cursor(document);
211 QTextList *list = cursor.createList(style: QTextListFormat::ListDisc);
212 cursor.insertText(text: "ListItem 1");
213 list->add(block: cursor.block());
214 cursor.insertBlock();
215 cursor.insertText(text: "ListItem 2");
216 list->add(block: cursor.block());
217
218 odfWriter->writeBlock(writer&: *xmlWriter, block: cursor.block());
219 QString xml = QString::fromLatin1(
220 str: "<text:list text:style-name=\"L2\">"
221 "<text:list-item>"
222 //"<text:numbered-paragraph text:style-name=\"L2\" text:level=\"1\">"
223 //"<text:number>")+ QChar(0x25cf) + QString::fromLatin1("</text:number>" // 0x25cf is a bullet
224 "<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">ListItem 2</text:span></text:p>"
225 "</text:list-item>"
226 "</text:list>");
227
228 QCOMPARE(getContentFromXml(), xml);
229}
230
231void tst_QTextOdfWriter::testWriteList2()
232{
233 QTextCursor cursor(document);
234 QTextList *list = cursor.createList(style: QTextListFormat::ListDisc);
235 cursor.insertText(text: "Cars");
236 list->add(block: cursor.block());
237 cursor.insertBlock();
238 QTextListFormat level2;
239 level2.setStyle(QTextListFormat::ListSquare);
240 level2.setIndent(2);
241 QTextList *list2 = cursor.createList(format: level2);
242 cursor.insertText(text: "Model T");
243 list2->add(block: cursor.block());
244 cursor.insertBlock();
245 cursor.insertText(text: "Kitt");
246 list2->add(block: cursor.block());
247 cursor.insertBlock();
248 cursor.insertText(text: "Animals");
249 list->add(block: cursor.block());
250
251 cursor.insertBlock(format: QTextBlockFormat(), charFormat: QTextCharFormat()); // start a new completely unrelated list.
252 QTextList *list3 = cursor.createList(style: QTextListFormat::ListDecimal);
253 cursor.insertText(text: "Foo");
254 list3->add(block: cursor.block());
255
256 // and another block thats NOT in a list.
257 cursor.insertBlock(format: QTextBlockFormat(), charFormat: QTextCharFormat());
258 cursor.insertText(text: "Bar");
259
260 odfWriter->writeFrame(writer&: *xmlWriter, frame: document->rootFrame());
261 QString xml = QString::fromLatin1(
262 str: "<text:list text:style-name=\"L2\">"
263 "<text:list-item>"
264 //"<text:numbered-paragraph text:style-name=\"L2\" text:level=\"1\">"
265 //"<text:number>")+ QChar(0x25cf) + QString::fromLatin1("</text:number>" // 0x25cf is a bullet
266 "<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">Cars</text:span></text:p>"
267 "</text:list-item>"
268 "<text:list-item>"
269 "<text:list text:style-name=\"L4\">"
270 "<text:list-item>"
271 "<text:p text:style-name=\"p5\"><text:span text:style-name=\"c0\">Model T</text:span></text:p>"
272 "</text:list-item>"
273 "<text:list-item>"
274 "<text:p text:style-name=\"p5\"><text:span text:style-name=\"c0\">Kitt</text:span></text:p>"
275 "</text:list-item>"
276 "</text:list>"
277 "</text:list-item>"
278 "<text:list-item>"
279 "<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">Animals</text:span></text:p>"
280 "</text:list-item>"
281 "</text:list>"
282 "<text:list text:style-name=\"L6\">"
283 "<text:list-item>"
284 "<text:p text:style-name=\"p7\"><text:span text:style-name=\"c0\">Foo</text:span></text:p>"
285 "</text:list-item>"
286 "</text:list>"
287 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">Bar</text:span></text:p>");
288
289 // QString x = getContentFromXml();
290 // for (int i=0; i < x.length(); i+=150) qDebug() << x.mid(i, 150);
291 QCOMPARE(getContentFromXml(), xml);
292}
293
294
295void tst_QTextOdfWriter::createArchive()
296{
297 document->setPlainText("a"); // simple doc is enough ;)
298 QTextOdfWriter writer(*document, buffer);
299 QCOMPARE(writer.createArchive(), true); // default
300 writer.writeAll();
301/*
302QFile file("createArchive-odt");
303file.open(QIODevice::WriteOnly);
304file.write(buffer->data());
305file.close();
306*/
307 QVERIFY(buffer->data().length() > 80);
308 QCOMPARE(buffer->data()[0], 'P'); // its a zip :)
309 QCOMPARE(buffer->data()[1], 'K');
310 QString mimetype(buffer->data().mid(index: 38, len: 39));
311 QCOMPARE(mimetype, QString::fromLatin1("application/vnd.oasis.opendocument.text"));
312}
313
314void tst_QTextOdfWriter::testWriteAll()
315{
316 document->setPlainText("a"); // simple doc is enough ;)
317 QTextOdfWriter writer(*document, buffer);
318 QCOMPARE(writer.createArchive(), true);
319 writer.setCreateArchive(false);
320 writer.writeAll();
321 QString result = QString(buffer->data());
322 // details we check elsewhere, all we have to do is check availability.
323 QVERIFY(result.indexOf("office:automatic-styles") >= 0);
324 QVERIFY(result.indexOf("<style:style style:name=\"p1\"") >= 0);
325 QVERIFY(result.indexOf("<style:style style:name=\"c0\"") >= 0);
326 QVERIFY(result.indexOf("office:body") >= 0);
327 QVERIFY(result.indexOf("office:text") >= 0);
328 QVERIFY(result.indexOf("style:style") >= 0);
329}
330
331void tst_QTextOdfWriter::testWriteSection()
332{
333 QTextCursor cursor(document);
334 cursor.insertText(text: "foo\nBar");
335 QTextFrameFormat ff;
336 cursor.insertFrame(format: ff);
337 cursor.insertText(text: "baz");
338
339 odfWriter->writeFrame(writer&: *xmlWriter, frame: document->rootFrame());
340 QString xml = QString::fromLatin1(
341 str: "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foo</text:span></text:p>"
342 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">Bar</text:span></text:p>"
343 "<text:section>"
344 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">baz</text:span></text:p>"
345 "</text:section>"
346 "<text:p text:style-name=\"p1\"/>");
347
348 QCOMPARE(getContentFromXml(), xml);
349}
350
351void tst_QTextOdfWriter::testWriteTable()
352{
353 // create table with merged cells
354 QTextCursor cursor(document);
355 QTextTable * table = cursor.insertTable(rows: 3, cols: 3);
356 table->mergeCells(row: 1, col: 0, numRows: 2, numCols: 2);
357 table->mergeCells(row: 0, col: 1, numRows: 1, numCols: 2);
358 cursor = table->cellAt(row: 0, col: 0).firstCursorPosition();
359 cursor.insertText(text: "a");
360 cursor.movePosition(op: QTextCursor::NextCell);
361 cursor.insertText(text: "b");
362 cursor.movePosition(op: QTextCursor::NextCell);
363 cursor.insertText(text: "c");
364 cursor.movePosition(op: QTextCursor::NextCell);
365 cursor.insertText(text: "d");
366 cursor.movePosition(op: QTextCursor::NextCell);
367 cursor.insertText(text: "e");
368 /*
369 +-+---+
370 |a|b |
371 +-+-+-+
372 |c |d|
373 + +-+
374 | |e|
375 +-+-+-+
376 */
377
378 odfWriter->writeFrame(writer&: *xmlWriter, frame: document->rootFrame());
379 QString xml = QString::fromLatin1(
380 str: "<text:p text:style-name=\"p1\"/>"
381 "<table:table table:style-name=\"Table2\">"
382 "<table:table-column table:number-columns-repeated=\"3\"/>"
383 "<table:table-row>"
384 "<table:table-cell table:style-name=\"T3\">"
385 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">a</text:span></text:p>"
386 "</table:table-cell>"
387 "<table:table-cell table:number-columns-spanned=\"2\" table:style-name=\"T6\">"
388 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c7\">b</text:span></text:p>"
389 "</table:table-cell>"
390 "</table:table-row>"
391 "<table:table-row>"
392 "<table:table-cell table:number-columns-spanned=\"2\" table:number-rows-spanned=\"2\" table:style-name=\"T5\">"
393 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c8\">c</text:span></text:p>"
394 "</table:table-cell>"
395 "<table:table-cell table:style-name=\"T3\">"
396 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">d</text:span></text:p>"
397 "</table:table-cell>"
398 "</table:table-row>"
399 "<table:table-row>"
400 "<table:table-cell table:style-name=\"T3\">"
401 "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">e</text:span></text:p>"
402 "</table:table-cell>"
403 "</table:table-row>"
404 "</table:table>"
405 "<text:p text:style-name=\"p1\"/>");
406
407 QCOMPARE(getContentFromXml(), xml);
408}
409
410void tst_QTextOdfWriter::testWriteFrameFormat()
411{
412 QTextFrameFormat tff;
413 tff.setTopMargin(20);
414 tff.setBottomMargin(20);
415 tff.setLeftMargin(20);
416 tff.setRightMargin(20);
417 QTextCursor tc(document);
418 odfWriter->writeFrameFormat(writer&: *xmlWriter, format: tff, formatIndex: 0);
419 // Value of 15pt is based on the pixelToPoint() calculation done in qtextodfwriter.cpp
420 QString xml = QString::fromLatin1(
421 str: "<style:style style:name=\"s0\" style:family=\"section\">"
422 "<style:section-properties fo:margin-top=\"15pt\" fo:margin-bottom=\"15pt\""
423 " fo:margin-left=\"15pt\" fo:margin-right=\"15pt\"/>"
424 "</style:style>");
425 QCOMPARE(getContentFromXml(), xml);
426}
427
428QTEST_MAIN(tst_QTextOdfWriter)
429#include "tst_qtextodfwriter.moc"
430

source code of qtbase/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp