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