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