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