sc: factor out some more code
[LibreOffice.git] / sw / source / filter / ww8 / ww8graf.cxx
blob16fca927f57dbb136543ef081079a0b1c838a7c0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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 <editeng/lrspitem.hxx>
26 #include <svx/xflbmtit.hxx>
27 #include <svx/xfillit0.hxx>
28 #include <svx/xlineit0.hxx>
29 #include <svx/xlnclit.hxx>
30 #include <svx/xlnwtit.hxx>
31 #include <svx/xlndsit.hxx>
32 #include <svx/xlnstit.hxx>
33 #include <svx/xlnedit.hxx>
34 #include <svx/xlnstwit.hxx>
35 #include <svx/xlnedwit.hxx>
36 #include <svx/xlnstcit.hxx>
37 #include <svx/xlnedcit.hxx>
38 #include <svx/xflclit.hxx>
39 #include <svx/xbtmpit.hxx>
40 #include <svx/svdmodel.hxx>
41 #include <svx/svdocapt.hxx>
42 #include <svx/sxctitm.hxx>
43 #include <svx/sdggaitm.hxx>
44 #include <svx/sdgluitm.hxx>
45 #include <svx/sdgmoitm.hxx>
46 #include <svx/sdmetitm.hxx>
47 #include <svx/sdooitm.hxx>
48 #include <svx/sdshitm.hxx>
49 #include <svx/sdsxyitm.hxx>
50 #include <svx/sdtagitm.hxx>
51 #include <svx/sdtditm.hxx>
52 #include <svx/sdtfsitm.hxx>
53 #include <editeng/editeng.hxx>
54 #include <svx/svdpage.hxx>
55 #include <svx/svdopath.hxx>
56 #include <svx/svdocirc.hxx>
57 #include <editeng/outlobj.hxx>
58 #include <svx/svdogrp.hxx>
59 #include <svx/svdograf.hxx>
60 #include <svx/svdoole2.hxx>
61 #include <editeng/colritem.hxx>
62 #include <editeng/ulspitem.hxx>
63 #include <editeng/brushitem.hxx>
64 #include <editeng/opaqitem.hxx>
65 #include <editeng/shaditem.hxx>
66 #include <editeng/boxitem.hxx>
67 #include <editeng/outliner.hxx>
68 #include <editeng/frmdiritem.hxx>
69 #include <svx/xfltrit.hxx>
70 #include <filter/msfilter/msdffimp.hxx>
71 #include <frmatr.hxx>
72 #include <grfatr.hxx>
73 #include <fmtornt.hxx>
74 #include <fmtcntnt.hxx>
75 #include <frmfmt.hxx>
76 #include <fmtanchr.hxx>
77 #include <pam.hxx>
78 #include <doc.hxx>
79 #include <drawdoc.hxx>
80 #include <IDocumentDrawModelAccess.hxx>
81 #include <ndgrf.hxx>
82 #include <dcontact.hxx>
83 #include <docsh.hxx>
84 #include <mdiexp.hxx>
85 #include "ww8struc.hxx"
86 #include "ww8scan.hxx"
87 #include "ww8par.hxx"
88 #include "ww8par2.hxx"
89 #include "ww8graf.hxx"
90 #include <fmtinfmt.hxx>
91 #include <editeng/eeitem.hxx>
92 #include <editeng/flditem.hxx>
93 #include <fmtfollowtextflow.hxx>
94 #include "writerhelper.hxx"
95 #include "writerwordglue.hxx"
96 #include <basegfx/point/b2dpoint.hxx>
97 #include <basegfx/polygon/b2dpolygon.hxx>
98 #include <editeng/editobj.hxx>
99 #include <math.h>
100 #include <fmturl.hxx>
101 #include <o3tl/enumrange.hxx>
102 #include <o3tl/safeint.hxx>
103 #include <memory>
104 #include <optional>
105 #include <filter/msfilter/escherex.hxx>
106 #include <utility>
107 #include "sprmids.hxx"
109 using ::editeng::SvxBorderLine;
110 using namespace ::com::sun::star;
111 using namespace sw::types;
112 using namespace sw::util;
114 // helper methods
115 static Color WW8TransCol(SVBT32 nWC)
117 #if 1 // 1 = use predefined color, 0 = ignore
119 // color table to convert RGB values to pre-defined colors
120 // (to make the writer UI show the right color names)
121 // the table is split in base 3, the greys are missing as
122 // they don't fit into that system (4 values: bw, wb, 2 * grey)
123 static const Color eColA[] = { // B G R B G R B G R
124 COL_BLACK, COL_RED, COL_LIGHTRED, // 0 0 0, 0 0 1, 0 0 2
125 COL_GREEN, COL_BROWN, COL_BLACK, // 0 1 0, 0 1 1, 0 1 2
126 COL_LIGHTGREEN, COL_BLACK, COL_YELLOW, // 0 2 0, 0 2 1, 0 2 2
127 COL_BLUE, COL_MAGENTA, COL_BLACK, // 1 0 0, 1 0 1, 1 0 2
128 COL_CYAN, COL_LIGHTGRAY, COL_BLACK, // 1 1 0, 1 1 1, 1 1 2
129 COL_BLACK, COL_BLACK, COL_BLACK, // 1 2 0, 1 2 1, 1 2 2
130 COL_LIGHTBLUE, COL_BLACK, COL_LIGHTMAGENTA, // 2 0 0, 2 0 1, 2 0 2
131 COL_BLACK, COL_BLACK, COL_BLACK, // 2 1 0, 2 1 1, 2 1 2
132 COL_LIGHTCYAN, COL_BLACK, COL_WHITE }; // 2 2 0, 2 2 1, 2 2 2
134 // In nWC[3] is a byte that's not described in the WW documentation.
135 // Its meaning appears to be the following: For 0, it's a normal color
136 // whose RGB values are in nWC[0..2]. If nWC[3] is 0x1, 0x7d or 0x83,
137 // it's a grey value whose black portion is given in 0.5% in nWC[0].
138 // I guess that BIT(0) in nWC[3] is relevant for distinguishing RGB/Grey.
140 if( !( nWC[3] & 0x1 ) && // not special (grey)
141 ( ( nWC[0] == 0 || nWC[0]== 0x80 || nWC[0] == 0xff ) // R
142 && ( nWC[1] == 0 || nWC[1]== 0x80 || nWC[1] == 0xff ) // G
143 && ( nWC[2] == 0 || nWC[2]== 0x80 || nWC[2] == 0xff ) ) ){// B
144 int nIdx = 0; // and now: Idx-calculation in base 3
145 for (int i = 2; i >= 0; i--)
147 nIdx *= 3;
148 if (nWC[i])
149 nIdx += ((nWC[i] == 0xff) ? 2 : 1);
151 if (eColA[nIdx] != COL_BLACK)
152 return eColA[nIdx]; // default color
154 #endif
156 if (nWC[3] & 0x1)
158 // Special color gray
159 sal_uInt8 u = static_cast<sal_uInt8>( static_cast<sal_uLong>( 200 - nWC[0] ) * 256 / 200 );
160 return Color(u, u, u);
163 // User-Color
164 return Color(nWC[0], nWC[1], nWC[2]);
167 void wwFrameNamer::SetUniqueGraphName(SwFrameFormat *pFrameFormat, std::u16string_view rFixed)
169 if (mbIsDisabled || rFixed.empty())
170 return;
172 pFrameFormat->SetFormatName(msSeed+OUString::number(++mnImportedGraphicsCount) + ": " + rFixed);
175 // ReadGrafStart reads object data and if necessary creates an anchor
176 bool SwWW8ImplReader::ReadGrafStart(void* pData, short nDataSiz,
177 WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
179 if (sal_uInt16 n = SVBT16ToUInt16(pHd->cb); n < sizeof(WW8_DPHEAD) + nDataSiz)
181 OSL_ENSURE( false, "+graphic element: too short?" );
182 m_pStrm->SeekRel(n - sizeof(WW8_DPHEAD));
183 return false;
186 bool bCouldRead = checkRead(*m_pStrm, pData, nDataSiz);
187 OSL_ENSURE(bCouldRead, "Short Graphic header");
188 if (!bCouldRead)
189 return false;
191 SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR );
192 aAnchor.SetAnchor( m_pPaM->GetPoint() );
193 rSet.Put( aAnchor );
195 m_nDrawXOfs2 = m_nDrawXOfs;
196 m_nDrawYOfs2 = m_nDrawYOfs;
198 return true;
201 // SetStdAttr() sets standard attributes
202 static void SetStdAttr( SfxItemSet& rSet, WW8_DP_LINETYPE& rL,
203 WW8_DP_SHADOW const & rSh )
205 if( sal_uInt16 n = SVBT16ToUInt16( rL.lnps ); n == 5 ){ // invisible
206 rSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
207 }else{ // visible
208 Color aCol( WW8TransCol( rL.lnpc ) ); // line color
209 rSet.Put( XLineColorItem( OUString(), aCol ) );
210 sal_uInt16 nLen = SVBT16ToUInt16( rL.lnpw );
211 rSet.Put( XLineWidthItem( nLen ) );
212 // line thickness
213 if( n >= 1
214 && n <= 4 ){ // line style
215 rSet.Put( XLineStyleItem( drawing::LineStyle_DASH ) );
216 XDash aD( css::drawing::DashStyle_RECT, 1, 2 * nLen, 1, 5 * nLen, 5 * nLen );
217 switch( n ){
218 case 1: aD.SetDots( 0 ); // Dash
219 aD.SetDashLen( 6 * nLen );
220 aD.SetDistance( 4 * nLen );
221 break;
222 case 2: aD.SetDashes( 0 ); break; // Dot
223 case 3: break; // Dash Dot
224 case 4: aD.SetDots( 2 ); break; // Dash Dot Dot
226 rSet.Put( XLineDashItem( OUString(), aD ) );
227 }else{
228 rSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) ); // needed for TextBox
231 if( SVBT16ToUInt16( rSh.shdwpi ) ){ // shadow
232 rSet.Put(makeSdrShadowItem(true));
233 rSet.Put( makeSdrShadowXDistItem( SVBT16ToUInt16( rSh.xaOffset ) ) );
234 rSet.Put( makeSdrShadowYDistItem( SVBT16ToUInt16( rSh.yaOffset ) ) );
238 // SetFill() sets fill attributes such as fore- and background color and
239 // pattern by reducing to a color
240 // SetFill() doesn't yet set a pattern, because Sdr can't easily do that
241 // and the Sdr hatching (XDash) isn't finished yet.
242 // Instead, a mixed color will be picked that's between the selected ones.
243 static void SetFill( SfxItemSet& rSet, WW8_DP_FILL& rFill )
245 static const sal_uInt8 nPatA[] =
247 0, 0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80,
248 90, 50, 50, 50, 50, 50, 50, 33, 33, 33, 33, 33, 33
250 sal_uInt16 nPat = SVBT16ToUInt16(rFill.flpp);
252 if (nPat == 0) // transparent
253 rSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
254 else
256 rSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); // necessary for textbox
257 if (nPat <= 1 || (SAL_N_ELEMENTS(nPatA) <= nPat))
259 // Solid background or unknown
260 rSet.Put(XFillColorItem(OUString(), WW8TransCol(rFill.dlpcBg)));
262 else
263 { // Brush -> color mix
264 Color aB( WW8TransCol( rFill.dlpcBg ) );
265 Color aF( WW8TransCol( rFill.dlpcFg ) );
266 aB.SetRed( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetRed()) * nPatA[nPat]
267 + static_cast<sal_uLong>(aB.GetRed()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
268 aB.SetGreen( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetGreen()) * nPatA[nPat]
269 + static_cast<sal_uLong>(aB.GetGreen()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
270 aB.SetBlue( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetBlue()) * nPatA[nPat]
271 + static_cast<sal_uLong>(aB.GetBlue()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
272 rSet.Put( XFillColorItem( OUString(), aB ) );
277 static void SetLineEndAttr( SfxItemSet& rSet, WW8_DP_LINEEND const & rLe,
278 WW8_DP_LINETYPE const & rLt )
280 sal_uInt16 aSB = SVBT16ToUInt16( rLe.aStartBits );
281 if( aSB & 0x3 )
283 ::basegfx::B2DPolygon aPolygon;
284 aPolygon.append(::basegfx::B2DPoint(0.0, 330.0));
285 aPolygon.append(::basegfx::B2DPoint(100.0, 0.0));
286 aPolygon.append(::basegfx::B2DPoint(200.0, 330.0));
287 aPolygon.setClosed(true);
288 rSet.Put( XLineEndItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) );
289 sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw )
290 * ( ( aSB >> 2 & 0x3 ) + ( aSB >> 4 & 0x3 ) );
291 if( nSiz < 220 ) nSiz = 220;
292 rSet.Put(XLineEndWidthItem(nSiz));
293 rSet.Put(XLineEndCenterItem(false));
296 sal_uInt16 aEB = SVBT16ToUInt16( rLe.aEndBits );
297 if( !(aEB & 0x3) ) return;
299 ::basegfx::B2DPolygon aPolygon;
300 aPolygon.append(::basegfx::B2DPoint(0.0, 330.0));
301 aPolygon.append(::basegfx::B2DPoint(100.0, 0.0));
302 aPolygon.append(::basegfx::B2DPoint(200.0, 330.0));
303 aPolygon.setClosed(true);
304 rSet.Put( XLineStartItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) );
305 sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw )
306 * ( ( aEB >> 2 & 0x3 ) + ( aEB >> 4 & 0x3 ) );
307 if( nSiz < 220 ) nSiz = 220;
308 rSet.Put(XLineStartWidthItem(nSiz));
309 rSet.Put(XLineStartCenterItem(false));
312 // start of routines for the different objects
313 rtl::Reference<SdrObject> SwWW8ImplReader::ReadLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
315 WW8_DP_LINE aLine;
317 if( !ReadGrafStart( static_cast<void*>(&aLine), sizeof( aLine ), pHd, rSet ) )
318 return nullptr;
320 Point aP[2];
322 Point& rP0 = aP[0];
323 Point& rP1 = aP[1];
325 rP0.setX(SVBT16ToInt16(pHd->xa) + m_nDrawXOfs2);
326 rP0.setY(SVBT16ToInt16(pHd->ya) + m_nDrawYOfs2);
327 rP1 = rP0;
328 rP0.AdjustX(SVBT16ToInt16(aLine.xaStart));
329 rP0.AdjustY(SVBT16ToInt16(aLine.yaStart));
330 rP1.AdjustX(SVBT16ToInt16(aLine.xaEnd));
331 rP1.AdjustY(SVBT16ToInt16(aLine.yaEnd));
334 ::basegfx::B2DPolygon aPolygon;
335 aPolygon.append(::basegfx::B2DPoint(aP[0].X(), aP[0].Y()));
336 aPolygon.append(::basegfx::B2DPoint(aP[1].X(), aP[1].Y()));
337 rtl::Reference<SdrObject> pObj = new SdrPathObj(
338 *m_pDrawModel,
339 SdrObjKind::Line,
340 ::basegfx::B2DPolyPolygon(aPolygon));
342 SetStdAttr( rSet, aLine.aLnt, aLine.aShd );
343 SetLineEndAttr( rSet, aLine.aEpp, aLine.aLnt );
345 return pObj;
348 rtl::Reference<SdrObject> SwWW8ImplReader::ReadRect(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
350 WW8_DP_RECT aRect;
352 if( !ReadGrafStart( static_cast<void*>(&aRect), sizeof( aRect ), pHd, rSet ) )
353 return nullptr;
355 Point aP0(SVBT16ToInt16(pHd->xa) + m_nDrawXOfs2, SVBT16ToInt16(pHd->ya) + m_nDrawYOfs2);
356 Point aP1( aP0 );
357 aP1.AdjustX(SVBT16ToInt16(pHd->dxa));
358 aP1.AdjustY(SVBT16ToInt16(pHd->dya));
360 rtl::Reference<SdrObject> pObj = new SdrRectObj(
361 *m_pDrawModel,
362 tools::Rectangle(aP0, aP1));
364 SetStdAttr( rSet, aRect.aLnt, aRect.aShd );
365 SetFill( rSet, aRect.aFill );
367 return pObj;
370 rtl::Reference<SdrObject> SwWW8ImplReader::ReadEllipse(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
372 WW8_DP_ELLIPSE aEllipse;
374 if( !ReadGrafStart( static_cast<void*>(&aEllipse), sizeof( aEllipse ), pHd, rSet ) )
375 return nullptr;
377 Point aP0(SVBT16ToInt16(pHd->xa) + m_nDrawXOfs2, SVBT16ToInt16(pHd->ya) + m_nDrawYOfs2);
378 Point aP1( aP0 );
379 aP1.AdjustX(SVBT16ToInt16(pHd->dxa));
380 aP1.AdjustY(SVBT16ToInt16(pHd->dya));
382 rtl::Reference<SdrObject> pObj = new SdrCircObj(
383 *m_pDrawModel,
384 SdrCircKind::Full,
385 tools::Rectangle(aP0, aP1));
387 SetStdAttr( rSet, aEllipse.aLnt, aEllipse.aShd );
388 SetFill( rSet, aEllipse.aFill );
390 return pObj;
393 rtl::Reference<SdrObject> SwWW8ImplReader::ReadArc(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
395 WW8_DP_ARC aArc;
397 if( !ReadGrafStart( static_cast<void*>(&aArc), sizeof( aArc ), pHd, rSet ) )
398 return nullptr;
400 Point aP0(SVBT16ToInt16(pHd->xa) + m_nDrawXOfs2, SVBT16ToInt16(pHd->ya) + m_nDrawYOfs2);
401 Point aP1( aP0 );
402 aP1.AdjustX(SVBT16ToInt16(pHd->dxa) * 2);
403 aP1.AdjustY(SVBT16ToInt16(pHd->dya) * 2);
405 short nA[] = { 2, 3, 1, 0 };
406 short nW = nA[ ( ( aArc.fLeft & 1 ) << 1 ) + ( aArc.fUp & 1 ) ];
407 if( !aArc.fLeft ){
408 aP0.AdjustY(-SVBT16ToInt16(pHd->dya));
409 aP1.AdjustY(-SVBT16ToInt16(pHd->dya));
411 if( aArc.fUp ){
412 aP0.AdjustX(-SVBT16ToInt16(pHd->dxa));
413 aP1.AdjustX(-SVBT16ToInt16(pHd->dxa));
416 rtl::Reference<SdrObject> pObj = new SdrCircObj(
417 *m_pDrawModel,
418 SdrCircKind::Section,
419 tools::Rectangle(aP0, aP1),
420 Degree100(nW * 9000),
421 Degree100(( ( nW + 1 ) & 3 ) * 9000));
423 SetStdAttr( rSet, aArc.aLnt, aArc.aShd );
424 SetFill( rSet, aArc.aFill );
426 return pObj;
429 rtl::Reference<SdrObject> SwWW8ImplReader::ReadPolyLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
431 WW8_DP_POLYLINE aPoly;
433 if( !ReadGrafStart( static_cast<void*>(&aPoly), sizeof( aPoly ), pHd, rSet ) )
434 return nullptr;
436 sal_uInt16 nCount = SVBT16ToUInt16( aPoly.aBits1 ) >> 1 & 0x7fff;
437 std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]);
439 bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4); // read points
440 OSL_ENSURE(bCouldRead, "Short PolyLine header");
441 if (!bCouldRead)
442 return nullptr;
444 tools::Polygon aP( nCount );
445 Point aPt;
446 for (sal_uInt16 i=0; i<nCount; ++i)
448 aPt.setX(SVBT16ToUInt16(xP[i << 1]) + m_nDrawXOfs2 + SVBT16ToInt16(pHd->xa));
449 aPt.setY(SVBT16ToUInt16(xP[(i << 1) + 1]) + m_nDrawYOfs2 + SVBT16ToInt16(pHd->ya));
450 aP[i] = aPt;
452 xP.reset();
454 rtl::Reference<SdrObject> pObj = new SdrPathObj(
455 *m_pDrawModel,
456 (SVBT16ToUInt16(aPoly.aBits1) & 0x1) ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
457 ::basegfx::B2DPolyPolygon(aP.getB2DPolygon()));
459 SetStdAttr( rSet, aPoly.aLnt, aPoly.aShd );
460 SetFill( rSet, aPoly.aFill );
462 return pObj;
465 static ESelection GetESelection(EditEngine const &rDrawEditEngine, tools::Long nCpStart, tools::Long nCpEnd)
467 sal_Int32 nPCnt = rDrawEditEngine.GetParagraphCount();
468 sal_Int32 nSP = 0;
469 sal_Int32 nEP = 0;
470 while( (nSP < nPCnt)
471 && (nCpStart >= rDrawEditEngine.GetTextLen( nSP ) + 1) )
473 nCpStart -= rDrawEditEngine.GetTextLen( nSP ) + 1;
474 nSP++;
476 // at the end, switch to the new line only 1 character later as
477 // otherwise line attributes reach one line too far
478 while( (nEP < nPCnt)
479 && (nCpEnd > rDrawEditEngine.GetTextLen( nEP ) + 1) )
481 nCpEnd -= rDrawEditEngine.GetTextLen( nEP ) + 1;
482 nEP++;
484 return ESelection( nSP, nCpStart, nEP, nCpEnd );
487 // InsertTxbxStyAttrs() sets style attributes into the passed ItemSet.
488 // SW styles are used since import-WW-styles are already destroyed.
489 // SW styles are examined in depth first search order (with parent styles)
490 // for the attributes given in aSrcTab. They're cloned, and the clones'
491 // Which-IDs are changed according to the aDstTab table so that the
492 // EditEngine will not ignore them.
493 // Both Paragraph and character attributes are stuffed into the ItemSet.
494 void SwWW8ImplReader::InsertTxbxStyAttrs(SfxItemSet& rS, sal_uInt16 nColl, ManTypes eType)
496 SwWW8StyInf * pStyInf = GetStyle(nColl);
497 if( !(pStyInf != nullptr && pStyInf->m_pFormat && pStyInf->m_bColl) )
498 return;
500 const SfxPoolItem* pItem;
501 for( sal_uInt16 i = POOLATTR_BEGIN; i < POOLATTR_END; i++ )
503 // If we are set in the source and not set in the destination
504 // then add it in.
505 if ( SfxItemState::SET == pStyInf->m_pFormat->GetItemState(
506 i, true, &pItem ) )
508 SfxItemPool *pEditPool = rS.GetPool();
509 sal_uInt16 nWhich = i;
510 sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich);
512 if (nWhich == RES_MARGIN_FIRSTLINE || nWhich == RES_MARGIN_TEXTLEFT
513 || nWhich == RES_MARGIN_RIGHT)
515 // MSO ignores paragraph indents for comments in DOC format
516 if (eType == MAN_AND)
517 continue;
519 if (SfxItemState::SET == rS.GetItemState(EE_PARA_LRSPACE, false))
520 continue;
522 // LO7.6 split SW RES_LR_SPACE into three pieces,
523 // but EditEng still uses the combined SvxLRSpaceItem, so recombine
524 assert(!pEditPool->GetTrueWhichIDFromSlotID(nSlotId)
525 && "unnecessary when EditEng learns about the separate pieces");
527 SvxLRSpaceItem aLR(rS.Get(EE_PARA_LRSPACE));
528 const auto& rFirstLine = pStyInf->m_pFormat->GetFirstLineIndent();
529 aLR.SetTextFirstLineOffset(rFirstLine.GetTextFirstLineOffset());
530 aLR.SetTextLeft(pStyInf->m_pFormat->GetTextLeftMargin().GetTextLeft());
531 aLR.SetRight(pStyInf->m_pFormat->GetRightMargin().GetRight());
532 rS.Put(aLR);
534 else if (nSlotId && nWhich != nSlotId
535 && 0 != (nWhich = pEditPool->GetTrueWhichIDFromSlotID(nSlotId))
536 && SfxItemState::SET != rS.GetItemState(nWhich, false))
539 rS.Put( pItem->CloneSetWhich(nWhich) );
546 static void lcl_StripFields(OUString &rString, WW8_CP &rNewStartCp)
548 sal_Int32 nStartPos = 0;
549 for (;;)
551 nStartPos = rString.indexOf(0x13, nStartPos);
552 if (nStartPos<0)
553 return;
555 const sal_Unicode cStops[] = {0x14, 0x15, 0};
556 const sal_Int32 nStopPos = comphelper::string::indexOfAny(rString, cStops, nStartPos);
557 if (nStopPos<0)
559 rNewStartCp += rString.getLength()-nStartPos;
560 rString = rString.copy(0, nStartPos);
561 return;
564 const bool was0x14 = rString[nStopPos]==0x14;
565 rString = rString.replaceAt(nStartPos, nStopPos+1-nStartPos, u"");
566 rNewStartCp += nStopPos-nStartPos;
568 if (was0x14)
570 ++rNewStartCp;
571 nStartPos = rString.indexOf(0x15, nStartPos);
572 if (nStartPos<0)
573 return;
574 rString = rString.replaceAt(nStartPos, 1, u"");
579 namespace {
581 class Chunk
583 private:
584 OUString msURL;
585 tools::Long mnStartPos; // 0x13
586 tools::Long mnEndPos; // 0x15
587 public:
588 explicit Chunk(tools::Long nStart, OUString aURL)
589 : msURL(std::move(aURL)), mnStartPos(nStart), mnEndPos(0) {}
591 void SetEndPos(tools::Long nEnd) { mnEndPos = nEnd; }
592 tools::Long GetStartPos() const {return mnStartPos;}
593 tools::Long GetEndPos() const {return mnEndPos;}
594 const OUString &GetURL() const {return msURL;}
595 void Adjust(sal_Int32 nAdjust)
597 mnStartPos-=nAdjust;
598 mnEndPos-=nAdjust;
602 bool IsValidSel(const EditEngine& rEngine, const ESelection& rSel)
604 const auto nParaCount = rEngine.GetParagraphCount();
605 if (rSel.start.nPara < nParaCount && rSel.end.nPara < nParaCount)
606 return rSel.start.nIndex >= 0 && rSel.end.nIndex >= 0;
607 return false;
611 // InsertAttrsAsDrawingAttrs() sets attributes between StartCp and EndCp.
612 // Style attributes are set as hard, paragraph and character attributes.
613 void SwWW8ImplReader::InsertAttrsAsDrawingAttrs(WW8_CP nStartCp, WW8_CP nEndCp,
614 ManTypes eType, bool bONLYnPicLocFc)
617 Save and create new plcxman for this drawing object, of the type that
618 will include the para end mark inside a paragraph property range, as
619 drawing boxes have real paragraph marks as part of their text, while
620 normal writer has separate nodes for each paragraph and so has no actual
621 paragraph mark as part of the paragraph text.
623 WW8ReaderSave aSave(this);
624 m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), eType, nStartCp, true);
626 WW8_CP nStart = m_xPlcxMan->Where();
627 WW8_CP nNext, nStartReplace=0;
629 bool bDoingSymbol = false;
630 sal_Unicode cReplaceSymbol = m_cSymbol;
632 std::optional<SfxItemSet> pS(m_pDrawEditEngine->GetEmptyItemSet());
633 WW8PLCFManResult aRes;
635 std::deque<Chunk> aChunks;
637 // Here store stack location
638 size_t nCurrentCount = m_xCtrlStck->size();
639 while (nStart < nEndCp)
641 // nStart is the beginning of the attributes for this range, and
642 // may be before the text itself. So watch out for that
643 WW8_CP nTextStart = nStart;
644 if (nTextStart < nStartCp)
645 nTextStart = nStartCp;
647 // get position of next SPRM
648 bool bStartAttr = m_xPlcxMan->Get(&aRes);
649 m_nCurrentColl = m_xPlcxMan->GetColl();
650 if (aRes.nSprmId)
652 if( bONLYnPicLocFc )
654 if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) )
656 Read_PicLoc(aRes.nSprmId, aRes.pMemPos +
657 m_oSprmParser->DistanceToData(aRes.nSprmId), 4);
658 // Ok, that's what we were looking for. Now let's get
659 // out of here!
660 break;
663 else if ((eFTN > aRes.nSprmId) || (0x0800 <= aRes.nSprmId))
665 // Here place them onto our usual stack and we will pop them
666 // off and convert them later
667 if (bStartAttr)
669 ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
670 if (!bDoingSymbol && m_bSymbol)
672 bDoingSymbol = true;
673 nStartReplace = nTextStart;
674 cReplaceSymbol = m_cSymbol;
677 else
679 EndSprm( aRes.nSprmId );
680 if (!m_bSymbol && bDoingSymbol)
682 bDoingSymbol = false;
684 ESelection aReplaceSel(GetESelection(*m_pDrawEditEngine, nStartReplace - nStartCp,
685 nTextStart - nStartCp));
687 sal_Int32 nParaCount = m_pDrawEditEngine->GetParagraphCount();
688 bool bBadSelection = aReplaceSel.start.nPara >= nParaCount || aReplaceSel.end.nPara >= nParaCount;
690 SAL_WARN_IF(bBadSelection, "sw.ww8", "editengine has different amount of text than expected");
692 if (!bBadSelection)
694 sal_Int32 nCount = nTextStart - nStartReplace;
695 OUStringBuffer sTemp(nCount);
696 comphelper::string::padToLength(sTemp, nCount, cReplaceSymbol);
697 m_pDrawEditEngine->QuickInsertText(sTemp.makeStringAndClear(), aReplaceSel);
702 else if (aRes.nSprmId == eFLD)
704 if (bStartAttr)
706 size_t nCount = m_xCtrlStck->size();
707 if (m_aFieldStack.empty() && Read_Field(&aRes))
709 OUString sURL;
710 for (size_t nI = m_xCtrlStck->size(); nI > nCount; --nI)
712 const SfxPoolItem *pItem = ((*m_xCtrlStck)[nI-1]).m_pAttr.get();
713 sal_uInt16 nWhich = pItem->Which();
714 if (nWhich == RES_TXTATR_INETFMT)
716 const SwFormatINetFormat *pURL =
717 static_cast<const SwFormatINetFormat *>(pItem);
718 sURL = pURL->GetValue();
720 m_xCtrlStck->DeleteAndDestroy(nI-1);
722 aChunks.emplace_back(nStart, sURL);
725 else
727 if (!m_aFieldStack.empty() && End_Field() && !aChunks.empty())
728 aChunks.back().SetEndPos(nStart+1);
733 m_xPlcxMan->advance();
734 nNext = m_xPlcxMan->Where();
736 const WW8_CP nEnd = ( nNext < nEndCp ) ? nNext : nEndCp;
737 if (!bONLYnPicLocFc && nNext != nStart && nEnd >= nStartCp)
739 SfxItemPool *pEditPool = pS->GetPool();
741 // Here read current properties and convert them into pS
742 // and put those attrs into the draw box if they can be converted
743 // to draw attributes
744 if (m_xCtrlStck->size() - nCurrentCount)
746 for (size_t i = nCurrentCount; i < m_xCtrlStck->size(); ++i)
748 const SfxPoolItem *pItem = ((*m_xCtrlStck)[i]).m_pAttr.get();
749 sal_uInt16 nWhich = pItem->Which();
750 if( nWhich < RES_FLTRATTR_BEGIN ||
751 nWhich >= RES_FLTRATTR_END )
753 sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich);
754 if (nWhich == RES_CHRATR_BACKGROUND || nWhich == RES_CHRATR_HIGHLIGHT)
756 Color aColor(static_cast<const SvxBrushItem*>(pItem)->GetColor());
757 pS->Put(SvxColorItem(aColor, EE_CHAR_BKGCOLOR));
759 else if (nWhich == RES_MARGIN_FIRSTLINE || nWhich == RES_MARGIN_TEXTLEFT
760 || nWhich == RES_MARGIN_RIGHT)
762 // MSO ignores paragraph indents for comments in DOC format
763 if (eType == MAN_AND)
764 continue;
766 // LO7.6 split SW RES_LR_SPACE into three pieces,
767 // but EE still uses the combined SvxLRSpaceItem, so recombine
768 assert(!pEditPool->GetTrueWhichIDFromSlotID(nSlotId)
769 && "unnecessary when EditEng learns about the separate pieces");
771 SvxLRSpaceItem aLR(pS->Get(EE_PARA_LRSPACE));
772 if (nWhich == RES_MARGIN_FIRSTLINE)
774 const auto* pFirstLine
775 = static_cast<const SvxFirstLineIndentItem*>(pItem);
776 aLR.SetTextFirstLineOffset(pFirstLine->GetTextFirstLineOffset());
778 else if (nWhich == RES_MARGIN_TEXTLEFT)
780 aLR.SetTextLeft(static_cast<const SvxTextLeftMarginItem*>(pItem)
781 ->GetTextLeft());
783 else
785 aLR.SetRight(
786 static_cast<const SvxRightMarginItem*>(pItem)->GetRight());
788 pS->Put(aLR);
790 else if (nSlotId && nWhich != nSlotId
791 && 0 != (nWhich = pEditPool->GetTrueWhichIDFromSlotID(nSlotId)))
793 pS->Put( pItem->CloneSetWhich(nWhich) );
798 // Fill in the remainder from the style
799 InsertTxbxStyAttrs(*pS, m_nCurrentColl, eType);
801 if( pS->Count() )
803 m_pDrawEditEngine->QuickSetAttribs( *pS,
804 GetESelection(*m_pDrawEditEngine, nTextStart - nStartCp, nEnd - nStartCp ) );
805 pS.emplace(m_pDrawEditEngine->GetEmptyItemSet());
808 nStart = nNext;
810 pS.reset();
812 // pop off as far as recorded location just in case there were some left
813 // unclosed
814 for (size_t nI = m_xCtrlStck->size(); nI > nCurrentCount; --nI)
815 m_xCtrlStck->DeleteAndDestroy(nI-1);
817 auto aEnd = aChunks.end();
818 for (auto aIter = aChunks.begin(); aIter != aEnd; ++aIter)
820 ESelection aSel(GetESelection(*m_pDrawEditEngine, aIter->GetStartPos()-nStartCp,
821 aIter->GetEndPos()-nStartCp));
822 if (!IsValidSel(*m_pDrawEditEngine, aSel))
823 continue;
824 OUString aString(m_pDrawEditEngine->GetText(aSel));
825 const sal_Int32 nOrigLen = aString.getLength();
826 WW8_CP nDummy(0);
827 lcl_StripFields(aString, nDummy);
829 sal_Int32 nChanged;
830 if (!aIter->GetURL().isEmpty())
832 SvxURLField aURL(aIter->GetURL(), aString, SvxURLFormat::AppDefault);
833 m_pDrawEditEngine->QuickInsertField(SvxFieldItem(aURL, EE_FEATURE_FIELD), aSel);
834 nChanged = nOrigLen - 1;
836 else
838 m_pDrawEditEngine->QuickInsertText(aString, aSel);
839 nChanged = nOrigLen - aString.getLength();
841 for (auto aIter2 = aIter+1; aIter2 != aEnd; ++aIter2)
842 aIter2->Adjust(nChanged);
846 Don't worry about the new pPlcxMan, the restore removes it when
847 replacing the current one with the old one.
849 aSave.Restore(this);
852 bool SwWW8ImplReader::GetTxbxTextSttEndCp(WW8_CP& rStartCp, WW8_CP& rEndCp,
853 sal_uInt16 nTxBxS, sal_uInt16 nSequence)
855 // grab the TextBox-PLCF quickly
856 WW8PLCFspecial* pT = m_xPlcxMan ? m_xPlcxMan->GetTxbx() : nullptr;
857 if( !pT )
859 OSL_ENSURE( false, "+where's the text graphic (1)?" );
860 return false;
863 // if applicable first find the right TextBox-Story
864 bool bCheckTextBoxStory = ( nTxBxS && pT->GetIMax() >= nTxBxS );
865 if( bCheckTextBoxStory )
866 pT->SetIdx( nTxBxS-1 );
868 // then determine start and end
869 void* pT0;
870 if (!pT->Get(rStartCp, pT0) || rStartCp < 0)
872 OSL_ENSURE( false, "+where's the text graphic (2)?" );
873 return false;
876 if( bCheckTextBoxStory )
878 bool bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable ));
879 while( bReusable )
881 pT->advance();
882 if( !pT->Get( rStartCp, pT0 ) )
884 OSL_ENSURE( false, "+where's the text graphic (2a)?" );
885 return false;
887 bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable ));
890 pT->advance();
891 if (!pT->Get(rEndCp, pT0) || rEndCp < 0)
893 OSL_ENSURE( false, "+where's the text graphic (3)?" );
894 return false;
897 // find the right page in the break table (if necessary)
898 if( bCheckTextBoxStory )
900 // special case: entire chain should be determined - done!
901 if( USHRT_MAX > nSequence )
903 tools::Long nMinStartCp = rStartCp;
904 tools::Long nMaxEndCp = rEndCp;
905 // quickly grab the TextBox-Break-Descriptor-PLCF
906 pT = m_xPlcxMan->GetTxbxBkd();
907 if (!pT) // It can occur on occasion, Caolan
908 return false;
910 // find first entry for this TextBox story
911 if( !pT->SeekPos( rStartCp ) )
913 OSL_ENSURE( false, "+where's the text graphic (4)" );
914 return false;
916 // if needed skip the appropriate number of entries
917 for (sal_uInt16 iSequence = 0; iSequence < nSequence; ++iSequence)
918 pT->advance();
919 // and determine actual start and end
920 if( (!pT->Get( rStartCp, pT0 ))
921 || ( nMinStartCp > rStartCp ) )
923 OSL_ENSURE( false, "+where's the text graphic (5)?" );
924 return false;
926 if( rStartCp >= nMaxEndCp )
927 rEndCp = rStartCp; // not an error: empty string
928 else
930 pT->advance();
931 if ( (!pT->Get(rEndCp, pT0)) || (nMaxEndCp < rEndCp-1) )
933 OSL_ENSURE( false, "+where's the text graphic (6)?" );
934 return false;
936 rEndCp -= 1;
939 else
940 rEndCp -= 1;
942 else
943 rEndCp -= 1;
944 return true;
947 // TxbxText() grabs the text from the WW file and returns that along with
948 // the StartCp and the corrected (by -2, or -1 for version 8) EndCp.
949 sal_Int32 SwWW8ImplReader::GetRangeAsDrawingString(OUString& rString, tools::Long nStartCp, tools::Long nEndCp, ManTypes eType)
951 WW8_CP nOffset = 0;
952 m_xWwFib->GetBaseCp(eType, &nOffset); //TODO: check return value
954 OSL_ENSURE(nStartCp <= nEndCp, "+where's the graphic text (7)?");
955 if (nStartCp == nEndCp)
956 rString.clear(); // empty string: entirely possible
957 else if (nStartCp < nEndCp)
959 // read the text: can be split into multiple pieces
960 const sal_Int32 nLen = m_xSBase->WW8ReadString(*m_pStrm, rString,
961 nStartCp + nOffset, nEndCp - nStartCp, GetCurrentCharSet());
962 OSL_ENSURE(nLen, "+where's the text graphic (8)?");
963 if (nLen>0)
965 if( rString[nLen-1]==0x0d )
966 rString = rString.copy(0, nLen-1);
968 rString = rString.replace( 0xb, 0xa );
969 return nLen;
972 return 0;
975 //EditEngine::InsertText will replace dos lines resulting in a shorter
976 //string than is passed in, so inserting attributes based on the original
977 //string len can fail. So here replace the dos line ends similar to
978 //how EditEngine does it, but preserve the length and replace the extra
979 //chars with placeholders, record the position of the placeholders and
980 //remove those extra chars after attributes have been inserted
981 static std::vector<sal_Int32> replaceDosLineEndsButPreserveLength(OUString &rIn)
983 OUStringBuffer aNewData(rIn);
984 std::vector<sal_Int32> aDosLineEndDummies;
985 sal_Int32 i = 0;
986 sal_Int32 nStrLen = rIn.getLength();
987 while (i < nStrLen)
989 // \r or \n causes linebreak
990 if (rIn[i] == '\r' || rIn[i] == '\n')
992 // skip char if \r\n or \n\r
993 if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) &&
994 (rIn[i] != rIn[i+1]) )
996 ++i;
997 aDosLineEndDummies.push_back(i);
998 aNewData[i] = 0;
1001 ++i;
1003 rIn = aNewData.makeStringAndClear();
1004 return aDosLineEndDummies;
1007 static void removePositions(EditEngine &rDrawEditEngine, const std::vector<sal_Int32>& rDosLineEndDummies)
1009 for (auto aIter = rDosLineEndDummies.rbegin(); aIter != rDosLineEndDummies.rend(); ++aIter)
1011 sal_Int32 nCharPos(*aIter);
1012 rDrawEditEngine.QuickDelete(GetESelection(rDrawEditEngine, nCharPos, nCharPos+1));
1016 std::optional<OutlinerParaObject> SwWW8ImplReader::ImportAsOutliner(OUString &rString, WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType)
1018 std::optional<OutlinerParaObject> pRet;
1020 sal_Int32 nLen = GetRangeAsDrawingString(rString, nStartCp, nEndCp, eType);
1021 if (nLen > 0)
1023 if (m_bFuzzing && rString.getLength() > 1024)
1025 SAL_WARN("sw.ww8", "Truncating long EditEngine strings when fuzzing for performance");
1026 rString = rString.copy(0, 1024);
1029 if (!m_pDrawEditEngine)
1031 m_pDrawEditEngine.reset(new EditEngine(nullptr));
1034 //replace dos line endings with editeng ones, replace any extra chars with
1035 //placeholders to keep the inserted string len in sync with the attribute cps
1036 //and record in aDosLineEnds the superfluous positions
1037 OUString sEEString(rString);
1038 std::vector<sal_Int32> aDosLineEnds(replaceDosLineEndsButPreserveLength(sEEString));
1039 m_pDrawEditEngine->SetText(sEEString);
1040 InsertAttrsAsDrawingAttrs(nStartCp, nStartCp+nLen, eType);
1041 //remove any superfluous placeholders of replaceDosLineEndsButPreserveLength
1042 //after attributes have been inserted
1043 removePositions(*m_pDrawEditEngine, aDosLineEnds);
1045 // Annotations typically begin with a (useless) 0x5
1046 if ((eType == MAN_AND) && m_pDrawEditEngine->GetTextLen())
1048 ESelection aFirstChar(0, 0, 0, 1);
1049 if (m_pDrawEditEngine->GetText( aFirstChar ) == "\x05")
1050 m_pDrawEditEngine->QuickDelete(aFirstChar);
1053 std::unique_ptr<EditTextObject> pTemporaryText = m_pDrawEditEngine->CreateTextObject();
1054 pRet.emplace( std::move(pTemporaryText) );
1055 pRet->SetOutlinerMode( OutlinerMode::TextObject );
1057 m_pDrawEditEngine->SetText( OUString() );
1058 m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet());
1060 // Strip out fields, leaving the result
1061 WW8_CP nDummy(0);
1062 lcl_StripFields(rString, nDummy);
1063 // Strip out word's special characters for the simple string
1064 rString = rString.replaceAll("\x01", "");
1065 rString = rString.replaceAll("\x05", "");
1066 rString = rString.replaceAll("\x08", "");
1067 rString = rString.replaceAll("\007\007", "\007\012");
1068 rString = rString.replace(0x7, ' ');
1071 return pRet;
1074 // InsertTxbxText() adds the Text and the Attributes for TextBoxes and CaptionBoxes
1075 void SwWW8ImplReader::InsertTxbxText(SdrTextObj* pTextObj,
1076 Size const * pObjSiz, sal_uInt16 nTxBxS, sal_uInt16 nSequence, tools::Long nPosCp,
1077 SwFrameFormat const * pOldFlyFormat, bool bMakeSdrGrafObj, bool& rbEraseTextObj,
1078 bool* pbTestTxbxContainsText, tools::Long* pnStartCp, tools::Long* pnEndCp,
1079 bool* pbContainsGraphics, SvxMSDffImportRec const * pRecord)
1081 SwFrameFormat* pFlyFormat = nullptr;
1082 sal_uInt64 nOld = m_pStrm->Tell();
1084 ManTypes eType = m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX;
1086 rbEraseTextObj = false;
1088 OUString aString;
1089 WW8_CP nStartCp, nEndCp;
1090 bool bContainsGraphics = false;
1091 bool bTextWasRead = GetTxbxTextSttEndCp(nStartCp, nEndCp, nTxBxS, nSequence) &&
1092 GetRangeAsDrawingString(aString, nStartCp, nEndCp, eType) > 0;
1094 if (!m_pDrawEditEngine)
1096 m_pDrawEditEngine.reset(new EditEngine(nullptr));
1098 if( pObjSiz )
1099 m_pDrawEditEngine->SetPaperSize( *pObjSiz );
1101 if (m_bFuzzing && aString.getLength() > 1024)
1103 SAL_WARN("sw.ww8", "Truncating long EditEngine strings when fuzzing for performance");
1104 aString = aString.copy(0, 1024);
1107 const OUString aOrigString(aString);
1108 if( bTextWasRead )
1110 WW8_CP nNewStartCp = nStartCp;
1111 lcl_StripFields(aString, nNewStartCp);
1113 if (aString.getLength()!=1)
1115 bContainsGraphics = aString.indexOf(0x1)<0 || aString.indexOf(0x8)<0;
1117 else // May be a single graphic or object
1119 bool bDone = true;
1120 switch( aString[0] )
1122 case 0x1:
1123 if (!pbTestTxbxContainsText)
1125 WW8ReaderSave aSave(this, nNewStartCp -1);
1126 bool bOldEmbeddObj = m_bEmbeddObj;
1127 // bEmbeddObj Ordinarily would have been set by field
1128 // parse, but this is impossible here so...
1129 m_bEmbeddObj = true;
1131 // 1st look for OLE- or Graph-Indicator Sprms
1132 WW8PLCFx_Cp_FKP* pChp = m_xPlcxMan->GetChpPLCF();
1133 WW8PLCFxDesc aDesc;
1134 pChp->GetSprms( &aDesc );
1135 WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, *m_oSprmParser);
1137 for( int nLoop = 0; nLoop < 2; ++nLoop )
1139 while (aSprmIter.GetSprms())
1141 const sal_uInt8 *const pParams(aSprmIter.GetCurrentParams());
1142 if (nullptr == pParams)
1143 break;
1144 sal_uInt16 nCurrentId = aSprmIter.GetCurrentId();
1145 switch( nCurrentId )
1147 case 75:
1148 case 118:
1149 case 0x080A:
1150 case 0x0856:
1151 Read_Obj(nCurrentId, pParams, 1);
1152 break;
1153 case 68: // Read_Pic()
1154 case 0x6A03:
1155 case NS_sprm::LN_CObjLocation:
1156 Read_PicLoc(nCurrentId, pParams, 1);
1157 break;
1159 aSprmIter.advance();
1162 if( !nLoop )
1164 pChp->GetPCDSprms( aDesc );
1165 aSprmIter.SetSprms( aDesc.pMemPos,
1166 aDesc.nSprmsLen );
1169 aSave.Restore(this);
1170 m_bEmbeddObj=bOldEmbeddObj;
1172 // then import either an OLE of a Graphic
1173 if( m_bObj )
1175 if( bMakeSdrGrafObj && pTextObj &&
1176 pTextObj->getParentSdrObjectFromSdrObject() )
1178 // use SdrOleObj/SdrGrafObj instead of
1179 // SdrTextObj in this Group
1181 Graphic aGraph;
1182 rtl::Reference<SdrObject> pNew = ImportOleBase(aGraph);
1184 if( !pNew )
1186 pNew = new SdrGrafObj(*m_pDrawModel);
1187 static_cast<SdrGrafObj*>(pNew.get())->SetGraphic(aGraph);
1190 GraphicCtor();
1192 pNew->SetLogicRect( pTextObj->GetCurrentBoundRect() );
1193 pNew->SetLayer( pTextObj->GetLayer() );
1195 pTextObj->getParentSdrObjectFromSdrObject()->GetSubList()->
1196 ReplaceObject(pNew.get(), pTextObj->GetOrdNum());
1198 else
1199 pFlyFormat = ImportOle();
1200 m_bObj = false;
1202 else
1204 InsertAttrsAsDrawingAttrs(nNewStartCp, nNewStartCp+1,
1205 eType, true);
1206 pFlyFormat = ImportGraf(bMakeSdrGrafObj ? pTextObj : nullptr,
1207 pOldFlyFormat);
1210 break;
1211 case 0x8:
1212 if ( (!pbTestTxbxContainsText) && (!m_bObj) )
1213 pFlyFormat = Read_GrafLayer( nPosCp );
1214 break;
1215 default:
1216 bDone = false;
1217 break;
1220 if( bDone )
1222 if( pFlyFormat && pRecord )
1224 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END>
1225 aFlySet( m_rDoc.GetAttrPool() );
1227 tools::Rectangle aInnerDist( pRecord->nDxTextLeft,
1228 pRecord->nDyTextTop,
1229 pRecord->nDxTextRight,
1230 pRecord->nDyTextBottom );
1231 MatchSdrItemsIntoFlySet( pTextObj,
1232 aFlySet,
1233 pRecord->eLineStyle,
1234 pRecord->eLineDashing,
1235 pRecord->eShapeType,
1236 aInnerDist );
1238 pFlyFormat->SetFormatAttr( aFlySet );
1240 MapWrapIntoFlyFormat(*pRecord, *pFlyFormat);
1242 aString.clear();
1243 rbEraseTextObj = (nullptr != pFlyFormat);
1248 if( pnStartCp )
1249 *pnStartCp = nStartCp;
1250 if( pnEndCp )
1251 *pnEndCp = nEndCp;
1253 if( pbTestTxbxContainsText )
1254 *pbTestTxbxContainsText = bTextWasRead && ! rbEraseTextObj;
1255 else if( !rbEraseTextObj )
1257 if( bTextWasRead )
1259 m_pDrawEditEngine->SetText(aOrigString);
1260 InsertAttrsAsDrawingAttrs(nStartCp, nEndCp, eType);
1263 bool bVertical = pTextObj->IsVerticalWriting();
1264 OutlinerParaObject aOp(m_pDrawEditEngine->CreateTextObject());
1265 aOp.SetOutlinerMode( OutlinerMode::TextObject );
1266 aOp.SetVertical( bVertical );
1267 pTextObj->NbcSetOutlinerParaObject( std::move(aOp) );
1268 pTextObj->SetVerticalWriting(bVertical);
1270 // For the next TextBox also remove the old paragraph attributes
1271 // and styles, otherwise the next box will start with the wrong
1272 // attributes.
1273 // Course of action: delete text = reduce to one paragraph
1274 // and on this one delete the paragraph attributes
1275 // and styles
1276 m_pDrawEditEngine->SetText( OUString() );
1277 m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet());
1280 m_pStrm->Seek( nOld );
1281 if (pbContainsGraphics)
1282 *pbContainsGraphics = bContainsGraphics;
1285 bool SwWW8ImplReader::TxbxChainContainsRealText(sal_uInt16 nTxBxS, tools::Long& rStartCp,
1286 tools::Long& rEndCp)
1288 bool bErase, bContainsText;
1289 InsertTxbxText( nullptr,nullptr,nTxBxS,USHRT_MAX,0,nullptr,false, bErase, &bContainsText,
1290 &rStartCp, &rEndCp );
1291 return bContainsText;
1294 // TextBoxes only for Ver67 !!
1295 rtl::Reference<SdrObject> SwWW8ImplReader::ReadTextBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
1297 bool bDummy;
1298 WW8_DP_TXTBOX aTextB;
1300 if( !ReadGrafStart( static_cast<void*>(&aTextB), sizeof( aTextB ), pHd, rSet ) )
1301 return nullptr;
1303 Point aP0(SVBT16ToInt16(pHd->xa) + m_nDrawXOfs2, SVBT16ToInt16(pHd->ya) + m_nDrawYOfs2);
1304 Point aP1( aP0 );
1305 aP1.AdjustX(SVBT16ToInt16(pHd->dxa));
1306 aP1.AdjustY(SVBT16ToInt16(pHd->dya));
1308 rtl::Reference<SdrRectObj> pObj = new SdrRectObj(
1309 *m_pDrawModel,
1310 SdrObjKind::Text,
1311 tools::Rectangle(aP0, aP1));
1313 pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1));
1314 Size aSize(SVBT16ToInt16(pHd->dxa), SVBT16ToInt16(pHd->dya));
1316 tools::Long nStartCpFly,nEndCpFly;
1317 bool bContainsGraphics;
1318 InsertTxbxText(pObj.get(), &aSize, 0, 0, 0, nullptr, false,
1319 bDummy,nullptr,&nStartCpFly,&nEndCpFly,&bContainsGraphics);
1321 SetStdAttr( rSet, aTextB.aLnt, aTextB.aShd );
1322 SetFill( rSet, aTextB.aFill );
1324 rSet.Put( SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE ) );
1325 rSet.Put( makeSdrTextAutoGrowWidthItem(false));
1326 rSet.Put( makeSdrTextAutoGrowHeightItem(false));
1327 rSet.Put( makeSdrTextLeftDistItem( MIN_BORDER_DIST*2 ) );
1328 rSet.Put( makeSdrTextRightDistItem( MIN_BORDER_DIST*2 ) );
1329 rSet.Put( makeSdrTextUpperDistItem( MIN_BORDER_DIST ) );
1330 rSet.Put( makeSdrTextLowerDistItem( MIN_BORDER_DIST ) );
1332 return pObj;
1335 rtl::Reference<SdrObject> SwWW8ImplReader::ReadCaptionBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
1337 static const SdrCaptionType aCaptA[] = { SdrCaptionType::Type1, SdrCaptionType::Type2,
1338 SdrCaptionType::Type3, SdrCaptionType::Type4 };
1340 WW8_DP_CALLOUT_TXTBOX aCallB;
1342 if( !ReadGrafStart( static_cast<void*>(&aCallB), sizeof( aCallB ), pHd, rSet ) )
1343 return nullptr;
1345 sal_uInt16 nCount = SVBT16ToUInt16( aCallB.dpPolyLine.aBits1 ) >> 1 & 0x7fff;
1346 if (nCount < 1)
1348 SAL_WARN("sw.ww8", "Short CaptionBox header");
1349 return nullptr;
1352 std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]);
1354 bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4); // read points
1355 if (!bCouldRead)
1357 SAL_WARN("sw.ww8", "Short CaptionBox header");
1358 return nullptr;
1361 sal_uInt8 nTyp = static_cast<sal_uInt8>(nCount) - 1;
1362 if( nTyp == 1 && SVBT16ToUInt16( xP[0] ) == SVBT16ToUInt16( xP[2] ) )
1363 nTyp = 0;
1365 Point aP0(SVBT16ToInt16(pHd->xa) + SVBT16ToInt16(aCallB.dpheadTxbx.xa) + m_nDrawXOfs2,
1366 SVBT16ToInt16(pHd->ya) + SVBT16ToInt16(aCallB.dpheadTxbx.ya) + m_nDrawYOfs2);
1367 Point aP1( aP0 );
1368 aP1.AdjustX(SVBT16ToInt16(aCallB.dpheadTxbx.dxa));
1369 aP1.AdjustY(SVBT16ToInt16(aCallB.dpheadTxbx.dya));
1370 Point aP2( SVBT16ToInt16( pHd->xa )
1371 + SVBT16ToInt16( aCallB.dpheadPolyLine.xa )
1372 + m_nDrawXOfs2 + SVBT16ToInt16( xP[0] ),
1373 SVBT16ToInt16( pHd->ya )
1374 + SVBT16ToInt16( aCallB.dpheadPolyLine.ya )
1375 + m_nDrawYOfs2 + SVBT16ToInt16( xP[1] ) );
1376 xP.reset();
1378 rtl::Reference<SdrCaptionObj> pObj = new SdrCaptionObj(
1379 *m_pDrawModel,
1380 tools::Rectangle(aP0, aP1),
1381 aP2);
1383 pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1));
1384 Size aSize(SVBT16ToInt16(aCallB.dpheadTxbx.dxa), SVBT16ToInt16(aCallB.dpheadTxbx.dya));
1385 bool bEraseThisObject;
1387 InsertTxbxText(pObj.get(), &aSize, 0, 0, 0, nullptr, false, bEraseThisObject );
1389 if( SVBT16ToUInt16( aCallB.dptxbx.aLnt.lnps ) != 5 ) // Is border visible ?
1390 SetStdAttr( rSet, aCallB.dptxbx.aLnt, aCallB.dptxbx.aShd );
1391 else // no -> take lines
1392 SetStdAttr( rSet, aCallB.dpPolyLine.aLnt, aCallB.dptxbx.aShd );
1393 SetFill( rSet, aCallB.dptxbx.aFill );
1394 rSet.Put(SdrCaptionTypeItem(aCaptA[nTyp % SAL_N_ELEMENTS(aCaptA)]));
1396 return pObj;
1399 rtl::Reference<SdrObject> SwWW8ImplReader::ReadGroup(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
1401 sal_Int16 nGrouped;
1403 if( !ReadGrafStart( static_cast<void*>(&nGrouped), sizeof( nGrouped ), pHd, rSet ) )
1404 return nullptr;
1406 #ifdef OSL_BIGENDIAN
1407 nGrouped = (sal_Int16)OSL_SWAPWORD( nGrouped );
1408 #endif
1410 m_nDrawXOfs = m_nDrawXOfs + SVBT16ToInt16(pHd->xa);
1411 m_nDrawYOfs = m_nDrawYOfs + SVBT16ToInt16(pHd->ya);
1413 rtl::Reference<SdrObject> pObj = new SdrObjGroup(*m_pDrawModel);
1415 short nLeft = SVBT16ToInt16(pHd->cb) - sizeof(WW8_DPHEAD);
1416 for (int i = 0; i < nGrouped && nLeft >= static_cast<short>(sizeof(WW8_DPHEAD)); ++i)
1418 SfxAllItemSet aSet(m_pDrawModel->GetItemPool());
1419 if (rtl::Reference<SdrObject> pObject = ReadGrafPrimitive(nLeft, aSet))
1421 // first add and then set ItemSet
1422 SdrObjList *pSubGroup = pObj->GetSubList();
1423 OSL_ENSURE(pSubGroup, "Why no sublist available?");
1424 if (pSubGroup)
1425 pSubGroup->InsertObject(pObject.get(), 0);
1426 pObject->SetMergedItemSetAndBroadcast(aSet);
1430 m_nDrawXOfs = m_nDrawXOfs - SVBT16ToInt16(pHd->xa);
1431 m_nDrawYOfs = m_nDrawYOfs - SVBT16ToInt16(pHd->ya);
1433 return pObj;
1436 rtl::Reference<SdrObject> SwWW8ImplReader::ReadGrafPrimitive(short& rLeft, SfxAllItemSet &rSet)
1438 // This whole archaic word 6 graphic import can probably be refactored
1439 // into an object hierarchy with a little effort.
1440 rtl::Reference<SdrObject> pRet;
1441 WW8_DPHEAD aHd; // Read Draw-Primitive-Header
1442 bool bCouldRead = checkRead(*m_pStrm, &aHd, sizeof(WW8_DPHEAD)) &&
1443 SVBT16ToUInt16(aHd.cb) >= sizeof(WW8_DPHEAD);
1444 OSL_ENSURE(bCouldRead, "Graphic Primitive header short read" );
1445 if (!bCouldRead)
1447 rLeft=0;
1448 return pRet;
1451 if( rLeft >= SVBT16ToUInt16(aHd.cb) ) // precautions
1453 rSet.Put(SwFormatSurround(css::text::WrapTextMode_THROUGH));
1454 switch (SVBT16ToUInt16(aHd.dpk) & 0xff )
1456 case 0:
1457 pRet = ReadGroup(&aHd, rSet);
1458 break;
1459 case 1:
1460 pRet = ReadLine(&aHd, rSet);
1461 break;
1462 case 2:
1463 pRet = ReadTextBox(&aHd, rSet);
1464 break;
1465 case 3:
1466 pRet = ReadRect(&aHd, rSet);
1467 break;
1468 case 4:
1469 pRet = ReadEllipse(&aHd, rSet);
1470 break;
1471 case 5:
1472 pRet = ReadArc(&aHd, rSet);
1473 break;
1474 case 6:
1475 pRet = ReadPolyLine(&aHd, rSet);
1476 break;
1477 case 7:
1478 pRet = ReadCaptionBox(&aHd, rSet);
1479 break;
1480 default: // unknown
1481 m_pStrm->SeekRel(SVBT16ToUInt16(aHd.cb) - sizeof(WW8_DPHEAD));
1482 break;
1485 else
1487 OSL_ENSURE( false, "+Grafik-Overlap" );
1489 rLeft = rLeft - SVBT16ToUInt16( aHd.cb );
1490 return pRet;
1493 void SwWW8ImplReader::ReadGrafLayer1(WW8PLCFspecial& rPF, tools::Long nGrafAnchorCp)
1495 rPF.SeekPos(nGrafAnchorCp);
1496 WW8_FC nStartFc;
1497 void* pF0;
1498 if (!rPF.Get(nStartFc, pF0))
1500 OSL_ENSURE( false, "+Where is the graphic (2) ?" );
1501 return;
1503 WW8_FDOA* pF = static_cast<WW8_FDOA*>(pF0);
1504 if( !SVBT32ToUInt32( pF->fc ) )
1506 OSL_ENSURE( false, "+Where is the graphic (3) ?" );
1507 return;
1510 sal_uInt32 nPosFc = SVBT32ToUInt32(pF->fc);
1512 //skip duplicate graphics when fuzzing
1513 if (m_bFuzzing)
1515 if (!m_aGrafPosSet.insert(nPosFc).second)
1516 return;
1519 bool bCouldSeek = checkSeek(*m_pStrm, nPosFc);
1520 OSL_ENSURE(bCouldSeek, "Invalid graphic offset");
1521 if (!bCouldSeek)
1522 return;
1524 // read Draw-Header
1525 WW8_DO aDo;
1526 bool bCouldRead = checkRead(*m_pStrm, &aDo, sizeof(WW8_DO));
1527 OSL_ENSURE(bCouldRead, "Short graphic header");
1528 if (!bCouldRead)
1529 return;
1531 short nLeft = SVBT16ToUInt16( aDo.cb ) - sizeof( WW8_DO );
1532 while (nLeft > static_cast<short>(sizeof(WW8_DPHEAD)))
1534 SfxAllItemSet aSet( m_pDrawModel->GetItemPool() );
1535 if (rtl::Reference<SdrObject> pObject = ReadGrafPrimitive(nLeft, aSet))
1537 m_xWWZOrder->InsertDrawingObject(pObject.get(), SVBT16ToUInt16(aDo.dhgt));
1539 tools::Rectangle aRect(pObject->GetSnapRect());
1541 const sal_uInt32 nCntRelTo = 3;
1543 // Adjustment is horizontally relative to...
1544 static const sal_Int16 aHoriRelOriTab[nCntRelTo] =
1546 text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
1547 text::RelOrientation::PAGE_FRAME, // 1 is page margin
1548 text::RelOrientation::FRAME, // 2 is relative to paragraph
1551 // Adjustment is vertically relative to...
1552 static const sal_Int16 aVertRelOriTab[nCntRelTo] =
1554 text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
1555 text::RelOrientation::PAGE_FRAME, // 1 is page margin
1556 text::RelOrientation::FRAME, // 2 is relative to paragraph
1559 const int nXAlign = aDo.bx < nCntRelTo ? aDo.bx : 0;
1560 const int nYAlign = aDo.by < nCntRelTo ? aDo.by : 0;
1562 aSet.Put(SwFormatHoriOrient(aRect.Left(), text::HoriOrientation::NONE,
1563 aHoriRelOriTab[ nXAlign ]));
1564 aSet.Put(SwFormatVertOrient(aRect.Top(), text::VertOrientation::NONE,
1565 aVertRelOriTab[ nYAlign ]));
1567 SwFrameFormat *pFrame = m_rDoc.getIDocumentContentOperations().InsertDrawObj( *m_pPaM, *pObject, aSet );
1568 pObject->SetMergedItemSet(aSet);
1570 if (SwDrawFrameFormat *pDrawFrame = dynamic_cast<SwDrawFrameFormat*>(pFrame))
1572 pDrawFrame->PosAttrSet();
1575 AddAutoAnchor(pFrame);
1580 sal_Int32 SwMSDffManager::GetEscherLineMatch(MSO_LineStyle eStyle,
1581 MSO_SPT eShapeType, sal_Int32 &rThick)
1583 sal_Int32 nOutsideThick = 0;
1585 Note: In contrast to the regular WinWord table and frame border width,
1586 where the overall border width has to be calculated from the width of *one*
1587 line, the data from ESCHER already contains the overall width [twips]!
1589 The WinWord default is 15 tw. We take for this our 20 tw line.
1590 (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
1591 0.05 pt hairline.) The hairline we only set by WinWord width up to max.
1592 0.5 pt.
1594 switch( eStyle )
1596 case mso_lineTriple:
1597 case mso_lineSimple:
1598 nOutsideThick = eShapeType != mso_sptTextBox ? rThick : rThick/2;
1599 break;
1600 case mso_lineDouble:
1601 if (eShapeType == mso_sptTextBox)
1603 nOutsideThick = rThick/6;
1604 rThick = rThick*2/3;
1606 else
1607 nOutsideThick = rThick*2/3;
1608 break;
1609 case mso_lineThickThin:
1610 if (eShapeType == mso_sptTextBox)
1612 nOutsideThick = rThick*3/10;
1613 rThick = rThick*4/5;
1615 else
1616 nOutsideThick = rThick*4/5;
1617 break;
1618 case mso_lineThinThick:
1620 if (eShapeType == mso_sptTextBox)
1622 nOutsideThick = rThick/10;
1623 rThick = rThick*3/5;
1625 else
1626 nOutsideThick = rThick*3/5;
1628 break;
1629 default:
1630 break;
1632 return nOutsideThick;
1635 // Returns the thickness of the line outside the frame, the logic of
1636 // words positioning of borders around floating objects is that of a
1637 // disturbed mind.
1638 sal_Int32 SwWW8ImplReader::MatchSdrBoxIntoFlyBoxItem(const Color& rLineColor,
1639 MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, sal_Int32 &rLineThick,
1640 SvxBoxItem& rBox )
1642 sal_Int32 nOutsideThick = 0;
1643 if( !rLineThick )
1644 return nOutsideThick;
1646 SvxBorderLineStyle nIdx = SvxBorderLineStyle::NONE;
1648 sal_Int32 nLineThick=rLineThick;
1649 nOutsideThick = SwMSDffManager::GetEscherLineMatch(eLineStyle,
1650 eShapeType, rLineThick);
1653 Note: In contrast to the regular WinWord table and frame border width,
1654 where the overall border width has to be calculated from the width of *one*
1655 line, the data from ESCHER already contains the overall width [twips]!
1657 The WinWord default is 15 tw. We take for this our 20 tw line.
1658 (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
1659 0.05 pt hairline.) The hairline we only set by WinWord width up to max.
1660 0.5 pt.
1662 switch( +eLineStyle )
1664 // first the single lines
1665 case mso_lineSimple:
1666 nIdx = SvxBorderLineStyle::SOLID;
1667 break;
1668 // second the double lines
1669 case mso_lineDouble:
1670 nIdx = SvxBorderLineStyle::DOUBLE;
1671 break;
1672 case mso_lineThickThin:
1673 nIdx = SvxBorderLineStyle::THICKTHIN_SMALLGAP;
1674 break;
1675 case mso_lineThinThick:
1676 nIdx = SvxBorderLineStyle::THINTHICK_SMALLGAP;
1677 break;
1678 // We have no triple border, use double instead.
1679 case mso_lineTriple:
1680 nIdx = SvxBorderLineStyle::DOUBLE;
1681 break;
1682 // no line style is set
1683 case MSO_LineStyle(USHRT_MAX):
1684 break;
1685 // erroneously not implemented line style is set
1686 default:
1687 OSL_ENSURE(false, "eLineStyle is not (yet) implemented!");
1688 break;
1691 switch( eDashing )
1693 case mso_lineDashGEL:
1694 nIdx = SvxBorderLineStyle::DASHED;
1695 break;
1696 case mso_lineDotGEL:
1697 nIdx = SvxBorderLineStyle::DOTTED;
1698 break;
1699 default:
1700 break;
1703 if (SvxBorderLineStyle::NONE != nIdx)
1705 SvxBorderLine aLine;
1706 aLine.SetColor( rLineColor );
1708 aLine.SetWidth( nLineThick ); // No conversion here, nLineThick is already in twips
1709 aLine.SetBorderLineStyle(nIdx);
1711 for(SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>())
1713 // aLine is cloned by SetLine
1714 rBox.SetLine(&aLine, nLine);
1718 return nOutsideThick;
1721 #define WW8ITEMVALUE(ItemSet,Id,Cast) ItemSet.GetItem<Cast>(Id)->GetValue()
1723 void SwWW8ImplReader::MatchSdrItemsIntoFlySet( SdrObject const * pSdrObj,
1724 SfxItemSet& rFlySet, MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType,
1725 tools::Rectangle& rInnerDist )
1728 attributes to be set on the frame
1729 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1730 SwFormatFrameSize if not set, set here
1731 SvxLRSpaceItem set here
1732 SvxULSpaceItem set here
1733 SvxOpaqueItem (Currently not possible for frames! khz 10.2.1999)
1734 SwFormatSurround already set
1735 SwFormatVertOrient already set
1736 SwFormatHoriOrient already set
1737 SwFormatAnchor already set
1738 SvxBoxItem set here
1739 SvxBrushItem set here
1740 SvxShadowItem set here
1743 // 1. GraphicObject of documents?
1744 GraphicCtor();
1746 const SfxItemSet& rOldSet = pSdrObj->GetMergedItemSet();
1748 // some Items can be taken over directly
1749 static sal_uInt16 const aDirectMatch[]
1751 RES_LR_SPACE, // outer spacing left/right: SvxLRSpaceItem
1752 RES_UL_SPACE // outer spacing top/bottom: SvxULSpaceItem
1754 const SfxPoolItem* pPoolItem;
1755 for(sal_uInt16 i : aDirectMatch)
1756 if( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) )
1758 rFlySet.Put( *pPoolItem );
1761 // take new XATTR items directly. Skip old RES_BACKGROUND if new FILLSTYLE taken.
1762 bool bSkipResBackground = false;
1763 SfxItemPool* pPool = rFlySet.GetPool();
1764 if ( pPool )
1766 for ( sal_uInt16 i = XATTR_START; i < XATTR_END; ++i )
1768 // Not all Fly types support XATTRs - skip unsupported attributes
1769 SfxItemPool* pAttrPool = pPool->GetMasterPool();
1770 while ( pAttrPool && !pAttrPool->IsInRange(i) )
1771 pAttrPool = pAttrPool->GetSecondaryPool();
1772 if ( !pAttrPool )
1773 continue;
1775 if ( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) )
1777 rFlySet.Put( *pPoolItem );
1778 if ( i == XATTR_FILLSTYLE )
1780 const drawing::FillStyle eFill = static_cast<const XFillStyleItem*>(pPoolItem)->GetValue();
1781 // Transparency forced in certain situations when fillstyle is none - use old logic for that case still
1782 // which is especially needed for export purposes (tdf112618).
1783 if ( eFill != drawing::FillStyle_NONE )
1784 bSkipResBackground = true;
1790 // now calculate the borders and build the box: The unit is needed for the
1791 // frame SIZE!
1792 SvxBoxItem aBox(rFlySet.Get(RES_BOX));
1793 // dashed or solid becomes solid
1794 // WW-default: 0.75 pt = 15 twips
1795 sal_Int32 nLineThick = 15, nOutside=0;
1797 // check if LineStyle is *really* set!
1799 SfxItemState eState = rOldSet.GetItemState(XATTR_LINESTYLE);
1800 if( eState == SfxItemState::SET )
1802 // Now, that we know there is a line style we will make use the
1803 // parameter given to us when calling the method... :-)
1804 const Color aLineColor = rOldSet.Get(XATTR_LINECOLOR).GetColorValue();
1805 nLineThick = WW8ITEMVALUE(rOldSet, XATTR_LINEWIDTH, XLineWidthItem);
1807 if( !nLineThick )
1808 nLineThick = 1; // for Writer, zero is "no border", so set a minimal value
1810 nOutside = MatchSdrBoxIntoFlyBoxItem(aLineColor, eLineStyle,
1811 eDashing, eShapeType, nLineThick, aBox);
1814 rInnerDist.AdjustLeft(nLineThick );
1815 rInnerDist.AdjustTop(nLineThick );
1816 rInnerDist.AdjustRight(nLineThick );
1817 rInnerDist.AdjustBottom(nLineThick );
1819 rInnerDist.AdjustLeft( -(aBox.CalcLineWidth( SvxBoxItemLine::LEFT )) );
1820 rInnerDist.AdjustTop( -(aBox.CalcLineWidth( SvxBoxItemLine::TOP )) );
1821 rInnerDist.AdjustRight( -(aBox.CalcLineWidth( SvxBoxItemLine::RIGHT )) );
1822 rInnerDist.AdjustBottom( -(aBox.CalcLineWidth( SvxBoxItemLine::BOTTOM )) );
1824 // set distances from box's border to text contained within the box
1825 if( 0 < rInnerDist.Left() )
1826 aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Left()), SvxBoxItemLine::LEFT );
1827 if( 0 < rInnerDist.Top() )
1828 aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Top()), SvxBoxItemLine::TOP );
1829 if( 0 < rInnerDist.Right() )
1830 aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Right()), SvxBoxItemLine::RIGHT );
1831 if( 0 < rInnerDist.Bottom() )
1832 aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Bottom()), SvxBoxItemLine::BOTTOM );
1834 bool bFixSize = !(WW8ITEMVALUE(rOldSet, SDRATTR_TEXT_AUTOGROWHEIGHT,
1835 SdrOnOffItem));
1837 // Size: SwFormatFrameSize
1838 if( SfxItemState::SET != rFlySet.GetItemState(RES_FRM_SIZE, false) )
1840 const tools::Rectangle& rSnapRect = pSdrObj->GetSnapRect();
1841 // if necessary adapt width and position of the framework: The
1842 // recorded interior is to remain equally large despite thick edges.
1843 rFlySet.Put( SwFormatFrameSize(bFixSize ? SwFrameSize::Fixed : SwFrameSize::Variable,
1844 rSnapRect.GetWidth() + 2*nOutside,
1845 rSnapRect.GetHeight() + 2*nOutside) );
1847 else // If a size is set, adjust it to consider border thickness
1849 SwFormatFrameSize aSize = rFlySet.Get(RES_FRM_SIZE);
1851 SwFormatFrameSize aNewSize(bFixSize ? SwFrameSize::Fixed : SwFrameSize::Variable,
1852 aSize.GetWidth() + 2*nOutside,
1853 aSize.GetHeight() + 2*nOutside);
1854 aNewSize.SetWidthSizeType(aSize.GetWidthSizeType());
1855 rFlySet.Put( aNewSize );
1858 // Sadly word puts escher borders outside the graphic, but orients the
1859 // graphic in relation to the top left inside the border. We don't
1860 if (nOutside)
1862 SwFormatHoriOrient aHori = rFlySet.Get(RES_HORI_ORIENT);
1863 aHori.SetPos(MakeSafePositioningValue(aHori.GetPos()-nOutside));
1864 rFlySet.Put(aHori);
1866 SwFormatVertOrient aVert = rFlySet.Get(RES_VERT_ORIENT);
1867 aVert.SetPos(aVert.GetPos()-nOutside);
1868 rFlySet.Put(aVert);
1871 // now set the border
1872 rFlySet.Put( aBox );
1874 // shadow of the box: SvxShadowItem
1875 if( WW8ITEMVALUE(rOldSet, SDRATTR_SHADOW, SdrOnOffItem) )
1877 SvxShadowItem aShadow( RES_SHADOW );
1879 const Color aShdColor = rOldSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue();
1880 const sal_Int32 nShdDistX = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWXDIST,
1881 SdrMetricItem);
1882 const sal_Int32 nShdDistY = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWYDIST,
1883 SdrMetricItem);
1885 aShadow.SetColor( aShdColor );
1887 aShadow.SetWidth(writer_cast<sal_uInt16>((std::abs( nShdDistX) +
1888 std::abs( nShdDistY )) / 2 ));
1890 SvxShadowLocation eShdPosi;
1891 if( 0 <= nShdDistX )
1893 if( 0 <= nShdDistY )
1894 eShdPosi = SvxShadowLocation::BottomRight;
1895 else
1896 eShdPosi = SvxShadowLocation::TopRight;
1898 else
1900 if( 0 <= nShdDistY )
1901 eShdPosi = SvxShadowLocation::BottomLeft;
1902 else
1903 eShdPosi = SvxShadowLocation::TopLeft;
1905 aShadow.SetLocation( eShdPosi );
1907 rFlySet.Put( aShadow );
1909 SvxBrushItem aBrushItem(COL_WHITE, RES_BACKGROUND);
1910 bool bBrushItemOk = false;
1911 sal_uInt8 nTrans = 0;
1913 // Separate transparency
1914 eState = rOldSet.GetItemState(XATTR_FILLTRANSPARENCE);
1915 if (!bSkipResBackground && eState == SfxItemState::SET)
1917 sal_uInt16 nRes = WW8ITEMVALUE(rOldSet, XATTR_FILLTRANSPARENCE,
1918 XFillTransparenceItem);
1919 nTrans = sal_uInt8((nRes * 0xFE) / 100);
1920 aBrushItem.GetColor().SetAlpha(255 - nTrans);
1921 bBrushItemOk = true;
1924 // Background: SvxBrushItem
1925 const XFillStyleItem* pFillStyleItem = rOldSet.GetItemIfSet(XATTR_FILLSTYLE);
1926 if (!bSkipResBackground && pFillStyleItem)
1928 const drawing::FillStyle eFill = pFillStyleItem->GetValue();
1930 switch (eFill)
1932 default:
1933 case drawing::FillStyle_NONE:
1934 // Writer graphics don't have it yet
1935 if (eShapeType != mso_sptPictureFrame)
1937 aBrushItem.GetColor().SetAlpha(1);
1938 bBrushItemOk = true;
1940 break;
1941 case drawing::FillStyle_SOLID:
1942 case drawing::FillStyle_GRADIENT:
1944 const Color aColor =
1945 rOldSet.Get(XATTR_FILLCOLOR).GetColorValue();
1946 aBrushItem.SetColor(aColor);
1948 if (bBrushItemOk) // has trans
1949 aBrushItem.GetColor().SetAlpha(255 - nTrans);
1951 bBrushItemOk = true;
1953 break;
1954 case drawing::FillStyle_HATCH:
1955 break;
1956 case drawing::FillStyle_BITMAP:
1958 GraphicObject aGrfObj(rOldSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
1959 const bool bTile(WW8ITEMVALUE(rOldSet, XATTR_FILLBMP_TILE, XFillBmpTileItem));
1961 if(bBrushItemOk) // has trans
1963 GraphicAttr aAttr(aGrfObj.GetAttr());
1965 aAttr.SetAlpha(255 - nTrans);
1966 aGrfObj.SetAttr(aAttr);
1969 aBrushItem.SetGraphicObject(aGrfObj);
1970 aBrushItem.SetGraphicPos(bTile ? GPOS_TILED : GPOS_AREA);
1971 bBrushItemOk = true;
1973 break;
1977 if (bBrushItemOk)
1978 rFlySet.Put(aBrushItem);
1981 void SwWW8ImplReader::AdjustLRWrapForWordMargins(
1982 const SvxMSDffImportRec &rRecord, SvxLRSpaceItem &rLR)
1984 sal_uInt32 nXRelTo = SvxMSDffImportRec::RELTO_DEFAULT;
1985 if ( rRecord.nXRelTo )
1987 nXRelTo = *rRecord.nXRelTo;
1990 // Left adjustments - if horizontally aligned to left of
1991 // margin or column then remove the left wrapping
1992 if (rRecord.nXAlign == 1)
1994 if ((nXRelTo == 0) || (nXRelTo == 2))
1995 rLR.SetLeft(SvxIndentValue::zero());
1998 // Right adjustments - if horizontally aligned to right of
1999 // margin or column then remove the right wrapping
2000 if (rRecord.nXAlign == 3)
2002 if ((nXRelTo == 0) || (nXRelTo == 2))
2003 rLR.SetRight(SvxIndentValue::zero());
2006 // Inside margin, remove left wrapping
2007 if ((rRecord.nXAlign == 4) && (nXRelTo == 0))
2009 rLR.SetLeft(SvxIndentValue::zero());
2012 // Outside margin, remove left wrapping
2013 if ((rRecord.nXAlign == 5) && (nXRelTo == 0))
2015 rLR.SetRight(SvxIndentValue::zero());
2019 void SwWW8ImplReader::AdjustULWrapForWordMargins(
2020 const SvxMSDffImportRec &rRecord, SvxULSpaceItem &rUL)
2022 sal_uInt32 nYRelTo = SvxMSDffImportRec::RELTO_DEFAULT;
2023 if ( rRecord.nYRelTo )
2025 nYRelTo = *rRecord.nYRelTo;
2028 // Top adjustment - remove upper wrapping if aligned to page
2029 // printable area or to page
2030 if (rRecord.nYAlign == 1)
2032 if ((nYRelTo == 0) || (nYRelTo == 1))
2033 rUL.SetUpper(sal_uInt16(0));
2036 // Bottom adjustment - remove bottom wrapping if aligned to page or
2037 // printable area or to page
2038 if (rRecord.nYAlign == 3)
2040 if ((nYRelTo == 0) || (nYRelTo == 1))
2041 rUL.SetLower(sal_uInt16(0));
2044 // Remove top margin if aligned vertically inside margin
2045 if ((rRecord.nYAlign == 4) && (nYRelTo == 0))
2046 rUL.SetUpper(sal_uInt16(0));
2049 void SwWW8ImplReader::MapWrapIntoFlyFormat(const SvxMSDffImportRec& rRecord,
2050 SwFrameFormat& rFlyFormat)
2052 if (rRecord.nDxWrapDistLeft || rRecord.nDxWrapDistRight)
2054 SvxLRSpaceItem aLR(SvxIndentValue::twips(writer_cast<sal_uInt16>(rRecord.nDxWrapDistLeft)),
2055 SvxIndentValue::twips(writer_cast<sal_uInt16>(rRecord.nDxWrapDistRight)),
2056 SvxIndentValue::zero(), RES_LR_SPACE);
2057 AdjustLRWrapForWordMargins(rRecord, aLR);
2058 rFlyFormat.SetFormatAttr(aLR);
2060 if (rRecord.nDyWrapDistTop || rRecord.nDyWrapDistBottom)
2062 SvxULSpaceItem aUL(writer_cast<sal_uInt16>(rRecord.nDyWrapDistTop),
2063 writer_cast<sal_uInt16>(rRecord.nDyWrapDistBottom), RES_UL_SPACE);
2064 AdjustULWrapForWordMargins(rRecord, aUL);
2065 rFlyFormat.SetFormatAttr(aUL);
2068 // If we are contoured and have a custom polygon...
2069 if (rRecord.pWrapPolygon && rFlyFormat.GetSurround().IsContour())
2071 if (SwNoTextNode* pNd = GetNoTextNodeFromSwFrameFormat(rFlyFormat))
2074 Gather round children and hear of a tale that will raise the
2075 hairs on the back of your neck this dark halloween night.
2077 There is a polygon in word that describes the wrapping around
2078 the graphic.
2080 Here are some sample values for the simplest case of a square
2081 around some solid coloured graphics
2083 X Y Pixel size of graphic
2084 TopLeft -54 21600 400x400
2085 Bottom Right 0 21546
2087 TopLeft -108 21600 200x200
2088 Bottom Right 0 21492
2090 TopLeft -216 21600 100x100
2091 Bottom Right 0 21384
2093 TopLeft -432 21600 50x50
2094 Bottom Right 0 21168
2096 TopLeft -76 21600 283x212
2097 Bottom Right 0 21498
2099 So given that the size of the values remains pretty much the
2100 same despite the size of the graphic, we can tell that the
2101 polygon is measured in units that are independent of the
2102 graphic. But why does the left corner move a different value
2103 to the left each time, and why does the bottom move upwards
2104 each time, when the right and top remain at the same value ?
2106 I have no idea, but clearly once we calculate the values out
2107 we see that the left margin is always a fixed realworld
2108 distance from the true left and the polygon bottom is the same
2109 fixed value from the bottom. i.e. 15twips.
2111 So here we take our word provided polygon, shift it to the
2112 right by 15twips and rescale it widthwise to shrink the width
2113 a little to fit the now moved right margin back to where it
2114 was, and stretch the height a little to make the bottom move
2115 down the missing 15twips then we get a polygon that matches
2116 what I actually see in word
2119 tools::PolyPolygon aPoly(*rRecord.pWrapPolygon);
2120 const Size aSize = pNd->GetTwipSize();
2122 Move to the left by 15twips, and rescale to
2123 a) shrink right bound back to orig position
2124 b) stretch bottom bound to where I think it should have been
2125 in the first place
2127 Fraction aMoveHack(ww::nWrap100Percent, aSize.Width());
2128 aMoveHack *= Fraction(15, 1);
2129 tools::Long nMove(aMoveHack);
2130 aPoly.Move(nMove, 0);
2132 Fraction aHackX(ww::nWrap100Percent, ww::nWrap100Percent + nMove);
2133 Fraction aHackY(ww::nWrap100Percent, ww::nWrap100Percent - nMove);
2134 aPoly.Scale(double(aHackX), double(aHackY));
2136 // Turn polygon back into units that match the graphic's
2137 const Size aOrigSize = pNd->GetGraphic().GetPrefSize();
2138 Fraction aMapPolyX(aOrigSize.Width(), ww::nWrap100Percent);
2139 Fraction aMapPolyY(aOrigSize.Height(), ww::nWrap100Percent);
2140 aPoly.Scale(double(aMapPolyX), double(aMapPolyY));
2142 // #i47277# - contour is already in unit of the
2143 // graphic preferred unit. Thus, call method <SetContour(..)>
2144 pNd->SetContour(&aPoly);
2147 else if (rFlyFormat.GetSurround().IsContour())
2149 const SdrObject* pSdrObj = rFlyFormat.FindSdrObject();
2150 SdrObjKind eKind = pSdrObj ? pSdrObj->GetObjIdentifier() : SdrObjKind::Graphic;
2151 switch (eKind)
2153 case SdrObjKind::Text:
2154 break;
2155 case SdrObjKind::SwFlyDrawObjIdentifier:
2156 default:
2158 // Contour is enabled, but no polygon is set: disable contour, because Word does not
2159 // Writer-style auto-contour in that case.
2160 SwFormatSurround aSurround(rFlyFormat.GetSurround());
2161 aSurround.SetContour(false);
2162 rFlyFormat.SetFormatAttr(aSurround);
2168 static sal_Int32 lcl_ConvertCrop(sal_uInt32 const nCrop, sal_Int32 const nSize)
2170 // cast to sal_Int32 to handle negative crop properly
2171 sal_Int32 const nIntegral(static_cast<sal_Int32>(nCrop) >> 16);
2172 // fdo#77454: heuristic to detect mangled values written by old OOo/LO
2173 if (abs(nIntegral) >= 50) // FIXME: what's a good cut-off?
2175 SAL_INFO("sw.ww8", "ignoring suspiciously large crop: " << nIntegral);
2176 return 0;
2178 return (nIntegral * nSize) + (((nCrop & 0xffff) * nSize) >> 16);
2181 void SwWW8ImplReader::SetAttributesAtGrfNode(const SvxMSDffImportRec& rRecord,
2182 const SwFrameFormat& rFlyFormat, WW8_FSPA const *pF)
2184 const SwNodeIndex* pIdx = rFlyFormat.GetContent(false).GetContentIdx();
2185 SwGrfNode *const pGrfNd(
2186 pIdx ? m_rDoc.GetNodes()[pIdx->GetIndex() + 1]->GetGrfNode() : nullptr);
2187 if (!pGrfNd)
2188 return;
2190 Size aSz(pGrfNd->GetTwipSize());
2191 // use type <sal_uInt64> instead of sal_uLong to get correct results
2192 // in the following calculations.
2193 sal_uInt64 nHeight = aSz.Height();
2194 sal_uInt64 nWidth = aSz.Width();
2195 if (!nWidth && pF)
2196 nWidth = o3tl::saturating_sub(pF->nXaRight, pF->nXaLeft);
2197 else if (!nHeight && pF)
2198 nHeight = o3tl::saturating_sub(pF->nYaBottom, pF->nYaTop);
2200 if (rRecord.nCropFromTop || rRecord.nCropFromBottom ||
2201 rRecord.nCropFromLeft || rRecord.nCropFromRight)
2203 SwCropGrf aCrop; // Cropping is stored in 'fixed floats'
2204 // 16.16 (fraction times total
2205 if (rRecord.nCropFromTop) // image width or height resp.)
2207 aCrop.SetTop(lcl_ConvertCrop(rRecord.nCropFromTop, nHeight));
2209 if (rRecord.nCropFromBottom)
2211 aCrop.SetBottom(lcl_ConvertCrop(rRecord.nCropFromBottom, nHeight));
2213 if (rRecord.nCropFromLeft)
2215 aCrop.SetLeft(lcl_ConvertCrop(rRecord.nCropFromLeft, nWidth));
2217 if (rRecord.nCropFromRight)
2219 aCrop.SetRight(lcl_ConvertCrop(rRecord.nCropFromRight, nWidth));
2222 pGrfNd->SetAttr( aCrop );
2225 bool bFlipH(rRecord.nFlags & ShapeFlag::FlipH);
2226 bool bFlipV(rRecord.nFlags & ShapeFlag::FlipV);
2227 if ( bFlipH || bFlipV )
2229 SwMirrorGrf aMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
2230 if( bFlipH )
2232 if( bFlipV )
2233 aMirror.SetValue(MirrorGraph::Both);
2234 else
2235 aMirror.SetValue(MirrorGraph::Vertical);
2237 else
2238 aMirror.SetValue(MirrorGraph::Horizontal);
2240 pGrfNd->SetAttr( aMirror );
2243 if (!rRecord.pObj)
2244 return;
2246 const SfxItemSet& rOldSet = rRecord.pObj->GetMergedItemSet();
2247 // contrast
2248 if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFCONTRAST,
2249 SdrGrafContrastItem))
2251 SwContrastGrf aContrast(
2252 WW8ITEMVALUE(rOldSet,
2253 SDRATTR_GRAFCONTRAST, SdrGrafContrastItem));
2254 pGrfNd->SetAttr( aContrast );
2257 // luminance
2258 if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFLUMINANCE,
2259 SdrGrafLuminanceItem))
2261 SwLuminanceGrf aLuminance(WW8ITEMVALUE(rOldSet,
2262 SDRATTR_GRAFLUMINANCE, SdrGrafLuminanceItem));
2263 pGrfNd->SetAttr( aLuminance );
2265 // gamma
2266 if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA, SdrGrafGamma100Item))
2268 double fVal = WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA,
2269 SdrGrafGamma100Item);
2270 pGrfNd->SetAttr(SwGammaGrf(fVal/100.));
2273 // drawmode
2274 auto nGrafMode = rOldSet.GetItem<SdrGrafModeItem>(SDRATTR_GRAFMODE)->GetValue();
2275 if ( nGrafMode != GraphicDrawMode::Standard)
2277 SwDrawModeGrf aDrawMode( nGrafMode );
2278 pGrfNd->SetAttr( aDrawMode );
2282 SdrObject* SwWW8ImplReader::CreateContactObject(SwFrameFormat* pFlyFormat)
2284 if (pFlyFormat)
2286 SdrObject* pNewObject = m_bNewDoc ? nullptr : pFlyFormat->FindRealSdrObject();
2287 if (!pNewObject)
2288 pNewObject = pFlyFormat->FindSdrObject();
2289 if (!pNewObject )
2290 if (auto pFlyFrameFormat = dynamic_cast<SwFlyFrameFormat *>( pFlyFormat ))
2292 SwFlyDrawContact* pContactObject = pFlyFrameFormat->GetOrCreateContact();
2293 pNewObject = pContactObject->GetMaster();
2295 return pNewObject;
2297 return nullptr;
2300 // Miserable miserable hack to fudge word's graphic layout in RTL mode to ours.
2301 bool SwWW8ImplReader::MiserableRTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth,
2302 sal_Int16 eHoriOri, sal_Int16 eHoriRel)
2304 if (!IsRightToLeft())
2305 return false;
2306 return RTLGraphicsHack(rLeft, nWidth, eHoriOri, eHoriRel,
2307 m_aSectionManager.GetPageLeft(),
2308 m_aSectionManager.GetPageRight(),
2309 m_aSectionManager.GetPageWidth());
2312 RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec& rRecord, WW8_FSPA& rFSPA,
2313 SfxItemSet &rFlySet)
2315 bool bCurSectionVertical = m_aSectionManager.CurrentSectionIsVertical();
2316 bool bIsObjectLayoutInTableCell
2317 = m_nInTable && IsObjectLayoutInTableCell(rRecord.nGroupShapeBooleanProperties);
2319 if (!rRecord.nXRelTo)
2321 rRecord.nXRelTo = sal_Int32(rFSPA.nbx);
2323 if (!rRecord.nYRelTo)
2325 rRecord.nYRelTo = sal_Int32(rFSPA.nby);
2328 // nXAlign - abs. Position, Left, Centered, Right, Inside, Outside
2329 // nYAlign - abs. Position, Top, Centered, Bottom, Inside, Outside
2331 // nXRelTo - Page printable area, Page, Column, Character
2332 // nYRelTo - Page printable area, Page, Paragraph, Line
2334 const sal_uInt32 nCntXAlign = 6;
2335 const sal_uInt32 nCntYAlign = 6;
2337 const sal_uInt32 nCntRelTo = 4;
2339 const sal_uInt32 nXAlign = nCntXAlign > rRecord.nXAlign ? rRecord.nXAlign : 1;
2340 const sal_uInt32 nYAlign = nCntYAlign > rRecord.nYAlign ? rRecord.nYAlign : 1;
2342 // #i52565# - try to handle special case for objects in tables regarding its X Rel
2344 // if X and Y Rel values are on default take it as a hint, that they have not been set
2345 // by <SwMSDffManager::ProcessObj(..)>
2346 const bool bXYRelHaveDefaultValues = *rRecord.nXRelTo == 2 && *rRecord.nYRelTo == 2;
2347 if (bXYRelHaveDefaultValues && m_nInTable > 0 && !bCurSectionVertical)
2349 if (sal_uInt32(rFSPA.nby) != rRecord.nYRelTo)
2350 rRecord.nYRelTo = sal_uInt32(rFSPA.nby);
2353 const sal_uInt32 nXRelTo
2354 = (rRecord.nXRelTo && nCntRelTo > rRecord.nXRelTo) ? *rRecord.nXRelTo : 1;
2355 const sal_uInt32 nYRelTo
2356 = (rRecord.nYRelTo && nCntRelTo > rRecord.nYRelTo) ? *rRecord.nYRelTo : 1;
2358 const RndStdIds eAnchor
2359 = IsInlineEscherHack() ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_CHAR; // #i43718#
2361 SwFormatAnchor aAnchor( eAnchor );
2362 aAnchor.SetAnchor( m_pPaM->GetPoint() );
2363 rFlySet.Put( aAnchor );
2365 // #i18732#
2366 // Given new layout where everything is changed to be anchored to
2367 // character the following 4 tables may need to be changed.
2369 // horizontal Adjustment
2370 static const sal_Int16 aHoriOriTab[ nCntXAlign ] =
2372 text::HoriOrientation::NONE, // From left position
2373 text::HoriOrientation::LEFT, // left
2374 text::HoriOrientation::CENTER, // centered
2375 text::HoriOrientation::RIGHT, // right
2376 // #i36649#
2377 // - inside -> text::HoriOrientation::LEFT and outside -> text::HoriOrientation::RIGHT
2378 text::HoriOrientation::LEFT, // inside
2379 text::HoriOrientation::RIGHT // outside
2382 // generic vertical Adjustment
2383 static const sal_Int16 aVertOriTab[ nCntYAlign ] =
2385 text::VertOrientation::NONE, // From Top position
2386 text::VertOrientation::TOP, // top
2387 text::VertOrientation::CENTER, // centered
2388 text::VertOrientation::BOTTOM, // bottom
2389 text::VertOrientation::LINE_TOP, // inside (obscure)
2390 text::VertOrientation::LINE_BOTTOM // outside (obscure)
2393 // #i22673# - to-line vertical alignment
2394 static const sal_Int16 aToLineVertOriTab[ nCntYAlign ] =
2396 text::VertOrientation::NONE, // below
2397 text::VertOrientation::LINE_BOTTOM, // top
2398 text::VertOrientation::LINE_CENTER, // centered
2399 text::VertOrientation::LINE_TOP, // bottom
2400 text::VertOrientation::LINE_BOTTOM, // inside (obscure)
2401 text::VertOrientation::LINE_TOP // outside (obscure)
2404 // Adjustment is horizontally relative to...
2405 static const sal_Int16 aHoriRelOriTab[nCntRelTo] =
2407 text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
2408 text::RelOrientation::PAGE_FRAME, // 1 is page margin
2409 text::RelOrientation::FRAME, // 2 is relative to column
2410 text::RelOrientation::CHAR // 3 is relative to character
2413 // Adjustment is vertically relative to...
2414 // #i22673# - adjustment for new vertical alignment at top of line.
2415 static const sal_Int16 aVertRelOriTab[nCntRelTo] =
2417 text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
2418 text::RelOrientation::PAGE_FRAME, // 1 is page margin
2419 text::RelOrientation::FRAME, // 2 is relative to paragraph
2420 text::RelOrientation::TEXT_LINE // 3 is relative to line
2423 if (!bIsObjectLayoutInTableCell && m_nInTable && eAnchor == RndStdIds::FLY_AT_CHAR)
2425 // Microsoft apparently forces layoutInCell behaviour
2426 // if either horizontal orientation is based on character
2427 // or vertical orientation is based on line
2428 // so make it explicit instead of trying to hack in tons of adjustments.
2429 if (aVertRelOriTab[nYRelTo] == text::RelOrientation::TEXT_LINE
2430 || aHoriRelOriTab[nXRelTo] == text::RelOrientation::CHAR)
2432 bIsObjectLayoutInTableCell = true;
2433 rFlySet.Put(SwFormatFollowTextFlow(true));
2437 // If the image is inline, then the relative orientation means nothing,
2438 // so set it up so that if the user changes it into an anchor, it positions usefully.
2439 sal_Int16 eHoriOri
2440 = IsInlineEscherHack() ? text::HoriOrientation::CENTER : aHoriOriTab[ nXAlign ];
2441 sal_Int16 eHoriRel
2442 = IsInlineEscherHack() ? text::RelOrientation::FRAME : aHoriRelOriTab[nXRelTo];
2444 // #i36649# - adjustments for certain alignments
2445 if (eHoriOri == text::HoriOrientation::LEFT && eHoriRel == text::RelOrientation::PAGE_FRAME)
2447 // convert 'left to page' to 'from left -<width> to page text area'
2448 eHoriOri = text::HoriOrientation::NONE;
2449 eHoriRel = text::RelOrientation::PAGE_PRINT_AREA;
2450 const tools::Long nWidth = rFSPA.nXaRight - rFSPA.nXaLeft;
2451 rFSPA.nXaLeft = -nWidth;
2452 rFSPA.nXaRight = 0;
2454 else if (eHoriOri == text::HoriOrientation::RIGHT && eHoriRel == text::RelOrientation::PAGE_FRAME)
2456 // convert 'right to page' to 'from left 0 to right page border'
2457 eHoriOri = text::HoriOrientation::NONE;
2458 eHoriRel = text::RelOrientation::PAGE_RIGHT;
2459 const tools::Long nWidth = rFSPA.nXaRight - rFSPA.nXaLeft;
2460 rFSPA.nXaLeft = 0;
2461 rFSPA.nXaRight = nWidth;
2463 else if ((eHoriOri == text::HoriOrientation::LEFT || eHoriOri == text::HoriOrientation::RIGHT)
2464 && eHoriRel == text::RelOrientation::FRAME)
2466 // relative left/right honors paragraph margins, but not with center or none/absolute offset
2468 if (bIsObjectLayoutInTableCell || !m_nInTable)
2469 eHoriRel = text::RelOrientation::PRINT_AREA;
2472 // #i24255# - position of floating screen objects in
2473 // R2L layout are given in L2R layout, thus convert them of all
2474 // floating screen objects, which are imported.
2476 // Miserable miserable hack.
2477 SwTwips nWidth = o3tl::saturating_sub(rFSPA.nXaRight, rFSPA.nXaLeft);
2478 SwTwips nLeft = rFSPA.nXaLeft;
2479 if (MiserableRTLGraphicsHack(nLeft, nWidth, eHoriOri,
2480 eHoriRel))
2482 rFSPA.nXaLeft = nLeft;
2483 rFSPA.nXaRight = rFSPA.nXaLeft + nWidth;
2487 // if the object is anchored inside a table cell, is horizontally aligned to the paragraph,
2488 // but it is not being forced to 'layout in table cell',
2489 // then convert its horizontal alignment to page text area
2490 // because MSO strangely doesn't orient the object to the cell paragraph it is anchored to
2491 // but to the paragraph that contains the entire table (which is equivalent to page margins).
2492 if (!bIsObjectLayoutInTableCell && m_nInTable && eHoriRel == text::RelOrientation::FRAME)
2494 eHoriRel = text::RelOrientation::PAGE_PRINT_AREA;
2497 // Writer honours this wrap distance when aligned as "left" or "right",
2498 // Word doesn't. Writer doesn't honour it when its "from left".
2499 if (eHoriOri == text::HoriOrientation::LEFT)
2500 rRecord.nDxWrapDistLeft = 0;
2501 else if (eHoriOri == text::HoriOrientation::RIGHT)
2502 rRecord.nDxWrapDistRight = 0;
2504 sal_Int16 eVertRel;
2506 eVertRel = aVertRelOriTab[ nYRelTo ]; // #i18732#
2507 if (bCurSectionVertical && nYRelTo == 2)
2508 eVertRel = text::RelOrientation::PAGE_PRINT_AREA;
2509 // #i22673# - fill <eVertOri> in dependence of <eVertRel>
2510 sal_Int16 eVertOri;
2511 if (eVertRel == text::RelOrientation::TEXT_LINE)
2513 eVertOri = aToLineVertOriTab[ nYAlign ];
2515 else
2517 eVertOri = aVertOriTab[ nYAlign ];
2520 if (bIsObjectLayoutInTableCell && eAnchor == RndStdIds::FLY_AT_CHAR)
2522 // Microsoft is buggy and inconsistent in how they handle layoutInCell.
2523 // Map wrongly-implemented settings to the closest implemented setting
2525 // "page" is implemented as if it was "margin" - to cell spacing, not edge
2526 if (eVertRel == text::RelOrientation::PAGE_FRAME)
2527 eVertRel = text::RelOrientation::PAGE_PRINT_AREA;
2528 // only "from top" and "top" are appropriate. Others are implemented as Top
2529 if (eVertOri != text::VertOrientation::NONE)
2530 eVertOri = text::VertOrientation::TOP;
2533 // Below line in word is a positive value, while in writer its
2534 // negative
2535 tools::Long nYPos = rFSPA.nYaTop;
2536 // #i22673#
2537 if ((eVertRel == text::RelOrientation::TEXT_LINE) && (eVertOri == text::VertOrientation::NONE))
2538 nYPos = -nYPos;
2540 SwFormatHoriOrient aHoriOri(MakeSafePositioningValue(bCurSectionVertical ? nYPos : rFSPA.nXaLeft),
2541 bCurSectionVertical ? eVertOri : eHoriOri,
2542 bCurSectionVertical ? eVertRel : eHoriRel);
2543 if (4 <= nXAlign)
2544 aHoriOri.SetPosToggle(true);
2545 rFlySet.Put(aHoriOri);
2547 rFlySet.Put(SwFormatVertOrient(MakeSafePositioningValue(!bCurSectionVertical ? nYPos : -rFSPA.nXaRight),
2548 !bCurSectionVertical ? eVertOri : eHoriOri,
2549 !bCurSectionVertical ? eVertRel : eHoriRel));
2551 return eAnchor;
2554 // #i84783#
2555 // Return whether the fly specifies layoutInCell.
2556 // (NOTE: there are circumstances where layoutInCell is implemented even when set to false)
2557 bool SwWW8ImplReader::IsObjectLayoutInTableCell(const sal_uInt32 nGroupShapeBooleanProperties) const
2559 bool bIsObjectLayoutInTableCell = false;
2561 if ( m_bVer8 )
2563 sal_uInt16 nWWVersion = m_xWwFib->m_nProduct & 0xE000;
2564 if (nWWVersion == 0)
2566 // 0 nProduct can happen for Word >97 as well, check cswNew in this case instead.
2567 if (m_xWwFib->m_cswNew > 0)
2569 // This is Word >=2000.
2570 nWWVersion = 0x2000;
2574 switch ( nWWVersion )
2576 case 0x0000: // version 8 aka Microsoft Word 97
2578 bIsObjectLayoutInTableCell = false;
2579 OSL_ENSURE(nGroupShapeBooleanProperties == 0,
2580 "no explicit object attribute layout in table cell expected." );
2582 break;
2583 case 0x2000: // version 9 aka Microsoft Word 2000
2584 case 0x4000: // version 10 aka Microsoft Word 2002
2585 case 0x6000: // version 11 aka Microsoft Word 2003
2586 case 0x8000: // version 12 aka Microsoft Word 2007
2587 case 0xC000: // version 14 aka Microsoft Word 2010
2588 case 0xE000: // version 15 aka Microsoft Word 2013
2590 // Documented in [MS-ODRAW], 2.3.4.44 "Group Shape Boolean Properties".
2591 bool fUsefLayoutInCell = (nGroupShapeBooleanProperties & 0x80000000) >> 31;
2592 bool fLayoutInCell = (nGroupShapeBooleanProperties & 0x8000) >> 15;
2593 // If unspecified, defaults to true
2594 bIsObjectLayoutInTableCell = !fUsefLayoutInCell || fLayoutInCell;
2596 break;
2597 default:
2599 OSL_FAIL( "unknown version." );
2604 return bIsObjectLayoutInTableCell;
2607 SwFrameFormat* SwWW8ImplReader::Read_GrafLayer( tools::Long nGrafAnchorCp )
2609 if( m_nIniFlags & WW8FL_NO_GRAFLAYER )
2610 return nullptr;
2612 ::SetProgressState(m_nProgress, m_pDocShell); // Update
2614 m_nDrawCpO = 0;
2615 m_bDrawCpOValid = m_xWwFib->GetBaseCp(m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX, &m_nDrawCpO);
2617 GraphicCtor();
2619 WW8PLCFspecial* pPF = m_xPlcxMan->GetFdoa();
2620 if( !pPF )
2622 OSL_ENSURE( false, "Where is the graphic (1) ?" );
2623 return nullptr;
2626 if( m_bVer67 )
2628 sal_uInt64 nOldPos = m_pStrm->Tell();
2630 m_nDrawXOfs = m_nDrawYOfs = 0;
2631 ReadGrafLayer1(*pPF, nGrafAnchorCp);
2633 m_pStrm->Seek( nOldPos );
2634 return nullptr;
2637 // Normal case of Word 8+ version stuff
2638 pPF->SeekPos( nGrafAnchorCp );
2640 WW8_FC nStartFc;
2641 void* pF0;
2642 if (!pPF->Get(nStartFc, pF0))
2644 OSL_ENSURE( false, "+Where is the graphic (2) ?" );
2645 return nullptr;
2648 WW8_FSPA_SHADOW& rFS = *static_cast<WW8_FSPA_SHADOW*>(pF0);
2649 WW8_FSPA aFSFA;
2650 WW8FSPAShadowToReal(rFS, aFSFA);
2651 if (!aFSFA.nSpId)
2653 OSL_ENSURE( false, "+Where is the graphic (3) ?" );
2654 return nullptr;
2657 if (!m_xMSDffManager->GetModel())
2658 m_xMSDffManager->SetModel(m_pDrawModel, 1440);
2660 tools::Rectangle aRect(aFSFA.nXaLeft, aFSFA.nYaTop, aFSFA.nXaRight, aFSFA.nYaBottom);
2661 SvxMSDffImportData aData( aRect );
2663 rtl::Reference<SdrObject> pObject;
2664 bool bOk = (m_xMSDffManager->GetShape(aFSFA.nSpId, pObject, aData) && pObject);
2666 if (!bOk)
2668 OSL_ENSURE( false, "Where is the Shape ?" );
2669 return nullptr;
2672 // tdf#118375 Word relates position to the unrotated rectangle,
2673 // Writer uses the rotated one.
2674 if (pObject->GetRotateAngle())
2676 tools::Rectangle aObjSnapRect(pObject->GetSnapRect()); // recalculates the SnapRect
2677 aFSFA.nXaLeft = aObjSnapRect.Left();
2678 aFSFA.nYaTop = aObjSnapRect.Top();
2679 aFSFA.nXaRight = aObjSnapRect.Right();
2680 aFSFA.nYaBottom = aObjSnapRect.Bottom();
2683 bool bDone = false;
2684 rtl::Reference<SdrObject> pOurNewObject;
2685 bool bReplaceable = false;
2687 switch (pObject->GetObjIdentifier())
2689 case SdrObjKind::Graphic:
2690 bReplaceable = true;
2691 bDone = true;
2692 break;
2693 case SdrObjKind::OLE2:
2694 bReplaceable = true;
2695 break;
2696 default:
2697 break;
2701 // when in a header or footer word appears to treat all elements as wrap through
2703 // determine wrapping mode
2704 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END> aFlySet(m_rDoc.GetAttrPool());
2705 Reader::ResetFrameFormatAttrs(aFlySet); // tdf#122425: Explicitly remove borders and spacing
2706 css::text::WrapTextMode eSurround = css::text::WrapTextMode_PARALLEL;
2707 bool bContour = false;
2708 switch (aFSFA.nwr)
2710 case 0: // 0 like 2, but doesn't require absolute object
2711 case 2: // 2 wrap around absolute object
2712 eSurround = css::text::WrapTextMode_PARALLEL;
2713 break;
2714 case 1: // 1 no text next to shape
2715 eSurround = css::text::WrapTextMode_NONE;
2716 break;
2717 case 3: // 3 wrap as if no object present
2718 // Special case: on export, inline images are wrapped through as a hack for old formats.
2719 // That is irrelevant for Writer, so instead use the default wrap in that case,
2720 // so that when the user changes it into an anchor, it wraps nicely, and not through.
2721 if (!IsInlineEscherHack())
2722 eSurround = css::text::WrapTextMode_THROUGH;
2723 break;
2724 case 4: // 4 wrap tightly around object
2725 case 5: // 5 wrap tightly, but allow holes
2726 eSurround = css::text::WrapTextMode_PARALLEL;
2727 bContour = true;
2728 break;
2731 // if mode 2 or 4 also regard the additional parameters
2732 if ((2 == aFSFA.nwr) || (4 == aFSFA.nwr))
2734 switch (aFSFA.nwrk)
2736 // 0 wrap both sides
2737 case 0:
2738 eSurround = css::text::WrapTextMode_PARALLEL;
2739 break;
2740 // 1 wrap only on left
2741 case 1:
2742 eSurround = css::text::WrapTextMode_LEFT;
2743 break;
2744 // 2 wrap only on right
2745 case 2:
2746 eSurround = css::text::WrapTextMode_RIGHT;
2747 break;
2748 // 3 wrap only on largest side
2749 case 3:
2750 eSurround = css::text::WrapTextMode_DYNAMIC;
2751 break;
2755 SwFormatSurround aSur( eSurround );
2756 aSur.SetContour( bContour );
2757 aSur.SetOutside(true); // Winword can only do outside contours
2758 aFlySet.Put( aSur );
2760 // now position imported object correctly and so on (can be a whole group)
2762 OSL_ENSURE(!((aData.size() != 1) && bReplaceable),
2763 "Replaceable drawing with > 1 entries ?");
2765 if (aData.size() != 1)
2766 bReplaceable = false;
2769 Get the record for top level object, so we can get the word anchoring
2770 and wrapping information for it.
2772 SvxMSDffImportRec* pRecord = aData.find(pObject.get());
2773 OSL_ENSURE(pRecord, "how did that happen?");
2774 if (!pRecord)
2776 // remove old object from the Z-Order list
2777 m_xMSDffManager->RemoveFromShapeOrder(pObject.get());
2778 return nullptr;
2780 const bool bLayoutInTableCell =
2781 m_nInTable && IsObjectLayoutInTableCell(pRecord->nGroupShapeBooleanProperties);
2783 // #i18732# - Switch on 'follow text flow', if object is laid out
2784 // inside table cell
2785 if (bLayoutInTableCell)
2787 SwFormatFollowTextFlow aFollowTextFlow( true );
2788 aFlySet.Put( aFollowTextFlow );
2791 // #i21847#
2792 // Some shapes are set to *hidden*, don't import those ones.
2793 if (pRecord->bHidden)
2795 // remove old object from the Z-Order list
2796 m_xMSDffManager->RemoveFromShapeOrder(pObject.get());
2797 return nullptr;
2800 sal_uInt16 nCount = pObject->GetUserDataCount();
2801 if(nCount)
2803 OUString lnName, aObjName, aTarFrame;
2804 for (sal_uInt16 i = 0; i < nCount; i++ )
2806 SdrObjUserData* pData = pObject->GetUserData( i );
2807 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
2808 && pData->GetId() == SW_UD_IMAPDATA)
2810 SwMacroInfo* macInf = dynamic_cast<SwMacroInfo*>(pData);
2811 if (macInf && macInf->GetShapeId() == aFSFA.nSpId)
2813 lnName = macInf->GetHlink();
2814 aObjName = macInf->GetName();
2815 aTarFrame = macInf->GetTarFrame();
2816 break;
2820 std::unique_ptr<SwFormatURL> pFormatURL(new SwFormatURL());
2821 pFormatURL->SetURL( lnName, false );
2822 if (!aObjName.isEmpty())
2823 pFormatURL->SetName(aObjName);
2824 if (!aTarFrame.isEmpty())
2825 pFormatURL->SetTargetFrameName(aTarFrame);
2826 pFormatURL->SetMap(nullptr);
2827 aFlySet.Put(std::move(pFormatURL));
2830 // If we are to be "below text" then we are not to be opaque
2831 // #i14045# MM If we are in a header or footer then make the object transparent
2832 // Not exactly like word but close enough for now
2834 // both flags <bBelowText> and <bDrawHell> have to be set to move object into the background.
2835 // #i46794# - it reveals that value of flag <bBelowText> can be neglected.
2836 const bool bMoveToBackground = pRecord->bDrawHell ||
2837 ((m_bIsHeader || m_bIsFooter) && aFSFA.nwr == 3);
2838 if ( bMoveToBackground )
2839 aFlySet.Put(SvxOpaqueItem(RES_OPAQUE,false));
2841 OUString aObjName = pObject->GetName();
2843 bool bDrawObj = false;
2844 bool bFrame = false;
2846 SwFrameFormat* pRetFrameFormat = nullptr;
2847 if (bReplaceable)
2849 // Single graphics or ole objects
2850 pRetFrameFormat = ImportReplaceableDrawables(pObject, pOurNewObject, *pRecord, aFSFA, aFlySet);
2852 else
2854 bDrawObj = true;
2856 // Drawing objects, (e.g. ovals or drawing groups)
2857 if (aFSFA.bRcaSimple)
2859 aFSFA.nbx = WW8_FSPA::RelPageBorder;
2860 aFSFA.nby = WW8_FSPA::RelPageBorder;
2863 RndStdIds eAnchor = ProcessEscherAlign(*pRecord, aFSFA, aFlySet);
2865 // Should we, and is it possible to make this into a writer textbox
2866 if ((!(m_nIniFlags1 & WW8FL_NO_FLY_FOR_TXBX)) && pRecord->bReplaceByFly)
2868 pRetFrameFormat
2869 = ConvertDrawTextToFly(pObject, pOurNewObject, *pRecord, eAnchor, aFSFA, aFlySet);
2870 if (pRetFrameFormat)
2872 bDone = true;
2873 bDrawObj = false;
2874 bFrame = true;
2878 if (!bDone)
2880 sw::util::SetLayer aSetLayer(m_rDoc);
2881 if ( bMoveToBackground )
2882 aSetLayer.SendObjectToHell(*pObject);
2883 else
2884 aSetLayer.SendObjectToHeaven(*pObject);
2886 if (!IsInlineEscherHack())
2888 /* Need to make sure that the correct layer ordering is applied. */
2889 // pass information, if object is in page header|footer to method.
2890 m_xWWZOrder->InsertEscherObject(pObject.get(), aFSFA.nSpId, pRecord->bDrawHell,
2891 m_bIsHeader || m_bIsFooter);
2893 else
2895 m_xWWZOrder->InsertTextLayerObject(pObject.get());
2898 pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pObject, aFlySet );
2900 OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() ==
2901 eAnchor, "Not the anchor type requested!");
2904 Insert text if necessary into textboxes contained in groups.
2906 for (const auto& it : aData)
2908 pRecord = it.get();
2909 if (pRecord->pObj && pRecord->aTextId.nTxBxS)
2910 { // #i52825# pRetFrameFormat can be NULL
2911 pRetFrameFormat = MungeTextIntoDrawBox(
2912 *pRecord, nGrafAnchorCp, pRetFrameFormat);
2918 SwDrawFrameFormat* pDrawFrameFormat = dynamic_cast<SwDrawFrameFormat*>(pRetFrameFormat);
2919 // #i44344#, #i44681# - positioning attributes already set
2920 if (pDrawFrameFormat)
2921 pDrawFrameFormat->PosAttrSet();
2922 if (!IsInlineEscherHack() && pRetFrameFormat)
2923 MapWrapIntoFlyFormat(*pRecord, *pRetFrameFormat);
2925 // Set frame name with object name
2926 if (pRetFrameFormat /*#i52825# */)
2928 if (!aObjName.isEmpty())
2929 pRetFrameFormat->SetFormatName( aObjName );
2930 if (pRetFrameFormat->GetName().isEmpty())
2932 if (bDrawObj)
2933 pRetFrameFormat->SetFormatName(m_rDoc.GetUniqueDrawObjectName());
2934 else if (bFrame)
2935 pRetFrameFormat->SetFormatName(m_rDoc.GetUniqueFrameName());
2938 return AddAutoAnchor(pRetFrameFormat);
2941 SwFrameFormat *SwWW8ImplReader::AddAutoAnchor(SwFrameFormat *pFormat)
2944 * anchored to character at the current position will move along the
2945 * paragraph as text is added because we are at the insertion point.
2947 * Leave to later and set the correct location then.
2949 if (pFormat && (pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR))
2951 m_xAnchorStck->AddAnchor(*m_pPaM->GetPoint(), pFormat);
2953 return pFormat;
2956 SwFrameFormat* SwWW8ImplReader::MungeTextIntoDrawBox(SvxMSDffImportRec& rRecord,
2957 tools::Long nGrafAnchorCp, SwFrameFormat* pRetFrameFormat)
2959 rtl::Reference<SdrObject> pTrueObject = rRecord.pObj;
2961 rtl::Reference<SdrTextObj> pSdrTextObj;
2963 // check for group object (e.g. two parentheses)
2964 if (SdrObjGroup* pThisGroup = dynamic_cast<SdrObjGroup*>(rRecord.pObj.get()))
2966 // Group objects don't have text. Insert a text object into
2967 // the group for holding the text.
2968 pSdrTextObj = new SdrRectObj(
2969 *m_pDrawModel,
2970 SdrObjKind::Text,
2971 pThisGroup->GetCurrentBoundRect());
2973 SfxItemSet aSet(m_pDrawModel->GetItemPool());
2974 aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2975 aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2976 aSet.Put(SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE ));
2977 aSet.Put(makeSdrTextAutoGrowHeightItem(false));
2978 aSet.Put(makeSdrTextAutoGrowWidthItem(false));
2979 pSdrTextObj->SetMergedItemSet(aSet);
2980 pSdrTextObj->NbcSetLayer( pThisGroup->GetLayer() );
2981 pThisGroup->GetSubList()->NbcInsertObject(pSdrTextObj.get());
2983 else
2984 pSdrTextObj = DynCastSdrTextObj(rRecord.pObj.get());
2986 if( pSdrTextObj )
2988 Size aObjSize(pSdrTextObj->GetSnapRect().GetWidth(),
2989 pSdrTextObj->GetSnapRect().GetHeight());
2991 // Object is part of a group?
2992 SdrObject* pGroupObject = pSdrTextObj->getParentSdrObjectFromSdrObject();
2994 const size_t nOrdNum = pSdrTextObj->GetOrdNum();
2995 bool bEraseThisObject;
2996 InsertTxbxText(pSdrTextObj.get(), &aObjSize, rRecord.aTextId.nTxBxS, rRecord.aTextId.nSequence,
2997 nGrafAnchorCp, pRetFrameFormat,
2998 (pSdrTextObj.get() != pTrueObject.get()) || (nullptr != pGroupObject), bEraseThisObject,
2999 nullptr, nullptr, nullptr, nullptr, &rRecord);
3001 // was this object replaced ??
3002 if (bEraseThisObject)
3004 if( pGroupObject || (pSdrTextObj.get() != pTrueObject.get()) )
3006 // Object is already replaced by a new SdrGrafObj (in the group
3007 // and) the Drawing-Page.
3009 SdrObject* pNewObj = pGroupObject ?
3010 pGroupObject->GetSubList()->GetObj(nOrdNum) : pTrueObject.get();
3011 if (pSdrTextObj.get() != pNewObj)
3013 // Replace object in the Z-Order-List
3014 m_xMSDffManager->ExchangeInShapeOrder(pSdrTextObj.get(), 0, pNewObj);
3015 // and save the new object.
3016 rRecord.pObj = pNewObj;
3019 else
3021 // remove the object from Z-Order list
3022 m_xMSDffManager->RemoveFromShapeOrder( pSdrTextObj.get() );
3023 // take the object from the drawing page
3024 if( pSdrTextObj->getSdrPageFromSdrObject() )
3025 m_pDrawPg->RemoveObject( pSdrTextObj->GetOrdNum() );
3026 // and delete FrameFormat, because replaced by graphic
3027 // (this also deletes the object)
3028 m_rDoc.DelFrameFormat( pRetFrameFormat );
3029 pRetFrameFormat = nullptr;
3030 // also delete the object record
3031 rRecord.pObj = nullptr;
3034 else
3036 // use ww8-default border distance
3037 SfxItemSetFixed<SDRATTR_TEXT_LEFTDIST, SDRATTR_TEXT_LOWERDIST>
3038 aItemSet(m_pDrawModel->GetItemPool());
3039 aItemSet.Put(makeSdrTextLeftDistItem(rRecord.nDxTextLeft));
3040 aItemSet.Put(makeSdrTextRightDistItem(rRecord.nDxTextRight));
3041 aItemSet.Put(makeSdrTextUpperDistItem(rRecord.nDyTextTop));
3042 aItemSet.Put(makeSdrTextLowerDistItem(rRecord.nDyTextBottom));
3043 pSdrTextObj->SetMergedItemSetAndBroadcast(aItemSet);
3046 return pRetFrameFormat;
3049 SwFlyFrameFormat* SwWW8ImplReader::ConvertDrawTextToFly(rtl::Reference<SdrObject>& rpObject,
3050 rtl::Reference<SdrObject>& rpOurNewObject,
3051 const SvxMSDffImportRec& rRecord,
3052 RndStdIds eAnchor, const WW8_FSPA& rF,
3053 SfxItemSet &rFlySet)
3055 SwFlyFrameFormat* pRetFrameFormat = nullptr;
3056 tools::Long nStartCp;
3057 tools::Long nEndCp;
3059 // Check if this textbox chain contains text as conversion of an empty
3060 // chain would not make sense.
3061 if (TxbxChainContainsRealText(rRecord.aTextId.nTxBxS, nStartCp, nEndCp))
3063 // The Text is not read into SdrTextObj! Rather insert a frame and
3064 // insert the text from nStartCp to nEndCp.
3066 // More attributes can be used in a frame compared to the
3067 // Edit-Engine, and it can contain field, OLEs or graphics...
3068 tools::Rectangle aInnerDist(rRecord.nDxTextLeft, rRecord.nDyTextTop, rRecord.nDxTextRight,
3069 rRecord.nDyTextBottom);
3071 SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, rF.nXaRight - rF.nXaLeft,
3072 rF.nYaBottom - rF.nYaTop);
3073 aFrameSize.SetWidthSizeType(rRecord.bAutoWidth ? SwFrameSize::Variable
3074 : SwFrameSize::Fixed);
3075 rFlySet.Put(aFrameSize);
3077 MatchSdrItemsIntoFlySet(rpObject.get(), rFlySet, rRecord.eLineStyle, rRecord.eLineDashing,
3078 rRecord.eShapeType, aInnerDist);
3080 SdrTextObj *pSdrTextObj = DynCastSdrTextObj(rpObject.get());
3081 if (pSdrTextObj && pSdrTextObj->IsVerticalWriting())
3082 rFlySet.Put(SvxFrameDirectionItem(SvxFrameDirection::Vertical_RL_TB, RES_FRAMEDIR));
3084 pRetFrameFormat = m_rDoc.MakeFlySection(eAnchor, m_pPaM->GetPoint(), &rFlySet);
3085 OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() == eAnchor,
3086 "Not the anchor type requested!");
3088 // if everything is OK, find pointer on new object and correct
3089 // Z-order list (or delete entry)
3090 rpOurNewObject = CreateContactObject(pRetFrameFormat);
3092 // remove old object from the Z-Order list
3093 m_xMSDffManager->RemoveFromShapeOrder( rpObject.get() );
3095 // and delete the object
3096 rpObject.clear();
3098 NB: only query pOrgShapeObject starting here!
3101 if (rpOurNewObject)
3104 We do not store our rpOutNewObject in the ShapeOrder because we
3105 have a FrameFormat from which we can regenerate the contact object when
3106 we need it. Because, we can have frames anchored to paragraphs in
3107 header/footers and we can copy header/footers, if we do copy a
3108 header/footer with a nonpage anchored frame in it then the contact
3109 objects are invalidated. Under this condition the FrameFormat will be
3110 updated to reflect this change and can be used to get a new
3111 contact object, while a raw rpOutNewObject stored here becomes
3112 deleted and useless.
3114 m_xMSDffManager->StoreShapeOrder(rF.nSpId,
3115 (static_cast<sal_uLong>(rRecord.aTextId.nTxBxS) << 16) +
3116 rRecord.aTextId.nSequence, nullptr, pRetFrameFormat);
3118 // The Contact object has to be inserted into the draw page, so
3119 // SwWW8ImplReader::LoadDoc1() can determine the z-order.
3120 if (!rpOurNewObject->IsInserted())
3122 // pass information, if object is in page header|footer to method.
3123 m_xWWZOrder->InsertEscherObject(rpOurNewObject.get(), rF.nSpId, rRecord.bDrawHell,
3124 m_bIsHeader || m_bIsFooter);
3128 // Box-0 receives the text for the whole chain!
3129 if (!rRecord.aTextId.nSequence)
3131 // save flags etc and reset them
3132 WW8ReaderSave aSave( this );
3134 MoveInsideFly(pRetFrameFormat);
3136 m_xWWZOrder->InsideEscher(rF.nSpId);
3138 // read in the text
3139 m_bTxbxFlySection = true;
3140 bool bJoined = ReadText(nStartCp, (nEndCp-nStartCp),
3141 MAN_MAINTEXT == m_xPlcxMan->GetManType() ?
3142 MAN_TXBX : MAN_TXBX_HDFT);
3144 m_xWWZOrder->OutsideEscher();
3146 MoveOutsideFly(pRetFrameFormat, aSave.GetStartPos(),!bJoined);
3148 aSave.Restore( this );
3150 StripNegativeAfterIndent(pRetFrameFormat);
3154 return pRetFrameFormat;
3157 void MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec &rRecord, SfxItemSet &rFlySet)
3159 if (rRecord.bVFlip || rRecord.bHFlip)
3161 MirrorGraph eType(MirrorGraph::Dont);
3162 if (rRecord.bVFlip && rRecord.bHFlip)
3163 eType = MirrorGraph::Both;
3164 else if (rRecord.bVFlip)
3165 eType = MirrorGraph::Horizontal;
3166 else
3167 eType = MirrorGraph::Vertical;
3168 rFlySet.Put( SwMirrorGrf(eType) );
3172 SwFlyFrameFormat* SwWW8ImplReader::ImportReplaceableDrawables(rtl::Reference<SdrObject> &rpObject,
3173 rtl::Reference<SdrObject> &rpOurNewObject,
3174 SvxMSDffImportRec& rRecord,
3175 WW8_FSPA& rF,
3176 SfxItemSet &rFlySet )
3178 SwFlyFrameFormat* pRetFrameFormat = nullptr;
3179 sal_Int32 nWidthTw = o3tl::saturating_sub(rF.nXaRight, rF.nXaLeft);
3180 if (0 > nWidthTw)
3181 nWidthTw = 0;
3182 sal_Int32 nHeightTw = o3tl::saturating_sub(rF.nYaBottom, rF.nYaTop);
3183 if (0 > nHeightTw)
3184 nHeightTw = 0;
3186 ProcessEscherAlign(rRecord, rF, rFlySet);
3188 rFlySet.Put(SwFormatFrameSize(SwFrameSize::Fixed, nWidthTw, nHeightTw));
3190 SfxItemSetFixed<RES_GRFATR_BEGIN, RES_GRFATR_END-1> aGrSet(m_rDoc.GetAttrPool());
3192 // Note that the escher inner distance only seems to be honoured in
3193 // word for textboxes, not for graphics and ole objects.
3194 tools::Rectangle aInnerDist(0, 0, 0, 0);
3196 MatchSdrItemsIntoFlySet(rpObject.get(), rFlySet, rRecord.eLineStyle, rRecord.eLineDashing,
3197 rRecord.eShapeType, aInnerDist);
3199 MatchEscherMirrorIntoFlySet(rRecord, aGrSet);
3201 OUString aObjectName(rpObject->GetName());
3202 if (SdrObjKind::OLE2 == rpObject->GetObjIdentifier())
3203 pRetFrameFormat = InsertOle(*static_cast<SdrOle2Obj*>(rpObject.get()), rFlySet, &aGrSet);
3204 else
3206 const SdrGrafObj *pGrf = static_cast<const SdrGrafObj*>(rpObject.get());
3207 bool bDone = false;
3208 if (pGrf->IsLinkedGraphic() && !pGrf->GetFileName().isEmpty())
3210 GraphicType eType = pGrf->GetGraphicType();
3211 OUString aGrfName(
3212 URIHelper::SmartRel2Abs(
3213 INetURLObject(m_sBaseURL), pGrf->GetFileName(),
3214 URIHelper::GetMaybeFileHdl()));
3215 // correction of fix for issue #i10939#:
3216 // One of the two conditions have to be true to insert the graphic
3217 // as a linked graphic -
3218 if (GraphicType::NONE == eType || CanUseRemoteLink(aGrfName))
3220 pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
3221 *m_pPaM, aGrfName, OUString(), nullptr,
3222 &rFlySet, &aGrSet, nullptr);
3223 bDone = true;
3226 if (!bDone)
3228 const Graphic& rGraph = pGrf->GetGraphic();
3229 pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
3230 *m_pPaM, OUString(), OUString(), &rGraph,
3231 &rFlySet, &aGrSet, nullptr);
3235 if (pRetFrameFormat)
3237 if (SdrObjKind::OLE2 != rpObject->GetObjIdentifier())
3238 SetAttributesAtGrfNode(rRecord, *pRetFrameFormat, &rF);
3239 // avoid multiple occurrences of the same graphic name
3240 m_aGrfNameGenerator.SetUniqueGraphName(pRetFrameFormat, aObjectName);
3242 // if everything is OK, determine pointer to new object and correct
3243 // Z-Order-List accordingly (or delete entry)
3244 rpOurNewObject = CreateContactObject(pRetFrameFormat);
3246 // remove old object from Z-Order-List
3247 m_xMSDffManager->RemoveFromShapeOrder( rpObject.get() );
3248 // remove from Drawing-Page
3249 if( rpObject->getSdrPageFromSdrObject() )
3250 m_pDrawPg->RemoveObject( rpObject->GetOrdNum() );
3252 // and delete the object
3253 rpObject.clear();
3256 Warning: from now on query only pOrgShapeObject!
3259 // add Contact-Object to the Z-Order-List and the page
3260 if (rpOurNewObject)
3262 if (!m_bHdFtFootnoteEdn)
3263 m_xMSDffManager->StoreShapeOrder(rF.nSpId, 0, rpOurNewObject.get());
3265 // The Contact-Object MUST be set in the Draw-Page, so that in
3266 // SwWW8ImplReader::LoadDoc1() the Z-Order can be defined !!!
3267 if (!rpOurNewObject->IsInserted())
3269 // pass information, if object is in page header|footer to method.
3270 m_xWWZOrder->InsertEscherObject(rpOurNewObject.get(), rF.nSpId, rRecord.bDrawHell,
3271 m_bIsHeader || m_bIsFooter);
3274 return pRetFrameFormat;
3277 void SwWW8ImplReader::GraphicCtor() // For SVDraw and VCControls and Escher
3279 if (m_pDrawModel)
3280 return;
3282 m_rDoc.getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed
3283 m_pDrawModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
3284 OSL_ENSURE(m_pDrawModel, "Cannot create DrawModel");
3285 m_pDrawPg = m_pDrawModel->GetPage(0);
3287 m_xMSDffManager.reset(new SwMSDffManager(*this, m_bSkipImages));
3288 m_xMSDffManager->SetModel(m_pDrawModel, 1440);
3290 Now the dff manager always needs a controls converter as well, but a
3291 control converter may still exist without a dffmanager.
3293 m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM));
3295 m_xWWZOrder.reset(new wwZOrderer(sw::util::SetLayer(m_rDoc), m_pDrawPg,
3296 m_xMSDffManager->GetShapeOrders()));
3299 void SwWW8ImplReader::GraphicDtor()
3301 m_pDrawEditEngine.reset(); // maybe created by graphic
3302 m_xWWZOrder.reset(); // same
3305 void SwWW8FltAnchorStack::AddAnchor(const SwPosition& rPos, SwFrameFormat *pFormat)
3307 OSL_ENSURE(pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR,
3308 "Don't use fltanchors with inline frames, slap!");
3309 NewAttr(rPos, SwFltAnchor(pFormat));
3312 void SwWW8FltAnchorStack::Flush()
3314 size_t nCnt = size();
3315 while (nCnt)
3317 SwFltStackEntry &rEntry = (*this)[0];
3318 SwPosition aDummy(rEntry.m_aMkPos.m_nNode);
3319 SetAttrInDoc(aDummy, rEntry);
3320 DeleteAndDestroy(0);
3321 --nCnt;
3325 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */