1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/ulspitem.hxx>
62 #include <editeng/brushitem.hxx>
63 #include <editeng/opaqitem.hxx>
64 #include <editeng/shaditem.hxx>
65 #include <editeng/boxitem.hxx>
66 #include <editeng/outliner.hxx>
67 #include <editeng/frmdiritem.hxx>
68 #include <svx/xfltrit.hxx>
69 #include <filter/msfilter/msdffimp.hxx>
71 #include <fmtornt.hxx>
72 #include <fmtcntnt.hxx>
74 #include <fmtanchr.hxx>
77 #include <drawdoc.hxx>
78 #include <IDocumentDrawModelAccess.hxx>
80 #include <dcontact.hxx>
83 #include "ww8struc.hxx"
84 #include "ww8scan.hxx"
86 #include "ww8par2.hxx"
87 #include "ww8graf.hxx"
88 #include <fmtinfmt.hxx>
89 #include <editeng/eeitem.hxx>
90 #include <editeng/flditem.hxx>
91 #include <fmtfollowtextflow.hxx>
92 #include "writerhelper.hxx"
93 #include "writerwordglue.hxx"
94 #include <basegfx/point/b2dpoint.hxx>
95 #include <basegfx/polygon/b2dpolygon.hxx>
96 #include <editeng/editobj.hxx>
99 #include <o3tl/enumrange.hxx>
100 #include <o3tl/safeint.hxx>
103 #include <filter/msfilter/escherex.hxx>
105 #include "sprmids.hxx"
107 using ::editeng::SvxBorderLine
;
108 using namespace ::com::sun::star
;
109 using namespace sw::types
;
110 using namespace sw::util
;
113 static Color
WW8TransCol(SVBT32 nWC
)
115 #if 1 // 1 = use predefined color, 0 = ignore
117 // color table to convert RGB values to pre-defined colors
118 // (to make the writer UI show the right color names)
119 // the table is split in base 3, the greys are missing as
120 // they don't fit into that system (4 values: bw, wb, 2 * grey)
121 static const Color eColA
[] = { // B G R B G R B G R
122 COL_BLACK
, COL_RED
, COL_LIGHTRED
, // 0 0 0, 0 0 1, 0 0 2
123 COL_GREEN
, COL_BROWN
, COL_BLACK
, // 0 1 0, 0 1 1, 0 1 2
124 COL_LIGHTGREEN
, COL_BLACK
, COL_YELLOW
, // 0 2 0, 0 2 1, 0 2 2
125 COL_BLUE
, COL_MAGENTA
, COL_BLACK
, // 1 0 0, 1 0 1, 1 0 2
126 COL_CYAN
, COL_LIGHTGRAY
, COL_BLACK
, // 1 1 0, 1 1 1, 1 1 2
127 COL_BLACK
, COL_BLACK
, COL_BLACK
, // 1 2 0, 1 2 1, 1 2 2
128 COL_LIGHTBLUE
, COL_BLACK
, COL_LIGHTMAGENTA
, // 2 0 0, 2 0 1, 2 0 2
129 COL_BLACK
, COL_BLACK
, COL_BLACK
, // 2 1 0, 2 1 1, 2 1 2
130 COL_LIGHTCYAN
, COL_BLACK
, COL_WHITE
}; // 2 2 0, 2 2 1, 2 2 2
132 // In nWC[3] is a byte that's not described in the WW documentation.
133 // Its meaning appears to be the following: For 0, it's a normal color
134 // whose RGB values are in nWC[0..2]. If nWC[3] is 0x1, 0x7d or 0x83,
135 // it's a grey value whose black portion is given in 0.5% in nWC[0].
136 // I guess that BIT(0) in nWC[3] is relevant for distinguishing RGB/Grey.
138 if( !( nWC
[3] & 0x1 ) && // not special (grey)
139 ( ( nWC
[0] == 0 || nWC
[0]== 0x80 || nWC
[0] == 0xff ) // R
140 && ( nWC
[1] == 0 || nWC
[1]== 0x80 || nWC
[1] == 0xff ) // G
141 && ( nWC
[2] == 0 || nWC
[2]== 0x80 || nWC
[2] == 0xff ) ) ){// B
142 int nIdx
= 0; // and now: Idx-calculation in base 3
143 for (int i
= 2; i
>= 0; i
--)
147 nIdx
+= ((nWC
[i
] == 0xff) ? 2 : 1);
149 if (eColA
[nIdx
] != COL_BLACK
)
150 return eColA
[nIdx
]; // default color
156 // Special color gray
157 sal_uInt8 u
= static_cast<sal_uInt8
>( static_cast<sal_uLong
>( 200 - nWC
[0] ) * 256 / 200 );
158 return Color(u
, u
, u
);
162 return Color(nWC
[0], nWC
[1], nWC
[2]);
165 void wwFrameNamer::SetUniqueGraphName(SwFrameFormat
*pFrameFormat
, std::u16string_view rFixed
)
167 if (mbIsDisabled
|| rFixed
.empty())
170 pFrameFormat
->SetFormatName(msSeed
+OUString::number(++mnImportedGraphicsCount
) + ": " + rFixed
);
173 // ReadGrafStart reads object data and if necessary creates an anchor
174 bool SwWW8ImplReader::ReadGrafStart(void* pData
, short nDataSiz
,
175 WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
177 if (SVBT16ToUInt16(pHd
->cb
) < sizeof(WW8_DPHEAD
) + nDataSiz
)
179 OSL_ENSURE( false, "+graphic element: too short?" );
180 m_pStrm
->SeekRel(SVBT16ToUInt16(pHd
->cb
) - sizeof(WW8_DPHEAD
));
184 bool bCouldRead
= checkRead(*m_pStrm
, pData
, nDataSiz
);
185 OSL_ENSURE(bCouldRead
, "Short Graphic header");
189 SwFormatAnchor
aAnchor( RndStdIds::FLY_AT_CHAR
);
190 aAnchor
.SetAnchor( m_pPaM
->GetPoint() );
193 m_nDrawXOfs2
= m_nDrawXOfs
;
194 m_nDrawYOfs2
= m_nDrawYOfs
;
199 // SetStdAttr() sets standard attributes
200 static void SetStdAttr( SfxItemSet
& rSet
, WW8_DP_LINETYPE
& rL
,
201 WW8_DP_SHADOW
const & rSh
)
203 if( SVBT16ToUInt16( rL
.lnps
) == 5 ){ // invisible
204 rSet
.Put( XLineStyleItem( drawing::LineStyle_NONE
) );
206 Color
aCol( WW8TransCol( rL
.lnpc
) ); // line color
207 rSet
.Put( XLineColorItem( OUString(), aCol
) );
208 rSet
.Put( XLineWidthItem( SVBT16ToUInt16( rL
.lnpw
) ) );
210 if( SVBT16ToUInt16( rL
.lnps
) >= 1
211 && SVBT16ToUInt16(rL
.lnps
) <= 4 ){ // line style
212 rSet
.Put( XLineStyleItem( drawing::LineStyle_DASH
) );
213 sal_Int16 nLen
= SVBT16ToUInt16( rL
.lnpw
);
214 XDash
aD( css::drawing::DashStyle_RECT
, 1, 2 * nLen
, 1, 5 * nLen
, 5 * nLen
);
215 switch( SVBT16ToUInt16( rL
.lnps
) ){
216 case 1: aD
.SetDots( 0 ); // Dash
217 aD
.SetDashLen( 6 * nLen
);
218 aD
.SetDistance( 4 * nLen
);
220 case 2: aD
.SetDashes( 0 ); break; // Dot
221 case 3: break; // Dash Dot
222 case 4: aD
.SetDots( 2 ); break; // Dash Dot Dot
224 rSet
.Put( XLineDashItem( OUString(), aD
) );
226 rSet
.Put( XLineStyleItem( drawing::LineStyle_SOLID
) ); // needed for TextBox
229 if( SVBT16ToUInt16( rSh
.shdwpi
) ){ // shadow
230 rSet
.Put(makeSdrShadowItem(true));
231 rSet
.Put( makeSdrShadowXDistItem( SVBT16ToUInt16( rSh
.xaOffset
) ) );
232 rSet
.Put( makeSdrShadowYDistItem( SVBT16ToUInt16( rSh
.yaOffset
) ) );
236 // SetFill() sets fill attributes such as fore- and background color and
237 // pattern by reducing to a color
238 // SetFill() doesn't yet set a pattern, because Sdr can't easily do that
239 // and the Sdr hatching (XDash) isn't finished yet.
240 // Instead, a mixed color will be picked that's between the selected ones.
241 static void SetFill( SfxItemSet
& rSet
, WW8_DP_FILL
& rFill
)
243 static const sal_uInt8 nPatA
[] =
245 0, 0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80,
246 90, 50, 50, 50, 50, 50, 50, 33, 33, 33, 33, 33, 33
248 sal_uInt16 nPat
= SVBT16ToUInt16(rFill
.flpp
);
250 if (nPat
== 0) // transparent
251 rSet
.Put(XFillStyleItem(drawing::FillStyle_NONE
));
254 rSet
.Put(XFillStyleItem(drawing::FillStyle_SOLID
)); // necessary for textbox
255 if (nPat
<= 1 || (SAL_N_ELEMENTS(nPatA
) <= nPat
))
257 // Solid background or unknown
258 rSet
.Put(XFillColorItem(OUString(), WW8TransCol(rFill
.dlpcBg
)));
261 { // Brush -> color mix
262 Color
aB( WW8TransCol( rFill
.dlpcBg
) );
263 Color
aF( WW8TransCol( rFill
.dlpcFg
) );
264 aB
.SetRed( static_cast<sal_uInt8
>( ( static_cast<sal_uLong
>(aF
.GetRed()) * nPatA
[nPat
]
265 + static_cast<sal_uLong
>(aB
.GetRed()) * ( 100 - nPatA
[nPat
] ) ) / 100 ) );
266 aB
.SetGreen( static_cast<sal_uInt8
>( ( static_cast<sal_uLong
>(aF
.GetGreen()) * nPatA
[nPat
]
267 + static_cast<sal_uLong
>(aB
.GetGreen()) * ( 100 - nPatA
[nPat
] ) ) / 100 ) );
268 aB
.SetBlue( static_cast<sal_uInt8
>( ( static_cast<sal_uLong
>(aF
.GetBlue()) * nPatA
[nPat
]
269 + static_cast<sal_uLong
>(aB
.GetBlue()) * ( 100 - nPatA
[nPat
] ) ) / 100 ) );
270 rSet
.Put( XFillColorItem( OUString(), aB
) );
275 static void SetLineEndAttr( SfxItemSet
& rSet
, WW8_DP_LINEEND
const & rLe
,
276 WW8_DP_LINETYPE
const & rLt
)
278 sal_uInt16 aSB
= SVBT16ToUInt16( rLe
.aStartBits
);
281 ::basegfx::B2DPolygon aPolygon
;
282 aPolygon
.append(::basegfx::B2DPoint(0.0, 330.0));
283 aPolygon
.append(::basegfx::B2DPoint(100.0, 0.0));
284 aPolygon
.append(::basegfx::B2DPoint(200.0, 330.0));
285 aPolygon
.setClosed(true);
286 rSet
.Put( XLineEndItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon
) ) );
287 sal_uInt16 nSiz
= SVBT16ToUInt16( rLt
.lnpw
)
288 * ( ( aSB
>> 2 & 0x3 ) + ( aSB
>> 4 & 0x3 ) );
289 if( nSiz
< 220 ) nSiz
= 220;
290 rSet
.Put(XLineEndWidthItem(nSiz
));
291 rSet
.Put(XLineEndCenterItem(false));
294 sal_uInt16 aEB
= SVBT16ToUInt16( rLe
.aEndBits
);
295 if( !(aEB
& 0x3) ) return;
297 ::basegfx::B2DPolygon aPolygon
;
298 aPolygon
.append(::basegfx::B2DPoint(0.0, 330.0));
299 aPolygon
.append(::basegfx::B2DPoint(100.0, 0.0));
300 aPolygon
.append(::basegfx::B2DPoint(200.0, 330.0));
301 aPolygon
.setClosed(true);
302 rSet
.Put( XLineStartItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon
) ) );
303 sal_uInt16 nSiz
= SVBT16ToUInt16( rLt
.lnpw
)
304 * ( ( aEB
>> 2 & 0x3 ) + ( aEB
>> 4 & 0x3 ) );
305 if( nSiz
< 220 ) nSiz
= 220;
306 rSet
.Put(XLineStartWidthItem(nSiz
));
307 rSet
.Put(XLineStartCenterItem(false));
310 // start of routines for the different objects
311 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadLine(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
315 if( !ReadGrafStart( static_cast<void*>(&aLine
), sizeof( aLine
), pHd
, rSet
) )
323 rP0
.setX( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
)) + m_nDrawXOfs2
);
324 rP0
.setY( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
)) + m_nDrawYOfs2
);
326 rP0
.AdjustX(static_cast<sal_Int16
>(SVBT16ToUInt16( aLine
.xaStart
)) );
327 rP0
.AdjustY(static_cast<sal_Int16
>(SVBT16ToUInt16( aLine
.yaStart
)) );
328 rP1
.AdjustX(static_cast<sal_Int16
>(SVBT16ToUInt16( aLine
.xaEnd
)) );
329 rP1
.AdjustY(static_cast<sal_Int16
>(SVBT16ToUInt16( aLine
.yaEnd
)) );
332 ::basegfx::B2DPolygon aPolygon
;
333 aPolygon
.append(::basegfx::B2DPoint(aP
[0].X(), aP
[0].Y()));
334 aPolygon
.append(::basegfx::B2DPoint(aP
[1].X(), aP
[1].Y()));
335 rtl::Reference
<SdrObject
> pObj
= new SdrPathObj(
338 ::basegfx::B2DPolyPolygon(aPolygon
));
340 SetStdAttr( rSet
, aLine
.aLnt
, aLine
.aShd
);
341 SetLineEndAttr( rSet
, aLine
.aEpp
, aLine
.aLnt
);
346 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadRect(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
350 if( !ReadGrafStart( static_cast<void*>(&aRect
), sizeof( aRect
), pHd
, rSet
) )
353 Point
aP0( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
)) + m_nDrawXOfs2
,
354 static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
)) + m_nDrawYOfs2
);
356 aP1
.AdjustX(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dxa
)) );
357 aP1
.AdjustY(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dya
)) );
359 rtl::Reference
<SdrObject
> pObj
= new SdrRectObj(
361 tools::Rectangle(aP0
, aP1
));
363 SetStdAttr( rSet
, aRect
.aLnt
, aRect
.aShd
);
364 SetFill( rSet
, aRect
.aFill
);
369 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadEllipse(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
371 WW8_DP_ELLIPSE aEllipse
;
373 if( !ReadGrafStart( static_cast<void*>(&aEllipse
), sizeof( aEllipse
), pHd
, rSet
) )
376 Point
aP0( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
)) + m_nDrawXOfs2
,
377 static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
)) + m_nDrawYOfs2
);
379 aP1
.AdjustX(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dxa
)) );
380 aP1
.AdjustY(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dya
)) );
382 rtl::Reference
<SdrObject
> pObj
= new SdrCircObj(
385 tools::Rectangle(aP0
, aP1
));
387 SetStdAttr( rSet
, aEllipse
.aLnt
, aEllipse
.aShd
);
388 SetFill( rSet
, aEllipse
.aFill
);
393 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadArc(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
397 if( !ReadGrafStart( static_cast<void*>(&aArc
), sizeof( aArc
), pHd
, rSet
) )
400 Point
aP0( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
)) + m_nDrawXOfs2
,
401 static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
)) + m_nDrawYOfs2
);
403 aP1
.AdjustX(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dxa
)) * 2 );
404 aP1
.AdjustY(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dya
)) * 2 );
406 short nA
[] = { 2, 3, 1, 0 };
407 short nW
= nA
[ ( ( aArc
.fLeft
& 1 ) << 1 ) + ( aArc
.fUp
& 1 ) ];
409 aP0
.AdjustY( -static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dya
)) );
410 aP1
.AdjustY( -static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dya
)) );
413 aP0
.AdjustX( -static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dxa
)) );
414 aP1
.AdjustX( -static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dxa
)) );
417 rtl::Reference
<SdrObject
> pObj
= new SdrCircObj(
419 SdrCircKind::Section
,
420 tools::Rectangle(aP0
, aP1
),
421 Degree100(nW
* 9000),
422 Degree100(( ( nW
+ 1 ) & 3 ) * 9000));
424 SetStdAttr( rSet
, aArc
.aLnt
, aArc
.aShd
);
425 SetFill( rSet
, aArc
.aFill
);
430 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadPolyLine(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
432 WW8_DP_POLYLINE aPoly
;
434 if( !ReadGrafStart( static_cast<void*>(&aPoly
), sizeof( aPoly
), pHd
, rSet
) )
437 sal_uInt16 nCount
= SVBT16ToUInt16( aPoly
.aBits1
) >> 1 & 0x7fff;
438 std::unique_ptr
<SVBT16
[]> xP(new SVBT16
[nCount
* 2]);
440 bool bCouldRead
= checkRead(*m_pStrm
, xP
.get(), nCount
* 4); // read points
441 OSL_ENSURE(bCouldRead
, "Short PolyLine header");
445 tools::Polygon
aP( nCount
);
447 for (sal_uInt16 i
=0; i
<nCount
; ++i
)
449 aPt
.setX( SVBT16ToUInt16( xP
[i
<< 1] ) + m_nDrawXOfs2
450 + static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
)) );
451 aPt
.setY( SVBT16ToUInt16( xP
[( i
<< 1 ) + 1] ) + m_nDrawYOfs2
452 + static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
)) );
457 rtl::Reference
<SdrObject
> pObj
= new SdrPathObj(
459 (SVBT16ToUInt16(aPoly
.aBits1
) & 0x1) ? SdrObjKind::Polygon
: SdrObjKind::PolyLine
,
460 ::basegfx::B2DPolyPolygon(aP
.getB2DPolygon()));
462 SetStdAttr( rSet
, aPoly
.aLnt
, aPoly
.aShd
);
463 SetFill( rSet
, aPoly
.aFill
);
468 static ESelection
GetESelection(EditEngine
const &rDrawEditEngine
, tools::Long nCpStart
, tools::Long nCpEnd
)
470 sal_Int32 nPCnt
= rDrawEditEngine
.GetParagraphCount();
474 && (nCpStart
>= rDrawEditEngine
.GetTextLen( nSP
) + 1) )
476 nCpStart
-= rDrawEditEngine
.GetTextLen( nSP
) + 1;
479 // at the end, switch to the new line only 1 character later as
480 // otherwise line attributes reach one line too far
482 && (nCpEnd
> rDrawEditEngine
.GetTextLen( nEP
) + 1) )
484 nCpEnd
-= rDrawEditEngine
.GetTextLen( nEP
) + 1;
487 return ESelection( nSP
, nCpStart
, nEP
, nCpEnd
);
490 // InsertTxbxStyAttrs() sets style attributes into the passed ItemSet.
491 // SW styles are used since import-WW-styles are already destroyed.
492 // SW styles are examined in depth first search order (with parent styles)
493 // for the attributes given in aSrcTab. They're cloned, and the clones'
494 // Which-IDs are changed according to the aDstTab table so that the
495 // EditEngine will not ignore them.
496 // Both Paragraph and character attributes are stuffed into the ItemSet.
497 void SwWW8ImplReader::InsertTxbxStyAttrs( SfxItemSet
& rS
, sal_uInt16 nColl
)
499 SwWW8StyInf
* pStyInf
= GetStyle(nColl
);
500 if( !(pStyInf
!= nullptr && pStyInf
->m_pFormat
&& pStyInf
->m_bColl
) )
503 const SfxPoolItem
* pItem
;
504 for( sal_uInt16 i
= POOLATTR_BEGIN
; i
< POOLATTR_END
; i
++ )
506 // If we are set in the source and not set in the destination
508 if ( SfxItemState::SET
== pStyInf
->m_pFormat
->GetItemState(
511 SfxItemPool
*pEditPool
= rS
.GetPool();
512 sal_uInt16 nWhich
= i
;
513 sal_uInt16 nSlotId
= m_rDoc
.GetAttrPool().GetSlotId(nWhich
);
515 nSlotId
&& nWhich
!= nSlotId
&&
516 0 != (nWhich
= pEditPool
->GetWhich(nSlotId
)) &&
518 ( SfxItemState::SET
!= rS
.GetItemState(nWhich
, false) )
521 rS
.Put( pItem
->CloneSetWhich(nWhich
) );
528 static void lcl_StripFields(OUString
&rString
, WW8_CP
&rNewStartCp
)
530 sal_Int32 nStartPos
= 0;
533 nStartPos
= rString
.indexOf(0x13, nStartPos
);
537 const sal_Unicode cStops
[] = {0x14, 0x15, 0};
538 const sal_Int32 nStopPos
= comphelper::string::indexOfAny(rString
, cStops
, nStartPos
);
541 rNewStartCp
+= rString
.getLength()-nStartPos
;
542 rString
= rString
.copy(0, nStartPos
);
546 const bool was0x14
= rString
[nStopPos
]==0x14;
547 rString
= rString
.replaceAt(nStartPos
, nStopPos
+1-nStartPos
, u
"");
548 rNewStartCp
+= nStopPos
-nStartPos
;
553 nStartPos
= rString
.indexOf(0x15, nStartPos
);
556 rString
= rString
.replaceAt(nStartPos
, 1, u
"");
567 tools::Long mnStartPos
; // 0x13
568 tools::Long mnEndPos
; // 0x15
570 explicit Chunk(tools::Long nStart
, OUString aURL
)
571 : msURL(std::move(aURL
)), mnStartPos(nStart
), mnEndPos(0) {}
573 void SetEndPos(tools::Long nEnd
) { mnEndPos
= nEnd
; }
574 tools::Long
GetStartPos() const {return mnStartPos
;}
575 tools::Long
GetEndPos() const {return mnEndPos
;}
576 const OUString
&GetURL() const {return msURL
;}
577 void Adjust(sal_Int32 nAdjust
)
584 bool IsValidSel(const EditEngine
& rEngine
, const ESelection
& rSel
)
586 const auto nParaCount
= rEngine
.GetParagraphCount();
587 if (rSel
.nStartPara
< nParaCount
&& rSel
.nEndPara
< nParaCount
)
588 return rSel
.nStartPos
>= 0 && rSel
.nEndPos
>= 0;
593 // InsertAttrsAsDrawingAttrs() sets attributes between StartCp and EndCp.
594 // Style attributes are set as hard, paragraph and character attributes.
595 void SwWW8ImplReader::InsertAttrsAsDrawingAttrs(WW8_CP nStartCp
, WW8_CP nEndCp
,
596 ManTypes eType
, bool bONLYnPicLocFc
)
599 Save and create new plcxman for this drawing object, of the type that
600 will include the para end mark inside a paragraph property range, as
601 drawing boxes have real paragraph marks as part of their text, while
602 normal writer has separate nodes for each paragraph and so has no actual
603 paragraph mark as part of the paragraph text.
605 WW8ReaderSave
aSave(this);
606 m_xPlcxMan
= std::make_shared
<WW8PLCFMan
>(m_xSBase
.get(), eType
, nStartCp
, true);
608 WW8_CP nStart
= m_xPlcxMan
->Where();
609 WW8_CP nNext
, nStartReplace
=0;
611 bool bDoingSymbol
= false;
612 sal_Unicode cReplaceSymbol
= m_cSymbol
;
614 std::optional
<SfxItemSet
> pS(m_pDrawEditEngine
->GetEmptyItemSet());
615 WW8PLCFManResult aRes
;
617 std::deque
<Chunk
> aChunks
;
619 // Here store stack location
620 size_t nCurrentCount
= m_xCtrlStck
->size();
621 while (nStart
< nEndCp
)
623 // nStart is the beginning of the attributes for this range, and
624 // may be before the text itself. So watch out for that
625 WW8_CP nTextStart
= nStart
;
626 if (nTextStart
< nStartCp
)
627 nTextStart
= nStartCp
;
629 // get position of next SPRM
630 bool bStartAttr
= m_xPlcxMan
->Get(&aRes
);
631 m_nCurrentColl
= m_xPlcxMan
->GetColl();
636 if ( (68 == aRes
.nSprmId
) || (0x6A03 == aRes
.nSprmId
) )
638 Read_PicLoc(aRes
.nSprmId
, aRes
.pMemPos
+
639 m_oSprmParser
->DistanceToData(aRes
.nSprmId
), 4);
640 // Ok, that's what we were looking for. Now let's get
645 else if ((eFTN
> aRes
.nSprmId
) || (0x0800 <= aRes
.nSprmId
))
647 // Here place them onto our usual stack and we will pop them
648 // off and convert them later
651 ImportSprm(aRes
.pMemPos
, aRes
.nMemLen
, aRes
.nSprmId
);
652 if (!bDoingSymbol
&& m_bSymbol
)
655 nStartReplace
= nTextStart
;
656 cReplaceSymbol
= m_cSymbol
;
661 EndSprm( aRes
.nSprmId
);
662 if (!m_bSymbol
&& bDoingSymbol
)
664 bDoingSymbol
= false;
666 ESelection
aReplaceSel(GetESelection(*m_pDrawEditEngine
, nStartReplace
- nStartCp
,
667 nTextStart
- nStartCp
));
669 sal_Int32 nParaCount
= m_pDrawEditEngine
->GetParagraphCount();
670 bool bBadSelection
= aReplaceSel
.nStartPara
>= nParaCount
|| aReplaceSel
.nEndPara
>= nParaCount
;
672 SAL_WARN_IF(bBadSelection
, "sw.ww8", "editengine has different amount of text than expected");
676 sal_Int32 nCount
= nTextStart
- nStartReplace
;
677 OUStringBuffer
sTemp(nCount
);
678 comphelper::string::padToLength(sTemp
, nCount
, cReplaceSymbol
);
679 m_pDrawEditEngine
->QuickInsertText(sTemp
.makeStringAndClear(), aReplaceSel
);
684 else if (aRes
.nSprmId
== eFLD
)
688 size_t nCount
= m_xCtrlStck
->size();
689 if (m_aFieldStack
.empty() && Read_Field(&aRes
))
692 for (size_t nI
= m_xCtrlStck
->size(); nI
> nCount
; --nI
)
694 const SfxPoolItem
*pItem
= ((*m_xCtrlStck
)[nI
-1]).m_pAttr
.get();
695 sal_uInt16 nWhich
= pItem
->Which();
696 if (nWhich
== RES_TXTATR_INETFMT
)
698 const SwFormatINetFormat
*pURL
=
699 static_cast<const SwFormatINetFormat
*>(pItem
);
700 sURL
= pURL
->GetValue();
702 m_xCtrlStck
->DeleteAndDestroy(nI
-1);
704 aChunks
.emplace_back(nStart
, sURL
);
709 if (!m_aFieldStack
.empty() && End_Field() && !aChunks
.empty())
710 aChunks
.back().SetEndPos(nStart
+1);
715 m_xPlcxMan
->advance();
716 nNext
= m_xPlcxMan
->Where();
718 const WW8_CP nEnd
= ( nNext
< nEndCp
) ? nNext
: nEndCp
;
719 if (!bONLYnPicLocFc
&& nNext
!= nStart
&& nEnd
>= nStartCp
)
721 SfxItemPool
*pEditPool
= pS
->GetPool();
723 // Here read current properties and convert them into pS
724 // and put those attrs into the draw box if they can be converted
725 // to draw attributes
726 if (m_xCtrlStck
->size() - nCurrentCount
)
728 for (size_t i
= nCurrentCount
; i
< m_xCtrlStck
->size(); ++i
)
730 const SfxPoolItem
*pItem
= ((*m_xCtrlStck
)[i
]).m_pAttr
.get();
731 sal_uInt16 nWhich
= pItem
->Which();
732 if( nWhich
< RES_FLTRATTR_BEGIN
||
733 nWhich
>= RES_FLTRATTR_END
)
735 sal_uInt16 nSlotId
= m_rDoc
.GetAttrPool().GetSlotId(nWhich
);
736 if (nWhich
== RES_CHRATR_BACKGROUND
|| nWhich
== RES_CHRATR_HIGHLIGHT
)
738 Color
aColor(static_cast<const SvxBrushItem
*>(pItem
)->GetColor());
739 pS
->Put(SvxColorItem(aColor
, EE_CHAR_BKGCOLOR
));
742 nSlotId
&& nWhich
!= nSlotId
&&
743 0 != (nWhich
= pEditPool
->GetWhich(nSlotId
)) &&
747 pS
->Put( pItem
->CloneSetWhich(nWhich
) );
752 // Fill in the remainder from the style
753 InsertTxbxStyAttrs(*pS
, m_nCurrentColl
);
757 m_pDrawEditEngine
->QuickSetAttribs( *pS
,
758 GetESelection(*m_pDrawEditEngine
, nTextStart
- nStartCp
, nEnd
- nStartCp
) );
759 pS
.emplace(m_pDrawEditEngine
->GetEmptyItemSet());
766 // pop off as far as recorded location just in case there were some left
768 for (size_t nI
= m_xCtrlStck
->size(); nI
> nCurrentCount
; --nI
)
769 m_xCtrlStck
->DeleteAndDestroy(nI
-1);
771 auto aEnd
= aChunks
.end();
772 for (auto aIter
= aChunks
.begin(); aIter
!= aEnd
; ++aIter
)
774 ESelection
aSel(GetESelection(*m_pDrawEditEngine
, aIter
->GetStartPos()-nStartCp
,
775 aIter
->GetEndPos()-nStartCp
));
776 if (!IsValidSel(*m_pDrawEditEngine
, aSel
))
778 OUString
aString(m_pDrawEditEngine
->GetText(aSel
));
779 const sal_Int32 nOrigLen
= aString
.getLength();
781 lcl_StripFields(aString
, nDummy
);
784 if (!aIter
->GetURL().isEmpty())
786 SvxURLField
aURL(aIter
->GetURL(), aString
, SvxURLFormat::AppDefault
);
787 m_pDrawEditEngine
->QuickInsertField(SvxFieldItem(aURL
, EE_FEATURE_FIELD
), aSel
);
788 nChanged
= nOrigLen
- 1;
792 m_pDrawEditEngine
->QuickInsertText(aString
, aSel
);
793 nChanged
= nOrigLen
- aString
.getLength();
795 for (auto aIter2
= aIter
+1; aIter2
!= aEnd
; ++aIter2
)
796 aIter2
->Adjust(nChanged
);
800 Don't worry about the new pPlcxMan, the restore removes it when
801 replacing the current one with the old one.
806 bool SwWW8ImplReader::GetTxbxTextSttEndCp(WW8_CP
& rStartCp
, WW8_CP
& rEndCp
,
807 sal_uInt16 nTxBxS
, sal_uInt16 nSequence
)
809 // grab the TextBox-PLCF quickly
810 WW8PLCFspecial
* pT
= m_xPlcxMan
? m_xPlcxMan
->GetTxbx() : nullptr;
813 OSL_ENSURE( false, "+where's the text graphic (1)?" );
817 // if applicable first find the right TextBox-Story
818 bool bCheckTextBoxStory
= ( nTxBxS
&& pT
->GetIMax() >= nTxBxS
);
819 if( bCheckTextBoxStory
)
820 pT
->SetIdx( nTxBxS
-1 );
822 // then determine start and end
824 if (!pT
->Get(rStartCp
, pT0
) || rStartCp
< 0)
826 OSL_ENSURE( false, "+where's the text graphic (2)?" );
830 if( bCheckTextBoxStory
)
832 bool bReusable
= (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS
*>(pT0
)->fReusable
));
836 if( !pT
->Get( rStartCp
, pT0
) )
838 OSL_ENSURE( false, "+where's the text graphic (2a)?" );
841 bReusable
= (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS
*>(pT0
)->fReusable
));
845 if (!pT
->Get(rEndCp
, pT0
) || rEndCp
< 0)
847 OSL_ENSURE( false, "+where's the text graphic (3)?" );
851 // find the right page in the break table (if necessary)
852 if( bCheckTextBoxStory
)
854 // special case: entire chain should be determined - done!
855 if( USHRT_MAX
> nSequence
)
857 tools::Long nMinStartCp
= rStartCp
;
858 tools::Long nMaxEndCp
= rEndCp
;
859 // quickly grab the TextBox-Break-Descriptor-PLCF
860 pT
= m_xPlcxMan
->GetTxbxBkd();
861 if (!pT
) // It can occur on occasion, Caolan
864 // find first entry for this TextBox story
865 if( !pT
->SeekPos( rStartCp
) )
867 OSL_ENSURE( false, "+where's the text graphic (4)" );
870 // if needed skip the appropriate number of entries
871 for (sal_uInt16 iSequence
= 0; iSequence
< nSequence
; ++iSequence
)
873 // and determine actual start and end
874 if( (!pT
->Get( rStartCp
, pT0
))
875 || ( nMinStartCp
> rStartCp
) )
877 OSL_ENSURE( false, "+where's the text graphic (5)?" );
880 if( rStartCp
>= nMaxEndCp
)
881 rEndCp
= rStartCp
; // not an error: empty string
885 if ( (!pT
->Get(rEndCp
, pT0
)) || (nMaxEndCp
< rEndCp
-1) )
887 OSL_ENSURE( false, "+where's the text graphic (6)?" );
901 // TxbxText() grabs the text from the WW file and returns that along with
902 // the StartCp and the corrected (by -2, or -1 for version 8) EndCp.
903 sal_Int32
SwWW8ImplReader::GetRangeAsDrawingString(OUString
& rString
, tools::Long nStartCp
, tools::Long nEndCp
, ManTypes eType
)
906 m_xWwFib
->GetBaseCp(eType
, &nOffset
); //TODO: check return value
908 OSL_ENSURE(nStartCp
<= nEndCp
, "+where's the graphic text (7)?");
909 if (nStartCp
== nEndCp
)
910 rString
.clear(); // empty string: entirely possible
911 else if (nStartCp
< nEndCp
)
913 // read the text: can be split into multiple pieces
914 const sal_Int32 nLen
= m_xSBase
->WW8ReadString(*m_pStrm
, rString
,
915 nStartCp
+ nOffset
, nEndCp
- nStartCp
, GetCurrentCharSet());
916 OSL_ENSURE(nLen
, "+where's the text graphic (8)?");
919 if( rString
[nLen
-1]==0x0d )
920 rString
= rString
.copy(0, nLen
-1);
922 rString
= rString
.replace( 0xb, 0xa );
929 //EditEngine::InsertText will replace dos lines resulting in a shorter
930 //string than is passed in, so inserting attributes based on the original
931 //string len can fail. So here replace the dos line ends similar to
932 //how EditEngine does it, but preserve the length and replace the extra
933 //chars with placeholders, record the position of the placeholders and
934 //remove those extra chars after attributes have been inserted
935 static std::vector
<sal_Int32
> replaceDosLineEndsButPreserveLength(OUString
&rIn
)
937 OUStringBuffer
aNewData(rIn
);
938 std::vector
<sal_Int32
> aDosLineEndDummies
;
940 sal_Int32 nStrLen
= rIn
.getLength();
943 // \r or \n causes linebreak
944 if (rIn
[i
] == '\r' || rIn
[i
] == '\n')
946 // skip char if \r\n or \n\r
947 if ( (i
+1) < nStrLen
&& ((rIn
[i
+1] == '\r') || (rIn
[i
+1] == '\n')) &&
948 (rIn
[i
] != rIn
[i
+1]) )
951 aDosLineEndDummies
.push_back(i
);
957 rIn
= aNewData
.makeStringAndClear();
958 return aDosLineEndDummies
;
961 static void removePositions(EditEngine
&rDrawEditEngine
, const std::vector
<sal_Int32
>& rDosLineEndDummies
)
963 for (auto aIter
= rDosLineEndDummies
.rbegin(); aIter
!= rDosLineEndDummies
.rend(); ++aIter
)
965 sal_Int32
nCharPos(*aIter
);
966 rDrawEditEngine
.QuickDelete(GetESelection(rDrawEditEngine
, nCharPos
, nCharPos
+1));
970 std::optional
<OutlinerParaObject
> SwWW8ImplReader::ImportAsOutliner(OUString
&rString
, WW8_CP nStartCp
, WW8_CP nEndCp
, ManTypes eType
)
972 std::optional
<OutlinerParaObject
> pRet
;
974 sal_Int32 nLen
= GetRangeAsDrawingString(rString
, nStartCp
, nEndCp
, eType
);
977 if (m_bFuzzing
&& rString
.getLength() > 1024)
979 SAL_WARN("sw.ww8", "Truncating long EditEngine strings when fuzzing for performance");
980 rString
= rString
.copy(0, 1024);
983 if (!m_pDrawEditEngine
)
985 m_pDrawEditEngine
.reset(new EditEngine(nullptr));
988 //replace dos line endings with editeng ones, replace any extra chars with
989 //placeholders to keep the inserted string len in sync with the attribute cps
990 //and record in aDosLineEnds the superfluous positions
991 OUString
sEEString(rString
);
992 std::vector
<sal_Int32
> aDosLineEnds(replaceDosLineEndsButPreserveLength(sEEString
));
993 m_pDrawEditEngine
->SetText(sEEString
);
994 InsertAttrsAsDrawingAttrs(nStartCp
, nStartCp
+nLen
, eType
);
995 //remove any superfluous placeholders of replaceDosLineEndsButPreserveLength
996 //after attributes have been inserted
997 removePositions(*m_pDrawEditEngine
, aDosLineEnds
);
999 // Annotations typically begin with a (useless) 0x5
1000 if ((eType
== MAN_AND
) && m_pDrawEditEngine
->GetTextLen())
1002 ESelection
aFirstChar(0, 0, 0, 1);
1003 if (m_pDrawEditEngine
->GetText( aFirstChar
) == "\x05")
1004 m_pDrawEditEngine
->QuickDelete(aFirstChar
);
1007 std::unique_ptr
<EditTextObject
> pTemporaryText
= m_pDrawEditEngine
->CreateTextObject();
1008 pRet
.emplace( std::move(pTemporaryText
) );
1009 pRet
->SetOutlinerMode( OutlinerMode::TextObject
);
1011 m_pDrawEditEngine
->SetText( OUString() );
1012 m_pDrawEditEngine
->SetParaAttribs(0, m_pDrawEditEngine
->GetEmptyItemSet());
1014 // Strip out fields, leaving the result
1016 lcl_StripFields(rString
, nDummy
);
1017 // Strip out word's special characters for the simple string
1018 rString
= rString
.replaceAll("\x01", "");
1019 rString
= rString
.replaceAll("\x05", "");
1020 rString
= rString
.replaceAll("\x08", "");
1021 rString
= rString
.replaceAll("\007\007", "\007\012");
1022 rString
= rString
.replace(0x7, ' ');
1028 // InsertTxbxText() adds the Text and the Attributes for TextBoxes and CaptionBoxes
1029 void SwWW8ImplReader::InsertTxbxText(SdrTextObj
* pTextObj
,
1030 Size
const * pObjSiz
, sal_uInt16 nTxBxS
, sal_uInt16 nSequence
, tools::Long nPosCp
,
1031 SwFrameFormat
const * pOldFlyFormat
, bool bMakeSdrGrafObj
, bool& rbEraseTextObj
,
1032 bool* pbTestTxbxContainsText
, tools::Long
* pnStartCp
, tools::Long
* pnEndCp
,
1033 bool* pbContainsGraphics
, SvxMSDffImportRec
const * pRecord
)
1035 SwFrameFormat
* pFlyFormat
= nullptr;
1036 sal_uInt64 nOld
= m_pStrm
->Tell();
1038 ManTypes eType
= m_xPlcxMan
->GetManType() == MAN_HDFT
? MAN_TXBX_HDFT
: MAN_TXBX
;
1040 rbEraseTextObj
= false;
1043 WW8_CP nStartCp
, nEndCp
;
1044 bool bContainsGraphics
= false;
1045 bool bTextWasRead
= GetTxbxTextSttEndCp(nStartCp
, nEndCp
, nTxBxS
, nSequence
) &&
1046 GetRangeAsDrawingString(aString
, nStartCp
, nEndCp
, eType
) > 0;
1048 if (!m_pDrawEditEngine
)
1050 m_pDrawEditEngine
.reset(new EditEngine(nullptr));
1053 m_pDrawEditEngine
->SetPaperSize( *pObjSiz
);
1055 if (m_bFuzzing
&& aString
.getLength() > 1024)
1057 SAL_WARN("sw.ww8", "Truncating long EditEngine strings when fuzzing for performance");
1058 aString
= aString
.copy(0, 1024);
1061 const OUString
aOrigString(aString
);
1064 WW8_CP nNewStartCp
= nStartCp
;
1065 lcl_StripFields(aString
, nNewStartCp
);
1067 if (aString
.getLength()!=1)
1069 bContainsGraphics
= aString
.indexOf(0x1)<0 || aString
.indexOf(0x8)<0;
1071 else // May be a single graphic or object
1074 switch( aString
[0] )
1077 if (!pbTestTxbxContainsText
)
1079 WW8ReaderSave
aSave(this, nNewStartCp
-1);
1080 bool bOldEmbeddObj
= m_bEmbeddObj
;
1081 // bEmbeddObj Ordinarily would have been set by field
1082 // parse, but this is impossible here so...
1083 m_bEmbeddObj
= true;
1085 // 1st look for OLE- or Graph-Indicator Sprms
1086 WW8PLCFx_Cp_FKP
* pChp
= m_xPlcxMan
->GetChpPLCF();
1088 pChp
->GetSprms( &aDesc
);
1089 WW8SprmIter
aSprmIter(aDesc
.pMemPos
, aDesc
.nSprmsLen
, *m_oSprmParser
);
1091 for( int nLoop
= 0; nLoop
< 2; ++nLoop
)
1093 while (aSprmIter
.GetSprms())
1095 const sal_uInt8
*const pParams(aSprmIter
.GetCurrentParams());
1096 if (nullptr == pParams
)
1098 sal_uInt16 nCurrentId
= aSprmIter
.GetCurrentId();
1099 switch( nCurrentId
)
1105 Read_Obj(nCurrentId
, pParams
, 1);
1107 case 68: // Read_Pic()
1109 case NS_sprm::LN_CObjLocation
:
1110 Read_PicLoc(nCurrentId
, pParams
, 1);
1113 aSprmIter
.advance();
1118 pChp
->GetPCDSprms( aDesc
);
1119 aSprmIter
.SetSprms( aDesc
.pMemPos
,
1123 aSave
.Restore(this);
1124 m_bEmbeddObj
=bOldEmbeddObj
;
1126 // then import either an OLE of a Graphic
1129 if( bMakeSdrGrafObj
&& pTextObj
&&
1130 pTextObj
->getParentSdrObjectFromSdrObject() )
1132 // use SdrOleObj/SdrGrafObj instead of
1133 // SdrTextObj in this Group
1136 rtl::Reference
<SdrObject
> pNew
= ImportOleBase(aGraph
);
1140 pNew
= new SdrGrafObj(*m_pDrawModel
);
1141 static_cast<SdrGrafObj
*>(pNew
.get())->SetGraphic(aGraph
);
1146 pNew
->SetLogicRect( pTextObj
->GetCurrentBoundRect() );
1147 pNew
->SetLayer( pTextObj
->GetLayer() );
1149 pTextObj
->getParentSdrObjectFromSdrObject()->GetSubList()->
1150 ReplaceObject(pNew
.get(), pTextObj
->GetOrdNum());
1153 pFlyFormat
= ImportOle();
1158 InsertAttrsAsDrawingAttrs(nNewStartCp
, nNewStartCp
+1,
1160 pFlyFormat
= ImportGraf(bMakeSdrGrafObj
? pTextObj
: nullptr,
1166 if ( (!pbTestTxbxContainsText
) && (!m_bObj
) )
1167 pFlyFormat
= Read_GrafLayer( nPosCp
);
1176 if( pFlyFormat
&& pRecord
)
1178 SfxItemSetFixed
<RES_FRMATR_BEGIN
, RES_FRMATR_END
-1, XATTR_START
, XATTR_END
>
1179 aFlySet( m_rDoc
.GetAttrPool() );
1181 tools::Rectangle
aInnerDist( pRecord
->nDxTextLeft
,
1182 pRecord
->nDyTextTop
,
1183 pRecord
->nDxTextRight
,
1184 pRecord
->nDyTextBottom
);
1185 MatchSdrItemsIntoFlySet( pTextObj
,
1187 pRecord
->eLineStyle
,
1188 pRecord
->eLineDashing
,
1189 pRecord
->eShapeType
,
1192 pFlyFormat
->SetFormatAttr( aFlySet
);
1194 MapWrapIntoFlyFormat(*pRecord
, *pFlyFormat
);
1197 rbEraseTextObj
= (nullptr != pFlyFormat
);
1203 *pnStartCp
= nStartCp
;
1207 if( pbTestTxbxContainsText
)
1208 *pbTestTxbxContainsText
= bTextWasRead
&& ! rbEraseTextObj
;
1209 else if( !rbEraseTextObj
)
1213 m_pDrawEditEngine
->SetText(aOrigString
);
1214 InsertAttrsAsDrawingAttrs(nStartCp
, nEndCp
, eType
);
1217 bool bVertical
= pTextObj
->IsVerticalWriting();
1218 OutlinerParaObject
aOp(m_pDrawEditEngine
->CreateTextObject());
1219 aOp
.SetOutlinerMode( OutlinerMode::TextObject
);
1220 aOp
.SetVertical( bVertical
);
1221 pTextObj
->NbcSetOutlinerParaObject( std::move(aOp
) );
1222 pTextObj
->SetVerticalWriting(bVertical
);
1224 // For the next TextBox also remove the old paragraph attributes
1225 // and styles, otherwise the next box will start with the wrong
1227 // Course of action: delete text = reduce to one paragraph
1228 // and on this one delete the paragraph attributes
1230 m_pDrawEditEngine
->SetText( OUString() );
1231 m_pDrawEditEngine
->SetParaAttribs(0, m_pDrawEditEngine
->GetEmptyItemSet());
1234 m_pStrm
->Seek( nOld
);
1235 if (pbContainsGraphics
)
1236 *pbContainsGraphics
= bContainsGraphics
;
1239 bool SwWW8ImplReader::TxbxChainContainsRealText(sal_uInt16 nTxBxS
, tools::Long
& rStartCp
,
1240 tools::Long
& rEndCp
)
1242 bool bErase
, bContainsText
;
1243 InsertTxbxText( nullptr,nullptr,nTxBxS
,USHRT_MAX
,0,nullptr,false, bErase
, &bContainsText
,
1244 &rStartCp
, &rEndCp
);
1245 return bContainsText
;
1248 // TextBoxes only for Ver67 !!
1249 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadTextBox(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
1252 WW8_DP_TXTBOX aTextB
;
1254 if( !ReadGrafStart( static_cast<void*>(&aTextB
), sizeof( aTextB
), pHd
, rSet
) )
1257 Point
aP0( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
)) + m_nDrawXOfs2
,
1258 static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
)) + m_nDrawYOfs2
);
1260 aP1
.AdjustX(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dxa
)) );
1261 aP1
.AdjustY(static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dya
)) );
1263 rtl::Reference
<SdrRectObj
> pObj
= new SdrRectObj(
1266 tools::Rectangle(aP0
, aP1
));
1268 pObj
->NbcSetSnapRect(tools::Rectangle(aP0
, aP1
));
1269 Size
aSize( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dxa
)) ,
1270 static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->dya
)) );
1272 tools::Long nStartCpFly
,nEndCpFly
;
1273 bool bContainsGraphics
;
1274 InsertTxbxText(pObj
.get(), &aSize
, 0, 0, 0, nullptr, false,
1275 bDummy
,nullptr,&nStartCpFly
,&nEndCpFly
,&bContainsGraphics
);
1277 SetStdAttr( rSet
, aTextB
.aLnt
, aTextB
.aShd
);
1278 SetFill( rSet
, aTextB
.aFill
);
1280 rSet
.Put( SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE
) );
1281 rSet
.Put( makeSdrTextAutoGrowWidthItem(false));
1282 rSet
.Put( makeSdrTextAutoGrowHeightItem(false));
1283 rSet
.Put( makeSdrTextLeftDistItem( MIN_BORDER_DIST
*2 ) );
1284 rSet
.Put( makeSdrTextRightDistItem( MIN_BORDER_DIST
*2 ) );
1285 rSet
.Put( makeSdrTextUpperDistItem( MIN_BORDER_DIST
) );
1286 rSet
.Put( makeSdrTextLowerDistItem( MIN_BORDER_DIST
) );
1291 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadCaptionBox(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
1293 static const SdrCaptionType aCaptA
[] = { SdrCaptionType::Type1
, SdrCaptionType::Type2
,
1294 SdrCaptionType::Type3
, SdrCaptionType::Type4
};
1296 WW8_DP_CALLOUT_TXTBOX aCallB
;
1298 if( !ReadGrafStart( static_cast<void*>(&aCallB
), sizeof( aCallB
), pHd
, rSet
) )
1301 sal_uInt16 nCount
= SVBT16ToUInt16( aCallB
.dpPolyLine
.aBits1
) >> 1 & 0x7fff;
1304 SAL_WARN("sw.ww8", "Short CaptionBox header");
1308 std::unique_ptr
<SVBT16
[]> xP(new SVBT16
[nCount
* 2]);
1310 bool bCouldRead
= checkRead(*m_pStrm
, xP
.get(), nCount
* 4); // read points
1313 SAL_WARN("sw.ww8", "Short CaptionBox header");
1317 sal_uInt8 nTyp
= static_cast<sal_uInt8
>(nCount
) - 1;
1318 if( nTyp
== 1 && SVBT16ToUInt16( xP
[0] ) == SVBT16ToUInt16( xP
[2] ) )
1321 Point
aP0( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
)) +
1322 static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadTxbx
.xa
)) + m_nDrawXOfs2
,
1323 static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
))
1324 + static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadTxbx
.ya
)) + m_nDrawYOfs2
);
1326 aP1
.AdjustX(static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadTxbx
.dxa
)) );
1327 aP1
.AdjustY(static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadTxbx
.dya
)) );
1328 Point
aP2( static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
))
1329 + static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadPolyLine
.xa
))
1330 + m_nDrawXOfs2
+ static_cast<sal_Int16
>(SVBT16ToUInt16( xP
[0] )),
1331 static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
))
1332 + static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadPolyLine
.ya
))
1333 + m_nDrawYOfs2
+ static_cast<sal_Int16
>(SVBT16ToUInt16( xP
[1] )) );
1336 rtl::Reference
<SdrCaptionObj
> pObj
= new SdrCaptionObj(
1338 tools::Rectangle(aP0
, aP1
),
1341 pObj
->NbcSetSnapRect(tools::Rectangle(aP0
, aP1
));
1342 Size
aSize( static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadTxbx
.dxa
)),
1343 static_cast<sal_Int16
>(SVBT16ToUInt16( aCallB
.dpheadTxbx
.dya
)) );
1344 bool bEraseThisObject
;
1346 InsertTxbxText(pObj
.get(), &aSize
, 0, 0, 0, nullptr, false, bEraseThisObject
);
1348 if( SVBT16ToUInt16( aCallB
.dptxbx
.aLnt
.lnps
) != 5 ) // Is border visible ?
1349 SetStdAttr( rSet
, aCallB
.dptxbx
.aLnt
, aCallB
.dptxbx
.aShd
);
1350 else // no -> take lines
1351 SetStdAttr( rSet
, aCallB
.dpPolyLine
.aLnt
, aCallB
.dptxbx
.aShd
);
1352 SetFill( rSet
, aCallB
.dptxbx
.aFill
);
1353 rSet
.Put(SdrCaptionTypeItem(aCaptA
[nTyp
% SAL_N_ELEMENTS(aCaptA
)]));
1358 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadGroup(WW8_DPHEAD
const * pHd
, SfxAllItemSet
&rSet
)
1362 if( !ReadGrafStart( static_cast<void*>(&nGrouped
), sizeof( nGrouped
), pHd
, rSet
) )
1365 #ifdef OSL_BIGENDIAN
1366 nGrouped
= (sal_Int16
)OSL_SWAPWORD( nGrouped
);
1369 m_nDrawXOfs
= m_nDrawXOfs
+ static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
));
1370 m_nDrawYOfs
= m_nDrawYOfs
+ static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
));
1372 rtl::Reference
<SdrObject
> pObj
= new SdrObjGroup(*m_pDrawModel
);
1374 short nLeft
= static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->cb
)) - sizeof( WW8_DPHEAD
);
1375 for (int i
= 0; i
< nGrouped
&& nLeft
>= static_cast<short>(sizeof(WW8_DPHEAD
)); ++i
)
1377 SfxAllItemSet
aSet(m_pDrawModel
->GetItemPool());
1378 if (rtl::Reference
<SdrObject
> pObject
= ReadGrafPrimitive(nLeft
, aSet
))
1380 // first add and then set ItemSet
1381 SdrObjList
*pSubGroup
= pObj
->GetSubList();
1382 OSL_ENSURE(pSubGroup
, "Why no sublist available?");
1384 pSubGroup
->InsertObject(pObject
.get(), 0);
1385 pObject
->SetMergedItemSetAndBroadcast(aSet
);
1389 m_nDrawXOfs
= m_nDrawXOfs
- static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->xa
));
1390 m_nDrawYOfs
= m_nDrawYOfs
- static_cast<sal_Int16
>(SVBT16ToUInt16( pHd
->ya
));
1395 rtl::Reference
<SdrObject
> SwWW8ImplReader::ReadGrafPrimitive(short& rLeft
, SfxAllItemSet
&rSet
)
1397 // This whole archaic word 6 graphic import can probably be refactored
1398 // into an object hierarchy with a little effort.
1399 rtl::Reference
<SdrObject
> pRet
;
1400 WW8_DPHEAD aHd
; // Read Draw-Primitive-Header
1401 bool bCouldRead
= checkRead(*m_pStrm
, &aHd
, sizeof(WW8_DPHEAD
)) &&
1402 SVBT16ToUInt16(aHd
.cb
) >= sizeof(WW8_DPHEAD
);
1403 OSL_ENSURE(bCouldRead
, "Graphic Primitive header short read" );
1410 if( rLeft
>= SVBT16ToUInt16(aHd
.cb
) ) // precautions
1412 rSet
.Put(SwFormatSurround(css::text::WrapTextMode_THROUGH
));
1413 switch (SVBT16ToUInt16(aHd
.dpk
) & 0xff )
1416 pRet
= ReadGroup(&aHd
, rSet
);
1419 pRet
= ReadLine(&aHd
, rSet
);
1422 pRet
= ReadTextBox(&aHd
, rSet
);
1425 pRet
= ReadRect(&aHd
, rSet
);
1428 pRet
= ReadEllipse(&aHd
, rSet
);
1431 pRet
= ReadArc(&aHd
, rSet
);
1434 pRet
= ReadPolyLine(&aHd
, rSet
);
1437 pRet
= ReadCaptionBox(&aHd
, rSet
);
1440 m_pStrm
->SeekRel(SVBT16ToUInt16(aHd
.cb
) - sizeof(WW8_DPHEAD
));
1446 OSL_ENSURE( false, "+Grafik-Overlap" );
1448 rLeft
= rLeft
- SVBT16ToUInt16( aHd
.cb
);
1452 void SwWW8ImplReader::ReadGrafLayer1(WW8PLCFspecial
& rPF
, tools::Long nGrafAnchorCp
)
1454 rPF
.SeekPos(nGrafAnchorCp
);
1457 if (!rPF
.Get(nStartFc
, pF0
))
1459 OSL_ENSURE( false, "+Where is the graphic (2) ?" );
1462 WW8_FDOA
* pF
= static_cast<WW8_FDOA
*>(pF0
);
1463 if( !SVBT32ToUInt32( pF
->fc
) )
1465 OSL_ENSURE( false, "+Where is the graphic (3) ?" );
1469 sal_uInt32 nPosFc
= SVBT32ToUInt32(pF
->fc
);
1471 //skip duplicate graphics when fuzzing
1474 if (!m_aGrafPosSet
.insert(nPosFc
).second
)
1478 bool bCouldSeek
= checkSeek(*m_pStrm
, nPosFc
);
1479 OSL_ENSURE(bCouldSeek
, "Invalid graphic offset");
1485 bool bCouldRead
= checkRead(*m_pStrm
, &aDo
, sizeof(WW8_DO
));
1486 OSL_ENSURE(bCouldRead
, "Short graphic header");
1490 short nLeft
= SVBT16ToUInt16( aDo
.cb
) - sizeof( WW8_DO
);
1491 while (nLeft
> static_cast<short>(sizeof(WW8_DPHEAD
)))
1493 SfxAllItemSet
aSet( m_pDrawModel
->GetItemPool() );
1494 if (rtl::Reference
<SdrObject
> pObject
= ReadGrafPrimitive(nLeft
, aSet
))
1496 m_xWWZOrder
->InsertDrawingObject(pObject
.get(), SVBT16ToUInt16(aDo
.dhgt
));
1498 tools::Rectangle
aRect(pObject
->GetSnapRect());
1500 const sal_uInt32 nCntRelTo
= 3;
1502 // Adjustment is horizontally relative to...
1503 static const sal_Int16 aHoriRelOriTab
[nCntRelTo
] =
1505 text::RelOrientation::PAGE_PRINT_AREA
, // 0 is page textarea margin
1506 text::RelOrientation::PAGE_FRAME
, // 1 is page margin
1507 text::RelOrientation::FRAME
, // 2 is relative to paragraph
1510 // Adjustment is vertically relative to...
1511 static const sal_Int16 aVertRelOriTab
[nCntRelTo
] =
1513 text::RelOrientation::PAGE_PRINT_AREA
, // 0 is page textarea margin
1514 text::RelOrientation::PAGE_FRAME
, // 1 is page margin
1515 text::RelOrientation::FRAME
, // 2 is relative to paragraph
1518 const int nXAlign
= aDo
.bx
< nCntRelTo
? aDo
.bx
: 0;
1519 const int nYAlign
= aDo
.by
< nCntRelTo
? aDo
.by
: 0;
1521 aSet
.Put(SwFormatHoriOrient(aRect
.Left(), text::HoriOrientation::NONE
,
1522 aHoriRelOriTab
[ nXAlign
]));
1523 aSet
.Put(SwFormatVertOrient(aRect
.Top(), text::VertOrientation::NONE
,
1524 aVertRelOriTab
[ nYAlign
]));
1526 SwFrameFormat
*pFrame
= m_rDoc
.getIDocumentContentOperations().InsertDrawObj( *m_pPaM
, *pObject
, aSet
);
1527 pObject
->SetMergedItemSet(aSet
);
1529 if (SwDrawFrameFormat
*pDrawFrame
= dynamic_cast<SwDrawFrameFormat
*>(pFrame
))
1531 pDrawFrame
->PosAttrSet();
1534 AddAutoAnchor(pFrame
);
1539 sal_Int32
SwMSDffManager::GetEscherLineMatch(MSO_LineStyle eStyle
,
1540 MSO_SPT eShapeType
, sal_Int32
&rThick
)
1542 sal_Int32 nOutsideThick
= 0;
1544 Note: In contrast to the regular WinWord table and frame border width,
1545 where the overall border width has to be calculated from the width of *one*
1546 line, the data from ESCHER already contains the overall width [twips]!
1548 The WinWord default is 15 tw. We take for this our 20 tw line.
1549 (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
1550 0.05 pt hairline.) The hairline we only set by WinWord width up to max.
1555 case mso_lineTriple
:
1556 case mso_lineSimple
:
1557 nOutsideThick
= eShapeType
!= mso_sptTextBox
? rThick
: rThick
/2;
1559 case mso_lineDouble
:
1560 if (eShapeType
== mso_sptTextBox
)
1562 nOutsideThick
= rThick
/6;
1563 rThick
= rThick
*2/3;
1566 nOutsideThick
= rThick
*2/3;
1568 case mso_lineThickThin
:
1569 if (eShapeType
== mso_sptTextBox
)
1571 nOutsideThick
= rThick
*3/10;
1572 rThick
= rThick
*4/5;
1575 nOutsideThick
= rThick
*4/5;
1577 case mso_lineThinThick
:
1579 if (eShapeType
== mso_sptTextBox
)
1581 nOutsideThick
= rThick
/10;
1582 rThick
= rThick
*3/5;
1585 nOutsideThick
= rThick
*3/5;
1591 return nOutsideThick
;
1594 // Returns the thickness of the line outside the frame, the logic of
1595 // words positioning of borders around floating objects is that of a
1597 sal_Int32
SwWW8ImplReader::MatchSdrBoxIntoFlyBoxItem(const Color
& rLineColor
,
1598 MSO_LineStyle eLineStyle
, MSO_LineDashing eDashing
, MSO_SPT eShapeType
, sal_Int32
&rLineThick
,
1601 sal_Int32 nOutsideThick
= 0;
1603 return nOutsideThick
;
1605 SvxBorderLineStyle nIdx
= SvxBorderLineStyle::NONE
;
1607 sal_Int32 nLineThick
=rLineThick
;
1608 nOutsideThick
= SwMSDffManager::GetEscherLineMatch(eLineStyle
,
1609 eShapeType
, rLineThick
);
1612 Note: In contrast to the regular WinWord table and frame border width,
1613 where the overall border width has to be calculated from the width of *one*
1614 line, the data from ESCHER already contains the overall width [twips]!
1616 The WinWord default is 15 tw. We take for this our 20 tw line.
1617 (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
1618 0.05 pt hairline.) The hairline we only set by WinWord width up to max.
1621 switch( +eLineStyle
)
1623 // first the single lines
1624 case mso_lineSimple
:
1625 nIdx
= SvxBorderLineStyle::SOLID
;
1627 // second the double lines
1628 case mso_lineDouble
:
1629 nIdx
= SvxBorderLineStyle::DOUBLE
;
1631 case mso_lineThickThin
:
1632 nIdx
= SvxBorderLineStyle::THICKTHIN_SMALLGAP
;
1634 case mso_lineThinThick
:
1635 nIdx
= SvxBorderLineStyle::THINTHICK_SMALLGAP
;
1637 // We have no triple border, use double instead.
1638 case mso_lineTriple
:
1639 nIdx
= SvxBorderLineStyle::DOUBLE
;
1641 // no line style is set
1642 case MSO_LineStyle(USHRT_MAX
):
1644 // erroneously not implemented line style is set
1646 OSL_ENSURE(false, "eLineStyle is not (yet) implemented!");
1652 case mso_lineDashGEL
:
1653 nIdx
= SvxBorderLineStyle::DASHED
;
1655 case mso_lineDotGEL
:
1656 nIdx
= SvxBorderLineStyle::DOTTED
;
1662 if (SvxBorderLineStyle::NONE
!= nIdx
)
1664 SvxBorderLine aLine
;
1665 aLine
.SetColor( rLineColor
);
1667 aLine
.SetWidth( nLineThick
); // No conversion here, nLineThick is already in twips
1668 aLine
.SetBorderLineStyle(nIdx
);
1670 for(SvxBoxItemLine nLine
: o3tl::enumrange
<SvxBoxItemLine
>())
1672 // aLine is cloned by SetLine
1673 rBox
.SetLine(&aLine
, nLine
);
1677 return nOutsideThick
;
1680 #define WW8ITEMVALUE(ItemSet,Id,Cast) ItemSet.GetItem<Cast>(Id)->GetValue()
1682 void SwWW8ImplReader::MatchSdrItemsIntoFlySet( SdrObject
const * pSdrObj
,
1683 SfxItemSet
& rFlySet
, MSO_LineStyle eLineStyle
, MSO_LineDashing eDashing
, MSO_SPT eShapeType
,
1684 tools::Rectangle
& rInnerDist
)
1687 attributes to be set on the frame
1688 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1689 SwFormatFrameSize if not set, set here
1690 SvxLRSpaceItem set here
1691 SvxULSpaceItem set here
1692 SvxOpaqueItem (Currently not possible for frames! khz 10.2.1999)
1693 SwFormatSurround already set
1694 SwFormatVertOrient already set
1695 SwFormatHoriOrient already set
1696 SwFormatAnchor already set
1698 SvxBrushItem set here
1699 SvxShadowItem set here
1702 // 1. GraphicObject of documents?
1705 const SfxItemSet
& rOldSet
= pSdrObj
->GetMergedItemSet();
1707 // some Items can be taken over directly
1708 static sal_uInt16
const aDirectMatch
[]
1710 RES_LR_SPACE
, // outer spacing left/right: SvxLRSpaceItem
1711 RES_UL_SPACE
// outer spacing top/bottom: SvxULSpaceItem
1713 const SfxPoolItem
* pPoolItem
;
1714 for(sal_uInt16 i
: aDirectMatch
)
1715 if( SfxItemState::SET
== rOldSet
.GetItemState(i
, false, &pPoolItem
) )
1717 rFlySet
.Put( *pPoolItem
);
1720 // take new XATTR items directly. Skip old RES_BACKGROUND if new FILLSTYLE taken.
1721 bool bSkipResBackground
= false;
1722 SfxItemPool
* pPool
= rFlySet
.GetPool();
1725 for ( sal_uInt16 i
= XATTR_START
; i
< XATTR_END
; ++i
)
1727 // Not all Fly types support XATTRs - skip unsupported attributes
1728 SfxItemPool
* pAttrPool
= pPool
->GetMasterPool();
1729 while ( pAttrPool
&& !pAttrPool
->IsInRange(i
) )
1730 pAttrPool
= pAttrPool
->GetSecondaryPool();
1734 if ( SfxItemState::SET
== rOldSet
.GetItemState(i
, false, &pPoolItem
) )
1736 rFlySet
.Put( *pPoolItem
);
1737 if ( i
== XATTR_FILLSTYLE
)
1739 const drawing::FillStyle eFill
= static_cast<const XFillStyleItem
*>(pPoolItem
)->GetValue();
1740 // Transparency forced in certain situations when fillstyle is none - use old logic for that case still
1741 // which is especially needed for export purposes (tdf112618).
1742 if ( eFill
!= drawing::FillStyle_NONE
)
1743 bSkipResBackground
= true;
1749 // now calculate the borders and build the box: The unit is needed for the
1751 SvxBoxItem
aBox(rFlySet
.Get(RES_BOX
));
1752 // dashed or solid becomes solid
1753 // WW-default: 0.75 pt = 15 twips
1754 sal_Int32 nLineThick
= 15, nOutside
=0;
1756 // check if LineStyle is *really* set!
1758 SfxItemState eState
= rOldSet
.GetItemState(XATTR_LINESTYLE
);
1759 if( eState
== SfxItemState::SET
)
1761 // Now, that we know there is a line style we will make use the
1762 // parameter given to us when calling the method... :-)
1763 const Color aLineColor
= rOldSet
.Get(XATTR_LINECOLOR
).GetColorValue();
1764 nLineThick
= WW8ITEMVALUE(rOldSet
, XATTR_LINEWIDTH
, XLineWidthItem
);
1767 nLineThick
= 1; // for Writer, zero is "no border", so set a minimal value
1769 nOutside
= MatchSdrBoxIntoFlyBoxItem(aLineColor
, eLineStyle
,
1770 eDashing
, eShapeType
, nLineThick
, aBox
);
1773 rInnerDist
.AdjustLeft(nLineThick
);
1774 rInnerDist
.AdjustTop(nLineThick
);
1775 rInnerDist
.AdjustRight(nLineThick
);
1776 rInnerDist
.AdjustBottom(nLineThick
);
1778 rInnerDist
.AdjustLeft( -(aBox
.CalcLineWidth( SvxBoxItemLine::LEFT
)) );
1779 rInnerDist
.AdjustTop( -(aBox
.CalcLineWidth( SvxBoxItemLine::TOP
)) );
1780 rInnerDist
.AdjustRight( -(aBox
.CalcLineWidth( SvxBoxItemLine::RIGHT
)) );
1781 rInnerDist
.AdjustBottom( -(aBox
.CalcLineWidth( SvxBoxItemLine::BOTTOM
)) );
1783 // set distances from box's border to text contained within the box
1784 if( 0 < rInnerDist
.Left() )
1785 aBox
.SetDistance( o3tl::narrowing
<sal_uInt16
>(rInnerDist
.Left()), SvxBoxItemLine::LEFT
);
1786 if( 0 < rInnerDist
.Top() )
1787 aBox
.SetDistance( o3tl::narrowing
<sal_uInt16
>(rInnerDist
.Top()), SvxBoxItemLine::TOP
);
1788 if( 0 < rInnerDist
.Right() )
1789 aBox
.SetDistance( o3tl::narrowing
<sal_uInt16
>(rInnerDist
.Right()), SvxBoxItemLine::RIGHT
);
1790 if( 0 < rInnerDist
.Bottom() )
1791 aBox
.SetDistance( o3tl::narrowing
<sal_uInt16
>(rInnerDist
.Bottom()), SvxBoxItemLine::BOTTOM
);
1793 bool bFixSize
= !(WW8ITEMVALUE(rOldSet
, SDRATTR_TEXT_AUTOGROWHEIGHT
,
1796 // Size: SwFormatFrameSize
1797 if( SfxItemState::SET
!= rFlySet
.GetItemState(RES_FRM_SIZE
, false) )
1799 const tools::Rectangle
& rSnapRect
= pSdrObj
->GetSnapRect();
1800 // if necessary adapt width and position of the framework: The
1801 // recorded interior is to remain equally large despite thick edges.
1802 rFlySet
.Put( SwFormatFrameSize(bFixSize
? SwFrameSize::Fixed
: SwFrameSize::Variable
,
1803 rSnapRect
.GetWidth() + 2*nOutside
,
1804 rSnapRect
.GetHeight() + 2*nOutside
) );
1806 else // If a size is set, adjust it to consider border thickness
1808 SwFormatFrameSize aSize
= rFlySet
.Get(RES_FRM_SIZE
);
1810 SwFormatFrameSize
aNewSize(bFixSize
? SwFrameSize::Fixed
: SwFrameSize::Variable
,
1811 aSize
.GetWidth() + 2*nOutside
,
1812 aSize
.GetHeight() + 2*nOutside
);
1813 aNewSize
.SetWidthSizeType(aSize
.GetWidthSizeType());
1814 rFlySet
.Put( aNewSize
);
1817 // Sadly word puts escher borders outside the graphic, but orients the
1818 // graphic in relation to the top left inside the border. We don't
1821 SwFormatHoriOrient aHori
= rFlySet
.Get(RES_HORI_ORIENT
);
1822 aHori
.SetPos(MakeSafePositioningValue(aHori
.GetPos()-nOutside
));
1825 SwFormatVertOrient aVert
= rFlySet
.Get(RES_VERT_ORIENT
);
1826 aVert
.SetPos(aVert
.GetPos()-nOutside
);
1830 // now set the border
1831 rFlySet
.Put( aBox
);
1833 // shadow of the box: SvxShadowItem
1834 if( WW8ITEMVALUE(rOldSet
, SDRATTR_SHADOW
, SdrOnOffItem
) )
1836 SvxShadowItem
aShadow( RES_SHADOW
);
1838 const Color aShdColor
= rOldSet
.Get(SDRATTR_SHADOWCOLOR
).GetColorValue();
1839 const sal_Int32 nShdDistX
= WW8ITEMVALUE(rOldSet
, SDRATTR_SHADOWXDIST
,
1841 const sal_Int32 nShdDistY
= WW8ITEMVALUE(rOldSet
, SDRATTR_SHADOWYDIST
,
1844 aShadow
.SetColor( aShdColor
);
1846 aShadow
.SetWidth(writer_cast
<sal_uInt16
>((std::abs( nShdDistX
) +
1847 std::abs( nShdDistY
)) / 2 ));
1849 SvxShadowLocation eShdPosi
;
1850 if( 0 <= nShdDistX
)
1852 if( 0 <= nShdDistY
)
1853 eShdPosi
= SvxShadowLocation::BottomRight
;
1855 eShdPosi
= SvxShadowLocation::TopRight
;
1859 if( 0 <= nShdDistY
)
1860 eShdPosi
= SvxShadowLocation::BottomLeft
;
1862 eShdPosi
= SvxShadowLocation::TopLeft
;
1864 aShadow
.SetLocation( eShdPosi
);
1866 rFlySet
.Put( aShadow
);
1868 SvxBrushItem
aBrushItem(COL_WHITE
, RES_BACKGROUND
);
1869 bool bBrushItemOk
= false;
1870 sal_uInt8 nTrans
= 0;
1872 // Separate transparency
1873 eState
= rOldSet
.GetItemState(XATTR_FILLTRANSPARENCE
);
1874 if (!bSkipResBackground
&& eState
== SfxItemState::SET
)
1876 sal_uInt16 nRes
= WW8ITEMVALUE(rOldSet
, XATTR_FILLTRANSPARENCE
,
1877 XFillTransparenceItem
);
1878 nTrans
= sal_uInt8((nRes
* 0xFE) / 100);
1879 aBrushItem
.GetColor().SetAlpha(255 - nTrans
);
1880 bBrushItemOk
= true;
1883 // Background: SvxBrushItem
1884 const XFillStyleItem
* pFillStyleItem
= rOldSet
.GetItemIfSet(XATTR_FILLSTYLE
);
1885 if (!bSkipResBackground
&& pFillStyleItem
)
1887 const drawing::FillStyle eFill
= pFillStyleItem
->GetValue();
1892 case drawing::FillStyle_NONE
:
1893 // Writer graphics don't have it yet
1894 if (eShapeType
!= mso_sptPictureFrame
)
1896 aBrushItem
.GetColor().SetAlpha(1);
1897 bBrushItemOk
= true;
1900 case drawing::FillStyle_SOLID
:
1901 case drawing::FillStyle_GRADIENT
:
1903 const Color aColor
=
1904 rOldSet
.Get(XATTR_FILLCOLOR
).GetColorValue();
1905 aBrushItem
.SetColor(aColor
);
1907 if (bBrushItemOk
) // has trans
1908 aBrushItem
.GetColor().SetAlpha(255 - nTrans
);
1910 bBrushItemOk
= true;
1913 case drawing::FillStyle_HATCH
:
1915 case drawing::FillStyle_BITMAP
:
1917 GraphicObject
aGrfObj(rOldSet
.Get(XATTR_FILLBITMAP
).GetGraphicObject());
1918 const bool bTile(WW8ITEMVALUE(rOldSet
, XATTR_FILLBMP_TILE
, XFillBmpTileItem
));
1920 if(bBrushItemOk
) // has trans
1922 GraphicAttr
aAttr(aGrfObj
.GetAttr());
1924 aAttr
.SetAlpha(255 - nTrans
);
1925 aGrfObj
.SetAttr(aAttr
);
1928 aBrushItem
.SetGraphicObject(aGrfObj
);
1929 aBrushItem
.SetGraphicPos(bTile
? GPOS_TILED
: GPOS_AREA
);
1930 bBrushItemOk
= true;
1937 rFlySet
.Put(aBrushItem
);
1940 void SwWW8ImplReader::AdjustLRWrapForWordMargins(
1941 const SvxMSDffImportRec
&rRecord
, SvxLRSpaceItem
&rLR
)
1943 sal_uInt32 nXRelTo
= SvxMSDffImportRec::RELTO_DEFAULT
;
1944 if ( rRecord
.nXRelTo
)
1946 nXRelTo
= *rRecord
.nXRelTo
;
1949 // Left adjustments - if horizontally aligned to left of
1950 // margin or column then remove the left wrapping
1951 if (rRecord
.nXAlign
== 1)
1953 if ((nXRelTo
== 0) || (nXRelTo
== 2))
1954 rLR
.SetLeft(sal_uInt16(0));
1957 // Right adjustments - if horizontally aligned to right of
1958 // margin or column then remove the right wrapping
1959 if (rRecord
.nXAlign
== 3)
1961 if ((nXRelTo
== 0) || (nXRelTo
== 2))
1962 rLR
.SetRight(sal_uInt16(0));
1965 // Inside margin, remove left wrapping
1966 if ((rRecord
.nXAlign
== 4) && (nXRelTo
== 0))
1968 rLR
.SetLeft(sal_uInt16(0));
1971 // Outside margin, remove left wrapping
1972 if ((rRecord
.nXAlign
== 5) && (nXRelTo
== 0))
1974 rLR
.SetRight(sal_uInt16(0));
1978 void SwWW8ImplReader::AdjustULWrapForWordMargins(
1979 const SvxMSDffImportRec
&rRecord
, SvxULSpaceItem
&rUL
)
1981 sal_uInt32 nYRelTo
= SvxMSDffImportRec::RELTO_DEFAULT
;
1982 if ( rRecord
.nYRelTo
)
1984 nYRelTo
= *rRecord
.nYRelTo
;
1987 // Top adjustment - remove upper wrapping if aligned to page
1988 // printable area or to page
1989 if (rRecord
.nYAlign
== 1)
1991 if ((nYRelTo
== 0) || (nYRelTo
== 1))
1992 rUL
.SetUpper(sal_uInt16(0));
1995 // Bottom adjustment - remove bottom wrapping if aligned to page or
1996 // printable area or to page
1997 if (rRecord
.nYAlign
== 3)
1999 if ((nYRelTo
== 0) || (nYRelTo
== 1))
2000 rUL
.SetLower(sal_uInt16(0));
2003 // Remove top margin if aligned vertically inside margin
2004 if ((rRecord
.nYAlign
== 4) && (nYRelTo
== 0))
2005 rUL
.SetUpper(sal_uInt16(0));
2008 void SwWW8ImplReader::MapWrapIntoFlyFormat(const SvxMSDffImportRec
& rRecord
,
2009 SwFrameFormat
& rFlyFormat
)
2011 if (rRecord
.nDxWrapDistLeft
|| rRecord
.nDxWrapDistRight
)
2013 SvxLRSpaceItem
aLR(writer_cast
<sal_uInt16
>(rRecord
.nDxWrapDistLeft
),
2014 writer_cast
<sal_uInt16
>(rRecord
.nDxWrapDistRight
), 0, RES_LR_SPACE
);
2015 AdjustLRWrapForWordMargins(rRecord
, aLR
);
2016 rFlyFormat
.SetFormatAttr(aLR
);
2018 if (rRecord
.nDyWrapDistTop
|| rRecord
.nDyWrapDistBottom
)
2020 SvxULSpaceItem
aUL(writer_cast
<sal_uInt16
>(rRecord
.nDyWrapDistTop
),
2021 writer_cast
<sal_uInt16
>(rRecord
.nDyWrapDistBottom
), RES_UL_SPACE
);
2022 AdjustULWrapForWordMargins(rRecord
, aUL
);
2023 rFlyFormat
.SetFormatAttr(aUL
);
2026 // If we are contoured and have a custom polygon...
2027 if (rRecord
.pWrapPolygon
&& rFlyFormat
.GetSurround().IsContour())
2029 if (SwNoTextNode
* pNd
= GetNoTextNodeFromSwFrameFormat(rFlyFormat
))
2032 Gather round children and hear of a tale that will raise the
2033 hairs on the back of your neck this dark halloween night.
2035 There is a polygon in word that describes the wrapping around
2038 Here are some sample values for the simplest case of a square
2039 around some solid coloured graphics
2041 X Y Pixel size of graphic
2042 TopLeft -54 21600 400x400
2043 Bottom Right 0 21546
2045 TopLeft -108 21600 200x200
2046 Bottom Right 0 21492
2048 TopLeft -216 21600 100x100
2049 Bottom Right 0 21384
2051 TopLeft -432 21600 50x50
2052 Bottom Right 0 21168
2054 TopLeft -76 21600 283x212
2055 Bottom Right 0 21498
2057 So given that the size of the values remains pretty much the
2058 same despite the size of the graphic, we can tell that the
2059 polygon is measured in units that are independent of the
2060 graphic. But why does the left corner move a different value
2061 to the left each time, and why does the bottom move upwards
2062 each time, when the right and top remain at the same value ?
2064 I have no idea, but clearly once we calculate the values out
2065 we see that the left margin is always a fixed realworld
2066 distance from the true left and the polygon bottom is the same
2067 fixed value from the bottom. i.e. 15twips.
2069 So here we take our word provided polygon, shift it to the
2070 right by 15twips and rescale it widthwise to shrink the width
2071 a little to fit the now moved right margin back to where it
2072 was, and stretch the height a little to make the bottom move
2073 down the missing 15twips then we get a polygon that matches
2074 what I actually see in word
2077 tools::PolyPolygon
aPoly(*rRecord
.pWrapPolygon
);
2078 const Size
&rSize
= pNd
->GetTwipSize();
2080 Move to the left by 15twips, and rescale to
2081 a) shrink right bound back to orig position
2082 b) stretch bottom bound to where I think it should have been
2085 Fraction
aMoveHack(ww::nWrap100Percent
, rSize
.Width());
2086 aMoveHack
*= Fraction(15, 1);
2087 tools::Long
nMove(aMoveHack
);
2088 aPoly
.Move(nMove
, 0);
2090 Fraction
aHackX(ww::nWrap100Percent
, ww::nWrap100Percent
+ nMove
);
2091 Fraction
aHackY(ww::nWrap100Percent
, ww::nWrap100Percent
- nMove
);
2092 aPoly
.Scale(double(aHackX
), double(aHackY
));
2094 // Turn polygon back into units that match the graphic's
2095 const Size
&rOrigSize
= pNd
->GetGraphic().GetPrefSize();
2096 Fraction
aMapPolyX(rOrigSize
.Width(), ww::nWrap100Percent
);
2097 Fraction
aMapPolyY(rOrigSize
.Height(), ww::nWrap100Percent
);
2098 aPoly
.Scale(double(aMapPolyX
), double(aMapPolyY
));
2100 // #i47277# - contour is already in unit of the
2101 // graphic preferred unit. Thus, call method <SetContour(..)>
2102 pNd
->SetContour(&aPoly
);
2105 else if (rFlyFormat
.GetSurround().IsContour())
2107 const SdrObject
* pSdrObj
= rFlyFormat
.FindSdrObject();
2108 SdrObjKind eKind
= pSdrObj
? pSdrObj
->GetObjIdentifier() : SdrObjKind::Graphic
;
2111 case SdrObjKind::Text
:
2113 case SdrObjKind::SwFlyDrawObjIdentifier
:
2116 // Contour is enabled, but no polygon is set: disable contour, because Word does not
2117 // Writer-style auto-contour in that case.
2118 SwFormatSurround
aSurround(rFlyFormat
.GetSurround());
2119 aSurround
.SetContour(false);
2120 rFlyFormat
.SetFormatAttr(aSurround
);
2126 static sal_Int32
lcl_ConvertCrop(sal_uInt32
const nCrop
, sal_Int32
const nSize
)
2128 // cast to sal_Int32 to handle negative crop properly
2129 sal_Int32
const nIntegral(static_cast<sal_Int32
>(nCrop
) >> 16);
2130 // fdo#77454: heuristic to detect mangled values written by old OOo/LO
2131 if (abs(nIntegral
) >= 50) // FIXME: what's a good cut-off?
2133 SAL_INFO("sw.ww8", "ignoring suspiciously large crop: " << nIntegral
);
2136 return (nIntegral
* nSize
) + (((nCrop
& 0xffff) * nSize
) >> 16);
2139 void SwWW8ImplReader::SetAttributesAtGrfNode(const SvxMSDffImportRec
& rRecord
,
2140 const SwFrameFormat
& rFlyFormat
, WW8_FSPA
const *pF
)
2142 const SwNodeIndex
* pIdx
= rFlyFormat
.GetContent(false).GetContentIdx();
2143 SwGrfNode
*const pGrfNd(
2144 pIdx
? m_rDoc
.GetNodes()[pIdx
->GetIndex() + 1]->GetGrfNode() : nullptr);
2148 Size
aSz(pGrfNd
->GetTwipSize());
2149 // use type <sal_uInt64> instead of sal_uLong to get correct results
2150 // in the following calculations.
2151 sal_uInt64 nHeight
= aSz
.Height();
2152 sal_uInt64 nWidth
= aSz
.Width();
2154 nWidth
= o3tl::saturating_sub(pF
->nXaRight
, pF
->nXaLeft
);
2155 else if (!nHeight
&& pF
)
2156 nHeight
= o3tl::saturating_sub(pF
->nYaBottom
, pF
->nYaTop
);
2158 if (rRecord
.nCropFromTop
|| rRecord
.nCropFromBottom
||
2159 rRecord
.nCropFromLeft
|| rRecord
.nCropFromRight
)
2161 SwCropGrf aCrop
; // Cropping is stored in 'fixed floats'
2162 // 16.16 (fraction times total
2163 if (rRecord
.nCropFromTop
) // image width or height resp.)
2165 aCrop
.SetTop(lcl_ConvertCrop(rRecord
.nCropFromTop
, nHeight
));
2167 if (rRecord
.nCropFromBottom
)
2169 aCrop
.SetBottom(lcl_ConvertCrop(rRecord
.nCropFromBottom
, nHeight
));
2171 if (rRecord
.nCropFromLeft
)
2173 aCrop
.SetLeft(lcl_ConvertCrop(rRecord
.nCropFromLeft
, nWidth
));
2175 if (rRecord
.nCropFromRight
)
2177 aCrop
.SetRight(lcl_ConvertCrop(rRecord
.nCropFromRight
, nWidth
));
2180 pGrfNd
->SetAttr( aCrop
);
2183 bool bFlipH(rRecord
.nFlags
& ShapeFlag::FlipH
);
2184 bool bFlipV(rRecord
.nFlags
& ShapeFlag::FlipV
);
2185 if ( bFlipH
|| bFlipV
)
2187 SwMirrorGrf aMirror
= pGrfNd
->GetSwAttrSet().GetMirrorGrf();
2191 aMirror
.SetValue(MirrorGraph::Both
);
2193 aMirror
.SetValue(MirrorGraph::Vertical
);
2196 aMirror
.SetValue(MirrorGraph::Horizontal
);
2198 pGrfNd
->SetAttr( aMirror
);
2204 const SfxItemSet
& rOldSet
= rRecord
.pObj
->GetMergedItemSet();
2206 if (WW8ITEMVALUE(rOldSet
, SDRATTR_GRAFCONTRAST
,
2207 SdrGrafContrastItem
))
2209 SwContrastGrf
aContrast(
2210 WW8ITEMVALUE(rOldSet
,
2211 SDRATTR_GRAFCONTRAST
, SdrGrafContrastItem
));
2212 pGrfNd
->SetAttr( aContrast
);
2216 if (WW8ITEMVALUE(rOldSet
, SDRATTR_GRAFLUMINANCE
,
2217 SdrGrafLuminanceItem
))
2219 SwLuminanceGrf
aLuminance(WW8ITEMVALUE(rOldSet
,
2220 SDRATTR_GRAFLUMINANCE
, SdrGrafLuminanceItem
));
2221 pGrfNd
->SetAttr( aLuminance
);
2224 if (WW8ITEMVALUE(rOldSet
, SDRATTR_GRAFGAMMA
, SdrGrafGamma100Item
))
2226 double fVal
= WW8ITEMVALUE(rOldSet
, SDRATTR_GRAFGAMMA
,
2227 SdrGrafGamma100Item
);
2228 pGrfNd
->SetAttr(SwGammaGrf(fVal
/100.));
2232 auto nGrafMode
= rOldSet
.GetItem
<SdrGrafModeItem
>(SDRATTR_GRAFMODE
)->GetValue();
2233 if ( nGrafMode
!= GraphicDrawMode::Standard
)
2235 SwDrawModeGrf
aDrawMode( nGrafMode
);
2236 pGrfNd
->SetAttr( aDrawMode
);
2240 SdrObject
* SwWW8ImplReader::CreateContactObject(SwFrameFormat
* pFlyFormat
)
2244 SdrObject
* pNewObject
= m_bNewDoc
? nullptr : pFlyFormat
->FindRealSdrObject();
2246 pNewObject
= pFlyFormat
->FindSdrObject();
2248 if (auto pFlyFrameFormat
= dynamic_cast<SwFlyFrameFormat
*>( pFlyFormat
))
2250 SwFlyDrawContact
* pContactObject
= pFlyFrameFormat
->GetOrCreateContact();
2251 pNewObject
= pContactObject
->GetMaster();
2258 // Miserable miserable hack to fudge word's graphic layout in RTL mode to ours.
2259 bool SwWW8ImplReader::MiserableRTLGraphicsHack(SwTwips
&rLeft
, SwTwips nWidth
,
2260 sal_Int16 eHoriOri
, sal_Int16 eHoriRel
)
2262 if (!IsRightToLeft())
2264 return RTLGraphicsHack(rLeft
, nWidth
, eHoriOri
, eHoriRel
,
2265 m_aSectionManager
.GetPageLeft(),
2266 m_aSectionManager
.GetPageRight(),
2267 m_aSectionManager
.GetPageWidth());
2270 RndStdIds
SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec
& rRecord
, WW8_FSPA
& rFSPA
,
2271 SfxItemSet
&rFlySet
)
2273 bool bCurSectionVertical
= m_aSectionManager
.CurrentSectionIsVertical();
2275 if (!rRecord
.nXRelTo
)
2277 rRecord
.nXRelTo
= sal_Int32(rFSPA
.nbx
);
2279 if (!rRecord
.nYRelTo
)
2281 rRecord
.nYRelTo
= sal_Int32(rFSPA
.nby
);
2284 // nXAlign - abs. Position, Left, Centered, Right, Inside, Outside
2285 // nYAlign - abs. Position, Top, Centered, Bottom, Inside, Outside
2287 // nXRelTo - Page printable area, Page, Column, Character
2288 // nYRelTo - Page printable area, Page, Paragraph, Line
2290 const sal_uInt32 nCntXAlign
= 6;
2291 const sal_uInt32 nCntYAlign
= 6;
2293 const sal_uInt32 nCntRelTo
= 4;
2295 sal_uInt32 nXAlign
= nCntXAlign
> rRecord
.nXAlign
? rRecord
.nXAlign
: 1;
2296 sal_uInt32 nYAlign
= nCntYAlign
> rRecord
.nYAlign
? rRecord
.nYAlign
: 1;
2298 // #i52565# - try to handle special case for objects in tables regarding its X Rel
2300 // if X and Y Rel values are on default take it as a hint, that they have not been set
2301 // by <SwMSDffManager::ProcessObj(..)>
2302 const bool bXYRelHaveDefaultValues
= *rRecord
.nXRelTo
== 2 && *rRecord
.nYRelTo
== 2;
2303 if (bXYRelHaveDefaultValues
&& m_nInTable
> 0 && !bCurSectionVertical
)
2305 if (sal_uInt32(rFSPA
.nby
) != rRecord
.nYRelTo
)
2306 rRecord
.nYRelTo
= sal_uInt32(rFSPA
.nby
);
2309 sal_uInt32 nXRelTo
= (rRecord
.nXRelTo
&& nCntRelTo
> rRecord
.nXRelTo
) ? *rRecord
.nXRelTo
: 1;
2310 sal_uInt32 nYRelTo
= (rRecord
.nYRelTo
&& nCntRelTo
> rRecord
.nYRelTo
) ? *rRecord
.nYRelTo
: 1;
2312 RndStdIds eAnchor
= IsInlineEscherHack() ? RndStdIds::FLY_AS_CHAR
: RndStdIds::FLY_AT_CHAR
; // #i43718#
2314 SwFormatAnchor
aAnchor( eAnchor
);
2315 aAnchor
.SetAnchor( m_pPaM
->GetPoint() );
2316 rFlySet
.Put( aAnchor
);
2319 // Given new layout where everything is changed to be anchored to
2320 // character the following 4 tables may need to be changed.
2322 // horizontal Adjustment
2323 static const sal_Int16 aHoriOriTab
[ nCntXAlign
] =
2325 text::HoriOrientation::NONE
, // From left position
2326 text::HoriOrientation::LEFT
, // left
2327 text::HoriOrientation::CENTER
, // centered
2328 text::HoriOrientation::RIGHT
, // right
2330 // - inside -> text::HoriOrientation::LEFT and outside -> text::HoriOrientation::RIGHT
2331 text::HoriOrientation::LEFT
, // inside
2332 text::HoriOrientation::RIGHT
// outside
2335 // generic vertical Adjustment
2336 static const sal_Int16 aVertOriTab
[ nCntYAlign
] =
2338 text::VertOrientation::NONE
, // From Top position
2339 text::VertOrientation::TOP
, // top
2340 text::VertOrientation::CENTER
, // centered
2341 text::VertOrientation::BOTTOM
, // bottom
2342 text::VertOrientation::LINE_TOP
, // inside (obscure)
2343 text::VertOrientation::LINE_BOTTOM
// outside (obscure)
2346 // #i22673# - to-line vertical alignment
2347 static const sal_Int16 aToLineVertOriTab
[ nCntYAlign
] =
2349 text::VertOrientation::NONE
, // below
2350 text::VertOrientation::LINE_BOTTOM
, // top
2351 text::VertOrientation::LINE_CENTER
, // centered
2352 text::VertOrientation::LINE_TOP
, // bottom
2353 text::VertOrientation::LINE_BOTTOM
, // inside (obscure)
2354 text::VertOrientation::LINE_TOP
// outside (obscure)
2357 // Adjustment is horizontally relative to...
2358 static const sal_Int16 aHoriRelOriTab
[nCntRelTo
] =
2360 text::RelOrientation::PAGE_PRINT_AREA
, // 0 is page textarea margin
2361 text::RelOrientation::PAGE_FRAME
, // 1 is page margin
2362 text::RelOrientation::FRAME
, // 2 is relative to column
2363 text::RelOrientation::CHAR
// 3 is relative to character
2366 // Adjustment is vertically relative to...
2367 // #i22673# - adjustment for new vertical alignment at top of line.
2368 static const sal_Int16 aVertRelOriTab
[nCntRelTo
] =
2370 text::RelOrientation::PAGE_PRINT_AREA
, // 0 is page textarea margin
2371 text::RelOrientation::PAGE_FRAME
, // 1 is page margin
2372 text::RelOrientation::FRAME
, // 2 is relative to paragraph
2373 text::RelOrientation::TEXT_LINE
// 3 is relative to line
2376 // If the image is inline, then the relative orientation means nothing,
2377 // so set it up so that if the user changes it into an anchor, it positions usefully.
2379 = IsInlineEscherHack() ? text::HoriOrientation::CENTER
: aHoriOriTab
[ nXAlign
];
2381 = IsInlineEscherHack() ? text::RelOrientation::FRAME
: aHoriRelOriTab
[nXRelTo
];
2383 // #i36649# - adjustments for certain alignments
2384 if (eHoriOri
== text::HoriOrientation::LEFT
&& eHoriRel
== text::RelOrientation::PAGE_FRAME
)
2386 // convert 'left to page' to 'from left -<width> to page text area'
2387 eHoriOri
= text::HoriOrientation::NONE
;
2388 eHoriRel
= text::RelOrientation::PAGE_PRINT_AREA
;
2389 const tools::Long nWidth
= rFSPA
.nXaRight
- rFSPA
.nXaLeft
;
2390 rFSPA
.nXaLeft
= -nWidth
;
2393 else if (eHoriOri
== text::HoriOrientation::RIGHT
&& eHoriRel
== text::RelOrientation::PAGE_FRAME
)
2395 // convert 'right to page' to 'from left 0 to right page border'
2396 eHoriOri
= text::HoriOrientation::NONE
;
2397 eHoriRel
= text::RelOrientation::PAGE_RIGHT
;
2398 const tools::Long nWidth
= rFSPA
.nXaRight
- rFSPA
.nXaLeft
;
2400 rFSPA
.nXaRight
= nWidth
;
2403 // #i24255# - position of floating screen objects in
2404 // R2L layout are given in L2R layout, thus convert them of all
2405 // floating screen objects, which are imported.
2407 // Miserable miserable hack.
2408 SwTwips nWidth
= o3tl::saturating_sub(rFSPA
.nXaRight
, rFSPA
.nXaLeft
);
2409 SwTwips nLeft
= rFSPA
.nXaLeft
;
2410 if (MiserableRTLGraphicsHack(nLeft
, nWidth
, eHoriOri
,
2413 rFSPA
.nXaLeft
= nLeft
;
2414 rFSPA
.nXaRight
= rFSPA
.nXaLeft
+ nWidth
;
2418 // if the object is anchored inside a table cell, is horizontal aligned
2419 // at frame|character and has wrap through, but its attribute
2420 // 'layout in table cell' isn't set, convert its horizontal alignment to page text area.
2421 // #i84783# - use new method <IsObjectLayoutInTableCell()>
2423 (eHoriRel
== text::RelOrientation::FRAME
|| eHoriRel
== text::RelOrientation::CHAR
) &&
2425 !IsObjectLayoutInTableCell(rRecord
.nGroupShapeBooleanProperties
))
2427 eHoriRel
= text::RelOrientation::PAGE_PRINT_AREA
;
2430 // Writer honours this wrap distance when aligned as "left" or "right",
2431 // Word doesn't. Writer doesn't honour it when its "from left".
2432 if (eHoriOri
== text::HoriOrientation::LEFT
)
2433 rRecord
.nDxWrapDistLeft
= 0;
2434 else if (eHoriOri
== text::HoriOrientation::RIGHT
)
2435 rRecord
.nDxWrapDistRight
= 0;
2439 eVertRel
= aVertRelOriTab
[ nYRelTo
]; // #i18732#
2440 if (bCurSectionVertical
&& nYRelTo
== 2)
2441 eVertRel
= text::RelOrientation::PAGE_PRINT_AREA
;
2442 // #i22673# - fill <eVertOri> in dependence of <eVertRel>
2444 if (eVertRel
== text::RelOrientation::TEXT_LINE
)
2446 eVertOri
= aToLineVertOriTab
[ nYAlign
];
2450 eVertOri
= aVertOriTab
[ nYAlign
];
2453 // Below line in word is a positive value, while in writer its
2455 tools::Long nYPos
= rFSPA
.nYaTop
;
2457 if ((eVertRel
== text::RelOrientation::TEXT_LINE
) && (eVertOri
== text::VertOrientation::NONE
))
2460 SwFormatHoriOrient
aHoriOri(MakeSafePositioningValue(bCurSectionVertical
? nYPos
: rFSPA
.nXaLeft
),
2461 bCurSectionVertical
? eVertOri
: eHoriOri
,
2462 bCurSectionVertical
? eVertRel
: eHoriRel
);
2464 aHoriOri
.SetPosToggle(true);
2465 rFlySet
.Put(aHoriOri
);
2467 rFlySet
.Put(SwFormatVertOrient(MakeSafePositioningValue(!bCurSectionVertical
? nYPos
: -rFSPA
.nXaRight
),
2468 !bCurSectionVertical
? eVertOri
: eHoriOri
,
2469 !bCurSectionVertical
? eVertRel
: eHoriRel
));
2475 bool SwWW8ImplReader::IsObjectLayoutInTableCell(const sal_uInt32 nGroupShapeBooleanProperties
) const
2477 bool bIsObjectLayoutInTableCell
= false;
2481 sal_uInt16 nWWVersion
= m_xWwFib
->m_nProduct
& 0xE000;
2482 if (nWWVersion
== 0)
2484 // 0 nProduct can happen for Word >97 as well, check cswNew in this case instead.
2485 if (m_xWwFib
->m_cswNew
> 0)
2487 // This is Word >=2000.
2488 nWWVersion
= 0x2000;
2492 switch ( nWWVersion
)
2494 case 0x0000: // version 8 aka Microsoft Word 97
2496 bIsObjectLayoutInTableCell
= false;
2497 OSL_ENSURE(nGroupShapeBooleanProperties
== 0,
2498 "no explicit object attribute layout in table cell expected." );
2501 case 0x2000: // version 9 aka Microsoft Word 2000
2502 case 0x4000: // version 10 aka Microsoft Word 2002
2503 case 0x6000: // version 11 aka Microsoft Word 2003
2504 case 0x8000: // version 12 aka Microsoft Word 2007
2505 case 0xC000: // version 14 aka Microsoft Word 2010
2506 case 0xE000: // version 15 aka Microsoft Word 2013
2508 // Documented in [MS-ODRAW], 2.3.4.44 "Group Shape Boolean Properties".
2509 bool fUsefLayoutInCell
= (nGroupShapeBooleanProperties
& 0x80000000) >> 31;
2510 bool fLayoutInCell
= (nGroupShapeBooleanProperties
& 0x8000) >> 15;
2511 // If unspecified, defaults to true
2512 bIsObjectLayoutInTableCell
= !fUsefLayoutInCell
|| fLayoutInCell
;
2517 OSL_FAIL( "unknown version." );
2522 return bIsObjectLayoutInTableCell
;
2525 SwFrameFormat
* SwWW8ImplReader::Read_GrafLayer( tools::Long nGrafAnchorCp
)
2527 if( m_nIniFlags
& WW8FL_NO_GRAFLAYER
)
2530 ::SetProgressState(m_nProgress
, m_pDocShell
); // Update
2533 m_bDrawCpOValid
= m_xWwFib
->GetBaseCp(m_xPlcxMan
->GetManType() == MAN_HDFT
? MAN_TXBX_HDFT
: MAN_TXBX
, &m_nDrawCpO
);
2537 WW8PLCFspecial
* pPF
= m_xPlcxMan
->GetFdoa();
2540 OSL_ENSURE( false, "Where is the graphic (1) ?" );
2546 sal_uInt64 nOldPos
= m_pStrm
->Tell();
2548 m_nDrawXOfs
= m_nDrawYOfs
= 0;
2549 ReadGrafLayer1(*pPF
, nGrafAnchorCp
);
2551 m_pStrm
->Seek( nOldPos
);
2555 // Normal case of Word 8+ version stuff
2556 pPF
->SeekPos( nGrafAnchorCp
);
2560 if (!pPF
->Get(nStartFc
, pF0
))
2562 OSL_ENSURE( false, "+Where is the graphic (2) ?" );
2566 WW8_FSPA_SHADOW
& rFS
= *static_cast<WW8_FSPA_SHADOW
*>(pF0
);
2568 WW8FSPAShadowToReal(rFS
, aFSFA
);
2571 OSL_ENSURE( false, "+Where is the graphic (3) ?" );
2575 if (!m_xMSDffManager
->GetModel())
2576 m_xMSDffManager
->SetModel(m_pDrawModel
, 1440);
2578 tools::Rectangle
aRect(aFSFA
.nXaLeft
, aFSFA
.nYaTop
, aFSFA
.nXaRight
, aFSFA
.nYaBottom
);
2579 SvxMSDffImportData
aData( aRect
);
2581 rtl::Reference
<SdrObject
> pObject
;
2582 bool bOk
= (m_xMSDffManager
->GetShape(aFSFA
.nSpId
, pObject
, aData
) && pObject
);
2586 OSL_ENSURE( false, "Where is the Shape ?" );
2590 // tdf#118375 Word relates position to the unrotated rectangle,
2591 // Writer uses the rotated one.
2592 if (pObject
->GetRotateAngle())
2594 tools::Rectangle
aObjSnapRect(pObject
->GetSnapRect()); // recalculates the SnapRect
2595 aFSFA
.nXaLeft
= aObjSnapRect
.Left();
2596 aFSFA
.nYaTop
= aObjSnapRect
.Top();
2597 aFSFA
.nXaRight
= aObjSnapRect
.Right();
2598 aFSFA
.nYaBottom
= aObjSnapRect
.Bottom();
2602 rtl::Reference
<SdrObject
> pOurNewObject
;
2603 bool bReplaceable
= false;
2605 switch (pObject
->GetObjIdentifier())
2607 case SdrObjKind::Graphic
:
2608 bReplaceable
= true;
2611 case SdrObjKind::OLE2
:
2612 bReplaceable
= true;
2619 // when in a header or footer word appears to treat all elements as wrap through
2621 // determine wrapping mode
2622 SfxItemSetFixed
<RES_FRMATR_BEGIN
, RES_FRMATR_END
-1, XATTR_START
, XATTR_END
> aFlySet(m_rDoc
.GetAttrPool());
2623 Reader::ResetFrameFormatAttrs(aFlySet
); // tdf#122425: Explicitly remove borders and spacing
2624 css::text::WrapTextMode eSurround
= css::text::WrapTextMode_PARALLEL
;
2625 bool bContour
= false;
2628 case 0: // 0 like 2, but doesn't require absolute object
2629 case 2: // 2 wrap around absolute object
2630 eSurround
= css::text::WrapTextMode_PARALLEL
;
2632 case 1: // 1 no text next to shape
2633 eSurround
= css::text::WrapTextMode_NONE
;
2635 case 3: // 3 wrap as if no object present
2636 // Special case: on export, inline images are wrapped through as a hack for old formats.
2637 // That is irrelevant for Writer, so instead use the default wrap in that case,
2638 // so that when the user changes it into an anchor, it wraps nicely, and not through.
2639 if (!IsInlineEscherHack())
2640 eSurround
= css::text::WrapTextMode_THROUGH
;
2642 case 4: // 4 wrap tightly around object
2643 case 5: // 5 wrap tightly, but allow holes
2644 eSurround
= css::text::WrapTextMode_PARALLEL
;
2649 // if mode 2 or 4 also regard the additional parameters
2650 if ((2 == aFSFA
.nwr
) || (4 == aFSFA
.nwr
))
2654 // 0 wrap both sides
2656 eSurround
= css::text::WrapTextMode_PARALLEL
;
2658 // 1 wrap only on left
2660 eSurround
= css::text::WrapTextMode_LEFT
;
2662 // 2 wrap only on right
2664 eSurround
= css::text::WrapTextMode_RIGHT
;
2666 // 3 wrap only on largest side
2668 eSurround
= css::text::WrapTextMode_DYNAMIC
;
2673 SwFormatSurround
aSur( eSurround
);
2674 aSur
.SetContour( bContour
);
2675 aSur
.SetOutside(true); // Winword can only do outside contours
2676 aFlySet
.Put( aSur
);
2678 // now position imported object correctly and so on (can be a whole group)
2680 OSL_ENSURE(!((aData
.size() != 1) && bReplaceable
),
2681 "Replaceable drawing with > 1 entries ?");
2683 if (aData
.size() != 1)
2684 bReplaceable
= false;
2687 Get the record for top level object, so we can get the word anchoring
2688 and wrapping information for it.
2690 SvxMSDffImportRec
* pRecord
= aData
.find(pObject
.get());
2691 OSL_ENSURE(pRecord
, "how did that happen?");
2694 // remove old object from the Z-Order list
2695 m_xMSDffManager
->RemoveFromShapeOrder(pObject
.get());
2698 const bool bLayoutInTableCell
=
2699 m_nInTable
&& IsObjectLayoutInTableCell(pRecord
->nGroupShapeBooleanProperties
);
2701 // #i18732# - Switch on 'follow text flow', if object is laid out
2702 // inside table cell
2703 if (bLayoutInTableCell
)
2705 SwFormatFollowTextFlow
aFollowTextFlow( true );
2706 aFlySet
.Put( aFollowTextFlow
);
2710 // Some shapes are set to *hidden*, don't import those ones.
2711 if (pRecord
->bHidden
)
2713 // remove old object from the Z-Order list
2714 m_xMSDffManager
->RemoveFromShapeOrder(pObject
.get());
2718 sal_uInt16 nCount
= pObject
->GetUserDataCount();
2721 OUString lnName
, aObjName
, aTarFrame
;
2722 for (sal_uInt16 i
= 0; i
< nCount
; i
++ )
2724 SdrObjUserData
* pData
= pObject
->GetUserData( i
);
2725 if( pData
&& pData
->GetInventor() == SdrInventor::ScOrSwDraw
2726 && pData
->GetId() == SW_UD_IMAPDATA
)
2728 SwMacroInfo
* macInf
= dynamic_cast<SwMacroInfo
*>(pData
);
2729 if (macInf
&& macInf
->GetShapeId() == aFSFA
.nSpId
)
2731 lnName
= macInf
->GetHlink();
2732 aObjName
= macInf
->GetName();
2733 aTarFrame
= macInf
->GetTarFrame();
2738 std::unique_ptr
<SwFormatURL
> pFormatURL(new SwFormatURL());
2739 pFormatURL
->SetURL( lnName
, false );
2740 if (!aObjName
.isEmpty())
2741 pFormatURL
->SetName(aObjName
);
2742 if (!aTarFrame
.isEmpty())
2743 pFormatURL
->SetTargetFrameName(aTarFrame
);
2744 pFormatURL
->SetMap(nullptr);
2745 aFlySet
.Put(std::move(pFormatURL
));
2748 // If we are to be "below text" then we are not to be opaque
2749 // #i14045# MM If we are in a header or footer then make the object transparent
2750 // Not exactly like word but close enough for now
2752 // both flags <bBelowText> and <bDrawHell> have to be set to move object into the background.
2753 // #i46794# - it reveals that value of flag <bBelowText> can be neglected.
2754 const bool bMoveToBackground
= pRecord
->bDrawHell
||
2755 ((m_bIsHeader
|| m_bIsFooter
) && aFSFA
.nwr
== 3);
2756 if ( bMoveToBackground
)
2757 aFlySet
.Put(SvxOpaqueItem(RES_OPAQUE
,false));
2759 OUString aObjName
= pObject
->GetName();
2761 bool bDrawObj
= false;
2762 bool bFrame
= false;
2764 SwFrameFormat
* pRetFrameFormat
= nullptr;
2767 // Single graphics or ole objects
2768 pRetFrameFormat
= ImportReplaceableDrawables(pObject
, pOurNewObject
, *pRecord
, aFSFA
, aFlySet
);
2774 // Drawing objects, (e.g. ovals or drawing groups)
2775 if (aFSFA
.bRcaSimple
)
2777 aFSFA
.nbx
= WW8_FSPA::RelPageBorder
;
2778 aFSFA
.nby
= WW8_FSPA::RelPageBorder
;
2781 RndStdIds eAnchor
= ProcessEscherAlign(*pRecord
, aFSFA
, aFlySet
);
2783 // Should we, and is it possible to make this into a writer textbox
2784 if ((!(m_nIniFlags1
& WW8FL_NO_FLY_FOR_TXBX
)) && pRecord
->bReplaceByFly
)
2787 = ConvertDrawTextToFly(pObject
, pOurNewObject
, *pRecord
, eAnchor
, aFSFA
, aFlySet
);
2788 if (pRetFrameFormat
)
2798 sw::util::SetLayer
aSetLayer(m_rDoc
);
2799 if ( bMoveToBackground
)
2800 aSetLayer
.SendObjectToHell(*pObject
);
2802 aSetLayer
.SendObjectToHeaven(*pObject
);
2804 if (!IsInlineEscherHack())
2806 /* Need to make sure that the correct layer ordering is applied. */
2807 // pass information, if object is in page header|footer to method.
2808 m_xWWZOrder
->InsertEscherObject(pObject
.get(), aFSFA
.nSpId
, pRecord
->bDrawHell
,
2809 m_bIsHeader
|| m_bIsFooter
);
2813 m_xWWZOrder
->InsertTextLayerObject(pObject
.get());
2816 pRetFrameFormat
= m_rDoc
.getIDocumentContentOperations().InsertDrawObj(*m_pPaM
, *pObject
, aFlySet
);
2818 OSL_ENSURE(pRetFrameFormat
->GetAnchor().GetAnchorId() ==
2819 eAnchor
, "Not the anchor type requested!");
2822 Insert text if necessary into textboxes contained in groups.
2824 for (const auto& it
: aData
)
2827 if (pRecord
->pObj
&& pRecord
->aTextId
.nTxBxS
)
2828 { // #i52825# pRetFrameFormat can be NULL
2829 pRetFrameFormat
= MungeTextIntoDrawBox(
2830 *pRecord
, nGrafAnchorCp
, pRetFrameFormat
);
2836 SwDrawFrameFormat
* pDrawFrameFormat
= dynamic_cast<SwDrawFrameFormat
*>(pRetFrameFormat
);
2837 // #i44344#, #i44681# - positioning attributes already set
2838 if (pDrawFrameFormat
)
2839 pDrawFrameFormat
->PosAttrSet();
2840 if (!IsInlineEscherHack() && pRetFrameFormat
)
2841 MapWrapIntoFlyFormat(*pRecord
, *pRetFrameFormat
);
2843 // Set frame name with object name
2844 if (pRetFrameFormat
/*#i52825# */)
2846 if (!aObjName
.isEmpty())
2847 pRetFrameFormat
->SetFormatName( aObjName
);
2848 if (pRetFrameFormat
->GetName().isEmpty())
2851 pRetFrameFormat
->SetFormatName(m_rDoc
.GetUniqueDrawObjectName());
2853 pRetFrameFormat
->SetFormatName(m_rDoc
.GetUniqueFrameName());
2856 return AddAutoAnchor(pRetFrameFormat
);
2859 SwFrameFormat
*SwWW8ImplReader::AddAutoAnchor(SwFrameFormat
*pFormat
)
2862 * anchored to character at the current position will move along the
2863 * paragraph as text is added because we are at the insertion point.
2865 * Leave to later and set the correct location then.
2867 if (pFormat
&& (pFormat
->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR
))
2869 m_xAnchorStck
->AddAnchor(*m_pPaM
->GetPoint(), pFormat
);
2874 SwFrameFormat
* SwWW8ImplReader::MungeTextIntoDrawBox(SvxMSDffImportRec
& rRecord
,
2875 tools::Long nGrafAnchorCp
, SwFrameFormat
* pRetFrameFormat
)
2877 rtl::Reference
<SdrObject
> pTrueObject
= rRecord
.pObj
;
2879 rtl::Reference
<SdrTextObj
> pSdrTextObj
;
2881 // check for group object (e.g. two parentheses)
2882 if (SdrObjGroup
* pThisGroup
= dynamic_cast<SdrObjGroup
*>(rRecord
.pObj
.get()))
2884 // Group objects don't have text. Insert a text object into
2885 // the group for holding the text.
2886 pSdrTextObj
= new SdrRectObj(
2889 pThisGroup
->GetCurrentBoundRect());
2891 SfxItemSet
aSet(m_pDrawModel
->GetItemPool());
2892 aSet
.Put(XFillStyleItem(drawing::FillStyle_NONE
));
2893 aSet
.Put(XLineStyleItem(drawing::LineStyle_NONE
));
2894 aSet
.Put(SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE
));
2895 aSet
.Put(makeSdrTextAutoGrowHeightItem(false));
2896 aSet
.Put(makeSdrTextAutoGrowWidthItem(false));
2897 pSdrTextObj
->SetMergedItemSet(aSet
);
2898 pSdrTextObj
->NbcSetLayer( pThisGroup
->GetLayer() );
2899 pThisGroup
->GetSubList()->NbcInsertObject(pSdrTextObj
.get());
2902 pSdrTextObj
= DynCastSdrTextObj(rRecord
.pObj
.get());
2906 Size
aObjSize(pSdrTextObj
->GetSnapRect().GetWidth(),
2907 pSdrTextObj
->GetSnapRect().GetHeight());
2909 // Object is part of a group?
2910 SdrObject
* pGroupObject
= pSdrTextObj
->getParentSdrObjectFromSdrObject();
2912 const size_t nOrdNum
= pSdrTextObj
->GetOrdNum();
2913 bool bEraseThisObject
;
2914 InsertTxbxText(pSdrTextObj
.get(), &aObjSize
, rRecord
.aTextId
.nTxBxS
, rRecord
.aTextId
.nSequence
,
2915 nGrafAnchorCp
, pRetFrameFormat
,
2916 (pSdrTextObj
.get() != pTrueObject
.get()) || (nullptr != pGroupObject
), bEraseThisObject
,
2917 nullptr, nullptr, nullptr, nullptr, &rRecord
);
2919 // was this object replaced ??
2920 if (bEraseThisObject
)
2922 if( pGroupObject
|| (pSdrTextObj
.get() != pTrueObject
.get()) )
2924 // Object is already replaced by a new SdrGrafObj (in the group
2925 // and) the Drawing-Page.
2927 SdrObject
* pNewObj
= pGroupObject
?
2928 pGroupObject
->GetSubList()->GetObj(nOrdNum
) : pTrueObject
.get();
2929 if (pSdrTextObj
.get() != pNewObj
)
2931 // Replace object in the Z-Order-List
2932 m_xMSDffManager
->ExchangeInShapeOrder(pSdrTextObj
.get(), 0, pNewObj
);
2933 // and save the new object.
2934 rRecord
.pObj
= pNewObj
;
2939 // remove the object from Z-Order list
2940 m_xMSDffManager
->RemoveFromShapeOrder( pSdrTextObj
.get() );
2941 // take the object from the drawing page
2942 if( pSdrTextObj
->getSdrPageFromSdrObject() )
2943 m_pDrawPg
->RemoveObject( pSdrTextObj
->GetOrdNum() );
2944 // and delete FrameFormat, because replaced by graphic
2945 // (this also deletes the object)
2946 m_rDoc
.DelFrameFormat( pRetFrameFormat
);
2947 pRetFrameFormat
= nullptr;
2948 // also delete the object record
2949 rRecord
.pObj
= nullptr;
2954 // use ww8-default border distance
2955 SfxItemSetFixed
<SDRATTR_TEXT_LEFTDIST
, SDRATTR_TEXT_LOWERDIST
>
2956 aItemSet(m_pDrawModel
->GetItemPool());
2957 aItemSet
.Put(makeSdrTextLeftDistItem(rRecord
.nDxTextLeft
));
2958 aItemSet
.Put(makeSdrTextRightDistItem(rRecord
.nDxTextRight
));
2959 aItemSet
.Put(makeSdrTextUpperDistItem(rRecord
.nDyTextTop
));
2960 aItemSet
.Put(makeSdrTextLowerDistItem(rRecord
.nDyTextBottom
));
2961 pSdrTextObj
->SetMergedItemSetAndBroadcast(aItemSet
);
2964 return pRetFrameFormat
;
2967 SwFlyFrameFormat
* SwWW8ImplReader::ConvertDrawTextToFly(rtl::Reference
<SdrObject
>& rpObject
,
2968 rtl::Reference
<SdrObject
>& rpOurNewObject
,
2969 const SvxMSDffImportRec
& rRecord
,
2970 RndStdIds eAnchor
, const WW8_FSPA
& rF
,
2971 SfxItemSet
&rFlySet
)
2973 SwFlyFrameFormat
* pRetFrameFormat
= nullptr;
2974 tools::Long nStartCp
;
2977 // Check if this textbox chain contains text as conversion of an empty
2978 // chain would not make sense.
2979 if (TxbxChainContainsRealText(rRecord
.aTextId
.nTxBxS
, nStartCp
, nEndCp
))
2981 // The Text is not read into SdrTextObj! Rather insert a frame and
2982 // insert the text from nStartCp to nEndCp.
2984 // More attributes can be used in a frame compared to the
2985 // Edit-Engine, and it can contain field, OLEs or graphics...
2986 tools::Rectangle
aInnerDist(rRecord
.nDxTextLeft
, rRecord
.nDyTextTop
, rRecord
.nDxTextRight
,
2987 rRecord
.nDyTextBottom
);
2989 SwFormatFrameSize
aFrameSize(SwFrameSize::Fixed
, rF
.nXaRight
- rF
.nXaLeft
,
2990 rF
.nYaBottom
- rF
.nYaTop
);
2991 aFrameSize
.SetWidthSizeType(rRecord
.bAutoWidth
? SwFrameSize::Variable
2992 : SwFrameSize::Fixed
);
2993 rFlySet
.Put(aFrameSize
);
2995 MatchSdrItemsIntoFlySet(rpObject
.get(), rFlySet
, rRecord
.eLineStyle
, rRecord
.eLineDashing
,
2996 rRecord
.eShapeType
, aInnerDist
);
2998 SdrTextObj
*pSdrTextObj
= DynCastSdrTextObj(rpObject
.get());
2999 if (pSdrTextObj
&& pSdrTextObj
->IsVerticalWriting())
3000 rFlySet
.Put(SvxFrameDirectionItem(SvxFrameDirection::Vertical_RL_TB
, RES_FRAMEDIR
));
3002 pRetFrameFormat
= m_rDoc
.MakeFlySection(eAnchor
, m_pPaM
->GetPoint(), &rFlySet
);
3003 OSL_ENSURE(pRetFrameFormat
->GetAnchor().GetAnchorId() == eAnchor
,
3004 "Not the anchor type requested!");
3006 // if everything is OK, find pointer on new object and correct
3007 // Z-order list (or delete entry)
3008 rpOurNewObject
= CreateContactObject(pRetFrameFormat
);
3010 // remove old object from the Z-Order list
3011 m_xMSDffManager
->RemoveFromShapeOrder( rpObject
.get() );
3013 // and delete the object
3016 NB: only query pOrgShapeObject starting here!
3022 We do not store our rpOutNewObject in the ShapeOrder because we
3023 have a FrameFormat from which we can regenerate the contact object when
3024 we need it. Because, we can have frames anchored to paragraphs in
3025 header/footers and we can copy header/footers, if we do copy a
3026 header/footer with a nonpage anchored frame in it then the contact
3027 objects are invalidated. Under this condition the FrameFormat will be
3028 updated to reflect this change and can be used to get a new
3029 contact object, while a raw rpOutNewObject stored here becomes
3030 deleted and useless.
3032 m_xMSDffManager
->StoreShapeOrder(rF
.nSpId
,
3033 (static_cast<sal_uLong
>(rRecord
.aTextId
.nTxBxS
) << 16) +
3034 rRecord
.aTextId
.nSequence
, nullptr, pRetFrameFormat
);
3036 // The Contact object has to be inserted into the draw page, so
3037 // SwWW8ImplReader::LoadDoc1() can determine the z-order.
3038 if (!rpOurNewObject
->IsInserted())
3040 // pass information, if object is in page header|footer to method.
3041 m_xWWZOrder
->InsertEscherObject(rpOurNewObject
.get(), rF
.nSpId
, rRecord
.bDrawHell
,
3042 m_bIsHeader
|| m_bIsFooter
);
3046 // Box-0 receives the text for the whole chain!
3047 if (!rRecord
.aTextId
.nSequence
)
3049 // save flags etc and reset them
3050 WW8ReaderSave
aSave( this );
3052 MoveInsideFly(pRetFrameFormat
);
3054 m_xWWZOrder
->InsideEscher(rF
.nSpId
);
3057 m_bTxbxFlySection
= true;
3058 bool bJoined
= ReadText(nStartCp
, (nEndCp
-nStartCp
),
3059 MAN_MAINTEXT
== m_xPlcxMan
->GetManType() ?
3060 MAN_TXBX
: MAN_TXBX_HDFT
);
3062 m_xWWZOrder
->OutsideEscher();
3064 MoveOutsideFly(pRetFrameFormat
, aSave
.GetStartPos(),!bJoined
);
3066 aSave
.Restore( this );
3068 StripNegativeAfterIndent(pRetFrameFormat
);
3072 return pRetFrameFormat
;
3075 void MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec
&rRecord
, SfxItemSet
&rFlySet
)
3077 if (rRecord
.bVFlip
|| rRecord
.bHFlip
)
3079 MirrorGraph
eType(MirrorGraph::Dont
);
3080 if (rRecord
.bVFlip
&& rRecord
.bHFlip
)
3081 eType
= MirrorGraph::Both
;
3082 else if (rRecord
.bVFlip
)
3083 eType
= MirrorGraph::Horizontal
;
3085 eType
= MirrorGraph::Vertical
;
3086 rFlySet
.Put( SwMirrorGrf(eType
) );
3090 SwFlyFrameFormat
* SwWW8ImplReader::ImportReplaceableDrawables(rtl::Reference
<SdrObject
> &rpObject
,
3091 rtl::Reference
<SdrObject
> &rpOurNewObject
,
3092 SvxMSDffImportRec
& rRecord
,
3094 SfxItemSet
&rFlySet
)
3096 SwFlyFrameFormat
* pRetFrameFormat
= nullptr;
3097 sal_Int32 nWidthTw
= o3tl::saturating_sub(rF
.nXaRight
, rF
.nXaLeft
);
3100 sal_Int32 nHeightTw
= o3tl::saturating_sub(rF
.nYaBottom
, rF
.nYaTop
);
3104 ProcessEscherAlign(rRecord
, rF
, rFlySet
);
3106 rFlySet
.Put(SwFormatFrameSize(SwFrameSize::Fixed
, nWidthTw
, nHeightTw
));
3108 SfxItemSetFixed
<RES_GRFATR_BEGIN
, RES_GRFATR_END
-1> aGrSet(m_rDoc
.GetAttrPool());
3110 // Note that the escher inner distance only seems to be honoured in
3111 // word for textboxes, not for graphics and ole objects.
3112 tools::Rectangle
aInnerDist(0, 0, 0, 0);
3114 MatchSdrItemsIntoFlySet(rpObject
.get(), rFlySet
, rRecord
.eLineStyle
, rRecord
.eLineDashing
,
3115 rRecord
.eShapeType
, aInnerDist
);
3117 MatchEscherMirrorIntoFlySet(rRecord
, aGrSet
);
3119 OUString
aObjectName(rpObject
->GetName());
3120 if (SdrObjKind::OLE2
== rpObject
->GetObjIdentifier())
3121 pRetFrameFormat
= InsertOle(*static_cast<SdrOle2Obj
*>(rpObject
.get()), rFlySet
, &aGrSet
);
3124 const SdrGrafObj
*pGrf
= static_cast<const SdrGrafObj
*>(rpObject
.get());
3126 if (pGrf
->IsLinkedGraphic() && !pGrf
->GetFileName().isEmpty())
3128 GraphicType eType
= pGrf
->GetGraphicType();
3130 URIHelper::SmartRel2Abs(
3131 INetURLObject(m_sBaseURL
), pGrf
->GetFileName(),
3132 URIHelper::GetMaybeFileHdl()));
3133 // correction of fix for issue #i10939#:
3134 // One of the two conditions have to be true to insert the graphic
3135 // as a linked graphic -
3136 if (GraphicType::NONE
== eType
|| CanUseRemoteLink(aGrfName
))
3138 pRetFrameFormat
= m_rDoc
.getIDocumentContentOperations().InsertGraphic(
3139 *m_pPaM
, aGrfName
, OUString(), nullptr,
3140 &rFlySet
, &aGrSet
, nullptr);
3146 const Graphic
& rGraph
= pGrf
->GetGraphic();
3147 pRetFrameFormat
= m_rDoc
.getIDocumentContentOperations().InsertGraphic(
3148 *m_pPaM
, OUString(), OUString(), &rGraph
,
3149 &rFlySet
, &aGrSet
, nullptr);
3153 if (pRetFrameFormat
)
3155 if (SdrObjKind::OLE2
!= rpObject
->GetObjIdentifier())
3156 SetAttributesAtGrfNode(rRecord
, *pRetFrameFormat
, &rF
);
3157 // avoid multiple occurrences of the same graphic name
3158 m_aGrfNameGenerator
.SetUniqueGraphName(pRetFrameFormat
, aObjectName
);
3160 // if everything is OK, determine pointer to new object and correct
3161 // Z-Order-List accordingly (or delete entry)
3162 rpOurNewObject
= CreateContactObject(pRetFrameFormat
);
3164 // remove old object from Z-Order-List
3165 m_xMSDffManager
->RemoveFromShapeOrder( rpObject
.get() );
3166 // remove from Drawing-Page
3167 if( rpObject
->getSdrPageFromSdrObject() )
3168 m_pDrawPg
->RemoveObject( rpObject
->GetOrdNum() );
3170 // and delete the object
3174 Warning: from now on query only pOrgShapeObject!
3177 // add Contact-Object to the Z-Order-List and the page
3180 if (!m_bHdFtFootnoteEdn
)
3181 m_xMSDffManager
->StoreShapeOrder(rF
.nSpId
, 0, rpOurNewObject
.get());
3183 // The Contact-Object MUST be set in the Draw-Page, so that in
3184 // SwWW8ImplReader::LoadDoc1() the Z-Order can be defined !!!
3185 if (!rpOurNewObject
->IsInserted())
3187 // pass information, if object is in page header|footer to method.
3188 m_xWWZOrder
->InsertEscherObject(rpOurNewObject
.get(), rF
.nSpId
, rRecord
.bDrawHell
,
3189 m_bIsHeader
|| m_bIsFooter
);
3192 return pRetFrameFormat
;
3195 void SwWW8ImplReader::GraphicCtor() // For SVDraw and VCControls and Escher
3200 m_rDoc
.getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed
3201 m_pDrawModel
= m_rDoc
.getIDocumentDrawModelAccess().GetDrawModel();
3202 OSL_ENSURE(m_pDrawModel
, "Cannot create DrawModel");
3203 m_pDrawPg
= m_pDrawModel
->GetPage(0);
3205 m_xMSDffManager
.reset(new SwMSDffManager(*this, m_bSkipImages
));
3206 m_xMSDffManager
->SetModel(m_pDrawModel
, 1440);
3208 Now the dff manager always needs a controls converter as well, but a
3209 control converter may still exist without a dffmanager.
3211 m_xFormImpl
.reset(new SwMSConvertControls(m_pDocShell
, m_pPaM
));
3213 m_xWWZOrder
.reset(new wwZOrderer(sw::util::SetLayer(m_rDoc
), m_pDrawPg
,
3214 m_xMSDffManager
->GetShapeOrders()));
3217 void SwWW8ImplReader::GraphicDtor()
3219 m_pDrawEditEngine
.reset(); // maybe created by graphic
3220 m_xWWZOrder
.reset(); // same
3223 void SwWW8FltAnchorStack::AddAnchor(const SwPosition
& rPos
, SwFrameFormat
*pFormat
)
3225 OSL_ENSURE(pFormat
->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR
,
3226 "Don't use fltanchors with inline frames, slap!");
3227 NewAttr(rPos
, SwFltAnchor(pFormat
));
3230 void SwWW8FltAnchorStack::Flush()
3232 size_t nCnt
= size();
3233 for (size_t i
=0; i
< nCnt
; ++i
)
3235 SwFltStackEntry
&rEntry
= (*this)[i
];
3236 SwPosition
aDummy(rEntry
.m_aMkPos
.m_nNode
);
3237 SetAttrInDoc(aDummy
, rEntry
);
3238 DeleteAndDestroy(i
--);
3243 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */