File indexing completed on 2025-01-12 10:34:22

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2010 KO GmbH <jos.van.den.oever@kogmbh.com>
0003    SPDX-FileCopyrightText: 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
0004 
0005    SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 /**
0009  * Note that the implementations here are supposed to be defined by DrawingML.
0010  * This details the geometry etc. in Appendix D, in the file
0011  * presetShapeDefinitions.xml.
0012  *
0013  * @note Of the {Bent,Curved}Connector[2345] shapes, the MS Office only seems to
0014  * support the [23] variants. The remainder have been coded in a manner
0015  * consistent with the [23] variants, but have not been tested.
0016  */
0017 
0018 #include "ODrawToOdf.h"
0019 #include "drawstyle.h"
0020 #include "msodraw.h"
0021 #include "generated/leinputstream.h"
0022 #include "writeodf/writeodfdraw.h"
0023 
0024 #include <QDebug>
0025 #include <QPainterPath>
0026 #include <QTransform>
0027 #include <QBuffer>
0028 #include <QUrl>
0029 
0030 #include <cmath>
0031 
0032 
0033 using namespace MSO;
0034 using namespace writeodf;
0035 
0036 qint16
0037 ODrawToOdf::normalizeRotation(qreal rotation)
0038 {
0039     qint16 angle = ((qint16)rotation) % 360;
0040     if (angle < 0) {
0041         angle = angle + 360;
0042     }
0043     return angle;
0044 }
0045 
0046 /**
0047  * Return the bounding rectangle for this object.
0048  **/
0049 QRectF
0050 ODrawToOdf::getRect(const OfficeArtSpContainer &o)
0051 {
0052     if (o.childAnchor) {
0053         const OfficeArtChildAnchor& r = *o.childAnchor;
0054         return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
0055     } else if (o.clientAnchor && client) {
0056         return client->getRect(*o.clientAnchor);
0057     } else if (o.shapeProp.fHaveAnchor && client) {
0058         return client->getReserveRect();
0059     } else {
0060         return QRectF();
0061     }
0062 }
0063 
0064 QRectF
0065 ODrawToOdf::processRect(const quint16 shapeType, const qreal rotation, QRectF &rect)
0066 {
0067     bool transform_anchor = false;
0068     qint16 nrotation = normalizeRotation(rotation);
0069 
0070     //TODO: Add other shapes here!
0071 
0072     switch (shapeType) {
0073     case msosptNotPrimitive:
0074         if ( ((nrotation >= 45) && (nrotation < 135)) ||
0075              ((nrotation >= 225) && (nrotation < 315)))
0076         {
0077             transform_anchor = true;
0078         }
0079         break;
0080     default:
0081         break;
0082     }
0083     if (transform_anchor) {
0084         QPointF center = rect.center();
0085         QTransform transform;
0086         transform.rotate(90);
0087         rect = transform.mapRect(rect.translated(-center)).translated(center);
0088     }
0089     return rect;
0090 }
0091 
0092 void ODrawToOdf::processRectangle(const OfficeArtSpContainer& o, Writer& out)
0093 {
0094     // TODO: Use client->isPlaceholder - might require an update of the
0095     // placeholderAllowed function in the PPT filter.  Trying to save as many
0096     // shapes into draw:text-box at the moment, because vertical alignment in
0097     // draw:custom-shape does not work properly (bug 288047).
0098     if (o.clientData && client->processRectangleAsTextBox(*o.clientData)) {
0099         processTextBox(o, out);
0100     } else {
0101         const DrawStyle ds(0, 0, &o);
0102         if (ds.pib()) {
0103             // see bug https://bugs.kde.org/show_bug.cgi?id=285577
0104             processPictureFrame(o, out);
0105         } else {
0106             draw_custom_shape rect(&out.xml);
0107             processStyleAndText(o, out);
0108             draw_enhanced_geometry eg(rect.add_draw_enhanced_geometry());
0109             eg.set_svg_viewBox("0 0 21600 21600");
0110             eg.set_draw_enhanced_path("M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N");
0111             eg.set_draw_type("rectangle");
0112             setShapeMirroring(o, out);
0113         }
0114     }
0115 }
0116 
0117 void ODrawToOdf::processTextBox(const OfficeArtSpContainer& o, Writer& out)
0118 {
0119     draw_frame frame(&out.xml);
0120     processStyle(o, out);
0121     draw_text_box text(frame.add_draw_text_box());
0122     processText(o, out);
0123 }
0124 
0125 void ODrawToOdf::processLine(const OfficeArtSpContainer& o, Writer& out)
0126 {
0127     const QRectF rect = getRect(o);
0128     qreal x1 = rect.x();
0129     qreal y1 = rect.y();
0130     qreal x2 = rect.x() + rect.width();
0131     qreal y2 = rect.y() + rect.height();
0132 
0133     // shape mirroring
0134     if (o.shapeProp.fFlipV) {
0135         qSwap(y1, y2);
0136     }
0137     if (o.shapeProp.fFlipH) {
0138         qSwap(x1, x2);
0139     }
0140 
0141     draw_line line(&out.xml,
0142                    client->formatPos(out.hOffset(x1)),
0143                    client->formatPos(out.hOffset(x2)),
0144                    client->formatPos(out.vOffset(y1)),
0145                    client->formatPos(out.vOffset(y2)));
0146     addGraphicStyleToDrawElement(out, o);
0147     line.set_draw_layer("layout");
0148     processText(o, out);
0149 }
0150 
0151 void ODrawToOdf::drawStraightConnector1(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0152 {
0153     out.xml.addAttribute("draw:type", "line");
0154     shapePath.moveTo(l, t);
0155     shapePath.lineTo(r, b);
0156 }
0157 
0158 void ODrawToOdf::drawPathBentConnector2(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0159 {
0160     Q_UNUSED(out);
0161     shapePath.moveTo(l, t);
0162     shapePath.lineTo(r, t);
0163     shapePath.lineTo(r, b);
0164 }
0165 
0166 void ODrawToOdf::drawPathBentConnector3(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0167 {
0168     Q_UNUSED(out);
0169     qreal w = qAbs(r - l);
0170     qreal adj1 = 50000;
0171     qreal x1 = w * adj1 / 100000;
0172 
0173     shapePath.moveTo(l, t);
0174     shapePath.lineTo(l + x1, t);
0175     shapePath.lineTo(l + x1, b);
0176     shapePath.lineTo(r, b);
0177 }
0178 
0179 void ODrawToOdf::drawPathBentConnector4(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0180 {
0181     Q_UNUSED(out);
0182     qreal w = qAbs(r - l);
0183     qreal h = qAbs(b - t);
0184     qreal adj1 = 50000;
0185     qreal adj2 = 50000;
0186     qreal x1 = w * adj1 / 100000;
0187 //    qreal x2 = x1 + r / 2;
0188     qreal y2 = h * adj2 / 100000;
0189 //    qreal y1 = t + y2 / 2;
0190 
0191     shapePath.moveTo(l, t);
0192     shapePath.lineTo(l + x1, t);
0193     shapePath.lineTo(l + x1, y2);
0194     shapePath.lineTo(r, y2);
0195     shapePath.lineTo(r, b);
0196 }
0197 
0198 void ODrawToOdf::drawPathBentConnector5(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0199 {
0200     Q_UNUSED(out);
0201     qreal w = qAbs(r - l);
0202     qreal h = qAbs(b - t);
0203     qreal adj1 = 50000;
0204     qreal adj2 = 50000;
0205     qreal adj3 = 50000;
0206     qreal x1 = w * adj1 / 100000;
0207     qreal x3 = w * adj3 / 100000;
0208 //    qreal x2 = x1 + x3 / 2;
0209     qreal y2 = h * adj2 / 100000;
0210 //    qreal y1 = t + y2 / 2;
0211 //    qreal y3 = b + y2 / 2;
0212 
0213     shapePath.moveTo(l, t);
0214     shapePath.lineTo(l + x1, t);
0215     shapePath.lineTo(l + x1, y2);
0216     shapePath.lineTo(l + x3, y2);
0217     shapePath.lineTo(l + x3, b);
0218     shapePath.lineTo(r, b);
0219 }
0220 
0221 void ODrawToOdf::drawPathCurvedConnector2(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0222 {
0223     Q_UNUSED(out);
0224     qreal w = qAbs(r - l);
0225     qreal h = qAbs(b - t);
0226 
0227     shapePath.moveTo(l, t);
0228     shapePath.cubicTo(l + w / 2, t, r, h / 2, r, b);
0229 }
0230 
0231 void ODrawToOdf::drawPathCurvedConnector3(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0232 {
0233     Q_UNUSED(out);
0234     qreal w = qAbs(r - l);
0235     qreal h = qAbs(b - t);
0236     qreal adj1 = 50000;
0237     qreal x2 = w * adj1 / 100000;
0238     qreal x1 = l + x2 /*/ 2*/;
0239 //    qreal x3 = r + x2 / 2;
0240 //    qreal y3 = h * 3 / 4;
0241 
0242     shapePath.moveTo(l, t);
0243     shapePath.cubicTo(x1, t, x1, t + h / 2, l + x2, t + h / 2);
0244     shapePath.cubicTo(l + x2, t + h / 2, l + x2, b, r, b);
0245 }
0246 
0247 void ODrawToOdf::drawPathCurvedConnector4(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0248 {
0249     Q_UNUSED(out);
0250     qreal w = qAbs(r - l);
0251     qreal h = qAbs(b - t);
0252     qreal adj1 = 50000;
0253     qreal adj2 = 50000;
0254     qreal x2 = w * adj1 / 100000;
0255     qreal x1 = l + x2 / 2;
0256     qreal x3 = r + x2 / 2;
0257     qreal x4 = x2 + x3 / 2;
0258     qreal x5 = x3 + r / 2;
0259     qreal y4 = h * adj2 / 100000;
0260     qreal y1 = t + y4 / 2;
0261     qreal y2 = t + y1 / 2;
0262     qreal y3 = y1 + y4 / 2;
0263     qreal y5 = b + y4 / 2;
0264 
0265     shapePath.moveTo(l, t);
0266     shapePath.cubicTo(x1, t, l + x2, y2, l + x2, y1);
0267     shapePath.cubicTo(l + x2, y3, x4, y4, x3, y4);
0268     shapePath.cubicTo(x5, y4, r, y5, r, b);
0269 }
0270 
0271 void ODrawToOdf::drawPathCurvedConnector5(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0272 {
0273     Q_UNUSED(out);
0274     qreal w = qAbs(r - l);
0275     qreal h = qAbs(b - t);
0276     qreal adj1 = 50000;
0277     qreal adj2 = 50000;
0278     qreal adj3 = 50000;
0279     qreal x3 = w * adj1 / 100000;
0280     qreal x6 = w * adj3 / 100000;
0281     qreal x1 = x3 + x6 / 2;
0282     qreal x2 = l + x3 / 2;
0283     qreal x4 = x3 + x1 / 2;
0284     qreal x5 = x6 + x1 / 2;
0285     qreal x7 = x6 + r / 2;
0286     qreal y4 = h * adj2 / 100000;
0287     qreal y1 = t + y4 / 2;
0288     qreal y2 = t + y1 / 2;
0289     qreal y3 = y1 + y4 / 2;
0290     qreal y5 = b + y4 / 2;
0291     qreal y6 = y5 + y4 / 2;
0292     qreal y7 = y5 + b / 2;
0293 
0294     shapePath.moveTo(l, t);
0295     shapePath.cubicTo(x2, t, l + x3, y2, l + x3, y1);
0296     shapePath.cubicTo(x3, y3, x4, y4, x1, y4);
0297     shapePath.cubicTo(x5, y4, l + x6, y6, l + x6, y5);
0298     shapePath.cubicTo(l + x6, y7, x7, b, r, b);
0299 }
0300 
0301 /**
0302  * Common handler for Connectors.
0303  */
0304 void ODrawToOdf::processConnector(const OfficeArtSpContainer& o, Writer& out, PathArtist drawPath)
0305 {
0306     const OfficeArtDggContainer * drawingGroup = 0;
0307     if (client) {
0308         drawingGroup = client->getOfficeArtDggContainer();
0309     }
0310 
0311     const OfficeArtSpContainer* master = 0;
0312     const DrawStyle ds(drawingGroup, master, &o);
0313     qreal rotation = toQReal( ds.rotation() );
0314 
0315     const QRectF rect = getRect(o);
0316     qreal x1 = rect.x();
0317     qreal y1 = rect.y();
0318     qreal x2 = rect.x() + rect.width();
0319     qreal y2 = rect.y() + rect.height();
0320 
0321     QRectF shapeRect = rect;
0322 
0323     qreal sx1 = x1;
0324     qreal sy1 = y1;
0325     qreal sx2 = x2;
0326     qreal sy2 = y2;
0327 
0328     if (rotation != 0.0) {
0329         QTransform m;
0330         m.rotate( -rotation );
0331         shapeRect = m.mapRect(rect.translated(-rect.center())).translated(rect.center());
0332 
0333         sx1 = shapeRect.topLeft().x();
0334         sy1 = shapeRect.topLeft().y();
0335         sx2 = shapeRect.bottomRight().x();
0336         sy2 = shapeRect.bottomRight().y();
0337     }
0338 
0339     // Prepare to transform the path according the shape properties like flip
0340     // and rotation.
0341     QTransform m;
0342     m.reset();
0343     m.translate( -shapeRect.center().x(), -shapeRect.center().y() );
0344 
0345     // Mirroring
0346     if (o.shapeProp.fFlipH){
0347         m.scale(-1,1);
0348     }
0349 
0350     if (o.shapeProp.fFlipV){
0351         m.scale(1,-1);
0352     }
0353 
0354     if (rotation != 0) {
0355         m.rotate(rotation);
0356     }
0357 
0358     m.translate( shapeRect.center().x(), shapeRect.center().y() );
0359 
0360     // the viewbox should be set, where is this done for draw:connector?
0361     out.xml.startElement("draw:connector");
0362     addGraphicStyleToDrawElement(out, o);
0363     out.xml.addAttribute("draw:layer", "layout");
0364 
0365     // Compute path and transform it.
0366     QPainterPath shapePath;
0367     (this->*drawPath)(sx1, sy1, sx2, sy2, out, shapePath);
0368 
0369     shapePath = m.map(shapePath);
0370 
0371     // translate the QPainterPath into svg:d attribute
0372     QString path = path2svg(shapePath);
0373 
0374     out.xml.addAttribute("svg:x1", client->formatPos(out.hOffset(x1)));
0375     out.xml.addAttribute("svg:y1", client->formatPos(out.vOffset(y1)));
0376     out.xml.addAttribute("svg:x2", client->formatPos(out.hOffset(x2)));
0377     out.xml.addAttribute("svg:y2", client->formatPos(out.vOffset(y2)));
0378     if (!path.isEmpty()) {
0379         out.xml.addAttribute("svg:d", path);
0380     }
0381 
0382     processText(o, out);
0383     out.xml.endElement();
0384 }
0385 
0386 void ODrawToOdf::processPictureFrame(const OfficeArtSpContainer& o, Writer& out)
0387 {
0388     DrawStyle ds(0, &o);
0389 
0390     // A value of 0x00000000 MUST be ignored.  [MS-ODRAW] — v20101219
0391     if (!ds.pib()) return;
0392 
0393     draw_frame frame(&out.xml);
0394     processStyle(o, out);
0395 
0396     //NOTE: OfficeArtClienData might contain additional information
0397     //about a shape.
0398 
0399     QString url;
0400     if (client) {
0401         url = client->getPicturePath(ds.pib());
0402     }
0403     // if the image cannot be found, just place an empty frame
0404     if (url.isEmpty()) {
0405         return;
0406     }
0407     draw_image image(frame.add_draw_image());
0408     image.set_xlink_href(QUrl(url));
0409     image.set_xlink_type("simple");
0410     image.set_xlink_show("embed");
0411     image.set_xlink_actuate("onLoad");
0412 }
0413 
0414 void ODrawToOdf::processNotPrimitive(const MSO::OfficeArtSpContainer& o, Writer& out)
0415 {
0416     draw_custom_shape shape(&out.xml);
0417     processStyleAndText(o, out);
0418     draw_enhanced_geometry eg(shape.add_draw_enhanced_geometry());
0419     setEnhancedGeometry(o, out);
0420 }
0421 
0422 
0423 void ODrawToOdf::processDrawingObject(const OfficeArtSpContainer& o, Writer& out)
0424 {
0425     if (!client) {
0426         qWarning() << "Warning: There's no Client!";
0427         return;
0428     }
0429 
0430     quint16 shapeType = o.shapeProp.rh.recInstance;
0431     client->m_currentShapeType = o.shapeProp.rh.recInstance;
0432 
0433     switch (shapeType) {
0434     case msosptNotPrimitive:
0435         processNotPrimitive(o, out);
0436         break;
0437     case msosptRectangle:
0438         processRectangle(o, out);
0439         break;
0440     case msosptRoundRectangle:
0441         processRoundRectangle(o, out);
0442         break;
0443     case msosptEllipse:
0444         // TODO: Something has to be done here (LukasT).  LukasT:
0445         // "Great comment", can you provide more details? :)
0446         processEllipse(o, out);
0447         break;
0448     case msosptDiamond:
0449         processDiamond(o, out);
0450         break;
0451     case msosptIsocelesTriangle:
0452         processIsocelesTriangle(o, out);
0453         break;
0454     case msosptRightTriangle:
0455         processRightTriangle(o, out);
0456         break;
0457     case msosptParallelogram:
0458         processParallelogram(o, out);
0459         break;
0460     case msosptTrapezoid:
0461         processTrapezoid(o, out);
0462         break;
0463     case msosptHexagon:
0464         processHexagon(o, out);
0465         break;
0466     case msosptOctagon:
0467         processOctagon(o, out);
0468         break;
0469     case msosptPlus:
0470         processPlus(o, out);
0471         break;
0472     case msosptStar:
0473         processStar(o, out);
0474         break;
0475     case msosptArrow:
0476         processArrow(o, out);
0477         break;
0478     //
0479     // TODO: msosptThickArrow
0480     //
0481     case msosptHomePlate:
0482         processHomePlate(o, out);
0483         break;
0484     case msosptCube:
0485         processCube(o, out);
0486         break;
0487     //
0488     // TODO: msosptBaloon, msosptSeal
0489     //
0490 
0491     // NOTE: OpenOffice treats msosptNotchedCircularArrow as msosptArc.  The
0492     // msosptNotchedCircularArrow value SHOULD NOT be used according to the
0493     // MS-ODRAW spec.  However it occurs in many Word8 files.
0494     case msosptArc:
0495         processNotchedCircularArrow(o, out);
0496         break;
0497     case msosptLine:
0498         processLine(o, out);
0499         break;
0500     case msosptPlaque:
0501         processPlaque(o, out);
0502         break;
0503     case msosptCan:
0504         processCan(o, out);
0505         break;
0506     case msosptDonut:
0507         processDonut(o, out);
0508         break;
0509     //
0510     // TODO: msosptTextSimple, msosptTextOctagon, msosptTextHexagon,
0511     // msosptTextCurve, msosptTextWave, msosptTextRing, msosptTextOnCurve,
0512     // msosptTextOnRing
0513     //
0514     case msosptStraightConnector1:
0515         processConnector(o, out, &ODrawToOdf::drawStraightConnector1);
0516         break;
0517     case msosptBentConnector2:
0518         processConnector(o, out, &ODrawToOdf::drawPathBentConnector2);
0519         break;
0520     case msosptBentConnector3:
0521         processConnector(o, out, &ODrawToOdf::drawPathBentConnector3);
0522         break;
0523     case msosptBentConnector4:
0524         processConnector(o, out, &ODrawToOdf::drawPathBentConnector4);
0525         break;
0526     case msosptBentConnector5:
0527         processConnector(o, out, &ODrawToOdf::drawPathBentConnector5);
0528         break;
0529     case msosptCurvedConnector2:
0530         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector2);
0531         break;
0532     case msosptCurvedConnector3:
0533         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector3);
0534         break;
0535     case msosptCurvedConnector4:
0536         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector4);
0537         break;
0538     case msosptCurvedConnector5:
0539         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector5);
0540         break;
0541     case msosptCallout1:
0542         processCallout1(o, out);
0543         break;
0544     case msosptCallout2:
0545         processCallout2(o, out);
0546         break;
0547     case msosptCallout3:
0548         processCallout3(o, out);
0549         break;
0550     case msosptAccentCallout1:
0551         processAccentCallout1(o, out);
0552         break;
0553     case msosptAccentCallout2:
0554         processAccentCallout2(o, out);
0555         break;
0556     case msosptAccentCallout3:
0557         processAccentCallout3(o, out);
0558         break;
0559     case msosptBorderCallout1:
0560         processBorderCallout1(o, out);
0561         break;
0562     case msosptBorderCallout2:
0563         processBorderCallout2(o, out);
0564         break;
0565     case msosptBorderCallout3:
0566         processBorderCallout3(o, out);
0567         break;
0568     case msosptAccentBorderCallout1:
0569         processAccentBorderCallout1(o, out);
0570         break;
0571     case msosptAccentBorderCallout2:
0572         processAccentBorderCallout2(o, out);
0573         break;
0574     case msosptAccentBorderCallout3:
0575         processAccentBorderCallout3(o, out);
0576         break;
0577     case msosptRibbon:
0578         processRibbon(o, out);
0579         break;
0580     case msosptRibbon2:
0581         processRibbon2(o, out);
0582         break;
0583     case msosptChevron:
0584         processChevron(o, out);
0585         break;
0586     case msosptPentagon:
0587         processPentagon(o, out);
0588         break;
0589     case msosptNoSmoking:
0590         processNoSmoking(o, out);
0591         break;
0592     case msosptSeal8:
0593         processSeal8(o, out);
0594         break;
0595     case msosptSeal16:
0596         processSeal16(o, out);
0597         break;
0598     case msosptSeal32:
0599         processSeal32(o, out);
0600         break;
0601     case msosptWedgeRectCallout:
0602         processWedgeRectCallout(o, out);
0603         break;
0604     case msosptWedgeRRectCallout:
0605         processWedgeRRectCallout(o, out);
0606         break;
0607     case msosptWedgeEllipseCallout:
0608         processWedgeEllipseCallout(o, out);
0609         break;
0610     case msosptWave:
0611         processWave(o, out);
0612         break;
0613     case msosptFoldedCorner:
0614         processFoldedCorner(o, out);
0615         break;
0616     case msosptLeftArrow:
0617         processLeftArrow(o, out);
0618         break;
0619     case msosptDownArrow:
0620         processDownArrow(o, out);
0621         break;
0622     case msosptUpArrow:
0623         processUpArrow(o, out);
0624         break;
0625     case msosptLeftRightArrow:
0626         processLeftRightArrow(o, out);
0627         break;
0628     case msosptUpDownArrow:
0629         processUpDownArrow(o, out);
0630         break;
0631     case msosptIrregularSeal1:
0632         processIrregularSeal1(o, out);
0633         break;
0634     case msosptIrregularSeal2:
0635         processIrregularSeal2(o, out);
0636         break;
0637     case msosptLightningBolt:
0638         processLightningBolt(o, out);
0639         break;
0640     case msosptHeart:
0641         processHeart(o, out);
0642         break;
0643     case msosptPictureFrame:
0644         processPictureFrame(o, out);
0645         break;
0646     case msosptQuadArrow:
0647         processQuadArrow(o, out);
0648         break;
0649     case msosptLeftArrowCallout:
0650         processLeftArrowCallout(o, out);
0651         break;
0652     case msosptRightArrowCallout:
0653         processRightArrowCallout(o, out);
0654         break;
0655     case msosptUpArrowCallout:
0656         processUpArrowCallout(o, out);
0657         break;
0658     case msosptDownArrowCallout:
0659         processDownArrowCallout(o, out);
0660         break;
0661     case msosptLeftRightArrowCallout:
0662         processLeftRightArrowCallout(o, out);
0663         break;
0664     case msosptUpDownArrowCallout:
0665         processUpDownArrowCallout(o, out);
0666         break;
0667     case msosptQuadArrowCallout:
0668         processQuadArrowCallout(o, out);
0669         break;
0670     case msosptBevel:
0671         processBevel(o, out);
0672         break;
0673     case msosptLeftBracket:
0674         processLeftBracket(o, out);
0675         break;
0676     case msosptRightBracket:
0677         processRightBracket(o, out);
0678         break;
0679     case msosptLeftBrace:
0680         processLeftBrace(o, out);
0681         break;
0682     case msosptRightBrace:
0683         processRightBrace(o, out);
0684         break;
0685     case msosptLeftUpArrow:
0686         processLeftUpArrow(o, out);
0687         break;
0688     case msosptBentUpArrow:
0689         processBentUpArrow(o, out);
0690         break;
0691     case msosptBentArrow:
0692         processBentArrow(o, out);
0693         break;
0694     case msosptSeal24:
0695         processSeal24(o, out);
0696         break;
0697     case msosptStripedRightArrow:
0698         processStripedRightArrow(o, out);
0699         break;
0700     case msosptNotchedRightArrow:
0701         processNotchedRightArrow(o, out);
0702         break;
0703     case msosptBlockArc:
0704         processBlockArc(o, out);
0705         break;
0706     case msosptSmileyFace:
0707         processSmileyFace(o, out);
0708         break;
0709     case msosptVerticalScroll:
0710         processVerticalScroll(o, out);
0711         break;
0712     case msosptHorizontalScroll:
0713         processHorizontalScroll(o, out);
0714         break;
0715     case msosptCircularArrow:
0716         processCircularArrow(o, out);
0717         break;
0718     case msosptNotchedCircularArrow:
0719         processNotchedCircularArrow(o, out);
0720         break;
0721     case msosptUturnArrow:
0722         processUturnArrow(o, out);
0723         break;
0724     case msosptCurvedRightArrow:
0725         processCurvedRightArrow(o, out);
0726         break;
0727     case msosptCurvedLeftArrow:
0728         processCurvedLeftArrow(o, out);
0729         break;
0730     case msosptCurvedUpArrow:
0731         processCurvedUpArrow(o, out);
0732         break;
0733     case msosptCurvedDownArrow:
0734         processCurvedDownArrow(o, out);
0735         break;
0736     case msosptCloudCallout:
0737         processCloudCallout(o, out);
0738         break;
0739     case msosptEllipseRibbon:
0740         processEllipseRibbon(o, out);
0741         break;
0742     case msosptEllipseRibbon2:
0743         processEllipseRibbon2(o, out);
0744         break;
0745     case msosptFlowChartProcess:
0746         processFlowChartProcess(o, out);
0747         break;
0748     case msosptFlowChartDecision:
0749         processFlowChartDecision(o, out);
0750         break;
0751     case msosptFlowChartInputOutput:
0752         processFlowChartInputOutput(o, out);
0753         break;
0754     case msosptFlowChartPredefinedProcess:
0755         processFlowChartPredefinedProcess(o, out);
0756         break;
0757     case msosptFlowChartInternalStorage:
0758         processFlowChartInternalStorage(o, out);
0759         break;
0760     case msosptFlowChartDocument:
0761         processFlowChartDocument(o, out);
0762         break;
0763     case msosptFlowChartMultidocument:
0764         processFlowChartMultidocument(o, out);
0765         break;
0766     case msosptFlowChartTerminator:
0767         processFlowChartTerminator(o, out);
0768         break;
0769     case msosptFlowChartPreparation:
0770         processFlowChartPreparation(o, out);
0771         break;
0772     case msosptFlowChartManualInput:
0773         processFlowChartManualInput(o, out);
0774         break;
0775     case msosptFlowChartManualOperation:
0776         processFlowChartManualOperation(o, out);
0777         break;
0778     case msosptFlowChartConnector:
0779         processFlowChartConnector(o, out);
0780         break;
0781     case msosptFlowChartPunchedCard:
0782         processFlowChartPunchedCard(o, out);
0783         break;
0784     case msosptFlowChartPunchedTape:
0785         processFlowChartPunchedTape(o, out);
0786         break;
0787     case msosptFlowChartSummingJunction:
0788         processFlowChartSummingJunction(o, out);
0789         break;
0790     case msosptFlowChartOr:
0791         processFlowChartOr(o, out);
0792         break;
0793     case msosptFlowChartCollate:
0794         processFlowChartCollate(o, out);
0795         break;
0796     case msosptFlowChartSort:
0797         processFlowChartSort(o, out);
0798         break;
0799     case msosptFlowChartExtract:
0800         processFlowChartExtract(o, out);
0801         break;
0802     case msosptFlowChartMerge:
0803         processFlowChartMerge(o, out);
0804         break;
0805     //
0806     // TODO: msosptFlowChartOfflineStorage
0807     //
0808     case msosptFlowChartOnlineStorage:
0809         processFlowChartOnlineStorage(o, out);
0810         break;
0811     case msosptFlowChartMagneticTape:
0812         processFlowChartMagneticTape(o, out);
0813         break;
0814     case msosptFlowChartMagneticDisk:
0815         processFlowChartMagneticDisk(o, out);
0816         break;
0817     case msosptFlowChartMagneticDrum:
0818         processFlowChartMagneticDrum(o, out);
0819         break;
0820     case msosptFlowChartDisplay:
0821         processFlowChartDisplay(o, out);
0822         break;
0823     case msosptFlowChartDelay:
0824         processFlowChartDelay(o, out);
0825         break;
0826     //
0827     // TODO: msosptTextPlainText, msosptTextStop, msosptTextTriangle,
0828     // msosptTextTriangleInverted, msosptTextChevron,
0829     // msosptTextChevronInverted, msosptTextRingInside, msosptTextRingOutside,
0830     // msosptTextArchUpCurve, msosptTextArchDownCurve, msosptTextCircleCurve,
0831     // msosptTextButtonCurve, msosptTextArchUpPour, msosptTextArchDownPour,
0832     // msosptTextCirclePour, msosptTextButtonPour, msosptTextCurveUp,
0833     // msosptTextCurveDown, msosptTextCascadeUp, msosptTextCascadeDown,
0834     // msosptTextWave1, msosptTextWave2, msosptTextWave3, msosptTextWave4,
0835     // msosptTextInflate, msosptTextDeflate, msosptTextInflateBottom,
0836     // msosptTextDeflateBottom, msosptTextInflateTop, msosptTextDeflateTop,
0837     // msosptTextDeflateInflate, msosptTextDeflateInflateDeflate,
0838     // msosptTextFadeRight, msosptTextFadeLeft, msosptTextFadeUp,
0839     // msosptTextFadeDown, msosptTextSlantUp, msosptTextSlantDown,
0840     // msosptTextCanUp, msosptTextCanDown
0841     //
0842     case msosptFlowChartAlternateProcess:
0843         processFlowChartAlternateProcess(o, out);
0844         break;
0845     case msosptFlowChartOffpageConnector:
0846         processFlowChartOffpageConnector(o, out);
0847         break;
0848     case msosptCallout90:
0849         processCallout90(o, out);
0850         break;
0851     case msosptAccentCallout90:
0852         processAccentCallout90(o, out);
0853         break;
0854     case msosptBorderCallout90:
0855         processBorderCallout90(o, out);
0856         break;
0857     case msosptAccentBorderCallout90:
0858         processAccentBorderCallout90(o, out);
0859         break;
0860     case msosptLeftRightUpArrow:
0861         processLeftRightUpArrow(o, out);
0862         break;
0863     case msosptSun:
0864         processSun(o, out);
0865         break;
0866     case msosptMoon:
0867         processMoon(o, out);
0868         break;
0869     case msosptBracketPair:
0870         processBracketPair(o, out);
0871         break;
0872     case msosptBracePair:
0873         processBracePair(o, out);
0874         break;
0875     case msosptSeal4:
0876         processSeal4(o, out);
0877         break;
0878     case msosptDoubleWave:
0879         processDoubleWave(o, out);
0880         break;
0881     case msosptActionButtonBlank:
0882         processActionButtonBlank(o, out);
0883         break;
0884     case msosptActionButtonHome:
0885         processActionButtonHome(o, out);
0886         break;
0887     case msosptActionButtonHelp:
0888         processActionButtonHelp(o, out);
0889         break;
0890     case msosptActionButtonInformation:
0891         processActionButtonInformation(o, out);
0892         break;
0893     case msosptActionButtonForwardNext:
0894         processActionButtonForwardNext(o, out);
0895         break;
0896     case msosptActionButtonBackPrevious:
0897         processActionButtonBackPrevious(o, out);
0898         break;
0899     case msosptActionButtonEnd:
0900         processActionButtonEnd(o, out);
0901         break;
0902     case msosptActionButtonBeginning:
0903         processActionButtonBeginning(o, out);
0904         break;
0905     case msosptActionButtonReturn:
0906         processActionButtonReturn(o, out);
0907         break;
0908     case msosptActionButtonDocument:
0909         processActionButtonDocument(o, out);
0910         break;
0911     case msosptActionButtonSound:
0912         processActionButtonSound(o, out);
0913         break;
0914     case msosptActionButtonMovie:
0915         processActionButtonMovie(o, out);
0916         break;
0917     case msosptHostControl:
0918         processPictureFrame(o, out);
0919         break;
0920     case msosptTextBox:
0921         processTextBox(o, out);
0922         break;
0923     default:
0924         qDebug() << "Cannot handle shape 0x" << hex << shapeType;
0925         break;
0926     }
0927 }
0928 
0929 void ODrawToOdf::processStyleAndText(const MSO::OfficeArtSpContainer& o,
0930                                      Writer& out)
0931 {
0932     processStyle(o, out);
0933     processText(o, out);
0934 }
0935 
0936 void ODrawToOdf::processStyle(const MSO::OfficeArtSpContainer& o,
0937                               Writer& out)
0938 {
0939     addGraphicStyleToDrawElement(out, o);
0940     set2dGeometry(o, out);
0941 }
0942 
0943 void ODrawToOdf::processText(const MSO::OfficeArtSpContainer& o,
0944                              Writer& out)
0945 {
0946     if (!client) {
0947         qWarning() << "Warning: There's no Client!";
0948         return;
0949     }
0950 
0951     if (o.clientData && client->onlyClientData(*o.clientData)) {
0952         client->processClientData(o.clientTextbox.data(), *o.clientData, out);
0953     } else if (o.clientTextbox) {
0954         client->processClientTextBox(*o.clientTextbox, o.clientData.data(), out);
0955     }
0956 }
0957 
0958 void ODrawToOdf::processModifiers(const MSO::OfficeArtSpContainer &o, Writer &out, const QList<int>& defaults)
0959 {
0960     const AdjustValue* val1 = get<AdjustValue>(o);
0961     if (!val1 && defaults.isEmpty()) return;
0962     const Adjust2Value* val2 = get<Adjust2Value>(o);
0963     const Adjust3Value* val3 = get<Adjust3Value>(o);
0964     const Adjust4Value* val4 = get<Adjust4Value>(o);
0965     const Adjust5Value* val5 = get<Adjust5Value>(o);
0966     const Adjust6Value* val6 = get<Adjust6Value>(o);
0967     const Adjust7Value* val7 = get<Adjust7Value>(o);
0968     const Adjust8Value* val8 = get<Adjust8Value>(o);
0969 
0970     QString modifiers = QString::number(val1 ? val1->adjustvalue : defaults[0]);
0971     if (val2 || defaults.size() > 1) {
0972         modifiers += QString(" %1").arg(val2 ? val2->adjust2value : defaults[1]);
0973         if (val3 || defaults.size() > 2) {
0974             modifiers += QString(" %1").arg(val3 ? val3->adjust3value : defaults[2]);
0975             if (val4 || defaults.size() > 3) {
0976                 modifiers += QString(" %1").arg(val4 ? val4->adjust4value : defaults[3]);
0977                 if (val5 || defaults.size() > 4) {
0978                     modifiers += QString(" %1").arg(val5 ? val5->adjust5value : defaults[4]);
0979                     if (val6 || defaults.size() > 5) {
0980                         modifiers += QString(" %1").arg(val6 ? val6->adjust6value : defaults[5]);
0981                         if (val7 || defaults.size() > 6) {
0982                             modifiers += QString(" %1").arg(val7 ? val7->adjust7value : defaults[6]);
0983                             if (val8 || defaults.size() > 7) {
0984                                 modifiers += QString(" %1").arg(val8 ? val8->adjust8value : defaults[7]);
0985                             }
0986                         }
0987                     }
0988                 }
0989             }
0990         }
0991     }
0992 
0993     out.xml.addAttribute("draw:modifiers", modifiers);
0994 }
0995 
0996 // Position the shape into the slide or into a group shape
0997 void ODrawToOdf::set2dGeometry(const OfficeArtSpContainer& o, Writer& out)
0998 {
0999     const OfficeArtDggContainer* dgg = 0;
1000     const OfficeArtSpContainer* master = 0;
1001     const DrawStyle ds(dgg, master, &o);
1002     const qreal rotation = toQReal(ds.rotation());
1003 
1004     //transform the rectangle into the coordinate system of the group shape
1005     QRectF rect = getRect(o);
1006     QRectF trect (out.hOffset(rect.x()), out.vOffset(rect.y()),
1007                   out.hLength(rect.width()), out.vLength(rect.height()));
1008 
1009     //draw:caption-id
1010     //draw:class-names
1011     //draw:data
1012     //draw:engine
1013     //draw:layer
1014     out.xml.addAttribute("draw:layer", "layout");
1015     //draw:name
1016     //draw:style-name
1017     //draw:text-style-name
1018     //draw:transform
1019     if (rotation) {
1020 
1021         const quint16 shapeType = o.shapeProp.rh.recInstance;
1022         const quint16 nrotation = normalizeRotation(rotation);
1023         const qreal angle = (nrotation / (qreal)180) * M_PI;
1024 
1025         trect = processRect(shapeType, rotation, trect);
1026 
1027         static const QString transform_str("translate(%1 %2) rotate(%3) translate(%4 %5)");
1028         const QPointF center = trect.center();
1029         const qreal height = trect.height();
1030         const qreal width = trect.width();
1031 
1032         out.xml.addAttribute("draw:transform",
1033                              transform_str.arg(client->formatPos(-width/2)).arg(client->formatPos(-height/2)).arg(-angle).arg(client->formatPos(center.x())).arg(client->formatPos(center.y())));
1034     }
1035     //svg:x
1036     //svg:y
1037     else {
1038         out.xml.addAttribute("svg:x", client->formatPos(trect.x()));
1039         out.xml.addAttribute("svg:y", client->formatPos(trect.y()));
1040     }
1041     //NOTE: z-index is set in ODrawToOdf::Client::addTextStyles
1042     //draw:z-index
1043     //presentation:class-names
1044     //presentation:style-name
1045     //svg:height
1046     out.xml.addAttribute("svg:height", client->formatPos(trect.height()));
1047     //svg:width
1048     out.xml.addAttribute("svg:width", client->formatPos(trect.width()));
1049     //table:end-cell-address
1050     //table:end-x
1051     //table:end-y
1052     //table:table-background
1053     //text:anchor-page-number
1054     //text:anchor-type
1055     //xml:id
1056 }
1057 
1058 void ODrawToOdf::setEnhancedGeometry(const MSO::OfficeArtSpContainer& o, Writer& out)
1059 {
1060     const OfficeArtDggContainer* drawingGroup = 0;
1061     const OfficeArtSpContainer* master = 0;
1062     const DrawStyle ds(drawingGroup, master, &o);
1063 
1064     IMsoArray _v = ds.pVertices_complex();
1065     IMsoArray segmentInfo = ds.pSegmentInfo_complex();
1066 
1067     if (!_v.data.isEmpty() && !segmentInfo.data.isEmpty()) {
1068 
1069         QVector<QPoint> verticesPoints;
1070 
1071         //_v.data is an array of POINTs, MS-ODRAW, page 89
1072         QByteArray xArray(sizeof(int), 0), yArray(sizeof(int), 0);
1073         int step = _v.cbElem;
1074         if (step == 0xfff0) {
1075             step = 4;
1076         }
1077 
1078         int maxX = 0, minX = INT_MAX, maxY = 0, minY = INT_MAX;
1079         int x,y;
1080 
1081         //get vertice points
1082         for (int i = 0, offset = 0; i < _v.nElems; i++, offset += step) {
1083             // x coordinate of this point
1084             xArray.replace(0, step/2, _v.data.mid(offset, step/2));
1085             x = *(int*) xArray.data();
1086 
1087             // y coordinate of this point
1088             yArray.replace(0, step/2, _v.data.mid(offset + step/2, step/2));
1089             y = *(int*) yArray.data();
1090 
1091             verticesPoints.append(QPoint(x, y));
1092 
1093             // find maximum and minimum coordinates
1094             if (maxY < y) {
1095                 maxY = y;
1096             }
1097             if (minY > y) {
1098                 minY = y ;
1099             }
1100             if (maxX < x) {
1101                 maxX = x;
1102             }
1103             if (minX > x) {
1104                 minX = x;
1105             }
1106         }
1107 
1108         //TODO: geoLeft, geoTop, geoRight, geoBottom
1109         QString viewBox = QString::number(minX) + ' ' + QString::number(minY) + ' ' +
1110                           QString::number(maxX) + ' ' + QString::number(maxY);
1111 
1112         // combine segmentationInfoData and verticePoints into enhanced-path string
1113         QString enhancedPath;
1114         ushort msopathtype;
1115         bool nOffRange = false;
1116 
1117         for (int i = 0, n = 0; ((i < segmentInfo.nElems) && !nOffRange); i++) {
1118 
1119             msopathtype = (((*(ushort *)(segmentInfo.data.data() + i * 2)) >> 13) & 0x7);
1120 
1121             switch (msopathtype) {
1122             case msopathLineTo:
1123             {
1124                 if (n >= verticesPoints.size()) {
1125                     qDebug() << "EnhancedGeometry: index into verticesPoints out of range!";
1126                     nOffRange = true;
1127                     break;
1128                 }
1129                 enhancedPath = enhancedPath + "L " + QString::number(verticesPoints[n].x()) + ' ' +
1130                                QString::number(verticesPoints[n].y()) + ' ';
1131                 n++;
1132                 break;
1133             }
1134             case msopathCurveTo:
1135             {
1136                 if (n + 2 >= verticesPoints.size()) {
1137                     qDebug() << "EnhancedGeometry: index into verticesPoints out of range!";
1138                     nOffRange = true;
1139                     break;
1140                 }
1141                 QPoint pt1 = verticesPoints.at(n);
1142                 QPoint pt2 = verticesPoints.at(n + 1);
1143                 QPoint pt3 = verticesPoints.at(n + 2);
1144 
1145                 enhancedPath = enhancedPath + "C " +
1146                         QString::number(pt1.x()) + ' ' +
1147                         QString::number(pt1.y()) + ' ' +
1148                         QString::number(pt2.x()) + ' ' +
1149                         QString::number(pt2.y()) + ' ' +
1150                         QString::number(pt3.x()) + ' ' +
1151                         QString::number(pt3.y()) + ' ';
1152                 n = n + 3;
1153                 break;
1154             }
1155             case msopathMoveTo:
1156             {
1157                 if (n >= verticesPoints.size()) {
1158                     qDebug() << "EnhancedGeometry: index into verticesPoints out of range!";
1159                     nOffRange = true;
1160                     break;
1161                 }
1162                 enhancedPath = enhancedPath + "M " + QString::number(verticesPoints[n].x()) + ' ' +
1163                                QString::number(verticesPoints[n].y()) + ' ';
1164                 n++;
1165                 break;
1166             }
1167             case msopathClose:
1168                 enhancedPath = enhancedPath + "Z ";
1169                 break;
1170             case msopathEnd:
1171                 enhancedPath = enhancedPath + "N ";
1172                 break;
1173             case msopathEscape:
1174             case msopathClientEscape:
1175                  break;
1176             }
1177         }
1178         //dr3d:projection
1179         //dr3d:shade-mode
1180         //draw:concentric-gradient-fill-allowed
1181         //draw:enhanced-path
1182         out.xml.addAttribute("draw:enhanced-path", enhancedPath);
1183         //draw:extrusion
1184         //draw:extrusion-allowed
1185         //draw:extrusion-brightness
1186         //draw:extrusion-color
1187         //draw:extrusion-depth
1188         //draw:extrusion-diffusion
1189         //draw:extrusion-first-light-direction
1190         //draw:extrusion-first-light-harsh
1191         //draw:extrusion-first-light-level
1192         //draw:extrusion-light-face
1193         //draw:extrusion-metal
1194         //draw:extrusion-number-of-line-segments
1195         //draw:extrusion-origin
1196         //draw:extrusion-rotation-angle
1197         //draw:extrusion-rotation-center
1198         //draw:extrusion-second-light-direction
1199         //draw:extrusion-second-light-harsh
1200         //draw:extrusion-second-light-level
1201         //draw:extrusion-shininess
1202         //draw:extrusion-skew
1203         //draw:extrusion-specularity
1204         //draw:extrusion-viewpoint
1205         //draw:glue-point-leaving-directions
1206         //draw:glue-points
1207         //draw:glue-point-type
1208         //draw:mirror-horizontal
1209         if (o.shapeProp.fFlipH) {
1210             out.xml.addAttribute("draw:mirror-horizontal", "true");
1211         }
1212         //draw:mirror-vertical
1213         if (o.shapeProp.fFlipV) {
1214             out.xml.addAttribute("draw:mirror-vertical", "true");
1215         }
1216         //draw:modifiers
1217         //draw:path-stretchpoint-x
1218         //draw:path-stretchpoint-y
1219         //draw:text-areas
1220         //draw:text-path
1221         //draw:text-path-allowed
1222         //draw:text-path-mode
1223         //draw:text-path-same-letter-heights
1224         //draw:text-path-scale
1225         //draw:text-rotate-angle
1226         //draw:type
1227         out.xml.addAttribute("draw:type", "non-primitive");
1228         //svg:viewBox
1229         out.xml.addAttribute("svg:viewBox", viewBox);
1230     }
1231 }
1232 
1233 QString ODrawToOdf::path2svg(const QPainterPath &path)
1234 {
1235     QString d;
1236 
1237     int count = path.elementCount();
1238     for (int i = 0; i < count; i++) {
1239 
1240         QPainterPath::Element e = path.elementAt(i);
1241         switch(e.type) {
1242         case QPainterPath::MoveToElement:
1243             d.append(QString("M %1 %2").arg(e.x).arg(e.y));
1244             break;
1245         case QPainterPath::LineToElement:
1246             d.append(QString("L %1 %2").arg(e.x).arg(e.y));
1247             break;
1248         case QPainterPath::CurveToElement:
1249             d.append(QString("C %1 %2").arg(e.x).arg(e.y));
1250             break;
1251         case QPainterPath::CurveToDataElement:
1252             d.append(QString(" %1 %2").arg(e.x).arg(e.y));
1253             break;
1254         default:
1255             qWarning() << "This element unhandled: " << e.type;
1256         }
1257     }
1258     return d;
1259 }
1260 
1261 void ODrawToOdf::setShapeMirroring(const MSO::OfficeArtSpContainer& o, Writer& out)
1262 {
1263     if (o.shapeProp.fFlipV) {
1264         out.xml.addAttribute("draw:mirror-vertical", "true");
1265     }
1266     if (o.shapeProp.fFlipH) {
1267         out.xml.addAttribute("draw:mirror-horizontal", "true");
1268     }
1269 }