1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <com/sun/star/util/CloseVetoException.hpp>
24 #include <com/sun/star/util/XCloseable.hpp>
25 
26 #include <doc.hxx>
27 #include "writerhelper.hxx"
28 #include <msfilter.hxx>
29 #include <com/sun/star/container/XChild.hpp>
30 
31 #include <algorithm>
32 #include <svl/itemiter.hxx>
33 #include <svx/svdobj.hxx>
34 #include <svx/svdoole2.hxx>
35 #include <svx/fmglob.hxx>
36 #include <editeng/formatbreakitem.hxx>
37 #include <editeng/tstpitem.hxx>
38 #include <ndtxt.hxx>
39 #include <ndnotxt.hxx>
40 #include <fmtcntnt.hxx>
41 #include <swtable.hxx>
42 #include <frmfmt.hxx>
43 #include <flypos.hxx>
44 #include <fmtanchr.hxx>
45 #include <ndgrf.hxx>
46 #include <fmtfsize.hxx>
47 #include <SwStyleNameMapper.hxx>
48 #include <docary.hxx>
49 #include <charfmt.hxx>
50 #include <fchrfmt.hxx>
51 #include <redline.hxx>
52 #include "types.hxx"
53 #include <unotools/streamwrap.hxx>
54 #include <svtools/embedhlp.hxx>
55 #include <numrule.hxx>
56 #include <vcl/svapp.hxx>
57 #include <IDocumentDrawModelAccess.hxx>
58 #include <IDocumentLayoutAccess.hxx>
59 #include <IDocumentStylePoolAccess.hxx>
60 #include <IDocumentMarkAccess.hxx>
61 #include <IMark.hxx>
62 
63 using namespace com::sun::star;
64 
65 namespace
66 {
67     // #i98791# - adjust sorting
68     // Utility to sort SwTextFormatColl's by their assigned outline style list level
69     class outlinecmp
70     {
71     public:
operator ()(const SwTextFormatColl * pA,const SwTextFormatColl * pB) const72         bool operator()(const SwTextFormatColl *pA, const SwTextFormatColl *pB) const
73         {
74             // #i98791#
75             bool bResult( false );
76             const bool bIsAAssignedToOutlineStyle( pA->IsAssignedToListLevelOfOutlineStyle() );
77             const bool bIsBAssignedToOutlineStyle( pB->IsAssignedToListLevelOfOutlineStyle() );
78             if ( bIsAAssignedToOutlineStyle != bIsBAssignedToOutlineStyle )
79             {
80                 bResult = bIsBAssignedToOutlineStyle;
81             }
82             else if ( !bIsAAssignedToOutlineStyle )
83             {
84                 // pA and pB are equal regarding the sorting criteria.
85                 // Thus return value does not matter.
86                 bResult = false;
87             }
88             else
89             {
90                 bResult = pA->GetAssignedOutlineStyleLevel() < pB->GetAssignedOutlineStyleLevel();
91             }
92 
93             return bResult;
94        }
95     };
96 
IsValidSlotWhich(sal_uInt16 nSlotId,sal_uInt16 nWhichId)97     bool IsValidSlotWhich(sal_uInt16 nSlotId, sal_uInt16 nWhichId)
98     {
99         return (nSlotId != 0 && nWhichId != 0 && nSlotId != nWhichId);
100     }
101 
102     /*
103      Utility to convert a SwPosFlyFrames into a simple vector of ww8::Frames
104 
105      The crucial thing is that a ww8::Frame always has an anchor which
106      points to some content in the document. This is a requirement of exporting
107      to Word
108     */
SwPosFlyFramesToFrames(const SwPosFlyFrames & rFlys)109     ww8::Frames SwPosFlyFramesToFrames(const SwPosFlyFrames &rFlys)
110     {
111         ww8::Frames aRet;
112 
113         for(const auto& rpFly : rFlys)
114         {
115             const SwFrameFormat &rEntry = rpFly->GetFormat();
116 
117             if (const SwPosition* pAnchor = rEntry.GetAnchor().GetContentAnchor())
118             {
119                 // the anchor position will be invalidated by SetRedlineFlags
120                 // so set a dummy position and fix it in UpdateFramePositions
121                 SwPosition const dummy(SwNodeIndex(
122                             const_cast<SwNodes&>(pAnchor->nNode.GetNodes())));
123                 aRet.emplace_back(rEntry, dummy);
124             }
125             else
126             {
127                 SwPosition aPos(rpFly->GetNdIndex());
128 
129                 if (SwTextNode* pTextNd = aPos.nNode.GetNode().GetTextNode())
130                 {
131                     aPos.nContent.Assign(pTextNd, 0);
132                 }
133 
134                 aRet.emplace_back(rEntry, aPos);
135             }
136         }
137         return aRet;
138     }
139 
140     //Utility to test if a frame is anchored at a given node index
141     class anchoredto
142     {
143     private:
144         sal_uLong const mnNode;
145     public:
anchoredto(sal_uLong nNode)146         explicit anchoredto(sal_uLong nNode) : mnNode(nNode) {}
operator ()(const ww8::Frame & rFrame) const147         bool operator()(const ww8::Frame &rFrame) const
148         {
149             return (mnNode == rFrame.GetPosition().nNode.GetNode().GetIndex());
150         }
151     };
152 }
153 
154 namespace ww8
155 {
156     //For i120928,size conversion before exporting graphic of bullet
Frame(const Graphic & rGrf,const SwPosition & rPos)157     Frame::Frame(const Graphic &rGrf, const SwPosition &rPos)
158         : mpFlyFrame(nullptr)
159         , maPos(rPos)
160         , maSize()
161         , maLayoutSize()
162         , meWriterType(eBulletGrf)
163         , mpStartFrameContent(nullptr)
164         , mbIsInline(true)
165         , mbForBullet(true)
166         , maGrf(rGrf)
167     {
168         const MapMode aMap100mm( MapUnit::Map100thMM );
169         Size    aSize( rGrf.GetPrefSize() );
170         if ( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() )
171         {
172             aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap100mm );
173         }
174         else
175         {
176             aSize = OutputDevice::LogicToLogic( aSize,rGrf.GetPrefMapMode(), aMap100mm );
177         }
178         maSize = aSize;
179         maLayoutSize = maSize;
180     }
181 
Frame(const SwFrameFormat & rFormat,const SwPosition & rPos)182     Frame::Frame(const SwFrameFormat &rFormat, const SwPosition &rPos)
183         : mpFlyFrame(&rFormat)
184         , maPos(rPos)
185         , maSize()
186         , maLayoutSize() // #i43447#
187         , meWriterType(eTextBox)
188         , mpStartFrameContent(nullptr)
189         // #i43447# - move to initialization list
190         , mbIsInline( (rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) )
191         // #i120928# - handle graphic of bullet within existing implementation
192         , mbForBullet(false)
193         , maGrf()
194     {
195         switch (rFormat.Which())
196         {
197             case RES_FLYFRMFMT:
198                 if (const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx())
199                 {
200                     SwNodeIndex aIdx(*pIdx, 1);
201                     const SwNode &rNd = aIdx.GetNode();
202                     // #i43447# - determine layout size
203                     {
204                         SwRect aLayRect( rFormat.FindLayoutRect() );
205                         tools::Rectangle aRect( aLayRect.SVRect() );
206                         // The Object is not rendered (e.g. something in unused
207                         // header/footer) - thus, get the values from the format.
208                         if ( aLayRect.IsEmpty() )
209                         {
210                             aRect.SetSize( rFormat.GetFrameSize().GetSize() );
211                         }
212                         maLayoutSize = aRect.GetSize();
213                     }
214                     switch (rNd.GetNodeType())
215                     {
216                         case SwNodeType::Grf:
217                             meWriterType = eGraphic;
218                             maSize = rNd.GetNoTextNode()->GetTwipSize();
219                             break;
220                         case SwNodeType::Ole:
221                             meWriterType = eOle;
222                             maSize = rNd.GetNoTextNode()->GetTwipSize();
223                             break;
224                         default:
225                             meWriterType = eTextBox;
226                             // #i43447# - Size equals layout size for text boxes
227                             maSize = maLayoutSize;
228                             break;
229                     }
230                     mpStartFrameContent = &rNd;
231                 }
232                 else
233                 {
234                     OSL_ENSURE(false, "Impossible");
235                     meWriterType = eTextBox;
236                 }
237                 break;
238             default:
239                 if (const SdrObject* pObj = rFormat.FindRealSdrObject())
240                 {
241                     if (pObj->GetObjInventor() == SdrInventor::FmForm)
242                         meWriterType = eFormControl;
243                     else
244                         meWriterType = eDrawing;
245                     maSize = pObj->GetSnapRect().GetSize();
246                     maLayoutSize = maSize;
247                 }
248                 else
249                 {
250                     OSL_ENSURE(false, "Impossible");
251                     meWriterType = eDrawing;
252                 }
253                 break;
254         }
255     }
256 
257 
ForceTreatAsInline()258     void Frame::ForceTreatAsInline()
259     {
260         mbIsInline = true;
261     }
262 }
263 
264 namespace sw
265 {
266     namespace hack
267     {
268 
TransformWhichBetweenPools(const SfxItemPool & rDestPool,const SfxItemPool & rSrcPool,sal_uInt16 nWhich)269         sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool,
270             const SfxItemPool &rSrcPool, sal_uInt16 nWhich)
271         {
272             sal_uInt16 nSlotId = rSrcPool.GetSlotId(nWhich);
273             if (IsValidSlotWhich(nSlotId, nWhich))
274                 nWhich = rDestPool.GetWhich(nSlotId);
275             else
276                 nWhich = 0;
277             return nWhich;
278         }
279 
GetSetWhichFromSwDocWhich(const SfxItemSet & rSet,const SwDoc & rDoc,sal_uInt16 nWhich)280         sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet,
281             const SwDoc &rDoc, sal_uInt16 nWhich)
282         {
283             if (RES_WHICHHINT_END < *(rSet.GetRanges()))
284             {
285                 nWhich = TransformWhichBetweenPools(*rSet.GetPool(),
286                     rDoc.GetAttrPool(), nWhich);
287             }
288             return nWhich;
289         }
290 
DrawingOLEAdaptor(SdrOle2Obj & rObj,SfxObjectShell & rPers)291         DrawingOLEAdaptor::DrawingOLEAdaptor(SdrOle2Obj &rObj,
292             SfxObjectShell &rPers)
293             : mxIPRef(rObj.GetObjRef()), mrPers(rPers),
294             mpGraphic( rObj.GetGraphic() )
295         {
296             rObj.AbandonObject();
297         }
298 
TransferToDoc(OUString & rName)299         bool DrawingOLEAdaptor::TransferToDoc( OUString &rName )
300         {
301             OSL_ENSURE(mxIPRef.is(), "Transferring invalid object to doc");
302             if (!mxIPRef.is())
303                 return false;
304 
305             uno::Reference < container::XChild > xChild( mxIPRef, uno::UNO_QUERY );
306             if ( xChild.is() )
307                 xChild->setParent( mrPers.GetModel() );
308 
309             bool bSuccess = mrPers.GetEmbeddedObjectContainer().InsertEmbeddedObject( mxIPRef, rName );
310             if (bSuccess)
311             {
312                 if ( mpGraphic )
313                     ::svt::EmbeddedObjectRef::SetGraphicToContainer( *mpGraphic,
314                                                                     mrPers.GetEmbeddedObjectContainer(),
315                                                                     rName,
316                                                                     OUString() );
317 
318                 mxIPRef = nullptr;
319             }
320 
321             return bSuccess;
322         }
323 
~DrawingOLEAdaptor()324         DrawingOLEAdaptor::~DrawingOLEAdaptor()
325         {
326             if (mxIPRef.is())
327             {
328                 OSL_ENSURE( !mrPers.GetEmbeddedObjectContainer().HasEmbeddedObject( mxIPRef ), "Object in adaptor is inserted?!" );
329                 try
330                 {
331                     mxIPRef->close(true);
332                 }
333                 catch ( const css::util::CloseVetoException& )
334                 {
335                 }
336 
337                 mxIPRef = nullptr;
338             }
339         }
340     }
341 
342     namespace util
343     {
MakeSafePositioningValue(SwTwips nIn)344         SwTwips MakeSafePositioningValue(SwTwips nIn)
345         {
346             if (nIn > SHRT_MAX)
347                 nIn = SHRT_MAX;
348             else if (nIn < SHRT_MIN)
349                 nIn = SHRT_MIN;
350             return nIn;
351         }
352 
SendObjectToHell(SdrObject & rObject) const353         void SetLayer::SendObjectToHell(SdrObject &rObject) const
354         {
355             SetObjectLayer(rObject, eHell);
356         }
357 
SendObjectToHeaven(SdrObject & rObject) const358         void SetLayer::SendObjectToHeaven(SdrObject &rObject) const
359         {
360             SetObjectLayer(rObject, eHeaven);
361         }
362 
SetObjectLayer(SdrObject & rObject,Layer eLayer) const363         void SetLayer::SetObjectLayer(SdrObject &rObject, Layer eLayer) const
364         {
365             if (SdrInventor::FmForm == rObject.GetObjInventor())
366                 rObject.SetLayer(mnFormLayer);
367             else
368             {
369                 switch (eLayer)
370                 {
371                     case eHeaven:
372                         rObject.SetLayer(mnHeavenLayer);
373                         break;
374                     case eHell:
375                         rObject.SetLayer(mnHellLayer);
376                         break;
377                 }
378             }
379         }
380 
381         //SetLayer boilerplate begin
382 
383         // #i38889# - by default put objects into the invisible layers.
SetLayer(const SwDoc & rDoc)384         SetLayer::SetLayer(const SwDoc &rDoc)
385             : mnHeavenLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId()),
386               mnHellLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()),
387               mnFormLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleControlsId())
388         {
389         }
390         //SetLayer boilerplate end
391 
GetPoolItems(const SfxItemSet & rSet,ww8::PoolItems & rItems,bool bExportParentItemSet)392         void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet )
393         {
394             if( bExportParentItemSet )
395             {
396                 sal_uInt16 nTotal = rSet.TotalCount();
397                 for( sal_uInt16 nItem =0; nItem < nTotal; ++nItem )
398                 {
399                     const SfxPoolItem* pItem = nullptr;
400                     if( SfxItemState::SET == rSet.GetItemState( rSet.GetWhichByPos( nItem ), true, &pItem ) )
401                     {
402                         rItems[pItem->Which()] = pItem;
403                     }
404                 }
405             }
406             else if( rSet.Count())
407             {
408                 SfxItemIter aIter(rSet);
409                 if (const SfxPoolItem *pItem = aIter.GetCurItem())
410                 {
411                     do
412                         rItems[pItem->Which()] = pItem;
413                     while ((pItem = aIter.NextItem()));
414                 }
415             }
416         }
417 
SearchPoolItems(const ww8::PoolItems & rItems,sal_uInt16 eType)418         const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems,
419             sal_uInt16 eType)
420         {
421             auto aIter = rItems.find(eType);
422             if (aIter != rItems.end())
423                 return aIter->second;
424             return nullptr;
425         }
426 
ClearOverridesFromSet(const SwFormatCharFormat & rFormat,SfxItemSet & rSet)427         void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet)
428         {
429             if (const SwCharFormat* pCharFormat = rFormat.GetCharFormat())
430             {
431                 if (pCharFormat->GetAttrSet().Count())
432                 {
433                     SfxItemIter aIter(pCharFormat->GetAttrSet());
434                     const SfxPoolItem *pItem = aIter.GetCurItem();
435                     do
436                         rSet.ClearItem(pItem->Which());
437                     while ((pItem = aIter.NextItem()));
438                 }
439             }
440         }
441 
GetParaStyles(const SwDoc & rDoc)442         ww8::ParaStyles GetParaStyles(const SwDoc &rDoc)
443         {
444             ww8::ParaStyles aStyles;
445             typedef ww8::ParaStyles::size_type mysizet;
446 
447             const SwTextFormatColls *pColls = rDoc.GetTextFormatColls();
448             mysizet nCount = pColls ? pColls->size() : 0;
449             aStyles.reserve(nCount);
450             for (mysizet nI = 0; nI < nCount; ++nI)
451                 aStyles.push_back((*pColls)[ static_cast< sal_uInt16 >(nI) ]);
452             return aStyles;
453         }
454 
GetParaStyle(SwDoc & rDoc,const OUString & rName)455         SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName)
456         {
457             // Search first in the Doc-Styles
458             SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName(rName);
459             if (!pColl)
460             {
461                 // Collection not found, try in Pool ?
462                 sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
463                     SwGetPoolIdFromName::TxtColl);
464                 if (n != SAL_MAX_UINT16)       // found or standard
465                     pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(n, false);
466             }
467             return pColl;
468         }
469 
GetCharStyle(SwDoc & rDoc,const OUString & rName)470         SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName)
471         {
472             SwCharFormat *pFormat = rDoc.FindCharFormatByName(rName);
473             if (!pFormat)
474             {
475                 // Collection not found, try in Pool ?
476                 sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
477                     SwGetPoolIdFromName::ChrFmt);
478                 if (n != SAL_MAX_UINT16)       // found or standard
479                     pFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(n);
480             }
481             return pFormat;
482         }
483 
484         // #i98791# - adjust sorting algorithm
SortByAssignedOutlineStyleListLevel(ww8::ParaStyles & rStyles)485         void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles)
486         {
487             std::sort(rStyles.begin(), rStyles.end(), outlinecmp());
488         }
489 
490         /*
491            Utility to extract FlyFormats from a document, potentially from a
492            selection.
493            */
GetFrames(const SwDoc & rDoc,SwPaM const * pPaM)494         ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM /*, bool bAll*/)
495         {
496             SwPosFlyFrames aFlys(rDoc.GetAllFlyFormats(pPaM, true));
497             ww8::Frames aRet(SwPosFlyFramesToFrames(aFlys));
498             return aRet;
499         }
500 
UpdateFramePositions(ww8::Frames & rFrames)501         void UpdateFramePositions(ww8::Frames & rFrames)
502         {
503             for (ww8::Frame & rFrame : rFrames)
504             {
505                 SwFormatAnchor const& rAnchor = rFrame.GetFrameFormat().GetAnchor();
506                 if (SwPosition const*const pAnchor = rAnchor.GetContentAnchor())
507                 {
508                     rFrame.SetPosition(*pAnchor);
509                 }
510                 else
511                 {   // these don't need to be corrected, they're not in redlines
512                     assert(RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId());
513                 }
514             }
515         }
516 
GetFramesInNode(const ww8::Frames & rFrames,const SwNode & rNode)517         ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode)
518         {
519             ww8::Frames aRet;
520             std::copy_if(rFrames.begin(), rFrames.end(),
521                 std::back_inserter(aRet), anchoredto(rNode.GetIndex()));
522             return aRet;
523         }
524 
GetNumFormatFromSwNumRuleLevel(const SwNumRule & rRule,int nLevel)525         const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule,
526             int nLevel)
527         {
528             if (nLevel < 0 || nLevel >= MAXLEVEL)
529             {
530                 OSL_FAIL("Invalid level");
531                 return nullptr;
532             }
533             return &(rRule.Get( static_cast< sal_uInt16 >(nLevel) ));
534         }
535 
GetNumFormatFromTextNode(const SwTextNode & rTextNode)536         const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode)
537         {
538             const SwNumRule *pRule = nullptr;
539             if (
540                 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
541                 nullptr != (pRule = rTextNode.GetNumRule())
542                 )
543             {
544                 return GetNumFormatFromSwNumRuleLevel(*pRule,
545                     rTextNode.GetActualListLevel());
546             }
547 
548             OSL_ENSURE(rTextNode.GetDoc(), "No document for node?, suspicious");
549             if (!rTextNode.GetDoc())
550                 return nullptr;
551 
552             if (
553                 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
554                 nullptr != (pRule = rTextNode.GetDoc()->GetOutlineNumRule())
555                 )
556             {
557                 return GetNumFormatFromSwNumRuleLevel(*pRule,
558                     rTextNode.GetActualListLevel());
559             }
560 
561             return nullptr;
562         }
563 
GetNumRuleFromTextNode(const SwTextNode & rTextNode)564         const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNode)
565         {
566             return GetNormalNumRuleFromTextNode(rTextNode);
567         }
568 
GetNormalNumRuleFromTextNode(const SwTextNode & rTextNode)569         const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNode)
570         {
571             const SwNumRule *pRule = nullptr;
572 
573             if (
574                 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
575                 nullptr != (pRule = rTextNode.GetNumRule())
576                )
577             {
578                 return pRule;
579             }
580             return nullptr;
581         }
582 
GetNoTextNodeFromSwFrameFormat(const SwFrameFormat & rFormat)583         SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat)
584         {
585             const SwNodeIndex *pIndex = rFormat.GetContent().GetContentIdx();
586             OSL_ENSURE(pIndex, "No NodeIndex in SwFrameFormat ?, suspicious");
587             if (!pIndex)
588                 return nullptr;
589             SwNodeIndex aIdx(*pIndex, 1);
590             return aIdx.GetNode().GetNoTextNode();
591         }
592 
HasPageBreak(const SwNode & rNd)593         bool HasPageBreak(const SwNode &rNd)
594         {
595             const SvxFormatBreakItem *pBreak = nullptr;
596             if (rNd.IsTableNode() && rNd.GetTableNode())
597             {
598                 const SwTable& rTable = rNd.GetTableNode()->GetTable();
599                 const SwFrameFormat* pApply = rTable.GetFrameFormat();
600                 OSL_ENSURE(pApply, "impossible");
601                 if (pApply)
602                     pBreak = &(ItemGet<SvxFormatBreakItem>(*pApply, RES_BREAK));
603             }
604             else if (const SwContentNode *pNd = rNd.GetContentNode())
605                 pBreak = &(ItemGet<SvxFormatBreakItem>(*pNd, RES_BREAK));
606 
607             return pBreak && pBreak->GetBreak() == SvxBreak::PageBefore;
608         }
609 
PolygonFromPolyPolygon(const tools::PolyPolygon & rPolyPoly)610         tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly)
611         {
612             if(1 == rPolyPoly.Count())
613             {
614                 return rPolyPoly[0];
615             }
616             else
617             {
618                 // This method will now just concatenate the polygons contained
619                 // in the given PolyPolygon. Anything else which might be thought of
620                 // for reducing to a single polygon will just need more power and
621                 // cannot create more correct results.
622                 sal_uInt32 nPointCount(0);
623                 sal_uInt16 a;
624 
625                 for(a = 0; a < rPolyPoly.Count(); a++)
626                 {
627                     nPointCount += static_cast<sal_uInt32>(rPolyPoly[a].GetSize());
628                 }
629 
630                 if(nPointCount > 0x0000ffff)
631                 {
632                     OSL_FAIL("PolygonFromPolyPolygon: too many points for a single polygon (!)");
633                     nPointCount = 0x0000ffff;
634                 }
635 
636                 tools::Polygon aRetval(static_cast<sal_uInt16>(nPointCount));
637                 sal_uInt32 nAppendIndex(0);
638 
639                 for(a = 0; a < rPolyPoly.Count(); a++)
640                 {
641                     const tools::Polygon& rCandidate = rPolyPoly[a];
642 
643                     for(sal_uInt16 b(0); nAppendIndex <= nPointCount && b < rCandidate.GetSize(); b++)
644                     {
645                         aRetval[static_cast<sal_uInt16>(nAppendIndex++)] = rCandidate[b];
646                     }
647                 }
648 
649                 return aRetval;
650             }
651         }
652 
CorrectWordWrapPolygonForExport(const tools::PolyPolygon & rPolyPoly,const SwNoTextNode * pNd)653         tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd)
654         {
655             tools::Polygon aPoly(PolygonFromPolyPolygon(rPolyPoly));
656             const Size &rOrigSize = pNd->GetGraphic().GetPrefSize();
657             Fraction aMapPolyX(ww::nWrap100Percent, rOrigSize.Width());
658             Fraction aMapPolyY(ww::nWrap100Percent, rOrigSize.Height());
659             aPoly.Scale(double(aMapPolyX), double(aMapPolyY));
660 
661             /*
662              a) stretch right bound by 15twips
663              b) shrink bottom bound to where it would have been in word
664              c) Move it to the left by 15twips
665 
666              See the import for details
667             */
668             const Size &rSize = pNd->GetTwipSize();
669             Fraction aMoveHack(ww::nWrap100Percent, rSize.Width());
670             aMoveHack *= Fraction(15, 1);
671             long nMove(aMoveHack);
672 
673             Fraction aHackX(ww::nWrap100Percent + nMove,
674                     ww::nWrap100Percent);
675             Fraction aHackY(ww::nWrap100Percent - nMove,
676                     ww::nWrap100Percent);
677             aPoly.Scale(double(aHackX), double(aHackY));
678 
679             aPoly.Move(-nMove, 0);
680             return aPoly;
681         }
682 
open(const SwPosition & rPos,const SfxPoolItem & rAttr)683         void RedlineStack::open(const SwPosition& rPos, const SfxPoolItem& rAttr)
684         {
685             OSL_ENSURE(rAttr.Which() == RES_FLTR_REDLINE, "not a redline");
686             maStack.emplace_back(new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone())));
687         }
688 
689         class SameOpenRedlineType
690         {
691         private:
692             RedlineType const meType;
693         public:
SameOpenRedlineType(RedlineType eType)694             explicit SameOpenRedlineType(RedlineType eType) : meType(eType) {}
operator ()(const std::unique_ptr<SwFltStackEntry> & pEntry) const695             bool operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const
696             {
697                 const SwFltRedline *pTest = static_cast<const SwFltRedline *>
698                     (pEntry->pAttr.get());
699                 return (pEntry->bOpen && (pTest->eType == meType));
700             }
701         };
702 
close(const SwPosition & rPos,RedlineType eType)703         bool RedlineStack::close(const SwPosition& rPos, RedlineType eType)
704         {
705             //Search from end for same type
706             auto aResult = std::find_if(maStack.rbegin(), maStack.rend(),
707                 SameOpenRedlineType(eType));
708             if (aResult != maStack.rend())
709             {
710                 SwTextNode *const pNode(rPos.nNode.GetNode().GetTextNode());
711                 sal_Int32 const nIndex(rPos.nContent.GetIndex());
712                 // HACK to prevent overlap of field-mark and redline,
713                 // which would destroy field-mark invariants when the redline
714                 // is hidden: move the redline end one to the left
715                 if (pNode && nIndex > 0
716                     && pNode->GetText()[nIndex - 1] == CH_TXT_ATR_FIELDEND)
717                 {
718                     SwPosition const end(*rPos.nNode.GetNode().GetTextNode(),
719                                          nIndex - 1);
720                     sw::mark::IFieldmark *const pFieldMark(
721                         rPos.GetDoc()->getIDocumentMarkAccess()->getFieldmarkAt(end));
722                     SAL_WARN_IF(!pFieldMark, "sw.ww8", "expected a field mark");
723                     if (pFieldMark && pFieldMark->GetMarkPos().nNode.GetIndex() == (*aResult)->m_aMkPos.m_nNode.GetIndex()+1
724                         && pFieldMark->GetMarkPos().nContent.GetIndex() < (*aResult)->m_aMkPos.m_nContent)
725                     {
726                         (*aResult)->SetEndPos(end);
727                         return true;
728                     }
729                 }
730                 (*aResult)->SetEndPos(rPos);
731                 return true;
732             }
733             return false;
734         }
735 
closeall(const SwPosition & rPos)736         void RedlineStack::closeall(const SwPosition& rPos)
737         {
738             std::for_each(maStack.begin(), maStack.end(), SetEndIfOpen(rPos));
739         }
740 
MoveAttrs(const SwPosition & rPos,MoveAttrsMode const eMode)741         void RedlineStack::MoveAttrs(const SwPosition& rPos, MoveAttrsMode const eMode)
742         {
743             size_t nCnt = maStack.size();
744             sal_Int32 const nInserted = eMode == MoveAttrsMode::FieldmarkInserted
745                 ? 2 // CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP
746                 : 1;
747             sal_uLong nPosNd = rPos.nNode.GetIndex();
748             sal_Int32 nPosCt = rPos.nContent.GetIndex() - nInserted;
749 
750             for (size_t i=0; i < nCnt; ++i)
751             {
752                 SwFltStackEntry& rEntry = *maStack[i];
753                 bool const isPoint(rEntry.m_aMkPos == rEntry.m_aPtPos);
754                 if ((rEntry.m_aMkPos.m_nNode.GetIndex()+1 == nPosNd) &&
755                     (nPosCt <= rEntry.m_aMkPos.m_nContent))
756                 {
757                     rEntry.m_aMkPos.m_nContent += nInserted;
758                     SAL_WARN_IF(rEntry.m_aMkPos.m_nContent > rPos.nNode.GetNodes()[nPosNd]->GetContentNode()->Len(),
759                             "sw.ww8", "redline ends after end of line");
760                     if (isPoint) // sigh ... important special case...
761                     {
762                         rEntry.m_aPtPos.m_nContent += nInserted;
763                         continue;
764                     }
765                 }
766                 // for the end position, leave it alone if it's *on* the dummy
767                 // char position, that should remain *before*
768                 if ((rEntry.m_aPtPos.m_nNode.GetIndex()+1 == nPosNd) &&
769                     (nPosCt < rEntry.m_aPtPos.m_nContent))
770                 {
771                     rEntry.m_aPtPos.m_nContent += nInserted;
772                     SAL_WARN_IF(rEntry.m_aPtPos.m_nContent > rPos.nNode.GetNodes()[nPosNd]->GetContentNode()->Len(),
773                             "sw.ww8", "redline ends after end of line");
774                 }
775             }
776         }
777 
operator ()(std::unique_ptr<SwFltStackEntry> & pEntry)778         void SetInDocAndDelete::operator()(std::unique_ptr<SwFltStackEntry>& pEntry)
779         {
780             SwPaM aRegion(pEntry->m_aMkPos.m_nNode);
781             if (pEntry->MakeRegion(&mrDoc, aRegion,
782                     SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark) &&
783                 (*aRegion.GetPoint() != *aRegion.GetMark())
784             )
785             {
786                 mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert |
787                                          RedlineFlags::ShowDelete);
788                 const SwFltRedline *pFltRedline = static_cast<const SwFltRedline*>
789                     (pEntry->pAttr.get());
790 
791                 SwRedlineData aData(pFltRedline->eType, pFltRedline->nAutorNo,
792                         pFltRedline->aStamp, OUString(), nullptr);
793 
794                 SwRangeRedline *const pNewRedline(new SwRangeRedline(aData, aRegion));
795                 // the point node may be deleted in AppendRedline, so park
796                 // the PaM somewhere safe
797                 aRegion.DeleteMark();
798                 *aRegion.GetPoint() = SwPosition(SwNodeIndex(mrDoc.GetNodes()));
799                 mrDoc.getIDocumentRedlineAccess().AppendRedline(pNewRedline, true);
800                 mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::NONE | RedlineFlags::ShowInsert |
801                      RedlineFlags::ShowDelete );
802             }
803             pEntry.reset();
804         }
805 
operator ()(const std::unique_ptr<SwFltStackEntry> & pOneE,const std::unique_ptr<SwFltStackEntry> & pTwoE) const806         bool CompareRedlines::operator()(const std::unique_ptr<SwFltStackEntry> & pOneE,
807             const std::unique_ptr<SwFltStackEntry> & pTwoE) const
808         {
809             const SwFltRedline *pOne= static_cast<const SwFltRedline*>
810                 (pOneE->pAttr.get());
811             const SwFltRedline *pTwo= static_cast<const SwFltRedline*>
812                 (pTwoE->pAttr.get());
813 
814             //Return the earlier time, if two have the same time, prioritize
815             //inserts over deletes
816             if (pOne->aStamp == pTwo->aStamp)
817                 return (pOne->eType == RedlineType::Insert && pTwo->eType != RedlineType::Insert);
818             else
819                 return (pOne->aStamp < pTwo->aStamp);
820         }
821 
~RedlineStack()822         RedlineStack::~RedlineStack()
823         {
824             std::sort(maStack.begin(), maStack.end(), CompareRedlines());
825             std::for_each(maStack.begin(), maStack.end(), SetInDocAndDelete(mrDoc));
826         }
827 
AddName(const OUString & rNm)828         sal_uInt16 WrtRedlineAuthor::AddName( const OUString& rNm )
829         {
830             sal_uInt16 nRet;
831             auto aIter = std::find(maAuthors.begin(), maAuthors.end(), rNm);
832             if (aIter != maAuthors.end())
833                 nRet = static_cast< sal_uInt16 >(aIter - maAuthors.begin());
834             else
835             {
836                 nRet = static_cast< sal_uInt16 >(maAuthors.size());
837                 maAuthors.push_back(rNm);
838             }
839             return nRet;
840         }
841     }
842 
843     namespace util
844     {
InsertedTableListener(SwTableNode & rNode)845         InsertedTableListener::InsertedTableListener(SwTableNode& rNode)
846             : m_pTableNode(&rNode)
847         {
848             StartListening(rNode.GetNotifier());
849         }
850 
GetTableNode()851         SwTableNode* InsertedTableListener::GetTableNode()
852             { return m_pTableNode; }
853 
Notify(const SfxHint & rHint)854         void  InsertedTableListener::Notify(const SfxHint& rHint)
855         {
856             if(rHint.GetId() == SfxHintId::Dying)
857                 m_pTableNode = nullptr;
858         }
859 
InsertedTablesManager(const SwDoc & rDoc)860         InsertedTablesManager::InsertedTablesManager(const SwDoc &rDoc)
861             : mbHasRoot(rDoc.getIDocumentLayoutAccess().GetCurrentLayout())
862         { }
863 
DelAndMakeTableFrames()864         void InsertedTablesManager::DelAndMakeTableFrames()
865         {
866             if (!mbHasRoot)
867                 return;
868             for (auto& aTable : maTables)
869             {
870                 // If already a layout exists, then the BoxFrames must recreated at this table
871                 SwTableNode *pTable = aTable.first->GetTableNode();
872                 OSL_ENSURE(pTable, "Why no expected table");
873                 if (pTable)
874                 {
875                     SwFrameFormat * pFrameFormat = pTable->GetTable().GetFrameFormat();
876 
877                     if (pFrameFormat != nullptr)
878                     {
879                         SwNodeIndex *pIndex = aTable.second;
880                         pTable->DelFrames();
881                         pTable->MakeOwnFrames(pIndex);
882                     }
883                 }
884             }
885         }
886 
InsertTable(SwTableNode & rTableNode,SwPaM & rPaM)887         void InsertedTablesManager::InsertTable(SwTableNode& rTableNode, SwPaM& rPaM)
888         {
889             if (!mbHasRoot)
890                 return;
891             //Associate this tablenode with this after position, replace an //old
892             //node association if necessary
893             maTables.emplace(
894                     std::unique_ptr<InsertedTableListener>(new InsertedTableListener(rTableNode)),
895                     &(rPaM.GetPoint()->nNode));
896         }
897     }
898 
899 }
900 
901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
902