1 /*
2 * This file is part of Office 2007 Filters for Calligra
3 *
4 * Copyright (C) 2010 Sebastian Sauer <sebsauer@kdab.com>
5 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 *
7 * Contact: Suresh Chande suresh.chande@nokia.com
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * version 2.1 as published by the Free Software Foundation.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 #include "XlsxXmlDrawingReader.h"
26 #include "XlsxXmlWorksheetReader.h"
27 #include "XlsxXmlWorksheetReader_p.h"
28 #include "XlsxXmlChartReader.h"
29 #include "XlsxImport.h"
30 #include "Charting.h"
31 #include "XlsxChartOdfWriter.h"
32
33 #include <MsooXmlSchemas.h>
34 #include <MsooXmlUtils.h>
35 #include <MsooXmlRelationships.h>
36 #include <MsooXmlUnits.h>
37 #include <MsooXmlDiagramReader.h>
38
39 #include <KoXmlWriter.h>
40 #include <KoGenStyles.h>
41 #include <KoOdfGraphicStyles.h>
42 #include <QFontMetricsF>
43
44 #include <sheets/Util.h>
45
46 #define MSOOXML_CURRENT_NS "xdr"
47 #define MSOOXML_CURRENT_CLASS XlsxXmlDrawingReader
48 #define BIND_READ_CLASS MSOOXML_CURRENT_CLASS
49
50 #include <MsooXmlReader_p.h>
51 #include <MsooXmlContentTypes.h>
52
53 // calculates the column width in pixels
columnWidth2(unsigned long col,unsigned long dx=0,qreal defaultColumnWidth=8.43)54 int columnWidth2(unsigned long col, unsigned long dx = 0, qreal defaultColumnWidth = 8.43) {
55 QFont font("Arial", 10);
56 QFontMetricsF fm(font);
57 const qreal characterWidth = fm.width("h");
58 defaultColumnWidth *= characterWidth;
59 return (defaultColumnWidth * col) + (dx / 1024.0 * defaultColumnWidth);
60 }
61
62 // calculates the row height in pixels
rowHeight2(unsigned long row,unsigned long dy=0,qreal defaultRowHeight=12.75)63 int rowHeight2(unsigned long row, unsigned long dy = 0, qreal defaultRowHeight = 12.75)
64 {
65 return defaultRowHeight * row + dy;
66 }
67
68 // Returns A for 0, B for 1, C for 2, etc.
columnName2(uint column)69 QString columnName2(uint column)
70 {
71 QString s;
72 unsigned digits = 1;
73 unsigned offset = 0;
74 for (unsigned limit = 26; column >= limit + offset; limit *= 26, ++digits)
75 offset += limit;
76 for (unsigned col = column - offset; digits; --digits, col /= 26)
77 s.prepend(QChar('A' + (col % 26)));
78 return s;
79 }
80
setShape(XlsxShape * shape)81 KoXmlWriter* XlsxDrawingObject::setShape(XlsxShape* shape)
82 {
83 m_type = Shape;
84 m_shape = shape;
85
86 delete m_shapeBody;
87 m_shapeBody = new KoXmlWriter(new QBuffer);
88 return m_shapeBody;
89 }
90
save(KoXmlWriter * xmlWriter)91 void XlsxDrawingObject::save(KoXmlWriter* xmlWriter)
92 {
93 switch(m_type) {
94 case Unknown: {
95 // nothing to do for us
96 } break;
97 case Chart: {
98 m_chart->m_chartWriter->saveIndex(xmlWriter);
99 } break;
100 case Diagram: {
101 xmlWriter->startElement("draw:g");
102 xmlWriter->addAttribute("draw:name", "SmartArt Shapes Group");
103 xmlWriter->addAttribute("draw:z-index", "0");
104 //xmlWriter->addAttribute("table:end-cell-address", fromCellAddress());
105 //xmlWriter->addAttribute("table:end-x", rect.width());
106 //xmlWriter->addAttribute("table:end-y", rect.height());
107 m_diagram->saveIndex(xmlWriter, positionRect());
108 xmlWriter->endElement(); // draw:g
109 } break;
110 case Picture: {
111 m_picture->saveXml(xmlWriter);
112 delete m_picture;
113 m_type = Unknown;
114 } break;
115 case Shape: {
116 Q_ASSERT(m_shapeBody);
117 QByteArray data = static_cast<QBuffer*>(m_shapeBody->device())->buffer().constData();
118 xmlWriter->addCompleteElement(data);
119 delete m_shapeBody;
120 m_shapeBody = 0;
121 } break;
122 }
123 }
124
pictureWriter()125 KoXmlWriter* XlsxDrawingObject::pictureWriter()
126 {
127 if (m_type == Unknown) {
128 setPicture(new XlsxXmlEmbeddedPicture());
129 }
130 Q_ASSERT(m_picture);
131 return m_picture->pictureWriter();
132 }
133
134
135
positionRect() const136 QRect XlsxDrawingObject::positionRect() const
137 {
138 QRect rect(QPoint(0,0),QSize(0,0));
139 if(m_positions.contains(FromAnchor)) {
140 qreal defaultColumnWidth = 8.43;
141 qreal defaultRowHeight = 12.75;
142 Position f1 = m_positions[FromAnchor];
143 rect.setX( EMU_TO_POINT(f1.m_colOff) );
144 rect.setY( EMU_TO_POINT(f1.m_rowOff) );
145 if(m_positions.contains(ToAnchor)) {
146 Position f2 = m_positions[ToAnchor];
147 if(f2.m_col > 0 && f2.m_row > 0) {
148 rect.setWidth( columnWidth2( f2.m_col - f1.m_col - 1, EMU_TO_POINT(f2.m_colOff), defaultColumnWidth) );
149 rect.setHeight( rowHeight2( f2.m_row - f1.m_row - 1, EMU_TO_POINT(f2.m_rowOff), defaultRowHeight) );
150 }
151 }
152 Q_ASSERT(rect.x() >= 0);
153 Q_ASSERT(rect.y() >= 0);
154 Q_ASSERT(rect.width() >= 0);
155 Q_ASSERT(rect.height() >= 0);
156 }
157 return rect;
158 }
159
cellAddress(const QString & sheetname,int row,int column) const160 QString XlsxDrawingObject::cellAddress(const QString &sheetname, int row, int column) const
161 {
162 QString result;
163 if(!sheetname.isEmpty())
164 result += sheetname + '.';
165 result += columnName2(column) + QString::number(row + 1);
166 return result;
167 }
168
fromCellAddress() const169 QString XlsxDrawingObject::fromCellAddress() const
170 {
171 if(!m_positions.contains(FromAnchor)) return QString();
172 Position f = m_positions[FromAnchor];
173 return cellAddress(m_sheet->m_name, f.m_row, f.m_col);
174 }
175
toCellAddress() const176 QString XlsxDrawingObject::toCellAddress() const
177 {
178 if(!m_positions.contains(ToAnchor)) return QString();
179 Position f = m_positions[ToAnchor];
180 return cellAddress(m_sheet->m_name, f.m_row, f.m_col);
181 }
182
isAnchoredToCell() const183 bool XlsxDrawingObject::isAnchoredToCell() const
184 {
185 return (m_positions.contains(FromAnchor));
186 }
187
188
XlsxXmlDrawingReaderContext(XlsxXmlWorksheetReaderContext * _worksheetReaderContext,Sheet * _sheet,const QString & _path,const QString & _file)189 XlsxXmlDrawingReaderContext::XlsxXmlDrawingReaderContext(XlsxXmlWorksheetReaderContext* _worksheetReaderContext, Sheet* _sheet, const QString& _path, const QString& _file)
190 : MSOOXML::MsooXmlReaderContext(_worksheetReaderContext->relationships)
191 , import(_worksheetReaderContext->import)
192 , path(_path)
193 , file(_file)
194 , themes((_worksheetReaderContext->themes))
195 , worksheetReaderContext(_worksheetReaderContext)
196 , sheet(_sheet)
197 , m_groupDepthCounter(0)
198 {
199 }
200
~XlsxXmlDrawingReaderContext()201 XlsxXmlDrawingReaderContext::~XlsxXmlDrawingReaderContext()
202 {
203 }
204
XlsxXmlDrawingReader(KoOdfWriters * writers)205 XlsxXmlDrawingReader::XlsxXmlDrawingReader(KoOdfWriters *writers)
206 : MSOOXML::MsooXmlCommonReader(writers)
207 , m_context(0)
208 , m_currentDrawingObject(0)
209 , m_anchorType(XlsxDrawingObject::NoAnchor)
210 , m_chartNumber(0)
211 {
212 initDrawingML();
213 }
214
~XlsxXmlDrawingReader()215 XlsxXmlDrawingReader::~XlsxXmlDrawingReader()
216 {
217 Q_ASSERT(!m_currentDrawingObject);
218 }
219
read(MSOOXML::MsooXmlReaderContext * context)220 KoFilter::ConversionStatus XlsxXmlDrawingReader::read(MSOOXML::MsooXmlReaderContext* context)
221 {
222 m_context = dynamic_cast<XlsxXmlDrawingReaderContext*>(context);
223 Q_ASSERT(m_context);
224
225 readNext();
226 if (!isStartDocument()) {
227 return KoFilter::WrongFormat;
228 }
229
230 readNext();
231 if (!expectEl("xdr:wsDr")) {
232 return KoFilter::WrongFormat;
233 }
234
235 while (!atEnd()) {
236 readNext();
237 if (isEndElement() && name() == "wsDr") {
238 break;
239 }
240 if (isStartElement()) {
241 TRY_READ_IF(oneCellAnchor)
242 ELSE_TRY_READ_IF(twoCellAnchor)
243 ELSE_TRY_READ_IF(absoluteAnchor)
244 SKIP_UNKNOWN
245 }
246 }
247 #if 0
248 m_context->saveCurrentCellData();
249 #endif
250 m_context = 0;
251 return KoFilter::OK;
252 }
253
254 #undef CURRENT_EL
255 #define CURRENT_EL twoCellAnchor
read_twoCellAnchor()256 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_twoCellAnchor()
257 {
258 READ_PROLOGUE
259
260 return read_anchor("twoCellAnchor");
261
262 READ_EPILOGUE
263 }
264
265 #undef CURRENT_EL
266 #define CURRENT_EL oneCellAnchor
read_oneCellAnchor()267 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_oneCellAnchor()
268 {
269 READ_PROLOGUE
270
271 return read_anchor("oneCellAnchor");
272
273 READ_EPILOGUE
274 }
275
276 #undef CURRENT_EL
277 #define CURRENT_EL absoluteAnchor
read_absoluteAnchor()278 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_absoluteAnchor()
279 {
280 READ_PROLOGUE
281
282 return read_anchor("absoluteAnchor");
283
284 READ_EPILOGUE
285 }
286
read_anchor(const QString & reference)287 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_anchor(const QString& reference)
288 {
289 class DrawingObjectGuard { // like QScopedPointer but sets the pointer to NULL afterwards
290 public:
291 DrawingObjectGuard(XlsxDrawingObject** obj) : m_obj(obj) {}
292 ~DrawingObjectGuard() { delete *m_obj; *m_obj = 0; }
293 private:
294 XlsxDrawingObject** m_obj;
295 };
296
297 Q_ASSERT(!m_currentDrawingObject);
298 m_currentDrawingObject = new XlsxDrawingObject(m_context->sheet);
299 DrawingObjectGuard _guard(&m_currentDrawingObject);
300
301 while (!atEnd()) {
302 readNext();
303 if (isEndElement() && name() == reference) {
304 break;
305 }
306 if (isStartElement()) {
307 // twoCellAnchor does define the 'from' and 'to' elements which do define the anchor-points
308 TRY_READ_IF(from)
309 ELSE_TRY_READ_IF(to)
310 else if (qualifiedName() == QLatin1String("xdr:ext")) {
311 // read_ext expects the qualifed name a:ext
312 const QXmlStreamAttributes attrs(attributes());
313
314 READ_ATTR_WITHOUT_NS(cx)
315 STRING_TO_INT(cx, m_svgWidth, "ext@cx")
316 READ_ATTR_WITHOUT_NS(cy)
317 STRING_TO_INT(cy, m_svgHeight, "ext@cy")
318 readNext();
319 }
320 ELSE_TRY_READ_IF(ext)
321 // a shape
322 ELSE_TRY_READ_IF(sp)
323 // the reference to a picture
324 ELSE_TRY_READ_IF(pic)
325 // a graphic-frame
326 ELSE_TRY_READ_IF(graphicFrame)
327 ELSE_TRY_READ_IF(cxnSp)
328 ELSE_TRY_READ_IF(grpSp)
329 SKIP_UNKNOWN
330 }
331 }
332
333 if (m_currentDrawingObject->m_type != XlsxDrawingObject::Unknown) {
334 if (m_currentDrawingObject->isAnchoredToCell()) {
335 XlsxDrawingObject::Position pos = m_currentDrawingObject->m_positions[XlsxDrawingObject::FromAnchor];
336 Cell* cell = m_context->sheet->cell(pos.m_col, pos.m_row, true);
337 cell->appendDrawing(m_currentDrawingObject);
338 m_currentDrawingObject = 0;
339 }
340 }
341 return KoFilter::OK;
342 }
343
344 #undef CURRENT_EL
345 #define CURRENT_EL from
read_from()346 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_from()
347 {
348 READ_PROLOGUE
349 #if 0
350 m_context->saveCurrentCellData();
351 #endif
352 m_anchorType = XlsxDrawingObject::FromAnchor;
353 while (!atEnd()) {
354 readNext();
355 BREAK_IF_END_OF(CURRENT_EL)
356 if (isStartElement()) {
357 TRY_READ_IF(col)
358 ELSE_TRY_READ_IF(row)
359 ELSE_TRY_READ_IF(colOff)
360 ELSE_TRY_READ_IF(rowOff)
361 }
362 }
363 m_anchorType = XlsxDrawingObject::NoAnchor;
364 READ_EPILOGUE
365 }
366
367 #undef CURRENT_EL
368 #define CURRENT_EL to
read_to()369 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_to()
370 {
371 READ_PROLOGUE
372 m_anchorType = XlsxDrawingObject::ToAnchor;
373 while (!atEnd()) {
374 readNext();
375 BREAK_IF_END_OF(CURRENT_EL)
376 if (isStartElement()) {
377 TRY_READ_IF(col)
378 ELSE_TRY_READ_IF(row)
379 ELSE_TRY_READ_IF(colOff)
380 ELSE_TRY_READ_IF(rowOff)
381 }
382 }
383 m_anchorType = XlsxDrawingObject::NoAnchor;
384 READ_EPILOGUE
385 }
386
387 #undef CURRENT_EL
388 #define CURRENT_EL col
read_col()389 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_col()
390 {
391 m_currentDrawingObject->m_positions[m_anchorType].m_col = readElementText().toInt(); // default value is zero
392 return KoFilter::OK;
393 }
394
395 #undef CURRENT_EL
396 #define CURRENT_EL row
read_row()397 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_row()
398 {
399 m_currentDrawingObject->m_positions[m_anchorType].m_row = readElementText().toInt(); // default value is zero
400 return KoFilter::OK;
401 }
402
403 #undef CURRENT_EL
404 #define CURRENT_EL colOff
read_colOff()405 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_colOff()
406 {
407 m_currentDrawingObject->m_positions[m_anchorType].m_colOff = readElementText().toInt(); // default value is zero
408 return KoFilter::OK;
409 }
410
411 #undef CURRENT_EL
412 #define CURRENT_EL rowOff
read_rowOff()413 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_rowOff()
414 {
415 m_currentDrawingObject->m_positions[m_anchorType].m_rowOff = readElementText().toInt(); // default value is zero
416 return KoFilter::OK;
417 }
418
419 #undef CURRENT_EL
420 #define CURRENT_EL graphicFrame
421 //! graphicFrame (Graphic Frame)
422 /*! ECMA-376, 20.5.2.16, p. 3524 (SpreadSheetML)
423 This element describes a single graphical object frame for a spreadsheet
424 which contains a graphical object.
425
426 Parent Elements:
427 - [done] absoluteAnchor (§20.5.2.1)
428 - [done] grpSp (§20.5.2.17)
429 - [done] oneCellAnchor (§20.5.2.24)
430 - [done] twoCellAnchor (§20.5.2.33)
431
432 Child Elements:
433 - [done] graphic (Graphic Object) (§20.1.2.2.16)
434 - nvGraphicFramePr (Non-Visual Properties for a Graphic Frame) (§20.5.2.20)
435 - xfrm (2D Transform for Graphic Frame) (§20.5.2.36)
436 */
read_graphicFrame()437 KoFilter::ConversionStatus XlsxXmlDrawingReader::read_graphicFrame()
438 {
439 READ_PROLOGUE
440
441 //TODO: Create a graphic style for the frame.
442
443 MSOOXML::Utils::XmlWriteBuffer buffer;
444 body = buffer.setWriter(body);
445
446 while (!atEnd()) {
447 readNext();
448 BREAK_IF_END_OF(CURRENT_EL)
449 if (isStartElement()) {
450 TRY_READ_IF_NS(a, graphic)
451 SKIP_UNKNOWN
452 }
453 }
454
455 body = buffer.originalWriter();
456
457 if (m_context->graphicObjectIsGroup) {
458 body->startElement("draw:g");
459 } else {
460 body->startElement("draw:frame");
461 }
462
463 buffer.releaseWriter();
464 body->endElement(); //draw:g/draw:frame
465
466 READ_EPILOGUE
467 }
468
XlsxXmlEmbeddedPicture()469 XlsxXmlEmbeddedPicture::XlsxXmlEmbeddedPicture():m_pictureWriter(0)
470 {
471 m_pictureBuffer.open(QIODevice::ReadWrite);
472 }
473
~XlsxXmlEmbeddedPicture()474 XlsxXmlEmbeddedPicture::~XlsxXmlEmbeddedPicture()
475 {
476 delete m_pictureWriter;
477 }
478
479
pictureWriter()480 KoXmlWriter* XlsxXmlEmbeddedPicture::pictureWriter()
481 {
482 if (!m_pictureWriter) {
483 m_pictureWriter = new KoXmlWriter(&m_pictureBuffer);
484 }
485 return m_pictureWriter;
486 }
487
488
saveXml(KoXmlWriter * xmlWriter)489 bool XlsxXmlEmbeddedPicture::saveXml(KoXmlWriter *xmlWriter) // save all needed attributes to .ods
490 {
491 Q_ASSERT(m_pictureWriter);
492 if (!m_pictureWriter || !m_pictureWriter->device()->size()){
493 return false;
494 }
495
496 xmlWriter->addCompleteElement(m_pictureWriter->device());
497 return true;
498 }
499
500
501 #undef MSOOXML_CURRENT_NS
502 #define MSOOXML_CURRENT_NS "a" // required for the next header file
503
504 // in PPTX we do not have pPr, so p@text:style-name should be added earlier
505 //#define SETUP_PARA_STYLE_IN_READ_P
506 #include <MsooXmlCommonReaderImpl.h> // adds a:p, a:pPr, a:t, a:r, etc.
507
508 #define DRAWINGML_NS "a"
509 #define DRAWINGML_PIC_NS "xdr" // DrawingML/Picture
510 #define DRAWINGML_TXBODY_NS "xdr" // DrawingML/Picture
511 #define XLSXXMLDRAWINGREADER_CPP
512
513 #include <MsooXmlCommonReaderDrawingMLImpl.h> // adds p:pic, etc.
514 //#include <MsooXmlDrawingReaderTableImpl.h> // adds a:tbl
515