1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include "rtfsdrexport.hxx"
21 #include <memory>
22 #include "rtfattributeoutput.hxx"
23 #include <svtools/rtfkeywd.hxx>
24 #include <svtools/unitconv.hxx>
25 #include <filter/msfilter/rtfutil.hxx>
26 #include <editeng/editobj.hxx>
27 #include <editeng/editids.hrc>
28 #include <editeng/fontitem.hxx>
29 #include <editeng/fhgtitem.hxx>
30 #include <svx/svdotext.hxx>
31 #include <svx/unoapi.hxx>
32 #include <vcl/cvtgrf.hxx>
33 #include <textboxhelper.hxx>
34 #include <dcontact.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <sal/log.hxx>
37 #include <algorithm>
38 #include "rtfexport.hxx"
39
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/graphic/XGraphic.hpp>
42
43 using namespace css;
44
RtfSdrExport(RtfExport & rExport)45 RtfSdrExport::RtfSdrExport(RtfExport& rExport)
46 : EscherEx(std::make_shared<EscherExGlobal>(), nullptr)
47 , m_rExport(rExport)
48 , m_rAttrOutput(static_cast<RtfAttributeOutput&>(m_rExport.AttrOutput()))
49 , m_pSdrObject(nullptr)
50 , m_nShapeType(ESCHER_ShpInst_Nil)
51 , m_nShapeFlags(ShapeFlag::NONE)
52 , m_aShapeStyle(200)
53 , m_pShapeTypeWritten(new bool[ESCHER_ShpInst_COUNT])
54 {
55 mnGroupLevel = 1;
56 memset(m_pShapeTypeWritten.get(), 0, ESCHER_ShpInst_COUNT * sizeof(bool));
57 }
58
~RtfSdrExport()59 RtfSdrExport::~RtfSdrExport()
60 {
61 delete mpOutStrm;
62 mpOutStrm = nullptr;
63 }
64
OpenContainer(sal_uInt16 nEscherContainer,int nRecInstance)65 void RtfSdrExport::OpenContainer(sal_uInt16 nEscherContainer, int nRecInstance)
66 {
67 EscherEx::OpenContainer(nEscherContainer, nRecInstance);
68
69 if (nEscherContainer == ESCHER_SpContainer)
70 {
71 m_nShapeType = ESCHER_ShpInst_Nil;
72 m_aShapeStyle.setLength(0);
73 m_aShapeStyle.ensureCapacity(200);
74 m_aShapeProps.clear();
75 }
76 }
77
CloseContainer()78 void RtfSdrExport::CloseContainer()
79 {
80 if (mRecTypes.back() == ESCHER_SpContainer)
81 {
82 // write the shape now when we have all the info
83 sal_Int32 nShapeElement = StartShape();
84 EndShape(nShapeElement);
85
86 // cleanup
87 m_nShapeType = ESCHER_ShpInst_Nil;
88 }
89
90 EscherEx::CloseContainer();
91 }
92
EnterGroup(const OUString &,const tools::Rectangle *)93 sal_uInt32 RtfSdrExport::EnterGroup(const OUString& /*rShapeName*/,
94 const tools::Rectangle* /*pRect*/)
95 {
96 m_bInGroup = true;
97 return GenerateShapeId();
98 }
99
LeaveGroup()100 void RtfSdrExport::LeaveGroup() { m_bInGroup = false; }
101
AddShape(sal_uInt32 nShapeType,ShapeFlag nShapeFlags,sal_uInt32)102 void RtfSdrExport::AddShape(sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 /*nShapeId*/)
103 {
104 m_nShapeType = nShapeType;
105 m_nShapeFlags = nShapeFlags;
106 }
107
impl_GetUInt16(const sal_uInt8 * & pVal)108 static sal_uInt16 impl_GetUInt16(const sal_uInt8*& pVal)
109 {
110 sal_uInt16 nRet = *pVal++;
111 nRet += (*pVal++) << 8;
112 return nRet;
113 }
114
impl_GetPointComponent(const sal_uInt8 * & pVal,std::size_t & rVerticesPos,sal_uInt16 nPointSize)115 static sal_Int32 impl_GetPointComponent(const sal_uInt8*& pVal, std::size_t& rVerticesPos,
116 sal_uInt16 nPointSize)
117 {
118 sal_Int32 nRet = 0;
119 if ((nPointSize == 0xfff0) || (nPointSize == 4))
120 {
121 sal_uInt16 nUnsigned = *pVal++;
122 nUnsigned += (*pVal++) << 8;
123 rVerticesPos += 2;
124
125 nRet = sal_Int16(nUnsigned);
126 }
127 else if (nPointSize == 8)
128 {
129 sal_uInt32 nUnsigned = *pVal++;
130 nUnsigned += (*pVal++) << 8;
131 nUnsigned += (*pVal++) << 16;
132 nUnsigned += (*pVal++) << 24;
133 rVerticesPos += 4;
134
135 nRet = nUnsigned;
136 }
137
138 return nRet;
139 }
140
Commit(EscherPropertyContainer & rProps,const tools::Rectangle & rRect)141 void RtfSdrExport::Commit(EscherPropertyContainer& rProps, const tools::Rectangle& rRect)
142 {
143 if (m_nShapeType == ESCHER_ShpInst_Nil)
144 return;
145
146 if (m_nShapeType == ESCHER_ShpInst_Line)
147 AddLineDimensions(rRect);
148 else
149 AddRectangleDimensions(m_aShapeStyle, rRect);
150
151 // properties
152 const EscherProperties& rOpts = rProps.GetOpts();
153 for (const auto& rOpt : rOpts)
154 {
155 sal_uInt16 nId = (rOpt.nPropId & 0x0FFF);
156
157 switch (nId)
158 {
159 case ESCHER_Prop_WrapText:
160 {
161 int nWrapType = 0;
162 switch (rOpt.nPropValue)
163 {
164 case ESCHER_WrapSquare:
165 nWrapType = 2;
166 break;
167 case ESCHER_WrapByPoints:
168 nWrapType = 4;
169 break;
170 case ESCHER_WrapNone:
171 nWrapType = 3;
172 break;
173 case ESCHER_WrapTopBottom:
174 nWrapType = 1;
175 break;
176 case ESCHER_WrapThrough:
177 nWrapType = 5;
178 break;
179 }
180 if (nWrapType)
181 m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPWR)
182 .append(static_cast<sal_Int32>(nWrapType));
183 }
184 break;
185 case ESCHER_Prop_fillColor:
186 m_aShapeProps.insert(
187 std::pair<OString, OString>("fillColor", OString::number(rOpt.nPropValue)));
188 break;
189 case ESCHER_Prop_fillBackColor:
190 m_aShapeProps.insert(
191 std::pair<OString, OString>("fillBackColor", OString::number(rOpt.nPropValue)));
192 break;
193 case ESCHER_Prop_AnchorText:
194 m_aShapeProps.insert(
195 std::pair<OString, OString>("anchorText", OString::number(rOpt.nPropValue)));
196 break;
197 case ESCHER_Prop_fNoFillHitTest:
198 if (rOpt.nPropValue)
199 m_aShapeProps.insert(
200 std::pair<OString, OString>("fNoFillHitTest", OString::number(1)));
201 break;
202 case ESCHER_Prop_fNoLineDrawDash:
203 // for some reason the value is set to 0x90000 if lines are switched off
204 if (rOpt.nPropValue == 0x90000)
205 m_aShapeProps.insert(std::pair<OString, OString>("fLine", OString::number(0)));
206 break;
207 case ESCHER_Prop_lineColor:
208 m_aShapeProps.insert(
209 std::pair<OString, OString>("lineColor", OString::number(rOpt.nPropValue)));
210 break;
211 case ESCHER_Prop_lineBackColor:
212 m_aShapeProps.insert(
213 std::pair<OString, OString>("lineBackColor", OString::number(rOpt.nPropValue)));
214 break;
215 case ESCHER_Prop_lineJoinStyle:
216 m_aShapeProps.insert(
217 std::pair<OString, OString>("lineJoinStyle", OString::number(rOpt.nPropValue)));
218 break;
219 case ESCHER_Prop_fshadowObscured:
220 if (rOpt.nPropValue)
221 m_aShapeProps.insert(std::pair<OString, OString>("fshadowObscured", "1"));
222 break;
223 case ESCHER_Prop_geoLeft:
224 case ESCHER_Prop_geoTop:
225 {
226 sal_uInt32 nLeft = 0;
227 sal_uInt32 nTop = 0;
228
229 if (nId == ESCHER_Prop_geoLeft)
230 {
231 nLeft = rOpt.nPropValue;
232 rProps.GetOpt(ESCHER_Prop_geoTop, nTop);
233 }
234 else
235 {
236 nTop = rOpt.nPropValue;
237 rProps.GetOpt(ESCHER_Prop_geoLeft, nLeft);
238 }
239
240 m_aShapeProps.insert(
241 std::pair<OString, OString>("geoLeft", OString::number(sal_Int32(nLeft))));
242 m_aShapeProps.insert(
243 std::pair<OString, OString>("geoTop", OString::number(sal_Int32(nTop))));
244 }
245 break;
246
247 case ESCHER_Prop_geoRight:
248 case ESCHER_Prop_geoBottom:
249 {
250 sal_uInt32 nLeft = 0;
251 sal_uInt32 nRight = 0;
252 sal_uInt32 nTop = 0;
253 sal_uInt32 nBottom = 0;
254 rProps.GetOpt(ESCHER_Prop_geoLeft, nLeft);
255 rProps.GetOpt(ESCHER_Prop_geoTop, nTop);
256
257 if (nId == ESCHER_Prop_geoRight)
258 {
259 nRight = rOpt.nPropValue;
260 rProps.GetOpt(ESCHER_Prop_geoBottom, nBottom);
261 }
262 else
263 {
264 nBottom = rOpt.nPropValue;
265 rProps.GetOpt(ESCHER_Prop_geoRight, nRight);
266 }
267
268 m_aShapeProps.insert(std::pair<OString, OString>(
269 "geoRight", OString::number(sal_Int32(nRight) - sal_Int32(nLeft))));
270 m_aShapeProps.insert(std::pair<OString, OString>(
271 "geoBottom", OString::number(sal_Int32(nBottom) - sal_Int32(nTop))));
272 }
273 break;
274 case ESCHER_Prop_pVertices:
275 case ESCHER_Prop_pSegmentInfo:
276 {
277 EscherPropSortStruct aVertices;
278 EscherPropSortStruct aSegments;
279
280 if (rProps.GetOpt(ESCHER_Prop_pVertices, aVertices)
281 && rProps.GetOpt(ESCHER_Prop_pSegmentInfo, aSegments)
282 && aVertices.nProp.size() >= 6 && aSegments.nProp.size() >= 6)
283 {
284 const sal_uInt8* pVerticesIt = aVertices.nProp.data() + 6;
285 std::size_t nVerticesPos = 6;
286 const sal_uInt8* pSegmentIt = aSegments.nProp.data();
287
288 OStringBuffer aSegmentInfo(512);
289 OStringBuffer aVerticies(512);
290
291 sal_uInt16 nPointSize = aVertices.nProp[4] + (aVertices.nProp[5] << 8);
292
293 // number of segments
294 sal_uInt16 nSegments = impl_GetUInt16(pSegmentIt);
295 sal_Int32 nVertices = 0;
296 aSegmentInfo.append("2;").append(static_cast<sal_Int32>(nSegments));
297 pSegmentIt += 4;
298
299 for (; nSegments; --nSegments)
300 {
301 sal_uInt16 nSeg = impl_GetUInt16(pSegmentIt);
302
303 // The segment type is stored in the upper 3 bits
304 // and segment count is stored in the lower 13
305 // bits.
306 unsigned char nSegmentType = (nSeg & 0xE000) >> 13;
307 unsigned short nSegmentCount = nSeg & 0x03FF;
308
309 aSegmentInfo.append(';').append(static_cast<sal_Int32>(nSeg));
310 switch (nSegmentType)
311 {
312 case msopathLineTo:
313 for (unsigned short i = 0; i < nSegmentCount; ++i)
314 {
315 sal_Int32 nX = impl_GetPointComponent(pVerticesIt, nVerticesPos,
316 nPointSize);
317 sal_Int32 nY = impl_GetPointComponent(pVerticesIt, nVerticesPos,
318 nPointSize);
319 aVerticies.append(";(")
320 .append(nX)
321 .append(",")
322 .append(nY)
323 .append(")");
324 nVertices++;
325 }
326 break;
327 case msopathMoveTo:
328 {
329 sal_Int32 nX
330 = impl_GetPointComponent(pVerticesIt, nVerticesPos, nPointSize);
331 sal_Int32 nY
332 = impl_GetPointComponent(pVerticesIt, nVerticesPos, nPointSize);
333 aVerticies.append(";(").append(nX).append(",").append(nY).append(
334 ")");
335 nVertices++;
336 break;
337 }
338 case msopathCurveTo:
339 for (unsigned short j = 0; j < nSegmentCount; ++j)
340 {
341 for (int i = 0; i < 3; i++)
342 {
343 sal_Int32 nX = impl_GetPointComponent(
344 pVerticesIt, nVerticesPos, nPointSize);
345 sal_Int32 nY = impl_GetPointComponent(
346 pVerticesIt, nVerticesPos, nPointSize);
347 aVerticies.append(";(")
348 .append(nX)
349 .append(",")
350 .append(nY)
351 .append(")");
352 nVertices++;
353 }
354 }
355 break;
356 case msopathEscape:
357 {
358 // If the segment type is msopathEscape, the lower 13 bits are
359 // divided in a 5 bit escape code and 8 bit
360 // vertex count (not segment count!)
361 unsigned char nVertexCount = nSegmentCount & 0x00FF;
362 nVerticesPos += nVertexCount;
363 break;
364 }
365 case msopathClientEscape:
366 case msopathClose:
367 case msopathEnd:
368 break;
369 default:
370 SAL_WARN("sw.rtf", "Totally b0rked");
371 break;
372 }
373 }
374
375 if (!aVerticies.isEmpty())
376 {
377 // We know the number of vertices at the end only, so we have to prepend them here.
378 m_aShapeProps.insert(std::pair<OString, OString>(
379 "pVerticies",
380 "8;" + OString::number(nVertices) + aVerticies.makeStringAndClear()));
381 }
382 if (!aSegmentInfo.isEmpty())
383 m_aShapeProps.insert(std::pair<OString, OString>(
384 "pSegmentInfo", aSegmentInfo.makeStringAndClear()));
385 }
386 else
387 SAL_INFO(
388 "sw.rtf",
389 OSL_THIS_FUNC
390 << ": unhandled shape path, missing either pVertices or pSegmentInfo");
391 }
392 break;
393 case ESCHER_Prop_shapePath:
394 // noop, we use pSegmentInfo instead
395 break;
396 case ESCHER_Prop_fFillOK:
397 if (!rOpt.nPropValue)
398 m_aShapeProps.insert(std::pair<OString, OString>("fFillOK", "0"));
399 break;
400 case ESCHER_Prop_dxTextLeft:
401 m_aShapeProps.insert(
402 std::pair<OString, OString>("dxTextLeft", OString::number(rOpt.nPropValue)));
403 break;
404 case ESCHER_Prop_dyTextTop:
405 m_aShapeProps.insert(
406 std::pair<OString, OString>("dyTextTop", OString::number(rOpt.nPropValue)));
407 break;
408 case ESCHER_Prop_dxTextRight:
409 m_aShapeProps.insert(
410 std::pair<OString, OString>("dxTextRight", OString::number(rOpt.nPropValue)));
411 break;
412 case ESCHER_Prop_dyTextBottom:
413 m_aShapeProps.insert(
414 std::pair<OString, OString>("dyTextBottom", OString::number(rOpt.nPropValue)));
415 break;
416 case ESCHER_Prop_FitTextToShape:
417 // Size text to fit shape size: not supported by RTF
418 break;
419 case ESCHER_Prop_adjustValue:
420 m_aShapeProps.insert(
421 std::pair<OString, OString>("adjustValue", OString::number(rOpt.nPropValue)));
422 break;
423 case ESCHER_Prop_txflTextFlow:
424 m_aShapeProps.insert(
425 std::pair<OString, OString>("txflTextFlow", OString::number(rOpt.nPropValue)));
426 break;
427 case ESCHER_Prop_fillType:
428 m_aShapeProps.insert(
429 std::pair<OString, OString>("fillType", OString::number(rOpt.nPropValue)));
430 break;
431 case ESCHER_Prop_fillOpacity:
432 m_aShapeProps.insert(
433 std::pair<OString, OString>("fillOpacity", OString::number(rOpt.nPropValue)));
434 break;
435 case ESCHER_Prop_fillBlip:
436 {
437 OStringBuffer aBuf;
438 aBuf.append('{')
439 .append(OOO_STRING_SVTOOLS_RTF_PICT)
440 .append(OOO_STRING_SVTOOLS_RTF_PNGBLIP)
441 .append(SAL_NEWLINE_STRING);
442 int nHeaderSize
443 = 25; // The first bytes are WW8-specific, we're only interested in the PNG
444 aBuf.append(msfilter::rtfutil::WriteHex(rOpt.nProp.data() + nHeaderSize,
445 rOpt.nProp.size() - nHeaderSize));
446 aBuf.append('}');
447 m_aShapeProps.insert(
448 std::pair<OString, OString>("fillBlip", aBuf.makeStringAndClear()));
449 }
450 break;
451 default:
452 SAL_INFO("sw.rtf", OSL_THIS_FUNC << ": unhandled property: " << nId
453 << " (value: " << rOpt.nPropValue << ")");
454 break;
455 }
456 }
457 }
458
AddLineDimensions(const tools::Rectangle & rRectangle)459 void RtfSdrExport::AddLineDimensions(const tools::Rectangle& rRectangle)
460 {
461 // We get the position relative to (the current?) character
462 m_aShapeProps.insert(std::pair<OString, OString>("posrelh", "3"));
463
464 if (m_nShapeFlags & ShapeFlag::FlipV)
465 m_aShapeProps.insert(std::pair<OString, OString>("fFlipV", "1"));
466
467 if (m_nShapeFlags & ShapeFlag::FlipH)
468 m_aShapeProps.insert(std::pair<OString, OString>("fFlipH", "1"));
469
470 // the actual dimensions
471 m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPLEFT).append(rRectangle.Left());
472 m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPTOP).append(rRectangle.Top());
473 m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPRIGHT).append(rRectangle.Right());
474 m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM).append(rRectangle.Bottom());
475 }
476
AddRectangleDimensions(OStringBuffer & rBuffer,const tools::Rectangle & rRectangle)477 void RtfSdrExport::AddRectangleDimensions(OStringBuffer& rBuffer,
478 const tools::Rectangle& rRectangle)
479 {
480 // We get the position relative to (the current?) character
481 m_aShapeProps.insert(std::pair<OString, OString>("posrelh", "3"));
482
483 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPLEFT).append(rRectangle.Left());
484 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPTOP).append(rRectangle.Top());
485 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPRIGHT).append(rRectangle.Right());
486 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM).append(rRectangle.Bottom());
487 }
488
489 extern const char* pShapeTypes[];
490
lcl_AppendSP(OStringBuffer & rRunText,const char cName[],const OString & rValue)491 static void lcl_AppendSP(OStringBuffer& rRunText, const char cName[], const OString& rValue)
492 {
493 rRunText.append('{')
494 .append(OOO_STRING_SVTOOLS_RTF_SP)
495 .append('{')
496 .append(OOO_STRING_SVTOOLS_RTF_SN " ")
497 .append(cName)
498 .append('}')
499 .append('{')
500 .append(OOO_STRING_SVTOOLS_RTF_SV " ")
501 .append(rValue)
502 .append('}')
503 .append('}');
504 }
505
impl_writeGraphic()506 void RtfSdrExport::impl_writeGraphic()
507 {
508 // Get the Graphic object from the Sdr one.
509 uno::Reference<drawing::XShape> xShape
510 = GetXShapeForSdrObject(const_cast<SdrObject*>(m_pSdrObject));
511 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
512
513 uno::Reference<graphic::XGraphic> xGraphic;
514
515 Graphic aGraphic;
516
517 try
518 {
519 xPropertySet->getPropertyValue("Graphic") >>= xGraphic;
520 }
521 catch (beans::UnknownPropertyException const&)
522 {
523 DBG_UNHANDLED_EXCEPTION("sw.rtf");
524 }
525
526 if (xGraphic.is())
527 {
528 aGraphic = Graphic(xGraphic);
529 }
530
531 // Export it to a stream.
532 SvMemoryStream aStream;
533 (void)GraphicConverter::Export(aStream, aGraphic, ConvertDataFormat::PNG);
534 sal_uInt32 nSize = aStream.TellEnd();
535 auto pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
536
537 Size aMapped(aGraphic.GetPrefSize());
538
539 // Add it to the properties.
540 RtfStringBuffer aBuf;
541 aBuf->append('{').append(OOO_STRING_SVTOOLS_RTF_PICT).append(OOO_STRING_SVTOOLS_RTF_PNGBLIP);
542 aBuf->append(OOO_STRING_SVTOOLS_RTF_PICW).append(sal_Int32(aMapped.Width()));
543 aBuf->append(OOO_STRING_SVTOOLS_RTF_PICH)
544 .append(sal_Int32(aMapped.Height()))
545 .append(SAL_NEWLINE_STRING);
546 aBuf->append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
547 aBuf->append('}');
548 m_aShapeProps.insert(std::pair<OString, OString>("pib", aBuf.makeStringAndClear()));
549 }
550
StartShape()551 sal_Int32 RtfSdrExport::StartShape()
552 {
553 if (m_nShapeType == ESCHER_ShpInst_Nil)
554 return -1;
555
556 m_aShapeProps.insert(std::pair<OString, OString>("shapeType", OString::number(m_nShapeType)));
557 if (ESCHER_ShpInst_PictureFrame == m_nShapeType)
558 impl_writeGraphic();
559
560 m_rAttrOutput.RunText().append('{').append(OOO_STRING_SVTOOLS_RTF_SHP);
561 m_rAttrOutput.RunText()
562 .append('{')
563 .append(OOO_STRING_SVTOOLS_RTF_IGNORE)
564 .append(OOO_STRING_SVTOOLS_RTF_SHPINST);
565
566 m_rAttrOutput.RunText().append(m_aShapeStyle.makeStringAndClear());
567 // Ignore \shpbxpage, \shpbxmargin, and \shpbxcolumn, in favor of the posrelh property.
568 m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPBXIGNORE);
569 // Ignore \shpbypage, \shpbymargin, and \shpbycolumn, in favor of the posrelh property.
570 m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPBYIGNORE);
571
572 // Write ZOrder.
573 if (!m_bInGroup)
574 {
575 // Order inside the group shape is not relevant for the flat shape list
576 // we write.
577 m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPZ);
578 m_rAttrOutput.RunText().append(OString::number(m_pSdrObject->GetOrdNum()));
579 }
580
581 for (auto it = m_aShapeProps.rbegin(); it != m_aShapeProps.rend(); ++it)
582 lcl_AppendSP(m_rAttrOutput.RunText(), (*it).first.getStr(), (*it).second);
583
584 lcl_AppendSP(m_rAttrOutput.RunText(), "wzDescription",
585 msfilter::rtfutil::OutString(m_pSdrObject->GetDescription(),
586 m_rExport.GetCurrentEncoding()));
587 lcl_AppendSP(
588 m_rAttrOutput.RunText(), "wzName",
589 msfilter::rtfutil::OutString(m_pSdrObject->GetName(), m_rExport.GetCurrentEncoding()));
590
591 // now check if we have some text
592 const SwFrameFormat* pShape = FindFrameFormat(m_pSdrObject);
593 if (pShape)
594 {
595 if (SwFrameFormat* pTextBox
596 = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
597 {
598 ww8::Frame* pFrame = nullptr;
599 for (auto& rFrame : m_rExport.m_aFrames)
600 {
601 if (pTextBox == &rFrame.GetFrameFormat())
602 {
603 pFrame = &rFrame;
604 break;
605 }
606 }
607
608 if (pFrame)
609 m_rAttrOutput.writeTextFrame(*pFrame, /*bTextBox=*/true);
610 return m_nShapeType;
611 }
612 }
613
614 auto pTextObj = dynamic_cast<const SdrTextObj*>(m_pSdrObject);
615 if (pTextObj)
616 {
617 const OutlinerParaObject* pParaObj = nullptr;
618 std::unique_ptr<const OutlinerParaObject> pOwnedParaObj;
619
620 /*
621 #i13885#
622 When the object is actively being edited, that text is not set into
623 the objects normal text object, but lives in a separate object.
624 */
625 if (pTextObj->IsTextEditActive())
626 {
627 pOwnedParaObj = pTextObj->CreateEditOutlinerParaObject();
628 pParaObj = pOwnedParaObj.get();
629 }
630 else
631 {
632 pParaObj = pTextObj->GetOutlinerParaObject();
633 }
634
635 if (pParaObj)
636 {
637 // this is reached only in case some text is attached to the shape
638 // Watermark or TextBox?
639 if (pTextObj->TakeObjNameSingul().match("Text Frame"))
640 WriteOutliner(*pParaObj, TXT_HFTXTBOX);
641 else
642 {
643 const EditTextObject& rEditObj = pParaObj->GetTextObject();
644 const SfxItemSet& rItemSet = rEditObj.GetParaAttribs(0);
645
646 lcl_AppendSP(m_rAttrOutput.RunText(), "gtextUNICODE",
647 msfilter::rtfutil::OutString(rEditObj.GetText(0),
648 m_rExport.GetCurrentEncoding()));
649
650 auto pFontFamily
651 = static_cast<const SvxFontItem*>(rItemSet.GetItem(SID_ATTR_CHAR_FONT));
652 if (pFontFamily)
653 {
654 lcl_AppendSP(m_rAttrOutput.RunText(), "gtextFont",
655 msfilter::rtfutil::OutString(pFontFamily->GetFamilyName(),
656 m_rExport.GetCurrentEncoding()));
657 }
658
659 auto pFontHeight = static_cast<const SvxFontHeightItem*>(
660 rItemSet.GetItem(SID_ATTR_CHAR_FONTHEIGHT));
661 if (pFontHeight)
662 {
663 long nFontHeight = TransformMetric(pFontHeight->GetHeight(), FieldUnit::TWIP,
664 FieldUnit::POINT);
665 lcl_AppendSP(
666 m_rAttrOutput.RunText(), "gtextSize",
667 msfilter::rtfutil::OutString(OUString::number(nFontHeight * RTF_MULTIPLIER),
668 m_rExport.GetCurrentEncoding()));
669 }
670
671 // RTF angle: 0-360 * 2^16 clockwise
672 // LO angle: 0-360 * 100 counter-clockwise
673 sal_Int32 nRotation
674 = -1 * pTextObj->GetGeoStat().nRotationAngle * RTF_MULTIPLIER / 100;
675 lcl_AppendSP(m_rAttrOutput.RunText(), "rotation",
676 msfilter::rtfutil::OutString(OUString::number(nRotation),
677 m_rExport.GetCurrentEncoding()));
678 }
679 }
680 }
681
682 return m_nShapeType;
683 }
684
WriteOutliner(const OutlinerParaObject & rParaObj,TextTypes eType)685 void RtfSdrExport::WriteOutliner(const OutlinerParaObject& rParaObj, TextTypes eType)
686 {
687 SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start");
688
689 const EditTextObject& rEditObj = rParaObj.GetTextObject();
690 MSWord_SdrAttrIter aAttrIter(m_rExport, rEditObj, eType);
691
692 sal_Int32 nPara = rEditObj.GetParagraphCount();
693
694 bool bShape = eType == TXT_HFTXTBOX;
695 if (bShape)
696 m_rAttrOutput.RunText().append('{').append(OOO_STRING_SVTOOLS_RTF_SHPTXT).append(' ');
697 for (sal_Int32 n = 0; n < nPara; ++n)
698 {
699 if (n)
700 aAttrIter.NextPara(n);
701
702 rtl_TextEncoding eChrSet = aAttrIter.GetNodeCharSet();
703
704 OUString aStr(rEditObj.GetText(n));
705 sal_Int32 nCurrentPos = 0;
706 const sal_Int32 nEnd = aStr.getLength();
707
708 aAttrIter.OutParaAttr(false);
709 m_rAttrOutput.RunText().append(m_rAttrOutput.MoveCharacterProperties(true));
710
711 do
712 {
713 const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
714 rtl_TextEncoding eNextChrSet = aAttrIter.GetNextCharSet();
715
716 aAttrIter.OutAttr(nCurrentPos);
717 m_rAttrOutput.RunText().append('{');
718 m_rAttrOutput.RunText().append(m_rAttrOutput.MoveCharacterProperties(true));
719 m_rAttrOutput.RunText().append(SAL_NEWLINE_STRING);
720 bool bTextAtr = aAttrIter.IsTextAttr(nCurrentPos);
721 if (!bTextAtr)
722 {
723 OUString aOut(aStr.copy(nCurrentPos, nNextAttr - nCurrentPos));
724 m_rAttrOutput.RunText().append(msfilter::rtfutil::OutString(aOut, eChrSet));
725 }
726
727 m_rAttrOutput.RunText().append('}');
728
729 nCurrentPos = nNextAttr;
730 eChrSet = eNextChrSet;
731 aAttrIter.NextPos();
732 } while (nCurrentPos < nEnd);
733 if (bShape || n + 1 < nPara)
734 m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_PAR);
735 }
736 if (bShape)
737 m_rAttrOutput.RunText().append('}');
738
739 SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end");
740 }
741
EndShape(sal_Int32 nShapeElement)742 void RtfSdrExport::EndShape(sal_Int32 nShapeElement)
743 {
744 if (nShapeElement >= 0)
745 {
746 // end of the shape
747 m_rAttrOutput.RunText().append('}').append('}');
748 }
749 }
750
AddSdrObject(const SdrObject & rObj)751 void RtfSdrExport::AddSdrObject(const SdrObject& rObj)
752 {
753 m_pSdrObject = &rObj;
754 EscherEx::AddSdrObject(rObj);
755 }
756
isTextBox(const SwFrameFormat & rFrameFormat)757 bool RtfSdrExport::isTextBox(const SwFrameFormat& rFrameFormat)
758 {
759 return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
760 }
761
762 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
763