1 /* This file is part of the Calligra project
2    Copyright (C) 2003 Werner Trobin <trobin@kde.org>
3    Copyright (C) 2003 David Faure <faure@kde.org>
4    Copyright (C) 2010 KO GmbH <jos.van.den.oever@kogmbh.com>
5    Copyright (C) 2010, 2011 Matus Uzak <matus.uzak@ixonos.com>
6    Copyright (C) 2010, 2011 Matus Hanzes <matus.hanzes@ixonos.com>
7 
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the Library GNU General Public
10    version 2 of the License, or (at your option) version 3 or,
11    at the discretion of KDE e.V (which shall act as a proxy as in
12    section 14 of the GPLv3), any later version..
13 
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18 
19    You should have received a copy of the GNU Library General Public License
20    along with this library; see the file COPYING.LIB.  If not, write to
21    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22    Boston, MA 02110-1301, USA.
23 */
24 
25 #include "graphicshandler.h"
26 
27 #include "generated/leinputstream.h"
28 #include "ODrawToOdf.h"
29 #include "drawstyle.h"
30 #include "pictures.h"
31 #include "msodraw.h"
32 
33 #include "conversion.h"
34 #include "document.h"
35 #include "msdoc.h"
36 #include "MsDocDebug.h"
37 
38 #include <KoGenStyle.h>
39 
40 #include <QColor>
41 #include <QByteArray>
42 
43 //#define USE_OFFICEARTDGG_CONTAINER
44 //#define DEBUG_GHANDLER
45 
46 using namespace wvWare;
47 using namespace MSO;
48 
49 using Conversion::twipsToPt;
50 
51 // Specifies the format of the picture data for the PICF structure.
52 enum
53 {
54     MM_SHAPE = 0x0064,
55     MM_SHAPEFILE = 0x0066
56 };
57 
58 namespace
59 {
format(double v)60 QString format(double v) {
61     static const QString f("%1");
62     static const QString e("");
63     static const QRegExp r("\\.?0+$");
64     return f.arg(v, 0, 'f').replace(r, e);
65 }
mm(double v)66 QString mm(double v) {
67     static const QString mm("mm");
68     return format(v) + mm;
69 }
70 }
71 
72 /*
73  * ************************************************
74  * Drawing Writer
75  * ************************************************
76  */
DrawingWriter(KoXmlWriter & xmlWriter,KoGenStyles & styles,bool stylesxml_)77 DrawingWriter::DrawingWriter(KoXmlWriter& xmlWriter, KoGenStyles& styles,  bool stylesxml_)
78         : Writer(xmlWriter, styles, stylesxml_),
79           xLeft(0),
80           xRight(0),
81           yTop(0),
82           yBottom(0)
83 {
84     scaleX = 25.4 / 1440;
85     scaleY = 25.4 / 1440;
86 }
87 
vLength()88 qreal DrawingWriter::vLength()
89 {
90     return Writer::vLength(yBottom - yTop);
91 }
92 
hLength()93 qreal DrawingWriter::hLength()
94 {
95     return Writer::hLength(xRight - xLeft);
96 }
97 
vOffset()98 qreal DrawingWriter::vOffset()
99 {
100     return Writer::vOffset(yTop);
101 }
102 
hOffset()103 qreal DrawingWriter::hOffset()
104 {
105     return Writer::hOffset(xLeft);
106 }
107 
setRectangle(wvWare::Word97::FSPA & spa)108 void DrawingWriter::setRectangle(wvWare::Word97::FSPA& spa)
109 {
110     xLeft = spa.xaLeft;
111     xRight = spa.xaRight;
112     yTop = spa.yaTop;
113     yBottom = spa.yaBottom;
114 }
115 
116 //FIXME: It doesn't make sense with current initialization, because when first
117 //time called, scaleX and scaleY are both set to zero!  Both xOffset and
118 //yOffset doesn't change!
setGroupRectangle(MSO::OfficeArtFSPGR & fspgr)119 void DrawingWriter::setGroupRectangle(MSO::OfficeArtFSPGR& fspgr)
120 {
121     if (fspgr.xRight == fspgr.xLeft) {
122         return;
123     }
124 
125     if (fspgr.yBottom == fspgr.yTop) {
126         return;
127     }
128 
129     xOffset = xOffset + xLeft*scaleX;
130     yOffset = yOffset + yTop*scaleY;
131 
132     scaleX = scaleX * (xRight - xLeft)/(qreal)(fspgr.xRight - fspgr.xLeft);
133     scaleY = scaleY * (yBottom - yTop)/(qreal)(fspgr.yBottom - fspgr.yTop);
134 
135     xOffset = xOffset - fspgr.xLeft * scaleX;
136     yOffset = yOffset - fspgr.yTop * scaleY;
137 }
138 
setChildRectangle(MSO::OfficeArtChildAnchor & anchor)139 void DrawingWriter::setChildRectangle(MSO::OfficeArtChildAnchor& anchor)
140 {
141     xLeft = anchor.xLeft;
142     xRight = anchor.xRight;
143     yTop = anchor.yTop;
144     yBottom = anchor.yBottom;
145 }
146 
setRect(const QRect & rect)147 void DrawingWriter::setRect(const QRect& rect)
148 {
149     xLeft = rect.left();
150     xRight = rect.right();
151     yTop = rect.top();
152     yBottom = rect.bottom();
153 }
154 
155 /*
156  * ************************************************
157  * Graphics Handler
158  * ************************************************
159  */
WordsGraphicsHandler(Document * doc,KoXmlWriter * bodyWriter,KoXmlWriter * manifestWriter,KoStore * store,KoGenStyles * mainStyles,const wvWare::Drawings * p_drawings,const wvWare::Word97::FIB & fib)160 WordsGraphicsHandler::WordsGraphicsHandler(Document* doc,
161                                            KoXmlWriter* bodyWriter,
162                                            KoXmlWriter* manifestWriter,
163                                            KoStore* store, KoGenStyles* mainStyles,
164                                            const wvWare::Drawings* p_drawings,
165                                            const wvWare::Word97::FIB& fib)
166 : QObject()
167 , m_document(doc)
168 , m_store(store)
169 , m_currentWriter(bodyWriter)
170 , m_manifestWriter(manifestWriter)
171 , m_mainStyles(mainStyles)
172 , m_drawings(p_drawings)
173 , m_fib(fib)
174 , m_pOfficeArtHeaderDgContainer(0)
175 , m_pOfficeArtBodyDgContainer(0)
176 , m_processingGroup(false)
177 , m_objectType(Inline)
178 , m_rgbUid(0)
179 , m_zIndex(0)
180 , m_picf(0)
181 , m_pSpa(0)
182 {
183     debugMsDoc ;
184     init();
185 }
186 
~WordsGraphicsHandler()187 WordsGraphicsHandler::~WordsGraphicsHandler()
188 {
189     delete m_pOfficeArtHeaderDgContainer;
190     delete m_pOfficeArtBodyDgContainer;
191 }
192 
193 /*
194  * NOTE: All containers parsed by this function are optional.
195  */
init()196 void WordsGraphicsHandler::init()
197 {
198     debugMsDoc;
199 
200     parseOfficeArtContainers();
201 
202     //create default GraphicStyle using information from OfficeArtDggContainer
203     defineDefaultGraphicStyle(m_mainStyles);
204 
205     const OfficeArtBStoreContainer* blipStore = 0;
206     blipStore = m_officeArtDggContainer.blipStore.data();
207 
208     if (!blipStore) {
209 #ifdef DEBUG_GHANDLER
210         debugMsDoc << "Container of BLIPs not present.";
211 #endif
212         return;
213     }
214     //parse and store floating pictures
215     if (!parseFloatingPictures(blipStore)) {
216         m_store->enterDirectory("Pictures");
217         m_picNames = createPictures(m_store, m_manifestWriter, &blipStore->rgfb);
218         m_store->leaveDirectory();
219     }
220 }
221 
getBgDrawStyle()222 DrawStyle WordsGraphicsHandler::getBgDrawStyle()
223 {
224     const OfficeArtSpContainer* shape = 0;
225     if (m_pOfficeArtBodyDgContainer) {
226         shape = (m_pOfficeArtBodyDgContainer->shape).data();
227     }
228     return DrawStyle(&m_officeArtDggContainer, 0, shape);
229 }
230 
emitTextBoxFound(unsigned int index,bool stylesxml)231 void WordsGraphicsHandler::emitTextBoxFound(unsigned int index, bool stylesxml)
232 {
233     emit textBoxFound(index, stylesxml);
234 }
235 
handleInlineObject(const wvWare::PictureData & data,const bool isBulletPicture)236 QString WordsGraphicsHandler::handleInlineObject(const wvWare::PictureData& data, const bool isBulletPicture)
237 {
238     //TODO: The globalCP might be required to obtain the SPA structure for
239     //inline MS-ODRAW shapes with missing OfficeArtClientAnchor.
240 
241     //TODO: It seems that both inline and floating objects have placement and
242     //dimensions stored in SPA structures.  Check the OfficeArtClientAnchor for
243     //the index into plcfSpa.  However the border information for inline
244     //msosptPictureFrame shapes is stored in the PICF structure.
245 
246     debugMsDoc ;
247     QString ret;
248     quint32 size = (data.picf->lcb - data.picf->cbHeader);
249 
250 #ifdef DEBUG_GHANDLER
251     debugMsDoc << "\nPICF DEBUG:"
252                   << "\nPICF size: 0x" << hex << data.picf->cbHeader
253                   << "\nOfficeArtInlineSpContainer size:" << dec << size
254                   << "\nStorage Format: 0x" << hex << data.picf->mfp.mm;
255 #endif
256 
257     //the picture is store in some external file
258     if (data.picf->mfp.mm == MM_SHAPEFILE) {
259         if (!isBulletPicture) {
260             DrawingWriter out(*m_currentWriter, *m_mainStyles, m_document->writingHeader());
261             m_objectType = Inline;
262             m_picf = data.picf;
263             insertEmptyInlineFrame(out);
264         }
265         return ret;
266     }
267 
268     // going to parse and process the Data stream content
269     LEInputStream* in = m_document->dataStream();
270     if (!in) {
271         debugMsDoc << "Data stream not provided, no access to inline shapes!";
272         return ret;
273     }
274     if (data.fcPic > in->getSize()) {
275         debugMsDoc << "OfficeArtInlineSpContainer offset out of range, skipping!";
276         return ret;
277     }
278 
279 #ifdef DEBUG_GHANDLER
280     debugMsDoc << "\nCurrent stream position:" << in->getPosition()
281                   << "\nOfficeArtInlineSpContainer offset:" << dec << data.fcPic;
282 #endif
283 
284     // parse the OfficeArtInlineSpContainer and rewind the stream
285     LEInputStream::Mark _zero;
286     _zero = in->setMark();
287     in->skip(data.fcPic);
288 
289     OfficeArtInlineSpContainer co;
290     try {
291         parseOfficeArtInlineSpContainer(*in, co);
292     } catch (const IOException& e) {
293         debugMsDoc << e.msg;
294         in->rewind(_zero);
295         return ret;
296     } catch (...) {
297         warnMsDoc << "Warning: Caught an unknown exception!";
298         in->rewind(_zero);
299         return ret;
300     }
301     in->rewind(_zero);
302 
303     int n = (data.fcPic + size) - in->getPosition();
304     if (n) {
305         debugMsDoc << n << "bytes left while parsing OfficeArtInlineSpContainer";
306     }
307 
308     PictureReference ref;
309     // store picture data if present and update m_picNames
310     m_store->enterDirectory("Pictures");
311     foreach (const OfficeArtBStoreContainerFileBlock& block, co.rgfb) {
312         const OfficeArtFBSE* fbse = block.anon.get<MSO::OfficeArtFBSE>();
313         if (!fbse) {
314             debugMsDoc << "Warning: FBSE container not found, skipping ";
315         } else {
316             //check if this BLIP is already in hash table
317             if (m_picNames.contains(fbse->rgbUid)) {
318                 ref.uid = fbse->rgbUid;
319                 ref.name = m_picNames[fbse->rgbUid];
320                 continue;
321             } else {
322                 ref = savePicture(block, m_store);
323                 if (ref.name.length() == 0) {
324                     debugMsDoc << "empty name in picture reference";
325                     break;
326                 }
327                 m_manifestWriter->addManifestEntry("Pictures/" + ref.name, ref.mimetype);
328                 m_picNames[ref.uid] = ref.name;
329             }
330         }
331     }
332     m_store->leaveDirectory();
333 
334     if (isBulletPicture) {
335         return ref.name;
336     }
337 
338     bool inStylesXml = m_document->writingHeader();
339     DrawingWriter out(*m_currentWriter, *m_mainStyles, inStylesXml);
340 
341     //global attributes
342     m_objectType = Inline;
343     m_rgbUid = ref.uid;
344     m_picf = data.picf;
345 
346     const OfficeArtSpContainer* o = &(co.shape);
347     processDrawingObject(*o, out);
348 
349     return ret;
350 }
351 
handleFloatingObject(unsigned int globalCP)352 void WordsGraphicsHandler::handleFloatingObject(unsigned int globalCP)
353 {
354 #ifdef DEBUG_GHANDLER
355     debugMsDoc << "globalCP" << globalCP ;
356 #endif
357     if (!m_drawings) {
358         return;
359     }
360 
361     const PLCF<Word97::FSPA>* plcfSpa = 0;
362     MSO::OfficeArtDgContainer* dg = 0;
363     uint threshold = 0;
364 
365     if (m_document->writingHeader()) {
366         plcfSpa = m_drawings->getSpaHdr();
367         dg = m_pOfficeArtHeaderDgContainer;
368         threshold = m_fib.ccpText + m_fib.ccpFtn;
369     } else {
370         plcfSpa = m_drawings->getSpaMom();
371         dg = m_pOfficeArtBodyDgContainer;
372     }
373 
374     if (!plcfSpa) {
375         debugMsDoc << "MISSING plcfSpa!";
376         return;
377     }
378     if (!dg) {
379         debugMsDoc << "MISSING OfficeArtDgContainer!";
380         return;
381     }
382 
383     PLCFIterator<Word97::FSPA> it(plcfSpa->at(0));
384     for (size_t i = 0; i < plcfSpa->count(); i++, ++it) {
385 #ifdef DEBUG_GHANDLER
386         debugMsDoc << "FSPA start:" << it.currentStart();
387         debugMsDoc << "FSPA spid:" << it.current()->spid;
388 #endif
389 
390         if ((it.currentStart() + threshold) == globalCP) {
391             bool inStylesXml = m_document->writingHeader();
392             DrawingWriter out(*m_currentWriter, *m_mainStyles, inStylesXml);
393 
394             //global attributes
395             m_objectType = Floating;
396             m_pSpa = it.current();
397             m_zIndex = 1;
398 
399             locateDrawing((dg->groupShape).data(), it.current(), (uint)it.current()->spid, out);
400 
401             //reset global attributes
402             m_pSpa = 0;
403             return;
404         }
405     }
406 }
407 
locateDrawing(const MSO::OfficeArtSpgrContainer * spgr,wvWare::Word97::FSPA * spa,uint spid,DrawingWriter & out)408 void WordsGraphicsHandler::locateDrawing(const MSO::OfficeArtSpgrContainer* spgr,
409                                          wvWare::Word97::FSPA* spa,
410                                          uint spid,
411                                          DrawingWriter& out)
412 {
413     if (!spgr) {
414         return;
415     }
416 
417     //FIXME: combine childAnchor, shapeGroup coordinates with information from
418     //clientAnchor pointing to the SPA structure!
419 
420     //NOTE: The OfficeArtSpgrContainer record specifies a container for groups
421     //(4) of shapes.  The group (4) container contains a variable number of
422     //shape containers and other group (4) containers.  Each group (4) is a
423     //shape.  The first container MUST be an OfficeArtSpContainer record, which
424     //MUST contain shape information for the group.  MS-ODRAW, 2.2.16
425     const OfficeArtSpContainer* sp = spgr->rgfb[0].anon.get<OfficeArtSpContainer>();
426     if (sp && (sp->shapeProp.spid == spid)) {
427         debugMsDoc << "An unprocessed shape referred from text, ignoring!";
428         return;
429     }
430 
431     for(int i = 1; i < spgr->rgfb.size(); i++) {
432         const OfficeArtSpgrContainerFileBlock& co = spgr->rgfb[i];
433         if (co.anon.is<OfficeArtSpgrContainer>()) {
434             sp = (*co.anon.get<OfficeArtSpgrContainer>()).rgfb[0].anon.get<OfficeArtSpContainer>();
435             if (sp && sp->shapeProp.spid == spid) {
436                 processGroupShape(*co.anon.get<OfficeArtSpgrContainer>(), out);
437                 m_processingGroup = false;
438                 break;
439             } else {
440                 m_zIndex = m_zIndex + (*co.anon.get<OfficeArtSpgrContainer>()).rgfb.size();
441             }
442         } else {
443             sp = co.anon.get<OfficeArtSpContainer>();
444             if (sp && sp->shapeProp.spid == spid) {
445                 out.setRectangle(*spa);
446                 processDrawingObject(*sp, out);
447                 break;
448             }
449             m_zIndex++;
450         }
451     }
452 }
453 
getRect(const MSO::OfficeArtSpContainer & o)454 QRect WordsGraphicsHandler::getRect(const MSO::OfficeArtSpContainer &o)
455 {
456     if (o.clientAnchor) {
457         const DocOfficeArtClientAnchor* a = o.clientAnchor->anon.get<DocOfficeArtClientAnchor>();
458         if (!a) {
459             return QRect();
460         }
461         const PLCF<wvWare::Word97::FSPA>* plcfSpa = 0;
462         if (m_document->writingHeader()) {
463             plcfSpa = m_drawings->getSpaHdr();
464         } else {
465             plcfSpa = m_drawings->getSpaMom();
466         }
467         PLCFIterator<wvWare::Word97::FSPA> it(plcfSpa->at(a->clientAnchor));
468         const wvWare::Word97::FSPA* spa = it.current();
469     Q_ASSERT(m_pSpa == spa);
470         return QRect(spa->xaLeft, spa->yaTop, spa->xaRight - spa->xaLeft, spa->yaBottom - spa->yaTop);
471     }
472     else if (o.childAnchor) {
473         const MSO::OfficeArtChildAnchor& r = *o.childAnchor;
474         return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
475     } else {
476         return QRect();
477     }
478 }
479 
processGroupShape(const MSO::OfficeArtSpgrContainer & o,DrawingWriter & out)480 void WordsGraphicsHandler::processGroupShape(const MSO::OfficeArtSpgrContainer& o, DrawingWriter& out)
481 {
482     if (o.rgfb.size() < 2) {
483         return;
484     }
485 
486     const OfficeArtSpContainer *sp = o.rgfb[0].anon.get<OfficeArtSpContainer>();
487 
488     if (sp && sp->shapeGroup) {
489         QRect oldCoords = getRect(*sp);
490         if (oldCoords.isValid()) {
491             out.setRect(oldCoords);
492             //process shape information for the group
493             out.setGroupRectangle(*sp->shapeGroup);
494     }
495     }
496 
497     //create graphic style for the group shape
498     QString styleName;
499     KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
500     style.setAutoStyleInStylesDotXml(out.stylesxml);
501 
502     DrawStyle ds(&m_officeArtDggContainer, 0, sp);
503     DrawClient drawclient(this);
504     ODrawToOdf odrawtoodf(drawclient);
505     odrawtoodf.defineGraphicProperties(style, ds, out.styles);
506     definePositionAttributes(style, ds);
507     defineWrappingAttributes(style, ds);
508     styleName = out.styles.insert(style, "gr");
509 
510     out.xml.startElement("draw:g");
511     out.xml.addAttribute("draw:style-name", styleName);
512     setAnchorTypeAttribute(out);
513     setZIndexAttribute(out);
514     m_processingGroup = true;
515 
516     for (int i = 1; i < o.rgfb.size(); ++i) {
517         if (o.rgfb[i].anon.is<OfficeArtSpContainer>()) {
518             OfficeArtSpContainer sp = *o.rgfb[i].anon.get<OfficeArtSpContainer>();
519             if (sp.childAnchor) {
520                 out.setChildRectangle(*sp.childAnchor);
521             }
522             processDrawingObject(sp, out);
523         } else {
524             processGroupShape(*o.rgfb[i].anon.get<OfficeArtSpgrContainer>(), out);
525         }
526     }
527     out.xml.endElement(); // draw:g
528 }
529 
processDrawingObject(const MSO::OfficeArtSpContainer & o,DrawingWriter out)530 void WordsGraphicsHandler::processDrawingObject(const MSO::OfficeArtSpContainer& o, DrawingWriter out)
531 {
532     debugMsDoc;
533 
534     DrawStyle ds(0, 0, &o);
535     DrawClient drawclient(this);
536     ODrawToOdf odrawtoodf(drawclient);
537 
538 #ifdef DEBUG_GHANDLER
539     debugMsDoc << "shapeType: 0x" << hex << o.shapeProp.rh.recInstance;
540     debugMsDoc << "grupShape: " << o.shapeProp.fGroup;
541     debugMsDoc << "Selected properties: ";
542     debugMsDoc << "pib: " << ds.pib();
543 #endif
544 
545     switch (o.shapeProp.rh.recInstance) {
546     case msosptTextBox:
547 #ifdef DEBUG_GHANDLER
548         debugMsDoc<< "processing TextBox";
549 #endif
550         processTextBox(o, out);
551         break;
552     case msosptRectangle:
553         if (ds.fHorizRule()) {
554 #ifdef DEBUG_GHANDLER
555             debugMsDoc<< "processing Line";
556 #endif
557             processLineShape(o, out);
558         } else {
559             odrawtoodf.processDrawingObject(o, out);
560         }
561         break;
562     case msosptPictureFrame:
563 #ifdef DEBUG_GHANDLER
564         debugMsDoc<< "processing PictureFrame";
565 #endif
566         if (m_objectType == Inline) {
567             processInlinePictureFrame(o, out);
568         } else {
569             processFloatingPictureFrame(o, out);
570         }
571         break;
572     case msosptHostControl:
573 #ifdef DEBUG_GHANDLER
574         debugMsDoc<< "processing Host Control";
575 #endif
576         processTextBox(o, out);
577         break;
578     default:
579         odrawtoodf.processDrawingObject(o, out);
580         break;
581     }
582 }
583 
parseOfficeArtContainers()584 void WordsGraphicsHandler::parseOfficeArtContainers()
585 {
586     debugMsDoc;
587 
588     if (!m_fib.lcbDggInfo) return;
589 
590     POLE::Stream& stream = m_document->poleTableStream();
591     if (stream.fail()) {
592         debugMsDoc << "Table stream not provided, no access to OfficeArt file records!";
593         return;
594     }
595 
596     QByteArray array;
597     QBuffer buffer;
598     array.resize(m_fib.lcbDggInfo);
599     stream.seek(m_fib.fcDggInfo);
600     unsigned long n = stream.read((unsigned char*) array.data(), m_fib.lcbDggInfo);
601     if (n != m_fib.lcbDggInfo) {
602         errorMsDoc << "Error while reading from " << stream.fullName().data() << "stream";
603         return;
604     }
605 
606     buffer.setData(array);
607     buffer.open(QIODevice::ReadOnly);
608     LEInputStream in(&buffer);
609 
610     //parse OfficeArfDggContainer from msdoc
611     try {
612         parseOfficeArtDggContainer(in, m_officeArtDggContainer);
613     }
614     catch (const IOException& e) {
615         debugMsDoc << "Caught IOException while parsing OfficeArtDggContainer.";
616         debugMsDoc << e.msg;
617         return;
618     }
619     catch (...) {
620         debugMsDoc << "Caught UNKNOWN exception while parsing OfficeArtDggContainer.";
621         return;
622     }
623 #ifdef DEBUG_GHANDLER
624     debugMsDoc << "OfficeArtDggContainer [ OK ]" ;
625 #endif
626 
627     // parse drawingsVariable from msdoc
628     // 0 - next OfficeArtDgContainer belongs to Main document;
629     // 1 - next OfficeArtDgContainer belongs to Header Document
630     unsigned char drawingsVariable = 0;
631     try {
632         drawingsVariable = in.readuint8();
633     }
634     catch (const IOException& e) {
635         debugMsDoc << "Caught IOException while parsing DrawingsVariable.";
636         debugMsDoc << e.msg;
637         return;
638     }
639     catch (...) {
640         debugMsDoc << "Caught UNKNOWN exception while parsing DrawingsVariable.";
641         return;
642     }
643 
644     //parse OfficeArfDgContainer from msdoc
645     OfficeArtDgContainer *pDgContainer = 0;
646     try {
647         pDgContainer = new OfficeArtDgContainer();
648         if (drawingsVariable == 0) {
649             m_pOfficeArtBodyDgContainer = pDgContainer;
650         } else {
651             m_pOfficeArtHeaderDgContainer = pDgContainer;
652         }
653         parseOfficeArtDgContainer(in, *pDgContainer);
654     }
655     catch (const IOException& e) {
656         debugMsDoc << "Caught IOException while parsing OfficeArtDgContainer.";
657         debugMsDoc << e.msg;
658         return;
659     }
660     catch (...) {
661         debugMsDoc << "Caught UNKNOWN exception while parsing OfficeArtDgContainer.";
662         return;
663     }
664 #ifdef DEBUG_GHANDLER
665     debugMsDoc << "OfficeArtDgContainer (" << (drawingsVariable ? "Headers" : "Body") << ") [ OK ]";
666 #endif
667 
668     // parse drawingsVariable from msdoc
669     // 0 - next OfficeArtDgContainer belongs to Main Document
670     // 1 - next OfficeArtDgContainer belongs to Header Document
671     try {
672         drawingsVariable = in.readuint8();
673     }
674     catch (const IOException& e) {
675         debugMsDoc << "Caught IOException while parsing the 2nd DrawingsVariable.";
676         debugMsDoc << e.msg;
677         return;
678     }
679     catch (...) {
680         debugMsDoc << "Caught UNKNOWN exception while parsing the 2nd DrawingsVariable.";
681         return;
682     }
683 
684     //parse OfficeArfDgContainer from msdoc
685     pDgContainer = 0;
686     try {
687         pDgContainer = new OfficeArtDgContainer();
688         if (drawingsVariable == 0) {
689             if (m_pOfficeArtBodyDgContainer != 0){
690                 delete m_pOfficeArtBodyDgContainer;
691             }
692             m_pOfficeArtBodyDgContainer = pDgContainer;
693         } else {
694             if (m_pOfficeArtHeaderDgContainer != 0) {
695                 delete m_pOfficeArtHeaderDgContainer;
696             }
697             m_pOfficeArtHeaderDgContainer = pDgContainer;
698         }
699         parseOfficeArtDgContainer(in, *pDgContainer);
700     }
701     catch (const IOException& e) {
702         debugMsDoc << "Caught IOException while parsing the 2nd OfficeArtDgContainer.";
703         debugMsDoc << e.msg;
704         return;
705     }
706     catch (...) {
707         debugMsDoc << "Caught UNKNOWN exception while parsing the 2nd OfficeArtDgContainer.";
708         return;
709     }
710 
711 #ifdef DEBUG_GHANDLER
712     debugMsDoc << "OfficeArtDgContainer (" << (drawingsVariable ? "Headers" : "Body") << ") [ OK ]";
713 #endif
714 
715     quint32 r = buffer.size() - in.getPosition();
716     if (r > 0) {
717         errorMsDoc << "Error:" << r << "bytes left to parse from the OfficeArtContent!";
718     }
719 }
720 
parseFloatingPictures(const OfficeArtBStoreContainer * blipStore)721 int WordsGraphicsHandler::parseFloatingPictures(const OfficeArtBStoreContainer* blipStore)
722 {
723     debugMsDoc;
724 
725     if (!blipStore) return(1);
726 
727     // WordDocument stream equals the Delay stream, [MS-DOC] — v20101219
728     LEInputStream& in = m_document->wdocumentStream();
729 
730     for (int i = 0; i < blipStore->rgfb.size(); i++) {
731         OfficeArtBStoreContainerFileBlock block = blipStore->rgfb[i];
732 
733     //Parse content of the Delay stream by using offsets from OfficeArtFBSE
734     //containers.  Not parsing Blip store because MD4 digests in
735     //OfficeArtFBSE happen to be out-dated, which complicates the pib to
736     //picture path association.
737         if (block.anon.is<OfficeArtFBSE>()) {
738             OfficeArtFBSE* fbse = block.anon.get<OfficeArtFBSE>();
739             if (!fbse->embeddedBlip) {
740 
741                 //NOTE: An foDelay value of 0xffffffff specifies that the file
742                 //is not in the delay stream and cRef must be zero.  A cRef
743                 //value of 0x00000000 specifies an empty slot in the
744                 //OfficeArtBStoreContainer.
745 
746                 if (fbse->foDelay == 0xffffffff) {
747 #ifdef DEBUG_GHANDLER
748                     debugMsDoc << "File not in the delay stream, continuing.";
749 #endif
750                     continue;
751                 }
752                 if (!fbse->cRef) {
753 #ifdef DEBUG_GHANDLER
754                     debugMsDoc << "Empty slot, continuing.";
755 #endif
756                     continue;
757                 }
758                 LEInputStream::Mark _zero;
759                 _zero = in.setMark();
760                 in.skip(fbse->foDelay);
761 
762                 //let's check the record header if there's a BLIP stored
763                 LEInputStream::Mark _m;
764                 _m = in.setMark();
765                 OfficeArtRecordHeader rh;
766                 try {
767                     parseOfficeArtRecordHeader(in, rh);
768                 } catch (const IOException& e) {
769                     debugMsDoc << e.msg;
770                     in.rewind(_zero);
771                     continue;
772                 } catch (...) {
773                     warnMsDoc << "Warning: Caught an unknown exception!";
774                     in.rewind(_zero);
775                     continue;
776                 }
777                 in.rewind(_m);
778                 if ( !(rh.recType >= 0xF018 && rh.recType <= 0xF117) ) {
779                     continue;
780                 }
781                 fbse->embeddedBlip = QSharedPointer<OfficeArtBlip>(new OfficeArtBlip(fbse));
782                 try {
783                     parseOfficeArtBlip(in, *(fbse->embeddedBlip.data()));
784                 } catch (const IOException& e) {
785                     debugMsDoc << e.msg;
786                     in.rewind(_zero);
787                     continue;
788                 } catch (...) {
789                     warnMsDoc << "Warning: Caught an unknown exception!";
790                     in.rewind(_zero);
791                     continue;
792                 }
793                 in.rewind(_zero);
794             }
795         }
796     }
797     return(0);
798 }
799 
getPicturePath(quint32 pib) const800 QString WordsGraphicsHandler::getPicturePath(quint32 pib) const
801 {
802     quint32 offset = 0;
803     QByteArray rgbUid = getRgbUid(m_officeArtDggContainer, pib, offset);
804 
805     if (rgbUid.length()) {
806         if (m_picNames.contains(rgbUid)) {
807             return "Pictures/" + m_picNames[rgbUid];
808         } else {
809             debugMsDoc << "UNKNOWN picture reference!";
810         }
811     }
812     return QString();
813 }
814 
defineDefaultGraphicStyle(KoGenStyles * styles)815 void WordsGraphicsHandler::defineDefaultGraphicStyle(KoGenStyles* styles)
816 {
817     // write style <style:default-style style:family="graphic">
818     KoGenStyle style(KoGenStyle::GraphicStyle, "graphic");
819     style.setDefaultStyle(true);
820     DrawStyle ds(&m_officeArtDggContainer);
821     DrawClient drawclient(this);
822     ODrawToOdf odrawtoodf(drawclient);
823     odrawtoodf.defineGraphicProperties(style, ds, *styles);
824     styles->insert(style);
825 
826     MSO::OfficeArtCOLORREF fc = ds.fillColor();
827     QColor color = QColor(fc.red, fc.green, fc.blue);
828     m_document->updateBgColor(color.name());
829 }
830 
defineWrappingAttributes(KoGenStyle & style,const DrawStyle & ds)831 void WordsGraphicsHandler::defineWrappingAttributes(KoGenStyle& style, const DrawStyle& ds)
832 {
833     if (m_processingGroup) return;
834     if (m_objectType == Inline) return;
835 
836     const KoGenStyle::PropertyType gt = KoGenStyle::GraphicType;
837     wvWare::Word97::FSPA* spa = m_pSpa;
838 
839     // style:number-wrapped-paragraphs
840     // style:run-through
841     // style:wrap
842     // style:wrap-contour
843     // style:wrap-contour-mode
844     // style:wrap-dynamic-threshold
845     if (spa != 0) {
846         bool check_wrk = false;
847         switch (spa->wr) {
848         case 0: //wrap around the object
849         case 2: //square wrapping
850             check_wrk = true;
851             break;
852         case 1: //top and bottom wrapping
853             style.addProperty("style:wrap", "none", gt);
854             break;
855         case 3: //in front or behind the text
856             style.addProperty("style:wrap", "run-through", gt);
857             //check if shape is behind the text
858             if ((spa->fBelowText == 1) || (ds.fBehindDocument())) {
859                 style.addProperty("style:run-through", "background", gt);
860             } else {
861                 style.addProperty("style:run-through", "foreground", gt);
862             }
863             break;
864         case 4: //tight wrapping
865             check_wrk = true;
866             style.addProperty("style:wrap-contour", "true", gt);
867             style.addProperty("style:wrap-contour-mode", "outside", gt);
868             break;
869         case 5: //through wrapping
870             check_wrk = true;
871             style.addProperty("style:wrap-contour", "true", gt);
872             style.addProperty("style:wrap-contour-mode", "full", gt);
873             break;
874         }
875         //check details of the text wrapping around this shape
876         if (check_wrk) {
877             switch (spa->wrk) {
878             case 0:
879                 style.addProperty("style:wrap", "parallel", gt);
880                 break;
881             case 1:
882                 style.addProperty("style:wrap", "left", gt);
883                 break;
884             case 2:
885                 style.addProperty("style:wrap", "right", gt);
886                 break;
887             case 3:
888                 style.addProperty("style:wrap", "biggest", gt);
889                 break;
890             }
891         }
892         // ODF-1.2: specifies the number of paragraphs that can wrap around a
893         // frame if wrap mode is in {left, right, parallel, dynamic} and anchor
894         // type is in {char, paragraph}
895         if ((spa->wr != 1) && (spa->wr != 3)) {
896             style.addProperty("style:number-wrapped-paragraphs", "no-limit");
897         }
898     } else {
899         style.addProperty("style:wrap", "run-through", gt);
900         if (ds.fBehindDocument()) {
901             style.addProperty("style:run-through", "background", gt);
902         } else {
903             style.addProperty("style:run-through", "foreground", gt);
904         }
905     }
906 }
907 
definePositionAttributes(KoGenStyle & style,const DrawStyle & ds)908 void WordsGraphicsHandler::definePositionAttributes(KoGenStyle& style, const DrawStyle& ds)
909 {
910     if (m_processingGroup) return;
911 
912     const KoGenStyle::PropertyType gt = KoGenStyle::GraphicType;
913     if (m_objectType == Inline) {
914         style.addProperty("style:vertical-rel", "baseline", gt);
915         style.addProperty("style:vertical-pos", "top", gt);
916     } else {
917         style.addProperty("style:horizontal-pos", getHorizontalPos(ds.posH()), gt);
918         style.addProperty("style:horizontal-rel", getHorizontalRel(ds.posRelH()), gt);
919         style.addProperty("style:vertical-pos", getVerticalPos(ds.posV()), gt);
920         style.addProperty("style:vertical-rel", getVerticalRel(ds.posRelV()), gt);
921     }
922 }
923 
setAnchorTypeAttribute(DrawingWriter & out)924 void WordsGraphicsHandler::setAnchorTypeAttribute(DrawingWriter& out)
925 {
926     if (m_processingGroup) return;
927 
928     // text:anchor-type
929     if (m_objectType == Inline) {
930         out.xml.addAttribute("text:anchor-type", "as-char");
931     } else {
932         out.xml.addAttribute("text:anchor-type", "char");
933     }
934 }
935 
setZIndexAttribute(DrawingWriter & out)936 void WordsGraphicsHandler::setZIndexAttribute(DrawingWriter& out)
937 {
938     if (m_processingGroup) return;
939 
940     // draw:z-index
941     if (m_objectType == Floating) {
942         out.xml.addAttribute("draw:z-index", m_zIndex);
943     } else {
944         out.xml.addAttribute("draw:z-index", 0);
945     }
946 }
947 
processTextBox(const MSO::OfficeArtSpContainer & o,DrawingWriter out)948 void WordsGraphicsHandler::processTextBox(const MSO::OfficeArtSpContainer& o, DrawingWriter out)
949 {
950     QString styleName;
951     KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
952     style.setAutoStyleInStylesDotXml(out.stylesxml);
953 
954     const MSO::OfficeArtDggContainer *dgg = 0;
955 #ifdef USE_OFFICEARTDGG_CONTAINER
956     dgg = &m_officeArtDggContainer;
957 #endif
958 
959     DrawStyle ds(dgg, 0, &o);
960     DrawClient drawclient(this);
961     ODrawToOdf odrawtoodf(drawclient);
962     odrawtoodf.defineGraphicProperties(style, ds, out.styles);
963     definePositionAttributes(style, ds);
964     defineWrappingAttributes(style, ds);
965     styleName = out.styles.insert(style);
966 
967     out.xml.startElement("draw:frame");
968     out.xml.addAttribute("draw:style-name", styleName);
969 
970     setAnchorTypeAttribute(out);
971     setZIndexAttribute(out);
972 
973     switch(ds.txflTextFlow()) {
974     case 1: //msotxflTtoBA up-down
975     case 3: //msotxflTtoBN up-down
976     case 5: //msotxflVertN up-down
977         out.xml.addAttribute("svg:width", mm(out.vLength()));
978         out.xml.addAttribute("svg:height", mm(out.hLength()));
979         out.xml.addAttribute("draw:transform","matrix(0 1 -1 0 " +
980                 mm(((Writer *)&out)->hOffset(out.xRight)) + " " + mm(out.vOffset()) + ")");
981         break;
982     case 2: //msotxflBtoT down-up
983         out.xml.addAttribute("svg:width", mm(out.vLength()));
984         out.xml.addAttribute("svg:height", mm(out.hLength()));
985         out.xml.addAttribute("draw:transform","matrix(0 -1 1 0 " +
986                 mm(out.hOffset()) + " " + mm(((Writer *)&out)->vOffset(out.yBottom)) + ")");
987         break;
988     default : //standard text flow
989         out.xml.addAttribute("svg:width", mm(out.hLength()));
990         out.xml.addAttribute("svg:height", mm(out.vLength()));
991         out.xml.addAttribute("svg:x", mm(out.hOffset()));
992         out.xml.addAttribute("svg:y", mm(out.vOffset()));
993     }
994 
995     out.xml.startElement("draw:text-box");
996 
997     // Especially Word8 files with (nFib == Word8nFib2) do not provide
998     // an OfficeArtClientTextBox.
999     bool textIdValid = false;
1000     quint32 textId = 0;
1001 
1002     if (o.clientTextbox) {
1003         const DocOfficeArtClientTextBox* tb = o.clientTextbox->anon.get<DocOfficeArtClientTextBox>();
1004         if (tb) {
1005             textId = tb->clientTextBox;
1006             textIdValid = true;
1007         } else {
1008             debugMsDoc << "DocOfficeArtClientTextBox missing!";
1009         }
1010     } else {
1011         if (ds.iTxid() < 0) {
1012             debugMsDoc << "lTxid property - negative text identifier!";
1013         } else {
1014             textId = (quint32)ds.iTxid();
1015             textIdValid = true;
1016         }
1017     }
1018     if (textIdValid) {
1019         emit textBoxFound(((textId / 0x10000) - 1), out.stylesxml);
1020     }
1021     out.xml.endElement(); //draw:text-box
1022     out.xml.endElement(); //draw:frame
1023 }
1024 
processInlinePictureFrame(const MSO::OfficeArtSpContainer & o,DrawingWriter & out)1025 void WordsGraphicsHandler::processInlinePictureFrame(const MSO::OfficeArtSpContainer& o, DrawingWriter& out)
1026 {
1027     debugMsDoc ;
1028 
1029     // Shape instance contained in OfficeArtInlineSpContainer.  BLIP properties
1030     // contained in o.shapePrimaryOptions or o.shapeTertiaryOptions1 are stored
1031     // in the order they are encountered, and the property values
1032     // OfficeArtFOPTE.opid.fBid, OfficeArtFOPTE.opid.fComplex, and
1033     // OfficeArtFOPTE.op MUST be ignored.  [MS-ODRAW] — v20101219
1034 
1035     QString styleName;
1036     KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
1037     style.setAutoStyleInStylesDotXml(out.stylesxml);
1038 
1039     const MSO::OfficeArtDggContainer *dgg = 0;
1040 #ifdef USE_OFFICEARTDGG_CONTAINER
1041     dgg = &m_officeArtDggContainer;
1042 #endif
1043 
1044     DrawStyle ds(dgg, 0, &o);
1045     DrawClient drawclient(this);
1046     ODrawToOdf odrawtoodf(drawclient);
1047     odrawtoodf.defineGraphicProperties(style, ds, out.styles);
1048     definePositionAttributes(style, ds);
1049 
1050     style.addProperty("fo:border-top", Conversion::setBorderAttributes(m_picf->brcTop));
1051     style.addProperty("fo:border-left", Conversion::setBorderAttributes(m_picf->brcLeft));
1052     style.addProperty("fo:border-bottom", Conversion::setBorderAttributes(m_picf->brcBottom));
1053     style.addProperty("fo:border-right", Conversion::setBorderAttributes(m_picf->brcRight));
1054 
1055     // NOTE: The default margin-left/margin-right values DO NOT make sense for
1056     // inline pictures, also after conversion of test files to DOCX, both
1057     // attributes were set to ZEROs.  Default margin-top/margin-bottom is ZERO.
1058     style.addPropertyPt("fo:margin", 0);
1059 
1060     styleName = out.styles.insert(style);
1061 
1062     // A diagram drawing canvas placed inline with surrounding text.
1063     if (ds.fPseudoInline()) {
1064         out.xml.startElement("draw:rect");
1065     } else {
1066         out.xml.startElement("draw:frame");
1067     }
1068     out.xml.addAttribute("draw:style-name", styleName);
1069     setAnchorTypeAttribute(out);
1070     setZIndexAttribute(out);
1071 
1072     double hscale = m_picf->mx / 1000.0;
1073     double vscale = m_picf->my / 1000.0;
1074     out.xml.addAttributePt("svg:width", twipsToPt(m_picf->dxaGoal) * hscale);
1075     out.xml.addAttributePt("svg:height", twipsToPt(m_picf->dyaGoal) * vscale);
1076 
1077     QString name = m_picNames.value(m_rgbUid);
1078     QString url;
1079     if (!name.isEmpty()) {
1080         url.append("Pictures/");
1081         url.append(name);
1082     } else {
1083         // if the image cannot be found, just place an empty frame
1084         out.xml.endElement(); //draw:frame (draw:rect)
1085         return;
1086     }
1087     //TODO: process border information (complex properties)
1088 
1089     out.xml.startElement("draw:image");
1090     out.xml.addAttribute("xlink:href", url);
1091     out.xml.addAttribute("xlink:type", "simple");
1092     out.xml.addAttribute("xlink:show", "embed");
1093     out.xml.addAttribute("xlink:actuate", "onLoad");
1094     out.xml.endElement(); //draw:image
1095     out.xml.endElement(); //draw:frame
1096     return;
1097 }
1098 
processFloatingPictureFrame(const MSO::OfficeArtSpContainer & o,DrawingWriter & out)1099 void WordsGraphicsHandler::processFloatingPictureFrame(const MSO::OfficeArtSpContainer& o, DrawingWriter& out)
1100 {
1101     debugMsDoc ;
1102 
1103     const MSO::OfficeArtDggContainer *dgg = 0;
1104 #ifdef USE_OFFICEARTDGG_CONTAINER
1105     dgg = &m_officeArtDggContainer;
1106 #endif
1107     DrawStyle ds(dgg, 0, &o);
1108 
1109     // A value of 0x00000000 MUST be ignored.  [MS-ODRAW] — v20101219
1110     if (!ds.pib()) return;
1111 
1112     QString styleName;
1113     KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
1114     style.setAutoStyleInStylesDotXml(out.stylesxml);
1115 
1116     DrawClient drawclient(this);
1117     ODrawToOdf odrawtoodf(drawclient);
1118     odrawtoodf.defineGraphicProperties(style, ds, out.styles);
1119     definePositionAttributes(style, ds);
1120     defineWrappingAttributes(style, ds);
1121     styleName = out.styles.insert(style);
1122 
1123     out.xml.startElement("draw:frame");
1124     out.xml.addAttribute("draw:style-name", styleName);
1125     setAnchorTypeAttribute(out);
1126     setZIndexAttribute(out);
1127 
1128     out.xml.addAttribute("svg:width", mm(out.hLength()));
1129     out.xml.addAttribute("svg:height", mm(out.vLength()));
1130     out.xml.addAttribute("svg:x", mm(out.hOffset()));
1131     out.xml.addAttribute("svg:y", mm(out.vOffset()));
1132 
1133     QString url = getPicturePath(ds.pib());
1134 
1135     //if the image cannot be found, just place an empty frame
1136     if (url.isEmpty()) {
1137         out.xml.endElement(); //draw:frame
1138         return;
1139     }
1140     out.xml.startElement("draw:image");
1141     out.xml.addAttribute("xlink:href", url);
1142     out.xml.addAttribute("xlink:type", "simple");
1143     out.xml.addAttribute("xlink:show", "embed");
1144     out.xml.addAttribute("xlink:actuate", "onLoad");
1145     out.xml.endElement(); //draw:image
1146 
1147     //check for user edited wrap points
1148 #if 0
1149     if (ds.fEditedWrap()) {
1150         QString points;
1151         IMsoArray _v = ds.pWrapPolygonVertices_complex();
1152         if (_v.data.size()) {
1153             //_v.data is an array of POINTs, MS-ODRAW, page 89
1154             QByteArray a, a2;
1155             int* p;
1156 
1157             for (int i = 0, offset = 0; i < _v.nElems; i++, offset += _v.cbElem) {
1158                 // x coordinate of this point
1159                 a = _v.data.mid(offset, _v.cbElem);
1160                 a2 = a.mid(0, _v.cbElem / 2);
1161                 p = (int*) a2.data();
1162                 points.append(QString::number(twipsToPt(*p), 'f'));
1163                 points.append(",");
1164                 // y coordinate of this point
1165                 a2 = a.mid(_v.cbElem / 2, _v.cbElem / 2);
1166                 p = (int*) a2.data();
1167                 points.append(QString::number(twipsToPt(*p), 'f'));
1168                 points.append(" ");
1169             }
1170             points.chop(1); //remove last space
1171         }
1172         out.xml.startElement("draw:contour-polygon");
1173         out.xml.addAttribute("draw:points", points);
1174         out.xml.endElement(); //draw:contour-polygon
1175     }
1176 #endif
1177     out.xml.endElement(); //draw:frame
1178     return;
1179 }
1180 
processLineShape(const MSO::OfficeArtSpContainer & o,DrawingWriter & out)1181 void WordsGraphicsHandler::processLineShape(const MSO::OfficeArtSpContainer& o, DrawingWriter& out)
1182 {
1183     debugMsDoc ;
1184 
1185     QString styleName;
1186     KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
1187     style.setAutoStyleInStylesDotXml(out.stylesxml);
1188 
1189     const MSO::OfficeArtDggContainer *dgg = 0;
1190 #ifdef USE_OFFICEARTDGG_CONTAINER
1191     dgg = &m_officeArtDggContainer;
1192 #endif
1193 
1194     DrawStyle ds(dgg, 0, &o);
1195     DrawClient drawclient(this);
1196     ODrawToOdf odrawtoodf(drawclient);
1197     odrawtoodf.defineGraphicProperties(style, ds, out.styles);
1198     definePositionAttributes(style, ds);
1199     //TODO: maybe wrapping related attributes have to be set
1200 
1201     //NOTE: also the dxWidthHR properties may store the width information
1202     float width = ds.pctHR() / 10.0;
1203 
1204     QString hrAlign;
1205     QString xPos = QString::number(0.0f).append("in");
1206     const float base_width = 6.1378f;
1207 
1208     switch (ds.alignHR()) {
1209     case hAlignLeft:
1210         hrAlign = QString("left");
1211         xPos = QString::number(0.0f).append("in");
1212         break;
1213     case hAlignCenter:
1214         hrAlign = QString("center");
1215         xPos = QString::number((base_width / 2.0) - ((width * base_width) / 200.0), 'f').append("in");
1216         break;
1217     case hAlignRight:
1218         hrAlign = QString("right");
1219         xPos = QString::number(base_width - (width * base_width) / 100.0, 'f').append("in");
1220         break;
1221     }
1222     //process the content of HR specific properties
1223     style.addProperty("draw:textarea-horizontal-align", hrAlign);
1224     style.addProperty("draw:textarea-vertical-align", "top");
1225     if (ds.fNoshadeHR()) {
1226         style.addProperty("draw:shadow", "hidden");
1227     }
1228     else {
1229         style.addProperty("draw:shadow", "visible");
1230     }
1231     styleName = out.styles.insert(style);
1232 
1233     //create a custom shape
1234     out.xml.startElement("draw:custom-shape");
1235     out.xml.addAttribute("draw:style-name", styleName);
1236 
1237     setAnchorTypeAttribute(out);
1238     setZIndexAttribute(out);
1239 
1240     QString height = QString::number(ds.dxHeightHR() / 1440.0f, 'f').append("in");
1241     out.xml.addAttribute("svg:height", height);
1242 
1243     QString width_str = QString::number(width * base_width / 100.0f, 'f').append("in");
1244     out.xml.addAttribute("svg:width", width_str);
1245     out.xml.addAttribute("svg:x", xPos);
1246 
1247     //--------------------
1248     out.xml.startElement("draw:enhanced-geometry");
1249     out.xml.addAttribute("svg:viewBox", "0 0 21600 21600");
1250     out.xml.addAttribute("draw:type", "rectangle");
1251     out.xml.addAttribute("draw:enhanced-path", "M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N");
1252     out.xml.endElement(); //enhanced-geometry
1253     out.xml.endElement(); //custom-shape
1254 }
1255 
insertEmptyInlineFrame(DrawingWriter & out)1256 void WordsGraphicsHandler::insertEmptyInlineFrame(DrawingWriter& out)
1257 {
1258     if (m_objectType != Inline) return;
1259 
1260     QString styleName;
1261     KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
1262     style.setAutoStyleInStylesDotXml(out.stylesxml);
1263 
1264     DrawStyle ds;
1265     DrawClient drawclient(this);
1266     ODrawToOdf odrawtoodf(drawclient);
1267     odrawtoodf.defineGraphicProperties(style, ds, out.styles);
1268     definePositionAttributes(style, ds);
1269     defineWrappingAttributes(style, ds);
1270     styleName = out.styles.insert(style);
1271 
1272     out.xml.startElement("draw:frame");
1273     out.xml.addAttribute("draw:style-name", styleName);
1274     setAnchorTypeAttribute(out);
1275     setZIndexAttribute(out);
1276     double hscale = m_picf->mx / 1000.0;
1277     double vscale = m_picf->my / 1000.0;
1278     out.xml.addAttributePt("svg:width", twipsToPt(m_picf->dxaGoal) * hscale);
1279     out.xml.addAttributePt("svg:height", twipsToPt(m_picf->dyaGoal) * vscale);
1280     out.xml.endElement(); //draw:frame
1281 }
1282