1 /* This file is part of the KDE project
2    Copyright (C) 2010 KO GmbH <jos.van.den.oever@kogmbh.com>
3    Copyright (C) 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
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  * Note that the implementations here are supposed to be defined by DrawingML.
23  * This details the geometry etc. in Appendix D, in the file
24  * presetShapeDefinitions.xml.
25  *
26  * @note Of the {Bent,Curved}Connector[2345] shapes, the MS Office only seems to
27  * support the [23] variants. The remainder have been coded in a manner
28  * consistent with the [23] variants, but have not been tested.
29  */
30 
31 #include "ODrawToOdf.h"
32 #include "drawstyle.h"
33 #include "msodraw.h"
34 #include "generated/leinputstream.h"
35 #include "writeodf/writeodfdraw.h"
36 
37 #include <QDebug>
38 #include <QPainterPath>
39 #include <QTransform>
40 #include <QBuffer>
41 #include <QUrl>
42 
43 #include <cmath>
44 
45 
46 using namespace MSO;
47 using namespace writeodf;
48 
49 qint16
normalizeRotation(qreal rotation)50 ODrawToOdf::normalizeRotation(qreal rotation)
51 {
52     qint16 angle = ((qint16)rotation) % 360;
53     if (angle < 0) {
54         angle = angle + 360;
55     }
56     return angle;
57 }
58 
59 /**
60  * Return the bounding rectangle for this object.
61  **/
62 QRectF
getRect(const OfficeArtSpContainer & o)63 ODrawToOdf::getRect(const OfficeArtSpContainer &o)
64 {
65     if (o.childAnchor) {
66         const OfficeArtChildAnchor& r = *o.childAnchor;
67         return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
68     } else if (o.clientAnchor && client) {
69         return client->getRect(*o.clientAnchor);
70     } else if (o.shapeProp.fHaveAnchor && client) {
71         return client->getReserveRect();
72     } else {
73         return QRectF();
74     }
75 }
76 
77 QRectF
processRect(const quint16 shapeType,const qreal rotation,QRectF & rect)78 ODrawToOdf::processRect(const quint16 shapeType, const qreal rotation, QRectF &rect)
79 {
80     bool transform_anchor = false;
81     qint16 nrotation = normalizeRotation(rotation);
82 
83     //TODO: Add other shapes here!
84 
85     switch (shapeType) {
86     case msosptNotPrimitive:
87         if ( ((nrotation >= 45) && (nrotation < 135)) ||
88              ((nrotation >= 225) && (nrotation < 315)))
89         {
90             transform_anchor = true;
91         }
92         break;
93     default:
94         break;
95     }
96     if (transform_anchor) {
97         QPointF center = rect.center();
98         QTransform transform;
99         transform.rotate(90);
100         rect = transform.mapRect(rect.translated(-center)).translated(center);
101     }
102     return rect;
103 }
104 
processRectangle(const OfficeArtSpContainer & o,Writer & out)105 void ODrawToOdf::processRectangle(const OfficeArtSpContainer& o, Writer& out)
106 {
107     // TODO: Use client->isPlaceholder - might require an update of the
108     // placeholderAllowed function in the PPT filter.  Trying to save as many
109     // shapes into draw:text-box at the moment, because vertical alignment in
110     // draw:custom-shape does not work properly (bug 288047).
111     if (o.clientData && client->processRectangleAsTextBox(*o.clientData)) {
112         processTextBox(o, out);
113     } else {
114         const DrawStyle ds(0, 0, &o);
115         if (ds.pib()) {
116             // see bug https://bugs.kde.org/show_bug.cgi?id=285577
117             processPictureFrame(o, out);
118         } else {
119             draw_custom_shape rect(&out.xml);
120             processStyleAndText(o, out);
121             draw_enhanced_geometry eg(rect.add_draw_enhanced_geometry());
122             eg.set_svg_viewBox("0 0 21600 21600");
123             eg.set_draw_enhanced_path("M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N");
124             eg.set_draw_type("rectangle");
125             setShapeMirroring(o, out);
126         }
127     }
128 }
129 
processTextBox(const OfficeArtSpContainer & o,Writer & out)130 void ODrawToOdf::processTextBox(const OfficeArtSpContainer& o, Writer& out)
131 {
132     draw_frame frame(&out.xml);
133     processStyle(o, out);
134     draw_text_box text(frame.add_draw_text_box());
135     processText(o, out);
136 }
137 
processLine(const OfficeArtSpContainer & o,Writer & out)138 void ODrawToOdf::processLine(const OfficeArtSpContainer& o, Writer& out)
139 {
140     const QRectF rect = getRect(o);
141     qreal x1 = rect.x();
142     qreal y1 = rect.y();
143     qreal x2 = rect.x() + rect.width();
144     qreal y2 = rect.y() + rect.height();
145 
146     // shape mirroring
147     if (o.shapeProp.fFlipV) {
148         qSwap(y1, y2);
149     }
150     if (o.shapeProp.fFlipH) {
151         qSwap(x1, x2);
152     }
153 
154     draw_line line(&out.xml,
155                    client->formatPos(out.hOffset(x1)),
156                    client->formatPos(out.hOffset(x2)),
157                    client->formatPos(out.vOffset(y1)),
158                    client->formatPos(out.vOffset(y2)));
159     addGraphicStyleToDrawElement(out, o);
160     line.set_draw_layer("layout");
161     processText(o, out);
162 }
163 
drawStraightConnector1(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const164 void ODrawToOdf::drawStraightConnector1(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
165 {
166     out.xml.addAttribute("draw:type", "line");
167     shapePath.moveTo(l, t);
168     shapePath.lineTo(r, b);
169 }
170 
drawPathBentConnector2(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const171 void ODrawToOdf::drawPathBentConnector2(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
172 {
173     Q_UNUSED(out);
174     shapePath.moveTo(l, t);
175     shapePath.lineTo(r, t);
176     shapePath.lineTo(r, b);
177 }
178 
drawPathBentConnector3(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const179 void ODrawToOdf::drawPathBentConnector3(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
180 {
181     Q_UNUSED(out);
182     qreal w = qAbs(r - l);
183     qreal adj1 = 50000;
184     qreal x1 = w * adj1 / 100000;
185 
186     shapePath.moveTo(l, t);
187     shapePath.lineTo(l + x1, t);
188     shapePath.lineTo(l + x1, b);
189     shapePath.lineTo(r, b);
190 }
191 
drawPathBentConnector4(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const192 void ODrawToOdf::drawPathBentConnector4(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
193 {
194     Q_UNUSED(out);
195     qreal w = qAbs(r - l);
196     qreal h = qAbs(b - t);
197     qreal adj1 = 50000;
198     qreal adj2 = 50000;
199     qreal x1 = w * adj1 / 100000;
200 //    qreal x2 = x1 + r / 2;
201     qreal y2 = h * adj2 / 100000;
202 //    qreal y1 = t + y2 / 2;
203 
204     shapePath.moveTo(l, t);
205     shapePath.lineTo(l + x1, t);
206     shapePath.lineTo(l + x1, y2);
207     shapePath.lineTo(r, y2);
208     shapePath.lineTo(r, b);
209 }
210 
drawPathBentConnector5(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const211 void ODrawToOdf::drawPathBentConnector5(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
212 {
213     Q_UNUSED(out);
214     qreal w = qAbs(r - l);
215     qreal h = qAbs(b - t);
216     qreal adj1 = 50000;
217     qreal adj2 = 50000;
218     qreal adj3 = 50000;
219     qreal x1 = w * adj1 / 100000;
220     qreal x3 = w * adj3 / 100000;
221 //    qreal x2 = x1 + x3 / 2;
222     qreal y2 = h * adj2 / 100000;
223 //    qreal y1 = t + y2 / 2;
224 //    qreal y3 = b + y2 / 2;
225 
226     shapePath.moveTo(l, t);
227     shapePath.lineTo(l + x1, t);
228     shapePath.lineTo(l + x1, y2);
229     shapePath.lineTo(l + x3, y2);
230     shapePath.lineTo(l + x3, b);
231     shapePath.lineTo(r, b);
232 }
233 
drawPathCurvedConnector2(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const234 void ODrawToOdf::drawPathCurvedConnector2(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
235 {
236     Q_UNUSED(out);
237     qreal w = qAbs(r - l);
238     qreal h = qAbs(b - t);
239 
240     shapePath.moveTo(l, t);
241     shapePath.cubicTo(l + w / 2, t, r, h / 2, r, b);
242 }
243 
drawPathCurvedConnector3(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const244 void ODrawToOdf::drawPathCurvedConnector3(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
245 {
246     Q_UNUSED(out);
247     qreal w = qAbs(r - l);
248     qreal h = qAbs(b - t);
249     qreal adj1 = 50000;
250     qreal x2 = w * adj1 / 100000;
251     qreal x1 = l + x2 /*/ 2*/;
252 //    qreal x3 = r + x2 / 2;
253 //    qreal y3 = h * 3 / 4;
254 
255     shapePath.moveTo(l, t);
256     shapePath.cubicTo(x1, t, x1, t + h / 2, l + x2, t + h / 2);
257     shapePath.cubicTo(l + x2, t + h / 2, l + x2, b, r, b);
258 }
259 
drawPathCurvedConnector4(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const260 void ODrawToOdf::drawPathCurvedConnector4(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
261 {
262     Q_UNUSED(out);
263     qreal w = qAbs(r - l);
264     qreal h = qAbs(b - t);
265     qreal adj1 = 50000;
266     qreal adj2 = 50000;
267     qreal x2 = w * adj1 / 100000;
268     qreal x1 = l + x2 / 2;
269     qreal x3 = r + x2 / 2;
270     qreal x4 = x2 + x3 / 2;
271     qreal x5 = x3 + r / 2;
272     qreal y4 = h * adj2 / 100000;
273     qreal y1 = t + y4 / 2;
274     qreal y2 = t + y1 / 2;
275     qreal y3 = y1 + y4 / 2;
276     qreal y5 = b + y4 / 2;
277 
278     shapePath.moveTo(l, t);
279     shapePath.cubicTo(x1, t, l + x2, y2, l + x2, y1);
280     shapePath.cubicTo(l + x2, y3, x4, y4, x3, y4);
281     shapePath.cubicTo(x5, y4, r, y5, r, b);
282 }
283 
drawPathCurvedConnector5(qreal l,qreal t,qreal r,qreal b,Writer & out,QPainterPath & shapePath) const284 void ODrawToOdf::drawPathCurvedConnector5(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
285 {
286     Q_UNUSED(out);
287     qreal w = qAbs(r - l);
288     qreal h = qAbs(b - t);
289     qreal adj1 = 50000;
290     qreal adj2 = 50000;
291     qreal adj3 = 50000;
292     qreal x3 = w * adj1 / 100000;
293     qreal x6 = w * adj3 / 100000;
294     qreal x1 = x3 + x6 / 2;
295     qreal x2 = l + x3 / 2;
296     qreal x4 = x3 + x1 / 2;
297     qreal x5 = x6 + x1 / 2;
298     qreal x7 = x6 + r / 2;
299     qreal y4 = h * adj2 / 100000;
300     qreal y1 = t + y4 / 2;
301     qreal y2 = t + y1 / 2;
302     qreal y3 = y1 + y4 / 2;
303     qreal y5 = b + y4 / 2;
304     qreal y6 = y5 + y4 / 2;
305     qreal y7 = y5 + b / 2;
306 
307     shapePath.moveTo(l, t);
308     shapePath.cubicTo(x2, t, l + x3, y2, l + x3, y1);
309     shapePath.cubicTo(x3, y3, x4, y4, x1, y4);
310     shapePath.cubicTo(x5, y4, l + x6, y6, l + x6, y5);
311     shapePath.cubicTo(l + x6, y7, x7, b, r, b);
312 }
313 
314 /**
315  * Common handler for Connectors.
316  */
processConnector(const OfficeArtSpContainer & o,Writer & out,PathArtist drawPath)317 void ODrawToOdf::processConnector(const OfficeArtSpContainer& o, Writer& out, PathArtist drawPath)
318 {
319     const OfficeArtDggContainer * drawingGroup = 0;
320     if (client) {
321         drawingGroup = client->getOfficeArtDggContainer();
322     }
323 
324     const OfficeArtSpContainer* master = 0;
325     const DrawStyle ds(drawingGroup, master, &o);
326     qreal rotation = toQReal( ds.rotation() );
327 
328     const QRectF rect = getRect(o);
329     qreal x1 = rect.x();
330     qreal y1 = rect.y();
331     qreal x2 = rect.x() + rect.width();
332     qreal y2 = rect.y() + rect.height();
333 
334     QRectF shapeRect = rect;
335 
336     qreal sx1 = x1;
337     qreal sy1 = y1;
338     qreal sx2 = x2;
339     qreal sy2 = y2;
340 
341     if (rotation != 0.0) {
342         QTransform m;
343         m.rotate( -rotation );
344         shapeRect = m.mapRect(rect.translated(-rect.center())).translated(rect.center());
345 
346         sx1 = shapeRect.topLeft().x();
347         sy1 = shapeRect.topLeft().y();
348         sx2 = shapeRect.bottomRight().x();
349         sy2 = shapeRect.bottomRight().y();
350     }
351 
352     // Prepare to transform the path according the shape properties like flip
353     // and rotation.
354     QTransform m;
355     m.reset();
356     m.translate( -shapeRect.center().x(), -shapeRect.center().y() );
357 
358     // Mirroring
359     if (o.shapeProp.fFlipH){
360         m.scale(-1,1);
361     }
362 
363     if (o.shapeProp.fFlipV){
364         m.scale(1,-1);
365     }
366 
367     if (rotation != 0) {
368         m.rotate(rotation);
369     }
370 
371     m.translate( shapeRect.center().x(), shapeRect.center().y() );
372 
373     // the viewbox should be set, where is this done for draw:connector?
374     out.xml.startElement("draw:connector");
375     addGraphicStyleToDrawElement(out, o);
376     out.xml.addAttribute("draw:layer", "layout");
377 
378     // Compute path and transform it.
379     QPainterPath shapePath;
380     (this->*drawPath)(sx1, sy1, sx2, sy2, out, shapePath);
381 
382     shapePath = m.map(shapePath);
383 
384     // translate the QPainterPath into svg:d attribute
385     QString path = path2svg(shapePath);
386 
387     out.xml.addAttribute("svg:x1", client->formatPos(out.hOffset(x1)));
388     out.xml.addAttribute("svg:y1", client->formatPos(out.vOffset(y1)));
389     out.xml.addAttribute("svg:x2", client->formatPos(out.hOffset(x2)));
390     out.xml.addAttribute("svg:y2", client->formatPos(out.vOffset(y2)));
391     if (!path.isEmpty()) {
392         out.xml.addAttribute("svg:d", path);
393     }
394 
395     processText(o, out);
396     out.xml.endElement();
397 }
398 
processPictureFrame(const OfficeArtSpContainer & o,Writer & out)399 void ODrawToOdf::processPictureFrame(const OfficeArtSpContainer& o, Writer& out)
400 {
401     DrawStyle ds(0, &o);
402 
403     // A value of 0x00000000 MUST be ignored.  [MS-ODRAW] — v20101219
404     if (!ds.pib()) return;
405 
406     draw_frame frame(&out.xml);
407     processStyle(o, out);
408 
409     //NOTE: OfficeArtClienData might contain additional information
410     //about a shape.
411 
412     QString url;
413     if (client) {
414         url = client->getPicturePath(ds.pib());
415     }
416     // if the image cannot be found, just place an empty frame
417     if (url.isEmpty()) {
418         return;
419     }
420     draw_image image(frame.add_draw_image());
421     image.set_xlink_href(QUrl(url));
422     image.set_xlink_type("simple");
423     image.set_xlink_show("embed");
424     image.set_xlink_actuate("onLoad");
425 }
426 
processNotPrimitive(const MSO::OfficeArtSpContainer & o,Writer & out)427 void ODrawToOdf::processNotPrimitive(const MSO::OfficeArtSpContainer& o, Writer& out)
428 {
429     draw_custom_shape shape(&out.xml);
430     processStyleAndText(o, out);
431     draw_enhanced_geometry eg(shape.add_draw_enhanced_geometry());
432     setEnhancedGeometry(o, out);
433 }
434 
435 
processDrawingObject(const OfficeArtSpContainer & o,Writer & out)436 void ODrawToOdf::processDrawingObject(const OfficeArtSpContainer& o, Writer& out)
437 {
438     if (!client) {
439         qWarning() << "Warning: There's no Client!";
440         return;
441     }
442 
443     quint16 shapeType = o.shapeProp.rh.recInstance;
444     client->m_currentShapeType = o.shapeProp.rh.recInstance;
445 
446     switch (shapeType) {
447     case msosptNotPrimitive:
448         processNotPrimitive(o, out);
449         break;
450     case msosptRectangle:
451         processRectangle(o, out);
452         break;
453     case msosptRoundRectangle:
454         processRoundRectangle(o, out);
455         break;
456     case msosptEllipse:
457         // TODO: Something has to be done here (LukasT).  LukasT:
458         // "Great comment", can you provide more details? :)
459         processEllipse(o, out);
460         break;
461     case msosptDiamond:
462         processDiamond(o, out);
463         break;
464     case msosptIsocelesTriangle:
465         processIsocelesTriangle(o, out);
466         break;
467     case msosptRightTriangle:
468         processRightTriangle(o, out);
469         break;
470     case msosptParallelogram:
471         processParallelogram(o, out);
472         break;
473     case msosptTrapezoid:
474         processTrapezoid(o, out);
475         break;
476     case msosptHexagon:
477         processHexagon(o, out);
478         break;
479     case msosptOctagon:
480         processOctagon(o, out);
481         break;
482     case msosptPlus:
483         processPlus(o, out);
484         break;
485     case msosptStar:
486         processStar(o, out);
487         break;
488     case msosptArrow:
489         processArrow(o, out);
490         break;
491     //
492     // TODO: msosptThickArrow
493     //
494     case msosptHomePlate:
495         processHomePlate(o, out);
496         break;
497     case msosptCube:
498         processCube(o, out);
499         break;
500     //
501     // TODO: msosptBaloon, msosptSeal
502     //
503 
504     // NOTE: OpenOffice treats msosptNotchedCircularArrow as msosptArc.  The
505     // msosptNotchedCircularArrow value SHOULD NOT be used according to the
506     // MS-ODRAW spec.  However it occurs in many Word8 files.
507     case msosptArc:
508         processNotchedCircularArrow(o, out);
509         break;
510     case msosptLine:
511         processLine(o, out);
512         break;
513     case msosptPlaque:
514         processPlaque(o, out);
515         break;
516     case msosptCan:
517         processCan(o, out);
518         break;
519     case msosptDonut:
520         processDonut(o, out);
521         break;
522     //
523     // TODO: msosptTextSimple, msosptTextOctagon, msosptTextHexagon,
524     // msosptTextCurve, msosptTextWave, msosptTextRing, msosptTextOnCurve,
525     // msosptTextOnRing
526     //
527     case msosptStraightConnector1:
528         processConnector(o, out, &ODrawToOdf::drawStraightConnector1);
529         break;
530     case msosptBentConnector2:
531         processConnector(o, out, &ODrawToOdf::drawPathBentConnector2);
532         break;
533     case msosptBentConnector3:
534         processConnector(o, out, &ODrawToOdf::drawPathBentConnector3);
535         break;
536     case msosptBentConnector4:
537         processConnector(o, out, &ODrawToOdf::drawPathBentConnector4);
538         break;
539     case msosptBentConnector5:
540         processConnector(o, out, &ODrawToOdf::drawPathBentConnector5);
541         break;
542     case msosptCurvedConnector2:
543         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector2);
544         break;
545     case msosptCurvedConnector3:
546         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector3);
547         break;
548     case msosptCurvedConnector4:
549         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector4);
550         break;
551     case msosptCurvedConnector5:
552         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector5);
553         break;
554     case msosptCallout1:
555         processCallout1(o, out);
556         break;
557     case msosptCallout2:
558         processCallout2(o, out);
559         break;
560     case msosptCallout3:
561         processCallout3(o, out);
562         break;
563     case msosptAccentCallout1:
564         processAccentCallout1(o, out);
565         break;
566     case msosptAccentCallout2:
567         processAccentCallout2(o, out);
568         break;
569     case msosptAccentCallout3:
570         processAccentCallout3(o, out);
571         break;
572     case msosptBorderCallout1:
573         processBorderCallout1(o, out);
574         break;
575     case msosptBorderCallout2:
576         processBorderCallout2(o, out);
577         break;
578     case msosptBorderCallout3:
579         processBorderCallout3(o, out);
580         break;
581     case msosptAccentBorderCallout1:
582         processAccentBorderCallout1(o, out);
583         break;
584     case msosptAccentBorderCallout2:
585         processAccentBorderCallout2(o, out);
586         break;
587     case msosptAccentBorderCallout3:
588         processAccentBorderCallout3(o, out);
589         break;
590     case msosptRibbon:
591         processRibbon(o, out);
592         break;
593     case msosptRibbon2:
594         processRibbon2(o, out);
595         break;
596     case msosptChevron:
597         processChevron(o, out);
598         break;
599     case msosptPentagon:
600         processPentagon(o, out);
601         break;
602     case msosptNoSmoking:
603         processNoSmoking(o, out);
604         break;
605     case msosptSeal8:
606         processSeal8(o, out);
607         break;
608     case msosptSeal16:
609         processSeal16(o, out);
610         break;
611     case msosptSeal32:
612         processSeal32(o, out);
613         break;
614     case msosptWedgeRectCallout:
615         processWedgeRectCallout(o, out);
616         break;
617     case msosptWedgeRRectCallout:
618         processWedgeRRectCallout(o, out);
619         break;
620     case msosptWedgeEllipseCallout:
621         processWedgeEllipseCallout(o, out);
622         break;
623     case msosptWave:
624         processWave(o, out);
625         break;
626     case msosptFoldedCorner:
627         processFoldedCorner(o, out);
628         break;
629     case msosptLeftArrow:
630         processLeftArrow(o, out);
631         break;
632     case msosptDownArrow:
633         processDownArrow(o, out);
634         break;
635     case msosptUpArrow:
636         processUpArrow(o, out);
637         break;
638     case msosptLeftRightArrow:
639         processLeftRightArrow(o, out);
640         break;
641     case msosptUpDownArrow:
642         processUpDownArrow(o, out);
643         break;
644     case msosptIrregularSeal1:
645         processIrregularSeal1(o, out);
646         break;
647     case msosptIrregularSeal2:
648         processIrregularSeal2(o, out);
649         break;
650     case msosptLightningBolt:
651         processLightningBolt(o, out);
652         break;
653     case msosptHeart:
654         processHeart(o, out);
655         break;
656     case msosptPictureFrame:
657         processPictureFrame(o, out);
658         break;
659     case msosptQuadArrow:
660         processQuadArrow(o, out);
661         break;
662     case msosptLeftArrowCallout:
663         processLeftArrowCallout(o, out);
664         break;
665     case msosptRightArrowCallout:
666         processRightArrowCallout(o, out);
667         break;
668     case msosptUpArrowCallout:
669         processUpArrowCallout(o, out);
670         break;
671     case msosptDownArrowCallout:
672         processDownArrowCallout(o, out);
673         break;
674     case msosptLeftRightArrowCallout:
675         processLeftRightArrowCallout(o, out);
676         break;
677     case msosptUpDownArrowCallout:
678         processUpDownArrowCallout(o, out);
679         break;
680     case msosptQuadArrowCallout:
681         processQuadArrowCallout(o, out);
682         break;
683     case msosptBevel:
684         processBevel(o, out);
685         break;
686     case msosptLeftBracket:
687         processLeftBracket(o, out);
688         break;
689     case msosptRightBracket:
690         processRightBracket(o, out);
691         break;
692     case msosptLeftBrace:
693         processLeftBrace(o, out);
694         break;
695     case msosptRightBrace:
696         processRightBrace(o, out);
697         break;
698     case msosptLeftUpArrow:
699         processLeftUpArrow(o, out);
700         break;
701     case msosptBentUpArrow:
702         processBentUpArrow(o, out);
703         break;
704     case msosptBentArrow:
705         processBentArrow(o, out);
706         break;
707     case msosptSeal24:
708         processSeal24(o, out);
709         break;
710     case msosptStripedRightArrow:
711         processStripedRightArrow(o, out);
712         break;
713     case msosptNotchedRightArrow:
714         processNotchedRightArrow(o, out);
715         break;
716     case msosptBlockArc:
717         processBlockArc(o, out);
718         break;
719     case msosptSmileyFace:
720         processSmileyFace(o, out);
721         break;
722     case msosptVerticalScroll:
723         processVerticalScroll(o, out);
724         break;
725     case msosptHorizontalScroll:
726         processHorizontalScroll(o, out);
727         break;
728     case msosptCircularArrow:
729         processCircularArrow(o, out);
730         break;
731     case msosptNotchedCircularArrow:
732         processNotchedCircularArrow(o, out);
733         break;
734     case msosptUturnArrow:
735         processUturnArrow(o, out);
736         break;
737     case msosptCurvedRightArrow:
738         processCurvedRightArrow(o, out);
739         break;
740     case msosptCurvedLeftArrow:
741         processCurvedLeftArrow(o, out);
742         break;
743     case msosptCurvedUpArrow:
744         processCurvedUpArrow(o, out);
745         break;
746     case msosptCurvedDownArrow:
747         processCurvedDownArrow(o, out);
748         break;
749     case msosptCloudCallout:
750         processCloudCallout(o, out);
751         break;
752     case msosptEllipseRibbon:
753         processEllipseRibbon(o, out);
754         break;
755     case msosptEllipseRibbon2:
756         processEllipseRibbon2(o, out);
757         break;
758     case msosptFlowChartProcess:
759         processFlowChartProcess(o, out);
760         break;
761     case msosptFlowChartDecision:
762         processFlowChartDecision(o, out);
763         break;
764     case msosptFlowChartInputOutput:
765         processFlowChartInputOutput(o, out);
766         break;
767     case msosptFlowChartPredefinedProcess:
768         processFlowChartPredefinedProcess(o, out);
769         break;
770     case msosptFlowChartInternalStorage:
771         processFlowChartInternalStorage(o, out);
772         break;
773     case msosptFlowChartDocument:
774         processFlowChartDocument(o, out);
775         break;
776     case msosptFlowChartMultidocument:
777         processFlowChartMultidocument(o, out);
778         break;
779     case msosptFlowChartTerminator:
780         processFlowChartTerminator(o, out);
781         break;
782     case msosptFlowChartPreparation:
783         processFlowChartPreparation(o, out);
784         break;
785     case msosptFlowChartManualInput:
786         processFlowChartManualInput(o, out);
787         break;
788     case msosptFlowChartManualOperation:
789         processFlowChartManualOperation(o, out);
790         break;
791     case msosptFlowChartConnector:
792         processFlowChartConnector(o, out);
793         break;
794     case msosptFlowChartPunchedCard:
795         processFlowChartPunchedCard(o, out);
796         break;
797     case msosptFlowChartPunchedTape:
798         processFlowChartPunchedTape(o, out);
799         break;
800     case msosptFlowChartSummingJunction:
801         processFlowChartSummingJunction(o, out);
802         break;
803     case msosptFlowChartOr:
804         processFlowChartOr(o, out);
805         break;
806     case msosptFlowChartCollate:
807         processFlowChartCollate(o, out);
808         break;
809     case msosptFlowChartSort:
810         processFlowChartSort(o, out);
811         break;
812     case msosptFlowChartExtract:
813         processFlowChartExtract(o, out);
814         break;
815     case msosptFlowChartMerge:
816         processFlowChartMerge(o, out);
817         break;
818     //
819     // TODO: msosptFlowChartOfflineStorage
820     //
821     case msosptFlowChartOnlineStorage:
822         processFlowChartOnlineStorage(o, out);
823         break;
824     case msosptFlowChartMagneticTape:
825         processFlowChartMagneticTape(o, out);
826         break;
827     case msosptFlowChartMagneticDisk:
828         processFlowChartMagneticDisk(o, out);
829         break;
830     case msosptFlowChartMagneticDrum:
831         processFlowChartMagneticDrum(o, out);
832         break;
833     case msosptFlowChartDisplay:
834         processFlowChartDisplay(o, out);
835         break;
836     case msosptFlowChartDelay:
837         processFlowChartDelay(o, out);
838         break;
839     //
840     // TODO: msosptTextPlainText, msosptTextStop, msosptTextTriangle,
841     // msosptTextTriangleInverted, msosptTextChevron,
842     // msosptTextChevronInverted, msosptTextRingInside, msosptTextRingOutside,
843     // msosptTextArchUpCurve, msosptTextArchDownCurve, msosptTextCircleCurve,
844     // msosptTextButtonCurve, msosptTextArchUpPour, msosptTextArchDownPour,
845     // msosptTextCirclePour, msosptTextButtonPour, msosptTextCurveUp,
846     // msosptTextCurveDown, msosptTextCascadeUp, msosptTextCascadeDown,
847     // msosptTextWave1, msosptTextWave2, msosptTextWave3, msosptTextWave4,
848     // msosptTextInflate, msosptTextDeflate, msosptTextInflateBottom,
849     // msosptTextDeflateBottom, msosptTextInflateTop, msosptTextDeflateTop,
850     // msosptTextDeflateInflate, msosptTextDeflateInflateDeflate,
851     // msosptTextFadeRight, msosptTextFadeLeft, msosptTextFadeUp,
852     // msosptTextFadeDown, msosptTextSlantUp, msosptTextSlantDown,
853     // msosptTextCanUp, msosptTextCanDown
854     //
855     case msosptFlowChartAlternateProcess:
856         processFlowChartAlternateProcess(o, out);
857         break;
858     case msosptFlowChartOffpageConnector:
859         processFlowChartOffpageConnector(o, out);
860         break;
861     case msosptCallout90:
862         processCallout90(o, out);
863         break;
864     case msosptAccentCallout90:
865         processAccentCallout90(o, out);
866         break;
867     case msosptBorderCallout90:
868         processBorderCallout90(o, out);
869         break;
870     case msosptAccentBorderCallout90:
871         processAccentBorderCallout90(o, out);
872         break;
873     case msosptLeftRightUpArrow:
874         processLeftRightUpArrow(o, out);
875         break;
876     case msosptSun:
877         processSun(o, out);
878         break;
879     case msosptMoon:
880         processMoon(o, out);
881         break;
882     case msosptBracketPair:
883         processBracketPair(o, out);
884         break;
885     case msosptBracePair:
886         processBracePair(o, out);
887         break;
888     case msosptSeal4:
889         processSeal4(o, out);
890         break;
891     case msosptDoubleWave:
892         processDoubleWave(o, out);
893         break;
894     case msosptActionButtonBlank:
895         processActionButtonBlank(o, out);
896         break;
897     case msosptActionButtonHome:
898         processActionButtonHome(o, out);
899         break;
900     case msosptActionButtonHelp:
901         processActionButtonHelp(o, out);
902         break;
903     case msosptActionButtonInformation:
904         processActionButtonInformation(o, out);
905         break;
906     case msosptActionButtonForwardNext:
907         processActionButtonForwardNext(o, out);
908         break;
909     case msosptActionButtonBackPrevious:
910         processActionButtonBackPrevious(o, out);
911         break;
912     case msosptActionButtonEnd:
913         processActionButtonEnd(o, out);
914         break;
915     case msosptActionButtonBeginning:
916         processActionButtonBeginning(o, out);
917         break;
918     case msosptActionButtonReturn:
919         processActionButtonReturn(o, out);
920         break;
921     case msosptActionButtonDocument:
922         processActionButtonDocument(o, out);
923         break;
924     case msosptActionButtonSound:
925         processActionButtonSound(o, out);
926         break;
927     case msosptActionButtonMovie:
928         processActionButtonMovie(o, out);
929         break;
930     case msosptHostControl:
931         processPictureFrame(o, out);
932         break;
933     case msosptTextBox:
934         processTextBox(o, out);
935         break;
936     default:
937         qDebug() << "Cannot handle shape 0x" << hex << shapeType;
938         break;
939     }
940 }
941 
processStyleAndText(const MSO::OfficeArtSpContainer & o,Writer & out)942 void ODrawToOdf::processStyleAndText(const MSO::OfficeArtSpContainer& o,
943                                      Writer& out)
944 {
945     processStyle(o, out);
946     processText(o, out);
947 }
948 
processStyle(const MSO::OfficeArtSpContainer & o,Writer & out)949 void ODrawToOdf::processStyle(const MSO::OfficeArtSpContainer& o,
950                               Writer& out)
951 {
952     addGraphicStyleToDrawElement(out, o);
953     set2dGeometry(o, out);
954 }
955 
processText(const MSO::OfficeArtSpContainer & o,Writer & out)956 void ODrawToOdf::processText(const MSO::OfficeArtSpContainer& o,
957                              Writer& out)
958 {
959     if (!client) {
960         qWarning() << "Warning: There's no Client!";
961         return;
962     }
963 
964     if (o.clientData && client->onlyClientData(*o.clientData)) {
965         client->processClientData(o.clientTextbox.data(), *o.clientData, out);
966     } else if (o.clientTextbox) {
967         client->processClientTextBox(*o.clientTextbox, o.clientData.data(), out);
968     }
969 }
970 
processModifiers(const MSO::OfficeArtSpContainer & o,Writer & out,const QList<int> & defaults)971 void ODrawToOdf::processModifiers(const MSO::OfficeArtSpContainer &o, Writer &out, const QList<int>& defaults)
972 {
973     const AdjustValue* val1 = get<AdjustValue>(o);
974     if (!val1 && defaults.isEmpty()) return;
975     const Adjust2Value* val2 = get<Adjust2Value>(o);
976     const Adjust3Value* val3 = get<Adjust3Value>(o);
977     const Adjust4Value* val4 = get<Adjust4Value>(o);
978     const Adjust5Value* val5 = get<Adjust5Value>(o);
979     const Adjust6Value* val6 = get<Adjust6Value>(o);
980     const Adjust7Value* val7 = get<Adjust7Value>(o);
981     const Adjust8Value* val8 = get<Adjust8Value>(o);
982 
983     QString modifiers = QString::number(val1 ? val1->adjustvalue : defaults[0]);
984     if (val2 || defaults.size() > 1) {
985         modifiers += QString(" %1").arg(val2 ? val2->adjust2value : defaults[1]);
986         if (val3 || defaults.size() > 2) {
987             modifiers += QString(" %1").arg(val3 ? val3->adjust3value : defaults[2]);
988             if (val4 || defaults.size() > 3) {
989                 modifiers += QString(" %1").arg(val4 ? val4->adjust4value : defaults[3]);
990                 if (val5 || defaults.size() > 4) {
991                     modifiers += QString(" %1").arg(val5 ? val5->adjust5value : defaults[4]);
992                     if (val6 || defaults.size() > 5) {
993                         modifiers += QString(" %1").arg(val6 ? val6->adjust6value : defaults[5]);
994                         if (val7 || defaults.size() > 6) {
995                             modifiers += QString(" %1").arg(val7 ? val7->adjust7value : defaults[6]);
996                             if (val8 || defaults.size() > 7) {
997                                 modifiers += QString(" %1").arg(val8 ? val8->adjust8value : defaults[7]);
998                             }
999                         }
1000                     }
1001                 }
1002             }
1003         }
1004     }
1005 
1006     out.xml.addAttribute("draw:modifiers", modifiers);
1007 }
1008 
1009 // Position the shape into the slide or into a group shape
set2dGeometry(const OfficeArtSpContainer & o,Writer & out)1010 void ODrawToOdf::set2dGeometry(const OfficeArtSpContainer& o, Writer& out)
1011 {
1012     const OfficeArtDggContainer* dgg = 0;
1013     const OfficeArtSpContainer* master = 0;
1014     const DrawStyle ds(dgg, master, &o);
1015     const qreal rotation = toQReal(ds.rotation());
1016 
1017     //transform the rectangle into the coordinate system of the group shape
1018     QRectF rect = getRect(o);
1019     QRectF trect (out.hOffset(rect.x()), out.vOffset(rect.y()),
1020                   out.hLength(rect.width()), out.vLength(rect.height()));
1021 
1022     //draw:caption-id
1023     //draw:class-names
1024     //draw:data
1025     //draw:engine
1026     //draw:layer
1027     out.xml.addAttribute("draw:layer", "layout");
1028     //draw:name
1029     //draw:style-name
1030     //draw:text-style-name
1031     //draw:transform
1032     if (rotation) {
1033 
1034         const quint16 shapeType = o.shapeProp.rh.recInstance;
1035         const quint16 nrotation = normalizeRotation(rotation);
1036         const qreal angle = (nrotation / (qreal)180) * M_PI;
1037 
1038         trect = processRect(shapeType, rotation, trect);
1039 
1040         static const QString transform_str("translate(%1 %2) rotate(%3) translate(%4 %5)");
1041         const QPointF center = trect.center();
1042         const qreal height = trect.height();
1043         const qreal width = trect.width();
1044 
1045         out.xml.addAttribute("draw:transform",
1046                              transform_str.arg(client->formatPos(-width/2)).arg(client->formatPos(-height/2)).arg(-angle).arg(client->formatPos(center.x())).arg(client->formatPos(center.y())));
1047     }
1048     //svg:x
1049     //svg:y
1050     else {
1051         out.xml.addAttribute("svg:x", client->formatPos(trect.x()));
1052         out.xml.addAttribute("svg:y", client->formatPos(trect.y()));
1053     }
1054     //NOTE: z-index is set in ODrawToOdf::Client::addTextStyles
1055     //draw:z-index
1056     //presentation:class-names
1057     //presentation:style-name
1058     //svg:height
1059     out.xml.addAttribute("svg:height", client->formatPos(trect.height()));
1060     //svg:width
1061     out.xml.addAttribute("svg:width", client->formatPos(trect.width()));
1062     //table:end-cell-address
1063     //table:end-x
1064     //table:end-y
1065     //table:table-background
1066     //text:anchor-page-number
1067     //text:anchor-type
1068     //xml:id
1069 }
1070 
setEnhancedGeometry(const MSO::OfficeArtSpContainer & o,Writer & out)1071 void ODrawToOdf::setEnhancedGeometry(const MSO::OfficeArtSpContainer& o, Writer& out)
1072 {
1073     const OfficeArtDggContainer* drawingGroup = 0;
1074     const OfficeArtSpContainer* master = 0;
1075     const DrawStyle ds(drawingGroup, master, &o);
1076 
1077     IMsoArray _v = ds.pVertices_complex();
1078     IMsoArray segmentInfo = ds.pSegmentInfo_complex();
1079 
1080     if (!_v.data.isEmpty() && !segmentInfo.data.isEmpty()) {
1081 
1082         QVector<QPoint> verticesPoints;
1083 
1084         //_v.data is an array of POINTs, MS-ODRAW, page 89
1085         QByteArray xArray(sizeof(int), 0), yArray(sizeof(int), 0);
1086         int step = _v.cbElem;
1087         if (step == 0xfff0) {
1088             step = 4;
1089         }
1090 
1091         int maxX = 0, minX = INT_MAX, maxY = 0, minY = INT_MAX;
1092         int x,y;
1093 
1094         //get vertice points
1095         for (int i = 0, offset = 0; i < _v.nElems; i++, offset += step) {
1096             // x coordinate of this point
1097             xArray.replace(0, step/2, _v.data.mid(offset, step/2));
1098             x = *(int*) xArray.data();
1099 
1100             // y coordinate of this point
1101             yArray.replace(0, step/2, _v.data.mid(offset + step/2, step/2));
1102             y = *(int*) yArray.data();
1103 
1104             verticesPoints.append(QPoint(x, y));
1105 
1106             // find maximum and minimum coordinates
1107             if (maxY < y) {
1108                 maxY = y;
1109             }
1110             if (minY > y) {
1111                 minY = y ;
1112             }
1113             if (maxX < x) {
1114                 maxX = x;
1115             }
1116             if (minX > x) {
1117                 minX = x;
1118             }
1119         }
1120 
1121         //TODO: geoLeft, geoTop, geoRight, geoBottom
1122         QString viewBox = QString::number(minX) + ' ' + QString::number(minY) + ' ' +
1123                           QString::number(maxX) + ' ' + QString::number(maxY);
1124 
1125         // combine segmentationInfoData and verticePoints into enhanced-path string
1126         QString enhancedPath;
1127         ushort msopathtype;
1128         bool nOffRange = false;
1129 
1130         for (int i = 0, n = 0; ((i < segmentInfo.nElems) && !nOffRange); i++) {
1131 
1132             msopathtype = (((*(ushort *)(segmentInfo.data.data() + i * 2)) >> 13) & 0x7);
1133 
1134             switch (msopathtype) {
1135             case msopathLineTo:
1136             {
1137                 if (n >= verticesPoints.size()) {
1138                     qDebug() << "EnhancedGeometry: index into verticesPoints out of range!";
1139                     nOffRange = true;
1140                     break;
1141                 }
1142                 enhancedPath = enhancedPath + "L " + QString::number(verticesPoints[n].x()) + ' ' +
1143                                QString::number(verticesPoints[n].y()) + ' ';
1144                 n++;
1145                 break;
1146             }
1147             case msopathCurveTo:
1148             {
1149                 if (n + 2 >= verticesPoints.size()) {
1150                     qDebug() << "EnhancedGeometry: index into verticesPoints out of range!";
1151                     nOffRange = true;
1152                     break;
1153                 }
1154                 QPoint pt1 = verticesPoints.at(n);
1155                 QPoint pt2 = verticesPoints.at(n + 1);
1156                 QPoint pt3 = verticesPoints.at(n + 2);
1157 
1158                 enhancedPath = enhancedPath + "C " +
1159                         QString::number(pt1.x()) + ' ' +
1160                         QString::number(pt1.y()) + ' ' +
1161                         QString::number(pt2.x()) + ' ' +
1162                         QString::number(pt2.y()) + ' ' +
1163                         QString::number(pt3.x()) + ' ' +
1164                         QString::number(pt3.y()) + ' ';
1165                 n = n + 3;
1166                 break;
1167             }
1168             case msopathMoveTo:
1169             {
1170                 if (n >= verticesPoints.size()) {
1171                     qDebug() << "EnhancedGeometry: index into verticesPoints out of range!";
1172                     nOffRange = true;
1173                     break;
1174                 }
1175                 enhancedPath = enhancedPath + "M " + QString::number(verticesPoints[n].x()) + ' ' +
1176                                QString::number(verticesPoints[n].y()) + ' ';
1177                 n++;
1178                 break;
1179             }
1180             case msopathClose:
1181                 enhancedPath = enhancedPath + "Z ";
1182                 break;
1183             case msopathEnd:
1184                 enhancedPath = enhancedPath + "N ";
1185                 break;
1186             case msopathEscape:
1187             case msopathClientEscape:
1188                  break;
1189             }
1190         }
1191         //dr3d:projection
1192         //dr3d:shade-mode
1193         //draw:concentric-gradient-fill-allowed
1194         //draw:enhanced-path
1195         out.xml.addAttribute("draw:enhanced-path", enhancedPath);
1196         //draw:extrusion
1197         //draw:extrusion-allowed
1198         //draw:extrusion-brightness
1199         //draw:extrusion-color
1200         //draw:extrusion-depth
1201         //draw:extrusion-diffusion
1202         //draw:extrusion-first-light-direction
1203         //draw:extrusion-first-light-harsh
1204         //draw:extrusion-first-light-level
1205         //draw:extrusion-light-face
1206         //draw:extrusion-metal
1207         //draw:extrusion-number-of-line-segments
1208         //draw:extrusion-origin
1209         //draw:extrusion-rotation-angle
1210         //draw:extrusion-rotation-center
1211         //draw:extrusion-second-light-direction
1212         //draw:extrusion-second-light-harsh
1213         //draw:extrusion-second-light-level
1214         //draw:extrusion-shininess
1215         //draw:extrusion-skew
1216         //draw:extrusion-specularity
1217         //draw:extrusion-viewpoint
1218         //draw:glue-point-leaving-directions
1219         //draw:glue-points
1220         //draw:glue-point-type
1221         //draw:mirror-horizontal
1222         if (o.shapeProp.fFlipH) {
1223             out.xml.addAttribute("draw:mirror-horizontal", "true");
1224         }
1225         //draw:mirror-vertical
1226         if (o.shapeProp.fFlipV) {
1227             out.xml.addAttribute("draw:mirror-vertical", "true");
1228         }
1229         //draw:modifiers
1230         //draw:path-stretchpoint-x
1231         //draw:path-stretchpoint-y
1232         //draw:text-areas
1233         //draw:text-path
1234         //draw:text-path-allowed
1235         //draw:text-path-mode
1236         //draw:text-path-same-letter-heights
1237         //draw:text-path-scale
1238         //draw:text-rotate-angle
1239         //draw:type
1240         out.xml.addAttribute("draw:type", "non-primitive");
1241         //svg:viewBox
1242         out.xml.addAttribute("svg:viewBox", viewBox);
1243     }
1244 }
1245 
path2svg(const QPainterPath & path)1246 QString ODrawToOdf::path2svg(const QPainterPath &path)
1247 {
1248     QString d;
1249 
1250     int count = path.elementCount();
1251     for (int i = 0; i < count; i++) {
1252 
1253         QPainterPath::Element e = path.elementAt(i);
1254         switch(e.type) {
1255         case QPainterPath::MoveToElement:
1256             d.append(QString("M %1 %2").arg(e.x).arg(e.y));
1257             break;
1258         case QPainterPath::LineToElement:
1259             d.append(QString("L %1 %2").arg(e.x).arg(e.y));
1260             break;
1261         case QPainterPath::CurveToElement:
1262             d.append(QString("C %1 %2").arg(e.x).arg(e.y));
1263             break;
1264         case QPainterPath::CurveToDataElement:
1265             d.append(QString(" %1 %2").arg(e.x).arg(e.y));
1266             break;
1267         default:
1268             qWarning() << "This element unhandled: " << e.type;
1269         }
1270     }
1271     return d;
1272 }
1273 
setShapeMirroring(const MSO::OfficeArtSpContainer & o,Writer & out)1274 void ODrawToOdf::setShapeMirroring(const MSO::OfficeArtSpContainer& o, Writer& out)
1275 {
1276     if (o.shapeProp.fFlipV) {
1277         out.xml.addAttribute("draw:mirror-vertical", "true");
1278     }
1279     if (o.shapeProp.fFlipH) {
1280         out.xml.addAttribute("draw:mirror-horizontal", "true");
1281     }
1282 }
1283