File indexing completed on 2024-12-08 12:55:57

0001 /* This file is part of the KDE project
0002    Copyright (C) 2010 KO GmbH <jos.van.den.oever@kogmbh.com>
0003    Copyright (C) 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 /**
0022  * Note that the implementations here are supposed to be defined by DrawingML.
0023  * This details the geometry etc. in Appendix D, in the file
0024  * presetShapeDefinitions.xml.
0025  *
0026  * @note Of the {Bent,Curved}Connector[2345] shapes, the MS Office only seems to
0027  * support the [23] variants. The remainder have been coded in a manner
0028  * consistent with the [23] variants, but have not been tested.
0029  */
0030 
0031 #include "ODrawToOdf.h"
0032 #include "drawstyle.h"
0033 #include "msodraw.h"
0034 #include "generated/leinputstream.h"
0035 #include "writeodf/writeodfdraw.h"
0036 
0037 #include <QDebug>
0038 #include <QPainterPath>
0039 #include <QTransform>
0040 #include <QBuffer>
0041 #include <QUrl>
0042 
0043 #include <cmath>
0044 
0045 
0046 using namespace MSO;
0047 using namespace writeodf;
0048 
0049 qint16
0050 ODrawToOdf::normalizeRotation(qreal rotation)
0051 {
0052     qint16 angle = ((qint16)rotation) % 360;
0053     if (angle < 0) {
0054         angle = angle + 360;
0055     }
0056     return angle;
0057 }
0058 
0059 /**
0060  * Return the bounding rectangle for this object.
0061  **/
0062 QRectF
0063 ODrawToOdf::getRect(const OfficeArtSpContainer &o)
0064 {
0065     if (o.childAnchor) {
0066         const OfficeArtChildAnchor& r = *o.childAnchor;
0067         return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
0068     } else if (o.clientAnchor && client) {
0069         return client->getRect(*o.clientAnchor);
0070     } else if (o.shapeProp.fHaveAnchor && client) {
0071         return client->getReserveRect();
0072     } else {
0073         return QRectF();
0074     }
0075 }
0076 
0077 QRectF
0078 ODrawToOdf::processRect(const quint16 shapeType, const qreal rotation, QRectF &rect)
0079 {
0080     bool transform_anchor = false;
0081     qint16 nrotation = normalizeRotation(rotation);
0082 
0083     //TODO: Add other shapes here!
0084 
0085     switch (shapeType) {
0086     case msosptNotPrimitive:
0087         if ( ((nrotation >= 45) && (nrotation < 135)) ||
0088              ((nrotation >= 225) && (nrotation < 315)))
0089         {
0090             transform_anchor = true;
0091         }
0092         break;
0093     default:
0094         break;
0095     }
0096     if (transform_anchor) {
0097         QPointF center = rect.center();
0098         QTransform transform;
0099         transform.rotate(90);
0100         rect = transform.mapRect(rect.translated(-center)).translated(center);
0101     }
0102     return rect;
0103 }
0104 
0105 void ODrawToOdf::processRectangle(const OfficeArtSpContainer& o, Writer& out)
0106 {
0107     // TODO: Use client->isPlaceholder - might require an update of the
0108     // placeholderAllowed function in the PPT filter.  Trying to save as many
0109     // shapes into draw:text-box at the moment, because vertical alignment in
0110     // draw:custom-shape does not work properly (bug 288047).
0111     if (o.clientData && client->processRectangleAsTextBox(*o.clientData)) {
0112         processTextBox(o, out);
0113     } else {
0114         const DrawStyle ds(0, 0, &o);
0115         if (ds.pib()) {
0116             // see bug https://bugs.kde.org/show_bug.cgi?id=285577
0117             processPictureFrame(o, out);
0118         } else {
0119             draw_custom_shape rect(&out.xml);
0120             processStyleAndText(o, out);
0121             draw_enhanced_geometry eg(rect.add_draw_enhanced_geometry());
0122             eg.set_svg_viewBox("0 0 21600 21600");
0123             eg.set_draw_enhanced_path("M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N");
0124             eg.set_draw_type("rectangle");
0125             setShapeMirroring(o, out);
0126         }
0127     }
0128 }
0129 
0130 void ODrawToOdf::processTextBox(const OfficeArtSpContainer& o, Writer& out)
0131 {
0132     draw_frame frame(&out.xml);
0133     processStyle(o, out);
0134     draw_text_box text(frame.add_draw_text_box());
0135     processText(o, out);
0136 }
0137 
0138 void ODrawToOdf::processLine(const OfficeArtSpContainer& o, Writer& out)
0139 {
0140     const QRectF rect = getRect(o);
0141     qreal x1 = rect.x();
0142     qreal y1 = rect.y();
0143     qreal x2 = rect.x() + rect.width();
0144     qreal y2 = rect.y() + rect.height();
0145 
0146     // shape mirroring
0147     if (o.shapeProp.fFlipV) {
0148         qSwap(y1, y2);
0149     }
0150     if (o.shapeProp.fFlipH) {
0151         qSwap(x1, x2);
0152     }
0153 
0154     draw_line line(&out.xml,
0155                    client->formatPos(out.hOffset(x1)),
0156                    client->formatPos(out.hOffset(x2)),
0157                    client->formatPos(out.vOffset(y1)),
0158                    client->formatPos(out.vOffset(y2)));
0159     addGraphicStyleToDrawElement(out, o);
0160     line.set_draw_layer("layout");
0161     processText(o, out);
0162 }
0163 
0164 void ODrawToOdf::drawStraightConnector1(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0165 {
0166     out.xml.addAttribute("draw:type", "line");
0167     shapePath.moveTo(l, t);
0168     shapePath.lineTo(r, b);
0169 }
0170 
0171 void ODrawToOdf::drawPathBentConnector2(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0172 {
0173     Q_UNUSED(out);
0174     shapePath.moveTo(l, t);
0175     shapePath.lineTo(r, t);
0176     shapePath.lineTo(r, b);
0177 }
0178 
0179 void ODrawToOdf::drawPathBentConnector3(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 adj1 = 50000;
0184     qreal x1 = w * adj1 / 100000;
0185 
0186     shapePath.moveTo(l, t);
0187     shapePath.lineTo(l + x1, t);
0188     shapePath.lineTo(l + x1, b);
0189     shapePath.lineTo(r, b);
0190 }
0191 
0192 void ODrawToOdf::drawPathBentConnector4(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0193 {
0194     Q_UNUSED(out);
0195     qreal w = qAbs(r - l);
0196     qreal h = qAbs(b - t);
0197     qreal adj1 = 50000;
0198     qreal adj2 = 50000;
0199     qreal x1 = w * adj1 / 100000;
0200 //    qreal x2 = x1 + r / 2;
0201     qreal y2 = h * adj2 / 100000;
0202 //    qreal y1 = t + y2 / 2;
0203 
0204     shapePath.moveTo(l, t);
0205     shapePath.lineTo(l + x1, t);
0206     shapePath.lineTo(l + x1, y2);
0207     shapePath.lineTo(r, y2);
0208     shapePath.lineTo(r, b);
0209 }
0210 
0211 void ODrawToOdf::drawPathBentConnector5(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0212 {
0213     Q_UNUSED(out);
0214     qreal w = qAbs(r - l);
0215     qreal h = qAbs(b - t);
0216     qreal adj1 = 50000;
0217     qreal adj2 = 50000;
0218     qreal adj3 = 50000;
0219     qreal x1 = w * adj1 / 100000;
0220     qreal x3 = w * adj3 / 100000;
0221 //    qreal x2 = x1 + x3 / 2;
0222     qreal y2 = h * adj2 / 100000;
0223 //    qreal y1 = t + y2 / 2;
0224 //    qreal y3 = b + y2 / 2;
0225 
0226     shapePath.moveTo(l, t);
0227     shapePath.lineTo(l + x1, t);
0228     shapePath.lineTo(l + x1, y2);
0229     shapePath.lineTo(l + x3, y2);
0230     shapePath.lineTo(l + x3, b);
0231     shapePath.lineTo(r, b);
0232 }
0233 
0234 void ODrawToOdf::drawPathCurvedConnector2(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0235 {
0236     Q_UNUSED(out);
0237     qreal w = qAbs(r - l);
0238     qreal h = qAbs(b - t);
0239 
0240     shapePath.moveTo(l, t);
0241     shapePath.cubicTo(l + w / 2, t, r, h / 2, r, b);
0242 }
0243 
0244 void ODrawToOdf::drawPathCurvedConnector3(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0245 {
0246     Q_UNUSED(out);
0247     qreal w = qAbs(r - l);
0248     qreal h = qAbs(b - t);
0249     qreal adj1 = 50000;
0250     qreal x2 = w * adj1 / 100000;
0251     qreal x1 = l + x2 /*/ 2*/;
0252 //    qreal x3 = r + x2 / 2;
0253 //    qreal y3 = h * 3 / 4;
0254 
0255     shapePath.moveTo(l, t);
0256     shapePath.cubicTo(x1, t, x1, t + h / 2, l + x2, t + h / 2);
0257     shapePath.cubicTo(l + x2, t + h / 2, l + x2, b, r, b);
0258 }
0259 
0260 void ODrawToOdf::drawPathCurvedConnector4(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0261 {
0262     Q_UNUSED(out);
0263     qreal w = qAbs(r - l);
0264     qreal h = qAbs(b - t);
0265     qreal adj1 = 50000;
0266     qreal adj2 = 50000;
0267     qreal x2 = w * adj1 / 100000;
0268     qreal x1 = l + x2 / 2;
0269     qreal x3 = r + x2 / 2;
0270     qreal x4 = x2 + x3 / 2;
0271     qreal x5 = x3 + r / 2;
0272     qreal y4 = h * adj2 / 100000;
0273     qreal y1 = t + y4 / 2;
0274     qreal y2 = t + y1 / 2;
0275     qreal y3 = y1 + y4 / 2;
0276     qreal y5 = b + y4 / 2;
0277 
0278     shapePath.moveTo(l, t);
0279     shapePath.cubicTo(x1, t, l + x2, y2, l + x2, y1);
0280     shapePath.cubicTo(l + x2, y3, x4, y4, x3, y4);
0281     shapePath.cubicTo(x5, y4, r, y5, r, b);
0282 }
0283 
0284 void ODrawToOdf::drawPathCurvedConnector5(qreal l, qreal t, qreal r, qreal b, Writer& out, QPainterPath &shapePath) const
0285 {
0286     Q_UNUSED(out);
0287     qreal w = qAbs(r - l);
0288     qreal h = qAbs(b - t);
0289     qreal adj1 = 50000;
0290     qreal adj2 = 50000;
0291     qreal adj3 = 50000;
0292     qreal x3 = w * adj1 / 100000;
0293     qreal x6 = w * adj3 / 100000;
0294     qreal x1 = x3 + x6 / 2;
0295     qreal x2 = l + x3 / 2;
0296     qreal x4 = x3 + x1 / 2;
0297     qreal x5 = x6 + x1 / 2;
0298     qreal x7 = x6 + r / 2;
0299     qreal y4 = h * adj2 / 100000;
0300     qreal y1 = t + y4 / 2;
0301     qreal y2 = t + y1 / 2;
0302     qreal y3 = y1 + y4 / 2;
0303     qreal y5 = b + y4 / 2;
0304     qreal y6 = y5 + y4 / 2;
0305     qreal y7 = y5 + b / 2;
0306 
0307     shapePath.moveTo(l, t);
0308     shapePath.cubicTo(x2, t, l + x3, y2, l + x3, y1);
0309     shapePath.cubicTo(x3, y3, x4, y4, x1, y4);
0310     shapePath.cubicTo(x5, y4, l + x6, y6, l + x6, y5);
0311     shapePath.cubicTo(l + x6, y7, x7, b, r, b);
0312 }
0313 
0314 /**
0315  * Common handler for Connectors.
0316  */
0317 void ODrawToOdf::processConnector(const OfficeArtSpContainer& o, Writer& out, PathArtist drawPath)
0318 {
0319     const OfficeArtDggContainer * drawingGroup = 0;
0320     if (client) {
0321         drawingGroup = client->getOfficeArtDggContainer();
0322     }
0323 
0324     const OfficeArtSpContainer* master = 0;
0325     const DrawStyle ds(drawingGroup, master, &o);
0326     qreal rotation = toQReal( ds.rotation() );
0327 
0328     const QRectF rect = getRect(o);
0329     qreal x1 = rect.x();
0330     qreal y1 = rect.y();
0331     qreal x2 = rect.x() + rect.width();
0332     qreal y2 = rect.y() + rect.height();
0333 
0334     QRectF shapeRect = rect;
0335 
0336     qreal sx1 = x1;
0337     qreal sy1 = y1;
0338     qreal sx2 = x2;
0339     qreal sy2 = y2;
0340 
0341     if (rotation != 0.0) {
0342         QTransform m;
0343         m.rotate( -rotation );
0344         shapeRect = m.mapRect(rect.translated(-rect.center())).translated(rect.center());
0345 
0346         sx1 = shapeRect.topLeft().x();
0347         sy1 = shapeRect.topLeft().y();
0348         sx2 = shapeRect.bottomRight().x();
0349         sy2 = shapeRect.bottomRight().y();
0350     }
0351 
0352     // Prepare to transform the path according the shape properties like flip
0353     // and rotation.
0354     QTransform m;
0355     m.reset();
0356     m.translate( -shapeRect.center().x(), -shapeRect.center().y() );
0357 
0358     // Mirroring
0359     if (o.shapeProp.fFlipH){
0360         m.scale(-1,1);
0361     }
0362 
0363     if (o.shapeProp.fFlipV){
0364         m.scale(1,-1);
0365     }
0366 
0367     if (rotation != 0) {
0368         m.rotate(rotation);
0369     }
0370 
0371     m.translate( shapeRect.center().x(), shapeRect.center().y() );
0372 
0373     // the viewbox should be set, where is this done for draw:connector?
0374     out.xml.startElement("draw:connector");
0375     addGraphicStyleToDrawElement(out, o);
0376     out.xml.addAttribute("draw:layer", "layout");
0377 
0378     // Compute path and transform it.
0379     QPainterPath shapePath;
0380     (this->*drawPath)(sx1, sy1, sx2, sy2, out, shapePath);
0381 
0382     shapePath = m.map(shapePath);
0383 
0384     // translate the QPainterPath into svg:d attribute
0385     QString path = path2svg(shapePath);
0386 
0387     out.xml.addAttribute("svg:x1", client->formatPos(out.hOffset(x1)));
0388     out.xml.addAttribute("svg:y1", client->formatPos(out.vOffset(y1)));
0389     out.xml.addAttribute("svg:x2", client->formatPos(out.hOffset(x2)));
0390     out.xml.addAttribute("svg:y2", client->formatPos(out.vOffset(y2)));
0391     if (!path.isEmpty()) {
0392         out.xml.addAttribute("svg:d", path);
0393     }
0394 
0395     processText(o, out);
0396     out.xml.endElement();
0397 }
0398 
0399 void ODrawToOdf::processPictureFrame(const OfficeArtSpContainer& o, Writer& out)
0400 {
0401     DrawStyle ds(0, &o);
0402 
0403     // A value of 0x00000000 MUST be ignored.  [MS-ODRAW] — v20101219
0404     if (!ds.pib()) return;
0405 
0406     draw_frame frame(&out.xml);
0407     processStyle(o, out);
0408 
0409     //NOTE: OfficeArtClienData might contain additional information
0410     //about a shape.
0411 
0412     QString url;
0413     if (client) {
0414         url = client->getPicturePath(ds.pib());
0415     }
0416     // if the image cannot be found, just place an empty frame
0417     if (url.isEmpty()) {
0418         return;
0419     }
0420     draw_image image(frame.add_draw_image());
0421     image.set_xlink_href(QUrl(url));
0422     image.set_xlink_type("simple");
0423     image.set_xlink_show("embed");
0424     image.set_xlink_actuate("onLoad");
0425 }
0426 
0427 void ODrawToOdf::processNotPrimitive(const MSO::OfficeArtSpContainer& o, Writer& out)
0428 {
0429     draw_custom_shape shape(&out.xml);
0430     processStyleAndText(o, out);
0431     draw_enhanced_geometry eg(shape.add_draw_enhanced_geometry());
0432     setEnhancedGeometry(o, out);
0433 }
0434 
0435 
0436 void ODrawToOdf::processDrawingObject(const OfficeArtSpContainer& o, Writer& out)
0437 {
0438     if (!client) {
0439         qWarning() << "Warning: There's no Client!";
0440         return;
0441     }
0442 
0443     quint16 shapeType = o.shapeProp.rh.recInstance;
0444     client->m_currentShapeType = o.shapeProp.rh.recInstance;
0445 
0446     switch (shapeType) {
0447     case msosptNotPrimitive:
0448         processNotPrimitive(o, out);
0449         break;
0450     case msosptRectangle:
0451         processRectangle(o, out);
0452         break;
0453     case msosptRoundRectangle:
0454         processRoundRectangle(o, out);
0455         break;
0456     case msosptEllipse:
0457         // TODO: Something has to be done here (LukasT).  LukasT:
0458         // "Great comment", can you provide more details? :)
0459         processEllipse(o, out);
0460         break;
0461     case msosptDiamond:
0462         processDiamond(o, out);
0463         break;
0464     case msosptIsocelesTriangle:
0465         processIsocelesTriangle(o, out);
0466         break;
0467     case msosptRightTriangle:
0468         processRightTriangle(o, out);
0469         break;
0470     case msosptParallelogram:
0471         processParallelogram(o, out);
0472         break;
0473     case msosptTrapezoid:
0474         processTrapezoid(o, out);
0475         break;
0476     case msosptHexagon:
0477         processHexagon(o, out);
0478         break;
0479     case msosptOctagon:
0480         processOctagon(o, out);
0481         break;
0482     case msosptPlus:
0483         processPlus(o, out);
0484         break;
0485     case msosptStar:
0486         processStar(o, out);
0487         break;
0488     case msosptArrow:
0489         processArrow(o, out);
0490         break;
0491     //
0492     // TODO: msosptThickArrow
0493     //
0494     case msosptHomePlate:
0495         processHomePlate(o, out);
0496         break;
0497     case msosptCube:
0498         processCube(o, out);
0499         break;
0500     //
0501     // TODO: msosptBaloon, msosptSeal
0502     //
0503 
0504     // NOTE: OpenOffice treats msosptNotchedCircularArrow as msosptArc.  The
0505     // msosptNotchedCircularArrow value SHOULD NOT be used according to the
0506     // MS-ODRAW spec.  However it occurs in many Word8 files.
0507     case msosptArc:
0508         processNotchedCircularArrow(o, out);
0509         break;
0510     case msosptLine:
0511         processLine(o, out);
0512         break;
0513     case msosptPlaque:
0514         processPlaque(o, out);
0515         break;
0516     case msosptCan:
0517         processCan(o, out);
0518         break;
0519     case msosptDonut:
0520         processDonut(o, out);
0521         break;
0522     //
0523     // TODO: msosptTextSimple, msosptTextOctagon, msosptTextHexagon,
0524     // msosptTextCurve, msosptTextWave, msosptTextRing, msosptTextOnCurve,
0525     // msosptTextOnRing
0526     //
0527     case msosptStraightConnector1:
0528         processConnector(o, out, &ODrawToOdf::drawStraightConnector1);
0529         break;
0530     case msosptBentConnector2:
0531         processConnector(o, out, &ODrawToOdf::drawPathBentConnector2);
0532         break;
0533     case msosptBentConnector3:
0534         processConnector(o, out, &ODrawToOdf::drawPathBentConnector3);
0535         break;
0536     case msosptBentConnector4:
0537         processConnector(o, out, &ODrawToOdf::drawPathBentConnector4);
0538         break;
0539     case msosptBentConnector5:
0540         processConnector(o, out, &ODrawToOdf::drawPathBentConnector5);
0541         break;
0542     case msosptCurvedConnector2:
0543         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector2);
0544         break;
0545     case msosptCurvedConnector3:
0546         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector3);
0547         break;
0548     case msosptCurvedConnector4:
0549         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector4);
0550         break;
0551     case msosptCurvedConnector5:
0552         processConnector(o, out, &ODrawToOdf::drawPathCurvedConnector5);
0553         break;
0554     case msosptCallout1:
0555         processCallout1(o, out);
0556         break;
0557     case msosptCallout2:
0558         processCallout2(o, out);
0559         break;
0560     case msosptCallout3:
0561         processCallout3(o, out);
0562         break;
0563     case msosptAccentCallout1:
0564         processAccentCallout1(o, out);
0565         break;
0566     case msosptAccentCallout2:
0567         processAccentCallout2(o, out);
0568         break;
0569     case msosptAccentCallout3:
0570         processAccentCallout3(o, out);
0571         break;
0572     case msosptBorderCallout1:
0573         processBorderCallout1(o, out);
0574         break;
0575     case msosptBorderCallout2:
0576         processBorderCallout2(o, out);
0577         break;
0578     case msosptBorderCallout3:
0579         processBorderCallout3(o, out);
0580         break;
0581     case msosptAccentBorderCallout1:
0582         processAccentBorderCallout1(o, out);
0583         break;
0584     case msosptAccentBorderCallout2:
0585         processAccentBorderCallout2(o, out);
0586         break;
0587     case msosptAccentBorderCallout3:
0588         processAccentBorderCallout3(o, out);
0589         break;
0590     case msosptRibbon:
0591         processRibbon(o, out);
0592         break;
0593     case msosptRibbon2:
0594         processRibbon2(o, out);
0595         break;
0596     case msosptChevron:
0597         processChevron(o, out);
0598         break;
0599     case msosptPentagon:
0600         processPentagon(o, out);
0601         break;
0602     case msosptNoSmoking:
0603         processNoSmoking(o, out);
0604         break;
0605     case msosptSeal8:
0606         processSeal8(o, out);
0607         break;
0608     case msosptSeal16:
0609         processSeal16(o, out);
0610         break;
0611     case msosptSeal32:
0612         processSeal32(o, out);
0613         break;
0614     case msosptWedgeRectCallout:
0615         processWedgeRectCallout(o, out);
0616         break;
0617     case msosptWedgeRRectCallout:
0618         processWedgeRRectCallout(o, out);
0619         break;
0620     case msosptWedgeEllipseCallout:
0621         processWedgeEllipseCallout(o, out);
0622         break;
0623     case msosptWave:
0624         processWave(o, out);
0625         break;
0626     case msosptFoldedCorner:
0627         processFoldedCorner(o, out);
0628         break;
0629     case msosptLeftArrow:
0630         processLeftArrow(o, out);
0631         break;
0632     case msosptDownArrow:
0633         processDownArrow(o, out);
0634         break;
0635     case msosptUpArrow:
0636         processUpArrow(o, out);
0637         break;
0638     case msosptLeftRightArrow:
0639         processLeftRightArrow(o, out);
0640         break;
0641     case msosptUpDownArrow:
0642         processUpDownArrow(o, out);
0643         break;
0644     case msosptIrregularSeal1:
0645         processIrregularSeal1(o, out);
0646         break;
0647     case msosptIrregularSeal2:
0648         processIrregularSeal2(o, out);
0649         break;
0650     case msosptLightningBolt:
0651         processLightningBolt(o, out);
0652         break;
0653     case msosptHeart:
0654         processHeart(o, out);
0655         break;
0656     case msosptPictureFrame:
0657         processPictureFrame(o, out);
0658         break;
0659     case msosptQuadArrow:
0660         processQuadArrow(o, out);
0661         break;
0662     case msosptLeftArrowCallout:
0663         processLeftArrowCallout(o, out);
0664         break;
0665     case msosptRightArrowCallout:
0666         processRightArrowCallout(o, out);
0667         break;
0668     case msosptUpArrowCallout:
0669         processUpArrowCallout(o, out);
0670         break;
0671     case msosptDownArrowCallout:
0672         processDownArrowCallout(o, out);
0673         break;
0674     case msosptLeftRightArrowCallout:
0675         processLeftRightArrowCallout(o, out);
0676         break;
0677     case msosptUpDownArrowCallout:
0678         processUpDownArrowCallout(o, out);
0679         break;
0680     case msosptQuadArrowCallout:
0681         processQuadArrowCallout(o, out);
0682         break;
0683     case msosptBevel:
0684         processBevel(o, out);
0685         break;
0686     case msosptLeftBracket:
0687         processLeftBracket(o, out);
0688         break;
0689     case msosptRightBracket:
0690         processRightBracket(o, out);
0691         break;
0692     case msosptLeftBrace:
0693         processLeftBrace(o, out);
0694         break;
0695     case msosptRightBrace:
0696         processRightBrace(o, out);
0697         break;
0698     case msosptLeftUpArrow:
0699         processLeftUpArrow(o, out);
0700         break;
0701     case msosptBentUpArrow:
0702         processBentUpArrow(o, out);
0703         break;
0704     case msosptBentArrow:
0705         processBentArrow(o, out);
0706         break;
0707     case msosptSeal24:
0708         processSeal24(o, out);
0709         break;
0710     case msosptStripedRightArrow:
0711         processStripedRightArrow(o, out);
0712         break;
0713     case msosptNotchedRightArrow:
0714         processNotchedRightArrow(o, out);
0715         break;
0716     case msosptBlockArc:
0717         processBlockArc(o, out);
0718         break;
0719     case msosptSmileyFace:
0720         processSmileyFace(o, out);
0721         break;
0722     case msosptVerticalScroll:
0723         processVerticalScroll(o, out);
0724         break;
0725     case msosptHorizontalScroll:
0726         processHorizontalScroll(o, out);
0727         break;
0728     case msosptCircularArrow:
0729         processCircularArrow(o, out);
0730         break;
0731     case msosptNotchedCircularArrow:
0732         processNotchedCircularArrow(o, out);
0733         break;
0734     case msosptUturnArrow:
0735         processUturnArrow(o, out);
0736         break;
0737     case msosptCurvedRightArrow:
0738         processCurvedRightArrow(o, out);
0739         break;
0740     case msosptCurvedLeftArrow:
0741         processCurvedLeftArrow(o, out);
0742         break;
0743     case msosptCurvedUpArrow:
0744         processCurvedUpArrow(o, out);
0745         break;
0746     case msosptCurvedDownArrow:
0747         processCurvedDownArrow(o, out);
0748         break;
0749     case msosptCloudCallout:
0750         processCloudCallout(o, out);
0751         break;
0752     case msosptEllipseRibbon:
0753         processEllipseRibbon(o, out);
0754         break;
0755     case msosptEllipseRibbon2:
0756         processEllipseRibbon2(o, out);
0757         break;
0758     case msosptFlowChartProcess:
0759         processFlowChartProcess(o, out);
0760         break;
0761     case msosptFlowChartDecision:
0762         processFlowChartDecision(o, out);
0763         break;
0764     case msosptFlowChartInputOutput:
0765         processFlowChartInputOutput(o, out);
0766         break;
0767     case msosptFlowChartPredefinedProcess:
0768         processFlowChartPredefinedProcess(o, out);
0769         break;
0770     case msosptFlowChartInternalStorage:
0771         processFlowChartInternalStorage(o, out);
0772         break;
0773     case msosptFlowChartDocument:
0774         processFlowChartDocument(o, out);
0775         break;
0776     case msosptFlowChartMultidocument:
0777         processFlowChartMultidocument(o, out);
0778         break;
0779     case msosptFlowChartTerminator:
0780         processFlowChartTerminator(o, out);
0781         break;
0782     case msosptFlowChartPreparation:
0783         processFlowChartPreparation(o, out);
0784         break;
0785     case msosptFlowChartManualInput:
0786         processFlowChartManualInput(o, out);
0787         break;
0788     case msosptFlowChartManualOperation:
0789         processFlowChartManualOperation(o, out);
0790         break;
0791     case msosptFlowChartConnector:
0792         processFlowChartConnector(o, out);
0793         break;
0794     case msosptFlowChartPunchedCard:
0795         processFlowChartPunchedCard(o, out);
0796         break;
0797     case msosptFlowChartPunchedTape:
0798         processFlowChartPunchedTape(o, out);
0799         break;
0800     case msosptFlowChartSummingJunction:
0801         processFlowChartSummingJunction(o, out);
0802         break;
0803     case msosptFlowChartOr:
0804         processFlowChartOr(o, out);
0805         break;
0806     case msosptFlowChartCollate:
0807         processFlowChartCollate(o, out);
0808         break;
0809     case msosptFlowChartSort:
0810         processFlowChartSort(o, out);
0811         break;
0812     case msosptFlowChartExtract:
0813         processFlowChartExtract(o, out);
0814         break;
0815     case msosptFlowChartMerge:
0816         processFlowChartMerge(o, out);
0817         break;
0818     //
0819     // TODO: msosptFlowChartOfflineStorage
0820     //
0821     case msosptFlowChartOnlineStorage:
0822         processFlowChartOnlineStorage(o, out);
0823         break;
0824     case msosptFlowChartMagneticTape:
0825         processFlowChartMagneticTape(o, out);
0826         break;
0827     case msosptFlowChartMagneticDisk:
0828         processFlowChartMagneticDisk(o, out);
0829         break;
0830     case msosptFlowChartMagneticDrum:
0831         processFlowChartMagneticDrum(o, out);
0832         break;
0833     case msosptFlowChartDisplay:
0834         processFlowChartDisplay(o, out);
0835         break;
0836     case msosptFlowChartDelay:
0837         processFlowChartDelay(o, out);
0838         break;
0839     //
0840     // TODO: msosptTextPlainText, msosptTextStop, msosptTextTriangle,
0841     // msosptTextTriangleInverted, msosptTextChevron,
0842     // msosptTextChevronInverted, msosptTextRingInside, msosptTextRingOutside,
0843     // msosptTextArchUpCurve, msosptTextArchDownCurve, msosptTextCircleCurve,
0844     // msosptTextButtonCurve, msosptTextArchUpPour, msosptTextArchDownPour,
0845     // msosptTextCirclePour, msosptTextButtonPour, msosptTextCurveUp,
0846     // msosptTextCurveDown, msosptTextCascadeUp, msosptTextCascadeDown,
0847     // msosptTextWave1, msosptTextWave2, msosptTextWave3, msosptTextWave4,
0848     // msosptTextInflate, msosptTextDeflate, msosptTextInflateBottom,
0849     // msosptTextDeflateBottom, msosptTextInflateTop, msosptTextDeflateTop,
0850     // msosptTextDeflateInflate, msosptTextDeflateInflateDeflate,
0851     // msosptTextFadeRight, msosptTextFadeLeft, msosptTextFadeUp,
0852     // msosptTextFadeDown, msosptTextSlantUp, msosptTextSlantDown,
0853     // msosptTextCanUp, msosptTextCanDown
0854     //
0855     case msosptFlowChartAlternateProcess:
0856         processFlowChartAlternateProcess(o, out);
0857         break;
0858     case msosptFlowChartOffpageConnector:
0859         processFlowChartOffpageConnector(o, out);
0860         break;
0861     case msosptCallout90:
0862         processCallout90(o, out);
0863         break;
0864     case msosptAccentCallout90:
0865         processAccentCallout90(o, out);
0866         break;
0867     case msosptBorderCallout90:
0868         processBorderCallout90(o, out);
0869         break;
0870     case msosptAccentBorderCallout90:
0871         processAccentBorderCallout90(o, out);
0872         break;
0873     case msosptLeftRightUpArrow:
0874         processLeftRightUpArrow(o, out);
0875         break;
0876     case msosptSun:
0877         processSun(o, out);
0878         break;
0879     case msosptMoon:
0880         processMoon(o, out);
0881         break;
0882     case msosptBracketPair:
0883         processBracketPair(o, out);
0884         break;
0885     case msosptBracePair:
0886         processBracePair(o, out);
0887         break;
0888     case msosptSeal4:
0889         processSeal4(o, out);
0890         break;
0891     case msosptDoubleWave:
0892         processDoubleWave(o, out);
0893         break;
0894     case msosptActionButtonBlank:
0895         processActionButtonBlank(o, out);
0896         break;
0897     case msosptActionButtonHome:
0898         processActionButtonHome(o, out);
0899         break;
0900     case msosptActionButtonHelp:
0901         processActionButtonHelp(o, out);
0902         break;
0903     case msosptActionButtonInformation:
0904         processActionButtonInformation(o, out);
0905         break;
0906     case msosptActionButtonForwardNext:
0907         processActionButtonForwardNext(o, out);
0908         break;
0909     case msosptActionButtonBackPrevious:
0910         processActionButtonBackPrevious(o, out);
0911         break;
0912     case msosptActionButtonEnd:
0913         processActionButtonEnd(o, out);
0914         break;
0915     case msosptActionButtonBeginning:
0916         processActionButtonBeginning(o, out);
0917         break;
0918     case msosptActionButtonReturn:
0919         processActionButtonReturn(o, out);
0920         break;
0921     case msosptActionButtonDocument:
0922         processActionButtonDocument(o, out);
0923         break;
0924     case msosptActionButtonSound:
0925         processActionButtonSound(o, out);
0926         break;
0927     case msosptActionButtonMovie:
0928         processActionButtonMovie(o, out);
0929         break;
0930     case msosptHostControl:
0931         processPictureFrame(o, out);
0932         break;
0933     case msosptTextBox:
0934         processTextBox(o, out);
0935         break;
0936     default:
0937         qDebug() << "Cannot handle shape 0x" << hex << shapeType;
0938         break;
0939     }
0940 }
0941 
0942 void ODrawToOdf::processStyleAndText(const MSO::OfficeArtSpContainer& o,
0943                                      Writer& out)
0944 {
0945     processStyle(o, out);
0946     processText(o, out);
0947 }
0948 
0949 void ODrawToOdf::processStyle(const MSO::OfficeArtSpContainer& o,
0950                               Writer& out)
0951 {
0952     addGraphicStyleToDrawElement(out, o);
0953     set2dGeometry(o, out);
0954 }
0955 
0956 void ODrawToOdf::processText(const MSO::OfficeArtSpContainer& o,
0957                              Writer& out)
0958 {
0959     if (!client) {
0960         qWarning() << "Warning: There's no Client!";
0961         return;
0962     }
0963 
0964     if (o.clientData && client->onlyClientData(*o.clientData)) {
0965         client->processClientData(o.clientTextbox.data(), *o.clientData, out);
0966     } else if (o.clientTextbox) {
0967         client->processClientTextBox(*o.clientTextbox, o.clientData.data(), out);
0968     }
0969 }
0970 
0971 void ODrawToOdf::processModifiers(const MSO::OfficeArtSpContainer &o, Writer &out, const QList<int>& defaults)
0972 {
0973     const AdjustValue* val1 = get<AdjustValue>(o);
0974     if (!val1 && defaults.isEmpty()) return;
0975     const Adjust2Value* val2 = get<Adjust2Value>(o);
0976     const Adjust3Value* val3 = get<Adjust3Value>(o);
0977     const Adjust4Value* val4 = get<Adjust4Value>(o);
0978     const Adjust5Value* val5 = get<Adjust5Value>(o);
0979     const Adjust6Value* val6 = get<Adjust6Value>(o);
0980     const Adjust7Value* val7 = get<Adjust7Value>(o);
0981     const Adjust8Value* val8 = get<Adjust8Value>(o);
0982 
0983     QString modifiers = QString::number(val1 ? val1->adjustvalue : defaults[0]);
0984     if (val2 || defaults.size() > 1) {
0985         modifiers += QString(" %1").arg(val2 ? val2->adjust2value : defaults[1]);
0986         if (val3 || defaults.size() > 2) {
0987             modifiers += QString(" %1").arg(val3 ? val3->adjust3value : defaults[2]);
0988             if (val4 || defaults.size() > 3) {
0989                 modifiers += QString(" %1").arg(val4 ? val4->adjust4value : defaults[3]);
0990                 if (val5 || defaults.size() > 4) {
0991                     modifiers += QString(" %1").arg(val5 ? val5->adjust5value : defaults[4]);
0992                     if (val6 || defaults.size() > 5) {
0993                         modifiers += QString(" %1").arg(val6 ? val6->adjust6value : defaults[5]);
0994                         if (val7 || defaults.size() > 6) {
0995                             modifiers += QString(" %1").arg(val7 ? val7->adjust7value : defaults[6]);
0996                             if (val8 || defaults.size() > 7) {
0997                                 modifiers += QString(" %1").arg(val8 ? val8->adjust8value : defaults[7]);
0998                             }
0999                         }
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
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 
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 
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 
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 }