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 <comphelper/string.hxx>
21 #include <svl/urihelper.hxx>
22 #include <hintids.hxx>
23 #include <osl/endian.h>
24 #include <sal/log.hxx>
25 #include <svx/fmglob.hxx>
26 #include <svx/sdtaitm.hxx>
27 #include <editeng/lrspitem.hxx>
28 #include <editeng/udlnitem.hxx>
29 #include <svx/xfillit0.hxx>
30 #include <svx/xlineit0.hxx>
31 #include <svx/xlnclit.hxx>
32 #include <svx/xlnwtit.hxx>
33 #include <svx/xlndsit.hxx>
34 #include <svx/xlnstit.hxx>
35 #include <svx/xlnedit.hxx>
36 #include <svx/xlnstwit.hxx>
37 #include <svx/xlnedwit.hxx>
38 #include <svx/xlnstcit.hxx>
39 #include <svx/xlnedcit.hxx>
40 #include <svx/xflclit.hxx>
41 #include <svx/xbtmpit.hxx>
42 #include <svx/svdmodel.hxx>
43 #include <svx/svdocapt.hxx>
44 #include <svx/sxctitm.hxx>
45 #include <svx/sdggaitm.hxx>
46 #include <svx/sdgluitm.hxx>
47 #include <svx/sdgmoitm.hxx>
48 #include <svx/sdmetitm.hxx>
49 #include <svx/sdooitm.hxx>
50 #include <svx/sdshitm.hxx>
51 #include <svx/sdsxyitm.hxx>
52 #include <svx/sdtagitm.hxx>
53 #include <svx/sdtditm.hxx>
54 #include <svx/sdtfsitm.hxx>
55 #include <editeng/editeng.hxx>
56 #include <svx/svdpage.hxx>
57 #include <svx/svdopath.hxx>
58 #include <svx/svdocirc.hxx>
59 #include <editeng/outlobj.hxx>
60 #include <svx/svdogrp.hxx>
61 #include <svx/svdograf.hxx>
62 #include <svx/svdoole2.hxx>
63 #include <editeng/colritem.hxx>
64 #include <editeng/fhgtitem.hxx>
65 #include <editeng/postitem.hxx>
66 #include <editeng/adjustitem.hxx>
67 #include <editeng/wghtitem.hxx>
68 #include <editeng/crossedoutitem.hxx>
69 #include <editeng/contouritem.hxx>
70 #include <editeng/shdditem.hxx>
71 #include <editeng/fontitem.hxx>
72 #include <editeng/ulspitem.hxx>
73 #include <svx/svdoattr.hxx>
74 #include <editeng/brushitem.hxx>
75 #include <svx/rectenum.hxx>
76 #include <editeng/opaqitem.hxx>
77 #include <editeng/shaditem.hxx>
78 #include <editeng/boxitem.hxx>
79 #include <editeng/outliner.hxx>
80 #include <editeng/frmdiritem.hxx>
81 #include <svx/xfltrit.hxx>
82 #include <filter/msfilter/msdffimp.hxx>
83 #include <grfatr.hxx>
84 #include <fmtornt.hxx>
85 #include <fmtcntnt.hxx>
86 #include <frmfmt.hxx>
87 #include <fmtanchr.hxx>
88 #include <pam.hxx>
89 #include <doc.hxx>
90 #include <drawdoc.hxx>
91 #include <IDocumentDrawModelAccess.hxx>
92 #include <docary.hxx>
93 #include <ndgrf.hxx>
94 #include <ndtxt.hxx>
95 #include <dcontact.hxx>
96 #include <docsh.hxx>
97 #include <mdiexp.hxx>
98 #include <fmtcnct.hxx>
99 #include "ww8struc.hxx"
100 #include "ww8scan.hxx"
101 #include "ww8par.hxx"
102 #include "ww8par2.hxx"
103 #include "ww8graf.hxx"
104 #include <fmtinfmt.hxx>
105 #include <editeng/eeitem.hxx>
106 #include <editeng/flditem.hxx>
107 #include <fmtfollowtextflow.hxx>
108 #include "writerhelper.hxx"
109 #include "writerwordglue.hxx"
110 #include <basegfx/point/b2dpoint.hxx>
111 #include <basegfx/polygon/b2dpolygon.hxx>
112 #include <editeng/editobj.hxx>
113 #include <math.h>
114 #include <fmturl.hxx>
115 #include <svx/hlnkitem.hxx>
116 #include <svl/whiter.hxx>
117 #include <o3tl/enumrange.hxx>
118 #include <o3tl/safeint.hxx>
119 #include <memory>
120 #include <filter/msfilter/escherex.hxx>
121 #include "sprmids.hxx"
122 
123 using ::editeng::SvxBorderLine;
124 using namespace ::com::sun::star;
125 using namespace sw::types;
126 using namespace sw::util;
127 
128 // helper methods
WW8TransCol(SVBT32 nWC)129 static Color WW8TransCol(SVBT32 nWC)
130 {
131 #if 1               // 1 = use predefined color, 0 = ignore
132 
133     // color table to convert RGB values to pre-defined colors
134     // (to make the writer UI show the right color names)
135     // the table is split in base 3, the greys are missing as
136     // they don't fit into that system (4 values: bw, wb, 2 * grey)
137     static const Color eColA[] = {                  //  B G R  B G R  B G R
138         COL_BLACK, COL_RED, COL_LIGHTRED,           //  0 0 0, 0 0 1, 0 0 2
139         COL_GREEN, COL_BROWN, COL_BLACK,            //  0 1 0, 0 1 1, 0 1 2
140         COL_LIGHTGREEN, COL_BLACK, COL_YELLOW,      //  0 2 0, 0 2 1, 0 2 2
141         COL_BLUE, COL_MAGENTA, COL_BLACK,           //  1 0 0, 1 0 1, 1 0 2
142         COL_CYAN, COL_LIGHTGRAY, COL_BLACK,         //  1 1 0, 1 1 1, 1 1 2
143         COL_BLACK, COL_BLACK, COL_BLACK,            //  1 2 0, 1 2 1, 1 2 2
144         COL_LIGHTBLUE, COL_BLACK, COL_LIGHTMAGENTA, //  2 0 0, 2 0 1, 2 0 2
145         COL_BLACK, COL_BLACK, COL_BLACK,            //  2 1 0, 2 1 1, 2 1 2
146         COL_LIGHTCYAN, COL_BLACK, COL_WHITE };      //  2 2 0, 2 2 1, 2 2 2
147 
148     // In nWC[3] is a byte that's not described in the WW documentation.
149     // Its meaning appears to be the following: For 0, it's a normal color
150     // whose RGB values are in nWC[0..2]. If nWC[3] is 0x1, 0x7d or 0x83,
151     // it's a grey value whose black portion is given in 0.5% in nWC[0].
152     // I guess that BIT(0) in nWC[3] is relevant for distinguishing RGB/Grey.
153 
154     if( !( nWC[3] & 0x1 ) &&                        // not special (grey)
155         (    ( nWC[0] == 0 ||  nWC[0]== 0x80 || nWC[0] == 0xff )    // R
156           && ( nWC[1] == 0 ||  nWC[1]== 0x80 || nWC[1] == 0xff )    // G
157           && ( nWC[2] == 0 ||  nWC[2]== 0x80 || nWC[2] == 0xff ) ) ){// B
158         int nIdx = 0;       // and now: Idx-calculation in base 3
159         for (int i = 2; i >= 0; i--)
160         {
161             nIdx *= 3;
162             if (nWC[i])
163                 nIdx += ((nWC[i] == 0xff) ? 2 : 1);
164         }
165         if (eColA[nIdx] != COL_BLACK)
166             return eColA[nIdx];  // default color
167     }
168 #endif
169 
170     if (nWC[3] & 0x1)
171     {
172         // Special color gray
173         sal_uInt8 u = static_cast<sal_uInt8>( static_cast<sal_uLong>( 200 - nWC[0] ) * 256 / 200 );
174         return Color(u, u, u);
175     }
176 
177     // User-Color
178     return Color(nWC[0], nWC[1], nWC[2]);
179 }
180 
SetUniqueGraphName(SwFrameFormat * pFrameFormat,const OUString & rFixed)181 void wwFrameNamer::SetUniqueGraphName(SwFrameFormat *pFrameFormat, const OUString &rFixed)
182 {
183     if (mbIsDisabled || rFixed.isEmpty())
184         return;
185 
186     pFrameFormat->SetName(msSeed+OUString::number(++mnImportedGraphicsCount) + ": " + rFixed);
187 }
188 
189 // ReadGrafStart reads object data and if necessary creates an anchor
ReadGrafStart(void * pData,short nDataSiz,WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)190 bool SwWW8ImplReader::ReadGrafStart(void* pData, short nDataSiz,
191     WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
192 {
193     if (SVBT16ToUInt16(pHd->cb) < sizeof(WW8_DPHEAD) + nDataSiz)
194     {
195         OSL_ENSURE( false, "+graphic element: too short?" );
196         m_pStrm->SeekRel(SVBT16ToUInt16(pHd->cb) - sizeof(WW8_DPHEAD));
197         return false;
198     }
199 
200     bool bCouldRead = checkRead(*m_pStrm, pData, nDataSiz);
201     OSL_ENSURE(bCouldRead, "Short Graphic header");
202     if (!bCouldRead)
203         return false;
204 
205     SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR );
206     aAnchor.SetAnchor( m_pPaM->GetPoint() );
207     rSet.Put( aAnchor );
208 
209     m_nDrawXOfs2 = m_nDrawXOfs;
210     m_nDrawYOfs2 = m_nDrawYOfs;
211 
212     return true;
213 }
214 
215 // SetStdAttr() sets standard attributes
SetStdAttr(SfxItemSet & rSet,WW8_DP_LINETYPE & rL,WW8_DP_SHADOW const & rSh)216 static void SetStdAttr( SfxItemSet& rSet, WW8_DP_LINETYPE& rL,
217                         WW8_DP_SHADOW const & rSh )
218 {
219     if( SVBT16ToUInt16( rL.lnps ) == 5 ){            // invisible
220         rSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
221     }else{                                          // visible
222         Color aCol( WW8TransCol( rL.lnpc ) );           // line color
223         rSet.Put( XLineColorItem( OUString(), aCol ) );
224         rSet.Put( XLineWidthItem( SVBT16ToUInt16( rL.lnpw ) ) );
225                                                     // line thickness
226         if( SVBT16ToUInt16( rL.lnps ) >= 1
227             && SVBT16ToUInt16(rL.lnps ) <= 4 ){      // line style
228             rSet.Put( XLineStyleItem( drawing::LineStyle_DASH ) );
229             sal_Int16 nLen = SVBT16ToUInt16( rL.lnpw );
230             XDash aD( css::drawing::DashStyle_RECT, 1, 2 * nLen, 1, 5 * nLen, 5 * nLen );
231             switch( SVBT16ToUInt16( rL.lnps ) ){
232             case 1: aD.SetDots( 0 );            // Dash
233                     aD.SetDashLen( 6 * nLen );
234                     aD.SetDistance( 4 * nLen );
235                     break;
236             case 2: aD.SetDashes( 0 ); break;   // Dot
237             case 3: break;                      // Dash Dot
238             case 4: aD.SetDots( 2 ); break;     // Dash Dot Dot
239             }
240             rSet.Put( XLineDashItem( OUString(), aD ) );
241         }else{
242             rSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) );  // needed for TextBox
243         }
244     }
245     if( SVBT16ToUInt16( rSh.shdwpi ) ){                  // shadow
246         rSet.Put(makeSdrShadowItem(true));
247         rSet.Put( makeSdrShadowXDistItem( SVBT16ToUInt16( rSh.xaOffset ) ) );
248         rSet.Put( makeSdrShadowYDistItem( SVBT16ToUInt16( rSh.yaOffset ) ) );
249     }
250 }
251 
252 // SetFill() sets fill attributes such as fore- and background color and
253 // pattern by reducing to a color
254 // SetFill() doesn't yet set a pattern, because Sdr can't easily do that
255 // and the Sdr hatching (XDash) isn't finished yet.
256 // Instead, a mixed color will be picked that's between the selected ones.
SetFill(SfxItemSet & rSet,WW8_DP_FILL & rFill)257 static void SetFill( SfxItemSet& rSet, WW8_DP_FILL& rFill )
258 {
259     static const sal_uInt8 nPatA[] =
260     {
261              0,  0,  5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80,
262             90, 50, 50, 50, 50, 50, 50, 33, 33, 33, 33, 33, 33
263     };
264     sal_uInt16 nPat = SVBT16ToUInt16(rFill.flpp);
265 
266     if (nPat == 0) // transparent
267         rSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
268     else
269     {
270         rSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));  // necessary for textbox
271         if (nPat <= 1 || (SAL_N_ELEMENTS(nPatA) <= nPat))
272         {
273             // Solid background or unknown
274             rSet.Put(XFillColorItem(OUString(), WW8TransCol(rFill.dlpcBg)));
275         }
276         else
277         {                                      // Brush -> color mix
278             Color aB( WW8TransCol( rFill.dlpcBg ) );
279             Color aF( WW8TransCol( rFill.dlpcFg ) );
280             aB.SetRed( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetRed()) * nPatA[nPat]
281                         + static_cast<sal_uLong>(aB.GetRed()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
282             aB.SetGreen( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetGreen()) * nPatA[nPat]
283                         + static_cast<sal_uLong>(aB.GetGreen()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
284             aB.SetBlue( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetBlue()) * nPatA[nPat]
285                         + static_cast<sal_uLong>(aB.GetBlue()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
286             rSet.Put( XFillColorItem( OUString(), aB ) );
287         }
288     }
289 }
290 
SetLineEndAttr(SfxItemSet & rSet,WW8_DP_LINEEND const & rLe,WW8_DP_LINETYPE const & rLt)291 static void SetLineEndAttr( SfxItemSet& rSet, WW8_DP_LINEEND const & rLe,
292                             WW8_DP_LINETYPE const & rLt )
293 {
294     sal_uInt16 aSB = SVBT16ToUInt16( rLe.aStartBits );
295     if( aSB & 0x3 )
296     {
297         ::basegfx::B2DPolygon aPolygon;
298         aPolygon.append(::basegfx::B2DPoint(0.0, 330.0));
299         aPolygon.append(::basegfx::B2DPoint(100.0, 0.0));
300         aPolygon.append(::basegfx::B2DPoint(200.0, 330.0));
301         aPolygon.setClosed(true);
302         rSet.Put( XLineEndItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) );
303         sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw )
304                         * ( ( aSB >> 2 & 0x3 ) + ( aSB >> 4 & 0x3 ) );
305         if( nSiz < 220 ) nSiz = 220;
306         rSet.Put(XLineEndWidthItem(nSiz));
307         rSet.Put(XLineEndCenterItem(false));
308     }
309 
310     sal_uInt16 aEB = SVBT16ToUInt16( rLe.aEndBits );
311     if( aEB & 0x3 ){
312         ::basegfx::B2DPolygon aPolygon;
313         aPolygon.append(::basegfx::B2DPoint(0.0, 330.0));
314         aPolygon.append(::basegfx::B2DPoint(100.0, 0.0));
315         aPolygon.append(::basegfx::B2DPoint(200.0, 330.0));
316         aPolygon.setClosed(true);
317         rSet.Put( XLineStartItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) );
318         sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw )
319                         * ( ( aEB >> 2 & 0x3 ) + ( aEB >> 4 & 0x3 ) );
320         if( nSiz < 220 ) nSiz = 220;
321         rSet.Put(XLineStartWidthItem(nSiz));
322         rSet.Put(XLineStartCenterItem(false));
323     }
324 }
325 
326 // start of routines for the different objects
ReadLine(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)327 SdrObject* SwWW8ImplReader::ReadLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
328 {
329     WW8_DP_LINE aLine;
330 
331     if( !ReadGrafStart( static_cast<void*>(&aLine), sizeof( aLine ), pHd, rSet ) )
332         return nullptr;
333 
334     Point aP[2];
335     {
336         Point& rP0 = aP[0];
337         Point& rP1 = aP[1];
338 
339         rP0.setX( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2 );
340         rP0.setY( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
341         rP1 = rP0;
342         rP0.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.xaStart )) );
343         rP0.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.yaStart )) );
344         rP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.xaEnd )) );
345         rP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.yaEnd )) );
346     }
347 
348     ::basegfx::B2DPolygon aPolygon;
349     aPolygon.append(::basegfx::B2DPoint(aP[0].X(), aP[0].Y()));
350     aPolygon.append(::basegfx::B2DPoint(aP[1].X(), aP[1].Y()));
351     SdrObject* pObj = new SdrPathObj(
352         *m_pDrawModel,
353         OBJ_LINE,
354         ::basegfx::B2DPolyPolygon(aPolygon));
355 
356     SetStdAttr( rSet, aLine.aLnt, aLine.aShd );
357     SetLineEndAttr( rSet, aLine.aEpp, aLine.aLnt );
358 
359     return pObj;
360 }
361 
ReadRect(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)362 SdrObject* SwWW8ImplReader::ReadRect(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
363 {
364     WW8_DP_RECT aRect;
365 
366     if( !ReadGrafStart( static_cast<void*>(&aRect), sizeof( aRect ), pHd, rSet ) )
367         return nullptr;
368 
369     Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
370                static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
371     Point aP1( aP0 );
372     aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
373     aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
374 
375     SdrObject* pObj = new SdrRectObj(
376         *m_pDrawModel,
377         tools::Rectangle(aP0, aP1));
378 
379     SetStdAttr( rSet, aRect.aLnt, aRect.aShd );
380     SetFill( rSet, aRect.aFill );
381 
382     return pObj;
383 }
384 
ReadElipse(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)385 SdrObject* SwWW8ImplReader::ReadElipse(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
386 {
387     WW8_DP_ELIPSE aElipse;
388 
389     if( !ReadGrafStart( static_cast<void*>(&aElipse), sizeof( aElipse ), pHd, rSet ) )
390         return nullptr;
391 
392     Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
393                static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
394     Point aP1( aP0 );
395     aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
396     aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
397 
398     SdrObject* pObj = new SdrCircObj(
399         *m_pDrawModel,
400         SdrCircKind::Full,
401         tools::Rectangle(aP0, aP1));
402 
403     SetStdAttr( rSet, aElipse.aLnt, aElipse.aShd );
404     SetFill( rSet, aElipse.aFill );
405 
406     return pObj;
407 }
408 
ReadArc(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)409 SdrObject* SwWW8ImplReader::ReadArc(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
410 {
411     WW8_DP_ARC aArc;
412 
413     if( !ReadGrafStart( static_cast<void*>(&aArc), sizeof( aArc ), pHd, rSet ) )
414         return nullptr;
415 
416     Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
417                static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
418     Point aP1( aP0 );
419     aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) * 2 );
420     aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) * 2 );
421 
422     short nA[] = { 2, 3, 1, 0 };
423     short nW = nA[ ( ( aArc.fLeft & 1 ) << 1 ) + ( aArc.fUp & 1 ) ];
424     if( !aArc.fLeft ){
425         aP0.AdjustY( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
426         aP1.AdjustY( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
427     }
428     if( aArc.fUp ){
429         aP0.AdjustX( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
430         aP1.AdjustX( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
431     }
432 
433     SdrObject* pObj = new SdrCircObj(
434         *m_pDrawModel,
435         SdrCircKind::Section,
436         tools::Rectangle(aP0, aP1),
437         nW * 9000,
438         ( ( nW + 1 ) & 3 ) * 9000);
439 
440     SetStdAttr( rSet, aArc.aLnt, aArc.aShd );
441     SetFill( rSet, aArc.aFill );
442 
443     return pObj;
444 }
445 
ReadPolyLine(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)446 SdrObject* SwWW8ImplReader::ReadPolyLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
447 {
448     WW8_DP_POLYLINE aPoly;
449 
450     if( !ReadGrafStart( static_cast<void*>(&aPoly), sizeof( aPoly ), pHd, rSet ) )
451         return nullptr;
452 
453     sal_uInt16 nCount = SVBT16ToUInt16( aPoly.aBits1 ) >> 1 & 0x7fff;
454     std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]);
455 
456     bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4);      // read points
457     OSL_ENSURE(bCouldRead, "Short PolyLine header");
458     if (!bCouldRead)
459         return nullptr;
460 
461     tools::Polygon aP( nCount );
462     Point aPt;
463     for (sal_uInt16 i=0; i<nCount; ++i)
464     {
465         aPt.setX( SVBT16ToUInt16( xP[i << 1] ) + m_nDrawXOfs2
466                   + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) );
467         aPt.setY( SVBT16ToUInt16( xP[( i << 1 ) + 1] ) + m_nDrawYOfs2
468                   + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) );
469         aP[i] = aPt;
470     }
471     xP.reset();
472 
473     SdrObject* pObj = new SdrPathObj(
474         *m_pDrawModel,
475         (SVBT16ToUInt16(aPoly.aBits1) & 0x1) ? OBJ_POLY : OBJ_PLIN,
476         ::basegfx::B2DPolyPolygon(aP.getB2DPolygon()));
477 
478     SetStdAttr( rSet, aPoly.aLnt, aPoly.aShd );
479     SetFill( rSet, aPoly.aFill );
480 
481     return pObj;
482 }
483 
GetESelection(EditEngine const & rDrawEditEngine,long nCpStart,long nCpEnd)484 static ESelection GetESelection(EditEngine const &rDrawEditEngine, long nCpStart, long nCpEnd)
485 {
486     sal_Int32 nPCnt = rDrawEditEngine.GetParagraphCount();
487     sal_Int32 nSP = 0;
488     sal_Int32 nEP = 0;
489     while(      (nSP < nPCnt)
490             &&  (nCpStart >= rDrawEditEngine.GetTextLen( nSP ) + 1) )
491     {
492         nCpStart -= rDrawEditEngine.GetTextLen( nSP ) + 1;
493         nSP++;
494     }
495         // at the end, switch to the new line only 1 character later as
496         // otherwise line attributes reach one line too far
497     while(      (nEP < nPCnt)
498             &&  (nCpEnd > rDrawEditEngine.GetTextLen( nEP ) + 1) )
499     {
500         nCpEnd -= rDrawEditEngine.GetTextLen( nEP ) + 1;
501         nEP++;
502     }
503     return ESelection( nSP, nCpStart, nEP, nCpEnd );
504 }
505 
506 // InsertTxbxStyAttrs() sets style attributes into the passed ItemSet.
507 // SW styles are used since import-WW-styles are already destroyed.
508 // SW styles are examined in depth first search order (with parent styles)
509 // for the attributes given in aSrcTab. They're cloned, and the clones'
510 // Which-IDs are changed according to the aDstTab table so that the
511 // EditEngine will not ignore them.
512 // Both Paragraph and character attributes are stuffed into the ItemSet.
InsertTxbxStyAttrs(SfxItemSet & rS,sal_uInt16 nColl)513 void SwWW8ImplReader::InsertTxbxStyAttrs( SfxItemSet& rS, sal_uInt16 nColl )
514 {
515     SwWW8StyInf * pStyInf = GetStyle(nColl);
516     if( pStyInf != nullptr && pStyInf->m_pFormat && pStyInf->m_bColl )
517     {
518         const SfxPoolItem* pItem;
519         for( sal_uInt16 i = POOLATTR_BEGIN; i < POOLATTR_END; i++ )
520         {
521             // If we are set in the source and not set in the destination
522             // then add it in.
523             if ( SfxItemState::SET == pStyInf->m_pFormat->GetItemState(
524                 i, true, &pItem ) )
525             {
526                 SfxItemPool *pEditPool = rS.GetPool();
527                 sal_uInt16 nWhich = i;
528                 sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich);
529                 if (
530                     nSlotId && nWhich != nSlotId &&
531                     0 != (nWhich = pEditPool->GetWhich(nSlotId)) &&
532                     nWhich != nSlotId &&
533                     ( SfxItemState::SET != rS.GetItemState(nWhich, false) )
534                    )
535                 {
536                     rS.Put( pItem->CloneSetWhich(nWhich) );
537                 }
538             }
539         }
540     }
541 
542 }
543 
lcl_StripFields(OUString & rString,WW8_CP & rNewStartCp)544 static void lcl_StripFields(OUString &rString, WW8_CP &rNewStartCp)
545 {
546     sal_Int32 nStartPos = 0;
547     for (;;)
548     {
549         nStartPos = rString.indexOf(0x13, nStartPos);
550         if (nStartPos<0)
551             return;
552 
553         const sal_Unicode cStops[] = {0x14, 0x15, 0};
554         const sal_Int32 nStopPos = comphelper::string::indexOfAny(rString, cStops, nStartPos);
555         if (nStopPos<0)
556         {
557             rNewStartCp += rString.getLength()-nStartPos;
558             rString = rString.copy(0, nStartPos);
559             return;
560         }
561 
562         const bool was0x14 = rString[nStopPos]==0x14;
563         rString = rString.replaceAt(nStartPos, nStopPos+1-nStartPos, "");
564         rNewStartCp += nStopPos-nStartPos;
565 
566         if (was0x14)
567         {
568             ++rNewStartCp;
569             nStartPos = rString.indexOf(0x15, nStartPos);
570             if (nStartPos<0)
571                 return;
572             rString = rString.replaceAt(nStartPos, 1, "");
573         }
574     }
575 }
576 
577 class Chunk
578 {
579 private:
580     OUString msURL;
581     long mnStartPos; // 0x13
582     long mnEndPos;   // 0x15
583 public:
Chunk(long nStart,const OUString & rURL)584     explicit Chunk(long nStart, const OUString &rURL)
585         : msURL(rURL), mnStartPos(nStart), mnEndPos(0)  {}
586 
SetEndPos(long nEnd)587     void SetEndPos(long nEnd) { mnEndPos = nEnd; }
GetStartPos() const588     long GetStartPos() const {return mnStartPos;}
GetEndPos() const589     long GetEndPos() const {return mnEndPos;}
GetURL() const590     const OUString &GetURL() const {return msURL;}
Adjust(sal_Int32 nAdjust)591     void Adjust(sal_Int32 nAdjust)
592     {
593         mnStartPos-=nAdjust;
594         mnEndPos-=nAdjust;
595     }
596 };
597 
598 namespace
599 {
IsValidSel(const EditEngine & rEngine,const ESelection & rSel)600     bool IsValidSel(const EditEngine& rEngine, const ESelection& rSel)
601     {
602         const auto nParaCount = rEngine.GetParagraphCount();
603         if (rSel.nStartPara < nParaCount && rSel.nEndPara < nParaCount)
604             return rSel.nStartPos >= 0 && rSel.nEndPos >= 0;
605         return false;
606     }
607 }
608 
609 // InsertAttrsAsDrawingAttrs() sets attributes between StartCp and EndCp.
610 // Style attributes are set as hard, paragraph and character attributes.
InsertAttrsAsDrawingAttrs(WW8_CP nStartCp,WW8_CP nEndCp,ManTypes eType,bool bONLYnPicLocFc)611 void SwWW8ImplReader::InsertAttrsAsDrawingAttrs(WW8_CP nStartCp, WW8_CP nEndCp,
612     ManTypes eType, bool bONLYnPicLocFc)
613 {
614     /*
615      Save and create new plcxman for this drawing object, of the type that
616      will include the para end mark inside a paragraph property range, as
617      drawing boxes have real paragraph marks as part of their text, while
618      normal writer has separate nodes for each paragraph and so has no actual
619      paragraph mark as part of the paragraph text.
620     */
621     WW8ReaderSave aSave(this);
622     m_xPlcxMan.reset(new WW8PLCFMan(m_xSBase.get(), eType, nStartCp, true));
623 
624     WW8_CP nStart = m_xPlcxMan->Where();
625     WW8_CP nNext, nStartReplace=0;
626 
627     bool bDoingSymbol = false;
628     sal_Unicode cReplaceSymbol = m_cSymbol;
629 
630     std::unique_ptr<SfxItemSet> pS(new SfxItemSet(m_pDrawEditEngine->GetEmptyItemSet()));
631     WW8PLCFManResult aRes;
632 
633     std::deque<Chunk> aChunks;
634 
635     // Here store stack location
636     size_t nCurrentCount = m_xCtrlStck->size();
637     while (nStart < nEndCp)
638     {
639         // nStart is the beginning of the attributes for this range, and
640         // may be before the text itself. So watch out for that
641         WW8_CP nTextStart = nStart;
642         if (nTextStart < nStartCp)
643             nTextStart = nStartCp;
644 
645         // get position of next SPRM
646         bool bStartAttr = m_xPlcxMan->Get(&aRes);
647         m_nCurrentColl = m_xPlcxMan->GetColl();
648         if (aRes.nSprmId)
649         {
650             if( bONLYnPicLocFc )
651             {
652                 if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) )
653                 {
654                     Read_PicLoc(aRes.nSprmId, aRes.pMemPos +
655                         m_xSprmParser->DistanceToData(aRes.nSprmId), 4);
656                      // Ok, that's what we were looking for.  Now let's get
657                      // out of here!
658                     break;
659                 }
660             }
661             else if ((eFTN > aRes.nSprmId) || (0x0800 <= aRes.nSprmId))
662             {
663                 // Here place them onto our usual stack and we will pop them
664                 // off and convert them later
665                 if (bStartAttr)
666                 {
667                     ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
668                     if (!bDoingSymbol && m_bSymbol)
669                     {
670                         bDoingSymbol = true;
671                         nStartReplace = nTextStart;
672                         cReplaceSymbol = m_cSymbol;
673                     }
674                 }
675                 else
676                 {
677                     EndSprm( aRes.nSprmId );
678                     if (!m_bSymbol && bDoingSymbol)
679                     {
680                         bDoingSymbol = false;
681                         OUStringBuffer sTemp;
682                         comphelper::string::padToLength(sTemp,
683                             nTextStart - nStartReplace, cReplaceSymbol);
684                         m_pDrawEditEngine->QuickInsertText(sTemp.makeStringAndClear(),
685                             GetESelection(*m_pDrawEditEngine, nStartReplace - nStartCp,
686                             nTextStart - nStartCp ) );
687                     }
688                 }
689             }
690             else if (aRes.nSprmId == eFLD)
691             {
692                 if (bStartAttr)
693                 {
694                     size_t nCount = m_xCtrlStck->size();
695                     if (m_aFieldStack.empty() && Read_Field(&aRes))
696                     {
697                         OUString sURL;
698                         for (size_t nI = m_xCtrlStck->size(); nI > nCount; --nI)
699                         {
700                             const SfxPoolItem *pItem = ((*m_xCtrlStck)[nI-1]).pAttr.get();
701                             sal_uInt16 nWhich = pItem->Which();
702                             if (nWhich == RES_TXTATR_INETFMT)
703                             {
704                                 const SwFormatINetFormat *pURL =
705                                     static_cast<const SwFormatINetFormat *>(pItem);
706                                 sURL = pURL->GetValue();
707                             }
708                             m_xCtrlStck->DeleteAndDestroy(nI-1);
709                         }
710                         aChunks.emplace_back(nStart, sURL);
711                     }
712                 }
713                 else
714                 {
715                     if (!m_aFieldStack.empty() && End_Field() && !aChunks.empty())
716                         aChunks.back().SetEndPos(nStart+1);
717                 }
718             }
719         }
720 
721         m_xPlcxMan->advance();
722         nNext = m_xPlcxMan->Where();
723 
724         const WW8_CP nEnd = ( nNext < nEndCp ) ? nNext : nEndCp;
725         if (!bONLYnPicLocFc && nNext != nStart && nEnd >= nStartCp)
726         {
727             SfxItemPool *pEditPool = pS->GetPool();
728 
729             // Here read current properties and convert them into pS
730             // and put those attrs into the draw box if they can be converted
731             // to draw attributes
732             if (m_xCtrlStck->size() - nCurrentCount)
733             {
734                 for (size_t i = nCurrentCount; i < m_xCtrlStck->size(); ++i)
735                 {
736                     const SfxPoolItem *pItem = ((*m_xCtrlStck)[i]).pAttr.get();
737                     sal_uInt16 nWhich = pItem->Which();
738                     if( nWhich < RES_FLTRATTR_BEGIN ||
739                         nWhich >= RES_FLTRATTR_END )
740                     {
741                         sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich);
742                         if (
743                             nSlotId && nWhich != nSlotId &&
744                             0 != (nWhich = pEditPool->GetWhich(nSlotId)) &&
745                             nWhich != nSlotId
746                         )
747                         {
748                             pS->Put( pItem->CloneSetWhich(nWhich) );
749                         }
750                     }
751                 }
752             }
753             // Fill in the remainder from the style
754             InsertTxbxStyAttrs(*pS, m_nCurrentColl);
755 
756             if( pS->Count() )
757             {
758                 m_pDrawEditEngine->QuickSetAttribs( *pS,
759                     GetESelection(*m_pDrawEditEngine, nTextStart - nStartCp, nEnd - nStartCp ) );
760                 pS.reset( new SfxItemSet(m_pDrawEditEngine->GetEmptyItemSet()) );
761             }
762         }
763         nStart = nNext;
764     }
765     pS.reset();
766 
767     // pop off as far as recorded location just in case there were some left
768     // unclosed
769     for (size_t nI = m_xCtrlStck->size(); nI > nCurrentCount; --nI)
770         m_xCtrlStck->DeleteAndDestroy(nI-1);
771 
772     auto aEnd = aChunks.end();
773     for (auto aIter = aChunks.begin(); aIter != aEnd; ++aIter)
774     {
775         ESelection aSel(GetESelection(*m_pDrawEditEngine, aIter->GetStartPos()-nStartCp,
776             aIter->GetEndPos()-nStartCp));
777         if (!IsValidSel(*m_pDrawEditEngine, aSel))
778             continue;
779         OUString aString(m_pDrawEditEngine->GetText(aSel));
780         const sal_Int32 nOrigLen = aString.getLength();
781         WW8_CP nDummy(0);
782         lcl_StripFields(aString, nDummy);
783 
784         sal_Int32 nChanged;
785         if (!aIter->GetURL().isEmpty())
786         {
787             SvxURLField aURL(aIter->GetURL(), aString, SvxURLFormat::AppDefault);
788             m_pDrawEditEngine->QuickInsertField(SvxFieldItem(aURL, EE_FEATURE_FIELD), aSel);
789             nChanged = nOrigLen - 1;
790         }
791         else
792         {
793             m_pDrawEditEngine->QuickInsertText(aString, aSel);
794             nChanged = nOrigLen - aString.getLength();
795         }
796         for (auto aIter2 = aIter+1; aIter2 != aEnd; ++aIter2)
797             aIter2->Adjust(nChanged);
798     }
799 
800     /*
801      Don't worry about the new pPlcxMan, the restore removes it when
802      replacing the current one with the old one.
803     */
804     aSave.Restore(this);
805 }
806 
GetTxbxTextSttEndCp(WW8_CP & rStartCp,WW8_CP & rEndCp,sal_uInt16 nTxBxS,sal_uInt16 nSequence)807 bool SwWW8ImplReader::GetTxbxTextSttEndCp(WW8_CP& rStartCp, WW8_CP& rEndCp,
808     sal_uInt16 nTxBxS, sal_uInt16 nSequence)
809 {
810     // grab the TextBox-PLCF quickly
811     WW8PLCFspecial* pT = m_xPlcxMan ? m_xPlcxMan->GetTxbx() : nullptr;
812     if( !pT )
813     {
814         OSL_ENSURE( false, "+where's the text graphic (1)?" );
815         return false;
816     }
817 
818     // if applicable first find the right TextBox-Story
819     bool bCheckTextBoxStory = ( nTxBxS && pT->GetIMax() >= nTxBxS );
820     if(  bCheckTextBoxStory )
821         pT->SetIdx( nTxBxS-1 );
822 
823     // then determine start and end
824     void* pT0;
825     if (!pT->Get(rStartCp, pT0) || rStartCp < 0)
826     {
827         OSL_ENSURE( false, "+where's the text graphic (2)?" );
828         return false;
829     }
830 
831     if( bCheckTextBoxStory )
832     {
833         bool bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable ));
834         while( bReusable )
835         {
836             pT->advance();
837             if( !pT->Get( rStartCp, pT0 ) )
838             {
839                 OSL_ENSURE( false, "+where's the text graphic (2a)?" );
840                 return false;
841             }
842             bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable ));
843         }
844     }
845     pT->advance();
846     if (!pT->Get(rEndCp, pT0) || rEndCp < 0)
847     {
848         OSL_ENSURE( false, "+where's the text graphic (3)?" );
849         return false;
850     }
851 
852     // find the right page in the break table (if necessary)
853     if( bCheckTextBoxStory )
854     {
855         // special case: entire chain should be determined - done!
856         if( USHRT_MAX > nSequence )
857         {
858             long nMinStartCp = rStartCp;
859             long nMaxEndCp   = rEndCp;
860             // quickly grab the TextBox-Break-Descriptor-PLCF
861             pT = m_xPlcxMan->GetTxbxBkd();
862             if (!pT) // It can occur on occasion, Caolan
863                 return false;
864 
865             // find first entry for this TextBox story
866             if( !pT->SeekPos( rStartCp ) )
867             {
868                 OSL_ENSURE( false, "+where's the text graphic (4)" );
869                 return false;
870             }
871             // if needed skip the appropriate number of entries
872             for (sal_uInt16 iSequence = 0; iSequence < nSequence; ++iSequence)
873                 pT->advance();
874             // and determine actual start and end
875             if(    (!pT->Get( rStartCp, pT0 ))
876                 || ( nMinStartCp > rStartCp  ) )
877             {
878                 OSL_ENSURE( false, "+where's the text graphic (5)?" );
879                 return false;
880             }
881             if( rStartCp >= nMaxEndCp )
882                 rEndCp = rStartCp;  // not an error: empty string
883             else
884             {
885                 pT->advance();
886                 if ( (!pT->Get(rEndCp, pT0)) || (nMaxEndCp < rEndCp-1) )
887                 {
888                     OSL_ENSURE( false, "+where's the text graphic (6)?" );
889                     return false;
890                 }
891                 rEndCp -= 1;
892             }
893         }
894         else
895             rEndCp -= 1;
896     }
897     else
898         rEndCp -= 1;
899     return true;
900 }
901 
902 // TxbxText() grabs the text from the WW file and returns that along with
903 // the StartCp and the corrected (by -2, or -1 for version 8) EndCp.
GetRangeAsDrawingString(OUString & rString,long nStartCp,long nEndCp,ManTypes eType)904 sal_Int32 SwWW8ImplReader::GetRangeAsDrawingString(OUString& rString, long nStartCp, long nEndCp, ManTypes eType)
905 {
906     WW8_CP nOffset = 0;
907     m_xWwFib->GetBaseCp(eType, &nOffset); //TODO: check return value
908 
909     OSL_ENSURE(nStartCp <= nEndCp, "+where's the graphic text (7)?");
910     if (nStartCp == nEndCp)
911         rString.clear();      // empty string: entirely possible
912     else if (nStartCp < nEndCp)
913     {
914         // read the text: can be split into multiple pieces
915         const sal_Int32 nLen = m_xSBase->WW8ReadString(*m_pStrm, rString,
916             nStartCp + nOffset, nEndCp - nStartCp, GetCurrentCharSet());
917         OSL_ENSURE(nLen, "+where's the text graphic (8)?");
918         if (nLen>0)
919         {
920             if( rString[nLen-1]==0x0d )
921                 rString = rString.copy(0, nLen-1);
922 
923             rString = rString.replace( 0xb, 0xa );
924             return nLen;
925         }
926     }
927     return 0;
928 }
929 
930 //EditEngine::InsertText will replace dos lines resulting in a shorter
931 //string than is passed in, so inserting attributes based on the original
932 //string len can fail. So here replace the dos line ends similar to
933 //how EditEngine does it, but preserve the length and replace the extra
934 //chars with placeholders, record the position of the placeholders and
935 //remove those extra chars after attributes have been inserted
replaceDosLineEndsButPreserveLength(OUString & rIn)936 static std::vector<sal_Int32> replaceDosLineEndsButPreserveLength(OUString &rIn)
937 {
938     OUStringBuffer aNewData(rIn);
939     std::vector<sal_Int32> aDosLineEndDummies;
940     sal_Int32 i = 0;
941     sal_Int32 nStrLen = rIn.getLength();
942     while (i < nStrLen)
943     {
944         // \r or \n causes linebreak
945         if (rIn[i] == '\r' || rIn[i] == '\n')
946         {
947             // skip char if \r\n or \n\r
948             if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) &&
949                  (rIn[i] != rIn[i+1]) )
950             {
951                 ++i;
952                 aDosLineEndDummies.push_back(i);
953                 aNewData[i] = 0;
954             }
955         }
956         ++i;
957     }
958     rIn = aNewData.makeStringAndClear();
959     return aDosLineEndDummies;
960 }
961 
removePositions(EditEngine & rDrawEditEngine,const std::vector<sal_Int32> & rDosLineEndDummies)962 static void removePositions(EditEngine &rDrawEditEngine, const std::vector<sal_Int32>& rDosLineEndDummies)
963 {
964     for (auto aIter = rDosLineEndDummies.rbegin(); aIter != rDosLineEndDummies.rend(); ++aIter)
965     {
966         sal_Int32 nCharPos(*aIter);
967         rDrawEditEngine.QuickDelete(GetESelection(rDrawEditEngine, nCharPos, nCharPos+1));
968     }
969 }
970 
ImportAsOutliner(OUString & rString,WW8_CP nStartCp,WW8_CP nEndCp,ManTypes eType)971 std::unique_ptr<OutlinerParaObject> SwWW8ImplReader::ImportAsOutliner(OUString &rString, WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType)
972 {
973     std::unique_ptr<OutlinerParaObject> pRet;
974 
975     sal_Int32 nLen = GetRangeAsDrawingString(rString, nStartCp, nEndCp, eType);
976     if (nLen > 0)
977     {
978         if (!m_pDrawEditEngine)
979         {
980             m_pDrawEditEngine.reset(new EditEngine(nullptr));
981         }
982 
983         //replace dos line endings with editeng ones, replace any extra chars with
984         //placeholders to keep the inserted string len in sync with the attribute cps
985         //and record in aDosLineEnds the superfluous positions
986         OUString sEEString(rString);
987         std::vector<sal_Int32> aDosLineEnds(replaceDosLineEndsButPreserveLength(sEEString));
988         m_pDrawEditEngine->SetText(sEEString);
989         InsertAttrsAsDrawingAttrs(nStartCp, nStartCp+nLen, eType);
990         //remove any superfluous placeholders of replaceDosLineEndsButPreserveLength
991         //after attributes have been inserted
992         removePositions(*m_pDrawEditEngine, aDosLineEnds);
993 
994         // Annotations typically begin with a (useless) 0x5
995         if ((eType == MAN_AND) && m_pDrawEditEngine->GetTextLen())
996         {
997             ESelection aFirstChar(0, 0, 0, 1);
998             if (m_pDrawEditEngine->GetText( aFirstChar ) == "\x05")
999                 m_pDrawEditEngine->QuickDelete(aFirstChar);
1000         }
1001 
1002         std::unique_ptr<EditTextObject> pTemporaryText = m_pDrawEditEngine->CreateTextObject();
1003         pRet.reset( new OutlinerParaObject( std::move(pTemporaryText) ) );
1004         pRet->SetOutlinerMode( OutlinerMode::TextObject );
1005 
1006         m_pDrawEditEngine->SetText( OUString() );
1007         m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet());
1008 
1009         // Strip out fields, leaving the result
1010         WW8_CP nDummy(0);
1011         lcl_StripFields(rString, nDummy);
1012         // Strip out word's special characters for the simple string
1013         rString = rString.replaceAll("\x01", "");
1014         rString = rString.replaceAll("\x05", "");
1015         rString = rString.replaceAll("\x08", "");
1016         rString = rString.replaceAll("\007\007", "\007\012");
1017         rString = rString.replace(0x7, ' ');
1018     }
1019 
1020     return pRet;
1021 }
1022 
1023 // InsertTxbxText() adds the Text and the Attributes for TextBoxes and CaptionBoxes
InsertTxbxText(SdrTextObj * pTextObj,Size const * pObjSiz,sal_uInt16 nTxBxS,sal_uInt16 nSequence,long nPosCp,SwFrameFormat const * pOldFlyFormat,bool bMakeSdrGrafObj,bool & rbEraseTextObj,bool * pbTestTxbxContainsText,long * pnStartCp,long * pnEndCp,bool * pbContainsGraphics,SvxMSDffImportRec const * pRecord)1024 void SwWW8ImplReader::InsertTxbxText(SdrTextObj* pTextObj,
1025     Size const * pObjSiz, sal_uInt16 nTxBxS, sal_uInt16 nSequence, long nPosCp,
1026     SwFrameFormat const * pOldFlyFormat, bool bMakeSdrGrafObj, bool& rbEraseTextObj,
1027     bool* pbTestTxbxContainsText, long* pnStartCp, long* pnEndCp,
1028     bool* pbContainsGraphics, SvxMSDffImportRec const * pRecord)
1029 {
1030     SwFrameFormat* pFlyFormat = nullptr;
1031     sal_uLong nOld = m_pStrm->Tell();
1032 
1033     ManTypes eType = m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX;
1034 
1035     rbEraseTextObj = false;
1036 
1037     OUString aString;
1038     WW8_CP nStartCp, nEndCp;
1039     bool bContainsGraphics = false;
1040     bool bTextWasRead = GetTxbxTextSttEndCp(nStartCp, nEndCp, nTxBxS, nSequence) &&
1041                         GetRangeAsDrawingString(aString, nStartCp, nEndCp, eType) > 0;
1042 
1043     if (!m_pDrawEditEngine)
1044     {
1045         m_pDrawEditEngine.reset(new EditEngine(nullptr));
1046     }
1047     if( pObjSiz )
1048         m_pDrawEditEngine->SetPaperSize( *pObjSiz );
1049 
1050     const OUString aOrigString(aString);
1051     if( bTextWasRead )
1052     {
1053         WW8_CP nNewStartCp = nStartCp;
1054         lcl_StripFields(aString, nNewStartCp);
1055 
1056         if (aString.getLength()!=1)
1057         {
1058             bContainsGraphics = aString.indexOf(0x1)<0 || aString.indexOf(0x8)<0;
1059         }
1060         else        // May be a single graphic or object
1061         {
1062             bool bDone = true;
1063             switch( aString[0] )
1064             {
1065                 case 0x1:
1066                     if (!pbTestTxbxContainsText)
1067                     {
1068                         WW8ReaderSave aSave(this, nNewStartCp -1);
1069                         bool bOldEmbeddObj = m_bEmbeddObj;
1070                         // bEmbeddObj Ordinarily would have been set by field
1071                         // parse, but this is impossible here so...
1072                         m_bEmbeddObj = true;
1073 
1074                         // 1st look for OLE- or Graph-Indicator Sprms
1075                         WW8PLCFx_Cp_FKP* pChp = m_xPlcxMan->GetChpPLCF();
1076                         WW8PLCFxDesc aDesc;
1077                         pChp->GetSprms( &aDesc );
1078                         WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, *m_xSprmParser);
1079 
1080                         for( int nLoop = 0; nLoop < 2; ++nLoop )
1081                         {
1082                             while (aSprmIter.GetSprms())
1083                             {
1084                                 const sal_uInt8 *const pParams(aSprmIter.GetCurrentParams());
1085                                 if (nullptr == pParams)
1086                                     break;
1087                                 sal_uInt16 nCurrentId = aSprmIter.GetCurrentId();
1088                                 switch( nCurrentId )
1089                                 {
1090                                     case     75:
1091                                     case    118:
1092                                     case 0x080A:
1093                                     case 0x0856:
1094                                             Read_Obj(nCurrentId, pParams, 1);
1095                                         break;
1096                                     case     68:  // Read_Pic()
1097                                     case 0x6A03:
1098                                     case NS_sprm::LN_CObjLocation:
1099                                             Read_PicLoc(nCurrentId, pParams, 1);
1100                                         break;
1101                                 }
1102                                 aSprmIter.advance();
1103                             }
1104 
1105                             if( !nLoop )
1106                             {
1107                                 pChp->GetPCDSprms(  aDesc );
1108                                 aSprmIter.SetSprms( aDesc.pMemPos,
1109                                     aDesc.nSprmsLen );
1110                             }
1111                         }
1112                         aSave.Restore(this);
1113                         m_bEmbeddObj=bOldEmbeddObj;
1114 
1115                         // then import either an OLE of a Graphic
1116                         if( m_bObj )
1117                         {
1118                             if( bMakeSdrGrafObj && pTextObj &&
1119                                 pTextObj->getParentSdrObjectFromSdrObject() )
1120                             {
1121                                 // use SdrOleObj/SdrGrafObj instead of
1122                                 // SdrTextObj in this Group
1123 
1124                                 Graphic aGraph;
1125                                 SdrObject* pNew = ImportOleBase(aGraph);
1126 
1127                                 if( !pNew )
1128                                 {
1129                                     pNew = new SdrGrafObj(*m_pDrawModel);
1130                                     static_cast<SdrGrafObj*>(pNew)->SetGraphic(aGraph);
1131                                 }
1132 
1133                                 GrafikCtor();
1134 
1135                                 pNew->SetLogicRect( pTextObj->GetCurrentBoundRect() );
1136                                 pNew->SetLayer( pTextObj->GetLayer() );
1137 
1138                                 pTextObj->getParentSdrObjectFromSdrObject()->GetSubList()->
1139                                     ReplaceObject(pNew, pTextObj->GetOrdNum());
1140                             }
1141                             else
1142                                 pFlyFormat = ImportOle();
1143                             m_bObj = false;
1144                         }
1145                         else
1146                         {
1147                             InsertAttrsAsDrawingAttrs(nNewStartCp, nNewStartCp+1,
1148                                 eType, true);
1149                             pFlyFormat = ImportGraf(bMakeSdrGrafObj ? pTextObj : nullptr,
1150                                 pOldFlyFormat);
1151                         }
1152                     }
1153                     break;
1154                 case 0x8:
1155                     if ( (!pbTestTxbxContainsText) && (!m_bObj) )
1156                         pFlyFormat = Read_GrafLayer( nPosCp );
1157                     break;
1158                 default:
1159                     bDone = false;
1160                     break;
1161             }
1162 
1163             if( bDone )
1164             {
1165                 if( pFlyFormat && pRecord )
1166                 {
1167                     SfxItemSet aFlySet( m_rDoc.GetAttrPool(),
1168                         svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END>{} );
1169 
1170                     tools::Rectangle aInnerDist(   pRecord->nDxTextLeft,
1171                                              pRecord->nDyTextTop,
1172                                              pRecord->nDxTextRight,
1173                                              pRecord->nDyTextBottom  );
1174                     MatchSdrItemsIntoFlySet( pTextObj,
1175                                              aFlySet,
1176                                              pRecord->eLineStyle,
1177                                              pRecord->eLineDashing,
1178                                              pRecord->eShapeType,
1179                                              aInnerDist );
1180 
1181                     pFlyFormat->SetFormatAttr( aFlySet );
1182 
1183                     MapWrapIntoFlyFormat(pRecord, pFlyFormat);
1184                 }
1185                 aString.clear();
1186                 rbEraseTextObj = (nullptr != pFlyFormat);
1187             }
1188         }
1189     }
1190 
1191     if( pnStartCp )
1192         *pnStartCp = nStartCp;
1193     if( pnEndCp )
1194         *pnEndCp = nEndCp;
1195 
1196     if( pbTestTxbxContainsText )
1197         *pbTestTxbxContainsText = bTextWasRead && ! rbEraseTextObj;
1198     else if( !rbEraseTextObj )
1199     {
1200         if( bTextWasRead )
1201         {
1202             m_pDrawEditEngine->SetText(aOrigString);
1203             InsertAttrsAsDrawingAttrs(nStartCp, nEndCp, eType);
1204         }
1205 
1206         bool bVertical = pTextObj->IsVerticalWriting();
1207         std::unique_ptr<EditTextObject> pTemporaryText = m_pDrawEditEngine->CreateTextObject();
1208         std::unique_ptr<OutlinerParaObject> pOp( new OutlinerParaObject(*pTemporaryText) );
1209         pOp->SetOutlinerMode( OutlinerMode::TextObject );
1210         pOp->SetVertical( bVertical );
1211         pTemporaryText.reset();
1212         pTextObj->NbcSetOutlinerParaObject( std::move(pOp) );
1213         pTextObj->SetVerticalWriting(bVertical);
1214 
1215         // For the next TextBox also remove the old paragraph attributes
1216         // and styles, otherwise the next box will start with the wrong
1217         // attributes.
1218         // Course of action: delete text = reduce to one paragraph
1219         //                   and on this one delete the paragraph attributes
1220         //                   and styles
1221         m_pDrawEditEngine->SetText( OUString() );
1222         m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet());
1223     }
1224 
1225     m_pStrm->Seek( nOld );
1226     if (pbContainsGraphics)
1227         *pbContainsGraphics = bContainsGraphics;
1228 }
1229 
TxbxChainContainsRealText(sal_uInt16 nTxBxS,long & rStartCp,long & rEndCp)1230 bool SwWW8ImplReader::TxbxChainContainsRealText(sal_uInt16 nTxBxS, long& rStartCp,
1231     long&  rEndCp)
1232 {
1233     bool bErase, bContainsText;
1234     InsertTxbxText( nullptr,nullptr,nTxBxS,USHRT_MAX,0,nullptr,false, bErase, &bContainsText,
1235         &rStartCp, &rEndCp );
1236     return bContainsText;
1237 }
1238 
1239 // TextBoxes only for Ver67 !!
ReadTextBox(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)1240 SdrObject* SwWW8ImplReader::ReadTextBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
1241 {
1242     bool bDummy;
1243     WW8_DP_TXTBOX aTextB;
1244 
1245     if( !ReadGrafStart( static_cast<void*>(&aTextB), sizeof( aTextB ), pHd, rSet ) )
1246         return nullptr;
1247 
1248     Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
1249                static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
1250     Point aP1( aP0 );
1251     aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
1252     aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
1253 
1254     SdrRectObj* pObj = new SdrRectObj(
1255         *m_pDrawModel,
1256         OBJ_TEXT,
1257         tools::Rectangle(aP0, aP1));
1258 
1259     pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1));
1260     Size aSize( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) ,
1261         static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
1262 
1263     long nStartCpFly,nEndCpFly;
1264     bool bContainsGraphics;
1265     InsertTxbxText(pObj, &aSize, 0, 0, 0, nullptr, false,
1266         bDummy,nullptr,&nStartCpFly,&nEndCpFly,&bContainsGraphics);
1267 
1268     SetStdAttr( rSet, aTextB.aLnt, aTextB.aShd );
1269     SetFill( rSet, aTextB.aFill );
1270 
1271     rSet.Put( SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE ) );
1272     rSet.Put( makeSdrTextAutoGrowWidthItem(false));
1273     rSet.Put( makeSdrTextAutoGrowHeightItem(false));
1274     rSet.Put( makeSdrTextLeftDistItem(  MIN_BORDER_DIST*2 ) );
1275     rSet.Put( makeSdrTextRightDistItem( MIN_BORDER_DIST*2 ) );
1276     rSet.Put( makeSdrTextUpperDistItem( MIN_BORDER_DIST ) );
1277     rSet.Put( makeSdrTextLowerDistItem( MIN_BORDER_DIST ) );
1278 
1279     return pObj;
1280 }
1281 
ReadCaptionBox(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)1282 SdrObject* SwWW8ImplReader::ReadCaptionBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
1283 {
1284     static const SdrCaptionType aCaptA[] = { SdrCaptionType::Type1, SdrCaptionType::Type2,
1285                                        SdrCaptionType::Type3, SdrCaptionType::Type4 };
1286 
1287     WW8_DP_CALLOUT_TXTBOX aCallB;
1288 
1289     if( !ReadGrafStart( static_cast<void*>(&aCallB), sizeof( aCallB ), pHd, rSet ) )
1290         return nullptr;
1291 
1292     sal_uInt16 nCount = SVBT16ToUInt16( aCallB.dpPolyLine.aBits1 ) >> 1 & 0x7fff;
1293     if (nCount < 1)
1294     {
1295         SAL_WARN("sw.ww8", "Short CaptionBox header");
1296         return nullptr;
1297     }
1298 
1299     std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]);
1300 
1301     bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4);      // read points
1302     if (!bCouldRead)
1303     {
1304         SAL_WARN("sw.ww8", "Short CaptionBox header");
1305         return nullptr;
1306     }
1307 
1308     sal_uInt8 nTyp = static_cast<sal_uInt8>(nCount) - 1;
1309     if( nTyp == 1 && SVBT16ToUInt16( xP[0] ) == SVBT16ToUInt16( xP[2] ) )
1310         nTyp = 0;
1311 
1312     Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) +
1313                static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.xa )) + m_nDrawXOfs2,
1314                static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ))
1315                + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.ya )) + m_nDrawYOfs2 );
1316     Point aP1( aP0 );
1317     aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dxa )) );
1318     aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dya )) );
1319     Point aP2( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa ))
1320                 + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadPolyLine.xa ))
1321                 + m_nDrawXOfs2 + static_cast<sal_Int16>(SVBT16ToUInt16( xP[0] )),
1322                static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ))
1323                + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadPolyLine.ya ))
1324                + m_nDrawYOfs2 + static_cast<sal_Int16>(SVBT16ToUInt16( xP[1] )) );
1325     xP.reset();
1326 
1327     SdrCaptionObj* pObj = new SdrCaptionObj(
1328         *m_pDrawModel,
1329         tools::Rectangle(aP0, aP1),
1330         aP2);
1331 
1332     pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1));
1333     Size aSize( static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dxa )),
1334                            static_cast<sal_Int16>(SVBT16ToUInt16(  aCallB.dpheadTxbx.dya )) );
1335     bool bEraseThisObject;
1336 
1337     InsertTxbxText(pObj, &aSize, 0, 0, 0, nullptr, false, bEraseThisObject );
1338 
1339     if( SVBT16ToUInt16( aCallB.dptxbx.aLnt.lnps ) != 5 ) // Is border visible ?
1340         SetStdAttr( rSet, aCallB.dptxbx.aLnt, aCallB.dptxbx.aShd );
1341     else                                                // no -> take lines
1342         SetStdAttr( rSet, aCallB.dpPolyLine.aLnt, aCallB.dptxbx.aShd );
1343     SetFill( rSet, aCallB.dptxbx.aFill );
1344     rSet.Put(SdrCaptionTypeItem(aCaptA[nTyp % SAL_N_ELEMENTS(aCaptA)]));
1345 
1346     return pObj;
1347 }
1348 
ReadGroup(WW8_DPHEAD const * pHd,SfxAllItemSet & rSet)1349 SdrObject *SwWW8ImplReader::ReadGroup(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
1350 {
1351     sal_Int16 nGrouped;
1352 
1353     if( !ReadGrafStart( static_cast<void*>(&nGrouped), sizeof( nGrouped ), pHd, rSet ) )
1354         return nullptr;
1355 
1356 #ifdef OSL_BIGENDIAN
1357     nGrouped = (sal_Int16)OSL_SWAPWORD( nGrouped );
1358 #endif
1359 
1360     m_nDrawXOfs = m_nDrawXOfs + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa ));
1361     m_nDrawYOfs = m_nDrawYOfs + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ));
1362 
1363     SdrObject* pObj = new SdrObjGroup(*m_pDrawModel);
1364 
1365     short nLeft = static_cast<sal_Int16>(SVBT16ToUInt16( pHd->cb )) - sizeof( WW8_DPHEAD );
1366     for (int i = 0; i < nGrouped && nLeft >= static_cast<short>(sizeof(WW8_DPHEAD)); ++i)
1367     {
1368         SfxAllItemSet aSet(m_pDrawModel->GetItemPool());
1369         if (SdrObject *pObject = ReadGrafPrimitive(nLeft, aSet))
1370         {
1371             // first add and then set ItemSet
1372             SdrObjList *pSubGroup = pObj->GetSubList();
1373             OSL_ENSURE(pSubGroup, "Why no sublist available?");
1374             if (pSubGroup)
1375                 pSubGroup->InsertObject(pObject, 0);
1376             pObject->SetMergedItemSetAndBroadcast(aSet);
1377         }
1378     }
1379 
1380     m_nDrawXOfs = m_nDrawXOfs - static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa ));
1381     m_nDrawYOfs = m_nDrawYOfs - static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ));
1382 
1383     return pObj;
1384 }
1385 
ReadGrafPrimitive(short & rLeft,SfxAllItemSet & rSet)1386 SdrObject* SwWW8ImplReader::ReadGrafPrimitive(short& rLeft, SfxAllItemSet &rSet)
1387 {
1388     // This whole archaic word 6 graphic import can probably be refactored
1389     // into an object hierarchy with a little effort.
1390     SdrObject *pRet=nullptr;
1391     WW8_DPHEAD aHd;                         // Read Draw-Primitive-Header
1392     bool bCouldRead = checkRead(*m_pStrm, &aHd, sizeof(WW8_DPHEAD)) &&
1393                       SVBT16ToUInt16(aHd.cb) >= sizeof(WW8_DPHEAD);
1394     OSL_ENSURE(bCouldRead, "Graphic Primitive header short read" );
1395     if (!bCouldRead)
1396     {
1397         rLeft=0;
1398         return pRet;
1399     }
1400 
1401     if( rLeft >= SVBT16ToUInt16(aHd.cb) )    // precautions
1402     {
1403         rSet.Put(SwFormatSurround(css::text::WrapTextMode_THROUGH));
1404         switch (SVBT16ToUInt16(aHd.dpk) & 0xff )
1405         {
1406             case 0:
1407                 pRet = ReadGroup(&aHd, rSet);
1408                 break;
1409             case 1:
1410                 pRet = ReadLine(&aHd, rSet);
1411                 break;
1412             case 2:
1413                 pRet = ReadTextBox(&aHd, rSet);
1414                 break;
1415             case 3:
1416                 pRet = ReadRect(&aHd, rSet);
1417                 break;
1418             case 4:
1419                 pRet = ReadElipse(&aHd, rSet);
1420                 break;
1421             case 5:
1422                 pRet = ReadArc(&aHd, rSet);
1423                 break;
1424             case 6:
1425                 pRet = ReadPolyLine(&aHd, rSet);
1426                 break;
1427             case 7:
1428                 pRet = ReadCaptionBox(&aHd, rSet);
1429                 break;
1430             default:    // unknown
1431                 m_pStrm->SeekRel(SVBT16ToUInt16(aHd.cb) - sizeof(WW8_DPHEAD));
1432                 break;
1433         }
1434     }
1435     else
1436     {
1437         OSL_ENSURE( false, "+Grafik-Overlap" );
1438     }
1439     rLeft = rLeft - SVBT16ToUInt16( aHd.cb );
1440     return pRet;
1441 }
1442 
ReadGrafLayer1(WW8PLCFspecial * pPF,long nGrafAnchorCp)1443 void SwWW8ImplReader::ReadGrafLayer1( WW8PLCFspecial* pPF, long nGrafAnchorCp )
1444 {
1445     pPF->SeekPos( nGrafAnchorCp );
1446     WW8_FC nStartFc;
1447     void* pF0;
1448     if( !pPF->Get( nStartFc, pF0 ) )
1449     {
1450         OSL_ENSURE( false, "+Where is the graphic (2) ?" );
1451         return;
1452     }
1453     WW8_FDOA* pF = static_cast<WW8_FDOA*>(pF0);
1454     if( !SVBT32ToUInt32( pF->fc ) )
1455     {
1456         OSL_ENSURE( false, "+Where is the graphic (3) ?" );
1457         return;
1458     }
1459 
1460     bool bCouldSeek = checkSeek(*m_pStrm, SVBT32ToUInt32(pF->fc));
1461     OSL_ENSURE(bCouldSeek, "Invalid graphic offset");
1462     if (!bCouldSeek)
1463         return;
1464 
1465     // read Draw-Header
1466     WW8_DO aDo;
1467     bool bCouldRead = checkRead(*m_pStrm, &aDo, sizeof(WW8_DO));
1468     OSL_ENSURE(bCouldRead, "Short graphic header");
1469     if (!bCouldRead)
1470         return;
1471 
1472     short nLeft = SVBT16ToUInt16( aDo.cb ) - sizeof( WW8_DO );
1473     while (nLeft > static_cast<short>(sizeof(WW8_DPHEAD)))
1474     {
1475         SfxAllItemSet aSet( m_pDrawModel->GetItemPool() );
1476         if (SdrObject *pObject = ReadGrafPrimitive(nLeft, aSet))
1477         {
1478             m_xWWZOrder->InsertDrawingObject(pObject, SVBT16ToUInt16(aDo.dhgt));
1479 
1480             tools::Rectangle aRect(pObject->GetSnapRect());
1481 
1482             const sal_uInt32 nCntRelTo = 3;
1483 
1484             // Adjustment is horizontally relative to...
1485             static const sal_Int16 aHoriRelOriTab[nCntRelTo] =
1486             {
1487                 text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
1488                 text::RelOrientation::PAGE_FRAME,   // 1 is page margin
1489                 text::RelOrientation::FRAME,          // 2 is relative to paragraph
1490             };
1491 
1492             // Adjustment is vertically relative to...
1493             static const sal_Int16 aVertRelOriTab[nCntRelTo] =
1494             {
1495                 text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
1496                 text::RelOrientation::PAGE_FRAME,   // 1 is page margin
1497                 text::RelOrientation::FRAME,          // 2 is relative to paragraph
1498             };
1499 
1500             const int nXAlign = aDo.bx < nCntRelTo ? aDo.bx : 0;
1501             const int nYAlign = aDo.by < nCntRelTo ? aDo.by : 0;
1502 
1503             aSet.Put(SwFormatHoriOrient(aRect.Left(), text::HoriOrientation::NONE,
1504                 aHoriRelOriTab[ nXAlign ]));
1505             aSet.Put(SwFormatVertOrient(aRect.Top(), text::VertOrientation::NONE,
1506                 aVertRelOriTab[ nYAlign ]));
1507 
1508             SwFrameFormat *pFrame = m_rDoc.getIDocumentContentOperations().InsertDrawObj( *m_pPaM, *pObject, aSet );
1509             pObject->SetMergedItemSet(aSet);
1510 
1511             if (SwDrawFrameFormat *pDrawFrame = dynamic_cast<SwDrawFrameFormat*>(pFrame))
1512             {
1513                 pDrawFrame->PosAttrSet();
1514             }
1515 
1516             AddAutoAnchor(pFrame);
1517         }
1518     }
1519 }
1520 
GetEscherLineMatch(MSO_LineStyle eStyle,MSO_SPT eShapeType,sal_Int32 & rThick)1521 sal_Int32 SwMSDffManager::GetEscherLineMatch(MSO_LineStyle eStyle,
1522     MSO_SPT eShapeType, sal_Int32 &rThick)
1523 {
1524     sal_Int32 nOutsideThick = 0;
1525     /*
1526     Note: In contrast to the regular WinWord table and frame border width,
1527     where the overall border width has to be calculated from the width of *one*
1528     line, the data from ESCHER already contains the overall width [twips]!
1529 
1530     The WinWord default is 15 tw. We take for this our 20 tw line.
1531     (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
1532     0.05 pt hairline.) The hairline we only set by WinWord width up to max.
1533     0.5 pt.
1534     */
1535     switch( eStyle )
1536     {
1537     case mso_lineTriple:
1538     case mso_lineSimple:
1539         nOutsideThick = eShapeType != mso_sptTextBox ? rThick : rThick/2;
1540         break;
1541     case mso_lineDouble:
1542         if (eShapeType == mso_sptTextBox)
1543         {
1544             nOutsideThick = rThick/6;
1545             rThick = rThick*2/3;
1546         }
1547         else
1548             nOutsideThick = rThick*2/3;
1549         break;
1550     case mso_lineThickThin:
1551         if (eShapeType == mso_sptTextBox)
1552         {
1553             nOutsideThick = rThick*3/10;
1554             rThick = rThick*4/5;
1555         }
1556         else
1557             nOutsideThick = rThick*4/5;
1558         break;
1559     case mso_lineThinThick:
1560         {
1561         if (eShapeType == mso_sptTextBox)
1562         {
1563             nOutsideThick = rThick/10;
1564             rThick = rThick*3/5;
1565         }
1566         else
1567             nOutsideThick = rThick*3/5;
1568         }
1569         break;
1570     default:
1571         break;
1572     }
1573     return nOutsideThick;
1574 }
1575 
1576 // Returns the thickness of the line outside the frame, the logic of
1577 // words positioning of borders around floating objects is that of a
1578 // disturbed mind.
MatchSdrBoxIntoFlyBoxItem(const Color & rLineColor,MSO_LineStyle eLineStyle,MSO_LineDashing eDashing,MSO_SPT eShapeType,sal_Int32 & rLineThick,SvxBoxItem & rBox)1579 sal_Int32 SwWW8ImplReader::MatchSdrBoxIntoFlyBoxItem(const Color& rLineColor,
1580     MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, sal_Int32 &rLineThick,
1581     SvxBoxItem& rBox )
1582 {
1583     sal_Int32 nOutsideThick = 0;
1584     if( !rLineThick )
1585         return nOutsideThick;
1586 
1587     SvxBorderLineStyle nIdx = SvxBorderLineStyle::NONE;
1588 
1589     sal_Int32 nLineThick=rLineThick;
1590     nOutsideThick = SwMSDffManager::GetEscherLineMatch(eLineStyle,
1591         eShapeType, rLineThick);
1592 
1593     /*
1594     Note: In contrast to the regular WinWord table and frame border width,
1595     where the overall border width has to be calculated from the width of *one*
1596     line, the data from ESCHER already contains the overall width [twips]!
1597 
1598     The WinWord default is 15 tw. We take for this our 20 tw line.
1599     (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
1600     0.05 pt hairline.) The hairline we only set by WinWord width up to max.
1601     0.5 pt.
1602     */
1603     switch( +eLineStyle )
1604     {
1605     // first the single lines
1606     case mso_lineSimple:
1607         nIdx = SvxBorderLineStyle::SOLID;
1608     break;
1609     // second the double lines
1610     case mso_lineDouble:
1611         nIdx = SvxBorderLineStyle::DOUBLE;
1612     break;
1613     case mso_lineThickThin:
1614         nIdx = SvxBorderLineStyle::THICKTHIN_SMALLGAP;
1615     break;
1616     case mso_lineThinThick:
1617         nIdx = SvxBorderLineStyle::THINTHICK_SMALLGAP;
1618     break;
1619     // We have no triple border, use double instead.
1620     case mso_lineTriple:
1621         nIdx = SvxBorderLineStyle::DOUBLE;
1622     break;
1623     // no line style is set
1624     case MSO_LineStyle(USHRT_MAX):
1625         break;
1626     // erroneously not implemented line style is set
1627     default:
1628         OSL_ENSURE(false, "eLineStyle is not (yet) implemented!");
1629         break;
1630     }
1631 
1632     switch( eDashing )
1633     {
1634         case mso_lineDashGEL:
1635             nIdx = SvxBorderLineStyle::DASHED;
1636             break;
1637         case mso_lineDotGEL:
1638             nIdx = SvxBorderLineStyle::DOTTED;
1639             break;
1640         default:
1641             break;
1642     }
1643 
1644     if (SvxBorderLineStyle::NONE != nIdx)
1645     {
1646         SvxBorderLine aLine;
1647         aLine.SetColor( rLineColor );
1648 
1649         aLine.SetWidth( nLineThick ); // No conversion here, nLineThick is already in twips
1650         aLine.SetBorderLineStyle(nIdx);
1651 
1652         for(SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>())
1653         {
1654             // aLine is cloned by SetLine
1655             rBox.SetLine(&aLine, nLine);
1656         }
1657     }
1658 
1659     return nOutsideThick;
1660 }
1661 
1662 #define WW8ITEMVALUE(ItemSet,Id,Cast)  ItemSet.GetItem<Cast>(Id)->GetValue()
1663 
MatchSdrItemsIntoFlySet(SdrObject const * pSdrObj,SfxItemSet & rFlySet,MSO_LineStyle eLineStyle,MSO_LineDashing eDashing,MSO_SPT eShapeType,tools::Rectangle & rInnerDist)1664 void SwWW8ImplReader::MatchSdrItemsIntoFlySet( SdrObject const * pSdrObj,
1665     SfxItemSet& rFlySet, MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType,
1666     tools::Rectangle& rInnerDist )
1667 {
1668     /*
1669     attributes to be set on the frame
1670     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1671     SwFormatFrameSize       if not set, set here
1672     SvxLRSpaceItem          set here
1673     SvxULSpaceItem          set here
1674     SvxOpaqueItem           (Currently not possible for frames! khz 10.2.1999)
1675     SwFormatSurround        already set
1676     SwFormatVertOrient      already set
1677     SwFormatHoriOrient      already set
1678     SwFormatAnchor          already set
1679     SvxBoxItem              set here
1680     SvxBrushItem            set here
1681     SvxShadowItem           set here
1682     */
1683 
1684     // 1. GraphicObject of documents?
1685     GrafikCtor();
1686 
1687     const SfxItemSet& rOldSet = pSdrObj->GetMergedItemSet();
1688 
1689     // some Items can be taken over directly
1690     static sal_uInt16 const aDirectMatch[]
1691     {
1692         RES_LR_SPACE,   // outer spacing left/right: SvxLRSpaceItem
1693         RES_UL_SPACE    // outer spacing top/bottom: SvxULSpaceItem
1694     };
1695     const SfxPoolItem* pPoolItem;
1696     for(sal_uInt16 i : aDirectMatch)
1697         if( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) )
1698         {
1699             rFlySet.Put( *pPoolItem );
1700         }
1701 
1702     // take new XATTR items directly. Skip old RES_BACKGROUND if new FILLSTYLE taken.
1703     bool bSkipResBackground = false;
1704     SfxItemPool* pPool = rFlySet.GetPool();
1705     if ( pPool )
1706     {
1707         for ( sal_uInt16 i = XATTR_START; i < XATTR_END; ++i )
1708         {
1709             // Not all Fly types support XATTRs - skip unsupported attributes
1710             SfxItemPool* pAttrPool = pPool->GetMasterPool();
1711             while ( pAttrPool && !pAttrPool->IsInRange(i) )
1712                 pAttrPool = pAttrPool->GetSecondaryPool();
1713             if ( !pAttrPool )
1714                 continue;
1715 
1716             if ( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) )
1717             {
1718                 rFlySet.Put( *pPoolItem );
1719                 if ( i == XATTR_FILLSTYLE )
1720                 {
1721                     const drawing::FillStyle eFill = static_cast<const XFillStyleItem*>(pPoolItem)->GetValue();
1722                     // Transparency forced in certain situations when fillstyle is none - use old logic for that case still
1723                     // which is especially needed for export purposes (tdf112618).
1724                     if ( eFill != drawing::FillStyle_NONE )
1725                         bSkipResBackground = true;
1726                 }
1727             }
1728         }
1729     }
1730 
1731     // now calculate the borders and build the box: The unit is needed for the
1732     // frame SIZE!
1733     SvxBoxItem aBox(sw::util::ItemGet<SvxBoxItem>(rFlySet, RES_BOX));
1734     // dashed or solid becomes solid
1735     // WW-default: 0.75 pt = 15 twips
1736     sal_Int32 nLineThick = 15, nOutside=0;
1737 
1738     // check if LineStyle is *really* set!
1739     const SfxPoolItem* pItem;
1740 
1741     SfxItemState eState = rOldSet.GetItemState(XATTR_LINESTYLE,true,&pItem);
1742     if( eState == SfxItemState::SET )
1743     {
1744         // Now, that we know there is a line style we will make use the
1745         // parameter given to us when calling the method...  :-)
1746         const Color aLineColor = rOldSet.Get(XATTR_LINECOLOR).GetColorValue();
1747         nLineThick = WW8ITEMVALUE(rOldSet, XATTR_LINEWIDTH, XLineWidthItem);
1748 
1749         if( !nLineThick )
1750             nLineThick = 1; // for Writer, zero is "no border", so set a minimal value
1751 
1752         nOutside = MatchSdrBoxIntoFlyBoxItem(aLineColor, eLineStyle,
1753             eDashing, eShapeType, nLineThick, aBox);
1754     }
1755 
1756     rInnerDist.AdjustLeft(nLineThick );
1757     rInnerDist.AdjustTop(nLineThick );
1758     rInnerDist.AdjustRight(nLineThick );
1759     rInnerDist.AdjustBottom(nLineThick );
1760 
1761     rInnerDist.AdjustLeft( -(aBox.CalcLineWidth( SvxBoxItemLine::LEFT )) );
1762     rInnerDist.AdjustTop( -(aBox.CalcLineWidth( SvxBoxItemLine::TOP )) );
1763     rInnerDist.AdjustRight( -(aBox.CalcLineWidth( SvxBoxItemLine::RIGHT )) );
1764     rInnerDist.AdjustBottom( -(aBox.CalcLineWidth( SvxBoxItemLine::BOTTOM )) );
1765 
1766     // set distances from box's border to text contained within the box
1767     if( 0 < rInnerDist.Left() )
1768         aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Left()), SvxBoxItemLine::LEFT );
1769     if( 0 < rInnerDist.Top() )
1770         aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Top()), SvxBoxItemLine::TOP );
1771     if( 0 < rInnerDist.Right() )
1772         aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Right()), SvxBoxItemLine::RIGHT );
1773     if( 0 < rInnerDist.Bottom() )
1774         aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Bottom()), SvxBoxItemLine::BOTTOM );
1775 
1776     bool bFixSize = !(WW8ITEMVALUE(rOldSet, SDRATTR_TEXT_AUTOGROWHEIGHT,
1777         SdrOnOffItem));
1778 
1779     // Size: SwFormatFrameSize
1780     if( SfxItemState::SET != rFlySet.GetItemState(RES_FRM_SIZE, false) )
1781     {
1782         const tools::Rectangle& rSnapRect = pSdrObj->GetSnapRect();
1783         // if necessary adapt width and position of the framework: The
1784         // recorded interior is to remain equally large despite thick edges.
1785         rFlySet.Put( SwFormatFrameSize(bFixSize ? ATT_FIX_SIZE : ATT_VAR_SIZE,
1786             rSnapRect.GetWidth()  + 2*nOutside,
1787             rSnapRect.GetHeight() + 2*nOutside) );
1788     }
1789     else // If a size is set, adjust it to consider border thickness
1790     {
1791         SwFormatFrameSize aSize = rFlySet.Get(RES_FRM_SIZE);
1792 
1793         SwFormatFrameSize aNewSize(bFixSize ? ATT_FIX_SIZE : ATT_VAR_SIZE,
1794                                    aSize.GetWidth()  + 2*nOutside,
1795                                    aSize.GetHeight() + 2*nOutside);
1796         aNewSize.SetWidthSizeType(aSize.GetWidthSizeType());
1797         rFlySet.Put( aNewSize );
1798     }
1799 
1800     // Sadly word puts escher borders outside the graphic, but orients the
1801     // graphic in relation to the top left inside the border. We don't
1802     if (nOutside)
1803     {
1804         SwFormatHoriOrient aHori = rFlySet.Get(RES_HORI_ORIENT);
1805         aHori.SetPos(MakeSafePositioningValue(aHori.GetPos()-nOutside));
1806         rFlySet.Put(aHori);
1807 
1808         SwFormatVertOrient aVert = rFlySet.Get(RES_VERT_ORIENT);
1809         aVert.SetPos(aVert.GetPos()-nOutside);
1810         rFlySet.Put(aVert);
1811     }
1812 
1813     // now set the border
1814     rFlySet.Put( aBox );
1815 
1816     // shadow of the box: SvxShadowItem
1817     if( WW8ITEMVALUE(rOldSet, SDRATTR_SHADOW, SdrOnOffItem) )
1818     {
1819         SvxShadowItem aShadow( RES_SHADOW );
1820 
1821         const Color aShdColor = rOldSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue();
1822         const sal_Int32 nShdDistX = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWXDIST,
1823             SdrMetricItem);
1824         const sal_Int32 nShdDistY = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWYDIST,
1825             SdrMetricItem);
1826 
1827         aShadow.SetColor( aShdColor );
1828 
1829         aShadow.SetWidth(writer_cast<sal_uInt16>((std::abs( nShdDistX) +
1830             std::abs( nShdDistY )) / 2 ));
1831 
1832         SvxShadowLocation eShdPosi;
1833         if( 0 <= nShdDistX )
1834         {
1835             if( 0 <= nShdDistY )
1836                 eShdPosi = SvxShadowLocation::BottomRight;
1837             else
1838                 eShdPosi = SvxShadowLocation::TopRight;
1839         }
1840         else
1841         {
1842             if( 0 <= nShdDistY )
1843                 eShdPosi = SvxShadowLocation::BottomLeft;
1844             else
1845                 eShdPosi = SvxShadowLocation::TopLeft;
1846         }
1847         aShadow.SetLocation( eShdPosi );
1848 
1849         rFlySet.Put( aShadow );
1850     }
1851     SvxBrushItem aBrushItem(COL_WHITE, RES_BACKGROUND);
1852     bool bBrushItemOk = false;
1853     sal_uInt8 nTrans = 0;
1854 
1855     // Separate transparency
1856     eState = rOldSet.GetItemState(XATTR_FILLTRANSPARENCE, true, &pItem);
1857     if (!bSkipResBackground && eState == SfxItemState::SET)
1858     {
1859         sal_uInt16 nRes = WW8ITEMVALUE(rOldSet, XATTR_FILLTRANSPARENCE,
1860             XFillTransparenceItem);
1861         nTrans = sal_uInt8((nRes * 0xFE) / 100);
1862         aBrushItem.GetColor().SetTransparency(nTrans);
1863         bBrushItemOk = true;
1864     }
1865 
1866     // Background: SvxBrushItem
1867     eState = rOldSet.GetItemState(XATTR_FILLSTYLE, true, &pItem);
1868     if (!bSkipResBackground && eState == SfxItemState::SET)
1869     {
1870         const drawing::FillStyle eFill = static_cast<const XFillStyleItem*>(pItem)->GetValue();
1871 
1872         switch (eFill)
1873         {
1874             default:
1875             case drawing::FillStyle_NONE:
1876                 // Writer graphics don't have it yet
1877                 if (eShapeType != mso_sptPictureFrame)
1878                 {
1879                     aBrushItem.GetColor().SetTransparency(0xFE);
1880                     bBrushItemOk = true;
1881                 }
1882             break;
1883             case drawing::FillStyle_SOLID:
1884             case drawing::FillStyle_GRADIENT:
1885                 {
1886                     const Color aColor =
1887                         rOldSet.Get(XATTR_FILLCOLOR).GetColorValue();
1888                     aBrushItem.SetColor(aColor);
1889 
1890                     if (bBrushItemOk) // has trans
1891                         aBrushItem.GetColor().SetTransparency(nTrans);
1892 
1893                     bBrushItemOk = true;
1894                 }
1895             break;
1896             case drawing::FillStyle_HATCH:
1897             break;
1898             case drawing::FillStyle_BITMAP:
1899                 {
1900                     GraphicObject aGrfObj(rOldSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
1901                     const bool bTile(WW8ITEMVALUE(rOldSet, XATTR_FILLBMP_TILE, SfxBoolItem));
1902 
1903                     if(bBrushItemOk) // has trans
1904                     {
1905                         GraphicAttr aAttr(aGrfObj.GetAttr());
1906 
1907                         aAttr.SetTransparency(nTrans);
1908                         aGrfObj.SetAttr(aAttr);
1909                     }
1910 
1911                     aBrushItem.SetGraphicObject(aGrfObj);
1912                     aBrushItem.SetGraphicPos(bTile ? GPOS_TILED : GPOS_AREA);
1913                     bBrushItemOk = true;
1914                 }
1915             break;
1916         }
1917     }
1918 
1919     if (bBrushItemOk)
1920         rFlySet.Put(aBrushItem);
1921 }
1922 
AdjustLRWrapForWordMargins(const SvxMSDffImportRec & rRecord,SvxLRSpaceItem & rLR)1923 void SwWW8ImplReader::AdjustLRWrapForWordMargins(
1924     const SvxMSDffImportRec &rRecord, SvxLRSpaceItem &rLR)
1925 {
1926     sal_uInt32 nXRelTo = SvxMSDffImportRec::RELTO_DEFAULT;
1927     if ( rRecord.nXRelTo )
1928     {
1929         nXRelTo = rRecord.nXRelTo.get();
1930     }
1931 
1932     // Left adjustments - if horizontally aligned to left of
1933     // margin or column then remove the left wrapping
1934     if (rRecord.nXAlign == 1)
1935     {
1936         if ((nXRelTo == 0) || (nXRelTo == 2))
1937             rLR.SetLeft(sal_uInt16(0));
1938     }
1939 
1940     // Right adjustments - if horizontally aligned to right of
1941     // margin or column then remove the right wrapping
1942     if (rRecord.nXAlign == 3)
1943     {
1944         if ((nXRelTo == 0) || (nXRelTo == 2))
1945             rLR.SetRight(sal_uInt16(0));
1946     }
1947 
1948     // Inside margin, remove left wrapping
1949     if ((rRecord.nXAlign == 4) && (nXRelTo == 0))
1950     {
1951         rLR.SetLeft(sal_uInt16(0));
1952     }
1953 
1954     // Outside margin, remove left wrapping
1955     if ((rRecord.nXAlign == 5) && (nXRelTo == 0))
1956     {
1957         rLR.SetRight(sal_uInt16(0));
1958     }
1959 }
1960 
AdjustULWrapForWordMargins(const SvxMSDffImportRec & rRecord,SvxULSpaceItem & rUL)1961 void SwWW8ImplReader::AdjustULWrapForWordMargins(
1962     const SvxMSDffImportRec &rRecord, SvxULSpaceItem &rUL)
1963 {
1964     sal_uInt32 nYRelTo = SvxMSDffImportRec::RELTO_DEFAULT;
1965     if ( rRecord.nYRelTo )
1966     {
1967         nYRelTo = rRecord.nYRelTo.get();
1968     }
1969 
1970     // Top adjustment - remove upper wrapping if aligned to page
1971     // printable area or to page
1972     if (rRecord.nYAlign == 1)
1973     {
1974         if ((nYRelTo == 0) || (nYRelTo == 1))
1975             rUL.SetUpper(sal_uInt16(0));
1976     }
1977 
1978     // Bottom adjustment - remove bottom wrapping if aligned to page or
1979     // printable area or to page
1980     if (rRecord.nYAlign == 3)
1981     {
1982         if ((nYRelTo == 0) || (nYRelTo == 1))
1983             rUL.SetLower(sal_uInt16(0));
1984     }
1985 
1986     // Remove top margin if aligned vertically inside margin
1987     if ((rRecord.nYAlign == 4) && (nYRelTo == 0))
1988         rUL.SetUpper(sal_uInt16(0));
1989 }
1990 
MapWrapIntoFlyFormat(SvxMSDffImportRec const * pRecord,SwFrameFormat * pFlyFormat)1991 void SwWW8ImplReader::MapWrapIntoFlyFormat(SvxMSDffImportRec const * pRecord,
1992     SwFrameFormat* pFlyFormat)
1993 {
1994     if (!pRecord || !pFlyFormat)
1995         return;
1996 
1997     if (pRecord->nDxWrapDistLeft || pRecord->nDxWrapDistRight)
1998     {
1999         SvxLRSpaceItem aLR(writer_cast<sal_uInt16>(pRecord->nDxWrapDistLeft),
2000             writer_cast<sal_uInt16>(pRecord->nDxWrapDistRight), 0, 0, RES_LR_SPACE);
2001         AdjustLRWrapForWordMargins(*pRecord, aLR);
2002         pFlyFormat->SetFormatAttr(aLR);
2003     }
2004     if (pRecord->nDyWrapDistTop || pRecord->nDyWrapDistBottom)
2005     {
2006         SvxULSpaceItem aUL(writer_cast<sal_uInt16>(pRecord->nDyWrapDistTop),
2007             writer_cast<sal_uInt16>(pRecord->nDyWrapDistBottom), RES_UL_SPACE);
2008         AdjustULWrapForWordMargins(*pRecord, aUL);
2009         pFlyFormat->SetFormatAttr(aUL);
2010     }
2011 
2012     // If we are contoured and have a custom polygon...
2013     if (pRecord->pWrapPolygon && pFlyFormat->GetSurround().IsContour())
2014     {
2015         if (SwNoTextNode *pNd = GetNoTextNodeFromSwFrameFormat(*pFlyFormat))
2016         {
2017 
2018             /*
2019              Gather round children and hear of a tale that will raise the
2020              hairs on the back of your neck this dark halloween night.
2021 
2022              There is a polygon in word that describes the wrapping around
2023              the graphic.
2024 
2025              Here are some sample values for the simplest case of a square
2026              around some solid coloured graphics
2027 
2028                                 X       Y       Pixel size of graphic
2029                 TopLeft         -54     21600   400x400
2030                 Bottom Right    0       21546
2031 
2032                 TopLeft         -108    21600   200x200
2033                 Bottom Right    0       21492
2034 
2035                 TopLeft         -216    21600   100x100
2036                 Bottom Right    0       21384
2037 
2038                 TopLeft         -432    21600   50x50
2039                 Bottom Right    0       21168
2040 
2041                 TopLeft         -76     21600   283x212
2042                 Bottom Right    0       21498
2043 
2044              So given that the size of the values remains pretty much the
2045              same despite the size of the graphic, we can tell that the
2046              polygon is measured in units that are independent of the
2047              graphic. But why does the left corner move a different value
2048              to the left each time, and why does the bottom move upwards
2049              each time, when the right and top remain at the same value ?
2050 
2051              I have no idea, but clearly once we calculate the values out
2052              we see that the left margin is always a fixed realworld
2053              distance from the true left and the polygon bottom is the same
2054              fixed value from the bottom. i.e. 15twips.
2055 
2056              So here we take our word provided polygon, shift it to the
2057              right by 15twips and rescale it widthwise to shrink the width
2058              a little to fit the now moved right margin back to where it
2059              was, and stretch the height a little to make the bottom move
2060              down the missing 15twips then we get a polygon that matches
2061              what I actually see in word
2062             */
2063 
2064             tools::PolyPolygon aPoly(*pRecord->pWrapPolygon);
2065             const Size &rSize = pNd->GetTwipSize();
2066             /*
2067              Move to the left by 15twips, and rescale to
2068              a) shrink right bound back to orig position
2069              b) stretch bottom bound to where I think it should have been
2070              in the first place
2071             */
2072             Fraction aMoveHack(ww::nWrap100Percent, rSize.Width());
2073             aMoveHack *= Fraction(15, 1);
2074             long nMove(aMoveHack);
2075             aPoly.Move(nMove, 0);
2076 
2077             Fraction aHackX(ww::nWrap100Percent, ww::nWrap100Percent + nMove);
2078             Fraction aHackY(ww::nWrap100Percent, ww::nWrap100Percent - nMove);
2079             aPoly.Scale(double(aHackX), double(aHackY));
2080 
2081             // Turn polygon back into units that match the graphic's
2082             const Size &rOrigSize = pNd->GetGraphic().GetPrefSize();
2083             Fraction aMapPolyX(rOrigSize.Width(), ww::nWrap100Percent);
2084             Fraction aMapPolyY(rOrigSize.Height(), ww::nWrap100Percent);
2085             aPoly.Scale(double(aMapPolyX), double(aMapPolyY));
2086 
2087             // #i47277# - contour is already in unit of the
2088             // graphic preferred unit. Thus, call method <SetContour(..)>
2089             pNd->SetContour(&aPoly);
2090         }
2091     }
2092     else if (pFlyFormat->GetSurround().IsContour())
2093     {
2094         // Contour is enabled, but no polygon is set: disable contour, because Word does not
2095         // Writer-style auto-contour in that case.
2096         SwFormatSurround aSurround(pFlyFormat->GetSurround());
2097         aSurround.SetContour(false);
2098         pFlyFormat->SetFormatAttr(aSurround);
2099     }
2100 }
2101 
lcl_ConvertCrop(sal_uInt32 const nCrop,sal_Int32 const nSize)2102 static sal_Int32 lcl_ConvertCrop(sal_uInt32 const nCrop, sal_Int32 const nSize)
2103 {
2104     // cast to sal_Int32 to handle negative crop properly
2105     sal_Int32 const nIntegral(static_cast<sal_Int32>(nCrop) >> 16);
2106     // fdo#77454: heuristic to detect mangled values written by old OOo/LO
2107     if (abs(nIntegral) >= 50) // FIXME: what's a good cut-off?
2108     {
2109         SAL_INFO("sw.ww8", "ignoring suspiciously large crop: " << nIntegral);
2110         return 0;
2111     }
2112     return (nIntegral * nSize) + (((nCrop & 0xffff) * nSize) >> 16);
2113 }
2114 
2115 void
SetAttributesAtGrfNode(SvxMSDffImportRec const * const pRecord,SwFrameFormat const * pFlyFormat,WW8_FSPA const * pF)2116 SwWW8ImplReader::SetAttributesAtGrfNode(SvxMSDffImportRec const*const pRecord,
2117     SwFrameFormat const *pFlyFormat, WW8_FSPA const *pF )
2118 {
2119     const SwNodeIndex* pIdx = pFlyFormat->GetContent(false).GetContentIdx();
2120     SwGrfNode *const pGrfNd(
2121         pIdx ? m_rDoc.GetNodes()[pIdx->GetIndex() + 1]->GetGrfNode() : nullptr);
2122     if (pGrfNd)
2123     {
2124         Size aSz(pGrfNd->GetTwipSize());
2125         // use type <sal_uInt64> instead of sal_uLong to get correct results
2126         // in the following calculations.
2127         sal_uInt64 nHeight = aSz.Height();
2128         sal_uInt64 nWidth  = aSz.Width();
2129         if (!nWidth && pF)
2130             nWidth = o3tl::saturating_sub(pF->nXaRight, pF->nXaLeft);
2131         else if (!nHeight && pF)
2132             nHeight = o3tl::saturating_sub(pF->nYaBottom, pF->nYaTop);
2133 
2134         if( pRecord->nCropFromTop || pRecord->nCropFromBottom ||
2135             pRecord->nCropFromLeft || pRecord->nCropFromRight )
2136         {
2137             SwCropGrf aCrop;            // Cropping is stored in 'fixed floats'
2138                                         // 16.16 (fraction times total
2139             if( pRecord->nCropFromTop ) //        image width or height resp.)
2140             {
2141                 aCrop.SetTop(lcl_ConvertCrop(pRecord->nCropFromTop, nHeight));
2142             }
2143             if( pRecord->nCropFromBottom )
2144             {
2145                 aCrop.SetBottom(lcl_ConvertCrop(pRecord->nCropFromBottom, nHeight));
2146             }
2147             if( pRecord->nCropFromLeft )
2148             {
2149                 aCrop.SetLeft(lcl_ConvertCrop(pRecord->nCropFromLeft, nWidth));
2150             }
2151             if( pRecord->nCropFromRight )
2152             {
2153                 aCrop.SetRight(lcl_ConvertCrop(pRecord->nCropFromRight, nWidth));
2154             }
2155 
2156             pGrfNd->SetAttr( aCrop );
2157         }
2158 
2159         bool bFlipH(pRecord->nFlags & ShapeFlag::FlipH);
2160         bool bFlipV(pRecord->nFlags & ShapeFlag::FlipV);
2161         if ( bFlipH || bFlipV )
2162         {
2163             SwMirrorGrf aMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
2164             if( bFlipH )
2165             {
2166                 if( bFlipV )
2167                     aMirror.SetValue(MirrorGraph::Both);
2168                 else
2169                     aMirror.SetValue(MirrorGraph::Vertical);
2170             }
2171             else
2172                 aMirror.SetValue(MirrorGraph::Horizontal);
2173 
2174             pGrfNd->SetAttr( aMirror );
2175         }
2176 
2177         if (pRecord->pObj)
2178         {
2179             const SfxItemSet& rOldSet = pRecord->pObj->GetMergedItemSet();
2180             // contrast
2181             if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFCONTRAST,
2182                 SdrGrafContrastItem))
2183             {
2184                 SwContrastGrf aContrast(
2185                     WW8ITEMVALUE(rOldSet,
2186                     SDRATTR_GRAFCONTRAST, SdrGrafContrastItem));
2187                 pGrfNd->SetAttr( aContrast );
2188             }
2189 
2190             // luminance
2191             if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFLUMINANCE,
2192                 SdrGrafLuminanceItem))
2193             {
2194                 SwLuminanceGrf aLuminance(WW8ITEMVALUE(rOldSet,
2195                     SDRATTR_GRAFLUMINANCE, SdrGrafLuminanceItem));
2196                 pGrfNd->SetAttr( aLuminance );
2197             }
2198             // gamma
2199             if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA, SdrGrafGamma100Item))
2200             {
2201                 double fVal = WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA,
2202                     SdrGrafGamma100Item);
2203                 pGrfNd->SetAttr(SwGammaGrf(fVal/100.));
2204             }
2205 
2206             // drawmode
2207             auto nGrafMode = rOldSet.GetItem<SdrGrafModeItem>(SDRATTR_GRAFMODE)->GetValue();
2208             if ( nGrafMode != GraphicDrawMode::Standard)
2209             {
2210                 SwDrawModeGrf aDrawMode( nGrafMode );
2211                 pGrfNd->SetAttr( aDrawMode );
2212             }
2213         }
2214     }
2215 }
2216 
CreateContactObject(SwFrameFormat * pFlyFormat)2217 SdrObject* SwWW8ImplReader::CreateContactObject(SwFrameFormat* pFlyFormat)
2218 {
2219     if (pFlyFormat)
2220     {
2221         SdrObject* pNewObject = m_bNewDoc ? nullptr : pFlyFormat->FindRealSdrObject();
2222         if (!pNewObject)
2223             pNewObject = pFlyFormat->FindSdrObject();
2224         if (!pNewObject && dynamic_cast< const SwFlyFrameFormat *>( pFlyFormat ) !=  nullptr)
2225         {
2226             SwFlyDrawContact* pContactObject(static_cast<SwFlyFrameFormat*>(pFlyFormat)->GetOrCreateContact());
2227             pNewObject = pContactObject->GetMaster();
2228         }
2229         return pNewObject;
2230     }
2231     return nullptr;
2232 }
2233 
2234 // Miserable miserable hack to fudge word's graphic layout in RTL mode to ours.
MiserableRTLGraphicsHack(SwTwips & rLeft,SwTwips nWidth,sal_Int16 eHoriOri,sal_Int16 eHoriRel)2235 bool SwWW8ImplReader::MiserableRTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth,
2236     sal_Int16 eHoriOri, sal_Int16 eHoriRel)
2237 {
2238     if (!IsRightToLeft())
2239         return false;
2240     return RTLGraphicsHack(rLeft, nWidth, eHoriOri, eHoriRel,
2241             m_aSectionManager.GetPageLeft(),
2242             m_aSectionManager.GetPageRight(),
2243             m_aSectionManager.GetPageWidth());
2244 }
2245 
ProcessEscherAlign(SvxMSDffImportRec * pRecord,WW8_FSPA * pFSPA,SfxItemSet & rFlySet)2246 RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec* pRecord,
2247     WW8_FSPA *pFSPA, SfxItemSet &rFlySet)
2248 {
2249     OSL_ENSURE(pRecord || pFSPA, "give me something! to work with for anchoring");
2250     if (!pRecord && !pFSPA)
2251         return RndStdIds::FLY_AT_PAGE;
2252     bool bCurSectionVertical = m_aSectionManager.CurrentSectionIsVertical();
2253 
2254     SvxMSDffImportRec aRecordFromFSPA;
2255     if (!pRecord)
2256         pRecord = &aRecordFromFSPA;
2257     if (!(pRecord->nXRelTo) && pFSPA)
2258     {
2259         pRecord->nXRelTo = sal_Int32(pFSPA->nbx);
2260     }
2261     if (!(pRecord->nYRelTo) && pFSPA)
2262     {
2263         pRecord->nYRelTo = sal_Int32(pFSPA->nby);
2264     }
2265 
2266     // nXAlign - abs. Position, Left,  Centered,  Right,  Inside, Outside
2267     // nYAlign - abs. Position, Top,   Centered,  Bottom, Inside, Outside
2268 
2269     // nXRelTo - Page printable area, Page,  Column,    Character
2270     // nYRelTo - Page printable area, Page,  Paragraph, Line
2271 
2272     const sal_uInt32 nCntXAlign = 6;
2273     const sal_uInt32 nCntYAlign = 6;
2274 
2275     const sal_uInt32 nCntRelTo  = 4;
2276 
2277     sal_uInt32 nXAlign = nCntXAlign > pRecord->nXAlign ? pRecord->nXAlign : 1;
2278     sal_uInt32 nYAlign = nCntYAlign > pRecord->nYAlign ? pRecord->nYAlign : 1;
2279 
2280     if (pFSPA)
2281     {
2282         // #i52565# - try to handle special case for objects in tables regarding its X Rel
2283 
2284         // if X and Y Rel values are on default take it as a hint, that they have not been set
2285         // by <SwMSDffManager::ProcessObj(..)>
2286         const bool bXYRelHaveDefaultValues = pRecord->nXRelTo.get() == 2 && pRecord->nYRelTo.get() == 2;
2287         if ( bXYRelHaveDefaultValues
2288              && m_nInTable > 0
2289              && !bCurSectionVertical )
2290         {
2291             if ( sal_uInt32(pFSPA->nby) != pRecord->nYRelTo )
2292             {
2293                 pRecord->nYRelTo = sal_uInt32(pFSPA->nby);
2294             }
2295         }
2296     }
2297 
2298     sal_uInt32 nXRelTo = (pRecord->nXRelTo && nCntRelTo > pRecord->nXRelTo) ? pRecord->nXRelTo.get() : 1;
2299     sal_uInt32 nYRelTo = (pRecord->nYRelTo && nCntRelTo > pRecord->nYRelTo) ? pRecord->nYRelTo.get() : 1;
2300 
2301     RndStdIds eAnchor = IsInlineEscherHack() ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_CHAR; // #i43718#
2302 
2303     SwFormatAnchor aAnchor( eAnchor );
2304     aAnchor.SetAnchor( m_pPaM->GetPoint() );
2305     rFlySet.Put( aAnchor );
2306 
2307     if (pFSPA)
2308     {
2309         // #i18732#
2310         // Given new layout where everything is changed to be anchored to
2311         // character the following 4 tables may need to be changed.
2312 
2313         // horizontal Adjustment
2314         static const sal_Int16 aHoriOriTab[ nCntXAlign ] =
2315         {
2316             text::HoriOrientation::NONE,     // From left position
2317             text::HoriOrientation::LEFT,     // left
2318             text::HoriOrientation::CENTER,   // centered
2319             text::HoriOrientation::RIGHT,    // right
2320             // #i36649#
2321             // - inside -> text::HoriOrientation::LEFT and outside -> text::HoriOrientation::RIGHT
2322             text::HoriOrientation::LEFT,   // inside
2323             text::HoriOrientation::RIGHT   // outside
2324         };
2325 
2326         // generic vertical Adjustment
2327         static const sal_Int16 aVertOriTab[ nCntYAlign ] =
2328         {
2329             text::VertOrientation::NONE,         // From Top position
2330             text::VertOrientation::TOP,          // top
2331             text::VertOrientation::CENTER,       // centered
2332             text::VertOrientation::BOTTOM,       // bottom
2333             text::VertOrientation::LINE_TOP,     // inside (obscure)
2334             text::VertOrientation::LINE_BOTTOM   // outside (obscure)
2335         };
2336 
2337         // #i22673# - to-line vertical alignment
2338         static const sal_Int16 aToLineVertOriTab[ nCntYAlign ] =
2339         {
2340             text::VertOrientation::NONE,         // below
2341             text::VertOrientation::LINE_BOTTOM,  // top
2342             text::VertOrientation::LINE_CENTER,  // centered
2343             text::VertOrientation::LINE_TOP,     // bottom
2344             text::VertOrientation::LINE_BOTTOM,  // inside (obscure)
2345             text::VertOrientation::LINE_TOP      // outside (obscure)
2346         };
2347 
2348         // Adjustment is horizontally relative to...
2349         static const sal_Int16 aHoriRelOriTab[nCntRelTo] =
2350         {
2351             text::RelOrientation::PAGE_PRINT_AREA,    // 0 is page textarea margin
2352             text::RelOrientation::PAGE_FRAME,  // 1 is page margin
2353             text::RelOrientation::FRAME,         // 2 is relative to column
2354             text::RelOrientation::CHAR       // 3 is relative to character
2355         };
2356 
2357         // Adjustment is vertically relative to...
2358         // #i22673# - adjustment for new vertical alignment at top of line.
2359         static const sal_Int16 aVertRelOriTab[nCntRelTo] =
2360         {
2361             text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
2362             text::RelOrientation::PAGE_FRAME,   // 1 is page margin
2363             text::RelOrientation::FRAME,          // 2 is relative to paragraph
2364             text::RelOrientation::TEXT_LINE   // 3 is relative to line
2365         };
2366 
2367         sal_Int16 eHoriOri = aHoriOriTab[ nXAlign ];
2368         sal_Int16 eHoriRel = aHoriRelOriTab[  nXRelTo ];
2369 
2370         // #i36649# - adjustments for certain alignments
2371         if ( eHoriOri == text::HoriOrientation::LEFT && eHoriRel == text::RelOrientation::PAGE_FRAME )
2372         {
2373             // convert 'left to page' to 'from left -<width> to page text area'
2374             eHoriOri = text::HoriOrientation::NONE;
2375             eHoriRel = text::RelOrientation::PAGE_PRINT_AREA;
2376             const long nWidth = pFSPA->nXaRight - pFSPA->nXaLeft;
2377             pFSPA->nXaLeft = -nWidth;
2378             pFSPA->nXaRight = 0;
2379         }
2380         else if ( eHoriOri == text::HoriOrientation::RIGHT && eHoriRel == text::RelOrientation::PAGE_FRAME )
2381         {
2382             // convert 'right to page' to 'from left 0 to right page border'
2383             eHoriOri = text::HoriOrientation::NONE;
2384             eHoriRel = text::RelOrientation::PAGE_RIGHT;
2385             const long nWidth = pFSPA->nXaRight - pFSPA->nXaLeft;
2386             pFSPA->nXaLeft = 0;
2387             pFSPA->nXaRight = nWidth;
2388         }
2389 
2390         // #i24255# - position of floating screen objects in
2391         // R2L layout are given in L2R layout, thus convert them of all
2392         // floating screen objects, which are imported.
2393         {
2394             // Miserable miserable hack.
2395             SwTwips nWidth = o3tl::saturating_sub(pFSPA->nXaRight, pFSPA->nXaLeft);
2396             SwTwips nLeft = pFSPA->nXaLeft;
2397             if (MiserableRTLGraphicsHack(nLeft, nWidth, eHoriOri,
2398                 eHoriRel))
2399             {
2400                 pFSPA->nXaLeft = nLeft;
2401                 pFSPA->nXaRight = pFSPA->nXaLeft + nWidth;
2402             }
2403         }
2404 
2405         // if the object is anchored inside a table cell, is horizontal aligned
2406         // at frame|character and has wrap through, but its attribute
2407         // 'layout in table cell' isn't set, convert its horizontal alignment to page text area.
2408         // #i84783# - use new method <IsObjectLayoutInTableCell()>
2409         if ( m_nInTable &&
2410              ( eHoriRel == text::RelOrientation::FRAME || eHoriRel == text::RelOrientation::CHAR ) &&
2411              pFSPA->nwr == 3 &&
2412              !IsObjectLayoutInTableCell( pRecord->nLayoutInTableCell ) )
2413         {
2414             eHoriRel = text::RelOrientation::PAGE_PRINT_AREA;
2415         }
2416 
2417         // Writer honours this wrap distance when aligned as "left" or "right",
2418         // Word doesn't. Writer doesn't honour it when its "from left".
2419         if (eHoriOri == text::HoriOrientation::LEFT)
2420             pRecord->nDxWrapDistLeft=0;
2421         else if (eHoriOri == text::HoriOrientation::RIGHT)
2422             pRecord->nDxWrapDistRight=0;
2423 
2424         sal_Int16 eVertRel;
2425 
2426         eVertRel = aVertRelOriTab[  nYRelTo ]; // #i18732#
2427         if ( bCurSectionVertical && nYRelTo == 2 )
2428             eVertRel = text::RelOrientation::PAGE_PRINT_AREA;
2429         // #i22673# - fill <eVertOri> in dependence of <eVertRel>
2430         sal_Int16 eVertOri;
2431         if ( eVertRel == text::RelOrientation::TEXT_LINE )
2432         {
2433             eVertOri = aToLineVertOriTab[ nYAlign ];
2434         }
2435         else
2436         {
2437             eVertOri = aVertOriTab[ nYAlign ];
2438         }
2439 
2440         // Below line in word is a positive value, while in writer its
2441         // negative
2442         long nYPos = pFSPA->nYaTop;
2443         // #i22673#
2444         if ((eVertRel == text::RelOrientation::TEXT_LINE) && (eVertOri == text::VertOrientation::NONE))
2445             nYPos = -nYPos;
2446 
2447         SwFormatHoriOrient aHoriOri(MakeSafePositioningValue(  bCurSectionVertical ? nYPos : pFSPA->nXaLeft ),
2448                                                             bCurSectionVertical ? eVertOri : eHoriOri,
2449                                                             bCurSectionVertical ? eVertRel : eHoriRel);
2450         if( 4 <= nXAlign )
2451             aHoriOri.SetPosToggle(true);
2452         rFlySet.Put( aHoriOri );
2453 
2454         rFlySet.Put(SwFormatVertOrient(MakeSafePositioningValue( !bCurSectionVertical ? nYPos : -pFSPA->nXaRight ),
2455                                                                 !bCurSectionVertical ? eVertOri : eHoriOri,
2456                                                                 !bCurSectionVertical ? eVertRel : eHoriRel ));
2457     }
2458 
2459     return eAnchor;
2460 }
2461 
2462 // #i84783#
IsObjectLayoutInTableCell(const sal_uInt32 nLayoutInTableCell) const2463 bool SwWW8ImplReader::IsObjectLayoutInTableCell( const sal_uInt32 nLayoutInTableCell ) const
2464 {
2465     bool bIsObjectLayoutInTableCell = false;
2466 
2467     if ( m_bVer8 )
2468     {
2469         sal_uInt16 nWWVersion = m_xWwFib->m_nProduct & 0xE000;
2470         if (nWWVersion == 0)
2471         {
2472             // 0 nProduct can happen for Word >97 as well, check cswNew in this case instead.
2473             if (m_xWwFib->m_cswNew > 0)
2474             {
2475                 // This is Word >=2000.
2476                 nWWVersion = 0x2000;
2477             }
2478         }
2479 
2480         switch ( nWWVersion )
2481         {
2482             case 0x0000: // version 8 aka Microsoft Word 97
2483             {
2484                 bIsObjectLayoutInTableCell = false;
2485                 OSL_ENSURE( nLayoutInTableCell == 0xFFFFFFFF,
2486                         "no explicit object attribute layout in table cell expected." );
2487             }
2488             break;
2489             case 0x2000: // version 9 aka Microsoft Word 2000
2490             case 0x4000: // version 10 aka Microsoft Word 2002
2491             case 0x6000: // version 11 aka Microsoft Word 2003
2492             case 0x8000: // version 12 aka Microsoft Word 2007
2493             case 0xC000: // version 14 aka Microsoft Word 2010
2494             {
2495                 // #i98037#
2496                 // adjustment of conditions needed after deeper analysis of
2497                 // certain test cases.
2498                 if ( nLayoutInTableCell == 0xFFFFFFFF || // no explicit attribute value given
2499                      nLayoutInTableCell == 0x80008000 ||
2500                      ( nLayoutInTableCell & 0x02000000 &&
2501                        !(nLayoutInTableCell & 0x80000000 ) ) )
2502                 {
2503                     bIsObjectLayoutInTableCell = true;
2504                 }
2505                 else
2506                 {
2507                     // Documented in [MS-ODRAW], 2.3.4.44 "Group Shape Boolean Properties".
2508                     bool fUsefLayoutInCell = (nLayoutInTableCell & 0x80000000) >> 31;
2509                     bool fLayoutInCell = (nLayoutInTableCell & 0x8000) >> 15;
2510                     bIsObjectLayoutInTableCell = fUsefLayoutInCell && fLayoutInCell;
2511                 }
2512             }
2513             break;
2514             default:
2515             {
2516                 OSL_FAIL( "unknown version." );
2517             }
2518         }
2519     }
2520 
2521     return bIsObjectLayoutInTableCell;
2522 }
2523 
Read_GrafLayer(long nGrafAnchorCp)2524 SwFrameFormat* SwWW8ImplReader::Read_GrafLayer( long nGrafAnchorCp )
2525 {
2526     if( m_nIniFlags & WW8FL_NO_GRAFLAYER )
2527         return nullptr;
2528 
2529     ::SetProgressState(m_nProgress, m_pDocShell);     // Update
2530 
2531     m_nDrawCpO = 0;
2532     m_bDrawCpOValid = m_xWwFib->GetBaseCp(m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX, &m_nDrawCpO);
2533 
2534     GrafikCtor();
2535 
2536     WW8PLCFspecial* pPF = m_xPlcxMan->GetFdoa();
2537     if( !pPF )
2538     {
2539         OSL_ENSURE( false, "Where is the graphic (1) ?" );
2540         return nullptr;
2541     }
2542 
2543     if( m_bVer67 )
2544     {
2545         long nOldPos = m_pStrm->Tell();
2546 
2547         m_nDrawXOfs = m_nDrawYOfs = 0;
2548         ReadGrafLayer1( pPF, nGrafAnchorCp );
2549 
2550         m_pStrm->Seek( nOldPos );
2551         return nullptr;
2552     }
2553 
2554     // Normal case of Word 8+ version stuff
2555     pPF->SeekPos( nGrafAnchorCp );
2556 
2557     WW8_FC nStartFc;
2558     void* pF0;
2559     if( !pPF->Get( nStartFc, pF0 ) ){
2560         OSL_ENSURE( false, "+Where is the graphic (2) ?" );
2561         return nullptr;
2562     }
2563 
2564     WW8_FSPA_SHADOW* pFS = static_cast<WW8_FSPA_SHADOW*>(pF0);
2565     WW8_FSPA*        pF;
2566     WW8_FSPA aFSFA;
2567     pF = &aFSFA;
2568     WW8FSPAShadowToReal( pFS, pF );
2569     if( !pF->nSpId )
2570     {
2571         OSL_ENSURE( false, "+Where is the graphic (3) ?" );
2572         return nullptr;
2573     }
2574 
2575     if (!m_xMSDffManager->GetModel())
2576          m_xMSDffManager->SetModel(m_pDrawModel, 1440);
2577 
2578     tools::Rectangle aRect(pF->nXaLeft,  pF->nYaTop, pF->nXaRight, pF->nYaBottom);
2579     SvxMSDffImportData aData( aRect );
2580 
2581     /*
2582     #i20540#
2583     The SdrOle2Obj will try and manage any ole objects it finds, causing all
2584     sorts of trouble later on
2585     */
2586     SwDocShell* pPersist = m_rDoc.GetDocShell();
2587     m_rDoc.SetDocShell(nullptr);         // #i20540# Persist guard
2588 
2589     SdrObject* pObject = nullptr;
2590     bool bOk = (m_xMSDffManager->GetShape(pF->nSpId, pObject, aData) && pObject);
2591 
2592     m_rDoc.SetDocShell(pPersist);  // #i20540# Persist guard
2593 
2594     if (!bOk)
2595     {
2596         OSL_ENSURE( false, "Where is the Shape ?" );
2597         return nullptr;
2598     }
2599 
2600     // tdf#118375 Word relates position to the unrotated rectangle,
2601     // Writer uses the rotated one.
2602     if (pObject->GetRotateAngle())
2603     {
2604         tools::Rectangle aObjSnapRect(pObject->GetSnapRect()); // recalculates the SnapRect
2605         pF->nXaLeft = aObjSnapRect.Left();
2606         pF->nYaTop = aObjSnapRect.Top();
2607         pF->nXaRight = aObjSnapRect.Right();
2608         pF->nYaBottom = aObjSnapRect.Bottom();
2609     }
2610 
2611     bool bDone = false;
2612     SdrObject* pOurNewObject = nullptr;
2613     bool bReplaceable = false;
2614 
2615     switch (SdrObjKind(pObject->GetObjIdentifier()))
2616     {
2617         case OBJ_GRAF:
2618             bReplaceable = true;
2619             bDone = true;
2620             break;
2621         case OBJ_OLE2:
2622             bReplaceable = true;
2623             break;
2624         default:
2625             break;
2626 
2627     }
2628 
2629     // when in a header or footer word appears to treat all elements as wrap through
2630 
2631     // determine wrapping mode
2632     SfxItemSet aFlySet(m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END>{});
2633     Reader::ResetFrameFormatAttrs(aFlySet); // tdf#122425: Explicitly remove borders and spacing
2634     css::text::WrapTextMode eSurround = css::text::WrapTextMode_PARALLEL;
2635     bool bContour = false;
2636     switch (pF->nwr)
2637     {
2638         case 0: // 0 like 2, but doesn't require absolute object
2639         case 2: // 2 wrap around absolute object
2640             eSurround = css::text::WrapTextMode_PARALLEL;
2641             break;
2642         case 1: // 1 no text next to shape
2643             eSurround = css::text::WrapTextMode_NONE;
2644             break;
2645         case 3: // 3 wrap as if no object present
2646             eSurround = css::text::WrapTextMode_THROUGH;
2647             break;
2648         case 4: // 4 wrap tightly around object
2649         case 5: // 5 wrap tightly, but allow holes
2650             eSurround = css::text::WrapTextMode_PARALLEL;
2651             bContour = true;
2652             break;
2653     }
2654 
2655     // if mode 2 or 4 also regard the additional parameters
2656     if ( (2 == pF->nwr) || (4 == pF->nwr) )
2657     {
2658         switch( pF->nwrk )
2659         {
2660             // 0 wrap both sides
2661             case 0:
2662                 eSurround = css::text::WrapTextMode_PARALLEL;
2663                 break;
2664             // 1 wrap only on left
2665             case 1:
2666                 eSurround = css::text::WrapTextMode_LEFT;
2667                 break;
2668             // 2 wrap only on right
2669             case 2:
2670                 eSurround = css::text::WrapTextMode_RIGHT;
2671                 break;
2672             // 3 wrap only on largest side
2673             case 3:
2674                 eSurround = css::text::WrapTextMode_DYNAMIC;
2675                 break;
2676         }
2677     }
2678 
2679     SwFormatSurround aSur( eSurround );
2680     aSur.SetContour( bContour );
2681     aSur.SetOutside(true); // Winword can only do outside contours
2682     aFlySet.Put( aSur );
2683 
2684     // now position imported object correctly and so on (can be a whole group)
2685 
2686     OSL_ENSURE(!((aData.size() != 1) && bReplaceable),
2687         "Replaceable drawing with > 1 entries ?");
2688 
2689     if (aData.size() != 1)
2690         bReplaceable = false;
2691 
2692     /*
2693         Get the record for top level object, so we can get the word anchoring
2694         and wrapping information for it.
2695     */
2696     SvxMSDffImportRec* pRecord = aData.find(pObject);
2697     OSL_ENSURE(pRecord, "how did that happen?");
2698     if (!pRecord)
2699     {
2700         // remove old object from the Z-Order list
2701         m_xMSDffManager->RemoveFromShapeOrder(pObject);
2702         // and delete the object
2703         SdrObject::Free(pObject);
2704         return nullptr;
2705     }
2706 
2707     const bool bLayoutInTableCell =
2708         m_nInTable && IsObjectLayoutInTableCell( pRecord->nLayoutInTableCell );
2709 
2710     // #i18732# - Switch on 'follow text flow', if object is laid out
2711     // inside table cell
2712     if (bLayoutInTableCell)
2713     {
2714         SwFormatFollowTextFlow aFollowTextFlow( true );
2715         aFlySet.Put( aFollowTextFlow );
2716     }
2717 
2718     // #i21847#
2719     // Some shapes are set to *hidden*, don't import those ones.
2720     if (pRecord->bHidden)
2721     {
2722         // remove old object from the Z-Order list
2723         m_xMSDffManager->RemoveFromShapeOrder(pObject);
2724         // and delete the object
2725         SdrObject::Free(pObject);
2726         return nullptr;
2727     }
2728 
2729     sal_uInt16 nCount = pObject->GetUserDataCount();
2730     if(nCount)
2731     {
2732         OUString lnName, aObjName, aTarFrame;
2733         for (sal_uInt16 i = 0; i < nCount; i++ )
2734         {
2735             SdrObjUserData* pData = pObject->GetUserData( i );
2736             if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
2737                     && pData->GetId() == SW_UD_IMAPDATA)
2738             {
2739                 SwMacroInfo* macInf = dynamic_cast<SwMacroInfo*>(pData);
2740                 if( macInf && macInf->GetShapeId() == pF->nSpId )
2741                 {
2742                     lnName = macInf->GetHlink();
2743                     aObjName = macInf->GetName();
2744                     aTarFrame = macInf->GetTarFrame();
2745                     break;
2746                 }
2747             }
2748         }
2749         SwFormatURL* pFormatURL = new SwFormatURL();
2750         pFormatURL->SetURL( lnName, false );
2751         if (!aObjName.isEmpty())
2752             pFormatURL->SetName(aObjName);
2753         if (!aTarFrame.isEmpty())
2754             pFormatURL->SetTargetFrameName(aTarFrame);
2755         pFormatURL->SetMap(nullptr);
2756         aFlySet.Put(*pFormatURL);
2757     }
2758 
2759     // If we are to be "below text" then we are not to be opaque
2760     // #i14045# MM If we are in a header or footer then make the object transparent
2761     // Not exactly like word but close enough for now
2762 
2763     // both flags <bBelowText> and <bDrawHell> have to be set to move object into the background.
2764     // #i46794# - it reveals that value of flag <bBelowText> can be neglected.
2765     const bool bMoveToBackgrd = pRecord->bDrawHell ||
2766                                 ( ( m_bIsHeader || m_bIsFooter ) && pF->nwr == 3 );
2767     if ( bMoveToBackgrd )
2768         aFlySet.Put(SvxOpaqueItem(RES_OPAQUE,false));
2769 
2770     OUString aObjName = pObject->GetName();
2771 
2772     SwFrameFormat* pRetFrameFormat = nullptr;
2773     if (bReplaceable)
2774     {
2775         // Single graphics or ole objects
2776         pRetFrameFormat = ImportReplaceableDrawables(pObject, pOurNewObject, pRecord,
2777             pF, aFlySet);
2778     }
2779     else
2780     {
2781         // Drawing objects, (e.g. ovals or drawing groups)
2782         if (pF->bRcaSimple)
2783         {
2784             pF->nbx = WW8_FSPA::RelPageBorder;
2785             pF->nby = WW8_FSPA::RelPageBorder;
2786         }
2787 
2788         RndStdIds eAnchor = ProcessEscherAlign(pRecord, pF, aFlySet);
2789 
2790         // Should we, and is it possible to make this into a writer textbox
2791         if ((!(m_nIniFlags1 & WW8FL_NO_FLY_FOR_TXBX)) && pRecord->bReplaceByFly)
2792         {
2793             pRetFrameFormat = ConvertDrawTextToFly(pObject, pOurNewObject, pRecord,
2794                 eAnchor, pF, aFlySet);
2795             if (pRetFrameFormat)
2796                 bDone = true;
2797         }
2798 
2799         if (!bDone)
2800         {
2801             sw::util::SetLayer aSetLayer(m_rDoc);
2802             if ( bMoveToBackgrd )
2803                 aSetLayer.SendObjectToHell(*pObject);
2804             else
2805                 aSetLayer.SendObjectToHeaven(*pObject);
2806 
2807             if (!IsInlineEscherHack())
2808             {
2809                 /* Need to make sure that the correct layer ordering is applied. */
2810                 //  pass information, if object is in page header|footer to method.
2811                 m_xWWZOrder->InsertEscherObject( pObject, pF->nSpId,
2812                                                m_bIsHeader || m_bIsFooter );
2813             }
2814             else
2815             {
2816                 m_xWWZOrder->InsertTextLayerObject(pObject);
2817             }
2818 
2819             pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pObject, aFlySet );
2820 
2821             OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() ==
2822                 eAnchor, "Not the anchor type requested!");
2823 
2824             /*
2825                 Insert text if necessary into textboxes contained in groups.
2826             */
2827             for (const auto& it : aData)
2828             {
2829                 pRecord = it.get();
2830                 if (pRecord->pObj && pRecord->aTextId.nTxBxS)
2831                 { // #i52825# pRetFrameFormat can be NULL
2832                     pRetFrameFormat = MungeTextIntoDrawBox(
2833                         pRecord, nGrafAnchorCp, pRetFrameFormat);
2834                 }
2835             }
2836         }
2837     }
2838 
2839     // #i44344#, #i44681# - positioning attributes already set
2840     if ( pRetFrameFormat /*#i52825# */ && dynamic_cast< const SwDrawFrameFormat *>( pRetFrameFormat ) !=  nullptr )
2841     {
2842         static_cast<SwDrawFrameFormat*>(pRetFrameFormat)->PosAttrSet();
2843     }
2844     if (!IsInlineEscherHack())
2845         MapWrapIntoFlyFormat(pRecord, pRetFrameFormat);
2846 
2847     // Set frame name with object name
2848     if( pRetFrameFormat /*#i52825# */ && !aObjName.isEmpty() )
2849         pRetFrameFormat->SetName( aObjName );
2850     return AddAutoAnchor(pRetFrameFormat);
2851 }
2852 
AddAutoAnchor(SwFrameFormat * pFormat)2853 SwFrameFormat *SwWW8ImplReader::AddAutoAnchor(SwFrameFormat *pFormat)
2854 {
2855     /*
2856      * anchored to character at the current position will move along the
2857      * paragraph as text is added because we are at the insertion point.
2858      *
2859      * Leave to later and set the correct location then.
2860      */
2861     if (pFormat && (pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR))
2862     {
2863         m_xAnchorStck->AddAnchor(*m_pPaM->GetPoint(), pFormat);
2864     }
2865     return pFormat;
2866 }
2867 
MungeTextIntoDrawBox(SvxMSDffImportRec * pRecord,long nGrafAnchorCp,SwFrameFormat * pRetFrameFormat)2868 SwFrameFormat* SwWW8ImplReader::MungeTextIntoDrawBox(SvxMSDffImportRec *pRecord,
2869     long nGrafAnchorCp, SwFrameFormat* pRetFrameFormat)
2870 {
2871     SdrObject* pTrueObject = pRecord->pObj;
2872 
2873     SdrTextObj* pSdrTextObj;
2874 
2875     // check for group object (e.g. two parentheses)
2876     if (SdrObjGroup* pThisGroup = dynamic_cast<SdrObjGroup*>( pRecord->pObj) )
2877     {
2878         // Group objects don't have text. Insert a text object into
2879         // the group for holding the text.
2880         pSdrTextObj = new SdrRectObj(
2881             *m_pDrawModel,
2882             OBJ_TEXT,
2883             pThisGroup->GetCurrentBoundRect());
2884 
2885         SfxItemSet aSet(m_pDrawModel->GetItemPool());
2886         aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2887         aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2888         aSet.Put(SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE ));
2889         aSet.Put(makeSdrTextAutoGrowHeightItem(false));
2890         aSet.Put(makeSdrTextAutoGrowWidthItem(false));
2891         pSdrTextObj->SetMergedItemSet(aSet);
2892         pSdrTextObj->NbcSetLayer( pThisGroup->GetLayer() );
2893         pThisGroup->GetSubList()->NbcInsertObject(pSdrTextObj);
2894     }
2895     else
2896         pSdrTextObj = dynamic_cast<SdrTextObj*>( pRecord->pObj );
2897 
2898     if( pSdrTextObj )
2899     {
2900         Size aObjSize(pSdrTextObj->GetSnapRect().GetWidth(),
2901             pSdrTextObj->GetSnapRect().GetHeight());
2902 
2903         // Object is part of a group?
2904         SdrObject* pGroupObject = pSdrTextObj->getParentSdrObjectFromSdrObject();
2905 
2906         const size_t nOrdNum = pSdrTextObj->GetOrdNum();
2907         bool bEraseThisObject;
2908         InsertTxbxText( pSdrTextObj, &aObjSize, pRecord->aTextId.nTxBxS,
2909             pRecord->aTextId.nSequence, nGrafAnchorCp, pRetFrameFormat,
2910             (pSdrTextObj != pTrueObject) || (nullptr != pGroupObject),
2911             bEraseThisObject, nullptr, nullptr, nullptr, nullptr, pRecord);
2912 
2913         // was this object replaced ??
2914         if (bEraseThisObject)
2915         {
2916             if( pGroupObject || (pSdrTextObj != pTrueObject) )
2917             {
2918                 // Object is already replaced by a new SdrGrafObj (in the group
2919                 // and) the Drawing-Page.
2920 
2921                 SdrObject* pNewObj = pGroupObject ?
2922                     pGroupObject->GetSubList()->GetObj(nOrdNum) : pTrueObject;
2923                 if (pSdrTextObj != pNewObj)
2924                 {
2925                     // Replace object in the Z-Order-List
2926                     m_xMSDffManager->ExchangeInShapeOrder(pSdrTextObj, 0, pNewObj);
2927                     // now delete object
2928                     SdrObject::Free( pRecord->pObj );
2929                     // and save the new object.
2930                     pRecord->pObj = pNewObj;
2931                 }
2932             }
2933             else
2934             {
2935                 // remove the object from Z-Order list
2936                 m_xMSDffManager->RemoveFromShapeOrder( pSdrTextObj );
2937                 // take the object from the drawing page
2938                 if( pSdrTextObj->getSdrPageFromSdrObject() )
2939                     m_pDrawPg->RemoveObject( pSdrTextObj->GetOrdNum() );
2940                 // and delete FrameFormat, because replaced by graphic
2941                 // (this also deletes the object)
2942                 m_rDoc.DelFrameFormat( pRetFrameFormat );
2943                 pRetFrameFormat = nullptr;
2944                 // also delete the object record
2945                 pRecord->pObj = nullptr;
2946             }
2947         }
2948         else
2949         {
2950             // use ww8-default border distance
2951             SfxItemSet aItemSet(m_pDrawModel->GetItemPool(),
2952                 svl::Items<SDRATTR_TEXT_LEFTDIST, SDRATTR_TEXT_LOWERDIST>{});
2953             aItemSet.Put( makeSdrTextLeftDistItem( pRecord->nDxTextLeft ) );
2954             aItemSet.Put( makeSdrTextRightDistItem( pRecord->nDxTextRight  ) );
2955             aItemSet.Put( makeSdrTextUpperDistItem( pRecord->nDyTextTop    ) );
2956             aItemSet.Put( makeSdrTextLowerDistItem( pRecord->nDyTextBottom ) );
2957             pSdrTextObj->SetMergedItemSetAndBroadcast(aItemSet);
2958         }
2959     }
2960     return pRetFrameFormat;
2961 }
2962 
ConvertDrawTextToFly(SdrObject * & rpObject,SdrObject * & rpOurNewObject,SvxMSDffImportRec const * pRecord,RndStdIds eAnchor,WW8_FSPA const * pF,SfxItemSet & rFlySet)2963 SwFlyFrameFormat* SwWW8ImplReader::ConvertDrawTextToFly(SdrObject* &rpObject,
2964     SdrObject* &rpOurNewObject, SvxMSDffImportRec const * pRecord, RndStdIds eAnchor,
2965     WW8_FSPA const *pF, SfxItemSet &rFlySet)
2966 {
2967     SwFlyFrameFormat* pRetFrameFormat = nullptr;
2968     long nStartCp;
2969     long nEndCp;
2970 
2971     // Check if this textbox chain contains text as conversion of an empty
2972     // chain would not make sense.
2973     if ( TxbxChainContainsRealText(pRecord->aTextId.nTxBxS,nStartCp,nEndCp) )
2974     {
2975         // The Text is not read into SdrTextObj!  Rather insert a frame and
2976         // insert the text from nStartCp to nEndCp.
2977 
2978         // More attributes can be used in a frame compared to the
2979         // Edit-Engine, and it can contain field, OLEs or graphics...
2980         tools::Rectangle aInnerDist(pRecord->nDxTextLeft, pRecord->nDyTextTop,
2981             pRecord->nDxTextRight, pRecord->nDyTextBottom);
2982 
2983         SwFormatFrameSize aFrameSize(ATT_FIX_SIZE, pF->nXaRight - pF->nXaLeft, pF->nYaBottom - pF->nYaTop);
2984         aFrameSize.SetWidthSizeType(pRecord->bAutoWidth ? ATT_VAR_SIZE : ATT_FIX_SIZE);
2985         rFlySet.Put(aFrameSize);
2986 
2987         MatchSdrItemsIntoFlySet( rpObject, rFlySet, pRecord->eLineStyle,
2988             pRecord->eLineDashing, pRecord->eShapeType, aInnerDist );
2989 
2990         SdrTextObj *pSdrTextObj = dynamic_cast<SdrTextObj*>(rpObject);
2991         if (pSdrTextObj && pSdrTextObj->IsVerticalWriting())
2992             rFlySet.Put(SvxFrameDirectionItem(SvxFrameDirection::Vertical_RL_TB, RES_FRAMEDIR));
2993 
2994         pRetFrameFormat = m_rDoc.MakeFlySection(eAnchor, m_pPaM->GetPoint(), &rFlySet);
2995         OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() == eAnchor,
2996             "Not the anchor type requested!");
2997 
2998         // if everything is OK, find pointer on new object and correct
2999         // Z-order list (or delete entry)
3000         rpOurNewObject = CreateContactObject(pRetFrameFormat);
3001 
3002         // remove old object from the Z-Order list
3003         m_xMSDffManager->RemoveFromShapeOrder( rpObject );
3004 
3005         // and delete the object
3006         SdrObject::Free( rpObject );
3007         /*
3008             NB: only query pOrgShapeObject starting here!
3009         */
3010 
3011         if (rpOurNewObject)
3012         {
3013             /*
3014             We do not store our rpOutNewObject in the ShapeOrder because we
3015             have a FrameFormat from which we can regenerate the contact object when
3016             we need it. Because, we can have frames anchored to paragraphs in
3017             header/footers and we can copy header/footers, if we do copy a
3018             header/footer with a nonpage anchored frame in it then the contact
3019             objects are invalidated. Under this condition the FrameFormat will be
3020             updated to reflect this change and can be used to get a new
3021             contact object, while a raw rpOutNewObject stored here becomes
3022             deleted and useless.
3023             */
3024             m_xMSDffManager->StoreShapeOrder(pF->nSpId,
3025                 (static_cast<sal_uLong>(pRecord->aTextId.nTxBxS) << 16) +
3026                 pRecord->aTextId.nSequence, nullptr, pRetFrameFormat);
3027 
3028             // The Contact object has to be inserted into the draw page, so
3029             // SwWW8ImplReader::LoadDoc1() can determine the z-order.
3030             if (!rpOurNewObject->IsInserted())
3031             {
3032                 // pass information, if object is in page header|footer to method.
3033                 m_xWWZOrder->InsertEscherObject( rpOurNewObject, pF->nSpId,
3034                                                m_bIsHeader || m_bIsFooter );
3035             }
3036         }
3037 
3038         // Box-0 receives the text for the whole chain!
3039         if( !pRecord->aTextId.nSequence )
3040         {
3041             // save flags etc and reset them
3042             WW8ReaderSave aSave( this );
3043 
3044             MoveInsideFly(pRetFrameFormat);
3045 
3046             SwNodeIndex aStart(m_pPaM->GetPoint()->nNode);
3047 
3048             m_xWWZOrder->InsideEscher(pF->nSpId);
3049 
3050             // read in the text
3051             m_bTxbxFlySection = true;
3052             bool bJoined = ReadText(nStartCp, (nEndCp-nStartCp),
3053                 MAN_MAINTEXT == m_xPlcxMan->GetManType() ?
3054                         MAN_TXBX : MAN_TXBX_HDFT);
3055 
3056             m_xWWZOrder->OutsideEscher();
3057 
3058             MoveOutsideFly(pRetFrameFormat, aSave.GetStartPos(),!bJoined);
3059 
3060             aSave.Restore( this );
3061 
3062             StripNegativeAfterIndent(pRetFrameFormat);
3063         }
3064 
3065     }
3066     return pRetFrameFormat;
3067 }
3068 
MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec & rRecord,SfxItemSet & rFlySet)3069 void MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec &rRecord,
3070     SfxItemSet &rFlySet)
3071 {
3072     if (rRecord.bVFlip || rRecord.bHFlip)
3073     {
3074         MirrorGraph eType(MirrorGraph::Dont);
3075         if (rRecord.bVFlip && rRecord.bHFlip)
3076             eType = MirrorGraph::Both;
3077         else if (rRecord.bVFlip)
3078             eType = MirrorGraph::Horizontal;
3079         else
3080             eType = MirrorGraph::Vertical;
3081         rFlySet.Put( SwMirrorGrf(eType) );
3082     }
3083 }
3084 
ImportReplaceableDrawables(SdrObject * & rpObject,SdrObject * & rpOurNewObject,SvxMSDffImportRec * pRecord,WW8_FSPA * pF,SfxItemSet & rFlySet)3085 SwFlyFrameFormat* SwWW8ImplReader::ImportReplaceableDrawables( SdrObject* &rpObject,
3086     SdrObject* &rpOurNewObject, SvxMSDffImportRec* pRecord, WW8_FSPA *pF,
3087     SfxItemSet &rFlySet )
3088 {
3089     SwFlyFrameFormat* pRetFrameFormat = nullptr;
3090     sal_Int32 nWidthTw = o3tl::saturating_sub(pF->nXaRight, pF->nXaLeft);
3091     if (0 > nWidthTw)
3092         nWidthTw = 0;
3093     sal_Int32 nHeightTw = o3tl::saturating_sub(pF->nYaBottom, pF->nYaTop);
3094     if (0 > nHeightTw)
3095         nHeightTw = 0;
3096 
3097     ProcessEscherAlign(pRecord, pF, rFlySet);
3098 
3099     rFlySet.Put(SwFormatFrameSize(ATT_FIX_SIZE, nWidthTw, nHeightTw));
3100 
3101     SfxItemSet aGrSet(m_rDoc.GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, RES_GRFATR_END-1>{});
3102 
3103     if (pRecord)
3104     {
3105         // Note that the escher inner distance only seems to be honoured in
3106         // word for textboxes, not for graphics and ole objects.
3107         tools::Rectangle aInnerDist(0, 0, 0, 0);
3108 
3109         MatchSdrItemsIntoFlySet(rpObject, rFlySet, pRecord->eLineStyle,
3110             pRecord->eLineDashing, pRecord->eShapeType, aInnerDist);
3111 
3112         MatchEscherMirrorIntoFlySet(*pRecord, aGrSet);
3113     }
3114 
3115     OUString aObjectName(rpObject->GetName());
3116     if (OBJ_OLE2 == SdrObjKind(rpObject->GetObjIdentifier()))
3117         pRetFrameFormat = InsertOle(*static_cast<SdrOle2Obj*>(rpObject), rFlySet, &aGrSet);
3118     else
3119     {
3120         const SdrGrafObj *pGrf = static_cast<const SdrGrafObj*>(rpObject);
3121         bool bDone = false;
3122         if (pGrf->IsLinkedGraphic() && !pGrf->GetFileName().isEmpty())
3123         {
3124             GraphicType eType = pGrf->GetGraphicType();
3125             OUString aGrfName(
3126                 URIHelper::SmartRel2Abs(
3127                     INetURLObject(m_sBaseURL), pGrf->GetFileName(),
3128                     URIHelper::GetMaybeFileHdl()));
3129             // correction of fix for issue #i10939#:
3130             // One of the two conditions have to be true to insert the graphic
3131             // as a linked graphic -
3132             if (GraphicType::NONE == eType || CanUseRemoteLink(aGrfName))
3133             {
3134                 pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
3135                     *m_pPaM, aGrfName, OUString(), nullptr,
3136                     &rFlySet, &aGrSet, nullptr);
3137                 bDone = true;
3138             }
3139         }
3140         if (!bDone)
3141         {
3142             const Graphic& rGraph = pGrf->GetGraphic();
3143             pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
3144                 *m_pPaM, OUString(), OUString(), &rGraph,
3145                 &rFlySet, &aGrSet, nullptr);
3146         }
3147     }
3148 
3149     if (pRetFrameFormat)
3150     {
3151         if( pRecord )
3152         {
3153             if( OBJ_OLE2 != SdrObjKind(rpObject->GetObjIdentifier()) )
3154                 SetAttributesAtGrfNode( pRecord, pRetFrameFormat, pF );
3155         }
3156         // avoid multiple occurrences of the same graphic name
3157         m_aGrfNameGenerator.SetUniqueGraphName(pRetFrameFormat, aObjectName);
3158     }
3159     // if everything is OK, determine pointer to new object and correct
3160     // Z-Order-List accordingly (or delete entry)
3161     rpOurNewObject = CreateContactObject(pRetFrameFormat);
3162 
3163     // remove old object from Z-Order-List
3164     m_xMSDffManager->RemoveFromShapeOrder( rpObject );
3165     // remove from Drawing-Page
3166     if( rpObject->getSdrPageFromSdrObject() )
3167         m_pDrawPg->RemoveObject( rpObject->GetOrdNum() );
3168 
3169     // and delete the object
3170     SdrObject::Free( rpObject );
3171     /*
3172         Warning: from now on query only pOrgShapeObject!
3173     */
3174 
3175     // add Contact-Object to the Z-Order-List and the page
3176     if (rpOurNewObject)
3177     {
3178         if (!m_bHdFtFootnoteEdn)
3179             m_xMSDffManager->StoreShapeOrder(pF->nSpId, 0, rpOurNewObject );
3180 
3181         // The Contact-Object MUST be set in the Draw-Page, so that in
3182         // SwWW8ImplReader::LoadDoc1() the Z-Order can be defined !!!
3183         if (!rpOurNewObject->IsInserted())
3184         {
3185             // pass information, if object is in page header|footer to method.
3186             m_xWWZOrder->InsertEscherObject( rpOurNewObject, pF->nSpId,
3187                                            m_bIsHeader || m_bIsFooter );
3188         }
3189     }
3190     return pRetFrameFormat;
3191 }
3192 
GrafikCtor()3193 void SwWW8ImplReader::GrafikCtor()  // For SVDraw and VCControls and Escher
3194 {
3195     if (!m_pDrawModel)
3196     {
3197         m_rDoc.getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed
3198         m_pDrawModel  = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
3199         OSL_ENSURE(m_pDrawModel, "Cannot create DrawModel");
3200         m_pDrawPg = m_pDrawModel->GetPage(0);
3201 
3202         m_xMSDffManager.reset(new SwMSDffManager(*this, m_bSkipImages));
3203         m_xMSDffManager->SetModel(m_pDrawModel, 1440);
3204         /*
3205          Now the dff manager always needs a controls converter as well, but a
3206          control converter may still exist without a dffmanager.
3207         */
3208         m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM));
3209 
3210         m_xWWZOrder.reset(new wwZOrderer(sw::util::SetLayer(m_rDoc), m_pDrawPg,
3211             m_xMSDffManager->GetShapeOrders()));
3212     }
3213 }
3214 
GrafikDtor()3215 void SwWW8ImplReader::GrafikDtor()
3216 {
3217     m_pDrawEditEngine.reset(); // maybe created by graphic
3218     m_xWWZOrder.reset();       // same
3219 }
3220 
AddAnchor(const SwPosition & rPos,SwFrameFormat * pFormat)3221 void SwWW8FltAnchorStack::AddAnchor(const SwPosition& rPos, SwFrameFormat *pFormat)
3222 {
3223     OSL_ENSURE(pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR,
3224         "Don't use fltanchors with inline frames, slap!");
3225     NewAttr(rPos, SwFltAnchor(pFormat));
3226 }
3227 
Flush()3228 void SwWW8FltAnchorStack::Flush()
3229 {
3230     size_t nCnt = size();
3231     for (size_t i=0; i < nCnt; ++i)
3232     {
3233         SwFltStackEntry &rEntry = (*this)[i];
3234         SwPosition aDummy(rEntry.m_aMkPos.m_nNode);
3235         SetAttrInDoc(aDummy, rEntry);
3236         DeleteAndDestroy(i--);
3237         --nCnt;
3238     }
3239 }
3240 
3241 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3242