summaryrefslogtreecommitdiffstats
path: root/drawinglayer/source
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drawinglayer/source/animation/animationtiming.cxx346
-rw-r--r--drawinglayer/source/attribute/fillgradientattribute.cxx186
-rw-r--r--drawinglayer/source/attribute/fillgraphicattribute.cxx156
-rw-r--r--drawinglayer/source/attribute/fillhatchattribute.cxx166
-rw-r--r--drawinglayer/source/attribute/fontattribute.cxx151
-rw-r--r--drawinglayer/source/attribute/lineattribute.cxx152
-rw-r--r--drawinglayer/source/attribute/linestartendattribute.cxx132
-rw-r--r--drawinglayer/source/attribute/materialattribute3d.cxx133
-rw-r--r--drawinglayer/source/attribute/sdrallattribute3d.cxx59
-rw-r--r--drawinglayer/source/attribute/sdrfillattribute.cxx165
-rw-r--r--drawinglayer/source/attribute/sdrfillgraphicattribute.cxx304
-rw-r--r--drawinglayer/source/attribute/sdrglowattribute.cxx37
-rw-r--r--drawinglayer/source/attribute/sdrlightattribute3d.cxx96
-rw-r--r--drawinglayer/source/attribute/sdrlightingattribute3d.cxx172
-rw-r--r--drawinglayer/source/attribute/sdrlineattribute.cxx181
-rw-r--r--drawinglayer/source/attribute/sdrlinestartendattribute.cxx187
-rw-r--r--drawinglayer/source/attribute/sdrobjectattribute3d.cxx184
-rw-r--r--drawinglayer/source/attribute/sdrsceneattribute3d.cxx147
-rw-r--r--drawinglayer/source/attribute/sdrshadowattribute.cxx151
-rw-r--r--drawinglayer/source/attribute/strokeattribute.cxx125
-rw-r--r--drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx183
-rw-r--r--drawinglayer/source/dumper/EnhancedShapeDumper.cxx1081
-rw-r--r--drawinglayer/source/dumper/EnhancedShapeDumper.hxx133
-rw-r--r--drawinglayer/source/dumper/XShapeDumper.cxx1986
-rw-r--r--drawinglayer/source/geometry/viewinformation2d.cxx295
-rw-r--r--drawinglayer/source/geometry/viewinformation3d.cxx366
-rw-r--r--drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx46
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx65
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx88
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx130
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx82
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx91
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx88
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx125
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx100
-rw-r--r--drawinglayer/source/primitive2d/Primitive2DContainer.cxx155
-rw-r--r--drawinglayer/source/primitive2d/Tools.cxx235
-rw-r--r--drawinglayer/source/primitive2d/animatedprimitive2d.cxx203
-rw-r--r--drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx94
-rw-r--r--drawinglayer/source/primitive2d/baseprimitive2d.cxx106
-rw-r--r--drawinglayer/source/primitive2d/bitmapprimitive2d.cxx77
-rw-r--r--drawinglayer/source/primitive2d/borderlineprimitive2d.cxx430
-rw-r--r--drawinglayer/source/primitive2d/controlprimitive2d.cxx348
-rw-r--r--drawinglayer/source/primitive2d/cropprimitive2d.cxx155
-rw-r--r--drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx102
-rw-r--r--drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx311
-rw-r--r--drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx146
-rw-r--r--drawinglayer/source/primitive2d/epsprimitive2d.cxx84
-rw-r--r--drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx334
-rw-r--r--drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx131
-rw-r--r--drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx198
-rw-r--r--drawinglayer/source/primitive2d/glowprimitive2d.cxx62
-rw-r--r--drawinglayer/source/primitive2d/graphicprimitive2d.cxx219
-rw-r--r--drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx697
-rw-r--r--drawinglayer/source/primitive2d/gridprimitive2d.cxx336
-rw-r--r--drawinglayer/source/primitive2d/groupprimitive2d.cxx75
-rw-r--r--drawinglayer/source/primitive2d/helplineprimitive2d.cxx186
-rw-r--r--drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx52
-rw-r--r--drawinglayer/source/primitive2d/invertprimitive2d.cxx43
-rw-r--r--drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx136
-rw-r--r--drawinglayer/source/primitive2d/maskprimitive2d.cxx62
-rw-r--r--drawinglayer/source/primitive2d/mediaprimitive2d.cxx148
-rw-r--r--drawinglayer/source/primitive2d/metafileprimitive2d.cxx135
-rw-r--r--drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx67
-rw-r--r--drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx60
-rw-r--r--drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx40
-rw-r--r--drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx150
-rw-r--r--drawinglayer/source/primitive2d/patternfillprimitive2d.cxx370
-rw-r--r--drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx77
-rw-r--r--drawinglayer/source/primitive2d/polygonprimitive2d.cxx611
-rw-r--r--drawinglayer/source/primitive2d/primitivetools2d.cxx127
-rw-r--r--drawinglayer/source/primitive2d/sceneprimitive2d.cxx689
-rw-r--r--drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx106
-rw-r--r--drawinglayer/source/primitive2d/shadowprimitive2d.cxx94
-rw-r--r--drawinglayer/source/primitive2d/softedgeprimitive2d.cxx73
-rw-r--r--drawinglayer/source/primitive2d/structuretagprimitive2d.cxx71
-rw-r--r--drawinglayer/source/primitive2d/svggradientprimitive2d.cxx1096
-rw-r--r--drawinglayer/source/primitive2d/textbreakuphelper.cxx276
-rw-r--r--drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx388
-rw-r--r--drawinglayer/source/primitive2d/texteffectprimitive2d.cxx245
-rw-r--r--drawinglayer/source/primitive2d/textenumsprimitive2d.cxx105
-rw-r--r--drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx152
-rw-r--r--drawinglayer/source/primitive2d/textlayoutdevice.cxx456
-rw-r--r--drawinglayer/source/primitive2d/textlineprimitive2d.cxx290
-rw-r--r--drawinglayer/source/primitive2d/textprimitive2d.cxx309
-rw-r--r--drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx259
-rw-r--r--drawinglayer/source/primitive2d/transformprimitive2d.cxx64
-rw-r--r--drawinglayer/source/primitive2d/transparenceprimitive2d.cxx57
-rw-r--r--drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx112
-rw-r--r--drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx257
-rw-r--r--drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx105
-rw-r--r--drawinglayer/source/primitive3d/Tools.cxx67
-rw-r--r--drawinglayer/source/primitive3d/baseprimitive3d.cxx183
-rw-r--r--drawinglayer/source/primitive3d/groupprimitive3d.cxx61
-rw-r--r--drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx310
-rw-r--r--drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx51
-rw-r--r--drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx64
-rw-r--r--drawinglayer/source/primitive3d/polygonprimitive3d.cxx148
-rw-r--r--drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx769
-rw-r--r--drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx64
-rw-r--r--drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx199
-rw-r--r--drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx324
-rw-r--r--drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx982
-rw-r--r--drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx502
-rw-r--r--drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx360
-rw-r--r--drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx176
-rw-r--r--drawinglayer/source/primitive3d/sdrprimitive3d.cxx106
-rw-r--r--drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx205
-rw-r--r--drawinglayer/source/primitive3d/shadowprimitive3d.cxx63
-rw-r--r--drawinglayer/source/primitive3d/textureprimitive3d.cxx183
-rw-r--r--drawinglayer/source/primitive3d/transformprimitive3d.cxx61
-rw-r--r--drawinglayer/source/processor2d/baseprocessor2d.cxx73
-rw-r--r--drawinglayer/source/processor2d/contourextractor2d.cxx193
-rw-r--r--drawinglayer/source/processor2d/getdigitlanguage.cxx31
-rw-r--r--drawinglayer/source/processor2d/getdigitlanguage.hxx22
-rw-r--r--drawinglayer/source/processor2d/helperwrongspellrenderer.cxx76
-rw-r--r--drawinglayer/source/processor2d/helperwrongspellrenderer.hxx47
-rw-r--r--drawinglayer/source/processor2d/hittestprocessor2d.cxx551
-rw-r--r--drawinglayer/source/processor2d/linegeometryextractor2d.cxx125
-rw-r--r--drawinglayer/source/processor2d/objectinfoextractor2d.cxx77
-rw-r--r--drawinglayer/source/processor2d/processor2dtools.cxx62
-rw-r--r--drawinglayer/source/processor2d/processorfromoutputdevice.cxx50
-rw-r--r--drawinglayer/source/processor2d/textaspolygonextractor2d.cxx231
-rw-r--r--drawinglayer/source/processor2d/vclhelperbufferdevice.cxx444
-rw-r--r--drawinglayer/source/processor2d/vclhelperbufferdevice.hxx51
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx2571
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx217
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx1351
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.hxx116
-rw-r--r--drawinglayer/source/processor2d/vclprocessor2d.cxx1480
-rw-r--r--drawinglayer/source/processor2d/vclprocessor2d.hxx119
-rw-r--r--drawinglayer/source/processor3d/baseprocessor3d.cxx63
-rw-r--r--drawinglayer/source/processor3d/cutfindprocessor3d.cxx192
-rw-r--r--drawinglayer/source/processor3d/defaultprocessor3d.cxx598
-rw-r--r--drawinglayer/source/processor3d/geometry2dextractor.cxx153
-rw-r--r--drawinglayer/source/processor3d/shadow3dextractor.cxx297
-rw-r--r--drawinglayer/source/processor3d/zbufferprocessor3d.cxx638
-rw-r--r--drawinglayer/source/texture/texture.cxx766
-rw-r--r--drawinglayer/source/texture/texture3d.cxx351
-rw-r--r--drawinglayer/source/tools/converters.cxx272
-rw-r--r--drawinglayer/source/tools/emfpbrush.cxx317
-rw-r--r--drawinglayer/source/tools/emfpbrush.hxx128
-rw-r--r--drawinglayer/source/tools/emfpcustomlinecap.cxx121
-rw-r--r--drawinglayer/source/tools/emfpcustomlinecap.hxx41
-rw-r--r--drawinglayer/source/tools/emfpfont.cxx76
-rw-r--r--drawinglayer/source/tools/emfpfont.hxx48
-rw-r--r--drawinglayer/source/tools/emfphelperdata.cxx2268
-rw-r--r--drawinglayer/source/tools/emfphelperdata.hxx270
-rw-r--r--drawinglayer/source/tools/emfpimage.cxx80
-rw-r--r--drawinglayer/source/tools/emfpimage.hxx48
-rw-r--r--drawinglayer/source/tools/emfpimageattributes.cxx73
-rw-r--r--drawinglayer/source/tools/emfpimageattributes.hxx33
-rw-r--r--drawinglayer/source/tools/emfplushelper.cxx45
-rw-r--r--drawinglayer/source/tools/emfppath.cxx226
-rw-r--r--drawinglayer/source/tools/emfppath.hxx43
-rw-r--r--drawinglayer/source/tools/emfppen.cxx382
-rw-r--r--drawinglayer/source/tools/emfppen.hxx130
-rw-r--r--drawinglayer/source/tools/emfpregion.cxx134
-rw-r--r--drawinglayer/source/tools/emfpregion.hxx50
-rw-r--r--drawinglayer/source/tools/emfpstringformat.cxx195
-rw-r--r--drawinglayer/source/tools/emfpstringformat.hxx104
-rw-r--r--drawinglayer/source/tools/primitive2dxmldump.cxx1182
-rw-r--r--drawinglayer/source/tools/wmfemfhelper.cxx3036
163 files changed, 45180 insertions, 0 deletions
diff --git a/drawinglayer/source/animation/animationtiming.cxx b/drawinglayer/source/animation/animationtiming.cxx
new file mode 100644
index 000000000..c1471e43b
--- /dev/null
+++ b/drawinglayer/source/animation/animationtiming.cxx
@@ -0,0 +1,346 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace drawinglayer::animation
+{
+
+
+ AnimationEntry::AnimationEntry()
+ {
+ }
+
+ AnimationEntry::~AnimationEntry()
+ {
+ }
+
+
+ AnimationEntryFixed::AnimationEntryFixed(double fDuration, double fState)
+ : mfDuration(fDuration),
+ mfState(fState)
+ {
+ }
+
+ AnimationEntryFixed::~AnimationEntryFixed()
+ {
+ }
+
+ std::unique_ptr<AnimationEntry> AnimationEntryFixed::clone() const
+ {
+ return std::make_unique<AnimationEntryFixed>(mfDuration, mfState);
+ }
+
+ bool AnimationEntryFixed::operator==(const AnimationEntry& rCandidate) const
+ {
+ const AnimationEntryFixed* pCompare = dynamic_cast< const AnimationEntryFixed* >(&rCandidate);
+
+ return (pCompare
+ && basegfx::fTools::equal(mfDuration, pCompare->mfDuration)
+ && basegfx::fTools::equal(mfState, pCompare->mfState));
+ }
+
+ double AnimationEntryFixed::getDuration() const
+ {
+ return mfDuration;
+ }
+
+ double AnimationEntryFixed::getStateAtTime(double /*fTime*/) const
+ {
+ return mfState;
+ }
+
+ double AnimationEntryFixed::getNextEventTime(double fTime) const
+ {
+ if(basegfx::fTools::less(fTime, mfDuration))
+ {
+ return mfDuration;
+ }
+ else
+ {
+ return 0.0;
+ }
+ }
+
+
+ AnimationEntryLinear::AnimationEntryLinear(double fDuration, double fFrequency, double fStart, double fStop)
+ : mfDuration(fDuration),
+ mfFrequency(fFrequency),
+ mfStart(fStart),
+ mfStop(fStop)
+ {
+ }
+
+ AnimationEntryLinear::~AnimationEntryLinear()
+ {
+ }
+
+ std::unique_ptr<AnimationEntry> AnimationEntryLinear::clone() const
+ {
+ return std::make_unique<AnimationEntryLinear>(mfDuration, mfFrequency, mfStart, mfStop);
+ }
+
+ bool AnimationEntryLinear::operator==(const AnimationEntry& rCandidate) const
+ {
+ const AnimationEntryLinear* pCompare = dynamic_cast< const AnimationEntryLinear* >(&rCandidate);
+
+ return (pCompare
+ && basegfx::fTools::equal(mfDuration, pCompare->mfDuration)
+ && basegfx::fTools::equal(mfStart, pCompare->mfStart)
+ && basegfx::fTools::equal(mfStop, pCompare->mfStop));
+ }
+
+ double AnimationEntryLinear::getDuration() const
+ {
+ return mfDuration;
+ }
+
+ double AnimationEntryLinear::getStateAtTime(double fTime) const
+ {
+ if(basegfx::fTools::more(mfDuration, 0.0))
+ {
+ const double fFactor(fTime / mfDuration);
+
+ if(fFactor > 1.0)
+ {
+ return mfStop;
+ }
+ else
+ {
+ return mfStart + ((mfStop - mfStart) * fFactor);
+ }
+ }
+ else
+ {
+ return mfStart;
+ }
+ }
+
+ double AnimationEntryLinear::getNextEventTime(double fTime) const
+ {
+ if(basegfx::fTools::less(fTime, mfDuration))
+ {
+ // use the simple solution: just add the frequency. More correct (but also more
+ // complicated) would be to calculate the slice of time we are in and when this
+ // slice will end. For the animations, this makes no quality difference.
+ fTime += mfFrequency;
+
+ if(basegfx::fTools::more(fTime, mfDuration))
+ {
+ fTime = mfDuration;
+ }
+
+ return fTime;
+ }
+ else
+ {
+ return 0.0;
+ }
+ }
+
+
+ AnimationEntryList::Entries::size_type AnimationEntryList::impGetIndexAtTime(double fTime, double &rfAddedTime) const
+ {
+ Entries::size_type nIndex(0);
+
+ while(nIndex < maEntries.size() && basegfx::fTools::lessOrEqual(rfAddedTime + maEntries[nIndex]->getDuration(), fTime))
+ {
+ rfAddedTime += maEntries[nIndex++]->getDuration();
+ }
+
+ return nIndex;
+ }
+
+ AnimationEntryList::AnimationEntryList()
+ : mfDuration(0.0)
+ {
+ }
+
+ AnimationEntryList::~AnimationEntryList()
+ {
+ }
+
+ std::unique_ptr<AnimationEntry> AnimationEntryList::clone() const
+ {
+ std::unique_ptr<AnimationEntryList> pNew(std::make_unique<AnimationEntryList>());
+
+ for(const auto &i : maEntries)
+ {
+ pNew->append(*i);
+ }
+
+ return pNew;
+ }
+
+ bool AnimationEntryList::operator==(const AnimationEntry& rCandidate) const
+ {
+ const AnimationEntryList* pCompare = dynamic_cast< const AnimationEntryList* >(&rCandidate);
+
+ if(pCompare && mfDuration == pCompare->mfDuration)
+ {
+ for(size_t a(0); a < maEntries.size(); a++)
+ {
+ if(!(*maEntries[a] == *pCompare->maEntries[a]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void AnimationEntryList::append(const AnimationEntry& rCandidate)
+ {
+ const double fDuration(rCandidate.getDuration());
+
+ if(!basegfx::fTools::equalZero(fDuration))
+ {
+ maEntries.push_back(rCandidate.clone());
+ mfDuration += fDuration;
+ }
+ }
+
+ double AnimationEntryList::getDuration() const
+ {
+ return mfDuration;
+ }
+
+ double AnimationEntryList::getStateAtTime(double fTime) const
+ {
+ if(!basegfx::fTools::equalZero(mfDuration))
+ {
+ double fAddedTime(0.0);
+ const auto nIndex(impGetIndexAtTime(fTime, fAddedTime));
+
+ if(nIndex < maEntries.size())
+ {
+ return maEntries[nIndex]->getStateAtTime(fTime - fAddedTime);
+ }
+ }
+
+ return 0.0;
+ }
+
+ double AnimationEntryList::getNextEventTime(double fTime) const
+ {
+ double fNewTime(0.0);
+
+ if(!basegfx::fTools::equalZero(mfDuration))
+ {
+ double fAddedTime(0.0);
+ const auto nIndex(impGetIndexAtTime(fTime, fAddedTime));
+
+ if(nIndex < maEntries.size())
+ {
+ fNewTime = maEntries[nIndex]->getNextEventTime(fTime - fAddedTime) + fAddedTime;
+ }
+ }
+
+ return fNewTime;
+ }
+
+
+ AnimationEntryLoop::AnimationEntryLoop(sal_uInt32 nRepeat)
+ : mnRepeat(nRepeat)
+ {
+ }
+
+ AnimationEntryLoop::~AnimationEntryLoop()
+ {
+ }
+
+ std::unique_ptr<AnimationEntry> AnimationEntryLoop::clone() const
+ {
+ std::unique_ptr<AnimationEntryLoop> pNew(std::make_unique<AnimationEntryLoop>(mnRepeat));
+
+ for(const auto &i : maEntries)
+ {
+ pNew->append(*i);
+ }
+
+ return pNew;
+ }
+
+ bool AnimationEntryLoop::operator==(const AnimationEntry& rCandidate) const
+ {
+ const AnimationEntryLoop* pCompare = dynamic_cast< const AnimationEntryLoop* >(&rCandidate);
+
+ return (pCompare
+ && mnRepeat == pCompare->mnRepeat
+ && AnimationEntryList::operator==(rCandidate));
+ }
+
+ double AnimationEntryLoop::getDuration() const
+ {
+ return (mfDuration * static_cast<double>(mnRepeat));
+ }
+
+ double AnimationEntryLoop::getStateAtTime(double fTime) const
+ {
+ if(mnRepeat && !basegfx::fTools::equalZero(mfDuration))
+ {
+ const sal_uInt32 nCurrentLoop(static_cast<sal_uInt32>(fTime / mfDuration));
+
+ if(nCurrentLoop > mnRepeat)
+ {
+ return 1.0;
+ }
+ else
+ {
+ const double fTimeAtLoopStart(static_cast<double>(nCurrentLoop) * mfDuration);
+ const double fRelativeTime(fTime - fTimeAtLoopStart);
+ return AnimationEntryList::getStateAtTime(fRelativeTime);
+ }
+ }
+
+ return 0.0;
+ }
+
+ double AnimationEntryLoop::getNextEventTime(double fTime) const
+ {
+ double fNewTime(0.0);
+
+ if(mnRepeat && !basegfx::fTools::equalZero(mfDuration))
+ {
+ const sal_uInt32 nCurrentLoop(static_cast<sal_uInt32>(fTime / mfDuration));
+
+ if(nCurrentLoop <= mnRepeat)
+ {
+ const double fTimeAtLoopStart(static_cast<double>(nCurrentLoop) * mfDuration);
+ const double fRelativeTime(fTime - fTimeAtLoopStart);
+ const double fNextEventAtLoop(AnimationEntryList::getNextEventTime(fRelativeTime));
+
+ if(!basegfx::fTools::equalZero(fNextEventAtLoop))
+ {
+ fNewTime = fNextEventAtLoop + fTimeAtLoopStart;
+ }
+ }
+ }
+
+ return fNewTime;
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx b/drawinglayer/source/attribute/fillgradientattribute.cxx
new file mode 100644
index 000000000..b8b06e20b
--- /dev/null
+++ b/drawinglayer/source/attribute/fillgradientattribute.cxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpFillGradientAttribute
+ {
+ public:
+ // data definitions
+ double mfBorder;
+ double mfOffsetX;
+ double mfOffsetY;
+ double mfAngle;
+ basegfx::BColor maStartColor;
+ basegfx::BColor maEndColor;
+ GradientStyle meStyle;
+ sal_uInt16 mnSteps;
+
+ ImpFillGradientAttribute(
+ GradientStyle eStyle,
+ double fBorder,
+ double fOffsetX,
+ double fOffsetY,
+ double fAngle,
+ const basegfx::BColor& rStartColor,
+ const basegfx::BColor& rEndColor,
+ sal_uInt16 nSteps)
+ : mfBorder(fBorder),
+ mfOffsetX(fOffsetX),
+ mfOffsetY(fOffsetY),
+ mfAngle(fAngle),
+ maStartColor(rStartColor),
+ maEndColor(rEndColor),
+ meStyle(eStyle),
+ mnSteps(nSteps)
+ {
+ }
+
+ ImpFillGradientAttribute()
+ : mfBorder(0.0),
+ mfOffsetX(0.0),
+ mfOffsetY(0.0),
+ mfAngle(0.0),
+ meStyle(GradientStyle::Linear),
+ mnSteps(0)
+ {
+ }
+
+ // data read access
+ GradientStyle getStyle() const { return meStyle; }
+ double getBorder() const { return mfBorder; }
+ double getOffsetX() const { return mfOffsetX; }
+ double getOffsetY() const { return mfOffsetY; }
+ double getAngle() const { return mfAngle; }
+ const basegfx::BColor& getStartColor() const { return maStartColor; }
+ const basegfx::BColor& getEndColor() const { return maEndColor; }
+ sal_uInt16 getSteps() const { return mnSteps; }
+
+ bool operator==(const ImpFillGradientAttribute& rCandidate) const
+ {
+ return (getStyle() == rCandidate.getStyle()
+ && getBorder() == rCandidate.getBorder()
+ && getOffsetX() == rCandidate.getOffsetX()
+ && getOffsetY() == rCandidate.getOffsetY()
+ && getAngle() == rCandidate.getAngle()
+ && getStartColor() == rCandidate.getStartColor()
+ && getEndColor() == rCandidate.getEndColor()
+ && getSteps() == rCandidate.getSteps());
+ }
+ };
+
+ namespace
+ {
+ FillGradientAttribute::ImplType& theGlobalDefault()
+ {
+ static FillGradientAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ FillGradientAttribute::FillGradientAttribute(
+ GradientStyle eStyle,
+ double fBorder,
+ double fOffsetX,
+ double fOffsetY,
+ double fAngle,
+ const basegfx::BColor& rStartColor,
+ const basegfx::BColor& rEndColor,
+ sal_uInt16 nSteps)
+ : mpFillGradientAttribute(ImpFillGradientAttribute(
+ eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rStartColor, rEndColor, nSteps))
+ {
+ }
+
+ FillGradientAttribute::FillGradientAttribute()
+ : mpFillGradientAttribute(theGlobalDefault())
+ {
+ }
+
+ FillGradientAttribute::FillGradientAttribute(const FillGradientAttribute&) = default;
+
+ FillGradientAttribute::FillGradientAttribute(FillGradientAttribute&&) = default;
+
+ FillGradientAttribute::~FillGradientAttribute() = default;
+
+ bool FillGradientAttribute::isDefault() const
+ {
+ return mpFillGradientAttribute.same_object(theGlobalDefault());
+ }
+
+ FillGradientAttribute& FillGradientAttribute::operator=(const FillGradientAttribute&) = default;
+
+ FillGradientAttribute& FillGradientAttribute::operator=(FillGradientAttribute&&) = default;
+
+ bool FillGradientAttribute::operator==(const FillGradientAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpFillGradientAttribute == mpFillGradientAttribute;
+ }
+
+ const basegfx::BColor& FillGradientAttribute::getStartColor() const
+ {
+ return mpFillGradientAttribute->getStartColor();
+ }
+
+ const basegfx::BColor& FillGradientAttribute::getEndColor() const
+ {
+ return mpFillGradientAttribute->getEndColor();
+ }
+
+ double FillGradientAttribute::getBorder() const
+ {
+ return mpFillGradientAttribute->getBorder();
+ }
+
+ double FillGradientAttribute::getOffsetX() const
+ {
+ return mpFillGradientAttribute->getOffsetX();
+ }
+
+ double FillGradientAttribute::getOffsetY() const
+ {
+ return mpFillGradientAttribute->getOffsetY();
+ }
+
+ double FillGradientAttribute::getAngle() const
+ {
+ return mpFillGradientAttribute->getAngle();
+ }
+
+ GradientStyle FillGradientAttribute::getStyle() const
+ {
+ return mpFillGradientAttribute->getStyle();
+ }
+
+ sal_uInt16 FillGradientAttribute::getSteps() const
+ {
+ return mpFillGradientAttribute->getSteps();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/fillgraphicattribute.cxx b/drawinglayer/source/attribute/fillgraphicattribute.cxx
new file mode 100644
index 000000000..b36520d4f
--- /dev/null
+++ b/drawinglayer/source/attribute/fillgraphicattribute.cxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <vcl/graph.hxx>
+
+namespace drawinglayer::attribute
+{
+ class ImpFillGraphicAttribute
+ {
+ public:
+ // data definitions
+ Graphic maGraphic;
+ basegfx::B2DRange maGraphicRange;
+
+ bool mbTiling : 1;
+
+ // tiling definitions, offsets in X/Y in percent for each 2nd row.
+ // If both are set, Y is ignored (X has precedence)
+ double mfOffsetX;
+ double mfOffsetY;
+
+ ImpFillGraphicAttribute(
+ const Graphic& rGraphic,
+ const basegfx::B2DRange& rGraphicRange,
+ bool bTiling,
+ double fOffsetX,
+ double fOffsetY)
+ : maGraphic(rGraphic),
+ maGraphicRange(rGraphicRange),
+ mbTiling(bTiling),
+ mfOffsetX(fOffsetX),
+ mfOffsetY(fOffsetY)
+ {
+ // access once to ensure that the buffered bitmap exists, else
+ // the SolarMutex may be needed to create it. This may not be
+ // available when a renderer works with multi-threading.
+ // When changing this, please check if it is still possible to
+ // use a metafile as texture for a 3D object
+ maGraphic.GetBitmapEx();
+ }
+
+ ImpFillGraphicAttribute()
+ : mbTiling(false),
+ mfOffsetX(0.0),
+ mfOffsetY(0.0)
+ {
+ }
+
+ // data read access
+ const Graphic& getGraphic() const { return maGraphic; }
+ const basegfx::B2DRange& getGraphicRange() const { return maGraphicRange; }
+ bool getTiling() const { return mbTiling; }
+ double getOffsetX() const { return mfOffsetX; }
+ double getOffsetY() const { return mfOffsetY; }
+
+ bool operator==(const ImpFillGraphicAttribute& rCandidate) const
+ {
+ return (getGraphic() == rCandidate.getGraphic()
+ && getGraphicRange() == rCandidate.getGraphicRange()
+ && getTiling() == rCandidate.getTiling()
+ && getOffsetX() == rCandidate.getOffsetX()
+ && getOffsetY() == rCandidate.getOffsetY());
+ }
+ };
+
+ namespace
+ {
+ FillGraphicAttribute::ImplType& theGlobalDefault()
+ {
+ static FillGraphicAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ FillGraphicAttribute::FillGraphicAttribute(
+ const Graphic& rGraphic,
+ const basegfx::B2DRange& rGraphicRange,
+ bool bTiling,
+ double fOffsetX,
+ double fOffsetY)
+ : mpFillGraphicAttribute(ImpFillGraphicAttribute(
+ rGraphic, rGraphicRange, bTiling,
+ std::clamp(fOffsetX, 0.0, 1.0),
+ std::clamp(fOffsetY, 0.0, 1.0)))
+ {
+ }
+
+ FillGraphicAttribute::FillGraphicAttribute(const FillGraphicAttribute&) = default;
+
+ FillGraphicAttribute::~FillGraphicAttribute() = default;
+
+ bool FillGraphicAttribute::isDefault() const
+ {
+ return mpFillGraphicAttribute.same_object(theGlobalDefault());
+ }
+
+ FillGraphicAttribute& FillGraphicAttribute::operator=(const FillGraphicAttribute&) = default;
+
+ bool FillGraphicAttribute::operator==(const FillGraphicAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpFillGraphicAttribute == mpFillGraphicAttribute;
+ }
+
+ const Graphic& FillGraphicAttribute::getGraphic() const
+ {
+ return mpFillGraphicAttribute->getGraphic();
+ }
+
+ const basegfx::B2DRange& FillGraphicAttribute::getGraphicRange() const
+ {
+ return mpFillGraphicAttribute->getGraphicRange();
+ }
+
+ bool FillGraphicAttribute::getTiling() const
+ {
+ return mpFillGraphicAttribute->getTiling();
+ }
+
+ double FillGraphicAttribute::getOffsetX() const
+ {
+ return mpFillGraphicAttribute->getOffsetX();
+ }
+
+ double FillGraphicAttribute::getOffsetY() const
+ {
+ return mpFillGraphicAttribute->getOffsetY();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/fillhatchattribute.cxx b/drawinglayer/source/attribute/fillhatchattribute.cxx
new file mode 100644
index 000000000..bc892a024
--- /dev/null
+++ b/drawinglayer/source/attribute/fillhatchattribute.cxx
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/fillhatchattribute.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpFillHatchAttribute
+ {
+ public:
+ // data definitions
+ HatchStyle meStyle;
+ double mfDistance;
+ double mfAngle;
+ basegfx::BColor maColor;
+ sal_uInt32 mnMinimalDiscreteDistance;
+
+ bool mbFillBackground : 1;
+
+ ImpFillHatchAttribute(
+ HatchStyle eStyle,
+ double fDistance,
+ double fAngle,
+ const basegfx::BColor& rColor,
+ sal_uInt32 nMinimalDiscreteDistance,
+ bool bFillBackground)
+ : meStyle(eStyle),
+ mfDistance(fDistance),
+ mfAngle(fAngle),
+ maColor(rColor),
+ mnMinimalDiscreteDistance(nMinimalDiscreteDistance),
+ mbFillBackground(bFillBackground)
+ {
+ }
+
+ ImpFillHatchAttribute()
+ : meStyle(HatchStyle::Single),
+ mfDistance(0.0),
+ mfAngle(0.0),
+ mnMinimalDiscreteDistance(3), // same as VCL
+ mbFillBackground(false)
+ {
+ }
+
+ // data read access
+ HatchStyle getStyle() const { return meStyle; }
+ double getDistance() const { return mfDistance; }
+ double getAngle() const { return mfAngle; }
+ const basegfx::BColor& getColor() const { return maColor; }
+ sal_uInt32 getMinimalDiscreteDistance() const { return mnMinimalDiscreteDistance; }
+ bool isFillBackground() const { return mbFillBackground; }
+
+ bool operator==(const ImpFillHatchAttribute& rCandidate) const
+ {
+ return (getStyle() == rCandidate.getStyle()
+ && getDistance() == rCandidate.getDistance()
+ && getAngle() == rCandidate.getAngle()
+ && getColor() == rCandidate.getColor()
+ && getMinimalDiscreteDistance() == rCandidate.getMinimalDiscreteDistance()
+ && isFillBackground() == rCandidate.isFillBackground());
+ }
+ };
+
+ namespace
+ {
+ FillHatchAttribute::ImplType& theGlobalDefault()
+ {
+ static FillHatchAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ FillHatchAttribute::FillHatchAttribute(
+ HatchStyle eStyle,
+ double fDistance,
+ double fAngle,
+ const basegfx::BColor& rColor,
+ sal_uInt32 nMinimalDiscreteDistance,
+ bool bFillBackground)
+ : mpFillHatchAttribute(ImpFillHatchAttribute(
+ eStyle, fDistance, fAngle, rColor,
+ nMinimalDiscreteDistance, bFillBackground))
+ {
+ }
+
+ FillHatchAttribute::FillHatchAttribute()
+ : mpFillHatchAttribute(theGlobalDefault())
+ {
+ }
+
+ FillHatchAttribute::FillHatchAttribute(const FillHatchAttribute&) = default;
+
+ FillHatchAttribute::FillHatchAttribute(FillHatchAttribute&&) = default;
+
+ FillHatchAttribute::~FillHatchAttribute() = default;
+
+ bool FillHatchAttribute::isDefault() const
+ {
+ return mpFillHatchAttribute.same_object(theGlobalDefault());
+ }
+
+ FillHatchAttribute& FillHatchAttribute::operator=(const FillHatchAttribute&) = default;
+
+ FillHatchAttribute& FillHatchAttribute::operator=(FillHatchAttribute&&) = default;
+
+ bool FillHatchAttribute::operator==(const FillHatchAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpFillHatchAttribute == mpFillHatchAttribute;
+ }
+
+ // data read access
+ HatchStyle FillHatchAttribute::getStyle() const
+ {
+ return mpFillHatchAttribute->getStyle();
+ }
+
+ double FillHatchAttribute::getDistance() const
+ {
+ return mpFillHatchAttribute->getDistance();
+ }
+
+ double FillHatchAttribute::getAngle() const
+ {
+ return mpFillHatchAttribute->getAngle();
+ }
+
+ const basegfx::BColor& FillHatchAttribute::getColor() const
+ {
+ return mpFillHatchAttribute->getColor();
+ }
+
+ sal_uInt32 FillHatchAttribute::getMinimalDiscreteDistance() const
+ {
+ return mpFillHatchAttribute->getMinimalDiscreteDistance();
+ }
+
+ bool FillHatchAttribute::isFillBackground() const
+ {
+ return mpFillHatchAttribute->isFillBackground();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/fontattribute.cxx b/drawinglayer/source/attribute/fontattribute.cxx
new file mode 100644
index 000000000..8ae3836d8
--- /dev/null
+++ b/drawinglayer/source/attribute/fontattribute.cxx
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <rtl/ustring.hxx>
+
+namespace drawinglayer::attribute
+{
+class ImpFontAttribute
+{
+public:
+ /// core data
+ OUString maFamilyName; // Font Family Name
+ OUString maStyleName; // Font Style Name
+ sal_uInt16 mnWeight; // Font weight
+
+ bool mbSymbol : 1; // Symbol Font Flag
+ bool mbVertical : 1; // Vertical Text Flag
+ bool mbItalic : 1; // Italic Flag
+ bool mbOutline : 1; // Outline Flag
+ bool mbRTL : 1; // RTL Flag
+ bool mbBiDiStrong : 1; // BiDi Flag
+ bool mbMonospaced : 1;
+
+ ImpFontAttribute(const OUString& rFamilyName, const OUString& rStyleName, sal_uInt16 nWeight,
+ bool bSymbol, bool bVertical, bool bItalic, bool bMonospaced, bool bOutline,
+ bool bRTL, bool bBiDiStrong)
+ : maFamilyName(rFamilyName)
+ , maStyleName(rStyleName)
+ , mnWeight(nWeight)
+ , mbSymbol(bSymbol)
+ , mbVertical(bVertical)
+ , mbItalic(bItalic)
+ , mbOutline(bOutline)
+ , mbRTL(bRTL)
+ , mbBiDiStrong(bBiDiStrong)
+ , mbMonospaced(bMonospaced)
+ {
+ }
+
+ ImpFontAttribute()
+ : mnWeight(0)
+ , mbSymbol(false)
+ , mbVertical(false)
+ , mbItalic(false)
+ , mbOutline(false)
+ , mbRTL(false)
+ , mbBiDiStrong(false)
+ , mbMonospaced(false)
+ {
+ }
+
+ // data read access
+ const OUString& getFamilyName() const { return maFamilyName; }
+ const OUString& getStyleName() const { return maStyleName; }
+ sal_uInt16 getWeight() const { return mnWeight; }
+ bool getSymbol() const { return mbSymbol; }
+ bool getVertical() const { return mbVertical; }
+ bool getItalic() const { return mbItalic; }
+ bool getOutline() const { return mbOutline; }
+ bool getRTL() const { return mbRTL; }
+ bool getBiDiStrong() const { return mbBiDiStrong; }
+ bool getMonospaced() const { return mbMonospaced; }
+
+ bool operator==(const ImpFontAttribute& rCompare) const
+ {
+ return (getFamilyName() == rCompare.getFamilyName()
+ && getStyleName() == rCompare.getStyleName() && getWeight() == rCompare.getWeight()
+ && getSymbol() == rCompare.getSymbol() && getVertical() == rCompare.getVertical()
+ && getItalic() == rCompare.getItalic() && getOutline() == rCompare.getOutline()
+ && getRTL() == rCompare.getRTL() && getBiDiStrong() == rCompare.getBiDiStrong()
+ && getMonospaced() == rCompare.getMonospaced());
+ }
+};
+
+namespace
+{
+FontAttribute::ImplType& theGlobalDefault()
+{
+ static FontAttribute::ImplType SINGLETON;
+ return SINGLETON;
+}
+}
+
+FontAttribute::FontAttribute(const OUString& rFamilyName, const OUString& rStyleName,
+ sal_uInt16 nWeight, bool bSymbol, bool bVertical, bool bItalic,
+ bool bMonospaced, bool bOutline, bool bRTL, bool bBiDiStrong)
+ : mpFontAttribute(ImpFontAttribute(rFamilyName, rStyleName, nWeight, bSymbol, bVertical,
+ bItalic, bMonospaced, bOutline, bRTL, bBiDiStrong))
+{
+}
+
+FontAttribute::FontAttribute()
+ : mpFontAttribute(theGlobalDefault())
+{
+}
+
+FontAttribute::FontAttribute(const FontAttribute&) = default;
+
+FontAttribute::FontAttribute(FontAttribute&&) = default;
+
+FontAttribute::~FontAttribute() = default;
+
+FontAttribute& FontAttribute::operator=(const FontAttribute&) = default;
+
+FontAttribute& FontAttribute::operator=(FontAttribute&&) = default;
+
+bool FontAttribute::operator==(const FontAttribute& rCandidate) const
+{
+ return rCandidate.mpFontAttribute == mpFontAttribute;
+}
+
+const OUString& FontAttribute::getFamilyName() const { return mpFontAttribute->getFamilyName(); }
+
+const OUString& FontAttribute::getStyleName() const { return mpFontAttribute->getStyleName(); }
+
+sal_uInt16 FontAttribute::getWeight() const { return mpFontAttribute->getWeight(); }
+
+bool FontAttribute::getSymbol() const { return mpFontAttribute->getSymbol(); }
+
+bool FontAttribute::getVertical() const { return mpFontAttribute->getVertical(); }
+
+bool FontAttribute::getItalic() const { return mpFontAttribute->getItalic(); }
+
+bool FontAttribute::getOutline() const { return mpFontAttribute->getOutline(); }
+
+bool FontAttribute::getRTL() const { return mpFontAttribute->getRTL(); }
+
+bool FontAttribute::getBiDiStrong() const { return mpFontAttribute->getBiDiStrong(); }
+
+bool FontAttribute::getMonospaced() const { return mpFontAttribute->getMonospaced(); }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/lineattribute.cxx b/drawinglayer/source/attribute/lineattribute.cxx
new file mode 100644
index 000000000..d4f2418c1
--- /dev/null
+++ b/drawinglayer/source/attribute/lineattribute.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpLineAttribute
+ {
+ public:
+ // data definitions
+ basegfx::BColor maColor; // color
+ double mfWidth; // absolute line width
+ basegfx::B2DLineJoin meLineJoin; // type of LineJoin
+ css::drawing::LineCap meLineCap; // BUTT, ROUND, or SQUARE
+ double mfMiterMinimumAngle; // as needed for createAreaGeometry
+
+ ImpLineAttribute(
+ const basegfx::BColor& rColor,
+ double fWidth,
+ basegfx::B2DLineJoin aB2DLineJoin,
+ css::drawing::LineCap aLineCap,
+ double fMiterMinimumAngle)
+ : maColor(rColor),
+ mfWidth(fWidth),
+ meLineJoin(aB2DLineJoin),
+ meLineCap(aLineCap),
+ mfMiterMinimumAngle(fMiterMinimumAngle)
+ {
+ }
+
+ ImpLineAttribute()
+ : mfWidth(0.0),
+ meLineJoin(basegfx::B2DLineJoin::Round),
+ meLineCap(css::drawing::LineCap_BUTT),
+ mfMiterMinimumAngle(basegfx::deg2rad(15.0))
+ {
+ }
+
+ // data read access
+ const basegfx::BColor& getColor() const { return maColor; }
+ double getWidth() const { return mfWidth; }
+ basegfx::B2DLineJoin getLineJoin() const { return meLineJoin; }
+ css::drawing::LineCap getLineCap() const { return meLineCap; }
+ double getMiterMinimumAngle() const { return mfMiterMinimumAngle; }
+
+ bool operator==(const ImpLineAttribute& rCandidate) const
+ {
+ return (getColor() == rCandidate.getColor()
+ && getWidth() == rCandidate.getWidth()
+ && getLineJoin() == rCandidate.getLineJoin()
+ && getLineCap() == rCandidate.getLineCap()
+ && getMiterMinimumAngle() == rCandidate.getMiterMinimumAngle());
+ }
+ };
+
+ namespace
+ {
+ LineAttribute::ImplType& theGlobalDefault()
+ {
+ static LineAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ LineAttribute::LineAttribute(
+ const basegfx::BColor& rColor,
+ double fWidth,
+ basegfx::B2DLineJoin aB2DLineJoin,
+ css::drawing::LineCap aLineCap,
+ double fMiterMinimumAngle)
+ : mpLineAttribute(
+ ImpLineAttribute(
+ rColor,
+ fWidth,
+ aB2DLineJoin,
+ aLineCap,
+ fMiterMinimumAngle))
+ {
+ }
+
+ LineAttribute::LineAttribute()
+ : mpLineAttribute(theGlobalDefault())
+ {
+ }
+
+ LineAttribute::LineAttribute(const LineAttribute&) = default;
+
+ LineAttribute::~LineAttribute() = default;
+
+ bool LineAttribute::isDefault() const
+ {
+ return mpLineAttribute.same_object(theGlobalDefault());
+ }
+
+ LineAttribute& LineAttribute::operator=(const LineAttribute&) = default;
+
+ bool LineAttribute::operator==(const LineAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpLineAttribute == mpLineAttribute;
+ }
+
+ const basegfx::BColor& LineAttribute::getColor() const
+ {
+ return mpLineAttribute->getColor();
+ }
+
+ double LineAttribute::getWidth() const
+ {
+ return mpLineAttribute->getWidth();
+ }
+
+ basegfx::B2DLineJoin LineAttribute::getLineJoin() const
+ {
+ return mpLineAttribute->getLineJoin();
+ }
+
+ css::drawing::LineCap LineAttribute::getLineCap() const
+ {
+ return mpLineAttribute->getLineCap();
+ }
+
+ double LineAttribute::getMiterMinimumAngle() const
+ {
+ return mpLineAttribute->getMiterMinimumAngle();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/linestartendattribute.cxx b/drawinglayer/source/attribute/linestartendattribute.cxx
new file mode 100644
index 000000000..57a680db3
--- /dev/null
+++ b/drawinglayer/source/attribute/linestartendattribute.cxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/linestartendattribute.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpLineStartEndAttribute
+ {
+ public:
+ // data definitions
+ double mfWidth; // absolute line StartEndGeometry base width
+ basegfx::B2DPolyPolygon maPolyPolygon; // the StartEndGeometry PolyPolygon
+
+ bool mbCentered : 1; // use centered to lineStart/End point?
+
+ ImpLineStartEndAttribute(
+ double fWidth,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ bool bCentered)
+ : mfWidth(fWidth),
+ maPolyPolygon(rPolyPolygon),
+ mbCentered(bCentered)
+ {
+ }
+
+ ImpLineStartEndAttribute()
+ : mfWidth(0.0),
+ mbCentered(false)
+ {
+ }
+
+ // data read access
+ double getWidth() const { return mfWidth; }
+ const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; }
+ bool isCentered() const { return mbCentered; }
+
+ bool operator==(const ImpLineStartEndAttribute& rCandidate) const
+ {
+ return (basegfx::fTools::equal(getWidth(), rCandidate.getWidth())
+ && getB2DPolyPolygon() == rCandidate.getB2DPolyPolygon()
+ && isCentered() == rCandidate.isCentered());
+ }
+ };
+
+ namespace
+ {
+ LineStartEndAttribute::ImplType& theGlobalDefault()
+ {
+ static LineStartEndAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ LineStartEndAttribute::LineStartEndAttribute(
+ double fWidth,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ bool bCentered)
+ : mpLineStartEndAttribute(ImpLineStartEndAttribute(
+ fWidth, rPolyPolygon, bCentered))
+ {
+ }
+
+ LineStartEndAttribute::LineStartEndAttribute()
+ : mpLineStartEndAttribute(theGlobalDefault())
+ {
+ }
+
+ LineStartEndAttribute::LineStartEndAttribute(const LineStartEndAttribute&) = default;
+
+ LineStartEndAttribute::~LineStartEndAttribute() = default;
+
+ bool LineStartEndAttribute::isDefault() const
+ {
+ return mpLineStartEndAttribute.same_object(theGlobalDefault());
+ }
+
+ LineStartEndAttribute& LineStartEndAttribute::operator=(const LineStartEndAttribute&) = default;
+
+ bool LineStartEndAttribute::operator==(const LineStartEndAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpLineStartEndAttribute == mpLineStartEndAttribute;
+ }
+
+ double LineStartEndAttribute::getWidth() const
+ {
+ return mpLineStartEndAttribute->getWidth();
+ }
+
+ const basegfx::B2DPolyPolygon& LineStartEndAttribute::getB2DPolyPolygon() const
+ {
+ return mpLineStartEndAttribute->getB2DPolyPolygon();
+ }
+
+ bool LineStartEndAttribute::isCentered() const
+ {
+ return mpLineStartEndAttribute->isCentered();
+ }
+
+ bool LineStartEndAttribute::isActive() const
+ {
+ return (0.0 != getWidth()
+ && 0 != getB2DPolyPolygon().count()
+ && 0 != getB2DPolyPolygon().getB2DPolygon(0).count());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/materialattribute3d.cxx b/drawinglayer/source/attribute/materialattribute3d.cxx
new file mode 100644
index 000000000..7206ad54b
--- /dev/null
+++ b/drawinglayer/source/attribute/materialattribute3d.cxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpMaterialAttribute3D
+ {
+ public:
+ // materialAttribute3D definitions
+ basegfx::BColor maColor; // object color
+ basegfx::BColor maSpecular; // material specular color
+ basegfx::BColor maEmission; // material emissive color
+ sal_uInt16 mnSpecularIntensity; // material specular intensity [0..128]
+
+ ImpMaterialAttribute3D(const basegfx::BColor& rColor, const basegfx::BColor& rSpecular, const basegfx::BColor& rEmission, sal_uInt16 nSpecularIntensity)
+ : maColor(rColor),
+ maSpecular(rSpecular),
+ maEmission(rEmission),
+ mnSpecularIntensity(nSpecularIntensity)
+ {
+ }
+
+ explicit ImpMaterialAttribute3D(const basegfx::BColor& rColor)
+ : maColor(rColor),
+ maSpecular(1.0, 1.0, 1.0),
+ mnSpecularIntensity(15)
+ {
+ }
+
+ ImpMaterialAttribute3D()
+ : mnSpecularIntensity(0)
+ {
+ }
+
+ // data read access
+ const basegfx::BColor& getColor() const { return maColor; }
+ const basegfx::BColor& getSpecular() const { return maSpecular; }
+ const basegfx::BColor& getEmission() const { return maEmission; }
+ sal_uInt16 getSpecularIntensity() const { return mnSpecularIntensity; }
+
+ bool operator==(const ImpMaterialAttribute3D& rCandidate) const
+ {
+ return (getColor() == rCandidate.getColor()
+ && getSpecular() == rCandidate.getSpecular()
+ && getEmission() == rCandidate.getEmission()
+ && getSpecularIntensity() == rCandidate.getSpecularIntensity());
+ }
+ };
+
+ namespace
+ {
+ MaterialAttribute3D::ImplType& theGlobalDefault()
+ {
+ static MaterialAttribute3D::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ MaterialAttribute3D::MaterialAttribute3D(
+ const basegfx::BColor& rColor,
+ const basegfx::BColor& rSpecular,
+ const basegfx::BColor& rEmission,
+ sal_uInt16 nSpecularIntensity)
+ : mpMaterialAttribute3D(ImpMaterialAttribute3D(
+ rColor, rSpecular, rEmission, nSpecularIntensity))
+ {
+ }
+
+ MaterialAttribute3D::MaterialAttribute3D(
+ const basegfx::BColor& rColor)
+ : mpMaterialAttribute3D(ImpMaterialAttribute3D(rColor))
+ {
+ }
+
+ MaterialAttribute3D::MaterialAttribute3D()
+ : mpMaterialAttribute3D(theGlobalDefault())
+ {
+ }
+
+ MaterialAttribute3D::MaterialAttribute3D(const MaterialAttribute3D&) = default;
+
+ MaterialAttribute3D::~MaterialAttribute3D() = default;
+
+ MaterialAttribute3D& MaterialAttribute3D::operator=(const MaterialAttribute3D&) = default;
+
+ bool MaterialAttribute3D::operator==(const MaterialAttribute3D& rCandidate) const
+ {
+ return rCandidate.mpMaterialAttribute3D == mpMaterialAttribute3D;
+ }
+
+ const basegfx::BColor& MaterialAttribute3D::getColor() const
+ {
+ return mpMaterialAttribute3D->getColor();
+ }
+
+ const basegfx::BColor& MaterialAttribute3D::getSpecular() const
+ {
+ return mpMaterialAttribute3D->getSpecular();
+ }
+
+ const basegfx::BColor& MaterialAttribute3D::getEmission() const
+ {
+ return mpMaterialAttribute3D->getEmission();
+ }
+
+ sal_uInt16 MaterialAttribute3D::getSpecularIntensity() const
+ {
+ return mpMaterialAttribute3D->getSpecularIntensity();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrallattribute3d.cxx b/drawinglayer/source/attribute/sdrallattribute3d.cxx
new file mode 100644
index 000000000..079f65579
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrallattribute3d.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrallattribute3d.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ SdrLineFillShadowAttribute3D::SdrLineFillShadowAttribute3D(
+ const SdrLineAttribute& rLine,
+ const SdrFillAttribute& rFill,
+ const SdrLineStartEndAttribute& rLineStartEnd,
+ const SdrShadowAttribute& rShadow,
+ const FillGradientAttribute& rFillFloatTransGradient)
+ : maLine(rLine),
+ maFill(rFill),
+ maLineStartEnd(rLineStartEnd),
+ maShadow(rShadow),
+ maFillFloatTransGradient(rFillFloatTransGradient)
+ {
+ }
+
+ SdrLineFillShadowAttribute3D::SdrLineFillShadowAttribute3D()
+ : maLine(),
+ maFill(),
+ maLineStartEnd(),
+ maShadow(),
+ maFillFloatTransGradient()
+ {
+ }
+
+ bool SdrLineFillShadowAttribute3D::operator==(const SdrLineFillShadowAttribute3D& rCandidate) const
+ {
+ return(getLine() == rCandidate.getLine()
+ && getFill() == rCandidate.getFill()
+ && maLineStartEnd == rCandidate.maLineStartEnd
+ && getShadow() == rCandidate.getShadow()
+ && getFillFloatTransGradient() == rCandidate.getFillFloatTransGradient());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrfillattribute.cxx b/drawinglayer/source/attribute/sdrfillattribute.cxx
new file mode 100644
index 000000000..131061099
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrfillattribute.cxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <drawinglayer/attribute/fillhatchattribute.hxx>
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrFillAttribute
+ {
+ public:
+ // fill definitions
+ double mfTransparence; // [0.0 .. 1.0], 0.0==no transp.
+ basegfx::BColor maColor; // fill color
+ FillGradientAttribute maGradient; // fill gradient (if used)
+ FillHatchAttribute maHatch; // fill hatch (if used)
+ SdrFillGraphicAttribute maFillGraphic; // fill graphic (if used)
+
+ public:
+ ImpSdrFillAttribute(
+ double fTransparence,
+ const basegfx::BColor& rColor,
+ const FillGradientAttribute& rGradient,
+ const FillHatchAttribute& rHatch,
+ const SdrFillGraphicAttribute& rFillGraphic)
+ : mfTransparence(fTransparence),
+ maColor(rColor),
+ maGradient(rGradient),
+ maHatch(rHatch),
+ maFillGraphic(rFillGraphic)
+ {
+ }
+
+ ImpSdrFillAttribute()
+ : mfTransparence(0.0)
+ {
+ }
+
+ // data read access
+ double getTransparence() const { return mfTransparence; }
+ const basegfx::BColor& getColor() const { return maColor; }
+ const FillGradientAttribute& getGradient() const { return maGradient; }
+ const FillHatchAttribute& getHatch() const { return maHatch; }
+ const SdrFillGraphicAttribute& getFillGraphic() const { return maFillGraphic; }
+
+ // compare operator
+ bool operator==(const ImpSdrFillAttribute& rCandidate) const
+ {
+ return(getTransparence() == rCandidate.getTransparence()
+ && getColor() == rCandidate.getColor()
+ && getGradient() == rCandidate.getGradient()
+ && getHatch() == rCandidate.getHatch()
+ && getFillGraphic() == rCandidate.getFillGraphic());
+ }
+ };
+
+ namespace
+ {
+ SdrFillAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrFillAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ SdrFillAttribute::ImplType& slideBackgroundFillGlobalDefault()
+ {
+ static SdrFillAttribute::ImplType SINGLETON2;
+ return SINGLETON2;
+ }
+ }
+
+ SdrFillAttribute::SdrFillAttribute(
+ double fTransparence,
+ const basegfx::BColor& rColor,
+ const FillGradientAttribute& rGradient,
+ const FillHatchAttribute& rHatch,
+ const SdrFillGraphicAttribute& rFillGraphic)
+ : mpSdrFillAttribute(ImpSdrFillAttribute(
+ fTransparence, rColor, rGradient, rHatch, rFillGraphic))
+ {
+ }
+
+ SdrFillAttribute::SdrFillAttribute(bool bSlideBackgroundFill)
+ : mpSdrFillAttribute(bSlideBackgroundFill
+ ? slideBackgroundFillGlobalDefault()
+ : theGlobalDefault())
+ {
+ }
+
+ SdrFillAttribute::SdrFillAttribute(const SdrFillAttribute&) = default;
+
+ SdrFillAttribute::SdrFillAttribute(SdrFillAttribute&&) = default;
+
+ SdrFillAttribute::~SdrFillAttribute() = default;
+
+ bool SdrFillAttribute::isDefault() const
+ {
+ return mpSdrFillAttribute.same_object(theGlobalDefault());
+ }
+
+ bool SdrFillAttribute::isSlideBackgroundFill() const
+ {
+ return mpSdrFillAttribute.same_object(slideBackgroundFillGlobalDefault());
+ }
+
+ SdrFillAttribute& SdrFillAttribute::operator=(const SdrFillAttribute&) = default;
+
+ SdrFillAttribute& SdrFillAttribute::operator=(SdrFillAttribute&&) = default;
+
+ bool SdrFillAttribute::operator==(const SdrFillAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrFillAttribute == mpSdrFillAttribute;
+ }
+
+ double SdrFillAttribute::getTransparence() const
+ {
+ return mpSdrFillAttribute->getTransparence();
+ }
+
+ const basegfx::BColor& SdrFillAttribute::getColor() const
+ {
+ return mpSdrFillAttribute->getColor();
+ }
+
+ const FillGradientAttribute& SdrFillAttribute::getGradient() const
+ {
+ return mpSdrFillAttribute->getGradient();
+ }
+
+ const FillHatchAttribute& SdrFillAttribute::getHatch() const
+ {
+ return mpSdrFillAttribute->getHatch();
+ }
+
+ const SdrFillGraphicAttribute& SdrFillAttribute::getFillGraphic() const
+ {
+ return mpSdrFillAttribute->getFillGraphic();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx b/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx
new file mode 100644
index 000000000..14f53cf03
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <vcl/graph.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrFillGraphicAttribute
+ {
+ public:
+ // data definitions
+ Graphic maFillGraphic;
+ basegfx::B2DVector maGraphicLogicSize;
+ basegfx::B2DVector maSize;
+ basegfx::B2DVector maOffset;
+ basegfx::B2DVector maOffsetPosition;
+ basegfx::B2DVector maRectPoint;
+
+ bool mbTiling : 1;
+ bool mbStretch : 1;
+ bool mbLogSize : 1;
+
+ ImpSdrFillGraphicAttribute(
+ const Graphic& rFillGraphic,
+ const basegfx::B2DVector& rGraphicLogicSize,
+ const basegfx::B2DVector& rSize,
+ const basegfx::B2DVector& rOffset,
+ const basegfx::B2DVector& rOffsetPosition,
+ const basegfx::B2DVector& rRectPoint,
+ bool bTiling,
+ bool bStretch,
+ bool bLogSize)
+ : maFillGraphic(rFillGraphic),
+ maGraphicLogicSize(rGraphicLogicSize),
+ maSize(rSize),
+ maOffset(rOffset),
+ maOffsetPosition(rOffsetPosition),
+ maRectPoint(rRectPoint),
+ mbTiling(bTiling),
+ mbStretch(bStretch),
+ mbLogSize(bLogSize)
+ {
+ }
+
+ ImpSdrFillGraphicAttribute()
+ : mbTiling(false),
+ mbStretch(false),
+ mbLogSize(false)
+ {
+ }
+
+ // data read access
+ const Graphic& getFillGraphic() const { return maFillGraphic; }
+ const basegfx::B2DVector& getGraphicLogicSize() const { return maGraphicLogicSize; }
+ const basegfx::B2DVector& getSize() const { return maSize; }
+ const basegfx::B2DVector& getOffset() const { return maOffset; }
+ const basegfx::B2DVector& getOffsetPosition() const { return maOffsetPosition; }
+ const basegfx::B2DVector& getRectPoint() const { return maRectPoint; }
+ bool getTiling() const { return mbTiling; }
+ bool getStretch() const { return mbStretch; }
+
+ bool operator==(const ImpSdrFillGraphicAttribute& rCandidate) const
+ {
+ return (getFillGraphic() == rCandidate.getFillGraphic()
+ && getGraphicLogicSize() == rCandidate.getGraphicLogicSize()
+ && getSize() == rCandidate.getSize()
+ && getOffset() == rCandidate.getOffset()
+ && getOffsetPosition() == rCandidate.getOffsetPosition()
+ && getRectPoint() == rCandidate.getRectPoint()
+ && getTiling() == rCandidate.getTiling()
+ && getStretch() == rCandidate.getStretch()
+ && mbLogSize == rCandidate.mbLogSize);
+ }
+ };
+
+ namespace
+ {
+ SdrFillGraphicAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrFillGraphicAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrFillGraphicAttribute::SdrFillGraphicAttribute(
+ const Graphic& rFillGraphic,
+ const basegfx::B2DVector& rGraphicLogicSize,
+ const basegfx::B2DVector& rSize,
+ const basegfx::B2DVector& rOffset,
+ const basegfx::B2DVector& rOffsetPosition,
+ const basegfx::B2DVector& rRectPoint,
+ bool bTiling,
+ bool bStretch,
+ bool bLogSize)
+ : mpSdrFillGraphicAttribute(
+ ImpSdrFillGraphicAttribute(
+ rFillGraphic,
+ rGraphicLogicSize,
+ rSize,
+ rOffset,
+ rOffsetPosition,
+ rRectPoint,
+ bTiling,
+ bStretch,
+ bLogSize))
+ {
+ }
+
+ SdrFillGraphicAttribute::SdrFillGraphicAttribute()
+ : mpSdrFillGraphicAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrFillGraphicAttribute::SdrFillGraphicAttribute(const SdrFillGraphicAttribute&) = default;
+
+ SdrFillGraphicAttribute::SdrFillGraphicAttribute(SdrFillGraphicAttribute&&) = default;
+
+ SdrFillGraphicAttribute::~SdrFillGraphicAttribute() = default;
+
+ bool SdrFillGraphicAttribute::isDefault() const
+ {
+ return mpSdrFillGraphicAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrFillGraphicAttribute& SdrFillGraphicAttribute::operator=(const SdrFillGraphicAttribute&) = default;
+
+ SdrFillGraphicAttribute& SdrFillGraphicAttribute::operator=(SdrFillGraphicAttribute&&) = default;
+
+ bool SdrFillGraphicAttribute::operator==(const SdrFillGraphicAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrFillGraphicAttribute == mpSdrFillGraphicAttribute;
+ }
+
+ const Graphic& SdrFillGraphicAttribute::getFillGraphic() const
+ {
+ return mpSdrFillGraphicAttribute->getFillGraphic();
+ }
+
+ const basegfx::B2DVector& SdrFillGraphicAttribute::getGraphicLogicSize() const
+ {
+ return mpSdrFillGraphicAttribute->getGraphicLogicSize();
+ }
+
+ const basegfx::B2DVector& SdrFillGraphicAttribute::getSize() const
+ {
+ return mpSdrFillGraphicAttribute->getSize();
+ }
+
+ const basegfx::B2DVector& SdrFillGraphicAttribute::getOffset() const
+ {
+ return mpSdrFillGraphicAttribute->getOffset();
+ }
+
+ const basegfx::B2DVector& SdrFillGraphicAttribute::getOffsetPosition() const
+ {
+ return mpSdrFillGraphicAttribute->getOffsetPosition();
+ }
+
+ const basegfx::B2DVector& SdrFillGraphicAttribute::getRectPoint() const
+ {
+ return mpSdrFillGraphicAttribute->getRectPoint();
+ }
+
+ bool SdrFillGraphicAttribute::getTiling() const
+ {
+ return mpSdrFillGraphicAttribute->getTiling();
+ }
+
+ FillGraphicAttribute SdrFillGraphicAttribute::createFillGraphicAttribute(const basegfx::B2DRange& rRange) const
+ {
+ // get logical size of bitmap (before possibly expanding it)
+ Graphic aGraphic(getFillGraphic());
+
+ // init values with defaults for stretched
+ basegfx::B2DPoint aBitmapSize(1.0, 1.0);
+ basegfx::B2DVector aBitmapTopLeft(0.0, 0.0);
+
+ // are changes needed? When stretched we are already done, all other values will have no influence
+ if(getTiling() || !mpSdrFillGraphicAttribute->getStretch())
+ {
+ // init values with range sizes
+ const double fRangeWidth(0.0 != rRange.getWidth() ? rRange.getWidth() : 1.0);
+ const double fRangeHeight(0.0 != rRange.getHeight() ? rRange.getHeight() : 1.0);
+ aBitmapSize = basegfx::B2DPoint(fRangeWidth, fRangeHeight);
+
+ // size changes
+ if(0.0 != getSize().getX())
+ {
+ if(getSize().getX() < 0.0)
+ {
+ aBitmapSize.setX(aBitmapSize.getX() * (getSize().getX() * -0.01));
+ }
+ else
+ {
+ aBitmapSize.setX(getSize().getX());
+ }
+ }
+ else
+ {
+ // #i124002# use GraphicLogicSize directly, do not try to use GetPrefSize
+ // of the graphic, that may not be adapted to the MapMode of the target
+ aBitmapSize.setX(getGraphicLogicSize().getX());
+ }
+
+ if(0.0 != getSize().getY())
+ {
+ if(getSize().getY() < 0.0)
+ {
+ aBitmapSize.setY(aBitmapSize.getY() * (getSize().getY() * -0.01));
+ }
+ else
+ {
+ aBitmapSize.setY(getSize().getY());
+ }
+ }
+ else
+ {
+ // #i124002# use GraphicLogicSize directly, do not try to use GetPrefSize
+ // of the graphic, that may not be adapted to the MapMode of the target
+ aBitmapSize.setY(getGraphicLogicSize().getY());
+ }
+
+ // position changes X
+ if(0.0 == getRectPoint().getX())
+ {
+ aBitmapTopLeft.setX((fRangeWidth - aBitmapSize.getX()) * 0.5);
+ }
+ else if(1.0 == getRectPoint().getX())
+ {
+ aBitmapTopLeft.setX(fRangeWidth - aBitmapSize.getX());
+ }
+
+ // offset positions are only meaningful when tiled
+ if(getTiling() && 0.0 != getOffsetPosition().getX())
+ {
+ aBitmapTopLeft.setX(aBitmapTopLeft.getX() + (aBitmapSize.getX() * (getOffsetPosition().getX() * 0.01)));
+ }
+
+ // position changes Y
+ if(0.0 == getRectPoint().getY())
+ {
+ aBitmapTopLeft.setY((fRangeHeight - aBitmapSize.getY()) * 0.5);
+ }
+ else if(1.0 == getRectPoint().getY())
+ {
+ aBitmapTopLeft.setY(fRangeHeight - aBitmapSize.getY());
+ }
+
+ // offset positions are only meaningful when tiled
+ if(getTiling() && 0.0 != getOffsetPosition().getY())
+ {
+ aBitmapTopLeft.setY(aBitmapTopLeft.getY() + (aBitmapSize.getY() * (getOffsetPosition().getY() * 0.01)));
+ }
+
+ // apply bitmap size scaling to unit rectangle
+ aBitmapTopLeft.setX(aBitmapTopLeft.getX() / fRangeWidth);
+ aBitmapTopLeft.setY(aBitmapTopLeft.getY() / fRangeHeight);
+ aBitmapSize.setX(aBitmapSize.getX() / fRangeWidth);
+ aBitmapSize.setY(aBitmapSize.getY() / fRangeHeight);
+ }
+
+ // get offset in percent
+ const double fOffsetX(std::clamp(getOffset().getX() * 0.01, 0.0, 1.0));
+ const double fOffsetY(std::clamp(getOffset().getY() * 0.01, 0.0, 1.0));
+
+ // create FillGraphicAttribute
+ return FillGraphicAttribute(
+ aGraphic,
+ basegfx::B2DRange(aBitmapTopLeft, aBitmapTopLeft + aBitmapSize),
+ getTiling(),
+ fOffsetX,
+ fOffsetY);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrglowattribute.cxx b/drawinglayer/source/attribute/sdrglowattribute.cxx
new file mode 100644
index 000000000..c27390d64
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrglowattribute.cxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <drawinglayer/attribute/sdrglowattribute.hxx>
+
+namespace drawinglayer::attribute
+{
+SdrGlowAttribute::SdrGlowAttribute(sal_Int32 nRadius, const Color& rColor)
+ : m_nRadius(nRadius)
+ , m_color(rColor)
+{
+}
+
+SdrGlowAttribute::SdrGlowAttribute() = default;
+
+SdrGlowAttribute::SdrGlowAttribute(const SdrGlowAttribute&) = default;
+
+SdrGlowAttribute::SdrGlowAttribute(SdrGlowAttribute&&) = default;
+
+SdrGlowAttribute& SdrGlowAttribute::operator=(const SdrGlowAttribute&) = default;
+
+SdrGlowAttribute& SdrGlowAttribute::operator=(SdrGlowAttribute&&) = default;
+
+bool SdrGlowAttribute::operator==(const SdrGlowAttribute& rCandidate) const
+{
+ return m_nRadius == rCandidate.m_nRadius && m_color == rCandidate.m_color;
+}
+
+} // end of namespace drawinglayer::attribute
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrlightattribute3d.cxx b/drawinglayer/source/attribute/sdrlightattribute3d.cxx
new file mode 100644
index 000000000..511590f5f
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrlightattribute3d.cxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/vector/b3dvector.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdr3DLightAttribute
+ {
+ public:
+ // 3D light attribute definitions
+ basegfx::BColor maColor;
+ basegfx::B3DVector maDirection;
+
+ bool mbSpecular : 1;
+
+ ImpSdr3DLightAttribute(
+ const basegfx::BColor& rColor,
+ const basegfx::B3DVector& rDirection,
+ bool bSpecular)
+ : maColor(rColor),
+ maDirection(rDirection),
+ mbSpecular(bSpecular)
+ {
+ }
+
+ // data read access
+ const basegfx::BColor& getColor() const { return maColor; }
+ const basegfx::B3DVector& getDirection() const { return maDirection; }
+ bool getSpecular() const { return mbSpecular; }
+
+ bool operator==(const ImpSdr3DLightAttribute& rCandidate) const
+ {
+ return (getColor() == rCandidate.getColor()
+ && getDirection() == rCandidate.getDirection()
+ && getSpecular() == rCandidate.getSpecular());
+ }
+ };
+
+ Sdr3DLightAttribute::Sdr3DLightAttribute(
+ const basegfx::BColor& rColor,
+ const basegfx::B3DVector& rDirection,
+ bool bSpecular)
+ : mpSdr3DLightAttribute(ImpSdr3DLightAttribute(
+ rColor, rDirection, bSpecular))
+ {
+ }
+
+ Sdr3DLightAttribute::Sdr3DLightAttribute(const Sdr3DLightAttribute&) = default;
+
+ Sdr3DLightAttribute::~Sdr3DLightAttribute() = default;
+
+ Sdr3DLightAttribute& Sdr3DLightAttribute::operator=(const Sdr3DLightAttribute&) = default;
+
+ bool Sdr3DLightAttribute::operator==(const Sdr3DLightAttribute& rCandidate) const
+ {
+ return rCandidate.mpSdr3DLightAttribute == mpSdr3DLightAttribute;
+ }
+
+ const basegfx::BColor& Sdr3DLightAttribute::getColor() const
+ {
+ return mpSdr3DLightAttribute->getColor();
+ }
+
+ const basegfx::B3DVector& Sdr3DLightAttribute::getDirection() const
+ {
+ return mpSdr3DLightAttribute->getDirection();
+ }
+
+ bool Sdr3DLightAttribute::getSpecular() const
+ {
+ return mpSdr3DLightAttribute->getSpecular();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrlightingattribute3d.cxx b/drawinglayer/source/attribute/sdrlightingattribute3d.cxx
new file mode 100644
index 000000000..4f9b75cd1
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrlightingattribute3d.cxx
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrlightingattribute3d.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/vector/b3dvector.hxx>
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrLightingAttribute
+ {
+ public:
+ // 3D light attribute definitions
+ basegfx::BColor maAmbientLight;
+ std::vector< Sdr3DLightAttribute > maLightVector;
+
+ ImpSdrLightingAttribute(
+ const basegfx::BColor& rAmbientLight,
+ std::vector< Sdr3DLightAttribute >&& rLightVector)
+ : maAmbientLight(rAmbientLight),
+ maLightVector(std::move(rLightVector))
+ {
+ }
+
+ ImpSdrLightingAttribute()
+ {
+ }
+
+ // data read access
+ const basegfx::BColor& getAmbientLight() const { return maAmbientLight; }
+ const std::vector< Sdr3DLightAttribute >& getLightVector() const { return maLightVector; }
+
+ bool operator==(const ImpSdrLightingAttribute& rCandidate) const
+ {
+ return (getAmbientLight() == rCandidate.getAmbientLight()
+ && getLightVector() == rCandidate.getLightVector());
+ }
+ };
+
+ namespace
+ {
+ SdrLightingAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrLightingAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrLightingAttribute::SdrLightingAttribute(
+ const basegfx::BColor& rAmbientLight,
+ std::vector< Sdr3DLightAttribute >&& rLightVector)
+ : mpSdrLightingAttribute(ImpSdrLightingAttribute(
+ rAmbientLight, std::move(rLightVector)))
+ {
+ }
+
+ SdrLightingAttribute::SdrLightingAttribute()
+ : mpSdrLightingAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrLightingAttribute::SdrLightingAttribute(const SdrLightingAttribute&) = default;
+
+ SdrLightingAttribute::SdrLightingAttribute(SdrLightingAttribute&&) = default;
+
+ SdrLightingAttribute::~SdrLightingAttribute() = default;
+
+
+ bool SdrLightingAttribute::isDefault() const
+ {
+ return mpSdrLightingAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrLightingAttribute& SdrLightingAttribute::operator=(const SdrLightingAttribute&) = default;
+
+ SdrLightingAttribute& SdrLightingAttribute::operator=(SdrLightingAttribute&&) = default;
+
+ bool SdrLightingAttribute::operator==(const SdrLightingAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrLightingAttribute == mpSdrLightingAttribute;
+ }
+
+ const std::vector< Sdr3DLightAttribute >& SdrLightingAttribute::getLightVector() const
+ {
+ return mpSdrLightingAttribute->getLightVector();
+ }
+
+ const basegfx::BColor& SdrLightingAttribute::getAmbientLightColor() const
+ {
+ return mpSdrLightingAttribute->maAmbientLight;
+ }
+
+ // color model solver
+ basegfx::BColor SdrLightingAttribute::solveColorModel(
+ const basegfx::B3DVector& rNormalInEyeCoordinates,
+ const basegfx::BColor& rColor, const basegfx::BColor& rSpecular,
+ const basegfx::BColor& rEmission, sal_uInt16 nSpecularIntensity) const
+ {
+ // initialize with emissive color
+ basegfx::BColor aRetval(rEmission);
+
+ // take care of global ambient light
+ aRetval += mpSdrLightingAttribute->getAmbientLight() * rColor;
+
+ const std::vector<Sdr3DLightAttribute>& rLightVector = mpSdrLightingAttribute->getLightVector();
+
+ // prepare light access. Is there a light?
+ const sal_uInt32 nLightCount(rLightVector.size());
+
+ if(nLightCount && !rNormalInEyeCoordinates.equalZero())
+ {
+ // prepare normal
+ basegfx::B3DVector aEyeNormal(rNormalInEyeCoordinates);
+ aEyeNormal.normalize();
+
+ for(sal_uInt32 a(0); a < nLightCount; a++)
+ {
+ const Sdr3DLightAttribute& rLight(rLightVector[a]);
+ const double fCosFac(rLight.getDirection().scalar(aEyeNormal));
+
+ if(basegfx::fTools::more(fCosFac, 0.0))
+ {
+ aRetval += (rLight.getColor() * rColor) * fCosFac;
+
+ if(rLight.getSpecular())
+ {
+ // expand by (0.0, 0.0, 1.0) in Z
+ basegfx::B3DVector aSpecularNormal(rLight.getDirection().getX(), rLight.getDirection().getY(), rLight.getDirection().getZ() + 1.0);
+ aSpecularNormal.normalize();
+ double fCosFac2(aSpecularNormal.scalar(aEyeNormal));
+
+ if(basegfx::fTools::more(fCosFac2, 0.0))
+ {
+ fCosFac2 = pow(fCosFac2, static_cast<double>(nSpecularIntensity));
+ aRetval += rSpecular * fCosFac2;
+ }
+ }
+ }
+ }
+ }
+
+ // clamp to color space before usage
+ aRetval.clamp();
+
+ return aRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrlineattribute.cxx b/drawinglayer/source/attribute/sdrlineattribute.cxx
new file mode 100644
index 000000000..f8d4aefb0
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrlineattribute.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrLineAttribute
+ {
+ public:
+ // line definitions
+ double mfWidth; // 1/100th mm, 0.0==hair
+ double mfTransparence; // [0.0 .. 1.0], 0.0==no transp.
+ double mfFullDotDashLen; // sum of maDotDashArray (for convenience)
+ basegfx::BColor maColor; // color of line
+ std::vector< double > maDotDashArray; // array of double which defines the dot-dash pattern
+ basegfx::B2DLineJoin meJoin; // B2DLINEJOIN_* defines
+ css::drawing::LineCap meCap; // BUTT, ROUND, or SQUARE
+
+ ImpSdrLineAttribute(
+ basegfx::B2DLineJoin eJoin,
+ double fWidth,
+ double fTransparence,
+ const basegfx::BColor& rColor,
+ css::drawing::LineCap eCap,
+ std::vector< double >&& rDotDashArray,
+ double fFullDotDashLen)
+ : mfWidth(fWidth),
+ mfTransparence(fTransparence),
+ mfFullDotDashLen(fFullDotDashLen),
+ maColor(rColor),
+ maDotDashArray(std::move(rDotDashArray)),
+ meJoin(eJoin),
+ meCap(eCap)
+ {
+ }
+
+ ImpSdrLineAttribute()
+ : mfWidth(0.0),
+ mfTransparence(0.0),
+ mfFullDotDashLen(0.0),
+ meJoin(basegfx::B2DLineJoin::Round),
+ meCap(css::drawing::LineCap_BUTT)
+ {
+ }
+
+ // data read access
+ basegfx::B2DLineJoin getJoin() const { return meJoin; }
+ double getWidth() const { return mfWidth; }
+ double getTransparence() const { return mfTransparence; }
+ const basegfx::BColor& getColor() const { return maColor; }
+ css::drawing::LineCap getCap() const { return meCap; }
+ const std::vector< double >& getDotDashArray() const { return maDotDashArray; }
+ double getFullDotDashLen() const { return mfFullDotDashLen; }
+
+ bool operator==(const ImpSdrLineAttribute& rCandidate) const
+ {
+ return (getJoin() == rCandidate.getJoin()
+ && getWidth() == rCandidate.getWidth()
+ && getTransparence() == rCandidate.getTransparence()
+ && getColor() == rCandidate.getColor()
+ && getCap() == rCandidate.getCap()
+ && getDotDashArray() == rCandidate.getDotDashArray());
+ }
+ };
+
+ namespace
+ {
+ SdrLineAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrLineAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrLineAttribute::SdrLineAttribute(
+ basegfx::B2DLineJoin eJoin,
+ double fWidth,
+ double fTransparence,
+ const basegfx::BColor& rColor,
+ css::drawing::LineCap eCap,
+ std::vector< double >&& rDotDashArray,
+ double fFullDotDashLen)
+ : mpSdrLineAttribute(
+ ImpSdrLineAttribute(
+ eJoin,
+ fWidth,
+ fTransparence,
+ rColor,
+ eCap,
+ std::move(rDotDashArray),
+ fFullDotDashLen))
+
+ {
+ }
+
+ SdrLineAttribute::SdrLineAttribute()
+ : mpSdrLineAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrLineAttribute::SdrLineAttribute(const SdrLineAttribute&) = default;
+
+ SdrLineAttribute::SdrLineAttribute(SdrLineAttribute&&) = default;
+
+ SdrLineAttribute::~SdrLineAttribute() = default;
+
+ bool SdrLineAttribute::isDefault() const
+ {
+ return mpSdrLineAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrLineAttribute& SdrLineAttribute::operator=(const SdrLineAttribute&) = default;
+
+ SdrLineAttribute& SdrLineAttribute::operator=(SdrLineAttribute&&) = default;
+
+ bool SdrLineAttribute::operator==(const SdrLineAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrLineAttribute == mpSdrLineAttribute;
+ }
+
+ basegfx::B2DLineJoin SdrLineAttribute::getJoin() const
+ {
+ return mpSdrLineAttribute->getJoin();
+ }
+
+ double SdrLineAttribute::getWidth() const
+ {
+ return mpSdrLineAttribute->getWidth();
+ }
+
+ double SdrLineAttribute::getTransparence() const
+ {
+ return mpSdrLineAttribute->getTransparence();
+ }
+
+ const basegfx::BColor& SdrLineAttribute::getColor() const
+ {
+ return mpSdrLineAttribute->getColor();
+ }
+
+ const std::vector< double >& SdrLineAttribute::getDotDashArray() const
+ {
+ return mpSdrLineAttribute->getDotDashArray();
+ }
+
+ double SdrLineAttribute::getFullDotDashLen() const
+ {
+ return mpSdrLineAttribute->getFullDotDashLen();
+ }
+
+ css::drawing::LineCap SdrLineAttribute::getCap() const
+ {
+ return mpSdrLineAttribute->getCap();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrlinestartendattribute.cxx b/drawinglayer/source/attribute/sdrlinestartendattribute.cxx
new file mode 100644
index 000000000..aa052236c
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrlinestartendattribute.cxx
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrLineStartEndAttribute
+ {
+ public:
+ // line arrow definitions
+ basegfx::B2DPolyPolygon maStartPolyPolygon; // start Line PolyPolygon
+ basegfx::B2DPolyPolygon maEndPolyPolygon; // end Line PolyPolygon
+ double mfStartWidth; // 1/100th mm
+ double mfEndWidth; // 1/100th mm
+
+ bool mbStartActive : 1; // start of Line is active
+ bool mbEndActive : 1; // end of Line is active
+ bool mbStartCentered : 1; // Line is centered on line start point
+ bool mbEndCentered : 1; // Line is centered on line end point
+
+ ImpSdrLineStartEndAttribute(
+ const basegfx::B2DPolyPolygon& rStartPolyPolygon,
+ const basegfx::B2DPolyPolygon& rEndPolyPolygon,
+ double fStartWidth,
+ double fEndWidth,
+ bool bStartActive,
+ bool bEndActive,
+ bool bStartCentered,
+ bool bEndCentered)
+ : maStartPolyPolygon(rStartPolyPolygon),
+ maEndPolyPolygon(rEndPolyPolygon),
+ mfStartWidth(fStartWidth),
+ mfEndWidth(fEndWidth),
+ mbStartActive(bStartActive),
+ mbEndActive(bEndActive),
+ mbStartCentered(bStartCentered),
+ mbEndCentered(bEndCentered)
+ {
+ }
+
+ ImpSdrLineStartEndAttribute()
+ : mfStartWidth(0.0),
+ mfEndWidth(0.0),
+ mbStartActive(false),
+ mbEndActive(false),
+ mbStartCentered(false),
+ mbEndCentered(false)
+ {
+ }
+
+ // data read access
+ const basegfx::B2DPolyPolygon& getStartPolyPolygon() const { return maStartPolyPolygon; }
+ const basegfx::B2DPolyPolygon& getEndPolyPolygon() const { return maEndPolyPolygon; }
+ double getStartWidth() const { return mfStartWidth; }
+ double getEndWidth() const { return mfEndWidth; }
+ bool isStartActive() const { return mbStartActive; }
+ bool isEndActive() const { return mbEndActive; }
+ bool isStartCentered() const { return mbStartCentered; }
+ bool isEndCentered() const { return mbEndCentered; }
+
+ bool operator==(const ImpSdrLineStartEndAttribute& rCandidate) const
+ {
+ return (getStartPolyPolygon() == rCandidate.getStartPolyPolygon()
+ && getEndPolyPolygon() == rCandidate.getEndPolyPolygon()
+ && getStartWidth() == rCandidate.getStartWidth()
+ && getEndWidth() == rCandidate.getEndWidth()
+ && isStartActive() == rCandidate.isStartActive()
+ && isEndActive() == rCandidate.isEndActive()
+ && isStartCentered() == rCandidate.isStartCentered()
+ && isEndCentered() == rCandidate.isEndCentered());
+ }
+ };
+
+ namespace
+ {
+ SdrLineStartEndAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrLineStartEndAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrLineStartEndAttribute::SdrLineStartEndAttribute(
+ const basegfx::B2DPolyPolygon& rStartPolyPolygon,
+ const basegfx::B2DPolyPolygon& rEndPolyPolygon,
+ double fStartWidth,
+ double fEndWidth,
+ bool bStartActive,
+ bool bEndActive,
+ bool bStartCentered,
+ bool bEndCentered)
+ : mpSdrLineStartEndAttribute(ImpSdrLineStartEndAttribute(
+ rStartPolyPolygon, rEndPolyPolygon, fStartWidth, fEndWidth, bStartActive, bEndActive, bStartCentered, bEndCentered))
+ {
+ }
+
+ SdrLineStartEndAttribute::SdrLineStartEndAttribute()
+ : mpSdrLineStartEndAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrLineStartEndAttribute::SdrLineStartEndAttribute(const SdrLineStartEndAttribute&) = default;
+
+ SdrLineStartEndAttribute::SdrLineStartEndAttribute(SdrLineStartEndAttribute&&) = default;
+
+ SdrLineStartEndAttribute::~SdrLineStartEndAttribute() = default;
+
+ bool SdrLineStartEndAttribute::isDefault() const
+ {
+ return mpSdrLineStartEndAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrLineStartEndAttribute& SdrLineStartEndAttribute::operator=(const SdrLineStartEndAttribute&) = default;
+
+ SdrLineStartEndAttribute& SdrLineStartEndAttribute::operator=(SdrLineStartEndAttribute&&) = default;
+
+ bool SdrLineStartEndAttribute::operator==(const SdrLineStartEndAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrLineStartEndAttribute == mpSdrLineStartEndAttribute;
+ }
+
+ const basegfx::B2DPolyPolygon& SdrLineStartEndAttribute::getStartPolyPolygon() const
+ {
+ return mpSdrLineStartEndAttribute->getStartPolyPolygon();
+ }
+
+ const basegfx::B2DPolyPolygon& SdrLineStartEndAttribute::getEndPolyPolygon() const
+ {
+ return mpSdrLineStartEndAttribute->getEndPolyPolygon();
+ }
+
+ double SdrLineStartEndAttribute::getStartWidth() const
+ {
+ return mpSdrLineStartEndAttribute->getStartWidth();
+ }
+
+ double SdrLineStartEndAttribute::getEndWidth() const
+ {
+ return mpSdrLineStartEndAttribute->getEndWidth();
+ }
+
+ bool SdrLineStartEndAttribute::isStartActive() const
+ {
+ return mpSdrLineStartEndAttribute->isStartActive();
+ }
+
+ bool SdrLineStartEndAttribute::isEndActive() const
+ {
+ return mpSdrLineStartEndAttribute->isEndActive();
+ }
+
+ bool SdrLineStartEndAttribute::isStartCentered() const
+ {
+ return mpSdrLineStartEndAttribute->isStartCentered();
+ }
+
+ bool SdrLineStartEndAttribute::isEndCentered() const
+ {
+ return mpSdrLineStartEndAttribute->isEndCentered();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrobjectattribute3d.cxx b/drawinglayer/source/attribute/sdrobjectattribute3d.cxx
new file mode 100644
index 000000000..dad8a07ee
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrobjectattribute3d.cxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrobjectattribute3d.hxx>
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdr3DObjectAttribute
+ {
+ public:
+ // 3D object attribute definitions
+ css::drawing::NormalsKind maNormalsKind; // normals type (0..2)
+ css::drawing::TextureProjectionMode maTextureProjectionX; // texture projection type X (0..2)
+ css::drawing::TextureProjectionMode maTextureProjectionY; // texture projection type Y (0..2)
+ css::drawing::TextureKind2 maTextureKind; // texture kind (see uno API)
+ css::drawing::TextureMode maTextureMode; // texture kind (see uno API)
+ MaterialAttribute3D maMaterial; // object, specular and emissive colors, SpecularIntensity
+
+ bool mbNormalsInvert : 1; // invert normals
+ bool mbDoubleSided : 1; // surfaces are double sided
+ bool mbShadow3D : 1; // display shadow in 3D (if on), params for that are at scene
+ bool mbTextureFilter : 1; // filter texture to make more smooth
+ bool mbReducedLineGeometry : 1; // use reduced line geometry (object specific)
+
+ ImpSdr3DObjectAttribute(
+ css::drawing::NormalsKind aNormalsKind,
+ css::drawing::TextureProjectionMode aTextureProjectionX,
+ css::drawing::TextureProjectionMode aTextureProjectionY,
+ css::drawing::TextureKind2 aTextureKind,
+ css::drawing::TextureMode aTextureMode,
+ const MaterialAttribute3D& rMaterial,
+ bool bNormalsInvert,
+ bool bDoubleSided,
+ bool bShadow3D,
+ bool bTextureFilter,
+ bool bReducedLineGeometry)
+ : maNormalsKind(aNormalsKind),
+ maTextureProjectionX(aTextureProjectionX),
+ maTextureProjectionY(aTextureProjectionY),
+ maTextureKind(aTextureKind),
+ maTextureMode(aTextureMode),
+ maMaterial(rMaterial),
+ mbNormalsInvert(bNormalsInvert),
+ mbDoubleSided(bDoubleSided),
+ mbShadow3D(bShadow3D),
+ mbTextureFilter(bTextureFilter),
+ mbReducedLineGeometry(bReducedLineGeometry)
+ {
+ }
+
+ // data read access
+ css::drawing::NormalsKind getNormalsKind() const { return maNormalsKind; }
+ css::drawing::TextureProjectionMode getTextureProjectionX() const { return maTextureProjectionX; }
+ css::drawing::TextureProjectionMode getTextureProjectionY() const { return maTextureProjectionY; }
+ css::drawing::TextureKind2 getTextureKind() const { return maTextureKind; }
+ css::drawing::TextureMode getTextureMode() const { return maTextureMode; }
+ const MaterialAttribute3D& getMaterial() const { return maMaterial; }
+ bool getNormalsInvert() const { return mbNormalsInvert; }
+ bool getDoubleSided() const { return mbDoubleSided; }
+ bool getShadow3D() const { return mbShadow3D; }
+ bool getTextureFilter() const { return mbTextureFilter; }
+ bool getReducedLineGeometry() const { return mbReducedLineGeometry; }
+
+ bool operator==(const ImpSdr3DObjectAttribute& rCandidate) const
+ {
+ return (getNormalsKind() == rCandidate.getNormalsKind()
+ && getTextureProjectionX() == rCandidate.getTextureProjectionX()
+ && getTextureProjectionY() == rCandidate.getTextureProjectionY()
+ && getTextureKind() == rCandidate.getTextureKind()
+ && getTextureMode() == rCandidate.getTextureMode()
+ && getMaterial() == rCandidate.getMaterial()
+ && getNormalsInvert() == rCandidate.getNormalsInvert()
+ && getDoubleSided() == rCandidate.getDoubleSided()
+ && getShadow3D() == rCandidate.getShadow3D()
+ && getTextureFilter() == rCandidate.getTextureFilter()
+ && getReducedLineGeometry() == rCandidate.getReducedLineGeometry());
+ }
+ };
+
+ Sdr3DObjectAttribute::Sdr3DObjectAttribute(
+ css::drawing::NormalsKind aNormalsKind,
+ css::drawing::TextureProjectionMode aTextureProjectionX,
+ css::drawing::TextureProjectionMode aTextureProjectionY,
+ css::drawing::TextureKind2 aTextureKind,
+ css::drawing::TextureMode aTextureMode,
+ const MaterialAttribute3D& rMaterial,
+ bool bNormalsInvert,
+ bool bDoubleSided,
+ bool bShadow3D,
+ bool bTextureFilter,
+ bool bReducedLineGeometry)
+ : mpSdr3DObjectAttribute(ImpSdr3DObjectAttribute(
+ aNormalsKind, aTextureProjectionX, aTextureProjectionY, aTextureKind, aTextureMode,
+ rMaterial, bNormalsInvert, bDoubleSided, bShadow3D, bTextureFilter, bReducedLineGeometry))
+ {
+ }
+
+ Sdr3DObjectAttribute::Sdr3DObjectAttribute(const Sdr3DObjectAttribute&) = default;
+
+ Sdr3DObjectAttribute::~Sdr3DObjectAttribute() = default;
+
+ Sdr3DObjectAttribute& Sdr3DObjectAttribute::operator=(const Sdr3DObjectAttribute&) = default;
+
+ bool Sdr3DObjectAttribute::operator==(const Sdr3DObjectAttribute& rCandidate) const
+ {
+ return rCandidate.mpSdr3DObjectAttribute == mpSdr3DObjectAttribute;
+ }
+
+ css::drawing::NormalsKind Sdr3DObjectAttribute::getNormalsKind() const
+ {
+ return mpSdr3DObjectAttribute->getNormalsKind();
+ }
+
+ css::drawing::TextureProjectionMode Sdr3DObjectAttribute::getTextureProjectionX() const
+ {
+ return mpSdr3DObjectAttribute->getTextureProjectionX();
+ }
+
+ css::drawing::TextureProjectionMode Sdr3DObjectAttribute::getTextureProjectionY() const
+ {
+ return mpSdr3DObjectAttribute->getTextureProjectionY();
+ }
+
+ css::drawing::TextureKind2 Sdr3DObjectAttribute::getTextureKind() const
+ {
+ return mpSdr3DObjectAttribute->getTextureKind();
+ }
+
+ css::drawing::TextureMode Sdr3DObjectAttribute::getTextureMode() const
+ {
+ return mpSdr3DObjectAttribute->getTextureMode();
+ }
+
+ const MaterialAttribute3D& Sdr3DObjectAttribute::getMaterial() const
+ {
+ return mpSdr3DObjectAttribute->getMaterial();
+ }
+
+ bool Sdr3DObjectAttribute::getNormalsInvert() const
+ {
+ return mpSdr3DObjectAttribute->getNormalsInvert();
+ }
+
+ bool Sdr3DObjectAttribute::getDoubleSided() const
+ {
+ return mpSdr3DObjectAttribute->getDoubleSided();
+ }
+
+ bool Sdr3DObjectAttribute::getShadow3D() const
+ {
+ return mpSdr3DObjectAttribute->getShadow3D();
+ }
+
+ bool Sdr3DObjectAttribute::getTextureFilter() const
+ {
+ return mpSdr3DObjectAttribute->getTextureFilter();
+ }
+
+ bool Sdr3DObjectAttribute::getReducedLineGeometry() const
+ {
+ return mpSdr3DObjectAttribute->getReducedLineGeometry();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrsceneattribute3d.cxx b/drawinglayer/source/attribute/sdrsceneattribute3d.cxx
new file mode 100644
index 000000000..840fe2e30
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrsceneattribute3d.cxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrsceneattribute3d.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrSceneAttribute
+ {
+ public:
+ // 3D scene attribute definitions
+ double mfDistance;
+ double mfShadowSlant;
+ css::drawing::ProjectionMode maProjectionMode;
+ css::drawing::ShadeMode maShadeMode;
+
+ bool mbTwoSidedLighting : 1;
+
+ public:
+ ImpSdrSceneAttribute(
+ double fDistance,
+ double fShadowSlant,
+ css::drawing::ProjectionMode aProjectionMode,
+ css::drawing::ShadeMode aShadeMode,
+ bool bTwoSidedLighting)
+ : mfDistance(fDistance),
+ mfShadowSlant(fShadowSlant),
+ maProjectionMode(aProjectionMode),
+ maShadeMode(aShadeMode),
+ mbTwoSidedLighting(bTwoSidedLighting)
+ {
+ }
+
+ ImpSdrSceneAttribute()
+ : mfDistance(0.0),
+ mfShadowSlant(0.0),
+ maProjectionMode(css::drawing::ProjectionMode_PARALLEL),
+ maShadeMode(css::drawing::ShadeMode_FLAT),
+ mbTwoSidedLighting(false)
+ {
+ }
+
+ // data read access
+ double getShadowSlant() const { return mfShadowSlant; }
+ css::drawing::ProjectionMode getProjectionMode() const { return maProjectionMode; }
+ css::drawing::ShadeMode getShadeMode() const { return maShadeMode; }
+ bool getTwoSidedLighting() const { return mbTwoSidedLighting; }
+
+ bool operator==(const ImpSdrSceneAttribute& rCandidate) const
+ {
+ return (mfDistance == rCandidate.mfDistance
+ && getShadowSlant() == rCandidate.getShadowSlant()
+ && getProjectionMode() == rCandidate.getProjectionMode()
+ && getShadeMode() == rCandidate.getShadeMode()
+ && getTwoSidedLighting() == rCandidate.getTwoSidedLighting());
+ }
+ };
+
+ namespace
+ {
+ SdrSceneAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrSceneAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrSceneAttribute::SdrSceneAttribute(
+ double fDistance,
+ double fShadowSlant,
+ css::drawing::ProjectionMode aProjectionMode,
+ css::drawing::ShadeMode aShadeMode,
+ bool bTwoSidedLighting)
+ : mpSdrSceneAttribute(ImpSdrSceneAttribute(
+ fDistance, fShadowSlant, aProjectionMode, aShadeMode, bTwoSidedLighting))
+ {
+ }
+
+ SdrSceneAttribute::SdrSceneAttribute()
+ : mpSdrSceneAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrSceneAttribute::SdrSceneAttribute(const SdrSceneAttribute&) = default;
+
+ SdrSceneAttribute::SdrSceneAttribute(SdrSceneAttribute&&) = default;
+
+ SdrSceneAttribute::~SdrSceneAttribute() = default;
+
+ bool SdrSceneAttribute::isDefault() const
+ {
+ return mpSdrSceneAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrSceneAttribute& SdrSceneAttribute::operator=(const SdrSceneAttribute&) = default;
+
+ SdrSceneAttribute& SdrSceneAttribute::operator=(SdrSceneAttribute&&) = default;
+
+ bool SdrSceneAttribute::operator==(const SdrSceneAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrSceneAttribute == mpSdrSceneAttribute;
+ }
+
+ double SdrSceneAttribute::getShadowSlant() const
+ {
+ return mpSdrSceneAttribute->getShadowSlant();
+ }
+
+ css::drawing::ProjectionMode SdrSceneAttribute::getProjectionMode() const
+ {
+ return mpSdrSceneAttribute->getProjectionMode();
+ }
+
+ css::drawing::ShadeMode SdrSceneAttribute::getShadeMode() const
+ {
+ return mpSdrSceneAttribute->getShadeMode();
+ }
+
+ bool SdrSceneAttribute::getTwoSidedLighting() const
+ {
+ return mpSdrSceneAttribute->getTwoSidedLighting();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/sdrshadowattribute.cxx b/drawinglayer/source/attribute/sdrshadowattribute.cxx
new file mode 100644
index 000000000..6e046f1f0
--- /dev/null
+++ b/drawinglayer/source/attribute/sdrshadowattribute.cxx
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrShadowAttribute
+ {
+ public:
+ // shadow definitions
+ basegfx::B2DVector maOffset; // shadow offset 1/100th mm
+ basegfx::B2DVector maSize; // [0.0 .. 2.0]
+ double mfTransparence; // [0.0 .. 1.0], 0.0==no transp.
+ sal_Int32 mnBlur; // [0 .. 180], radius of the blur
+ basegfx::BColor maColor; // color of shadow
+
+ ImpSdrShadowAttribute(
+ const basegfx::B2DVector& rOffset,
+ const basegfx::B2DVector& rSize,
+ double fTransparence,
+ sal_Int32 nBlur,
+ const basegfx::BColor& rColor)
+ : maOffset(rOffset),
+ maSize(rSize),
+ mfTransparence(fTransparence),
+ mnBlur(nBlur),
+ maColor(rColor)
+ {
+ }
+
+ ImpSdrShadowAttribute()
+ : mfTransparence(0.0),
+ mnBlur(0)
+ {
+ }
+
+ // data read access
+ const basegfx::B2DVector& getOffset() const { return maOffset; }
+ const basegfx::B2DVector& getSize() const { return maSize; }
+ double getTransparence() const { return mfTransparence; }
+ sal_Int32 getBlur() const { return mnBlur; }
+ const basegfx::BColor& getColor() const { return maColor; }
+
+ bool operator==(const ImpSdrShadowAttribute& rCandidate) const
+ {
+ return (getOffset() == rCandidate.getOffset()
+ && getSize() == rCandidate.getSize()
+ && getTransparence() == rCandidate.getTransparence()
+ && getBlur() == rCandidate.getBlur()
+ && getColor() == rCandidate.getColor());
+ }
+ };
+
+ namespace
+ {
+ SdrShadowAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrShadowAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+
+ SdrShadowAttribute::SdrShadowAttribute(
+ const basegfx::B2DVector& rOffset,
+ const basegfx::B2DVector& rSize,
+ double fTransparence,
+ sal_Int32 nBlur,
+ const basegfx::BColor& rColor)
+ : mpSdrShadowAttribute(ImpSdrShadowAttribute(
+ rOffset, rSize, fTransparence,nBlur, rColor))
+ {
+ }
+
+ SdrShadowAttribute::SdrShadowAttribute()
+ : mpSdrShadowAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrShadowAttribute::SdrShadowAttribute(const SdrShadowAttribute&) = default;
+
+ SdrShadowAttribute::SdrShadowAttribute(SdrShadowAttribute&&) = default;
+
+ SdrShadowAttribute::~SdrShadowAttribute() = default;
+
+ bool SdrShadowAttribute::isDefault() const
+ {
+ return mpSdrShadowAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrShadowAttribute& SdrShadowAttribute::operator=(const SdrShadowAttribute&) = default;
+
+ SdrShadowAttribute& SdrShadowAttribute::operator=(SdrShadowAttribute&&) = default;
+
+ bool SdrShadowAttribute::operator==(const SdrShadowAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return mpSdrShadowAttribute == rCandidate.mpSdrShadowAttribute;
+ }
+
+ const basegfx::B2DVector& SdrShadowAttribute::getOffset() const
+ {
+ return mpSdrShadowAttribute->getOffset();
+ }
+
+ const basegfx::B2DVector& SdrShadowAttribute::getSize() const
+ {
+ return mpSdrShadowAttribute->getSize();
+ }
+
+ double SdrShadowAttribute::getTransparence() const
+ {
+ return mpSdrShadowAttribute->getTransparence();
+ }
+
+ sal_Int32 SdrShadowAttribute::getBlur() const
+ {
+ return mpSdrShadowAttribute->getBlur();
+ }
+
+ const basegfx::BColor& SdrShadowAttribute::getColor() const
+ {
+ return mpSdrShadowAttribute->getColor();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/attribute/strokeattribute.cxx b/drawinglayer/source/attribute/strokeattribute.cxx
new file mode 100644
index 000000000..d925eb65a
--- /dev/null
+++ b/drawinglayer/source/attribute/strokeattribute.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <numeric>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpStrokeAttribute
+ {
+ public:
+ // data definitions
+ std::vector< double > maDotDashArray; // array of double which defines the dot-dash pattern
+ double mfFullDotDashLen; // sum of maDotDashArray (for convenience)
+
+ ImpStrokeAttribute(
+ std::vector< double >&& rDotDashArray,
+ double fFullDotDashLen)
+ : maDotDashArray(std::move(rDotDashArray)),
+ mfFullDotDashLen(fFullDotDashLen)
+ {
+ }
+
+ ImpStrokeAttribute()
+ : mfFullDotDashLen(0.0)
+ {
+ }
+
+ // data read access
+ const std::vector< double >& getDotDashArray() const { return maDotDashArray; }
+ double getFullDotDashLen() const
+ {
+ if(0.0 == mfFullDotDashLen && !maDotDashArray.empty())
+ {
+ // calculate length on demand
+ const double fAccumulated(std::accumulate(maDotDashArray.begin(), maDotDashArray.end(), 0.0));
+ const_cast< ImpStrokeAttribute* >(this)->mfFullDotDashLen = fAccumulated;
+ }
+
+ return mfFullDotDashLen;
+ }
+
+ bool operator==(const ImpStrokeAttribute& rCandidate) const
+ {
+ return (getDotDashArray() == rCandidate.getDotDashArray()
+ && getFullDotDashLen() == rCandidate.getFullDotDashLen());
+ }
+ };
+
+ namespace
+ {
+ StrokeAttribute::ImplType& theGlobalDefault()
+ {
+ static StrokeAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ StrokeAttribute::StrokeAttribute(
+ std::vector< double >&& rDotDashArray,
+ double fFullDotDashLen)
+ : mpStrokeAttribute(ImpStrokeAttribute(
+ std::move(rDotDashArray), fFullDotDashLen))
+ {
+ }
+
+ StrokeAttribute::StrokeAttribute()
+ : mpStrokeAttribute(theGlobalDefault())
+ {
+ }
+
+ StrokeAttribute::StrokeAttribute(const StrokeAttribute&) = default;
+
+ StrokeAttribute::StrokeAttribute(StrokeAttribute&&) = default;
+
+ StrokeAttribute::~StrokeAttribute() = default;
+
+ bool StrokeAttribute::isDefault() const
+ {
+ return mpStrokeAttribute.same_object(theGlobalDefault());
+ }
+
+ StrokeAttribute& StrokeAttribute::operator=(const StrokeAttribute&) = default;
+
+ StrokeAttribute& StrokeAttribute::operator=(StrokeAttribute&&) = default;
+
+ bool StrokeAttribute::operator==(const StrokeAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpStrokeAttribute == mpStrokeAttribute;
+ }
+
+ const std::vector< double >& StrokeAttribute::getDotDashArray() const
+ {
+ return mpStrokeAttribute->getDotDashArray();
+ }
+
+ double StrokeAttribute::getFullDotDashLen() const
+ {
+ return mpStrokeAttribute->getFullDotDashLen();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
new file mode 100644
index 000000000..be58c77dd
--- /dev/null
+++ b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/graphic/XPrimitive2DRenderer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase2.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/canvastools.hxx>
+#include <com/sun/star/geometry/RealRectangle2D.hpp>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+
+#include <drawinglayer/converters.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+
+using namespace ::com::sun::star;
+
+
+namespace drawinglayer::unorenderer
+{
+ namespace {
+
+ class XPrimitive2DRenderer:
+ public cppu::WeakAggImplHelper2<
+ css::graphic::XPrimitive2DRenderer, css::lang::XServiceInfo>
+ {
+ public:
+ XPrimitive2DRenderer();
+
+ XPrimitive2DRenderer(const XPrimitive2DRenderer&) = delete;
+ const XPrimitive2DRenderer& operator=(const XPrimitive2DRenderer&) = delete;
+
+ // XPrimitive2DRenderer
+ virtual uno::Reference< rendering::XBitmap > SAL_CALL rasterize(
+ const uno::Sequence< uno::Reference< graphic::XPrimitive2D > >& Primitive2DSequence,
+ const uno::Sequence< beans::PropertyValue >& aViewInformationSequence,
+ ::sal_uInt32 DPI_X,
+ ::sal_uInt32 DPI_Y,
+ const css::geometry::RealRectangle2D& Range,
+ ::sal_uInt32 MaximumQuadraticPixels) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString&) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+ };
+
+ }
+
+ XPrimitive2DRenderer::XPrimitive2DRenderer()
+ {
+ }
+
+ uno::Reference< rendering::XBitmap > XPrimitive2DRenderer::rasterize(
+ const uno::Sequence< uno::Reference< graphic::XPrimitive2D > >& aPrimitive2DSequence,
+ const uno::Sequence< beans::PropertyValue >& aViewInformationSequence,
+ ::sal_uInt32 DPI_X,
+ ::sal_uInt32 DPI_Y,
+ const css::geometry::RealRectangle2D& Range,
+ ::sal_uInt32 MaximumQuadraticPixels)
+ {
+ o3tl::Length eRangeUnit = o3tl::Length::mm100;
+ comphelper::SequenceAsHashMap aViewInformationMap(aViewInformationSequence);
+ auto it = aViewInformationMap.find("RangeUnit");
+ if (it != aViewInformationMap.end())
+ {
+ sal_Int32 nVal{};
+ it->second >>= nVal;
+ eRangeUnit = static_cast<o3tl::Length>(nVal);
+ }
+
+ uno::Reference< rendering::XBitmap > XBitmap;
+
+ if(aPrimitive2DSequence.hasElements())
+ {
+ const basegfx::B2DRange aRange(Range.X1, Range.Y1, Range.X2, Range.Y2);
+ const double fWidth(aRange.getWidth());
+ const double fHeight(aRange.getHeight());
+
+ if(basegfx::fTools::more(fWidth, 0.0) && basegfx::fTools::more(fHeight, 0.0))
+ {
+ if(0 == DPI_X)
+ {
+ DPI_X = 75;
+ }
+
+ if(0 == DPI_Y)
+ {
+ DPI_Y = 75;
+ }
+
+ if(0 == MaximumQuadraticPixels)
+ {
+ MaximumQuadraticPixels = 500000;
+ }
+
+ const auto aViewInformation2D = geometry::createViewInformation2D(aViewInformationSequence);
+ const sal_uInt32 nDiscreteWidth(basegfx::fround(o3tl::convert(fWidth, eRangeUnit, o3tl::Length::in) * DPI_X));
+ const sal_uInt32 nDiscreteHeight(basegfx::fround(o3tl::convert(fHeight, eRangeUnit, o3tl::Length::in) * DPI_Y));
+
+ basegfx::B2DHomMatrix aEmbedding(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -aRange.getMinX(),
+ -aRange.getMinY()));
+
+ aEmbedding.scale(
+ nDiscreteWidth / fWidth,
+ nDiscreteHeight / fHeight);
+
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(
+ aEmbedding,
+ aPrimitive2DSequence));
+ primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+
+ BitmapEx aBitmapEx(
+ convertToBitmapEx(
+ std::move(xEmbedSeq),
+ aViewInformation2D,
+ nDiscreteWidth,
+ nDiscreteHeight,
+ MaximumQuadraticPixels));
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ aBitmapEx.SetPrefSize(Size(basegfx::fround(fWidth), basegfx::fround(fHeight)));
+ XBitmap = vcl::unotools::xBitmapFromBitmapEx(aBitmapEx);
+ }
+ }
+ }
+
+ return XBitmap;
+ }
+
+ OUString SAL_CALL XPrimitive2DRenderer::getImplementationName()
+ {
+ return "drawinglayer::unorenderer::XPrimitive2DRenderer";
+ }
+
+ sal_Bool SAL_CALL XPrimitive2DRenderer::supportsService(const OUString& rServiceName)
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ uno::Sequence< OUString > SAL_CALL XPrimitive2DRenderer::getSupportedServiceNames()
+ {
+ return { "com.sun.star.graphic.Primitive2DTools" };
+ }
+
+} // end of namespace
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+drawinglayer_XPrimitive2DRenderer(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new drawinglayer::unorenderer::XPrimitive2DRenderer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/dumper/EnhancedShapeDumper.cxx b/drawinglayer/source/dumper/EnhancedShapeDumper.cxx
new file mode 100644
index 000000000..6e83f15db
--- /dev/null
+++ b/drawinglayer/source/dumper/EnhancedShapeDumper.cxx
@@ -0,0 +1,1081 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "EnhancedShapeDumper.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+using namespace com::sun::star;
+
+
+// ---------- EnhancedCustomShapeExtrusion.idl ----------
+
+
+void EnhancedShapeDumper::dumpEnhancedCustomShapeExtrusionService(const uno::Reference< beans::XPropertySet >& xPropSet)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Extrusion");
+ bool bExtrusion;
+ if(anotherAny >>= bExtrusion)
+ dumpExtrusionAsAttribute(bExtrusion);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Brightness");
+ double aBrightness = double();
+ if(anotherAny >>= aBrightness)
+ dumpBrightnessAsAttribute(aBrightness);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Depth");
+ drawing::EnhancedCustomShapeParameterPair aDepth;
+ if(anotherAny >>= aDepth)
+ dumpDepthAsElement(aDepth);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Diffusion");
+ double aDiffusion = double();
+ if(anotherAny >>= aDiffusion)
+ dumpDiffusionAsAttribute(aDiffusion);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("NumberOfLineSegments");
+ sal_Int32 aNumberOfLineSegments = sal_Int32();
+ if(anotherAny >>= aNumberOfLineSegments)
+ dumpNumberOfLineSegmentsAsAttribute(aNumberOfLineSegments);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LightFace");
+ bool bLightFace;
+ if(anotherAny >>= bLightFace)
+ dumpLightFaceAsAttribute(bLightFace);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FirstLightHarsh");
+ bool bFirstLightHarsh;
+ if(anotherAny >>= bFirstLightHarsh)
+ dumpFirstLightHarshAsAttribute(bFirstLightHarsh);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("SecondLightHarsh");
+ bool bSecondLightHarsh;
+ if(anotherAny >>= bSecondLightHarsh)
+ dumpSecondLightHarshAsAttribute(bSecondLightHarsh);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FirstLightLevel");
+ double aFirstLightLevel = double();
+ if(anotherAny >>= aFirstLightLevel)
+ dumpFirstLightLevelAsAttribute(aFirstLightLevel);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("SecondLightLevel");
+ double aSecondLightLevel = double();
+ if(anotherAny >>= aSecondLightLevel)
+ dumpSecondLightLevelAsAttribute(aSecondLightLevel);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FirstLightDirection");
+ drawing::Direction3D aFirstLightDirection;
+ if(anotherAny >>= aFirstLightDirection)
+ dumpFirstLightDirectionAsElement(aFirstLightDirection);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("SecondLightDirection");
+ drawing::Direction3D aSecondLightDirection;
+ if(anotherAny >>= aSecondLightDirection)
+ dumpSecondLightDirectionAsElement(aSecondLightDirection);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Metal");
+ bool bMetal;
+ if(anotherAny >>= bMetal)
+ dumpMetalAsAttribute(bMetal);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ShadeMode");
+ drawing::ShadeMode eShadeMode;
+ if(anotherAny >>= eShadeMode)
+ dumpShadeModeAsAttribute(eShadeMode);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RotateAngle");
+ drawing::EnhancedCustomShapeParameterPair aRotateAngle;
+ if(anotherAny >>= aRotateAngle)
+ dumpRotateAngleAsElement(aRotateAngle);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RotationCenter");
+ drawing::Direction3D aRotationCenter;
+ if(anotherAny >>= aRotationCenter)
+ dumpRotationCenterAsElement(aRotationCenter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Shininess");
+ double aShininess = double();
+ if(anotherAny >>= aShininess)
+ dumpShininessAsAttribute(aShininess);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Skew");
+ drawing::EnhancedCustomShapeParameterPair aSkew;
+ if(anotherAny >>= aSkew)
+ dumpSkewAsElement(aSkew);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Specularity");
+ double aSpecularity = double();
+ if(anotherAny >>= aSpecularity)
+ dumpSpecularityAsAttribute(aSpecularity);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ProjectionMode");
+ drawing::ProjectionMode eProjectionMode;
+ if(anotherAny >>= eProjectionMode)
+ dumpProjectionModeAsAttribute(eProjectionMode);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ViewPoint");
+ drawing::Position3D aViewPoint;
+ if(anotherAny >>= aViewPoint)
+ dumpViewPointAsElement(aViewPoint);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Origin");
+ drawing::EnhancedCustomShapeParameterPair aOrigin;
+ if(anotherAny >>= aOrigin)
+ dumpOriginAsElement(aOrigin);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ExtrusionColor");
+ bool bExtrusionColor;
+ if(anotherAny >>= bExtrusionColor)
+ dumpExtrusionColorAsAttribute(bExtrusionColor);
+ }
+}
+void EnhancedShapeDumper::dumpExtrusionAsAttribute(bool bExtrusion)
+{
+ if(bExtrusion)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusion"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusion"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpBrightnessAsAttribute(double aBrightness)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("brightness"), "%f", aBrightness);
+}
+
+void EnhancedShapeDumper::dumpEnhancedCustomShapeParameterPair(
+ const drawing::EnhancedCustomShapeParameterPair& aParameterPair)
+{
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "First" ));
+ dumpEnhancedCustomShapeParameter(aParameterPair.First);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Second" ));
+ dumpEnhancedCustomShapeParameter(aParameterPair.Second);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+}
+
+void EnhancedShapeDumper::dumpDepthAsElement(const drawing::EnhancedCustomShapeParameterPair& aDepth)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Depth" ));
+ dumpEnhancedCustomShapeParameterPair(aDepth);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpDiffusionAsAttribute(double aDiffusion)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("diffusion"), "%f", aDiffusion);
+}
+
+void EnhancedShapeDumper::dumpNumberOfLineSegmentsAsAttribute(sal_Int32 aNumberOfLineSegments)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("numberOfLineSegments"), "%" SAL_PRIdINT32, aNumberOfLineSegments);
+}
+
+void EnhancedShapeDumper::dumpLightFaceAsAttribute(bool bLightFace)
+{
+ if(bLightFace)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lightFace"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lightFace"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpFirstLightHarshAsAttribute(bool bFirstLightHarsh)
+{
+ if(bFirstLightHarsh)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightHarsh"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightHarsh"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpSecondLightHarshAsAttribute(bool bSecondLightHarsh)
+{
+ if(bSecondLightHarsh)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightHarsh"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightHarsh"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpFirstLightLevelAsAttribute(double aFirstLightLevel)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightLevel"), "%f", aFirstLightLevel);
+}
+
+void EnhancedShapeDumper::dumpSecondLightLevelAsAttribute(double aSecondLightLevel)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightLevel"), "%f", aSecondLightLevel);
+}
+
+void EnhancedShapeDumper::dumpDirection3D(drawing::Direction3D aDirection3D)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionX"), "%f", aDirection3D.DirectionX);
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionY"), "%f", aDirection3D.DirectionY);
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionZ"), "%f", aDirection3D.DirectionZ);
+}
+
+void EnhancedShapeDumper::dumpFirstLightDirectionAsElement(drawing::Direction3D aFirstLightDirection)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FirstLightDirection" ));
+ dumpDirection3D(aFirstLightDirection);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpSecondLightDirectionAsElement(drawing::Direction3D aSecondLightDirection)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "SecondLightDirection" ));
+ dumpDirection3D(aSecondLightDirection);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpMetalAsAttribute(bool bMetal)
+{
+ if(bMetal)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("metal"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("metal"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpShadeModeAsAttribute(drawing::ShadeMode eShadeMode)
+{
+ switch(eShadeMode)
+ {
+ case drawing::ShadeMode_FLAT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "FLAT");
+ break;
+ case drawing::ShadeMode_PHONG:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "PHONG");
+ break;
+ case drawing::ShadeMode_SMOOTH:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "SMOOTH");
+ break;
+ case drawing::ShadeMode_DRAFT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "DRAFT");
+ break;
+ default:
+ break;
+ }
+}
+
+void EnhancedShapeDumper::dumpRotateAngleAsElement(const drawing::EnhancedCustomShapeParameterPair& aRotateAngle)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RotateAngle" ));
+ dumpEnhancedCustomShapeParameterPair(aRotateAngle);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpRotationCenterAsElement(drawing::Direction3D aRotationCenter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RotationCenter" ));
+ dumpDirection3D(aRotationCenter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpShininessAsAttribute(double aShininess)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shininess"), "%f", aShininess);
+}
+
+void EnhancedShapeDumper::dumpSkewAsElement(const drawing::EnhancedCustomShapeParameterPair& aSkew)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Skew" ));
+ dumpEnhancedCustomShapeParameterPair(aSkew);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpSpecularityAsAttribute(double aSpecularity)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("specularity"), "%f", aSpecularity);
+}
+
+void EnhancedShapeDumper::dumpProjectionModeAsAttribute(drawing::ProjectionMode eProjectionMode)
+{
+ switch(eProjectionMode)
+ {
+ case drawing::ProjectionMode_PARALLEL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("projectionMode"), "%s", "PARALLEL");
+ break;
+ case drawing::ProjectionMode_PERSPECTIVE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("projectionMode"), "%s", "PERSPECTIVE");
+ break;
+ default:
+ break;
+ }
+}
+
+void EnhancedShapeDumper::dumpViewPointAsElement(drawing::Position3D aViewPoint)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ViewPoint" ));
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionX"), "%f", aViewPoint.PositionX);
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionY"), "%f", aViewPoint.PositionY);
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionZ"), "%f", aViewPoint.PositionZ);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpOriginAsElement(const drawing::EnhancedCustomShapeParameterPair& aOrigin)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Origin" ));
+ dumpEnhancedCustomShapeParameterPair(aOrigin);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpExtrusionColorAsAttribute(bool bExtrusionColor)
+{
+ if(bExtrusionColor)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionColor"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionColor"), "%s", "false");
+}
+
+
+// ---------- EnhancedCustomShapeGeometry.idl -----------
+
+
+void EnhancedShapeDumper::dumpEnhancedCustomShapeGeometryService(const uno::Reference< beans::XPropertySet >& xPropSet)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Type");
+ OUString sType;
+ if(anotherAny >>= sType)
+ dumpTypeAsAttribute(sType);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ViewBox");
+ awt::Rectangle aViewBox;
+ if(anotherAny >>= aViewBox)
+ dumpViewBoxAsElement(aViewBox);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("MirroredX");
+ bool bMirroredX;
+ if(anotherAny >>= bMirroredX)
+ dumpMirroredXAsAttribute(bMirroredX);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("MirroredY");
+ bool bMirroredY;
+ if(anotherAny >>= bMirroredY)
+ dumpMirroredYAsAttribute(bMirroredY);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextRotateAngle");
+ double aTextRotateAngle = double();
+ if(anotherAny >>= aTextRotateAngle)
+ dumpTextRotateAngleAsAttribute(aTextRotateAngle);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("AdjustmentValues");
+ uno::Sequence< drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentValues;
+ if(anotherAny >>= aAdjustmentValues)
+ dumpAdjustmentValuesAsElement(aAdjustmentValues);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Extrusion");
+ uno::Sequence< beans::PropertyValue > aExtrusion;
+ if(anotherAny >>= aExtrusion)
+ dumpExtrusionAsElement(aExtrusion);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Path");
+ uno::Sequence< beans::PropertyValue > aPath;
+ if(anotherAny >>= aPath)
+ dumpPathAsElement(aPath);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextPath");
+ uno::Sequence< beans::PropertyValue > aTextPath;
+ if(anotherAny >>= aTextPath)
+ dumpTextPathAsElement(aTextPath);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Equations");
+ uno::Sequence< OUString > aEquations;
+ if(anotherAny >>= aEquations)
+ dumpEquationsAsElement(aEquations);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Handles");
+ uno::Sequence< beans::PropertyValues > aHandles;
+ if(anotherAny >>= aHandles)
+ dumpHandlesAsElement(aHandles);
+ }
+}
+void EnhancedShapeDumper::dumpTypeAsAttribute(std::u16string_view sType)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%s",
+ OUStringToOString(sType, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void EnhancedShapeDumper::dumpViewBoxAsElement(awt::Rectangle aViewBox)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ViewBox" ));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("x"), "%" SAL_PRIdINT32, aViewBox.X);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("y"), "%" SAL_PRIdINT32, aViewBox.Y);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, aViewBox.Width);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, aViewBox.Height);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpMirroredXAsAttribute(bool bMirroredX)
+{
+ if(bMirroredX)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredX"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredX"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpMirroredYAsAttribute(bool bMirroredY)
+{
+ if(bMirroredY)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredY"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredY"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpTextRotateAngleAsAttribute(double aTextRotateAngle)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textRotateAngle"), "%f", aTextRotateAngle);
+}
+
+void EnhancedShapeDumper::dumpAdjustmentValuesAsElement(const uno::Sequence< drawing::EnhancedCustomShapeAdjustmentValue>& aAdjustmentValues)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "AdjustmentValues" ));
+ for (const auto& i : aAdjustmentValues)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeAdjustmentValue" ));
+ uno::Any aAny = i.Value;
+ OUString sValue;
+ float fValue;
+ sal_Int32 nValue;
+ bool bValue;
+ if(aAny >>= sValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s",
+ OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ else if(aAny >>= nValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue);
+ }
+ else if(aAny >>= fValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue);
+ }
+ else if(aAny >>= bValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false"));
+ }
+
+ switch(i.State)
+ {
+ case beans::PropertyState_DIRECT_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE");
+ break;
+ case beans::PropertyState_DEFAULT_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE");
+ break;
+ case beans::PropertyState_AMBIGUOUS_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE");
+ break;
+ default:
+ break;
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpPropertyValueAsElement(const beans::PropertyValue& aPropertyValue)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValue" ));
+
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s",
+ OUStringToOString(aPropertyValue.Name, RTL_TEXTENCODING_UTF8).getStr());
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("handle"), "%" SAL_PRIdINT32, aPropertyValue.Handle);
+
+ uno::Any aAny = aPropertyValue.Value;
+ OUString sValue;
+ if(aAny >>= sValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s",
+ OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ switch(aPropertyValue.State)
+ {
+ case beans::PropertyState_DIRECT_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE");
+ break;
+ case beans::PropertyState_DEFAULT_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE");
+ break;
+ case beans::PropertyState_AMBIGUOUS_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE");
+ break;
+ default:
+ break;
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpExtrusionAsElement(const uno::Sequence< beans::PropertyValue >& aExtrusion)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Extrusion" ));
+ for (const auto& i : aExtrusion)
+ {
+ dumpPropertyValueAsElement(i);
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpPathAsElement(const uno::Sequence< beans::PropertyValue >& aPath)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Path" ));
+ for (const auto& i : aPath)
+ {
+ dumpPropertyValueAsElement(i);
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpTextPathAsElement(const uno::Sequence< beans::PropertyValue >& aTextPath)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TextPath" ));
+ for (const auto& i : aTextPath)
+ {
+ dumpPropertyValueAsElement(i);
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpEquationsAsElement(const uno::Sequence< OUString >& aEquations)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Equations" ));
+ for (const auto& i : aEquations)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s",
+ OUStringToOString(i, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+// PropertyValues specifies a sequence of PropertyValue instances.
+// so in this case it's a Sequence of a Sequence of a PropertyValue instances.
+// Welcome to Sequenception again.
+void EnhancedShapeDumper::dumpHandlesAsElement(const uno::Sequence< beans::PropertyValues >& aHandles)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Handles" ));
+ for (const auto& i : aHandles)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValues" ));
+ uno::Sequence< beans::PropertyValue > propertyValueSequence = i;
+ for (const auto& j : propertyValueSequence)
+ {
+ dumpPropertyValueAsElement(j);
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+
+// ---------- EnhancedCustomShapeHandle.idl -----------
+
+
+void EnhancedShapeDumper::dumpEnhancedCustomShapeHandleService(const uno::Reference< beans::XPropertySet >& xPropSet)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("MirroredX");
+ bool bMirroredX;
+ if(anotherAny >>= bMirroredX)
+ dumpMirroredXAsAttribute(bMirroredX);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("MirroredY");
+ bool bMirroredY;
+ if(anotherAny >>= bMirroredY)
+ dumpMirroredYAsAttribute(bMirroredY);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Switched");
+ bool bSwitched;
+ if(anotherAny >>= bSwitched)
+ dumpSwitchedAsAttribute(bSwitched);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Position");
+ drawing::EnhancedCustomShapeParameterPair aPosition;
+ if(anotherAny >>= aPosition)
+ dumpPositionAsElement(aPosition);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Polar");
+ drawing::EnhancedCustomShapeParameterPair aPolar;
+ if(anotherAny >>= aPolar)
+ dumpPolarAsElement(aPolar);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RefX");
+ sal_Int32 aRefX = sal_Int32();
+ if(anotherAny >>= aRefX)
+ dumpRefXAsAttribute(aRefX);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RefY");
+ sal_Int32 aRefY = sal_Int32();
+ if(anotherAny >>= aRefY)
+ dumpRefYAsAttribute(aRefY);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RefAngle");
+ sal_Int32 aRefAngle = sal_Int32();
+ if(anotherAny >>= aRefAngle)
+ dumpRefAngleAsAttribute(aRefAngle);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RefR");
+ sal_Int32 aRefR = sal_Int32();
+ if(anotherAny >>= aRefR)
+ dumpRefRAsAttribute(aRefR);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RangeXMinimum");
+ drawing::EnhancedCustomShapeParameter aRangeXMinimum;
+ if(anotherAny >>= aRangeXMinimum)
+ dumpRangeXMinimumAsElement(aRangeXMinimum);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RangeXMaximum");
+ drawing::EnhancedCustomShapeParameter aRangeXMaximum;
+ if(anotherAny >>= aRangeXMaximum)
+ dumpRangeXMaximumAsElement(aRangeXMaximum);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RangeYMinimum");
+ drawing::EnhancedCustomShapeParameter aRangeYMinimum;
+ if(anotherAny >>= aRangeYMinimum)
+ dumpRangeYMinimumAsElement(aRangeYMinimum);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RangeYMaximum");
+ drawing::EnhancedCustomShapeParameter aRangeYMaximum;
+ if(anotherAny >>= aRangeYMaximum)
+ dumpRangeYMaximumAsElement(aRangeYMaximum);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RadiusRangeMinimum");
+ drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
+ if(anotherAny >>= aRadiusRangeMinimum)
+ dumpRadiusRangeMinimumAsElement(aRadiusRangeMinimum);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("RadiusRangeMaximum");
+ drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
+ if(anotherAny >>= aRadiusRangeMaximum)
+ dumpRadiusRangeMaximumAsElement(aRadiusRangeMaximum);
+ }
+}
+
+void EnhancedShapeDumper::dumpSwitchedAsAttribute(bool bSwitched)
+{
+ if(bSwitched)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("switched"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("switched"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpPositionAsElement(const drawing::EnhancedCustomShapeParameterPair& aPosition)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Position" ));
+ dumpEnhancedCustomShapeParameterPair(aPosition);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpPolarAsElement(const drawing::EnhancedCustomShapeParameterPair& aPolar)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Polar" ));
+ dumpEnhancedCustomShapeParameterPair(aPolar);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpRefXAsAttribute(sal_Int32 aRefX)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refX"), "%" SAL_PRIdINT32, aRefX);
+}
+
+void EnhancedShapeDumper::dumpRefYAsAttribute(sal_Int32 aRefY)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refY"), "%" SAL_PRIdINT32, aRefY);
+}
+
+void EnhancedShapeDumper::dumpRefAngleAsAttribute(sal_Int32 aRefAngle)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refAngle"), "%" SAL_PRIdINT32, aRefAngle);
+}
+
+void EnhancedShapeDumper::dumpRefRAsAttribute(sal_Int32 aRefR)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refR"), "%" SAL_PRIdINT32, aRefR);
+}
+
+void EnhancedShapeDumper::dumpEnhancedCustomShapeParameter(
+ const drawing::EnhancedCustomShapeParameter& aParameter)
+{
+ uno::Any aAny = aParameter.Value;
+ OUString sValue;
+ float fValue;
+ sal_Int32 nValue;
+ bool bValue;
+ if(aAny >>= sValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s",
+ OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ else if(aAny >>= nValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue);
+ }
+ else if(aAny >>= fValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue);
+ }
+ else if(aAny >>= bValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false"));
+ }
+ sal_Int32 aType = aParameter.Type;
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%" SAL_PRIdINT32, aType);
+}
+
+void EnhancedShapeDumper::dumpRangeXMinimumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeXMinimum)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeXMinimum" ));
+ dumpEnhancedCustomShapeParameter(aRangeXMinimum);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpRangeXMaximumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeXMaximum)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeXMaximum" ));
+ dumpEnhancedCustomShapeParameter(aRangeXMaximum);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpRangeYMinimumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeYMinimum)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeYMinimum" ));
+ dumpEnhancedCustomShapeParameter(aRangeYMinimum);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpRangeYMaximumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeYMaximum)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeYMaximum" ));
+ dumpEnhancedCustomShapeParameter(aRangeYMaximum);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpRadiusRangeMinimumAsElement(const drawing::EnhancedCustomShapeParameter& aRadiusRangeMinimum)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RadiusRangeMinimum" ));
+ dumpEnhancedCustomShapeParameter(aRadiusRangeMinimum);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpRadiusRangeMaximumAsElement(const drawing::EnhancedCustomShapeParameter& aRadiusRangeMaximum)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RadiusRangeMaximum" ));
+ dumpEnhancedCustomShapeParameter(aRadiusRangeMaximum);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+
+// ---------- EnhancedCustomShapePath.idl ---------------
+
+
+void EnhancedShapeDumper::dumpEnhancedCustomShapePathService(const uno::Reference< beans::XPropertySet >& xPropSet)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Coordinates");
+ uno::Sequence< drawing::EnhancedCustomShapeParameterPair > aCoordinates;
+ if(anotherAny >>= aCoordinates)
+ dumpCoordinatesAsElement(aCoordinates);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Segments");
+ uno::Sequence< drawing::EnhancedCustomShapeSegment > aSegments;
+ if(anotherAny >>= aSegments)
+ dumpSegmentsAsElement(aSegments);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("StretchX");
+ sal_Int32 aStretchX = sal_Int32();
+ if(anotherAny >>= aStretchX)
+ dumpStretchXAsAttribute(aStretchX);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("StretchY");
+ sal_Int32 aStretchY = sal_Int32();
+ if(anotherAny >>= aStretchY)
+ dumpStretchYAsAttribute(aStretchY);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextFrames");
+ uno::Sequence< drawing::EnhancedCustomShapeTextFrame > aTextFrames;
+ if(anotherAny >>= aTextFrames)
+ dumpTextFramesAsElement(aTextFrames);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("GluePoints");
+ uno::Sequence< drawing::EnhancedCustomShapeParameterPair > aGluePoints;
+ if(anotherAny >>= aGluePoints)
+ dumpGluePointsAsElement(aGluePoints);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("GluePointLeavingDirections");
+ uno::Sequence< double > aGluePointLeavingDirections;
+ if(anotherAny >>= aGluePointLeavingDirections)
+ dumpGluePointLeavingDirectionsAsElement(aGluePointLeavingDirections);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("GluePointType");
+ sal_Int32 aGluePointType = sal_Int32();
+ if(anotherAny >>= aGluePointType)
+ dumpGluePointTypeAsAttribute(aGluePointType);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ExtrusionAllowed");
+ bool bExtrusionAllowed;
+ if(anotherAny >>= bExtrusionAllowed)
+ dumpExtrusionAllowedAsAttribute(bExtrusionAllowed);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ConcentricGradientFillAllowed");
+ bool bConcentricGradientFillAllowed;
+ if(anotherAny >>= bConcentricGradientFillAllowed)
+ dumpConcentricGradientFillAllowedAsAttribute(bConcentricGradientFillAllowed);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextPathAllowed");
+ bool bTextPathAllowed;
+ if(anotherAny >>= bTextPathAllowed)
+ dumpTextPathAllowedAsAttribute(bTextPathAllowed);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("SubViewSize");
+ uno::Sequence< awt::Size > aSubViewSize;
+ if(anotherAny >>= aSubViewSize)
+ dumpSubViewSizeAsElement(aSubViewSize);
+ }
+}
+
+void EnhancedShapeDumper::dumpCoordinatesAsElement(const uno::Sequence< drawing::EnhancedCustomShapeParameterPair >& aCoordinates)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Coordinates" ));
+ for (const auto& i : aCoordinates)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeParameterPair" ));
+ dumpEnhancedCustomShapeParameterPair(i);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpSegmentsAsElement(const uno::Sequence< drawing::EnhancedCustomShapeSegment >& aSegments)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Segments" ));
+ for (const auto& i : aSegments)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeSegment" ));
+ sal_Int32 aCommand = i.Command;
+ sal_Int32 aCount = i.Count;
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("command"), "%" SAL_PRIdINT32, aCommand);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("count"), "%" SAL_PRIdINT32, aCount);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpStretchXAsAttribute(sal_Int32 aStretchX)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stretchX"), "%" SAL_PRIdINT32, aStretchX);
+}
+
+void EnhancedShapeDumper::dumpStretchYAsAttribute(sal_Int32 aStretchY)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stretchY"), "%" SAL_PRIdINT32, aStretchY);
+}
+
+void EnhancedShapeDumper::dumpTextFramesAsElement(const uno::Sequence< drawing::EnhancedCustomShapeTextFrame >& aTextFrames)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TextFrames" ));
+ for (const auto& i : aTextFrames)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeTextFrame" ));
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TopLeft" ));
+ dumpEnhancedCustomShapeParameterPair(i.TopLeft);
+ (void)xmlTextWriterEndElement( xmlWriter );
+
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "BottomRight" ));
+ dumpEnhancedCustomShapeParameterPair(i.BottomRight);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpGluePointsAsElement(const uno::Sequence< drawing::EnhancedCustomShapeParameterPair >& aGluePoints)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "GluePoints" ));
+ for (const auto& i : aGluePoints)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeParameterPair" ));
+ dumpEnhancedCustomShapeParameterPair(i);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpGluePointLeavingDirectionsAsElement(const uno::Sequence< double >& aGluePointLeavingDirections)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "GluePointLeavingDirections" ));
+ for (const auto& i : aGluePointLeavingDirections)
+ {
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("value"), "%f", i);
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void EnhancedShapeDumper::dumpGluePointTypeAsAttribute(sal_Int32 aGluePointType)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("gluePointType"), "%" SAL_PRIdINT32, aGluePointType);
+}
+
+void EnhancedShapeDumper::dumpExtrusionAllowedAsAttribute(bool bExtrusionAllowed)
+{
+ if(bExtrusionAllowed)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionAllowed"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionAllowed"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpConcentricGradientFillAllowedAsAttribute(bool bConcentricGradientFillAllowed)
+{
+ if(bConcentricGradientFillAllowed)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("concentricGradientFillAllowed"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("concentricGradientFillAllowed"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpTextPathAllowedAsAttribute(bool bTextPathAllowed)
+{
+ if(bTextPathAllowed)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathAllowed"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathAllowed"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpSubViewSizeAsElement(const uno::Sequence< awt::Size >& aSubViewSize)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "SubViewSize" ));
+ for (const auto& i : aSubViewSize)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Size" ));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, i.Width);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, i.Height);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+
+// ---------- EnhancedCustomShapeTextPath.idl ---------------
+
+
+void EnhancedShapeDumper::dumpEnhancedCustomShapeTextPathService(const uno::Reference< beans::XPropertySet >& xPropSet)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextPath");
+ bool bTextPath;
+ if(anotherAny >>= bTextPath)
+ dumpTextPathAsAttribute(bTextPath);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextPathMode");
+ drawing::EnhancedCustomShapeTextPathMode eTextPathMode;
+ if(anotherAny >>= eTextPathMode)
+ dumpTextPathModeAsAttribute(eTextPathMode);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ScaleX");
+ bool bScaleX;
+ if(anotherAny >>= bScaleX)
+ dumpScaleXAsAttribute(bScaleX);
+ }
+}
+
+void EnhancedShapeDumper::dumpTextPathAsAttribute(bool bTextPath)
+{
+ if(bTextPath)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPath"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPath"), "%s", "false");
+}
+
+void EnhancedShapeDumper::dumpTextPathModeAsAttribute(drawing::EnhancedCustomShapeTextPathMode eTextPathMode)
+{
+ switch(eTextPathMode)
+ {
+ case drawing::EnhancedCustomShapeTextPathMode_NORMAL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "NORMAL");
+ break;
+ case drawing::EnhancedCustomShapeTextPathMode_PATH:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "PATH");
+ break;
+ case drawing::EnhancedCustomShapeTextPathMode_SHAPE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "SHAPE");
+ break;
+ default:
+ break;
+ }
+}
+
+void EnhancedShapeDumper::dumpScaleXAsAttribute(bool bScaleX)
+{
+ if(bScaleX)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("scaleX"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("scaleX"), "%s", "false");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/dumper/EnhancedShapeDumper.hxx b/drawinglayer/source/dumper/EnhancedShapeDumper.hxx
new file mode 100644
index 000000000..f6c8453e9
--- /dev/null
+++ b/drawinglayer/source/dumper/EnhancedShapeDumper.hxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <libxml/xmlwriter.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <com/sun/star/drawing/Position3D.hpp>
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameter.hpp>
+
+#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
+#include <com/sun/star/awt/Size.hpp>
+
+#include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp>
+
+class EnhancedShapeDumper
+{
+public:
+ explicit EnhancedShapeDumper(xmlTextWriterPtr writer)
+ : xmlWriter(writer)
+ {
+ }
+
+ // auxiliary functions
+ void dumpEnhancedCustomShapeParameterPair(
+ const css::drawing::EnhancedCustomShapeParameterPair& aParameterPair);
+ void dumpDirection3D(css::drawing::Direction3D aDirection3D);
+ void dumpPropertyValueAsElement(const css::beans::PropertyValue& aPropertyValue);
+ void
+ dumpEnhancedCustomShapeParameter(const css::drawing::EnhancedCustomShapeParameter& aParameter);
+
+ // EnhancedCustomShapeExtrusion.idl
+ void dumpEnhancedCustomShapeExtrusionService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet);
+ void dumpExtrusionAsAttribute(bool bExtrusion);
+ void dumpBrightnessAsAttribute(double aBrightness);
+ void dumpDepthAsElement(const css::drawing::EnhancedCustomShapeParameterPair& aDepth);
+ void dumpDiffusionAsAttribute(double aDiffusion);
+ void dumpNumberOfLineSegmentsAsAttribute(sal_Int32 aNumberOfLineSegments);
+ void dumpLightFaceAsAttribute(bool bLightFace);
+ void dumpFirstLightHarshAsAttribute(bool bFirstLightHarsh);
+ void dumpSecondLightHarshAsAttribute(bool bSecondLightHarsh);
+ void dumpFirstLightLevelAsAttribute(double aFirstLightLevel);
+ void dumpSecondLightLevelAsAttribute(double aSecondLightLevel);
+ void dumpFirstLightDirectionAsElement(css::drawing::Direction3D aFirstLightDirection);
+ void dumpSecondLightDirectionAsElement(css::drawing::Direction3D aSecondLightDirection);
+ void dumpMetalAsAttribute(bool bMetal);
+ void dumpShadeModeAsAttribute(css::drawing::ShadeMode eShadeMode);
+ void dumpRotateAngleAsElement(const css::drawing::EnhancedCustomShapeParameterPair& aRotateAngle);
+ void dumpRotationCenterAsElement(css::drawing::Direction3D aRotationCenter);
+ void dumpShininessAsAttribute(double aShininess);
+ void dumpSkewAsElement(const css::drawing::EnhancedCustomShapeParameterPair& aSkew);
+ void dumpSpecularityAsAttribute(double aSpecularity);
+ void dumpProjectionModeAsAttribute(css::drawing::ProjectionMode eProjectionMode);
+ void dumpViewPointAsElement(css::drawing::Position3D aViewPoint);
+ void dumpOriginAsElement(const css::drawing::EnhancedCustomShapeParameterPair& aOrigin);
+ void dumpExtrusionColorAsAttribute(bool bExtrusionColor);
+
+ // EnhancedCustomShapeGeometry.idl
+ void dumpEnhancedCustomShapeGeometryService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet);
+ void dumpTypeAsAttribute(std::u16string_view sType);
+ void dumpViewBoxAsElement(css::awt::Rectangle aViewBox);
+ void dumpMirroredXAsAttribute(bool bMirroredX); // also used in EnhancedCustomShapeHandle
+ void dumpMirroredYAsAttribute(bool bMirroredY); // also used in EnhancedCustomShapeHandle
+ void dumpTextRotateAngleAsAttribute(double aTextRotateAngle);
+ void dumpAdjustmentValuesAsElement(const css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue>& aAdjustmentValues);
+ void dumpExtrusionAsElement(const css::uno::Sequence< css::beans::PropertyValue >& aExtrusion);
+ void dumpPathAsElement(const css::uno::Sequence< css::beans::PropertyValue >& aPath);
+ void dumpTextPathAsElement(const css::uno::Sequence< css::beans::PropertyValue >& aTextPath);
+ void dumpEquationsAsElement(const css::uno::Sequence< OUString >& aEquations);
+ void dumpHandlesAsElement(const css::uno::Sequence< css::beans::PropertyValues >& aHandles);
+
+ // EnhancedCustomShapeHandle.idl
+ void dumpEnhancedCustomShapeHandleService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet);
+ void dumpSwitchedAsAttribute(bool bSwitched);
+ void dumpPositionAsElement(const css::drawing::EnhancedCustomShapeParameterPair& aPosition);
+ void dumpPolarAsElement(const css::drawing::EnhancedCustomShapeParameterPair& aPolar);
+ void dumpRefXAsAttribute(sal_Int32 aRefX);
+ void dumpRefYAsAttribute(sal_Int32 aRefY);
+ void dumpRefAngleAsAttribute(sal_Int32 aRefAngle);
+ void dumpRefRAsAttribute(sal_Int32 aRefR);
+ void dumpRangeXMinimumAsElement(const css::drawing::EnhancedCustomShapeParameter& aRangeXMinimum);
+ void dumpRangeXMaximumAsElement(const css::drawing::EnhancedCustomShapeParameter& aRangeXMaximum);
+ void dumpRangeYMinimumAsElement(const css::drawing::EnhancedCustomShapeParameter& aRangeYMinimum);
+ void dumpRangeYMaximumAsElement(const css::drawing::EnhancedCustomShapeParameter& aRangeXMaximum);
+ void dumpRadiusRangeMinimumAsElement(const css::drawing::EnhancedCustomShapeParameter& aRadiusRangeMinimum);
+ void dumpRadiusRangeMaximumAsElement(const css::drawing::EnhancedCustomShapeParameter& aRadiusRangeMaximum);
+
+ // EnhancedCustomShapePath.idl
+ void dumpEnhancedCustomShapePathService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet);
+ void dumpCoordinatesAsElement(const css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair >& aCoordinates);
+ void dumpSegmentsAsElement(const css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment >& aSegments);
+ void dumpStretchXAsAttribute(sal_Int32 aStretchX);
+ void dumpStretchYAsAttribute(sal_Int32 aStretchY);
+ void dumpTextFramesAsElement(const css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame >& aTextFrames);
+ void dumpGluePointsAsElement(const css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair >& aGluePoints);
+ void dumpGluePointLeavingDirectionsAsElement(const css::uno::Sequence< double >& aGluePointLeavingDirections);
+ void dumpGluePointTypeAsAttribute(sal_Int32 aGluePointType);
+ void dumpExtrusionAllowedAsAttribute(bool bExtrusionAllowed);
+ void dumpConcentricGradientFillAllowedAsAttribute(bool bConcentricGradientFillAllowed);
+ void dumpTextPathAllowedAsAttribute(bool bTextPathAllowed);
+ void dumpSubViewSizeAsElement(const css::uno::Sequence< css::awt::Size >& aSubViewSize);
+
+ // EnhancedCustomShapePath.idl
+ void dumpEnhancedCustomShapeTextPathService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet);
+ void dumpTextPathAsAttribute(bool bTextPath);
+ void dumpTextPathModeAsAttribute(css::drawing::EnhancedCustomShapeTextPathMode eTextPathMode);
+ void dumpScaleXAsAttribute(bool bScaleX);
+
+private:
+ xmlTextWriterPtr xmlWriter;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/dumper/XShapeDumper.cxx b/drawinglayer/source/dumper/XShapeDumper.cxx
new file mode 100644
index 000000000..d0e2cf97a
--- /dev/null
+++ b/drawinglayer/source/dumper/XShapeDumper.cxx
@@ -0,0 +1,1986 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <drawinglayer/XShapeDumper.hxx>
+#include "EnhancedShapeDumper.hxx"
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/drawing/RectanglePoint.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/drawing/LineJoint.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+
+#include <com/sun/star/drawing/PolygonKind.hpp>
+
+#include <com/sun/star/drawing/TextFitToSizeType.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/TextAnimationDirection.hpp>
+#include <com/sun/star/drawing/TextAnimationKind.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+
+#include <com/sun/star/drawing/HomogenMatrixLine3.hpp>
+#include <com/sun/star/drawing/HomogenMatrix3.hpp>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <rtl/strbuf.hxx>
+#include <libxml/xmlwriter.h>
+#include <iostream>
+#include <string_view>
+#include <rtl/ustring.hxx>
+
+#define DEBUG_DUMPER 0
+
+using namespace com::sun::star;
+
+namespace {
+
+// FUNCTION DECLARATIONS
+
+// auxiliary functions
+void dumpGradientProperty(const css::awt::Gradient& rGradient, xmlTextWriterPtr xmlWriter);
+void dumpPolyPolygonBezierCoords(const css::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoords, xmlTextWriterPtr xmlWriter);
+void dumpPointSequenceSequence(const css::drawing::PointSequenceSequence& rPointSequenceSequence, const uno::Sequence<uno::Sequence<drawing::PolygonFlags> >*, xmlTextWriterPtr xmlWriter);
+void dumpPropertyValueAsElement(const beans::PropertyValue& rPropertyValue, xmlTextWriterPtr xmlWriter);
+
+// FillProperties.idl
+void dumpFillStyleAsAttribute(css::drawing::FillStyle eFillStyle, xmlTextWriterPtr xmlWriter);
+void dumpFillColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter);
+void dumpFillTransparenceAsAttribute(sal_Int32 aTransparence, xmlTextWriterPtr xmlWriter);
+void dumpFillTransparenceGradientNameAsAttribute(std::u16string_view sTranspGradName, xmlTextWriterPtr xmlWriter);
+void dumpFillTransparenceGradientAsElement(const css::awt::Gradient& rTranspGrad, xmlTextWriterPtr xmlWriter);
+void dumpFillGradientNameAsAttribute(std::u16string_view sGradName, xmlTextWriterPtr xmlWriter);
+void dumpFillGradientAsElement(const css::awt::Gradient& rGradient, xmlTextWriterPtr xmlWriter);
+void dumpFillHatchAsElement(const css::drawing::Hatch& rHatch, xmlTextWriterPtr xmlWriter);
+void dumpFillBackgroundAsAttribute(bool bBackground, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapAsElement(const css::uno::Reference<css::awt::XBitmap>& xBitmap, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapPositionOffsetXAsAttribute(sal_Int32 aBitmapPositionOffsetX, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapPositionOffsetYAsAttribute(sal_Int32 aBitmapPositionOffsetY, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapOffsetXAsAttribute(sal_Int32 aBitmapOffsetX, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapOffsetYAsAttribute(sal_Int32 aBitmapOffsetY, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapRectanglePointAsAttribute(css::drawing::RectanglePoint eBitmapRectanglePoint, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapLogicalSizeAsAttribute(bool bBitmapLogicalSize, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapSizeXAsAttribute(sal_Int32 aBitmapSizeX, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapSizeYAsAttribute(sal_Int32 aBitmapSizeY, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapModeAsAttribute(css::drawing::BitmapMode eBitmapMode, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapStretchAsAttribute(bool bBitmapStretch, xmlTextWriterPtr xmlWriter);
+void dumpFillBitmapTileAsAttribute(bool bBitmapTile, xmlTextWriterPtr xmlWriter);
+
+// LineProperties.idl
+void dumpLineStyleAsAttribute(css::drawing::LineStyle eLineStyle, xmlTextWriterPtr xmlWriter);
+void dumpLineDashAsElement(const css::drawing::LineDash& rLineDash, xmlTextWriterPtr xmlWriter);
+void dumpLineDashNameAsAttribute(std::u16string_view sLineDashName, xmlTextWriterPtr xmlWriter);
+void dumpLineColorAsAttribute(sal_Int32 aLineColor, xmlTextWriterPtr xmlWriter);
+void dumpLineTransparenceAsAttribute(sal_Int32 aLineTransparence, xmlTextWriterPtr xmlWriter);
+void dumpLineWidthAsAttribute(sal_Int32 aLineWidth, xmlTextWriterPtr xmlWriter);
+void dumpLineJointAsAttribute(css::drawing::LineJoint eLineJoint, xmlTextWriterPtr xmlWriter);
+void dumpLineStartNameAsAttribute(std::u16string_view sLineStartName, xmlTextWriterPtr xmlWriter);
+void dumpLineEndNameAsAttribute(std::u16string_view sLineEndName, xmlTextWriterPtr xmlWriter);
+void dumpLineStartAsElement(const css::drawing::PolyPolygonBezierCoords& rLineStart, xmlTextWriterPtr xmlWriter);
+void dumpLineEndAsElement(const css::drawing::PolyPolygonBezierCoords& rLineEnd, xmlTextWriterPtr xmlWriter);
+void dumpLineStartCenterAsAttribute(bool bLineStartCenter, xmlTextWriterPtr xmlWriter);
+void dumpLineStartWidthAsAttribute(sal_Int32 aLineStartWidth, xmlTextWriterPtr xmlWriter);
+void dumpLineEndCenterAsAttribute(bool bLineEndCenter, xmlTextWriterPtr xmlWriter);
+void dumpLineEndWidthAsAttribute(sal_Int32 aLineEndWidth, xmlTextWriterPtr xmlWriter);
+
+// PolyPolygonDescriptor.idl
+void dumpPolygonKindAsAttribute(css::drawing::PolygonKind ePolygonKind, xmlTextWriterPtr xmlWriter);
+void dumpPolyPolygonAsElement(const css::drawing::PointSequenceSequence& rPolyPolygon, xmlTextWriterPtr xmlWriter);
+void dumpGeometryAsElement(const css::drawing::PointSequenceSequence& rGeometry, xmlTextWriterPtr xmlWriter);
+
+// CharacterProperties.idl
+void dumpCharHeightAsAttribute(float fHeight, xmlTextWriterPtr xmlWriter);
+void dumpCharColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter);
+
+// TextProperties.idl
+void dumpIsNumberingAsAttribute(bool bIsNumbering, xmlTextWriterPtr xmlWriter);
+void dumpTextAutoGrowHeightAsAttribute(bool bTextAutoGrowHeight, xmlTextWriterPtr xmlWriter);
+void dumpTextAutoGrowWidthAsAttribute(bool bTextAutoGrowWidth, xmlTextWriterPtr xmlWriter);
+void dumpTextContourFrameAsAttribute(bool bTextContourFrame, xmlTextWriterPtr xmlWriter);
+void dumpTextFitToSizeAsAttribute(css::drawing::TextFitToSizeType eTextFitToSize, xmlTextWriterPtr xmlWriter);
+void dumpTextHorizontalAdjustAsAttribute(css::drawing::TextHorizontalAdjust eTextHorizontalAdjust, xmlTextWriterPtr xmlWriter);
+void dumpTextVerticalAdjustAsAttribute(css::drawing::TextVerticalAdjust eTextVerticalAdjust, xmlTextWriterPtr xmlWriter);
+void dumpTextLeftDistanceAsAttribute(sal_Int32 aTextLeftDistance, xmlTextWriterPtr xmlWriter);
+void dumpTextRightDistanceAsAttribute(sal_Int32 aTextRightDistance, xmlTextWriterPtr xmlWriter);
+void dumpTextUpperDistanceAsAttribute(sal_Int32 aTextUpperDistance, xmlTextWriterPtr xmlWriter);
+void dumpTextLowerDistanceAsAttribute(sal_Int32 aTextLowerDistance, xmlTextWriterPtr xmlWriter);
+void dumpTextMaximumFrameHeightAsAttribute(sal_Int32 aTextMaximumFrameHeight, xmlTextWriterPtr xmlWriter);
+void dumpTextMaximumFrameWidthAsAttribute(sal_Int32 aTextMaximumFrameWidth, xmlTextWriterPtr xmlWriter);
+void dumpTextMinimumFrameHeightAsAttribute(sal_Int32 aTextMinimumFrameHeight, xmlTextWriterPtr xmlWriter);
+void dumpTextMinimumFrameWidthAsAttribute(sal_Int32 aTextMinimumFrameWidth, xmlTextWriterPtr xmlWriter);
+void dumpTextAnimationAmountAsAttribute(sal_Int32 aTextAnimationAmount, xmlTextWriterPtr xmlWriter);
+void dumpTextAnimationCountAsAttribute(sal_Int32 aTextAnimationCount, xmlTextWriterPtr xmlWriter);
+void dumpTextAnimationDelayAsAttribute(sal_Int32 aTextAnimationDelay, xmlTextWriterPtr xmlWriter);
+void dumpTextAnimationDirectionAsAttribute(css::drawing::TextAnimationDirection eTextAnimationDirection, xmlTextWriterPtr xmlWriter);
+void dumpTextAnimationKindAsAttribute(css::drawing::TextAnimationKind eTextAnimationKind, xmlTextWriterPtr xmlWriter);
+void dumpTextAnimationStartInsideAsAttribute(bool bTextAnimationStartInside, xmlTextWriterPtr xmlWriter);
+void dumpTextAnimationStopInsideAsAttribute(bool bTextAnimationStopInside, xmlTextWriterPtr xmlWriter);
+void dumpTextWritingModeAsAttribute(css::text::WritingMode eWritingMode, xmlTextWriterPtr xmlWriter);
+
+// ShadowProperties.idl
+void dumpShadowAsAttribute(bool bShadow, xmlTextWriterPtr xmlWriter);
+void dumpShadowColorAsAttribute(sal_Int32 aShadowColor, xmlTextWriterPtr xmlWriter);
+void dumpShadowTransparenceAsAttribute(sal_Int32 aShadowTransparence, xmlTextWriterPtr xmlWriter);
+void dumpShadowXDistanceAsAttribute(sal_Int32 aShadowXDistance, xmlTextWriterPtr xmlWriter);
+void dumpShadowYDistanceAsAttribute(sal_Int32 aShadowYDistance, xmlTextWriterPtr xmlWriter);
+
+//Shape.idl
+void dumpZOrderAsAttribute(sal_Int32 aZOrder, xmlTextWriterPtr xmlWriter);
+void dumpLayerIDAsAttribute(sal_Int32 aLayerID, xmlTextWriterPtr xmlWriter);
+void dumpLayerNameAsAttribute(std::u16string_view sLayerName, xmlTextWriterPtr xmlWriter);
+void dumpVisibleAsAttribute(bool bVisible, xmlTextWriterPtr xmlWriter);
+void dumpPrintableAsAttribute(bool bPrintable, xmlTextWriterPtr xmlWriter);
+void dumpMoveProtectAsAttribute(bool bMoveProtect, xmlTextWriterPtr xmlWriter);
+void dumpNameAsAttribute(std::u16string_view sName, xmlTextWriterPtr xmlWriter);
+void dumpSizeProtectAsAttribute(bool bSizeProtect, xmlTextWriterPtr xmlWriter);
+void dumpHomogenMatrixLine3(const css::drawing::HomogenMatrixLine3& rLine, xmlTextWriterPtr xmlWriter);
+void dumpTransformationAsElement(const css::drawing::HomogenMatrix3& rTransformation, xmlTextWriterPtr xmlWriter);
+void dumpNavigationOrderAsAttribute(sal_Int32 aNavigationOrder, xmlTextWriterPtr xmlWriter);
+void dumpHyperlinkAsAttribute(std::u16string_view sHyperlink, xmlTextWriterPtr xmlWriter);
+void dumpInteropGrabBagAsElement(const uno::Sequence< beans::PropertyValue>& aInteropGrabBag, xmlTextWriterPtr xmlWriter);
+
+// CustomShape.idl
+void dumpCustomShapeEngineAsAttribute(std::u16string_view sCustomShapeEngine, xmlTextWriterPtr xmlWriter);
+void dumpCustomShapeDataAsAttribute(
+ std::u16string_view sCustomShapeData, xmlTextWriterPtr xmlWriter);
+void dumpCustomShapeGeometryAsElement(const css::uno::Sequence< css::beans::PropertyValue>& aCustomShapeGeometry, xmlTextWriterPtr xmlWriter);
+void dumpCustomShapeReplacementURLAsAttribute(std::u16string_view sCustomShapeReplacementURL, xmlTextWriterPtr xmlWriter);
+
+// XShape.idl
+void dumpPositionAsAttribute(const css::awt::Point& rPoint, xmlTextWriterPtr xmlWriter);
+void dumpSizeAsAttribute(const css::awt::Size& rSize, xmlTextWriterPtr xmlWriter);
+
+// the rest
+void dumpShapeDescriptorAsAttribute( const css::uno::Reference< css::drawing::XShapeDescriptor >& xDescr, xmlTextWriterPtr xmlWriter );
+void dumpXShape(const css::uno::Reference< css::drawing::XShape >& xShape, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties);
+void dumpXShapes( const css::uno::Reference< css::drawing::XShapes >& xShapes, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties );
+void dumpTextPropertiesService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter);
+void dumpFillPropertiesService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter);
+void dumpLinePropertiesService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter);
+void dumpShadowPropertiesService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter);
+void dumpPolyPolygonDescriptorService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter);
+void dumpShapeService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties);
+void dumpPolyPolygonBezierDescriptorService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter);
+void dumpCustomShapeService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter);
+
+
+int writeCallback(void* pContext, const char* sBuffer, int nLen)
+{
+ OStringBuffer* pBuffer = static_cast<OStringBuffer*>(pContext);
+ pBuffer->append(sBuffer);
+ return nLen;
+}
+
+int closeCallback(void* )
+{
+ return 0;
+}
+
+bool m_bNameDumped;
+
+
+// ---------- FillProperties.idl ----------
+
+void dumpFillStyleAsAttribute(drawing::FillStyle eFillStyle, xmlTextWriterPtr xmlWriter)
+{
+ switch(eFillStyle)
+ {
+ case drawing::FillStyle_NONE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "NONE");
+ break;
+ case drawing::FillStyle_SOLID:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "SOLID");
+ break;
+ case drawing::FillStyle_GRADIENT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "GRADIENT");
+ break;
+ case drawing::FillStyle_HATCH:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "HATCH");
+ break;
+ case drawing::FillStyle_BITMAP:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "BITMAP");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpFillColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillColor"), "%06x", static_cast<unsigned int>(aColor));
+}
+
+void dumpFillTransparenceAsAttribute(sal_Int32 aTransparence, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillTransparence"), "%" SAL_PRIdINT32, aTransparence);
+}
+
+void dumpFillTransparenceGradientNameAsAttribute(std::u16string_view sTranspGradName, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillTransparenceGradientName"), "%s",
+ OUStringToOString(sTranspGradName, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+//because there's more awt::Gradient properties to dump
+void dumpGradientProperty(const awt::Gradient& rGradient, xmlTextWriterPtr xmlWriter)
+{
+ switch (rGradient.Style) //enum GradientStyle
+ {
+ case awt::GradientStyle_LINEAR:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "LINEAR");
+ break;
+ case awt::GradientStyle_AXIAL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "AXIAL");
+ break;
+ case awt::GradientStyle_RADIAL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RADIAL");
+ break;
+ case awt::GradientStyle_ELLIPTICAL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ELLIPTICAL");
+ break;
+ case awt::GradientStyle_SQUARE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "SQUARE");
+ break;
+ case awt::GradientStyle_RECT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECT");
+ break;
+ default:
+ break;
+ }
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("startColor"), "%06x", static_cast<unsigned int>(rGradient.StartColor));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("endColor"), "%06x", static_cast<unsigned int>(rGradient.EndColor));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("angle"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.Angle));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("border"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.Border));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("xOffset"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.XOffset));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("yOffset"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.YOffset));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("startIntensity"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.StartIntensity));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("endIntensity"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.EndIntensity));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stepCount"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.StepCount));
+}
+
+void dumpFillTransparenceGradientAsElement(const awt::Gradient& rTranspGrad, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillTransparenceGradient" ));
+ dumpGradientProperty(rTranspGrad, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpFillGradientNameAsAttribute(std::u16string_view sGradName, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillGradientName"), "%s",
+ OUStringToOString(sGradName, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpFillGradientAsElement(const awt::Gradient& rGradient, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillGradient" ));
+ dumpGradientProperty(rGradient, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpFillHatchAsElement(const drawing::Hatch& rHatch, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillHatch" ));
+ switch (rHatch.Style)
+ {
+ case drawing::HatchStyle_SINGLE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "SINGLE");
+ break;
+ case drawing::HatchStyle_DOUBLE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "DOUBLE");
+ break;
+ case drawing::HatchStyle_TRIPLE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "TRIPLE");
+ break;
+ default:
+ break;
+ }
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("color"), "%06x", static_cast<unsigned int>(rHatch.Color));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("distance"), "%" SAL_PRIdINT32, rHatch.Distance);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("angle"), "%" SAL_PRIdINT32, rHatch.Angle);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpFillBackgroundAsAttribute(bool bBackground, xmlTextWriterPtr xmlWriter)
+{
+ if(bBackground)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBackground"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBackground"), "%s", "false");
+}
+
+void dumpFillBitmapAsElement(const uno::Reference<awt::XBitmap>& xBitmap, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillBitmap" ));
+ if (xBitmap.is())
+ {
+ awt::Size const aSize = xBitmap->getSize();
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, aSize.Width);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, aSize.Height);
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpFillBitmapPositionOffsetXAsAttribute(sal_Int32 aBitmapPositionOffsetX, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapPositionOffsetX"), "%" SAL_PRIdINT32, aBitmapPositionOffsetX);
+}
+
+void dumpFillBitmapPositionOffsetYAsAttribute(sal_Int32 aBitmapPositionOffsetY, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapPositionOffsetY"), "%" SAL_PRIdINT32, aBitmapPositionOffsetY);
+}
+
+void dumpFillBitmapOffsetXAsAttribute(sal_Int32 aBitmapOffsetX, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapOffsetX"), "%" SAL_PRIdINT32, aBitmapOffsetX);
+}
+
+void dumpFillBitmapOffsetYAsAttribute(sal_Int32 aBitmapOffsetY, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapOffsetY"), "%" SAL_PRIdINT32, aBitmapOffsetY);
+}
+
+void dumpFillBitmapRectanglePointAsAttribute(drawing::RectanglePoint eBitmapRectanglePoint, xmlTextWriterPtr xmlWriter)
+{
+ switch(eBitmapRectanglePoint)
+ {
+ case drawing::RectanglePoint_LEFT_TOP:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_TOP");
+ break;
+ case drawing::RectanglePoint_MIDDLE_TOP:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_TOP");
+ break;
+ case drawing::RectanglePoint_RIGHT_TOP:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_TOP");
+ break;
+ case drawing::RectanglePoint_LEFT_MIDDLE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_MIDDLE");
+ break;
+ case drawing::RectanglePoint_MIDDLE_MIDDLE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_MIDDLE");
+ break;
+ case drawing::RectanglePoint_RIGHT_MIDDLE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_MIDDLE");
+ break;
+ case drawing::RectanglePoint_LEFT_BOTTOM:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_BOTTOM");
+ break;
+ case drawing::RectanglePoint_MIDDLE_BOTTOM:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_BOTTOM");
+ break;
+ case drawing::RectanglePoint_RIGHT_BOTTOM:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_BOTTOM");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpFillBitmapLogicalSizeAsAttribute(bool bBitmapLogicalSize, xmlTextWriterPtr xmlWriter)
+{
+ if(bBitmapLogicalSize)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapLogicalSize"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapLogicalSize"), "%s", "false");
+}
+
+void dumpFillBitmapSizeXAsAttribute(sal_Int32 aBitmapSizeX, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapSizeX"), "%" SAL_PRIdINT32, aBitmapSizeX);
+}
+
+void dumpFillBitmapSizeYAsAttribute(sal_Int32 aBitmapSizeY, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapSizeY"), "%" SAL_PRIdINT32, aBitmapSizeY);
+}
+
+void dumpFillBitmapModeAsAttribute(drawing::BitmapMode eBitmapMode, xmlTextWriterPtr xmlWriter)
+{
+ switch(eBitmapMode)
+ {
+ case drawing::BitmapMode_REPEAT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "REPEAT");
+ break;
+ case drawing::BitmapMode_STRETCH:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "STRETCH");
+ break;
+ case drawing::BitmapMode_NO_REPEAT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "NO_REPEAT");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpFillBitmapStretchAsAttribute(bool bBitmapStretch, xmlTextWriterPtr xmlWriter)
+{
+ if(bBitmapStretch)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapStretch"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapStretch"), "%s", "false");
+}
+
+void dumpFillBitmapTileAsAttribute(bool bBitmapTile, xmlTextWriterPtr xmlWriter)
+{
+ if(bBitmapTile)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapTile"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapTile"), "%s", "false");
+}
+
+
+// ---------- LineProperties.idl ----------
+
+
+void dumpLineStyleAsAttribute(drawing::LineStyle eLineStyle, xmlTextWriterPtr xmlWriter)
+{
+ switch(eLineStyle)
+ {
+ case drawing::LineStyle_NONE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "NONE");
+ break;
+ case drawing::LineStyle_SOLID:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "SOLID");
+ break;
+ case drawing::LineStyle_DASH:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "DASH");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpLineDashAsElement(const drawing::LineDash& rLineDash, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineDash" ));
+ switch (rLineDash.Style)
+ {
+ case drawing::DashStyle_RECT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECT");
+ break;
+ case drawing::DashStyle_ROUND:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ROUND");
+ break;
+ case drawing::DashStyle_RECTRELATIVE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECTRELATIVE");
+ break;
+ case drawing::DashStyle_ROUNDRELATIVE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ROUNDRELATIVE");
+ break;
+ default:
+ break;
+ }
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dots"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rLineDash.Dots));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dotLen"), "%" SAL_PRIdINT32, rLineDash.DotLen);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dashes"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rLineDash.Dashes));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dashLen"), "%" SAL_PRIdINT32, rLineDash.DashLen);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("distance"), "%" SAL_PRIdINT32, rLineDash.Distance);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpLineDashNameAsAttribute(std::u16string_view sLineDashName, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineDashName"), "%s",
+ OUStringToOString(sLineDashName, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpLineColorAsAttribute(sal_Int32 aLineColor, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineColor"), "%06x", static_cast<unsigned int>(aLineColor));
+}
+
+void dumpLineTransparenceAsAttribute(sal_Int32 aLineTransparence, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineTransparence"), "%" SAL_PRIdINT32, aLineTransparence);
+}
+
+void dumpLineWidthAsAttribute(sal_Int32 aLineWidth, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineWidth"), "%" SAL_PRIdINT32, aLineWidth);
+}
+
+void dumpLineJointAsAttribute(drawing::LineJoint eLineJoint, xmlTextWriterPtr xmlWriter)
+{
+ switch(eLineJoint)
+ {
+ case drawing::LineJoint_NONE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "NONE");
+ break;
+ case drawing::LineJoint_MIDDLE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "MIDDLE");
+ break;
+ case drawing::LineJoint_BEVEL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "BEVEL");
+ break;
+ case drawing::LineJoint_MITER:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "MITER");
+ break;
+ case drawing::LineJoint_ROUND:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "ROUND");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpLineStartNameAsAttribute(std::u16string_view sLineStartName, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineStartName"), "%s",
+ OUStringToOString(sLineStartName, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpLineEndNameAsAttribute(std::u16string_view sLineEndName, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineEndName"), "%s",
+ OUStringToOString(sLineEndName, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpPolyPolygonBezierCoords(const drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoords, xmlTextWriterPtr xmlWriter)
+{
+ dumpPointSequenceSequence(rPolyPolygonBezierCoords.Coordinates, &rPolyPolygonBezierCoords.Flags, xmlWriter);
+}
+
+void dumpLineStartAsElement(const drawing::PolyPolygonBezierCoords& rLineStart, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineStart" ));
+ dumpPolyPolygonBezierCoords(rLineStart, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpLineEndAsElement(const drawing::PolyPolygonBezierCoords& rLineEnd, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineEnd" ));
+ dumpPolyPolygonBezierCoords(rLineEnd, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpLineStartCenterAsAttribute(bool bLineStartCenter, xmlTextWriterPtr xmlWriter)
+{
+ if(bLineStartCenter)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStartCenter"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStartCenter"), "%s", "false");
+}
+
+void dumpLineStartWidthAsAttribute(sal_Int32 aLineStartWidth, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineStartWidth"), "%" SAL_PRIdINT32, aLineStartWidth);
+}
+
+void dumpLineEndCenterAsAttribute(bool bLineEndCenter, xmlTextWriterPtr xmlWriter)
+{
+ if(bLineEndCenter)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineEndCenter"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineEndCenter"), "%s", "false");
+}
+
+void dumpLineEndWidthAsAttribute(sal_Int32 aLineEndWidth, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineEndWidth"), "%" SAL_PRIdINT32, aLineEndWidth);
+}
+
+
+// ---------- PolyPolygonDescriptor.idl ----------
+
+
+void dumpPolygonKindAsAttribute(drawing::PolygonKind ePolygonKind, xmlTextWriterPtr xmlWriter)
+{
+ switch(ePolygonKind)
+ {
+ case drawing::PolygonKind_LINE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "LINE");
+ break;
+ case drawing::PolygonKind_POLY:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "POLY");
+ break;
+ case drawing::PolygonKind_PLIN:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PLIN");
+ break;
+ case drawing::PolygonKind_PATHLINE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHLINE");
+ break;
+ case drawing::PolygonKind_PATHFILL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHFILL");
+ break;
+ case drawing::PolygonKind_FREELINE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "FREELINE");
+ break;
+ case drawing::PolygonKind_FREEFILL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "FREEFILL");
+ break;
+ case drawing::PolygonKind_PATHPOLY:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHPOLY");
+ break;
+ case drawing::PolygonKind_PATHPLIN:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHPLIN");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpPointSequenceSequence(const drawing::PointSequenceSequence& aPointSequenceSequence, const uno::Sequence<uno::Sequence< drawing::PolygonFlags > >* pFlags, xmlTextWriterPtr xmlWriter)
+{
+ // LibreOffice proudly presents - The Sequenception
+ uno::Sequence<uno::Sequence< awt::Point > > pointSequenceSequence = aPointSequenceSequence;
+ sal_Int32 nPointsSequence = pointSequenceSequence.getLength();
+
+ for (sal_Int32 i = 0; i < nPointsSequence; ++i)
+ {
+ uno::Sequence< awt::Point > pointSequence = pointSequenceSequence[i];
+ sal_Int32 nPoints = pointSequence.getLength();
+
+ uno::Sequence< drawing::PolygonFlags> flagsSequence;
+ if(pFlags)
+ flagsSequence = (*pFlags)[i];
+
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "pointSequence" ));
+
+ for(sal_Int32 j = 0; j < nPoints; ++j)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "point" ));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionX"), "%" SAL_PRIdINT32, pointSequence[j].X);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionY"), "%" SAL_PRIdINT32, pointSequence[j].Y);
+
+ if(pFlags)
+ {
+ switch(flagsSequence[j])
+ {
+ case drawing::PolygonFlags_NORMAL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "NORMAL");
+ break;
+ case drawing::PolygonFlags_SMOOTH:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "SMOOTH");
+ break;
+ case drawing::PolygonFlags_CONTROL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "CONTROL");
+ break;
+ case drawing::PolygonFlags_SYMMETRIC:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "SYMMETRIC");
+ break;
+ default:
+ break;
+ }
+ }
+
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+}
+
+void dumpPolyPolygonAsElement(const drawing::PointSequenceSequence& rPolyPolygon, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PolyPolygon" ));
+ dumpPointSequenceSequence(rPolyPolygon, nullptr, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpGeometryAsElement(const drawing::PointSequenceSequence& aGeometry, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Geometry" ));
+ dumpPointSequenceSequence(aGeometry, nullptr, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+// CharacterProperties.idl
+void dumpCharHeightAsAttribute(float fHeight, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fontHeight"), "%f", fHeight );
+}
+
+void dumpCharColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fontColor"), "%06x", static_cast<unsigned int>(aColor));
+}
+
+
+// ---------- TextProperties.idl ----------
+
+
+void dumpIsNumberingAsAttribute(bool bIsNumbering, xmlTextWriterPtr xmlWriter)
+{
+ if(bIsNumbering)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("isNumbering"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("isNumbering"), "%s", "false");
+}
+
+void dumpTextAutoGrowHeightAsAttribute(bool bTextAutoGrowHeight, xmlTextWriterPtr xmlWriter)
+{
+ if(bTextAutoGrowHeight)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowHeight"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowHeight"), "%s", "false");
+}
+
+void dumpTextAutoGrowWidthAsAttribute(bool bTextAutoGrowWidth, xmlTextWriterPtr xmlWriter)
+{
+ if(bTextAutoGrowWidth)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowWidth"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowWidth"), "%s", "false");
+}
+
+void dumpTextContourFrameAsAttribute(bool bTextContourFrame, xmlTextWriterPtr xmlWriter)
+{
+ if(bTextContourFrame)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textContourFrame"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textContourFrame"), "%s", "false");
+}
+
+void dumpTextFitToSizeAsAttribute(drawing::TextFitToSizeType eTextFitToSize, xmlTextWriterPtr xmlWriter)
+{
+ switch(eTextFitToSize)
+ {
+ case drawing::TextFitToSizeType_NONE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "NONE");
+ break;
+ case drawing::TextFitToSizeType_PROPORTIONAL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "PROPORTIONAL");
+ break;
+ case drawing::TextFitToSizeType_ALLLINES:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "ALLLINES");
+ break;
+ case drawing::TextFitToSizeType_AUTOFIT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "AUTOFIT");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpTextHorizontalAdjustAsAttribute(drawing::TextHorizontalAdjust eTextHorizontalAdjust, xmlTextWriterPtr xmlWriter)
+{
+ switch(eTextHorizontalAdjust)
+ {
+ case drawing::TextHorizontalAdjust_LEFT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "LEFT");
+ break;
+ case drawing::TextHorizontalAdjust_CENTER:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "CENTER");
+ break;
+ case drawing::TextHorizontalAdjust_RIGHT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "RIGHT");
+ break;
+ case drawing::TextHorizontalAdjust_BLOCK:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "BLOCK");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpTextVerticalAdjustAsAttribute(drawing::TextVerticalAdjust eTextVerticalAdjust, xmlTextWriterPtr xmlWriter)
+{
+ switch(eTextVerticalAdjust)
+ {
+ case drawing::TextVerticalAdjust_TOP:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "TOP");
+ break;
+ case drawing::TextVerticalAdjust_CENTER:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "CENTER");
+ break;
+ case drawing::TextVerticalAdjust_BOTTOM:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "BOTTOM");
+ break;
+ case drawing::TextVerticalAdjust_BLOCK:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "BLOCK");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpTextLeftDistanceAsAttribute(sal_Int32 aTextLeftDistance, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textLeftDistance"), "%" SAL_PRIdINT32, aTextLeftDistance);
+}
+
+void dumpTextRightDistanceAsAttribute(sal_Int32 aTextRightDistance, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textRightDistance"), "%" SAL_PRIdINT32, aTextRightDistance);
+}
+
+void dumpTextUpperDistanceAsAttribute(sal_Int32 aTextUpperDistance, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textUpperDistance"), "%" SAL_PRIdINT32, aTextUpperDistance);
+}
+
+void dumpTextLowerDistanceAsAttribute(sal_Int32 aTextLowerDistance, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textLowerDistance"), "%" SAL_PRIdINT32, aTextLowerDistance);
+}
+
+void dumpTextMaximumFrameHeightAsAttribute(sal_Int32 aTextMaximumFrameHeight, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMaximumFrameHeight"), "%" SAL_PRIdINT32, aTextMaximumFrameHeight);
+}
+
+void dumpTextMaximumFrameWidthAsAttribute(sal_Int32 aTextMaximumFrameWidth, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMaximumFrameWidth"), "%" SAL_PRIdINT32, aTextMaximumFrameWidth);
+}
+
+void dumpTextMinimumFrameHeightAsAttribute(sal_Int32 aTextMinimumFrameHeight, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMinimumFrameHeight"), "%" SAL_PRIdINT32, aTextMinimumFrameHeight);
+}
+
+void dumpTextMinimumFrameWidthAsAttribute(sal_Int32 aTextMinimumFrameWidth, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMinimumFrameWidth"), "%" SAL_PRIdINT32, aTextMinimumFrameWidth);
+}
+
+void dumpTextAnimationAmountAsAttribute(sal_Int32 aTextAnimationAmount, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationAmount"), "%" SAL_PRIdINT32, aTextAnimationAmount);
+}
+
+void dumpTextAnimationCountAsAttribute(sal_Int32 aTextAnimationCount, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationCount"), "%" SAL_PRIdINT32, aTextAnimationCount);
+}
+
+void dumpTextAnimationDelayAsAttribute(sal_Int32 aTextAnimationDelay, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationDelay"), "%" SAL_PRIdINT32, aTextAnimationDelay);
+}
+
+void dumpTextAnimationDirectionAsAttribute(drawing::TextAnimationDirection eTextAnimationDirection, xmlTextWriterPtr xmlWriter)
+{
+ switch(eTextAnimationDirection)
+ {
+ case drawing::TextAnimationDirection_LEFT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "LEFT");
+ break;
+ case drawing::TextAnimationDirection_RIGHT:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "RIGHT");
+ break;
+ case drawing::TextAnimationDirection_UP:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "UP");
+ break;
+ case drawing::TextAnimationDirection_DOWN:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "DOWN");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpTextAnimationKindAsAttribute(drawing::TextAnimationKind eTextAnimationKind, xmlTextWriterPtr xmlWriter)
+{
+ switch(eTextAnimationKind)
+ {
+ case drawing::TextAnimationKind_NONE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "NONE");
+ break;
+ case drawing::TextAnimationKind_BLINK:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "BLINK");
+ break;
+ case drawing::TextAnimationKind_SCROLL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "SCROLL");
+ break;
+ case drawing::TextAnimationKind_ALTERNATE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "ALTERNATE");
+ break;
+ case drawing::TextAnimationKind_SLIDE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "SLIDE");
+ break;
+ default:
+ break;
+ }
+}
+
+void dumpTextAnimationStartInsideAsAttribute(bool bTextAnimationStartInside, xmlTextWriterPtr xmlWriter)
+{
+ if(bTextAnimationStartInside)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStartInside"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStartInside"), "%s", "false");
+}
+
+void dumpTextAnimationStopInsideAsAttribute(bool bTextAnimationStopInside, xmlTextWriterPtr xmlWriter)
+{
+ if(bTextAnimationStopInside)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStopInside"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStopInside"), "%s", "false");
+}
+
+void dumpTextWritingModeAsAttribute(text::WritingMode eTextWritingMode, xmlTextWriterPtr xmlWriter)
+{
+ switch(eTextWritingMode)
+ {
+ case text::WritingMode_LR_TB:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "LR_TB");
+ break;
+ case text::WritingMode_RL_TB:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "RL_TB");
+ break;
+ case text::WritingMode_TB_RL:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "TB_RL");
+ break;
+ default:
+ break;
+ }
+}
+
+
+// ---------- ShadowProperties.idl ----------
+
+
+void dumpShadowAsAttribute(bool bShadow, xmlTextWriterPtr xmlWriter)
+{
+ if(bShadow)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadow"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadow"), "%s", "false");
+}
+
+void dumpShadowColorAsAttribute(sal_Int32 aShadowColor, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowColor"), "%06x", static_cast<unsigned int>(aShadowColor));
+}
+
+void dumpShadowTransparenceAsAttribute(sal_Int32 aShadowTransparence, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowTransparence"), "%" SAL_PRIdINT32, aShadowTransparence);
+}
+
+void dumpShadowXDistanceAsAttribute(sal_Int32 aShadowXDistance, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowXDistance"), "%" SAL_PRIdINT32, aShadowXDistance);
+}
+
+void dumpShadowYDistanceAsAttribute(sal_Int32 aShadowYDistance, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowYDistance"), "%" SAL_PRIdINT32, aShadowYDistance);
+}
+
+
+// ---------- Shape.idl ----------
+
+
+void dumpZOrderAsAttribute(sal_Int32 aZOrder, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("zOrder"), "%" SAL_PRIdINT32, aZOrder);
+}
+
+void dumpLayerIDAsAttribute(sal_Int32 aLayerID, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("layerID"), "%" SAL_PRIdINT32, aLayerID);
+}
+
+void dumpLayerNameAsAttribute(std::u16string_view sLayerName, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("layerName"), "%s",
+ OUStringToOString(sLayerName, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpVisibleAsAttribute(bool bVisible, xmlTextWriterPtr xmlWriter)
+{
+ if(bVisible)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("visible"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("visible"), "%s", "false");
+}
+
+void dumpPrintableAsAttribute(bool bPrintable, xmlTextWriterPtr xmlWriter)
+{
+ if(bPrintable)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("printable"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("printable"), "%s", "false");
+}
+
+void dumpMoveProtectAsAttribute(bool bMoveProtect, xmlTextWriterPtr xmlWriter)
+{
+ if(bMoveProtect)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("moveProtect"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("moveProtect"), "%s", "false");
+}
+
+void dumpNameAsAttribute(std::u16string_view sName, xmlTextWriterPtr xmlWriter)
+{
+ if(!sName.empty() && !m_bNameDumped)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr());
+ m_bNameDumped = true;
+ }
+}
+
+void dumpSizeProtectAsAttribute(bool bSizeProtect, xmlTextWriterPtr xmlWriter)
+{
+ if(bSizeProtect)
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("sizeProtect"), "%s", "true");
+ else
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("sizeProtect"), "%s", "false");
+}
+
+void dumpHomogenMatrixLine3(const drawing::HomogenMatrixLine3& rHomogenMatrixLine3, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column1"), "%f", rHomogenMatrixLine3.Column1);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column2"), "%f", rHomogenMatrixLine3.Column2);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column3"), "%f", rHomogenMatrixLine3.Column3);
+}
+
+void dumpTransformationAsElement(const drawing::HomogenMatrix3& rTransformation, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Transformation" ));
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line1" ));
+ dumpHomogenMatrixLine3(rTransformation.Line1, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line2" ));
+ dumpHomogenMatrixLine3(rTransformation.Line2, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line3" ));
+ dumpHomogenMatrixLine3(rTransformation.Line3, xmlWriter);
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpNavigationOrderAsAttribute(sal_Int32 aNavigationOrder, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("navigationOrder"), "%" SAL_PRIdINT32, aNavigationOrder);
+}
+
+void dumpHyperlinkAsAttribute(std::u16string_view sHyperlink, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("hyperlink"), "%s",
+ OUStringToOString(sHyperlink, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpInteropGrabBagAsElement(const uno::Sequence< beans::PropertyValue>& aInteropGrabBag, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "InteropGrabBag" ));
+
+ for (const auto& item: aInteropGrabBag)
+ dumpPropertyValueAsElement(item, xmlWriter);
+
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+
+// ---------- XShape.idl ----------
+
+
+void dumpPositionAsAttribute(const awt::Point& rPoint, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionX"), "%" SAL_PRIdINT32, rPoint.X);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionY"), "%" SAL_PRIdINT32, rPoint.Y);
+}
+
+void dumpSizeAsAttribute(const awt::Size& rSize, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("sizeX"), "%" SAL_PRIdINT32, rSize.Width);
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("sizeY"), "%" SAL_PRIdINT32, rSize.Height);
+}
+
+void dumpShapeDescriptorAsAttribute( const uno::Reference< drawing::XShapeDescriptor >& xDescr, xmlTextWriterPtr xmlWriter )
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%s",
+ OUStringToOString(xDescr->getShapeType(), RTL_TEXTENCODING_UTF8).getStr());
+}
+
+
+// ---------- CustomShape.idl ----------
+
+
+void dumpCustomShapeEngineAsAttribute(std::u16string_view sCustomShapeEngine, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeEngine"), "%s",
+ OUStringToOString(sCustomShapeEngine, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpCustomShapeDataAsAttribute(
+ std::u16string_view sCustomShapeData, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeData"), "%s",
+ OUStringToOString(sCustomShapeData, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+void dumpPropertyValueAsElement(const beans::PropertyValue& rPropertyValue, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValue" ));
+
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s",
+ OUStringToOString(rPropertyValue.Name, RTL_TEXTENCODING_UTF8).getStr());
+
+ uno::Any aAny = rPropertyValue.Value;
+ if(OUString sValue; aAny >>= sValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s",
+ OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ else if(sal_Int32 nValue; aAny >>= nValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue);
+ }
+ else if(float fValue; aAny >>= fValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue);
+ }
+ else if(bool bValue; aAny >>= bValue)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false"));
+ }
+ else if(awt::Rectangle aRectangleValue;
+ rPropertyValue.Name == "ViewBox" && (aAny >>= aRectangleValue))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpViewBoxAsElement(aRectangleValue);
+ }
+ else if(uno::Sequence< drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentValues;
+ rPropertyValue.Name == "AdjustmentValues" && (aAny >>= aAdjustmentValues))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpAdjustmentValuesAsElement(aAdjustmentValues);
+ }
+ else if(uno::Sequence< drawing::EnhancedCustomShapeParameterPair > aCoordinates;
+ rPropertyValue.Name == "Coordinates" && (aAny >>= aCoordinates))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpCoordinatesAsElement(aCoordinates);
+ }
+ else if(uno::Sequence< drawing::EnhancedCustomShapeSegment > aSegments;
+ rPropertyValue.Name == "Segments" && (aAny >>= aSegments))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpSegmentsAsElement(aSegments);
+ }
+ else if(uno::Sequence< beans::PropertyValue > aPropSeq; aAny >>= aPropSeq)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( OUStringToOString(rPropertyValue.Name, RTL_TEXTENCODING_UTF8).getStr() ));
+
+ sal_Int32 i = 0, nCount = aPropSeq.getLength();
+ for ( ; i < nCount; i++ )
+ dumpPropertyValueAsElement(aPropSeq[ i ], xmlWriter);
+
+ (void)xmlTextWriterEndElement(xmlWriter);
+ }
+
+ // TODO: Add here dumping of XDocument for future OOX Smart-Art
+ // properties.
+
+ // TODO more, if necessary
+
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("handle"), "%" SAL_PRIdINT32, rPropertyValue.Handle);
+
+ switch(rPropertyValue.State)
+ {
+ case beans::PropertyState_DIRECT_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE");
+ break;
+ case beans::PropertyState_DEFAULT_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE");
+ break;
+ case beans::PropertyState_AMBIGUOUS_VALUE:
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE");
+ break;
+ default:
+ break;
+ }
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpCustomShapeGeometryAsElement(const uno::Sequence< beans::PropertyValue>& aCustomShapeGeometry, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "CustomShapeGeometry" ));
+
+ sal_Int32 nLength = aCustomShapeGeometry.getLength();
+ for (sal_Int32 i = 0; i < nLength; ++i)
+ dumpPropertyValueAsElement(aCustomShapeGeometry[i], xmlWriter);
+
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpCustomShapeReplacementURLAsAttribute(std::u16string_view sCustomShapeReplacementURL, xmlTextWriterPtr xmlWriter)
+{
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeReplacementURL"), "%s",
+ OUStringToOString(sCustomShapeReplacementURL, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+// methods dumping whole services
+
+void dumpTextPropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter)
+{
+ uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo();
+ if(xInfo->hasPropertyByName("CharHeight"))
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("CharHeight");
+ float fHeight;
+ if(anotherAny >>= fHeight)
+ dumpCharHeightAsAttribute(fHeight, xmlWriter);
+ }
+ if(xInfo->hasPropertyByName("CharColor"))
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("CharColor");
+ sal_Int32 aColor = sal_Int32();
+ if(anotherAny >>= aColor)
+ dumpCharColorAsAttribute(aColor, xmlWriter);
+ }
+ // TODO - more properties from CharacterProperties.idl (similar to above)
+
+ if(xInfo->hasPropertyByName("IsNumbering"))
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("IsNumbering");
+ bool bIsNumbering;
+ if(anotherAny >>= bIsNumbering)
+ dumpIsNumberingAsAttribute(bIsNumbering, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAutoGrowHeight");
+ bool bTextAutoGrowHeight;
+ if(anotherAny >>= bTextAutoGrowHeight)
+ dumpTextAutoGrowHeightAsAttribute(bTextAutoGrowHeight, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAutoGrowWidth");
+ bool bTextAutoGrowWidth;
+ if(anotherAny >>= bTextAutoGrowWidth)
+ dumpTextAutoGrowWidthAsAttribute(bTextAutoGrowWidth, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextContourFrame");
+ bool bTextContourFrame;
+ if(anotherAny >>= bTextContourFrame)
+ dumpTextContourFrameAsAttribute(bTextContourFrame, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextFitToSize");
+ drawing::TextFitToSizeType eTextFitToSize;
+ if(anotherAny >>= eTextFitToSize)
+ dumpTextFitToSizeAsAttribute(eTextFitToSize, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextHorizontalAdjust");
+ drawing::TextHorizontalAdjust eTextHorizontalAdjust;
+ if(anotherAny >>= eTextHorizontalAdjust)
+ dumpTextHorizontalAdjustAsAttribute(eTextHorizontalAdjust, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextVerticalAdjust");
+ drawing::TextVerticalAdjust eTextVerticalAdjust;
+ if(anotherAny >>= eTextVerticalAdjust)
+ dumpTextVerticalAdjustAsAttribute(eTextVerticalAdjust, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextLeftDistance");
+ sal_Int32 aTextLeftDistance = sal_Int32();
+ if(anotherAny >>= aTextLeftDistance)
+ dumpTextLeftDistanceAsAttribute(aTextLeftDistance, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextRightDistance");
+ sal_Int32 aTextRightDistance = sal_Int32();
+ if(anotherAny >>= aTextRightDistance)
+ dumpTextRightDistanceAsAttribute(aTextRightDistance, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextUpperDistance");
+ sal_Int32 aTextUpperDistance = sal_Int32();
+ if(anotherAny >>= aTextUpperDistance)
+ dumpTextUpperDistanceAsAttribute(aTextUpperDistance, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextLowerDistance");
+ sal_Int32 aTextLowerDistance = sal_Int32();
+ if(anotherAny >>= aTextLowerDistance)
+ dumpTextLowerDistanceAsAttribute(aTextLowerDistance, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextMaximumFrameHeight");
+ sal_Int32 aTextMaximumFrameHeight = sal_Int32();
+ if(anotherAny >>= aTextMaximumFrameHeight)
+ dumpTextMaximumFrameHeightAsAttribute(aTextMaximumFrameHeight, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextMaximumFrameWidth");
+ sal_Int32 aTextMaximumFrameWidth = sal_Int32();
+ if(anotherAny >>= aTextMaximumFrameWidth)
+ dumpTextMaximumFrameWidthAsAttribute(aTextMaximumFrameWidth, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextMinimumFrameHeight");
+ sal_Int32 aTextMinimumFrameHeight = sal_Int32();
+ if(anotherAny >>= aTextMinimumFrameHeight)
+ dumpTextMinimumFrameHeightAsAttribute(aTextMinimumFrameHeight, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextMinimumFrameWidth");
+ sal_Int32 aTextMinimumFrameWidth = sal_Int32();
+ if(anotherAny >>= aTextMinimumFrameWidth)
+ dumpTextMinimumFrameWidthAsAttribute(aTextMinimumFrameWidth, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationAmount");
+ sal_Int32 aTextAnimationAmount = sal_Int32();
+ if(anotherAny >>= aTextAnimationAmount)
+ dumpTextAnimationAmountAsAttribute(aTextAnimationAmount, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationCount");
+ sal_Int32 aTextAnimationCount = sal_Int32();
+ if(anotherAny >>= aTextAnimationCount)
+ dumpTextAnimationCountAsAttribute(aTextAnimationCount, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationDelay");
+ sal_Int32 aTextAnimationDelay = sal_Int32();
+ if(anotherAny >>= aTextAnimationDelay)
+ dumpTextAnimationDelayAsAttribute(aTextAnimationDelay, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationDirection");
+ drawing::TextAnimationDirection eTextAnimationDirection;
+ if(anotherAny >>= eTextAnimationDirection)
+ dumpTextAnimationDirectionAsAttribute(eTextAnimationDirection, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationKind");
+ drawing::TextAnimationKind eTextAnimationKind;
+ if(anotherAny >>= eTextAnimationKind)
+ dumpTextAnimationKindAsAttribute(eTextAnimationKind, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationStartInside");
+ bool bTextAnimationStartInside;
+ if(anotherAny >>= bTextAnimationStartInside)
+ dumpTextAnimationStartInsideAsAttribute(bTextAnimationStartInside, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationStopInside");
+ bool bTextAnimationStopInside;
+ if(anotherAny >>= bTextAnimationStopInside)
+ dumpTextAnimationStopInsideAsAttribute(bTextAnimationStopInside, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("TextWritingMode");
+ text::WritingMode eTextWritingMode;
+ if(anotherAny >>= eTextWritingMode)
+ dumpTextWritingModeAsAttribute(eTextWritingMode, xmlWriter);
+ }
+}
+
+void dumpFillPropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillStyle");
+ drawing::FillStyle eFillStyle;
+ if(anotherAny >>= eFillStyle)
+ dumpFillStyleAsAttribute(eFillStyle, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillColor");
+ sal_Int32 aColor = sal_Int32();
+ if(anotherAny >>= aColor)
+ dumpFillColorAsAttribute(aColor, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillTransparence");
+ sal_Int32 aTransparence = sal_Int32();
+ if(anotherAny >>= aTransparence)
+ dumpFillTransparenceAsAttribute(aTransparence, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillTransparenceGradientName");
+ OUString sTranspGradName;
+ if(anotherAny >>= sTranspGradName)
+ dumpFillTransparenceGradientNameAsAttribute(sTranspGradName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillTransparenceGradient");
+ awt::Gradient aTranspGrad;
+ if(anotherAny >>= aTranspGrad)
+ dumpFillTransparenceGradientAsElement(aTranspGrad, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillGradientName");
+ OUString sGradName;
+ if(anotherAny >>= sGradName)
+ dumpFillGradientNameAsAttribute(sGradName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillGradient");
+ awt::Gradient aGradient;
+ if(anotherAny >>= aGradient)
+ dumpFillGradientAsElement(aGradient, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillHatchName");
+ OUString sHatchName;
+ if(anotherAny >>= sHatchName)
+ dumpFillGradientNameAsAttribute(sHatchName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillHatch");
+ drawing::Hatch aHatch;
+ if(anotherAny >>= aHatch)
+ dumpFillHatchAsElement(aHatch, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBackground");
+ bool bFillBackground;
+ if(anotherAny >>= bFillBackground)
+ dumpFillBackgroundAsAttribute(bFillBackground, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapName");
+ OUString sBitmapName;
+ if(anotherAny >>= sBitmapName)
+ dumpFillGradientNameAsAttribute(sBitmapName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmap");
+ uno::Reference<awt::XBitmap> xBitmap;
+ if(anotherAny >>= xBitmap)
+ dumpFillBitmapAsElement(xBitmap, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapPositionOffsetX");
+ sal_Int32 aBitmapPositionOffsetX = sal_Int32();
+ if(anotherAny >>= aBitmapPositionOffsetX)
+ dumpFillBitmapPositionOffsetXAsAttribute(aBitmapPositionOffsetX, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapPositionOffsetY");
+ sal_Int32 aBitmapPositionOffsetY = sal_Int32();
+ if(anotherAny >>= aBitmapPositionOffsetY)
+ dumpFillBitmapPositionOffsetYAsAttribute(aBitmapPositionOffsetY, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapOffsetX");
+ sal_Int32 aBitmapOffsetX = sal_Int32();
+ if(anotherAny >>= aBitmapOffsetX)
+ dumpFillBitmapOffsetXAsAttribute(aBitmapOffsetX, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapOffsetY");
+ sal_Int32 aBitmapOffsetY = sal_Int32();
+ if(anotherAny >>= aBitmapOffsetY)
+ dumpFillBitmapOffsetYAsAttribute(aBitmapOffsetY, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapRectanglePoint");
+ drawing::RectanglePoint eBitmapRectanglePoint;
+ if(anotherAny >>= eBitmapRectanglePoint)
+ dumpFillBitmapRectanglePointAsAttribute(eBitmapRectanglePoint, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapLogicalSize");
+ bool bBitmapLogicalSize;
+ if(anotherAny >>= bBitmapLogicalSize)
+ dumpFillBitmapLogicalSizeAsAttribute(bBitmapLogicalSize, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapSizeX");
+ sal_Int32 aBitmapSizeX = sal_Int32();
+ if(anotherAny >>= aBitmapSizeX)
+ dumpFillBitmapSizeXAsAttribute(aBitmapSizeX, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapSizeY");
+ sal_Int32 aBitmapSizeY = sal_Int32();
+ if(anotherAny >>= aBitmapSizeY)
+ dumpFillBitmapSizeYAsAttribute(aBitmapSizeY, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapMode");
+ drawing::BitmapMode eBitmapMode;
+ if(anotherAny >>= eBitmapMode)
+ dumpFillBitmapModeAsAttribute(eBitmapMode, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapStretch");
+ bool bBitmapStretch;
+ if(anotherAny >>= bBitmapStretch)
+ dumpFillBitmapStretchAsAttribute(bBitmapStretch, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapTile");
+ bool bBitmapTile;
+ if(anotherAny >>= bBitmapTile)
+ dumpFillBitmapTileAsAttribute(bBitmapTile, xmlWriter);
+ }
+}
+
+void dumpLinePropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineStyle");
+ drawing::LineStyle eLineStyle;
+ if(anotherAny >>= eLineStyle)
+ dumpLineStyleAsAttribute(eLineStyle, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineDash");
+ drawing::LineDash aLineDash;
+ if(anotherAny >>= aLineDash)
+ dumpLineDashAsElement(aLineDash, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineDashName");
+ OUString sLineDashName;
+ if(anotherAny >>= sLineDashName)
+ dumpLineDashNameAsAttribute(sLineDashName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineColor");
+ sal_Int32 aLineColor = sal_Int32();
+ if(anotherAny >>= aLineColor)
+ dumpLineColorAsAttribute(aLineColor, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineTransparence");
+ sal_Int32 aLineTransparence = sal_Int32();
+ if(anotherAny >>= aLineTransparence)
+ dumpLineTransparenceAsAttribute(aLineTransparence, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineWidth");
+ sal_Int32 aLineWidth = sal_Int32();
+ if(anotherAny >>= aLineWidth)
+ dumpLineWidthAsAttribute(aLineWidth, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineJoint");
+ drawing::LineJoint eLineJoint;
+ if(anotherAny >>= eLineJoint)
+ dumpLineJointAsAttribute(eLineJoint, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineStartName");
+ OUString sLineStartName;
+ if(anotherAny >>= sLineStartName)
+ dumpLineStartNameAsAttribute(sLineStartName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineEndName");
+ OUString sLineEndName;
+ if(anotherAny >>= sLineEndName)
+ dumpLineEndNameAsAttribute(sLineEndName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineStart");
+ drawing::PolyPolygonBezierCoords aLineStart;
+ if(anotherAny >>= aLineStart)
+ dumpLineStartAsElement(aLineStart, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineEnd");
+ drawing::PolyPolygonBezierCoords aLineEnd;
+ if(anotherAny >>= aLineEnd)
+ dumpLineEndAsElement(aLineEnd, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineStartCenter");
+ bool bLineStartCenter;
+ if(anotherAny >>= bLineStartCenter)
+ dumpLineStartCenterAsAttribute(bLineStartCenter, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineStartWidth");
+ sal_Int32 aLineStartWidth = sal_Int32();
+ if(anotherAny >>= aLineStartWidth)
+ dumpLineStartWidthAsAttribute(aLineStartWidth, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineEndCenter");
+ bool bLineEndCenter;
+ if(anotherAny >>= bLineEndCenter)
+ dumpLineEndCenterAsAttribute(bLineEndCenter, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LineEndWidth");
+ sal_Int32 aLineEndWidth = sal_Int32();
+ if(anotherAny >>= aLineEndWidth)
+ dumpLineEndWidthAsAttribute(aLineEndWidth, xmlWriter);
+ }
+}
+
+void dumpShadowPropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Shadow");
+ bool bShadow;
+ if(anotherAny >>= bShadow)
+ dumpShadowAsAttribute(bShadow, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ShadowColor");
+ sal_Int32 aShadowColor = sal_Int32();
+ if(anotherAny >>= aShadowColor)
+ dumpShadowColorAsAttribute(aShadowColor, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ShadowTransparence");
+ sal_Int32 aShadowTransparence = sal_Int32();
+ if(anotherAny >>= aShadowTransparence)
+ dumpShadowTransparenceAsAttribute(aShadowTransparence, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ShadowXDistance");
+ sal_Int32 aShadowXDistance = sal_Int32();
+ if(anotherAny >>= aShadowXDistance)
+ dumpShadowXDistanceAsAttribute(aShadowXDistance, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ShadowYDistance");
+ sal_Int32 aShadowYDistance = sal_Int32();
+ if(anotherAny >>= aShadowYDistance)
+ dumpShadowYDistanceAsAttribute(aShadowYDistance, xmlWriter);
+ }
+}
+
+void dumpPolyPolygonDescriptorService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("PolygonKind");
+ drawing::PolygonKind ePolygonKind;
+ if(anotherAny >>= ePolygonKind)
+ dumpPolygonKindAsAttribute(ePolygonKind, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("PolyPolygon");
+ drawing::PointSequenceSequence aPolyPolygon;
+ if(anotherAny >>= aPolyPolygon)
+ dumpPolyPolygonAsElement(aPolyPolygon, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Geometry");
+ drawing::PointSequenceSequence aGeometry;
+ if(anotherAny >>= aGeometry)
+ dumpGeometryAsElement(aGeometry, xmlWriter);
+ }
+}
+
+void dumpShapeService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties)
+{
+ uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo();
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("ZOrder");
+ sal_Int32 aZOrder = sal_Int32();
+ if(anotherAny >>= aZOrder)
+ dumpZOrderAsAttribute(aZOrder, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LayerID");
+ sal_Int32 aLayerID = sal_Int32();
+ if(anotherAny >>= aLayerID)
+ dumpLayerIDAsAttribute(aLayerID, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("LayerName");
+ OUString sLayerName;
+ if(anotherAny >>= sLayerName)
+ dumpLayerNameAsAttribute(sLayerName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Visible");
+ bool bVisible;
+ if(anotherAny >>= bVisible)
+ dumpVisibleAsAttribute(bVisible, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Printable");
+ bool bPrintable;
+ if(anotherAny >>= bPrintable)
+ dumpPrintableAsAttribute(bPrintable, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("MoveProtect");
+ bool bMoveProtect;
+ if(anotherAny >>= bMoveProtect)
+ dumpMoveProtectAsAttribute(bMoveProtect, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Name");
+ OUString sName;
+ if(anotherAny >>= sName)
+ dumpNameAsAttribute(sName, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("SizeProtect");
+ bool bSizeProtect;
+ if(anotherAny >>= bSizeProtect)
+ dumpSizeProtectAsAttribute(bSizeProtect, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Transformation");
+ drawing::HomogenMatrix3 aTransformation;
+ if(anotherAny >>= aTransformation)
+ dumpTransformationAsElement(aTransformation, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("NavigationOrder");
+ sal_Int32 aNavigationOrder = sal_Int32();
+ if(anotherAny >>= aNavigationOrder)
+ dumpNavigationOrderAsAttribute(aNavigationOrder, xmlWriter);
+ }
+ if(xInfo->hasPropertyByName("Hyperlink"))
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Hyperlink");
+ OUString sHyperlink;
+ if(anotherAny >>= sHyperlink)
+ dumpHyperlinkAsAttribute(sHyperlink, xmlWriter);
+ }
+ if(xInfo->hasPropertyByName("InteropGrabBag") && bDumpInteropProperties)
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("InteropGrabBag");
+ uno::Sequence< beans::PropertyValue> aInteropGrabBag;
+ if(anotherAny >>= aInteropGrabBag)
+ dumpInteropGrabBagAsElement(aInteropGrabBag, xmlWriter);
+ }
+}
+
+void dumpPolyPolygonBezierDescriptorService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter)
+{
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("PolygonKind");
+ drawing::PolygonKind ePolygonKind;
+ if(anotherAny >>= ePolygonKind)
+ dumpPolygonKindAsAttribute(ePolygonKind, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("PolyPolygonBezier");
+ drawing::PolyPolygonBezierCoords aPolyPolygonBezier;
+ if(anotherAny >>= aPolyPolygonBezier)
+ dumpPolyPolygonBezierCoords(aPolyPolygonBezier, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("Geometry");
+ drawing::PolyPolygonBezierCoords aGeometry;
+ if(anotherAny >>= aGeometry)
+ dumpPolyPolygonBezierCoords(aGeometry, xmlWriter);
+ }
+}
+
+void dumpCustomShapeService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter)
+{
+ uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo();
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeEngine");
+ OUString sCustomShapeEngine;
+ if(anotherAny >>= sCustomShapeEngine)
+ dumpCustomShapeEngineAsAttribute(sCustomShapeEngine, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeData");
+ OUString sCustomShapeData;
+ if(anotherAny >>= sCustomShapeData)
+ dumpCustomShapeDataAsAttribute(sCustomShapeData, xmlWriter);
+ }
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeGeometry");
+ uno::Sequence< beans::PropertyValue> aCustomShapeGeometry;
+ if(anotherAny >>= aCustomShapeGeometry)
+ dumpCustomShapeGeometryAsElement(aCustomShapeGeometry, xmlWriter);
+ }
+ if(xInfo->hasPropertyByName("CustomShapeReplacementURL"))
+ {
+ uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeReplacementURL");
+ OUString sCustomShapeReplacementURL;
+ if(anotherAny >>= sCustomShapeReplacementURL)
+ dumpCustomShapeReplacementURLAsAttribute(sCustomShapeReplacementURL, xmlWriter);
+ }
+}
+
+void dumpXShape(const uno::Reference< drawing::XShape >& xShape, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties)
+{
+ (void)xmlTextWriterStartElement( xmlWriter, BAD_CAST( "XShape" ) );
+ uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY_THROW);
+ OUString aName;
+ m_bNameDumped = false;
+
+ dumpPositionAsAttribute(xShape->getPosition(), xmlWriter);
+ dumpSizeAsAttribute(xShape->getSize(), xmlWriter);
+ uno::Reference< drawing::XShapeDescriptor > xDescr(xShape, uno::UNO_QUERY_THROW);
+ dumpShapeDescriptorAsAttribute(xDescr, xmlWriter);
+
+ // uno::Sequence<beans::Property> aProperties = xPropSetInfo->getProperties();
+
+ uno::Reference< lang::XServiceInfo > xServiceInfo( xShape, uno::UNO_QUERY_THROW );
+
+ uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo();
+ if(xInfo->hasPropertyByName("Name"))
+ {
+ uno::Any aAny = xPropSet->getPropertyValue("Name");
+ if ((aAny >>= aName) && !aName.isEmpty())
+ {
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(aName, RTL_TEXTENCODING_UTF8).getStr());
+ m_bNameDumped = true;
+ }
+ }
+
+ try
+ {
+ if (xServiceInfo->supportsService("com.sun.star.drawing.Text"))
+ {
+ uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY_THROW);
+ OUString aText = xText->getString();
+ if(!aText.isEmpty())
+ (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("text"), "%s", OUStringToOString(aText, RTL_TEXTENCODING_UTF8).getStr());
+ }
+ if(xServiceInfo->supportsService("com.sun.star.drawing.TextProperties"))
+ dumpTextPropertiesService(xPropSet, xmlWriter);
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
+ {
+ uno::Reference< drawing::XShapes > xShapes(xShape, uno::UNO_QUERY_THROW);
+ dumpXShapes(xShapes, xmlWriter, bDumpInteropProperties);
+ }
+ if(xServiceInfo->supportsService("com.sun.star.drawing.FillProperties"))
+ dumpFillPropertiesService(xPropSet, xmlWriter);
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.LineProperties"))
+ dumpLinePropertiesService(xPropSet, xmlWriter);
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonDescriptor"))
+ dumpPolyPolygonDescriptorService(xPropSet, xmlWriter);
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.ShadowProperties"))
+ dumpShadowPropertiesService(xPropSet, xmlWriter);
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.Shape"))
+ dumpShapeService(xPropSet, xmlWriter, bDumpInteropProperties);
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonBezierDescriptor"))
+ dumpPolyPolygonBezierDescriptorService(xPropSet, xmlWriter);
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.CustomShape"))
+ dumpCustomShapeService(xPropSet, xmlWriter);
+
+ // EnhancedShapeDumper used
+
+ if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeExtrusion"))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpEnhancedCustomShapeExtrusionService(xPropSet);
+ }
+ if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeGeometry"))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpEnhancedCustomShapeGeometryService(xPropSet);
+ }
+ if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeHandle"))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpEnhancedCustomShapeHandleService(xPropSet);
+ }
+ if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapePath"))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpEnhancedCustomShapePathService(xPropSet);
+ }
+ if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeTextPath"))
+ {
+ EnhancedShapeDumper enhancedDumper(xmlWriter);
+ enhancedDumper.dumpEnhancedCustomShapeTextPathService(xPropSet);
+ }
+ } // end of the 'try' block
+ catch (const beans::UnknownPropertyException& e)
+ {
+ std::cout << "Exception caught in XShapeDumper.cxx: " << e.Message << std::endl;
+ }
+
+ #if DEBUG_DUMPER
+ uno::Sequence< OUString > aServiceNames = xServiceInfo->getSupportedServiceNames();
+ sal_Int32 nServices = aServiceNames.getLength();
+ for (sal_Int32 i = 0; i < nServices; ++i)
+ {
+ (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ServiceName" ));
+ (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST( "name" ), "%s", OUStringToOString(aServiceNames[i], RTL_TEXTENCODING_UTF8).getStr());
+ (void)xmlTextWriterEndElement( xmlWriter );
+ }
+ #endif
+
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+
+void dumpXShapes( const uno::Reference< drawing::XShapes >& xShapes, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties )
+{
+ (void)xmlTextWriterStartElement( xmlWriter, BAD_CAST( "XShapes" ) );
+ uno::Reference< container::XIndexAccess > xIA( xShapes, uno::UNO_QUERY_THROW);
+ sal_Int32 nLength = xIA->getCount();
+ for (sal_Int32 i = 0; i < nLength; ++i)
+ {
+ uno::Reference< drawing::XShape > xShape( xIA->getByIndex( i ), uno::UNO_QUERY_THROW );
+ dumpXShape( xShape, xmlWriter, bDumpInteropProperties );
+ }
+
+ (void)xmlTextWriterEndElement( xmlWriter );
+}
+} //end of namespace
+
+OUString XShapeDumper::dump(const uno::Reference<drawing::XShapes>& xPageShapes, bool bDumpInteropProperties)
+{
+ OStringBuffer aString;
+ xmlOutputBufferPtr xmlOutBuffer = xmlOutputBufferCreateIO( writeCallback, closeCallback, &aString, nullptr );
+ xmlTextWriterPtr xmlWriter = xmlNewTextWriter( xmlOutBuffer );
+ xmlTextWriterSetIndent( xmlWriter, 1 );
+
+ (void)xmlTextWriterStartDocument( xmlWriter, nullptr, nullptr, nullptr );
+
+ try
+ {
+ dumpXShapes( xPageShapes, xmlWriter, bDumpInteropProperties );
+ }
+ catch (const beans::UnknownPropertyException& e)
+ {
+ std::cout << "Exception caught in XShapeDumper: " << e.Message << std::endl;
+ }
+
+ (void)xmlTextWriterEndDocument( xmlWriter );
+ xmlFreeTextWriter( xmlWriter );
+
+ return OUString::fromUtf8(aString.makeStringAndClear());
+}
+
+OUString XShapeDumper::dump(const uno::Reference<drawing::XShape>& xPageShapes, bool bDumpInteropProperties)
+{
+ OStringBuffer aString;
+ xmlOutputBufferPtr xmlOutBuffer = xmlOutputBufferCreateIO( writeCallback, closeCallback, &aString, nullptr );
+ xmlTextWriterPtr xmlWriter = xmlNewTextWriter( xmlOutBuffer );
+ xmlTextWriterSetIndent( xmlWriter, 1 );
+
+ (void)xmlTextWriterStartDocument( xmlWriter, nullptr, nullptr, nullptr );
+
+ try
+ {
+ dumpXShape( xPageShapes, xmlWriter, bDumpInteropProperties );
+ }
+ catch (const beans::UnknownPropertyException& e)
+ {
+ std::cout << "Exception caught in XShapeDumper: " << e.Message << std::endl;
+ }
+
+ (void)xmlTextWriterEndDocument( xmlWriter );
+ xmlFreeTextWriter( xmlWriter );
+
+ return OUString::fromUtf8(aString.makeStringAndClear());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/geometry/viewinformation2d.cxx b/drawinglayer/source/geometry/viewinformation2d.cxx
new file mode 100644
index 000000000..bcd6f020f
--- /dev/null
+++ b/drawinglayer/source/geometry/viewinformation2d.cxx
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/geometry/AffineMatrix2D.hpp>
+#include <com/sun/star/geometry/RealRectangle2D.hpp>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::geometry
+{
+namespace
+{
+constexpr OUStringLiteral g_PropertyName_ObjectTransformation = u"ObjectTransformation";
+constexpr OUStringLiteral g_PropertyName_ViewTransformation = u"ViewTransformation";
+constexpr OUStringLiteral g_PropertyName_Viewport = u"Viewport";
+constexpr OUStringLiteral g_PropertyName_Time = u"Time";
+constexpr OUStringLiteral g_PropertyName_VisualizedPage = u"VisualizedPage";
+constexpr OUStringLiteral g_PropertyName_ReducedDisplayQuality = u"ReducedDisplayQuality";
+}
+
+class ImpViewInformation2D
+{
+private:
+ // ViewInformation2D implementation can change refcount, so we have only
+ // two memory regions for pairs of ViewInformation2D/ImpViewInformation2D
+ friend class ::drawinglayer::geometry::ViewInformation2D;
+
+protected:
+ // the object transformation
+ basegfx::B2DHomMatrix maObjectTransformation;
+
+ // the view transformation
+ basegfx::B2DHomMatrix maViewTransformation;
+
+ // the ObjectToView and it's inverse, both on demand from ObjectTransformation
+ // and ViewTransformation
+ basegfx::B2DHomMatrix maObjectToViewTransformation;
+ basegfx::B2DHomMatrix maInverseObjectToViewTransformation;
+
+ // the visible range and the on-demand one in ViewCoordinates
+ basegfx::B2DRange maViewport;
+ basegfx::B2DRange maDiscreteViewport;
+
+ // the DrawPage which is target of visualisation. This is needed e.g. for
+ // the view-dependent decomposition of PageNumber TextFields.
+ // This parameter is buffered here, but mainly resides in mxExtendedInformation,
+ // so it will be interpreted, but held there. It will also not be added
+ // to mxExtendedInformation in impFillViewInformationFromContent (it's there already)
+ uno::Reference<drawing::XDrawPage> mxVisualizedPage;
+
+ // the point in time
+ double mfViewTime;
+
+ bool mbReducedDisplayQuality : 1;
+
+public:
+ ImpViewInformation2D(const basegfx::B2DHomMatrix& rObjectTransformation,
+ const basegfx::B2DHomMatrix& rViewTransformation,
+ const basegfx::B2DRange& rViewport,
+ const uno::Reference<drawing::XDrawPage>& rxDrawPage, double fViewTime,
+ bool bReducedDisplayQuality)
+ : maObjectTransformation(rObjectTransformation)
+ , maViewTransformation(rViewTransformation)
+ , maViewport(rViewport)
+ , mxVisualizedPage(rxDrawPage)
+ , mfViewTime(fViewTime)
+ , mbReducedDisplayQuality(bReducedDisplayQuality)
+ {
+ }
+
+ ImpViewInformation2D()
+ : mfViewTime(0.0)
+ , mbReducedDisplayQuality(false)
+ {
+ }
+
+ const basegfx::B2DHomMatrix& getObjectTransformation() const { return maObjectTransformation; }
+
+ const basegfx::B2DHomMatrix& getViewTransformation() const { return maViewTransformation; }
+
+ const basegfx::B2DRange& getViewport() const { return maViewport; }
+
+ const basegfx::B2DRange& getDiscreteViewport() const
+ {
+ if (maDiscreteViewport.isEmpty() && !maViewport.isEmpty())
+ {
+ basegfx::B2DRange aDiscreteViewport(maViewport);
+ aDiscreteViewport.transform(getViewTransformation());
+ const_cast<ImpViewInformation2D*>(this)->maDiscreteViewport = aDiscreteViewport;
+ }
+
+ return maDiscreteViewport;
+ }
+
+ const basegfx::B2DHomMatrix& getObjectToViewTransformation() const
+ {
+ if (maObjectToViewTransformation.isIdentity()
+ && (!maObjectTransformation.isIdentity() || !maViewTransformation.isIdentity()))
+ {
+ basegfx::B2DHomMatrix aObjectToView(maViewTransformation * maObjectTransformation);
+ const_cast<ImpViewInformation2D*>(this)->maObjectToViewTransformation = aObjectToView;
+ }
+
+ return maObjectToViewTransformation;
+ }
+
+ const basegfx::B2DHomMatrix& getInverseObjectToViewTransformation() const
+ {
+ if (maInverseObjectToViewTransformation.isIdentity()
+ && (!maObjectTransformation.isIdentity() || !maViewTransformation.isIdentity()))
+ {
+ basegfx::B2DHomMatrix aInverseObjectToView(maViewTransformation
+ * maObjectTransformation);
+ aInverseObjectToView.invert();
+ const_cast<ImpViewInformation2D*>(this)->maInverseObjectToViewTransformation
+ = aInverseObjectToView;
+ }
+
+ return maInverseObjectToViewTransformation;
+ }
+
+ double getViewTime() const { return mfViewTime; }
+
+ const uno::Reference<drawing::XDrawPage>& getVisualizedPage() const { return mxVisualizedPage; }
+
+ bool getReducedDisplayQuality() const { return mbReducedDisplayQuality; }
+
+ bool operator==(const ImpViewInformation2D& rCandidate) const
+ {
+ return (maObjectTransformation == rCandidate.maObjectTransformation
+ && maViewTransformation == rCandidate.maViewTransformation
+ && maViewport == rCandidate.maViewport
+ && mxVisualizedPage == rCandidate.mxVisualizedPage
+ && mfViewTime == rCandidate.mfViewTime);
+ }
+};
+
+namespace
+{
+ViewInformation2D::ImplType& theGlobalDefault()
+{
+ static ViewInformation2D::ImplType SINGLETON;
+ return SINGLETON;
+}
+}
+
+ViewInformation2D::ViewInformation2D(const basegfx::B2DHomMatrix& rObjectTransformation,
+ const basegfx::B2DHomMatrix& rViewTransformation,
+ const basegfx::B2DRange& rViewport,
+ const uno::Reference<drawing::XDrawPage>& rxDrawPage,
+ double fViewTime, bool bReducedDisplayQuality)
+ : mpViewInformation2D(ImpViewInformation2D(rObjectTransformation, rViewTransformation,
+ rViewport, rxDrawPage, fViewTime,
+ bReducedDisplayQuality))
+{
+}
+
+ViewInformation2D::ViewInformation2D()
+ : mpViewInformation2D(theGlobalDefault())
+{
+}
+
+ViewInformation2D::ViewInformation2D(const ViewInformation2D&) = default;
+
+ViewInformation2D::ViewInformation2D(ViewInformation2D&&) = default;
+
+ViewInformation2D::~ViewInformation2D() = default;
+
+ViewInformation2D& ViewInformation2D::operator=(const ViewInformation2D&) = default;
+
+ViewInformation2D& ViewInformation2D::operator=(ViewInformation2D&&) = default;
+
+bool ViewInformation2D::operator==(const ViewInformation2D& rCandidate) const
+{
+ return rCandidate.mpViewInformation2D == mpViewInformation2D;
+}
+
+const basegfx::B2DHomMatrix& ViewInformation2D::getObjectTransformation() const
+{
+ return mpViewInformation2D->getObjectTransformation();
+}
+
+const basegfx::B2DHomMatrix& ViewInformation2D::getViewTransformation() const
+{
+ return mpViewInformation2D->getViewTransformation();
+}
+
+const basegfx::B2DRange& ViewInformation2D::getViewport() const
+{
+ return mpViewInformation2D->getViewport();
+}
+
+double ViewInformation2D::getViewTime() const { return mpViewInformation2D->getViewTime(); }
+
+const uno::Reference<drawing::XDrawPage>& ViewInformation2D::getVisualizedPage() const
+{
+ return mpViewInformation2D->getVisualizedPage();
+}
+
+const basegfx::B2DHomMatrix& ViewInformation2D::getObjectToViewTransformation() const
+{
+ return mpViewInformation2D->getObjectToViewTransformation();
+}
+
+const basegfx::B2DHomMatrix& ViewInformation2D::getInverseObjectToViewTransformation() const
+{
+ return mpViewInformation2D->getInverseObjectToViewTransformation();
+}
+
+const basegfx::B2DRange& ViewInformation2D::getDiscreteViewport() const
+{
+ return mpViewInformation2D->getDiscreteViewport();
+}
+
+bool ViewInformation2D::getReducedDisplayQuality() const
+{
+ return mpViewInformation2D->getReducedDisplayQuality();
+}
+
+ViewInformation2D
+createViewInformation2D(const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters)
+{
+ if (!rViewParameters.hasElements())
+ return ViewInformation2D();
+
+ bool bReducedDisplayQuality = false;
+ basegfx::B2DHomMatrix aObjectTransformation;
+ basegfx::B2DHomMatrix aViewTransformation;
+ basegfx::B2DRange aViewport;
+ double fViewTime = 0.0;
+ uno::Reference<drawing::XDrawPage> xVisualizedPage;
+
+ for (auto const& rPropertyValue : rViewParameters)
+ {
+ if (rPropertyValue.Name == g_PropertyName_ReducedDisplayQuality)
+ {
+ rPropertyValue.Value >>= bReducedDisplayQuality;
+ }
+ else if (rPropertyValue.Name == g_PropertyName_ObjectTransformation)
+ {
+ css::geometry::AffineMatrix2D aAffineMatrix2D;
+ rPropertyValue.Value >>= aAffineMatrix2D;
+ basegfx::unotools::homMatrixFromAffineMatrix(aObjectTransformation, aAffineMatrix2D);
+ }
+ else if (rPropertyValue.Name == g_PropertyName_ViewTransformation)
+ {
+ css::geometry::AffineMatrix2D aAffineMatrix2D;
+ rPropertyValue.Value >>= aAffineMatrix2D;
+ basegfx::unotools::homMatrixFromAffineMatrix(aViewTransformation, aAffineMatrix2D);
+ }
+ else if (rPropertyValue.Name == g_PropertyName_Viewport)
+ {
+ css::geometry::RealRectangle2D aUnoViewport;
+ rPropertyValue.Value >>= aUnoViewport;
+ aViewport = basegfx::unotools::b2DRectangleFromRealRectangle2D(aUnoViewport);
+ }
+ else if (rPropertyValue.Name == g_PropertyName_Time)
+ {
+ rPropertyValue.Value >>= fViewTime;
+ }
+ else if (rPropertyValue.Name == g_PropertyName_VisualizedPage)
+ {
+ rPropertyValue.Value >>= xVisualizedPage;
+ }
+ }
+
+ return ViewInformation2D(aObjectTransformation, aViewTransformation, aViewport, xVisualizedPage,
+ fViewTime, bReducedDisplayQuality);
+}
+
+} // end of namespace drawinglayer::geometry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/geometry/viewinformation3d.cxx b/drawinglayer/source/geometry/viewinformation3d.cxx
new file mode 100644
index 000000000..a2a444ab3
--- /dev/null
+++ b/drawinglayer/source/geometry/viewinformation3d.cxx
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/geometry/AffineMatrix3D.hpp>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::geometry
+{
+ /** Implementation class for ViewInformation3D
+ */
+ class ImpViewInformation3D
+ {
+ private:
+ // ViewInformation3D implementation can change refcount, so we have only
+ // two memory regions for pairs of ViewInformation3D/ImpViewInformation3D
+ friend class ::drawinglayer::geometry::ViewInformation3D;
+
+ // the 3D transformations
+ // Object to World. This may change and being adapted when entering 3D transformation
+ // groups
+ basegfx::B3DHomMatrix maObjectTransformation;
+
+ // World to Camera. This includes VRP, VPN and VUV camera coordinate system
+ basegfx::B3DHomMatrix maOrientation;
+
+ // Camera to Device with X,Y and Z [-1.0 .. 1.0]. This is the
+ // 3D to 2D projection which may be parallel or perspective. When it is perspective,
+ // the last line of the homogen matrix will NOT be unused
+ basegfx::B3DHomMatrix maProjection;
+
+ // Device to View with X,Y and Z [0.0 .. 1.0]. This converts from -1 to 1 coordinates
+ // in camera coordinate system to 0 to 1 in unit 2D coordinates. This way it stays
+ // view-independent. To get discrete coordinates, the 2D transformation of a scene
+ // as 2D object needs to be involved
+ basegfx::B3DHomMatrix maDeviceToView;
+
+ // Object to View is the linear combination of all four transformations. It's
+ // buffered to avoid too much matrix multiplying and created on demand
+ basegfx::B3DHomMatrix maObjectToView;
+
+ // the point in time
+ double mfViewTime;
+
+ // the extra PropertyValues; does not contain the transformations
+ uno::Sequence< beans::PropertyValue > mxExtendedInformation;
+
+ // the local UNO API strings
+ static OUString getNamePropertyObjectTransformation()
+ {
+ return "ObjectTransformation";
+ }
+
+ static OUString getNamePropertyOrientation()
+ {
+ return "Orientation";
+ }
+
+ static OUString getNamePropertyProjection()
+ {
+ return "Projection";
+ }
+
+ static OUString getNamePropertyProjection_30()
+ {
+ return "Projection30";
+ }
+
+ static OUString getNamePropertyProjection_31()
+ {
+ return "Projection31";
+ }
+
+ static OUString getNamePropertyProjection_32()
+ {
+ return "Projection32";
+ }
+
+ static OUString getNamePropertyProjection_33()
+ {
+ return "Projection33";
+ }
+
+ static OUString getNamePropertyDeviceToView()
+ {
+ return "DeviceToView";
+ }
+
+ static OUString getNamePropertyTime()
+ {
+ return "Time";
+ }
+
+ // a central PropertyValue parsing method to allow transportation of
+ // all ViewParameters using UNO API
+ void impInterpretPropertyValues(const uno::Sequence< beans::PropertyValue >& rViewParameters)
+ {
+ if(!rViewParameters.hasElements())
+ return;
+
+ const sal_Int32 nCount(rViewParameters.getLength());
+ sal_Int32 nExtendedInsert(0);
+
+ // prepare extended information for filtering. Maximum size is nCount
+ mxExtendedInformation.realloc(nCount);
+ auto pExtendedInformation = mxExtendedInformation.getArray();
+
+ for(sal_Int32 a(0); a < nCount; a++)
+ {
+ const beans::PropertyValue& rProp = rViewParameters[a];
+
+ if(rProp.Name == getNamePropertyObjectTransformation())
+ {
+ css::geometry::AffineMatrix3D aAffineMatrix3D;
+ rProp.Value >>= aAffineMatrix3D;
+ maObjectTransformation = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D);
+ }
+ else if(rProp.Name == getNamePropertyOrientation())
+ {
+ css::geometry::AffineMatrix3D aAffineMatrix3D;
+ rProp.Value >>= aAffineMatrix3D;
+ maOrientation = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D);
+ }
+ else if(rProp.Name == getNamePropertyProjection())
+ {
+ // projection may be defined using a frustum in which case the last line of
+ // the 4x4 matrix is not (0,0,0,1). Since AffineMatrix3D does not support that,
+ // these four values need to be treated extra
+ const double f_30(maProjection.get(3, 0));
+ const double f_31(maProjection.get(3, 1));
+ const double f_32(maProjection.get(3, 2));
+ const double f_33(maProjection.get(3, 3));
+
+ css::geometry::AffineMatrix3D aAffineMatrix3D;
+ rProp.Value >>= aAffineMatrix3D;
+ maProjection = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D);
+
+ maProjection.set(3, 0, f_30);
+ maProjection.set(3, 1, f_31);
+ maProjection.set(3, 2, f_32);
+ maProjection.set(3, 3, f_33);
+ }
+ else if(rProp.Name == getNamePropertyProjection_30())
+ {
+ double f_30(0.0);
+ rProp.Value >>= f_30;
+ maProjection.set(3, 0, f_30);
+ }
+ else if(rProp.Name == getNamePropertyProjection_31())
+ {
+ double f_31(0.0);
+ rProp.Value >>= f_31;
+ maProjection.set(3, 1, f_31);
+ }
+ else if(rProp.Name == getNamePropertyProjection_32())
+ {
+ double f_32(0.0);
+ rProp.Value >>= f_32;
+ maProjection.set(3, 2, f_32);
+ }
+ else if(rProp.Name == getNamePropertyProjection_33())
+ {
+ double f_33(1.0);
+ rProp.Value >>= f_33;
+ maProjection.set(3, 3, f_33);
+ }
+ else if(rProp.Name == getNamePropertyDeviceToView())
+ {
+ css::geometry::AffineMatrix3D aAffineMatrix3D;
+ rProp.Value >>= aAffineMatrix3D;
+ maDeviceToView = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D);
+ }
+ else if(rProp.Name == getNamePropertyTime())
+ {
+ rProp.Value >>= mfViewTime;
+ }
+ else
+ {
+ // extra information; add to filtered information
+ pExtendedInformation[nExtendedInsert++] = rProp;
+ }
+ }
+
+ // extra information size is now known; realloc to final size
+ mxExtendedInformation.realloc(nExtendedInsert);
+ }
+
+ public:
+ ImpViewInformation3D(
+ const basegfx::B3DHomMatrix& rObjectTransformation,
+ const basegfx::B3DHomMatrix& rOrientation,
+ const basegfx::B3DHomMatrix& rProjection,
+ const basegfx::B3DHomMatrix& rDeviceToView,
+ double fViewTime,
+ const uno::Sequence< beans::PropertyValue >& rExtendedParameters)
+ : maObjectTransformation(rObjectTransformation),
+ maOrientation(rOrientation),
+ maProjection(rProjection),
+ maDeviceToView(rDeviceToView),
+ mfViewTime(fViewTime)
+ {
+ impInterpretPropertyValues(rExtendedParameters);
+ }
+
+ explicit ImpViewInformation3D(const uno::Sequence< beans::PropertyValue >& rViewParameters)
+ : mfViewTime()
+ {
+ impInterpretPropertyValues(rViewParameters);
+ }
+
+ ImpViewInformation3D()
+ : mfViewTime()
+ {
+ }
+
+ const basegfx::B3DHomMatrix& getObjectTransformation() const { return maObjectTransformation; }
+ const basegfx::B3DHomMatrix& getOrientation() const { return maOrientation; }
+ const basegfx::B3DHomMatrix& getProjection() const { return maProjection; }
+ const basegfx::B3DHomMatrix& getDeviceToView() const { return maDeviceToView; }
+ double getViewTime() const { return mfViewTime; }
+
+ const basegfx::B3DHomMatrix& getObjectToView() const
+ {
+ // on demand WorldToView creation
+
+ if(maObjectToView.isIdentity())
+ {
+ const_cast< ImpViewInformation3D* >(this)->maObjectToView = maDeviceToView * maProjection * maOrientation * maObjectTransformation;
+ }
+
+ return maObjectToView;
+ }
+
+ const uno::Sequence< beans::PropertyValue >& getExtendedInformationSequence() const
+ {
+ return mxExtendedInformation;
+ }
+
+ bool operator==(const ImpViewInformation3D& rCandidate) const
+ {
+ return (maObjectTransformation == rCandidate.maObjectTransformation
+ && maOrientation == rCandidate.maOrientation
+ && maProjection == rCandidate.maProjection
+ && maDeviceToView == rCandidate.maDeviceToView
+ && mfViewTime == rCandidate.mfViewTime
+ && mxExtendedInformation == rCandidate.mxExtendedInformation);
+ }
+ };
+} // end of namespace drawinglayer::geometry
+
+
+namespace drawinglayer::geometry
+{
+ namespace
+ {
+ ViewInformation3D::ImplType& theGlobalDefault()
+ {
+ static ViewInformation3D::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ ViewInformation3D::ViewInformation3D(
+ const basegfx::B3DHomMatrix& rObjectObjectTransformation,
+ const basegfx::B3DHomMatrix& rOrientation,
+ const basegfx::B3DHomMatrix& rProjection,
+ const basegfx::B3DHomMatrix& rDeviceToView,
+ double fViewTime,
+ const uno::Sequence< beans::PropertyValue >& rExtendedParameters)
+ : mpViewInformation3D(ImpViewInformation3D(
+ rObjectObjectTransformation, rOrientation, rProjection,
+ rDeviceToView, fViewTime, rExtendedParameters))
+ {
+ }
+
+ ViewInformation3D::ViewInformation3D(const uno::Sequence< beans::PropertyValue >& rViewParameters)
+ : mpViewInformation3D(ImpViewInformation3D(rViewParameters))
+ {
+ }
+
+ ViewInformation3D::ViewInformation3D()
+ : mpViewInformation3D(theGlobalDefault())
+ {
+ }
+
+ ViewInformation3D::ViewInformation3D(const ViewInformation3D&) = default;
+
+ ViewInformation3D::ViewInformation3D(ViewInformation3D&&) = default;
+
+ ViewInformation3D::~ViewInformation3D() = default;
+
+ bool ViewInformation3D::isDefault() const
+ {
+ return mpViewInformation3D.same_object(theGlobalDefault());
+ }
+
+ ViewInformation3D& ViewInformation3D::operator=(const ViewInformation3D&) = default;
+
+ ViewInformation3D& ViewInformation3D::operator=(ViewInformation3D&&) = default;
+
+ bool ViewInformation3D::operator==(const ViewInformation3D& rCandidate) const
+ {
+ return rCandidate.mpViewInformation3D == mpViewInformation3D;
+ }
+
+ const basegfx::B3DHomMatrix& ViewInformation3D::getObjectTransformation() const
+ {
+ return mpViewInformation3D->getObjectTransformation();
+ }
+
+ const basegfx::B3DHomMatrix& ViewInformation3D::getOrientation() const
+ {
+ return mpViewInformation3D->getOrientation();
+ }
+
+ const basegfx::B3DHomMatrix& ViewInformation3D::getProjection() const
+ {
+ return mpViewInformation3D->getProjection();
+ }
+
+ const basegfx::B3DHomMatrix& ViewInformation3D::getDeviceToView() const
+ {
+ return mpViewInformation3D->getDeviceToView();
+ }
+
+ const basegfx::B3DHomMatrix& ViewInformation3D::getObjectToView() const
+ {
+ return mpViewInformation3D->getObjectToView();
+ }
+
+ double ViewInformation3D::getViewTime() const
+ {
+ return mpViewInformation3D->getViewTime();
+ }
+
+ const uno::Sequence< beans::PropertyValue >& ViewInformation3D::getExtendedInformationSequence() const
+ {
+ return mpViewInformation3D->getExtendedInformationSequence();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
new file mode 100644
index 000000000..76fa1a976
--- /dev/null
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D() {}
+
+void BufferedDecompositionPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getBuffered2DDecomposition().empty())
+ {
+ Primitive2DContainer aNewSequence;
+ create2DDecomposition(aNewSequence, rViewInformation);
+ const_cast<BufferedDecompositionPrimitive2D*>(this)->setBuffered2DDecomposition(
+ std::move(aNewSequence));
+ }
+
+ rVisitor.visit(getBuffered2DDecomposition());
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx
new file mode 100644
index 000000000..b3aed429b
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+PolyPolygonColorPrimitive2D::PolyPolygonColorPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor)
+ : maPolyPolygon(rPolyPolygon)
+ , maBColor(rBColor)
+{
+}
+
+bool PolyPolygonColorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonColorPrimitive2D& rCompare
+ = static_cast<const PolyPolygonColorPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonColorPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // return range
+ return basegfx::utils::getRange(getB2DPolyPolygon());
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonColorPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
new file mode 100644
index 000000000..3e88b6c0d
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <rtl/ref.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonGradientPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getFillGradient().isDefault())
+ {
+ // create SubSequence with FillGradientPrimitive2D
+ const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange());
+ rtl::Reference<FillGradientPrimitive2D> pNewGradient = new FillGradientPrimitive2D(
+ aPolyPolygonRange, getDefinitionRange(), getFillGradient());
+ Primitive2DContainer aSubSequence{ pNewGradient };
+
+ // create mask primitive
+ rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)));
+ }
+}
+
+PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rPolyPolygon.getB2DRange())
+ , maFillGradient(rFillGradient)
+{
+}
+
+PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rDefinitionRange)
+ , maFillGradient(rFillGradient)
+{
+}
+
+bool PolyPolygonGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonGradientPrimitive2D& rCompare
+ = static_cast<const PolyPolygonGradientPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillGradient() == rCompare.getFillGradient());
+ }
+
+ return false;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonGradientPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx
new file mode 100644
index 000000000..a65d1d08a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <vcl/graph.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonGraphicPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getFillGraphic().isDefault())
+ return;
+
+ const Graphic& rGraphic = getFillGraphic().getGraphic();
+ const GraphicType aType(rGraphic.GetType());
+
+ // is there a bitmap or a metafile (do we have content)?
+ if (GraphicType::Bitmap != aType && GraphicType::GdiMetafile != aType)
+ return;
+
+ const Size aPrefSize(rGraphic.GetPrefSize());
+
+ // does content have a size?
+ if (!(aPrefSize.Width() && aPrefSize.Height()))
+ return;
+
+ // create SubSequence with FillGraphicPrimitive2D based on polygon range
+ const basegfx::B2DRange aOutRange(getB2DPolyPolygon().getB2DRange());
+ const basegfx::B2DHomMatrix aNewObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(aOutRange.getRange(),
+ aOutRange.getMinimum()));
+ Primitive2DReference xSubRef;
+
+ if (aOutRange != getDefinitionRange())
+ {
+ // we want to paint (tiled) content which is defined relative to DefinitionRange
+ // with the same tiling and offset(s) in the target range of the geometry (the
+ // polygon). The range given in the local FillGraphicAttribute defines the position
+ // of the graphic in unit coordinates relative to the DefinitionRange. Transform
+ // this using DefinitionRange to get to the global definition and then with the
+ // inverse transformation from the target range to go to unit coordinates relative
+ // to that target coordinate system.
+ basegfx::B2DRange aAdaptedRange(getFillGraphic().getGraphicRange());
+
+ const basegfx::B2DHomMatrix aFromDefinitionRangeToGlobal(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(getDefinitionRange().getRange(),
+ getDefinitionRange().getMinimum()));
+
+ aAdaptedRange.transform(aFromDefinitionRangeToGlobal);
+
+ basegfx::B2DHomMatrix aFromGlobalToOutRange(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(aOutRange.getRange(),
+ aOutRange.getMinimum()));
+ aFromGlobalToOutRange.invert();
+
+ aAdaptedRange.transform(aFromGlobalToOutRange);
+
+ const drawinglayer::attribute::FillGraphicAttribute aAdaptedFillGraphicAttribute(
+ getFillGraphic().getGraphic(), aAdaptedRange, getFillGraphic().getTiling(),
+ getFillGraphic().getOffsetX(), getFillGraphic().getOffsetY());
+
+ xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, aAdaptedFillGraphicAttribute);
+ }
+ else
+ {
+ xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, getFillGraphic());
+ }
+
+ // embed to mask primitive
+ rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), Primitive2DContainer{ xSubRef }));
+}
+
+PolyPolygonGraphicPrimitive2D::PolyPolygonGraphicPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ const attribute::FillGraphicAttribute& rFillGraphic)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rDefinitionRange)
+ , maFillGraphic(rFillGraphic)
+{
+}
+
+bool PolyPolygonGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonGraphicPrimitive2D& rCompare
+ = static_cast<const PolyPolygonGraphicPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillGraphic() == rCompare.getFillGraphic());
+ }
+
+ return false;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonGraphicPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx
new file mode 100644
index 000000000..2bfed5fb1
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonHairlinePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
+ const sal_uInt32 nCount(aPolyPolygon.count());
+
+ if (nCount)
+ {
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ rContainer.push_back(
+ new PolygonHairlinePrimitive2D(aPolyPolygon.getB2DPolygon(a), getBColor()));
+ }
+ }
+}
+
+PolyPolygonHairlinePrimitive2D::PolyPolygonHairlinePrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor)
+ : maPolyPolygon(rPolyPolygon)
+ , maBColor(rBColor)
+{
+}
+
+bool PolyPolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonHairlinePrimitive2D& rCompare
+ = static_cast<const PolyPolygonHairlinePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonHairlinePrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // return range
+ return basegfx::utils::getRange(getB2DPolyPolygon());
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonHairlinePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx
new file mode 100644
index 000000000..7fc0502c8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
+#include <rtl/ref.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonHatchPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getFillHatch().isDefault())
+ {
+ // create SubSequence with FillHatchPrimitive2D
+ const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange());
+ rtl::Reference<FillHatchPrimitive2D> pNewHatch = new FillHatchPrimitive2D(
+ aPolyPolygonRange, getDefinitionRange(), getBackgroundColor(), getFillHatch());
+ Primitive2DContainer aSubSequence{ pNewHatch };
+
+ // create mask primitive
+ rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)));
+ }
+}
+
+PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBackgroundColor,
+ const attribute::FillHatchAttribute& rFillHatch)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rPolyPolygon.getB2DRange())
+ , maBackgroundColor(rBackgroundColor)
+ , maFillHatch(rFillHatch)
+{
+}
+
+PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rBackgroundColor, const attribute::FillHatchAttribute& rFillHatch)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rDefinitionRange)
+ , maBackgroundColor(rBackgroundColor)
+ , maFillHatch(rFillHatch)
+{
+}
+
+bool PolyPolygonHatchPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonHatchPrimitive2D& rCompare
+ = static_cast<const PolyPolygonHatchPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getBackgroundColor() == rCompare.getBackgroundColor()
+ && getFillHatch() == rCompare.getFillHatch());
+ }
+
+ return false;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonHatchPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx
new file mode 100644
index 000000000..4f9f83298
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonMarkerPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
+ const sal_uInt32 nCount(aPolyPolygon.count());
+
+ if (nCount)
+ {
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aPolyPolygon.getB2DPolygon(a),
+ getRGBColorA(), getRGBColorB(),
+ getDiscreteDashLength()));
+ }
+ }
+}
+
+PolyPolygonMarkerPrimitive2D::PolyPolygonMarkerPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB, double fDiscreteDashLength)
+ : maPolyPolygon(rPolyPolygon)
+ , maRGBColorA(rRGBColorA)
+ , maRGBColorB(rRGBColorB)
+ , mfDiscreteDashLength(fDiscreteDashLength)
+{
+}
+
+bool PolyPolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonMarkerPrimitive2D& rCompare
+ = static_cast<const PolyPolygonMarkerPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonMarkerPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // return range
+ return basegfx::utils::getRange(getB2DPolyPolygon());
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonMarkerPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONMARKERPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx
new file mode 100644
index 000000000..d5ed17980
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonSelectionPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getTransparence() >= 1.0 || !getB2DPolyPolygon().count())
+ return;
+
+ Primitive2DContainer aRetval;
+
+ if (getFill() && getB2DPolyPolygon().isClosed())
+ {
+ // create fill primitive
+ const Primitive2DReference aFill(
+ new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), getColor()));
+
+ aRetval = Primitive2DContainer{ aFill };
+ }
+
+ if (getDiscreteGrow() > 0.0)
+ {
+ const attribute::LineAttribute aLineAttribute(getColor(),
+ getDiscreteGrow() * getDiscreteUnit() * 2.0);
+ const Primitive2DReference aFatLine(
+ new PolyPolygonStrokePrimitive2D(getB2DPolyPolygon(), aLineAttribute));
+
+ aRetval.push_back(aFatLine);
+ }
+
+ // embed filled to transparency (if used)
+ if (!aRetval.empty() && getTransparence() > 0.0)
+ {
+ const Primitive2DReference aTrans(
+ new UnifiedTransparencePrimitive2D(std::move(aRetval), getTransparence()));
+
+ aRetval = Primitive2DContainer{ aTrans };
+ }
+
+ rContainer.append(std::move(aRetval));
+}
+
+PolyPolygonSelectionPrimitive2D::PolyPolygonSelectionPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rColor,
+ double fTransparence, double fDiscreteGrow, bool bFill)
+ : maPolyPolygon(rPolyPolygon)
+ , maColor(rColor)
+ , mfTransparence(fTransparence)
+ , mfDiscreteGrow(fabs(fDiscreteGrow))
+ , mbFill(bFill)
+{
+}
+
+bool PolyPolygonSelectionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonSelectionPrimitive2D& rCompare
+ = static_cast<const PolyPolygonSelectionPrimitive2D&>(rPrimitive);
+
+ return (
+ getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() && getColor() == rCompare.getColor()
+ && getTransparence() == rCompare.getTransparence()
+ && getDiscreteGrow() == rCompare.getDiscreteGrow() && getFill() == rCompare.getFill());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonSelectionPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(basegfx::utils::getRange(getB2DPolyPolygon()));
+
+ if (getDiscreteGrow() > 0.0)
+ {
+ // get the current DiscreteUnit (not sure if getDiscreteUnit() is updated here, better go safe way)
+ const double fDiscreteUnit(
+ (rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0))
+ .getLength());
+
+ aRetval.grow(fDiscreteUnit * getDiscreteGrow());
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonSelectionPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONSELECTIONPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx
new file mode 100644
index 000000000..7191f3085
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonStrokePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
+ const sal_uInt32 nCount(aPolyPolygon.count());
+
+ if (nCount)
+ {
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ rContainer.push_back(new PolygonStrokePrimitive2D(
+ aPolyPolygon.getB2DPolygon(a), getLineAttribute(), getStrokeAttribute()));
+ }
+ }
+}
+
+PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ : maPolyPolygon(rPolyPolygon)
+ , maLineAttribute(rLineAttribute)
+ , maStrokeAttribute(rStrokeAttribute)
+{
+}
+
+PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute)
+ : maPolyPolygon(rPolyPolygon)
+ , maLineAttribute(rLineAttribute)
+{
+}
+
+bool PolyPolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonStrokePrimitive2D& rCompare
+ = static_cast<const PolyPolygonStrokePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getLineAttribute() == rCompare.getLineAttribute()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonStrokePrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // get range of it (subdivided)
+ basegfx::B2DRange aRetval(basegfx::utils::getRange(getB2DPolyPolygon()));
+
+ // if width, grow by line width
+ if (getLineAttribute().getWidth())
+ {
+ aRetval.grow(getLineAttribute().getWidth() / 2.0);
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonStrokePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/Primitive2DContainer.cxx b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx
new file mode 100644
index 000000000..48b0c625e
--- /dev/null
+++ b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+using namespace css;
+
+namespace drawinglayer::primitive2d
+{
+Primitive2DContainer::Primitive2DContainer(
+ const css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>>& rSource)
+{
+ for (const auto& rPrimitive : rSource)
+ append(static_cast<const UnoPrimitive2D*>(rPrimitive.get())->getBasePrimitive2D());
+}
+Primitive2DContainer::Primitive2DContainer(
+ const std::deque<css::uno::Reference<css::graphic::XPrimitive2D>>& rSource)
+{
+ for (const auto& rPrimitive : rSource)
+ append(static_cast<const UnoPrimitive2D*>(rPrimitive.get())->getBasePrimitive2D());
+}
+
+css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>>
+Primitive2DContainer::toSequence() const
+{
+ css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>> aVal(size());
+ auto p = aVal.getArray();
+ for (const auto& rPrimitive : *this)
+ {
+ *p = new UnoPrimitive2D(rPrimitive);
+ ++p;
+ }
+ return aVal;
+}
+
+Primitive2DContainer Primitive2DContainer::maybeInvert(bool bInvert)
+{
+ if (bInvert)
+ std::reverse(begin(), end());
+ return std::move(*this);
+}
+
+// get B2DRange from a given Primitive2DSequence
+basegfx::B2DRange
+Primitive2DContainer::getB2DRange(const geometry::ViewInformation2D& aViewInformation) const
+{
+ basegfx::B2DRange aRetval;
+
+ if (!empty())
+ {
+ const sal_Int32 nCount(size());
+
+ for (sal_Int32 a(0); a < nCount; a++)
+ {
+ aRetval.expand(getB2DRangeFromPrimitive2DReference((*this)[a], aViewInformation));
+ }
+ }
+
+ return aRetval;
+}
+
+bool Primitive2DContainer::operator==(const Primitive2DContainer& rB) const
+{
+ const bool bAHasElements(!empty());
+
+ if (bAHasElements != !rB.empty())
+ {
+ return false;
+ }
+
+ if (!bAHasElements)
+ {
+ return true;
+ }
+
+ const size_t nCount(size());
+
+ if (nCount != rB.size())
+ {
+ return false;
+ }
+
+ for (size_t a(0); a < nCount; a++)
+ {
+ if (!arePrimitive2DReferencesEqual((*this)[a], rB[a]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+Primitive2DContainer::~Primitive2DContainer() {}
+
+void Primitive2DContainer::append(const Primitive2DReference& rSource) { push_back(rSource); }
+
+void Primitive2DContainer::append(const Primitive2DContainer& rSource)
+{
+ insert(end(), rSource.begin(), rSource.end());
+}
+
+void Primitive2DContainer::append(Primitive2DContainer&& rSource)
+{
+ this->insert(this->end(), std::make_move_iterator(rSource.begin()),
+ std::make_move_iterator(rSource.end()));
+}
+
+UnoPrimitive2D::~UnoPrimitive2D() {}
+
+css::uno::Sequence<::css::uno::Reference<::css::graphic::XPrimitive2D>>
+ SAL_CALL UnoPrimitive2D::getDecomposition(
+ const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters)
+{
+ std::unique_lock aGuard(m_aMutex);
+ return mxPrimitive->getDecomposition(rViewParameters).toSequence();
+}
+
+css::geometry::RealRectangle2D SAL_CALL
+UnoPrimitive2D::getRange(const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters)
+{
+ std::unique_lock aGuard(m_aMutex);
+ return mxPrimitive->getRange(rViewParameters);
+}
+
+sal_Int64 SAL_CALL UnoPrimitive2D::estimateUsage()
+{
+ std::unique_lock aGuard(m_aMutex);
+ return mxPrimitive->estimateUsage();
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx
new file mode 100644
index 000000000..6a3482319
--- /dev/null
+++ b/drawinglayer/source/primitive2d/Tools.cxx
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+using namespace css;
+
+namespace drawinglayer::primitive2d
+{
+// get B2DRange from a given Primitive2DReference
+basegfx::B2DRange
+getB2DRangeFromPrimitive2DReference(const Primitive2DReference& rCandidate,
+ const geometry::ViewInformation2D& aViewInformation)
+{
+ if (!rCandidate)
+ return basegfx::B2DRange();
+
+ return rCandidate->getB2DRange(aViewInformation);
+}
+
+bool arePrimitive2DReferencesEqual(const Primitive2DReference& rxA, const Primitive2DReference& rxB)
+{
+ const bool bAIs(rxA.is());
+
+ if (bAIs != rxB.is())
+ {
+ return false;
+ }
+
+ if (!bAIs)
+ {
+ return true;
+ }
+
+ return rxA->operator==(*rxB);
+}
+
+bool arePrimitive2DReferencesEqual(const css::uno::Reference<css::graphic::XPrimitive2D>& rxA,
+ const css::uno::Reference<css::graphic::XPrimitive2D>& rxB)
+{
+ const bool bAIs(rxA.is());
+
+ if (bAIs != rxB.is())
+ {
+ return false;
+ }
+
+ if (!bAIs)
+ {
+ return true;
+ }
+
+ auto pA = static_cast<const UnoPrimitive2D*>(rxA.get());
+ auto pB = static_cast<const UnoPrimitive2D*>(rxB.get());
+
+ return (*pA->getBasePrimitive2D()) == (*pB->getBasePrimitive2D());
+}
+
+OUString idToString(sal_uInt32 nId)
+{
+ switch (nId)
+ {
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
+ return "TRANSPARENCE";
+ case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D:
+ return "ANIMATEDSWITCH";
+ case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D:
+ return "ANIMATEDBLINK";
+ case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D:
+ return "ANIMATEDINTERPOLATE";
+ case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D:
+ return "BACKGROUNDCOLOR";
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ return "BITMAP";
+ case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D:
+ return "CONTROL";
+ case PRIMITIVE2D_ID_EMBEDDED3DPRIMITIVE2D:
+ return "EMBEDDED3D";
+ case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
+ return "FILLGRAPHIC";
+ case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
+ return "FILLGRADIENT";
+ case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D:
+ return "FILLHATCH";
+ case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D:
+ return "GRAPHIC";
+ case PRIMITIVE2D_ID_GRIDPRIMITIVE2D:
+ return "GRID";
+ case PRIMITIVE2D_ID_GROUPPRIMITIVE2D:
+ return "GROUP";
+ case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D:
+ return "HELPLINE";
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+ return "MARKERARRAY";
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ return "MASK";
+ case PRIMITIVE2D_ID_MEDIAPRIMITIVE2D:
+ return "MEDIA";
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D:
+ return "METAFILE";
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+ return "MODIFIEDCOLOR";
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ return "POLYGONHAIRLINE";
+ case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D:
+ return "POLYGONMARKER";
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ return "POLYGONSTROKE";
+ case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D:
+ return "POLYGONSTROKEARROW";
+ case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D:
+ return "POLYPOLYGONSTROKE";
+ case PRIMITIVE2D_ID_POLYPOLYGONSTROKEARROWPRIMITIVE2D:
+ return "POLYPOLYGONSTROKEARROW";
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ return "POLYPOLYGONCOLOR";
+ case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D:
+ return "POLYPOLYGONGRADIENT";
+ case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D:
+ return "POLYPOLYGONHATCH";
+ case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
+ return "POLYPOLYGONGRAPHIC";
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D:
+ return "SCENE";
+ case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
+ return "SHADOW";
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
+ return "TEXTSIMPLEPORTION";
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
+ return "TEXTDECORATEDPORTION";
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ return "TRANSFORM";
+ case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+ return "UNIFIEDTRANSPARENCE";
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ return "POINTARRAY";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D:
+ return "TEXTHIERARCHYFIELD";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D:
+ return "TEXTHIERARCHYLINE";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D:
+ return "TEXTHIERARCHYPARAGRAPH";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D:
+ return "TEXTHIERARCHYBLOCK";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D:
+ return "TEXTHIERARCHYEDIT";
+ case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D:
+ return "POLYGONWAVE";
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D:
+ return "WRONGSPELL";
+ case PRIMITIVE2D_ID_TEXTEFFECTPRIMITIVE2D:
+ return "TEXTEFFECT";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D:
+ return "TEXTHIERARCHYBULLET";
+ case PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D:
+ return "POLYPOLYGONHAIRLINE";
+ case PRIMITIVE2D_ID_EXECUTEPRIMITIVE2D:
+ return "EXECUTE";
+ case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D:
+ return "PAGEPREVIEW";
+ case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D:
+ return "STRUCTURETAG";
+ case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D:
+ return "BORDERLINE";
+ case PRIMITIVE2D_ID_POLYPOLYGONMARKERPRIMITIVE2D:
+ return "POLYPOLYGONMARKER";
+ case PRIMITIVE2D_ID_HITTESTPRIMITIVE2D:
+ return "HITTEST";
+ case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
+ return "INVERT";
+ case PRIMITIVE2D_ID_DISCRETEBITMAPPRIMITIVE2D:
+ return "DISCRETEBITMAP";
+ case PRIMITIVE2D_ID_WALLPAPERBITMAPPRIMITIVE2D:
+ return "WALLPAPERBITMAP";
+ case PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D:
+ return "TEXTLINE";
+ case PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D:
+ return "TEXTCHARACTERSTRIKEOUT";
+ case PRIMITIVE2D_ID_TEXTGEOMETRYSTRIKEOUTPRIMITIVE2D:
+ return "TEXTGEOMETRYSTRIKEOUT";
+ case PRIMITIVE2D_ID_EPSPRIMITIVE2D:
+ return "EPS";
+ case PRIMITIVE2D_ID_DISCRETESHADOWPRIMITIVE2D:
+ return "DISCRETESHADOW";
+ case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D:
+ return "HIDDENGEOMETRY";
+ case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D:
+ return "SVGLINEARGRADIENT";
+ case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D:
+ return "SVGRADIALGRADIENT";
+ case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D:
+ return "SVGLINEARATOM";
+ case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D:
+ return "SVGRADIALATOM";
+ case PRIMITIVE2D_ID_CROPPRIMITIVE2D:
+ return "CROP";
+ case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D:
+ return "PATTERNFILL";
+ case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
+ return "OBJECTINFO";
+ case PRIMITIVE2D_ID_POLYPOLYGONSELECTIONPRIMITIVE2D:
+ return "POLYPOLYGONSELECTION";
+ case PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D:
+ return "PAGEHIERARCHY";
+ case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
+ return "GLOWPRIMITIVE";
+ case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
+ return "SOFTEDGEPRIMITIVE";
+ default:
+ return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF);
+ }
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/animatedprimitive2d.cxx b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx
new file mode 100644
index 000000000..67349a834
--- /dev/null
+++ b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void AnimatedSwitchPrimitive2D::setAnimationEntry(const animation::AnimationEntry& rNew)
+ {
+ // clone given animation description
+ mpAnimationEntry = rNew.clone();
+ }
+
+ AnimatedSwitchPrimitive2D::AnimatedSwitchPrimitive2D(
+ const animation::AnimationEntry& rAnimationEntry,
+ Primitive2DContainer&& aChildren,
+ bool bIsTextAnimation)
+ : GroupPrimitive2D(std::move(aChildren)),
+ mbIsTextAnimation(bIsTextAnimation)
+ {
+ // clone given animation description
+ mpAnimationEntry = rAnimationEntry.clone();
+ }
+
+ AnimatedSwitchPrimitive2D::~AnimatedSwitchPrimitive2D()
+ {
+ }
+
+ bool AnimatedSwitchPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const AnimatedSwitchPrimitive2D& rCompare = static_cast< const AnimatedSwitchPrimitive2D& >(rPrimitive);
+
+ return (getAnimationEntry() == rCompare.getAnimationEntry());
+ }
+
+ return false;
+ }
+
+ void AnimatedSwitchPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(getChildren().empty())
+ return;
+
+ const double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+ const sal_uInt32 nLen(getChildren().size());
+ sal_uInt32 nIndex(basegfx::fround(fState * static_cast<double>(nLen)));
+
+ if(nIndex >= nLen)
+ {
+ nIndex = nLen - 1;
+ }
+
+ const Primitive2DReference xRef(getChildren()[nIndex], uno::UNO_SET_THROW);
+ rVisitor.visit(xRef);
+ }
+
+ // provide unique ID
+ sal_uInt32 AnimatedSwitchPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ AnimatedBlinkPrimitive2D::AnimatedBlinkPrimitive2D(
+ const animation::AnimationEntry& rAnimationEntry,
+ Primitive2DContainer&& aChildren)
+ : AnimatedSwitchPrimitive2D(rAnimationEntry, std::move(aChildren), true/*bIsTextAnimation*/)
+ {
+ }
+
+ void AnimatedBlinkPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getChildren().empty())
+ {
+ const double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+
+ if(fState < 0.5)
+ {
+ getChildren(rVisitor);
+ }
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 AnimatedBlinkPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ AnimatedInterpolatePrimitive2D::AnimatedInterpolatePrimitive2D(
+ const std::vector< basegfx::B2DHomMatrix >& rmMatrixStack,
+ const animation::AnimationEntry& rAnimationEntry,
+ Primitive2DContainer&& aChildren)
+ : AnimatedSwitchPrimitive2D(rAnimationEntry, std::move(aChildren), true/*bIsTextAnimation*/)
+ {
+ // copy matrices to locally pre-decomposed matrix stack
+ const sal_uInt32 nCount(rmMatrixStack.size());
+ maMatrixStack.reserve(nCount);
+
+ for(const auto& a : rmMatrixStack)
+ {
+ maMatrixStack.emplace_back(a);
+ }
+ }
+
+ void AnimatedInterpolatePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ const sal_uInt32 nSize(maMatrixStack.size());
+
+ if(nSize)
+ {
+ double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+
+ if(fState < 0.0)
+ {
+ fState = 0.0;
+ }
+ else if(fState > 1.0)
+ {
+ fState = 1.0;
+ }
+
+ const double fIndex(fState * static_cast<double>(nSize - 1));
+ const sal_uInt32 nIndA(sal_uInt32(floor(fIndex)));
+ const double fOffset(fIndex - static_cast<double>(nIndA));
+ basegfx::B2DHomMatrix aTargetTransform;
+ std::vector< basegfx::utils::B2DHomMatrixBufferedDecompose >::const_iterator aMatA(maMatrixStack.begin() + nIndA);
+
+ if(basegfx::fTools::equalZero(fOffset))
+ {
+ // use matrix from nIndA directly
+ aTargetTransform = aMatA->getB2DHomMatrix();
+ }
+ else
+ {
+ // interpolate. Get involved buffered decomposed matrices
+ const sal_uInt32 nIndB((nIndA + 1) % nSize);
+ std::vector< basegfx::utils::B2DHomMatrixBufferedDecompose >::const_iterator aMatB(maMatrixStack.begin() + nIndB);
+
+ // interpolate for fOffset [0.0 .. 1.0[
+ const basegfx::B2DVector aScale(basegfx::interpolate(aMatA->getScale(), aMatB->getScale(), fOffset));
+ const basegfx::B2DVector aTranslate(basegfx::interpolate(aMatA->getTranslate(), aMatB->getTranslate(), fOffset));
+ const double fRotate(((aMatB->getRotate() - aMatA->getRotate()) * fOffset) + aMatA->getRotate());
+ const double fShearX(((aMatB->getShearX() - aMatA->getShearX()) * fOffset) + aMatA->getShearX());
+
+ // build matrix for state
+ aTargetTransform = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, fShearX, fRotate, aTranslate);
+ }
+
+ // create new transform primitive reference, return new sequence
+ Primitive2DReference xRef(new TransformPrimitive2D(aTargetTransform, Primitive2DContainer(getChildren())));
+ rVisitor.visit(xRef);
+ }
+ else
+ {
+ getChildren(rVisitor);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 AnimatedInterpolatePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D;
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx
new file mode 100644
index 000000000..8f68d9e73
--- /dev/null
+++ b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void BackgroundColorPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!rViewInformation.getViewport().isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rViewInformation.getViewport()));
+ rContainer.push_back(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), getBColor()));
+ }
+ }
+
+ BackgroundColorPrimitive2D::BackgroundColorPrimitive2D(
+ const basegfx::BColor& rBColor,
+ double fTransparency)
+ : maBColor(rBColor),
+ mfTransparency(fTransparency)
+ {
+ }
+
+ bool BackgroundColorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const BackgroundColorPrimitive2D& rCompare = static_cast<const BackgroundColorPrimitive2D&>(rPrimitive);
+
+ return (getBColor() == rCompare.getBColor() && getTransparency() == rCompare.getTransparency());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange BackgroundColorPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // always as big as the view
+ return rViewInformation.getViewport();
+ }
+
+ void BackgroundColorPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getBuffered2DDecomposition().empty() && (maLastViewport != rViewInformation.getViewport()))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< BackgroundColorPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange
+ const_cast< BackgroundColorPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 BackgroundColorPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/baseprimitive2d.cxx b/drawinglayer/source/primitive2d/baseprimitive2d.cxx
new file mode 100644
index 000000000..a2e0eaf6b
--- /dev/null
+++ b/drawinglayer/source/primitive2d/baseprimitive2d.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/utils/canvastools.hxx>
+
+using namespace css;
+
+namespace drawinglayer::primitive2d
+{
+BasePrimitive2D::BasePrimitive2D() {}
+
+BasePrimitive2D::~BasePrimitive2D() {}
+
+bool BasePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ return (getPrimitive2DID() == rPrimitive.getPrimitive2DID());
+}
+
+namespace
+{
+// Visitor class to get the B2D range from a tree of Primitive2DReference's
+//
+class B2DRangeVisitor : public Primitive2DDecompositionVisitor
+{
+public:
+ const geometry::ViewInformation2D& mrViewInformation;
+ basegfx::B2DRange maRetval;
+ B2DRangeVisitor(const geometry::ViewInformation2D& rViewInformation)
+ : mrViewInformation(rViewInformation)
+ {
+ }
+ virtual void visit(const Primitive2DReference& r) override
+ {
+ maRetval.expand(getB2DRangeFromPrimitive2DReference(r, mrViewInformation));
+ }
+ virtual void visit(const Primitive2DContainer& r) override
+ {
+ maRetval.expand(r.getB2DRange(mrViewInformation));
+ }
+ virtual void visit(Primitive2DContainer&& r) override
+ {
+ maRetval.expand(r.getB2DRange(mrViewInformation));
+ }
+};
+}
+
+basegfx::B2DRange
+BasePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ B2DRangeVisitor aVisitor(rViewInformation);
+ get2DDecomposition(aVisitor, rViewInformation);
+ return aVisitor.maRetval;
+}
+
+void BasePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& /*rVisitor*/,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+}
+
+Primitive2DContainer
+BasePrimitive2D::getDecomposition(const uno::Sequence<beans::PropertyValue>& rViewParameters)
+{
+ const auto aViewInformation = geometry::createViewInformation2D(rViewParameters);
+ Primitive2DContainer aContainer;
+ get2DDecomposition(aContainer, aViewInformation);
+ return aContainer;
+}
+
+css::geometry::RealRectangle2D
+BasePrimitive2D::getRange(const uno::Sequence<beans::PropertyValue>& rViewParameters)
+{
+ const auto aViewInformation = geometry::createViewInformation2D(rViewParameters);
+ return basegfx::unotools::rectangle2DFromB2DRectangle(getB2DRange(aViewInformation));
+}
+
+sal_Int64 BasePrimitive2D::estimateUsage()
+{
+ return 0; // for now ignore the objects themselves
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx
new file mode 100644
index 000000000..6f256b6e1
--- /dev/null
+++ b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+BitmapPrimitive2D::BitmapPrimitive2D(const css::uno::Reference<css::awt::XBitmap>& rXBitmap,
+ const basegfx::B2DHomMatrix& rTransform)
+ : maXBitmap(rXBitmap)
+ , maTransform(rTransform)
+{
+}
+
+bool BitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const BitmapPrimitive2D& rCompare = static_cast<const BitmapPrimitive2D&>(rPrimitive);
+
+ return (getXBitmap() == rCompare.getXBitmap() && getTransform() == rCompare.getTransform());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+BitmapPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(maTransform);
+ return aRetval;
+}
+
+sal_Int64 BitmapPrimitive2D::estimateUsage()
+{
+ if (!getXBitmap().is())
+ {
+ return 0;
+ }
+
+ uno::Reference<util::XAccounting> const xAcc(getXBitmap(), uno::UNO_QUERY);
+
+ if (!xAcc.is())
+ {
+ return 0;
+ }
+
+ return xAcc->estimateUsage();
+}
+
+// provide unique ID
+sal_uInt32 BitmapPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_BITMAPPRIMITIVE2D; }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx
new file mode 100644
index 000000000..7ede1f2c3
--- /dev/null
+++ b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <rtl/math.hxx>
+
+#include <algorithm>
+
+
+namespace drawinglayer::primitive2d
+{
+ BorderLine::BorderLine(
+ const drawinglayer::attribute::LineAttribute& rLineAttribute,
+ double fStartLeft,
+ double fStartRight,
+ double fEndLeft,
+ double fEndRight)
+ : maLineAttribute(rLineAttribute),
+ mfStartLeft(fStartLeft),
+ mfStartRight(fStartRight),
+ mfEndLeft(fEndLeft),
+ mfEndRight(fEndRight),
+ mbIsGap(false)
+ {
+ }
+
+ BorderLine::BorderLine(
+ double fWidth)
+ : maLineAttribute(basegfx::BColor(), fWidth),
+ mfStartLeft(0.0),
+ mfStartRight(0.0),
+ mfEndLeft(0.0),
+ mfEndRight(0.0),
+ mbIsGap(true)
+ {
+ }
+
+ BorderLine::~BorderLine()
+ {
+ }
+
+ bool BorderLine::operator==(const BorderLine& rBorderLine) const
+ {
+ return getLineAttribute() == rBorderLine.getLineAttribute()
+ && getStartLeft() == rBorderLine.getStartLeft()
+ && getStartRight() == rBorderLine.getStartRight()
+ && getEndLeft() == rBorderLine.getEndLeft()
+ && getEndRight() == rBorderLine.getEndRight()
+ && isGap() == rBorderLine.isGap();
+ }
+
+ // helper to add a centered, maybe stroked line primitive to rContainer
+ static void addPolygonStrokePrimitive2D(
+ Primitive2DContainer& rContainer,
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ {
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(rStart);
+ aPolygon.append(rEnd);
+
+ if (rStrokeAttribute.isDefault())
+ {
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(
+ aPolygon,
+ rLineAttribute));
+ }
+ else
+ {
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(
+ aPolygon,
+ rLineAttribute,
+ rStrokeAttribute));
+ }
+ }
+
+ double BorderLinePrimitive2D::getFullWidth() const
+ {
+ double fRetval(0.0);
+
+ for(const auto& candidate : maBorderLines)
+ {
+ fRetval += candidate.getLineAttribute().getWidth();
+ }
+
+ return fRetval;
+ }
+
+ void BorderLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if (getStart().equal(getEnd()) || getBorderLines().empty())
+ return;
+
+ // get data and vectors
+ basegfx::B2DVector aVector(getEnd() - getStart());
+ aVector.normalize();
+ const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
+ const double fFullWidth(getFullWidth());
+ double fOffset(fFullWidth * -0.5);
+
+ for(const auto& candidate : maBorderLines)
+ {
+ const double fWidth(candidate.getLineAttribute().getWidth());
+
+ if(!candidate.isGap())
+ {
+ const basegfx::B2DVector aDeltaY(aPerpendicular * (fOffset + (fWidth * 0.5)));
+ const basegfx::B2DPoint aStart(getStart() + aDeltaY);
+ const basegfx::B2DPoint aEnd(getEnd() + aDeltaY);
+ const bool bStartPerpendicular(rtl::math::approxEqual(candidate.getStartLeft(), candidate.getStartRight()));
+ const bool bEndPerpendicular(rtl::math::approxEqual(candidate.getEndLeft(), candidate.getEndRight()));
+
+ if(bStartPerpendicular && bEndPerpendicular)
+ {
+ // start and end extends lead to an edge perpendicular to the line, so we can just use
+ // a PolygonStrokePrimitive2D for representation
+ addPolygonStrokePrimitive2D(
+ rContainer,
+ aStart - (aVector * candidate.getStartLeft()),
+ aEnd + (aVector * candidate.getEndLeft()),
+ candidate.getLineAttribute(),
+ getStrokeAttribute());
+ }
+ else
+ {
+ // start and/or end extensions lead to a lineStart/End that is *not*
+ // perpendicular to the line itself
+ if(getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
+ {
+ // without stroke, we can simply represent that using a filled polygon
+ const basegfx::B2DVector aHalfLineOffset(aPerpendicular * (candidate.getLineAttribute().getWidth() * 0.5));
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(aStart - aHalfLineOffset - (aVector * candidate.getStartLeft()));
+ aPolygon.append(aEnd - aHalfLineOffset + (aVector * candidate.getEndLeft()));
+ aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight()));
+ aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
+
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ candidate.getLineAttribute().getColor()));
+ }
+ else
+ {
+ // with stroke, we have a problem - a filled polygon would lose the
+ // stroke. Let's represent the start and/or end as triangles, the main
+ // line still as PolygonStrokePrimitive2D.
+ // Fill default line Start/End for stroke, so we need no adaptations in else paths
+ basegfx::B2DPoint aStrokeStart(aStart - (aVector * candidate.getStartLeft()));
+ basegfx::B2DPoint aStrokeEnd(aEnd + (aVector * candidate.getEndLeft()));
+ const basegfx::B2DVector aHalfLineOffset(aPerpendicular * (candidate.getLineAttribute().getWidth() * 0.5));
+
+ if(!bStartPerpendicular)
+ {
+ const double fMin(std::min(candidate.getStartLeft(), candidate.getStartRight()));
+ const double fMax(std::max(candidate.getStartLeft(), candidate.getStartRight()));
+ basegfx::B2DPolygon aPolygon;
+
+ // create a triangle with min/max values for LineStart and add
+ if(rtl::math::approxEqual(candidate.getStartLeft(), fMax))
+ {
+ aPolygon.append(aStart - aHalfLineOffset - (aVector * candidate.getStartLeft()));
+ }
+
+ aPolygon.append(aStart - aHalfLineOffset - (aVector * fMin));
+ aPolygon.append(aStart + aHalfLineOffset - (aVector * fMin));
+
+ if(rtl::math::approxEqual(candidate.getStartRight(), fMax))
+ {
+ aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
+ }
+
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ candidate.getLineAttribute().getColor()));
+
+ // Adapt StrokeStart accordingly
+ aStrokeStart = aStart - (aVector * fMin);
+ }
+
+ if(!bEndPerpendicular)
+ {
+ const double fMin(std::min(candidate.getEndLeft(), candidate.getEndRight()));
+ const double fMax(std::max(candidate.getEndLeft(), candidate.getEndRight()));
+ basegfx::B2DPolygon aPolygon;
+
+ // create a triangle with min/max values for LineEnd and add
+ if(rtl::math::approxEqual(candidate.getEndLeft(), fMax))
+ {
+ aPolygon.append(aEnd - aHalfLineOffset + (aVector * candidate.getEndLeft()));
+ }
+
+ if(rtl::math::approxEqual(candidate.getEndRight(), fMax))
+ {
+ aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight()));
+ }
+
+ aPolygon.append(aEnd + aHalfLineOffset + (aVector * fMin));
+ aPolygon.append(aEnd - aHalfLineOffset + (aVector * fMin));
+
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ candidate.getLineAttribute().getColor()));
+
+ // Adapt StrokeEnd accordingly
+ aStrokeEnd = aEnd + (aVector * fMin);
+ }
+
+ addPolygonStrokePrimitive2D(
+ rContainer,
+ aStrokeStart,
+ aStrokeEnd,
+ candidate.getLineAttribute(),
+ getStrokeAttribute());
+ }
+ }
+ }
+
+ fOffset += fWidth;
+ }
+ }
+
+ bool BorderLinePrimitive2D::isHorizontalOrVertical(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if (!getStart().equal(getEnd()))
+ {
+ const basegfx::B2DHomMatrix& rOTVT = rViewInformation.getObjectToViewTransformation();
+ const basegfx::B2DVector aVector(rOTVT * getEnd() - rOTVT * getStart());
+
+ return basegfx::fTools::equalZero(aVector.getX()) || basegfx::fTools::equalZero(aVector.getY());
+ }
+
+ return false;
+ }
+
+ BorderLinePrimitive2D::BorderLinePrimitive2D(
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ std::vector< BorderLine >&& rBorderLines,
+ const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute)
+ : maStart(rStart),
+ maEnd(rEnd),
+ maBorderLines(std::move(rBorderLines)),
+ maStrokeAttribute(rStrokeAttribute)
+ {
+ }
+
+ bool BorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ return false;
+
+ const BorderLinePrimitive2D& rCompare = static_cast<const BorderLinePrimitive2D&>(rPrimitive);
+
+ if (getStart() == rCompare.getStart()
+ && getEnd() == rCompare.getEnd()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute())
+ {
+ if (getBorderLines().size() == rCompare.getBorderLines().size())
+ {
+ for (size_t a(0); a < getBorderLines().size(); a++)
+ {
+ if (!(getBorderLines()[a] == rCompare.getBorderLines()[a]))
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 BorderLinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D;
+ }
+
+ Primitive2DReference tryMergeBorderLinePrimitive2D(
+ const BorderLinePrimitive2D* pCandidateA,
+ const BorderLinePrimitive2D* pCandidateB)
+ {
+ assert(pCandidateA);
+ assert(pCandidateB);
+
+ // start of candidate has to match end of this
+ if(!pCandidateA->getEnd().equal(pCandidateB->getStart()))
+ {
+ return Primitive2DReference();
+ }
+
+ // candidate A needs a length
+ if(pCandidateA->getStart().equal(pCandidateA->getEnd()))
+ {
+ return Primitive2DReference();
+ }
+
+ // candidate B needs a length
+ if(pCandidateB->getStart().equal(pCandidateB->getEnd()))
+ {
+ return Primitive2DReference();
+ }
+
+ // StrokeAttribute has to be equal
+ if(!(pCandidateA->getStrokeAttribute() == pCandidateB->getStrokeAttribute()))
+ {
+ return Primitive2DReference();
+ }
+
+ // direction has to be equal -> cross product == 0.0
+ const basegfx::B2DVector aVT(pCandidateA->getEnd() - pCandidateA->getStart());
+ const basegfx::B2DVector aVC(pCandidateB->getEnd() - pCandidateB->getStart());
+ if(!rtl::math::approxEqual(0.0, aVC.cross(aVT)))
+ {
+ return Primitive2DReference();
+ }
+
+ // number BorderLines has to be equal
+ const size_t count(pCandidateA->getBorderLines().size());
+ if(count != pCandidateB->getBorderLines().size())
+ {
+ return Primitive2DReference();
+ }
+
+ for(size_t a(0); a < count; a++)
+ {
+ const BorderLine& rBT(pCandidateA->getBorderLines()[a]);
+ const BorderLine& rBC(pCandidateB->getBorderLines()[a]);
+
+ // LineAttribute has to be the same
+ if(!(rBC.getLineAttribute() == rBT.getLineAttribute()))
+ {
+ return Primitive2DReference();
+ }
+
+ // isGap has to be the same
+ if(rBC.isGap() != rBT.isGap())
+ {
+ return Primitive2DReference();
+ }
+
+ if(rBT.isGap())
+ {
+ // when gap, width has to be equal
+ if(!rtl::math::approxEqual(rBT.getLineAttribute().getWidth(), rBC.getLineAttribute().getWidth()))
+ {
+ return Primitive2DReference();
+ }
+ }
+ else
+ {
+ // when not gap, the line extends have at least reach to the center ( > 0.0),
+ // else there is an extend usage. When > 0.0 they just overlap, no problem
+ if(rBT.getEndLeft() >= 0.0
+ && rBT.getEndRight() >= 0.0
+ && rBC.getStartLeft() >= 0.0
+ && rBC.getStartRight() >= 0.0)
+ {
+ // okay
+ }
+ else
+ {
+ return Primitive2DReference();
+ }
+ }
+ }
+
+ // all conditions met, create merged primitive
+ std::vector< BorderLine > aMergedBorderLines;
+
+ for(size_t a(0); a < count; a++)
+ {
+ const BorderLine& rBT(pCandidateA->getBorderLines()[a]);
+ const BorderLine& rBC(pCandidateB->getBorderLines()[a]);
+
+ if(rBT.isGap())
+ {
+ aMergedBorderLines.push_back(rBT);
+ }
+ else
+ {
+ aMergedBorderLines.push_back(
+ BorderLine(
+ rBT.getLineAttribute(),
+ rBT.getStartLeft(), rBT.getStartRight(),
+ rBC.getEndLeft(), rBC.getEndRight()));
+ }
+ }
+
+ return Primitive2DReference(
+ new BorderLinePrimitive2D(
+ pCandidateA->getStart(),
+ pCandidateB->getEnd(),
+ std::move(aMergedBorderLines),
+ pCandidateA->getStrokeAttribute()));
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/controlprimitive2d.cxx b/drawinglayer/source/primitive2d/controlprimitive2d.cxx
new file mode 100644
index 000000000..461400daf
--- /dev/null
+++ b/drawinglayer/source/primitive2d/controlprimitive2d.cxx
@@ -0,0 +1,348 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <vcl/bitmapex.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <tools/diagnose_ex.h>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <vcl/window.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ void ControlPrimitive2D::createXControl()
+ {
+ if(mxXControl.is() || !getControlModel().is())
+ return;
+
+ uno::Reference< beans::XPropertySet > xSet(getControlModel(), uno::UNO_QUERY);
+
+ if(!xSet.is())
+ return;
+
+ uno::Any aValue(xSet->getPropertyValue("DefaultControl"));
+ OUString aUnoControlTypeName;
+
+ if(!(aValue >>= aUnoControlTypeName))
+ return;
+
+ if(aUnoControlTypeName.isEmpty())
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference< awt::XControl > xXControl(
+ xContext->getServiceManager()->createInstanceWithContext(aUnoControlTypeName, xContext), uno::UNO_QUERY);
+
+ if(xXControl.is())
+ {
+ xXControl->setModel(getControlModel());
+
+ // remember XControl
+ mxXControl = xXControl;
+ }
+ }
+
+ Primitive2DReference ControlPrimitive2D::createBitmapDecomposition(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DReference xRetval;
+ const uno::Reference< awt::XControl >& rXControl(getXControl());
+
+ if(rXControl.is())
+ {
+ uno::Reference< awt::XWindow > xControlWindow(rXControl, uno::UNO_QUERY);
+
+ if(xControlWindow.is())
+ {
+ // get decomposition to get size
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // get absolute discrete size (no mirror or rotate here)
+ aScale = basegfx::absolute(aScale);
+ basegfx::B2DVector aDiscreteSize(rViewInformation.getObjectToViewTransformation() * aScale);
+
+ // limit to a maximum square size, e.g. 300x150 pixels (45000)
+ const double fDiscreteMax(SvtOptionsDrawinglayer::GetQuadraticFormControlRenderLimit());
+ const double fDiscreteQuadratic(aDiscreteSize.getX() * aDiscreteSize.getY());
+ const bool bScaleUsed(fDiscreteQuadratic > fDiscreteMax);
+ double fFactor(1.0);
+
+ if(bScaleUsed)
+ {
+ // get factor and adapt to scaled size
+ fFactor = sqrt(fDiscreteMax / fDiscreteQuadratic);
+ aDiscreteSize *= fFactor;
+ }
+
+ // go to integer
+ const sal_Int32 nSizeX(basegfx::fround(aDiscreteSize.getX()));
+ const sal_Int32 nSizeY(basegfx::fround(aDiscreteSize.getY()));
+
+ if(nSizeX > 0 && nSizeY > 0)
+ {
+ // prepare VirtualDevice
+ ScopedVclPtrInstance< VirtualDevice > aVirtualDevice(*Application::GetDefaultDevice());
+ const Size aSizePixel(nSizeX, nSizeY);
+ aVirtualDevice->SetOutputSizePixel(aSizePixel);
+
+ // set size at control
+ xControlWindow->setPosSize(0, 0, nSizeX, nSizeY, awt::PosSize::POSSIZE);
+
+ // get graphics and view
+ uno::Reference< awt::XGraphics > xGraphics(aVirtualDevice->CreateUnoGraphics());
+ uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY);
+
+ if(xGraphics.is() && xControlView.is())
+ {
+ // link graphics and view
+ xControlView->setGraphics(xGraphics);
+
+ { // #i93162# For painting the control setting a Zoom (using setZoom() at the xControlView)
+ // is needed to define the font size. Normally this is done in
+ // ViewObjectContactOfUnoControl::createPrimitive2DSequence by using positionControlForPaint().
+ // For some reason the difference between MapUnit::MapTwipS and MapUnit::Map100thMM still plays
+ // a role there so that for Draw/Impress/Calc (the MapUnit::Map100thMM users) i need to set a zoom
+ // here, too. The factor includes the needed scale, but is calculated by pure comparisons. It
+ // is somehow related to the twips/100thmm relationship.
+ bool bUserIs100thmm(false);
+ const uno::Reference< awt::XControl > xControl(xControlView, uno::UNO_QUERY);
+
+ if(xControl.is())
+ {
+ uno::Reference<awt::XWindowPeer> xWindowPeer(xControl->getPeer());
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindowPeer);
+ if (pWindow)
+ {
+ pWindow = pWindow->GetParent();
+
+ if(pWindow && MapUnit::Map100thMM == pWindow->GetMapMode().GetMapUnit())
+ {
+ bUserIs100thmm = true;
+ }
+ }
+ }
+
+ if(bUserIs100thmm)
+ {
+ // calc screen zoom for text display. fFactor is already added indirectly in aDiscreteSize
+ basegfx::B2DVector aScreenZoom(
+ basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : aDiscreteSize.getX() / aScale.getX(),
+ basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : aDiscreteSize.getY() / aScale.getY());
+ static const double fZoomScale(28.0); // do not ask for this constant factor, but it gets the zoom right
+ aScreenZoom *= fZoomScale;
+
+ // set zoom at control view for text scaling
+ xControlView->setZoom(static_cast<float>(aScreenZoom.getX()), static_cast<float>(aScreenZoom.getY()));
+ }
+ }
+
+ try
+ {
+ // try to paint it to VirtualDevice
+ xControlView->draw(0, 0);
+
+ // get bitmap
+ const BitmapEx aContent(aVirtualDevice->GetBitmapEx(Point(), aSizePixel));
+
+ // to avoid scaling, use the Bitmap pixel size as primitive size
+ const Size aBitmapSize(aContent.GetSizePixel());
+ basegfx::B2DVector aBitmapSizeLogic(
+ rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1));
+
+ if(bScaleUsed)
+ {
+ // if scaled adapt to scaled size
+ aBitmapSizeLogic /= fFactor;
+ }
+
+ // short form for scale and translate transformation
+ const basegfx::B2DHomMatrix aBitmapTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aBitmapSizeLogic.getX(), aBitmapSizeLogic.getY(), aTranslate.getX(), aTranslate.getY()));
+
+ // create primitive
+ xRetval = new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(aContent),
+ aBitmapTransform);
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("drawinglayer");
+ }
+ }
+ }
+ }
+ }
+
+ return xRetval;
+ }
+
+ Primitive2DReference ControlPrimitive2D::createPlaceholderDecomposition() const
+ {
+ // create a gray placeholder hairline polygon in object size
+ basegfx::B2DRange aObjectRange(0.0, 0.0, 1.0, 1.0);
+ aObjectRange.transform(getTransform());
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aObjectRange));
+ const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0);
+
+ // The replacement object may also get a text like 'empty group' here later
+ Primitive2DReference xRetval(new PolygonHairlinePrimitive2D(aOutline, aGrayTone));
+
+ return xRetval;
+ }
+
+ void ControlPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // try to create a bitmap decomposition. If that fails for some reason,
+ // at least create a replacement decomposition.
+ Primitive2DReference xReference(createBitmapDecomposition(rViewInformation));
+
+ if(!xReference.is())
+ {
+ xReference = createPlaceholderDecomposition();
+ }
+
+ rContainer.push_back(xReference);
+ }
+
+ ControlPrimitive2D::ControlPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const uno::Reference< awt::XControlModel >& rxControlModel)
+ : maTransform(rTransform),
+ mxControlModel(rxControlModel)
+ {
+ }
+
+ ControlPrimitive2D::ControlPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const uno::Reference< awt::XControlModel >& rxControlModel,
+ const uno::Reference< awt::XControl >& rxXControl)
+ : maTransform(rTransform),
+ mxControlModel(rxControlModel),
+ mxXControl(rxXControl)
+ {
+ }
+
+ const uno::Reference< awt::XControl >& ControlPrimitive2D::getXControl() const
+ {
+ if(!mxXControl.is())
+ {
+ const_cast< ControlPrimitive2D* >(this)->createXControl();
+ }
+
+ return mxXControl;
+ }
+
+ bool ControlPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ // use base class compare operator
+ if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ return false;
+
+ const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive);
+
+ if(getTransform() != rCompare.getTransform())
+ return false;
+
+ // check if ControlModel references both are/are not
+ if (getControlModel().is() != rCompare.getControlModel().is())
+ return false;
+
+ if(getControlModel().is())
+ {
+ // both exist, check for equality
+ if (getControlModel() != rCompare.getControlModel())
+ return false;
+ }
+
+ // check if XControl references both are/are not
+ if (getXControl().is() != rCompare.getXControl().is())
+ return false;
+
+ if(getXControl().is())
+ {
+ // both exist, check for equality
+ if (getXControl() != rCompare.getXControl())
+ return false;
+ }
+
+ return true;
+ }
+
+ basegfx::B2DRange ControlPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // simply derivate from unit range
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+ return aRetval;
+ }
+
+ void ControlPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // this primitive is view-dependent related to the scaling. If scaling has changed,
+ // destroy existing decomposition. To detect change, use size of unit size in view coordinates
+ const basegfx::B2DVector aNewScaling(rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+
+ if(!getBuffered2DDecomposition().empty())
+ {
+ if(!maLastViewScaling.equal(aNewScaling))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ControlPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewTransformation
+ const_cast< ControlPrimitive2D* >(this)->maLastViewScaling = aNewScaling;
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 ControlPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_CONTROLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/cropprimitive2d.cxx b/drawinglayer/source/primitive2d/cropprimitive2d.cxx
new file mode 100644
index 000000000..76393ad05
--- /dev/null
+++ b/drawinglayer/source/primitive2d/cropprimitive2d.cxx
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive2d/cropprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ CropPrimitive2D::CropPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const basegfx::B2DHomMatrix& rTransformation,
+ double fCropLeft,
+ double fCropTop,
+ double fCropRight,
+ double fCropBottom)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maTransformation(rTransformation),
+ mfCropLeft(fCropLeft),
+ mfCropTop(fCropTop),
+ mfCropRight(fCropRight),
+ mfCropBottom(fCropBottom)
+ {
+ }
+
+ bool CropPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const CropPrimitive2D& rCompare = static_cast< const CropPrimitive2D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation()
+ && getCropLeft() == rCompare.getCropLeft()
+ && getCropTop() == rCompare.getCropTop()
+ && getCropRight() == rCompare.getCropRight()
+ && getCropBottom() == rCompare.getCropBottom());
+ }
+
+ return false;
+ }
+
+ void CropPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getChildren().empty())
+ return;
+
+ // get original object scale in unit coordinates (no mirroring)
+ const basegfx::B2DVector aObjectScale(basegfx::absolute(getTransformation() * basegfx::B2DVector(1.0, 1.0)));
+
+ // we handle cropping, so when no width or no height, content will be empty,
+ // so only do something when we have a width and a height
+ if(aObjectScale.equalZero())
+ return;
+
+ // calculate crop distances in unit coordinates. They are already combined with CropScaleFactor, thus
+ // are relative only to object scale
+ const double fBackScaleX(basegfx::fTools::equalZero(aObjectScale.getX()) ? 1.0 : 1.0 / fabs(aObjectScale.getX()));
+ const double fBackScaleY(basegfx::fTools::equalZero(aObjectScale.getY()) ? 1.0 : 1.0 / fabs(aObjectScale.getY()));
+ const double fLeft(getCropLeft() * fBackScaleX);
+ const double fTop(getCropTop() * fBackScaleY);
+ const double fRight(getCropRight() * fBackScaleX);
+ const double fBottom(getCropBottom() * fBackScaleY);
+
+ // calc new unit range for comparisons; the original range is the unit range
+ const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+ const basegfx::B2DRange aNewRange(
+ -fLeft,
+ -fTop,
+ 1.0 + fRight,
+ 1.0 + fBottom);
+
+ // if we have no overlap the crop has removed everything, so we do only
+ // have to create content if this is not the case
+ if(!aNewRange.overlaps(aUnitRange))
+ return;
+
+ // create new transform; first take out old transform to get
+ // to unit coordinates by inverting. Inverting should be flawless
+ // since we already checked that object size is not zero in X or Y
+ basegfx::B2DHomMatrix aNewTransform(getTransformation());
+
+ aNewTransform.invert();
+
+ // apply crop enlargement in unit coordinates
+ aNewTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aNewRange.getRange(),
+ aNewRange.getMinimum()) * aNewTransform;
+
+ // apply original transformation. Since we have manipulated the crop
+ // in unit coordinates we do not need to care about mirroring or
+ // a corrected point for a possible shear or rotation, this all comes for
+ // free
+ aNewTransform = getTransformation() * aNewTransform;
+
+ // prepare TransformPrimitive2D with xPrimitive
+ const Primitive2DReference xTransformPrimitive(
+ new TransformPrimitive2D(
+ aNewTransform,
+ Primitive2DContainer(getChildren())));
+
+ if(aUnitRange.isInside(aNewRange))
+ {
+ // the new range is completely inside the old range (unit range),
+ // so no masking is needed
+ rVisitor.visit(xTransformPrimitive);
+ }
+ else
+ {
+ // mask with original object's bounds
+ basegfx::B2DPolyPolygon aMaskPolyPolygon(basegfx::utils::createUnitPolygon());
+ aMaskPolyPolygon.transform(getTransformation());
+
+ // create maskPrimitive with aMaskPolyPolygon and aMaskContentVector
+ const Primitive2DReference xMask(
+ new MaskPrimitive2D(
+ aMaskPolyPolygon,
+ Primitive2DContainer { xTransformPrimitive }));
+
+ rVisitor.visit(xMask);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 CropPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_CROPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx
new file mode 100644
index 000000000..8a86f8320
--- /dev/null
+++ b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void DiscreteBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use getViewTransformation() and getObjectTransformation() from
+ // ObjectAndViewTransformationDependentPrimitive2D to create a BitmapPrimitive2D
+ // with the correct mapping
+
+ if(getBitmapEx().IsEmpty())
+ return;
+
+ // get discrete size
+ const Size& rSizePixel = getBitmapEx().GetSizePixel();
+ const basegfx::B2DVector aDiscreteSize(rSizePixel.Width(), rSizePixel.Height());
+
+ // get inverse ViewTransformation
+ basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation());
+ aInverseViewTransformation.invert();
+
+ // get size and position in world coordinates
+ const basegfx::B2DVector aWorldSize(aInverseViewTransformation * aDiscreteSize);
+ const basegfx::B2DPoint aWorldTopLeft(getObjectTransformation() * getTopLeft());
+
+ // build object matrix in world coordinates so that the top-left
+ // position remains, but possible transformations (e.g. rotations)
+ // in the ObjectToView stack remain and get correctly applied
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, aWorldSize.getX());
+ aObjectTransform.set(1, 1, aWorldSize.getY());
+ aObjectTransform.set(0, 2, aWorldTopLeft.getX());
+ aObjectTransform.set(1, 2, aWorldTopLeft.getY());
+
+ // get inverse ObjectTransformation
+ basegfx::B2DHomMatrix aInverseObjectTransformation(getObjectTransformation());
+ aInverseObjectTransformation.invert();
+
+ // transform to object coordinate system
+ aObjectTransform = aInverseObjectTransformation * aObjectTransform;
+
+ // create BitmapPrimitive2D with now object-local coordinate data
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
+ aObjectTransform));
+ }
+
+ DiscreteBitmapPrimitive2D::DiscreteBitmapPrimitive2D(
+ const BitmapEx& rBitmapEx,
+ const basegfx::B2DPoint& rTopLeft)
+ : maBitmapEx(rBitmapEx),
+ maTopLeft(rTopLeft)
+ {
+ }
+
+ bool DiscreteBitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(ObjectAndViewTransformationDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const DiscreteBitmapPrimitive2D& rCompare = static_cast<const DiscreteBitmapPrimitive2D&>(rPrimitive);
+
+ return (getBitmapEx() == rCompare.getBitmapEx()
+ && getTopLeft() == rCompare.getTopLeft());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 DiscreteBitmapPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_DISCRETEBITMAPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx
new file mode 100644
index 000000000..290d9d2d7
--- /dev/null
+++ b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <osl/diagnose.h>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ DiscreteShadow::DiscreteShadow(const BitmapEx& rBitmapEx)
+ : maBitmapEx(rBitmapEx)
+ {
+ const Size& rBitmapSize = getBitmapEx().GetSizePixel();
+
+ if(rBitmapSize.Width() != rBitmapSize.Height() || rBitmapSize.Width() < 7)
+ {
+ OSL_ENSURE(false, "DiscreteShadowPrimitive2D: wrong bitmap format (!)");
+ maBitmapEx = BitmapEx();
+ }
+ }
+
+ const BitmapEx& DiscreteShadow::getTopLeft() const
+ {
+ if(maTopLeft.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maTopLeft = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maTopLeft.Crop(
+ ::tools::Rectangle(Point(0, 0), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maTopLeft;
+ }
+
+ const BitmapEx& DiscreteShadow::getTop() const
+ {
+ if(maTop.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maTop = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maTop.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 1, 0), Size(1, nQuarter)));
+ }
+
+ return maTop;
+ }
+
+ const BitmapEx& DiscreteShadow::getTopRight() const
+ {
+ if(maTopRight.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maTopRight = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maTopRight.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 2, 0), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maTopRight;
+ }
+
+ const BitmapEx& DiscreteShadow::getRight() const
+ {
+ if(maRight.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maRight = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maRight.Crop(
+ ::tools::Rectangle(Point((nQuarter * 3) + 3, (nQuarter * 2) + 1), Size(nQuarter, 1)));
+ }
+
+ return maRight;
+ }
+
+ const BitmapEx& DiscreteShadow::getBottomRight() const
+ {
+ if(maBottomRight.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maBottomRight = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maBottomRight.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 2, (nQuarter * 2) + 2), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maBottomRight;
+ }
+
+ const BitmapEx& DiscreteShadow::getBottom() const
+ {
+ if(maBottom.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maBottom = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maBottom.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 1, (nQuarter * 3) + 3), Size(1, nQuarter)));
+ }
+
+ return maBottom;
+ }
+
+ const BitmapEx& DiscreteShadow::getBottomLeft() const
+ {
+ if(maBottomLeft.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maBottomLeft = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maBottomLeft.Crop(
+ ::tools::Rectangle(Point(0, (nQuarter * 2) + 2), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maBottomLeft;
+ }
+
+ const BitmapEx& DiscreteShadow::getLeft() const
+ {
+ if(maLeft.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maLeft = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maLeft.Crop(
+ ::tools::Rectangle(Point(0, (nQuarter * 2) + 1), Size(nQuarter, 1)));
+ }
+
+ return maLeft;
+ }
+
+} // end of namespace
+
+
+namespace drawinglayer::primitive2d
+{
+ void DiscreteShadowPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ Primitive2DContainer xRetval;
+
+ if(getDiscreteShadow().getBitmapEx().IsEmpty())
+ return;
+
+ const sal_Int32 nQuarter((getDiscreteShadow().getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const basegfx::B2DVector aScale(getTransform() * basegfx::B2DVector(1.0, 1.0));
+ const double fSingleX(getDiscreteUnit() / aScale.getX());
+ const double fSingleY(getDiscreteUnit() / aScale.getY());
+ const double fBorderX(fSingleX * nQuarter);
+ const double fBorderY(fSingleY * nQuarter);
+ const double fBigLenX((fBorderX * 2.0) + fSingleX);
+ const double fBigLenY((fBorderY * 2.0) + fSingleY);
+
+ xRetval.resize(8);
+
+ // TopLeft
+ xRetval[0] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopLeft()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ -fBorderX,
+ -fBorderY)));
+
+ // Top
+ xRetval[1] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTop()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ 1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX,
+ fBorderY,
+ fBorderX + fSingleX,
+ -fBorderY)));
+
+ // TopRight
+ xRetval[2] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopRight()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ 1.0 - fBorderX,
+ -fBorderY)));
+
+ // Right
+ xRetval[3] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getRight()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBorderX,
+ 1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY,
+ 1.0 + fSingleX,
+ fBorderY + fSingleY)));
+
+ // BottomRight
+ xRetval[4] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomRight()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ 1.0 - (fBorderX + fSingleX) + fSingleX,
+ 1.0 - (fBorderY + fSingleY) + fSingleY)));
+
+ // Bottom
+ xRetval[5] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottom()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ 1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX,
+ fBorderY,
+ fBorderX + fSingleX,
+ 1.0 + fSingleY)));
+
+ // BottomLeft
+ xRetval[6] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomLeft()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ -fBorderX,
+ 1.0 - fBorderY)));
+
+ // Left
+ xRetval[7] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getLeft()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBorderX,
+ 1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY,
+ -fBorderX,
+ fBorderY + fSingleY)));
+
+ // put all in object transformation to get to target positions
+ rContainer.push_back(
+ new TransformPrimitive2D(
+ getTransform(),
+ std::move(xRetval)));
+ }
+
+ DiscreteShadowPrimitive2D::DiscreteShadowPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const DiscreteShadow& rDiscreteShadow)
+ : maTransform(rTransform),
+ maDiscreteShadow(rDiscreteShadow)
+ {
+ }
+
+ bool DiscreteShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const DiscreteShadowPrimitive2D& rCompare = static_cast<const DiscreteShadowPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getDiscreteShadow() == rCompare.getDiscreteShadow());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange DiscreteShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(getDiscreteShadow().getBitmapEx().IsEmpty())
+ {
+ // no graphics without valid bitmap definition
+ return basegfx::B2DRange();
+ }
+ else
+ {
+ // prepare normal objectrange
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+
+ // extract discrete shadow size and grow
+ const basegfx::B2DVector aScale(rViewInformation.getViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+ const sal_Int32 nQuarter((getDiscreteShadow().getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const double fGrowX((1.0 / aScale.getX()) * nQuarter);
+ const double fGrowY((1.0 / aScale.getY()) * nQuarter);
+ aRetval.grow(std::max(fGrowX, fGrowY));
+
+ return aRetval;
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 DiscreteShadowPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_DISCRETESHADOWPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx
new file mode 100644
index 000000000..f072df0c3
--- /dev/null
+++ b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/embedded3dprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <processor3d/shadow3dextractor.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ bool Embedded3DPrimitive2D::impGetShadow3D() const
+ {
+ // create on demand
+ if(!mbShadow3DChecked && !getChildren3D().empty())
+ {
+ // create shadow extraction processor
+ processor3d::Shadow3DExtractingProcessor aShadowProcessor(
+ getViewInformation3D(),
+ getObjectTransformation(),
+ getLightNormal(),
+ getShadowSlant(),
+ getScene3DRange());
+
+ // process local primitives
+ aShadowProcessor.process(getChildren3D());
+
+ // fetch result and set checked flag
+ const_cast< Embedded3DPrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
+ const_cast< Embedded3DPrimitive2D* >(this)->mbShadow3DChecked = true;
+ }
+
+ // return if there are shadow primitives
+ return !maShadowPrimitives.empty();
+ }
+
+ void Embedded3DPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // use info to create a yellow 2d rectangle, similar to empty 3d scenes and/or groups
+ const basegfx::B2DRange aLocal2DRange(getB2DRange(rViewInformation));
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aLocal2DRange));
+ const basegfx::BColor aYellow(1.0, 1.0, 0.0);
+ rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, aYellow));
+ }
+
+ Embedded3DPrimitive2D::Embedded3DPrimitive2D(
+ const primitive3d::Primitive3DContainer& rxChildren3D,
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ const geometry::ViewInformation3D& rViewInformation3D,
+ const basegfx::B3DVector& rLightNormal,
+ double fShadowSlant,
+ const basegfx::B3DRange& rScene3DRange)
+ : mxChildren3D(rxChildren3D),
+ maObjectTransformation(rObjectTransformation),
+ maViewInformation3D(rViewInformation3D),
+ maLightNormal(rLightNormal),
+ mfShadowSlant(fShadowSlant),
+ maScene3DRange(rScene3DRange),
+ mbShadow3DChecked(false)
+ {
+ maLightNormal.normalize();
+ }
+
+ bool Embedded3DPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const Embedded3DPrimitive2D& rCompare = static_cast< const Embedded3DPrimitive2D& >(rPrimitive);
+
+ return (getChildren3D() == rCompare.getChildren3D()
+ && getObjectTransformation() == rCompare.getObjectTransformation()
+ && getViewInformation3D() == rCompare.getViewInformation3D()
+ && getLightNormal() == rCompare.getLightNormal()
+ && getShadowSlant() == rCompare.getShadowSlant()
+ && getScene3DRange() == rCompare.getScene3DRange());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange Embedded3DPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(maB2DRange.isEmpty())
+ {
+ // use the 3d transformation stack to create a projection of the 3D range
+ basegfx::B3DRange a3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
+ a3DRange.transform(getViewInformation3D().getObjectToView());
+
+ // create 2d range from projected 3d and transform with scene's object transformation
+ basegfx::B2DRange aNewRange;
+ aNewRange.expand(basegfx::B2DPoint(a3DRange.getMinX(), a3DRange.getMinY()));
+ aNewRange.expand(basegfx::B2DPoint(a3DRange.getMaxX(), a3DRange.getMaxY()));
+ aNewRange.transform(getObjectTransformation());
+
+ // check for 3D shadows and their 2D projections. If those exist, they need to be
+ // taken into account
+ if(impGetShadow3D())
+ {
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+
+ if(!aShadow2DRange.isEmpty())
+ {
+ aNewRange.expand(aShadow2DRange);
+ }
+ }
+
+ // assign to buffered value
+ const_cast< Embedded3DPrimitive2D* >(this)->maB2DRange = aNewRange;
+ }
+
+ return maB2DRange;
+ }
+
+ // provide unique ID
+ sal_uInt32 Embedded3DPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_EMBEDDED3DPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/epsprimitive2d.cxx b/drawinglayer/source/primitive2d/epsprimitive2d.cxx
new file mode 100644
index 000000000..9e876dad5
--- /dev/null
+++ b/drawinglayer/source/primitive2d/epsprimitive2d.cxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+ void EpsPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const GDIMetaFile& rSubstituteContent = getMetaFile();
+
+ if( rSubstituteContent.GetActionSize() )
+ {
+ // the default decomposition will use the Metafile replacement visualisation.
+ // To really use the Eps data, a renderer has to know and interpret this primitive
+ // directly.
+
+ rContainer.push_back(
+ new MetafilePrimitive2D(
+ getEpsTransform(),
+ rSubstituteContent));
+ }
+ }
+
+ EpsPrimitive2D::EpsPrimitive2D(
+ const basegfx::B2DHomMatrix& rEpsTransform,
+ const GfxLink& rGfxLink,
+ const GDIMetaFile& rMetaFile)
+ : maEpsTransform(rEpsTransform),
+ maGfxLink(rGfxLink),
+ maMetaFile(rMetaFile)
+ {
+ }
+
+ bool EpsPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const EpsPrimitive2D& rCompare = static_cast<const EpsPrimitive2D&>(rPrimitive);
+
+ return (getEpsTransform() == rCompare.getEpsTransform()
+ && getGfxLink() == rCompare.getGfxLink()
+ && getMetaFile() == rCompare.getMetaFile());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange EpsPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use own implementation to quickly answer the getB2DRange question.
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getEpsTransform());
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 EpsPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_EPSPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
new file mode 100644
index 000000000..7baf93bf5
--- /dev/null
+++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void FillGradientPrimitive2D::generateMatricesAndColors(
+ std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor) const
+ {
+ rEntries.clear();
+
+ // make sure steps is not too high/low
+ const basegfx::BColor aStart(getFillGradient().getStartColor());
+ const basegfx::BColor aEnd(getFillGradient().getEndColor());
+ const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5));
+ sal_uInt32 nSteps(getFillGradient().getSteps());
+
+ if(nSteps == 0)
+ {
+ nSteps = nMaxSteps;
+ }
+
+ if(nSteps < 2)
+ {
+ nSteps = 2;
+ }
+
+ if(nSteps > nMaxSteps)
+ {
+ nSteps = nMaxSteps;
+ }
+
+ nSteps = std::max(sal_uInt32(1), nSteps);
+
+ switch(getFillGradient().getStyle())
+ {
+ case attribute::GradientStyle::Linear:
+ {
+ texture::GeoTexSvxGradientLinear aGradient(
+ getDefinitionRange(),
+ getOutputRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Axial:
+ {
+ texture::GeoTexSvxGradientAxial aGradient(
+ getDefinitionRange(),
+ getOutputRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Radial:
+ {
+ texture::GeoTexSvxGradientRadial aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Elliptical:
+ {
+ texture::GeoTexSvxGradientElliptical aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Square:
+ {
+ texture::GeoTexSvxGradientSquare aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Rect:
+ {
+ texture::GeoTexSvxGradientRect aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ }
+ }
+
+ void FillGradientPrimitive2D::createOverlappingFill(
+ Primitive2DContainer& rContainer,
+ const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
+ const basegfx::BColor& rOuterColor,
+ const basegfx::B2DPolygon& rUnitPolygon) const
+ {
+ // create solid fill with outmost color
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(getOutputRange())),
+ rOuterColor));
+
+ // create solid fill steps
+ for(const auto &a : rEntries)
+ {
+ // create part polygon
+ basegfx::B2DPolygon aNewPoly(rUnitPolygon);
+
+ aNewPoly.transform(a.maB2DHomMatrix);
+
+ // create solid fill
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aNewPoly),
+ a.maBColor));
+ }
+ }
+
+ void FillGradientPrimitive2D::createNonOverlappingFill(
+ Primitive2DContainer& rContainer,
+ const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
+ const basegfx::BColor& rOuterColor,
+ const basegfx::B2DPolygon& rUnitPolygon) const
+ {
+ // get outmost visible range from object
+ basegfx::B2DRange aOutmostRange(getOutputRange());
+ basegfx::B2DPolyPolygon aCombinedPolyPoly;
+
+ if(!rEntries.empty())
+ {
+ // extend aOutmostRange with first polygon
+ basegfx::B2DPolygon aFirstPoly(rUnitPolygon);
+
+ aFirstPoly.transform(rEntries[0].maB2DHomMatrix);
+ aCombinedPolyPoly.append(aFirstPoly);
+ aOutmostRange.expand(aFirstPoly.getB2DRange());
+ }
+
+ // add outmost range to combined polypolygon (in 1st place), create first primitive
+ aCombinedPolyPoly.insert(0, basegfx::utils::createPolygonFromRect(aOutmostRange));
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ rOuterColor));
+
+ if(rEntries.empty())
+ return;
+
+ // reuse first polygon, it's the second one
+ aCombinedPolyPoly.remove(0);
+
+ for(size_t a(0); a < rEntries.size() - 1; a++)
+ {
+ // create next inner polygon, combined with last one
+ basegfx::B2DPolygon aNextPoly(rUnitPolygon);
+
+ aNextPoly.transform(rEntries[a + 1].maB2DHomMatrix);
+ aCombinedPolyPoly.append(aNextPoly);
+
+ // create primitive with correct color
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ rEntries[a].maBColor));
+
+ // reuse inner polygon, it's the 2nd one
+ aCombinedPolyPoly.remove(0);
+ }
+
+ // add last inner polygon with last color
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ rEntries[rEntries.size() - 1].maBColor));
+ }
+
+ void FillGradientPrimitive2D::createFill(Primitive2DContainer& rContainer, bool bOverlapping) const
+ {
+ // prepare shape of the Unit Polygon
+ basegfx::B2DPolygon aUnitPolygon;
+
+ switch(getFillGradient().getStyle())
+ {
+ case attribute::GradientStyle::Radial:
+ case attribute::GradientStyle::Elliptical:
+ {
+ aUnitPolygon = basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
+ break;
+ }
+ default: // GradientStyle::Linear, attribute::GradientStyle::Axial, attribute::GradientStyle::Square, attribute::GradientStyle::Rect
+ {
+ aUnitPolygon = basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
+ break;
+ }
+ }
+
+ // get the transform matrices and colors (where colors
+ // will have one more entry that matrices)
+ std::vector< drawinglayer::texture::B2DHomMatrixAndBColor > aEntries;
+ basegfx::BColor aOuterColor;
+
+ generateMatricesAndColors(aEntries, aOuterColor);
+
+ if(bOverlapping)
+ {
+ createOverlappingFill(rContainer, aEntries, aOuterColor, aUnitPolygon);
+ }
+ else
+ {
+ createNonOverlappingFill(rContainer, aEntries, aOuterColor, aUnitPolygon);
+ }
+ }
+
+ void FillGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // default creates overlapping fill which works with AntiAliasing and without.
+ // The non-overlapping version does not create single filled polygons, but
+ // PolyPolygons where each one describes a 'ring' for the gradient such
+ // that the rings will not overlap. This is useful for the old XOR-paint
+ // 'trick' of VCL which is recorded in Metafiles; so this version may be
+ // used from the MetafilePrimitive2D in its decomposition.
+
+ if(!getFillGradient().isDefault())
+ {
+ createFill(rContainer, /*bOverlapping*/true);
+ }
+ }
+
+ FillGradientPrimitive2D::FillGradientPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rOutputRange),
+ maFillGradient(rFillGradient)
+ {
+ }
+
+ FillGradientPrimitive2D::FillGradientPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::B2DRange& rDefinitionRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rDefinitionRange),
+ maFillGradient(rFillGradient)
+ {
+ }
+
+ bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const FillGradientPrimitive2D& rCompare = static_cast<const FillGradientPrimitive2D&>(rPrimitive);
+
+ return (getOutputRange() == rCompare.getOutputRange()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillGradient() == rCompare.getFillGradient());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return the geometrically visible area
+ return getOutputRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 FillGradientPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
new file mode 100644
index 000000000..0f386eea2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <texture/texture.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <primitive2d/graphicprimitivehelper2d.hxx>
+#include <vcl/graph.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void FillGraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const attribute::FillGraphicAttribute& rAttribute = getFillGraphic();
+
+ if(rAttribute.isDefault())
+ return;
+
+ const Graphic& rGraphic = rAttribute.getGraphic();
+
+ if(GraphicType::Bitmap != rGraphic.GetType() && GraphicType::GdiMetafile != rGraphic.GetType())
+ return;
+
+ const Size aSize(rGraphic.GetPrefSize());
+
+ if(!(aSize.Width() && aSize.Height()))
+ return;
+
+ // we have a graphic (bitmap or metafile) with some size
+ if(rAttribute.getTiling())
+ {
+ // get object range and create tiling matrices
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+ texture::GeoTexSvxTiled aTiling(
+ rAttribute.getGraphicRange(),
+ rAttribute.getOffsetX(),
+ rAttribute.getOffsetY());
+
+ // get matrices and realloc retval
+ aTiling.appendTransformations(aMatrices);
+
+ // prepare content primitive
+ Primitive2DContainer xSeq;
+ create2DDecompositionOfGraphic(xSeq,
+ rGraphic,
+ basegfx::B2DHomMatrix());
+
+ for(const auto &a : aMatrices)
+ {
+ rContainer.push_back(new TransformPrimitive2D(
+ getTransformation() * a,
+ Primitive2DContainer(xSeq)));
+ }
+ }
+ else
+ {
+ // add graphic without tiling
+ const basegfx::B2DHomMatrix aObjectTransform(
+ getTransformation() * basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rAttribute.getGraphicRange().getRange(),
+ rAttribute.getGraphicRange().getMinimum()));
+
+ create2DDecompositionOfGraphic(rContainer,
+ rGraphic,
+ aObjectTransform);
+ }
+ }
+
+ FillGraphicPrimitive2D::FillGraphicPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransformation,
+ const attribute::FillGraphicAttribute& rFillGraphic)
+ : maTransformation(rTransformation),
+ maFillGraphic(rFillGraphic)
+ {
+ }
+
+ bool FillGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const FillGraphicPrimitive2D& rCompare = static_cast< const FillGraphicPrimitive2D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation()
+ && getFillGraphic() == rCompare.getFillGraphic());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange FillGraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return range of it
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+ aPolygon.transform(getTransformation());
+
+ return basegfx::utils::getRange(aPolygon);
+ }
+
+ // provide unique ID
+ sal_uInt32 FillGraphicPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx
new file mode 100644
index 000000000..09a82bdd8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
+#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void FillHatchPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getFillHatch().isDefault())
+ return;
+
+ // create hatch
+ const basegfx::BColor aHatchColor(getFillHatch().getColor());
+ const double fAngle(getFillHatch().getAngle());
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+ double fDistance(getFillHatch().getDistance());
+ const bool bAdaptDistance(0 != getFillHatch().getMinimalDiscreteDistance());
+
+ // #i120230# evtl. adapt distance
+ if(bAdaptDistance)
+ {
+ const double fDiscreteDistance(getFillHatch().getDistance() / getDiscreteUnit());
+
+ if(fDiscreteDistance < static_cast<double>(getFillHatch().getMinimalDiscreteDistance()))
+ {
+ fDistance = static_cast<double>(getFillHatch().getMinimalDiscreteDistance()) * getDiscreteUnit();
+ }
+ }
+
+ // get hatch transformations
+ switch(getFillHatch().getStyle())
+ {
+ case attribute::HatchStyle::Triple:
+ {
+ // rotated 45 degrees
+ texture::GeoTexSvxHatch aHatch(
+ getDefinitionRange(),
+ getOutputRange(),
+ fDistance,
+ fAngle - M_PI_4);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Double:
+ {
+ // rotated 90 degrees
+ texture::GeoTexSvxHatch aHatch(
+ getDefinitionRange(),
+ getOutputRange(),
+ fDistance,
+ fAngle - M_PI_2);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Single:
+ {
+ // angle as given
+ texture::GeoTexSvxHatch aHatch(
+ getDefinitionRange(),
+ getOutputRange(),
+ fDistance,
+ fAngle);
+
+ aHatch.appendTransformations(aMatrices);
+ }
+ }
+
+ // prepare return value
+ const bool bFillBackground(getFillHatch().isFillBackground());
+
+ // evtl. create filled background
+ if(bFillBackground)
+ {
+ // create primitive for background
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(getOutputRange())), getBColor()));
+ }
+
+ // create primitives
+ const basegfx::B2DPoint aStart(0.0, 0.0);
+ const basegfx::B2DPoint aEnd(1.0, 0.0);
+
+ for (const auto &a : aMatrices)
+ {
+ const basegfx::B2DHomMatrix& rMatrix = a;
+ basegfx::B2DPolygon aNewLine;
+
+ aNewLine.append(rMatrix * aStart);
+ aNewLine.append(rMatrix * aEnd);
+
+ // create hairline
+ rContainer.push_back(new PolygonHairlinePrimitive2D(aNewLine, aHatchColor));
+ }
+ }
+
+ FillHatchPrimitive2D::FillHatchPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::BColor& rBColor,
+ const attribute::FillHatchAttribute& rFillHatch)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rOutputRange),
+ maFillHatch(rFillHatch),
+ maBColor(rBColor)
+ {
+ }
+
+ FillHatchPrimitive2D::FillHatchPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rBColor,
+ const attribute::FillHatchAttribute& rFillHatch)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rDefinitionRange),
+ maFillHatch(rFillHatch),
+ maBColor(rBColor)
+ {
+ }
+
+ bool FillHatchPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const FillHatchPrimitive2D& rCompare = static_cast<const FillHatchPrimitive2D&>(rPrimitive);
+
+ return (getOutputRange() == rCompare.getOutputRange()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillHatch() == rCompare.getFillHatch()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange FillHatchPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return the geometrically visible area
+ return getOutputRange();
+ }
+
+ void FillHatchPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ bool bAdaptDistance(0 != getFillHatch().getMinimalDiscreteDistance());
+
+ if(bAdaptDistance)
+ {
+ // behave view-dependent
+ DiscreteMetricDependentPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+ else
+ {
+ // behave view-independent
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 FillHatchPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
new file mode 100644
index 000000000..6fe14c222
--- /dev/null
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/glowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, double fRadius,
+ Primitive2DContainer&& rChildren)
+ : GroupPrimitive2D(std::move(rChildren))
+ , maGlowColor(rGlowColor)
+ , mfGlowRadius(fRadius)
+{
+}
+
+bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const GlowPrimitive2D& rCompare = static_cast<const GlowPrimitive2D&>(rPrimitive);
+
+ return (getGlowRadius() == rCompare.getGlowRadius()
+ && getGlowColor() == rCompare.getGlowColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+GlowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(GroupPrimitive2D::getB2DRange(rViewInformation));
+ // We need additional space for the glow from all sides
+ aRetval.grow(getGlowRadius());
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 GlowPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_GLOWPRIMITIVE2D; }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
new file mode 100644
index 000000000..4cd7a4963
--- /dev/null
+++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <primitive2d/cropprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <primitive2d/graphicprimitivehelper2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+namespace drawinglayer::primitive2d
+{
+void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D&) const
+{
+ if (0 == getGraphicAttr().GetAlpha())
+ {
+ // content is invisible, done
+ return;
+ }
+
+ // do not apply mirroring from GraphicAttr to the Metafile by calling
+ // GetTransformedGraphic, this will try to mirror the Metafile using Scale()
+ // at the Metafile. This again calls Scale at the single MetaFile actions,
+ // but this implementation never worked. I reworked that implementations,
+ // but for security reasons i will try not to use it.
+ basegfx::B2DHomMatrix aTransform(getTransform());
+
+ if (getGraphicAttr().IsMirrored())
+ {
+ // content needs mirroring
+ const bool bHMirr(getGraphicAttr().GetMirrorFlags() & BmpMirrorFlags::Horizontal);
+ const bool bVMirr(getGraphicAttr().GetMirrorFlags() & BmpMirrorFlags::Vertical);
+
+ // mirror by applying negative scale to the unit primitive and
+ // applying the object transformation on it.
+ aTransform
+ = basegfx::utils::createScaleB2DHomMatrix(bHMirr ? -1.0 : 1.0, bVMirr ? -1.0 : 1.0);
+ aTransform.translate(bHMirr ? 1.0 : 0.0, bVMirr ? 1.0 : 0.0);
+ aTransform = getTransform() * aTransform;
+ }
+
+ // Get transformed graphic. Suppress rotation and cropping, only filtering is needed
+ // here (and may be replaced later on). Cropping is handled below as mask primitive (if set).
+ // Also need to suppress mirroring, it is part of the transformation now (see above).
+ // Also move transparency handling to embedding to a UnifiedTransparencePrimitive2D; do
+ // that by remembering original transparency and applying that later if needed
+ GraphicAttr aSuppressGraphicAttr(getGraphicAttr());
+
+ aSuppressGraphicAttr.SetCrop(0, 0, 0, 0);
+ aSuppressGraphicAttr.SetRotation(0_deg10);
+ aSuppressGraphicAttr.SetMirrorFlags(BmpMirrorFlags::NONE);
+ aSuppressGraphicAttr.SetAlpha(255);
+
+ const GraphicObject& rGraphicObject = getGraphicObject();
+ Graphic aTransformedGraphic(rGraphicObject.GetGraphic());
+ const bool isBitmap(GraphicType::Bitmap == aTransformedGraphic.GetType()
+ && !aTransformedGraphic.getVectorGraphicData());
+ const bool isAdjusted(getGraphicAttr().IsAdjusted());
+ const bool isDrawMode(GraphicDrawMode::Standard != getGraphicAttr().GetDrawMode());
+
+ if (isBitmap && (isAdjusted || isDrawMode))
+ {
+ // the pure primitive solution with the color modifiers works well, too, but when
+ // it is a bitmap graphic the old modification currently is faster; so use it here
+ // instead of creating all as in create2DColorModifierEmbeddingsAsNeeded (see below).
+ // Still, crop, rotation, mirroring and transparency is handled by primitives already
+ // (see above).
+ // This could even be done when vector graphic, but we explicitly want to have the
+ // pure primitive solution for this; this will allow vector graphics to stay vector
+ // graphics, independent from the color filtering stuff. This will enhance e.g.
+ // SVG and print quality while reducing data size at the same time.
+ // The other way around the old modifications when only used on already bitmap objects
+ // will not lose any quality.
+ aTransformedGraphic = rGraphicObject.GetTransformedGraphic(&aSuppressGraphicAttr);
+
+ // reset GraphicAttr after use to not apply double
+ aSuppressGraphicAttr = GraphicAttr();
+ }
+
+ // create sub-content; helper takes care of correct handling of
+ // bitmap, svg or metafile content
+ Primitive2DContainer aRetval;
+ create2DDecompositionOfGraphic(aRetval, aTransformedGraphic, aTransform);
+
+ if (aRetval.empty())
+ {
+ // content is invisible, done
+ return;
+ }
+
+ if (isAdjusted || isDrawMode)
+ {
+ // embed to needed ModifiedColorPrimitive2D's if necessary. Do this for
+ // adjustments and draw mode specials
+ aRetval = create2DColorModifierEmbeddingsAsNeeded(
+ std::move(aRetval), aSuppressGraphicAttr.GetDrawMode(),
+ std::clamp(aSuppressGraphicAttr.GetLuminance() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetContrast() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetChannelR() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetChannelG() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetChannelB() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetGamma(), 0.0, 10.0),
+ aSuppressGraphicAttr.IsInvert());
+
+ if (aRetval.empty())
+ {
+ // content is invisible, done
+ return;
+ }
+ }
+
+ if (getGraphicAttr().IsTransparent())
+ {
+ // check for transparency
+ const double fTransparency(
+ std::clamp((255 - getGraphicAttr().GetAlpha()) * (1.0 / 255.0), 0.0, 1.0));
+
+ if (!basegfx::fTools::equalZero(fTransparency))
+ {
+ Primitive2DReference aUnifiedTransparence(
+ new UnifiedTransparencePrimitive2D(std::move(aRetval), fTransparency));
+
+ aRetval = Primitive2DContainer{ aUnifiedTransparence };
+ }
+ }
+
+ if (getGraphicAttr().IsCropped())
+ {
+ // check for cropping
+ // calculate scalings between real image size and logic object size. This
+ // is necessary since the crop values are relative to original bitmap size
+ const basegfx::B2DVector aObjectScale(aTransform * basegfx::B2DVector(1.0, 1.0));
+ const basegfx::B2DVector aCropScaleFactor(rGraphicObject.calculateCropScaling(
+ aObjectScale.getX(), aObjectScale.getY(), getGraphicAttr().GetLeftCrop(),
+ getGraphicAttr().GetTopCrop(), getGraphicAttr().GetRightCrop(),
+ getGraphicAttr().GetBottomCrop()));
+
+ // embed content in cropPrimitive
+ Primitive2DReference xPrimitive(
+ new CropPrimitive2D(std::move(aRetval), aTransform,
+ getGraphicAttr().GetLeftCrop() * aCropScaleFactor.getX(),
+ getGraphicAttr().GetTopCrop() * aCropScaleFactor.getY(),
+ getGraphicAttr().GetRightCrop() * aCropScaleFactor.getX(),
+ getGraphicAttr().GetBottomCrop() * aCropScaleFactor.getY()));
+
+ aRetval = Primitive2DContainer{ xPrimitive };
+ }
+
+ rContainer.append(std::move(aRetval));
+}
+
+GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform,
+ const GraphicObject& rGraphicObject,
+ const GraphicAttr& rGraphicAttr)
+ : maTransform(rTransform)
+ , maGraphicObject(rGraphicObject)
+ , maGraphicAttr(rGraphicAttr)
+{
+}
+
+GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform,
+ const GraphicObject& rGraphicObject)
+ : maTransform(rTransform)
+ , maGraphicObject(rGraphicObject)
+{
+}
+
+bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const GraphicPrimitive2D& rCompare = static_cast<const GraphicPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getGraphicObject() == rCompare.getGraphicObject()
+ && getGraphicAttr() == rCompare.getGraphicAttr());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+GraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 GraphicPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
new file mode 100644
index 000000000..c79af14dd
--- /dev/null
+++ b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
@@ -0,0 +1,697 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <primitive2d/graphicprimitivehelper2d.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+// helper class for animated graphics
+
+#include <vcl/animate/Animation.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ class AnimatedGraphicPrimitive2D : public AnimatedSwitchPrimitive2D
+ {
+ private:
+ /// the geometric definition
+ basegfx::B2DHomMatrix maTransform;
+
+ /** the Graphic with all its content possibilities, here only
+ animated is allowed and gets checked by isValidData().
+ an instance of Graphic is used here since it's ref-counted
+ and thus a safe copy for now
+ */
+ const Graphic maGraphic;
+
+ /// local animation processing data, excerpt from maGraphic
+ ::Animation maAnimation;
+
+ /// the on-demand created VirtualDevices for frame creation
+ ScopedVclPtrInstance< VirtualDevice > maVirtualDevice;
+ ScopedVclPtrInstance< VirtualDevice > maVirtualDeviceMask;
+
+ // index of the next frame that would be regularly prepared
+ sal_uInt32 mnNextFrameToPrepare;
+
+ /// buffering of 1st frame (always active)
+ Primitive2DReference maBufferedFirstFrame;
+
+ /// buffering of all frames
+ std::vector<Primitive2DReference> maBufferedPrimitives;
+ bool mbBufferingAllowed;
+
+ /// set if the animation is huge so that just always the next frame
+ /// is used instead of using timing
+ bool mbHugeSize;
+
+ /// helper methods
+ bool isValidData() const
+ {
+ return (GraphicType::Bitmap == maGraphic.GetType()
+ && maGraphic.IsAnimated()
+ && maAnimation.Count());
+ }
+
+ void ensureVirtualDeviceSizeAndState()
+ {
+ if (!isValidData())
+ return;
+
+ const Size aCurrent(maVirtualDevice->GetOutputSizePixel());
+ const Size aTarget(maAnimation.GetDisplaySizePixel());
+
+ if (aCurrent != aTarget)
+ {
+ maVirtualDevice->EnableMapMode(false);
+ maVirtualDeviceMask->EnableMapMode(false);
+ maVirtualDevice->SetOutputSizePixel(aTarget);
+ maVirtualDeviceMask->SetOutputSizePixel(aTarget);
+ }
+
+ maVirtualDevice->Erase();
+ maVirtualDeviceMask->Erase();
+ const ::tools::Rectangle aRect(Point(0, 0), aTarget);
+ maVirtualDeviceMask->SetFillColor(COL_BLACK);
+ maVirtualDeviceMask->SetLineColor();
+ maVirtualDeviceMask->DrawRect(aRect);
+ }
+
+ sal_uInt32 generateStepTime(sal_uInt32 nIndex) const
+ {
+ const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(nIndex));
+ sal_uInt32 nWaitTime(rAnimationBitmap.mnWait * 10);
+
+ // Take care of special value for MultiPage TIFFs. ATM these shall just
+ // show their first page. Later we will offer some switching when object
+ // is selected.
+ if (ANIMATION_TIMEOUT_ON_CLICK == rAnimationBitmap.mnWait)
+ {
+ // ATM the huge value would block the timer, so
+ // use a long time to show first page (whole day)
+ nWaitTime = 100 * 60 * 60 * 24;
+ }
+
+ // Bad trap: There are animated gifs with no set WaitTime (!).
+ // In that case use a default value.
+ if (0 == nWaitTime)
+ {
+ nWaitTime = 100;
+ }
+
+ return nWaitTime;
+ }
+
+ void createAndSetAnimationTiming()
+ {
+ if (!isValidData())
+ return;
+
+ animation::AnimationEntryLoop aAnimationLoop(maAnimation.GetLoopCount() ? maAnimation.GetLoopCount() : 0xffff);
+ const sal_uInt32 nCount(maAnimation.Count());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ const sal_uInt32 aStepTime(generateStepTime(a));
+ const animation::AnimationEntryFixed aTime(static_cast<double>(aStepTime), static_cast<double>(a) / static_cast<double>(nCount));
+
+ aAnimationLoop.append(aTime);
+ }
+
+ animation::AnimationEntryList aAnimationEntryList;
+ aAnimationEntryList.append(aAnimationLoop);
+
+ setAnimationEntry(aAnimationEntryList);
+ }
+
+ Primitive2DReference createFromBuffer() const
+ {
+ // create BitmapEx by extracting from VirtualDevices
+ const Bitmap aMainBitmap(maVirtualDevice->GetBitmap(Point(), maVirtualDevice->GetOutputSizePixel()));
+ bool useAlphaMask = false;
+#if defined(MACOSX) || defined(IOS)
+ useAlphaMask = true;
+#else
+ // GetBitmap()-> AlphaMask is optimized with SkiaSalBitmap::InterpretAs8Bit(), 1bpp mask is not.
+ if( SkiaHelper::isVCLSkiaEnabled())
+ useAlphaMask = true;
+#endif
+ BitmapEx bitmap;
+ if( useAlphaMask )
+ {
+ const AlphaMask aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
+ bitmap = BitmapEx(aMainBitmap, aMaskBitmap);
+ }
+ else
+ {
+ const Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
+ bitmap = BitmapEx(aMainBitmap, aMaskBitmap);
+ }
+
+ return Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(bitmap),
+ getTransform()));
+ }
+
+ void checkSafeToBuffer(sal_uInt32 nIndex)
+ {
+ if (mbBufferingAllowed)
+ {
+ // all frames buffered
+ if (!maBufferedPrimitives.empty() && nIndex < maBufferedPrimitives.size())
+ {
+ if (!maBufferedPrimitives[nIndex].is())
+ {
+ maBufferedPrimitives[nIndex] = createFromBuffer();
+
+ // check if buffering is complete
+ bool bBufferingComplete(true);
+
+ for (auto const & a: maBufferedPrimitives)
+ {
+ if (!a.is())
+ {
+ bBufferingComplete = false;
+ break;
+ }
+ }
+
+ if (bBufferingComplete)
+ {
+ maVirtualDevice.disposeAndClear();
+ maVirtualDeviceMask.disposeAndClear();
+ }
+ }
+ }
+ }
+ else
+ {
+ // always buffer first frame
+ if (0 == nIndex && !maBufferedFirstFrame.is())
+ {
+ maBufferedFirstFrame = createFromBuffer();
+ }
+ }
+ }
+
+ void createFrame(sal_uInt32 nTarget)
+ {
+ // mnNextFrameToPrepare is the target frame to create next (which implies that
+ // mnNextFrameToPrepare-1 *is* currently in the VirtualDevice when
+ // 0 != mnNextFrameToPrepare. nTarget is the target frame.
+ if (!isValidData())
+ return;
+
+ if (mnNextFrameToPrepare > nTarget)
+ {
+ // we are ahead request, reset mechanism to start at frame zero
+ ensureVirtualDeviceSizeAndState();
+ mnNextFrameToPrepare = 0;
+ }
+
+ while (mnNextFrameToPrepare <= nTarget)
+ {
+ // prepare step
+ const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(mnNextFrameToPrepare));
+
+ switch (rAnimationBitmap.meDisposal)
+ {
+ case Disposal::Not:
+ {
+ maVirtualDevice->DrawBitmapEx(rAnimationBitmap.maPositionPixel, rAnimationBitmap.maBitmapEx);
+ Bitmap aAlphaMask = rAnimationBitmap.maBitmapEx.GetAlpha();
+
+ if (aAlphaMask.IsEmpty())
+ {
+ const Point aEmpty;
+ const ::tools::Rectangle aRect(aEmpty, maVirtualDeviceMask->GetOutputSizePixel());
+ const Wallpaper aWallpaper(COL_BLACK);
+ maVirtualDeviceMask->DrawWallpaper(aRect, aWallpaper);
+ }
+ else
+ {
+ BitmapEx aExpandVisibilityMask(aAlphaMask, aAlphaMask);
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask);
+ }
+
+ break;
+ }
+ case Disposal::Back:
+ {
+ // #i70772# react on no mask, for primitives, too.
+ const Bitmap & rMask(rAnimationBitmap.maBitmapEx.GetAlpha());
+ const Bitmap & rContent(rAnimationBitmap.maBitmapEx.GetBitmap());
+
+ maVirtualDeviceMask->Erase();
+ maVirtualDevice->DrawBitmap(rAnimationBitmap.maPositionPixel, rContent);
+
+ if (rMask.IsEmpty())
+ {
+ const ::tools::Rectangle aRect(rAnimationBitmap.maPositionPixel, rContent.GetSizePixel());
+ maVirtualDeviceMask->SetFillColor(COL_BLACK);
+ maVirtualDeviceMask->SetLineColor();
+ maVirtualDeviceMask->DrawRect(aRect);
+ }
+ else
+ {
+ BitmapEx aExpandVisibilityMask(rMask, rMask);
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask);
+ }
+
+ break;
+ }
+ case Disposal::Previous:
+ {
+ maVirtualDevice->DrawBitmapEx(rAnimationBitmap.maPositionPixel, rAnimationBitmap.maBitmapEx);
+ BitmapEx aExpandVisibilityMask(rAnimationBitmap.maBitmapEx.GetAlpha(), rAnimationBitmap.maBitmapEx.GetAlpha());
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask);
+ break;
+ }
+ }
+
+ // to not waste created data, check adding to buffers
+ checkSafeToBuffer(mnNextFrameToPrepare);
+
+ mnNextFrameToPrepare++;
+ }
+ }
+
+ Primitive2DReference tryTogetFromBuffer(sal_uInt32 nIndex) const
+ {
+ if (mbBufferingAllowed)
+ {
+ // all frames buffered, check if available
+ if (!maBufferedPrimitives.empty() && nIndex < maBufferedPrimitives.size())
+ {
+ if (maBufferedPrimitives[nIndex].is())
+ {
+ return maBufferedPrimitives[nIndex];
+ }
+ }
+ }
+ else
+ {
+ // always buffer first frame, it's sometimes requested out-of-order
+ if (0 == nIndex && maBufferedFirstFrame.is())
+ {
+ return maBufferedFirstFrame;
+ }
+ }
+
+ return Primitive2DReference();
+ }
+
+ public:
+ /// constructor
+ AnimatedGraphicPrimitive2D(
+ const Graphic& rGraphic,
+ const basegfx::B2DHomMatrix& rTransform);
+
+ /// data read access
+ const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
+
+ /// compare operator
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ /// override to deliver the correct expected frame dependent of timing
+ virtual void get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const override;
+ };
+
+ }
+
+ AnimatedGraphicPrimitive2D::AnimatedGraphicPrimitive2D(
+ const Graphic& rGraphic,
+ const basegfx::B2DHomMatrix& rTransform)
+ : AnimatedSwitchPrimitive2D(
+ animation::AnimationEntryList(),
+ Primitive2DContainer(),
+ false),
+ maTransform(rTransform),
+ maGraphic(rGraphic),
+ maAnimation(rGraphic.GetAnimation()),
+ maVirtualDevice(*Application::GetDefaultDevice()),
+ maVirtualDeviceMask(*Application::GetDefaultDevice()),
+ mnNextFrameToPrepare(SAL_MAX_UINT32),
+ mbBufferingAllowed(false),
+ mbHugeSize(false)
+ {
+ // initialize AnimationTiming, needed to detect which frame is requested
+ // in get2DDecomposition
+ createAndSetAnimationTiming();
+
+ // check if we allow buffering
+ if (isValidData())
+ {
+ // allow buffering up to a size of:
+ // - 64 frames
+ // - sizes of 256x256 pixels
+ // This may be offered in option values if needed
+ static const sal_uInt64 nAllowedSize(64 * 256 * 256);
+ static const sal_uInt64 nHugeSize(10000000);
+ const Size aTarget(maAnimation.GetDisplaySizePixel());
+ const sal_uInt64 nUsedSize(static_cast<sal_uInt64>(maAnimation.Count()) * aTarget.Width() * aTarget.Height());
+
+ if (nUsedSize < nAllowedSize)
+ {
+ mbBufferingAllowed = true;
+ }
+
+ if (nUsedSize > nHugeSize)
+ {
+ mbHugeSize = true;
+ }
+ }
+
+ // prepare buffer space
+ if (mbBufferingAllowed && isValidData())
+ {
+ maBufferedPrimitives.resize(maAnimation.Count());
+ }
+ }
+
+ bool AnimatedGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ // do not use 'GroupPrimitive2D::operator==' here, that would compare
+ // the children. Also do not use 'BasePrimitive2D::operator==', that would
+ // check the ID-Type. Since we are a simple derivation without own ID,
+ // use the dynamic_cast RTTI directly
+ const AnimatedGraphicPrimitive2D* pCompare = dynamic_cast<const AnimatedGraphicPrimitive2D*>(&rPrimitive);
+
+ // use operator== of Graphic - if that is equal, the basic definition is equal
+ return (nullptr != pCompare
+ && getTransform() == pCompare->getTransform()
+ && maGraphic == pCompare->maGraphic);
+ }
+
+ void AnimatedGraphicPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if (!isValidData())
+ return;
+
+ Primitive2DReference aRetval;
+ const double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+ const sal_uInt32 nLen(maAnimation.Count());
+ sal_uInt32 nIndex(basegfx::fround(fState * static_cast<double>(nLen)));
+
+ // nIndex is the requested frame - it is in range [0..nLen[
+ // create frame representation in VirtualDevices
+ if (nIndex >= nLen)
+ {
+ nIndex = nLen - 1;
+ }
+
+ // check buffering shortcuts, may already be created
+ aRetval = tryTogetFromBuffer(nIndex);
+
+ if (aRetval.is())
+ {
+ rVisitor.visit(aRetval);
+ return;
+ }
+
+ // if huge size (and not the buffered 1st frame) simply
+ // create next frame
+ if (mbHugeSize && 0 != nIndex && mnNextFrameToPrepare <= nIndex)
+ {
+ nIndex = mnNextFrameToPrepare % nLen;
+ }
+
+ // frame not (yet) buffered or no buffering allowed, create it
+ const_cast<AnimatedGraphicPrimitive2D*>(this)->createFrame(nIndex);
+
+ // try to get from buffer again, may have been added from createFrame
+ aRetval = tryTogetFromBuffer(nIndex);
+
+ if (aRetval.is())
+ {
+ rVisitor.visit(aRetval);
+ return;
+ }
+
+ // did not work (not buffered and not 1st frame), create from buffer
+ aRetval = createFromBuffer();
+
+ rVisitor.visit(aRetval);
+ }
+
+} // end of namespace
+
+namespace drawinglayer::primitive2d
+{
+ void create2DDecompositionOfGraphic(
+ Primitive2DContainer& rContainer,
+ const Graphic& rGraphic,
+ const basegfx::B2DHomMatrix& rTransform)
+ {
+ Primitive2DContainer aRetval;
+
+ switch(rGraphic.GetType())
+ {
+ case GraphicType::Bitmap :
+ {
+ if(rGraphic.IsAnimated())
+ {
+ // prepare specialized AnimatedGraphicPrimitive2D
+ aRetval.resize(1);
+ aRetval[0] = new AnimatedGraphicPrimitive2D(
+ rGraphic,
+ rTransform);
+ }
+ else if(rGraphic.getVectorGraphicData())
+ {
+ // embedded Vector Graphic Data fill, create embed transform
+ const basegfx::B2DRange& rSvgRange(rGraphic.getVectorGraphicData()->getRange());
+
+ if(basegfx::fTools::more(rSvgRange.getWidth(), 0.0) && basegfx::fTools::more(rSvgRange.getHeight(), 0.0))
+ {
+ // translate back to origin, scale to unit coordinates
+ basegfx::B2DHomMatrix aEmbedVectorGraphic(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -rSvgRange.getMinX(),
+ -rSvgRange.getMinY()));
+
+ aEmbedVectorGraphic.scale(
+ 1.0 / rSvgRange.getWidth(),
+ 1.0 / rSvgRange.getHeight());
+
+ // apply created object transformation
+ aEmbedVectorGraphic = rTransform * aEmbedVectorGraphic;
+
+ // add Vector Graphic Data primitives embedded
+ aRetval.resize(1);
+ aRetval[0] = new TransformPrimitive2D(
+ aEmbedVectorGraphic,
+ Primitive2DContainer(rGraphic.getVectorGraphicData()->getPrimitive2DSequence()));
+ }
+ }
+ else
+ {
+ aRetval.resize(1);
+ aRetval[0] = new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(rGraphic.GetBitmapEx()),
+ rTransform);
+ }
+
+ break;
+ }
+
+ case GraphicType::GdiMetafile :
+ {
+ // create MetafilePrimitive2D
+ const GDIMetaFile& rMetafile = rGraphic.GetGDIMetaFile();
+
+ aRetval.resize(1);
+ aRetval[0] = new MetafilePrimitive2D(
+ rTransform,
+ rMetafile);
+
+ // #i100357# find out if clipping is needed for this primitive. Unfortunately,
+ // there exist Metafiles who's content is bigger than the proposed PrefSize set
+ // at them. This is an error, but we need to work around this
+ const Size aMetaFilePrefSize(rMetafile.GetPrefSize());
+ const Size aMetaFileRealSize(
+ rMetafile.GetBoundRect(
+ *Application::GetDefaultDevice()).GetSize());
+
+ if(aMetaFileRealSize.getWidth() > aMetaFilePrefSize.getWidth()
+ || aMetaFileRealSize.getHeight() > aMetaFilePrefSize.getHeight())
+ {
+ // clipping needed. Embed to MaskPrimitive2D. Create children and mask polygon
+ basegfx::B2DPolygon aMaskPolygon(basegfx::utils::createUnitPolygon());
+ aMaskPolygon.transform(rTransform);
+
+ Primitive2DReference mask = new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(aMaskPolygon),
+ std::move(aRetval));
+ aRetval = Primitive2DContainer { mask };
+ }
+ break;
+ }
+
+ default:
+ {
+ // nothing to create
+ break;
+ }
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ Primitive2DContainer create2DColorModifierEmbeddingsAsNeeded(
+ Primitive2DContainer&& rChildren,
+ GraphicDrawMode aGraphicDrawMode,
+ double fLuminance,
+ double fContrast,
+ double fRed,
+ double fGreen,
+ double fBlue,
+ double fGamma,
+ bool bInvert)
+ {
+ Primitive2DContainer aRetval;
+
+ if(rChildren.empty())
+ {
+ // no child content, done
+ return aRetval;
+ }
+
+ // set child content as retval; that is what will be used as child content in all
+ // embeddings from here
+ aRetval = std::move(rChildren);
+
+ if(GraphicDrawMode::Watermark == aGraphicDrawMode)
+ {
+ // this is solved by applying fixed values additionally to luminance
+ // and contrast, do it here and reset DrawMode to GraphicDrawMode::Standard
+ // original in svtools uses:
+ // #define WATERMARK_LUM_OFFSET 50
+ // #define WATERMARK_CON_OFFSET -70
+ fLuminance = std::clamp(fLuminance + 0.5, -1.0, 1.0);
+ fContrast = std::clamp(fContrast - 0.7, -1.0, 1.0);
+ aGraphicDrawMode = GraphicDrawMode::Standard;
+ }
+
+ // DrawMode (GraphicDrawMode::Watermark already handled)
+ switch(aGraphicDrawMode)
+ {
+ case GraphicDrawMode::Greys:
+ {
+ // convert to grey
+ const Primitive2DReference aPrimitiveGrey(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_gray>()));
+
+ aRetval = Primitive2DContainer { aPrimitiveGrey };
+ break;
+ }
+ case GraphicDrawMode::Mono:
+ {
+ // convert to mono (black/white with threshold 0.5)
+ const Primitive2DReference aPrimitiveBlackAndWhite(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_black_and_white>(0.5)));
+
+ aRetval = Primitive2DContainer { aPrimitiveBlackAndWhite };
+ break;
+ }
+ default: // case GraphicDrawMode::Standard:
+ {
+ assert(
+ aGraphicDrawMode != GraphicDrawMode::Watermark
+ && "OOps, GraphicDrawMode::Watermark should already be handled (see above)");
+ // nothing to do
+ break;
+ }
+ }
+
+ // mnContPercent, mnLumPercent, mnRPercent, mnGPercent, mnBPercent
+ // handled in a single call
+ if(!basegfx::fTools::equalZero(fLuminance)
+ || !basegfx::fTools::equalZero(fContrast)
+ || !basegfx::fTools::equalZero(fRed)
+ || !basegfx::fTools::equalZero(fGreen)
+ || !basegfx::fTools::equalZero(fBlue))
+ {
+ const Primitive2DReference aPrimitiveRGBLuminannceContrast(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_RGBLuminanceContrast>(
+ fRed,
+ fGreen,
+ fBlue,
+ fLuminance,
+ fContrast)));
+
+ aRetval = Primitive2DContainer { aPrimitiveRGBLuminannceContrast };
+ }
+
+ // gamma (boolean)
+ if(!basegfx::fTools::equal(fGamma, 1.0))
+ {
+ const Primitive2DReference aPrimitiveGamma(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_gamma>(
+ fGamma)));
+
+ aRetval = Primitive2DContainer { aPrimitiveGamma };
+ }
+
+ // invert (boolean)
+ if(bInvert)
+ {
+ const Primitive2DReference aPrimitiveInvert(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_invert>()));
+
+ aRetval = Primitive2DContainer { aPrimitiveInvert };
+ }
+
+ return aRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/gridprimitive2d.cxx b/drawinglayer/source/primitive2d/gridprimitive2d.cxx
new file mode 100644
index 000000000..ddb468e9a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/gridprimitive2d.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/gridprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void GridPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0))
+ return;
+
+ // decompose grid matrix to get logic size
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // create grid matrix which transforms from scaled logic to view
+ basegfx::B2DHomMatrix aRST(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+ aRST *= rViewInformation.getObjectToViewTransformation();
+
+ // get step widths
+ double fStepX(getWidth());
+ double fStepY(getHeight());
+ const double fMinimalStep(10.0);
+
+ // guarantee a step width of 10.0
+ if(basegfx::fTools::less(fStepX, fMinimalStep))
+ {
+ fStepX = fMinimalStep;
+ }
+
+ if(basegfx::fTools::less(fStepY, fMinimalStep))
+ {
+ fStepY = fMinimalStep;
+ }
+
+ // get relative distances in view coordinates
+ double fViewStepX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fStepX, 0.0)).getLength());
+ double fViewStepY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fStepY)).getLength());
+ double fSmallStepX(1.0), fViewSmallStepX(1.0), fSmallStepY(1.0), fViewSmallStepY(1.0);
+ sal_uInt32 nSmallStepsX(0), nSmallStepsY(0);
+
+ // setup subdivisions
+ if(getSubdivisionsX())
+ {
+ fSmallStepX = fStepX / getSubdivisionsX();
+ fViewSmallStepX = fViewStepX / getSubdivisionsX();
+ }
+
+ if(getSubdivisionsY())
+ {
+ fSmallStepY = fStepY / getSubdivisionsY();
+ fViewSmallStepY = fViewStepY / getSubdivisionsY();
+ }
+
+ // correct step width
+ while(fViewStepX < getSmallestViewDistance())
+ {
+ fViewStepX *= 2.0;
+ fStepX *= 2.0;
+ }
+
+ while(fViewStepY < getSmallestViewDistance())
+ {
+ fViewStepY *= 2.0;
+ fStepY *= 2.0;
+ }
+
+ // correct small step width
+ if(getSubdivisionsX())
+ {
+ while(fViewSmallStepX < getSmallestSubdivisionViewDistance())
+ {
+ fViewSmallStepX *= 2.0;
+ fSmallStepX *= 2.0;
+ }
+
+ nSmallStepsX = static_cast<sal_uInt32>(fStepX / fSmallStepX);
+ }
+
+ if(getSubdivisionsY())
+ {
+ while(fViewSmallStepY < getSmallestSubdivisionViewDistance())
+ {
+ fViewSmallStepY *= 2.0;
+ fSmallStepY *= 2.0;
+ }
+
+ nSmallStepsY = static_cast<sal_uInt32>(fStepY / fSmallStepY);
+ }
+
+ // calculate extended viewport in which grid points may lie at all
+ basegfx::B2DRange aExtendedViewport;
+
+ if(rViewInformation.getDiscreteViewport().isEmpty())
+ {
+ // not set, use logic size to travel over all potential grid points
+ aExtendedViewport = basegfx::B2DRange(0.0, 0.0, aScale.getX(), aScale.getY());
+ }
+ else
+ {
+ // transform unit range to discrete view
+ aExtendedViewport = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
+ basegfx::B2DHomMatrix aTrans(rViewInformation.getObjectToViewTransformation() * getTransform());
+ aExtendedViewport.transform(aTrans);
+
+ // intersect with visible part
+ aExtendedViewport.intersect(rViewInformation.getDiscreteViewport());
+
+ if(!aExtendedViewport.isEmpty())
+ {
+ // convert back and apply scale
+ aTrans.invert();
+ aTrans.scale(aScale.getX(), aScale.getY());
+ aExtendedViewport.transform(aTrans);
+
+ // crop start/end in X/Y to multiples of logical step width
+ const double fHalfCrossSize((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(3.0, 0.0)).getLength());
+ const double fMinX(floor((aExtendedViewport.getMinX() - fHalfCrossSize) / fStepX) * fStepX);
+ const double fMaxX(ceil((aExtendedViewport.getMaxX() + fHalfCrossSize) / fStepX) * fStepX);
+ const double fMinY(floor((aExtendedViewport.getMinY() - fHalfCrossSize) / fStepY) * fStepY);
+ const double fMaxY(ceil((aExtendedViewport.getMaxY() + fHalfCrossSize) / fStepY) * fStepY);
+
+ // put to aExtendedViewport and crop on object logic size
+ aExtendedViewport = basegfx::B2DRange(
+ std::max(fMinX, 0.0),
+ std::max(fMinY, 0.0),
+ std::min(fMaxX, aScale.getX()),
+ std::min(fMaxY, aScale.getY()));
+ }
+ }
+
+ if(aExtendedViewport.isEmpty())
+ return;
+
+ // prepare point vectors for point and cross markers
+ std::vector< basegfx::B2DPoint > aPositionsPoint;
+ std::vector< basegfx::B2DPoint > aPositionsCross;
+
+ for(double fX(aExtendedViewport.getMinX()); fX < aExtendedViewport.getMaxX(); fX += fStepX)
+ {
+ const bool bXZero(basegfx::fTools::equalZero(fX));
+
+ for(double fY(aExtendedViewport.getMinY()); fY < aExtendedViewport.getMaxY(); fY += fStepY)
+ {
+ const bool bYZero(basegfx::fTools::equalZero(fY));
+
+ if(!bXZero && !bYZero)
+ {
+ // get discrete position and test against 3x3 area surrounding it
+ // since it's a cross
+ const double fHalfCrossSize(3.0 * 0.5);
+ const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY));
+ const basegfx::B2DRange aDiscreteRangeCross(
+ aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize,
+ aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize);
+
+ if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross))
+ {
+ const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
+ aPositionsCross.push_back(aLogicPos);
+ }
+ }
+
+ if(getSubdivisionsX() && !bYZero)
+ {
+ double fF(fX + fSmallStepX);
+
+ for(sal_uInt32 a(1); a < nSmallStepsX && fF < aExtendedViewport.getMaxX(); a++, fF += fSmallStepX)
+ {
+ const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY));
+
+ if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
+ {
+ const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
+ aPositionsPoint.push_back(aLogicPos);
+ }
+ }
+ }
+
+ if(getSubdivisionsY() && !bXZero)
+ {
+ double fF(fY + fSmallStepY);
+
+ for(sal_uInt32 a(1); a < nSmallStepsY && fF < aExtendedViewport.getMaxY(); a++, fF += fSmallStepY)
+ {
+ const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF));
+
+ if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
+ {
+ const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
+ aPositionsPoint.push_back(aLogicPos);
+ }
+ }
+ }
+ }
+ }
+
+ // prepare return value
+ const sal_uInt32 nCountPoint(aPositionsPoint.size());
+ const sal_uInt32 nCountCross(aPositionsCross.size());
+
+ // add PointArrayPrimitive2D if point markers were added
+ if(nCountPoint)
+ {
+ rContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsPoint), getBColor()));
+ }
+
+ // add MarkerArrayPrimitive2D if cross markers were added
+ if(!nCountCross)
+ return;
+
+ if(!getSubdivisionsX() && !getSubdivisionsY())
+ {
+ // no subdivisions, so fall back to points at grid positions, no need to
+ // visualize a difference between divisions and sub-divisions
+ rContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsCross), getBColor()));
+ }
+ else
+ {
+ rContainer.push_back(new MarkerArrayPrimitive2D(std::move(aPositionsCross), getCrossMarker()));
+ }
+ }
+
+ GridPrimitive2D::GridPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ double fWidth,
+ double fHeight,
+ double fSmallestViewDistance,
+ double fSmallestSubdivisionViewDistance,
+ sal_uInt32 nSubdivisionsX,
+ sal_uInt32 nSubdivisionsY,
+ const basegfx::BColor& rBColor,
+ const BitmapEx& rCrossMarker)
+ : maTransform(rTransform),
+ mfWidth(fWidth),
+ mfHeight(fHeight),
+ mfSmallestViewDistance(fSmallestViewDistance),
+ mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance),
+ mnSubdivisionsX(nSubdivisionsX),
+ mnSubdivisionsY(nSubdivisionsY),
+ maBColor(rBColor),
+ maCrossMarker(rCrossMarker)
+ {
+ }
+
+ bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const GridPrimitive2D& rCompare = static_cast<const GridPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getWidth() == rCompare.getWidth()
+ && getHeight() == rCompare.getHeight()
+ && getSmallestViewDistance() == rCompare.getSmallestViewDistance()
+ && getSmallestSubdivisionViewDistance() == rCompare.getSmallestSubdivisionViewDistance()
+ && getSubdivisionsX() == rCompare.getSubdivisionsX()
+ && getSubdivisionsY() == rCompare.getSubdivisionsY()
+ && getBColor() == rCompare.getBColor()
+ && getCrossMarker() == rCompare.getCrossMarker());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get object's range
+ basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+ aUnitRange.transform(getTransform());
+
+ // intersect with visible part
+ aUnitRange.intersect(rViewInformation.getViewport());
+
+ return aUnitRange;
+ }
+
+ void GridPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getBuffered2DDecomposition().empty())
+ {
+ if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange and ViewTransformation
+ const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
+ const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 GridPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_GRIDPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/groupprimitive2d.cxx b/drawinglayer/source/primitive2d/groupprimitive2d.cxx
new file mode 100644
index 000000000..7a39bde2c
--- /dev/null
+++ b/drawinglayer/source/primitive2d/groupprimitive2d.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ GroupPrimitive2D::GroupPrimitive2D( Primitive2DContainer&& aChildren )
+ : maChildren(std::move(aChildren))
+ {
+ }
+
+ /** The compare opertator uses the Sequence::==operator, so only checking if
+ the references are equal. All non-equal references are interpreted as
+ non-equal.
+ */
+ bool GroupPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const GroupPrimitive2D& rCompare = static_cast< const GroupPrimitive2D& >(rPrimitive);
+
+ return getChildren() == rCompare.getChildren();
+ }
+
+ return false;
+ }
+
+ /// default: just return children, so all renderers not supporting group will use its content
+ void GroupPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ getChildren(rVisitor);
+ }
+
+ sal_Int64 GroupPrimitive2D::estimateUsage()
+ {
+ size_t nRet(0);
+ for (auto& it : getChildren())
+ {
+ if (it)
+ nRet += it->estimateUsage();
+ }
+ return nRet;
+ }
+
+ // provide unique ID
+ sal_uInt32 GroupPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_GROUPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/helplineprimitive2d.cxx b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx
new file mode 100644
index 000000000..07d6a7f22
--- /dev/null
+++ b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/helplineprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void HelplinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(rViewInformation.getViewport().isEmpty() || getDirection().equalZero())
+ return;
+
+ // position to view coordinates, DashLen and DashLen in logic
+ const basegfx::B2DPoint aViewPosition(rViewInformation.getObjectToViewTransformation() * getPosition());
+
+ switch(getStyle())
+ {
+ default : // HelplineStyle2D::Point
+ {
+ const double fViewFixValue(15.0);
+ basegfx::B2DVector aNormalizedDirection(getDirection());
+ aNormalizedDirection.normalize();
+ aNormalizedDirection *= fViewFixValue;
+ const basegfx::B2DPoint aStartA(aViewPosition - aNormalizedDirection);
+ const basegfx::B2DPoint aEndA(aViewPosition + aNormalizedDirection);
+ basegfx::B2DPolygon aLineA;
+ aLineA.append(aStartA);
+ aLineA.append(aEndA);
+ aLineA.transform(rViewInformation.getInverseObjectToViewTransformation());
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLineA, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+
+ const basegfx::B2DVector aPerpendicularNormalizedDirection(basegfx::getPerpendicular(aNormalizedDirection));
+ const basegfx::B2DPoint aStartB(aViewPosition - aPerpendicularNormalizedDirection);
+ const basegfx::B2DPoint aEndB(aViewPosition + aPerpendicularNormalizedDirection);
+ basegfx::B2DPolygon aLineB;
+ aLineB.append(aStartB);
+ aLineB.append(aEndB);
+ aLineB.transform(rViewInformation.getInverseObjectToViewTransformation());
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLineB, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+
+ break;
+ }
+ case HelplineStyle2D::Line :
+ {
+ basegfx::B2DPolygon aLine;
+
+ if(basegfx::areParallel(getDirection(), basegfx::B2DVector(1.0, 0.0)))
+ {
+ // parallel to X-Axis, get cuts with Y-Axes
+ const double fCutA((rViewInformation.getDiscreteViewport().getMinX() - aViewPosition.getX()) / getDirection().getX());
+ const double fCutB((rViewInformation.getDiscreteViewport().getMaxX() - aViewPosition.getX()) / getDirection().getX());
+ const basegfx::B2DPoint aPosA(aViewPosition + (fCutA * getDirection()));
+ const basegfx::B2DPoint aPosB(aViewPosition + (fCutB * getDirection()));
+ const bool bBothLeft(aPosA.getX() < rViewInformation.getDiscreteViewport().getMinX() && aPosB.getX() < rViewInformation.getDiscreteViewport().getMinX());
+ const bool bBothRight(aPosA.getX() > rViewInformation.getDiscreteViewport().getMaxX() && aPosB.getX() < rViewInformation.getDiscreteViewport().getMaxX());
+
+ if(!bBothLeft && !bBothRight)
+ {
+ aLine.append(aPosA);
+ aLine.append(aPosB);
+ }
+ }
+ else
+ {
+ // get cuts with X-Axes
+ const double fCutA((rViewInformation.getDiscreteViewport().getMinY() - aViewPosition.getY()) / getDirection().getY());
+ const double fCutB((rViewInformation.getDiscreteViewport().getMaxY() - aViewPosition.getY()) / getDirection().getY());
+ const basegfx::B2DPoint aPosA(aViewPosition + (fCutA * getDirection()));
+ const basegfx::B2DPoint aPosB(aViewPosition + (fCutB * getDirection()));
+ const bool bBothAbove(aPosA.getY() < rViewInformation.getDiscreteViewport().getMinY() && aPosB.getY() < rViewInformation.getDiscreteViewport().getMinY());
+ const bool bBothBelow(aPosA.getY() > rViewInformation.getDiscreteViewport().getMaxY() && aPosB.getY() < rViewInformation.getDiscreteViewport().getMaxY());
+
+ if(!bBothAbove && !bBothBelow)
+ {
+ aLine.append(aPosA);
+ aLine.append(aPosB);
+ }
+ }
+
+ if(aLine.count())
+ {
+ // clip against visible area
+ const basegfx::B2DPolyPolygon aResult(basegfx::utils::clipPolygonOnRange(aLine, rViewInformation.getDiscreteViewport(), true, true));
+
+ for(sal_uInt32 a(0); a < aResult.count(); a++)
+ {
+ basegfx::B2DPolygon aPart(aResult.getB2DPolygon(a));
+ aPart.transform(rViewInformation.getInverseObjectToViewTransformation());
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aPart, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ HelplinePrimitive2D::HelplinePrimitive2D(
+ const basegfx::B2DPoint& rPosition,
+ const basegfx::B2DVector& rDirection,
+ HelplineStyle2D eStyle,
+ const basegfx::BColor& rRGBColA,
+ const basegfx::BColor& rRGBColB,
+ double fDiscreteDashLength)
+ : maPosition(rPosition),
+ maDirection(rDirection),
+ meStyle(eStyle),
+ maRGBColA(rRGBColA),
+ maRGBColB(rRGBColB),
+ mfDiscreteDashLength(fDiscreteDashLength)
+ {
+ }
+
+ bool HelplinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const HelplinePrimitive2D& rCompare = static_cast<const HelplinePrimitive2D&>(rPrimitive);
+
+ return (getPosition() == rCompare.getPosition()
+ && getDirection() == rCompare.getDirection()
+ && getStyle() == rCompare.getStyle()
+ && getRGBColA() == rCompare.getRGBColA()
+ && getRGBColB() == rCompare.getRGBColB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+ }
+
+ void HelplinePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getBuffered2DDecomposition().empty())
+ {
+ if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< HelplinePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange and ViewTransformation
+ const_cast< HelplinePrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
+ const_cast< HelplinePrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 HelplinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx b/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx
new file mode 100644
index 000000000..c1a5442b2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ HiddenGeometryPrimitive2D::HiddenGeometryPrimitive2D(
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ basegfx::B2DRange HiddenGeometryPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ return getChildren().getB2DRange(rViewInformation);
+ }
+
+ void HiddenGeometryPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& /*rVisitor*/, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 HiddenGeometryPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/invertprimitive2d.cxx b/drawinglayer/source/primitive2d/invertprimitive2d.cxx
new file mode 100644
index 000000000..e2d01381e
--- /dev/null
+++ b/drawinglayer/source/primitive2d/invertprimitive2d.cxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ InvertPrimitive2D::InvertPrimitive2D(
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 InvertPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_INVERTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx
new file mode 100644
index 000000000..cdaa15e53
--- /dev/null
+++ b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void MarkerArrayPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ const std::vector< basegfx::B2DPoint >& rPositions = getPositions();
+ const sal_uInt32 nMarkerCount(rPositions.size());
+
+ if(!nMarkerCount || getMarker().IsEmpty())
+ return;
+
+ // get pixel size
+ Size aBitmapSize(getMarker().GetSizePixel());
+
+ if(!(aBitmapSize.Width() && aBitmapSize.Height()))
+ return;
+
+ // get logic half pixel size
+ basegfx::B2DVector aLogicHalfSize(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(aBitmapSize.getWidth() - 1.0, aBitmapSize.getHeight() - 1.0));
+
+ // use half size for expand
+ aLogicHalfSize *= 0.5;
+
+ for(const auto& rPosition : rPositions)
+ {
+ const basegfx::B2DRange aRange(rPosition - aLogicHalfSize, rPosition + aLogicHalfSize);
+ basegfx::B2DHomMatrix aTransform;
+
+ aTransform.set(0, 0, aRange.getWidth());
+ aTransform.set(1, 1, aRange.getHeight());
+ aTransform.set(0, 2, aRange.getMinX());
+ aTransform.set(1, 2, aRange.getMinY());
+
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getMarker()),
+ aTransform));
+ }
+ }
+
+ MarkerArrayPrimitive2D::MarkerArrayPrimitive2D(
+ std::vector< basegfx::B2DPoint >&& rPositions,
+ const BitmapEx& rMarker)
+ : maPositions(std::move(rPositions)),
+ maMarker(rMarker)
+ {
+ }
+
+ bool MarkerArrayPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const MarkerArrayPrimitive2D& rCompare = static_cast<const MarkerArrayPrimitive2D&>(rPrimitive);
+
+ return (getPositions() == rCompare.getPositions()
+ && getMarker() == rCompare.getMarker());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MarkerArrayPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval;
+
+ if(!getPositions().empty())
+ {
+ // get the basic range from the position vector
+ for (auto const& pos : getPositions())
+ {
+ aRetval.expand(pos);
+ }
+
+ if(!getMarker().IsEmpty())
+ {
+ // get pixel size
+ const Size aBitmapSize(getMarker().GetSizePixel());
+
+ if(aBitmapSize.Width() && aBitmapSize.Height())
+ {
+ // get logic half size
+ basegfx::B2DVector aLogicHalfSize(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(aBitmapSize.getWidth(), aBitmapSize.getHeight()));
+
+ // use half size for expand
+ aLogicHalfSize *= 0.5;
+
+ // apply aLogicHalfSize
+ aRetval.expand(aRetval.getMinimum() - aLogicHalfSize);
+ aRetval.expand(aRetval.getMaximum() + aLogicHalfSize);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 MarkerArrayPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/maskprimitive2d.cxx b/drawinglayer/source/primitive2d/maskprimitive2d.cxx
new file mode 100644
index 000000000..842085168
--- /dev/null
+++ b/drawinglayer/source/primitive2d/maskprimitive2d.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ MaskPrimitive2D::MaskPrimitive2D(
+ const basegfx::B2DPolyPolygon& rMask,
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maMask(rMask)
+ {
+ }
+
+ bool MaskPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const MaskPrimitive2D& rCompare = static_cast< const MaskPrimitive2D& >(rPrimitive);
+
+ return (getMask() == rCompare.getMask());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MaskPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ return getMask().getB2DRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 MaskPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MASKPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/mediaprimitive2d.cxx b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx
new file mode 100644
index 000000000..e097d2d98
--- /dev/null
+++ b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/mediaprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void MediaPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer xRetval;
+ xRetval.resize(1);
+
+ // create background object
+ basegfx::B2DPolygon aBackgroundPolygon(basegfx::utils::createUnitPolygon());
+ aBackgroundPolygon.transform(getTransform());
+ const Primitive2DReference xRefBackground(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aBackgroundPolygon),
+ getBackgroundColor()));
+ xRetval[0] = xRefBackground;
+
+ if(GraphicType::Bitmap == maSnapshot.GetType() || GraphicType::GdiMetafile == maSnapshot.GetType())
+ {
+ const GraphicObject aGraphicObject(maSnapshot);
+ const GraphicAttr aGraphicAttr;
+ xRetval.resize(2);
+ xRetval[1] = new GraphicPrimitive2D(getTransform(), aGraphicObject, aGraphicAttr);
+ }
+
+ if(getDiscreteBorder())
+ {
+ const basegfx::B2DVector aDiscreteInLogic(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(static_cast<double>(getDiscreteBorder()), static_cast<double>(getDiscreteBorder())));
+ const double fDiscreteSize(aDiscreteInLogic.getX() + aDiscreteInLogic.getY());
+
+ basegfx::B2DRange aSourceRange(0.0, 0.0, 1.0, 1.0);
+ aSourceRange.transform(getTransform());
+
+ basegfx::B2DRange aDestRange(aSourceRange);
+ aDestRange.grow(-0.5 * fDiscreteSize);
+
+ if(basegfx::fTools::equalZero(aDestRange.getWidth()) || basegfx::fTools::equalZero(aDestRange.getHeight()))
+ {
+ // shrunk primitive has no content (zero size in X or Y), nothing to display. Still create
+ // invisible content for HitTest and BoundRect
+ const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(std::move(xRetval)));
+
+ xRetval = Primitive2DContainer { xHiddenLines, };
+ }
+ else
+ {
+ // create transformation matrix from original range to shrunk range
+ basegfx::B2DHomMatrix aTransform;
+ aTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY());
+ aTransform.scale(aDestRange.getWidth() / aSourceRange.getWidth(), aDestRange.getHeight() / aSourceRange.getHeight());
+ aTransform.translate(aDestRange.getMinX(), aDestRange.getMinY());
+
+ // add transform primitive
+ Primitive2DReference aScaled(new TransformPrimitive2D(aTransform, std::move(xRetval)));
+ xRetval = Primitive2DContainer { aScaled };
+ }
+ }
+
+ rContainer.append(std::move(xRetval));
+ }
+
+ MediaPrimitive2D::MediaPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const OUString& rURL,
+ const basegfx::BColor& rBackgroundColor,
+ sal_uInt32 nDiscreteBorder,
+ const Graphic &rSnapshot)
+ : maTransform(rTransform),
+ maURL(rURL),
+ maBackgroundColor(rBackgroundColor),
+ mnDiscreteBorder(nDiscreteBorder),
+ maSnapshot(rSnapshot)
+ {
+ }
+
+ bool MediaPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const MediaPrimitive2D& rCompare = static_cast<const MediaPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && maURL == rCompare.maURL
+ && getBackgroundColor() == rCompare.getBackgroundColor()
+ && getDiscreteBorder() == rCompare.getDiscreteBorder()
+ && maSnapshot.IsNone() == rCompare.maSnapshot.IsNone());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MediaPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+
+ if(getDiscreteBorder())
+ {
+ const basegfx::B2DVector aDiscreteInLogic(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(static_cast<double>(getDiscreteBorder()), static_cast<double>(getDiscreteBorder())));
+ const double fDiscreteSize(aDiscreteInLogic.getX() + aDiscreteInLogic.getY());
+
+ aRetval.grow(-0.5 * fDiscreteSize);
+ }
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 MediaPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MEDIAPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
new file mode 100644
index 000000000..608be0bbc
--- /dev/null
+++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <wmfemfhelper.hxx>
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <vcl/canvastools.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ void MetafilePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // Interpret the Metafile and get the content. There should be only one target, as in the start condition,
+ // but iterating will be the right thing to do when some push/pop is not closed
+ Primitive2DContainer xRetval(wmfemfhelper::interpretMetafile(getMetaFile(), rViewInformation));
+
+ if(!xRetval.empty())
+ {
+ // get target size
+ const ::tools::Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
+ const basegfx::B2DRange aMtfRange(vcl::unotools::b2DRectangleFromRectangle(aMtfTarget));
+
+ // tdf#113197 get content range and check if we have an overlap with
+ // defined target range (aMtfRange)
+ if (!aMtfRange.isEmpty())
+ {
+ const basegfx::B2DRange aContentRange(xRetval.getB2DRange(rViewInformation));
+
+ // also test equal since isInside gives also true for equal
+ if (!aMtfRange.equal(aContentRange) && !aMtfRange.isInside(aContentRange))
+ {
+ // contentRange is partly larger than aMtfRange (stuff sticks
+ // outside), clipping is needed
+ const drawinglayer::primitive2d::Primitive2DReference xMask(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(
+ aMtfRange)),
+ std::move(xRetval)));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask };
+ }
+ }
+
+ // create transformation
+ basegfx::B2DHomMatrix aAdaptedTransform;
+
+ aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top());
+ aAdaptedTransform.scale(
+ aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0,
+ aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0);
+ aAdaptedTransform = getTransform() * aAdaptedTransform;
+
+ // embed to target transformation
+ const Primitive2DReference aEmbeddedTransform(
+ new TransformPrimitive2D(
+ aAdaptedTransform,
+ std::move(xRetval)));
+
+ xRetval = Primitive2DContainer { aEmbeddedTransform };
+ }
+
+ rContainer.append(std::move(xRetval));
+ }
+
+ MetafilePrimitive2D::MetafilePrimitive2D(
+ const basegfx::B2DHomMatrix& rMetaFileTransform,
+ const GDIMetaFile& rMetaFile)
+ : maMetaFileTransform(rMetaFileTransform),
+ maMetaFile(rMetaFile)
+ {
+ }
+
+ bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const MetafilePrimitive2D& rCompare = static_cast<const MetafilePrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getMetaFile() == rCompare.getMetaFile());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use own implementation to quickly answer the getB2DRange question. The
+ // MetafilePrimitive2D assumes that all geometry is inside of the shape. If
+ // this is not the case (i have already seen some wrong Metafiles) it should
+ // be embedded to a MaskPrimitive2D
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+
+ return aRetval;
+ }
+
+ // from MetafileAccessor
+ void MetafilePrimitive2D::accessMetafile(GDIMetaFile& rTargetMetafile) const
+ {
+ rTargetMetafile = maMetaFile;
+ }
+
+ // provide unique ID
+ sal_uInt32 MetafilePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_METAFILEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx
new file mode 100644
index 000000000..2d0f572da
--- /dev/null
+++ b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ ModifiedColorPrimitive2D::ModifiedColorPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const basegfx::BColorModifierSharedPtr& rColorModifier)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maColorModifier(rColorModifier)
+ {
+ }
+
+ bool ModifiedColorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const ModifiedColorPrimitive2D& rCompare = static_cast<const ModifiedColorPrimitive2D&>(rPrimitive);
+
+ if(getColorModifier().get() == rCompare.getColorModifier().get())
+ {
+ return true;
+ }
+
+ if(!getColorModifier() || !rCompare.getColorModifier())
+ {
+ return false;
+ }
+
+ return *getColorModifier()== *rCompare.getColorModifier();
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 ModifiedColorPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx
new file mode 100644
index 000000000..577f3171a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ ObjectInfoPrimitive2D::ObjectInfoPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDesc)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maName(rName),
+ maTitle(rTitle),
+ maDesc(rDesc)
+ {
+ }
+
+ bool ObjectInfoPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const ObjectInfoPrimitive2D& rCompare = static_cast<const ObjectInfoPrimitive2D&>(rPrimitive);
+
+ return (getName() == rCompare.getName()
+ && getTitle() == rCompare.getTitle()
+ && getDesc() == rCompare.getDesc());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 ObjectInfoPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx
new file mode 100644
index 000000000..8ffd7735a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/pagehierarchyprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ PageHierarchyPrimitive2D::PageHierarchyPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 PageHierarchyPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx
new file mode 100644
index 000000000..7aa1a4405
--- /dev/null
+++ b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void PagePreviewPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aContent(getPageContent());
+
+ if(!(!aContent.empty()
+ && basegfx::fTools::more(getContentWidth(), 0.0)
+ && basegfx::fTools::more(getContentHeight(), 0.0)))
+ return;
+
+ // the decomposed matrix will be needed
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if(!(basegfx::fTools::more(aScale.getX(), 0.0) && basegfx::fTools::more(aScale.getY(), 0.0)))
+ return;
+
+ // check if content overlaps with target size and needs to be embedded with a
+ // clipping primitive
+ const basegfx::B2DRange aRealContentRange(aContent.getB2DRange(rViewInformation));
+ const basegfx::B2DRange aAllowedContentRange(0.0, 0.0, getContentWidth(), getContentHeight());
+
+ if(!aAllowedContentRange.isInside(aRealContentRange))
+ {
+ const Primitive2DReference xReferenceA(
+ new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(aAllowedContentRange)), std::move(aContent)));
+ aContent = Primitive2DContainer { xReferenceA };
+ }
+
+ // create a mapping from content to object.
+ basegfx::B2DHomMatrix aPageTrans;
+
+ // #i101075# when keeping the aspect ratio is wanted, it is necessary to calculate
+ // an equidistant scaling in X and Y and a corresponding translation to
+ // center the output. Calculate needed scale factors
+ const double fScaleX(aScale.getX() / getContentWidth());
+ const double fScaleY(aScale.getY() / getContentHeight());
+
+ // to keep the aspect, use the smaller scale and adapt missing size by translation
+ if(fScaleX < fScaleY)
+ {
+ // height needs to be adapted
+ const double fNeededHeight(aScale.getY() / fScaleX);
+ const double fSpaceToAdd(fNeededHeight - getContentHeight());
+
+ aPageTrans.translate(0.0, fSpaceToAdd * 0.5);
+ aPageTrans.scale(fScaleX, aScale.getY() / fNeededHeight);
+ }
+ else
+ {
+ // width needs to be adapted
+ const double fNeededWidth(aScale.getX() / fScaleY);
+ const double fSpaceToAdd(fNeededWidth - getContentWidth());
+
+ aPageTrans.translate(fSpaceToAdd * 0.5, 0.0);
+ aPageTrans.scale(aScale.getX() / fNeededWidth, fScaleY);
+ }
+
+ // add the missing object transformation aspects
+ const basegfx::B2DHomMatrix aCombined(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+ aPageTrans = aCombined * aPageTrans;
+
+ // embed in necessary transformation to map from SdrPage to SdrPageObject
+ rContainer.push_back(new TransformPrimitive2D(aPageTrans, std::move(aContent)));
+ }
+
+ PagePreviewPrimitive2D::PagePreviewPrimitive2D(
+ const css::uno::Reference< css::drawing::XDrawPage >& rxDrawPage,
+ const basegfx::B2DHomMatrix& rTransform,
+ double fContentWidth,
+ double fContentHeight,
+ Primitive2DContainer&& rPageContent)
+ : mxDrawPage(rxDrawPage),
+ maPageContent(std::move(rPageContent)),
+ maTransform(rTransform),
+ mfContentWidth(fContentWidth),
+ mfContentHeight(fContentHeight)
+ {
+ }
+
+ bool PagePreviewPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PagePreviewPrimitive2D& rCompare = static_cast< const PagePreviewPrimitive2D& >(rPrimitive);
+
+ return (getXDrawPage() == rCompare.getXDrawPage()
+ && getPageContent() == rCompare.getPageContent()
+ && getTransform() == rCompare.getTransform()
+ && getContentWidth() == rCompare.getContentWidth()
+ && getContentHeight() == rCompare.getContentHeight());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange PagePreviewPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation`*/) const
+ {
+ // nothing is allowed to stick out of a PagePreviewPrimitive, thus we
+ // can quickly deliver our range here
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 PagePreviewPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
new file mode 100644
index 000000000..b667545b5
--- /dev/null
+++ b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
@@ -0,0 +1,370 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <drawinglayer/converters.hxx>
+
+using namespace com::sun::star;
+
+#define MAXIMUM_SQUARE_LENGTH (186.0)
+#define MINIMUM_SQUARE_LENGTH (16.0)
+#define MINIMUM_TILES_LENGTH (3)
+
+namespace drawinglayer::primitive2d
+{
+ void PatternFillPrimitive2D::calculateNeededDiscreteBufferSize(
+ sal_uInt32& rWidth,
+ sal_uInt32& rHeight,
+ const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // reset parameters
+ rWidth = rHeight = 0;
+
+ // check if resolution is in the range which may be buffered
+ const basegfx::B2DPolyPolygon& rMaskPolygon = getMask();
+ const basegfx::B2DRange aMaskRange(rMaskPolygon.getB2DRange());
+
+ // get discrete rounded up square size of a single tile
+ const basegfx::B2DHomMatrix aMaskRangeTransformation(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aMaskRange.getRange(),
+ aMaskRange.getMinimum()));
+ const basegfx::B2DHomMatrix aTransform(
+ rViewInformation.getObjectToViewTransformation() * aMaskRangeTransformation);
+ const basegfx::B2DPoint aTopLeft(aTransform * getReferenceRange().getMinimum());
+ const basegfx::B2DPoint aX(aTransform * basegfx::B2DPoint(getReferenceRange().getMaxX(), getReferenceRange().getMinY()));
+ const basegfx::B2DPoint aY(aTransform * basegfx::B2DPoint(getReferenceRange().getMinX(), getReferenceRange().getMaxY()));
+ const double fW(basegfx::B2DVector(aX - aTopLeft).getLength());
+ const double fH(basegfx::B2DVector(aY - aTopLeft).getLength());
+ const double fSquare(fW * fH);
+
+ if(fSquare <= 0.0)
+ return;
+
+ // check if less than a maximum square pixels is used
+ static const sal_uInt32 fMaximumSquare(MAXIMUM_SQUARE_LENGTH * MAXIMUM_SQUARE_LENGTH);
+
+ if(fSquare >= fMaximumSquare)
+ return;
+
+ // calculate needed number of tiles and check if used more than a minimum count
+ const texture::GeoTexSvxTiled aTiling(getReferenceRange());
+ const sal_uInt32 nTiles(aTiling.getNumberOfTiles());
+ static const sal_uInt32 nMinimumTiles(MINIMUM_TILES_LENGTH * MINIMUM_TILES_LENGTH);
+
+ if(nTiles < nMinimumTiles)
+ return;
+
+ rWidth = basegfx::fround(ceil(fW));
+ rHeight = basegfx::fround(ceil(fH));
+ static const sal_uInt32 fMinimumSquare(MINIMUM_SQUARE_LENGTH * MINIMUM_SQUARE_LENGTH);
+
+ if(fSquare < fMinimumSquare)
+ {
+ const double fRel(fW/fH);
+ rWidth = basegfx::fround(sqrt(fMinimumSquare * fRel));
+ rHeight = basegfx::fround(sqrt(fMinimumSquare / fRel));
+ }
+ }
+
+ void PatternFillPrimitive2D::getTileSize(
+ sal_uInt32& rWidth,
+ sal_uInt32& rHeight,
+ const geometry::ViewInformation2D& rViewInformation) const
+ {
+ const basegfx::B2DRange aMaskRange(getMask().getB2DRange());
+
+ // get discrete rounded up square size of a single tile
+ const basegfx::B2DHomMatrix aMaskRangeTransformation(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aMaskRange.getRange(),
+ aMaskRange.getMinimum()));
+ const basegfx::B2DHomMatrix aTransform(
+ rViewInformation.getObjectToViewTransformation() * aMaskRangeTransformation);
+ const basegfx::B2DPoint aTopLeft(aTransform * getReferenceRange().getMinimum());
+ const basegfx::B2DPoint aX(aTransform * basegfx::B2DPoint(getReferenceRange().getMaxX(), getReferenceRange().getMinY()));
+ const basegfx::B2DPoint aY(aTransform * basegfx::B2DPoint(getReferenceRange().getMinX(), getReferenceRange().getMaxY()));
+ const double fW(basegfx::B2DVector(aX - aTopLeft).getLength());
+ const double fH(basegfx::B2DVector(aY - aTopLeft).getLength());
+
+ rWidth = basegfx::fround(ceil(fW));
+ rHeight = basegfx::fround(ceil(fH));
+ }
+
+ Primitive2DContainer PatternFillPrimitive2D::createContent(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aContent;
+
+ // see if buffering is wanted. If so, create buffered content in given resolution
+ if(0 != mnDiscreteWidth && 0 != mnDiscreteHeight)
+ {
+ const geometry::ViewInformation2D aViewInformation2D;
+ primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(
+ basegfx::utils::createScaleB2DHomMatrix(mnDiscreteWidth, mnDiscreteHeight),
+ Primitive2DContainer(getChildren())));
+ primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+
+ const BitmapEx aBitmapEx(
+ convertToBitmapEx(
+ std::move(xEmbedSeq),
+ aViewInformation2D,
+ mnDiscreteWidth,
+ mnDiscreteHeight,
+ mnDiscreteWidth * mnDiscreteHeight));
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ const Size& rBmpPix = aBitmapEx.GetSizePixel();
+
+ if(rBmpPix.Width() > 0 && rBmpPix.Height() > 0)
+ {
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new primitive2d::BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(aBitmapEx),
+ basegfx::B2DHomMatrix()));
+ aContent = primitive2d::Primitive2DContainer { xEmbedRefBitmap };
+ }
+ }
+ }
+
+ if(aContent.empty())
+ {
+ // buffering was not tried or did fail - reset remembered buffered size
+ // in any case
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = pThat->mnDiscreteHeight = 0;
+
+ // use children as default context
+ aContent = getChildren();
+
+ // check if content needs to be clipped
+ const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+ const basegfx::B2DRange aContentRange(aContent.getB2DRange(rViewInformation));
+
+ if(!aUnitRange.isInside(aContentRange))
+ {
+ const Primitive2DReference xRef(
+ new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aUnitRange)),
+ std::move(aContent)));
+
+ aContent = Primitive2DContainer { xRef };
+ }
+ }
+
+ return aContent;
+ }
+
+ // create buffered content in given resolution
+ BitmapEx PatternFillPrimitive2D::createTileImage(sal_uInt32 nWidth, sal_uInt32 nHeight) const
+ {
+ const geometry::ViewInformation2D aViewInformation2D;
+ Primitive2DContainer aContent(createContent(aViewInformation2D));
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(
+ basegfx::utils::createScaleB2DHomMatrix(nWidth, nHeight),
+ std::move(aContent)));
+ primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+
+ return convertToBitmapEx(
+ std::move(xEmbedSeq),
+ aViewInformation2D,
+ nWidth,
+ nHeight,
+ nWidth * nHeight);
+ }
+
+ void PatternFillPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+
+ if(getChildren().empty())
+ return;
+
+ if(!(!getReferenceRange().isEmpty() && getReferenceRange().getWidth() > 0.0 && getReferenceRange().getHeight() > 0.0))
+ return;
+
+ const basegfx::B2DRange aMaskRange(getMask().getB2DRange());
+
+ if(!(!aMaskRange.isEmpty() && aMaskRange.getWidth() > 0.0 && aMaskRange.getHeight() > 0.0))
+ return;
+
+ // create tiling matrices
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+ texture::GeoTexSvxTiled aTiling(getReferenceRange());
+
+ aTiling.appendTransformations(aMatrices);
+
+ // create content
+ Primitive2DContainer aContent(createContent(rViewInformation));
+
+ // resize result
+ aRetval.resize(aMatrices.size());
+
+ // create one primitive for each matrix
+ for(size_t a(0); a < aMatrices.size(); a++)
+ {
+ aRetval[a] = new TransformPrimitive2D(
+ aMatrices[a],
+ Primitive2DContainer(aContent));
+ }
+
+ // transform result which is in unit coordinates to mask's object coordinates
+ {
+ const basegfx::B2DHomMatrix aMaskTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aMaskRange.getRange(),
+ aMaskRange.getMinimum()));
+
+ Primitive2DReference xRef(
+ new TransformPrimitive2D(
+ aMaskTransform,
+ std::move(aRetval)));
+
+ aRetval = Primitive2DContainer { xRef };
+ }
+
+ // embed result in mask
+ {
+ rContainer.push_back(
+ new MaskPrimitive2D(
+ getMask(),
+ std::move(aRetval)));
+ }
+ }
+
+ PatternFillPrimitive2D::PatternFillPrimitive2D(
+ const basegfx::B2DPolyPolygon& rMask,
+ Primitive2DContainer&& rChildren,
+ const basegfx::B2DRange& rReferenceRange)
+ : maMask(rMask),
+ maChildren(std::move(rChildren)),
+ maReferenceRange(rReferenceRange),
+ mnDiscreteWidth(0),
+ mnDiscreteHeight(0)
+ {
+ }
+
+ bool PatternFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PatternFillPrimitive2D& rCompare = static_cast< const PatternFillPrimitive2D& >(rPrimitive);
+
+ return (getMask() == rCompare.getMask()
+ && getChildren() == rCompare.getChildren()
+ && getReferenceRange() == rCompare.getReferenceRange());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange PatternFillPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /* rViewInformation */ ) const
+ {
+ return getMask().getB2DRange();
+ }
+
+ void PatternFillPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // The existing buffered decomposition uses a buffer in the remembered
+ // size or none if sizes are zero. Get new needed sizes which depend on
+ // the given ViewInformation
+ bool bResetBuffering = false;
+ sal_uInt32 nW(0);
+ sal_uInt32 nH(0);
+ calculateNeededDiscreteBufferSize(nW, nH, rViewInformation);
+ const bool bBufferingCurrentlyUsed(0 != mnDiscreteWidth && 0 != mnDiscreteHeight);
+ const bool bBufferingNextUsed(0 != nW && 0 != nH);
+
+ if(bBufferingNextUsed)
+ {
+ // buffering is now possible
+ if(bBufferingCurrentlyUsed)
+ {
+ if(nW > mnDiscreteWidth || nH > mnDiscreteHeight)
+ {
+ // Higher resolution is needed than used in the existing buffered
+ // decomposition - create new one
+ bResetBuffering = true;
+ }
+ else if(double(nW * nH) / double(mnDiscreteWidth * mnDiscreteHeight) <= 0.5)
+ {
+ // Size has shrunk for 50% or more - it's worth to refresh the buffering
+ // to spare some resources
+ bResetBuffering = true;
+ }
+ }
+ else
+ {
+ // currently no buffering used - reset evtl. unbuffered
+ // decomposition to start buffering
+ bResetBuffering = true;
+ }
+ }
+ else
+ {
+ // buffering is no longer possible
+ if(bBufferingCurrentlyUsed)
+ {
+ // reset decomposition to allow creation of unbuffered one
+ bResetBuffering = true;
+ }
+ }
+
+ if(bResetBuffering)
+ {
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = nW;
+ pThat->mnDiscreteHeight = nH;
+ pThat->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ // call parent
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ sal_Int64 PatternFillPrimitive2D::estimateUsage()
+ {
+ size_t nRet(0);
+ for (auto& it : getChildren())
+ if (it)
+ nRet += it->estimateUsage();
+ return nRet;
+ }
+
+ // provide unique ID
+ sal_uInt32 PatternFillPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx b/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx
new file mode 100644
index 000000000..6299e1850
--- /dev/null
+++ b/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ PointArrayPrimitive2D::PointArrayPrimitive2D(
+ std::vector< basegfx::B2DPoint >&& rPositions,
+ const basegfx::BColor& rRGBColor)
+ : maPositions(std::move(rPositions)),
+ maRGBColor(rRGBColor)
+ {
+ }
+
+ bool PointArrayPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PointArrayPrimitive2D& rCompare = static_cast<const PointArrayPrimitive2D&>(rPrimitive);
+
+ return (getPositions() == rCompare.getPositions()
+ && getRGBColor() == rCompare.getRGBColor());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange PointArrayPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(maB2DRange.isEmpty())
+ {
+ basegfx::B2DRange aNewRange;
+
+ // get the basic range from the position vector
+ for (auto const& pos : getPositions())
+ {
+ aNewRange.expand(pos);
+ }
+
+ // assign to buffered value
+ const_cast< PointArrayPrimitive2D* >(this)->maB2DRange = aNewRange;
+ }
+
+ return maB2DRange;
+ }
+
+ // provide unique ID
+ sal_uInt32 PointArrayPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
new file mode 100644
index 000000000..1cbde11ae
--- /dev/null
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -0,0 +1,611 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <com/sun/star/drawing/LineCap.hpp>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const basegfx::BColor& rBColor)
+ : maPolygon(rPolygon)
+ , maBColor(rBColor)
+{
+}
+
+bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonHairlinePrimitive2D& rCompare
+ = static_cast<const PolygonHairlinePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolygon() == rCompare.getB2DPolygon() && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size
+ basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
+
+ if (!aRetval.isEmpty())
+ {
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
+
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
+ {
+ aRetval.grow(fDiscreteHalfLineWidth);
+ }
+ }
+
+ // return range
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolygonHairlinePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D;
+}
+
+void PolygonMarkerPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+{
+ // calculate logic DashLength
+ const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(getDiscreteDashLength(), 0.0));
+ const double fLogicDashLength(aDashVector.getX());
+
+ if (fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB()))
+ {
+ // apply dashing; get line and gap snippets
+ std::vector<double> aDash;
+ basegfx::B2DPolyPolygon aDashedPolyPolyA;
+ basegfx::B2DPolyPolygon aDashedPolyPolyB;
+
+ aDash.push_back(fLogicDashLength);
+ aDash.push_back(fLogicDashLength);
+ basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA,
+ &aDashedPolyPolyB, 2.0 * fLogicDashLength);
+
+ rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyA, getRGBColorA()));
+ rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyB, getRGBColorB()));
+ }
+ else
+ {
+ rContainer.push_back(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
+ }
+}
+
+PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB,
+ double fDiscreteDashLength)
+ : maPolygon(rPolygon)
+ , maRGBColorA(rRGBColorA)
+ , maRGBColorB(rRGBColorB)
+ , mfDiscreteDashLength(fDiscreteDashLength)
+{
+}
+
+bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonMarkerPrimitive2D& rCompare
+ = static_cast<const PolygonMarkerPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolygon() == rCompare.getB2DPolygon()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size
+ basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
+
+ if (!aRetval.isEmpty())
+ {
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
+
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
+ {
+ aRetval.grow(fDiscreteHalfLineWidth);
+ }
+ }
+
+ // return range
+ return aRetval;
+}
+
+void PolygonMarkerPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ bool bNeedNewDecomposition(false);
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ if (rViewInformation.getInverseObjectToViewTransformation()
+ != maLastInverseObjectToViewTransformation)
+ {
+ bNeedNewDecomposition = true;
+ }
+ }
+
+ if (bNeedNewDecomposition)
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast<PolygonMarkerPrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // remember last used InverseObjectToViewTransformation
+ PolygonMarkerPrimitive2D* pThat = const_cast<PolygonMarkerPrimitive2D*>(this);
+ pThat->maLastInverseObjectToViewTransformation
+ = rViewInformation.getInverseObjectToViewTransformation();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+// provide unique ID
+sal_uInt32 PolygonMarkerPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D;
+}
+
+} // end of namespace
+
+namespace drawinglayer::primitive2d
+{
+void PolygonStrokePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getB2DPolygon().count())
+ return;
+
+ // #i102241# try to simplify before usage
+ const basegfx::B2DPolygon aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon()));
+ basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+
+ if (getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
+ {
+ // no line dashing, just copy
+ aHairLinePolyPolygon.append(aB2DPolygon);
+ }
+ else
+ {
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(aB2DPolygon, getStrokeAttribute().getDotDashArray(),
+ &aHairLinePolyPolygon, nullptr,
+ getStrokeAttribute().getFullDotDashLen());
+ }
+
+ const sal_uInt32 nCount(aHairLinePolyPolygon.count());
+
+ if (!getLineAttribute().isDefault() && getLineAttribute().getWidth())
+ {
+ // create fat line data
+ const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
+ const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
+ const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
+ basegfx::B2DPolyPolygon aAreaPolyPolygon;
+ const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ // New version of createAreaGeometry; now creates bezier polygons
+ aAreaPolyPolygon.append(basegfx::utils::createAreaGeometry(
+ aHairLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, aLineJoin, aLineCap,
+ basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/,
+ 0.4 /* default fMaxPartOfEdge*/, fMiterMinimumAngle));
+ }
+
+ // create primitive
+ for (sal_uInt32 b(0); b < aAreaPolyPolygon.count(); b++)
+ {
+ // put into single polyPolygon primitives to make clear that this is NOT meant
+ // to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a
+ // melting process may be used here one day.
+ const basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b));
+ const basegfx::BColor aColor(getLineAttribute().getColor());
+ rContainer.push_back(new PolyPolygonColorPrimitive2D(aNewPolyPolygon, aColor));
+ }
+ }
+ else
+ {
+ rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aHairLinePolyPolygon,
+ getLineAttribute().getColor()));
+ }
+}
+
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ : maPolygon(rPolygon)
+ , maLineAttribute(rLineAttribute)
+ , maStrokeAttribute(rStrokeAttribute)
+{
+ // MM01: keep these - these are no curve-decompposers but just checks
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+}
+
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute)
+ : maPolygon(rPolygon)
+ , maLineAttribute(rLineAttribute)
+{
+ // MM01: keep these - these are no curve-decompposers but just checks
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+}
+
+bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonStrokePrimitive2D& rCompare
+ = static_cast<const PolygonStrokePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolygon() == rCompare.getB2DPolygon()
+ && getLineAttribute() == rCompare.getLineAttribute()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval;
+
+ if (getLineAttribute().getWidth())
+ {
+ bool bUseDecomposition(false);
+
+ if (basegfx::B2DLineJoin::Miter == getLineAttribute().getLineJoin())
+ {
+ // if line is mitered, use parent call since mitered line
+ // geometry may use more space than the geometry grown by half line width
+ bUseDecomposition = true;
+ }
+
+ if (!bUseDecomposition && css::drawing::LineCap_SQUARE == getLineAttribute().getLineCap())
+ {
+ // when drawing::LineCap_SQUARE is used the below method to grow the polygon
+ // range by half line width will not work, so use decomposition. Interestingly,
+ // the grow method below works perfectly for LineCap_ROUND since the grow is in
+ // all directions and the rounded cap needs the same grow in all directions independent
+ // from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
+ bUseDecomposition = true;
+ }
+
+ if (bUseDecomposition)
+ {
+ // get correct range by using the decomposition fallback, reasons see above cases
+
+ // ofz#947 to optimize calculating the range, turn any slow dashes into a solid line
+ // when just calculating bounds
+ attribute::StrokeAttribute aOrigStrokeAttribute = maStrokeAttribute;
+ const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute
+ = attribute::StrokeAttribute();
+ aRetval = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute = aOrigStrokeAttribute;
+ }
+ else
+ {
+ // for all other B2DLINEJOIN_* get the range from the base geometry
+ // and expand by half the line width
+ aRetval = getB2DPolygon().getB2DRange();
+ aRetval.grow(getLineAttribute().getWidth() * 0.5);
+ }
+ }
+ else
+ {
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size
+ aRetval = getB2DPolygon().getB2DRange();
+
+ if (!aRetval.isEmpty())
+ {
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
+
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
+ {
+ aRetval.grow(fDiscreteHalfLineWidth);
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolygonStrokePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D;
+}
+
+void PolygonWavePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getB2DPolygon().count())
+ return;
+
+ const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
+ const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
+
+ if (bHasWidth && bHasHeight)
+ {
+ // create waveline curve
+ const basegfx::B2DPolygon aWaveline(
+ basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(aWaveline, getLineAttribute(), getStrokeAttribute()));
+ }
+ else
+ {
+ // flat waveline, decompose to simple line primitive
+ rContainer.push_back(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(),
+ getStrokeAttribute()));
+ }
+}
+
+PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute,
+ double fWaveWidth, double fWaveHeight)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
+ , mfWaveWidth(fWaveWidth)
+ , mfWaveHeight(fWaveHeight)
+{
+ if (mfWaveWidth < 0.0)
+ {
+ mfWaveWidth = 0.0;
+ }
+
+ if (mfWaveHeight < 0.0)
+ {
+ mfWaveHeight = 0.0;
+ }
+}
+
+PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ double fWaveWidth, double fWaveHeight)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
+ , mfWaveWidth(fWaveWidth)
+ , mfWaveHeight(fWaveHeight)
+{
+ if (mfWaveWidth < 0.0)
+ {
+ mfWaveWidth = 0.0;
+ }
+
+ if (mfWaveHeight < 0.0)
+ {
+ mfWaveHeight = 0.0;
+ }
+}
+
+bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (PolygonStrokePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonWavePrimitive2D& rCompare
+ = static_cast<const PolygonWavePrimitive2D&>(rPrimitive);
+
+ return (getWaveWidth() == rCompare.getWaveWidth()
+ && getWaveHeight() == rCompare.getWaveHeight());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // get range of parent
+ basegfx::B2DRange aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation));
+
+ // if WaveHeight, grow by it
+ if (basegfx::fTools::more(getWaveHeight(), 0.0))
+ {
+ aRetval.grow(getWaveHeight());
+ }
+
+ // if line width, grow by it
+ if (basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
+ {
+ aRetval.grow(getLineAttribute().getWidth() * 0.5);
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolygonWavePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D;
+}
+
+void PolygonStrokeArrowPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // copy local polygon, it may be changed
+ basegfx::B2DPolygon aLocalPolygon(getB2DPolygon());
+ aLocalPolygon.removeDoublePoints();
+ basegfx::B2DPolyPolygon aArrowA;
+ basegfx::B2DPolyPolygon aArrowB;
+
+ if (!aLocalPolygon.isClosed() && aLocalPolygon.count() > 1)
+ {
+ // apply arrows
+ const double fPolyLength(basegfx::utils::getLength(aLocalPolygon));
+ double fStart(0.0);
+ double fEnd(0.0);
+ double fStartOverlap(0.0);
+ double fEndOverlap(0.0);
+
+ if (!getStart().isDefault() && getStart().isActive())
+ {
+ // create start arrow primitive and consume
+ aArrowA = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
+ fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart);
+
+ // create some overlapping, compromise between straight and peaked markers
+ // for marker width 0.3cm and marker line width 0.02cm
+ fStartOverlap = getStart().getWidth() / 15.0;
+ }
+
+ if (!getEnd().isDefault() && getEnd().isActive())
+ {
+ // create end arrow primitive and consume
+ aArrowB = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
+ fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd);
+
+ // create some overlapping
+ fEndOverlap = getEnd().getWidth() / 15.0;
+ }
+
+ if (0.0 != fStart || 0.0 != fEnd)
+ {
+ // build new poly, consume something from old poly
+ aLocalPolygon
+ = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart - fStartOverlap,
+ fPolyLength - fEnd + fEndOverlap, fPolyLength);
+ }
+ }
+
+ // add shaft
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(aLocalPolygon, getLineAttribute(), getStrokeAttribute()));
+
+ if (aArrowA.count())
+ {
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(aArrowA, getLineAttribute().getColor()));
+ }
+
+ if (aArrowB.count())
+ {
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(aArrowB, getLineAttribute().getColor()));
+ }
+}
+
+PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute,
+ const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
+ , maStart(rStart)
+ , maEnd(rEnd)
+{
+}
+
+PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
+ , maStart(rStart)
+ , maEnd(rEnd)
+{
+}
+
+bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (PolygonStrokePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonStrokeArrowPrimitive2D& rCompare
+ = static_cast<const PolygonStrokeArrowPrimitive2D&>(rPrimitive);
+
+ return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getStart().isActive() || getEnd().isActive())
+ {
+ // use decomposition when line start/end is used
+ return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ else
+ {
+ // get range from parent
+ return PolygonStrokePrimitive2D::getB2DRange(rViewInformation);
+ }
+}
+
+// provide unique ID
+sal_uInt32 PolygonStrokeArrowPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/primitivetools2d.cxx b/drawinglayer/source/primitive2d/primitivetools2d.cxx
new file mode 100644
index 000000000..7c6d426e9
--- /dev/null
+++ b/drawinglayer/source/primitive2d/primitivetools2d.cxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/primitivetools2d.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void DiscreteMetricDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the current DiscreteUnit, look at X and Y and use the maximum
+ const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+ const double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY())));
+
+ if(!getBuffered2DDecomposition().empty() && !basegfx::fTools::equal(fDiscreteUnit, getDiscreteUnit()))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< DiscreteMetricDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid DiscreteUnit
+ const_cast< DiscreteMetricDependentPrimitive2D* >(this)->mfDiscreteUnit = fDiscreteUnit;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+
+
+
+ void ViewportDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the current Viewport
+ const basegfx::B2DRange& rViewport = rViewInformation.getViewport();
+
+ if(!getBuffered2DDecomposition().empty() && !rViewport.equal(getViewport()))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ViewportDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid DiscreteUnit
+ const_cast< ViewportDependentPrimitive2D* >(this)->maViewport = rViewport;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ void ViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the current ViewTransformation
+ const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation();
+
+ if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid ViewTransformation
+ const_cast< ViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ void ObjectAndViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the current ViewTransformation
+ const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation();
+
+ if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ // get the current ObjectTransformation
+ const basegfx::B2DHomMatrix& rObjectTransformation = rViewInformation.getObjectTransformation();
+
+ if(!getBuffered2DDecomposition().empty() && rObjectTransformation != getObjectTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid ViewTransformation, and ObjectTransformation
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation;
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->maObjectTransformation = rObjectTransformation;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
new file mode 100644
index 000000000..0bec2258d
--- /dev/null
+++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
@@ -0,0 +1,689 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <processor3d/zbufferprocessor3d.hxx>
+#include <processor3d/shadow3dextractor.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <processor3d/geometry2dextractor.hxx>
+#include <basegfx/raster/bzpixelraster.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <comphelper/threadpool.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+ BitmapEx BPixelRasterToBitmapEx(const basegfx::BZPixelRaster& rRaster, sal_uInt16 mnAntiAlialize)
+ {
+ BitmapEx aRetval;
+ const sal_uInt32 nWidth(mnAntiAlialize ? rRaster.getWidth()/mnAntiAlialize : rRaster.getWidth());
+ const sal_uInt32 nHeight(mnAntiAlialize ? rRaster.getHeight()/mnAntiAlialize : rRaster.getHeight());
+
+ if(nWidth && nHeight)
+ {
+ const Size aDestSize(nWidth, nHeight);
+ vcl::bitmap::RawBitmap aContent(aDestSize, 32);
+
+ if(mnAntiAlialize)
+ {
+ const sal_uInt16 nDivisor(mnAntiAlialize * mnAntiAlialize);
+
+ for(sal_uInt32 y(0); y < nHeight; y++)
+ {
+ for(sal_uInt32 x(0); x < nWidth; x++)
+ {
+ sal_uInt16 nRed(0);
+ sal_uInt16 nGreen(0);
+ sal_uInt16 nBlue(0);
+ sal_uInt16 nAlpha(0);
+ sal_uInt32 nIndex(rRaster.getIndexFromXY(x * mnAntiAlialize, y * mnAntiAlialize));
+
+ for(sal_uInt32 c(0); c < mnAntiAlialize; c++)
+ {
+ for(sal_uInt32 d(0); d < mnAntiAlialize; d++)
+ {
+ const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
+ nRed += rPixel.getRed();
+ nGreen += rPixel.getGreen();
+ nBlue += rPixel.getBlue();
+ nAlpha += rPixel.getAlpha();
+ }
+
+ nIndex += rRaster.getWidth() - mnAntiAlialize;
+ }
+
+ nAlpha /= nDivisor;
+
+ if(nAlpha)
+ {
+ aContent.SetPixel(y, x, Color(ColorAlpha,
+ static_cast<sal_uInt8>(nAlpha),
+ static_cast<sal_uInt8>(nRed / nDivisor),
+ static_cast<sal_uInt8>(nGreen / nDivisor),
+ static_cast<sal_uInt8>(nBlue / nDivisor) ));
+ }
+ else
+ aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
+ }
+ }
+ }
+ else
+ {
+ sal_uInt32 nIndex(0);
+
+ for(sal_uInt32 y(0); y < nHeight; y++)
+ {
+ for(sal_uInt32 x(0); x < nWidth; x++)
+ {
+ const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
+
+ if(rPixel.getAlpha())
+ {
+ aContent.SetPixel(y, x, Color(ColorAlpha, rPixel.getAlpha(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue()));
+ }
+ else
+ aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
+ }
+ }
+ }
+
+ aRetval = vcl::bitmap::CreateFromData(std::move(aContent));
+
+ // #i101811# set PrefMapMode and PrefSize at newly created Bitmap
+ aRetval.SetPrefMapMode(MapMode(MapUnit::MapPixel));
+ aRetval.SetPrefSize(Size(nWidth, nHeight));
+ }
+
+ return aRetval;
+ }
+} // end of anonymous namespace
+
+namespace drawinglayer::primitive2d
+{
+ bool ScenePrimitive2D::impGetShadow3D() const
+ {
+ // create on demand
+ if(!mbShadow3DChecked && !getChildren3D().empty())
+ {
+ basegfx::B3DVector aLightNormal;
+ const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
+ const basegfx::B3DRange aScene3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
+
+ if(!maSdrLightingAttribute.getLightVector().empty())
+ {
+ // get light normal from first light and normalize
+ aLightNormal = maSdrLightingAttribute.getLightVector()[0].getDirection();
+ aLightNormal.normalize();
+ }
+
+ // create shadow extraction processor
+ processor3d::Shadow3DExtractingProcessor aShadowProcessor(
+ getViewInformation3D(),
+ getObjectTransformation(),
+ aLightNormal,
+ fShadowSlant,
+ aScene3DRange);
+
+ // process local primitives
+ aShadowProcessor.process(getChildren3D());
+
+ // fetch result and set checked flag
+ const_cast< ScenePrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
+ const_cast< ScenePrimitive2D* >(this)->mbShadow3DChecked = true;
+ }
+
+ // return if there are shadow primitives
+ return !maShadowPrimitives.empty();
+ }
+
+ void ScenePrimitive2D::calculateDiscreteSizes(
+ const geometry::ViewInformation2D& rViewInformation,
+ basegfx::B2DRange& rDiscreteRange,
+ basegfx::B2DRange& rVisibleDiscreteRange,
+ basegfx::B2DRange& rUnitVisibleRange) const
+ {
+ // use unit range and transform to discrete coordinates
+ rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
+ rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // clip it against discrete Viewport (if set)
+ rVisibleDiscreteRange = rDiscreteRange;
+
+ if(!rViewInformation.getViewport().isEmpty())
+ {
+ rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
+ }
+
+ if(rVisibleDiscreteRange.isEmpty())
+ {
+ rUnitVisibleRange = rVisibleDiscreteRange;
+ }
+ else
+ {
+ // create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
+ // the relative position of rVisibleDiscreteRange inside rDiscreteRange
+ const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange.getWidth()) ? 1.0 : 1.0 / rDiscreteRange.getWidth());
+ const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange.getHeight()) ? 1.0 : 1.0 / rDiscreteRange.getHeight());
+
+ const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange.getMinX(), rDiscreteRange.getMinX())
+ ? 0.0
+ : (rVisibleDiscreteRange.getMinX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
+ const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange.getMinY(), rDiscreteRange.getMinY())
+ ? 0.0
+ : (rVisibleDiscreteRange.getMinY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
+
+ const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxX(), rDiscreteRange.getMaxX())
+ ? 1.0
+ : (rVisibleDiscreteRange.getMaxX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
+ const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxY(), rDiscreteRange.getMaxY())
+ ? 1.0
+ : (rVisibleDiscreteRange.getMaxY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
+
+ rUnitVisibleRange = basegfx::B2DRange(fMinX, fMinY, fMaxX, fMaxY);
+ }
+ }
+
+ void ScenePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
+ // there are some or not. Do this at start, the shadow might still be visible even when the scene is not
+ if(impGetShadow3D())
+ {
+ // test visibility
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+ const basegfx::B2DRange aViewRange(
+ rViewInformation.getViewport());
+
+ if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
+ {
+ // add extracted 2d shadows (before 3d scene creations itself)
+ rContainer.append(maShadowPrimitives);
+ }
+ }
+
+ // get the involved ranges (see helper method calculateDiscreteSizes for details)
+ basegfx::B2DRange aDiscreteRange;
+ basegfx::B2DRange aVisibleDiscreteRange;
+ basegfx::B2DRange aUnitVisibleRange;
+
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+
+ if(aVisibleDiscreteRange.isEmpty())
+ return;
+
+ // test if discrete view size (pixel) maybe too big and limit it
+ double fViewSizeX(aVisibleDiscreteRange.getWidth());
+ double fViewSizeY(aVisibleDiscreteRange.getHeight());
+ const double fViewVisibleArea(fViewSizeX * fViewSizeY);
+ const double fMaximumVisibleArea(SvtOptionsDrawinglayer::GetQuadratic3DRenderLimit());
+ double fReduceFactor(1.0);
+
+ if(fViewVisibleArea > fMaximumVisibleArea)
+ {
+ fReduceFactor = sqrt(fMaximumVisibleArea / fViewVisibleArea);
+ fViewSizeX *= fReduceFactor;
+ fViewSizeY *= fReduceFactor;
+ }
+
+ if(rViewInformation.getReducedDisplayQuality())
+ {
+ // when reducing the visualisation is allowed (e.g. an OverlayObject
+ // only needed for dragging), reduce resolution extra
+ // to speed up dragging interactions
+ const double fArea(fViewSizeX * fViewSizeY);
+ if (fArea != 0.0)
+ {
+ double fReducedVisualisationFactor(1.0 / (sqrt(fArea) * (1.0 / 170.0)));
+
+ if(fReducedVisualisationFactor > 1.0)
+ {
+ fReducedVisualisationFactor = 1.0;
+ }
+ else if(fReducedVisualisationFactor < 0.20)
+ {
+ fReducedVisualisationFactor = 0.20;
+ }
+
+ if(fReducedVisualisationFactor != 1.0)
+ {
+ fReduceFactor *= fReducedVisualisationFactor;
+ }
+ }
+ }
+
+ // determine the oversample value
+ static const sal_uInt16 nDefaultOversampleValue(3);
+ const sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue : 0);
+
+ geometry::ViewInformation3D aViewInformation3D(getViewInformation3D());
+ {
+ // calculate a transformation from DiscreteRange to evtl. rotated/sheared content.
+ // Start with full transformation from object to discrete units
+ basegfx::B2DHomMatrix aObjToUnit(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // bring to unit coordinates by applying inverse DiscreteRange
+ aObjToUnit.translate(-aDiscreteRange.getMinX(), -aDiscreteRange.getMinY());
+ if (aDiscreteRange.getWidth() != 0.0 && aDiscreteRange.getHeight() != 0.0)
+ {
+ aObjToUnit.scale(1.0 / aDiscreteRange.getWidth(), 1.0 / aDiscreteRange.getHeight());
+ }
+
+ // calculate transformed user coordinate system
+ const basegfx::B2DPoint aStandardNull(0.0, 0.0);
+ const basegfx::B2DPoint aUnitRangeTopLeft(aObjToUnit * aStandardNull);
+ const basegfx::B2DVector aStandardXAxis(1.0, 0.0);
+ const basegfx::B2DVector aUnitRangeXAxis(aObjToUnit * aStandardXAxis);
+ const basegfx::B2DVector aStandardYAxis(0.0, 1.0);
+ const basegfx::B2DVector aUnitRangeYAxis(aObjToUnit * aStandardYAxis);
+
+ if(!aUnitRangeTopLeft.equal(aStandardNull) || !aUnitRangeXAxis.equal(aStandardXAxis) || !aUnitRangeYAxis.equal(aStandardYAxis))
+ {
+ // build transformation from unit range to user coordinate system; the unit range
+ // X and Y axes are the column vectors, the null point is the offset
+ basegfx::B2DHomMatrix aUnitRangeToUser;
+
+ aUnitRangeToUser.set3x2(
+ aUnitRangeXAxis.getX(), aUnitRangeYAxis.getX(), aUnitRangeTopLeft.getX(),
+ aUnitRangeXAxis.getY(), aUnitRangeYAxis.getY(), aUnitRangeTopLeft.getY());
+
+ // decompose to allow to apply this to the 3D transformation
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aUnitRangeToUser.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // apply before DeviceToView and after Projection, 3D is in range [-1.0 .. 1.0] in X,Y and Z
+ // and not yet flipped in Y
+ basegfx::B3DHomMatrix aExtendedProjection(aViewInformation3D.getProjection());
+
+ // bring to unit coordinates, flip Y, leave Z unchanged
+ aExtendedProjection.scale(0.5, -0.5, 1.0);
+ aExtendedProjection.translate(0.5, 0.5, 0.0);
+
+ // apply extra; Y is flipped now, go with positive shear and rotate values
+ aExtendedProjection.scale(aScale.getX(), aScale.getY(), 1.0);
+ aExtendedProjection.shearXZ(fShearX, 0.0);
+ aExtendedProjection.rotate(0.0, 0.0, fRotate);
+ aExtendedProjection.translate(aTranslate.getX(), aTranslate.getY(), 0.0);
+
+ // back to state after projection
+ aExtendedProjection.translate(-0.5, -0.5, 0.0);
+ aExtendedProjection.scale(2.0, -2.0, 1.0);
+
+ aViewInformation3D = geometry::ViewInformation3D(
+ aViewInformation3D.getObjectTransformation(),
+ aViewInformation3D.getOrientation(),
+ aExtendedProjection,
+ aViewInformation3D.getDeviceToView(),
+ aViewInformation3D.getViewTime(),
+ aViewInformation3D.getExtendedInformationSequence());
+ }
+ }
+
+ // calculate logic render size in world coordinates for usage in renderer
+ const basegfx::B2DHomMatrix& aInverseOToV(rViewInformation.getInverseObjectToViewTransformation());
+ const double fLogicX((aInverseOToV * basegfx::B2DVector(aDiscreteRange.getWidth() * fReduceFactor, 0.0)).getLength());
+ const double fLogicY((aInverseOToV * basegfx::B2DVector(0.0, aDiscreteRange.getHeight() * fReduceFactor)).getLength());
+
+ // generate ViewSizes
+ const double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength());
+ const double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength());
+
+ // generate RasterWidth and RasterHeight for visible part
+ const sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1);
+ const sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1);
+
+ if(!(nRasterWidth && nRasterHeight))
+ return;
+
+ // create view unit buffer
+ basegfx::BZPixelRaster aBZPixelRaster(
+ nOversampleValue ? nRasterWidth * nOversampleValue : nRasterWidth,
+ nOversampleValue ? nRasterHeight * nOversampleValue : nRasterHeight);
+
+ // check for parallel execution possibilities
+ static bool bMultithreadAllowed = false; // loplugin:constvars:ignore
+ sal_Int32 nThreadCount(0);
+ comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
+
+ if(bMultithreadAllowed)
+ {
+ nThreadCount = rThreadPool.getWorkerCount();
+
+ if(nThreadCount > 1)
+ {
+ // at least use 10px per processor, so limit number of processors to
+ // target pixel size divided by 10 (which might be zero what is okay)
+ nThreadCount = std::min(nThreadCount, nRasterHeight / 10);
+ }
+ }
+
+ if(nThreadCount > 1)
+ {
+ class Executor : public comphelper::ThreadTask
+ {
+ private:
+ std::unique_ptr<processor3d::ZBufferProcessor3D> mpZBufferProcessor3D;
+ const primitive3d::Primitive3DContainer& mrChildren3D;
+
+ public:
+ explicit Executor(
+ std::shared_ptr<comphelper::ThreadTaskTag> const & rTag,
+ std::unique_ptr<processor3d::ZBufferProcessor3D> pZBufferProcessor3D,
+ const primitive3d::Primitive3DContainer& rChildren3D)
+ : comphelper::ThreadTask(rTag),
+ mpZBufferProcessor3D(std::move(pZBufferProcessor3D)),
+ mrChildren3D(rChildren3D)
+ {
+ }
+
+ virtual void doWork() override
+ {
+ mpZBufferProcessor3D->process(mrChildren3D);
+ mpZBufferProcessor3D->finish();
+ mpZBufferProcessor3D.reset();
+ }
+ };
+
+ const sal_uInt32 nLinesPerThread(aBZPixelRaster.getHeight() / nThreadCount);
+ std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ for(sal_Int32 a(0); a < nThreadCount; a++)
+ {
+ std::unique_ptr<processor3d::ZBufferProcessor3D> pNewZBufferProcessor3D(new processor3d::ZBufferProcessor3D(
+ aViewInformation3D,
+ getSdrSceneAttribute(),
+ getSdrLightingAttribute(),
+ aUnitVisibleRange,
+ nOversampleValue,
+ fFullViewSizeX,
+ fFullViewSizeY,
+ aBZPixelRaster,
+ nLinesPerThread * a,
+ a + 1 == nThreadCount ? aBZPixelRaster.getHeight() : nLinesPerThread * (a + 1)));
+ std::unique_ptr<Executor> pExecutor(new Executor(aTag, std::move(pNewZBufferProcessor3D), getChildren3D()));
+ rThreadPool.pushTask(std::move(pExecutor));
+ }
+
+ rThreadPool.waitUntilDone(aTag);
+ }
+ else
+ {
+ // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
+ processor3d::ZBufferProcessor3D aZBufferProcessor3D(
+ aViewInformation3D,
+ getSdrSceneAttribute(),
+ getSdrLightingAttribute(),
+ aUnitVisibleRange,
+ nOversampleValue,
+ fFullViewSizeX,
+ fFullViewSizeY,
+ aBZPixelRaster,
+ 0,
+ aBZPixelRaster.getHeight());
+
+ aZBufferProcessor3D.process(getChildren3D());
+ aZBufferProcessor3D.finish();
+ }
+
+ const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = BPixelRasterToBitmapEx(aBZPixelRaster, nOversampleValue);
+ const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
+
+ if(!(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight()))
+ return;
+
+ // create transform for the created bitmap in discrete coordinates first.
+ basegfx::B2DHomMatrix aNew2DTransform;
+
+ aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
+ aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
+ aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
+ aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
+
+ // transform back to world coordinates for usage in primitive creation
+ aNew2DTransform *= aInverseOToV;
+
+ // create bitmap primitive and add
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(maOldRenderedBitmap),
+ aNew2DTransform));
+
+ // test: Allow to add an outline in the debugger when tests are needed
+ static bool bAddOutlineToCreated3DSceneRepresentation(false); // loplugin:constvars:ignore
+
+ if(bAddOutlineToCreated3DSceneRepresentation)
+ {
+ basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
+ aOutline.transform(aNew2DTransform);
+ rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, basegfx::BColor(1.0, 0.0, 0.0)));
+ }
+ }
+
+ Primitive2DContainer ScenePrimitive2D::getGeometry2D() const
+ {
+ Primitive2DContainer aRetval;
+
+ // create 2D projected geometry from 3D geometry
+ if(!getChildren3D().empty())
+ {
+ // create 2D geometry extraction processor
+ processor3d::Geometry2DExtractingProcessor aGeometryProcessor(
+ getViewInformation3D(),
+ getObjectTransformation());
+
+ // process local primitives
+ aGeometryProcessor.process(getChildren3D());
+
+ // fetch result
+ aRetval = aGeometryProcessor.getPrimitive2DSequence();
+ }
+
+ return aRetval;
+ }
+
+ Primitive2DContainer ScenePrimitive2D::getShadow2D() const
+ {
+ Primitive2DContainer aRetval;
+
+ // create 2D shadows from contained 3D primitives
+ if(impGetShadow3D())
+ {
+ // add extracted 2d shadows (before 3d scene creations itself)
+ aRetval = maShadowPrimitives;
+ }
+
+ return aRetval;
+ }
+
+ bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
+ {
+ if(maOldRenderedBitmap.IsEmpty() || maOldUnitVisiblePart.isEmpty())
+ return false;
+
+ basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
+
+ if(!maOldUnitVisiblePart.isInside(aRelativePoint))
+ return false;
+
+ // calculate coordinates relative to visualized part
+ double fDivisorX(maOldUnitVisiblePart.getWidth());
+ double fDivisorY(maOldUnitVisiblePart.getHeight());
+
+ if(basegfx::fTools::equalZero(fDivisorX))
+ {
+ fDivisorX = 1.0;
+ }
+
+ if(basegfx::fTools::equalZero(fDivisorY))
+ {
+ fDivisorY = 1.0;
+ }
+
+ const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
+ const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
+
+ // combine with real BitmapSizePixel to get bitmap coordinates
+ const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
+ const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
+ const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
+
+ // try to get a statement about transparency in that pixel
+ o_rResult = (0 != maOldRenderedBitmap.GetAlpha(nX, nY));
+ return true;
+ }
+
+ ScenePrimitive2D::ScenePrimitive2D(
+ const primitive3d::Primitive3DContainer& rxChildren3D,
+ const attribute::SdrSceneAttribute& rSdrSceneAttribute,
+ const attribute::SdrLightingAttribute& rSdrLightingAttribute,
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ const geometry::ViewInformation3D& rViewInformation3D)
+ : mxChildren3D(rxChildren3D),
+ maSdrSceneAttribute(rSdrSceneAttribute),
+ maSdrLightingAttribute(rSdrLightingAttribute),
+ maObjectTransformation(rObjectTransformation),
+ maViewInformation3D(rViewInformation3D),
+ mbShadow3DChecked(false),
+ mfOldDiscreteSizeX(0.0),
+ mfOldDiscreteSizeY(0.0)
+ {
+ }
+
+ bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const ScenePrimitive2D& rCompare = static_cast<const ScenePrimitive2D&>(rPrimitive);
+
+ return (getChildren3D() == rCompare.getChildren3D()
+ && getSdrSceneAttribute() == rCompare.getSdrSceneAttribute()
+ && getSdrLightingAttribute() == rCompare.getSdrLightingAttribute()
+ && getObjectTransformation() == rCompare.getObjectTransformation()
+ && getViewInformation3D() == rCompare.getViewInformation3D());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange ScenePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // transform unit range to discrete coordinate range
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // force to discrete expanded bounds (it grows, so expanding works perfectly well)
+ aRetval.expand(basegfx::B2DTuple(floor(aRetval.getMinX()), floor(aRetval.getMinY())));
+ aRetval.expand(basegfx::B2DTuple(ceil(aRetval.getMaxX()), ceil(aRetval.getMaxY())));
+
+ // transform back from discrete (view) to world coordinates
+ aRetval.transform(rViewInformation.getInverseObjectToViewTransformation());
+
+ // expand by evtl. existing shadow primitives
+ if(impGetShadow3D())
+ {
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+
+ if(!aShadow2DRange.isEmpty())
+ {
+ aRetval.expand(aShadow2DRange);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void ScenePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the involved ranges (see helper method calculateDiscreteSizes for details)
+ basegfx::B2DRange aDiscreteRange;
+ basegfx::B2DRange aUnitVisibleRange;
+ bool bNeedNewDecomposition(false);
+ bool bDiscreteSizesAreCalculated(false);
+
+ if(!getBuffered2DDecomposition().empty())
+ {
+ basegfx::B2DRange aVisibleDiscreteRange;
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ bDiscreteSizesAreCalculated = true;
+
+ // needs to be painted when the new part is not part of the last
+ // decomposition
+ if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
+ {
+ bNeedNewDecomposition = true;
+ }
+
+ // display has changed and cannot be reused when resolution got bigger. It
+ // can be reused when resolution got smaller, though.
+ if(!bNeedNewDecomposition)
+ {
+ if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
+ basegfx::fTools::more(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
+ {
+ bNeedNewDecomposition = true;
+ }
+ }
+ }
+
+ if(bNeedNewDecomposition)
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ if(!bDiscreteSizesAreCalculated)
+ {
+ basegfx::B2DRange aVisibleDiscreteRange;
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ }
+
+ // remember last used NewDiscreteSize and NewUnitVisiblePart
+ ScenePrimitive2D* pThat = const_cast< ScenePrimitive2D* >(this);
+ pThat->mfOldDiscreteSizeX = aDiscreteRange.getWidth();
+ pThat->mfOldDiscreteSizeY = aDiscreteRange.getHeight();
+ pThat->maOldUnitVisiblePart = aUnitVisibleRange;
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 ScenePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SCENEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
new file mode 100644
index 000000000..5080a92a8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ const basegfx::B2DHomMatrix& rMatrix)
+ {
+ const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon());
+
+ return createHiddenGeometryPrimitives2D(
+ false/*bFilled*/,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ rMatrix);
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon)
+ {
+ return createHiddenGeometryPrimitives2D(
+ false/*bFilled*/,
+ rPolyPolygon,
+ basegfx::B2DHomMatrix());
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ bool bFilled,
+ const basegfx::B2DRange& rRange)
+ {
+ return createHiddenGeometryPrimitives2D(
+ bFilled,
+ rRange,
+ basegfx::B2DHomMatrix());
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ bool bFilled,
+ const basegfx::B2DRange& rRange,
+ const basegfx::B2DHomMatrix& rMatrix)
+ {
+ const basegfx::B2DPolyPolygon aOutline(basegfx::utils::createPolygonFromRect(rRange));
+
+ return createHiddenGeometryPrimitives2D(
+ bFilled,
+ aOutline,
+ rMatrix);
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ bool bFilled,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const basegfx::B2DHomMatrix& rMatrix)
+ {
+ // create fill or line primitive
+ Primitive2DReference xReference;
+ basegfx::B2DPolyPolygon aScaledOutline(rPolyPolygon);
+ aScaledOutline.transform(rMatrix);
+
+ if(bFilled)
+ {
+ xReference = new PolyPolygonColorPrimitive2D(
+ aScaledOutline,
+ basegfx::BColor(0.0, 0.0, 0.0));
+ }
+ else
+ {
+ const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0);
+
+ xReference = new PolyPolygonHairlinePrimitive2D(
+ aScaledOutline,
+ aGrayTone);
+ }
+
+ // create HiddenGeometryPrimitive2D
+ return Primitive2DReference(
+ new HiddenGeometryPrimitive2D(Primitive2DContainer { xReference }));
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
new file mode 100644
index 000000000..758aca3d2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ ShadowPrimitive2D::ShadowPrimitive2D(
+ const basegfx::B2DHomMatrix& rShadowTransform,
+ const basegfx::BColor& rShadowColor,
+ double fShadowBlur,
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maShadowTransform(rShadowTransform),
+ maShadowColor(rShadowColor),
+ mfShadowBlur(fShadowBlur)
+ {
+ }
+
+ bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const ShadowPrimitive2D& rCompare = static_cast< const ShadowPrimitive2D& >(rPrimitive);
+
+ return (getShadowTransform() == rCompare.getShadowTransform()
+ && getShadowColor() == rCompare.getShadowColor()
+ && getShadowBlur() == rCompare.getShadowBlur());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange ShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
+ aRetval.grow(getShadowBlur());
+ aRetval.transform(getShadowTransform());
+ return aRetval;
+ }
+
+ void ShadowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getChildren().empty())
+ return;
+
+ // create a modifiedColorPrimitive containing the shadow color and the content
+ const basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_replace>(
+ getShadowColor());
+ const Primitive2DReference xRefA(
+ new ModifiedColorPrimitive2D(
+ Primitive2DContainer(getChildren()),
+ aBColorModifier));
+ Primitive2DContainer aSequenceB { xRefA };
+
+ // build transformed primitiveVector with shadow offset and add to target
+ rVisitor.visit(new TransformPrimitive2D(getShadowTransform(), std::move(aSequenceB)));
+ }
+
+ // provide unique ID
+ sal_uInt32 ShadowPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SHADOWPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
new file mode 100644
index 000000000..59cf59cd7
--- /dev/null
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ , mfRadius(fRadius)
+{
+}
+
+bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (GroupPrimitive2D::operator==(rPrimitive))
+ {
+ auto& rCompare = static_cast<const SoftEdgePrimitive2D&>(rPrimitive);
+ return getRadius() == rCompare.getRadius();
+ }
+
+ return false;
+}
+
+void SoftEdgePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getChildren().empty())
+ return;
+
+ if (!mbInMaskGeneration)
+ {
+ GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ return;
+ }
+
+ // create a modifiedColorPrimitive containing the *black* color and the content. Using black
+ // on white allows creating useful mask in VclPixelProcessor2D::processSoftEdgePrimitive2D.
+ basegfx::BColorModifierSharedPtr aBColorModifier
+ = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor());
+
+ const Primitive2DReference xRef(
+ new ModifiedColorPrimitive2D(Primitive2DContainer(getChildren()), aBColorModifier));
+ rVisitor.visit(xRef);
+}
+
+sal_uInt32 SoftEdgePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
new file mode 100644
index 000000000..7e4de87ed
--- /dev/null
+++ b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ StructureTagPrimitive2D::StructureTagPrimitive2D(
+ const vcl::PDFWriter::StructElement& rStructureElement,
+ bool bBackground,
+ bool bIsImage,
+ Primitive2DContainer&& aChildren,
+ sal_Int32 const nAnchorStructureElementId)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maStructureElement(rStructureElement),
+ mbBackground(bBackground),
+ mbIsImage(bIsImage)
+ , m_nAnchorStructureElementId(nAnchorStructureElementId)
+ {
+ }
+
+ bool StructureTagPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const StructureTagPrimitive2D& rCompare = static_cast<const StructureTagPrimitive2D&>(rPrimitive);
+
+ return (isBackground() == rCompare.isBackground() &&
+ isImage() == rCompare.isImage());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 StructureTagPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D;
+ }
+
+ bool StructureTagPrimitive2D::isTaggedSdrObject() const
+ {
+ // note at the moment *all* StructureTagPrimitive2D are created for
+ // SdrObjects - if that ever changes, need another condition here
+ return !isBackground() || isImage();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
new file mode 100644
index 000000000..6b49c4b49
--- /dev/null
+++ b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
@@ -0,0 +1,1096 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <cmath>
+#include <vcl/skia/SkiaHelper.hxx>
+
+using namespace com::sun::star;
+
+
+namespace
+{
+ sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit)
+ {
+ // use color distance, assume to do every color step (full quality)
+ sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
+
+ if(nSteps)
+ {
+ // calc discrete length to change color all 1.5 discrete units (pixels)
+ const sal_uInt32 nDistSteps(basegfx::fround(fDelta / (fDiscreteUnit * 1.5)));
+
+ nSteps = std::min(nSteps, nDistSteps);
+ }
+
+ // roughly cut when too big or too small
+ nSteps = std::min(nSteps, sal_uInt32(255));
+ nSteps = std::max(nSteps, sal_uInt32(1));
+
+ return nSteps;
+ }
+} // end of anonymous namespace
+
+
+namespace drawinglayer::primitive2d
+{
+ void SvgGradientHelper::createSingleGradientEntryFill(Primitive2DContainer& rContainer) const
+ {
+ const SvgGradientEntryVector& rEntries = getGradientEntries();
+ const sal_uInt32 nCount(rEntries.size());
+
+ if(nCount)
+ {
+ const SvgGradientEntry& rSingleEntry = rEntries[nCount - 1];
+ const double fOpacity(rSingleEntry.getOpacity());
+
+ if(fOpacity > 0.0)
+ {
+ Primitive2DReference xRef(
+ new PolyPolygonColorPrimitive2D(
+ getPolyPolygon(),
+ rSingleEntry.getColor()));
+
+ if(fOpacity < 1.0)
+ {
+ Primitive2DContainer aContent { xRef };
+
+ xRef = Primitive2DReference(
+ new UnifiedTransparencePrimitive2D(
+ std::move(aContent),
+ 1.0 - fOpacity));
+ }
+
+ rContainer.push_back(xRef);
+ }
+ }
+ else
+ {
+ OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
+ }
+ }
+
+ void SvgGradientHelper::checkPreconditions()
+ {
+ mbPreconditionsChecked = true;
+ const SvgGradientEntryVector& rEntries = getGradientEntries();
+
+ if(rEntries.empty())
+ {
+ // no fill at all, done
+ return;
+ }
+
+ // sort maGradientEntries by offset, small to big
+ std::sort(maGradientEntries.begin(), maGradientEntries.end());
+
+ // gradient with at least two colors
+ bool bAllInvisible(true);
+ bool bInvalidEntries(false);
+
+ for(const SvgGradientEntry& rCandidate : rEntries)
+ {
+ if(basegfx::fTools::equalZero(rCandidate.getOpacity()))
+ {
+ // invisible
+ mbFullyOpaque = false;
+ }
+ else if(basegfx::fTools::equal(rCandidate.getOpacity(), 1.0))
+ {
+ // completely opaque
+ bAllInvisible = false;
+ }
+ else
+ {
+ // opacity
+ bAllInvisible = false;
+ mbFullyOpaque = false;
+ }
+
+ if(!basegfx::fTools::betweenOrEqualEither(rCandidate.getOffset(), 0.0, 1.0))
+ {
+ bInvalidEntries = true;
+ }
+ }
+
+ if(bAllInvisible)
+ {
+ // all invisible, nothing to do
+ return;
+ }
+
+ if(bInvalidEntries)
+ {
+ // invalid entries, do nothing
+ SAL_WARN("drawinglayer", "SvgGradientHelper got invalid SvgGradientEntries outside [0.0 .. 1.0]");
+ return;
+ }
+
+ const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+
+ if(aPolyRange.isEmpty())
+ {
+ // no range to fill, nothing to do
+ return;
+ }
+
+ const double fPolyWidth(aPolyRange.getWidth());
+ const double fPolyHeight(aPolyRange.getHeight());
+
+ if(basegfx::fTools::equalZero(fPolyWidth) || basegfx::fTools::equalZero(fPolyHeight))
+ {
+ // no width/height to fill, nothing to do
+ return;
+ }
+
+ mbCreatesContent = true;
+
+ if(1 == rEntries.size())
+ {
+ // fill with single existing color
+ setSingleEntry();
+ }
+ }
+
+ const SvgGradientEntry& SvgGradientHelper::FindEntryLessOrEqual(
+ sal_Int32& rInt,
+ const double fFrac) const
+ {
+ const bool bMirror(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent(bMirror ? getMirroredGradientEntries() : getGradientEntries());
+
+ for(SvgGradientEntryVector::const_reverse_iterator aIter(rCurrent.rbegin()); aIter != rCurrent.rend(); ++aIter)
+ {
+ if(basegfx::fTools::lessOrEqual(aIter->getOffset(), fFrac))
+ {
+ return *aIter;
+ }
+ }
+
+ // walk over gap to the left, be prepared for missing 0.0/1.0 entries
+ rInt--;
+ const bool bMirror2(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent2(bMirror2 ? getMirroredGradientEntries() : getGradientEntries());
+ return rCurrent2.back();
+ }
+
+ const SvgGradientEntry& SvgGradientHelper::FindEntryMore(
+ sal_Int32& rInt,
+ const double fFrac) const
+ {
+ const bool bMirror(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent(bMirror ? getMirroredGradientEntries() : getGradientEntries());
+
+ for(SvgGradientEntryVector::const_iterator aIter(rCurrent.begin()); aIter != rCurrent.end(); ++aIter)
+ {
+ if(basegfx::fTools::more(aIter->getOffset(), fFrac))
+ {
+ return *aIter;
+ }
+ }
+
+ // walk over gap to the right, be prepared for missing 0.0/1.0 entries
+ rInt++;
+ const bool bMirror2(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent2(bMirror2 ? getMirroredGradientEntries() : getGradientEntries());
+ return rCurrent2.front();
+ }
+
+ // tdf#124424 Adapted creation of color runs to do in a single effort. Previous
+ // version tried to do this from [0.0 .. 1.0] and to re-use transformed versions
+ // in the caller if SpreadMethod was on some repeat mode, but had problems when
+ // e.g. like in the bugdoc from the task a negative-only fStart/fEnd run was
+ // requested in which case it did nothing. Even when reusing the spread might
+ // not have been a full one from [0.0 .. 1.0].
+ // This gets complicated due to mirrored runs, but also for gradient definitions
+ // with missing entries for 0.0 and 1.0 in which case these have to be guessed
+ // to be there with same parametrisation as their nearest existing entries. These
+ // *could* have been added at checkPreconditions() but would then create unnecessary
+ // spreads on zone overlaps.
+ void SvgGradientHelper::createRun(
+ Primitive2DContainer& rTargetColor,
+ Primitive2DContainer& rTargetOpacity,
+ double fStart,
+ double fEnd) const
+ {
+ double fInt(0.0);
+ double fFrac(0.0);
+ double fEnd2(0.0);
+
+ if(SpreadMethod::Pad == getSpreadMethod())
+ {
+ if(fStart < 0.0)
+ {
+ fFrac = std::modf(fStart, &fInt);
+ const SvgGradientEntry& rFront(getGradientEntries().front());
+ const SvgGradientEntry aTemp(1.0 + fFrac, rFront.getColor(), rFront.getOpacity());
+ createAtom(rTargetColor, rTargetOpacity, aTemp, rFront, static_cast<sal_Int32>(fInt - 1), 0);
+ fStart = rFront.getOffset();
+ }
+
+ if(fEnd > 1.0)
+ {
+ // change fEnd early, but create geometry later (after range below)
+ fEnd2 = fEnd;
+ fEnd = getGradientEntries().back().getOffset();
+ }
+ }
+
+ while(fStart < fEnd)
+ {
+ fFrac = std::modf(fStart, &fInt);
+
+ if(fFrac < 0.0)
+ {
+ fInt -= 1;
+ fFrac = 1.0 + fFrac;
+ }
+
+ sal_Int32 nIntLeft(static_cast<sal_Int32>(fInt));
+ sal_Int32 nIntRight(nIntLeft);
+
+ const SvgGradientEntry& rLeft(FindEntryLessOrEqual(nIntLeft, fFrac));
+ const SvgGradientEntry& rRight(FindEntryMore(nIntRight, fFrac));
+ createAtom(rTargetColor, rTargetOpacity, rLeft, rRight, nIntLeft, nIntRight);
+
+ const double fNextfStart(static_cast<double>(nIntRight) + rRight.getOffset());
+
+ if(basegfx::fTools::more(fNextfStart, fStart))
+ {
+ fStart = fNextfStart;
+ }
+ else
+ {
+ SAL_WARN("drawinglayer", "SvgGradientHelper spread error");
+ fStart += 1.0;
+ }
+ }
+
+ if(fEnd2 > 1.0)
+ {
+ // create end run for SpreadMethod::Pad late to keep correct creation order
+ fFrac = std::modf(fEnd2, &fInt);
+ const SvgGradientEntry& rBack(getGradientEntries().back());
+ const SvgGradientEntry aTemp(fFrac, rBack.getColor(), rBack.getOpacity());
+ createAtom(rTargetColor, rTargetOpacity, rBack, aTemp, 0, static_cast<sal_Int32>(fInt));
+ }
+ }
+
+ void SvgGradientHelper::createResult(
+ Primitive2DContainer& rContainer,
+ Primitive2DContainer aTargetColor,
+ Primitive2DContainer aTargetOpacity,
+ const basegfx::B2DHomMatrix& rUnitGradientToObject,
+ bool bInvert) const
+ {
+ Primitive2DContainer aTargetColorEntries(aTargetColor.maybeInvert(bInvert));
+ Primitive2DContainer aTargetOpacityEntries(aTargetOpacity.maybeInvert(bInvert));
+
+ if(aTargetColorEntries.empty())
+ return;
+
+ Primitive2DReference xRefContent;
+
+ if(!aTargetOpacityEntries.empty())
+ {
+ const Primitive2DReference xRefOpacity = new TransparencePrimitive2D(
+ std::move(aTargetColorEntries),
+ std::move(aTargetOpacityEntries));
+
+ xRefContent = new TransformPrimitive2D(
+ rUnitGradientToObject,
+ Primitive2DContainer { xRefOpacity });
+ }
+ else
+ {
+ xRefContent = new TransformPrimitive2D(
+ rUnitGradientToObject,
+ std::move(aTargetColorEntries));
+ }
+
+ rContainer.push_back(new MaskPrimitive2D(
+ getPolyPolygon(),
+ Primitive2DContainer { xRefContent }));
+ }
+
+ SvgGradientHelper::SvgGradientHelper(
+ const basegfx::B2DHomMatrix& rGradientTransform,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ SvgGradientEntryVector&& rGradientEntries,
+ const basegfx::B2DPoint& rStart,
+ bool bUseUnitCoordinates,
+ SpreadMethod aSpreadMethod)
+ : maGradientTransform(rGradientTransform),
+ maPolyPolygon(rPolyPolygon),
+ maGradientEntries(std::move(rGradientEntries)),
+ maStart(rStart),
+ maSpreadMethod(aSpreadMethod),
+ mbPreconditionsChecked(false),
+ mbCreatesContent(false),
+ mbSingleEntry(false),
+ mbFullyOpaque(true),
+ mbUseUnitCoordinates(bUseUnitCoordinates)
+ {
+ }
+
+ SvgGradientHelper::~SvgGradientHelper()
+ {
+ }
+
+ const SvgGradientEntryVector& SvgGradientHelper::getMirroredGradientEntries() const
+ {
+ if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
+ {
+ const_cast< SvgGradientHelper* >(this)->createMirroredGradientEntries();
+ }
+
+ return maMirroredGradientEntries;
+ }
+
+ void SvgGradientHelper::createMirroredGradientEntries()
+ {
+ if(!maMirroredGradientEntries.empty() || getGradientEntries().empty())
+ return;
+
+ const sal_uInt32 nCount(getGradientEntries().size());
+ maMirroredGradientEntries.clear();
+ maMirroredGradientEntries.reserve(nCount);
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const SvgGradientEntry& rCandidate = getGradientEntries()[nCount - 1 - a];
+
+ maMirroredGradientEntries.emplace_back(
+ 1.0 - rCandidate.getOffset(),
+ rCandidate.getColor(),
+ rCandidate.getOpacity());
+ }
+ }
+
+ bool SvgGradientHelper::operator==(const SvgGradientHelper& rSvgGradientHelper) const
+ {
+ const SvgGradientHelper& rCompare = rSvgGradientHelper;
+
+ return (getGradientTransform() == rCompare.getGradientTransform()
+ && getPolyPolygon() == rCompare.getPolyPolygon()
+ && getGradientEntries() == rCompare.getGradientEntries()
+ && getStart() == rCompare.getStart()
+ && getUseUnitCoordinates() == rCompare.getUseUnitCoordinates()
+ && getSpreadMethod() == rCompare.getSpreadMethod());
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ void SvgLinearGradientPrimitive2D::checkPreconditions()
+ {
+ // call parent
+ SvgGradientHelper::checkPreconditions();
+
+ if(getCreatesContent())
+ {
+ // Check Vector
+ const basegfx::B2DVector aVector(getEnd() - getStart());
+
+ if(basegfx::fTools::equalZero(aVector.getX()) && basegfx::fTools::equalZero(aVector.getY()))
+ {
+ // fill with single color using last stop color
+ setSingleEntry();
+ }
+ }
+ }
+
+ void SvgLinearGradientPrimitive2D::createAtom(
+ Primitive2DContainer& rTargetColor,
+ Primitive2DContainer& rTargetOpacity,
+ const SvgGradientEntry& rFrom,
+ const SvgGradientEntry& rTo,
+ sal_Int32 nOffsetFrom,
+ sal_Int32 nOffsetTo) const
+ {
+ // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
+ if(rFrom.getOffset() == rTo.getOffset())
+ {
+ OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
+ }
+ else
+ {
+ rTargetColor.push_back(
+ new SvgLinearAtomPrimitive2D(
+ rFrom.getColor(), rFrom.getOffset() + nOffsetFrom,
+ rTo.getColor(), rTo.getOffset() + nOffsetTo));
+
+ if(!getFullyOpaque())
+ {
+ const double fTransFrom(1.0 - rFrom.getOpacity());
+ const double fTransTo(1.0 - rTo.getOpacity());
+ const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
+ const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
+
+ rTargetOpacity.push_back(
+ new SvgLinearAtomPrimitive2D(
+ aColorFrom, rFrom.getOffset() + nOffsetFrom,
+ aColorTo, rTo.getOffset() + nOffsetTo));
+ }
+ }
+ }
+
+ void SvgLinearGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(!getPreconditionsChecked())
+ {
+ const_cast< SvgLinearGradientPrimitive2D* >(this)->checkPreconditions();
+ }
+
+ if(getSingleEntry())
+ {
+ // fill with last existing color
+ createSingleGradientEntryFill(rContainer);
+ }
+ else if(getCreatesContent())
+ {
+ // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
+ // invisible, width and height to fill are not empty
+ const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+ const double fPolyWidth(aPolyRange.getWidth());
+ const double fPolyHeight(aPolyRange.getHeight());
+
+ // create ObjectTransform based on polygon range
+ const basegfx::B2DHomMatrix aObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fPolyWidth, fPolyHeight,
+ aPolyRange.getMinX(), aPolyRange.getMinY()));
+ basegfx::B2DHomMatrix aUnitGradientToObject;
+
+ if(getUseUnitCoordinates())
+ {
+ // interpret in unit coordinate system -> object aspect ratio will scale result
+ // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given
+ // gradient vector defined by Start,End
+ const basegfx::B2DVector aVector(getEnd() - getStart());
+ const double fVectorLength(aVector.getLength());
+
+ aUnitGradientToObject.scale(fVectorLength, 1.0);
+ aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
+ aUnitGradientToObject.translate(getStart().getX(), getStart().getY());
+
+ aUnitGradientToObject *= getGradientTransform();
+
+ // create full transform from unit gradient coordinates to object coordinates
+ // including the SvgGradient transformation
+ aUnitGradientToObject *= aObjectTransform;
+ }
+ else
+ {
+ // interpret in object coordinate system -> object aspect ratio will not scale result
+ const basegfx::B2DPoint aStart(aObjectTransform * getStart());
+ const basegfx::B2DPoint aEnd(aObjectTransform * getEnd());
+ const basegfx::B2DVector aVector(aEnd - aStart);
+
+ aUnitGradientToObject.scale(aVector.getLength(), 1.0);
+ aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
+ aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
+
+ aUnitGradientToObject *= getGradientTransform();
+ }
+
+ // create inverse from it
+ basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
+ aObjectToUnitGradient.invert();
+
+ // back-transform polygon to unit gradient coordinates and get
+ // UnitRage. This is the range the gradient has to cover
+ basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
+ aUnitPoly.transform(aObjectToUnitGradient);
+ const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
+
+ // prepare result vectors
+ Primitive2DContainer aTargetColor;
+ Primitive2DContainer aTargetOpacity;
+
+ if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0))
+ {
+ // add a pre-multiply to aUnitGradientToObject to allow
+ // multiplication of the polygon(xl, 0.0, xr, 1.0)
+ const basegfx::B2DHomMatrix aPreMultiply(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ 1.0, aUnitRange.getHeight(), 0.0, aUnitRange.getMinY()));
+ aUnitGradientToObject = aUnitGradientToObject * aPreMultiply;
+
+ // create full color run, including all SpreadMethod variants
+ createRun(
+ aTargetColor,
+ aTargetOpacity,
+ aUnitRange.getMinX(),
+ aUnitRange.getMaxX());
+ }
+
+ createResult(rContainer, std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject);
+ }
+ }
+
+ SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
+ const basegfx::B2DHomMatrix& rGradientTransform,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ SvgGradientEntryVector&& rGradientEntries,
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ bool bUseUnitCoordinates,
+ SpreadMethod aSpreadMethod)
+ : SvgGradientHelper(rGradientTransform, rPolyPolygon, std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod),
+ maEnd(rEnd)
+ {
+ }
+
+ SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
+ {
+ }
+
+ bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
+
+ if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper))
+ {
+ const SvgLinearGradientPrimitive2D& rCompare = static_cast< const SvgLinearGradientPrimitive2D& >(rPrimitive);
+
+ return (getEnd() == rCompare.getEnd());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return ObjectRange
+ return getPolyPolygon().getB2DRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgLinearGradientPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ void SvgRadialGradientPrimitive2D::checkPreconditions()
+ {
+ // call parent
+ SvgGradientHelper::checkPreconditions();
+
+ if(getCreatesContent())
+ {
+ // Check Radius
+ if(basegfx::fTools::equalZero(getRadius()))
+ {
+ // fill with single color using last stop color
+ setSingleEntry();
+ }
+ }
+ }
+
+ void SvgRadialGradientPrimitive2D::createAtom(
+ Primitive2DContainer& rTargetColor,
+ Primitive2DContainer& rTargetOpacity,
+ const SvgGradientEntry& rFrom,
+ const SvgGradientEntry& rTo,
+ sal_Int32 nOffsetFrom,
+ sal_Int32 nOffsetTo) const
+ {
+ // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
+ if(rFrom.getOffset() == rTo.getOffset())
+ {
+ OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
+ }
+ else
+ {
+ const double fScaleFrom(rFrom.getOffset() + nOffsetFrom);
+ const double fScaleTo(rTo.getOffset() + nOffsetTo);
+
+ if(isFocalSet())
+ {
+ const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
+ const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
+
+ rTargetColor.push_back(
+ new SvgRadialAtomPrimitive2D(
+ rFrom.getColor(), fScaleFrom, aTranslateFrom,
+ rTo.getColor(), fScaleTo, aTranslateTo));
+ }
+ else
+ {
+ rTargetColor.push_back(
+ new SvgRadialAtomPrimitive2D(
+ rFrom.getColor(), fScaleFrom,
+ rTo.getColor(), fScaleTo));
+ }
+
+ if(!getFullyOpaque())
+ {
+ const double fTransFrom(1.0 - rFrom.getOpacity());
+ const double fTransTo(1.0 - rTo.getOpacity());
+ const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
+ const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
+
+ if(isFocalSet())
+ {
+ const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
+ const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
+
+ rTargetOpacity.push_back(
+ new SvgRadialAtomPrimitive2D(
+ aColorFrom, fScaleFrom, aTranslateFrom,
+ aColorTo, fScaleTo, aTranslateTo));
+ }
+ else
+ {
+ rTargetOpacity.push_back(
+ new SvgRadialAtomPrimitive2D(
+ aColorFrom, fScaleFrom,
+ aColorTo, fScaleTo));
+ }
+ }
+ }
+ }
+
+ void SvgRadialGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(!getPreconditionsChecked())
+ {
+ const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions();
+ }
+
+ if(getSingleEntry())
+ {
+ // fill with last existing color
+ createSingleGradientEntryFill(rContainer);
+ }
+ else if(getCreatesContent())
+ {
+ // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
+ // invisible, width and height to fill are not empty
+ const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+ const double fPolyWidth(aPolyRange.getWidth());
+ const double fPolyHeight(aPolyRange.getHeight());
+
+ // create ObjectTransform based on polygon range
+ const basegfx::B2DHomMatrix aObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fPolyWidth, fPolyHeight,
+ aPolyRange.getMinX(), aPolyRange.getMinY()));
+ basegfx::B2DHomMatrix aUnitGradientToObject;
+
+ if(getUseUnitCoordinates())
+ {
+ // interpret in unit coordinate system -> object aspect ratio will scale result
+ // create unit transform from unit vector to given linear gradient vector
+ aUnitGradientToObject.scale(getRadius(), getRadius());
+ aUnitGradientToObject.translate(getStart().getX(), getStart().getY());
+
+ if(!getGradientTransform().isIdentity())
+ {
+ aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
+ }
+
+ // create full transform from unit gradient coordinates to object coordinates
+ // including the SvgGradient transformation
+ aUnitGradientToObject = aObjectTransform * aUnitGradientToObject;
+ }
+ else
+ {
+ // interpret in object coordinate system -> object aspect ratio will not scale result
+ // use X-Axis with radius, it was already made relative to object width when coming from
+ // SVG import
+ const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength());
+ const basegfx::B2DPoint aStart(aObjectTransform * getStart());
+
+ aUnitGradientToObject.scale(fRadius, fRadius);
+ aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
+
+ aUnitGradientToObject *= getGradientTransform();
+ }
+
+ // create inverse from it
+ basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
+ aObjectToUnitGradient.invert();
+
+ // back-transform polygon to unit gradient coordinates and get
+ // UnitRage. This is the range the gradient has to cover
+ basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
+ aUnitPoly.transform(aObjectToUnitGradient);
+ const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
+
+ // create range which the gradient has to cover to cover the whole given geometry.
+ // For circle, go from 0.0 to max radius in all directions (the corners)
+ double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength());
+ fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength());
+ fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength());
+ fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength());
+
+ // prepare result vectors
+ Primitive2DContainer aTargetColor;
+ Primitive2DContainer aTargetOpacity;
+
+ if(0.0 < fMax)
+ {
+ // prepare maFocalVector
+ if(isFocalSet())
+ {
+ const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax;
+ }
+
+ // create full color run, including all SpreadMethod variants
+ createRun(
+ aTargetColor,
+ aTargetOpacity,
+ 0.0,
+ fMax);
+ }
+
+ createResult(rContainer, std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject, true);
+ }
+ }
+
+ SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
+ const basegfx::B2DHomMatrix& rGradientTransform,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ SvgGradientEntryVector&& rGradientEntries,
+ const basegfx::B2DPoint& rStart,
+ double fRadius,
+ bool bUseUnitCoordinates,
+ SpreadMethod aSpreadMethod,
+ const basegfx::B2DPoint* pFocal)
+ : SvgGradientHelper(rGradientTransform, rPolyPolygon, std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod),
+ mfRadius(fRadius),
+ maFocal(rStart),
+ maFocalVector(0.0, 0.0),
+ maFocalLength(0.0),
+ mbFocalSet(false)
+ {
+ if(pFocal && !pFocal->equal(getStart()))
+ {
+ maFocal = *pFocal;
+ maFocalVector = maFocal - getStart();
+ mbFocalSet = true;
+ }
+ }
+
+ SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
+ {
+ }
+
+ bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
+
+ if(!pSvgGradientHelper || !SvgGradientHelper::operator==(*pSvgGradientHelper))
+ return false;
+
+ const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive);
+
+ if(getRadius() == rCompare.getRadius())
+ {
+ if(isFocalSet() == rCompare.isFocalSet())
+ {
+ if(isFocalSet())
+ {
+ return getFocal() == rCompare.getFocal();
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return ObjectRange
+ return getPolyPolygon().getB2DRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgRadialGradientPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+// SvgLinearAtomPrimitive2D class
+
+namespace drawinglayer::primitive2d
+{
+ void SvgLinearAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const double fDelta(getOffsetB() - getOffsetA());
+
+ if(basegfx::fTools::equalZero(fDelta))
+ return;
+
+ // use one discrete unit for overlap (one pixel)
+ const double fDiscreteUnit(getDiscreteUnit());
+
+ // use color distance and discrete lengths to calculate step count
+ const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit));
+
+ // HACK: Splitting a gradient into adjacent polygons with gradually changing color is silly.
+ // If antialiasing is used to draw them, the AA-ed adjacent edges won't line up perfectly
+ // because of the AA (see SkiaSalGraphicsImpl::mergePolyPolygonToPrevious()).
+ // Make the polygons a bit wider, so they the partial overlap "fixes" this.
+ const double fixup = SkiaHelper::isVCLSkiaEnabled() ? fDiscreteUnit / 2 : 0;
+
+ // tdf#117949 Use a small amount of discrete overlap at the edges. Usually this
+ // should be exactly 0.0 and 1.0, but there were cases when this gets clipped
+ // against the mask polygon which got numerically problematic.
+ // This change is unnecessary in that respect, but avoids that numerical havoc
+ // by at the same time doing no real harm AFAIK
+ // TTTT: Remove again when clipping is fixed (!)
+
+ // prepare polygon in needed width at start position (with discrete overlap)
+ const basegfx::B2DPolygon aPolygon(
+ basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(
+ getOffsetA() - fDiscreteUnit,
+ -0.0001, // TTTT -> should be 0.0, see comment above
+ getOffsetA() + (fDelta / nSteps) + fDiscreteUnit + fixup,
+ 1.0001))); // TTTT -> should be 1.0, see comment above
+
+ // prepare loop (inside to outside, [0.0 .. 1.0[)
+ double fUnitScale(0.0);
+ const double fUnitStep(1.0 / nSteps);
+
+ for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
+ {
+ basegfx::B2DPolygon aNew(aPolygon);
+
+ aNew.transform(basegfx::utils::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
+ rContainer.push_back(new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aNew),
+ basegfx::interpolate(getColorA(), getColorB(), fUnitScale)));
+ }
+ }
+
+ SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
+ const basegfx::BColor& aColorA, double fOffsetA,
+ const basegfx::BColor& aColorB, double fOffsetB)
+ : maColorA(aColorA),
+ maColorB(aColorB),
+ mfOffsetA(fOffsetA),
+ mfOffsetB(fOffsetB)
+ {
+ if(mfOffsetA > mfOffsetB)
+ {
+ OSL_ENSURE(false, "Wrong offset order (!)");
+ std::swap(mfOffsetA, mfOffsetB);
+ }
+ }
+
+ bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive);
+
+ return (getColorA() == rCompare.getColorA()
+ && getColorB() == rCompare.getColorB()
+ && getOffsetA() == rCompare.getOffsetA()
+ && getOffsetB() == rCompare.getOffsetB());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgLinearAtomPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+// SvgRadialAtomPrimitive2D class
+
+namespace drawinglayer::primitive2d
+{
+ void SvgRadialAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const double fDeltaScale(getScaleB() - getScaleA());
+
+ if(basegfx::fTools::equalZero(fDeltaScale))
+ return;
+
+ // use one discrete unit for overlap (one pixel)
+ const double fDiscreteUnit(getDiscreteUnit());
+
+ // use color distance and discrete lengths to calculate step count
+ const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale, fDiscreteUnit));
+
+ // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
+ double fUnitScale(0.0);
+ const double fUnitStep(1.0 / nSteps);
+
+ for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
+ {
+ basegfx::B2DHomMatrix aTransform;
+ const double fEndScale(getScaleB() - (fDeltaScale * fUnitScale));
+
+ if(isTranslateSet())
+ {
+ const basegfx::B2DVector aTranslate(
+ basegfx::interpolate(
+ getTranslateB(),
+ getTranslateA(),
+ fUnitScale));
+
+ aTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fEndScale,
+ fEndScale,
+ aTranslate.getX(),
+ aTranslate.getY());
+ }
+ else
+ {
+ aTransform = basegfx::utils::createScaleB2DHomMatrix(
+ fEndScale,
+ fEndScale);
+ }
+
+ basegfx::B2DPolygon aNew(basegfx::utils::createPolygonFromUnitCircle());
+
+ aNew.transform(aTransform);
+ rContainer.push_back(new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aNew),
+ basegfx::interpolate(getColorB(), getColorA(), fUnitScale)));
+ }
+ }
+
+ SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
+ const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA,
+ const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB)
+ : maColorA(aColorA),
+ maColorB(aColorB),
+ mfScaleA(fScaleA),
+ mfScaleB(fScaleB)
+ {
+ // check and evtl. set translations
+ if(!rTranslateA.equal(rTranslateB))
+ {
+ mpTranslate.reset( new VectorPair(rTranslateA, rTranslateB) );
+ }
+
+ // scale A and B have to be positive
+ mfScaleA = std::max(mfScaleA, 0.0);
+ mfScaleB = std::max(mfScaleB, 0.0);
+
+ // scale B has to be bigger than scale A; swap if different
+ if(mfScaleA > mfScaleB)
+ {
+ OSL_ENSURE(false, "Wrong offset order (!)");
+ std::swap(mfScaleA, mfScaleB);
+
+ if(mpTranslate)
+ {
+ std::swap(mpTranslate->maTranslateA, mpTranslate->maTranslateB);
+ }
+ }
+ }
+
+ SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
+ const basegfx::BColor& aColorA, double fScaleA,
+ const basegfx::BColor& aColorB, double fScaleB)
+ : maColorA(aColorA),
+ maColorB(aColorB),
+ mfScaleA(fScaleA),
+ mfScaleB(fScaleB)
+ {
+ // scale A and B have to be positive
+ mfScaleA = std::max(mfScaleA, 0.0);
+ mfScaleB = std::max(mfScaleB, 0.0);
+
+ // scale B has to be bigger than scale A; swap if different
+ if(mfScaleA > mfScaleB)
+ {
+ OSL_ENSURE(false, "Wrong offset order (!)");
+ std::swap(mfScaleA, mfScaleB);
+ }
+ }
+
+ SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
+ {
+ }
+
+ bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(!DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ return false;
+
+ const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive);
+
+ if(getColorA() == rCompare.getColorA()
+ && getColorB() == rCompare.getColorB()
+ && getScaleA() == rCompare.getScaleA()
+ && getScaleB() == rCompare.getScaleB())
+ {
+ if(isTranslateSet() && rCompare.isTranslateSet())
+ {
+ return (getTranslateA() == rCompare.getTranslateA()
+ && getTranslateB() == rCompare.getTranslateB());
+ }
+ else if(!isTranslateSet() && !rCompare.isTranslateSet())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgRadialAtomPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textbreakuphelper.cxx b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
new file mode 100644
index 000000000..5ca10ce63
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/textbreakuphelper.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/CharType.hpp>
+
+
+namespace drawinglayer::primitive2d
+{
+ TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource)
+ : mrSource(rSource),
+ mbNoDXArray(false)
+ {
+ maDecTrans = mrSource.getTextTransform();
+ mbNoDXArray = mrSource.getDXArray().empty();
+
+ if(mbNoDXArray)
+ {
+ // init TextLayouter when no dxarray
+ maTextLayouter.setFontAttribute(
+ mrSource.getFontAttribute(),
+ maDecTrans.getScale().getX(),
+ maDecTrans.getScale().getY(),
+ mrSource.getLocale());
+ }
+ }
+
+ TextBreakupHelper::~TextBreakupHelper()
+ {
+ }
+
+ void TextBreakupHelper::breakupPortion(Primitive2DContainer& rTempResult, sal_Int32 nIndex, sal_Int32 nLength, bool bWordLineMode)
+ {
+ if(!(nLength && (nIndex != mrSource.getTextPosition() || nLength != mrSource.getTextLength())))
+ return;
+
+ // prepare values for new portion
+ basegfx::B2DHomMatrix aNewTransform;
+ std::vector< double > aNewDXArray;
+ const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition());
+
+ if(!mbNoDXArray)
+ {
+ // prepare new DXArray for the single word
+ aNewDXArray = std::vector< double >(
+ mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()),
+ mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition()));
+ }
+
+ if(bNewStartIsNotOldStart)
+ {
+ // needs to be moved to a new start position
+ double fOffset(0.0);
+
+ if(mbNoDXArray)
+ {
+ // evaluate using TextLayouter
+ fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex);
+ }
+ else
+ {
+ // get from DXArray
+ const sal_Int32 nIndex2(nIndex - mrSource.getTextPosition());
+ fOffset = mrSource.getDXArray()[nIndex2 - 1];
+ }
+
+ // need offset without FontScale for building the new transformation. The
+ // new transformation will be multiplied with the current text transformation
+ // so FontScale would be double
+ double fOffsetNoScale(fOffset);
+ const double fFontScaleX(maDecTrans.getScale().getX());
+
+ if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+ && !basegfx::fTools::equalZero(fFontScaleX))
+ {
+ fOffsetNoScale /= fFontScaleX;
+ }
+
+ // apply needed offset to transformation
+ aNewTransform.translate(fOffsetNoScale, 0.0);
+
+ if(!mbNoDXArray)
+ {
+ // DXArray values need to be corrected with the offset, too. Here,
+ // take the scaled offset since the DXArray is scaled
+ const sal_uInt32 nArraySize(aNewDXArray.size());
+
+ for(sal_uInt32 a(0); a < nArraySize; a++)
+ {
+ aNewDXArray[a] -= fOffset;
+ }
+ }
+ }
+
+ // add text transformation to new transformation
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ aNewTransform *= maDecTrans.getB2DHomMatrix();
+
+ // callback to allow evtl. changes
+ const bool bCreate(allowChange(rTempResult.size(), aNewTransform, nIndex, nLength));
+
+ if(!bCreate)
+ return;
+
+ // check if we have a decorated primitive as source
+ const TextDecoratedPortionPrimitive2D* pTextDecoratedPortionPrimitive2D =
+ dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource);
+
+ if(pTextDecoratedPortionPrimitive2D)
+ {
+ // create a TextDecoratedPortionPrimitive2D
+ rTempResult.push_back(
+ new TextDecoratedPortionPrimitive2D(
+ aNewTransform,
+ mrSource.getText(),
+ nIndex,
+ nLength,
+ std::move(aNewDXArray),
+ mrSource.getFontAttribute(),
+ mrSource.getLocale(),
+ mrSource.getFontColor(),
+ mrSource.getTextFillColor(),
+
+ pTextDecoratedPortionPrimitive2D->getOverlineColor(),
+ pTextDecoratedPortionPrimitive2D->getTextlineColor(),
+ pTextDecoratedPortionPrimitive2D->getFontOverline(),
+ pTextDecoratedPortionPrimitive2D->getFontUnderline(),
+ pTextDecoratedPortionPrimitive2D->getUnderlineAbove(),
+ pTextDecoratedPortionPrimitive2D->getTextStrikeout(),
+
+ // reset WordLineMode when BreakupUnit::Word is executed; else copy original
+ !bWordLineMode && pTextDecoratedPortionPrimitive2D->getWordLineMode(),
+
+ pTextDecoratedPortionPrimitive2D->getTextEmphasisMark(),
+ pTextDecoratedPortionPrimitive2D->getEmphasisMarkAbove(),
+ pTextDecoratedPortionPrimitive2D->getEmphasisMarkBelow(),
+ pTextDecoratedPortionPrimitive2D->getTextRelief(),
+ pTextDecoratedPortionPrimitive2D->getShadow()));
+ }
+ else
+ {
+ // create a SimpleTextPrimitive
+ rTempResult.push_back(
+ new TextSimplePortionPrimitive2D(
+ aNewTransform,
+ mrSource.getText(),
+ nIndex,
+ nLength,
+ std::move(aNewDXArray),
+ mrSource.getFontAttribute(),
+ mrSource.getLocale(),
+ mrSource.getFontColor()));
+ }
+ }
+
+ bool TextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& /*rNewTransform*/, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/)
+ {
+ return true;
+ }
+
+ void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit)
+ {
+ if(!mrSource.getTextLength())
+ return;
+
+ Primitive2DContainer aTempResult;
+ static css::uno::Reference< css::i18n::XBreakIterator > xBreakIterator;
+
+ if(!xBreakIterator.is())
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ xBreakIterator = css::i18n::BreakIterator::create(xContext);
+ }
+
+ const OUString& rTxt = mrSource.getText();
+ const sal_Int32 nTextLength(mrSource.getTextLength());
+ const css::lang::Locale& rLocale = mrSource.getLocale();
+ const sal_Int32 nTextPosition(mrSource.getTextPosition());
+ sal_Int32 nCurrent(nTextPosition);
+
+ switch(aBreakupUnit)
+ {
+ case BreakupUnit::Character:
+ {
+ sal_Int32 nDone;
+ sal_Int32 nNextCellBreak(xBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
+ sal_Int32 a(nTextPosition);
+
+ for(; a < nTextPosition + nTextLength; a++)
+ {
+ if(a == nNextCellBreak)
+ {
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, false);
+ nCurrent = a;
+ nNextCellBreak = xBreakIterator->nextCharacters(rTxt, a, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
+ }
+ }
+
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, false);
+ break;
+ }
+ case BreakupUnit::Word:
+ {
+ css::i18n::Boundary nNextWordBoundary(xBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true));
+ sal_Int32 a(nTextPosition);
+
+ for(; a < nTextPosition + nTextLength; a++)
+ {
+ if(a == nNextWordBoundary.endPos)
+ {
+ if(a > nCurrent)
+ {
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
+ }
+
+ nCurrent = a;
+
+ // skip spaces (maybe enhanced with a bool later if needed)
+ {
+ const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, css::i18n::CharType::SPACE_SEPARATOR));
+
+ if(nEndOfSpaces > a)
+ {
+ nCurrent = nEndOfSpaces;
+ }
+ }
+
+ nNextWordBoundary = xBreakIterator->getWordBoundary(rTxt, a + 1, rLocale, css::i18n::WordType::ANY_WORD, true);
+ }
+ }
+
+ if(a > nCurrent)
+ {
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
+ }
+ break;
+ }
+ }
+
+ mxResult = aTempResult;
+ }
+
+ Primitive2DContainer TextBreakupHelper::extractResult(BreakupUnit aBreakupUnit)
+ {
+ if(mxResult.empty())
+ {
+ breakup(aBreakupUnit);
+ }
+
+ return std::move(mxResult);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
new file mode 100644
index 000000000..0db26fbeb
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -0,0 +1,388 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <primitive2d/texteffectprimitive2d.hxx>
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <primitive2d/textlineprimitive2d.hxx>
+#include <primitive2d/textstrikeoutprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textbreakuphelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
+ Primitive2DContainer& rTarget,
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const & rDecTrans,
+ const OUString& rText,
+ sal_Int32 nTextPosition,
+ sal_Int32 nTextLength,
+ const std::vector< double >& rDXArray,
+ const attribute::FontAttribute& rFontAttribute) const
+ {
+ // create the SimpleTextPrimitive needed in any case
+ rTarget.push_back(Primitive2DReference(
+ new TextSimplePortionPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ rText,
+ nTextPosition,
+ nTextLength,
+ std::vector(rDXArray),
+ rFontAttribute,
+ getLocale(),
+ getFontColor())));
+
+ // see if something else needs to be done
+ const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
+ const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
+ const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout());
+
+ if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
+ return;
+
+ // common preparations
+ TextLayouterDevice aTextLayouter;
+
+ // TextLayouterDevice is needed to get metrics for text decorations like
+ // underline/strikeout/emphasis marks from it. For setup, the font size is needed
+ aTextLayouter.setFontAttribute(
+ getFontAttribute(),
+ rDecTrans.getScale().getX(),
+ rDecTrans.getScale().getY(),
+ getLocale());
+
+ // get text width
+ double fTextWidth(0.0);
+
+ if(rDXArray.empty())
+ {
+ fTextWidth = aTextLayouter.getTextWidth(rText, nTextPosition, nTextLength);
+ }
+ else
+ {
+ fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
+ const double fFontScaleX(rDecTrans.getScale().getX());
+
+ if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+ && !basegfx::fTools::equalZero(fFontScaleX))
+ {
+ // need to take FontScaling out of the DXArray
+ fTextWidth /= fFontScaleX;
+ }
+ }
+
+ if(bOverlineUsed)
+ {
+ // create primitive geometry for overline
+ rTarget.push_back(Primitive2DReference(
+ new TextLinePrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ aTextLayouter.getOverlineOffset(),
+ aTextLayouter.getOverlineHeight(),
+ getFontOverline(),
+ getOverlineColor())));
+ }
+
+ if(bUnderlineUsed)
+ {
+ // create primitive geometry for underline
+ rTarget.push_back(Primitive2DReference(
+ new TextLinePrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ aTextLayouter.getUnderlineOffset(),
+ aTextLayouter.getUnderlineHeight(),
+ getFontUnderline(),
+ getTextlineColor())));
+ }
+
+ if(!bStrikeoutUsed)
+ return;
+
+ // create primitive geometry for strikeout
+ if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout())
+ {
+ // strikeout with character
+ const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');
+
+ rTarget.push_back(Primitive2DReference(
+ new TextCharacterStrikeoutPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ getFontColor(),
+ aStrikeoutChar,
+ getFontAttribute(),
+ getLocale())));
+ }
+ else
+ {
+ // strikeout with geometry
+ rTarget.push_back(Primitive2DReference(
+ new TextGeometryStrikeoutPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ getFontColor(),
+ aTextLayouter.getUnderlineHeight(),
+ aTextLayouter.getStrikeoutOffset(),
+ getTextStrikeout())));
+ }
+
+ // TODO: Handle Font Emphasis Above/Below
+ }
+
+ void TextDecoratedPortionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getWordLineMode())
+ {
+ // support for single word mode; split to single word primitives
+ // using TextBreakupHelper
+ TextBreakupHelper aTextBreakupHelper(*this);
+ Primitive2DContainer aBroken(aTextBreakupHelper.extractResult(BreakupUnit::Word));
+
+ if(!aBroken.empty())
+ {
+ // was indeed split to several words, use as result
+ rContainer.append(std::move(aBroken));
+ return;
+ }
+ else
+ {
+ // no split, was already a single word. Continue to
+ // decompose local entity
+ }
+ }
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
+ Primitive2DContainer aRetval;
+
+ // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
+ // Strikeout, etc...
+ // prepare new font attributes WITHOUT outline
+ const attribute::FontAttribute aNewFontAttribute(
+ getFontAttribute().getFamilyName(),
+ getFontAttribute().getStyleName(),
+ getFontAttribute().getWeight(),
+ getFontAttribute().getSymbol(),
+ getFontAttribute().getVertical(),
+ getFontAttribute().getItalic(),
+ getFontAttribute().getMonospaced(),
+ false, // no outline anymore, handled locally
+ getFontAttribute().getRTL(),
+ getFontAttribute().getBiDiStrong());
+
+ // handle as one word
+ impCreateGeometryContent(aRetval, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
+
+ // Handle Shadow, Outline and TextRelief
+ if(!aRetval.empty())
+ {
+ // outline AND shadow depend on NO TextRelief (see dialog)
+ const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
+ const bool bHasShadow(!bHasTextRelief && getShadow());
+ const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());
+
+ if(bHasShadow || bHasTextRelief || bHasOutline)
+ {
+ Primitive2DReference aShadow;
+
+ if(bHasShadow)
+ {
+ // create shadow with current content (in aRetval). Text shadow
+ // is constant, relative to font size, rotated with the text and has a
+ // constant color.
+ // shadow parameter values
+ static const double fFactor(1.0 / 24.0);
+ const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
+ static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);
+
+ // prepare shadow transform matrix
+ const basegfx::B2DHomMatrix aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ fTextShadowOffset, fTextShadowOffset));
+
+ // create shadow primitive
+ aShadow = new ShadowPrimitive2D(
+ aShadowTransform,
+ aShadowColor,
+ 0, // fShadowBlur = 0, there's no blur for text shadow yet.
+ Primitive2DContainer(aRetval));
+ }
+
+ if(bHasTextRelief)
+ {
+ // create emboss using an own helper primitive since this will
+ // be view-dependent
+ const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
+ const bool bDefaultTextColor(aBBlack == getFontColor());
+ TextEffectStyle2D aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed);
+
+ if(bDefaultTextColor)
+ {
+ if(TEXT_RELIEF_ENGRAVED == getTextRelief())
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault;
+ }
+ else
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault;
+ }
+ }
+ else
+ {
+ if(TEXT_RELIEF_ENGRAVED == getTextRelief())
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEngraved;
+ }
+ else
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossed;
+ }
+ }
+
+ Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ std::move(aRetval),
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ aTextEffectStyle2D));
+ aRetval = Primitive2DContainer { aNewTextEffect };
+ }
+ else if(bHasOutline)
+ {
+ // create outline using an own helper primitive since this will
+ // be view-dependent
+ Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ std::move(aRetval),
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ TextEffectStyle2D::Outline));
+ aRetval = Primitive2DContainer { aNewTextEffect };
+ }
+
+ if(aShadow.is())
+ {
+ // put shadow in front if there is one to paint timely before
+ // but placed behind content
+ aRetval.insert(aRetval.begin(), aShadow);
+ }
+ }
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
+ // TextSimplePortionPrimitive2D parameters
+ const basegfx::B2DHomMatrix& rNewTransform,
+ const OUString& rText,
+ sal_Int32 nTextPosition,
+ sal_Int32 nTextLength,
+ std::vector< double >&& rDXArray,
+ const attribute::FontAttribute& rFontAttribute,
+ const css::lang::Locale& rLocale,
+ const basegfx::BColor& rFontColor,
+ const Color& rFillColor,
+
+ // local parameters
+ const basegfx::BColor& rOverlineColor,
+ const basegfx::BColor& rTextlineColor,
+ TextLine eFontOverline,
+ TextLine eFontUnderline,
+ bool bUnderlineAbove,
+ TextStrikeout eTextStrikeout,
+ bool bWordLineMode,
+ TextEmphasisMark eTextEmphasisMark,
+ bool bEmphasisMarkAbove,
+ bool bEmphasisMarkBelow,
+ TextRelief eTextRelief,
+ bool bShadow)
+ : TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, nTextLength, std::move(rDXArray), rFontAttribute, rLocale, rFontColor, false, 0, rFillColor),
+ maOverlineColor(rOverlineColor),
+ maTextlineColor(rTextlineColor),
+ meFontOverline(eFontOverline),
+ meFontUnderline(eFontUnderline),
+ meTextStrikeout(eTextStrikeout),
+ meTextEmphasisMark(eTextEmphasisMark),
+ meTextRelief(eTextRelief),
+ mbUnderlineAbove(bUnderlineAbove),
+ mbWordLineMode(bWordLineMode),
+ mbEmphasisMarkAbove(bEmphasisMarkAbove),
+ mbEmphasisMarkBelow(bEmphasisMarkBelow),
+ mbShadow(bShadow)
+ {
+ }
+
+ bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
+ {
+ const TextDecoratedPortionPrimitive2D& rCompare = static_cast<const TextDecoratedPortionPrimitive2D&>(rPrimitive);
+
+ return (getOverlineColor() == rCompare.getOverlineColor()
+ && getTextlineColor() == rCompare.getTextlineColor()
+ && getFontOverline() == rCompare.getFontOverline()
+ && getFontUnderline() == rCompare.getFontUnderline()
+ && getTextStrikeout() == rCompare.getTextStrikeout()
+ && getTextEmphasisMark() == rCompare.getTextEmphasisMark()
+ && getTextRelief() == rCompare.getTextRelief()
+ && getUnderlineAbove() == rCompare.getUnderlineAbove()
+ && getWordLineMode() == rCompare.getWordLineMode()
+ && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
+ && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
+ && getShadow() == rCompare.getShadow());
+ }
+
+ return false;
+ }
+
+ // #i96475#
+ // Added missing implementation. Decorations may (will) stick out of the text's
+ // inking area, so add them if needed
+ basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // check if this needs to be a TextDecoratedPortionPrimitive2D or
+ // if a TextSimplePortionPrimitive2D would be sufficient
+ if (TEXT_LINE_NONE != getFontOverline()
+ || TEXT_LINE_NONE != getFontUnderline()
+ || TEXT_STRIKEOUT_NONE != getTextStrikeout()
+ || TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark()
+ || TEXT_RELIEF_NONE != getTextRelief()
+ || getShadow())
+ {
+ // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
+ // the own local decomposition for computation and thus creates all necessary
+ // geometric objects
+ return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ else
+ {
+ // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
+ return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 TextDecoratedPortionPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx
new file mode 100644
index 000000000..dae397e1a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive2d/texteffectprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+namespace drawinglayer::primitive2d
+{
+const double fDiscreteSize(1.1);
+
+void TextEffectPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+{
+ // get the distance of one discrete units from target display. Use between 1.0 and sqrt(2) to
+ // have good results on rotated objects, too
+ const basegfx::B2DVector aDistance(rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(fDiscreteSize, fDiscreteSize));
+ const basegfx::B2DVector aDiagonalDistance(aDistance * (1.0 / 1.44));
+
+ switch (getTextEffectStyle2D())
+ {
+ case TextEffectStyle2D::ReliefEmbossed:
+ case TextEffectStyle2D::ReliefEngraved:
+ case TextEffectStyle2D::ReliefEmbossedDefault:
+ case TextEffectStyle2D::ReliefEngravedDefault:
+ {
+ // prepare transform of sub-group back to (0,0) and align to X-Axis
+ basegfx::B2DHomMatrix aBackTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -getRotationCenter().getX(), -getRotationCenter().getY()));
+ aBackTransform.rotate(-getDirection());
+
+ // prepare transform of sub-group back to its position and rotation
+ basegfx::B2DHomMatrix aForwardTransform(
+ basegfx::utils::createRotateB2DHomMatrix(getDirection()));
+ aForwardTransform.translate(getRotationCenter().getX(), getRotationCenter().getY());
+
+ // create transformation for one discrete unit
+ const bool bEmbossed(TextEffectStyle2D::ReliefEmbossed == getTextEffectStyle2D()
+ || TextEffectStyle2D::ReliefEmbossedDefault
+ == getTextEffectStyle2D());
+ const bool bDefaultTextColor(
+ TextEffectStyle2D::ReliefEmbossedDefault == getTextEffectStyle2D()
+ || TextEffectStyle2D::ReliefEngravedDefault == getTextEffectStyle2D());
+ basegfx::B2DHomMatrix aTransform(aBackTransform);
+
+ if (bEmbossed)
+ {
+ // to bottom-right
+ aTransform.translate(aDiagonalDistance.getX(), aDiagonalDistance.getY());
+ }
+ else
+ {
+ // to top-left
+ aTransform.translate(-aDiagonalDistance.getX(), -aDiagonalDistance.getY());
+ }
+
+ aTransform *= aForwardTransform;
+
+ if (bDefaultTextColor)
+ {
+ // emboss/engrave in black, original forced to white
+ const basegfx::BColorModifierSharedPtr aBColorModifierToGray
+ = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0.0));
+ const Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D(
+ Primitive2DContainer(getTextContent()), aBColorModifierToGray));
+
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer{ xModifiedColor }));
+
+ // add original, too
+ const basegfx::BColorModifierSharedPtr aBColorModifierToWhite
+ = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1.0));
+
+ rContainer.push_back(new ModifiedColorPrimitive2D(
+ Primitive2DContainer(getTextContent()), aBColorModifierToWhite));
+ }
+ else
+ {
+ // emboss/engrave in gray, keep original's color
+ const basegfx::BColorModifierSharedPtr aBColorModifierToGray
+ = std::make_shared<basegfx::BColorModifier_replace>(
+ basegfx::BColor(0.75)); // 192
+ const Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D(
+ Primitive2DContainer(getTextContent()), aBColorModifierToGray));
+
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer{ xModifiedColor }));
+
+ // add original, too
+ rContainer.push_back(new GroupPrimitive2D(Primitive2DContainer(getTextContent())));
+ }
+
+ break;
+ }
+ case TextEffectStyle2D::Outline:
+ {
+ // create transform primitives in all directions
+ basegfx::B2DHomMatrix aTransform;
+
+ aTransform.set(0, 2, aDistance.getX());
+ aTransform.set(1, 2, 0.0);
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, aDiagonalDistance.getX());
+ aTransform.set(1, 2, aDiagonalDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, 0.0);
+ aTransform.set(1, 2, aDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, -aDiagonalDistance.getX());
+ aTransform.set(1, 2, aDiagonalDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, -aDistance.getX());
+ aTransform.set(1, 2, 0.0);
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, -aDiagonalDistance.getX());
+ aTransform.set(1, 2, -aDiagonalDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, 0.0);
+ aTransform.set(1, 2, -aDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, aDiagonalDistance.getX());
+ aTransform.set(1, 2, -aDiagonalDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ // at last, place original over it, but force to white
+ const basegfx::BColorModifierSharedPtr aBColorModifierToWhite
+ = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1.0, 1.0, 1.0));
+ rContainer.push_back(new ModifiedColorPrimitive2D(
+ Primitive2DContainer(getTextContent()), aBColorModifierToWhite));
+
+ break;
+ }
+ }
+}
+
+TextEffectPrimitive2D::TextEffectPrimitive2D(Primitive2DContainer&& rTextContent,
+ const basegfx::B2DPoint& rRotationCenter,
+ double fDirection,
+ TextEffectStyle2D eTextEffectStyle2D)
+ : maTextContent(std::move(rTextContent))
+ , maRotationCenter(rRotationCenter)
+ , mfDirection(fDirection)
+ , meTextEffectStyle2D(eTextEffectStyle2D)
+{
+}
+
+bool TextEffectPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const TextEffectPrimitive2D& rCompare
+ = static_cast<const TextEffectPrimitive2D&>(rPrimitive);
+
+ return (getTextContent() == rCompare.getTextContent()
+ && getRotationCenter() == rCompare.getRotationCenter()
+ && getDirection() == rCompare.getDirection()
+ && getTextEffectStyle2D() == rCompare.getTextEffectStyle2D());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+TextEffectPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // get range of content and grow by used fDiscreteSize. That way it is not necessary to ask
+ // the whole decomposition for its ranges (which may be expensive with outline mode which
+ // then will ask 9 times at nearly the same content. This may even be refined here using the
+ // TextEffectStyle information, e.g. for TEXTEFFECTSTYLE2D_RELIEF the grow needs only to
+ // be in two directions
+ basegfx::B2DRange aRetval(getTextContent().getB2DRange(rViewInformation));
+ aRetval.grow(fDiscreteSize);
+
+ return aRetval;
+}
+
+void TextEffectPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (!getBuffered2DDecomposition().empty())
+ {
+ if (maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast<TextEffectPrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange and ViewTransformation
+ const_cast<TextEffectPrimitive2D*>(this)->maLastObjectToViewTransformation
+ = rViewInformation.getObjectToViewTransformation();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+// provide unique ID
+sal_uInt32 TextEffectPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_TEXTEFFECTPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textenumsprimitive2d.cxx b/drawinglayer/source/primitive2d/textenumsprimitive2d.cxx
new file mode 100644
index 000000000..7f13cbbbe
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textenumsprimitive2d.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/textenumsprimitive2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ TextLine mapFontLineStyleToTextLine(FontLineStyle eLineStyle)
+ {
+ switch(eLineStyle)
+ {
+ case LINESTYLE_SINGLE: return TEXT_LINE_SINGLE;
+ case LINESTYLE_DOUBLE: return TEXT_LINE_DOUBLE;
+ case LINESTYLE_DOTTED: return TEXT_LINE_DOTTED;
+ case LINESTYLE_DASH: return TEXT_LINE_DASH;
+ case LINESTYLE_LONGDASH: return TEXT_LINE_LONGDASH;
+ case LINESTYLE_DASHDOT: return TEXT_LINE_DASHDOT;
+ case LINESTYLE_DASHDOTDOT: return TEXT_LINE_DASHDOTDOT;
+ case LINESTYLE_SMALLWAVE: return TEXT_LINE_SMALLWAVE;
+ case LINESTYLE_WAVE: return TEXT_LINE_WAVE;
+ case LINESTYLE_DOUBLEWAVE: return TEXT_LINE_DOUBLEWAVE;
+ case LINESTYLE_BOLD: return TEXT_LINE_BOLD;
+ case LINESTYLE_BOLDDOTTED: return TEXT_LINE_BOLDDOTTED;
+ case LINESTYLE_BOLDDASH: return TEXT_LINE_BOLDDASH;
+ case LINESTYLE_BOLDLONGDASH: return TEXT_LINE_BOLDLONGDASH;
+ case LINESTYLE_BOLDDASHDOT: return TEXT_LINE_BOLDDASHDOT;
+ case LINESTYLE_BOLDDASHDOTDOT: return TEXT_LINE_BOLDDASHDOTDOT;
+ case LINESTYLE_BOLDWAVE: return TEXT_LINE_BOLDWAVE;
+ // FontLineStyle_FORCE_EQUAL_SIZE, LINESTYLE_DONTKNOW, LINESTYLE_NONE
+ default: return TEXT_LINE_NONE;
+ }
+ }
+
+ FontLineStyle mapTextLineToFontLineStyle(TextLine eLineStyle)
+ {
+ switch(eLineStyle)
+ {
+ default: /*TEXT_LINE_NONE*/ return LINESTYLE_NONE;
+ case TEXT_LINE_SINGLE: return LINESTYLE_SINGLE;
+ case TEXT_LINE_DOUBLE: return LINESTYLE_DOUBLE;
+ case TEXT_LINE_DOTTED: return LINESTYLE_DOTTED;
+ case TEXT_LINE_DASH: return LINESTYLE_DASH;
+ case TEXT_LINE_LONGDASH: return LINESTYLE_LONGDASH;
+ case TEXT_LINE_DASHDOT: return LINESTYLE_DASHDOT;
+ case TEXT_LINE_DASHDOTDOT: return LINESTYLE_DASHDOTDOT;
+ case TEXT_LINE_SMALLWAVE: return LINESTYLE_SMALLWAVE;
+ case TEXT_LINE_WAVE: return LINESTYLE_WAVE;
+ case TEXT_LINE_DOUBLEWAVE: return LINESTYLE_DOUBLEWAVE;
+ case TEXT_LINE_BOLD: return LINESTYLE_BOLD;
+ case TEXT_LINE_BOLDDOTTED: return LINESTYLE_BOLDDOTTED;
+ case TEXT_LINE_BOLDDASH: return LINESTYLE_BOLDDASH;
+ case TEXT_LINE_BOLDLONGDASH: return LINESTYLE_LONGDASH;
+ case TEXT_LINE_BOLDDASHDOT: return LINESTYLE_BOLDDASHDOT;
+ case TEXT_LINE_BOLDDASHDOTDOT:return LINESTYLE_BOLDDASHDOTDOT;
+ case TEXT_LINE_BOLDWAVE: return LINESTYLE_BOLDWAVE;
+ }
+ }
+
+ TextStrikeout mapFontStrikeoutToTextStrikeout(FontStrikeout eFontStrikeout)
+ {
+ switch(eFontStrikeout)
+ {
+ case STRIKEOUT_SINGLE: return TEXT_STRIKEOUT_SINGLE;
+ case STRIKEOUT_DOUBLE: return TEXT_STRIKEOUT_DOUBLE;
+ case STRIKEOUT_BOLD: return TEXT_STRIKEOUT_BOLD;
+ case STRIKEOUT_SLASH: return TEXT_STRIKEOUT_SLASH;
+ case STRIKEOUT_X: return TEXT_STRIKEOUT_X;
+ // FontStrikeout_FORCE_EQUAL_SIZE, STRIKEOUT_NONE, STRIKEOUT_DONTKNOW
+ default: return TEXT_STRIKEOUT_NONE;
+ }
+ }
+
+ FontStrikeout mapTextStrikeoutToFontStrikeout(TextStrikeout eTextStrikeout)
+ {
+ switch(eTextStrikeout)
+ {
+ default: /*case primitive2d::TEXT_STRIKEOUT_NONE*/ return STRIKEOUT_NONE;
+ case TEXT_STRIKEOUT_SINGLE: return STRIKEOUT_SINGLE;
+ case TEXT_STRIKEOUT_DOUBLE: return STRIKEOUT_DOUBLE;
+ case TEXT_STRIKEOUT_BOLD: return STRIKEOUT_BOLD;
+ case TEXT_STRIKEOUT_SLASH: return STRIKEOUT_SLASH;
+ case TEXT_STRIKEOUT_X: return STRIKEOUT_X;
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
new file mode 100644
index 000000000..c7af9562a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ TextHierarchyLinePrimitive2D::TextHierarchyLinePrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyLinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D;
+ }
+
+
+ TextHierarchyParagraphPrimitive2D::TextHierarchyParagraphPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ sal_Int16 nOutlineLevel)
+ : GroupPrimitive2D(std::move(aChildren)),
+ mnOutlineLevel(nOutlineLevel)
+ {
+ }
+
+ bool TextHierarchyParagraphPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TextHierarchyParagraphPrimitive2D& rCompare = static_cast<const TextHierarchyParagraphPrimitive2D&>(rPrimitive);
+
+ return (getOutlineLevel() == rCompare.getOutlineLevel());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyParagraphPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D;
+ }
+
+
+
+ TextHierarchyBulletPrimitive2D::TextHierarchyBulletPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyBulletPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D;
+ }
+
+
+ TextHierarchyBlockPrimitive2D::TextHierarchyBlockPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyBlockPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D;
+ }
+
+
+ TextHierarchyFieldPrimitive2D::TextHierarchyFieldPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const FieldType& rFieldType,
+ const std::vector< std::pair< OUString, OUString>>* pNameValue)
+ : GroupPrimitive2D(std::move(aChildren)),
+ meType(rFieldType)
+ {
+ if (nullptr != pNameValue)
+ {
+ meNameValue = *pNameValue;
+ }
+ }
+
+ OUString TextHierarchyFieldPrimitive2D::getValue(const OUString& rName) const
+ {
+ for (const std::pair< OUString, OUString >& candidate : meNameValue)
+ {
+ if (candidate.first.equals(rName))
+ {
+ return candidate.second;
+ }
+ }
+
+ return OUString();
+ }
+
+ bool TextHierarchyFieldPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TextHierarchyFieldPrimitive2D& rCompare = static_cast<const TextHierarchyFieldPrimitive2D&>(rPrimitive);
+
+ return (getType() == rCompare.getType()
+ && meNameValue == rCompare.meNameValue);
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyFieldPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D;
+ }
+
+
+ TextHierarchyEditPrimitive2D::TextHierarchyEditPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyEditPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
new file mode 100644
index 000000000..f70f9f63b
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
@@ -0,0 +1,456 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/unique_disposing_ptr.hxx>
+#include <osl/diagnose.h>
+#include <tools/gen.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/font.hxx>
+#include <vcl/metric.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/svapp.hxx>
+
+namespace drawinglayer::primitive2d
+{
+namespace
+{
+class ImpTimedRefDev;
+
+// VDev RevDevice provider
+
+//the scoped_timed_RefDev owns an ImpTimeRefDev and releases it on dtor
+//or disposing of the default XComponentContext which causes the underlying
+//OutputDevice to get released
+
+//The ImpTimerRefDev itself, if the timeout ever gets hit, will call
+//reset on the scoped_timed_RefDev to release the ImpTimerRefDev early
+//if it's unused for a few minutes
+class scoped_timed_RefDev : public comphelper::unique_disposing_ptr<ImpTimedRefDev>
+{
+public:
+ scoped_timed_RefDev()
+ : comphelper::unique_disposing_ptr<ImpTimedRefDev>(
+ (css::uno::Reference<css::lang::XComponent>(
+ ::comphelper::getProcessComponentContext(), css::uno::UNO_QUERY_THROW)))
+ {
+ }
+};
+
+class the_scoped_timed_RefDev : public rtl::Static<scoped_timed_RefDev, the_scoped_timed_RefDev>
+{
+};
+
+class ImpTimedRefDev : public Timer
+{
+ scoped_timed_RefDev& mrOwnerOfMe;
+ VclPtr<VirtualDevice> mpVirDev;
+ sal_uInt32 mnUseCount;
+
+public:
+ explicit ImpTimedRefDev(scoped_timed_RefDev& rOwnerofMe);
+ virtual ~ImpTimedRefDev() override;
+ virtual void Invoke() override;
+
+ VirtualDevice& acquireVirtualDevice();
+ void releaseVirtualDevice();
+};
+
+ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev& rOwnerOfMe)
+ : Timer("drawinglayer ImpTimedRefDev destroy mpVirDev")
+ , mrOwnerOfMe(rOwnerOfMe)
+ , mpVirDev(nullptr)
+ , mnUseCount(0)
+{
+ SetTimeout(3L * 60L * 1000L); // three minutes
+ Start();
+}
+
+ImpTimedRefDev::~ImpTimedRefDev()
+{
+ OSL_ENSURE(0 == mnUseCount, "destruction of a still used ImpTimedRefDev (!)");
+ const SolarMutexGuard aSolarGuard;
+ mpVirDev.disposeAndClear();
+}
+
+void ImpTimedRefDev::Invoke()
+{
+ // for obvious reasons, do not call anything after this
+ mrOwnerOfMe.reset();
+}
+
+VirtualDevice& ImpTimedRefDev::acquireVirtualDevice()
+{
+ if (!mpVirDev)
+ {
+ mpVirDev = VclPtr<VirtualDevice>::Create();
+ mpVirDev->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1);
+ }
+
+ if (!mnUseCount)
+ {
+ Stop();
+ }
+
+ mnUseCount++;
+
+ return *mpVirDev;
+}
+
+void ImpTimedRefDev::releaseVirtualDevice()
+{
+ OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)");
+ mnUseCount--;
+
+ if (!mnUseCount)
+ {
+ Start();
+ }
+}
+
+VirtualDevice& acquireGlobalVirtualDevice()
+{
+ scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get();
+
+ if (!rStdRefDevice)
+ rStdRefDevice.reset(new ImpTimedRefDev(rStdRefDevice));
+
+ return rStdRefDevice->acquireVirtualDevice();
+}
+
+void releaseGlobalVirtualDevice()
+{
+ scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get();
+
+ OSL_ENSURE(rStdRefDevice,
+ "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)");
+ rStdRefDevice->releaseVirtualDevice();
+}
+
+} // end of anonymous namespace
+
+TextLayouterDevice::TextLayouterDevice()
+ : mrDevice(acquireGlobalVirtualDevice())
+{
+}
+
+TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE { releaseGlobalVirtualDevice(); }
+
+void TextLayouterDevice::setFont(const vcl::Font& rFont) { mrDevice.SetFont(rFont); }
+
+void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontAttribute,
+ double fFontScaleX, double fFontScaleY,
+ const css::lang::Locale& rLocale)
+{
+ setFont(getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale));
+}
+
+double TextLayouterDevice::getOverlineOffset() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = (rMetric.GetInternalLeading() / 2.0) - rMetric.GetAscent();
+ return fRet;
+}
+
+double TextLayouterDevice::getUnderlineOffset() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = rMetric.GetDescent() / 2.0;
+ return fRet;
+}
+
+double TextLayouterDevice::getStrikeoutOffset() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = (rMetric.GetAscent() - rMetric.GetInternalLeading()) / 3.0;
+ return fRet;
+}
+
+double TextLayouterDevice::getOverlineHeight() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = rMetric.GetInternalLeading() / 2.5;
+ return fRet;
+}
+
+double TextLayouterDevice::getUnderlineHeight() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = rMetric.GetDescent() / 4.0;
+ return fRet;
+}
+
+double TextLayouterDevice::getTextHeight() const { return mrDevice.GetTextHeight(); }
+
+double TextLayouterDevice::getTextWidth(const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength) const
+{
+ return mrDevice.GetTextWidth(rText, nIndex, nLength);
+}
+
+void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
+ const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength,
+ const std::vector<double>& rDXArray) const
+{
+ const sal_uInt32 nDXArrayCount(rDXArray.size());
+ sal_uInt32 nTextLength(nLength);
+ const sal_uInt32 nStringLength(rText.getLength());
+
+ if (nTextLength + nIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nIndex;
+ }
+
+ if (nDXArrayCount)
+ {
+ OSL_ENSURE(nDXArrayCount == nTextLength,
+ "DXArray size does not correspond to text portion size (!)");
+ std::vector<sal_Int32> aIntegerDXArray(nDXArrayCount);
+
+ for (sal_uInt32 a(0); a < nDXArrayCount; a++)
+ {
+ aIntegerDXArray[a] = basegfx::fround(rDXArray[a]);
+ }
+
+ mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength, 0,
+ aIntegerDXArray);
+ }
+ else
+ {
+ mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength);
+ }
+}
+
+basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength) const
+{
+ sal_uInt32 nTextLength(nLength);
+ const sal_uInt32 nStringLength(rText.getLength());
+
+ if (nTextLength + nIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nIndex;
+ }
+
+ if (nTextLength)
+ {
+ ::tools::Rectangle aRect;
+
+ mrDevice.GetTextBoundRect(aRect, rText, nIndex, nIndex, nLength);
+
+ // #i104432#, #i102556# take empty results into account
+ if (!aRect.IsEmpty())
+ {
+ return vcl::unotools::b2DRectangleFromRectangle(aRect);
+ }
+ }
+
+ return basegfx::B2DRange();
+}
+
+double TextLayouterDevice::getFontAscent() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ return rMetric.GetAscent();
+}
+
+double TextLayouterDevice::getFontDescent() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ return rMetric.GetDescent();
+}
+
+void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle& rRectangle,
+ const OUString& rText, DrawTextFlags nStyle,
+ GDIMetaFile& rGDIMetaFile) const
+{
+ mrDevice.AddTextRectActions(rRectangle, rText, nStyle, rGDIMetaFile);
+}
+
+std::vector<double> TextLayouterDevice::getTextArray(const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength) const
+{
+ std::vector<double> aRetval;
+ sal_uInt32 nTextLength(nLength);
+ const sal_uInt32 nStringLength(rText.getLength());
+
+ if (nTextLength + nIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nIndex;
+ }
+
+ if (nTextLength)
+ {
+ aRetval.reserve(nTextLength);
+ std::vector<sal_Int32> aArray(nTextLength);
+ mrDevice.GetTextArray(rText, &aArray, nIndex, nTextLength);
+ aRetval.assign(aArray.begin(), aArray.end());
+ }
+
+ return aRetval;
+}
+
+std::vector<double> TextLayouterDevice::getCaretPositions(const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength) const
+{
+ std::vector<double> aRetval;
+ sal_uInt32 nTextLength(nLength);
+ const sal_uInt32 nStringLength(rText.getLength());
+
+ if (nTextLength + nIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nIndex;
+ }
+
+ if (nTextLength)
+ {
+ aRetval.reserve(2 * nTextLength);
+ std::vector<sal_Int32> aArray(2 * nTextLength);
+ mrDevice.GetCaretPositions(rText, aArray.data(), nIndex, nTextLength);
+ aRetval.assign(aArray.begin(), aArray.end());
+ }
+
+ return aRetval;
+}
+
+// helper methods for vcl font handling
+
+vcl::Font getVclFontFromFontAttribute(const attribute::FontAttribute& rFontAttribute,
+ double fFontScaleX, double fFontScaleY, double fFontRotation,
+ const css::lang::Locale& rLocale)
+{
+ // detect FontScaling
+ const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY)));
+ const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX)));
+ const bool bFontIsScaled(nHeight != nWidth);
+
+#ifdef _WIN32
+ // for WIN32 systems, start with creating an unscaled font. If FontScaling
+ // is wanted, that width needs to be adapted using FontMetric again to get a
+ // width of the unscaled font
+ vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(),
+ Size(0, nHeight));
+#else
+ // for non-WIN32 systems things are easier since these accept a Font creation
+ // with initially nWidth != nHeight for FontScaling. Despite that, use zero for
+ // FontWidth when no scaling is used to explicitly have that zero when e.g. the
+ // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a
+ // set FontWidth; import that in a WIN32 system, and trouble is there)
+ vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(),
+ Size(bFontIsScaled ? std::max<sal_uInt32>(nWidth, 1) : 0, nHeight));
+#endif
+ // define various other FontAttribute
+ aRetval.SetAlignment(ALIGN_BASELINE);
+ aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL
+ : RTL_TEXTENCODING_UNICODE);
+ aRetval.SetVertical(rFontAttribute.getVertical());
+ aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight()));
+ aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE);
+ aRetval.SetOutline(rFontAttribute.getOutline());
+ aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE);
+ aRetval.SetLanguage(LanguageTag::convertToLanguageType(rLocale, false));
+
+#ifdef _WIN32
+ // for WIN32 systems, correct the FontWidth if FontScaling is used
+ if (bFontIsScaled && nHeight > 0)
+ {
+ const FontMetric aUnscaledFontMetric(
+ Application::GetDefaultDevice()->GetFontMetric(aRetval));
+
+ if (aUnscaledFontMetric.GetAverageFontWidth() > 0)
+ {
+ const double fScaleFactor(static_cast<double>(nWidth) / static_cast<double>(nHeight));
+ const sal_uInt32 nScaledWidth(basegfx::fround(
+ static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()) * fScaleFactor));
+ aRetval.SetAverageFontWidth(nScaledWidth);
+ }
+ }
+#endif
+ // handle FontRotation (if defined)
+ if (!basegfx::fTools::equalZero(fFontRotation))
+ {
+ int aRotate10th(-basegfx::rad2deg<10>(fFontRotation));
+ aRetval.SetOrientation(Degree10(aRotate10th % 3600));
+ }
+
+ return aRetval;
+}
+
+attribute::FontAttribute getFontAttributeFromVclFont(basegfx::B2DVector& o_rSize,
+ const vcl::Font& rFont, bool bRTL,
+ bool bBiDiStrong)
+{
+ const attribute::FontAttribute aRetval(
+ rFont.GetFamilyName(), rFont.GetStyleName(), static_cast<sal_uInt16>(rFont.GetWeight()),
+ RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(), rFont.IsVertical(),
+ ITALIC_NONE != rFont.GetItalic(), PITCH_FIXED == rFont.GetPitch(), rFont.IsOutline(), bRTL,
+ bBiDiStrong);
+ // TODO: eKerning
+
+ // set FontHeight and init to no FontScaling
+ o_rSize.setY(std::max<tools::Long>(rFont.GetFontSize().getHeight(), 0));
+ o_rSize.setX(o_rSize.getY());
+
+#ifdef _WIN32
+ // for WIN32 systems, the FontScaling at the Font is detected by
+ // checking that FontWidth != 0. When FontScaling is used, WIN32
+ // needs to do extra stuff to detect the correct width (since it's
+ // zero and not equal the font height) and its relationship to
+ // the height
+ if (rFont.GetFontSize().getWidth() > 0)
+ {
+ vcl::Font aUnscaledFont(rFont);
+ aUnscaledFont.SetAverageFontWidth(0);
+ const FontMetric aUnscaledFontMetric(
+ Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont));
+
+ if (aUnscaledFontMetric.GetAverageFontWidth() > 0)
+ {
+ const double fScaleFactor(
+ static_cast<double>(rFont.GetFontSize().getWidth())
+ / static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()));
+ o_rSize.setX(fScaleFactor * o_rSize.getY());
+ }
+ }
+#else
+ // For non-WIN32 systems the detection is the same, but the value
+ // is easier achieved since width == height is interpreted as no
+ // scaling. Ergo, Width == 0 means width == height, and width != 0
+ // means the scaling is in the direct relation of width to height
+ if (rFont.GetFontSize().getWidth() > 0)
+ {
+ o_rSize.setX(static_cast<double>(rFont.GetFontSize().getWidth()));
+ }
+#endif
+ return aRetval;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textlineprimitive2d.cxx b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx
new file mode 100644
index 000000000..461a83d75
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive2d/textlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void TextLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(TEXT_LINE_NONE == getTextLine())
+ return;
+
+ bool bDoubleLine(false);
+ bool bWaveLine(false);
+ bool bBoldLine(false);
+ const int* pDotDashArray(nullptr);
+ basegfx::B2DLineJoin eLineJoin(basegfx::B2DLineJoin::NONE);
+ double fOffset(getOffset());
+ double fHeight(getHeight());
+
+ static const int aDottedArray[] = { 1, 1, 0}; // DOTTED LINE
+ static const int aDotDashArray[] = { 1, 1, 4, 1, 0}; // DASHDOT
+ static const int aDashDotDotArray[] = { 1, 1, 1, 1, 4, 1, 0}; // DASHDOTDOT
+ static const int aDashedArray[] = { 5, 2, 0}; // DASHED LINE
+ static const int aLongDashArray[] = { 7, 2, 0}; // LONGDASH
+
+ // get decomposition
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getObjectTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ switch(getTextLine())
+ {
+ default: // case TEXT_LINE_SINGLE:
+ {
+ break;
+ }
+ case TEXT_LINE_DOUBLE:
+ {
+ bDoubleLine = true;
+ break;
+ }
+ case TEXT_LINE_DOTTED:
+ {
+ pDotDashArray = aDottedArray;
+ break;
+ }
+ case TEXT_LINE_DASH:
+ {
+ pDotDashArray = aDashedArray;
+ break;
+ }
+ case TEXT_LINE_LONGDASH:
+ {
+ pDotDashArray = aLongDashArray;
+ break;
+ }
+ case TEXT_LINE_DASHDOT:
+ {
+ pDotDashArray = aDotDashArray;
+ break;
+ }
+ case TEXT_LINE_DASHDOTDOT:
+ {
+ pDotDashArray = aDashDotDotArray;
+ break;
+ }
+ case TEXT_LINE_SMALLWAVE:
+ {
+ bWaveLine = true;
+ break;
+ }
+ case TEXT_LINE_WAVE:
+ {
+ bWaveLine = true;
+ break;
+ }
+ case TEXT_LINE_DOUBLEWAVE:
+ {
+ bDoubleLine = true;
+ bWaveLine = true;
+ break;
+ }
+ case TEXT_LINE_BOLD:
+ {
+ bBoldLine = true;
+ break;
+ }
+ case TEXT_LINE_BOLDDOTTED:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDottedArray;
+ break;
+ }
+ case TEXT_LINE_BOLDDASH:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDashedArray;
+ break;
+ }
+ case TEXT_LINE_BOLDLONGDASH:
+ {
+ bBoldLine = true;
+ pDotDashArray = aLongDashArray;
+ break;
+ }
+ case TEXT_LINE_BOLDDASHDOT:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDotDashArray;
+ break;
+ }
+ case TEXT_LINE_BOLDDASHDOTDOT:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDashDotDotArray;
+ break;
+ }
+ case TEXT_LINE_BOLDWAVE:
+ {
+ bWaveLine = true;
+ bBoldLine = true;
+ break;
+ }
+ }
+
+ if(bBoldLine)
+ {
+ fHeight *= 2.0;
+ }
+
+ if(bDoubleLine)
+ {
+ fOffset -= 0.50 * fHeight;
+ fHeight *= 0.64;
+ }
+
+ if(bWaveLine)
+ {
+ eLineJoin = basegfx::B2DLineJoin::Round;
+ fHeight *= 0.25;
+ }
+
+ // prepare Line and Stroke Attributes
+ const attribute::LineAttribute aLineAttribute(getLineColor(), fHeight, eLineJoin);
+ attribute::StrokeAttribute aStrokeAttribute;
+
+ if(pDotDashArray)
+ {
+ std::vector< double > aDoubleArray;
+
+ for(const int* p = pDotDashArray; *p; ++p)
+ {
+ aDoubleArray.push_back(static_cast<double>(*p) * fHeight);
+ }
+
+ aStrokeAttribute = attribute::StrokeAttribute(std::move(aDoubleArray));
+ }
+
+ // create base polygon and new primitive
+ basegfx::B2DPolygon aLine;
+ Primitive2DReference aNewPrimitive;
+
+ aLine.append(basegfx::B2DPoint(0.0, fOffset));
+ aLine.append(basegfx::B2DPoint(getWidth(), fOffset));
+
+ const basegfx::B2DHomMatrix aUnscaledTransform(
+ basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate));
+
+ aLine.transform(aUnscaledTransform);
+
+ if(bWaveLine)
+ {
+ double fWaveWidth(10.6 * fHeight);
+
+ if(TEXT_LINE_SMALLWAVE == getTextLine())
+ {
+ fWaveWidth *= 0.7;
+ }
+ else if(TEXT_LINE_WAVE == getTextLine())
+ {
+ // extra multiply to get the same WaveWidth as with the bold version
+ fWaveWidth *= 2.0;
+ }
+
+ aNewPrimitive = Primitive2DReference(new PolygonWavePrimitive2D(aLine, aLineAttribute, aStrokeAttribute, fWaveWidth, fWaveWidth * 0.5));
+ }
+ else
+ {
+ aNewPrimitive = Primitive2DReference(new PolygonStrokePrimitive2D(aLine, aLineAttribute, aStrokeAttribute));
+ }
+
+ // add primitive
+ rContainer.push_back(aNewPrimitive);
+
+ if(!bDoubleLine)
+ return;
+
+ // double line, create 2nd primitive with offset using TransformPrimitive based on
+ // already created NewPrimitive
+ double fLineDist(2.3 * fHeight);
+
+ if(bWaveLine)
+ {
+ fLineDist = 6.3 * fHeight;
+ }
+
+ // move base point of text to 0.0 and de-rotate
+ basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -aTranslate.getX(), -aTranslate.getY()));
+ aTransform.rotate(-fRotate);
+
+ // translate in Y by offset
+ aTransform.translate(0.0, fLineDist);
+
+ // move back and rotate
+ aTransform.rotate(fRotate);
+ aTransform.translate(aTranslate.getX(), aTranslate.getY());
+
+ // add transform primitive
+ Primitive2DContainer aContent { aNewPrimitive };
+ rContainer.push_back( new TransformPrimitive2D(aTransform, std::move(aContent)) );
+ }
+
+ TextLinePrimitive2D::TextLinePrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ double fOffset,
+ double fHeight,
+ TextLine eTextLine,
+ const basegfx::BColor& rLineColor)
+ : maObjectTransformation(rObjectTransformation),
+ mfWidth(fWidth),
+ mfOffset(fOffset),
+ mfHeight(fHeight),
+ meTextLine(eTextLine),
+ maLineColor(rLineColor)
+ {
+ }
+
+ bool TextLinePrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const TextLinePrimitive2D& rCompare = static_cast<const TextLinePrimitive2D&>(rPrimitive);
+
+ return (getObjectTransformation() == rCompare.getObjectTransformation()
+ && getWidth() == rCompare.getWidth()
+ && getOffset() == rCompare.getOffset()
+ && getHeight() == rCompare.getHeight()
+ && getTextLine() == rCompare.getTextLine()
+ && getLineColor() == rCompare.getLineColor());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextLinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx b/drawinglayer/source/primitive2d/textprimitive2d.cxx
new file mode 100644
index 000000000..00135a3aa
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <primitive2d/texteffectprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+namespace
+{
+// adapts fontScale for usage with TextLayouter. Input is rScale which is the extracted
+// scale from a text transformation. A copy is modified so that it contains only positive
+// scalings and XY-equal scalings to allow to get a non-X-scaled Vcl-Font for TextLayouter.
+// rScale is adapted accordingly to contain the corrected scale which would need to be
+// applied to e.g. outlines received from TextLayouter under usage of fontScale. This
+// includes Y-Scale, X-Scale-correction and mirrorings.
+basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale)
+{
+ // copy input value
+ basegfx::B2DVector aFontScale(rScale);
+
+ // correct FontHeight settings
+ if (basegfx::fTools::equalZero(aFontScale.getY()))
+ {
+ // no font height; choose one and adapt scale to get back to original scaling
+ static const double fDefaultFontScale(100.0);
+ rScale.setY(1.0 / fDefaultFontScale);
+ aFontScale.setY(fDefaultFontScale);
+ }
+ else if (basegfx::fTools::less(aFontScale.getY(), 0.0))
+ {
+ // negative font height; invert and adapt scale to get back to original scaling
+ aFontScale.setY(-aFontScale.getY());
+ rScale.setY(-1.0);
+ }
+ else
+ {
+ // positive font height; adapt scale; scaling will be part of the polygons
+ rScale.setY(1.0);
+ }
+
+ // correct FontWidth settings
+ if (basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY()))
+ {
+ // no FontScale, adapt scale
+ rScale.setX(1.0);
+ }
+ else
+ {
+ // If FontScale is used, force to no FontScale to get a non-scaled VCL font.
+ // Adapt scaling in X accordingly.
+ rScale.setX(aFontScale.getX() / aFontScale.getY());
+ aFontScale.setX(aFontScale.getY());
+ }
+
+ return aFontScale;
+}
+} // end of anonymous namespace
+
+void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(
+ basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const
+{
+ if (!getTextLength())
+ return;
+
+ // decompose object transformation to single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ // if decomposition returns false, create no geometry since e.g. scaling may
+ // be zero
+ if (!(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX)
+ && aScale.getX() != 0.0))
+ return;
+
+ // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
+ // be expressed as rotation by PI
+ if (basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
+ {
+ aScale = basegfx::absolute(aScale);
+ fRotate += M_PI;
+ }
+
+ // for the TextLayouterDevice, it is necessary to have a scaling representing
+ // the font size. Since we want to extract polygons here, it is okay to
+ // work just with scaling and to ignore shear, rotation and translation,
+ // all that can be applied to the polygons later
+ const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
+
+ // prepare textlayoutdevice
+ TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(),
+ getLocale());
+
+ // When getting outlines from stretched text (aScale.getX() != 1.0) it
+ // is necessary to inverse-scale the DXArray (if used) to not get the
+ // outlines already aligned to given, but wrong DXArray
+ if (!getDXArray().empty() && !basegfx::fTools::equal(aScale.getX(), 1.0))
+ {
+ std::vector<double> aScaledDXArray = getDXArray();
+ const double fDXArrayScale(1.0 / aScale.getX());
+
+ for (double& a : aScaledDXArray)
+ {
+ a *= fDXArrayScale;
+ }
+
+ // get the text outlines
+ aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(),
+ aScaledDXArray);
+ }
+ else
+ {
+ // get the text outlines
+ aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(),
+ getDXArray());
+ }
+
+ // create primitives for the outlines
+ const sal_uInt32 nCount(rTarget.size());
+
+ if (nCount)
+ {
+ // prepare object transformation for polygons
+ rTransformation = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, fShearX, fRotate, aTranslate);
+ }
+}
+
+void TextSimplePortionPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getTextLength())
+ return;
+
+ Primitive2DContainer aRetval;
+ basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
+ basegfx::B2DHomMatrix aPolygonTransform;
+
+ // get text outlines and their object transformation
+ getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
+
+ // create primitives for the outlines
+ const sal_uInt32 nCount(aB2DPolyPolyVector.size());
+
+ if (!nCount)
+ return;
+
+ // alloc space for the primitives
+ aRetval.resize(nCount);
+
+ // color-filled polypolygons
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ // prepare polypolygon
+ basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a];
+ rPolyPolygon.transform(aPolygonTransform);
+ aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor());
+ }
+
+ if (getFontAttribute().getOutline())
+ {
+ // decompose polygon transformation to single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aPolygonTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // create outline text effect with current content and replace
+ Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ std::move(aRetval), aTranslate, fRotate, TextEffectStyle2D::Outline));
+
+ aRetval = Primitive2DContainer{ aNewTextEffect };
+ }
+
+ rContainer.append(std::move(aRetval));
+}
+
+TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
+ const basegfx::B2DHomMatrix& rNewTransform, const OUString& rText, sal_Int32 nTextPosition,
+ sal_Int32 nTextLength, std::vector<double>&& rDXArray,
+ const attribute::FontAttribute& rFontAttribute, const css::lang::Locale& rLocale,
+ const basegfx::BColor& rFontColor, bool bFilled, tools::Long nWidthToFill,
+ const Color& rTextFillColor)
+ : maTextTransform(rNewTransform)
+ , maText(rText)
+ , mnTextPosition(nTextPosition)
+ , mnTextLength(nTextLength)
+ , maDXArray(std::move(rDXArray))
+ , maFontAttribute(rFontAttribute)
+ , maLocale(rLocale)
+ , maFontColor(rFontColor)
+ , mbFilled(bFilled)
+ , mnWidthToFill(nWidthToFill)
+ , maTextFillColor(rTextFillColor)
+{
+#if OSL_DEBUG_LEVEL > 0
+ const sal_Int32 aStringLength(getText().getLength());
+ OSL_ENSURE(aStringLength >= getTextPosition()
+ && aStringLength >= getTextPosition() + getTextLength(),
+ "TextSimplePortionPrimitive2D with text out of range (!)");
+#endif
+}
+
+bool LocalesAreEqual(const css::lang::Locale& rA, const css::lang::Locale& rB)
+{
+ return (rA.Language == rB.Language && rA.Country == rB.Country && rA.Variant == rB.Variant);
+}
+
+bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const TextSimplePortionPrimitive2D& rCompare
+ = static_cast<const TextSimplePortionPrimitive2D&>(rPrimitive);
+
+ return (getTextTransform() == rCompare.getTextTransform() && getText() == rCompare.getText()
+ && getTextPosition() == rCompare.getTextPosition()
+ && getTextLength() == rCompare.getTextLength()
+ && getDXArray() == rCompare.getDXArray()
+ && getFontAttribute() == rCompare.getFontAttribute()
+ && LocalesAreEqual(getLocale(), rCompare.getLocale())
+ && getFontColor() == rCompare.getFontColor() && mbFilled == rCompare.mbFilled
+ && mnWidthToFill == rCompare.mnWidthToFill
+ && maTextFillColor == rCompare.maTextFillColor);
+ }
+
+ return false;
+}
+
+basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (maB2DRange.isEmpty() && getTextLength())
+ {
+ // get TextBoundRect as base size
+ // decompose object transformation to single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ if (getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
+ {
+ // for the TextLayouterDevice, it is necessary to have a scaling representing
+ // the font size. Since we want to extract polygons here, it is okay to
+ // work just with scaling and to ignore shear, rotation and translation,
+ // all that can be applied to the polygons later
+ const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
+
+ // prepare textlayoutdevice
+ TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(),
+ getLocale());
+
+ // get basic text range
+ basegfx::B2DRange aNewRange(
+ aTextLayouter.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
+
+ // #i104432#, #i102556# take empty results into account
+ if (!aNewRange.isEmpty())
+ {
+ // prepare object transformation for range
+ const basegfx::B2DHomMatrix aRangeTransformation(
+ basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, fShearX, fRotate, aTranslate));
+
+ // apply range transformation to it
+ aNewRange.transform(aRangeTransformation);
+
+ // assign to buffered value
+ const_cast<TextSimplePortionPrimitive2D*>(this)->maB2DRange = aNewRange;
+ }
+ }
+ }
+
+ return maB2DRange;
+}
+
+// provide unique ID
+sal_uInt32 TextSimplePortionPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
new file mode 100644
index 000000000..ecd66b7a8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive2d/textstrikeoutprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ BaseTextStrikeoutPrimitive2D::BaseTextStrikeoutPrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ const basegfx::BColor& rFontColor)
+ : maObjectTransformation(rObjectTransformation),
+ mfWidth(fWidth),
+ maFontColor(rFontColor)
+ {
+ }
+
+ bool BaseTextStrikeoutPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const BaseTextStrikeoutPrimitive2D& rCompare = static_cast<const BaseTextStrikeoutPrimitive2D&>(rPrimitive);
+
+ return (getObjectTransformation() == rCompare.getObjectTransformation()
+ && getWidth() == rCompare.getWidth()
+ && getFontColor() == rCompare.getFontColor());
+ }
+
+ return false;
+ }
+
+
+ void TextCharacterStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // strikeout with character
+ const OUString aSingleCharString(getStrikeoutChar());
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ // get decomposition
+ getObjectTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // prepare TextLayouter
+ TextLayouterDevice aTextLayouter;
+
+ aTextLayouter.setFontAttribute(
+ getFontAttribute(),
+ aScale.getX(),
+ aScale.getY(),
+ getLocale());
+
+ const double fStrikeCharWidth(aTextLayouter.getTextWidth(aSingleCharString, 0, 1));
+ const double fStrikeCharCount(fabs(getWidth()/fStrikeCharWidth));
+ const sal_uInt32 nStrikeCharCount(static_cast< sal_uInt32 >(fStrikeCharCount + 0.5));
+ std::vector<double> aDXArray(nStrikeCharCount);
+ OUStringBuffer aStrikeoutString;
+
+ for(sal_uInt32 a(0); a < nStrikeCharCount; a++)
+ {
+ aStrikeoutString.append(aSingleCharString);
+ aDXArray[a] = (a + 1) * fStrikeCharWidth;
+ }
+
+ auto len = aStrikeoutString.getLength();
+ rContainer.push_back(
+ new TextSimplePortionPrimitive2D(
+ getObjectTransformation(),
+ aStrikeoutString.makeStringAndClear(),
+ 0,
+ len,
+ std::move(aDXArray),
+ getFontAttribute(),
+ getLocale(),
+ getFontColor()));
+ }
+
+ TextCharacterStrikeoutPrimitive2D::TextCharacterStrikeoutPrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ const basegfx::BColor& rFontColor,
+ sal_Unicode aStrikeoutChar,
+ const attribute::FontAttribute& rFontAttribute,
+ const css::lang::Locale& rLocale)
+ : BaseTextStrikeoutPrimitive2D(rObjectTransformation, fWidth, rFontColor),
+ maStrikeoutChar(aStrikeoutChar),
+ maFontAttribute(rFontAttribute),
+ maLocale(rLocale)
+ {
+ }
+
+ bool TextCharacterStrikeoutPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BaseTextStrikeoutPrimitive2D::operator==(rPrimitive))
+ {
+ const TextCharacterStrikeoutPrimitive2D& rCompare = static_cast<const TextCharacterStrikeoutPrimitive2D&>(rPrimitive);
+
+ return (getStrikeoutChar() == rCompare.getStrikeoutChar()
+ && getFontAttribute() == rCompare.getFontAttribute()
+ && LocalesAreEqual(getLocale(), rCompare.getLocale()));
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextCharacterStrikeoutPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D;
+ }
+
+ void TextGeometryStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ OSL_ENSURE(TEXT_STRIKEOUT_SLASH != getTextStrikeout() && TEXT_STRIKEOUT_X != getTextStrikeout(),
+ "Wrong TEXT_STRIKEOUT type; a TextCharacterStrikeoutPrimitive2D should be used (!)");
+
+ // strikeout with geometry
+ double fStrikeoutHeight(getHeight());
+ double fStrikeoutOffset(getOffset());
+ bool bDoubleLine(false);
+
+ // get decomposition
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getObjectTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // set line attribute
+ switch(getTextStrikeout())
+ {
+ default : // case primitive2d::TEXT_STRIKEOUT_SINGLE:
+ {
+ break;
+ }
+ case primitive2d::TEXT_STRIKEOUT_DOUBLE:
+ {
+ bDoubleLine = true;
+ break;
+ }
+ case primitive2d::TEXT_STRIKEOUT_BOLD:
+ {
+ fStrikeoutHeight *= 2.0;
+ break;
+ }
+ }
+
+ if(bDoubleLine)
+ {
+ fStrikeoutOffset -= 0.50 * fStrikeoutHeight;
+ fStrikeoutHeight *= 0.64;
+ }
+
+ // create base polygon and new primitive
+ basegfx::B2DPolygon aStrikeoutLine;
+
+ aStrikeoutLine.append(basegfx::B2DPoint(0.0, -fStrikeoutOffset));
+ aStrikeoutLine.append(basegfx::B2DPoint(getWidth(), -fStrikeoutOffset));
+
+ const basegfx::B2DHomMatrix aUnscaledTransform(
+ basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate));
+
+ aStrikeoutLine.transform(aUnscaledTransform);
+
+ // add primitive
+ const attribute::LineAttribute aLineAttribute(getFontColor(), fStrikeoutHeight, basegfx::B2DLineJoin::NONE);
+ Primitive2DContainer xRetval(1);
+ xRetval[0] = new PolygonStrokePrimitive2D(aStrikeoutLine, aLineAttribute);
+
+ if(bDoubleLine)
+ {
+ // double line, create 2nd primitive with offset using TransformPrimitive based on
+ // already created NewPrimitive
+ const double fLineDist(2.0 * fStrikeoutHeight);
+
+ // move base point of text to 0.0 and de-rotate
+ basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -aTranslate.getX(), -aTranslate.getY()));
+ aTransform.rotate(-fRotate);
+
+ // translate in Y by offset
+ aTransform.translate(0.0, -fLineDist);
+
+ // move back and rotate
+ aTransform.rotate(fRotate);
+ aTransform.translate(aTranslate.getX(), aTranslate.getY());
+
+ // add transform primitive
+ xRetval.push_back(
+ new TransformPrimitive2D(
+ aTransform,
+ Primitive2DContainer(xRetval)));
+ }
+
+ rContainer.append(std::move(xRetval));
+ }
+
+ TextGeometryStrikeoutPrimitive2D::TextGeometryStrikeoutPrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ const basegfx::BColor& rFontColor,
+ double fHeight,
+ double fOffset,
+ TextStrikeout eTextStrikeout)
+ : BaseTextStrikeoutPrimitive2D(rObjectTransformation, fWidth, rFontColor),
+ mfHeight(fHeight),
+ mfOffset(fOffset),
+ meTextStrikeout(eTextStrikeout)
+ {
+ }
+
+ bool TextGeometryStrikeoutPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BaseTextStrikeoutPrimitive2D::operator==(rPrimitive))
+ {
+ const TextGeometryStrikeoutPrimitive2D& rCompare = static_cast<const TextGeometryStrikeoutPrimitive2D&>(rPrimitive);
+
+ return (getHeight() == rCompare.getHeight()
+ && getOffset() == rCompare.getOffset()
+ && getTextStrikeout() == rCompare.getTextStrikeout());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextGeometryStrikeoutPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTGEOMETRYSTRIKEOUTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/transformprimitive2d.cxx b/drawinglayer/source/primitive2d/transformprimitive2d.cxx
new file mode 100644
index 000000000..ddd1594e2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/transformprimitive2d.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ TransformPrimitive2D::TransformPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransformation,
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maTransformation(rTransformation)
+ {
+ }
+
+ bool TransformPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TransformPrimitive2D& rCompare = static_cast< const TransformPrimitive2D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange TransformPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
+ aRetval.transform(getTransformation());
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 TransformPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx b/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx
new file mode 100644
index 000000000..8a86b1b29
--- /dev/null
+++ b/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ TransparencePrimitive2D::TransparencePrimitive2D(
+ Primitive2DContainer&& aChildren,
+ Primitive2DContainer&& aTransparence)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maTransparence(std::move(aTransparence))
+ {
+ }
+
+ bool TransparencePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TransparencePrimitive2D& rCompare = static_cast<const TransparencePrimitive2D&>(rPrimitive);
+
+ return (getTransparence() == rCompare.getTransparence());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TransparencePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx
new file mode 100644
index 000000000..a2ba512be
--- /dev/null
+++ b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ UnifiedTransparencePrimitive2D::UnifiedTransparencePrimitive2D(
+ Primitive2DContainer&& aChildren,
+ double fTransparence)
+ : GroupPrimitive2D(std::move(aChildren)),
+ mfTransparence(fTransparence)
+ {
+ }
+
+ bool UnifiedTransparencePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const UnifiedTransparencePrimitive2D& rCompare = static_cast<const UnifiedTransparencePrimitive2D&>(rPrimitive);
+
+ return (getTransparence() == rCompare.getTransparence());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange UnifiedTransparencePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // do not use the fallback to decomposition here since for a correct BoundRect we also
+ // need invisible (1.0 == getTransparence()) geometry; these would be deleted in the decomposition
+ return getChildren().getB2DRange( rViewInformation);
+ }
+
+ void UnifiedTransparencePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(0.0 == getTransparence())
+ {
+ // no transparence used, so just use the content
+ getChildren(rVisitor);
+ }
+ else if(getTransparence() > 0.0 && getTransparence() < 1.0)
+ {
+ // The idea is to create a TransparencePrimitive2D with transparent content using a fill color
+ // corresponding to the transparence value. Problem is that in most systems, the right
+ // and bottom pixel array is not filled when filling polygons, thus this would not
+ // always produce a complete transparent bitmap. There are some solutions:
+
+ // - Grow the used polygon range by one discrete unit in X and Y. This
+ // will make the decomposition view-dependent.
+
+ // - For all filled polygon renderings, draw the polygon outline extra. This
+ // would lead to unwanted side effects when using concatenated polygons.
+
+ // - At this decomposition, add a filled polygon and a hairline polygon. This
+ // solution stays view-independent.
+
+ // I will take the last one here. The small overhead of two primitives will only be
+ // used when UnifiedTransparencePrimitive2D is not handled directly.
+ const basegfx::B2DRange aPolygonRange(getChildren().getB2DRange(rViewInformation));
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(aPolygonRange));
+ const basegfx::BColor aGray(getTransparence(), getTransparence(), getTransparence());
+ Primitive2DContainer aTransparenceContent(2);
+
+ aTransparenceContent[0] = Primitive2DReference(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), aGray));
+ aTransparenceContent[1] = Primitive2DReference(new PolygonHairlinePrimitive2D(aPolygon, aGray));
+
+ // create sub-transparence group with a gray-colored rectangular fill polygon
+ rVisitor.visit(new TransparencePrimitive2D(Primitive2DContainer(getChildren()), std::move(aTransparenceContent)));
+ }
+ else
+ {
+ // completely transparent or invalid definition, add nothing
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 UnifiedTransparencePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx
new file mode 100644
index 000000000..6e7e134f2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive2d/wallpaperprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <vcl/graph.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void WallpaperBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ Primitive2DReference aRetval;
+
+ if(!getLocalObjectRange().isEmpty() && !getBitmapEx().IsEmpty())
+ {
+ // get bitmap PIXEL size
+ const Size& rPixelSize = getBitmapEx().GetSizePixel();
+
+ if(rPixelSize.Width() > 0 && rPixelSize.Height() > 0)
+ {
+ if(WallpaperStyle::Scale == getWallpaperStyle())
+ {
+ // shortcut for scale; use simple BitmapPrimitive2D
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, getLocalObjectRange().getWidth());
+ aObjectTransform.set(1, 1, getLocalObjectRange().getHeight());
+ aObjectTransform.set(0, 2, getLocalObjectRange().getMinX());
+ aObjectTransform.set(1, 2, getLocalObjectRange().getMinY());
+
+ Primitive2DReference xReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
+ aObjectTransform));
+
+ aRetval = xReference;
+ }
+ else
+ {
+ // transform to logic size
+ basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation());
+ aInverseViewTransformation.invert();
+ basegfx::B2DVector aLogicSize(rPixelSize.Width(), rPixelSize.Height());
+ aLogicSize = aInverseViewTransformation * aLogicSize;
+
+ // apply layout
+ basegfx::B2DPoint aTargetTopLeft(getLocalObjectRange().getMinimum());
+ bool bUseTargetTopLeft(true);
+ bool bNeedsClipping(false);
+
+ switch(getWallpaperStyle())
+ {
+ default: //case WallpaperStyle::Tile :, also WallpaperStyle::NONE and WallpaperStyle::ApplicationGradient
+ {
+ bUseTargetTopLeft = false;
+ break;
+ }
+ case WallpaperStyle::Scale :
+ {
+ // handled by shortcut above
+ break;
+ }
+ case WallpaperStyle::TopLeft :
+ {
+ // nothing to do
+ break;
+ }
+ case WallpaperStyle::Top :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5));
+ break;
+ }
+ case WallpaperStyle::TopRight :
+ {
+ aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX());
+ break;
+ }
+ case WallpaperStyle::Left :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5));
+ break;
+ }
+ case WallpaperStyle::Center :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft = aCenter - (aLogicSize * 0.5);
+ break;
+ }
+ case WallpaperStyle::Right :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX());
+ aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5));
+ break;
+ }
+ case WallpaperStyle::BottomLeft :
+ {
+ aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY());
+ break;
+ }
+ case WallpaperStyle::Bottom :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5));
+ aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY());
+ break;
+ }
+ case WallpaperStyle::BottomRight :
+ {
+ aTargetTopLeft = getLocalObjectRange().getMaximum() - aLogicSize;
+ break;
+ }
+ }
+
+ if(bUseTargetTopLeft)
+ {
+ // fill target range
+ const basegfx::B2DRange aTargetRange(aTargetTopLeft, aTargetTopLeft + aLogicSize);
+
+ // create aligned, single BitmapPrimitive2D
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, aTargetRange.getWidth());
+ aObjectTransform.set(1, 1, aTargetRange.getHeight());
+ aObjectTransform.set(0, 2, aTargetRange.getMinX());
+ aObjectTransform.set(1, 2, aTargetRange.getMinY());
+
+ Primitive2DReference xReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
+ aObjectTransform));
+ aRetval = xReference;
+
+ // clip when not completely inside object range
+ bNeedsClipping = !getLocalObjectRange().isInside(aTargetRange);
+ }
+ else
+ {
+ // WallpaperStyle::Tile, WallpaperStyle::NONE, WallpaperStyle::ApplicationGradient
+ // convert to relative positions
+ const basegfx::B2DVector aRelativeSize(
+ aLogicSize.getX() / (getLocalObjectRange().getWidth() ? getLocalObjectRange().getWidth() : 1.0),
+ aLogicSize.getY() / (getLocalObjectRange().getHeight() ? getLocalObjectRange().getHeight() : 1.0));
+ basegfx::B2DPoint aRelativeTopLeft(0.0, 0.0);
+
+ if(WallpaperStyle::Tile != getWallpaperStyle())
+ {
+ aRelativeTopLeft.setX(0.5 - aRelativeSize.getX());
+ aRelativeTopLeft.setY(0.5 - aRelativeSize.getY());
+ }
+
+ // prepare FillGraphicAttribute
+ const attribute::FillGraphicAttribute aFillGraphicAttribute(
+ Graphic(getBitmapEx()),
+ basegfx::B2DRange(aRelativeTopLeft, aRelativeTopLeft+ aRelativeSize),
+ true);
+
+ // create ObjectTransform
+ const basegfx::B2DHomMatrix aObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ getLocalObjectRange().getRange(),
+ getLocalObjectRange().getMinimum()));
+
+ // create FillBitmapPrimitive
+ const drawinglayer::primitive2d::Primitive2DReference xFillBitmap(
+ new drawinglayer::primitive2d::FillGraphicPrimitive2D(
+ aObjectTransform,
+ aFillGraphicAttribute));
+ aRetval = xFillBitmap;
+
+ // always embed tiled fill to clipping
+ bNeedsClipping = true;
+ }
+
+ if(bNeedsClipping)
+ {
+ // embed to clipping; this is necessary for tiled fills
+ const basegfx::B2DPolyPolygon aPolyPolygon(
+ basegfx::utils::createPolygonFromRect(getLocalObjectRange()));
+ const drawinglayer::primitive2d::Primitive2DReference xClippedFill(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aPolyPolygon,
+ { aRetval }));
+ aRetval = xClippedFill;
+ }
+ }
+ }
+ }
+
+ if (aRetval.is())
+ rContainer.push_back(aRetval);
+ }
+
+ WallpaperBitmapPrimitive2D::WallpaperBitmapPrimitive2D(
+ const basegfx::B2DRange& rObjectRange,
+ const BitmapEx& rBitmapEx,
+ WallpaperStyle eWallpaperStyle)
+ : maObjectRange(rObjectRange),
+ maBitmapEx(rBitmapEx),
+ meWallpaperStyle(eWallpaperStyle)
+ {
+ }
+
+ bool WallpaperBitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(ViewTransformationDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const WallpaperBitmapPrimitive2D& rCompare = static_cast<const WallpaperBitmapPrimitive2D&>(rPrimitive);
+
+ return (getLocalObjectRange() == rCompare.getLocalObjectRange()
+ && getBitmapEx() == rCompare.getBitmapEx()
+ && getWallpaperStyle() == rCompare.getWallpaperStyle());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange WallpaperBitmapPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ return getLocalObjectRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 WallpaperBitmapPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_WALLPAPERBITMAPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx
new file mode 100644
index 000000000..5249cdfb3
--- /dev/null
+++ b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void WrongSpellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // ATM this decompose is view-independent, what the original VCL-Display is not. To mimic
+ // the old behaviour here if wanted it is necessary to add get2DDecomposition and implement
+ // it similar to the usage in e.g. HelplinePrimitive2D. Remembering the ViewTransformation
+ // should be enough then.
+ // The view-independent wavelines work well (if You ask me). Maybe the old VCL-Behaviour is only
+ // in place because it was not possible/too expensive at that time to scale the wavelines with the
+ // view...
+ // With the VCL-PixelRenderer this will not even be used since it implements WrongSpellPrimitive2D
+ // directly and mimics the old VCL-Display there. If You implemented a new renderer without
+ // direct WrongSpellPrimitive2D support, You may want to do the described change here.
+
+ // get the font height (part of scale), so decompose the matrix
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // calculate distances based on a static default (to allow testing in debugger)
+ static const double fDefaultDistance(0.03);
+ const double fFontHeight(aScale.getY());
+ const double fUnderlineDistance(fFontHeight * fDefaultDistance);
+ const double fWaveWidth(2.0 * fUnderlineDistance);
+
+ // the Y-distance needs to be relative to FontHeight since the points get
+ // transformed with the transformation containing that scale already.
+ const double fRelativeUnderlineDistance(basegfx::fTools::equalZero(aScale.getY()) ? 0.0 : fUnderlineDistance / aScale.getY());
+ basegfx::B2DPoint aStart(getStart(), fRelativeUnderlineDistance);
+ basegfx::B2DPoint aStop(getStop(), fRelativeUnderlineDistance);
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(getTransformation() * aStart);
+ aPolygon.append(getTransformation() * aStop);
+
+ // prepare line attribute
+ const attribute::LineAttribute aLineAttribute(getColor());
+
+ // create the waveline primitive
+ rContainer.push_back(new PolygonWavePrimitive2D(aPolygon, aLineAttribute, fWaveWidth, 0.5 * fWaveWidth));
+ }
+
+ WrongSpellPrimitive2D::WrongSpellPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransformation,
+ double fStart,
+ double fStop,
+ const basegfx::BColor& rColor)
+ : maTransformation(rTransformation),
+ mfStart(fStart),
+ mfStop(fStop),
+ maColor(rColor)
+ {
+ }
+
+ bool WrongSpellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const WrongSpellPrimitive2D& rCompare = static_cast<const WrongSpellPrimitive2D&>(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation()
+ && getStart() == rCompare.getStart()
+ && getStop() == rCompare.getStop()
+ && getColor() == rCompare.getColor());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 WrongSpellPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/Tools.cxx b/drawinglayer/source/primitive3d/Tools.cxx
new file mode 100644
index 000000000..c27896b04
--- /dev/null
+++ b/drawinglayer/source/primitive3d/Tools.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <drawinglayer/primitive3d/Tools.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+using namespace css;
+
+namespace drawinglayer::primitive3d
+{
+OUString idToString(sal_uInt32 nId)
+{
+ switch (nId)
+ {
+ case PRIMITIVE3D_ID_GROUPPRIMITIVE3D:
+ return "GROUPPRIMITIVE3D";
+ case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D:
+ return "HATCHTEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D:
+ return "MODIFIEDCOLORPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D:
+ return "POLYGONHAIRLINEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYGONSTROKEPRIMITIVE3D:
+ return "POLYGONSTROKEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D:
+ return "POLYGONTUBEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D:
+ return "POLYPOLYGONMATERIALPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRCUBEPRIMITIVE3D:
+ return "SDRCUBEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D:
+ return "SDREXTRUDEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRLATHEPRIMITIVE3D:
+ return "SDRLATHEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRPOLYPOLYGONPRIMITIVE3D:
+ return "SDRPOLYPOLYGONPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRSPHEREPRIMITIVE3D:
+ return "SDRSPHEREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SHADOWPRIMITIVE3D:
+ return "SHADOWPRIMITIVE3D";
+ case PRIMITIVE3D_ID_UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D:
+ return "UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D:
+ return "GRADIENTTEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D:
+ return "BITMAPTEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_TRANSPARENCETEXTUREPRIMITIVE3D:
+ return "TRANSPARENCETEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D:
+ return "TRANSFORMPRIMITIVE3D";
+ case PRIMITIVE3D_ID_HIDDENGEOMETRYPRIMITIVE3D:
+ return "HIDDENGEOMETRYPRIMITIVE3D";
+ default:
+ return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF);
+ }
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/baseprimitive3d.cxx b/drawinglayer/source/primitive3d/baseprimitive3d.cxx
new file mode 100644
index 000000000..5bd980012
--- /dev/null
+++ b/drawinglayer/source/primitive3d/baseprimitive3d.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/baseprimitive3d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <comphelper/sequence.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ BasePrimitive3D::BasePrimitive3D()
+ : BasePrimitive3DImplBase(m_aMutex)
+ {
+ }
+
+ BasePrimitive3D::~BasePrimitive3D()
+ {
+ }
+
+ bool BasePrimitive3D::operator==( const BasePrimitive3D& rPrimitive ) const
+ {
+ return (getPrimitive3DID() == rPrimitive.getPrimitive3DID());
+ }
+
+ basegfx::B3DRange BasePrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ return get3DDecomposition(rViewInformation).getB3DRange(rViewInformation);
+ }
+
+ Primitive3DContainer BasePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return Primitive3DContainer();
+ }
+
+ css::uno::Sequence< ::css::uno::Reference< ::css::graphic::XPrimitive3D > > SAL_CALL BasePrimitive3D::getDecomposition( const uno::Sequence< beans::PropertyValue >& rViewParameters )
+ {
+ const geometry::ViewInformation3D aViewInformation(rViewParameters);
+ return comphelper::containerToSequence(get3DDecomposition(aViewInformation));
+ }
+
+ css::geometry::RealRectangle3D SAL_CALL BasePrimitive3D::getRange( const uno::Sequence< beans::PropertyValue >& rViewParameters )
+ {
+ const geometry::ViewInformation3D aViewInformation(rViewParameters);
+ return basegfx::unotools::rectangle3DFromB3DRectangle(getB3DRange(aViewInformation));
+ }
+
+
+ Primitive3DContainer BufferedDecompositionPrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return Primitive3DContainer();
+ }
+
+ BufferedDecompositionPrimitive3D::BufferedDecompositionPrimitive3D()
+ {
+ }
+
+ Primitive3DContainer BufferedDecompositionPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if(getBuffered3DDecomposition().empty())
+ {
+ const Primitive3DContainer aNewSequence(create3DDecomposition(rViewInformation));
+ const_cast< BufferedDecompositionPrimitive3D* >(this)->setBuffered3DDecomposition(aNewSequence);
+ }
+
+ return getBuffered3DDecomposition();
+ }
+
+// tooling
+
+ // get range3D from a given Primitive3DReference
+ basegfx::B3DRange getB3DRangeFromPrimitive3DReference(const Primitive3DReference& rCandidate, const geometry::ViewInformation3D& aViewInformation)
+ {
+ basegfx::B3DRange aRetval;
+
+ if(rCandidate.is())
+ {
+ const BasePrimitive3D* pCandidate(static_cast< BasePrimitive3D* >(rCandidate.get()));
+ aRetval.expand(pCandidate->getB3DRange(aViewInformation));
+ }
+
+ return aRetval;
+ }
+
+ // get range3D from a given Primitive3DContainer
+ basegfx::B3DRange Primitive3DContainer::getB3DRange(const geometry::ViewInformation3D& aViewInformation) const
+ {
+ basegfx::B3DRange aRetval;
+
+ if(!empty())
+ {
+ const size_t nCount(size());
+
+ for(size_t a(0); a < nCount; a++)
+ {
+ aRetval.expand(getB3DRangeFromPrimitive3DReference((*this)[a], aViewInformation));
+ }
+ }
+
+ return aRetval;
+ }
+
+ bool arePrimitive3DReferencesEqual(const Primitive3DReference& rxA, const Primitive3DReference& rxB)
+ {
+ const bool bAIs(rxA.is());
+
+ if(bAIs != rxB.is())
+ {
+ return false;
+ }
+
+ if(!bAIs)
+ {
+ return true;
+ }
+
+ const BasePrimitive3D* pA(static_cast< const BasePrimitive3D* >(rxA.get()));
+ const BasePrimitive3D* pB(static_cast< const BasePrimitive3D* >(rxB.get()));
+
+ return pA->operator==(*pB);
+ }
+
+ bool Primitive3DContainer::operator==(const Primitive3DContainer& rB) const
+ {
+ const bool bAHasElements(!empty());
+
+ if(bAHasElements != !rB.empty())
+ {
+ return false;
+ }
+
+ if(!bAHasElements)
+ {
+ return true;
+ }
+
+ const size_t nCount(size());
+
+ if(nCount != rB.size())
+ {
+ return false;
+ }
+
+ for(size_t a(0); a < nCount; a++)
+ {
+ if(!arePrimitive3DReferencesEqual((*this)[a], rB[a]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void Primitive3DContainer::append(const Primitive3DContainer& rSource)
+ {
+ insert(end(), rSource.begin(), rSource.end());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/groupprimitive3d.cxx b/drawinglayer/source/primitive3d/groupprimitive3d.cxx
new file mode 100644
index 000000000..69291e768
--- /dev/null
+++ b/drawinglayer/source/primitive3d/groupprimitive3d.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/groupprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ GroupPrimitive3D::GroupPrimitive3D( const Primitive3DContainer& rChildren )
+ : maChildren(rChildren)
+ {
+ }
+
+ /** The compare opertator uses the Sequence::==operator, so only checking if
+ the references are equal. All non-equal references are interpreted as
+ non-equal.
+ */
+ bool GroupPrimitive3D::operator==( const BasePrimitive3D& rPrimitive ) const
+ {
+ if(BasePrimitive3D::operator==(rPrimitive))
+ {
+ const GroupPrimitive3D& rCompare = static_cast< const GroupPrimitive3D& >(rPrimitive);
+
+ return getChildren() == rCompare.getChildren();
+ }
+
+ return false;
+ }
+
+ /// default: just return children, so all renderers not supporting group will use its content
+ Primitive3DContainer GroupPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return getChildren();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(GroupPrimitive3D, PRIMITIVE3D_ID_GROUPPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
new file mode 100644
index 000000000..a699f7170
--- /dev/null
+++ b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/hatchtextureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <texture/texture.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer HatchTexturePrimitive3D::impCreate3DDecomposition() const
+ {
+ Primitive3DContainer aRetval;
+
+ if(!getChildren().empty())
+ {
+ const Primitive3DContainer aSource(getChildren());
+ const size_t nSourceCount(aSource.size());
+ std::vector< Primitive3DReference > aDestination;
+
+ for(size_t a(0); a < nSourceCount; a++)
+ {
+ // get reference
+ const Primitive3DReference xReference(aSource[a]);
+
+ if(xReference.is())
+ {
+ const BasePrimitive3D* pBasePrimitive = static_cast< const BasePrimitive3D* >(xReference.get());
+
+ // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
+ // not all content is needed, remove transparencies and ModifiedColorPrimitives
+ switch(pBasePrimitive->getPrimitive3DID())
+ {
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
+ {
+ // polyPolygonMaterialPrimitive3D, check texturing and hatching
+ const PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const PolyPolygonMaterialPrimitive3D& >(*pBasePrimitive);
+ const basegfx::B3DPolyPolygon& aFillPolyPolygon(rPrimitive.getB3DPolyPolygon());
+
+ if(maHatch.isFillBackground())
+ {
+ // add original primitive for background
+ aDestination.push_back(xReference);
+ }
+
+ if(aFillPolyPolygon.areTextureCoordinatesUsed())
+ {
+ const sal_uInt32 nPolyCount(aFillPolyPolygon.count());
+ basegfx::B2DPolyPolygon aTexPolyPolygon;
+ basegfx::B2DPoint a2N;
+ basegfx::B2DVector a2X, a2Y;
+ basegfx::B3DPoint a3N;
+ basegfx::B3DVector a3X, a3Y;
+ bool b2N(false), b2X(false), b2Y(false);
+
+ for(sal_uInt32 b(0); b < nPolyCount; b++)
+ {
+ const basegfx::B3DPolygon& aPartPoly(aFillPolyPolygon.getB3DPolygon(b));
+ const sal_uInt32 nPointCount(aPartPoly.count());
+ basegfx::B2DPolygon aTexPolygon;
+
+ for(sal_uInt32 c(0); c < nPointCount; c++)
+ {
+ const basegfx::B2DPoint a2Candidate(aPartPoly.getTextureCoordinate(c));
+
+ if(!b2N)
+ {
+ a2N = a2Candidate;
+ a3N = aPartPoly.getB3DPoint(c);
+ b2N = true;
+ }
+ else if(!b2X && !a2N.equal(a2Candidate))
+ {
+ a2X = a2Candidate - a2N;
+ a3X = aPartPoly.getB3DPoint(c) - a3N;
+ b2X = true;
+ }
+ else if(!b2Y && !a2N.equal(a2Candidate) && !a2X.equal(a2Candidate))
+ {
+ a2Y = a2Candidate - a2N;
+
+ const double fCross(a2X.cross(a2Y));
+
+ if(!basegfx::fTools::equalZero(fCross))
+ {
+ a3Y = aPartPoly.getB3DPoint(c) - a3N;
+ b2Y = true;
+ }
+ }
+
+ aTexPolygon.append(a2Candidate);
+ }
+
+ aTexPolygon.setClosed(true);
+ aTexPolyPolygon.append(aTexPolygon);
+ }
+
+ if(b2N && b2X && b2Y)
+ {
+ // found two linearly independent 2D vectors
+ // get 2d range of texture coordinates
+ const basegfx::B2DRange aOutlineRange(basegfx::utils::getRange(aTexPolyPolygon));
+ const basegfx::BColor aHatchColor(getHatch().getColor());
+ const double fAngle(getHatch().getAngle());
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+
+ // get hatch transformations
+ switch(getHatch().getStyle())
+ {
+ case attribute::HatchStyle::Triple:
+ {
+ // rotated 45 degrees
+ texture::GeoTexSvxHatch aHatch(
+ aOutlineRange,
+ aOutlineRange,
+ getHatch().getDistance(),
+ fAngle - M_PI_4);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Double:
+ {
+ // rotated 90 degrees
+ texture::GeoTexSvxHatch aHatch(
+ aOutlineRange,
+ aOutlineRange,
+ getHatch().getDistance(),
+ fAngle - M_PI_2);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Single:
+ {
+ // angle as given
+ texture::GeoTexSvxHatch aHatch(
+ aOutlineRange,
+ aOutlineRange,
+ getHatch().getDistance(),
+ fAngle);
+
+ aHatch.appendTransformations(aMatrices);
+ }
+ }
+
+ // create geometry from unit line
+ basegfx::B2DPolyPolygon a2DHatchLines;
+ basegfx::B2DPolygon a2DUnitLine;
+ a2DUnitLine.append(basegfx::B2DPoint(0.0, 0.0));
+ a2DUnitLine.append(basegfx::B2DPoint(1.0, 0.0));
+
+ for(const basegfx::B2DHomMatrix & rMatrix : aMatrices)
+ {
+ basegfx::B2DPolygon aNewLine(a2DUnitLine);
+ aNewLine.transform(rMatrix);
+ a2DHatchLines.append(aNewLine);
+ }
+
+ if(a2DHatchLines.count())
+ {
+ // clip against texture polygon
+ a2DHatchLines = basegfx::utils::clipPolyPolygonOnPolyPolygon(a2DHatchLines, aTexPolyPolygon, true, true);
+ }
+
+ if(a2DHatchLines.count())
+ {
+ // create 2d matrix with 2d vectors as column vectors and 2d point as offset, this represents
+ // a coordinate system transformation from unit coordinates to the new coordinate system
+ basegfx::B2DHomMatrix a2D;
+ a2D.set(0, 0, a2X.getX());
+ a2D.set(1, 0, a2X.getY());
+ a2D.set(0, 1, a2Y.getX());
+ a2D.set(1, 1, a2Y.getY());
+ a2D.set(0, 2, a2N.getX());
+ a2D.set(1, 2, a2N.getY());
+
+ // invert that transformation, so we have a back-transformation from texture coordinates
+ // to unit coordinates
+ a2D.invert();
+ a2DHatchLines.transform(a2D);
+
+ // expand back-transformed geometry to 3D
+ basegfx::B3DPolyPolygon a3DHatchLines(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(a2DHatchLines, 0.0));
+
+ // create 3d matrix with 3d vectors as column vectors (0,0,1 as Z) and 3d point as offset, this represents
+ // a coordinate system transformation from unit coordinates to the object's 3d coordinate system
+ basegfx::B3DHomMatrix a3D;
+ a3D.set(0, 0, a3X.getX());
+ a3D.set(1, 0, a3X.getY());
+ a3D.set(2, 0, a3X.getZ());
+ a3D.set(0, 1, a3Y.getX());
+ a3D.set(1, 1, a3Y.getY());
+ a3D.set(2, 1, a3Y.getZ());
+ a3D.set(0, 3, a3N.getX());
+ a3D.set(1, 3, a3N.getY());
+ a3D.set(2, 3, a3N.getZ());
+
+ // transform hatch lines to 3D object coordinates
+ a3DHatchLines.transform(a3D);
+
+ // build primitives from this geometry
+ const sal_uInt32 nHatchLines(a3DHatchLines.count());
+
+ for(sal_uInt32 d(0); d < nHatchLines; d++)
+ {
+ const Primitive3DReference xRef(new PolygonHairlinePrimitive3D(a3DHatchLines.getB3DPolygon(d), aHatchColor));
+ aDestination.push_back(xRef);
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ default :
+ {
+ // add reference to result
+ aDestination.push_back(xReference);
+ break;
+ }
+ }
+ }
+ }
+
+ // prepare return value
+ const sal_uInt32 nDestSize(aDestination.size());
+ aRetval.resize(nDestSize);
+
+ for(sal_uInt32 b(0); b < nDestSize; b++)
+ {
+ aRetval[b] = aDestination[b];
+ }
+ }
+
+ return aRetval;
+ }
+
+ HatchTexturePrimitive3D::HatchTexturePrimitive3D(
+ const attribute::FillHatchAttribute& rHatch,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate,
+ bool bFilter)
+ : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
+ maHatch(rHatch)
+ {
+ }
+
+ bool HatchTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const HatchTexturePrimitive3D& rCompare = static_cast<const HatchTexturePrimitive3D&>(rPrimitive);
+
+ return (getHatch() == rCompare.getHatch());
+ }
+
+ return false;
+ }
+
+ Primitive3DContainer HatchTexturePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if(getBuffered3DDecomposition().empty())
+ {
+ const Primitive3DContainer aNewSequence(impCreate3DDecomposition());
+ const_cast< HatchTexturePrimitive3D* >(this)->maBuffered3DDecomposition = aNewSequence;
+ }
+
+ return getBuffered3DDecomposition();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(HatchTexturePrimitive3D, PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx b/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx
new file mode 100644
index 000000000..1a367e501
--- /dev/null
+++ b/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/hiddengeometryprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ HiddenGeometryPrimitive3D::HiddenGeometryPrimitive3D(
+ const Primitive3DContainer& rChildren)
+ : GroupPrimitive3D(rChildren)
+ {
+ }
+
+ basegfx::B3DRange HiddenGeometryPrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ return getChildren().getB3DRange(rViewInformation);
+ }
+
+ Primitive3DContainer HiddenGeometryPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // return empty sequence
+ return Primitive3DContainer();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(HiddenGeometryPrimitive3D, PRIMITIVE3D_ID_HIDDENGEOMETRYPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx
new file mode 100644
index 000000000..255f0235c
--- /dev/null
+++ b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ ModifiedColorPrimitive3D::ModifiedColorPrimitive3D(
+ const Primitive3DContainer& rChildren,
+ const basegfx::BColorModifierSharedPtr& rColorModifier)
+ : GroupPrimitive3D(rChildren),
+ maColorModifier(rColorModifier)
+ {
+ }
+
+ bool ModifiedColorPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const ModifiedColorPrimitive3D& rCompare = static_cast<const ModifiedColorPrimitive3D&>(rPrimitive);
+
+ if(getColorModifier().get() == rCompare.getColorModifier().get())
+ {
+ return true;
+ }
+
+ if(!getColorModifier() || !rCompare.getColorModifier())
+ {
+ return false;
+ }
+
+ return *getColorModifier() == *rCompare.getColorModifier();
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(ModifiedColorPrimitive3D, PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
new file mode 100644
index 000000000..8fc2b4d31
--- /dev/null
+++ b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <primitive3d/polygontubeprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ PolygonHairlinePrimitive3D::PolygonHairlinePrimitive3D(
+ const basegfx::B3DPolygon& rPolygon,
+ const basegfx::BColor& rBColor)
+ : maPolygon(rPolygon),
+ maBColor(rBColor)
+ {
+ }
+
+ bool PolygonHairlinePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BasePrimitive3D::operator==(rPrimitive))
+ {
+ const PolygonHairlinePrimitive3D& rCompare = static_cast<const PolygonHairlinePrimitive3D&>(rPrimitive);
+
+ return (getB3DPolygon() == rCompare.getB3DPolygon()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange PolygonHairlinePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return basegfx::utils::getRange(getB3DPolygon());
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolygonHairlinePrimitive3D, PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D)
+
+
+
+ Primitive3DContainer PolygonStrokePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ Primitive3DContainer aRetval;
+
+ if(getB3DPolygon().count())
+ {
+ basegfx::B3DPolyPolygon aHairLinePolyPolygon;
+
+ if(0.0 == getStrokeAttribute().getFullDotDashLen())
+ {
+ aHairLinePolyPolygon = basegfx::B3DPolyPolygon(getB3DPolygon());
+ }
+ else
+ {
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(getB3DPolygon(), getStrokeAttribute().getDotDashArray(), &aHairLinePolyPolygon, getStrokeAttribute().getFullDotDashLen());
+ }
+
+ // prepare result
+ aRetval.resize(aHairLinePolyPolygon.count());
+
+ if(getLineAttribute().getWidth())
+ {
+ // create fat line data
+ const double fRadius(getLineAttribute().getWidth() / 2.0);
+ const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
+ const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
+
+ for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
+ {
+ // create tube primitives
+ const Primitive3DReference xRef(
+ new PolygonTubePrimitive3D(
+ aHairLinePolyPolygon.getB3DPolygon(a),
+ getLineAttribute().getColor(),
+ fRadius,
+ aLineJoin,
+ aLineCap));
+ aRetval[a] = xRef;
+ }
+ }
+ else
+ {
+ // create hair line data for all sub polygons
+ for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
+ {
+ const basegfx::B3DPolygon& aCandidate = aHairLinePolyPolygon.getB3DPolygon(a);
+ const Primitive3DReference xRef(new PolygonHairlinePrimitive3D(aCandidate, getLineAttribute().getColor()));
+ aRetval[a] = xRef;
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ PolygonStrokePrimitive3D::PolygonStrokePrimitive3D(
+ const basegfx::B3DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ : maPolygon(rPolygon),
+ maLineAttribute(rLineAttribute),
+ maStrokeAttribute(rStrokeAttribute)
+ {
+ }
+
+ bool PolygonStrokePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive3D::operator==(rPrimitive))
+ {
+ const PolygonStrokePrimitive3D& rCompare = static_cast<const PolygonStrokePrimitive3D&>(rPrimitive);
+
+ return (getB3DPolygon() == rCompare.getB3DPolygon()
+ && getLineAttribute() == rCompare.getLineAttribute()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolygonStrokePrimitive3D, PRIMITIVE3D_ID_POLYGONSTROKEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
new file mode 100644
index 000000000..d8d3cc9c8
--- /dev/null
+++ b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
@@ -0,0 +1,769 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/polygontubeprimitive3d.hxx>
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <mutex>
+
+namespace drawinglayer::primitive3d
+{
+ namespace // anonymous namespace
+ {
+ class TubeBuffer
+ {
+ private:
+ // data for buffered tube primitives
+ Primitive3DContainer m_aLineTubeList;
+ sal_uInt32 m_nLineTubeSegments;
+ attribute::MaterialAttribute3D m_aLineMaterial;
+ std::mutex m_aMutex;
+ public:
+ TubeBuffer()
+ : m_nLineTubeSegments(0)
+ {
+ }
+
+ TubeBuffer(const TubeBuffer&) = delete;
+ const TubeBuffer& operator=(const TubeBuffer&) = delete;
+
+ Primitive3DContainer getLineTubeSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // may exclusively change cached data, use mutex
+ std::unique_lock aGuard(m_aMutex);
+
+ if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial))
+ {
+ m_nLineTubeSegments = nSegments;
+ m_aLineMaterial = rMaterial;
+ m_aLineTubeList = Primitive3DContainer();
+ }
+
+ if (m_aLineTubeList.empty() && m_nLineTubeSegments != 0)
+ {
+ const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0);
+ const basegfx::B3DPoint aRight(1.0, 0.0, 0.0);
+ basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0);
+ basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0);
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(2 * M_PI / static_cast<double>(m_nLineTubeSegments), 0.0, 0.0);
+ m_aLineTubeList.resize(m_nLineTubeSegments);
+
+ for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a)
+ {
+ const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
+ const basegfx::B3DPoint aNextRight(aRot * aLastRight);
+ basegfx::B3DPolygon aNewPolygon;
+
+ aNewPolygon.append(aNextLeft);
+ aNewPolygon.setNormal(0, basegfx::B3DVector(aNextLeft - aLeft));
+
+ aNewPolygon.append(aLastLeft);
+ aNewPolygon.setNormal(1, basegfx::B3DVector(aLastLeft - aLeft));
+
+ aNewPolygon.append(aLastRight);
+ aNewPolygon.setNormal(2, basegfx::B3DVector(aLastRight - aRight));
+
+ aNewPolygon.append(aNextRight);
+ aNewPolygon.setNormal(3, basegfx::B3DVector(aNextRight - aRight));
+
+ aNewPolygon.setClosed(true);
+
+ const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
+ m_aLineTubeList[a] = xRef;
+
+ aLastLeft = aNextLeft;
+ aLastRight = aNextRight;
+ }
+ }
+ return m_aLineTubeList;
+ }
+ };
+
+ Primitive3DContainer getLineTubeSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // static data for buffered tube primitives
+ static TubeBuffer theTubeBuffer;
+ return theTubeBuffer.getLineTubeSegments(nSegments, rMaterial);
+ }
+
+ class CapBuffer
+ {
+ private:
+ // data for buffered cap primitives
+ Primitive3DContainer m_aLineCapList;
+ sal_uInt32 m_nLineCapSegments;
+ attribute::MaterialAttribute3D m_aLineMaterial;
+ std::mutex m_aMutex;
+ public:
+ CapBuffer()
+ : m_nLineCapSegments(0)
+ {
+ }
+ CapBuffer(const CapBuffer&) = delete;
+ const CapBuffer& operator=(const CapBuffer&) = delete;
+
+ Primitive3DContainer getLineCapSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // may exclusively change cached data, use mutex
+ std::unique_lock aGuard(m_aMutex);
+
+ if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial))
+ {
+ m_nLineCapSegments = nSegments;
+ m_aLineMaterial = rMaterial;
+ m_aLineCapList = Primitive3DContainer();
+ }
+
+ if (m_aLineCapList.empty() && m_nLineCapSegments != 0)
+ {
+ const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
+ basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(2 * M_PI / static_cast<double>(m_nLineCapSegments), 0.0, 0.0);
+ m_aLineCapList.resize(m_nLineCapSegments);
+
+ for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a)
+ {
+ const basegfx::B3DPoint aNext(aRot * aLast);
+ basegfx::B3DPolygon aNewPolygon;
+
+ aNewPolygon.append(aLast);
+ aNewPolygon.setNormal(0, basegfx::B3DVector(aLast - aNull));
+
+ aNewPolygon.append(aNext);
+ aNewPolygon.setNormal(1, basegfx::B3DVector(aNext - aNull));
+
+ aNewPolygon.append(aNull);
+ aNewPolygon.setNormal(2, basegfx::B3DVector(-1.0, 0.0, 0.0));
+
+ aNewPolygon.setClosed(true);
+
+ const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
+ m_aLineCapList[a] = xRef;
+
+ aLast = aNext;
+ }
+ }
+
+ return m_aLineCapList;
+ }
+ };
+
+ Primitive3DContainer getLineCapSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // static data for buffered cap primitives
+ static CapBuffer theCapBuffer;
+ return theCapBuffer.getLineCapSegments(nSegments, rMaterial);
+ }
+
+ class CapRoundBuffer
+ {
+ private:
+ // data for buffered capround primitives
+ Primitive3DContainer m_aLineCapRoundList;
+ sal_uInt32 m_nLineCapRoundSegments;
+ attribute::MaterialAttribute3D m_aLineMaterial;
+ std::mutex m_aMutex;
+ public:
+ CapRoundBuffer()
+ : m_nLineCapRoundSegments(0)
+ {
+ }
+ CapRoundBuffer(const CapRoundBuffer&) = delete;
+ const CapRoundBuffer& operator=(const CapRoundBuffer&) = delete;
+
+ Primitive3DContainer getLineCapRoundSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // may exclusively change cached data, use mutex
+ std::unique_lock aGuard(m_aMutex);
+
+ if (nSegments != m_nLineCapRoundSegments || !(rMaterial == m_aLineMaterial))
+ {
+ m_nLineCapRoundSegments = nSegments;
+ m_aLineMaterial = rMaterial;
+ m_aLineCapRoundList = Primitive3DContainer();
+ }
+
+ if (m_aLineCapRoundList.empty() && m_nLineCapRoundSegments)
+ {
+ // calculate new horizontal segments
+ sal_uInt32 nVerSeg(nSegments / 2);
+
+ if (nVerSeg < 1)
+ {
+ nVerSeg = 1;
+ }
+
+ // create half-sphere; upper half of unit sphere
+ basegfx::B3DPolyPolygon aSphere(
+ basegfx::utils::createUnitSphereFillPolyPolygon(
+ nSegments,
+ nVerSeg,
+ true,
+ M_PI_2, 0.0,
+ 0.0, 2 * M_PI));
+ const sal_uInt32 nCount(aSphere.count());
+
+ if (nCount)
+ {
+ // rotate to have sphere cap oriented to negative X-Axis; do not
+ // forget to transform normals, too
+ basegfx::B3DHomMatrix aSphereTrans;
+
+ aSphereTrans.rotate(0.0, 0.0, M_PI_2);
+ aSphere.transform(aSphereTrans);
+ aSphere.transformNormals(aSphereTrans);
+
+ // realloc for primitives and create based on polygon snippets
+ m_aLineCapRoundList.resize(nCount);
+
+ for (sal_uInt32 a = 0; a < nCount; ++a)
+ {
+ const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
+ const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
+
+ // need to create one primitive per Polygon since the primitive
+ // is for planar PolyPolygons which is definitely not the case here
+ m_aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D(
+ aPartPolyPolygon,
+ rMaterial,
+ false);
+ }
+ }
+ }
+
+ return m_aLineCapRoundList;
+ }
+
+ };
+
+ Primitive3DContainer getLineCapRoundSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // static data for buffered cap primitives
+ static CapRoundBuffer theCapRoundBuffer;
+ return theCapRoundBuffer.getLineCapRoundSegments(nSegments, rMaterial);
+ }
+
+ Primitive3DContainer getLineJoinSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial,
+ double fAngle,
+ double fMiterMinimumAngle,
+ basegfx::B2DLineJoin aLineJoin)
+ {
+ // nSegments is for whole circle, adapt to half circle
+ const sal_uInt32 nVerSeg(nSegments >> 1);
+ std::vector< BasePrimitive3D* > aResultVector;
+
+ if(nVerSeg)
+ {
+ if(basegfx::B2DLineJoin::Round == aLineJoin)
+ {
+ // calculate new horizontal segments
+ const sal_uInt32 nHorSeg(basegfx::fround((fAngle / (2 * M_PI)) * static_cast<double>(nSegments)));
+
+ if(nHorSeg)
+ {
+ // create half-sphere
+ const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, M_PI_2, -M_PI_2, 0.0, fAngle));
+
+ for(sal_uInt32 a(0); a < aSphere.count(); a++)
+ {
+ const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
+ const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false));
+ }
+ }
+ else
+ {
+ // fallback to bevel when there is not at least one segment hor and ver
+ aLineJoin = basegfx::B2DLineJoin::Bevel;
+ }
+ }
+
+ if (basegfx::B2DLineJoin::Bevel == aLineJoin ||
+ basegfx::B2DLineJoin::Miter == aLineJoin)
+ {
+ if(basegfx::B2DLineJoin::Miter == aLineJoin)
+ {
+ const double fMiterAngle(fAngle/2.0);
+
+ if(fMiterAngle < fMiterMinimumAngle)
+ {
+ // fallback to bevel when miter's angle is too small
+ aLineJoin = basegfx::B2DLineJoin::Bevel;
+ }
+ }
+
+ const double fInc(M_PI / static_cast<double>(nVerSeg));
+ const double fSin(sin(-fAngle));
+ const double fCos(cos(-fAngle));
+ const bool bMiter(basegfx::B2DLineJoin::Miter == aLineJoin);
+ const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
+ const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
+ double fPos(-M_PI_2);
+ basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
+ basegfx::B3DPoint aCurrMiter, aNextMiter;
+ basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
+
+ // close polygon
+ aNewPolygon.setClosed(true);
+ aMiterPolygon.setClosed(true);
+
+ for(sal_uInt32 a(0); a < nVerSeg; a++)
+ {
+ const bool bFirst(0 == a);
+ const bool bLast(a + 1 == nVerSeg);
+
+ if(bFirst || !bLast)
+ {
+ fPos += fInc;
+
+ aNextPointOnXY = basegfx::B3DPoint(
+ cos(fPos),
+ sin(fPos),
+ 0.0);
+
+ aNextPointRotY = basegfx::B3DPoint(
+ aNextPointOnXY.getX() * fCos,
+ aNextPointOnXY.getY(),
+ aNextPointOnXY.getX() * fSin);
+
+ if(bMiter)
+ {
+ aNextMiter = basegfx::B3DPoint(
+ aNextPointOnXY.getX(),
+ aNextPointOnXY.getY(),
+ fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
+ }
+ }
+
+ if(bFirst)
+ {
+ aNewPolygon.clear();
+
+ if(bMiter)
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextMiter);
+
+ aMiterPolygon.clear();
+ aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
+ aMiterPolygon.append(aNextMiter);
+ aMiterPolygon.append(aNextPointRotY);
+ }
+ else
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextPointRotY);
+ }
+ }
+ else if(bLast)
+ {
+ aNewPolygon.clear();
+
+ if(bMiter)
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
+ aNewPolygon.append(aCurrMiter);
+ aNewPolygon.append(aPointOnXY);
+
+ aMiterPolygon.clear();
+ aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
+ aMiterPolygon.append(aPointRotY);
+ aMiterPolygon.append(aCurrMiter);
+ }
+ else
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
+ aNewPolygon.append(aPointRotY);
+ aNewPolygon.append(aPointOnXY);
+ }
+ }
+ else
+ {
+ aNewPolygon.clear();
+
+ if(bMiter)
+ {
+ aNewPolygon.append(aPointOnXY);
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextMiter);
+ aNewPolygon.append(aCurrMiter);
+
+ aMiterPolygon.clear();
+ aMiterPolygon.append(aCurrMiter);
+ aMiterPolygon.append(aNextMiter);
+ aMiterPolygon.append(aNextPointRotY);
+ aMiterPolygon.append(aPointRotY);
+ }
+ else
+ {
+ aNewPolygon.append(aPointRotY);
+ aNewPolygon.append(aPointOnXY);
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextPointRotY);
+ }
+ }
+
+ // set normals
+ for(sal_uInt32 b(0); b < aNewPolygon.count(); b++)
+ {
+ aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
+ }
+
+ // create primitive
+ if(aNewPolygon.count())
+ {
+ const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false));
+ }
+
+ if(bMiter && aMiterPolygon.count())
+ {
+ // set normals
+ for(sal_uInt32 c(0); c < aMiterPolygon.count(); c++)
+ {
+ aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
+ }
+
+ // create primitive
+ const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false));
+ }
+
+ // prepare next step
+ if(bFirst || !bLast)
+ {
+ aPointOnXY = aNextPointOnXY;
+ aPointRotY = aNextPointRotY;
+
+ if(bMiter)
+ {
+ aCurrMiter = aNextMiter;
+ }
+ }
+ }
+ }
+ }
+
+ Primitive3DContainer aRetval(aResultVector.size());
+
+ for(size_t a(0); a < aResultVector.size(); a++)
+ {
+ aRetval[a] = Primitive3DReference(aResultVector[a]);
+ }
+
+ return aRetval;
+ }
+
+ basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
+ {
+ // build transformation from unit vector to vector
+ basegfx::B3DHomMatrix aRetval;
+
+ // get applied rotations from angles in XY and in XZ (cartesian)
+ const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
+ const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
+
+ // apply rotations. Rot around Z needs to be done first, so apply in two steps
+ aRetval.rotate(0.0, 0.0, fRotInXY);
+ aRetval.rotate(0.0, fRotInXZ, 0.0);
+
+ return aRetval;
+ }
+ } // end of anonymous namespace
+
+
+using namespace com::sun::star;
+
+ Primitive3DContainer PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ const sal_uInt32 nPointCount(getB3DPolygon().count());
+ std::vector< BasePrimitive3D* > aResultVector;
+
+ if(nPointCount)
+ {
+ if(basegfx::fTools::more(getRadius(), 0.0))
+ {
+ const attribute::MaterialAttribute3D aMaterial(getBColor());
+ static const sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
+ const bool bClosed(getB3DPolygon().isClosed());
+ const bool bNoLineJoin(basegfx::B2DLineJoin::NONE == getLineJoin());
+ const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
+ basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1));
+ basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0));
+
+ for(sal_uInt32 a(0); a < nLoopCount; a++)
+ {
+ // get next data
+ const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount));
+ const basegfx::B3DVector aForw(aNext - aCurr);
+ const double fForwLen(aForw.getLength());
+
+ if(basegfx::fTools::more(fForwLen, 0.0))
+ {
+ // find out if linecap is active
+ const bool bFirst(!a);
+ const bool bLast(a + 1 == nLoopCount);
+ const bool bLineCapPossible(!bClosed && (bFirst || bLast));
+ const bool bLineCapRound(bLineCapPossible && css::drawing::LineCap_ROUND == getLineCap());
+ const bool bLineCapSquare(bLineCapPossible && css::drawing::LineCap_SQUARE == getLineCap());
+
+ // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
+ basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
+
+ // prepare transformations for tube and cap
+ basegfx::B3DHomMatrix aTubeTrans;
+ basegfx::B3DHomMatrix aCapTrans;
+
+ // cap gets radius size
+ aCapTrans.scale(getRadius(), getRadius(), getRadius());
+
+ if(bLineCapSquare)
+ {
+ // when square line cap just prolong line segment in X, maybe 2 x radius when
+ // first and last (simple line segment)
+ const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius());
+
+ aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius());
+
+ if(bFirst)
+ {
+ // correct start positions for tube and cap when first and square prolonged
+ aTubeTrans.translate(-getRadius(), 0.0, 0.0);
+ aCapTrans.translate(-getRadius(), 0.0, 0.0);
+ }
+ }
+ else
+ {
+ // normal tube size
+ aTubeTrans.scale(fForwLen, getRadius(), getRadius());
+ }
+
+ // rotate and translate tube and cap
+ aTubeTrans *= aRotVector;
+ aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+ aCapTrans *= aRotVector;
+ aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+
+ if(bNoLineJoin || (!bClosed && bFirst))
+ {
+ // line start edge, build transformed primitiveVector3D
+ Primitive3DContainer aSequence;
+
+ if(bLineCapRound && bFirst)
+ {
+ // LineCapRound used
+ aSequence = getLineCapRoundSegments(nSegments, aMaterial);
+ }
+ else
+ {
+ // simple closing cap
+ aSequence = getLineCapSegments(nSegments, aMaterial);
+ }
+
+ aResultVector.push_back(new TransformPrimitive3D(aCapTrans, aSequence));
+ }
+ else
+ {
+ const basegfx::B3DVector aBack(aCurr - aLast);
+ const double fCross(basegfx::cross(aBack, aForw).getLength());
+
+ if(!basegfx::fTools::equalZero(fCross))
+ {
+ // line connect non-parallel, aBack, aForw, use getLineJoin()
+ const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. M_PI_2
+ Primitive3DContainer aNewList(
+ getLineJoinSegments(
+ nSegments,
+ aMaterial,
+ fAngle,
+ getMiterMinimumAngle(),
+ getLineJoin()));
+
+ // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
+ basegfx::B3DHomMatrix aInvRotVector(aRotVector);
+ aInvRotVector.invert();
+ basegfx::B3DVector aTransBack(aInvRotVector * aBack);
+ const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
+
+ // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
+ // Also apply usual scaling and translation
+ basegfx::B3DHomMatrix aSphereTrans;
+ aSphereTrans.rotate(0.0, M_PI_2, 0.0);
+ aSphereTrans.rotate(M_PI - fRotInYZ, 0.0, 0.0);
+ aSphereTrans *= aRotVector;
+ aSphereTrans.scale(getRadius(), getRadius(), getRadius());
+ aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+
+ // line start edge, build transformed primitiveVector3D
+ aResultVector.push_back(
+ new TransformPrimitive3D(
+ aSphereTrans,
+ aNewList));
+ }
+ }
+
+ // create line segments, build transformed primitiveVector3D
+ aResultVector.push_back(
+ new TransformPrimitive3D(
+ aTubeTrans,
+ getLineTubeSegments(nSegments, aMaterial)));
+
+ if(bNoLineJoin || (!bClosed && bLast))
+ {
+ // line end edge
+ basegfx::B3DHomMatrix aBackCapTrans;
+
+ // Mirror (line end) and radius scale
+ aBackCapTrans.rotate(0.0, M_PI, 0.0);
+ aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
+
+ if(bLineCapSquare && bLast)
+ {
+ // correct position when square and prolonged
+ aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0);
+ }
+ else
+ {
+ // standard position
+ aBackCapTrans.translate(fForwLen, 0.0, 0.0);
+ }
+
+ // rotate and translate to destination
+ aBackCapTrans *= aRotVector;
+ aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+
+ // get primitiveVector3D
+ Primitive3DContainer aSequence;
+
+ if(bLineCapRound && bLast)
+ {
+ // LineCapRound used
+ aSequence = getLineCapRoundSegments(nSegments, aMaterial);
+ }
+ else
+ {
+ // simple closing cap
+ aSequence = getLineCapSegments(nSegments, aMaterial);
+ }
+
+ aResultVector.push_back(
+ new TransformPrimitive3D(
+ aBackCapTrans,
+ aSequence));
+ }
+ }
+
+ // prepare next loop step
+ aLast = aCurr;
+ aCurr = aNext;
+ }
+ }
+ else
+ {
+ // create hairline
+ aResultVector.push_back(new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor()));
+ }
+ }
+
+ // prepare return value
+ Primitive3DContainer aRetval(aResultVector.size());
+
+ for(size_t a(0); a < aResultVector.size(); a++)
+ {
+ aRetval[a] = Primitive3DReference(aResultVector[a]);
+ }
+
+ return aRetval;
+ }
+
+ PolygonTubePrimitive3D::PolygonTubePrimitive3D(
+ const basegfx::B3DPolygon& rPolygon,
+ const basegfx::BColor& rBColor,
+ double fRadius, basegfx::B2DLineJoin aLineJoin,
+ css::drawing::LineCap aLineCap,
+ double fDegreeStepWidth,
+ double fMiterMinimumAngle)
+ : PolygonHairlinePrimitive3D(rPolygon, rBColor),
+ mfRadius(fRadius),
+ mfDegreeStepWidth(fDegreeStepWidth),
+ mfMiterMinimumAngle(fMiterMinimumAngle),
+ maLineJoin(aLineJoin),
+ maLineCap(aLineCap)
+ {
+ }
+
+ bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
+ {
+ const PolygonTubePrimitive3D& rCompare = static_cast<const PolygonTubePrimitive3D&>(rPrimitive);
+
+ return (getRadius() == rCompare.getRadius()
+ && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
+ && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
+ && getLineJoin() == rCompare.getLineJoin()
+ && getLineCap() == rCompare.getLineCap());
+ }
+
+ return false;
+ }
+
+ Primitive3DContainer PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if(getLast3DDecomposition().empty())
+ {
+ const Primitive3DContainer aNewSequence(impCreate3DDecomposition(rViewInformation));
+ const_cast< PolygonTubePrimitive3D* >(this)->maLast3DDecomposition = aNewSequence;
+ }
+
+ return getLast3DDecomposition();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx
new file mode 100644
index 000000000..41e62b611
--- /dev/null
+++ b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ PolyPolygonMaterialPrimitive3D::PolyPolygonMaterialPrimitive3D(
+ const basegfx::B3DPolyPolygon& rPolyPolygon,
+ const attribute::MaterialAttribute3D& rMaterial,
+ bool bDoubleSided)
+ : maPolyPolygon(rPolyPolygon),
+ maMaterial(rMaterial),
+ mbDoubleSided(bDoubleSided)
+ {
+ }
+
+ bool PolyPolygonMaterialPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BasePrimitive3D::operator==(rPrimitive))
+ {
+ const PolyPolygonMaterialPrimitive3D& rCompare = static_cast<const PolyPolygonMaterialPrimitive3D&>(rPrimitive);
+
+ return (getB3DPolyPolygon() == rCompare.getB3DPolyPolygon()
+ && getMaterial() == rCompare.getMaterial()
+ && getDoubleSided() == rCompare.getDoubleSided());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange PolyPolygonMaterialPrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return basegfx::utils::getRange(getB3DPolyPolygon());
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolyPolygonMaterialPrimitive3D, PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx
new file mode 100644
index 000000000..6297c7df0
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrcubeprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrCubePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ const basegfx::B3DRange aUnitRange(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+ Primitive3DContainer aRetval;
+ basegfx::B3DPolyPolygon aFill(basegfx::utils::createCubeFillPolyPolygonFromB3DRange(aUnitRange));
+
+ // normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPECIFIC == getSdr3DObjectAttribute().getNormalsKind()
+ || css::drawing::NormalsKind_SPHERE == getSdr3DObjectAttribute().getNormalsKind())
+ {
+ // create sphere normals
+ const basegfx::B3DPoint aCenter(basegfx::utils::getRange(aFill).getCenter());
+ aFill = basegfx::utils::applyDefaultNormalsSphere(aFill, aCenter);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ // invert normals
+ aFill = basegfx::utils::invertNormals(aFill);
+ }
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // handle texture coordinates X
+ const bool bParallelX(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bObjectSpecificX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bSphereX(!bParallelX && (css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionX()));
+
+ // handle texture coordinates Y
+ const bool bParallelY(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bObjectSpecificY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bSphereY(!bParallelY && (css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionY()));
+
+ if(bParallelX || bParallelY)
+ {
+ // apply parallel texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFill, aRange, bParallelX, bParallelY);
+ }
+
+ if(bSphereX || bSphereY)
+ {
+ // apply spherical texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ const basegfx::B3DPoint aCenter(aRange.getCenter());
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesSphere(aFill, aCenter, bSphereX, bSphereY);
+ }
+
+ if(bObjectSpecificX || bObjectSpecificY)
+ {
+ // object-specific
+ for(sal_uInt32 a(0); a < aFill.count(); a++)
+ {
+ basegfx::B3DPolygon aTmpPoly(aFill.getB3DPolygon(a));
+
+ if(aTmpPoly.count() >= 4)
+ {
+ for(sal_uInt32 b(0); b < 4; b++)
+ {
+ basegfx::B2DPoint aPoint(aTmpPoly.getTextureCoordinate(b));
+
+ if(bObjectSpecificX)
+ {
+ aPoint.setX((1 == b || 2 == b) ? 1.0 : 0.0);
+ }
+
+ if(bObjectSpecificY)
+ {
+ aPoint.setY((2 == b || 3 == b) ? 1.0 : 0.0);
+ }
+
+ aTmpPoly.setTextureCoordinate(b, aPoint);
+ }
+
+ aFill.setB3DPolygon(a, aTmpPoly);
+ }
+ }
+ }
+
+ // transform texture coordinates to texture size
+ basegfx::B2DHomMatrix aTexMatrix;
+ aTexMatrix.scale(getTextureSize().getX(), getTextureSize().getY());
+ aFill.transformTextureCoordinates(aTexMatrix);
+ }
+
+ // build vector of PolyPolygons
+ std::vector< basegfx::B3DPolyPolygon > a3DPolyPolygonVector;
+
+ for(sal_uInt32 a(0); a < aFill.count(); a++)
+ {
+ a3DPolyPolygonVector.emplace_back(aFill.getB3DPolygon(a));
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ basegfx::B3DPolyPolygon aLine(basegfx::utils::createCubePolyPolygonFromB3DRange(aUnitRange));
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault() && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+
+ return aRetval;
+ }
+
+ SdrCubePrimitive3D::SdrCubePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute)
+ {
+ }
+
+ basegfx::B3DRange SdrCubePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2.
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return getStandard3DRange();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrCubePrimitive3D, PRIMITIVE3D_ID_SDRCUBEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
new file mode 100644
index 000000000..ec6bfaf3c
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive3d/baseprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <primitive3d/textureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
+#include <primitive3d/hatchtextureprimitive3d.hxx>
+#include <primitive3d/shadowprimitive3d.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrobjectattribute3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <primitive3d/hiddengeometryprimitive3d.hxx>
+#include <rtl/ref.hxx>
+
+
+namespace drawinglayer::primitive3d
+{
+ basegfx::B3DRange getRangeFrom3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill)
+ {
+ basegfx::B3DRange aRetval;
+
+ for(const basegfx::B3DPolyPolygon & a : rFill)
+ {
+ aRetval.expand(basegfx::utils::getRange(a));
+ }
+
+ return aRetval;
+ }
+
+ void applyNormalsKindSphereTo3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill, const basegfx::B3DRange& rRange)
+ {
+ // create sphere normals
+ const basegfx::B3DPoint aCenter(rRange.getCenter());
+
+ for(basegfx::B3DPolyPolygon & a : rFill)
+ {
+ a = basegfx::utils::applyDefaultNormalsSphere(a, aCenter);
+ }
+ }
+
+ void applyNormalsKindFlatTo3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill)
+ {
+ for(basegfx::B3DPolyPolygon & a : rFill)
+ {
+ a.clearNormals();
+ }
+ }
+
+ void applyNormalsInvertTo3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill)
+ {
+ // invert normals
+ for(basegfx::B3DPolyPolygon & a : rFill)
+ {
+ a = basegfx::utils::invertNormals(a);
+ }
+ }
+
+ void applyTextureTo3DGeometry(
+ css::drawing::TextureProjectionMode eModeX,
+ css::drawing::TextureProjectionMode eModeY,
+ std::vector< basegfx::B3DPolyPolygon >& rFill,
+ const basegfx::B3DRange& rRange,
+ const basegfx::B2DVector& rTextureSize)
+ {
+ // handle texture coordinates X
+ const bool bParallelX(css::drawing::TextureProjectionMode_PARALLEL == eModeX);
+ const bool bSphereX(!bParallelX && (css::drawing::TextureProjectionMode_SPHERE == eModeX));
+
+ // handle texture coordinates Y
+ const bool bParallelY(css::drawing::TextureProjectionMode_PARALLEL == eModeY);
+ const bool bSphereY(!bParallelY && (css::drawing::TextureProjectionMode_SPHERE == eModeY));
+
+ if(bParallelX || bParallelY)
+ {
+ // apply parallel texture coordinates in X and/or Y
+ for(auto & a: rFill)
+ {
+ a = basegfx::utils::applyDefaultTextureCoordinatesParallel(a, rRange, bParallelX, bParallelY);
+ }
+ }
+
+ if(bSphereX || bSphereY)
+ {
+ // apply spherical texture coordinates in X and/or Y
+ const basegfx::B3DPoint aCenter(rRange.getCenter());
+
+ for(auto & a: rFill)
+ {
+ a = basegfx::utils::applyDefaultTextureCoordinatesSphere(a, aCenter, bSphereX, bSphereY);
+ }
+ }
+
+ // transform texture coordinates to texture size
+ basegfx::B2DHomMatrix aTexMatrix;
+ aTexMatrix.scale(rTextureSize.getX(), rTextureSize.getY());
+
+ for(auto & a: rFill)
+ {
+ a.transformTextureCoordinates(aTexMatrix);
+ }
+ }
+
+ Primitive3DContainer create3DPolyPolygonLinePrimitives(
+ const basegfx::B3DPolyPolygon& rUnitPolyPolygon,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const attribute::SdrLineAttribute& rLine)
+ {
+ // prepare fully scaled polyPolygon
+ basegfx::B3DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
+ aScaledPolyPolygon.transform(rObjectTransform);
+
+ // create line and stroke attribute
+ const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap());
+ const attribute::StrokeAttribute aStrokeAttribute(std::vector(rLine.getDotDashArray()), rLine.getFullDotDashLen());
+
+ // create primitives
+ Primitive3DContainer aRetval(aScaledPolyPolygon.count());
+
+ for(sal_uInt32 a(0); a < aScaledPolyPolygon.count(); a++)
+ {
+ const Primitive3DReference xRef(new PolygonStrokePrimitive3D(aScaledPolyPolygon.getB3DPolygon(a), aLineAttribute, aStrokeAttribute));
+ aRetval[a] = xRef;
+ }
+
+ if(0.0 != rLine.getTransparence())
+ {
+ // create UnifiedTransparenceTexturePrimitive3D, add created primitives and exchange
+ const Primitive3DReference xRef(new UnifiedTransparenceTexturePrimitive3D(rLine.getTransparence(), aRetval));
+ aRetval = { xRef };
+ }
+
+ return aRetval;
+ }
+
+ Primitive3DContainer create3DPolyPolygonFillPrimitives(
+ const std::vector< basegfx::B3DPolyPolygon >& r3DPolyPolygonVector,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::Sdr3DObjectAttribute& aSdr3DObjectAttribute,
+ const attribute::SdrFillAttribute& rFill,
+ const attribute::FillGradientAttribute& rFillGradient)
+ {
+ Primitive3DContainer aRetval;
+
+ if(!r3DPolyPolygonVector.empty())
+ {
+ // create list of simple fill primitives
+ aRetval.resize(r3DPolyPolygonVector.size());
+
+ for(size_t a(0); a < r3DPolyPolygonVector.size(); a++)
+ {
+ // get scaled PolyPolygon
+ basegfx::B3DPolyPolygon aScaledPolyPolygon(r3DPolyPolygonVector[a]);
+ aScaledPolyPolygon.transform(rObjectTransform);
+
+ if(aScaledPolyPolygon.areNormalsUsed())
+ {
+ aScaledPolyPolygon.transformNormals(rObjectTransform);
+ }
+
+ const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(
+ aScaledPolyPolygon,
+ aSdr3DObjectAttribute.getMaterial(),
+ aSdr3DObjectAttribute.getDoubleSided()));
+ aRetval[a] = xRef;
+ }
+
+ // look for and evtl. build texture sub-group primitive
+ if(!rFill.getGradient().isDefault()
+ || !rFill.getHatch().isDefault()
+ || !rFill.getFillGraphic().isDefault())
+ {
+ bool bModulate(css::drawing::TextureMode_MODULATE == aSdr3DObjectAttribute.getTextureMode());
+ bool bFilter(aSdr3DObjectAttribute.getTextureFilter());
+ rtl::Reference<BasePrimitive3D> pNewTexturePrimitive3D;
+
+ if(!rFill.getGradient().isDefault())
+ {
+ // create gradientTexture3D with sublist, add to local aRetval
+ pNewTexturePrimitive3D = new GradientTexturePrimitive3D(
+ rFill.getGradient(),
+ aRetval,
+ rTextureSize,
+ bModulate,
+ bFilter);
+ }
+ else if(!rFill.getHatch().isDefault())
+ {
+ // create hatchTexture3D with sublist, add to local aRetval
+ pNewTexturePrimitive3D = new HatchTexturePrimitive3D(
+ rFill.getHatch(),
+ aRetval,
+ rTextureSize,
+ bModulate,
+ bFilter);
+ }
+ else // if(!rFill.getFillGraphic().isDefault())
+ {
+ // create bitmapTexture3D with sublist, add to local aRetval
+ const basegfx::B2DRange aTexRange(0.0, 0.0, rTextureSize.getX(), rTextureSize.getY());
+
+ pNewTexturePrimitive3D = new BitmapTexturePrimitive3D(
+ rFill.getFillGraphic().createFillGraphicAttribute(aTexRange),
+ aRetval,
+ rTextureSize,
+ bModulate,
+ bFilter);
+ }
+
+ // exchange aRetval content with texture group
+ const Primitive3DReference xRef(pNewTexturePrimitive3D);
+ aRetval = { xRef };
+
+ if(css::drawing::TextureKind2_LUMINANCE == aSdr3DObjectAttribute.getTextureKind())
+ {
+ // use modified color primitive to force textures to gray
+ const basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_gray>();
+ const Primitive3DReference xRef2(
+ new ModifiedColorPrimitive3D(
+ aRetval,
+ aBColorModifier));
+
+ aRetval = { xRef2 };
+ }
+ }
+
+ if(0.0 != rFill.getTransparence())
+ {
+ // create UnifiedTransparenceTexturePrimitive3D with sublist and exchange
+ const Primitive3DReference xRef(new UnifiedTransparenceTexturePrimitive3D(rFill.getTransparence(), aRetval));
+ aRetval = { xRef };
+ }
+ else if(!rFillGradient.isDefault())
+ {
+ // create TransparenceTexturePrimitive3D with sublist and exchange
+ const Primitive3DReference xRef(new TransparenceTexturePrimitive3D(rFillGradient, aRetval, rTextureSize));
+ aRetval = { xRef };
+ }
+ }
+
+ return aRetval;
+ }
+
+ Primitive3DContainer createShadowPrimitive3D(
+ const Primitive3DContainer& rSource,
+ const attribute::SdrShadowAttribute& rShadow,
+ bool bShadow3D)
+ {
+ // create Shadow primitives. Uses already created primitives
+ if(!rSource.empty() && !basegfx::fTools::moreOrEqual(rShadow.getTransparence(), 1.0))
+ {
+ // prepare new list for shadow geometry
+ basegfx::B2DHomMatrix aShadowOffset;
+ aShadowOffset.set(0, 2, rShadow.getOffset().getX());
+ aShadowOffset.set(1, 2, rShadow.getOffset().getY());
+
+ // create shadow primitive and add primitives
+ const Primitive3DReference xRef(new ShadowPrimitive3D(aShadowOffset, rShadow.getColor(), rShadow.getTransparence(), bShadow3D, rSource));
+ return { xRef };
+ }
+ else
+ {
+ return Primitive3DContainer();
+ }
+ }
+
+ Primitive3DContainer createHiddenGeometryPrimitives3D(
+ const std::vector< basegfx::B3DPolyPolygon >& r3DPolyPolygonVector,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::Sdr3DObjectAttribute& aSdr3DObjectAttribute)
+ {
+ // create hidden sub-geometry which can be used for HitTest
+ // and BoundRect calculations, but will not be visualized
+ const attribute::SdrFillAttribute aSimplifiedFillAttribute(
+ 0.0,
+ basegfx::BColor(),
+ attribute::FillGradientAttribute(),
+ attribute::FillHatchAttribute(),
+ attribute::SdrFillGraphicAttribute());
+
+ const Primitive3DReference aHidden(
+ new HiddenGeometryPrimitive3D(
+ create3DPolyPolygonFillPrimitives(
+ r3DPolyPolygonVector,
+ rObjectTransform,
+ rTextureSize,
+ aSdr3DObjectAttribute,
+ aSimplifiedFillAttribute,
+ attribute::FillGradientAttribute())));
+
+ return { aHidden };
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx
new file mode 100644
index 000000000..a7015ff8e
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx
@@ -0,0 +1,982 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrextrudelathetools3d.hxx>
+
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/range/b3drange.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <numeric>
+
+
+// decomposition helpers for extrude/lathe (rotation) objects
+
+namespace
+{
+
+ // common helpers
+
+ basegfx::B2DPolyPolygon impScalePolyPolygonOnCenter(
+ const basegfx::B2DPolyPolygon& rSource,
+ double fScale)
+ {
+ basegfx::B2DPolyPolygon aRetval(rSource);
+
+ if(!basegfx::fTools::equalZero(fScale))
+ {
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(rSource));
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ basegfx::B2DHomMatrix aTrans;
+
+ aTrans.translate(-aCenter.getX(), -aCenter.getY());
+ aTrans.scale(fScale, fScale);
+ aTrans.translate(aCenter.getX(), aCenter.getY());
+ aRetval.transform(aTrans);
+ }
+
+ return aRetval;
+ }
+
+ void impGetOuterPolyPolygon(
+ basegfx::B2DPolyPolygon& rPolygon,
+ basegfx::B2DPolyPolygon& rOuterPolyPolygon,
+ double fOffset,
+ bool bCharacterMode)
+ {
+ rOuterPolyPolygon = rPolygon;
+
+ if(!basegfx::fTools::more(fOffset, 0.0))
+ return;
+
+ if(bCharacterMode)
+ {
+ // grow the outside polygon and scale all polygons to original size. This is done
+ // to avoid a shrink which potentially would lead to self-intersections, but changes
+ // the original polygon -> not a precision step, so e.g. not usable for charts
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolygon));
+ rPolygon = basegfx::utils::growInNormalDirection(rPolygon, fOffset);
+ const basegfx::B2DRange aGrownRange(basegfx::utils::getRange(rPolygon));
+ const double fScaleX(basegfx::fTools::equalZero(aGrownRange.getWidth()) ? 1.0 : aRange.getWidth() / aGrownRange.getWidth());
+ const double fScaleY(basegfx::fTools::equalZero(aGrownRange.getHeight())? 1.0 : aRange.getHeight() / aGrownRange.getHeight());
+ basegfx::B2DHomMatrix aScaleTrans;
+
+ aScaleTrans.translate(-aGrownRange.getMinX(), -aGrownRange.getMinY());
+ aScaleTrans.scale(fScaleX, fScaleY);
+ aScaleTrans.translate(aRange.getMinX(), aRange.getMinY());
+ rPolygon.transform(aScaleTrans);
+ rOuterPolyPolygon.transform(aScaleTrans);
+ }
+ else
+ {
+ // use more precision, shrink the outer polygons. Since this may lead to self-intersections,
+ // some kind of correction should be applied here after that step
+ rOuterPolyPolygon = basegfx::utils::growInNormalDirection(rPolygon, -fOffset);
+ // basegfx::utils::correctGrowShrinkPolygonPair(rPolygon, rOuterPolyPolygon);
+ }
+ }
+
+ void impAddInBetweenFill(
+ basegfx::B3DPolyPolygon& rTarget,
+ const basegfx::B3DPolyPolygon& rPolA,
+ const basegfx::B3DPolyPolygon& rPolB,
+ double fTexVerStart,
+ double fTexVerStop,
+ bool bCreateNormals,
+ bool bCreateTextureCoordinates)
+ {
+ OSL_ENSURE(rPolA.count() == rPolB.count(), "impAddInBetweenFill: unequally sized polygons (!)");
+ const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ const basegfx::B3DPolygon& aSubA(rPolA.getB3DPolygon(a));
+ const basegfx::B3DPolygon& aSubB(rPolB.getB3DPolygon(a));
+ OSL_ENSURE(aSubA.count() == aSubB.count(), "impAddInBetweenFill: unequally sized polygons (!)");
+ const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(aSubA.isClosed() ? nPointCount : nPointCount - 1);
+ double fTexHorMultiplicatorA(0.0), fTexHorMultiplicatorB(0.0);
+ double fPolygonPosA(0.0), fPolygonPosB(0.0);
+
+ if(bCreateTextureCoordinates)
+ {
+ const double fPolygonLengthA(basegfx::utils::getLength(aSubA));
+ fTexHorMultiplicatorA = basegfx::fTools::equalZero(fPolygonLengthA) ? 1.0 : 1.0 / fPolygonLengthA;
+
+ const double fPolygonLengthB(basegfx::utils::getLength(aSubB));
+ fTexHorMultiplicatorB = basegfx::fTools::equalZero(fPolygonLengthB) ? 1.0 : 1.0 / fPolygonLengthB;
+ }
+
+ for(sal_uInt32 b(0); b < nEdgeCount; b++)
+ {
+ const sal_uInt32 nIndexA(b);
+ const sal_uInt32 nIndexB((b + 1) % nPointCount);
+
+ const basegfx::B3DPoint aStartA(aSubA.getB3DPoint(nIndexA));
+ const basegfx::B3DPoint aEndA(aSubA.getB3DPoint(nIndexB));
+ const basegfx::B3DPoint aStartB(aSubB.getB3DPoint(nIndexA));
+ const basegfx::B3DPoint aEndB(aSubB.getB3DPoint(nIndexB));
+
+ basegfx::B3DPolygon aNew;
+ aNew.setClosed(true);
+
+ aNew.append(aStartA);
+ aNew.append(aStartB);
+ aNew.append(aEndB);
+ aNew.append(aEndA);
+
+ if(bCreateNormals)
+ {
+ aNew.setNormal(0, aSubA.getNormal(nIndexA));
+ aNew.setNormal(1, aSubB.getNormal(nIndexA));
+ aNew.setNormal(2, aSubB.getNormal(nIndexB));
+ aNew.setNormal(3, aSubA.getNormal(nIndexB));
+ }
+
+ if(bCreateTextureCoordinates)
+ {
+ const double fRelTexAL(fPolygonPosA * fTexHorMultiplicatorA);
+ const double fEdgeLengthA(basegfx::B3DVector(aEndA - aStartA).getLength());
+ fPolygonPosA += fEdgeLengthA;
+ const double fRelTexAR(fPolygonPosA * fTexHorMultiplicatorA);
+
+ const double fRelTexBL(fPolygonPosB * fTexHorMultiplicatorB);
+ const double fEdgeLengthB(basegfx::B3DVector(aEndB - aStartB).getLength());
+ fPolygonPosB += fEdgeLengthB;
+ const double fRelTexBR(fPolygonPosB * fTexHorMultiplicatorB);
+
+ aNew.setTextureCoordinate(0, basegfx::B2DPoint(fRelTexAL, fTexVerStart));
+ aNew.setTextureCoordinate(1, basegfx::B2DPoint(fRelTexBL, fTexVerStop));
+ aNew.setTextureCoordinate(2, basegfx::B2DPoint(fRelTexBR, fTexVerStop));
+ aNew.setTextureCoordinate(3, basegfx::B2DPoint(fRelTexAR, fTexVerStart));
+ }
+
+ rTarget.append(aNew);
+ }
+ }
+ }
+ }
+
+ void impSetNormal(
+ basegfx::B3DPolyPolygon& rCandidate,
+ const basegfx::B3DVector& rNormal)
+ {
+ for(sal_uInt32 a(0); a < rCandidate.count(); a++)
+ {
+ basegfx::B3DPolygon aSub(rCandidate.getB3DPolygon(a));
+
+ for(sal_uInt32 b(0); b < aSub.count(); b++)
+ {
+ aSub.setNormal(b, rNormal);
+ }
+
+ rCandidate.setB3DPolygon(a, aSub);
+ }
+ }
+
+ void impCreateInBetweenNormals(
+ basegfx::B3DPolyPolygon& rPolA,
+ basegfx::B3DPolyPolygon& rPolB)
+ {
+ OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a));
+ basegfx::B3DPolygon aSubB(rPolB.getB3DPolygon(a));
+ OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
+
+ if(nPointCount)
+ {
+ basegfx::B3DPoint aPrevA(aSubA.getB3DPoint(nPointCount - 1));
+ basegfx::B3DPoint aCurrA(aSubA.getB3DPoint(0));
+ const bool bClosed(aSubA.isClosed());
+
+ for(sal_uInt32 b(0); b < nPointCount; b++)
+ {
+ const sal_uInt32 nIndNext((b + 1) % nPointCount);
+ const basegfx::B3DPoint aNextA(aSubA.getB3DPoint(nIndNext));
+ const basegfx::B3DPoint aCurrB(aSubB.getB3DPoint(b));
+
+ // vector to back
+ basegfx::B3DVector aDepth(aCurrB - aCurrA);
+ aDepth.normalize();
+
+ if(aDepth.equalZero())
+ {
+ // no difference, try to get depth from next point
+ const basegfx::B3DPoint aNextB(aSubB.getB3DPoint(nIndNext));
+ aDepth = aNextB - aNextA;
+ aDepth.normalize();
+ }
+
+ // vector to left (correct for non-closed lines)
+ const bool bFirstAndNotClosed(!bClosed && 0 == b);
+ basegfx::B3DVector aLeft(bFirstAndNotClosed ? aCurrA - aNextA : aPrevA - aCurrA);
+ aLeft.normalize();
+
+ // create left normal
+ const basegfx::B3DVector aNormalLeft(aDepth.getPerpendicular(aLeft));
+
+ // smooth horizontal normals
+ {
+ // vector to right (correct for non-closed lines)
+ const bool bLastAndNotClosed(!bClosed && b + 1 == nPointCount);
+ basegfx::B3DVector aRight(bLastAndNotClosed ? aCurrA - aPrevA : aNextA - aCurrA);
+ aRight.normalize();
+
+ // create right normal
+ const basegfx::B3DVector aNormalRight(aRight.getPerpendicular(aDepth));
+
+ // create smoothed in-between normal
+ basegfx::B3DVector aNewNormal(aNormalLeft + aNormalRight);
+ aNewNormal.normalize();
+
+ // set as new normal at polygons
+ aSubA.setNormal(b, aNewNormal);
+ aSubB.setNormal(b, aNewNormal);
+ }
+
+ // prepare next step
+ aPrevA = aCurrA;
+ aCurrA = aNextA;
+ }
+
+ rPolA.setB3DPolygon(a, aSubA);
+ rPolB.setB3DPolygon(a, aSubB);
+ }
+ }
+ }
+
+ void impMixNormals(
+ basegfx::B3DPolyPolygon& rPolA,
+ const basegfx::B3DPolyPolygon& rPolB,
+ double fWeightA)
+ {
+ const double fWeightB(1.0 - fWeightA);
+ OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a));
+ const basegfx::B3DPolygon& aSubB(rPolB.getB3DPolygon(a));
+ OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
+
+ for(sal_uInt32 b(0); b < nPointCount; b++)
+ {
+ const basegfx::B3DVector aVA(aSubA.getNormal(b) * fWeightA);
+ const basegfx::B3DVector aVB(aSubB.getNormal(b) * fWeightB);
+ basegfx::B3DVector aVNew(aVA + aVB);
+ aVNew.normalize();
+ aSubA.setNormal(b, aVNew);
+ }
+
+ rPolA.setB3DPolygon(a, aSubA);
+ }
+ }
+
+ bool impHasCutWith(const basegfx::B2DPolygon& rPoly, const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd)
+ {
+ // polygon is closed, one of the points is a member
+ const sal_uInt32 nPointCount(rPoly.count());
+
+ if(!nPointCount)
+ return false;
+
+ basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0));
+ const basegfx::B2DVector aVector(rEnd - rStart);
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex));
+ const basegfx::B2DVector aEdgeVector(aNext - aCurrent);
+
+ if(basegfx::utils::findCut(
+ rStart, aVector,
+ aCurrent, aEdgeVector) != CutFlagValue::NONE)
+ {
+ return true;
+ }
+
+ aCurrent = aNext;
+ }
+
+ return false;
+ }
+} // end of anonymous namespace
+
+
+namespace drawinglayer::primitive3d
+{
+ void createLatheSlices(
+ Slice3DVector& rSliceVector,
+ const basegfx::B2DPolyPolygon& rSource,
+ double fBackScale,
+ double fDiagonal,
+ double fRotation,
+ sal_uInt32 nSteps,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ {
+ if(basegfx::fTools::equalZero(fRotation) || 0 == nSteps)
+ {
+ // no rotation or no steps, just one plane
+ rSliceVector.emplace_back(rSource, basegfx::B3DHomMatrix());
+ }
+ else
+ {
+ const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0));
+ const bool bClosedRotation(!bBackScale && basegfx::fTools::equal(fRotation, 2 * M_PI));
+ basegfx::B2DPolyPolygon aFront(rSource);
+ basegfx::B2DPolyPolygon aBack(rSource);
+ basegfx::B3DHomMatrix aTransformBack;
+ basegfx::B2DPolyPolygon aOuterBack;
+
+ if(bClosedRotation)
+ {
+ bCloseFront = bCloseBack = false;
+ }
+
+ if(bBackScale)
+ {
+ // avoid null zoom
+ if(basegfx::fTools::equalZero(fBackScale))
+ {
+ fBackScale = 0.000001;
+ }
+
+ // back is scaled compared to front, create scaled version
+ aBack = impScalePolyPolygonOnCenter(aBack, fBackScale);
+ }
+
+ if(bCloseFront || bCloseBack)
+ {
+ const basegfx::B2DRange aBaseRange(basegfx::utils::getRange(aFront));
+ const double fOuterLength(aBaseRange.getMaxX() * fRotation);
+ const double fInnerLength(aBaseRange.getMinX() * fRotation);
+ const double fAverageLength((fOuterLength + fInnerLength) * 0.5);
+
+ if(bCloseFront)
+ {
+ const double fOffsetLen((fAverageLength / 12.0) * fDiagonal);
+ basegfx::B2DPolyPolygon aOuterFront;
+ impGetOuterPolyPolygon(aFront, aOuterFront, fOffsetLen, bCharacterMode);
+ basegfx::B3DHomMatrix aTransform;
+ aTransform.translate(0.0, 0.0, fOffsetLen);
+ rSliceVector.emplace_back(aOuterFront, aTransform, SLICETYPE3D_FRONTCAP);
+ }
+
+ if(bCloseBack)
+ {
+ const double fOffsetLen((fAverageLength / 12.0) * fDiagonal);
+ impGetOuterPolyPolygon(aBack, aOuterBack, fOffsetLen, bCharacterMode);
+ aTransformBack.translate(0.0, 0.0, -fOffsetLen);
+ aTransformBack.rotate(0.0, fRotation, 0.0);
+ }
+ }
+
+ // add start polygon (a = 0)
+ if(!bClosedRotation)
+ {
+ rSliceVector.emplace_back(aFront, basegfx::B3DHomMatrix());
+ }
+
+ // create segments (a + 1 .. nSteps)
+ const double fStepSize(1.0 / static_cast<double>(nSteps));
+
+ for(sal_uInt32 a(0); a < nSteps; a++)
+ {
+ const double fStep(static_cast<double>(a + 1) * fStepSize);
+ basegfx::B2DPolyPolygon aNewPoly(bBackScale ? basegfx::utils::interpolate(aFront, aBack, fStep) : aFront);
+ basegfx::B3DHomMatrix aNewMat;
+ aNewMat.rotate(0.0, fRotation * fStep, 0.0);
+ rSliceVector.emplace_back(aNewPoly, aNewMat);
+ }
+
+ if(bCloseBack)
+ {
+ rSliceVector.emplace_back(aOuterBack, aTransformBack, SLICETYPE3D_BACKCAP);
+ }
+ }
+ }
+
+ void createExtrudeSlices(
+ Slice3DVector& rSliceVector,
+ const basegfx::B2DPolyPolygon& rSource,
+ double fBackScale,
+ double fDiagonal,
+ double fDepth,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ {
+ if(basegfx::fTools::equalZero(fDepth))
+ {
+ // no depth, just one plane
+ rSliceVector.emplace_back(rSource, basegfx::B3DHomMatrix());
+ }
+ else
+ {
+ // there is depth, create Polygons for front,back and their default depth positions
+ basegfx::B2DPolyPolygon aFront(rSource);
+ basegfx::B2DPolyPolygon aBack(rSource);
+ const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0));
+ double fZFront(fDepth); // default depth for aFront
+ double fZBack(0.0); // default depth for aBack
+ basegfx::B2DPolyPolygon aOuterBack;
+
+ if(bBackScale)
+ {
+ // avoid null zoom
+ if(basegfx::fTools::equalZero(fBackScale))
+ {
+ fBackScale = 0.000001;
+ }
+
+ // aFront is scaled compared to aBack, create scaled version
+ aFront = impScalePolyPolygonOnCenter(aFront, fBackScale);
+ }
+
+ if(bCloseFront)
+ {
+ const double fOffset(fDepth * fDiagonal * 0.5);
+ fZFront = fDepth - fOffset;
+ basegfx::B2DPolyPolygon aOuterFront;
+ impGetOuterPolyPolygon(aFront, aOuterFront, fOffset, bCharacterMode);
+ basegfx::B3DHomMatrix aTransformFront;
+ aTransformFront.translate(0.0, 0.0, fDepth);
+ rSliceVector.emplace_back(aOuterFront, aTransformFront, SLICETYPE3D_FRONTCAP);
+ }
+
+ if(bCloseBack)
+ {
+ const double fOffset(fDepth * fDiagonal * 0.5);
+ fZBack = fOffset;
+ impGetOuterPolyPolygon(aBack, aOuterBack, fOffset, bCharacterMode);
+ }
+
+ // add front and back polygons at evtl. changed depths
+ {
+ basegfx::B3DHomMatrix aTransformA, aTransformB;
+
+ aTransformA.translate(0.0, 0.0, fZFront);
+ rSliceVector.emplace_back(aFront, aTransformA);
+
+ aTransformB.translate(0.0, 0.0, fZBack);
+ rSliceVector.emplace_back(aBack, aTransformB);
+ }
+
+ if(bCloseBack)
+ {
+ rSliceVector.emplace_back(aOuterBack, basegfx::B3DHomMatrix(), SLICETYPE3D_BACKCAP);
+ }
+ }
+ }
+
+ basegfx::B3DPolyPolygon extractHorizontalLinesFromSlice(const Slice3DVector& rSliceVector, bool bCloseHorLines)
+ {
+ basegfx::B3DPolyPolygon aRetval;
+ const sal_uInt32 nNumSlices(rSliceVector.size());
+
+ if(nNumSlices)
+ {
+ const sal_uInt32 nSlideSubPolygonCount(rSliceVector[0].getB3DPolyPolygon().count());
+
+ for(sal_uInt32 b(0); b < nSlideSubPolygonCount; b++)
+ {
+ const sal_uInt32 nSubPolygonPointCount(rSliceVector[0].getB3DPolyPolygon().getB3DPolygon(b).count());
+
+ for(sal_uInt32 c(0); c < nSubPolygonPointCount; c++)
+ {
+ basegfx::B3DPolygon aNew;
+
+ for(sal_uInt32 d(0); d < nNumSlices; d++)
+ {
+ const bool bSamePolygonCount(nSlideSubPolygonCount == rSliceVector[d].getB3DPolyPolygon().count());
+ const bool bSamePointCount(nSubPolygonPointCount == rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).count());
+
+ if(bSamePolygonCount && bSamePointCount)
+ {
+ aNew.append(rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).getB3DPoint(c));
+ }
+ else
+ {
+ OSL_ENSURE(bSamePolygonCount, "Slice tools::PolyPolygon with different Polygon count (!)");
+ OSL_ENSURE(bSamePointCount, "Slice Polygon with different point count (!)");
+ }
+ }
+
+ aNew.setClosed(bCloseHorLines);
+ aRetval.append(aNew);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ basegfx::B3DPolyPolygon extractVerticalLinesFromSlice(const Slice3DVector& rSliceVector)
+ {
+ basegfx::B3DPolyPolygon aRetval;
+ const sal_uInt32 nNumSlices(rSliceVector.size());
+
+ for(sal_uInt32 a(0); a < nNumSlices; a++)
+ {
+ aRetval.append(rSliceVector[a].getB3DPolyPolygon());
+ }
+
+ return aRetval;
+ }
+
+ void extractPlanesFromSlice(
+ std::vector< basegfx::B3DPolyPolygon >& rFill,
+ const Slice3DVector& rSliceVector,
+ bool bCreateNormals,
+ bool bSmoothNormals,
+ bool bSmoothLids,
+ bool bClosed,
+ double fSmoothNormalsMix,
+ double fSmoothLidsMix,
+ bool bCreateTextureCoordinates,
+ const basegfx::B2DHomMatrix& rTexTransform)
+ {
+ const sal_uInt32 nNumSlices(rSliceVector.size());
+
+ if(!nNumSlices)
+ return;
+
+ // common parameters
+ const sal_uInt32 nLoopCount(bClosed ? nNumSlices : nNumSlices - 1);
+ basegfx::B3DPolyPolygon aEdgeRounding;
+ sal_uInt32 a;
+
+ // texture parameters
+ double fInvTexHeight(1.0);
+ std::vector<double> aTexHeightArray;
+ basegfx::B3DRange aTexRangeFront;
+ basegfx::B3DRange aTexRangeBack;
+
+ if(bCreateTextureCoordinates)
+ {
+ aTexRangeFront = basegfx::utils::getRange(rSliceVector[0].getB3DPolyPolygon());
+ aTexRangeBack = basegfx::utils::getRange(rSliceVector[nNumSlices - 1].getB3DPolyPolygon());
+
+ if(aTexRangeBack.getDepth() > aTexRangeBack.getWidth())
+ {
+ // last polygon is rotated so that depth is bigger than width, exchange X and Z
+ // for making applyDefaultTextureCoordinatesParallel use Z instead of X for
+ // horizontal texture coordinate
+ aTexRangeBack = basegfx::B3DRange(
+ aTexRangeBack.getMinZ(), aTexRangeBack.getMinY(), aTexRangeBack.getMinX(),
+ aTexRangeBack.getMaxZ(), aTexRangeBack.getMaxY(), aTexRangeBack.getMaxX());
+ }
+
+ basegfx::B3DPoint aCenter(basegfx::utils::getRange(rSliceVector[0].getB3DPolyPolygon()).getCenter());
+
+ for(a = 0; a < nLoopCount; a++)
+ {
+ const basegfx::B3DPoint aNextCenter(basegfx::utils::getRange(rSliceVector[(a + 1) % nNumSlices].getB3DPolyPolygon()).getCenter());
+ const double fLength(basegfx::B3DVector(aNextCenter - aCenter).getLength());
+ aTexHeightArray.push_back(fLength);
+ aCenter = aNextCenter;
+ }
+
+ const double fTexHeight(std::accumulate(aTexHeightArray.begin(), aTexHeightArray.end(), 0.0));
+
+ if(!basegfx::fTools::equalZero(fTexHeight))
+ {
+ fInvTexHeight = 1.0 / fTexHeight;
+ }
+ }
+
+ if(nLoopCount)
+ {
+ double fTexHeightPos(0.0);
+ for(a = 0; a < nLoopCount; a++)
+ {
+ const Slice3D& rSliceA(rSliceVector[a]);
+ const Slice3D& rSliceB(rSliceVector[(a + 1) % nNumSlices]);
+ const bool bAcceptPair(SLICETYPE3D_REGULAR == rSliceA.getSliceType() && SLICETYPE3D_REGULAR == rSliceB.getSliceType());
+ basegfx::B3DPolyPolygon aPolA(rSliceA.getB3DPolyPolygon());
+ basegfx::B3DPolyPolygon aPolB(rSliceB.getB3DPolyPolygon());
+
+ if(bAcceptPair)
+ {
+ if(bCreateNormals)
+ {
+ impCreateInBetweenNormals(aPolB, aPolA);
+ }
+
+ {
+ const sal_uInt32 nIndPrev((a + nNumSlices - 1) % nNumSlices);
+ const Slice3D& rSlicePrev(rSliceVector[nIndPrev]);
+ basegfx::B3DPolyPolygon aPrev(rSlicePrev.getB3DPolyPolygon());
+ basegfx::B3DPolyPolygon aPolAA(rSliceA.getB3DPolyPolygon());
+
+ if(SLICETYPE3D_FRONTCAP == rSlicePrev.getSliceType())
+ {
+ basegfx::B3DPolyPolygon aFront(rSlicePrev.getB3DPolyPolygon());
+ const bool bHasSlant(aPolAA != aPrev);
+
+ if(bCreateTextureCoordinates)
+ {
+ aFront = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront);
+ }
+
+ if(bCreateNormals)
+ {
+ basegfx::B3DVector aNormal(0.0, 0.0, -1.0);
+
+ if(aFront.count())
+ {
+ aNormal = -aFront.getB3DPolygon(0).getNormal();
+ }
+
+ impSetNormal(aFront, aNormal);
+
+ if(bHasSlant)
+ {
+ impCreateInBetweenNormals(aPolAA, aPrev);
+
+ if(bSmoothNormals)
+ {
+ // smooth and copy
+ impMixNormals(aPolA, aPolAA, fSmoothNormalsMix);
+ aPolAA = aPolA;
+ }
+ else
+ {
+ // take over from surface
+ aPolAA = aPolA;
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aFront, aPrev, fSmoothLidsMix);
+ aPrev = aFront;
+ }
+ else
+ {
+ // take over from front
+ aPrev = aFront;
+ }
+ }
+ else
+ {
+ if(bSmoothNormals)
+ {
+ // smooth
+ impMixNormals(aPolA, aFront, fSmoothNormalsMix);
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aFront, aPolA, fSmoothLidsMix);
+ aPolA = aFront;
+ }
+ }
+ }
+
+ if(bHasSlant)
+ {
+ double fTexStart{};
+ double fTexStop{};
+ if(bCreateTextureCoordinates)
+ {
+ fTexStart = fTexHeightPos * fInvTexHeight;
+ fTexStop = (fTexHeightPos - aTexHeightArray[(a + nLoopCount - 1) % nLoopCount]) * fInvTexHeight;
+ }
+
+ impAddInBetweenFill(aEdgeRounding, aPolAA, aPrev, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
+ }
+
+ aFront.flip();
+ rFill.push_back(aFront);
+ }
+ else
+ {
+ if(bCreateNormals && bSmoothNormals && (nIndPrev != a + 1))
+ {
+ impCreateInBetweenNormals(aPolAA, aPrev);
+ impMixNormals(aPolA, aPolAA, 0.5);
+ }
+ }
+ }
+
+ {
+ const sal_uInt32 nIndNext((a + 2) % nNumSlices);
+ const Slice3D& rSliceNext(rSliceVector[nIndNext]);
+ basegfx::B3DPolyPolygon aNext(rSliceNext.getB3DPolyPolygon());
+ basegfx::B3DPolyPolygon aPolBB(rSliceB.getB3DPolyPolygon());
+
+ if(SLICETYPE3D_BACKCAP == rSliceNext.getSliceType())
+ {
+ basegfx::B3DPolyPolygon aBack(rSliceNext.getB3DPolyPolygon());
+ const bool bHasSlant(aPolBB != aNext);
+
+ if(bCreateTextureCoordinates)
+ {
+ aBack = basegfx::utils::applyDefaultTextureCoordinatesParallel(aBack, aTexRangeBack);
+ }
+
+ if(bCreateNormals)
+ {
+ const basegfx::B3DVector aNormal(aBack.count() ? aBack.getB3DPolygon(0).getNormal() : basegfx::B3DVector(0.0, 0.0, 1.0));
+ impSetNormal(aBack, aNormal);
+
+ if(bHasSlant)
+ {
+ impCreateInBetweenNormals(aNext, aPolBB);
+
+ if(bSmoothNormals)
+ {
+ // smooth and copy
+ impMixNormals(aPolB, aPolBB, fSmoothNormalsMix);
+ aPolBB = aPolB;
+ }
+ else
+ {
+ // take over from surface
+ aPolBB = aPolB;
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aBack, aNext, fSmoothLidsMix);
+ aNext = aBack;
+ }
+ else
+ {
+ // take over from back
+ aNext = aBack;
+ }
+ }
+ else
+ {
+ if(bSmoothNormals)
+ {
+ // smooth
+ impMixNormals(aPolB, aBack, fSmoothNormalsMix);
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aBack, aPolB, fSmoothLidsMix);
+ aPolB = aBack;
+ }
+ }
+ }
+
+ if(bHasSlant)
+ {
+ double fTexStart{};
+ double fTexStop{};
+ if(bCreateTextureCoordinates)
+ {
+ fTexStart = (fTexHeightPos + aTexHeightArray[a] + aTexHeightArray[(a + 1) % nLoopCount]) * fInvTexHeight;
+ fTexStop = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight;
+ }
+
+ impAddInBetweenFill(aEdgeRounding, aNext, aPolBB, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
+ }
+
+ rFill.push_back(aBack);
+ }
+ else
+ {
+ if(bCreateNormals && bSmoothNormals && (nIndNext != a))
+ {
+ impCreateInBetweenNormals(aNext, aPolBB);
+ impMixNormals(aPolB, aPolBB, 0.5);
+ }
+ }
+ }
+
+ double fTexStart{};
+ double fTexStop{};
+ if(bCreateTextureCoordinates)
+ {
+ fTexStart = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight;
+ fTexStop = fTexHeightPos * fInvTexHeight;
+ }
+
+ impAddInBetweenFill(aEdgeRounding, aPolB, aPolA, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
+ }
+
+ if(bCreateTextureCoordinates)
+ {
+ fTexHeightPos += aTexHeightArray[a];
+ }
+ }
+ }
+ else
+ {
+ // no loop, but a single slice (1 == nNumSlices), create a filling from the single
+ // front plane
+ const Slice3D& rSlice(rSliceVector[0]);
+ basegfx::B3DPolyPolygon aFront(rSlice.getB3DPolyPolygon());
+
+ if(bCreateTextureCoordinates)
+ {
+ aFront = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront);
+ }
+
+ if(bCreateNormals)
+ {
+ basegfx::B3DVector aNormal(0.0, 0.0, -1.0);
+
+ if(aFront.count())
+ {
+ aNormal = -aFront.getB3DPolygon(0).getNormal();
+ }
+
+ impSetNormal(aFront, aNormal);
+ }
+
+ aFront.flip();
+ rFill.push_back(aFront);
+ }
+
+ if(bCreateTextureCoordinates)
+ {
+ aEdgeRounding.transformTextureCoordinates(rTexTransform);
+ }
+
+ for(a = 0; a < aEdgeRounding.count(); a++)
+ {
+ rFill.emplace_back(aEdgeRounding.getB3DPolygon(a));
+ }
+ }
+
+ void createReducedOutlines(
+ const geometry::ViewInformation3D& rViewInformation,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const basegfx::B3DPolygon& rLoopA,
+ const basegfx::B3DPolygon& rLoopB,
+ basegfx::B3DPolyPolygon& rTarget)
+ {
+ const sal_uInt32 nPointCount(rLoopA.count());
+
+ // with identical polygons there are no outlines
+ if(rLoopA == rLoopB)
+ return;
+
+ if(!(nPointCount && nPointCount == rLoopB.count()))
+ return;
+
+ const basegfx::B3DHomMatrix aObjectTransform(rViewInformation.getObjectToView() * rObjectTransform);
+ const basegfx::B2DPolygon a2DLoopA(basegfx::utils::createB2DPolygonFromB3DPolygon(rLoopA, aObjectTransform));
+ const basegfx::B2DPolygon a2DLoopB(basegfx::utils::createB2DPolygonFromB3DPolygon(rLoopB, aObjectTransform));
+ const basegfx::B2DPoint a2DCenterA(a2DLoopA.getB2DRange().getCenter());
+ const basegfx::B2DPoint a2DCenterB(a2DLoopB.getB2DRange().getCenter());
+
+ // without detectable Y-Axis there are no outlines
+ if(a2DCenterA.equal(a2DCenterB))
+ return;
+
+ // search for outmost left and right inter-loop-edges which do not cut the loops
+ const basegfx::B2DPoint aCommonCenter(basegfx::average(a2DCenterA, a2DCenterB));
+ const basegfx::B2DVector aAxisVector(a2DCenterA - a2DCenterB);
+ double fMaxLeft(0.0);
+ double fMaxRight(0.0);
+ sal_uInt32 nIndexLeft(0);
+ sal_uInt32 nIndexRight(0);
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const basegfx::B2DPoint aStart(a2DLoopA.getB2DPoint(a));
+ const basegfx::B2DPoint aEnd(a2DLoopB.getB2DPoint(a));
+ const basegfx::B2DPoint aMiddle(basegfx::average(aStart, aEnd));
+
+ if(!basegfx::utils::isInside(a2DLoopA, aMiddle))
+ {
+ if(!basegfx::utils::isInside(a2DLoopB, aMiddle))
+ {
+ if(!impHasCutWith(a2DLoopA, aStart, aEnd))
+ {
+ if(!impHasCutWith(a2DLoopB, aStart, aEnd))
+ {
+ const basegfx::B2DVector aCandidateVector(aMiddle - aCommonCenter);
+ const double fCross(aCandidateVector.cross(aAxisVector));
+ const double fDistance(aCandidateVector.getLength());
+
+ if(fCross > 0.0)
+ {
+ if(fDistance > fMaxLeft)
+ {
+ fMaxLeft = fDistance;
+ nIndexLeft = a;
+ }
+ }
+ else if(fCross < 0.0)
+ {
+ if(fDistance > fMaxRight)
+ {
+ fMaxRight = fDistance;
+ nIndexRight = a;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(fMaxLeft != 0.0)
+ {
+ basegfx::B3DPolygon aToBeAdded;
+ aToBeAdded.append(rLoopA.getB3DPoint(nIndexLeft));
+ aToBeAdded.append(rLoopB.getB3DPoint(nIndexLeft));
+ rTarget.append(aToBeAdded);
+ }
+
+ if(fMaxRight != 0.0)
+ {
+ basegfx::B3DPolygon aToBeAdded;
+ aToBeAdded.append(rLoopA.getB3DPoint(nIndexRight));
+ aToBeAdded.append(rLoopB.getB3DPoint(nIndexRight));
+ rTarget.append(aToBeAdded);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx
new file mode 100644
index 000000000..919fbcf5a
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cmath>
+
+#include <drawinglayer/primitive3d/sdrextrudeprimitive3d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrExtrudePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ Primitive3DContainer aRetval;
+
+ // get slices
+ const Slice3DVector& rSliceVector = getSlices();
+
+ if(!rSliceVector.empty())
+ {
+ sal_uInt32 a;
+
+ // decide what to create
+ const css::drawing::NormalsKind eNormalsKind(getSdr3DObjectAttribute().getNormalsKind());
+ const bool bCreateNormals(css::drawing::NormalsKind_SPECIFIC == eNormalsKind);
+ const bool bCreateTextureCoordinatesX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bCreateTextureCoordinatesY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ basegfx::B2DHomMatrix aTexTransform;
+
+ if(!getSdrLFSAttribute().getFill().isDefault() && (bCreateTextureCoordinatesX || bCreateTextureCoordinatesY))
+ {
+ const basegfx::B2DPolygon aFirstPolygon(maCorrectedPolyPolygon.getB2DPolygon(0));
+ const double fFrontLength(basegfx::utils::getLength(aFirstPolygon));
+ const double fFrontArea(basegfx::utils::getArea(aFirstPolygon));
+ const double fSqrtFrontArea(sqrt(fFrontArea));
+ double fRelativeTextureWidth = basegfx::fTools::equalZero(fSqrtFrontArea) ? 1.0 : fFrontLength / fSqrtFrontArea;
+ fRelativeTextureWidth = std::trunc(fRelativeTextureWidth - 0.5);
+
+ if(fRelativeTextureWidth < 1.0)
+ {
+ fRelativeTextureWidth = 1.0;
+ }
+
+ aTexTransform.translate(-0.5, -0.5);
+ aTexTransform.scale(-1.0, -1.0);
+ aTexTransform.translate(0.5, 0.5);
+ aTexTransform.scale(fRelativeTextureWidth, 1.0);
+ }
+
+ // create geometry
+ std::vector< basegfx::B3DPolyPolygon > aFill;
+ extractPlanesFromSlice(aFill, rSliceVector,
+ bCreateNormals, getSmoothNormals(), getSmoothLids(), false,
+ 0.5, 0.6, bCreateTextureCoordinatesX || bCreateTextureCoordinatesY, aTexTransform);
+
+ // get full range
+ const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill));
+
+ // normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPHERE == eNormalsKind)
+ {
+ applyNormalsKindSphereTo3DGeometry(aFill, aRange);
+ }
+ else if(css::drawing::NormalsKind_FLAT == eNormalsKind)
+ {
+ applyNormalsKindFlatTo3DGeometry(aFill);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ applyNormalsInvertTo3DGeometry(aFill);
+ }
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ applyTextureTo3DGeometry(
+ getSdr3DObjectAttribute().getTextureProjectionX(),
+ getSdr3DObjectAttribute().getTextureProjectionY(),
+ aFill,
+ aRange,
+ getTextureSize());
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ // create geometric outlines with reduced line geometry for chart.
+ const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
+ const sal_uInt32 nCount(aVerLine.count());
+ basegfx::B3DPolyPolygon aReducedLoops;
+ basegfx::B3DPolyPolygon aNewLineGeometry;
+
+ // sort out doubles (front and back planes when no edge rounding is done). Since
+ // this is a line geometry merged from PolyPolygons, loop over all Polygons
+ for(a = 0; a < nCount; a++)
+ {
+ const sal_uInt32 nReducedCount(aReducedLoops.count());
+ const basegfx::B3DPolygon& aCandidate(aVerLine.getB3DPolygon(a));
+ bool bAdd(true);
+
+ if(nReducedCount)
+ {
+ for(sal_uInt32 b(0); bAdd && b < nReducedCount; b++)
+ {
+ if(aCandidate == aReducedLoops.getB3DPolygon(b))
+ {
+ bAdd = false;
+ }
+ }
+ }
+
+ if(bAdd)
+ {
+ aReducedLoops.append(aCandidate);
+ }
+ }
+
+ // from here work with reduced loops and reduced count without changing them
+ const sal_uInt32 nReducedCount(aReducedLoops.count());
+
+ if(nReducedCount > 1)
+ {
+ for(sal_uInt32 b(1); b < nReducedCount; b++)
+ {
+ // get loop pair
+ const basegfx::B3DPolygon& aCandA(aReducedLoops.getB3DPolygon(b - 1));
+ const basegfx::B3DPolygon& aCandB(aReducedLoops.getB3DPolygon(b));
+
+ // for each loop pair create the connection edges
+ createReducedOutlines(
+ rViewInformation,
+ getTransform(),
+ aCandA,
+ aCandB,
+ aNewLineGeometry);
+ }
+ }
+
+ // add reduced loops themselves
+ aNewLineGeometry.append(aReducedLoops);
+
+ // to create vertical edges at non-C1/C2 steady loops, use maCorrectedPolyPolygon
+ // directly since the 3D Polygons do not support this.
+ //
+ // Unfortunately there is no bezier polygon provided by the chart module; one reason is
+ // that the API for extrude wants a 3D polygon geometry (for historical reasons, i guess)
+ // and those have no beziers. Another reason is that he chart module uses self-created
+ // stuff to create the 2D geometry (in ShapeFactory::createPieSegment), but this geometry
+ // does not contain bezier infos, either. The only way which is possible for now is to 'detect'
+ // candidates for vertical edges of pie segments by looking for the angles in the polygon.
+ //
+ // This is all not very well designed ATM. Ideally, the ReducedLineGeometry is responsible
+ // for creating the outer geometry edges (createReducedOutlines), but for special edges
+ // like the vertical ones for pie center and both start/end, the incarnation with the
+ // knowledge about that it needs to create those and IS a pie segment -> in this case,
+ // the chart itself.
+ const sal_uInt32 nPolyCount(maCorrectedPolyPolygon.count());
+
+ for(sal_uInt32 c(0); c < nPolyCount; c++)
+ {
+ const basegfx::B2DPolygon aCandidate(maCorrectedPolyPolygon.getB2DPolygon(c));
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount > 2)
+ {
+ sal_uInt32 nIndexA(nPointCount);
+ sal_uInt32 nIndexB(nPointCount);
+ sal_uInt32 nIndexC(nPointCount);
+
+ for(sal_uInt32 d(0); d < nPointCount; d++)
+ {
+ const sal_uInt32 nPrevInd((d + nPointCount - 1) % nPointCount);
+ const sal_uInt32 nNextInd((d + 1) % nPointCount);
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(d));
+ const basegfx::B2DVector aPrev(aCandidate.getB2DPoint(nPrevInd) - aPoint);
+ const basegfx::B2DVector aNext(aCandidate.getB2DPoint(nNextInd) - aPoint);
+ const double fAngle(aPrev.angle(aNext));
+
+ // take each angle which deviates more than 10% from going straight as
+ // special edge. This will detect the two outer edges of pie segments,
+ // but not always the center one (think about a near 180 degree pie)
+ if(M_PI - fabs(fAngle) > M_PI * 0.1)
+ {
+ if(nPointCount == nIndexA)
+ {
+ nIndexA = d;
+ }
+ else if(nPointCount == nIndexB)
+ {
+ nIndexB = d;
+ }
+ else if(nPointCount == nIndexC)
+ {
+ nIndexC = d;
+ d = nPointCount;
+ }
+ }
+ }
+
+ const bool bIndexAUsed(nIndexA != nPointCount);
+ const bool bIndexBUsed(nIndexB != nPointCount);
+ bool bIndexCUsed(nIndexC != nPointCount);
+
+ if(bIndexCUsed)
+ {
+ // already three special edges found, so the center one was already detected
+ // and does not need to be searched
+ }
+ else if(bIndexAUsed && bIndexBUsed)
+ {
+ // outer edges detected (they are approx. 90 degrees), but center one not.
+ // Look with the knowledge that it's in-between the two found ones
+ if(((nIndexA + 2) % nPointCount) == nIndexB)
+ {
+ nIndexC = (nIndexA + 1) % nPointCount;
+ }
+ else if(((nIndexA + nPointCount - 2) % nPointCount) == nIndexB)
+ {
+ nIndexC = (nIndexA + nPointCount - 1) % nPointCount;
+ }
+
+ bIndexCUsed = (nIndexC != nPointCount);
+ }
+
+ if(bIndexAUsed)
+ {
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexA));
+ const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
+ const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
+ basegfx::B3DPolygon aToBeAdded;
+
+ aToBeAdded.append(aStart);
+ aToBeAdded.append(aEnd);
+ aNewLineGeometry.append(aToBeAdded);
+ }
+
+ if(bIndexBUsed)
+ {
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexB));
+ const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
+ const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
+ basegfx::B3DPolygon aToBeAdded;
+
+ aToBeAdded.append(aStart);
+ aToBeAdded.append(aEnd);
+ aNewLineGeometry.append(aToBeAdded);
+ }
+
+ if(bIndexCUsed)
+ {
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexC));
+ const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
+ const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
+ basegfx::B3DPolygon aToBeAdded;
+
+ aToBeAdded.append(aStart);
+ aToBeAdded.append(aEnd);
+ aNewLineGeometry.append(aToBeAdded);
+ }
+ }
+ }
+
+ // append loops themselves
+ aNewLineGeometry.append(aReducedLoops);
+
+ if(aNewLineGeometry.count())
+ {
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aNewLineGeometry, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+ }
+ else
+ {
+ // extract line geometry from slices
+ const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, false));
+ const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
+
+ // add horizontal lines
+ const Primitive3DContainer aHorLines(create3DPolyPolygonLinePrimitives(
+ aHorLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aHorLines);
+
+ // add vertical lines
+ const Primitive3DContainer aVerLines(create3DPolyPolygonLinePrimitives(
+ aVerLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aVerLines);
+ }
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault() && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void SdrExtrudePrimitive3D::impCreateSlices()
+ {
+ // prepare the polygon. No double points, correct orientations and a correct
+ // outmost polygon are needed
+ // Also important: subdivide here to ensure equal point count for all slices (!)
+ maCorrectedPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(getPolyPolygon());
+ maCorrectedPolyPolygon.removeDoublePoints();
+ maCorrectedPolyPolygon = basegfx::utils::correctOrientations(maCorrectedPolyPolygon);
+ maCorrectedPolyPolygon = basegfx::utils::correctOutmostPolygon(maCorrectedPolyPolygon);
+
+ // prepare slices as geometry
+ createExtrudeSlices(maSlices, maCorrectedPolyPolygon, getBackScale(), getDiagonal(), getDepth(), getCharacterMode(), getCloseFront(), getCloseBack());
+ }
+
+ const Slice3DVector& SdrExtrudePrimitive3D::getSlices() const
+ {
+ // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine()
+ // again when no longer geometry is needed for non-visible 3D objects as it is now for chart
+ if(getPolyPolygon().count() && maSlices.empty())
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const_cast< SdrExtrudePrimitive3D& >(*this).impCreateSlices();
+ }
+
+ return maSlices;
+ }
+
+ SdrExtrudePrimitive3D::SdrExtrudePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fDepth,
+ double fDiagonal,
+ double fBackScale,
+ bool bSmoothNormals,
+ bool bSmoothLids,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ maPolyPolygon(rPolyPolygon),
+ mfDepth(fDepth),
+ mfDiagonal(fDiagonal),
+ mfBackScale(fBackScale),
+ mbSmoothNormals(bSmoothNormals),
+ mbSmoothLids(bSmoothLids),
+ mbCharacterMode(bCharacterMode),
+ mbCloseFront(bCloseFront),
+ mbCloseBack(bCloseBack)
+ {
+ // make sure depth is positive
+ if(basegfx::fTools::lessOrEqual(getDepth(), 0.0))
+ {
+ mfDepth = 0.0;
+ }
+
+ // make sure the percentage value getDiagonal() is between 0.0 and 1.0
+ if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
+ {
+ mfDiagonal = 0.0;
+ }
+ else if(basegfx::fTools::moreOrEqual(getDiagonal(), 1.0))
+ {
+ mfDiagonal = 1.0;
+ }
+
+ // no close front/back when polygon is not closed
+ if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0).isClosed())
+ {
+ mbCloseFront = mbCloseBack = false;
+ }
+
+ // no edge rounding when not closing
+ if(!getCloseFront() && !getCloseBack())
+ {
+ mfDiagonal = 0.0;
+ }
+ }
+
+ SdrExtrudePrimitive3D::~SdrExtrudePrimitive3D()
+ {
+ }
+
+ bool SdrExtrudePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrExtrudePrimitive3D& rCompare = static_cast< const SdrExtrudePrimitive3D& >(rPrimitive);
+
+ return (getPolyPolygon() == rCompare.getPolyPolygon()
+ && getDepth() == rCompare.getDepth()
+ && getDiagonal() == rCompare.getDiagonal()
+ && getBackScale() == rCompare.getBackScale()
+ && getSmoothNormals() == rCompare.getSmoothNormals()
+ && getSmoothLids() == rCompare.getSmoothLids()
+ && getCharacterMode() == rCompare.getCharacterMode()
+ && getCloseFront() == rCompare.getCloseFront()
+ && getCloseBack() == rCompare.getCloseBack());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrExtrudePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return get3DRangeFromSlices(getSlices());
+ }
+
+ Primitive3DContainer SdrExtrudePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ if(!mpLastRLGViewInformation ||
+ (!getBuffered3DDecomposition().empty()
+ && *mpLastRLGViewInformation != rViewInformation))
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // conditions of last local decomposition with reduced lines have changed. Remember
+ // new one and clear current decompositiopn
+ SdrExtrudePrimitive3D* pThat = const_cast< SdrExtrudePrimitive3D* >(this);
+ pThat->setBuffered3DDecomposition(Primitive3DContainer());
+ pThat->mpLastRLGViewInformation = rViewInformation;
+ }
+ }
+
+ // no test for buffering needed, call parent
+ return SdrPrimitive3D::get3DDecomposition(rViewInformation);
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrExtrudePrimitive3D, PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx
new file mode 100644
index 000000000..2115ebc3a
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrlatheprimitive3d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrLathePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ Primitive3DContainer aRetval;
+
+ // get slices
+ const Slice3DVector& rSliceVector = getSlices();
+
+ if(!rSliceVector.empty())
+ {
+ const bool bBackScale(!basegfx::fTools::equal(getBackScale(), 1.0));
+ const bool bClosedRotation(!bBackScale && getHorizontalSegments() && basegfx::fTools::equal(getRotation(), 2 * M_PI));
+ sal_uInt32 a;
+
+ // decide what to create
+ const css::drawing::NormalsKind eNormalsKind(getSdr3DObjectAttribute().getNormalsKind());
+ const bool bCreateNormals(css::drawing::NormalsKind_SPECIFIC == eNormalsKind);
+ const bool bCreateTextureCoordinatesX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bCreateTextureCoordinatesY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ basegfx::B2DHomMatrix aTexTransform;
+
+ if(!getSdrLFSAttribute().getFill().isDefault()
+ && (bCreateTextureCoordinatesX || bCreateTextureCoordinatesY))
+ {
+ aTexTransform.set(0, 0, 0.0);
+ aTexTransform.set(0, 1, 1.0);
+ aTexTransform.set(1, 0, 1.0);
+ aTexTransform.set(1, 1, 0.0);
+
+ aTexTransform.translate(0.0, -0.5);
+ aTexTransform.scale(1.0, -1.0);
+ aTexTransform.translate(0.0, 0.5);
+ }
+
+ // create geometry
+ std::vector< basegfx::B3DPolyPolygon > aFill;
+ extractPlanesFromSlice(aFill, rSliceVector,
+ bCreateNormals, getSmoothNormals(), getSmoothLids(), bClosedRotation,
+ 0.85, 0.6, bCreateTextureCoordinatesX || bCreateTextureCoordinatesY, aTexTransform);
+
+ // get full range
+ const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill));
+
+ // normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPHERE == eNormalsKind)
+ {
+ applyNormalsKindSphereTo3DGeometry(aFill, aRange);
+ }
+ else if(css::drawing::NormalsKind_FLAT == eNormalsKind)
+ {
+ applyNormalsKindFlatTo3DGeometry(aFill);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ applyNormalsInvertTo3DGeometry(aFill);
+ }
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ applyTextureTo3DGeometry(
+ getSdr3DObjectAttribute().getTextureProjectionX(),
+ getSdr3DObjectAttribute().getTextureProjectionY(),
+ aFill,
+ aRange,
+ getTextureSize());
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ // create geometric outlines with reduced line geometry for chart
+ const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, bClosedRotation));
+ const sal_uInt32 nCount(aHorLine.count());
+ basegfx::B3DPolyPolygon aNewLineGeometry;
+
+ for(a = 1; a < nCount; a++)
+ {
+ // for each loop pair create the connection edges
+ createReducedOutlines(
+ rViewInformation,
+ getTransform(),
+ aHorLine.getB3DPolygon(a - 1),
+ aHorLine.getB3DPolygon(a),
+ aNewLineGeometry);
+ }
+
+ for(a = 0; a < nCount; a++)
+ {
+ // filter hor lines for empty loops (those who have their defining point on the Y-Axis)
+ basegfx::B3DPolygon aCandidate(aHorLine.getB3DPolygon(a));
+ aCandidate.removeDoublePoints();
+
+ if(aCandidate.count())
+ {
+ aNewLineGeometry.append(aCandidate);
+ }
+ }
+
+ if(aNewLineGeometry.count())
+ {
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aNewLineGeometry, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+ }
+ else
+ {
+ // extract line geometry from slices
+ const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, bClosedRotation));
+ const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
+
+ // add horizontal lines
+ const Primitive3DContainer aHorLines(create3DPolyPolygonLinePrimitives(
+ aHorLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aHorLines);
+
+ // add vertical lines
+ const Primitive3DContainer aVerLines(create3DPolyPolygonLinePrimitives(
+ aVerLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aVerLines);
+ }
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault()
+ && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void SdrLathePrimitive3D::impCreateSlices()
+ {
+ // prepare the polygon. No double points, correct orientations and a correct
+ // outmost polygon are needed
+ // Also important: subdivide here to ensure equal point count for all slices (!)
+ maCorrectedPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(getPolyPolygon());
+ maCorrectedPolyPolygon.removeDoublePoints();
+ maCorrectedPolyPolygon = basegfx::utils::correctOrientations(maCorrectedPolyPolygon);
+ maCorrectedPolyPolygon = basegfx::utils::correctOutmostPolygon(maCorrectedPolyPolygon);
+
+ // check edge count of first sub-polygon. If different, reSegment polyPolygon. This ensures
+ // that for polyPolygons, the subPolys 1..n only get reSegmented when polygon 0 is different
+ // at all (and not always)
+ const basegfx::B2DPolygon aSubCandidate(maCorrectedPolyPolygon.getB2DPolygon(0));
+ const sal_uInt32 nSubEdgeCount(aSubCandidate.isClosed() ? aSubCandidate.count() : (aSubCandidate.count() ? aSubCandidate.count() - 1 : 0));
+
+ if(nSubEdgeCount != getVerticalSegments())
+ {
+ maCorrectedPolyPolygon = basegfx::utils::reSegmentPolyPolygon(maCorrectedPolyPolygon, getVerticalSegments());
+ }
+
+ // prepare slices as geometry
+ createLatheSlices(maSlices, maCorrectedPolyPolygon, getBackScale(), getDiagonal(), getRotation(), getHorizontalSegments(), getCharacterMode(), getCloseFront(), getCloseBack());
+ }
+
+ const Slice3DVector& SdrLathePrimitive3D::getSlices() const
+ {
+ // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine()
+ // again when no longer geometry is needed for non-visible 3D objects as it is now for chart
+ if(getPolyPolygon().count() && maSlices.empty())
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const_cast< SdrLathePrimitive3D& >(*this).impCreateSlices();
+ }
+
+ return maSlices;
+ }
+
+ SdrLathePrimitive3D::SdrLathePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ sal_uInt32 nHorizontalSegments,
+ sal_uInt32 nVerticalSegments,
+ double fDiagonal,
+ double fBackScale,
+ double fRotation,
+ bool bSmoothNormals,
+ bool bSmoothLids,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ maPolyPolygon(rPolyPolygon),
+ mnHorizontalSegments(nHorizontalSegments),
+ mnVerticalSegments(nVerticalSegments),
+ mfDiagonal(fDiagonal),
+ mfBackScale(fBackScale),
+ mfRotation(fRotation),
+ mbSmoothNormals(bSmoothNormals),
+ mbSmoothLids(bSmoothLids),
+ mbCharacterMode(bCharacterMode),
+ mbCloseFront(bCloseFront),
+ mbCloseBack(bCloseBack)
+ {
+ // make sure Rotation is positive
+ if(basegfx::fTools::lessOrEqual(getRotation(), 0.0))
+ {
+ mfRotation = 0.0;
+ }
+
+ // make sure the percentage value getDiagonal() is between 0.0 and 1.0
+ if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
+ {
+ mfDiagonal = 0.0;
+ }
+ else if(basegfx::fTools::moreOrEqual(getDiagonal(), 1.0))
+ {
+ mfDiagonal = 1.0;
+ }
+
+ // no close front/back when polygon is not closed
+ if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0).isClosed())
+ {
+ mbCloseFront = mbCloseBack = false;
+ }
+
+ // no edge rounding when not closing
+ if(!getCloseFront() && !getCloseBack())
+ {
+ mfDiagonal = 0.0;
+ }
+ }
+
+ SdrLathePrimitive3D::~SdrLathePrimitive3D()
+ {
+ }
+
+ bool SdrLathePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrLathePrimitive3D& rCompare = static_cast< const SdrLathePrimitive3D& >(rPrimitive);
+
+ return (getPolyPolygon() == rCompare.getPolyPolygon()
+ && getHorizontalSegments() == rCompare.getHorizontalSegments()
+ && getVerticalSegments() == rCompare.getVerticalSegments()
+ && getDiagonal() == rCompare.getDiagonal()
+ && getBackScale() == rCompare.getBackScale()
+ && getRotation() == rCompare.getRotation()
+ && getSmoothNormals() == rCompare.getSmoothNormals()
+ && getSmoothLids() == rCompare.getSmoothLids()
+ && getCharacterMode() == rCompare.getCharacterMode()
+ && getCloseFront() == rCompare.getCloseFront()
+ && getCloseBack() == rCompare.getCloseBack());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrLathePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return get3DRangeFromSlices(getSlices());
+ }
+
+ Primitive3DContainer SdrLathePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ if(!mpLastRLGViewInformation ||
+ (!getBuffered3DDecomposition().empty()
+ && *mpLastRLGViewInformation != rViewInformation))
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // conditions of last local decomposition with reduced lines have changed. Remember
+ // new one and clear current decompositiopn
+ SdrLathePrimitive3D* pThat = const_cast< SdrLathePrimitive3D* >(this);
+ pThat->setBuffered3DDecomposition(Primitive3DContainer());
+ pThat->mpLastRLGViewInformation = rViewInformation;
+ }
+ }
+
+ // no test for buffering needed, call parent
+ return SdrPrimitive3D::get3DDecomposition(rViewInformation);
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrLathePrimitive3D, PRIMITIVE3D_ID_SDRLATHEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx
new file mode 100644
index 000000000..010083b42
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrpolypolygonprimitive3d.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrPolyPolygonPrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ Primitive3DContainer aRetval;
+
+ if(getPolyPolygon3D().count())
+ {
+ std::vector< basegfx::B3DPolyPolygon > aFill { getPolyPolygon3D() };
+
+ // get full range
+ const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill));
+
+ // #i98295# normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPHERE == getSdr3DObjectAttribute().getNormalsKind())
+ {
+ applyNormalsKindSphereTo3DGeometry(aFill, aRange);
+ }
+ else if(css::drawing::NormalsKind_FLAT == getSdr3DObjectAttribute().getNormalsKind())
+ {
+ applyNormalsKindFlatTo3DGeometry(aFill);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ applyNormalsInvertTo3DGeometry(aFill);
+ }
+ }
+
+ // #i98314# texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ applyTextureTo3DGeometry(
+ getSdr3DObjectAttribute().getTextureProjectionX(),
+ getSdr3DObjectAttribute().getTextureProjectionY(),
+ aFill,
+ aRange,
+ getTextureSize());
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ basegfx::B3DPolyPolygon aLine(getPolyPolygon3D());
+ aLine.clearNormals();
+ aLine.clearTextureCoordinates();
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault()
+ && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+ }
+
+ return aRetval;
+ }
+
+ SdrPolyPolygonPrimitive3D::SdrPolyPolygonPrimitive3D(
+ const basegfx::B3DPolyPolygon& rPolyPolygon3D,
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ maPolyPolygon3D(rPolyPolygon3D)
+ {
+ }
+
+ bool SdrPolyPolygonPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrPolyPolygonPrimitive3D& rCompare = static_cast< const SdrPolyPolygonPrimitive3D& >(rPrimitive);
+
+ return (getPolyPolygon3D() == rCompare.getPolyPolygon3D());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrPolyPolygonPrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // added this implementation to make sure that non-visible objects of this
+ // kind will deliver their expansion. If not implemented, it would never deliver
+ // the used space for non-visible objects since the decomposition for that
+ // case will be empty (what is correct). To support chart ATM which relies on
+ // non-visible objects occupying space in 3D, this method was added
+ basegfx::B3DRange aRetval;
+
+ if(getPolyPolygon3D().count())
+ {
+ aRetval = basegfx::utils::getRange(getPolyPolygon3D());
+ aRetval.transform(getTransform());
+
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ const attribute::SdrLineAttribute& rLine = getSdrLFSAttribute().getLine();
+
+ if(!rLine.isDefault() && !basegfx::fTools::equalZero(rLine.getWidth()))
+ {
+ // expand by half LineWidth as tube radius
+ aRetval.grow(rLine.getWidth() / 2.0);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrPolyPolygonPrimitive3D, PRIMITIVE3D_ID_SDRPOLYPOLYGONPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx
new file mode 100644
index 000000000..54fd2cf4b
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ basegfx::B3DRange SdrPrimitive3D::getStandard3DRange() const
+ {
+ basegfx::B3DRange aUnitRange(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+ aUnitRange.transform(getTransform());
+
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ const attribute::SdrLineAttribute& rLine = getSdrLFSAttribute().getLine();
+
+ if(!rLine.isDefault() && !basegfx::fTools::equalZero(rLine.getWidth()))
+ {
+ // expand by held LineWidth as tube radius
+ aUnitRange.grow(rLine.getWidth() / 2.0);
+ }
+ }
+
+ return aUnitRange;
+ }
+
+ basegfx::B3DRange SdrPrimitive3D::get3DRangeFromSlices(const Slice3DVector& rSlices) const
+ {
+ basegfx::B3DRange aRetval;
+
+ if(!rSlices.empty())
+ {
+ for(const auto & rSlice : rSlices)
+ {
+ aRetval.expand(basegfx::utils::getRange(rSlice.getB3DPolyPolygon()));
+ }
+
+ aRetval.transform(getTransform());
+
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ const attribute::SdrLineAttribute& rLine = getSdrLFSAttribute().getLine();
+
+ if(!rLine.isDefault() && !basegfx::fTools::equalZero(rLine.getWidth()))
+ {
+ // expand by half LineWidth as tube radius
+ aRetval.grow(rLine.getWidth() / 2.0);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ SdrPrimitive3D::SdrPrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
+ : maTransform(rTransform),
+ maTextureSize(rTextureSize),
+ maSdrLFSAttribute(rSdrLFSAttribute),
+ maSdr3DObjectAttribute(rSdr3DObjectAttribute)
+ {
+ }
+
+ bool SdrPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrPrimitive3D& rCompare = static_cast< const SdrPrimitive3D& >(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getTextureSize() == rCompare.getTextureSize()
+ && getSdrLFSAttribute() == rCompare.getSdrLFSAttribute()
+ && getSdr3DObjectAttribute() == rCompare.getSdr3DObjectAttribute());
+ }
+
+ return false;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx
new file mode 100644
index 000000000..c3127261f
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrsphereprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrSpherePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ Primitive3DContainer aRetval;
+ const basegfx::B3DRange aUnitRange(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+ const bool bCreateNormals(css::drawing::NormalsKind_SPECIFIC == getSdr3DObjectAttribute().getNormalsKind()
+ || css::drawing::NormalsKind_SPHERE == getSdr3DObjectAttribute().getNormalsKind());
+
+ // create unit geometry
+ basegfx::B3DPolyPolygon aFill(basegfx::utils::createSphereFillPolyPolygonFromB3DRange(aUnitRange,
+ getHorizontalSegments(), getVerticalSegments(), bCreateNormals));
+
+ // normal inversion
+ if(!getSdrLFSAttribute().getFill().isDefault()
+ && bCreateNormals
+ && getSdr3DObjectAttribute().getNormalsInvert()
+ && aFill.areNormalsUsed())
+ {
+ // invert normals
+ aFill = basegfx::utils::invertNormals(aFill);
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // handle texture coordinates X
+ const bool bParallelX(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bObjectSpecificX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bSphereX(css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionX());
+
+ // handle texture coordinates Y
+ const bool bParallelY(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bObjectSpecificY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bSphereY(css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionY());
+
+ if(bParallelX || bParallelY)
+ {
+ // apply parallel texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFill, aRange, bParallelX, bParallelY);
+ }
+
+ if(bSphereX || bObjectSpecificX || bSphereY || bObjectSpecificY)
+ {
+ double fRelativeAngle(0.0);
+
+ if(bObjectSpecificX)
+ {
+ // Since the texture coordinates are (for historical reasons)
+ // different from forced to sphere texture coordinates,
+ // create a old version from it by rotating to old state before applying
+ // the texture coordinates to emulate old behaviour
+ fRelativeAngle = 2 * M_PI * (static_cast<double>((getHorizontalSegments() >> 1) - 1) / static_cast<double>(getHorizontalSegments()));
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(0.0, fRelativeAngle, 0.0);
+ aFill.transform(aRot);
+ }
+
+ // apply spherical texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ const basegfx::B3DPoint aCenter(aRange.getCenter());
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesSphere(aFill, aCenter,
+ bSphereX || bObjectSpecificX, bSphereY || bObjectSpecificY);
+
+ if(bObjectSpecificX)
+ {
+ // rotate back again
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(0.0, -fRelativeAngle, 0.0);
+ aFill.transform(aRot);
+ }
+ }
+
+ // transform texture coordinates to texture size
+ basegfx::B2DHomMatrix aTexMatrix;
+ aTexMatrix.scale(getTextureSize().getX(), getTextureSize().getY());
+ aFill.transformTextureCoordinates(aTexMatrix);
+ }
+
+ // build vector of PolyPolygons
+ std::vector< basegfx::B3DPolyPolygon > a3DPolyPolygonVector;
+
+ for(sal_uInt32 a(0); a < aFill.count(); a++)
+ {
+ a3DPolyPolygonVector.emplace_back(aFill.getB3DPolygon(a));
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ basegfx::B3DPolyPolygon aSphere(basegfx::utils::createSpherePolyPolygonFromB3DRange(aUnitRange, getHorizontalSegments(), getVerticalSegments()));
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aSphere, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault()
+ && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+
+ return aRetval;
+ }
+
+ SdrSpherePrimitive3D::SdrSpherePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
+ sal_uInt32 nHorizontalSegments,
+ sal_uInt32 nVerticalSegments)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ mnHorizontalSegments(nHorizontalSegments),
+ mnVerticalSegments(nVerticalSegments)
+ {
+ }
+
+ bool SdrSpherePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrSpherePrimitive3D& rCompare = static_cast< const SdrSpherePrimitive3D& >(rPrimitive);
+
+ return (getHorizontalSegments() == rCompare.getHorizontalSegments()
+ && getVerticalSegments() == rCompare.getVerticalSegments());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrSpherePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return getStandard3DRange();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrSpherePrimitive3D, PRIMITIVE3D_ID_SDRSPHEREPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/shadowprimitive3d.cxx b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx
new file mode 100644
index 000000000..cca2e3c6f
--- /dev/null
+++ b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/shadowprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ ShadowPrimitive3D::ShadowPrimitive3D(
+ const basegfx::B2DHomMatrix& rShadowTransform,
+ const basegfx::BColor& rShadowColor,
+ double fShadowTransparence,
+ bool bShadow3D,
+ const Primitive3DContainer& rChildren)
+ : GroupPrimitive3D(rChildren),
+ maShadowTransform(rShadowTransform),
+ maShadowColor(rShadowColor),
+ mfShadowTransparence(fShadowTransparence),
+ mbShadow3D(bShadow3D)
+ {
+ }
+
+ bool ShadowPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const ShadowPrimitive3D& rCompare = static_cast<const ShadowPrimitive3D&>(rPrimitive);
+
+ return (getShadowTransform() == rCompare.getShadowTransform()
+ && getShadowColor() == rCompare.getShadowColor()
+ && getShadowTransparence() == rCompare.getShadowTransparence()
+ && getShadow3D() == rCompare.getShadow3D());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(ShadowPrimitive3D, PRIMITIVE3D_ID_SHADOWPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/textureprimitive3d.cxx b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
new file mode 100644
index 000000000..7c9226bf4
--- /dev/null
+++ b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/textureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ TexturePrimitive3D::TexturePrimitive3D(
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate, bool bFilter)
+ : GroupPrimitive3D(rChildren),
+ maTextureSize(rTextureSize),
+ mbModulate(bModulate),
+ mbFilter(bFilter)
+ {
+ }
+
+ bool TexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const TexturePrimitive3D& rCompare = static_cast<const TexturePrimitive3D&>(rPrimitive);
+
+ return (getModulate() == rCompare.getModulate()
+ && getFilter() == rCompare.getFilter());
+ }
+
+ return false;
+ }
+
+
+
+ UnifiedTransparenceTexturePrimitive3D::UnifiedTransparenceTexturePrimitive3D(
+ double fTransparence,
+ const Primitive3DContainer& rChildren)
+ : TexturePrimitive3D(rChildren, basegfx::B2DVector(), false, false),
+ mfTransparence(fTransparence)
+ {
+ }
+
+ bool UnifiedTransparenceTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const UnifiedTransparenceTexturePrimitive3D& rCompare = static_cast<const UnifiedTransparenceTexturePrimitive3D&>(rPrimitive);
+
+ return (getTransparence() == rCompare.getTransparence());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange UnifiedTransparenceTexturePrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ // do not use the fallback to decomposition here since for a correct BoundRect we also
+ // need invisible (1.0 == getTransparence()) geometry; these would be deleted in the decomposition
+ return getChildren().getB3DRange(rViewInformation);
+ }
+
+ Primitive3DContainer UnifiedTransparenceTexturePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ if(0.0 == getTransparence())
+ {
+ // no transparence used, so just use content
+ return getChildren();
+ }
+ else if(getTransparence() > 0.0 && getTransparence() < 1.0)
+ {
+ // create TransparenceTexturePrimitive3D with fixed transparence as replacement
+ const basegfx::BColor aGray(getTransparence(), getTransparence(), getTransparence());
+ const attribute::FillGradientAttribute aFillGradient(attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 0.0, aGray, aGray);
+ const Primitive3DReference xRef(new TransparenceTexturePrimitive3D(aFillGradient, getChildren(), getTextureSize()));
+ return { xRef };
+ }
+ else
+ {
+ // completely transparent or invalid definition, add nothing
+ return Primitive3DContainer();
+ }
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(UnifiedTransparenceTexturePrimitive3D, PRIMITIVE3D_ID_UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D)
+
+
+
+ GradientTexturePrimitive3D::GradientTexturePrimitive3D(
+ const attribute::FillGradientAttribute& rGradient,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate,
+ bool bFilter)
+ : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
+ maGradient(rGradient)
+ {
+ }
+
+ bool GradientTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const GradientTexturePrimitive3D& rCompare = static_cast<const GradientTexturePrimitive3D&>(rPrimitive);
+
+ return (getGradient() == rCompare.getGradient());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(GradientTexturePrimitive3D, PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D)
+
+
+
+ BitmapTexturePrimitive3D::BitmapTexturePrimitive3D(
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate, bool bFilter)
+ : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
+ maFillGraphicAttribute(rFillGraphicAttribute)
+ {
+ }
+
+ bool BitmapTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const BitmapTexturePrimitive3D& rCompare = static_cast<const BitmapTexturePrimitive3D&>(rPrimitive);
+
+ return (getFillGraphicAttribute() == rCompare.getFillGraphicAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(BitmapTexturePrimitive3D, PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D)
+
+
+
+ TransparenceTexturePrimitive3D::TransparenceTexturePrimitive3D(
+ const attribute::FillGradientAttribute& rGradient,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize)
+ : GradientTexturePrimitive3D(rGradient, rChildren, rTextureSize, false, false)
+ {
+ }
+
+ bool TransparenceTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ return GradientTexturePrimitive3D::operator==(rPrimitive);
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(TransparenceTexturePrimitive3D, PRIMITIVE3D_ID_TRANSPARENCETEXTUREPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/transformprimitive3d.cxx b/drawinglayer/source/primitive3d/transformprimitive3d.cxx
new file mode 100644
index 000000000..1ddb919bf
--- /dev/null
+++ b/drawinglayer/source/primitive3d/transformprimitive3d.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ TransformPrimitive3D::TransformPrimitive3D(
+ const basegfx::B3DHomMatrix& rTransformation,
+ const Primitive3DContainer& rChildren)
+ : GroupPrimitive3D(rChildren),
+ maTransformation(rTransformation)
+ {
+ }
+
+ bool TransformPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const TransformPrimitive3D& rCompare = static_cast< const TransformPrimitive3D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange TransformPrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ basegfx::B3DRange aRetval(getChildren().getB3DRange(rViewInformation));
+ aRetval.transform(getTransformation());
+ return aRetval;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(TransformPrimitive3D, PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/baseprocessor2d.cxx b/drawinglayer/source/processor2d/baseprocessor2d.cxx
new file mode 100644
index 000000000..211569f5f
--- /dev/null
+++ b/drawinglayer/source/processor2d/baseprocessor2d.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor2d
+{
+ void BaseProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& /*rCandidate*/)
+ {
+ }
+
+ BaseProcessor2D::BaseProcessor2D(const geometry::ViewInformation2D& rViewInformation)
+ : maViewInformation2D(rViewInformation)
+ {
+ }
+
+ BaseProcessor2D::~BaseProcessor2D()
+ {
+ }
+
+ void BaseProcessor2D::process(const primitive2d::BasePrimitive2D& rCandidate)
+ {
+ // use the visitor API to avoid the cost of constructing Primitive2DContainers
+ rCandidate.get2DDecomposition(*this, getViewInformation2D());
+ }
+
+ // Primitive2DDecompositionVisitor
+ void BaseProcessor2D::visit(const primitive2d::Primitive2DReference& rCandidate)
+ {
+ processBasePrimitive2D(*rCandidate);
+ }
+ void BaseProcessor2D::visit(const primitive2d::Primitive2DContainer& rContainer)
+ {
+ process(rContainer);
+ }
+ void BaseProcessor2D::visit(primitive2d::Primitive2DContainer&& rCandidate)
+ {
+ process(rCandidate);
+ }
+
+ void BaseProcessor2D::process(const primitive2d::Primitive2DContainer& rSource)
+ {
+ for (const primitive2d::Primitive2DReference& rCandidate : rSource)
+ {
+ if (rCandidate)
+ processBasePrimitive2D(*rCandidate);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/contourextractor2d.cxx b/drawinglayer/source/processor2d/contourextractor2d.cxx
new file mode 100644
index 000000000..a1d120018
--- /dev/null
+++ b/drawinglayer/source/processor2d/contourextractor2d.cxx
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor2d/contourextractor2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor2d
+{
+ ContourExtractor2D::ContourExtractor2D(
+ const geometry::ViewInformation2D& rViewInformation,
+ bool bExtractFillOnly)
+ : BaseProcessor2D(rViewInformation),
+ mbExtractFillOnly(bExtractFillOnly)
+ {
+ }
+
+ ContourExtractor2D::~ContourExtractor2D()
+ {
+ }
+
+ void ContourExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+ {
+ switch(rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
+ {
+ if(!mbExtractFillOnly)
+ {
+ // extract hairline in world coordinates
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate));
+ basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon());
+ aLocalPolygon.transform(getViewInformation2D().getObjectTransformation());
+
+ if(aLocalPolygon.isClosed())
+ {
+ // line polygons need to be represented as open polygons to differentiate them
+ // from filled polygons
+ basegfx::utils::openWithGeometryChange(aLocalPolygon);
+ }
+
+ maExtractedContour.emplace_back(aLocalPolygon);
+ }
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
+ {
+ // extract fill in world coordinates
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
+ aLocalPolyPolygon.transform(getViewInformation2D().getObjectTransformation());
+ maExtractedContour.push_back(aLocalPolyPolygon);
+ break;
+ }
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
+ {
+ // extract BoundRect from bitmaps in world coordinates
+ const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
+ basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectTransformation() * rBitmapCandidate.getTransform());
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+ aPolygon.transform(aLocalTransform);
+ maExtractedContour.emplace_back(aPolygon);
+ break;
+ }
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
+ {
+ // extract BoundRect from MetaFiles in world coordinates
+ const primitive2d::MetafilePrimitive2D& rMetaCandidate(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate));
+ basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectTransformation() * rMetaCandidate.getTransform());
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+ aPolygon.transform(aLocalTransform);
+ maExtractedContour.emplace_back(aPolygon);
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
+ {
+ // sub-transparence group. Look at children
+ const primitive2d::TransparencePrimitive2D& rTransCandidate(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate));
+ process(rTransCandidate.getChildren());
+ break;
+ }
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
+ {
+ // extract mask in world coordinates, ignore content
+ const primitive2d::MaskPrimitive2D& rMaskCandidate(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate));
+ basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
+ aMask.transform(getViewInformation2D().getObjectTransformation());
+ maExtractedContour.push_back(aMask);
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
+ {
+ // remember current ViewInformation2D
+ const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new local ViewInformation2D
+ const geometry::ViewInformation2D aViewInformation2D(
+ getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
+ getViewInformation2D().getViewTransformation(),
+ getViewInformation2D().getViewport(),
+ getViewInformation2D().getVisualizedPage(),
+ getViewInformation2D().getViewTime());
+ updateViewInformation(aViewInformation2D);
+
+ // process content
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+
+ break;
+ }
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
+ {
+ // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates
+ const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
+ const primitive2d::Primitive2DContainer xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D());
+ const primitive2d::Primitive2DContainer xExtracted2DSceneShadow(rScenePrimitive2DCandidate.getShadow2D());
+
+ // process content
+ if(!xExtracted2DSceneGeometry.empty())
+ {
+ process(xExtracted2DSceneGeometry);
+ }
+
+ // process content
+ if(!xExtracted2DSceneShadow.empty())
+ {
+ process(xExtracted2DSceneShadow);
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
+ {
+ // ignorable primitives
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
+ {
+ // primitives who's BoundRect will be added in world coordinates
+ basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
+ if (!aRange.isEmpty())
+ {
+ aRange.transform(getViewInformation2D().getObjectTransformation());
+ maExtractedContour.emplace_back(basegfx::utils::createPolygonFromRect(aRange));
+ }
+ break;
+ }
+ default :
+ {
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/getdigitlanguage.cxx b/drawinglayer/source/processor2d/getdigitlanguage.cxx
new file mode 100644
index 000000000..858284b23
--- /dev/null
+++ b/drawinglayer/source/processor2d/getdigitlanguage.cxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <i18nlangtag/lang.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <svl/ctloptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include "getdigitlanguage.hxx"
+
+LanguageType drawinglayer::detail::getDigitLanguage() {
+ switch (SvtCTLOptions().GetCTLTextNumerals()) {
+ case SvtCTLOptions::NUMERALS_ARABIC:
+ return LANGUAGE_ENGLISH;
+ case SvtCTLOptions::NUMERALS_HINDI:
+ return LANGUAGE_ARABIC_SAUDI_ARABIA;
+ default:
+ return Application::GetSettings().GetLanguageTag().getLanguageType();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/getdigitlanguage.hxx b/drawinglayer/source/processor2d/getdigitlanguage.hxx
new file mode 100644
index 000000000..c22076fa7
--- /dev/null
+++ b/drawinglayer/source/processor2d/getdigitlanguage.hxx
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <i18nlangtag/lang.h>
+
+namespace drawinglayer::detail
+{
+/// Get digit language derived from SvtCTLOptions
+LanguageType getDigitLanguage();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx b/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx
new file mode 100644
index 000000000..9f838a7e1
--- /dev/null
+++ b/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "helperwrongspellrenderer.hxx"
+#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
+#include <tools/gen.hxx>
+#include <vcl/outdev.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <vcl/outdev/ScopedStates.hxx>
+
+using namespace css;
+
+namespace drawinglayer
+{
+namespace
+{
+constexpr sal_uInt32 constMinimumFontHeight = 5; // #define WRONG_SHOW_MIN 5
+}
+
+bool renderWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellCandidate,
+ OutputDevice& rOutputDevice,
+ const basegfx::B2DHomMatrix& rObjectToViewTransformation,
+ const basegfx::BColorModifierStack& rBColorModifierStack)
+{
+ const basegfx::B2DHomMatrix aLocalTransform(rObjectToViewTransformation
+ * rWrongSpellCandidate.getTransformation());
+ const basegfx::B2DVector aFontVectorPixel(aLocalTransform * basegfx::B2DVector(0.0, 1.0));
+ const sal_uInt32 nFontPixelHeight(basegfx::fround(aFontVectorPixel.getLength()));
+
+ if (nFontPixelHeight <= constMinimumFontHeight)
+ return true;
+
+ const basegfx::B2DPoint aStart(aLocalTransform
+ * basegfx::B2DPoint(rWrongSpellCandidate.getStart(), 0.0));
+ const basegfx::B2DPoint aStop(aLocalTransform
+ * basegfx::B2DPoint(rWrongSpellCandidate.getStop(), 0.0));
+ const Point aVclStart(basegfx::fround(aStart.getX()), basegfx::fround(aStart.getY()));
+ const Point aVclStop(basegfx::fround(aStop.getX()), basegfx::fround(aStop.getY()));
+
+ // #i101075# draw it. Do not forget to use the evtl. offsetted origin of the target device,
+ // e.g. when used with mask/transparence buffer device
+ const Point aOrigin(rOutputDevice.GetMapMode().GetOrigin());
+
+ const basegfx::BColor aProcessedColor(
+ rBColorModifierStack.getModifiedColor(rWrongSpellCandidate.getColor()));
+ const bool bMapModeEnabledState(rOutputDevice.IsMapModeEnabled());
+
+ vcl::ScopedAntialiasing a(rOutputDevice, true);
+ rOutputDevice.EnableMapMode(false);
+ rOutputDevice.SetLineColor(Color(aProcessedColor));
+ rOutputDevice.SetFillColor();
+ rOutputDevice.DrawWaveLine(aOrigin + aVclStart, aOrigin + aVclStop);
+ rOutputDevice.EnableMapMode(bMapModeEnabledState);
+
+ // cannot really go wrong
+ return true;
+}
+} // end of namespace drawinglayer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx b/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx
new file mode 100644
index 000000000..886ce99bb
--- /dev/null
+++ b/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+class OutputDevice;
+
+namespace drawinglayer::primitive2d
+{
+class WrongSpellPrimitive2D;
+}
+
+namespace basegfx
+{
+class B2DHomMatrix;
+class BColorModifierStack;
+}
+
+// support WrongSpell rendering using VCL from primitives due to VCLs nice
+// and fast solution with wavelines
+
+namespace drawinglayer
+{
+bool renderWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellCandidate,
+ OutputDevice& rOutputDevice,
+ const basegfx::B2DHomMatrix& rObjectToViewTransformation,
+ const basegfx::BColorModifierStack& rBColorModifierStack);
+
+} // end of namespace drawinglayer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
new file mode 100644
index 000000000..dc7d6fa7c
--- /dev/null
+++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
@@ -0,0 +1,551 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor2d/hittestprocessor2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <drawinglayer/processor3d/cutfindprocessor3d.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <comphelper/lok.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+namespace drawinglayer::processor2d
+{
+ HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+ const basegfx::B2DPoint& rLogicHitPosition,
+ double fLogicHitTolerance,
+ bool bHitTextOnly)
+ : BaseProcessor2D(rViewInformation),
+ mfDiscreteHitTolerance(0.0),
+ mbCollectHitStack(false),
+ mbHit(false),
+ mbHitTextOnly(bHitTextOnly)
+ {
+ // init hit tolerance
+ mfDiscreteHitTolerance = fLogicHitTolerance;
+
+ if(basegfx::fTools::less(mfDiscreteHitTolerance, 0.0))
+ {
+ // ensure input parameter for hit tolerance is >= 0.0
+ mfDiscreteHitTolerance = 0.0;
+ }
+ else if(basegfx::fTools::more(mfDiscreteHitTolerance, 0.0))
+ {
+ // generate discrete hit tolerance
+ mfDiscreteHitTolerance = (getViewInformation2D().getObjectToViewTransformation()
+ * basegfx::B2DVector(mfDiscreteHitTolerance, 0.0)).getLength();
+ }
+
+ // generate discrete hit position
+ maDiscreteHitPosition = getViewInformation2D().getObjectToViewTransformation() * rLogicHitPosition;
+ }
+
+ HitTestProcessor2D::~HitTestProcessor2D()
+ {
+ }
+
+ bool HitTestProcessor2D::checkHairlineHitWithTolerance(
+ const basegfx::B2DPolygon& rPolygon,
+ double fDiscreteHitTolerance) const
+ {
+ basegfx::B2DPolygon aLocalPolygon(rPolygon);
+ aLocalPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
+
+ // get discrete range
+ basegfx::B2DRange aPolygonRange(aLocalPolygon.getB2DRange());
+
+ if(basegfx::fTools::more(fDiscreteHitTolerance, 0.0))
+ {
+ aPolygonRange.grow(fDiscreteHitTolerance);
+ }
+
+ // do rough range test first
+ if(aPolygonRange.isInside(getDiscreteHitPosition()))
+ {
+ // check if a polygon edge is hit
+ return basegfx::utils::isInEpsilonRange(
+ aLocalPolygon,
+ getDiscreteHitPosition(),
+ fDiscreteHitTolerance);
+ }
+
+ return false;
+ }
+
+ bool HitTestProcessor2D::checkFillHitWithTolerance(
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fDiscreteHitTolerance) const
+ {
+ bool bRetval(false);
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
+ aLocalPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
+
+ // get discrete range
+ basegfx::B2DRange aPolygonRange(aLocalPolyPolygon.getB2DRange());
+ const bool bDiscreteHitToleranceUsed(basegfx::fTools::more(fDiscreteHitTolerance, 0.0));
+
+ if(bDiscreteHitToleranceUsed)
+ {
+ aPolygonRange.grow(fDiscreteHitTolerance);
+ }
+
+ // do rough range test first
+ if(aPolygonRange.isInside(getDiscreteHitPosition()))
+ {
+ // if a HitTolerance is given, check for polygon edge hit in epsilon first
+ if(bDiscreteHitToleranceUsed &&
+ basegfx::utils::isInEpsilonRange(
+ aLocalPolyPolygon,
+ getDiscreteHitPosition(),
+ fDiscreteHitTolerance))
+ {
+ bRetval = true;
+ }
+
+ // check for hit in filled polyPolygon
+ if(!bRetval && basegfx::utils::isInside(
+ aLocalPolyPolygon,
+ getDiscreteHitPosition(),
+ true))
+ {
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+ }
+
+ void HitTestProcessor2D::check3DHit(const primitive2d::ScenePrimitive2D& rCandidate)
+ {
+ // calculate relative point in unified 2D scene
+ const basegfx::B2DPoint aLogicHitPosition(getViewInformation2D().getInverseObjectToViewTransformation() * getDiscreteHitPosition());
+
+ // use bitmap check in ScenePrimitive2D
+ bool bTryFastResult(false);
+
+ if(rCandidate.tryToCheckLastVisualisationDirectHit(aLogicHitPosition, bTryFastResult))
+ {
+ mbHit = bTryFastResult;
+ }
+ else
+ {
+ basegfx::B2DHomMatrix aInverseSceneTransform(rCandidate.getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * aLogicHitPosition);
+
+ // check if test point is inside scene's unified area at all
+ if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0
+ && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
+ {
+ // get 3D view information
+ const geometry::ViewInformation3D& rObjectViewInformation3D = rCandidate.getViewInformation3D();
+
+ // create HitPoint Front and Back, transform to object coordinates
+ basegfx::B3DHomMatrix aViewToObject(rObjectViewInformation3D.getObjectToView());
+ aViewToObject.invert();
+ const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
+ const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
+
+ if(!aFront.equal(aBack))
+ {
+ const primitive3d::Primitive3DContainer& rPrimitives = rCandidate.getChildren3D();
+
+ if(!rPrimitives.empty())
+ {
+ // make BoundVolume empty and overlapping test for speedup
+ const basegfx::B3DRange aObjectRange(
+ rPrimitives.getB3DRange(rObjectViewInformation3D));
+
+ if(!aObjectRange.isEmpty())
+ {
+ const basegfx::B3DRange aFrontBackRange(aFront, aBack);
+
+ if(aObjectRange.overlaps(aFrontBackRange))
+ {
+ // bound volumes hit, geometric cut tests needed
+ drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(
+ rObjectViewInformation3D,
+ aFront,
+ aBack,
+ true);
+ aCutFindProcessor.process(rPrimitives);
+
+ mbHit = (!aCutFindProcessor.getCutPoints().empty());
+ }
+ }
+ }
+ }
+ }
+
+ if(!getHit())
+ {
+ // empty 3D scene; Check for border hit
+ basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
+ aOutline.transform(rCandidate.getObjectTransformation());
+
+ mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
+ }
+ }
+ }
+
+ void HitTestProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+ {
+ if(getHit())
+ {
+ // stop processing as soon as a hit was recognized
+ return;
+ }
+
+ switch(rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
+ {
+ // remember current ViewInformation2D
+ const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new local ViewInformation2D containing transformation
+ const geometry::ViewInformation2D aViewInformation2D(
+ getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
+ getViewInformation2D().getViewTransformation(),
+ getViewInformation2D().getViewport(),
+ getViewInformation2D().getVisualizedPage(),
+ getViewInformation2D().getViewTime());
+ updateViewInformation(aViewInformation2D);
+
+ // process child content recursively
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ // create hairline in discrete coordinates
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate));
+
+ // use hairline test
+ mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ // handle marker like hairline; no need to decompose in dashes
+ const primitive2d::PolygonMarkerPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonMarkerPrimitive2D& >(rCandidate));
+
+ // use hairline test
+ mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ // handle stroke evtl. directly; no need to decompose to filled polygon outlines
+ const primitive2d::PolygonStrokePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate));
+ const attribute::LineAttribute& rLineAttribute = rPolygonCandidate.getLineAttribute();
+
+ if(basegfx::fTools::more(rLineAttribute.getWidth(), 0.0))
+ {
+ if(basegfx::B2DLineJoin::Miter == rLineAttribute.getLineJoin())
+ {
+ // if line is mitered, use decomposition since mitered line
+ // geometry may use more space than the geometry grown by half line width
+ process(rCandidate);
+ }
+ else
+ {
+ // for all other B2DLINEJOIN_* do a hairline HitTest with expanded tolerance
+ const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
+ * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, 0.0));
+ mbHit = checkHairlineHitWithTolerance(
+ rPolygonCandidate.getB2DPolygon(),
+ getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
+ }
+ }
+ else
+ {
+ // hairline; fallback to hairline test. Do not decompose
+ // since this may decompose the hairline to dashes
+ mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
+ }
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ // do not use decompose; just handle like a line with width
+ const primitive2d::PolygonWavePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonWavePrimitive2D& >(rCandidate));
+ double fLogicHitTolerance(0.0);
+
+ // if WaveHeight, grow by it
+ if(basegfx::fTools::more(rPolygonCandidate.getWaveHeight(), 0.0))
+ {
+ fLogicHitTolerance += rPolygonCandidate.getWaveHeight();
+ }
+
+ // if line width, grow by it
+ if(basegfx::fTools::more(rPolygonCandidate.getLineAttribute().getWidth(), 0.0))
+ {
+ fLogicHitTolerance += rPolygonCandidate.getLineAttribute().getWidth() * 0.5;
+ }
+
+ const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
+ * basegfx::B2DVector(fLogicHitTolerance, 0.0));
+
+ mbHit = checkHairlineHitWithTolerance(
+ rPolygonCandidate.getB2DPolygon(),
+ getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ // create filled polyPolygon in discrete coordinates
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
+
+ // use fill hit test
+ mbHit = checkFillHitWithTolerance(rPolygonCandidate.getB2DPolyPolygon(), getDiscreteHitTolerance());
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
+ {
+ // sub-transparence group
+ const primitive2d::TransparencePrimitive2D& rTransCandidate(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate));
+
+ // Currently the transparence content is not taken into account; only
+ // the children are recursively checked for hit. This may be refined for
+ // parts where the content is completely transparent if needed.
+ process(rTransCandidate.getChildren());
+
+ break;
+ }
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
+ {
+ // create mask in discrete coordinates; only recursively continue
+ // with content when HitTest position is inside the mask
+ const primitive2d::MaskPrimitive2D& rMaskCandidate(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate));
+
+ // use fill hit test
+ if(checkFillHitWithTolerance(rMaskCandidate.getMask(), getDiscreteHitTolerance()))
+ {
+ // recursively HitTest children
+ process(rMaskCandidate.getChildren());
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ const primitive2d::ScenePrimitive2D& rScenePrimitive2D(
+ static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
+ check3DHit(rScenePrimitive2D);
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
+ case PRIMITIVE2D_ID_GRIDPRIMITIVE2D :
+ case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D :
+ {
+ // ignorable primitives
+ break;
+ }
+ case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D :
+ {
+ // Ignore shadows; we do not want to have shadows hittable.
+ // Remove this one to make shadows hittable on demand.
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
+ {
+ // for text use the BoundRect of the primitive itself
+ const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
+
+ if(!aRange.isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
+ mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ // The recently added BitmapEx::GetTransparency() makes it easy to extend
+ // the BitmapPrimitive2D HitTest to take the contained BitmapEx and it's
+ // transparency into account
+ const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
+
+ if(!aRange.isEmpty())
+ {
+ const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
+ const BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap()));
+ const Size& rSizePixel(aBitmapEx.GetSizePixel());
+
+ // When tiled rendering, don't bother with the pixel size of the candidate.
+ if(rSizePixel.Width() && rSizePixel.Height() && !comphelper::LibreOfficeKit::isActive())
+ {
+ basegfx::B2DHomMatrix aBackTransform(
+ getViewInformation2D().getObjectToViewTransformation() *
+ rBitmapCandidate.getTransform());
+ aBackTransform.invert();
+
+ const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition());
+ const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+
+ if(aUnitRange.isInside(aRelativePoint))
+ {
+ const sal_Int32 nX(basegfx::fround(aRelativePoint.getX() * rSizePixel.Width()));
+ const sal_Int32 nY(basegfx::fround(aRelativePoint.getY() * rSizePixel.Height()));
+
+ mbHit = (0 != aBitmapEx.GetAlpha(nX, nY));
+ }
+ }
+ else
+ {
+ // fallback to standard HitTest
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
+ mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
+ }
+ }
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
+ case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D :
+ case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D :
+ case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D :
+ case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
+ case PRIMITIVE2D_ID_MEDIAPRIMITIVE2D:
+ {
+ if(!getHitTextOnly())
+ {
+ // Class of primitives for which just the BoundRect of the primitive itself
+ // will be used for HitTest currently.
+ //
+ // This may be refined in the future, e.g:
+ // - For Bitmaps, the mask and/or transparence information may be used
+ // - For MetaFiles, the MetaFile content may be used
+ const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
+
+ if(!aRange.isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
+ mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
+ }
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D :
+ {
+ // HiddenGeometryPrimitive2D; the default decomposition would return an empty sequence,
+ // so force this primitive to process its children directly if the switch is set
+ // (which is the default). Else, ignore invisible content
+ const primitive2d::HiddenGeometryPrimitive2D& rHiddenGeometry(static_cast< const primitive2d::HiddenGeometryPrimitive2D& >(rCandidate));
+ const primitive2d::Primitive2DContainer& rChildren = rHiddenGeometry.getChildren();
+
+ if(!rChildren.empty())
+ {
+ process(rChildren);
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
+ {
+ if(!getHitTextOnly())
+ {
+ const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate));
+ const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions();
+ const sal_uInt32 nCount(rPositions.size());
+
+ for(sal_uInt32 a(0); !getHit() && a < nCount; a++)
+ {
+ const basegfx::B2DPoint aPosition(getViewInformation2D().getObjectToViewTransformation() * rPositions[a]);
+ const basegfx::B2DVector aDistance(aPosition - getDiscreteHitPosition());
+
+ if(aDistance.getLength() <= getDiscreteHitTolerance())
+ {
+ mbHit = true;
+ }
+ }
+ }
+
+ break;
+ }
+ default :
+ {
+ // process recursively
+ process(rCandidate);
+
+ break;
+ }
+ }
+
+ if (getHit() && getCollectHitStack())
+ {
+ /// push candidate to HitStack to create it. This only happens when a hit is found and
+ /// creating the HitStack was requested (see collectHitStack)
+ maHitStack.append(primitive2d::Primitive2DReference(const_cast< primitive2d::BasePrimitive2D* >(&rCandidate)));
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/linegeometryextractor2d.cxx b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx
new file mode 100644
index 000000000..08ec1232d
--- /dev/null
+++ b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor2d
+{
+ LineGeometryExtractor2D::LineGeometryExtractor2D(const geometry::ViewInformation2D& rViewInformation)
+ : BaseProcessor2D(rViewInformation),
+ mbInLineGeometry(false)
+ {
+ }
+
+ LineGeometryExtractor2D::~LineGeometryExtractor2D()
+ {
+ }
+
+ void LineGeometryExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+ {
+ switch(rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D :
+ {
+ // enter a line geometry group (with or without LineEnds)
+ bool bOldState(mbInLineGeometry);
+ mbInLineGeometry = true;
+ process(rCandidate);
+ mbInLineGeometry = bOldState;
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
+ {
+ if(mbInLineGeometry)
+ {
+ // extract hairline line geometry in world coordinates
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate));
+ basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon());
+ aLocalPolygon.transform(getViewInformation2D().getObjectTransformation());
+ maExtractedHairlines.push_back(aLocalPolygon);
+ }
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
+ {
+ if(mbInLineGeometry)
+ {
+ // extract filled line geometry (line with width)
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
+ aLocalPolyPolygon.transform(getViewInformation2D().getObjectTransformation());
+ maExtractedLineFills.push_back(aLocalPolyPolygon);
+ }
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
+ {
+ // remember current transformation and ViewInformation
+ const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new transformations for CurrentTransformation and for local ViewInformation2D
+ const geometry::ViewInformation2D aViewInformation2D(
+ getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
+ getViewInformation2D().getViewTransformation(),
+ getViewInformation2D().getViewport(),
+ getViewInformation2D().getVisualizedPage(),
+ getViewInformation2D().getViewTime());
+ updateViewInformation(aViewInformation2D);
+
+ // process content
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+
+ break;
+ }
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
+ {
+ // ignorable primitives
+ break;
+ }
+ default :
+ {
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/objectinfoextractor2d.cxx b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
new file mode 100644
index 000000000..552406d53
--- /dev/null
+++ b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor2d/objectinfoextractor2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::processor2d
+{
+ void ObjectInfoPrimitiveExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+ {
+ if(mpFound)
+ return;
+
+ switch(rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D :
+ {
+ mpFound = dynamic_cast< const primitive2d::ObjectInfoPrimitive2D* >(&rCandidate);
+ break;
+ }
+ default :
+ {
+ // we look for an encapsulated primitive, so do not decompose primitives
+ // based on GroupPrimitive2D, just visit their children. It may be that more
+ // group-like primitives need to be added here, but all primitives with
+ // grouping functionality should be implemented based on the GroupPrimitive2D
+ // class and have their main content accessible as children
+ const primitive2d::GroupPrimitive2D* pGroupPrimitive2D = dynamic_cast< const primitive2d::GroupPrimitive2D* >(&rCandidate);
+
+ if(pGroupPrimitive2D)
+ {
+ // process group children recursively
+ process(pGroupPrimitive2D->getChildren());
+ }
+ else
+ {
+ // do not process recursively, we *only* want to find existing
+ // ObjectInfoPrimitive2D entries
+ }
+
+ break;
+ }
+ }
+ }
+
+ ObjectInfoPrimitiveExtractor2D::ObjectInfoPrimitiveExtractor2D(const geometry::ViewInformation2D& rViewInformation)
+ : BaseProcessor2D(rViewInformation),
+ mpFound(nullptr)
+ {
+ }
+
+ ObjectInfoPrimitiveExtractor2D::~ObjectInfoPrimitiveExtractor2D()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/processor2dtools.cxx b/drawinglayer/source/processor2d/processor2dtools.cxx
new file mode 100644
index 000000000..7bc0f5fa0
--- /dev/null
+++ b/drawinglayer/source/processor2d/processor2dtools.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <vcl/gdimtf.hxx>
+#include "vclpixelprocessor2d.hxx"
+#include "vclmetafileprocessor2d.hxx"
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor2d
+{
+ std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromOutputDevice(
+ OutputDevice& rTargetOutDev,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
+ {
+ // create Pixel Vcl-Processor
+ return std::make_unique<VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev);
+ }
+
+ std::unique_ptr<BaseProcessor2D> createProcessor2DFromOutputDevice(
+ OutputDevice& rTargetOutDev,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
+ {
+ const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile();
+ const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
+
+ if(bOutputToRecordingMetaFile)
+ {
+ // create MetaFile Vcl-Processor and process
+ return std::make_unique<VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev);
+ }
+ else
+ {
+ // create Pixel Vcl-Processor
+ return createPixelProcessor2DFromOutputDevice(
+ rTargetOutDev,
+ rViewInformation2D);
+ }
+ }
+
+} // end of namespace
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/processorfromoutputdevice.cxx b/drawinglayer/source/processor2d/processorfromoutputdevice.cxx
new file mode 100644
index 000000000..c8433753a
--- /dev/null
+++ b/drawinglayer/source/processor2d/processorfromoutputdevice.cxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/outdev.hxx>
+#include <vcl/gdimtf.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include "vclmetafileprocessor2d.hxx"
+#include "vclpixelprocessor2d.hxx"
+
+using namespace com::sun::star;
+
+namespace drawinglayer::processor2d
+{
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> createBaseProcessor2DFromOutputDevice(
+ OutputDevice& rTargetOutDev,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
+ {
+ const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile();
+ const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
+
+ if(bOutputToRecordingMetaFile)
+ {
+ // create MetaFile Vcl-Processor and process
+ return std::make_unique<drawinglayer::processor2d::VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev);
+ }
+ else
+ {
+ // create Pixel Vcl-Processor
+ return std::make_unique<drawinglayer::processor2d::VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev);
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx
new file mode 100644
index 000000000..fcf15b189
--- /dev/null
+++ b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+
+
+namespace drawinglayer::processor2d
+{
+ void TextAsPolygonExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+ {
+ switch(rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
+ {
+ // TextDecoratedPortionPrimitive2D can produce the following primitives
+ // when being decomposed:
+ //
+ // - TextSimplePortionPrimitive2D
+ // - PolygonWavePrimitive2D
+ // - PolygonStrokePrimitive2D
+ // - PolygonStrokePrimitive2D
+ // - PolyPolygonColorPrimitive2D
+ // - PolyPolygonHairlinePrimitive2D
+ // - PolygonHairlinePrimitive2D
+ // - ShadowPrimitive2D
+ // - ModifiedColorPrimitive2D
+ // - TransformPrimitive2D
+ // - TextEffectPrimitive2D
+ // - ModifiedColorPrimitive2D
+ // - TransformPrimitive2D
+ // - GroupPrimitive2D
+
+ // encapsulate with flag and use decomposition
+ mnInText++;
+ process(rCandidate);
+ mnInText--;
+
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
+ {
+ // TextSimplePortionPrimitive2D can produce the following primitives
+ // when being decomposed:
+ //
+ // - PolyPolygonColorPrimitive2D
+ // - TextEffectPrimitive2D
+ // - ModifiedColorPrimitive2D
+ // - TransformPrimitive2D
+ // - GroupPrimitive2D
+
+ // encapsulate with flag and use decomposition
+ mnInText++;
+ process(rCandidate);
+ mnInText--;
+
+ break;
+ }
+
+ // as can be seen from the TextSimplePortionPrimitive2D and the
+ // TextDecoratedPortionPrimitive2D, inside of the mnInText marks
+ // the following primitives can occur containing geometry data
+ // from text decomposition:
+ //
+ // - PolyPolygonColorPrimitive2D
+ // - PolygonHairlinePrimitive2D
+ // - PolyPolygonHairlinePrimitive2D (for convenience)
+ //
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
+ {
+ if(mnInText)
+ {
+ const primitive2d::PolyPolygonColorPrimitive2D& rPoPoCoCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
+ basegfx::B2DPolyPolygon aPolyPolygon(rPoPoCoCandidate.getB2DPolyPolygon());
+
+ if(aPolyPolygon.count())
+ {
+ // transform the PolyPolygon
+ aPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
+
+ // get evtl. corrected color
+ const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rPoPoCoCandidate.getBColor()));
+
+ // add to result vector
+ maTarget.emplace_back(aPolyPolygon, aColor, true);
+ }
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
+ {
+ if(mnInText)
+ {
+ const primitive2d::PolygonHairlinePrimitive2D& rPoHaCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate));
+ basegfx::B2DPolygon aPolygon(rPoHaCandidate.getB2DPolygon());
+
+ if(aPolygon.count())
+ {
+ // transform the Polygon
+ aPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
+
+ // get evtl. corrected color
+ const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rPoHaCandidate.getBColor()));
+
+ // add to result vector
+ maTarget.emplace_back(basegfx::B2DPolyPolygon(aPolygon), aColor, false);
+ }
+ }
+
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D :
+ {
+ if(mnInText)
+ {
+ const primitive2d::PolyPolygonHairlinePrimitive2D& rPoPoHaCandidate(static_cast< const primitive2d::PolyPolygonHairlinePrimitive2D& >(rCandidate));
+ basegfx::B2DPolyPolygon aPolyPolygon(rPoPoHaCandidate.getB2DPolyPolygon());
+
+ if(aPolyPolygon.count())
+ {
+ // transform the Polygon
+ aPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
+
+ // get evtl. corrected color
+ const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rPoPoHaCandidate.getBColor()));
+
+ // add to result vector
+ maTarget.emplace_back(aPolyPolygon, aColor, false);
+ }
+ }
+
+ break;
+ }
+
+ // usage of color modification stack is needed
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D :
+ {
+ const primitive2d::ModifiedColorPrimitive2D& rModifiedColorCandidate(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate));
+
+ if(!rModifiedColorCandidate.getChildren().empty())
+ {
+ maBColorModifierStack.push(rModifiedColorCandidate.getColorModifier());
+ process(rModifiedColorCandidate.getChildren());
+ maBColorModifierStack.pop();
+ }
+
+ break;
+ }
+
+ // usage of transformation stack is needed
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
+ {
+ // remember current transformation and ViewInformation
+ const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new transformations for CurrentTransformation and for local ViewInformation2D
+ const geometry::ViewInformation2D aViewInformation2D(
+ getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
+ getViewInformation2D().getViewTransformation(),
+ getViewInformation2D().getViewport(),
+ getViewInformation2D().getVisualizedPage(),
+ getViewInformation2D().getViewTime());
+ updateViewInformation(aViewInformation2D);
+
+ // process content
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+
+ break;
+ }
+
+ // ignorable primitives
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
+ {
+ break;
+ }
+
+ default :
+ {
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+ }
+
+ TextAsPolygonExtractor2D::TextAsPolygonExtractor2D(const geometry::ViewInformation2D& rViewInformation)
+ : BaseProcessor2D(rViewInformation),
+ maBColorModifierStack(),
+ mnInText(0)
+ {
+ }
+
+ TextAsPolygonExtractor2D::~TextAsPolygonExtractor2D()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
new file mode 100644
index 000000000..4a2934402
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
@@ -0,0 +1,444 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "vclhelperbufferdevice.hxx"
+#include <basegfx/range/b2drange.hxx>
+#include <vcl/bitmapex.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <tools/stream.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+#include <mutex>
+
+// buffered VDev usage
+
+namespace
+{
+class VDevBuffer : public Timer
+{
+private:
+ struct Entry
+ {
+ VclPtr<VirtualDevice> buf;
+ bool isTransparent = false;
+ Entry(const VclPtr<VirtualDevice>& vdev, bool bTransparent)
+ : buf(vdev)
+ , isTransparent(bTransparent)
+ {
+ }
+ };
+
+ std::mutex m_aMutex;
+
+ // available buffers
+ std::vector<Entry> maFreeBuffers;
+
+ // allocated/used buffers (remembered to allow deleting them in destructor)
+ std::vector<Entry> maUsedBuffers;
+
+ // remember what outputdevice was the template passed to VirtualDevice::Create
+ // so we can test if that OutputDevice was disposed before reusing a
+ // virtualdevice because that isn't safe to do at least for Gtk2
+ std::map<VclPtr<VirtualDevice>, VclPtr<OutputDevice>> maDeviceTemplates;
+
+ static bool isSizeSuitable(const VclPtr<VirtualDevice>& device, const Size& size);
+
+public:
+ VDevBuffer();
+ virtual ~VDevBuffer() override;
+
+ VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bTransparent);
+ void free(VirtualDevice& rDevice);
+
+ // Timer virtuals
+ virtual void Invoke() override;
+};
+
+VDevBuffer::VDevBuffer()
+ : Timer("drawinglayer::VDevBuffer via Invoke()")
+{
+ SetTimeout(10L * 1000L); // ten seconds
+}
+
+VDevBuffer::~VDevBuffer()
+{
+ std::unique_lock aGuard(m_aMutex);
+ Stop();
+
+ while (!maFreeBuffers.empty())
+ {
+ maFreeBuffers.back().buf.disposeAndClear();
+ maFreeBuffers.pop_back();
+ }
+
+ while (!maUsedBuffers.empty())
+ {
+ maUsedBuffers.back().buf.disposeAndClear();
+ maUsedBuffers.pop_back();
+ }
+}
+
+bool VDevBuffer::isSizeSuitable(const VclPtr<VirtualDevice>& device, const Size& rSizePixel)
+{
+ if (device->GetOutputWidthPixel() >= rSizePixel.getWidth()
+ && device->GetOutputHeightPixel() >= rSizePixel.getHeight())
+ {
+ bool requireSmall = false;
+#if defined(UNX)
+ // HACK: See the small size handling in SvpSalVirtualDevice::CreateSurface().
+ // Make sure to not reuse a larger device when a small one should be preferred.
+ if (device->GetRenderBackendName() == "svp")
+ requireSmall = true;
+#endif
+ // The same for Skia, see renderMethodToUseForSize().
+ if (SkiaHelper::isVCLSkiaEnabled())
+ requireSmall = true;
+ if (requireSmall)
+ {
+ if (rSizePixel.getWidth() <= 32 && rSizePixel.getHeight() <= 32
+ && (device->GetOutputWidthPixel() > 32 || device->GetOutputHeightPixel() > 32))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel,
+ bool bTransparent)
+{
+ std::unique_lock aGuard(m_aMutex);
+ VclPtr<VirtualDevice> pRetval;
+
+ sal_Int32 nBits = rOutDev.GetBitCount();
+
+ bool bOkay(false);
+ if (!maFreeBuffers.empty())
+ {
+ auto aFound(maFreeBuffers.end());
+
+ for (auto a = maFreeBuffers.begin(); a != maFreeBuffers.end(); ++a)
+ {
+ assert(a->buf && "Empty pointer in VDevBuffer (!)");
+
+ if (nBits == a->buf->GetBitCount() && bTransparent == a->isTransparent)
+ {
+ // candidate is valid due to bit depth
+ if (aFound != maFreeBuffers.end())
+ {
+ // already found
+ if (bOkay)
+ {
+ // found is valid
+ const bool bCandidateOkay = isSizeSuitable(a->buf, rSizePixel);
+
+ if (bCandidateOkay)
+ {
+ // found and candidate are valid
+ const sal_uLong aSquare(aFound->buf->GetOutputWidthPixel()
+ * aFound->buf->GetOutputHeightPixel());
+ const sal_uLong aCandidateSquare(a->buf->GetOutputWidthPixel()
+ * a->buf->GetOutputHeightPixel());
+
+ if (aCandidateSquare < aSquare)
+ {
+ // candidate is valid and smaller, use it
+ aFound = a;
+ }
+ }
+ else
+ {
+ // found is valid, candidate is not. Keep found
+ }
+ }
+ else
+ {
+ // found is invalid, use candidate
+ aFound = a;
+ bOkay = isSizeSuitable(aFound->buf, rSizePixel);
+ }
+ }
+ else
+ {
+ // none yet, use candidate
+ aFound = a;
+ bOkay = isSizeSuitable(aFound->buf, rSizePixel);
+ }
+ }
+ }
+
+ if (aFound != maFreeBuffers.end())
+ {
+ pRetval = aFound->buf;
+ maFreeBuffers.erase(aFound);
+ }
+ }
+
+ if (pRetval)
+ {
+ // found a suitable cached virtual device, but the
+ // outputdevice it was based on has been disposed,
+ // drop it and create a new one instead as reusing
+ // such devices is unsafe under at least Gtk2
+ if (maDeviceTemplates[pRetval]->isDisposed())
+ {
+ maDeviceTemplates.erase(pRetval);
+ pRetval.disposeAndClear();
+ }
+ else
+ {
+ if (bOkay)
+ {
+ pRetval->Erase(pRetval->PixelToLogic(
+ tools::Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight())));
+ }
+ else
+ {
+ pRetval->SetOutputSizePixel(rSizePixel, true);
+ }
+ }
+ }
+
+ // no success yet, create new buffer
+ if (!pRetval)
+ {
+ pRetval = VclPtr<VirtualDevice>::Create(rOutDev, DeviceFormat::DEFAULT,
+ bTransparent ? DeviceFormat::DEFAULT
+ : DeviceFormat::NONE);
+ maDeviceTemplates[pRetval] = &rOutDev;
+ pRetval->SetOutputSizePixel(rSizePixel, true);
+ }
+ else
+ {
+ // reused, reset some values
+ pRetval->SetMapMode();
+ pRetval->SetRasterOp(RasterOp::OverPaint);
+ }
+
+ // remember allocated buffer
+ maUsedBuffers.emplace_back(pRetval, bTransparent);
+
+ return pRetval;
+}
+
+void VDevBuffer::free(VirtualDevice& rDevice)
+{
+ std::unique_lock aGuard(m_aMutex);
+ const auto aUsedFound
+ = std::find_if(maUsedBuffers.begin(), maUsedBuffers.end(),
+ [&rDevice](const Entry& el) { return el.buf == &rDevice; });
+ SAL_WARN_IF(aUsedFound == maUsedBuffers.end(), "drawinglayer",
+ "OOps, non-registered buffer freed (!)");
+ if (aUsedFound != maUsedBuffers.end())
+ {
+ maFreeBuffers.emplace_back(*aUsedFound);
+ maUsedBuffers.erase(aUsedFound);
+ SAL_WARN_IF(maFreeBuffers.size() > 1000, "drawinglayer",
+ "excessive cached buffers, " << maFreeBuffers.size() << " entries!");
+ }
+ Start();
+}
+
+void VDevBuffer::Invoke()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ while (!maFreeBuffers.empty())
+ {
+ auto aLastOne = maFreeBuffers.back();
+ maDeviceTemplates.erase(aLastOne.buf);
+ aLastOne.buf.disposeAndClear();
+ maFreeBuffers.pop_back();
+ }
+}
+}
+
+// support for rendering Bitmap and BitmapEx contents
+
+namespace drawinglayer
+{
+// static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D)
+VDevBuffer& getVDevBuffer()
+{
+ // secure global instance with Vcl's safe destroyer of external (seen by
+ // library base) stuff, the remembered VDevs need to be deleted before
+ // Vcl's deinit
+ static vcl::DeleteOnDeinit<VDevBuffer> aVDevBuffer{};
+ return *aVDevBuffer.get();
+}
+
+impBufferDevice::impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange, bool bCrop)
+ : mrOutDev(rOutDev)
+ , mpContent(nullptr)
+ , mpAlpha(nullptr)
+{
+ basegfx::B2DRange aRangePixel(rRange);
+ aRangePixel.transform(mrOutDev.GetViewTransformation());
+ maDestPixel = tools::Rectangle(floor(aRangePixel.getMinX()), floor(aRangePixel.getMinY()),
+ ceil(aRangePixel.getMaxX()), ceil(aRangePixel.getMaxY()));
+ if (bCrop)
+ maDestPixel.Intersection({ {}, mrOutDev.GetOutputSizePixel() });
+
+ if (!isVisible())
+ return;
+
+ mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true);
+
+ // #i93485# assert when copying from window to VDev is used
+ SAL_WARN_IF(
+ mrOutDev.GetOutDevType() == OUTDEV_WINDOW, "drawinglayer",
+ "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)");
+
+ MapMode aNewMapMode(mrOutDev.GetMapMode());
+
+ const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
+ aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y()));
+
+ mpContent->SetMapMode(aNewMapMode);
+
+ // copy AA flag for new target
+ mpContent->SetAntialiasing(mrOutDev.GetAntialiasing());
+
+ // copy RasterOp (e.g. may be RasterOp::Xor on destination)
+ mpContent->SetRasterOp(mrOutDev.GetRasterOp());
+}
+
+impBufferDevice::~impBufferDevice()
+{
+ if (mpContent)
+ {
+ getVDevBuffer().free(*mpContent);
+ }
+
+ if (mpAlpha)
+ {
+ getVDevBuffer().free(*mpAlpha);
+ }
+}
+
+void impBufferDevice::paint(double fTrans)
+{
+ if (!isVisible())
+ return;
+
+ const Point aEmptyPoint;
+ const Size aSizePixel(maDestPixel.GetSize());
+ const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
+#ifdef DBG_UTIL
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+#endif
+
+ mrOutDev.EnableMapMode(false);
+ mpContent->EnableMapMode(false);
+
+#ifdef DBG_UTIL
+ if (bDoSaveForVisualControl)
+ {
+ SvFileStream aNew(
+#ifdef _WIN32
+ "c:\\content.bmp",
+#else
+ "~/content.bmp",
+#endif
+ StreamMode::WRITE | StreamMode::TRUNC);
+ Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
+ WriteDIB(aContent, aNew, false, true);
+ }
+#endif
+
+ // during painting the buffer, disable evtl. set RasterOp (may be RasterOp::Xor)
+ const RasterOp aOrigRasterOp(mrOutDev.GetRasterOp());
+ mrOutDev.SetRasterOp(RasterOp::OverPaint);
+
+ if (mpAlpha)
+ {
+ mpAlpha->EnableMapMode(false);
+ AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel));
+
+#ifdef DBG_UTIL
+ if (bDoSaveForVisualControl)
+ {
+ SvFileStream aNew(
+#ifdef _WIN32
+ "c:\\transparence.bmp",
+#else
+ "~/transparence.bmp",
+#endif
+ StreamMode::WRITE | StreamMode::TRUNC);
+ WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true);
+ }
+#endif
+
+ BitmapEx aContent(mpContent->GetBitmapEx(aEmptyPoint, aSizePixel));
+ aAlphaMask.BlendWith(aContent.GetAlpha());
+ mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent.GetBitmap(), aAlphaMask));
+ }
+ else if (0.0 != fTrans)
+ {
+ basegfx::B2DHomMatrix trans, scale;
+ trans.translate(maDestPixel.TopLeft().X(), maDestPixel.TopLeft().Y());
+ scale.scale(aSizePixel.Width(), aSizePixel.Height());
+ mrOutDev.DrawTransformedBitmapEx(
+ trans * scale, mpContent->GetBitmapEx(aEmptyPoint, aSizePixel), 1 - fTrans);
+ }
+ else
+ {
+ mrOutDev.DrawOutDev(maDestPixel.TopLeft(), aSizePixel, aEmptyPoint, aSizePixel, *mpContent);
+ }
+
+ mrOutDev.SetRasterOp(aOrigRasterOp);
+ mrOutDev.EnableMapMode(bWasEnabledDst);
+}
+
+VirtualDevice& impBufferDevice::getContent()
+{
+ SAL_WARN_IF(!mpContent, "drawinglayer",
+ "impBufferDevice: No content, check isVisible() before accessing (!)");
+ return *mpContent;
+}
+
+VirtualDevice& impBufferDevice::getTransparence()
+{
+ SAL_WARN_IF(!mpContent, "drawinglayer",
+ "impBufferDevice: No content, check isVisible() before accessing (!)");
+ if (!mpAlpha)
+ {
+ mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false);
+ mpAlpha->SetMapMode(mpContent->GetMapMode());
+
+ // copy AA flag for new target; masking needs to be smooth
+ mpAlpha->SetAntialiasing(mpContent->GetAntialiasing());
+ }
+
+ return *mpAlpha;
+}
+} // end of namespace drawinglayer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
new file mode 100644
index 000000000..99585b05b
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/virdev.hxx>
+
+namespace basegfx
+{
+class B2DRange;
+}
+
+// support methods for vcl direct gradient rendering
+
+namespace drawinglayer
+{
+class impBufferDevice
+{
+ OutputDevice& mrOutDev;
+ VclPtr<VirtualDevice> mpContent;
+ VclPtr<VirtualDevice> mpAlpha;
+ tools::Rectangle maDestPixel;
+
+public:
+ impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange, bool bCrop = true);
+ ~impBufferDevice();
+
+ void paint(double fTrans = 0.0);
+ bool isVisible() const { return !maDestPixel.IsEmpty(); }
+ VirtualDevice& getContent();
+ VirtualDevice& getTransparence();
+};
+} // end of namespace drawinglayer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
new file mode 100644
index 000000000..673fd093d
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -0,0 +1,2571 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cmath>
+#include <memory>
+#include "vclmetafileprocessor2d.hxx"
+#include "vclpixelprocessor2d.hxx"
+#include <rtl/ustring.hxx>
+#include <tools/gen.hxx>
+#include <tools/stream.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/processfactory.hxx>
+#include <config_global.h>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/graphictools.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/graph.hxx> // for PDFExtOutDevData Graphic support
+#include <toolkit/helper/formpdfexport.hxx> // for PDFExtOutDevData Graphic support
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
+#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> // for Title/Description metadata
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+using namespace com::sun::star;
+
+// #112245# definition for maximum allowed point count due to Metafile target.
+// To be on the safe side with the old tools polygon, use slightly less than
+// the theoretical maximum (bad experiences with tools polygon)
+
+#define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0)
+
+namespace
+{
+// #112245# helper to split line polygon in half
+void splitLinePolygon(const basegfx::B2DPolygon& rBasePolygon, basegfx::B2DPolygon& o_aLeft,
+ basegfx::B2DPolygon& o_aRight)
+{
+ const sal_uInt32 nCount(rBasePolygon.count());
+
+ if (nCount)
+ {
+ const sal_uInt32 nHalfCount((nCount - 1) >> 1);
+
+ o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1);
+ o_aLeft.setClosed(false);
+
+ o_aRight = basegfx::B2DPolygon(rBasePolygon, nHalfCount, nCount - nHalfCount);
+ o_aRight.setClosed(false);
+
+ if (rBasePolygon.isClosed())
+ {
+ o_aRight.append(rBasePolygon.getB2DPoint(0));
+
+ if (rBasePolygon.areControlPointsUsed())
+ {
+ o_aRight.setControlPoints(o_aRight.count() - 1, rBasePolygon.getPrevControlPoint(0),
+ rBasePolygon.getNextControlPoint(0));
+ }
+ }
+ }
+ else
+ {
+ o_aLeft.clear();
+ o_aRight.clear();
+ }
+}
+
+// #112245# helper to evtl. split filled polygons to maximum metafile point count
+void fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ const sal_uInt32 nPolyCount(rPolyPolygon.count());
+
+ if (!nPolyCount)
+ return;
+
+ basegfx::B2DPolyPolygon aSplitted;
+
+ for (sal_uInt32 a(0); a < nPolyCount; a++)
+ {
+ const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(a));
+ const sal_uInt32 nPointCount(aCandidate.count());
+ bool bNeedToSplit(false);
+
+ if (aCandidate.areControlPointsUsed())
+ {
+ // compare with the maximum for bezier curved polygons
+ bNeedToSplit = nPointCount > ((MAX_POLYGON_POINT_COUNT_METAFILE / 3L) - 1);
+ }
+ else
+ {
+ // compare with the maximum for simple point polygons
+ bNeedToSplit = nPointCount > (MAX_POLYGON_POINT_COUNT_METAFILE - 1);
+ }
+
+ if (bNeedToSplit)
+ {
+ // need to split the partial polygon
+ const basegfx::B2DRange aRange(aCandidate.getB2DRange());
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+
+ if (aRange.getWidth() > aRange.getHeight())
+ {
+ // clip in left and right
+ const basegfx::B2DPolyPolygon aLeft(basegfx::utils::clipPolygonOnParallelAxis(
+ aCandidate, false, true, aCenter.getX(), false));
+ const basegfx::B2DPolyPolygon aRight(basegfx::utils::clipPolygonOnParallelAxis(
+ aCandidate, false, false, aCenter.getX(), false));
+
+ aSplitted.append(aLeft);
+ aSplitted.append(aRight);
+ }
+ else
+ {
+ // clip in top and bottom
+ const basegfx::B2DPolyPolygon aTop(basegfx::utils::clipPolygonOnParallelAxis(
+ aCandidate, true, true, aCenter.getY(), false));
+ const basegfx::B2DPolyPolygon aBottom(basegfx::utils::clipPolygonOnParallelAxis(
+ aCandidate, true, false, aCenter.getY(), false));
+
+ aSplitted.append(aTop);
+ aSplitted.append(aBottom);
+ }
+ }
+ else
+ {
+ aSplitted.append(aCandidate);
+ }
+ }
+
+ if (aSplitted.count() != nPolyCount)
+ {
+ rPolyPolygon = aSplitted;
+ }
+}
+
+/** Filter input polypolygon for effectively empty sub-fills
+
+ Needed to fix fdo#37559
+
+ @param rPoly
+ tools::PolyPolygon to filter
+
+ @return converted tools PolyPolygon, w/o one-point fills
+ */
+tools::PolyPolygon getFillPolyPolygon(const ::basegfx::B2DPolyPolygon& rPoly)
+{
+ // filter input rPoly
+ basegfx::B2DPolyPolygon aPoly;
+ sal_uInt32 nCount(rPoly.count());
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ const basegfx::B2DPolygon& aCandidate(rPoly.getB2DPolygon(i));
+ if (!aCandidate.isClosed() || aCandidate.count() > 1)
+ aPoly.append(aCandidate);
+ }
+ return tools::PolyPolygon(aPoly);
+}
+
+} // end of anonymous namespace
+
+namespace drawinglayer::processor2d
+{
+tools::Rectangle
+VclMetafileProcessor2D::impDumpToMetaFile(const primitive2d::Primitive2DContainer& rContent,
+ GDIMetaFile& o_rContentMetafile)
+{
+ // Prepare VDev, MetaFile and connections
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ GDIMetaFile* pLastMetafile = mpMetaFile;
+ basegfx::B2DRange aPrimitiveRange(rContent.getB2DRange(getViewInformation2D()));
+
+ // transform primitive range with current transformation (e.g shadow offset)
+ aPrimitiveRange.transform(maCurrentTransformation);
+
+ const tools::Rectangle aPrimitiveRectangle(
+ basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()),
+ basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY()));
+ ScopedVclPtrInstance<VirtualDevice> aContentVDev;
+ MapMode aNewMapMode(pLastOutputDevice->GetMapMode());
+
+ mpOutputDevice = aContentVDev.get();
+ mpMetaFile = &o_rContentMetafile;
+ aContentVDev->EnableOutput(false);
+ aContentVDev->SetMapMode(pLastOutputDevice->GetMapMode());
+ o_rContentMetafile.Record(aContentVDev.get());
+ aContentVDev->SetLineColor(pLastOutputDevice->GetLineColor());
+ aContentVDev->SetFillColor(pLastOutputDevice->GetFillColor());
+ aContentVDev->SetFont(pLastOutputDevice->GetFont());
+ aContentVDev->SetDrawMode(pLastOutputDevice->GetDrawMode());
+ aContentVDev->SetSettings(pLastOutputDevice->GetSettings());
+ aContentVDev->SetRefPoint(pLastOutputDevice->GetRefPoint());
+
+ // dump to MetaFile
+ process(rContent);
+
+ // cleanups
+ o_rContentMetafile.Stop();
+ o_rContentMetafile.WindStart();
+ aNewMapMode.SetOrigin(aPrimitiveRectangle.TopLeft());
+ o_rContentMetafile.SetPrefMapMode(aNewMapMode);
+ o_rContentMetafile.SetPrefSize(aPrimitiveRectangle.GetSize());
+ mpOutputDevice = pLastOutputDevice;
+ mpMetaFile = pLastMetafile;
+
+ return aPrimitiveRectangle;
+}
+
+void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
+ Gradient& o_rVCLGradient, const attribute::FillGradientAttribute& rFiGrAtt,
+ bool bIsTransparenceGradient) const
+{
+ if (bIsTransparenceGradient)
+ {
+ // it's about transparence channel intensities (black/white), do not use color modifier
+ o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor()));
+ o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor()));
+ }
+ else
+ {
+ // use color modifier to influence start/end color of gradient
+ o_rVCLGradient.SetStartColor(
+ Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor())));
+ o_rVCLGradient.SetEndColor(
+ Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor())));
+ }
+
+ o_rVCLGradient.SetAngle(
+ Degree10(static_cast<sal_uInt32>(basegfx::rad2deg<10>(rFiGrAtt.getAngle()))));
+ o_rVCLGradient.SetBorder(static_cast<sal_uInt16>(rFiGrAtt.getBorder() * 100.0));
+ o_rVCLGradient.SetOfsX(static_cast<sal_uInt16>(rFiGrAtt.getOffsetX() * 100.0));
+ o_rVCLGradient.SetOfsY(static_cast<sal_uInt16>(rFiGrAtt.getOffsetY() * 100.0));
+ o_rVCLGradient.SetSteps(rFiGrAtt.getSteps());
+
+ // defaults for intensity; those were computed into the start/end colors already
+ o_rVCLGradient.SetStartIntensity(100);
+ o_rVCLGradient.SetEndIntensity(100);
+
+ switch (rFiGrAtt.getStyle())
+ {
+ default: // attribute::GradientStyle::Linear :
+ {
+ o_rVCLGradient.SetStyle(GradientStyle::Linear);
+ break;
+ }
+ case attribute::GradientStyle::Axial:
+ {
+ o_rVCLGradient.SetStyle(GradientStyle::Axial);
+ break;
+ }
+ case attribute::GradientStyle::Radial:
+ {
+ o_rVCLGradient.SetStyle(GradientStyle::Radial);
+ break;
+ }
+ case attribute::GradientStyle::Elliptical:
+ {
+ o_rVCLGradient.SetStyle(GradientStyle::Elliptical);
+ break;
+ }
+ case attribute::GradientStyle::Square:
+ {
+ o_rVCLGradient.SetStyle(GradientStyle::Square);
+ break;
+ }
+ case attribute::GradientStyle::Rect:
+ {
+ o_rVCLGradient.SetStyle(GradientStyle::Rect);
+ break;
+ }
+ }
+}
+
+void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill)
+{
+ if (pSvtGraphicFill && !mnSvtGraphicFillCount)
+ {
+ SvMemoryStream aMemStm;
+
+ WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill);
+ mpMetaFile->AddAction(new MetaCommentAction(
+ "XPATHFILL_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ aMemStm.TellEnd()));
+ mnSvtGraphicFillCount++;
+ }
+}
+
+void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill)
+{
+ if (pSvtGraphicFill && mnSvtGraphicFillCount)
+ {
+ mnSvtGraphicFillCount--;
+ mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
+ }
+}
+
+double VclMetafileProcessor2D::getTransformedLineWidth(double fWidth) const
+{
+ // #i113922# the LineWidth is duplicated in the MetaPolylineAction,
+ // and also inside the SvtGraphicStroke and needs transforming into
+ // the same space as its coordinates here cf. fdo#61789
+ // This is a partial fix. When an object transformation is used which
+ // e.g. contains a scaleX != scaleY, an unproportional scaling will happen.
+ const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation
+ * basegfx::B2DVector(fWidth, 0.0));
+
+ return aDiscreteUnit.getLength();
+}
+
+std::unique_ptr<SvtGraphicStroke> VclMetafileProcessor2D::impTryToCreateSvtGraphicStroke(
+ const basegfx::B2DPolygon& rB2DPolygon, const basegfx::BColor* pColor,
+ const attribute::LineAttribute* pLineAttribute,
+ const attribute::StrokeAttribute* pStrokeAttribute,
+ const attribute::LineStartEndAttribute* pStart, const attribute::LineStartEndAttribute* pEnd)
+{
+ std::unique_ptr<SvtGraphicStroke> pRetval;
+
+ if (rB2DPolygon.count() && !mnSvtGraphicStrokeCount)
+ {
+ basegfx::B2DPolygon aLocalPolygon(rB2DPolygon);
+ basegfx::BColor aStrokeColor;
+ basegfx::B2DPolyPolygon aStartArrow;
+ basegfx::B2DPolyPolygon aEndArrow;
+
+ if (pColor)
+ {
+ aStrokeColor = *pColor;
+ }
+ else if (pLineAttribute)
+ {
+ aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor());
+ }
+
+ // It IS needed to record the stroke color at all in the metafile,
+ // SvtGraphicStroke has NO entry for stroke color(!)
+ mpOutputDevice->SetLineColor(Color(aStrokeColor));
+
+ if (!aLocalPolygon.isClosed())
+ {
+ double fPolyLength(0.0);
+ double fStart(0.0);
+ double fEnd(0.0);
+
+ if (pStart && pStart->isActive())
+ {
+ fPolyLength = basegfx::utils::getLength(aLocalPolygon);
+
+ aStartArrow = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(),
+ fPolyLength, pStart->isCentered() ? 0.5 : 0.0, &fStart);
+ }
+
+ if (pEnd && pEnd->isActive())
+ {
+ if (basegfx::fTools::equalZero(fPolyLength))
+ {
+ fPolyLength = basegfx::utils::getLength(aLocalPolygon);
+ }
+
+ aEndArrow = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), fPolyLength,
+ pEnd->isCentered() ? 0.5 : 0.0, &fEnd);
+ }
+
+ if (0.0 != fStart || 0.0 != fEnd)
+ {
+ // build new poly, consume something from old poly
+ aLocalPolygon = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart,
+ fPolyLength - fEnd, fPolyLength);
+ }
+ }
+
+ SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone);
+ SvtGraphicStroke::CapType eCap(SvtGraphicStroke::capButt);
+ double fLineWidth(0.0);
+ double fMiterLength(0.0);
+ SvtGraphicStroke::DashArray aDashArray;
+
+ if (pLineAttribute)
+ {
+ fLineWidth = fMiterLength = getTransformedLineWidth(pLineAttribute->getWidth());
+
+ // get Join
+ switch (pLineAttribute->getLineJoin())
+ {
+ case basegfx::B2DLineJoin::NONE:
+ {
+ eJoin = SvtGraphicStroke::joinNone;
+ break;
+ }
+ case basegfx::B2DLineJoin::Bevel:
+ {
+ eJoin = SvtGraphicStroke::joinBevel;
+ break;
+ }
+ case basegfx::B2DLineJoin::Miter:
+ {
+ eJoin = SvtGraphicStroke::joinMiter;
+ // ATM 15 degrees is assumed
+ // TODO wait for P1383R0 and C++20's std::numbers::pi
+ fMiterLength /= std::sin(M_PI / 12);
+ break;
+ }
+ case basegfx::B2DLineJoin::Round:
+ {
+ eJoin = SvtGraphicStroke::joinRound;
+ break;
+ }
+ }
+
+ // get stroke
+ switch (pLineAttribute->getLineCap())
+ {
+ default: /* css::drawing::LineCap_BUTT */
+ {
+ eCap = SvtGraphicStroke::capButt;
+ break;
+ }
+ case css::drawing::LineCap_ROUND:
+ {
+ eCap = SvtGraphicStroke::capRound;
+ break;
+ }
+ case css::drawing::LineCap_SQUARE:
+ {
+ eCap = SvtGraphicStroke::capSquare;
+ break;
+ }
+ }
+ }
+
+ if (pStrokeAttribute)
+ {
+ // copy dash array
+ aDashArray = pStrokeAttribute->getDotDashArray();
+ }
+
+ // #i101734# apply current object transformation to created geometry.
+ // This is a partial fix. When an object transformation is used which
+ // e.g. contains a scaleX != scaleY, an unproportional scaling would
+ // have to be applied to the evtl. existing fat line. The current
+ // concept of PDF export and SvtGraphicStroke usage does simply not
+ // allow handling such definitions. The only clean way would be to
+ // add the transformation to SvtGraphicStroke and to handle it there
+ aLocalPolygon.transform(maCurrentTransformation);
+ aStartArrow.transform(maCurrentTransformation);
+ aEndArrow.transform(maCurrentTransformation);
+
+ pRetval.reset(
+ new SvtGraphicStroke(tools::Polygon(aLocalPolygon), tools::PolyPolygon(aStartArrow),
+ tools::PolyPolygon(aEndArrow), mfCurrentUnifiedTransparence,
+ fLineWidth, eCap, eJoin, fMiterLength, std::move(aDashArray)));
+ }
+
+ return pRetval;
+}
+
+void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke)
+{
+ if (pSvtGraphicStroke && !mnSvtGraphicStrokeCount)
+ {
+ SvMemoryStream aMemStm;
+
+ WriteSvtGraphicStroke(aMemStm, *pSvtGraphicStroke);
+ mpMetaFile->AddAction(new MetaCommentAction(
+ "XPATHSTROKE_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ aMemStm.TellEnd()));
+ mnSvtGraphicStrokeCount++;
+ }
+}
+
+void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke)
+{
+ if (pSvtGraphicStroke && mnSvtGraphicStrokeCount)
+ {
+ mnSvtGraphicStrokeCount--;
+ mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
+ }
+}
+
+void VclMetafileProcessor2D::popStructureElement(vcl::PDFWriter::StructElement eElem)
+{
+ if (!maListElements.empty() && maListElements.top() == eElem)
+ {
+ maListElements.pop();
+ mpPDFExtOutDevData->EndStructureElement();
+ }
+}
+
+void VclMetafileProcessor2D::popListItem()
+{
+ popStructureElement(vcl::PDFWriter::LIBody);
+ popStructureElement(vcl::PDFWriter::ListItem);
+}
+
+void VclMetafileProcessor2D::popList()
+{
+ popListItem();
+ popStructureElement(vcl::PDFWriter::List);
+}
+
+// init static break iterator
+uno::Reference<css::i18n::XBreakIterator> VclMetafileProcessor2D::mxBreakIterator;
+
+VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+ OutputDevice& rOutDev)
+ : VclProcessor2D(rViewInformation, rOutDev)
+ , mpMetaFile(rOutDev.GetConnectMetaFile())
+ , mnSvtGraphicFillCount(0)
+ , mnSvtGraphicStrokeCount(0)
+ , mfCurrentUnifiedTransparence(0.0)
+ , mpPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>(rOutDev.GetExtOutDevData()))
+ , mnCurrentOutlineLevel(-1)
+ , mbInListItem(false)
+ , mbBulletPresent(false)
+{
+ OSL_ENSURE(rOutDev.GetConnectMetaFile(),
+ "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)");
+ // draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation
+ // but only to ObjectTransformation. Do not change MapMode of destination.
+ maCurrentTransformation = rViewInformation.getObjectTransformation();
+}
+
+VclMetafileProcessor2D::~VclMetafileProcessor2D()
+{
+ // MapMode was not changed, no restore necessary
+}
+
+/***********************************************************************************************
+
+ Support of MetaCommentActions in the VclMetafileProcessor2D
+ Found MetaCommentActions and how they are supported:
+
+ XGRAD_SEQ_BEGIN, XGRAD_SEQ_END:
+
+ Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action.
+ It is used in various exporters/importers to have direct access to the gradient before it
+ is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g.
+ the Metafile to SdrObject import creates its gradient objects.
+ Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
+ map it back to the corresponding tools tools::PolyPolygon and the Gradient and just call
+ OutputDevice::DrawGradient which creates the necessary compatible actions.
+
+ XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END:
+
+ Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed
+ inside GDIMetaFile::Rotate, nothing to take care of here.
+ The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used
+ with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not
+ XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it
+ to the comment action. A closing end token is created in the destructor.
+ Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and
+ SdrRectObj.
+ The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind
+ of filled objects, even simple colored polygons. It is added as extra information; the
+ Metafile actions between the two tokens are interpreted as output generated from those
+ fills. Thus, users have the choice to use the SvtGraphicFill info or the created output
+ actions.
+ Even for XFillTransparenceItem it is used, thus it may need to be supported in
+ UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon.
+ Implemented for:
+ PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D,
+ PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D,
+ PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
+ PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D,
+ and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence
+
+ XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END:
+
+ Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one
+ is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the
+ contained path accordingly.
+ The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and
+ only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this
+ would hinder to make use of tools::PolyPolygon strokes. I will need to add support at:
+ PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
+ PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
+ PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
+ This can be done hierarchical, too.
+ Okay, base implementation done based on those three primitives.
+
+ FIELD_SEQ_BEGIN, FIELD_SEQ_END
+
+ Used from slideshow for URLs, created from diverse SvxField implementations inside
+ createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx
+ inside ImpEditEngine::Paint.
+ Created TextHierarchyFieldPrimitive2D and added needed infos there; it is a group primitive and wraps
+ text primitives (but is not limited to that). It contains the field type if special actions for the
+ support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is
+ needed, it may be supported there.
+ FIELD_SEQ_BEGIN;PageField
+ FIELD_SEQ_END
+ Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too.
+
+ XTEXT
+
+ XTEXT_EOC(i) end of character
+ XTEXT_EOW(i) end of word
+ XTEXT_EOS(i) end of sentence
+
+ this three are with index and are created with the help of an i18n::XBreakIterator in
+ ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some
+ data structure for holding those TEXT infos.
+ Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text
+ primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage
+ that this creations do not need to be done for all paints all the time. This would be
+ expensive since the BreakIterator and it's usage is expensive and for each paint also the
+ whole character stops would need to be created.
+ Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below)
+
+ XTEXT_EOL() end of line
+ XTEXT_EOP() end of paragraph
+
+ First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well,
+ i decided to solve it with structure. I added the TextHierarchyPrimitives for this,
+ namely:
+ - TextHierarchyLinePrimitive2D: Encapsulates single line
+ - TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph
+ - TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM)
+ Those are now supported in hierarchy. This means the MetaFile renderer will support them
+ by using them, recursively using their content and adding MetaFile comments as needed.
+ This also means that when another text layouter will be used it will be necessary to
+ create/support the same HierarchyPrimitives to support users.
+ To transport the information using this hierarchy is best suited to all future needs;
+ the slideshow will be able to profit from it directly when using primitives; all other
+ renderers not interested in the text structure will just ignore the encapsulations.
+
+ XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END
+ Supported now by the TextHierarchyBlockPrimitive2D.
+
+ EPSReplacementGraphic:
+ Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to
+ hold the original EPS which was imported in the same MetaFile as first 2 entries. Only
+ used to export the original again (if exists).
+ Not necessary to support with MetaFileRenderer.
+
+ XTEXT_SCROLLRECT, XTEXT_PAINTRECT
+ Currently used to get extra MetaFile infos using GraphicExporter which again uses
+ SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since
+ the rectangle data is added directly by the GraphicsExporter as comment. Does not need
+ to be adapted at once.
+ When adapting later, the only user - the diashow - should directly use the provided
+ Animation infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D)
+
+ PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END
+ VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as
+ a fix (hack) while VCL printing. It is needed to not downscale a bitmap which
+ was explicitly created for the printer already again to some default maximum
+ bitmap sizes.
+ Nothing to do here for the primitive renderer.
+
+ Support for vcl::PDFExtOutDevData:
+ PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at
+ the OutDev. When set, some extra data is written there. Trying simple PDF export and
+ watching if I get those infos.
+ Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses
+ the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check
+ if I get a PDFExtOutDevData at the target output device.
+ Indeed, I get one. Checking what all may be done when that extra-device-info is there.
+
+ All in all I have to talk to SJ. I will need to emulate some of those actions, but
+ i need to discuss which ones.
+ In the future, all those infos would be taken from the primitive sequence anyways,
+ thus these extensions would potentially be temporary, too.
+ Discussed with SJ, added the necessary support and tested it. Details follow.
+
+ - In ImpEditEngine::Paint, paragraph infos and URL stuff is added.
+ Added in primitive MetaFile renderer.
+ Checking URL: Indeed, current version exports it, but it is missing in primitive
+ CWS version. Adding support.
+ Okay, URLs work. Checked, Done.
+
+ - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
+ target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
+ This may be added in primitive MetaFile renderer.
+ Adding support...
+ OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace
+ svxform. Have to talk to FS if this has to be like that. Especially since
+ vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl.
+ Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move
+ that stuff to somewhere else, maybe tools or svtools ?!? We will see...
+ Moved to toolkit, so I have to link against it. I tried VCL first, but it did
+ not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other than the name
+ may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself,
+ the lowest movement plane is toolkit.
+ Checked form control export, it works well. Done.
+
+ - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
+ generated. I will need to check what happens here with primitives.
+ To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed.
+ Added support, but feature is broken in main version, so i cannot test at all.
+ Writing a bug to CL (or SJ) and seeing what happens (#i80380#).
+ SJ took a look and we got it working. Tested VCL MetaFile Renderer based export,
+ as intended, the original file is exported. Works, Done.
+
+
+ To be done:
+
+ - Maybe there are more places to take care of for vcl::PDFExtOutDevData!
+
+
+****************************************************************************************************/
+
+void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+{
+ switch (rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D:
+ {
+ // directdraw of wrong spell primitive
+ // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only
+ break;
+ }
+ case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D:
+ {
+ processGraphicPrimitive2D(
+ static_cast<const primitive2d::GraphicPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D:
+ {
+ processControlPrimitive2D(
+ static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D:
+ {
+ processTextHierarchyFieldPrimitive2D(
+ static_cast<const primitive2d::TextHierarchyFieldPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D:
+ {
+ processTextHierarchyLinePrimitive2D(
+ static_cast<const primitive2d::TextHierarchyLinePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D:
+ {
+ processTextHierarchyBulletPrimitive2D(
+ static_cast<const primitive2d::TextHierarchyBulletPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D:
+ {
+ processTextHierarchyParagraphPrimitive2D(
+ static_cast<const primitive2d::TextHierarchyParagraphPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D:
+ {
+ processTextHierarchyBlockPrimitive2D(
+ static_cast<const primitive2d::TextHierarchyBlockPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
+ {
+ // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate
+ processTextSimplePortionPrimitive2D(
+ static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ {
+ processPolygonHairlinePrimitive2D(
+ static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ {
+ processPolygonStrokePrimitive2D(
+ static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D:
+ {
+ processPolygonStrokeArrowPrimitive2D(
+ static_cast<const primitive2d::PolygonStrokeArrowPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ {
+ // direct draw of transformed BitmapEx primitive; use default processing, but without
+ // former testing if graphic content is inside discrete local viewport; this is not
+ // setup for metafile targets (metafile renderer tries to render in logic coordinates,
+ // the mapping is kept to the OutputDevice for better Metafile recording)
+ RenderBitmapPrimitive2D(static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
+ {
+ processPolyPolygonGraphicPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D:
+ {
+ processPolyPolygonHatchPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonHatchPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D:
+ {
+ processPolyPolygonGradientPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ {
+ processPolyPolygonColorPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ {
+ processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+ {
+ // modified color group. Force output to unified color. Use default processing.
+ RenderModifiedColorPrimitive2D(
+ static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+ {
+ processUnifiedTransparencePrimitive2D(
+ static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
+ {
+ processTransparencePrimitive2D(
+ static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ {
+ // use default transform group processing
+ RenderTransformPrimitive2D(
+ static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D:
+ {
+ // new XDrawPage for ViewInformation2D
+ RenderPagePreviewPrimitive2D(
+ static_cast<const primitive2d::PagePreviewPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+ {
+ // use default marker array processing
+ RenderMarkerArrayPrimitive2D(
+ static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ {
+ // use default point array processing
+ RenderPointArrayPrimitive2D(
+ static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D:
+ {
+ processStructureTagPrimitive2D(
+ static_cast<const primitive2d::StructureTagPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_EPSPRIMITIVE2D:
+ {
+ RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
+ {
+ processObjectInfoPrimitive2D(
+ static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
+ case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
+ case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
+ {
+ processPrimitive2DOnPixelProcessor(rCandidate);
+ break;
+ }
+ default:
+ {
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+}
+
+void VclMetafileProcessor2D::processObjectInfoPrimitive2D(
+ primitive2d::ObjectInfoPrimitive2D const& rObjectInfoPrimitive2D)
+{
+ // currently StructureTagPrimitive2D is only used for SdrObjects - have to
+ // avoid adding Alt text if the SdrObject is not actually tagged, as it
+ // would then end up on an unrelated structure element.
+ if (mpCurrentStructureTag && mpCurrentStructureTag->isTaggedSdrObject())
+ {
+ // Create image alternative description from ObjectInfoPrimitive2D info
+ // for PDF export, for the currently active SdrObject's structure element
+ if (mpPDFExtOutDevData->GetIsExportTaggedPDF())
+ {
+ OUString aAlternateDescription;
+
+ if (!rObjectInfoPrimitive2D.getTitle().isEmpty())
+ {
+ aAlternateDescription += rObjectInfoPrimitive2D.getTitle();
+ }
+
+ if (!rObjectInfoPrimitive2D.getDesc().isEmpty())
+ {
+ if (!aAlternateDescription.isEmpty())
+ {
+ aAlternateDescription += " - ";
+ }
+
+ aAlternateDescription += rObjectInfoPrimitive2D.getDesc();
+ }
+
+ // Use SetAlternateText to set it. This will work as long as some
+ // structure is used (see PDFWriterImpl::setAlternateText and
+ // m_nCurrentStructElement - tagged PDF export works with this in
+ // Draw/Impress/Writer, but not in Calc due to too less structure...)
+ //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
+ if (!aAlternateDescription.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
+ }
+ }
+ }
+
+ // process content
+ process(rObjectInfoPrimitive2D.getChildren());
+}
+
+void VclMetafileProcessor2D::processGraphicPrimitive2D(
+ const primitive2d::GraphicPrimitive2D& rGraphicPrimitive)
+{
+ bool bUsingPDFExtOutDevData(false);
+ basegfx::B2DVector aTranslate, aScale;
+ static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore
+
+ if (mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport)
+ {
+ // emulate data handling from UnoControlPDFExportContact, original see
+ // svtools/source/graphic/grfmgr.cxx
+ const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic();
+
+ if (rGraphic.IsGfxLink())
+ {
+ const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
+
+ if (!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted())
+ {
+ const basegfx::B2DHomMatrix& rTransform = rGraphicPrimitive.getTransform();
+ double fRotate, fShearX;
+ rTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if (basegfx::fTools::equalZero(fRotate) && (aScale.getX() > 0.0)
+ && (aScale.getY() > 0.0))
+ {
+ bUsingPDFExtOutDevData = true;
+ mpPDFExtOutDevData->BeginGroup();
+ }
+ }
+ }
+ }
+
+ // process recursively and add MetaFile comment
+ process(rGraphicPrimitive);
+
+ if (!bUsingPDFExtOutDevData)
+ return;
+
+ // emulate data handling from UnoControlPDFExportContact, original see
+ // svtools/source/graphic/grfmgr.cxx
+ const basegfx::B2DRange aCurrentRange(aTranslate.getX(), aTranslate.getY(),
+ aTranslate.getX() + aScale.getX(),
+ aTranslate.getY() + aScale.getY());
+ const tools::Rectangle aCurrentRect(
+ sal_Int32(floor(aCurrentRange.getMinX())), sal_Int32(floor(aCurrentRange.getMinY())),
+ sal_Int32(ceil(aCurrentRange.getMaxX())), sal_Int32(ceil(aCurrentRange.getMaxY())));
+ const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
+ // fdo#72530 don't pass empty Rectangle to EndGroup
+ tools::Rectangle aCropRect(aCurrentRect);
+
+ if (rAttr.IsCropped())
+ {
+ // calculate scalings between real image size and logic object size. This
+ // is necessary since the crop values are relative to original bitmap size
+ double fFactorX(1.0);
+ double fFactorY(1.0);
+
+ {
+ const MapMode aMapMode100thmm(MapUnit::Map100thMM);
+ const Size aBitmapSize(OutputDevice::LogicToLogic(
+ rGraphicPrimitive.getGraphicObject().GetPrefSize(),
+ rGraphicPrimitive.getGraphicObject().GetPrefMapMode(), aMapMode100thmm));
+ const double fDivX(aBitmapSize.Width() - rAttr.GetLeftCrop() - rAttr.GetRightCrop());
+ const double fDivY(aBitmapSize.Height() - rAttr.GetTopCrop() - rAttr.GetBottomCrop());
+
+ if (!basegfx::fTools::equalZero(fDivX))
+ {
+ fFactorX = aScale.getX() / fDivX;
+ }
+
+ if (!basegfx::fTools::equalZero(fDivY))
+ {
+ fFactorY = aScale.getY() / fDivY;
+ }
+ }
+
+ // calculate crop range and rect
+ basegfx::B2DRange aCropRange;
+ aCropRange.expand(
+ aCurrentRange.getMinimum()
+ - basegfx::B2DPoint(rAttr.GetLeftCrop() * fFactorX, rAttr.GetTopCrop() * fFactorY));
+ aCropRange.expand(
+ aCurrentRange.getMaximum()
+ + basegfx::B2DPoint(rAttr.GetRightCrop() * fFactorX, rAttr.GetBottomCrop() * fFactorY));
+
+ aCropRect = tools::Rectangle(
+ sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())),
+ sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
+ }
+
+ // #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped
+ // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded,
+ // uncropped region. Thus, correct order is aCropRect, aCurrentRect
+ mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(),
+ 255 - rAttr.GetAlpha(), aCropRect, aCurrentRect);
+}
+
+void VclMetafileProcessor2D::processControlPrimitive2D(
+ const primitive2d::ControlPrimitive2D& rControlPrimitive)
+{
+ const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl());
+ bool bIsPrintableControl(false);
+
+ // find out if control is printable
+ if (rXControl.is())
+ {
+ try
+ {
+ uno::Reference<beans::XPropertySet> xModelProperties(rXControl->getModel(),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropertyInfo(
+ xModelProperties.is() ? xModelProperties->getPropertySetInfo()
+ : uno::Reference<beans::XPropertySetInfo>());
+ static const OUStringLiteral sPrintablePropertyName(u"Printable");
+
+ if (xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName))
+ {
+ OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName)
+ >>= bIsPrintableControl);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("drawinglayer",
+ "VclMetafileProcessor2D: No access to printable flag of Control");
+ }
+ }
+
+ // PDF export and printing only for printable controls
+ if (!bIsPrintableControl)
+ return;
+
+ const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields());
+ bool bDoProcessRecursively(true);
+
+ if (bPDFExport)
+ {
+ // PDF export. Emulate data handling from UnoControlPDFExportContact
+ // I have now moved describePDFControl to toolkit, thus i can implement the PDF
+ // form control support now as follows
+ std::unique_ptr<vcl::PDFWriter::AnyWidget> pPDFControl(
+ ::toolkitform::describePDFControl(rXControl, *mpPDFExtOutDevData));
+
+ if (pPDFControl)
+ {
+ // still need to fill in the location (is a class Rectangle)
+ const basegfx::B2DRange aRangeLogic(
+ rControlPrimitive.getB2DRange(getViewInformation2D()));
+ const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aRangeLogic.getMinX())),
+ static_cast<sal_Int32>(floor(aRangeLogic.getMinY())),
+ static_cast<sal_Int32>(ceil(aRangeLogic.getMaxX())),
+ static_cast<sal_Int32>(ceil(aRangeLogic.getMaxY())));
+ pPDFControl->Location = aRectLogic;
+
+ Size aFontSize(pPDFControl->TextFont.GetFontSize());
+ aFontSize = OutputDevice::LogicToLogic(aFontSize, MapMode(MapUnit::MapPoint),
+ mpOutputDevice->GetMapMode());
+ pPDFControl->TextFont.SetFontSize(aFontSize);
+
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
+ mpPDFExtOutDevData->CreateControl(*pPDFControl);
+ mpPDFExtOutDevData->EndStructureElement();
+
+ // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
+ // do not process recursively
+ bDoProcessRecursively = false;
+ }
+ else
+ {
+ // PDF export did not work, try simple output.
+ // Fallback to printer output by not setting bDoProcessRecursively
+ // to false.
+ }
+ }
+
+ // #i93169# used flag the wrong way; true means that nothing was done yet
+ if (bDoProcessRecursively)
+ {
+ // printer output
+ try
+ {
+ // remember old graphics and create new
+ uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW);
+ const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics());
+ const uno::Reference<awt::XGraphics> xNewGraphics(mpOutputDevice->CreateUnoGraphics());
+
+ if (xNewGraphics.is())
+ {
+ // link graphics and view
+ xControlView->setGraphics(xNewGraphics);
+
+ // get position
+ const basegfx::B2DHomMatrix aObjectToDiscrete(
+ getViewInformation2D().getObjectToViewTransformation()
+ * rControlPrimitive.getTransform());
+ const basegfx::B2DPoint aTopLeftDiscrete(aObjectToDiscrete
+ * basegfx::B2DPoint(0.0, 0.0));
+
+ // draw it
+ xControlView->draw(basegfx::fround(aTopLeftDiscrete.getX()),
+ basegfx::fround(aTopLeftDiscrete.getY()));
+ bDoProcessRecursively = false;
+
+ // restore original graphics
+ xControlView->setGraphics(xOriginalGraphics);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("drawinglayer",
+ "VclMetafileProcessor2D: Printing of Control failed");
+ }
+ }
+
+ // process recursively if not done yet to export as decomposition (bitmap)
+ if (bDoProcessRecursively)
+ {
+ process(rControlPrimitive);
+ }
+}
+
+void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
+ const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive)
+{
+ // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
+ // thus do the MetafileAction embedding stuff but just handle recursively.
+ const OString aCommentStringCommon("FIELD_SEQ_BEGIN");
+ OUString aURL;
+
+ switch (rFieldPrimitive.getType())
+ {
+ default: // case drawinglayer::primitive2d::FIELD_TYPE_COMMON :
+ {
+ mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon));
+ break;
+ }
+ case drawinglayer::primitive2d::FIELD_TYPE_PAGE:
+ {
+ mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_BEGIN;PageField"));
+ break;
+ }
+ case drawinglayer::primitive2d::FIELD_TYPE_URL:
+ {
+ aURL = rFieldPrimitive.getValue("URL");
+
+ if (!aURL.isEmpty())
+ {
+ mpMetaFile->AddAction(new MetaCommentAction(
+ aCommentStringCommon, 0, reinterpret_cast<const sal_uInt8*>(aURL.getStr()),
+ 2 * aURL.getLength()));
+ }
+
+ break;
+ }
+ }
+
+ // process recursively
+ primitive2d::Primitive2DContainer rContent;
+ rFieldPrimitive.get2DDecomposition(rContent, getViewInformation2D());
+ process(rContent);
+
+ // for the end comment the type is not relevant yet, they are all the same. Just add.
+ mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_END"));
+
+ if (!(mpPDFExtOutDevData
+ && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType()))
+ return;
+
+ // emulate data handling from ImpEditEngine::Paint
+ const basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D()));
+ const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())),
+ static_cast<sal_Int32>(floor(aViewRange.getMinY())),
+ static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
+ static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
+ vcl::PDFExtOutDevBookmarkEntry aBookmark;
+ OUString const content(rFieldPrimitive.getValue("Representation"));
+ aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic, content);
+ aBookmark.aBookmark = aURL;
+ std::vector<vcl::PDFExtOutDevBookmarkEntry>& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
+ rBookmarks.push_back(aBookmark);
+}
+
+void VclMetafileProcessor2D::processTextHierarchyLinePrimitive2D(
+ const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive)
+{
+ // process recursively and add MetaFile comment
+ process(rLinePrimitive);
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOL"));
+}
+
+void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
+ const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive)
+{
+ // this is a part of list item, start LILabel ( = bullet)
+ if (mbInListItem)
+ {
+ maListElements.push(vcl::PDFWriter::LILabel);
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LILabel);
+ }
+
+ // process recursively and add MetaFile comment
+ process(rBulletPrimitive);
+ // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
+ // "XTEXT_EOC" is used, use here, too.
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"));
+
+ if (mbInListItem)
+ {
+ if (maListElements.top() == vcl::PDFWriter::LILabel)
+ {
+ maListElements.pop();
+ mpPDFExtOutDevData->EndStructureElement(); // end LILabel
+ mbBulletPresent = true;
+ }
+ }
+}
+
+void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
+ const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive)
+{
+ const OString aCommentString("XTEXT_EOP");
+ static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore
+
+ if (nullptr == mpPDFExtOutDevData || bSuppressPDFExtOutDevDataSupport)
+ {
+ // Non-PDF export behaviour (metafile only).
+ // Process recursively and add MetaFile comment.
+ process(rParagraphPrimitive);
+ mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
+ return;
+ }
+
+ if (!mpPDFExtOutDevData->GetIsExportTaggedPDF())
+ {
+ // No Tagged PDF -> Dump as Paragraph
+ // Emulate data handling from old ImpEditEngine::Paint
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+
+ // Process recursively and add MetaFile comment
+ process(rParagraphPrimitive);
+ mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
+
+ // Emulate data handling from ImpEditEngine::Paint
+ mpPDFExtOutDevData->EndStructureElement();
+ return;
+ }
+
+ // Create Tagged PDF -> deeper tagged data using StructureElements.
+ // Use OutlineLevel from ParagraphPrimitive, ensure not below -1 what
+ // means 'not active'
+ const sal_Int16 nNewOutlineLevel(
+ std::max(static_cast<sal_Int16>(-1), rParagraphPrimitive.getOutlineLevel()));
+
+ // Do we have a change in OutlineLevel compared to the current one?
+ if (nNewOutlineLevel != mnCurrentOutlineLevel)
+ {
+ if (nNewOutlineLevel > mnCurrentOutlineLevel)
+ {
+ // increase List level
+ for (sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; ++a)
+ {
+ maListElements.push(vcl::PDFWriter::List);
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::List);
+ }
+ }
+ else // if(nNewOutlineLevel < mnCurrentOutlineLevel)
+ {
+ // close list levels below nNewOutlineLevel completely by removing
+ // list items as well as list tag itself
+ for (sal_Int16 a(nNewOutlineLevel); a < mnCurrentOutlineLevel; ++a)
+ {
+ popList(); // end LBody LI and L
+ }
+
+ // on nNewOutlineLevel close the previous list item (LBody and LI)
+ popListItem();
+ }
+
+ // Remember new current OutlineLevel
+ mnCurrentOutlineLevel = nNewOutlineLevel;
+ }
+ else // the same list level
+ {
+ // close the previous list item (LBody and LI)
+ popListItem();
+ }
+
+ const bool bDumpAsListItem(-1 != mnCurrentOutlineLevel);
+
+ if (bDumpAsListItem)
+ {
+ // Dump as ListItem
+ maListElements.push(vcl::PDFWriter::ListItem);
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::ListItem);
+ mbInListItem = true;
+ }
+ else
+ {
+ // Dump as Paragraph
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+ }
+
+ // Process recursively and add MetaFile comment
+ process(rParagraphPrimitive);
+ mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
+
+ if (bDumpAsListItem)
+ mbInListItem = false;
+ else
+ mpPDFExtOutDevData->EndStructureElement(); // end Paragraph
+}
+
+void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D(
+ const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive)
+{
+ // add MetaFile comment, process recursively and add MetaFile comment
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_BEGIN"));
+ process(rBlockPrimitive);
+
+ if (mnCurrentOutlineLevel >= 0)
+ {
+ // end any opened List structure elements (LBody, LI, L)
+ for (sal_Int16 a(0); a <= mnCurrentOutlineLevel; ++a)
+ {
+ popList();
+ }
+ }
+
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_END"));
+}
+
+void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
+{
+ // Adapt evtl. used special DrawMode
+ const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
+ adaptTextToFillDrawMode();
+
+ // this is a 2nd portion of list item
+ // bullet has been already processed, start LIBody
+ if (mbInListItem && mbBulletPresent)
+ {
+ maListElements.push(vcl::PDFWriter::LIBody);
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LIBody);
+ }
+
+ // directdraw of text simple portion; use default processing
+ RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate);
+
+ if (mbInListItem && mbBulletPresent)
+ mbBulletPresent = false;
+
+ // restore DrawMode
+ mpOutputDevice->SetDrawMode(nOriginalDrawMode);
+
+ // #i101169# if(pTextDecoratedCandidate)
+ {
+ // support for TEXT_ MetaFile actions only for decorated texts
+ if (!mxBreakIterator.is())
+ {
+ uno::Reference<uno::XComponentContext> xContext(
+ ::comphelper::getProcessComponentContext());
+ mxBreakIterator = i18n::BreakIterator::create(xContext);
+ }
+
+ const OUString& rTxt = rTextCandidate.getText();
+ const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength());
+
+ if (nTextLength)
+ {
+ const css::lang::Locale& rLocale = rTextCandidate.getLocale();
+ const sal_Int32 nTextPosition(rTextCandidate.getTextPosition());
+
+ sal_Int32 nDone;
+ sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(
+ rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0,
+ nDone));
+ css::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(
+ rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true));
+ sal_Int32 nNextSentenceBreak(
+ mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale));
+ const OString aCommentStringA("XTEXT_EOC");
+ const OString aCommentStringB("XTEXT_EOW");
+ const OString aCommentStringC("XTEXT_EOS");
+
+ for (sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++)
+ {
+ // create the entries for the respective break positions
+ if (i == nNextCellBreak)
+ {
+ mpMetaFile->AddAction(
+ new MetaCommentAction(aCommentStringA, i - nTextPosition));
+ nNextCellBreak = mxBreakIterator->nextCharacters(
+ rTxt, i, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
+ }
+ if (i == nNextWordBoundary.endPos)
+ {
+ mpMetaFile->AddAction(
+ new MetaCommentAction(aCommentStringB, i - nTextPosition));
+ nNextWordBoundary = mxBreakIterator->getWordBoundary(
+ rTxt, i + 1, rLocale, css::i18n::WordType::ANY_WORD, true);
+ }
+ if (i == nNextSentenceBreak)
+ {
+ mpMetaFile->AddAction(
+ new MetaCommentAction(aCommentStringC, i - nTextPosition));
+ nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale);
+ }
+ }
+ }
+ }
+}
+
+void VclMetafileProcessor2D::processPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive)
+{
+ const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon();
+
+ if (rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
+ {
+ // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
+ // per polygon. If there are more, split the polygon in half and call recursively
+ basegfx::B2DPolygon aLeft, aRight;
+ splitLinePolygon(rBasePolygon, aLeft, aRight);
+ rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPLeft(
+ new primitive2d::PolygonHairlinePrimitive2D(aLeft, rHairlinePrimitive.getBColor()));
+ rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPRight(
+ new primitive2d::PolygonHairlinePrimitive2D(aRight, rHairlinePrimitive.getBColor()));
+
+ processBasePrimitive2D(*xPLeft);
+ processBasePrimitive2D(*xPRight);
+ }
+ else
+ {
+ // direct draw of hairline; use default processing
+ // support SvtGraphicStroke MetaCommentAction
+ const basegfx::BColor aLineColor(
+ maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor()));
+ std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke;
+
+ // #i121267# Not needed, does not give better quality compared with
+ // the MetaActionType::POLYPOLYGON written by RenderPolygonHairlinePrimitive2D
+ // below
+ const bool bSupportSvtGraphicStroke(false);
+
+ if (bSupportSvtGraphicStroke)
+ {
+ pSvtGraphicStroke
+ = impTryToCreateSvtGraphicStroke(rHairlinePrimitive.getB2DPolygon(), &aLineColor,
+ nullptr, nullptr, nullptr, nullptr);
+
+ impStartSvtGraphicStroke(pSvtGraphicStroke.get());
+ }
+
+ RenderPolygonHairlinePrimitive2D(rHairlinePrimitive, false);
+
+ if (bSupportSvtGraphicStroke)
+ {
+ impEndSvtGraphicStroke(pSvtGraphicStroke.get());
+ }
+ }
+}
+
+void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(
+ const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive)
+{
+ const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon();
+
+ if (rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
+ {
+ // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
+ // per polygon. If there are more, split the polygon in half and call recursively
+ basegfx::B2DPolygon aLeft, aRight;
+ splitLinePolygon(rBasePolygon, aLeft, aRight);
+ rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPLeft(
+ new primitive2d::PolygonStrokePrimitive2D(aLeft, rStrokePrimitive.getLineAttribute(),
+ rStrokePrimitive.getStrokeAttribute()));
+ rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPRight(
+ new primitive2d::PolygonStrokePrimitive2D(aRight, rStrokePrimitive.getLineAttribute(),
+ rStrokePrimitive.getStrokeAttribute()));
+
+ processBasePrimitive2D(*xPLeft);
+ processBasePrimitive2D(*xPRight);
+ }
+ else
+ {
+ mpOutputDevice->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
+
+ // support SvtGraphicStroke MetaCommentAction
+ std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
+ rBasePolygon, nullptr, &rStrokePrimitive.getLineAttribute(),
+ &rStrokePrimitive.getStrokeAttribute(), nullptr, nullptr);
+
+ impStartSvtGraphicStroke(pSvtGraphicStroke.get());
+ const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute();
+
+ // create MetaPolyLineActions, but without LineStyle::Dash
+ if (basegfx::fTools::more(rLine.getWidth(), 0.0))
+ {
+ const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute();
+
+ const basegfx::BColor aHairlineColor(
+ maBColorModifierStack.getModifiedColor(rLine.getColor()));
+ mpOutputDevice->SetLineColor(Color(aHairlineColor));
+ mpOutputDevice->SetFillColor();
+
+ // use the transformed line width
+ LineInfo aLineInfo(LineStyle::Solid,
+ basegfx::fround(getTransformedLineWidth(rLine.getWidth())));
+ aLineInfo.SetLineJoin(rLine.getLineJoin());
+ aLineInfo.SetLineCap(rLine.getLineCap());
+
+ basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+ if (0.0 == rStroke.getFullDotDashLen())
+ {
+ aHairLinePolyPolygon.append(rBasePolygon);
+ }
+ else
+ {
+ bool done = false;
+ const std::vector<double>& array = rStroke.getDotDashArray();
+ // The dotdash array should generally have the form
+ // (<dashLen> <distance>)+ (<dotLen> <distance>)*
+ // (where (,),+ and * have their regex meaning).
+ // Find out what the lengths and their counts are.
+ if (!array.empty() && array.size() % 2 == 0)
+ {
+ double dashLen = array[0];
+ double distance = array[1];
+ int dashCount = 1;
+ double dotLen = 0;
+ int dotCount = 0;
+ size_t pos = 2;
+ while (pos + 2 <= array.size())
+ {
+ if (array[pos] != dashLen || array[pos + 1] != distance)
+ break;
+ ++dashCount;
+ pos += 2;
+ }
+ if (pos + 2 <= array.size() && array[pos + 1] == distance)
+ {
+ dotLen = array[pos];
+ ++dotCount;
+ pos += 2;
+ while (pos + 2 <= array.size())
+ {
+ if (array[pos] != dotLen || array[pos + 1] != distance)
+ break;
+ ++dotCount;
+ pos += 2;
+ }
+ }
+ if (array.size() == pos)
+ {
+ aHairLinePolyPolygon.append(rBasePolygon);
+ // This will be used by setupStrokeAttributes() in cppcanvas.
+ aLineInfo.SetStyle(LineStyle::Dash);
+ aLineInfo.SetDashCount(dashCount);
+ aLineInfo.SetDashLen(getTransformedLineWidth(dashLen));
+ aLineInfo.SetDistance(getTransformedLineWidth(distance));
+ if (dotCount != 0)
+ {
+ aLineInfo.SetDotCount(dotCount);
+ aLineInfo.SetDotLen(getTransformedLineWidth(dotLen));
+ }
+ done = true;
+ }
+ }
+ if (!done)
+ {
+ // LineInfo can hold only limited info about dashing, apply dashing manually
+ // if LineInfo cannot describe it. That should not happen though.
+ SAL_WARN("drawinglayer", "dotdash array cannot be converted to LineInfo");
+ basegfx::utils::applyLineDashing(rBasePolygon, rStroke.getDotDashArray(),
+ &aHairLinePolyPolygon, nullptr,
+ rStroke.getFullDotDashLen());
+ }
+ }
+ aHairLinePolyPolygon.transform(maCurrentTransformation);
+
+ for (sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
+ {
+ const basegfx::B2DPolygon& aCandidate(aHairLinePolyPolygon.getB2DPolygon(a));
+
+ if (aCandidate.count() > 1)
+ {
+ const tools::Polygon aToolsPolygon(aCandidate);
+
+ mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo));
+ }
+ }
+ }
+ else
+ {
+ process(rStrokePrimitive);
+ }
+
+ impEndSvtGraphicStroke(pSvtGraphicStroke.get());
+
+ mpOutputDevice->Pop();
+ }
+}
+
+void VclMetafileProcessor2D::processPolygonStrokeArrowPrimitive2D(
+ const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive)
+{
+ const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon();
+
+ if (rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
+ {
+ // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
+ // per polygon. If there are more, split the polygon in half and call recursively
+ basegfx::B2DPolygon aLeft, aRight;
+ splitLinePolygon(rBasePolygon, aLeft, aRight);
+ const attribute::LineStartEndAttribute aEmpty;
+ rtl::Reference<primitive2d::PolygonStrokeArrowPrimitive2D> xPLeft(
+ new primitive2d::PolygonStrokeArrowPrimitive2D(
+ aLeft, rStrokeArrowPrimitive.getLineAttribute(),
+ rStrokeArrowPrimitive.getStrokeAttribute(), rStrokeArrowPrimitive.getStart(),
+ aEmpty));
+ rtl::Reference<primitive2d::PolygonStrokeArrowPrimitive2D> xPRight(
+ new primitive2d::PolygonStrokeArrowPrimitive2D(
+ aRight, rStrokeArrowPrimitive.getLineAttribute(),
+ rStrokeArrowPrimitive.getStrokeAttribute(), aEmpty,
+ rStrokeArrowPrimitive.getEnd()));
+
+ processBasePrimitive2D(*xPLeft);
+ processBasePrimitive2D(*xPRight);
+ }
+ else
+ {
+ // support SvtGraphicStroke MetaCommentAction
+ std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
+ rBasePolygon, nullptr, &rStrokeArrowPrimitive.getLineAttribute(),
+ &rStrokeArrowPrimitive.getStrokeAttribute(), &rStrokeArrowPrimitive.getStart(),
+ &rStrokeArrowPrimitive.getEnd());
+
+ // write LineGeometry start marker
+ impStartSvtGraphicStroke(pSvtGraphicStroke.get());
+
+ // #i116162# When B&W is set as DrawMode, DrawModeFlags::WhiteFill is used
+ // to let all fills be just white; for lines DrawModeFlags::BlackLine is used
+ // so all line geometry is supposed to get black. Since in the in-between
+ // stages of line geometry drawing filled polygons are used (e.g. line
+ // start/ends) it is necessary to change these drawmodes to preserve
+ // that lines shall be black; thus change DrawModeFlags::WhiteFill to
+ // DrawModeFlags::BlackFill during line geometry processing to have line geometry
+ // parts filled black.
+ const DrawModeFlags nOldDrawMode(mpOutputDevice->GetDrawMode());
+ const bool bDrawmodeChange(nOldDrawMode & DrawModeFlags::WhiteFill
+ && mnSvtGraphicStrokeCount);
+
+ if (bDrawmodeChange)
+ {
+ mpOutputDevice->SetDrawMode((nOldDrawMode & ~DrawModeFlags::WhiteFill)
+ | DrawModeFlags::BlackFill);
+ }
+
+ // process sub-line geometry (evtl. filled PolyPolygons)
+ process(rStrokeArrowPrimitive);
+
+ if (bDrawmodeChange)
+ {
+ mpOutputDevice->SetDrawMode(nOldDrawMode);
+ }
+
+ // write LineGeometry end marker
+ impEndSvtGraphicStroke(pSvtGraphicStroke.get());
+ }
+}
+
+void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D(
+ const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate)
+{
+ // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon());
+
+ if (!rBitmapCandidate.getDefinitionRange().isEmpty()
+ && aLocalPolyPolygon.getB2DRange() != rBitmapCandidate.getDefinitionRange())
+ {
+ // The range which defines the bitmap fill is defined and different from the
+ // range of the defining geometry (e.g. used for FillStyle UseSlideBackground).
+ // This cannot be done calling vcl, thus use decomposition here directly
+ process(rBitmapCandidate);
+ return;
+ }
+
+ fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
+
+ std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
+
+ if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
+ {
+ // #121194# Changed implementation and checked usages of convert to metafile,
+ // presentation start (uses SvtGraphicFill) and printing.
+
+ // calculate transformation. Get real object size, all values in FillGraphicAttribute
+ // are relative to the unified object
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+ const basegfx::B2DVector aOutlineSize(aLocalPolyPolygon.getB2DRange().getRange());
+
+ // the scaling needs scale from pixel to logic coordinate system
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute
+ = rBitmapCandidate.getFillGraphic();
+ const Size aBmpSizePixel(rFillGraphicAttribute.getGraphic().GetSizePixel());
+
+ // setup transformation like in impgrfll. Multiply with aOutlineSize
+ // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange()
+ // to object coordinates with object's top left being at (0,0). Divide
+ // by pixel size so that scale from pixel to logic will work in SvtGraphicFill.
+ const basegfx::B2DVector aTransformScale(
+ rFillGraphicAttribute.getGraphicRange().getRange()
+ / basegfx::B2DVector(std::max(1.0, double(aBmpSizePixel.Width())),
+ std::max(1.0, double(aBmpSizePixel.Height())))
+ * aOutlineSize);
+ const basegfx::B2DPoint aTransformPosition(
+ rFillGraphicAttribute.getGraphicRange().getMinimum() * aOutlineSize);
+
+ // setup transformation like in impgrfll
+ SvtGraphicFill::Transform aTransform;
+
+ // scale values are divided by bitmap pixel sizes
+ aTransform.matrix[0] = aTransformScale.getX();
+ aTransform.matrix[4] = aTransformScale.getY();
+
+ // translates are absolute
+ aTransform.matrix[2] = aTransformPosition.getX();
+ aTransform.matrix[5] = aTransformPosition.getY();
+
+ pSvtGraphicFill.reset(new SvtGraphicFill(
+ getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd,
+ SvtGraphicFill::fillTexture, aTransform, rFillGraphicAttribute.getTiling(),
+ SvtGraphicFill::hatchSingle, Color(), SvtGraphicFill::GradientType::Linear, Color(),
+ Color(), 0, rFillGraphicAttribute.getGraphic()));
+ }
+
+ // Do use decomposition; encapsulate with SvtGraphicFill
+ impStartSvtGraphicFill(pSvtGraphicFill.get());
+ process(rBitmapCandidate);
+ impEndSvtGraphicFill(pSvtGraphicFill.get());
+}
+
+void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(
+ const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate)
+{
+ // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
+ const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch();
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon());
+
+ if (aLocalPolyPolygon.getB2DRange() != rHatchCandidate.getDefinitionRange())
+ {
+ // the range which defines the hatch is different from the range of the
+ // geometry (used for writer frames). This cannot be done calling vcl, thus use
+ // decomposition here
+ process(rHatchCandidate);
+ return;
+ }
+
+ // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
+ // per polygon. Split polygon until there are less than that
+ fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
+
+ if (rFillHatchAttribute.isFillBackground())
+ {
+ // with fixing #i111954# (see below) the possible background
+ // fill of a hatched object was lost.Generate a background fill
+ // primitive and render it
+ const primitive2d::Primitive2DReference xBackground(
+ new primitive2d::PolyPolygonColorPrimitive2D(aLocalPolyPolygon,
+ rHatchCandidate.getBackgroundColor()));
+
+ process(primitive2d::Primitive2DContainer{ xBackground });
+ }
+
+ std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+
+ if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
+ {
+ // re-create a VCL hatch as base data
+ SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle);
+
+ switch (rFillHatchAttribute.getStyle())
+ {
+ default: // attribute::HatchStyle::Single :
+ {
+ eHatch = SvtGraphicFill::hatchSingle;
+ break;
+ }
+ case attribute::HatchStyle::Double:
+ {
+ eHatch = SvtGraphicFill::hatchDouble;
+ break;
+ }
+ case attribute::HatchStyle::Triple:
+ {
+ eHatch = SvtGraphicFill::hatchTriple;
+ break;
+ }
+ }
+
+ SvtGraphicFill::Transform aTransform;
+
+ // scale
+ aTransform.matrix[0] *= rFillHatchAttribute.getDistance();
+ aTransform.matrix[4] *= rFillHatchAttribute.getDistance();
+
+ // rotate (was never correct in impgrfll anyways, use correct angle now)
+ aTransform.matrix[0] *= cos(rFillHatchAttribute.getAngle());
+ aTransform.matrix[1] *= -sin(rFillHatchAttribute.getAngle());
+ aTransform.matrix[3] *= sin(rFillHatchAttribute.getAngle());
+ aTransform.matrix[4] *= cos(rFillHatchAttribute.getAngle());
+
+ pSvtGraphicFill.reset(new SvtGraphicFill(
+ getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd,
+ SvtGraphicFill::fillHatch, aTransform, false, eHatch,
+ Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())),
+ SvtGraphicFill::GradientType::Linear, Color(), Color(), 0, Graphic()));
+ }
+
+ // Do use decomposition; encapsulate with SvtGraphicFill
+ impStartSvtGraphicFill(pSvtGraphicFill.get());
+
+ // #i111954# do NOT use decomposition, but use direct VCL-command
+ // process(rCandidate.get2DDecomposition(getViewInformation2D()));
+ const tools::PolyPolygon aToolsPolyPolygon(
+ basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon));
+ const HatchStyle aHatchStyle(
+ attribute::HatchStyle::Single == rFillHatchAttribute.getStyle()
+ ? HatchStyle::Single
+ : attribute::HatchStyle::Double == rFillHatchAttribute.getStyle() ? HatchStyle::Double
+ : HatchStyle::Triple);
+
+ mpOutputDevice->DrawHatch(
+ aToolsPolyPolygon,
+ Hatch(aHatchStyle,
+ Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())),
+ basegfx::fround(rFillHatchAttribute.getDistance()),
+ Degree10(basegfx::fround(basegfx::rad2deg<10>(rFillHatchAttribute.getAngle())))));
+
+ impEndSvtGraphicFill(pSvtGraphicFill.get());
+}
+
+void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
+ const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate)
+{
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX))
+ {
+ // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
+ // This is because VCL Gradient mechanism does *not* support to rotate the gradient
+ // with objects and this case is not expressible in a Metafile (and cannot be added
+ // since the FileFormats used, e.g. *.wmf, do not support it either).
+ // Such cases happen when a graphic object uses a Metafile as graphic information or
+ // a fill style definition uses a Metafile. In this cases the graphic content is
+ // rotated with the graphic or filled object; this is not supported by the target
+ // format of this conversion renderer - Metafiles.
+ // To solve this, not a Gradient is written, but the decomposition of this object
+ // is written to the Metafile. This is the PolyPolygons building the gradient fill.
+ // These will need more space and time, but the result will be as if the Gradient
+ // was rotated with the object.
+ // This mechanism is used by all exporters still not using Primitives (e.g. Print,
+ // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
+ // transfers. One more reason to *change* these to primitives.
+ // BTW: One more example how useful the principles of primitives are; the decomposition
+ // is by definition a simpler, maybe more expensive representation of the same content.
+ process(rGradientCandidate);
+ return;
+ }
+
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon());
+
+ if (aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange())
+ {
+ // the range which defines the gradient is different from the range of the
+ // geometry (used for writer frames). This cannot be done calling vcl, thus use
+ // decomposition here
+ process(rGradientCandidate);
+ return;
+ }
+
+ // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
+ // per polygon. Split polygon until there are less than that
+ fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
+
+ // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END
+ // it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
+ // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
+ Gradient aVCLGradient;
+ impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(),
+ false);
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+
+ // #i82145# ATM VCL printing of gradients using curved shapes does not work,
+ // i submitted the bug with the given ID to THB. When that task is fixed it is
+ // necessary to again remove this subdivision since it decreases possible
+ // printing quality (not even resolution-dependent for now). THB will tell
+ // me when that task is fixed in the master
+ const tools::PolyPolygon aToolsPolyPolygon(
+ getFillPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon)));
+
+ // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
+ std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
+
+ if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
+ {
+ // setup gradient stuff like in impgrfll
+ SvtGraphicFill::GradientType eGrad(SvtGraphicFill::GradientType::Linear);
+
+ switch (aVCLGradient.GetStyle())
+ {
+ default: // GradientStyle::Linear:
+ case GradientStyle::Axial:
+ eGrad = SvtGraphicFill::GradientType::Linear;
+ break;
+ case GradientStyle::Radial:
+ case GradientStyle::Elliptical:
+ eGrad = SvtGraphicFill::GradientType::Radial;
+ break;
+ case GradientStyle::Square:
+ case GradientStyle::Rect:
+ eGrad = SvtGraphicFill::GradientType::Rectangular;
+ break;
+ }
+
+ pSvtGraphicFill.reset(new SvtGraphicFill(
+ aToolsPolyPolygon, Color(), 0.0, SvtGraphicFill::fillEvenOdd,
+ SvtGraphicFill::fillGradient, SvtGraphicFill::Transform(), false,
+ SvtGraphicFill::hatchSingle, Color(), eGrad, aVCLGradient.GetStartColor(),
+ aVCLGradient.GetEndColor(), aVCLGradient.GetSteps(), Graphic()));
+ }
+
+ // call VCL directly; encapsulate with SvtGraphicFill
+ impStartSvtGraphicFill(pSvtGraphicFill.get());
+ mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
+ impEndSvtGraphicFill(pSvtGraphicFill.get());
+}
+
+void VclMetafileProcessor2D::processPolyPolygonColorPrimitive2D(
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate)
+{
+ mpOutputDevice->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
+
+ // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
+ // per polygon. Split polygon until there are less than that
+ fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
+
+ const basegfx::BColor aPolygonColor(
+ maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+
+ // set line and fill color
+ mpOutputDevice->SetFillColor(Color(aPolygonColor));
+ mpOutputDevice->SetLineColor();
+
+ mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
+
+ mpOutputDevice->Pop();
+}
+
+void VclMetafileProcessor2D::processMaskPrimitive2D(
+ const primitive2d::MaskPrimitive2D& rMaskCandidate)
+{
+ // mask group. Special handling for MetaFiles.
+ if (rMaskCandidate.getChildren().empty())
+ return;
+
+ basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
+
+ if (aMask.count())
+ {
+ // prepare new mask polygon and rescue current one
+ aMask.transform(maCurrentTransformation);
+ const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon);
+
+ if (maClipPolyPolygon.count())
+ {
+ // there is already a clip polygon set; build clipped union of
+ // current mask polygon and new one
+ maClipPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ aMask, maClipPolyPolygon,
+ true, // #i106516# we want the inside of aMask, not the outside
+ false);
+ }
+ else
+ {
+ // use mask directly
+ maClipPolyPolygon = aMask;
+ }
+
+ if (maClipPolyPolygon.count())
+ {
+ // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!)
+ // Removed subdivision and fixed in vcl::Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
+ // the ClipRegion is built from the Polygon. An AdaptiveSubdivide on the source polygon was missing there
+ mpOutputDevice->Push(vcl::PushFlags::CLIPREGION);
+ mpOutputDevice->SetClipRegion(vcl::Region(maClipPolyPolygon));
+
+ // recursively paint content
+ // #i121267# Only need to process sub-content when clip polygon is *not* empty.
+ // If it is empty, the clip is empty and there can be nothing inside.
+ process(rMaskCandidate.getChildren());
+
+ // restore VCL clip region
+ mpOutputDevice->Pop();
+ }
+
+ // restore to rescued clip polygon
+ maClipPolyPolygon = aLastClipPolyPolygon;
+ }
+ else
+ {
+ // no mask, no clipping. recursively paint content
+ process(rMaskCandidate.getChildren());
+ }
+}
+
+void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(
+ const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate)
+{
+ mpOutputDevice->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
+ // for metafile: Need to examine what the pure vcl version is doing here actually
+ // - uses DrawTransparent with metafile for content and a gradient
+ // - uses DrawTransparent for single PolyPolygons directly. Can be detected by
+ // checking the content for single PolyPolygonColorPrimitive2D
+ const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren();
+
+ if (!rContent.empty())
+ {
+ if (0.0 == rUniTransparenceCandidate.getTransparence())
+ {
+ // not transparent at all, use content
+ process(rUniTransparenceCandidate.getChildren());
+ }
+ else if (rUniTransparenceCandidate.getTransparence() > 0.0
+ && rUniTransparenceCandidate.getTransparence() < 1.0)
+ {
+ // try to identify a single PolyPolygonColorPrimitive2D in the
+ // content part of the transparence primitive
+ const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = nullptr;
+ static bool bForceToMetafile(false); // loplugin:constvars:ignore
+
+ if (!bForceToMetafile && 1 == rContent.size())
+ {
+ const primitive2d::Primitive2DReference xReference(rContent[0]);
+ pPoPoColor = dynamic_cast<const primitive2d::PolyPolygonColorPrimitive2D*>(
+ xReference.get());
+ }
+
+ // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and
+ // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D.
+ // Check also for correct ID to exclude derived implementations
+ if (pPoPoColor
+ && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID())
+ {
+ // single transparent tools::PolyPolygon identified, use directly
+ const basegfx::BColor aPolygonColor(
+ maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor()));
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon());
+
+ // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
+ // per polygon. Split polygon until there are less than that
+ fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
+
+ // now transform
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+
+ // set line and fill color
+ const sal_uInt16 nTransPercentVcl(static_cast<sal_uInt16>(
+ basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0)));
+ mpOutputDevice->SetFillColor(Color(aPolygonColor));
+ mpOutputDevice->SetLineColor();
+
+ mpOutputDevice->DrawTransparent(tools::PolyPolygon(aLocalPolyPolygon),
+ nTransPercentVcl);
+ }
+ else
+ {
+ // save old mfCurrentUnifiedTransparence and set new one
+ // so that contained SvtGraphicStroke may use the current one
+ const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence);
+ // #i105377# paint the content metafile opaque as the transparency gets
+ // split of into the gradient below
+ // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence();
+ mfCurrentUnifiedTransparence = 0;
+
+ // various content, create content-metafile
+ GDIMetaFile aContentMetafile;
+ const tools::Rectangle aPrimitiveRectangle(
+ impDumpToMetaFile(rContent, aContentMetafile));
+
+ // restore mfCurrentUnifiedTransparence; it may have been used
+ // while processing the sub-content in impDumpToMetaFile
+ mfCurrentUnifiedTransparence = fLastCurrentUnifiedTransparence;
+
+ // create uniform VCL gradient for uniform transparency
+ Gradient aVCLGradient;
+ const sal_uInt8 nTransPercentVcl(static_cast<sal_uInt8>(
+ basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0)));
+ const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
+
+ aVCLGradient.SetStyle(GradientStyle::Linear);
+ aVCLGradient.SetStartColor(aTransColor);
+ aVCLGradient.SetEndColor(aTransColor);
+ aVCLGradient.SetAngle(0_deg10);
+ aVCLGradient.SetBorder(0);
+ aVCLGradient.SetOfsX(0);
+ aVCLGradient.SetOfsY(0);
+ aVCLGradient.SetStartIntensity(100);
+ aVCLGradient.SetEndIntensity(100);
+ aVCLGradient.SetSteps(2);
+
+ // render it to VCL
+ mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
+ aPrimitiveRectangle.GetSize(), aVCLGradient);
+ }
+ }
+ }
+
+ mpOutputDevice->Pop();
+}
+
+void VclMetafileProcessor2D::processTransparencePrimitive2D(
+ const primitive2d::TransparencePrimitive2D& rTransparenceCandidate)
+{
+ // for metafile: Need to examine what the pure vcl version is doing here actually
+ // - uses DrawTransparent with metafile for content and a gradient
+ // i can detect this here with checking the gradient part for a single
+ // FillGradientPrimitive2D and reconstruct the gradient.
+ // If that detection goes wrong, I have to create a transparence-blended bitmap. Eventually
+ // do that in stripes, else RenderTransparencePrimitive2D may just be used
+ const primitive2d::Primitive2DContainer& rContent = rTransparenceCandidate.getChildren();
+ const primitive2d::Primitive2DContainer& rTransparence
+ = rTransparenceCandidate.getTransparence();
+
+ if (rContent.empty() || rTransparence.empty())
+ return;
+
+ // try to identify a single FillGradientPrimitive2D in the
+ // transparence part of the primitive
+ const primitive2d::FillGradientPrimitive2D* pFiGradient = nullptr;
+ static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore
+
+ if (!bForceToBigTransparentVDev && 1 == rTransparence.size())
+ {
+ const primitive2d::Primitive2DReference xReference(rTransparence[0]);
+ pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(xReference.get());
+ }
+
+ // Check also for correct ID to exclude derived implementations
+ if (pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID())
+ {
+ // various content, create content-metafile
+ GDIMetaFile aContentMetafile;
+ const tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
+
+ // re-create a VCL-gradient from FillGradientPrimitive2D
+ Gradient aVCLGradient;
+ impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(),
+ true);
+
+ // render it to VCL
+ mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
+ aPrimitiveRectangle.GetSize(), aVCLGradient);
+ }
+ else
+ {
+ // sub-transparence group. Draw to VDev first.
+ // this may get refined to tiling when resolution is too big here
+
+ // need to avoid switching off MapMode stuff here; maybe need another
+ // tooling class, cannot just do the same as with the pixel renderer.
+ // Need to experiment...
+
+ // Okay, basic implementation finished and tested. The DPI stuff was hard
+ // and not easy to find out that it's needed.
+ // Since this will not yet happen normally (as long as no one constructs
+ // transparence primitives with non-trivial transparence content) i will for now not
+ // refine to tiling here.
+
+ basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D()));
+ aViewRange.transform(maCurrentTransformation);
+ const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())),
+ static_cast<sal_Int32>(floor(aViewRange.getMinY())),
+ static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
+ static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
+ const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic));
+ Size aSizePixel(aRectPixel.GetSize());
+ ScopedVclPtrInstance<VirtualDevice> aBufferDevice;
+ const sal_uInt32 nMaxSquarePixels(500000);
+ const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight());
+ double fReduceFactor(1.0);
+
+ if (nViewVisibleArea > nMaxSquarePixels)
+ {
+ // reduce render size
+ fReduceFactor = sqrt(double(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea));
+ aSizePixel = Size(
+ basegfx::fround(static_cast<double>(aSizePixel.getWidth()) * fReduceFactor),
+ basegfx::fround(static_cast<double>(aSizePixel.getHeight()) * fReduceFactor));
+ }
+
+ if (aBufferDevice->SetOutputSizePixel(aSizePixel))
+ {
+ // create and set MapModes for target devices
+ MapMode aNewMapMode(mpOutputDevice->GetMapMode());
+ aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top()));
+ aBufferDevice->SetMapMode(aNewMapMode);
+
+ // prepare view transformation for target renderers
+ // ATTENTION! Need to apply another scaling because of the potential DPI differences
+ // between Printer and VDev (mpOutputDevice and aBufferDevice here).
+ // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used.
+ basegfx::B2DHomMatrix aViewTransform(aBufferDevice->GetViewTransformation());
+ const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+ const Size aDPINew(aBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+ const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
+ / static_cast<double>(aDPINew.getWidth()));
+ const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
+ / static_cast<double>(aDPINew.getHeight()));
+
+ if (!basegfx::fTools::equal(fDPIXChange, 1.0)
+ || !basegfx::fTools::equal(fDPIYChange, 1.0))
+ {
+ aViewTransform.scale(fDPIXChange, fDPIYChange);
+ }
+
+ // also take scaling from Size reduction into account
+ if (!basegfx::fTools::equal(fReduceFactor, 1.0))
+ {
+ aViewTransform.scale(fReduceFactor, fReduceFactor);
+ }
+
+ // create view information and pixel renderer. Reuse known ViewInformation
+ // except new transformation and range
+ const geometry::ViewInformation2D aViewInfo(
+ getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange,
+ getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
+
+ VclPixelProcessor2D aBufferProcessor(aViewInfo, *aBufferDevice);
+
+ // draw content using pixel renderer
+ const Point aEmptyPoint;
+ aBufferProcessor.process(rContent);
+ const Bitmap aBmContent(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel));
+
+ // draw transparence using pixel renderer
+ aBufferDevice->Erase();
+ aBufferProcessor.process(rTransparence);
+ const AlphaMask aBmAlpha(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel));
+
+ // paint
+ mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(),
+ BitmapEx(aBmContent, aBmAlpha));
+ }
+ }
+}
+
+void VclMetafileProcessor2D::processStructureTagPrimitive2D(
+ const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate)
+{
+ ::comphelper::ValueRestorationGuard const g(mpCurrentStructureTag, &rStructureTagCandidate);
+
+ // structured tag primitive
+ const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
+ bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement));
+ sal_Int32 nPreviousElement(-1);
+
+ if (!rStructureTagCandidate.isTaggedSdrObject())
+ {
+ bTagUsed = false;
+ }
+
+ if (mpPDFExtOutDevData && bTagUsed)
+ {
+ // foreground object: tag as regular structure element
+ if (!rStructureTagCandidate.isBackground())
+ {
+ if (rStructureTagCandidate.GetAnchorStructureElementId() != -1)
+ {
+ auto const nTemp = mpPDFExtOutDevData->GetCurrentStructureElement();
+ bool const bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement(
+ rStructureTagCandidate.GetAnchorStructureElementId());
+ if (bSuccess)
+ {
+ nPreviousElement = nTemp;
+ }
+ else
+ {
+ SAL_WARN("drawinglayer", "anchor structure element not found?");
+ }
+ }
+ mpPDFExtOutDevData->BeginStructureElement(rTagElement);
+ switch (rTagElement)
+ {
+ case vcl::PDFWriter::H1:
+ case vcl::PDFWriter::H2:
+ case vcl::PDFWriter::H3:
+ case vcl::PDFWriter::H4:
+ case vcl::PDFWriter::H5:
+ case vcl::PDFWriter::H6:
+ case vcl::PDFWriter::Paragraph:
+ case vcl::PDFWriter::Heading:
+ case vcl::PDFWriter::Caption:
+ case vcl::PDFWriter::BlockQuote:
+ case vcl::PDFWriter::Table:
+ case vcl::PDFWriter::TableRow:
+ case vcl::PDFWriter::Formula:
+ case vcl::PDFWriter::Figure:
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement,
+ vcl::PDFWriter::Block);
+ break;
+ case vcl::PDFWriter::TableData:
+ case vcl::PDFWriter::TableHeader:
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement,
+ vcl::PDFWriter::Inline);
+ break;
+ default:
+ break;
+ }
+ switch (rTagElement)
+ {
+ case vcl::PDFWriter::Table:
+ case vcl::PDFWriter::Formula:
+ case vcl::PDFWriter::Figure:
+ {
+ auto const range(rStructureTagCandidate.getB2DRange(getViewInformation2D()));
+ tools::Rectangle const aLogicRect(
+ basegfx::fround(range.getMinX()), basegfx::fround(range.getMinY()),
+ basegfx::fround(range.getMaxX()), basegfx::fround(range.getMaxY()));
+ mpPDFExtOutDevData->SetStructureBoundingBox(aLogicRect);
+ break;
+ }
+ default:
+ break;
+ }
+ if (rTagElement == vcl::PDFWriter::TableHeader)
+ {
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope,
+ vcl::PDFWriter::Column);
+ }
+ }
+ // background object
+ else
+ {
+ // background image: tag as artifact
+ if (rStructureTagCandidate.isImage())
+ mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement);
+ // any other background object: do not tag
+ else
+ assert(false);
+ }
+ }
+
+ // process children normally
+ process(rStructureTagCandidate.getChildren());
+
+ if (mpPDFExtOutDevData && bTagUsed)
+ {
+ // write end tag
+ mpPDFExtOutDevData->EndStructureElement();
+ if (nPreviousElement != -1)
+ {
+#ifndef NDEBUG
+ bool const bSuccess =
+#endif
+ mpPDFExtOutDevData->SetCurrentStructureElement(nPreviousElement);
+ assert(bSuccess);
+ }
+ }
+}
+
+VclPtr<VirtualDevice>
+VclMetafileProcessor2D::CreateBufferDevice(const basegfx::B2DRange& rCandidateRange,
+ geometry::ViewInformation2D& rViewInfo,
+ tools::Rectangle& rRectLogic, Size& rSizePixel) const
+{
+ constexpr double fMaxSquarePixels = 500000;
+ basegfx::B2DRange aViewRange(rCandidateRange);
+ aViewRange.transform(maCurrentTransformation);
+ rRectLogic = tools::Rectangle(static_cast<tools::Long>(std::floor(aViewRange.getMinX())),
+ static_cast<tools::Long>(std::floor(aViewRange.getMinY())),
+ static_cast<tools::Long>(std::ceil(aViewRange.getMaxX())),
+ static_cast<tools::Long>(std::ceil(aViewRange.getMaxY())));
+ const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(rRectLogic));
+ rSizePixel = aRectPixel.GetSize();
+ const double fViewVisibleArea(rSizePixel.getWidth() * rSizePixel.getHeight());
+ double fReduceFactor(1.0);
+
+ if (fViewVisibleArea > fMaxSquarePixels)
+ {
+ // reduce render size
+ fReduceFactor = sqrt(fMaxSquarePixels / fViewVisibleArea);
+ rSizePixel = Size(basegfx::fround(rSizePixel.getWidth() * fReduceFactor),
+ basegfx::fround(rSizePixel.getHeight() * fReduceFactor));
+ }
+
+ VclPtrInstance<VirtualDevice> pBufferDevice(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
+ if (pBufferDevice->SetOutputSizePixel(rSizePixel))
+ {
+ // create and set MapModes for target devices
+ MapMode aNewMapMode(mpOutputDevice->GetMapMode());
+ aNewMapMode.SetOrigin(Point(-rRectLogic.Left(), -rRectLogic.Top()));
+ pBufferDevice->SetMapMode(aNewMapMode);
+
+ // prepare view transformation for target renderers
+ // ATTENTION! Need to apply another scaling because of the potential DPI differences
+ // between Printer and VDev (mpOutputDevice and pBufferDevice here).
+ // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used.
+ basegfx::B2DHomMatrix aViewTransform(pBufferDevice->GetViewTransformation());
+ const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+ const Size aDPINew(pBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+ const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
+ / static_cast<double>(aDPINew.getWidth()));
+ const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
+ / static_cast<double>(aDPINew.getHeight()));
+
+ if (!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0))
+ {
+ aViewTransform.scale(fDPIXChange, fDPIYChange);
+ }
+
+ // also take scaling from Size reduction into account
+ if (!basegfx::fTools::equal(fReduceFactor, 1.0))
+ {
+ aViewTransform.scale(fReduceFactor, fReduceFactor);
+ }
+
+ // create view information and pixel renderer. Reuse known ViewInformation
+ // except new transformation and range
+ rViewInfo = geometry::ViewInformation2D(
+ getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange,
+ getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
+ }
+ else
+ pBufferDevice.disposeAndClear();
+
+#if HAVE_P1155R3
+ return pBufferDevice;
+#else
+ return std::move(pBufferDevice);
+#endif
+}
+
+void VclMetafileProcessor2D::processPrimitive2DOnPixelProcessor(
+ const primitive2d::BasePrimitive2D& rCandidate)
+{
+ basegfx::B2DRange aViewRange(rCandidate.getB2DRange(getViewInformation2D()));
+ geometry::ViewInformation2D aViewInfo;
+ tools::Rectangle aRectLogic;
+ Size aSizePixel;
+ auto pBufferDevice(CreateBufferDevice(aViewRange, aViewInfo, aRectLogic, aSizePixel));
+ if (pBufferDevice)
+ {
+ VclPixelProcessor2D aBufferProcessor(aViewInfo, *pBufferDevice, maBColorModifierStack);
+
+ // draw content using pixel renderer
+ primitive2d::Primitive2DReference aRef(
+ &const_cast<primitive2d::BasePrimitive2D&>(rCandidate));
+ aBufferProcessor.process({ aRef });
+ const BitmapEx aBmContent(pBufferDevice->GetBitmapEx(Point(), aSizePixel));
+ mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(), aBmContent);
+
+ // aBufferProcessor dtor pops state off pBufferDevice pushed on by its ctor, let
+ // pBufferDevice live past aBufferProcessor scope to avoid warnings
+ }
+ pBufferDevice.disposeAndClear();
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
new file mode 100644
index 000000000..6b66a160b
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <stack>
+
+#include "vclprocessor2d.hxx"
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <vcl/pdfextoutdevdata.hxx> // vcl::PDFExtOutDevData support
+
+class GDIMetaFile;
+namespace tools
+{
+class Rectangle;
+}
+class Gradient;
+class SvtGraphicFill;
+class SvtGraphicStroke;
+
+namespace drawinglayer::attribute
+{
+class FillGradientAttribute;
+class LineAttribute;
+class StrokeAttribute;
+class LineStartEndAttribute;
+}
+
+namespace drawinglayer::primitive2d
+{
+class GraphicPrimitive2D;
+class ControlPrimitive2D;
+class TextHierarchyFieldPrimitive2D;
+class TextHierarchyLinePrimitive2D;
+class TextHierarchyBulletPrimitive2D;
+class TextHierarchyParagraphPrimitive2D;
+class TextHierarchyBlockPrimitive2D;
+class TextSimplePortionPrimitive2D;
+class PolygonHairlinePrimitive2D;
+class PolygonStrokePrimitive2D;
+class PolygonStrokeArrowPrimitive2D;
+class PolyPolygonGraphicPrimitive2D;
+class PolyPolygonHatchPrimitive2D;
+class PolyPolygonGradientPrimitive2D;
+class PolyPolygonColorPrimitive2D;
+class MaskPrimitive2D;
+class UnifiedTransparencePrimitive2D;
+class TransparencePrimitive2D;
+class ObjectInfoPrimitive2D;
+class StructureTagPrimitive2D;
+}
+
+namespace basegfx
+{
+class BColor;
+}
+
+namespace drawinglayer::processor2d
+{
+/** VclMetafileProcessor2D class
+
+ This processor derived from VclProcessor2D is the base class for rendering
+ all fed primitives to a classical VCL-Metafile, including all over the
+ time grown extra data in comments and PDF exception data creations. Also
+ printing needs some exception stuff.
+
+ All in all it is needed to emulate the old ::paint output from the old
+ Drawinglayer as long as exporters and/or filters still use the Metafile
+ and the extra-data added to it (which can be seen mostly as 'extensions'
+ or simply as 'hacks').
+ */
+class VclMetafileProcessor2D : public VclProcessor2D
+{
+private:
+ tools::Rectangle impDumpToMetaFile(const primitive2d::Primitive2DContainer& rContent,
+ GDIMetaFile& o_rContentMetafile);
+ void
+ impConvertFillGradientAttributeToVCLGradient(Gradient& o_rVCLGradient,
+ const attribute::FillGradientAttribute& rFiGrAtt,
+ bool bIsTransparenceGradient) const;
+ void impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill);
+ void impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill);
+ std::unique_ptr<SvtGraphicStroke>
+ impTryToCreateSvtGraphicStroke(const basegfx::B2DPolygon& rB2DPolygon,
+ const basegfx::BColor* pColor,
+ const attribute::LineAttribute* pLineAttribute,
+ const attribute::StrokeAttribute* pStrokeAttribute,
+ const attribute::LineStartEndAttribute* pStart,
+ const attribute::LineStartEndAttribute* pEnd);
+ void impStartSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke);
+ void impEndSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke);
+ void popStructureElement(vcl::PDFWriter::StructElement eElem);
+ void popListItem();
+ void popList();
+
+ void processGraphicPrimitive2D(const primitive2d::GraphicPrimitive2D& rGraphicPrimitive);
+ void processControlPrimitive2D(const primitive2d::ControlPrimitive2D& rControlPrimitive);
+ void processTextHierarchyFieldPrimitive2D(
+ const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive);
+ void processTextHierarchyLinePrimitive2D(
+ const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive);
+ void processTextHierarchyBulletPrimitive2D(
+ const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive);
+ void processTextHierarchyParagraphPrimitive2D(
+ const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive);
+ void processTextHierarchyBlockPrimitive2D(
+ const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive);
+ void processTextSimplePortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate);
+ void processPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive);
+ void
+ processPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive);
+ void processPolygonStrokeArrowPrimitive2D(
+ const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive);
+ void processPolyPolygonGraphicPrimitive2D(
+ const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate);
+ void processPolyPolygonHatchPrimitive2D(
+ const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate);
+ void processPolyPolygonGradientPrimitive2D(
+ const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate);
+ void processPolyPolygonColorPrimitive2D(
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate);
+ void processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate);
+ void processUnifiedTransparencePrimitive2D(
+ const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate);
+ void processTransparencePrimitive2D(
+ const primitive2d::TransparencePrimitive2D& rTransparenceCandidate);
+ void
+ processObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D);
+ void processStructureTagPrimitive2D(
+ const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate);
+ void processPrimitive2DOnPixelProcessor(const primitive2d::BasePrimitive2D& rCandidate);
+ VclPtr<VirtualDevice> CreateBufferDevice(const basegfx::B2DRange& rCandidateRange,
+ geometry::ViewInformation2D& rViewInfo,
+ tools::Rectangle& rRectLogic, Size& rSizePixel) const;
+
+ /// Convert the fWidth to the same space as its coordinates.
+ double getTransformedLineWidth(double fWidth) const;
+
+ /// the current clipping tools::PolyPolygon from MaskPrimitive2D
+ basegfx::B2DPolyPolygon maClipPolyPolygon;
+
+ /// the target MetaFile
+ GDIMetaFile* mpMetaFile;
+
+ /* do not allow embedding SvtGraphicFills into each other,
+ use a counter to prevent that
+ */
+ sal_uInt32 mnSvtGraphicFillCount;
+
+ /// same for SvtGraphicStroke
+ sal_uInt32 mnSvtGraphicStrokeCount;
+
+ /* hold the last unified transparence value to have it handy
+ on SvtGraphicStroke creation
+ */
+ double mfCurrentUnifiedTransparence;
+
+ /* break iterator support
+ made static so it only needs to be fetched once, even with many single
+ constructed VclMetafileProcessor2D. It's still incarnated on demand,
+ but exists for OOo runtime now by purpose.
+ */
+ static css::uno::Reference<css::i18n::XBreakIterator> mxBreakIterator;
+
+ /* vcl::PDFExtOutDevData support
+ For the first step, some extra actions at vcl::PDFExtOutDevData need to
+ be emulated with the VclMetafileProcessor2D. These are potentially temporarily
+ since PDF export may use PrimitiveSequences one day directly.
+ */
+ vcl::PDFExtOutDevData* mpPDFExtOutDevData;
+
+ // Remember the current OutlineLevel. This is used when tagged PDF export
+ // is used to create/write valid structured list entries using PDF statements
+ // like '/L', '/LI', 'LBody' instead of simple '/P' (Paragraph).
+ // The value -1 means 'no OutlineLevel' and values >= 0 express the level.
+ sal_Int16 mnCurrentOutlineLevel;
+ bool mbInListItem;
+ bool mbBulletPresent;
+
+ std::stack<vcl::PDFWriter::StructElement> maListElements;
+
+ primitive2d::StructureTagPrimitive2D const* mpCurrentStructureTag = nullptr;
+
+protected:
+ /* the local processor for BasePrimitive2D-Implementation based primitives,
+ called from the common process()-implementation
+ */
+ virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) override;
+
+public:
+ /// constructor/destructor
+ VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+ OutputDevice& rOutDev);
+ virtual ~VclMetafileProcessor2D() override;
+};
+} // end of namespace processor2d::drawinglayer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
new file mode 100644
index 000000000..9291c1830
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -0,0 +1,1351 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vclpixelprocessor2d.hxx"
+#include "vclhelperbufferdevice.hxx"
+#include "helperwrongspellrenderer.hxx"
+#include <comphelper/lok.hxx>
+
+#include <sal/log.hxx>
+#include <vcl/BitmapBasicMorphologyFilter.hxx>
+#include <vcl/BitmapFilterStackBlur.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/hatch.hxx>
+#include <vcl/canvastools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/glowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
+#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
+#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
+#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
+#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+
+#include <svtools/optionsdrawinglayer.hxx>
+#include <vcl/gradient.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::processor2d
+{
+struct VclPixelProcessor2D::Impl
+{
+ AntialiasingFlags m_nOrigAntiAliasing;
+
+ explicit Impl(OutputDevice const& rOutDev)
+ : m_nOrigAntiAliasing(rOutDev.GetAntialiasing())
+ {
+ }
+};
+
+VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+ OutputDevice& rOutDev,
+ const basegfx::BColorModifierStack& rInitStack)
+ : VclProcessor2D(rViewInformation, rOutDev, rInitStack)
+ , m_pImpl(new Impl(rOutDev))
+{
+ // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels
+ maCurrentTransformation = rViewInformation.getObjectToViewTransformation();
+
+ // prepare output directly to pixels
+ mpOutputDevice->Push(vcl::PushFlags::MAPMODE);
+ mpOutputDevice->SetMapMode();
+
+ // react on AntiAliasing settings
+ if (SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing | AntialiasingFlags::Enable);
+ }
+ else
+ {
+ mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing & ~AntialiasingFlags::Enable);
+ }
+}
+
+VclPixelProcessor2D::~VclPixelProcessor2D()
+{
+ // restore MapMode
+ mpOutputDevice->Pop();
+
+ // restore AntiAliasing
+ mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing);
+}
+
+void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(
+ const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency)
+{
+ if (!rSource.getB2DPolyPolygon().count() || fTransparency < 0.0 || fTransparency >= 1.0)
+ {
+ // no geometry, done
+ return;
+ }
+
+ const basegfx::BColor aPolygonColor(
+ maBColorModifierStack.getModifiedColor(rSource.getBColor()));
+
+ mpOutputDevice->SetFillColor(Color(aPolygonColor));
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->DrawTransparent(maCurrentTransformation, rSource.getB2DPolyPolygon(),
+ fTransparency);
+}
+
+bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect(
+ const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency)
+{
+ const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon());
+
+ if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0)
+ {
+ // no geometry, done
+ return true;
+ }
+
+ const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rSource.getBColor()));
+
+ mpOutputDevice->SetFillColor();
+ mpOutputDevice->SetLineColor(Color(aLineColor));
+ //aLocalPolygon.transform(maCurrentTransformation);
+
+ // try drawing; if it did not work, use standard fallback
+ return mpOutputDevice->DrawPolyLineDirect(maCurrentTransformation, rLocalPolygon, 0.0,
+ fTransparency);
+}
+
+bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(
+ const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency)
+{
+ const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon());
+
+ if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0)
+ {
+ // no geometry, done
+ return true;
+ }
+
+ if (basegfx::B2DLineJoin::NONE == rSource.getLineAttribute().getLineJoin()
+ && css::drawing::LineCap_BUTT != rSource.getLineAttribute().getLineCap())
+ {
+ // better use decompose to get that combination done for now, see discussion
+ // at https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff
+ return false;
+ }
+
+ // MM01: Radically change here - no dismantle/applyLineDashing,
+ // let that happen low-level at DrawPolyLineDirect implementations
+ // to open up for buffering and evtl. direct draw with sys-dep
+ // graphic systems. Check for stroke is in use
+ const bool bStrokeAttributeNotUsed(rSource.getStrokeAttribute().isDefault()
+ || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen());
+
+ const basegfx::BColor aLineColor(
+ maBColorModifierStack.getModifiedColor(rSource.getLineAttribute().getColor()));
+
+ mpOutputDevice->SetFillColor();
+ mpOutputDevice->SetLineColor(Color(aLineColor));
+
+ // MM01 draw direct, hand over dash data if available
+ return mpOutputDevice->DrawPolyLineDirect(
+ maCurrentTransformation, rLocalPolygon,
+ // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline)
+ rSource.getLineAttribute().getWidth(), fTransparency,
+ bStrokeAttributeNotUsed ? nullptr : &rSource.getStrokeAttribute().getDotDashArray(),
+ rSource.getLineAttribute().getLineJoin(), rSource.getLineAttribute().getLineCap(),
+ rSource.getLineAttribute().getMiterMinimumAngle());
+}
+
+namespace
+{
+GradientStyle convertGradientStyle(drawinglayer::attribute::GradientStyle eGradientStyle)
+{
+ switch (eGradientStyle)
+ {
+ case drawinglayer::attribute::GradientStyle::Axial:
+ return GradientStyle::Axial;
+ case drawinglayer::attribute::GradientStyle::Radial:
+ return GradientStyle::Radial;
+ case drawinglayer::attribute::GradientStyle::Elliptical:
+ return GradientStyle::Elliptical;
+ case drawinglayer::attribute::GradientStyle::Square:
+ return GradientStyle::Square;
+ case drawinglayer::attribute::GradientStyle::Rect:
+ return GradientStyle::Rect;
+ case drawinglayer::attribute::GradientStyle::Linear:
+ return GradientStyle::Linear;
+ default:
+ assert(false);
+ return GradientStyle::Linear;
+ }
+}
+
+} // end anonymous namespace
+
+void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+{
+ switch (rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D:
+ {
+ processWrongSpellPrimitive2D(
+ static_cast<const primitive2d::WrongSpellPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
+ {
+ processTextSimplePortionPrimitive2D(
+ static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
+ {
+ processTextDecoratedPortionPrimitive2D(
+ static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ {
+ processPolygonHairlinePrimitive2D(
+ static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ {
+ // direct draw of transformed BitmapEx primitive
+ processBitmapPrimitive2D(
+ static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
+ {
+ // direct draw of fillBitmapPrimitive
+ RenderFillGraphicPrimitive2D(
+ static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D:
+ {
+ processPolyPolygonGradientPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
+ {
+ // direct draw of bitmap
+ RenderPolyPolygonGraphicPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ {
+ processPolyPolygonColorPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D:
+ {
+ processMetaFilePrimitive2D(rCandidate);
+ break;
+ }
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ {
+ // mask group.
+ RenderMaskPrimitive2DPixel(
+ static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+ {
+ // modified color group. Force output to unified color.
+ RenderModifiedColorPrimitive2D(
+ static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+ {
+ processUnifiedTransparencePrimitive2D(
+ static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
+ {
+ // sub-transparence group. Draw to VDev first.
+ RenderTransparencePrimitive2D(
+ static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ {
+ // transform group.
+ RenderTransformPrimitive2D(
+ static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D:
+ {
+ // new XDrawPage for ViewInformation2D
+ RenderPagePreviewPrimitive2D(
+ static_cast<const primitive2d::PagePreviewPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+ {
+ // marker array
+ RenderMarkerArrayPrimitive2D(
+ static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ {
+ // point array
+ RenderPointArrayPrimitive2D(
+ static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D:
+ {
+ processControlPrimitive2D(
+ static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ {
+ processPolygonStrokePrimitive2D(
+ static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D:
+ {
+ processFillHatchPrimitive2D(
+ static_cast<const primitive2d::FillHatchPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D:
+ {
+ processBackgroundColorPrimitive2D(
+ static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D:
+ {
+ // #i97628#
+ // This primitive means that the content is derived from an active text edit,
+ // not from model data itself. Some renderers need to suppress this content, e.g.
+ // the pixel renderer used for displaying the edit view (like this one). It's
+ // not to be suppressed by the MetaFile renderers, so that the edited text is
+ // part of the MetaFile, e.g. needed for presentation previews.
+ // Action: Ignore here, do nothing.
+ break;
+ }
+ case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
+ {
+ processInvertPrimitive2D(rCandidate);
+ break;
+ }
+ case PRIMITIVE2D_ID_EPSPRIMITIVE2D:
+ {
+ RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D:
+ {
+ RenderSvgLinearAtomPrimitive2D(
+ static_cast<const primitive2d::SvgLinearAtomPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D:
+ {
+ RenderSvgRadialAtomPrimitive2D(
+ static_cast<const primitive2d::SvgRadialAtomPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D:
+ {
+ processBorderLinePrimitive2D(
+ static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
+ {
+ processGlowPrimitive2D(
+ static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
+ {
+ processSoftEdgePrimitive2D(
+ static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
+ {
+ processShadowPrimitive2D(
+ static_cast<const drawinglayer::primitive2d::ShadowPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
+ {
+ processFillGradientPrimitive2D(
+ static_cast<const drawinglayer::primitive2d::FillGradientPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D:
+ {
+ processPatternFillPrimitive2D(
+ static_cast<const drawinglayer::primitive2d::PatternFillPrimitive2D&>(rCandidate));
+ break;
+ }
+ default:
+ {
+ SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
+ rCandidate.getPrimitive2DID()));
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+}
+
+void VclPixelProcessor2D::processWrongSpellPrimitive2D(
+ const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive)
+{
+ if (!renderWrongSpellPrimitive2D(rWrongSpellPrimitive, *mpOutputDevice, maCurrentTransformation,
+ maBColorModifierStack))
+ {
+ // fallback to decomposition (MetaFile)
+ process(rWrongSpellPrimitive);
+ }
+}
+
+void VclPixelProcessor2D::processTextSimplePortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rCandidate)
+{
+ // Adapt evtl. used special DrawMode
+ const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
+ adaptTextToFillDrawMode();
+
+ if (SvtOptionsDrawinglayer::IsRenderSimpleTextDirect())
+ {
+ RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate);
+ }
+ else
+ {
+ process(rCandidate);
+ }
+
+ // restore DrawMode
+ mpOutputDevice->SetDrawMode(nOriginalDrawMode);
+}
+
+void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rCandidate)
+{
+ // Adapt evtl. used special DrawMode
+ const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
+ adaptTextToFillDrawMode();
+
+ if (SvtOptionsDrawinglayer::IsRenderDecoratedTextDirect())
+ {
+ RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate);
+ }
+ else
+ {
+ process(rCandidate);
+ }
+
+ // restore DrawMode
+ mpOutputDevice->SetDrawMode(nOriginalDrawMode);
+}
+
+void VclPixelProcessor2D::processPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D)
+{
+ if (tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D, 0.0))
+ {
+ return;
+ }
+
+ // direct draw of hairline
+ RenderPolygonHairlinePrimitive2D(rPolygonHairlinePrimitive2D, true);
+}
+
+void VclPixelProcessor2D::processBitmapPrimitive2D(
+ const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
+{
+ // check if graphic content is inside discrete local ViewPort
+ const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport());
+ const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
+ * rBitmapCandidate.getTransform());
+
+ if (!rDiscreteViewPort.isEmpty())
+ {
+ basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+
+ aUnitRange.transform(aLocalTransform);
+
+ if (!aUnitRange.overlaps(rDiscreteViewPort))
+ {
+ // content is outside discrete local ViewPort
+ return;
+ }
+ }
+
+ RenderBitmapPrimitive2D(rBitmapCandidate);
+}
+
+void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D(
+ const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate)
+{
+ // direct draw of gradient
+ const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient());
+ basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor()));
+ basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor()));
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
+
+ if (!aLocalPolyPolygon.count())
+ return;
+
+ if (aStartColor == aEndColor)
+ {
+ // no gradient at all, draw as polygon in AA and non-AA case
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->SetFillColor(Color(aStartColor));
+ mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
+ }
+ else
+ {
+ // use the primitive decomposition of the metafile
+ process(rPolygonCandidate);
+ }
+}
+
+void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D(
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D)
+{
+ // try to use directly
+ basegfx::B2DPolyPolygon aLocalPolyPolygon;
+
+ tryDrawPolyPolygonColorPrimitive2DDirect(rPolyPolygonColorPrimitive2D, 0.0);
+ // okay, done. In this case no gaps should have to be repaired, too
+
+ // when AA is on and this filled polygons are the result of stroked line geometry,
+ // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons
+ // Caution: This is needed in both cases (!)
+ if (!(mnPolygonStrokePrimitive2D && SvtOptionsDrawinglayer::IsAntiAliasing()
+ && (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::Enable)))
+ return;
+
+ const basegfx::BColor aPolygonColor(
+ maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor()));
+ sal_uInt32 nCount(aLocalPolyPolygon.count());
+
+ if (!nCount)
+ {
+ aLocalPolyPolygon = rPolyPolygonColorPrimitive2D.getB2DPolyPolygon();
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+ nCount = aLocalPolyPolygon.count();
+ }
+
+ mpOutputDevice->SetFillColor();
+ mpOutputDevice->SetLineColor(Color(aPolygonColor));
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ mpOutputDevice->DrawPolyLine(aLocalPolyPolygon.getB2DPolygon(a), 0.0);
+ }
+}
+
+void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D(
+ const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate)
+{
+ // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case,
+ // use the faster OutputDevice::DrawTransparent method
+ const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren();
+
+ if (rContent.empty())
+ return;
+
+ if (0.0 == rUniTransparenceCandidate.getTransparence())
+ {
+ // not transparent at all, use content
+ process(rUniTransparenceCandidate.getChildren());
+ }
+ else if (rUniTransparenceCandidate.getTransparence() > 0.0
+ && rUniTransparenceCandidate.getTransparence() < 1.0)
+ {
+ bool bDrawTransparentUsed(false);
+
+ if (1 == rContent.size())
+ {
+ const primitive2d::BasePrimitive2D* pBasePrimitive = rContent[0].get();
+
+ switch (pBasePrimitive->getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ {
+ // single transparent tools::PolyPolygon identified, use directly
+ const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor
+ = static_cast<const primitive2d::PolyPolygonColorPrimitive2D*>(
+ pBasePrimitive);
+ SAL_WARN_IF(!pPoPoColor, "drawinglayer",
+ "OOps, PrimitiveID and PrimitiveType do not match (!)");
+ bDrawTransparentUsed = true;
+ tryDrawPolyPolygonColorPrimitive2DDirect(
+ *pPoPoColor, rUniTransparenceCandidate.getTransparence());
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ {
+ // single transparent PolygonHairlinePrimitive2D identified, use directly
+ const primitive2d::PolygonHairlinePrimitive2D* pPoHair
+ = static_cast<const primitive2d::PolygonHairlinePrimitive2D*>(
+ pBasePrimitive);
+ SAL_WARN_IF(!pPoHair, "drawinglayer",
+ "OOps, PrimitiveID and PrimitiveType do not match (!)");
+
+ // do no tallow by default - problem is that self-overlapping parts of this geometry will
+ // not be in an all-same transparency but will already alpha-cover themselves with blending.
+ // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its
+ // content to be uniformly transparent.
+ // For hairline the effect is pretty minimal, but still not correct.
+ bDrawTransparentUsed = false;
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ {
+ // single transparent PolygonStrokePrimitive2D identified, use directly
+ const primitive2d::PolygonStrokePrimitive2D* pPoStroke
+ = static_cast<const primitive2d::PolygonStrokePrimitive2D*>(pBasePrimitive);
+ SAL_WARN_IF(!pPoStroke, "drawinglayer",
+ "OOps, PrimitiveID and PrimitiveType do not match (!)");
+
+ // do no tallow by default - problem is that self-overlapping parts of this geometry will
+ // not be in an all-same transparency but will already alpha-cover themselves with blending.
+ // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its
+ // content to be uniformly transparent.
+ // To check, activate and draw a wide transparent self-crossing line/curve
+ bDrawTransparentUsed = false;
+ break;
+ }
+ default:
+ SAL_INFO("drawinglayer",
+ "default case for " << drawinglayer::primitive2d::idToString(
+ rUniTransparenceCandidate.getPrimitive2DID()));
+ break;
+ }
+ }
+
+ if (!bDrawTransparentUsed)
+ {
+ // unified sub-transparence. Draw to VDev first.
+ RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate);
+ }
+ }
+}
+
+void VclPixelProcessor2D::processControlPrimitive2D(
+ const primitive2d::ControlPrimitive2D& rControlPrimitive)
+{
+ // control primitive
+ const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl());
+
+ try
+ {
+ // remember old graphics and create new
+ uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW);
+ const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics());
+ const uno::Reference<awt::XGraphics> xNewGraphics(mpOutputDevice->CreateUnoGraphics());
+
+ if (xNewGraphics.is())
+ {
+ // find out if the control is already visualized as a VCL-ChildWindow. If yes,
+ // it does not need to be painted at all.
+ uno::Reference<awt::XWindow2> xControlWindow(rXControl, uno::UNO_QUERY_THROW);
+ bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is()
+ && xControlWindow->isVisible());
+
+ // tdf#131281 The FormControls are not painted when using the Tiled Rendering for a simple
+ // reason: when e.g. bControlIsVisibleAsChildWindow is true. This is the case because the
+ // office is in non-layout mode (default for controls at startup). For the common office
+ // this means that there exists a real VCL-System-Window for the control, so it is *not*
+ // painted here due to being exactly obscured by that real Window (and creates danger of
+ // flickering, too).
+ // Tiled Rendering clients usually do *not* have real VCL-Windows for the controls, but
+ // exactly that would be needed on each client displaying the tiles (what would be hard
+ // to do but also would have advantages - the clients would have real controls in the
+ // shape of their target system which could be interacted with...). It is also what the
+ // office does.
+ // For now, fallback to just render these controls when Tiled Rendering is active to just
+ // have them displayed on all clients.
+ if (bControlIsVisibleAsChildWindow && comphelper::LibreOfficeKit::isActive())
+ {
+ // Do force paint when we are in Tiled Renderer and FormControl is 'visible'
+ bControlIsVisibleAsChildWindow = false;
+ }
+
+ if (!bControlIsVisibleAsChildWindow)
+ {
+ // Needs to be drawn. Link new graphics and view
+ xControlView->setGraphics(xNewGraphics);
+
+ // get position
+ const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation
+ * rControlPrimitive.getTransform());
+ const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0));
+
+ // Do not forget to use the evtl. offsetted origin of the target device,
+ // e.g. when used with mask/transparence buffer device
+ const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin());
+ xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()),
+ aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY()));
+
+ // restore original graphics
+ xControlView->setGraphics(xOriginalGraphics);
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ // #i116763# removing since there is a good alternative when the xControlView
+ // is not found and it is allowed to happen
+ // DBG_UNHANDLED_EXCEPTION();
+
+ // process recursively and use the decomposition as Bitmap
+ process(rControlPrimitive);
+ }
+}
+
+void VclPixelProcessor2D::processPolygonStrokePrimitive2D(
+ const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D)
+{
+ // try to use directly
+ if (tryDrawPolygonStrokePrimitive2DDirect(rPolygonStrokePrimitive2D, 0.0))
+ {
+ return;
+ }
+
+ // the stroke primitive may be decomposed to filled polygons. To keep
+ // evtl. set DrawModes aka DrawModeFlags::BlackLine, DrawModeFlags::GrayLine,
+ // DrawModeFlags::GhostedLine, DrawModeFlags::WhiteLine or DrawModeFlags::SettingsLine
+ // working, these need to be copied to the corresponding fill modes
+ const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
+ adaptLineToFillDrawMode();
+
+ // polygon stroke primitive
+
+ // Lines with 1 and 2 pixel width without AA need special treatment since their visualization
+ // as filled polygons is geometrically correct but looks wrong since polygon filling avoids
+ // the right and bottom pixels. The used method evaluates that and takes the correct action,
+ // including calling recursively with decomposition if line is wide enough
+ RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive2D);
+
+ // restore DrawMode
+ mpOutputDevice->SetDrawMode(nOriginalDrawMode);
+}
+
+void VclPixelProcessor2D::processFillHatchPrimitive2D(
+ const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive)
+{
+ if (SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ // if AA is used (or ignore smoothing is on), there is no need to smooth
+ // hatch painting, use decomposition
+ process(rFillHatchPrimitive);
+ }
+ else
+ {
+ // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel
+ // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother.
+ // This is wrong in principle, but looks nicer. This could also be done here directly
+ // without VCL usage if needed
+ const attribute::FillHatchAttribute& rFillHatchAttributes
+ = rFillHatchPrimitive.getFillHatch();
+
+ // create hatch polygon in range size and discrete coordinates
+ basegfx::B2DRange aHatchRange(rFillHatchPrimitive.getOutputRange());
+ aHatchRange.transform(maCurrentTransformation);
+ const basegfx::B2DPolygon aHatchPolygon(basegfx::utils::createPolygonFromRect(aHatchRange));
+
+ if (rFillHatchAttributes.isFillBackground())
+ {
+ // #i111846# background fill is active; draw fill polygon
+ const basegfx::BColor aPolygonColor(
+ maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor()));
+
+ mpOutputDevice->SetFillColor(Color(aPolygonColor));
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->DrawPolygon(aHatchPolygon);
+ }
+
+ // set hatch line color
+ const basegfx::BColor aHatchColor(
+ maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor()));
+ mpOutputDevice->SetFillColor();
+ mpOutputDevice->SetLineColor(Color(aHatchColor));
+
+ // get hatch style
+ HatchStyle eHatchStyle(HatchStyle::Single);
+
+ switch (rFillHatchAttributes.getStyle())
+ {
+ default: // HatchStyle::Single
+ {
+ break;
+ }
+ case attribute::HatchStyle::Double:
+ {
+ eHatchStyle = HatchStyle::Double;
+ break;
+ }
+ case attribute::HatchStyle::Triple:
+ {
+ eHatchStyle = HatchStyle::Triple;
+ break;
+ }
+ }
+
+ // create hatch
+ const basegfx::B2DVector aDiscreteDistance(
+ maCurrentTransformation * basegfx::B2DVector(rFillHatchAttributes.getDistance(), 0.0));
+ const sal_uInt32 nDistance(basegfx::fround(aDiscreteDistance.getLength()));
+ const sal_uInt32 nAngle10(
+ basegfx::rad2deg<10>(basegfx::fround(rFillHatchAttributes.getAngle())));
+ ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance,
+ Degree10(nAngle10));
+
+ // draw hatch using VCL
+ mpOutputDevice->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon)), aVCLHatch);
+ }
+}
+
+void VclPixelProcessor2D::processBackgroundColorPrimitive2D(
+ const primitive2d::BackgroundColorPrimitive2D& rPrimitive)
+{
+ // #i98404# Handle directly, especially when AA is active
+ const AntialiasingFlags nOriginalAA(mpOutputDevice->GetAntialiasing());
+
+ // switch AA off in all cases
+ mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~AntialiasingFlags::Enable);
+
+ // create color for fill
+ const basegfx::BColor aPolygonColor(
+ maBColorModifierStack.getModifiedColor(rPrimitive.getBColor()));
+ Color aFillColor(aPolygonColor);
+ aFillColor.SetAlpha(255 - sal_uInt8((rPrimitive.getTransparency() * 255.0) + 0.5));
+ mpOutputDevice->SetFillColor(aFillColor);
+ mpOutputDevice->SetLineColor();
+
+ // create rectangle for fill
+ const basegfx::B2DRange& aViewport(getViewInformation2D().getDiscreteViewport());
+ const ::tools::Rectangle aRectangle(static_cast<sal_Int32>(floor(aViewport.getMinX())),
+ static_cast<sal_Int32>(floor(aViewport.getMinY())),
+ static_cast<sal_Int32>(ceil(aViewport.getMaxX())),
+ static_cast<sal_Int32>(ceil(aViewport.getMaxY())));
+ mpOutputDevice->DrawRect(aRectangle);
+
+ // restore AA setting
+ mpOutputDevice->SetAntialiasing(nOriginalAA);
+}
+
+void VclPixelProcessor2D::processBorderLinePrimitive2D(
+ const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder)
+{
+ // Process recursively, but switch off AntiAliasing for
+ // horizontal/vertical lines (*not* diagonal lines).
+ // Checked using AntialiasingFlags::PixelSnapHairline instead,
+ // but with AntiAliasing on the display really is too 'ghosty' when
+ // using fine stroking. Correct, but 'ghosty'.
+
+ // It has shown that there are quite some problems here:
+ // - vcl OutDev renderer methods still use fallbacks to paint
+ // multiple single lines between discrete sizes of < 3.5 what
+ // looks bad and does not match
+ // - mix of filled Polygons and Lines is bad when AA switched off
+ // - Alignment of AA with non-AA may be bad in diverse different
+ // renderers
+ //
+ // Due to these reasons I change the strategy: Always draw AAed, but
+ // allow fallback to test/check and if needed. The normal case
+ // where BorderLines will be system-dependently snapped to have at
+ // least a single discrete width per partial line (there may be up to
+ // three) works well nowadays due to most renderers moving the AA stuff
+ // by 0.5 pixels (discrete units) to match well with the non-AAed parts.
+ //
+ // Env-Switch for steering this, default is off.
+ // Enable by setting at all (and to something)
+ static const char* pSwitchOffAntiAliasingForHorVerBorderlines(
+ getenv("SAL_SWITCH_OFF_ANTIALIASING_FOR_HOR_VER_BORTDERLINES"));
+ static bool bSwitchOffAntiAliasingForHorVerBorderlines(
+ nullptr != pSwitchOffAntiAliasingForHorVerBorderlines);
+
+ if (bSwitchOffAntiAliasingForHorVerBorderlines
+ && rBorder.isHorizontalOrVertical(getViewInformation2D()))
+ {
+ AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing();
+ mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::Enable);
+ process(rBorder);
+ mpOutputDevice->SetAntialiasing(nAntiAliasing);
+ }
+ else
+ {
+ process(rBorder);
+ }
+}
+
+void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+{
+ // invert primitive (currently only used for HighContrast fallback for selection in SW and SC).
+ // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.)
+ // Set OutDev to XOR and switch AA off (XOR does not work with AA)
+ mpOutputDevice->Push();
+ mpOutputDevice->SetRasterOp(RasterOp::Xor);
+ const AntialiasingFlags nAntiAliasing(mpOutputDevice->GetAntialiasing());
+ mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::Enable);
+
+ // process content recursively
+ process(rCandidate);
+
+ // restore OutDev
+ mpOutputDevice->Pop();
+ mpOutputDevice->SetAntialiasing(nAntiAliasing);
+}
+
+void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+{
+ // #i98289#
+ const bool bForceLineSnap(SvtOptionsDrawinglayer::IsAntiAliasing()
+ && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete());
+ const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing());
+
+ if (bForceLineSnap)
+ {
+ mpOutputDevice->SetAntialiasing(nOldAntiAliase | AntialiasingFlags::PixelSnapHairline);
+ }
+
+ process(rCandidate);
+
+ if (bForceLineSnap)
+ {
+ mpOutputDevice->SetAntialiasing(nOldAntiAliase);
+ }
+}
+
+namespace
+{
+/* Returns 8-bit alpha mask created from passed mask.
+
+ Negative fErodeDilateRadius values mean erode, positive - dilate.
+ nTransparency defines minimal transparency level.
+*/
+AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rMask, double fErodeDilateRadius,
+ double fBlurRadius, sal_uInt8 nTransparency,
+ bool bConvertTo1Bit = true)
+{
+ // Only completely white pixels on the initial mask must be considered for transparency. Any
+ // other color must be treated as black. This creates 1-bit B&W bitmap.
+ BitmapEx mask(bConvertTo1Bit ? rMask.CreateMask(COL_WHITE) : rMask);
+
+ // Scaling down increases performance without noticeable quality loss. Additionally,
+ // current blur implementation can only handle blur radius between 2 and 254.
+ Size aSize = mask.GetSizePixel();
+ double fScale = 1.0;
+ while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000)
+ {
+ fScale /= 2;
+ fBlurRadius /= 2;
+ fErodeDilateRadius /= 2;
+ aSize.setHeight(aSize.Height() / 2);
+ aSize.setWidth(aSize.Width() / 2);
+ }
+
+ // BmpScaleFlag::Fast is important for following color replacement
+ mask.Scale(fScale, fScale, BmpScaleFlag::Fast);
+
+ if (fErodeDilateRadius > 0)
+ BitmapFilter::Filter(mask, BitmapDilateFilter(fErodeDilateRadius));
+ else if (fErodeDilateRadius < 0)
+ BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF));
+
+ if (nTransparency)
+ {
+ const Color aTransparency(nTransparency, nTransparency, nTransparency);
+ mask.Replace(COL_BLACK, aTransparency);
+ }
+
+ // We need 8-bit grey mask for blurring
+ mask.Convert(BmpConversion::N8BitGreys);
+
+ // calculate blurry effect
+ BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius));
+
+ mask.Scale(rMask.GetSizePixel());
+
+ return AlphaMask(mask.GetBitmap());
+}
+
+drawinglayer::geometry::ViewInformation2D
+expandRange(const drawinglayer::geometry::ViewInformation2D& rViewInfo, double nAmount)
+{
+ basegfx::B2DRange viewport(rViewInfo.getViewport());
+ viewport.grow(nAmount);
+ return { rViewInfo.getObjectTransformation(),
+ rViewInfo.getViewTransformation(),
+ viewport,
+ rViewInfo.getVisualizedPage(),
+ rViewInfo.getViewTime(),
+ rViewInfo.getReducedDisplayQuality() };
+}
+}
+
+void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate)
+{
+ const double nGlowRadius(rCandidate.getGlowRadius());
+ // Avoid wrong effect on the cut-off side; so expand by radius
+ const auto aExpandedViewInfo(expandRange(getViewInformation2D(), nGlowRadius));
+ basegfx::B2DRange aRange(rCandidate.getB2DRange(aExpandedViewInfo));
+ aRange.transform(maCurrentTransformation);
+ basegfx::B2DVector aGlowRadiusVector(nGlowRadius, 0);
+ // Calculate the pixel size of glow radius in current transformation
+ aGlowRadiusVector *= maCurrentTransformation;
+ // Glow radius is the size of the halo from each side of the object. The halo is the
+ // border of glow color that fades from glow transparency level to fully transparent
+ // When blurring a sharp boundary (our case), it gets 50% of original intensity, and
+ // fades to both sides by the blur radius; thus blur radius is half of glow radius.
+ const double fBlurRadius = aGlowRadiusVector.getLength() / 2;
+ // Consider glow transparency (initial transparency near the object edge)
+ const sal_uInt8 nAlpha = rCandidate.getGlowColor().GetAlpha();
+
+ impBufferDevice aBufferDevice(*mpOutputDevice, aRange, false);
+ if (aBufferDevice.isVisible())
+ {
+ // remember last OutDev and set to content
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+ // We don't need antialiased mask here, which would only make effect thicker
+ const auto aPrevAA = mpOutputDevice->GetAntialiasing();
+ mpOutputDevice->SetAntialiasing(AntialiasingFlags::NONE);
+ process(rCandidate);
+
+ // Limit the bitmap size to the visible area.
+ basegfx::B2DRange bitmapRange(aRange);
+ if (!aExpandedViewInfo.getDiscreteViewport().isEmpty())
+ bitmapRange.intersect(aExpandedViewInfo.getDiscreteViewport());
+ if (!bitmapRange.isEmpty())
+ {
+ const tools::Rectangle aRect(
+ static_cast<tools::Long>(std::floor(bitmapRange.getMinX())),
+ static_cast<tools::Long>(std::floor(bitmapRange.getMinY())),
+ static_cast<tools::Long>(std::ceil(bitmapRange.getMaxX())),
+ static_cast<tools::Long>(std::ceil(bitmapRange.getMaxY())));
+ BitmapEx bmpEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize());
+ mpOutputDevice->SetAntialiasing(aPrevAA);
+
+ AlphaMask mask
+ = ProcessAndBlurAlphaMask(bmpEx.GetAlpha(), fBlurRadius, fBlurRadius, 255 - nAlpha);
+
+ // The end result is the bitmap filled with glow color and blurred 8-bit alpha mask
+ const basegfx::BColor aGlowColor(
+ maBColorModifierStack.getModifiedColor(rCandidate.getGlowColor().getBColor()));
+ Bitmap bmp = bmpEx.GetBitmap();
+ bmp.Erase(Color(aGlowColor));
+ BitmapEx result(bmp, mask);
+
+ // back to old OutDev
+ mpOutputDevice = pLastOutputDevice;
+ mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
+ }
+ else
+ {
+ mpOutputDevice = pLastOutputDevice;
+ }
+ }
+ else
+ SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
+}
+
+void VclPixelProcessor2D::processSoftEdgePrimitive2D(
+ const primitive2d::SoftEdgePrimitive2D& rCandidate)
+{
+ const double nRadius(rCandidate.getRadius());
+ // Avoid wrong effect on the cut-off side; so expand by diameter
+ const auto aExpandedViewInfo(expandRange(getViewInformation2D(), nRadius * 2));
+
+ basegfx::B2DRange aRange(rCandidate.getB2DRange(aExpandedViewInfo));
+ aRange.transform(maCurrentTransformation);
+ basegfx::B2DVector aRadiusVector(nRadius, 0);
+ // Calculate the pixel size of soft edge radius in current transformation
+ aRadiusVector *= maCurrentTransformation;
+ // Blur radius is equal to soft edge radius
+ const double fBlurRadius = aRadiusVector.getLength();
+
+ impBufferDevice aBufferDevice(*mpOutputDevice, aRange, false);
+ if (aBufferDevice.isVisible())
+ {
+ // remember last OutDev and set to content
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+ // Since the effect converts all children to bitmap, we can't disable antialiasing here,
+ // because it would result in poor quality in areas not affected by the effect
+ process(rCandidate);
+
+ // Limit the bitmap size to the visible area.
+ basegfx::B2DRange bitmapRange(aRange);
+ if (!aExpandedViewInfo.getDiscreteViewport().isEmpty())
+ bitmapRange.intersect(aExpandedViewInfo.getDiscreteViewport());
+ if (!bitmapRange.isEmpty())
+ {
+ const tools::Rectangle aRect(
+ static_cast<tools::Long>(std::floor(bitmapRange.getMinX())),
+ static_cast<tools::Long>(std::floor(bitmapRange.getMinY())),
+ static_cast<tools::Long>(std::ceil(bitmapRange.getMaxX())),
+ static_cast<tools::Long>(std::ceil(bitmapRange.getMaxY())));
+ BitmapEx bitmap = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize());
+
+ AlphaMask aMask = bitmap.GetAlpha();
+ AlphaMask blurMask = ProcessAndBlurAlphaMask(aMask, -fBlurRadius, fBlurRadius, 0);
+
+ aMask.BlendWith(blurMask);
+
+ // The end result is the original bitmap with blurred 8-bit alpha mask
+ BitmapEx result(bitmap.GetBitmap(), aMask);
+
+ // back to old OutDev
+ mpOutputDevice = pLastOutputDevice;
+ mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
+ }
+ else
+ {
+ mpOutputDevice = pLastOutputDevice;
+ }
+ }
+ else
+ SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
+}
+
+void VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate)
+{
+ if (rCandidate.getShadowBlur() == 0)
+ {
+ process(rCandidate);
+ return;
+ }
+
+ basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
+ aRange.transform(maCurrentTransformation);
+ basegfx::B2DVector aBlurRadiusVector(rCandidate.getShadowBlur(), 0);
+ aBlurRadiusVector *= maCurrentTransformation;
+ const double fBlurRadius = aBlurRadiusVector.getLength();
+
+ impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
+ if (aBufferDevice.isVisible() && !aRange.isEmpty())
+ {
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+
+ process(rCandidate);
+
+ const tools::Rectangle aRect(static_cast<tools::Long>(std::floor(aRange.getMinX())),
+ static_cast<tools::Long>(std::floor(aRange.getMinY())),
+ static_cast<tools::Long>(std::ceil(aRange.getMaxX())),
+ static_cast<tools::Long>(std::ceil(aRange.getMaxY())));
+
+ BitmapEx bitmapEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize());
+
+ AlphaMask mask = ProcessAndBlurAlphaMask(bitmapEx.GetAlpha(), 0, fBlurRadius, 0, false);
+
+ const basegfx::BColor aShadowColor(
+ maBColorModifierStack.getModifiedColor(rCandidate.getShadowColor()));
+
+ Bitmap bitmap = bitmapEx.GetBitmap();
+ bitmap.Erase(Color(aShadowColor));
+ BitmapEx result(bitmap, mask);
+
+ mpOutputDevice = pLastOutputDevice;
+ mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
+ }
+ else
+ SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
+}
+
+void VclPixelProcessor2D::processFillGradientPrimitive2D(
+ const primitive2d::FillGradientPrimitive2D& rPrimitive)
+{
+ const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient();
+
+ // VCL should be able to handle all styles, but for tdf#133477 the VCL result
+ // is different from processing the gradient manually by drawinglayer
+ // (and the Writer unittest for it fails). Keep using the drawinglayer code
+ // until somebody founds out what's wrong and fixes it.
+ if (rFillGradient.getStyle() != drawinglayer::attribute::GradientStyle::Linear
+ && rFillGradient.getStyle() != drawinglayer::attribute::GradientStyle::Axial
+ && rFillGradient.getStyle() != drawinglayer::attribute::GradientStyle::Radial)
+ {
+ process(rPrimitive);
+ return;
+ }
+
+ // tdf#149754 VCL gradient draw is not capable to handle all primitive gradient definitions,
+ // what should be clear due to being developed to ectend/replace them in
+ // capabilities & precision.
+ // It is e.g. not capable to correctly paint if the OutputRange is not completely
+ // inside the DefinitionRange, thus forcing to paint gradent parts *outside* the
+ // DefinitionRange.
+ // This happens for Writer with Frames anchored in Frames (and was broken due to
+ // falling back to VCL Gradient paint here), and for the new SlideBackgroundFill
+ // Fill mode for objects using it that reach outside the page (which is the
+ // DefinitionRange in that case).
+ // I see no real reason to fallback here to OutputDevice::DrawGradient and VCL
+ // gradient paint at all (system-dependent renderers wouldn't in the future), but
+ // will for convenience only add that needed additional correcting case
+ if (!rPrimitive.getDefinitionRange().isInside(rPrimitive.getOutputRange()))
+ {
+ process(rPrimitive);
+ return;
+ }
+
+ GradientStyle eGradientStyle = convertGradientStyle(rFillGradient.getStyle());
+
+ Gradient aGradient(eGradientStyle, Color(rFillGradient.getStartColor()),
+ Color(rFillGradient.getEndColor()));
+
+ aGradient.SetAngle(Degree10(static_cast<int>(basegfx::rad2deg<10>(rFillGradient.getAngle()))));
+ aGradient.SetBorder(rFillGradient.getBorder() * 100);
+ aGradient.SetOfsX(rFillGradient.getOffsetX() * 100.0);
+ aGradient.SetOfsY(rFillGradient.getOffsetY() * 100.0);
+ aGradient.SetSteps(rFillGradient.getSteps());
+
+ basegfx::B2DRange aOutputRange(rPrimitive.getOutputRange());
+ aOutputRange.transform(maCurrentTransformation);
+ basegfx::B2DRange aFullRange(rPrimitive.getDefinitionRange());
+ aFullRange.transform(maCurrentTransformation);
+
+ const tools::Rectangle aOutputRectangle(
+ std::floor(aOutputRange.getMinX()), std::floor(aOutputRange.getMinY()),
+ std::ceil(aOutputRange.getMaxX()), std::ceil(aOutputRange.getMaxY()));
+ const tools::Rectangle aFullRectangle(
+ std::floor(aFullRange.getMinX()), std::floor(aFullRange.getMinY()),
+ std::ceil(aFullRange.getMaxX()), std::ceil(aFullRange.getMaxY()));
+
+ mpOutputDevice->Push(vcl::PushFlags::CLIPREGION);
+ mpOutputDevice->IntersectClipRegion(aOutputRectangle);
+ mpOutputDevice->DrawGradient(aFullRectangle, aGradient);
+ mpOutputDevice->Pop();
+}
+
+void VclPixelProcessor2D::processPatternFillPrimitive2D(
+ const primitive2d::PatternFillPrimitive2D& rPrimitive)
+{
+ const basegfx::B2DRange& rReferenceRange = rPrimitive.getReferenceRange();
+ if (rReferenceRange.isEmpty() || rReferenceRange.getWidth() <= 0.0
+ || rReferenceRange.getHeight() <= 0.0)
+ return;
+
+ basegfx::B2DPolyPolygon aMask = rPrimitive.getMask();
+ aMask.transform(maCurrentTransformation);
+ const basegfx::B2DRange aMaskRange(aMask.getB2DRange());
+
+ if (aMaskRange.isEmpty() || aMaskRange.getWidth() <= 0.0 || aMaskRange.getHeight() <= 0.0)
+ return;
+
+ sal_uInt32 nTileWidth, nTileHeight;
+ rPrimitive.getTileSize(nTileWidth, nTileHeight, getViewInformation2D());
+ if (nTileWidth == 0 || nTileHeight == 0)
+ return;
+ BitmapEx aTileImage = rPrimitive.createTileImage(nTileWidth, nTileHeight);
+ tools::Rectangle aMaskRect = vcl::unotools::rectangleFromB2DRectangle(aMaskRange);
+
+ // Unless smooth edges are needed, simply use clipping.
+ if (basegfx::utils::isRectangle(aMask) || !SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ mpOutputDevice->Push(vcl::PushFlags::CLIPREGION);
+ mpOutputDevice->IntersectClipRegion(vcl::Region(aMask));
+ Wallpaper aWallpaper(aTileImage);
+ aWallpaper.SetColor(COL_TRANSPARENT);
+ mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper);
+ mpOutputDevice->Pop();
+ return;
+ }
+
+ impBufferDevice aBufferDevice(*mpOutputDevice, aMaskRange);
+
+ if (!aBufferDevice.isVisible())
+ return;
+
+ // remember last OutDev and set to content
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+
+ // if the tile is a single pixel big, just flood fill with that pixel color
+ if (nTileWidth == 1 && nTileHeight == 1)
+ {
+ Color col = aTileImage.GetPixelColor(0, 0);
+ mpOutputDevice->SetLineColor(col);
+ mpOutputDevice->SetFillColor(col);
+ mpOutputDevice->DrawRect(aMaskRect);
+ }
+ else
+ {
+ Wallpaper aWallpaper(aTileImage);
+ aWallpaper.SetColor(COL_TRANSPARENT);
+ mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper);
+ }
+
+ // back to old OutDev
+ mpOutputDevice = pLastOutputDevice;
+
+ // draw mask
+ VirtualDevice& rMask = aBufferDevice.getTransparence();
+ rMask.SetLineColor();
+ rMask.SetFillColor(COL_BLACK);
+ rMask.DrawPolyPolygon(aMask);
+
+ // dump buffer to outdev
+ aBufferDevice.paint();
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
new file mode 100644
index 000000000..eaf212c8e
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "vclprocessor2d.hxx"
+#include <vcl/outdev.hxx>
+
+#include <memory>
+
+namespace drawinglayer::primitive2d
+{
+class PolyPolygonColorPrimitive2D;
+class PolygonHairlinePrimitive2D;
+class PolygonStrokePrimitive2D;
+class WrongSpellPrimitive2D;
+class TextSimplePortionPrimitive;
+class BitmapPrimitive2D;
+class PolyPolygonGradientPrimitive2D;
+class UnifiedTransparencePrimitive2D;
+class ControlPrimitive2D;
+class PolygonStrokePrimitive2D;
+class FillHatchPrimitive2D;
+class BackgroundColorPrimitive2D;
+class BorderLinePrimitive2D;
+class GlowPrimitive2D;
+class ShadowPrimitive2D;
+class SoftEdgePrimitive2D;
+class FillGradientPrimitive2D;
+class PatternFillPrimitive2D;
+}
+
+namespace drawinglayer::processor2d
+{
+/** VclPixelProcessor2D class
+
+ This processor derived from VclProcessor2D is the base class for rendering
+ all fed primitives to a VCL Window. It is the currently used renderer
+ for all VCL editing output from the DrawingLayer.
+ */
+class VclPixelProcessor2D final : public VclProcessor2D
+{
+ struct Impl;
+ std::unique_ptr<Impl> m_pImpl;
+
+ /* the local processor for BasePrimitive2D-Implementation based primitives,
+ called from the common process()-implementation
+ */
+ virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) override;
+
+ // some helpers to try direct paints (shortcuts)
+ void tryDrawPolyPolygonColorPrimitive2DDirect(
+ const primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency);
+ bool
+ tryDrawPolygonHairlinePrimitive2DDirect(const primitive2d::PolygonHairlinePrimitive2D& rSource,
+ double fTransparency);
+ bool tryDrawPolygonStrokePrimitive2DDirect(const primitive2d::PolygonStrokePrimitive2D& rSource,
+ double fTransparency);
+
+ void
+ processWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive);
+ void processTextSimplePortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rCandidate);
+ void processTextDecoratedPortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rCandidate);
+ void processPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D);
+ void processBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate);
+ void processPolyPolygonGradientPrimitive2D(
+ const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate);
+ void processPolyPolygonColorPrimitive2D(
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D);
+ void processUnifiedTransparencePrimitive2D(
+ const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate);
+ void processControlPrimitive2D(const primitive2d::ControlPrimitive2D& rControlPrimitive);
+ void processPolygonStrokePrimitive2D(
+ const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D);
+ void processFillHatchPrimitive2D(const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive);
+ void
+ processBackgroundColorPrimitive2D(const primitive2d::BackgroundColorPrimitive2D& rPrimitive);
+ void
+ processBorderLinePrimitive2D(const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder);
+ void processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
+ void processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
+ void processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate);
+ void processSoftEdgePrimitive2D(const primitive2d::SoftEdgePrimitive2D& rCandidate);
+ void processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate);
+ void processFillGradientPrimitive2D(const primitive2d::FillGradientPrimitive2D& rPrimitive);
+ void processPatternFillPrimitive2D(const primitive2d::PatternFillPrimitive2D& rPrimitive);
+
+public:
+ /// constructor/destructor
+ VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev,
+ const basegfx::BColorModifierStack& rInitStack
+ = basegfx::BColorModifierStack());
+ virtual ~VclPixelProcessor2D() override;
+};
+} // end of namespace drawinglayer::processor2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx
new file mode 100644
index 000000000..b77c1d9c0
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -0,0 +1,1480 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vclprocessor2d.hxx"
+
+#include "getdigitlanguage.hxx"
+#include "vclhelperbufferdevice.hxx"
+#include <cmath>
+#include <comphelper/string.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <tools/debug.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/outdev.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textenumsprimitive2d.hxx>
+#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
+// for support of Title/Description in all apps when embedding pictures
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+// control support
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA,
+ const basegfx::BColor& rColorB, double fDelta,
+ double fDiscreteUnit)
+{
+ // use color distance, assume to do every color step
+ sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
+
+ if (nSteps)
+ {
+ // calc discrete length to change color each discrete unit (pixel)
+ const sal_uInt32 nDistSteps(basegfx::fround(fDelta / fDiscreteUnit));
+
+ nSteps = std::min(nSteps, nDistSteps);
+ }
+
+ // reduce quality to 3 discrete units or every 3rd color step for rendering
+ nSteps /= 2;
+
+ // roughly cut when too big or too small (not full quality, reduce complexity)
+ nSteps = std::min(nSteps, sal_uInt32(255));
+ nSteps = std::max(nSteps, sal_uInt32(1));
+
+ return nSteps;
+}
+}
+
+namespace drawinglayer::processor2d
+{
+// rendering support
+
+// directdraw of text simple portion or decorated portion primitive. When decorated, all the extra
+// information is translated to VCL parameters and set at the font.
+// Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring
+// for VCL)
+void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
+ * rTextCandidate.getTextTransform());
+ basegfx::B2DVector aFontScaling, aTranslate;
+ double fRotate, fShearX;
+ aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX);
+ bool bPrimitiveAccepted(false);
+
+ // tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored,
+ // especially if the effect is less than a pixel.
+ if (std::abs(aFontScaling.getY() * fShearX) < 1)
+ {
+ if (basegfx::fTools::less(aFontScaling.getX(), 0.0)
+ && basegfx::fTools::less(aFontScaling.getY(), 0.0))
+ {
+ // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
+ // be expressed as rotation by PI. Use this since the Font rendering will not
+ // apply the negative scales in any form
+ aFontScaling = basegfx::absolute(aFontScaling);
+ fRotate += M_PI;
+ }
+
+ if (basegfx::fTools::more(aFontScaling.getX(), 0.0)
+ && basegfx::fTools::more(aFontScaling.getY(), 0.0))
+ {
+ // Get the VCL font (use FontHeight as FontWidth)
+ vcl::Font aFont(primitive2d::getVclFontFromFontAttribute(
+ rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(),
+ fRotate, rTextCandidate.getLocale()));
+
+ // Don't draw fonts without height
+ if (aFont.GetFontHeight() <= 0)
+ return;
+
+ // set FillColor Attribute
+ const Color aFillColor(rTextCandidate.getTextFillColor());
+ if (aFillColor != COL_TRANSPARENT)
+ {
+ aFont.SetFillColor(aFillColor);
+ aFont.SetTransparent(false);
+ }
+
+ // handle additional font attributes
+ const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = nullptr;
+ if (rTextCandidate.getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
+ pTCPP = static_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>(
+ &rTextCandidate);
+
+ if (pTCPP != nullptr)
+ {
+ // set the color of text decorations
+ const basegfx::BColor aTextlineColor
+ = maBColorModifierStack.getModifiedColor(pTCPP->getTextlineColor());
+ mpOutputDevice->SetTextLineColor(Color(aTextlineColor));
+
+ // set Overline attribute
+ const FontLineStyle eFontOverline(
+ primitive2d::mapTextLineToFontLineStyle(pTCPP->getFontOverline()));
+ if (eFontOverline != LINESTYLE_NONE)
+ {
+ aFont.SetOverline(eFontOverline);
+ const basegfx::BColor aOverlineColor
+ = maBColorModifierStack.getModifiedColor(pTCPP->getOverlineColor());
+ mpOutputDevice->SetOverlineColor(Color(aOverlineColor));
+ if (pTCPP->getWordLineMode())
+ aFont.SetWordLineMode(true);
+ }
+
+ // set Underline attribute
+ const FontLineStyle eFontLineStyle(
+ primitive2d::mapTextLineToFontLineStyle(pTCPP->getFontUnderline()));
+ if (eFontLineStyle != LINESTYLE_NONE)
+ {
+ aFont.SetUnderline(eFontLineStyle);
+ if (pTCPP->getWordLineMode())
+ aFont.SetWordLineMode(true);
+ }
+
+ // set Strikeout attribute
+ const FontStrikeout eFontStrikeout(
+ primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP->getTextStrikeout()));
+
+ if (eFontStrikeout != STRIKEOUT_NONE)
+ aFont.SetStrikeout(eFontStrikeout);
+
+ // set EmphasisMark attribute
+ FontEmphasisMark eFontEmphasisMark = FontEmphasisMark::NONE;
+ switch (pTCPP->getTextEmphasisMark())
+ {
+ default:
+ SAL_WARN("drawinglayer",
+ "Unknown EmphasisMark style " << pTCPP->getTextEmphasisMark());
+ [[fallthrough]];
+ case primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE:
+ eFontEmphasisMark = FontEmphasisMark::NONE;
+ break;
+ case primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT:
+ eFontEmphasisMark = FontEmphasisMark::Dot;
+ break;
+ case primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE:
+ eFontEmphasisMark = FontEmphasisMark::Circle;
+ break;
+ case primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC:
+ eFontEmphasisMark = FontEmphasisMark::Disc;
+ break;
+ case primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT:
+ eFontEmphasisMark = FontEmphasisMark::Accent;
+ break;
+ }
+
+ if (eFontEmphasisMark != FontEmphasisMark::NONE)
+ {
+ DBG_ASSERT((pTCPP->getEmphasisMarkAbove() != pTCPP->getEmphasisMarkBelow()),
+ "DrawingLayer: Bad EmphasisMark position!");
+ if (pTCPP->getEmphasisMarkAbove())
+ eFontEmphasisMark |= FontEmphasisMark::PosAbove;
+ else
+ eFontEmphasisMark |= FontEmphasisMark::PosBelow;
+ aFont.SetEmphasisMark(eFontEmphasisMark);
+ }
+
+ // set Relief attribute
+ FontRelief eFontRelief = FontRelief::NONE;
+ switch (pTCPP->getTextRelief())
+ {
+ default:
+ SAL_WARN("drawinglayer", "Unknown Relief style " << pTCPP->getTextRelief());
+ [[fallthrough]];
+ case primitive2d::TEXT_RELIEF_NONE:
+ eFontRelief = FontRelief::NONE;
+ break;
+ case primitive2d::TEXT_RELIEF_EMBOSSED:
+ eFontRelief = FontRelief::Embossed;
+ break;
+ case primitive2d::TEXT_RELIEF_ENGRAVED:
+ eFontRelief = FontRelief::Engraved;
+ break;
+ }
+
+ if (eFontRelief != FontRelief::NONE)
+ aFont.SetRelief(eFontRelief);
+
+ // set Shadow attribute
+ if (pTCPP->getShadow())
+ aFont.SetShadow(true);
+ }
+
+ // create transformed integer DXArray in view coordinate system
+ std::vector<sal_Int32> aTransformedDXArray;
+
+ if (!rTextCandidate.getDXArray().empty())
+ {
+ aTransformedDXArray.reserve(rTextCandidate.getDXArray().size());
+ const basegfx::B2DVector aPixelVector(maCurrentTransformation
+ * basegfx::B2DVector(1.0, 0.0));
+ const double fPixelVectorFactor(aPixelVector.getLength());
+
+ for (auto const& elem : rTextCandidate.getDXArray())
+ {
+ aTransformedDXArray.push_back(basegfx::fround(elem * fPixelVectorFactor));
+ }
+ }
+
+ // set parameters and paint text snippet
+ const basegfx::BColor aRGBFontColor(
+ maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
+ const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0));
+ const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY()));
+ const vcl::text::ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode());
+
+ if (rTextCandidate.getFontAttribute().getRTL())
+ {
+ vcl::text::ComplexTextLayoutFlags nRTLLayoutMode(
+ nOldLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong);
+ nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl
+ | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+ mpOutputDevice->SetLayoutMode(nRTLLayoutMode);
+ }
+
+ mpOutputDevice->SetFont(aFont);
+ mpOutputDevice->SetTextColor(Color(aRGBFontColor));
+
+ OUString aText(rTextCandidate.getText());
+ sal_Int32 nPos = rTextCandidate.getTextPosition();
+ sal_Int32 nLen = rTextCandidate.getTextLength();
+
+ if (rTextCandidate.isFilled())
+ {
+ basegfx::B2DVector aOldFontScaling, aOldTranslate;
+ double fOldRotate, fOldShearX;
+ rTextCandidate.getTextTransform().decompose(aOldFontScaling, aOldTranslate,
+ fOldRotate, fOldShearX);
+
+ tools::Long nWidthToFill = static_cast<tools::Long>(
+ rTextCandidate.getWidthToFill() * aFontScaling.getX() / aOldFontScaling.getX());
+
+ tools::Long nWidth = mpOutputDevice->GetTextArray(rTextCandidate.getText(),
+ &aTransformedDXArray, 0, 1);
+ sal_Int32 nChars = 2;
+ if (nWidth)
+ nChars = nWidthToFill / nWidth;
+
+ OUStringBuffer aFilled(nChars);
+ comphelper::string::padToLength(aFilled, nChars, aText[0]);
+ aText = aFilled.makeStringAndClear();
+ nPos = 0;
+ nLen = nChars;
+
+ if (!aTransformedDXArray.empty())
+ {
+ sal_Int32 nDX = aTransformedDXArray[0];
+ aTransformedDXArray.resize(nLen);
+ for (sal_Int32 i = 1; i < nLen; ++i)
+ aTransformedDXArray[i] = aTransformedDXArray[i - 1] + nDX;
+ }
+ }
+
+ if (!aTransformedDXArray.empty())
+ {
+ mpOutputDevice->DrawTextArray(aStartPoint, aText, aTransformedDXArray, nPos, nLen);
+ }
+ else
+ {
+ mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen);
+ }
+
+ if (rTextCandidate.getFontAttribute().getRTL())
+ {
+ mpOutputDevice->SetLayoutMode(nOldLayoutMode);
+ }
+
+ bPrimitiveAccepted = true;
+ }
+ }
+
+ if (!bPrimitiveAccepted)
+ {
+ // let break down
+ process(rTextCandidate);
+ }
+}
+
+// direct draw of hairline
+void VclProcessor2D::RenderPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate, bool bPixelBased)
+{
+ const basegfx::BColor aHairlineColor(
+ maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
+ mpOutputDevice->SetLineColor(Color(aHairlineColor));
+ mpOutputDevice->SetFillColor();
+
+ basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon());
+ aLocalPolygon.transform(maCurrentTransformation);
+
+ if (bPixelBased && SvtOptionsDrawinglayer::IsAntiAliasing()
+ && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete())
+ {
+ // #i98289#
+ // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
+ // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
+ // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
+ // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
+ aLocalPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon);
+ }
+
+ mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0);
+}
+
+// direct draw of transformed BitmapEx primitive
+void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
+{
+ BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap()));
+ const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
+ * rBitmapCandidate.getTransform());
+
+ if (maBColorModifierStack.count())
+ {
+ aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
+
+ if (aBitmapEx.IsEmpty())
+ {
+ // color gets completely replaced, get it
+ const basegfx::BColor aModifiedColor(
+ maBColorModifierStack.getModifiedColor(basegfx::BColor()));
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+ aPolygon.transform(aLocalTransform);
+
+ mpOutputDevice->SetFillColor(Color(aModifiedColor));
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->DrawPolygon(aPolygon);
+
+ return;
+ }
+ }
+
+ // #122923# do no longer add Alpha channel here; the right place to do this is when really
+ // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
+
+ // draw using OutputDevice'sDrawTransformedBitmapEx
+ mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx);
+}
+
+void VclProcessor2D::RenderFillGraphicPrimitive2D(
+ const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate)
+{
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute(
+ rFillBitmapCandidate.getFillGraphic());
+ bool bPrimitiveAccepted(false);
+
+ // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
+ // renderer on pixel base to ensure tight fitting. Do not do this when
+ // the fill is rotated or sheared.
+ if (rFillGraphicAttribute.getTiling())
+ {
+ // content is bitmap(ex)
+ //
+ // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use
+ // the primitive representation of the vector data directly.
+ //
+ // when graphic is animated, force decomposition to use the correct graphic, else
+ // fill style will not be animated
+ if (GraphicType::Bitmap == rFillGraphicAttribute.getGraphic().GetType()
+ && !rFillGraphicAttribute.getGraphic().getVectorGraphicData()
+ && !rFillGraphicAttribute.getGraphic().IsAnimated())
+ {
+ // decompose matrix to check for shear, rotate and mirroring
+ basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
+ * rFillBitmapCandidate.getTransformation());
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // when nopt rotated/sheared
+ if (basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX))
+ {
+ // no shear or rotate, draw direct in pixel coordinates
+ bPrimitiveAccepted = true;
+
+ // transform object range to device coordinates (pixels). Use
+ // the device transformation for better accuracy
+ basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale);
+ aObjectRange.transform(mpOutputDevice->GetViewTransformation());
+
+ // extract discrete size of object
+ const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth()));
+ const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight()));
+
+ // only do something when object has a size in discrete units
+ if (nOWidth > 0 && nOHeight > 0)
+ {
+ // transform graphic range to device coordinates (pixels). Use
+ // the device transformation for better accuracy
+ basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
+ aGraphicRange.transform(mpOutputDevice->GetViewTransformation()
+ * aLocalTransform);
+
+ // extract discrete size of graphic
+ // caution: when getting to zero, nothing would be painted; thus, do not allow this
+ const sal_Int32 nBWidth(
+ std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth())));
+ const sal_Int32 nBHeight(
+ std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight())));
+
+ // only do something when bitmap fill has a size in discrete units
+ if (nBWidth > 0 && nBHeight > 0)
+ {
+ // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it
+ // in vcl many times, create a size-optimized version
+ const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
+ BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
+ const bool bPreScaled(nBWidth * nBHeight < (250 * 250));
+
+ // ... but only up to a maximum size, else it gets too expensive
+ if (bPreScaled)
+ {
+ // if color depth is below 24bit, expand before scaling for better quality.
+ // This is even needed for low colors, else the scale will produce
+ // a bitmap in gray or Black/White (!)
+ if (isPalettePixelFormat(aBitmapEx.getPixelFormat()))
+ {
+ aBitmapEx.Convert(BmpConversion::N24Bit);
+ }
+
+ aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate);
+ }
+
+ bool bPainted(false);
+
+ if (maBColorModifierStack.count())
+ {
+ // when color modifier, apply to bitmap
+ aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
+
+ // impModifyBitmapEx uses empty bitmap as sign to return that
+ // the content will be completely replaced to mono color, use shortcut
+ if (aBitmapEx.IsEmpty())
+ {
+ // color gets completely replaced, get it
+ const basegfx::BColor aModifiedColor(
+ maBColorModifierStack.getModifiedColor(basegfx::BColor()));
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+ aPolygon.transform(aLocalTransform);
+
+ mpOutputDevice->SetFillColor(Color(aModifiedColor));
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->DrawPolygon(aPolygon);
+
+ bPainted = true;
+ }
+ }
+
+ if (!bPainted)
+ {
+ sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX()));
+ sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY()));
+ const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX()));
+ const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY()));
+ sal_Int32 nPosX(0);
+ sal_Int32 nPosY(0);
+
+ if (nBLeft > nOLeft)
+ {
+ const sal_Int32 nDiff((nBLeft / nBWidth) + 1);
+
+ nPosX -= nDiff;
+ nBLeft -= nDiff * nBWidth;
+ }
+
+ if (nBLeft + nBWidth <= nOLeft)
+ {
+ const sal_Int32 nDiff(-nBLeft / nBWidth);
+
+ nPosX += nDiff;
+ nBLeft += nDiff * nBWidth;
+ }
+
+ if (nBTop > nOTop)
+ {
+ const sal_Int32 nDiff((nBTop / nBHeight) + 1);
+
+ nPosY -= nDiff;
+ nBTop -= nDiff * nBHeight;
+ }
+
+ if (nBTop + nBHeight <= nOTop)
+ {
+ const sal_Int32 nDiff(-nBTop / nBHeight);
+
+ nPosY += nDiff;
+ nBTop += nDiff * nBHeight;
+ }
+
+ // prepare OutDev
+ const Point aEmptyPoint(0, 0);
+ const ::tools::Rectangle aVisiblePixel(
+ aEmptyPoint, mpOutputDevice->GetOutputSizePixel());
+ const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
+ mpOutputDevice->EnableMapMode(false);
+
+ // check if offset is used
+ const sal_Int32 nOffsetX(
+ basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth));
+
+ if (nOffsetX)
+ {
+ // offset in X, so iterate over Y first and draw lines
+ for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight;
+ nYPos += nBHeight, nPosY++)
+ {
+ for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX
+ : nBLeft);
+ nXPos < nOLeft + nOWidth; nXPos += nBWidth)
+ {
+ const ::tools::Rectangle aOutRectPixel(
+ Point(nXPos, nYPos), aNeededBitmapSizePixel);
+
+ if (aOutRectPixel.Overlaps(aVisiblePixel))
+ {
+ if (bPreScaled)
+ {
+ mpOutputDevice->DrawBitmapEx(
+ aOutRectPixel.TopLeft(), aBitmapEx);
+ }
+ else
+ {
+ mpOutputDevice->DrawBitmapEx(
+ aOutRectPixel.TopLeft(), aNeededBitmapSizePixel,
+ aBitmapEx);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // check if offset is used
+ const sal_Int32 nOffsetY(
+ basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight));
+
+ // possible offset in Y, so iterate over X first and draw columns
+ for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth;
+ nXPos += nBWidth, nPosX++)
+ {
+ for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY
+ : nBTop);
+ nYPos < nOTop + nOHeight; nYPos += nBHeight)
+ {
+ const ::tools::Rectangle aOutRectPixel(
+ Point(nXPos, nYPos), aNeededBitmapSizePixel);
+
+ if (aOutRectPixel.Overlaps(aVisiblePixel))
+ {
+ if (bPreScaled)
+ {
+ mpOutputDevice->DrawBitmapEx(
+ aOutRectPixel.TopLeft(), aBitmapEx);
+ }
+ else
+ {
+ mpOutputDevice->DrawBitmapEx(
+ aOutRectPixel.TopLeft(), aNeededBitmapSizePixel,
+ aBitmapEx);
+ }
+ }
+ }
+ }
+ }
+
+ // restore OutDev
+ mpOutputDevice->EnableMapMode(bWasEnabled);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!bPrimitiveAccepted)
+ {
+ // do not accept, use decomposition
+ process(rFillBitmapCandidate);
+ }
+}
+
+// direct draw of Graphic
+void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(
+ const primitive2d::PolyPolygonGraphicPrimitive2D& rPolygonCandidate)
+{
+ bool bDone(false);
+ const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon();
+
+ // #121194# Todo: check if this works
+ if (!rPolyPolygon.count())
+ {
+ // empty polyPolygon, done
+ bDone = true;
+ }
+ else
+ {
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute
+ = rPolygonCandidate.getFillGraphic();
+
+ // try to catch cases where the graphic will be color-modified to a single
+ // color (e.g. shadow)
+ switch (rFillGraphicAttribute.getGraphic().GetType())
+ {
+ case GraphicType::GdiMetafile:
+ {
+ // metafiles are potentially transparent, cannot optimize, not done
+ break;
+ }
+ case GraphicType::Bitmap:
+ {
+ if (!rFillGraphicAttribute.getGraphic().IsTransparent()
+ && !rFillGraphicAttribute.getGraphic().IsAlpha())
+ {
+ // bitmap is not transparent and has no alpha
+ const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count());
+
+ if (nBColorModifierStackCount)
+ {
+ const basegfx::BColorModifierSharedPtr& rTopmostModifier
+ = maBColorModifierStack.getBColorModifier(nBColorModifierStackCount
+ - 1);
+ const basegfx::BColorModifier_replace* pReplacer
+ = dynamic_cast<const basegfx::BColorModifier_replace*>(
+ rTopmostModifier.get());
+
+ if (pReplacer)
+ {
+ // the bitmap fill is in unified color, so we can replace it with
+ // a single polygon fill. The form of the fill depends on tiling
+ if (rFillGraphicAttribute.getTiling())
+ {
+ // with tiling, fill the whole tools::PolyPolygon with the modifier color
+ basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
+
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
+ mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
+ }
+ else
+ {
+ // without tiling, only the area common to the bitmap tile and the
+ // tools::PolyPolygon is filled. Create the bitmap tile area in object
+ // coordinates. For this, the object transformation needs to be created
+ // from the already scaled PolyPolygon. The tile area in object
+ // coordinates will always be non-rotated, so it's not necessary to
+ // work with a polygon here
+ basegfx::B2DRange aTileRange(
+ rFillGraphicAttribute.getGraphicRange());
+ const basegfx::B2DRange aPolyPolygonRange(
+ rPolyPolygon.getB2DRange());
+ const basegfx::B2DHomMatrix aNewObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aPolyPolygonRange.getRange(),
+ aPolyPolygonRange.getMinimum()));
+
+ aTileRange.transform(aNewObjectTransform);
+
+ // now clip the object polyPolygon against the tile range
+ // to get the common area
+ basegfx::B2DPolyPolygon aTarget
+ = basegfx::utils::clipPolyPolygonOnRange(
+ rPolyPolygon, aTileRange, true, false);
+
+ if (aTarget.count())
+ {
+ aTarget.transform(maCurrentTransformation);
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
+ mpOutputDevice->DrawPolyPolygon(aTarget);
+ }
+ }
+
+ // simplified output executed, we are done
+ bDone = true;
+ }
+ }
+ }
+ break;
+ }
+ default: //GraphicType::NONE, GraphicType::Default
+ {
+ // empty graphic, we are done
+ bDone = true;
+ break;
+ }
+ }
+ }
+
+ if (!bDone)
+ {
+ // use default decomposition
+ process(rPolygonCandidate);
+ }
+}
+
+// mask group
+void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate)
+{
+ if (rMaskCandidate.getChildren().empty())
+ return;
+
+ basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
+
+ if (!aMask.count())
+ return;
+
+ aMask.transform(maCurrentTransformation);
+
+ // Unless smooth edges are needed, simply use clipping.
+ if (basegfx::utils::isRectangle(aMask) || !SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ mpOutputDevice->Push(vcl::PushFlags::CLIPREGION);
+ mpOutputDevice->IntersectClipRegion(vcl::Region(aMask));
+ process(rMaskCandidate.getChildren());
+ mpOutputDevice->Pop();
+ return;
+ }
+
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(aMask));
+ impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
+
+ if (!aBufferDevice.isVisible())
+ return;
+
+ // remember last OutDev and set to content
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+
+ // paint to it
+ process(rMaskCandidate.getChildren());
+
+ // back to old OutDev
+ mpOutputDevice = pLastOutputDevice;
+
+ // draw mask
+ VirtualDevice& rMask = aBufferDevice.getTransparence();
+ rMask.SetLineColor();
+ rMask.SetFillColor(COL_BLACK);
+ rMask.DrawPolyPolygon(aMask);
+
+ // dump buffer to outdev
+ aBufferDevice.paint();
+}
+
+// modified color group. Force output to unified color.
+void VclProcessor2D::RenderModifiedColorPrimitive2D(
+ const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
+{
+ if (!rModifiedCandidate.getChildren().empty())
+ {
+ maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
+ process(rModifiedCandidate.getChildren());
+ maBColorModifierStack.pop();
+ }
+}
+
+// unified sub-transparence. Draw to VDev first.
+void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(
+ const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate)
+{
+ if (rTransCandidate.getChildren().empty())
+ return;
+
+ if (0.0 == rTransCandidate.getTransparence())
+ {
+ // no transparence used, so just use the content
+ process(rTransCandidate.getChildren());
+ }
+ else if (rTransCandidate.getTransparence() > 0.0 && rTransCandidate.getTransparence() < 1.0)
+ {
+ // transparence is in visible range
+ basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
+ aRange.transform(maCurrentTransformation);
+ impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
+
+ if (aBufferDevice.isVisible())
+ {
+ // remember last OutDev and set to content
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+
+ // paint content to it
+ process(rTransCandidate.getChildren());
+
+ // back to old OutDev
+ mpOutputDevice = pLastOutputDevice;
+
+ // dump buffer to outdev using given transparence
+ aBufferDevice.paint(rTransCandidate.getTransparence());
+ }
+ }
+}
+
+// sub-transparence group. Draw to VDev first.
+void VclProcessor2D::RenderTransparencePrimitive2D(
+ const primitive2d::TransparencePrimitive2D& rTransCandidate)
+{
+ if (rTransCandidate.getChildren().empty())
+ return;
+
+ basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
+ aRange.transform(maCurrentTransformation);
+ impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
+
+ if (!aBufferDevice.isVisible())
+ return;
+
+ // remember last OutDev and set to content
+ OutputDevice* pLastOutputDevice = mpOutputDevice;
+ mpOutputDevice = &aBufferDevice.getContent();
+
+ // paint content to it
+ process(rTransCandidate.getChildren());
+
+ // set to mask
+ mpOutputDevice = &aBufferDevice.getTransparence();
+
+ // when painting transparence masks, reset the color stack
+ basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack);
+ maBColorModifierStack = basegfx::BColorModifierStack();
+
+ // paint mask to it (always with transparence intensities, evtl. with AA)
+ process(rTransCandidate.getTransparence());
+
+ // back to old color stack
+ maBColorModifierStack = aLastBColorModifierStack;
+
+ // back to old OutDev
+ mpOutputDevice = pLastOutputDevice;
+
+ // dump buffer to outdev
+ aBufferDevice.paint();
+}
+
+// transform group.
+void VclProcessor2D::RenderTransformPrimitive2D(
+ const primitive2d::TransformPrimitive2D& rTransformCandidate)
+{
+ // remember current transformation and ViewInformation
+ const basegfx::B2DHomMatrix aLastCurrentTransformation(maCurrentTransformation);
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new transformations for CurrentTransformation
+ // and for local ViewInformation2D
+ maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation();
+ const geometry::ViewInformation2D aViewInformation2D(
+ getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
+ getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(),
+ getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
+ updateViewInformation(aViewInformation2D);
+
+ // process content
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ maCurrentTransformation = aLastCurrentTransformation;
+ updateViewInformation(aLastViewInformation2D);
+}
+
+// new XDrawPage for ViewInformation2D
+void VclProcessor2D::RenderPagePreviewPrimitive2D(
+ const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate)
+{
+ // remember current transformation and ViewInformation
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new local ViewInformation2D
+ const geometry::ViewInformation2D aViewInformation2D(
+ getViewInformation2D().getObjectTransformation(),
+ getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(),
+ rPagePreviewCandidate.getXDrawPage(), getViewInformation2D().getViewTime());
+ updateViewInformation(aViewInformation2D);
+
+ // process decomposed content
+ process(rPagePreviewCandidate);
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+}
+
+// marker
+void VclProcessor2D::RenderMarkerArrayPrimitive2D(
+ const primitive2d::MarkerArrayPrimitive2D& rMarkArrayCandidate)
+{
+ // get data
+ const std::vector<basegfx::B2DPoint>& rPositions = rMarkArrayCandidate.getPositions();
+ const sal_uInt32 nCount(rPositions.size());
+
+ if (!nCount || rMarkArrayCandidate.getMarker().IsEmpty())
+ return;
+
+ // get pixel size
+ const BitmapEx& rMarker(rMarkArrayCandidate.getMarker());
+ const Size aBitmapSize(rMarker.GetSizePixel());
+
+ if (!(aBitmapSize.Width() && aBitmapSize.Height()))
+ return;
+
+ // get discrete half size
+ const basegfx::B2DVector aDiscreteHalfSize((aBitmapSize.getWidth() - 1.0) * 0.5,
+ (aBitmapSize.getHeight() - 1.0) * 0.5);
+ const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
+
+ // do not forget evtl. moved origin in target device MapMode when
+ // switching it off; it would be missing and lead to wrong positions.
+ // All his could be done using logic sizes and coordinates, too, but
+ // we want a 1:1 bitmap rendering here, so it's more safe and faster
+ // to work with switching off MapMode usage completely.
+ const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin());
+
+ mpOutputDevice->EnableMapMode(false);
+
+ for (auto const& pos : rPositions)
+ {
+ const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * pos)
+ - aDiscreteHalfSize);
+ const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()),
+ basegfx::fround(aDiscreteTopLeft.getY()));
+
+ mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker);
+ }
+
+ mpOutputDevice->EnableMapMode(bWasEnabled);
+}
+
+// point
+void VclProcessor2D::RenderPointArrayPrimitive2D(
+ const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
+{
+ const std::vector<basegfx::B2DPoint>& rPositions = rPointArrayCandidate.getPositions();
+ const basegfx::BColor aRGBColor(
+ maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
+ const Color aVCLColor(aRGBColor);
+
+ for (auto const& pos : rPositions)
+ {
+ const basegfx::B2DPoint aViewPosition(maCurrentTransformation * pos);
+ const Point aPos(basegfx::fround(aViewPosition.getX()),
+ basegfx::fround(aViewPosition.getY()));
+
+ mpOutputDevice->DrawPixel(aPos, aVCLColor);
+ }
+}
+
+void VclProcessor2D::RenderPolygonStrokePrimitive2D(
+ const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
+{
+ // #i101491# method restructured to clearly use the DrawPolyLine
+ // calls starting from a defined line width
+ const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute();
+ const double fLineWidth(rLineAttribute.getWidth());
+ bool bDone(false);
+
+ if (basegfx::fTools::more(fLineWidth, 0.0))
+ {
+ const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation
+ * basegfx::B2DVector(fLineWidth, 0.0));
+ const double fDiscreteLineWidth(aDiscreteUnit.getLength());
+ const attribute::StrokeAttribute& rStrokeAttribute
+ = rPolygonStrokeCandidate.getStrokeAttribute();
+ const basegfx::BColor aHairlineColor(
+ maBColorModifierStack.getModifiedColor(rLineAttribute.getColor()));
+ basegfx::B2DPolyPolygon aHairlinePolyPolygon;
+
+ mpOutputDevice->SetLineColor(Color(aHairlineColor));
+ mpOutputDevice->SetFillColor();
+
+ if (0.0 == rStrokeAttribute.getFullDotDashLen())
+ {
+ // no line dashing, just copy
+ aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon());
+ }
+ else
+ {
+ // else apply LineStyle
+ basegfx::utils::applyLineDashing(
+ rPolygonStrokeCandidate.getB2DPolygon(), rStrokeAttribute.getDotDashArray(),
+ &aHairlinePolyPolygon, nullptr, rStrokeAttribute.getFullDotDashLen());
+ }
+
+ const sal_uInt32 nCount(aHairlinePolyPolygon.count());
+
+ if (nCount)
+ {
+ const bool bAntiAliased(SvtOptionsDrawinglayer::IsAntiAliasing());
+ aHairlinePolyPolygon.transform(maCurrentTransformation);
+
+ if (bAntiAliased)
+ {
+ if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0))
+ {
+ // line in range ]0.0 .. 1.0[
+ // paint as simple hairline
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
+ }
+
+ bDone = true;
+ }
+ else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0))
+ {
+ // line in range [1.0 .. 2.0[
+ // paint as 2x2 with dynamic line distance
+ basegfx::B2DHomMatrix aMat;
+ const double fDistance(fDiscreteLineWidth - 1.0);
+ const double fHalfDistance(fDistance * 0.5);
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
+
+ aMat.set(0, 2, -fHalfDistance);
+ aMat.set(1, 2, -fHalfDistance);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, fDistance);
+ aMat.set(1, 2, 0.0);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, 0.0);
+ aMat.set(1, 2, fDistance);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, -fDistance);
+ aMat.set(1, 2, 0.0);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+ }
+
+ bDone = true;
+ }
+ else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0))
+ {
+ // line in range [2.0 .. 3.0]
+ // paint as cross in a 3x3 with dynamic line distance
+ basegfx::B2DHomMatrix aMat;
+ const double fDistance((fDiscreteLineWidth - 1.0) * 0.5);
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
+
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, -fDistance);
+ aMat.set(1, 2, 0.0);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, fDistance);
+ aMat.set(1, 2, -fDistance);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, fDistance);
+ aMat.set(1, 2, fDistance);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, -fDistance);
+ aMat.set(1, 2, fDistance);
+ aCandidate.transform(aMat);
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+ }
+
+ bDone = true;
+ }
+ else
+ {
+ // #i101491# line width above 3.0
+ }
+ }
+ else
+ {
+ if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5))
+ {
+ // line width below 1.5, draw the basic hairline polygon
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
+ }
+
+ bDone = true;
+ }
+ else if (basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5))
+ {
+ // line width is in range ]1.5 .. 2.5], use four hairlines
+ // drawn in a square
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
+ basegfx::B2DHomMatrix aMat;
+
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, 1.0);
+ aMat.set(1, 2, 0.0);
+ aCandidate.transform(aMat);
+
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, 0.0);
+ aMat.set(1, 2, 1.0);
+ aCandidate.transform(aMat);
+
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+
+ aMat.set(0, 2, -1.0);
+ aMat.set(1, 2, 0.0);
+ aCandidate.transform(aMat);
+
+ mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
+ }
+
+ bDone = true;
+ }
+ else
+ {
+ // #i101491# line width is above 2.5
+ }
+ }
+
+ if (!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000)
+ {
+ // #i101491# If the polygon complexity uses more than a given amount, do
+ // use OutputDevice::DrawPolyLine directly; this will avoid buffering all
+ // decompositions in primitives (memory) and fallback to old line painting
+ // for very complex polygons, too
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a),
+ fDiscreteLineWidth, rLineAttribute.getLineJoin(),
+ rLineAttribute.getLineCap(),
+ rLineAttribute.getMiterMinimumAngle());
+ }
+
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ {
+ // remember that we enter a PolygonStrokePrimitive2D decomposition,
+ // used for AA thick line drawing
+ mnPolygonStrokePrimitive2D++;
+
+ // line width is big enough for standard filled polygon visualisation or zero
+ process(rPolygonStrokeCandidate);
+
+ // leave PolygonStrokePrimitive2D
+ mnPolygonStrokePrimitive2D--;
+ }
+}
+
+void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D)
+{
+ // The new decomposition of Metafiles made it necessary to add an Eps
+ // primitive to handle embedded Eps data. On some devices, this can be
+ // painted directly (mac, printer).
+ // To be able to handle the replacement correctly, i need to handle it myself
+ // since DrawEPS will not be able e.g. to rotate the replacement. To be able
+ // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
+ // to know when EPS was handled directly already.
+ basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
+ aRange.transform(maCurrentTransformation * rEpsPrimitive2D.getEpsTransform());
+
+ if (aRange.isEmpty())
+ return;
+
+ const ::tools::Rectangle aRectangle(static_cast<sal_Int32>(floor(aRange.getMinX())),
+ static_cast<sal_Int32>(floor(aRange.getMinY())),
+ static_cast<sal_Int32>(ceil(aRange.getMaxX())),
+ static_cast<sal_Int32>(ceil(aRange.getMaxY())));
+
+ if (aRectangle.IsEmpty())
+ return;
+
+ bool bWillReallyRender = mpOutputDevice->IsDeviceOutputNecessary();
+ // try to paint EPS directly without fallback visualisation
+ const bool bEPSPaintedDirectly
+ = bWillReallyRender
+ && mpOutputDevice->DrawEPS(aRectangle.TopLeft(), aRectangle.GetSize(),
+ rEpsPrimitive2D.getGfxLink());
+
+ if (!bEPSPaintedDirectly)
+ {
+ // use the decomposition which will correctly handle the
+ // fallback visualisation using full transformation (e.g. rotation)
+ process(rEpsPrimitive2D);
+ }
+}
+
+void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(
+ const primitive2d::SvgLinearAtomPrimitive2D& rCandidate)
+{
+ const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA());
+
+ if (!basegfx::fTools::more(fDelta, 0.0))
+ return;
+
+ const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
+ const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB()));
+
+ // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
+ const basegfx::B2DVector aDiscreteVector(
+ getViewInformation2D().getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(1.0, 1.0));
+ const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
+
+ // use color distance and discrete lengths to calculate step count
+ const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit));
+
+ // switch off line painting
+ mpOutputDevice->SetLineColor();
+
+ // prepare polygon in needed width at start position (with discrete overlap)
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(rCandidate.getOffsetA() - fDiscreteUnit, 0.0,
+ rCandidate.getOffsetA() + (fDelta / nSteps) + fDiscreteUnit, 1.0)));
+
+ // prepare loop ([0.0 .. 1.0[)
+ double fUnitScale(0.0);
+ const double fUnitStep(1.0 / nSteps);
+
+ // loop and paint
+ for (sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
+ {
+ basegfx::B2DPolygon aNew(aPolygon);
+
+ aNew.transform(maCurrentTransformation
+ * basegfx::utils::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
+ mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorA, aColorB, fUnitScale)));
+ mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
+ }
+}
+
+void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(
+ const primitive2d::SvgRadialAtomPrimitive2D& rCandidate)
+{
+ const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA());
+
+ if (!basegfx::fTools::more(fDeltaScale, 0.0))
+ return;
+
+ const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
+ const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB()));
+
+ // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
+ const basegfx::B2DVector aDiscreteVector(
+ getViewInformation2D().getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(1.0, 1.0));
+ const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
+
+ // use color distance and discrete lengths to calculate step count
+ const sal_uInt32 nSteps(
+ calculateStepsForSvgGradient(aColorA, aColorB, fDeltaScale, fDiscreteUnit));
+
+ // switch off line painting
+ mpOutputDevice->SetLineColor();
+
+ // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
+ double fUnitScale(0.0);
+ const double fUnitStep(1.0 / nSteps);
+
+ for (sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
+ {
+ basegfx::B2DHomMatrix aTransform;
+ const double fEndScale(rCandidate.getScaleB() - (fDeltaScale * fUnitScale));
+
+ if (rCandidate.isTranslateSet())
+ {
+ const basegfx::B2DVector aTranslate(basegfx::interpolate(
+ rCandidate.getTranslateB(), rCandidate.getTranslateA(), fUnitScale));
+
+ aTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fEndScale, fEndScale, aTranslate.getX(), aTranslate.getY());
+ }
+ else
+ {
+ aTransform = basegfx::utils::createScaleB2DHomMatrix(fEndScale, fEndScale);
+ }
+
+ basegfx::B2DPolygon aNew(basegfx::utils::createPolygonFromUnitCircle());
+
+ aNew.transform(maCurrentTransformation * aTransform);
+ mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorB, aColorA, fUnitScale)));
+ mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
+ }
+}
+
+void VclProcessor2D::adaptLineToFillDrawMode() const
+{
+ const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
+
+ if (!(nOriginalDrawMode
+ & (DrawModeFlags::BlackLine | DrawModeFlags::GrayLine | DrawModeFlags::WhiteLine
+ | DrawModeFlags::SettingsLine)))
+ return;
+
+ DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
+
+ if (nOriginalDrawMode & DrawModeFlags::BlackLine)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::BlackFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
+ }
+
+ if (nOriginalDrawMode & DrawModeFlags::GrayLine)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::GrayFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
+ }
+
+ if (nOriginalDrawMode & DrawModeFlags::WhiteLine)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
+ }
+
+ if (nOriginalDrawMode & DrawModeFlags::SettingsLine)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
+ }
+
+ mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
+}
+
+void VclProcessor2D::adaptTextToFillDrawMode() const
+{
+ const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
+ if (!(nOriginalDrawMode
+ & (DrawModeFlags::BlackText | DrawModeFlags::GrayText | DrawModeFlags::WhiteText
+ | DrawModeFlags::SettingsText)))
+ return;
+
+ DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
+
+ if (nOriginalDrawMode & DrawModeFlags::BlackText)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::BlackFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
+ }
+
+ if (nOriginalDrawMode & DrawModeFlags::GrayText)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::GrayFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
+ }
+
+ if (nOriginalDrawMode & DrawModeFlags::WhiteText)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
+ }
+
+ if (nOriginalDrawMode & DrawModeFlags::SettingsText)
+ {
+ nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
+ }
+ else
+ {
+ nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
+ }
+
+ mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
+}
+
+// process support
+
+VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+ OutputDevice& rOutDev,
+ const basegfx::BColorModifierStack& rInitStack)
+ : BaseProcessor2D(rViewInformation)
+ , mpOutputDevice(&rOutDev)
+ , maBColorModifierStack(rInitStack)
+ , mnPolygonStrokePrimitive2D(0)
+{
+ // set digit language, derived from SvtCTLOptions to have the correct
+ // number display for arabic/hindi numerals
+ rOutDev.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
+}
+
+VclProcessor2D::~VclProcessor2D() {}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.hxx b/drawinglayer/source/processor2d/vclprocessor2d.hxx
new file mode 100644
index 000000000..1506f481e
--- /dev/null
+++ b/drawinglayer/source/processor2d/vclprocessor2d.hxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <vcl/vclptr.hxx>
+
+class OutputDevice;
+
+namespace drawinglayer::primitive2d
+{
+class TextSimplePortionPrimitive2D;
+class PolygonHairlinePrimitive2D;
+class BitmapPrimitive2D;
+class FillGraphicPrimitive2D;
+class PolyPolygonGradientPrimitive2D;
+class PolyPolygonGraphicPrimitive2D;
+class MetafilePrimitive2D;
+class MaskPrimitive2D;
+class UnifiedTransparencePrimitive2D;
+class TransparencePrimitive2D;
+class TransformPrimitive2D;
+class MarkerArrayPrimitive2D;
+class PointArrayPrimitive2D;
+class ModifiedColorPrimitive2D;
+class PolygonStrokePrimitive2D;
+class ControlPrimitive2D;
+class PagePreviewPrimitive2D;
+class EpsPrimitive2D;
+class SvgLinearAtomPrimitive2D;
+class SvgRadialAtomPrimitive2D;
+}
+
+namespace drawinglayer::processor2d
+{
+/** VclProcessor2D class
+
+ This processor is the base class for VCL-Based processors. It has no
+ processBasePrimitive2D implementation and thus is not usable directly.
+ */
+class VclProcessor2D : public BaseProcessor2D
+{
+protected:
+ // the destination OutDev
+ VclPtr<OutputDevice> mpOutputDevice;
+
+ // the modifiedColorPrimitive stack
+ basegfx::BColorModifierStack maBColorModifierStack;
+
+ // the current transformation. Since VCL pixel renderer transforms to pixels
+ // and VCL MetaFile renderer to World (logic) coordinates, the local
+ // ViewInformation2D cannot directly be used, but needs to be kept up to date
+ basegfx::B2DHomMatrix maCurrentTransformation;
+
+ // stack value (increment and decrement) to count how deep we are in
+ // PolygonStrokePrimitive2D's decompositions (normally only one)
+ sal_uInt32 mnPolygonStrokePrimitive2D;
+
+ // common VCL rendering support
+ void RenderTextSimpleOrDecoratedPortionPrimitive2D(
+ const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate);
+ void RenderPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate, bool bPixelBased);
+ void RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate);
+ void
+ RenderFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate);
+ void RenderPolyPolygonGraphicPrimitive2D(
+ const primitive2d::PolyPolygonGraphicPrimitive2D& rPolygonCandidate);
+ void RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate);
+ void
+ RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate);
+ void RenderUnifiedTransparencePrimitive2D(
+ const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate);
+ void RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransCandidate);
+ void RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate);
+ void
+ RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate);
+ void
+ RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& rMarkerArrayCandidate);
+ void
+ RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate);
+ void RenderPolygonStrokePrimitive2D(
+ const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate);
+ void RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D);
+ void RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D& rCandidate);
+ void RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D& rCandidate);
+
+ // DrawMode adaptation support
+ void adaptLineToFillDrawMode() const;
+ void adaptTextToFillDrawMode() const;
+
+public:
+ // constructor/destructor
+ VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev,
+ const basegfx::BColorModifierStack& rInitStack = basegfx::BColorModifierStack());
+ virtual ~VclProcessor2D() override;
+};
+} // end of namespace drawinglayer::processor2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor3d/baseprocessor3d.cxx b/drawinglayer/source/processor3d/baseprocessor3d.cxx
new file mode 100644
index 000000000..02d437a1a
--- /dev/null
+++ b/drawinglayer/source/processor3d/baseprocessor3d.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor3d/baseprocessor3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor3d
+{
+ void BaseProcessor3D::processBasePrimitive3D(const primitive3d::BasePrimitive3D& /*rCandidate*/)
+ {
+ }
+
+ BaseProcessor3D::BaseProcessor3D(const geometry::ViewInformation3D& rViewInformation)
+ : maViewInformation3D(rViewInformation)
+ {
+ }
+
+ BaseProcessor3D::~BaseProcessor3D()
+ {
+ }
+
+ void BaseProcessor3D::process(const primitive3d::Primitive3DContainer& rSource)
+ {
+ if(rSource.empty())
+ return;
+
+ const size_t nCount(rSource.size());
+
+ for(size_t a(0); a < nCount; a++)
+ {
+ // get reference
+ const primitive3d::Primitive3DReference xReference(rSource[a]);
+
+ if(xReference.is())
+ {
+ const primitive3d::BasePrimitive3D* pBasePrimitive = static_cast< const primitive3d::BasePrimitive3D* >(xReference.get());
+ processBasePrimitive3D(*pBasePrimitive);
+ }
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor3d/cutfindprocessor3d.cxx b/drawinglayer/source/processor3d/cutfindprocessor3d.cxx
new file mode 100644
index 000000000..cb1f737f5
--- /dev/null
+++ b/drawinglayer/source/processor3d/cutfindprocessor3d.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor3d/cutfindprocessor3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <primitive3d/hatchtextureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <primitive3d/hiddengeometryprimitive3d.hxx>
+
+
+namespace drawinglayer::processor3d
+{
+ CutFindProcessor::CutFindProcessor(const geometry::ViewInformation3D& rViewInformation,
+ const basegfx::B3DPoint& rFront,
+ const basegfx::B3DPoint& rBack,
+ bool bAnyHit)
+ : BaseProcessor3D(rViewInformation),
+ maFront(rFront),
+ maBack(rBack),
+ mbAnyHit(bAnyHit)
+ {
+ }
+
+ void CutFindProcessor::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate)
+ {
+ if(mbAnyHit && !maResult.empty())
+ {
+ // stop processing as soon as a hit was recognized
+ return;
+ }
+
+ // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
+ switch(rCandidate.getPrimitive3DID())
+ {
+ case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D :
+ {
+ // transform group.
+ const primitive3d::TransformPrimitive3D& rPrimitive = static_cast< const primitive3d::TransformPrimitive3D& >(rCandidate);
+
+ // remember old and transform front, back to object coordinates
+ const basegfx::B3DPoint aLastFront(maFront);
+ const basegfx::B3DPoint aLastBack(maBack);
+ basegfx::B3DHomMatrix aInverseTrans(rPrimitive.getTransformation());
+ aInverseTrans.invert();
+ maFront *= aInverseTrans;
+ maBack *= aInverseTrans;
+
+ // remember current and create new transformation; add new object transform from right side
+ const geometry::ViewInformation3D aLastViewInformation3D(getViewInformation3D());
+ const geometry::ViewInformation3D aNewViewInformation3D(
+ aLastViewInformation3D.getObjectTransformation() * rPrimitive.getTransformation(),
+ aLastViewInformation3D.getOrientation(),
+ aLastViewInformation3D.getProjection(),
+ aLastViewInformation3D.getDeviceToView(),
+ aLastViewInformation3D.getViewTime(),
+ aLastViewInformation3D.getExtendedInformationSequence());
+ updateViewInformation(aNewViewInformation3D);
+
+ // #i102956# remember needed back-transform for found cuts (combine from right side)
+ const basegfx::B3DHomMatrix aLastCombinedTransform(maCombinedTransform);
+ maCombinedTransform = maCombinedTransform * rPrimitive.getTransformation();
+
+ // let break down
+ process(rPrimitive.getChildren());
+
+ // restore transformations and front, back
+ maCombinedTransform = aLastCombinedTransform;
+ updateViewInformation(aLastViewInformation3D);
+ maFront = aLastFront;
+ maBack = aLastBack;
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D :
+ {
+ // PolygonHairlinePrimitive3D, not used for hit test with planes, ignore. This
+ // means that also thick line expansion will not be hit-tested as
+ // PolyPolygonMaterialPrimitive3D
+ break;
+ }
+ case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D :
+ {
+ // #i97321#
+ // For HatchTexturePrimitive3D, do not use the decomposition since it will produce
+ // clipped hatch lines in 3D. It can be used when the hatch also has a filling, but for
+ // simplicity, just use the children which are the PolyPolygonMaterialPrimitive3D
+ // which define the hatched areas anyways; for HitTest this is more than adequate
+ const primitive3d::HatchTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::HatchTexturePrimitive3D& >(rCandidate);
+ process(rPrimitive.getChildren());
+ break;
+ }
+ case PRIMITIVE3D_ID_HIDDENGEOMETRYPRIMITIVE3D :
+ {
+ // HiddenGeometryPrimitive3D; the default decomposition would return an empty sequence,
+ // so force this primitive to process its children directly if the switch is set
+ // (which is the default). Else, ignore invisible content
+ const primitive3d::HiddenGeometryPrimitive3D& rHiddenGeometry(static_cast< const primitive3d::HiddenGeometryPrimitive3D& >(rCandidate));
+ const primitive3d::Primitive3DContainer& rChildren = rHiddenGeometry.getChildren();
+
+ if(!rChildren.empty())
+ {
+ process(rChildren);
+ }
+
+ break;
+ }
+ case PRIMITIVE3D_ID_UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D :
+ {
+ const primitive3d::UnifiedTransparenceTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::UnifiedTransparenceTexturePrimitive3D& >(rCandidate);
+ const primitive3d::Primitive3DContainer& rChildren = rPrimitive.getChildren();
+
+ if(!rChildren.empty())
+ {
+ process(rChildren);
+ }
+
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
+ {
+ // PolyPolygonMaterialPrimitive3D
+ const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(rCandidate);
+
+ if(!maFront.equal(maBack))
+ {
+ const basegfx::B3DPolyPolygon& rPolyPolygon = rPrimitive.getB3DPolyPolygon();
+ const sal_uInt32 nPolyCount(rPolyPolygon.count());
+
+ if(nPolyCount)
+ {
+ const basegfx::B3DPolygon& aPolygon(rPolyPolygon.getB3DPolygon(0));
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ if(nPointCount > 2)
+ {
+ const basegfx::B3DVector aPlaneNormal(aPolygon.getNormal());
+
+ if(!aPlaneNormal.equalZero())
+ {
+ const basegfx::B3DPoint aPointOnPlane(aPolygon.getB3DPoint(0));
+ double fCut(0.0);
+
+ if(basegfx::utils::getCutBetweenLineAndPlane(aPlaneNormal, aPointOnPlane, maFront, maBack, fCut))
+ {
+ const basegfx::B3DPoint aCutPoint(basegfx::interpolate(maFront, maBack, fCut));
+
+ if(basegfx::utils::isInside(rPolyPolygon, aCutPoint))
+ {
+ // #i102956# add result. Do not forget to do this in the coordinate
+ // system the processor get started with, so use the collected
+ // combined transformation from processed TransformPrimitive3D's
+ maResult.push_back(maCombinedTransform * aCutPoint);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ default :
+ {
+ // process recursively
+ process(rCandidate.get3DDecomposition(getViewInformation3D()));
+ break;
+ }
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor3d/defaultprocessor3d.cxx b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
new file mode 100644
index 000000000..b9159c46c
--- /dev/null
+++ b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
@@ -0,0 +1,598 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <processor3d/defaultprocessor3d.hxx>
+#include <primitive3d/textureprimitive3d.hxx>
+#include <texture/texture.hxx>
+#include <texture/texture3d.hxx>
+#include <primitive3d/hatchtextureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <vcl/bitmapex.hxx>
+#include <drawinglayer/attribute/sdrsceneattribute3d.hxx>
+#include <drawinglayer/attribute/sdrlightingattribute3d.hxx>
+#include <vcl/graph.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor3d
+{
+ void DefaultProcessor3D::impRenderGradientTexturePrimitive3D(const primitive3d::GradientTexturePrimitive3D& rPrimitive, bool bTransparence)
+ {
+ const primitive3d::Primitive3DContainer& rSubSequence = rPrimitive.getChildren();
+
+ if(rSubSequence.empty())
+ return;
+
+ // rescue values
+ const bool bOldModulate(getModulate()); mbModulate = rPrimitive.getModulate();
+ const bool bOldFilter(getFilter()); mbFilter = rPrimitive.getFilter();
+ const bool bOldSimpleTextureActive(getSimpleTextureActive());
+ std::shared_ptr< texture::GeoTexSvx > pOldTex = bTransparence ? mpTransparenceGeoTexSvx : mpGeoTexSvx;
+
+ // create texture
+ const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getGradient();
+ const basegfx::B2DRange aOutlineRange(0.0, 0.0, rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY());
+ const attribute::GradientStyle aGradientStyle(rFillGradient.getStyle());
+ sal_uInt32 nSteps(rFillGradient.getSteps());
+ const basegfx::BColor& aStart(rFillGradient.getStartColor());
+ const basegfx::BColor& aEnd(rFillGradient.getEndColor());
+ const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5));
+ std::shared_ptr< texture::GeoTexSvx > pNewTex;
+
+ if(nMaxSteps)
+ {
+ // there IS a color distance
+ if(nSteps == 0)
+ {
+ nSteps = nMaxSteps;
+ }
+
+ if(nSteps < 2)
+ {
+ nSteps = 2;
+ }
+
+ if(nSteps > nMaxSteps)
+ {
+ nSteps = nMaxSteps;
+ }
+
+ switch(aGradientStyle)
+ {
+ case attribute::GradientStyle::Linear:
+ {
+ pNewTex = std::make_shared<texture::GeoTexSvxGradientLinear>(
+ aOutlineRange,
+ aOutlineRange,
+ aStart,
+ aEnd,
+ nSteps,
+ rFillGradient.getBorder(),
+ rFillGradient.getAngle());
+ break;
+ }
+ case attribute::GradientStyle::Axial:
+ {
+ pNewTex = std::make_shared<texture::GeoTexSvxGradientAxial>(
+ aOutlineRange,
+ aOutlineRange,
+ aStart,
+ aEnd,
+ nSteps,
+ rFillGradient.getBorder(),
+ rFillGradient.getAngle());
+ break;
+ }
+ case attribute::GradientStyle::Radial:
+ {
+ pNewTex =
+ std::make_shared<texture::GeoTexSvxGradientRadial>(
+ aOutlineRange,
+ aStart,
+ aEnd,
+ nSteps,
+ rFillGradient.getBorder(),
+ rFillGradient.getOffsetX(),
+ rFillGradient.getOffsetY());
+ break;
+ }
+ case attribute::GradientStyle::Elliptical:
+ {
+ pNewTex =
+ std::make_shared<texture::GeoTexSvxGradientElliptical>(
+ aOutlineRange,
+ aStart,
+ aEnd,
+ nSteps,
+ rFillGradient.getBorder(),
+ rFillGradient.getOffsetX(),
+ rFillGradient.getOffsetY(),
+ rFillGradient.getAngle());
+ break;
+ }
+ case attribute::GradientStyle::Square:
+ {
+ pNewTex =
+ std::make_shared<texture::GeoTexSvxGradientSquare>(
+ aOutlineRange,
+ aStart,
+ aEnd,
+ nSteps,
+ rFillGradient.getBorder(),
+ rFillGradient.getOffsetX(),
+ rFillGradient.getOffsetY(),
+ rFillGradient.getAngle());
+ break;
+ }
+ case attribute::GradientStyle::Rect:
+ {
+ pNewTex =
+ std::make_shared<texture::GeoTexSvxGradientRect>(
+ aOutlineRange,
+ aStart,
+ aEnd,
+ nSteps,
+ rFillGradient.getBorder(),
+ rFillGradient.getOffsetX(),
+ rFillGradient.getOffsetY(),
+ rFillGradient.getAngle());
+ break;
+ }
+ }
+
+ mbSimpleTextureActive = false;
+ }
+ else
+ {
+ // no color distance -> same color, use simple texture
+ pNewTex = std::make_shared<texture::GeoTexSvxMono>(aStart, 1.0 - aStart.luminance());
+ mbSimpleTextureActive = true;
+ }
+
+ // set created texture
+ if(bTransparence)
+ {
+ mpTransparenceGeoTexSvx = pNewTex;
+ }
+ else
+ {
+ mpGeoTexSvx = pNewTex;
+ }
+
+ // process sub-list
+ process(rSubSequence);
+
+ // restore values
+ mbModulate = bOldModulate;
+ mbFilter = bOldFilter;
+ mbSimpleTextureActive = bOldSimpleTextureActive;
+
+ if(bTransparence)
+ {
+ mpTransparenceGeoTexSvx = pOldTex;
+ }
+ else
+ {
+ mpGeoTexSvx = pOldTex;
+ }
+ }
+
+ void DefaultProcessor3D::impRenderHatchTexturePrimitive3D(const primitive3d::HatchTexturePrimitive3D& rPrimitive)
+ {
+ const primitive3d::Primitive3DContainer& rSubSequence = rPrimitive.getChildren();
+
+ if(rSubSequence.empty())
+ return;
+
+ // rescue values
+ const bool bOldModulate(getModulate()); mbModulate = rPrimitive.getModulate();
+ const bool bOldFilter(getFilter()); mbFilter = rPrimitive.getFilter();
+ std::shared_ptr< texture::GeoTexSvx > pOldTex = mpGeoTexSvx;
+
+ // calculate logic pixel size in object coordinates. Create transformation view
+ // to object by inverting ObjectToView
+ basegfx::B3DHomMatrix aInvObjectToView(getViewInformation3D().getObjectToView());
+ aInvObjectToView.invert();
+
+ // back-project discrete coordinates to object coordinates and extract
+ // maximum distance
+ const basegfx::B3DPoint aZero(aInvObjectToView * basegfx::B3DPoint(0.0, 0.0, 0.0));
+ const basegfx::B3DPoint aOne(aInvObjectToView * basegfx::B3DPoint(1.0, 1.0, 1.0));
+ const basegfx::B3DVector aLogicPixel(aOne - aZero);
+ double fLogicPixelSizeWorld(std::max(std::max(fabs(aLogicPixel.getX()), fabs(aLogicPixel.getY())), fabs(aLogicPixel.getZ())));
+
+ // calculate logic pixel size in texture coordinates
+ const double fLogicTexSizeX(fLogicPixelSizeWorld / rPrimitive.getTextureSize().getX());
+ const double fLogicTexSizeY(fLogicPixelSizeWorld / rPrimitive.getTextureSize().getY());
+ const double fLogicTexSize(std::max(fLogicTexSizeX, fLogicTexSizeY));
+
+ // create texture and set
+ mpGeoTexSvx = std::make_shared<texture::GeoTexSvxMultiHatch>(rPrimitive, fLogicTexSize);
+
+ // process sub-list
+ process(rSubSequence);
+
+ // restore values
+ mbModulate = bOldModulate;
+ mbFilter = bOldFilter;
+ mpGeoTexSvx = pOldTex;
+ }
+
+ void DefaultProcessor3D::impRenderBitmapTexturePrimitive3D(const primitive3d::BitmapTexturePrimitive3D& rPrimitive)
+ {
+ const primitive3d::Primitive3DContainer& rSubSequence = rPrimitive.getChildren();
+
+ if(rSubSequence.empty())
+ return;
+
+ // rescue values
+ const bool bOldModulate(getModulate()); mbModulate = rPrimitive.getModulate();
+ const bool bOldFilter(getFilter()); mbFilter = rPrimitive.getFilter();
+ std::shared_ptr< texture::GeoTexSvx > pOldTex = mpGeoTexSvx;
+
+ // create texture
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute = rPrimitive.getFillGraphicAttribute();
+
+ // #121194# For 3D texture we will use the BitmapRex representation
+ const BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
+
+ // create range scaled by texture size
+ basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
+
+ aGraphicRange.transform(
+ basegfx::utils::createScaleB2DHomMatrix(
+ rPrimitive.getTextureSize()));
+
+ if(rFillGraphicAttribute.getTiling())
+ {
+ mpGeoTexSvx =
+ std::make_shared<texture::GeoTexSvxBitmapExTiled>(
+ aBitmapEx,
+ aGraphicRange,
+ rFillGraphicAttribute.getOffsetX(),
+ rFillGraphicAttribute.getOffsetY());
+ }
+ else
+ {
+ mpGeoTexSvx =
+ std::make_shared<texture::GeoTexSvxBitmapEx>(
+ aBitmapEx,
+ aGraphicRange);
+ }
+
+ // process sub-list
+ process(rSubSequence);
+
+ // restore values
+ mbModulate = bOldModulate;
+ mbFilter = bOldFilter;
+ mpGeoTexSvx = pOldTex;
+ }
+
+ void DefaultProcessor3D::impRenderModifiedColorPrimitive3D(const primitive3d::ModifiedColorPrimitive3D& rModifiedCandidate)
+ {
+ const primitive3d::Primitive3DContainer& rSubSequence = rModifiedCandidate.getChildren();
+
+ if(!rSubSequence.empty())
+ {
+ // put modifier on stack
+ maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
+
+ // process sub-list
+ process(rModifiedCandidate.getChildren());
+
+ // remove modifier from stack
+ maBColorModifierStack.pop();
+ }
+ }
+
+ void DefaultProcessor3D::impRenderPolygonHairlinePrimitive3D(const primitive3d::PolygonHairlinePrimitive3D& rPrimitive) const
+ {
+ basegfx::B3DPolygon aHairline(rPrimitive.getB3DPolygon());
+
+ if(!aHairline.count())
+ return;
+
+ // hairlines need no extra data, clear it
+ aHairline.clearTextureCoordinates();
+ aHairline.clearNormals();
+ aHairline.clearBColors();
+
+ // transform to device coordinates (-1.0 .. 1.0) and check for visibility
+ aHairline.transform(getViewInformation3D().getObjectToView());
+ const basegfx::B3DRange a3DRange(basegfx::utils::getRange(aHairline));
+ const basegfx::B2DRange a2DRange(a3DRange.getMinX(), a3DRange.getMinY(), a3DRange.getMaxX(), a3DRange.getMaxY());
+
+ if(a2DRange.overlaps(maRasterRange))
+ {
+ const attribute::MaterialAttribute3D aMaterial(maBColorModifierStack.getModifiedColor(rPrimitive.getBColor()));
+
+ rasterconvertB3DPolygon(aMaterial, aHairline);
+ }
+ }
+
+ void DefaultProcessor3D::impRenderPolyPolygonMaterialPrimitive3D(const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive) const
+ {
+ basegfx::B3DPolyPolygon aFill(rPrimitive.getB3DPolyPolygon());
+ basegfx::BColor aObjectColor(rPrimitive.getMaterial().getColor());
+ bool bPaintIt(aFill.count());
+
+ // #i98295# get ShadeMode. Correct early when only flat is possible due to missing normals
+ const css::drawing::ShadeMode aShadeMode(
+ aFill.areNormalsUsed() ?
+ getSdrSceneAttribute().getShadeMode() : css::drawing::ShadeMode_FLAT);
+
+ if(bPaintIt)
+ {
+ // get rid of texture coordinates if there is no texture
+ if(aFill.areTextureCoordinatesUsed() && !getGeoTexSvx() && !getTransparenceGeoTexSvx())
+ {
+ aFill.clearTextureCoordinates();
+ }
+
+ // #i98295# get rid of normals and color early when not needed
+ if(css::drawing::ShadeMode_FLAT == aShadeMode)
+ {
+ aFill.clearNormals();
+ aFill.clearBColors();
+ }
+
+ // transform to device coordinates (-1.0 .. 1.0) and check for visibility
+ aFill.transform(getViewInformation3D().getObjectToView());
+ const basegfx::B3DRange a3DRange(basegfx::utils::getRange(aFill));
+ const basegfx::B2DRange a2DRange(a3DRange.getMinX(), a3DRange.getMinY(), a3DRange.getMaxX(), a3DRange.getMaxY());
+
+ bPaintIt = a2DRange.overlaps(maRasterRange);
+ }
+
+ // check if it shall be painted regarding hiding of normals (backface culling)
+ if(bPaintIt && !rPrimitive.getDoubleSided())
+ {
+ // get plane normal of polygon in view coordinates (with ZBuffer values),
+ // left-handed coordinate system
+ const basegfx::B3DVector aPlaneNormal(aFill.getB3DPolygon(0).getNormal());
+
+ if(aPlaneNormal.getZ() > 0.0)
+ {
+ bPaintIt = false;
+ }
+ }
+
+ if(!bPaintIt)
+ return;
+
+ // prepare ObjectToEye in NormalTransform
+ basegfx::B3DHomMatrix aNormalTransform(getViewInformation3D().getOrientation() * getViewInformation3D().getObjectTransformation());
+
+ if(getSdrSceneAttribute().getTwoSidedLighting())
+ {
+ // get plane normal of polygon in view coordinates (with ZBuffer values),
+ // left-handed coordinate system
+ const basegfx::B3DVector aPlaneNormal(aFill.getB3DPolygon(0).getNormal());
+
+ if(aPlaneNormal.getZ() > 0.0)
+ {
+ // mirror normals
+ aNormalTransform.scale(-1.0, -1.0, -1.0);
+ }
+ }
+
+ switch(aShadeMode)
+ {
+ case css::drawing::ShadeMode_PHONG:
+ {
+ // phong shading. Transform normals to eye coor
+ aFill.transformNormals(aNormalTransform);
+ break;
+ }
+ case css::drawing::ShadeMode_SMOOTH:
+ {
+ // gouraud shading. Transform normals to eye coor
+ aFill.transformNormals(aNormalTransform);
+
+ // prepare color model parameters, evtl. use blend color
+ const basegfx::BColor aColor(getModulate() ? basegfx::BColor(1.0, 1.0, 1.0) : rPrimitive.getMaterial().getColor());
+ const basegfx::BColor& rSpecular(rPrimitive.getMaterial().getSpecular());
+ const basegfx::BColor& rEmission(rPrimitive.getMaterial().getEmission());
+ const sal_uInt16 nSpecularIntensity(rPrimitive.getMaterial().getSpecularIntensity());
+
+ // solve color model for each normal vector, set colors at points. Clear normals.
+ for(sal_uInt32 a(0); a < aFill.count(); a++)
+ {
+ basegfx::B3DPolygon aPartFill(aFill.getB3DPolygon(a));
+
+ for(sal_uInt32 b(0); b < aPartFill.count(); b++)
+ {
+ // solve color model. Transform normal to eye coor
+ const basegfx::B3DVector aNormal(aPartFill.getNormal(b));
+ const basegfx::BColor aSolvedColor(getSdrLightingAttribute().solveColorModel(aNormal, aColor, rSpecular, rEmission, nSpecularIntensity));
+ aPartFill.setBColor(b, aSolvedColor);
+ }
+
+ // clear normals on this part polygon and write it back
+ aPartFill.clearNormals();
+ aFill.setB3DPolygon(a, aPartFill);
+ }
+ break;
+ }
+ case css::drawing::ShadeMode_FLAT:
+ {
+ // flat shading. Get plane vector in eye coordinates
+ const basegfx::B3DVector aPlaneEyeNormal(aNormalTransform * rPrimitive.getB3DPolyPolygon().getB3DPolygon(0).getNormal());
+
+ // prepare color model parameters, evtl. use blend color
+ const basegfx::BColor aColor(getModulate() ? basegfx::BColor(1.0, 1.0, 1.0) : rPrimitive.getMaterial().getColor());
+ const basegfx::BColor& rSpecular(rPrimitive.getMaterial().getSpecular());
+ const basegfx::BColor& rEmission(rPrimitive.getMaterial().getEmission());
+ const sal_uInt16 nSpecularIntensity(rPrimitive.getMaterial().getSpecularIntensity());
+
+ // solve color model for plane vector and use that color for whole plane
+ aObjectColor = getSdrLightingAttribute().solveColorModel(aPlaneEyeNormal, aColor, rSpecular, rEmission, nSpecularIntensity);
+ break;
+ }
+ default: // case css::drawing::ShadeMode_DRAFT:
+ {
+ // draft, just use object color which is already set. Delete all other infos
+ aFill.clearNormals();
+ aFill.clearBColors();
+ break;
+ }
+ }
+
+ // draw it to ZBuffer
+ const attribute::MaterialAttribute3D aMaterial(
+ maBColorModifierStack.getModifiedColor(aObjectColor),
+ rPrimitive.getMaterial().getSpecular(),
+ rPrimitive.getMaterial().getEmission(),
+ rPrimitive.getMaterial().getSpecularIntensity());
+
+ rasterconvertB3DPolyPolygon(aMaterial, aFill);
+ }
+
+ void DefaultProcessor3D::impRenderTransformPrimitive3D(const primitive3d::TransformPrimitive3D& rTransformCandidate)
+ {
+ // transform group. Remember current transformations
+ const geometry::ViewInformation3D aLastViewInformation3D(getViewInformation3D());
+
+ // create new transformation; add new object transform from right side
+ const geometry::ViewInformation3D aNewViewInformation3D(
+ aLastViewInformation3D.getObjectTransformation() * rTransformCandidate.getTransformation(),
+ aLastViewInformation3D.getOrientation(),
+ aLastViewInformation3D.getProjection(),
+ aLastViewInformation3D.getDeviceToView(),
+ aLastViewInformation3D.getViewTime(),
+ aLastViewInformation3D.getExtendedInformationSequence());
+ updateViewInformation(aNewViewInformation3D);
+
+ // let break down recursively
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation3D);
+ }
+
+ void DefaultProcessor3D::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rBasePrimitive)
+ {
+ // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
+ switch(rBasePrimitive.getPrimitive3DID())
+ {
+ case PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D :
+ {
+ // GradientTexturePrimitive3D
+ const primitive3d::GradientTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::GradientTexturePrimitive3D& >(rBasePrimitive);
+ impRenderGradientTexturePrimitive3D(rPrimitive, false);
+ break;
+ }
+ case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D :
+ {
+ // hatchTexturePrimitive3D
+ const primitive3d::HatchTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::HatchTexturePrimitive3D& >(rBasePrimitive);
+ impRenderHatchTexturePrimitive3D(rPrimitive);
+ break;
+ }
+ case PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D :
+ {
+ // BitmapTexturePrimitive3D
+ const primitive3d::BitmapTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::BitmapTexturePrimitive3D& >(rBasePrimitive);
+ impRenderBitmapTexturePrimitive3D(rPrimitive);
+ break;
+ }
+ case PRIMITIVE3D_ID_TRANSPARENCETEXTUREPRIMITIVE3D :
+ {
+ // TransparenceTexturePrimitive3D
+ const primitive3d::TransparenceTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::TransparenceTexturePrimitive3D& >(rBasePrimitive);
+ mnTransparenceCounter++;
+ impRenderGradientTexturePrimitive3D(rPrimitive, true);
+ mnTransparenceCounter--;
+ break;
+ }
+ case PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D :
+ {
+ // ModifiedColorPrimitive3D
+ // Force output to unified color.
+ const primitive3d::ModifiedColorPrimitive3D& rPrimitive = static_cast< const primitive3d::ModifiedColorPrimitive3D& >(rBasePrimitive);
+ impRenderModifiedColorPrimitive3D(rPrimitive);
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D :
+ {
+ // directdraw of PolygonHairlinePrimitive3D
+ const primitive3d::PolygonHairlinePrimitive3D& rPrimitive = static_cast< const primitive3d::PolygonHairlinePrimitive3D& >(rBasePrimitive);
+ impRenderPolygonHairlinePrimitive3D(rPrimitive);
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
+ {
+ // directdraw of PolyPolygonMaterialPrimitive3D
+ const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(rBasePrimitive);
+ impRenderPolyPolygonMaterialPrimitive3D(rPrimitive);
+ break;
+ }
+ case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D :
+ {
+ // transform group (TransformPrimitive3D)
+ impRenderTransformPrimitive3D(static_cast< const primitive3d::TransformPrimitive3D& >(rBasePrimitive));
+ break;
+ }
+ default:
+ {
+ // process recursively
+ process(rBasePrimitive.get3DDecomposition(getViewInformation3D()));
+ break;
+ }
+ }
+ }
+
+ DefaultProcessor3D::DefaultProcessor3D(
+ const geometry::ViewInformation3D& rViewInformation,
+ const attribute::SdrSceneAttribute& rSdrSceneAttribute,
+ const attribute::SdrLightingAttribute& rSdrLightingAttribute)
+ : BaseProcessor3D(rViewInformation),
+ mrSdrSceneAttribute(rSdrSceneAttribute),
+ mrSdrLightingAttribute(rSdrLightingAttribute),
+ maBColorModifierStack(),
+ mnTransparenceCounter(0),
+ mbModulate(false),
+ mbFilter(false),
+ mbSimpleTextureActive(false)
+ {
+ // a derivation has to set maRasterRange which is used in the basic render methods.
+ // Setting to default here ([0.0 .. 1.0] in X,Y) to avoid problems
+ maRasterRange.expand(basegfx::B2DTuple(0.0, 0.0));
+ maRasterRange.expand(basegfx::B2DTuple(1.0, 1.0));
+ }
+
+ DefaultProcessor3D::~DefaultProcessor3D()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor3d/geometry2dextractor.cxx b/drawinglayer/source/processor3d/geometry2dextractor.cxx
new file mode 100644
index 000000000..568496bb9
--- /dev/null
+++ b/drawinglayer/source/processor3d/geometry2dextractor.cxx
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <processor3d/geometry2dextractor.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <primitive3d/textureprimitive3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor3d
+{
+ // as tooling, the process() implementation takes over API handling and calls this
+ // virtual render method when the primitive implementation is BasePrimitive3D-based.
+ void Geometry2DExtractingProcessor::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate)
+ {
+ // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
+ switch(rCandidate.getPrimitive3DID())
+ {
+ case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D :
+ {
+ // transform group. Remember current transformations
+ const primitive3d::TransformPrimitive3D& rPrimitive = static_cast< const primitive3d::TransformPrimitive3D& >(rCandidate);
+ const geometry::ViewInformation3D aLastViewInformation3D(getViewInformation3D());
+
+ // create new transformation; add new object transform from right side
+ const geometry::ViewInformation3D aNewViewInformation3D(
+ aLastViewInformation3D.getObjectTransformation() * rPrimitive.getTransformation(),
+ aLastViewInformation3D.getOrientation(),
+ aLastViewInformation3D.getProjection(),
+ aLastViewInformation3D.getDeviceToView(),
+ aLastViewInformation3D.getViewTime(),
+ aLastViewInformation3D.getExtendedInformationSequence());
+ updateViewInformation(aNewViewInformation3D);
+
+ // let break down recursively
+ process(rPrimitive.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation3D);
+ break;
+ }
+ case PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D :
+ {
+ // ModifiedColorPrimitive3D; push, process and pop
+ const primitive3d::ModifiedColorPrimitive3D& rModifiedCandidate = static_cast< const primitive3d::ModifiedColorPrimitive3D& >(rCandidate);
+ const primitive3d::Primitive3DContainer& rSubSequence = rModifiedCandidate.getChildren();
+
+ if(!rSubSequence.empty())
+ {
+ maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
+ process(rModifiedCandidate.getChildren());
+ maBColorModifierStack.pop();
+ }
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D :
+ {
+ // PolygonHairlinePrimitive3D
+ const primitive3d::PolygonHairlinePrimitive3D& rPrimitive = static_cast< const primitive3d::PolygonHairlinePrimitive3D& >(rCandidate);
+ basegfx::B2DPolygon a2DHairline(basegfx::utils::createB2DPolygonFromB3DPolygon(rPrimitive.getB3DPolygon(), getViewInformation3D().getObjectToView()));
+
+ if(a2DHairline.count())
+ {
+ a2DHairline.transform(getObjectTransformation());
+ const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(rPrimitive.getBColor()));
+ const primitive2d::Primitive2DReference xRef(new primitive2d::PolygonHairlinePrimitive2D(a2DHairline, aModifiedColor));
+ maPrimitive2DSequence.push_back(xRef);
+ }
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
+ {
+ // PolyPolygonMaterialPrimitive3D
+ const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(rCandidate);
+ basegfx::B2DPolyPolygon a2DFill(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(rPrimitive.getB3DPolyPolygon(), getViewInformation3D().getObjectToView()));
+
+ if(a2DFill.count())
+ {
+ a2DFill.transform(getObjectTransformation());
+ const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(rPrimitive.getMaterial().getColor()));
+ const primitive2d::Primitive2DReference xRef(new primitive2d::PolyPolygonColorPrimitive2D(a2DFill, aModifiedColor));
+ maPrimitive2DSequence.push_back(xRef);
+ }
+ break;
+ }
+ case PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D :
+ case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D :
+ case PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D :
+ case PRIMITIVE3D_ID_TRANSPARENCETEXTUREPRIMITIVE3D :
+ case PRIMITIVE3D_ID_UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D :
+ {
+ // TexturePrimitive3D: Process children, do not try to decompose
+ const primitive3d::TexturePrimitive3D& rTexturePrimitive = static_cast< const primitive3d::TexturePrimitive3D& >(rCandidate);
+ const primitive3d::Primitive3DContainer& aChildren(rTexturePrimitive.getChildren());
+
+ if(!aChildren.empty())
+ {
+ process(aChildren);
+ }
+ break;
+ }
+ case PRIMITIVE3D_ID_SHADOWPRIMITIVE3D :
+ {
+ // accept but ignore labels and shadow; these should be extracted separately
+ break;
+ }
+ default :
+ {
+ // process recursively
+ process(rCandidate.get3DDecomposition(getViewInformation3D()));
+ break;
+ }
+ }
+ }
+
+ Geometry2DExtractingProcessor::Geometry2DExtractingProcessor(
+ const geometry::ViewInformation3D& rViewInformation,
+ const basegfx::B2DHomMatrix& rObjectTransformation)
+ : BaseProcessor3D(rViewInformation),
+ maObjectTransformation(rObjectTransformation),
+ maBColorModifierStack()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor3d/shadow3dextractor.cxx b/drawinglayer/source/processor3d/shadow3dextractor.cxx
new file mode 100644
index 000000000..91fc57aae
--- /dev/null
+++ b/drawinglayer/source/processor3d/shadow3dextractor.cxx
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <processor3d/shadow3dextractor.hxx>
+#include <primitive3d/shadowprimitive3d.hxx>
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <rtl/ref.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::processor3d
+{
+ // as tooling, the process() implementation takes over API handling and calls this
+ // virtual render method when the primitive implementation is BasePrimitive3D-based.
+ void Shadow3DExtractingProcessor::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate)
+ {
+ // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
+ switch(rCandidate.getPrimitive3DID())
+ {
+ case PRIMITIVE3D_ID_SHADOWPRIMITIVE3D :
+ {
+ // shadow3d object. Call recursive with content and start conversion
+ const primitive3d::ShadowPrimitive3D& rPrimitive = static_cast< const primitive3d::ShadowPrimitive3D& >(rCandidate);
+
+ // set new target
+ primitive2d::Primitive2DContainer aNewSubList;
+ primitive2d::Primitive2DContainer* pLastTargetSequence = mpPrimitive2DSequence;
+ mpPrimitive2DSequence = &aNewSubList;
+
+ // activate convert
+ const bool bLastConvert(mbConvert);
+ mbConvert = true;
+
+ // set projection flag
+ const bool bLastUseProjection(mbUseProjection);
+ mbUseProjection = rPrimitive.getShadow3D();
+
+ // process content
+ process(rPrimitive.getChildren());
+
+ // restore values
+ mbUseProjection = bLastUseProjection;
+ mbConvert = bLastConvert;
+ mpPrimitive2DSequence = pLastTargetSequence;
+
+ // create 2d shadow primitive with result. This also fetches all entries
+ // from aNewSubList, so there is no need to delete them
+ rtl::Reference<primitive2d::BasePrimitive2D> pNew = new primitive2d::ShadowPrimitive2D(
+ rPrimitive.getShadowTransform(),
+ rPrimitive.getShadowColor(),
+ 0, // shadow3d doesn't have rPrimitive.getShadowBlur() yet.
+ std::move(aNewSubList));
+
+ if(basegfx::fTools::more(rPrimitive.getShadowTransparence(), 0.0))
+ {
+ // create simpleTransparencePrimitive, add created primitives
+ primitive2d::Primitive2DContainer aNewTransPrimitiveVector { pNew };
+
+ pNew = new primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aNewTransPrimitiveVector),
+ rPrimitive.getShadowTransparence());
+ }
+
+ mpPrimitive2DSequence->push_back(pNew);
+
+ break;
+ }
+ case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D :
+ {
+ // transform group. Remember current transformations
+ const primitive3d::TransformPrimitive3D& rPrimitive = static_cast< const primitive3d::TransformPrimitive3D& >(rCandidate);
+ const geometry::ViewInformation3D aLastViewInformation3D(getViewInformation3D());
+
+ // create new transformation; add new object transform from right side
+ const geometry::ViewInformation3D aNewViewInformation3D(
+ aLastViewInformation3D.getObjectTransformation() * rPrimitive.getTransformation(),
+ aLastViewInformation3D.getOrientation(),
+ aLastViewInformation3D.getProjection(),
+ aLastViewInformation3D.getDeviceToView(),
+ aLastViewInformation3D.getViewTime(),
+ aLastViewInformation3D.getExtendedInformationSequence());
+ updateViewInformation(aNewViewInformation3D);
+
+ if(mbShadowProjectionIsValid)
+ {
+ // update buffered WorldToEye and EyeToView
+ maWorldToEye = getViewInformation3D().getOrientation() * getViewInformation3D().getObjectTransformation();
+ maEyeToView = getViewInformation3D().getDeviceToView() * getViewInformation3D().getProjection();
+ }
+
+ // let break down
+ process(rPrimitive.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation3D);
+
+ if(mbShadowProjectionIsValid)
+ {
+ // update buffered WorldToEye and EyeToView
+ maWorldToEye = getViewInformation3D().getOrientation() * getViewInformation3D().getObjectTransformation();
+ maEyeToView = getViewInformation3D().getDeviceToView() * getViewInformation3D().getProjection();
+ }
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D :
+ {
+ // PolygonHairlinePrimitive3D
+ if(mbConvert)
+ {
+ const primitive3d::PolygonHairlinePrimitive3D& rPrimitive = static_cast< const primitive3d::PolygonHairlinePrimitive3D& >(rCandidate);
+ basegfx::B2DPolygon a2DHairline;
+
+ if(mbUseProjection)
+ {
+ if(mbShadowProjectionIsValid)
+ {
+ a2DHairline = impDoShadowProjection(rPrimitive.getB3DPolygon());
+ }
+ }
+ else
+ {
+ a2DHairline = basegfx::utils::createB2DPolygonFromB3DPolygon(rPrimitive.getB3DPolygon(), getViewInformation3D().getObjectToView());
+ }
+
+ if(a2DHairline.count())
+ {
+ a2DHairline.transform(getObjectTransformation());
+ mpPrimitive2DSequence->push_back(
+ new primitive2d::PolygonHairlinePrimitive2D(
+ a2DHairline,
+ basegfx::BColor()));
+ }
+ }
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
+ {
+ // PolyPolygonMaterialPrimitive3D
+ if(mbConvert)
+ {
+ const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(rCandidate);
+ basegfx::B2DPolyPolygon a2DFill;
+
+ if(mbUseProjection)
+ {
+ if(mbShadowProjectionIsValid)
+ {
+ a2DFill = impDoShadowProjection(rPrimitive.getB3DPolyPolygon());
+ }
+ }
+ else
+ {
+ a2DFill = basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(rPrimitive.getB3DPolyPolygon(), getViewInformation3D().getObjectToView());
+ }
+
+ if(a2DFill.count())
+ {
+ a2DFill.transform(getObjectTransformation());
+ mpPrimitive2DSequence->push_back(
+ new primitive2d::PolyPolygonColorPrimitive2D(
+ a2DFill,
+ basegfx::BColor()));
+ }
+ }
+ break;
+ }
+ default :
+ {
+ // process recursively
+ process(rCandidate.get3DDecomposition(getViewInformation3D()));
+ break;
+ }
+ }
+ }
+
+ Shadow3DExtractingProcessor::Shadow3DExtractingProcessor(
+ const geometry::ViewInformation3D& rViewInformation,
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ const basegfx::B3DVector& rLightNormal,
+ double fShadowSlant,
+ const basegfx::B3DRange& rContained3DRange)
+ : BaseProcessor3D(rViewInformation),
+ mpPrimitive2DSequence(&maPrimitive2DSequence),
+ maObjectTransformation(rObjectTransformation),
+ maLightNormal(rLightNormal),
+ mfLightPlaneScalar(0.0),
+ mbShadowProjectionIsValid(false),
+ mbConvert(false),
+ mbUseProjection(false)
+ {
+ // normalize light normal, get and normalize shadow plane normal and calculate scalar from it
+ maLightNormal.normalize();
+ maShadowPlaneNormal = basegfx::B3DVector(0.0, sin(fShadowSlant), cos(fShadowSlant));
+ maShadowPlaneNormal.normalize();
+ mfLightPlaneScalar = maLightNormal.scalar(maShadowPlaneNormal);
+
+ // use only when scalar is > 0.0, so the light is in front of the object
+ if(!basegfx::fTools::more(mfLightPlaneScalar, 0.0))
+ return;
+
+ // prepare buffered WorldToEye and EyeToView
+ maWorldToEye = getViewInformation3D().getOrientation() * getViewInformation3D().getObjectTransformation();
+ maEyeToView = getViewInformation3D().getDeviceToView() * getViewInformation3D().getProjection();
+
+ // calculate range to get front edge around which to rotate the shadow's projection
+ basegfx::B3DRange aContained3DRange(rContained3DRange);
+ aContained3DRange.transform(getWorldToEye());
+ maPlanePoint.setX(maShadowPlaneNormal.getX() < 0.0 ? aContained3DRange.getMinX() : aContained3DRange.getMaxX());
+ maPlanePoint.setY(maShadowPlaneNormal.getY() > 0.0 ? aContained3DRange.getMinY() : aContained3DRange.getMaxY());
+ maPlanePoint.setZ(aContained3DRange.getMinZ() - (aContained3DRange.getDepth() / 8.0));
+
+ // set flag that shadow projection is prepared and allowed
+ mbShadowProjectionIsValid = true;
+ }
+
+ Shadow3DExtractingProcessor::~Shadow3DExtractingProcessor()
+ {
+ OSL_ENSURE(maPrimitive2DSequence.empty(),
+ "OOps, someone used Shadow3DExtractingProcessor, but did not fetch the results (!)");
+ }
+
+ basegfx::B2DPolygon Shadow3DExtractingProcessor::impDoShadowProjection(const basegfx::B3DPolygon& rSource)
+ {
+ basegfx::B2DPolygon aRetval;
+
+ for(sal_uInt32 a(0); a < rSource.count(); a++)
+ {
+ // get point, transform to eye coordinate system
+ basegfx::B3DPoint aCandidate(rSource.getB3DPoint(a));
+ aCandidate *= getWorldToEye();
+
+ // we are in eye coordinates
+ // ray is (aCandidate + fCut * maLightNormal)
+ // plane is (maPlanePoint, maShadowPlaneNormal)
+ // maLightNormal.scalar(maShadowPlaneNormal) is already in mfLightPlaneScalar and > 0.0
+ // get cut point of ray with shadow plane
+ const double fCut(basegfx::B3DVector(maPlanePoint - aCandidate).scalar(maShadowPlaneNormal) / mfLightPlaneScalar);
+ aCandidate += maLightNormal * fCut;
+
+ // transform to view, use 2d coordinates
+ aCandidate *= maEyeToView;
+ aRetval.append(basegfx::B2DPoint(aCandidate.getX(), aCandidate.getY()));
+ }
+
+ // copy closed flag
+ aRetval.setClosed(rSource.isClosed());
+
+ return aRetval;
+ }
+
+ basegfx::B2DPolyPolygon Shadow3DExtractingProcessor::impDoShadowProjection(const basegfx::B3DPolyPolygon& rSource)
+ {
+ basegfx::B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0); a < rSource.count(); a++)
+ {
+ aRetval.append(impDoShadowProjection(rSource.getB3DPolygon(a)));
+ }
+
+ return aRetval;
+ }
+
+ const primitive2d::Primitive2DContainer& Shadow3DExtractingProcessor::getPrimitive2DSequence() const
+ {
+ return maPrimitive2DSequence;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor3d/zbufferprocessor3d.cxx b/drawinglayer/source/processor3d/zbufferprocessor3d.cxx
new file mode 100644
index 000000000..aa319161b
--- /dev/null
+++ b/drawinglayer/source/processor3d/zbufferprocessor3d.cxx
@@ -0,0 +1,638 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <processor3d/zbufferprocessor3d.hxx>
+#include <basegfx/raster/bzpixelraster.hxx>
+#include <basegfx/raster/rasterconvert3d.hxx>
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+#include <texture/texture.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/attribute/sdrlightingattribute3d.hxx>
+#include <o3tl/safeint.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+using namespace com::sun::star;
+
+class ZBufferRasterConverter3D : public basegfx::RasterConverter3D
+{
+private:
+ const drawinglayer::processor3d::DefaultProcessor3D& mrProcessor;
+ basegfx::BZPixelRaster& mrBuffer;
+
+ // interpolators for a single line span
+ basegfx::ip_single maIntZ;
+ basegfx::ip_triple maIntColor;
+ basegfx::ip_triple maIntNormal;
+ basegfx::ip_double maIntTexture;
+ basegfx::ip_triple maIntInvTexture;
+
+ // current material to use for rasterconversion
+ const drawinglayer::attribute::MaterialAttribute3D* mpCurrentMaterial;
+
+ // some boolean flags for line span interpolator usages
+ bool mbModifyColor : 1;
+ bool mbUseTex : 1;
+ bool mbHasTexCoor : 1;
+ bool mbHasInvTexCoor : 1;
+ bool mbUseNrm : 1;
+ bool mbUseCol : 1;
+
+ void getTextureCoor(basegfx::B2DPoint& rTarget) const
+ {
+ if(mbHasTexCoor)
+ {
+ rTarget.setX(maIntTexture.getX().getVal());
+ rTarget.setY(maIntTexture.getY().getVal());
+ }
+ else if(mbHasInvTexCoor)
+ {
+ const double fZFactor(maIntInvTexture.getZ().getVal());
+ const double fInvZFactor(basegfx::fTools::equalZero(fZFactor) ? 1.0 : 1.0 / fZFactor);
+ rTarget.setX(maIntInvTexture.getX().getVal() * fInvZFactor);
+ rTarget.setY(maIntInvTexture.getY().getVal() * fInvZFactor);
+ }
+ }
+
+ void incrementLineSpanInterpolators(double fStep)
+ {
+ maIntZ.increment(fStep);
+
+ if(mbUseTex)
+ {
+ if(mbHasTexCoor)
+ {
+ maIntTexture.increment(fStep);
+ }
+ else if(mbHasInvTexCoor)
+ {
+ maIntInvTexture.increment(fStep);
+ }
+ }
+
+ if(mbUseNrm)
+ {
+ maIntNormal.increment(fStep);
+ }
+
+ if(mbUseCol)
+ {
+ maIntColor.increment(fStep);
+ }
+ }
+
+ double decideColorAndOpacity(basegfx::BColor& rColor) const
+ {
+ // init values with full opacity and material color
+ OSL_ENSURE(nullptr != mpCurrentMaterial, "CurrentMaterial not set (!)");
+ double fOpacity(1.0);
+ rColor = mpCurrentMaterial->getColor();
+
+ if(mbUseTex)
+ {
+ basegfx::B2DPoint aTexCoor(0.0, 0.0);
+ getTextureCoor(aTexCoor);
+
+ if(mrProcessor.getGeoTexSvx())
+ {
+ // calc color in spot. This may also set to invisible already when
+ // e.g. bitmap textures have transparent parts
+ mrProcessor.getGeoTexSvx()->modifyBColor(aTexCoor, rColor, fOpacity);
+ }
+
+ if(basegfx::fTools::more(fOpacity, 0.0) && mrProcessor.getTransparenceGeoTexSvx())
+ {
+ // calc opacity. Object has a 2nd texture, a transparence texture
+ mrProcessor.getTransparenceGeoTexSvx()->modifyOpacity(aTexCoor, fOpacity);
+ }
+ }
+
+ if(basegfx::fTools::more(fOpacity, 0.0))
+ {
+ if(mrProcessor.getGeoTexSvx())
+ {
+ if(mbUseNrm)
+ {
+ // blend texture with phong
+ rColor = mrProcessor.getSdrLightingAttribute().solveColorModel(
+ basegfx::B3DVector(maIntNormal.getX().getVal(), maIntNormal.getY().getVal(), maIntNormal.getZ().getVal()),
+ rColor,
+ mpCurrentMaterial->getSpecular(),
+ mpCurrentMaterial->getEmission(),
+ mpCurrentMaterial->getSpecularIntensity());
+ }
+ else if(mbUseCol)
+ {
+ // blend texture with gouraud
+ basegfx::BColor aBlendColor(maIntColor.getX().getVal(), maIntColor.getY().getVal(), maIntColor.getZ().getVal());
+ rColor *= aBlendColor;
+ }
+ else if(mrProcessor.getModulate())
+ {
+ // blend texture with single material color
+ rColor *= mpCurrentMaterial->getColor();
+ }
+ }
+ else
+ {
+ if(mbUseNrm)
+ {
+ // modify color with phong
+ rColor = mrProcessor.getSdrLightingAttribute().solveColorModel(
+ basegfx::B3DVector(maIntNormal.getX().getVal(), maIntNormal.getY().getVal(), maIntNormal.getZ().getVal()),
+ rColor,
+ mpCurrentMaterial->getSpecular(),
+ mpCurrentMaterial->getEmission(),
+ mpCurrentMaterial->getSpecularIntensity());
+ }
+ else if(mbUseCol)
+ {
+ // modify color with gouraud
+ rColor.setRed(maIntColor.getX().getVal());
+ rColor.setGreen(maIntColor.getY().getVal());
+ rColor.setBlue(maIntColor.getZ().getVal());
+ }
+ }
+
+ if(mbModifyColor)
+ {
+ rColor = mrProcessor.getBColorModifierStack().getModifiedColor(rColor);
+ }
+ }
+
+ return fOpacity;
+ }
+
+ void setupLineSpanInterpolators(const basegfx::RasterConversionLineEntry3D& rA, const basegfx::RasterConversionLineEntry3D& rB)
+ {
+ // get inverse XDelta
+ const double xInvDelta(1.0 / (rB.getX().getVal() - rA.getX().getVal()));
+
+ // prepare Z-interpolator
+ const double fZA(rA.getZ().getVal());
+ const double fZB(rB.getZ().getVal());
+ maIntZ = basegfx::ip_single(fZA, (fZB - fZA) * xInvDelta);
+
+ // get bools and init other interpolators on demand accordingly
+ mbModifyColor = mrProcessor.getBColorModifierStack().count();
+ mbHasTexCoor = SCANLINE_EMPTY_INDEX != rA.getTextureIndex() && SCANLINE_EMPTY_INDEX != rB.getTextureIndex();
+ mbHasInvTexCoor = SCANLINE_EMPTY_INDEX != rA.getInverseTextureIndex() && SCANLINE_EMPTY_INDEX != rB.getInverseTextureIndex();
+ const bool bTextureActive(mrProcessor.getGeoTexSvx() || mrProcessor.getTransparenceGeoTexSvx());
+ mbUseTex = bTextureActive && (mbHasTexCoor || mbHasInvTexCoor || mrProcessor.getSimpleTextureActive());
+ const bool bUseColorTex(mbUseTex && mrProcessor.getGeoTexSvx());
+ const bool bNeedNrmOrCol(!bUseColorTex || mrProcessor.getModulate());
+ mbUseNrm = bNeedNrmOrCol && SCANLINE_EMPTY_INDEX != rA.getNormalIndex() && SCANLINE_EMPTY_INDEX != rB.getNormalIndex();
+ mbUseCol = !mbUseNrm && bNeedNrmOrCol && SCANLINE_EMPTY_INDEX != rA.getColorIndex() && SCANLINE_EMPTY_INDEX != rB.getColorIndex();
+
+ if(mbUseTex)
+ {
+ if(mbHasTexCoor)
+ {
+ const basegfx::ip_double& rTA(getTextureInterpolators()[rA.getTextureIndex()]);
+ const basegfx::ip_double& rTB(getTextureInterpolators()[rB.getTextureIndex()]);
+ maIntTexture = basegfx::ip_double(
+ rTA.getX().getVal(), (rTB.getX().getVal() - rTA.getX().getVal()) * xInvDelta,
+ rTA.getY().getVal(), (rTB.getY().getVal() - rTA.getY().getVal()) * xInvDelta);
+ }
+ else if(mbHasInvTexCoor)
+ {
+ const basegfx::ip_triple& rITA(getInverseTextureInterpolators()[rA.getInverseTextureIndex()]);
+ const basegfx::ip_triple& rITB(getInverseTextureInterpolators()[rB.getInverseTextureIndex()]);
+ maIntInvTexture = basegfx::ip_triple(
+ rITA.getX().getVal(), (rITB.getX().getVal() - rITA.getX().getVal()) * xInvDelta,
+ rITA.getY().getVal(), (rITB.getY().getVal() - rITA.getY().getVal()) * xInvDelta,
+ rITA.getZ().getVal(), (rITB.getZ().getVal() - rITA.getZ().getVal()) * xInvDelta);
+ }
+ }
+
+ if(mbUseNrm)
+ {
+ const basegfx::ip_triple& rNA(getNormalInterpolators()[rA.getNormalIndex()]);
+ const basegfx::ip_triple& rNB(getNormalInterpolators()[rB.getNormalIndex()]);
+ maIntNormal = basegfx::ip_triple(
+ rNA.getX().getVal(), (rNB.getX().getVal() - rNA.getX().getVal()) * xInvDelta,
+ rNA.getY().getVal(), (rNB.getY().getVal() - rNA.getY().getVal()) * xInvDelta,
+ rNA.getZ().getVal(), (rNB.getZ().getVal() - rNA.getZ().getVal()) * xInvDelta);
+ }
+
+ if(mbUseCol)
+ {
+ const basegfx::ip_triple& rCA(getColorInterpolators()[rA.getColorIndex()]);
+ const basegfx::ip_triple& rCB(getColorInterpolators()[rB.getColorIndex()]);
+ maIntColor = basegfx::ip_triple(
+ rCA.getX().getVal(), (rCB.getX().getVal() - rCA.getX().getVal()) * xInvDelta,
+ rCA.getY().getVal(), (rCB.getY().getVal() - rCA.getY().getVal()) * xInvDelta,
+ rCA.getZ().getVal(), (rCB.getZ().getVal() - rCA.getZ().getVal()) * xInvDelta);
+ }
+ }
+
+ virtual void processLineSpan(const basegfx::RasterConversionLineEntry3D& rA, const basegfx::RasterConversionLineEntry3D& rB, sal_Int32 nLine, sal_uInt32 nSpanCount) override;
+
+public:
+ ZBufferRasterConverter3D(basegfx::BZPixelRaster& rBuffer, const drawinglayer::processor3d::ZBufferProcessor3D& rProcessor)
+ : mrProcessor(rProcessor),
+ mrBuffer(rBuffer),
+ mpCurrentMaterial(nullptr),
+ mbModifyColor(false),
+ mbUseTex(false),
+ mbHasTexCoor(false),
+ mbHasInvTexCoor(false),
+ mbUseNrm(false),
+ mbUseCol(false)
+ {}
+
+ void setCurrentMaterial(const drawinglayer::attribute::MaterialAttribute3D& rMaterial)
+ {
+ mpCurrentMaterial = &rMaterial;
+ }
+};
+
+void ZBufferRasterConverter3D::processLineSpan(const basegfx::RasterConversionLineEntry3D& rA, const basegfx::RasterConversionLineEntry3D& rB, sal_Int32 nLine, sal_uInt32 nSpanCount)
+{
+ if(nSpanCount & 0x0001)
+ return;
+
+ if(nLine < 0 || o3tl::make_unsigned(nLine) >= mrBuffer.getHeight())
+ return;
+
+ sal_uInt32 nXA(std::min(mrBuffer.getWidth(), static_cast<sal_uInt32>(std::max(sal_Int32(0), basegfx::fround(rA.getX().getVal())))));
+ const sal_uInt32 nXB(std::min(mrBuffer.getWidth(), static_cast<sal_uInt32>(std::max(sal_Int32(0), basegfx::fround(rB.getX().getVal())))));
+
+ if(nXA >= nXB)
+ return;
+
+ // prepare the span interpolators
+ setupLineSpanInterpolators(rA, rB);
+
+ // bring span interpolators to start condition by incrementing with the possible difference of
+ // clamped and non-clamped XStart. Interpolators are setup relying on double precision
+ // X-values, so that difference is the correct value to compensate for possible clampings
+ incrementLineSpanInterpolators(static_cast<double>(nXA) - rA.getX().getVal());
+
+ // prepare scanline index
+ sal_uInt32 nScanlineIndex(mrBuffer.getIndexFromXY(nXA, static_cast<sal_uInt32>(nLine)));
+ basegfx::BColor aNewColor;
+
+ while(nXA < nXB)
+ {
+ // early-test Z values if we need to do anything at all
+ const double fNewZ(std::clamp(maIntZ.getVal(), 0.0, 65535.0));
+ const sal_uInt16 nNewZ(static_cast< sal_uInt16 >(fNewZ));
+ sal_uInt16& rOldZ(mrBuffer.getZ(nScanlineIndex));
+
+ if(nNewZ > rOldZ)
+ {
+ // detect color and opacity for this pixel
+ const sal_uInt16 nOpacity(std::max(sal_Int16(0), static_cast< sal_Int16 >(decideColorAndOpacity(aNewColor) * 255.0)));
+
+ if(nOpacity > 0)
+ {
+ // avoid color overrun
+ aNewColor.clamp();
+
+ if(nOpacity >= 0x00ff)
+ {
+ // full opacity (not transparent), set z and color
+ rOldZ = nNewZ;
+ mrBuffer.getBPixel(nScanlineIndex) = basegfx::BPixel(aNewColor, 0xff);
+ }
+ else
+ {
+ basegfx::BPixel& rDest = mrBuffer.getBPixel(nScanlineIndex);
+
+ if(rDest.getAlpha())
+ {
+ // mix new color by using
+ // color' = color * (1 - opacity) + newcolor * opacity
+ const sal_uInt16 nTransparence(255 - nOpacity);
+ rDest.setRed(static_cast<sal_uInt8>(((rDest.getRed() * nTransparence) + (static_cast<sal_uInt16>(255.0 * aNewColor.getRed()) * nOpacity)) >> 8));
+ rDest.setGreen(static_cast<sal_uInt8>(((rDest.getGreen() * nTransparence) + (static_cast<sal_uInt16>(255.0 * aNewColor.getGreen()) * nOpacity)) >> 8));
+ rDest.setBlue(static_cast<sal_uInt8>(((rDest.getBlue() * nTransparence) + (static_cast<sal_uInt16>(255.0 * aNewColor.getBlue()) * nOpacity)) >> 8));
+
+ if(255 != rDest.getAlpha())
+ {
+ // both are transparent, mix new opacity by using
+ // opacity = newopacity * (1 - oldopacity) + oldopacity
+ rDest.setAlpha(static_cast<sal_uInt8>((nOpacity * (255 - rDest.getAlpha())) >> 8) + rDest.getAlpha());
+ }
+ }
+ else
+ {
+ // dest is unused, set color
+ rDest = basegfx::BPixel(aNewColor, static_cast<sal_uInt8>(nOpacity));
+ }
+ }
+ }
+ }
+
+ // increments
+ nScanlineIndex++;
+ nXA++;
+ incrementLineSpanInterpolators(1.0);
+ }
+}
+
+// helper class to buffer output for transparent rasterprimitives (filled areas
+// and lines) until the end of processing. To ensure correct transparent
+// visualisation, ZBuffers require to not set Z and to mix with the transparent
+// color. If transparent rasterprimitives overlap, it gets necessary to
+// paint transparent rasterprimitives from back to front to ensure that the
+// mixing happens from back to front. For that purpose, transparent
+// rasterprimitives are held in this class during the processing run, remember
+// all data and will be rendered
+
+class RasterPrimitive3D
+{
+private:
+ std::shared_ptr< drawinglayer::texture::GeoTexSvx > mpGeoTexSvx;
+ std::shared_ptr< drawinglayer::texture::GeoTexSvx > mpTransparenceGeoTexSvx;
+ drawinglayer::attribute::MaterialAttribute3D maMaterial;
+ basegfx::B3DPolyPolygon maPolyPolygon;
+ double mfCenterZ;
+
+ bool mbModulate : 1;
+ bool mbFilter : 1;
+ bool mbSimpleTextureActive : 1;
+ bool mbIsLine : 1;
+
+public:
+ RasterPrimitive3D(
+ const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& pGeoTexSvx,
+ const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& pTransparenceGeoTexSvx,
+ const drawinglayer::attribute::MaterialAttribute3D& rMaterial,
+ const basegfx::B3DPolyPolygon& rPolyPolygon,
+ bool bModulate,
+ bool bFilter,
+ bool bSimpleTextureActive,
+ bool bIsLine)
+ : mpGeoTexSvx(pGeoTexSvx),
+ mpTransparenceGeoTexSvx(pTransparenceGeoTexSvx),
+ maMaterial(rMaterial),
+ maPolyPolygon(rPolyPolygon),
+ mfCenterZ(basegfx::utils::getRange(rPolyPolygon).getCenter().getZ()),
+ mbModulate(bModulate),
+ mbFilter(bFilter),
+ mbSimpleTextureActive(bSimpleTextureActive),
+ mbIsLine(bIsLine)
+ {
+ }
+
+ bool operator<(const RasterPrimitive3D& rComp) const
+ {
+ return mfCenterZ < rComp.mfCenterZ;
+ }
+
+ const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& getGeoTexSvx() const { return mpGeoTexSvx; }
+ const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& getTransparenceGeoTexSvx() const { return mpTransparenceGeoTexSvx; }
+ const drawinglayer::attribute::MaterialAttribute3D& getMaterial() const { return maMaterial; }
+ const basegfx::B3DPolyPolygon& getPolyPolygon() const { return maPolyPolygon; }
+ bool getModulate() const { return mbModulate; }
+ bool getFilter() const { return mbFilter; }
+ bool getSimpleTextureActive() const { return mbSimpleTextureActive; }
+ bool getIsLine() const { return mbIsLine; }
+};
+
+namespace drawinglayer::processor3d
+{
+ void ZBufferProcessor3D::rasterconvertB3DPolygon(const attribute::MaterialAttribute3D& rMaterial, const basegfx::B3DPolygon& rHairline) const
+ {
+ if(getTransparenceCounter())
+ {
+ // transparent output; record for later sorting and painting from
+ // back to front
+
+ maRasterPrimitive3Ds.push_back(RasterPrimitive3D(
+ getGeoTexSvx(),
+ getTransparenceGeoTexSvx(),
+ rMaterial,
+ basegfx::B3DPolyPolygon(rHairline),
+ getModulate(),
+ getFilter(),
+ getSimpleTextureActive(),
+ true));
+ }
+ else
+ {
+ // do rasterconversion
+ mpZBufferRasterConverter3D->setCurrentMaterial(rMaterial);
+
+ if(mnAntiAlialize > 1)
+ {
+ const bool bForceLineSnap(SvtOptionsDrawinglayer::IsAntiAliasing() && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete());
+
+ if(bForceLineSnap)
+ {
+ basegfx::B3DHomMatrix aTransform;
+ basegfx::B3DPolygon aSnappedHairline(rHairline);
+ const double fScaleDown(1.0 / mnAntiAlialize);
+ const double fScaleUp(mnAntiAlialize);
+
+ // take oversampling out
+ aTransform.scale(fScaleDown, fScaleDown, 1.0);
+ aSnappedHairline.transform(aTransform);
+
+ // snap to integer
+ aSnappedHairline = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aSnappedHairline);
+
+ // add oversampling again
+ aTransform.identity();
+ aTransform.scale(fScaleUp, fScaleUp, 1.0);
+
+ aSnappedHairline.transform(aTransform);
+
+ mpZBufferRasterConverter3D->rasterconvertB3DPolygon(aSnappedHairline, mnStartLine, mnStopLine, mnAntiAlialize);
+ }
+ else
+ {
+ mpZBufferRasterConverter3D->rasterconvertB3DPolygon(rHairline, mnStartLine, mnStopLine, mnAntiAlialize);
+ }
+ }
+ else
+ {
+ mpZBufferRasterConverter3D->rasterconvertB3DPolygon(rHairline, mnStartLine, mnStopLine, 1);
+ }
+ }
+ }
+
+ void ZBufferProcessor3D::rasterconvertB3DPolyPolygon(const attribute::MaterialAttribute3D& rMaterial, const basegfx::B3DPolyPolygon& rFill) const
+ {
+ if(getTransparenceCounter())
+ {
+ // transparent output; record for later sorting and painting from
+ // back to front
+ maRasterPrimitive3Ds.push_back(RasterPrimitive3D(
+ getGeoTexSvx(),
+ getTransparenceGeoTexSvx(),
+ rMaterial,
+ rFill,
+ getModulate(),
+ getFilter(),
+ getSimpleTextureActive(),
+ false));
+ }
+ else
+ {
+ mpZBufferRasterConverter3D->setCurrentMaterial(rMaterial);
+ mpZBufferRasterConverter3D->rasterconvertB3DPolyPolygon(rFill, &maInvEyeToView, mnStartLine, mnStopLine);
+ }
+ }
+
+ ZBufferProcessor3D::ZBufferProcessor3D(
+ const geometry::ViewInformation3D& rViewInformation3D,
+ const attribute::SdrSceneAttribute& rSdrSceneAttribute,
+ const attribute::SdrLightingAttribute& rSdrLightingAttribute,
+ const basegfx::B2DRange& rVisiblePart,
+ sal_uInt16 nAntiAlialize,
+ double fFullViewSizeX,
+ double fFullViewSizeY,
+ basegfx::BZPixelRaster& rBZPixelRaster,
+ sal_uInt32 nStartLine,
+ sal_uInt32 nStopLine)
+ : DefaultProcessor3D(rViewInformation3D, rSdrSceneAttribute, rSdrLightingAttribute),
+ mnAntiAlialize(nAntiAlialize),
+ mnStartLine(nStartLine),
+ mnStopLine(nStopLine)
+ {
+ // create DeviceToView for Z-Buffer renderer since Z is handled
+ // different from standard 3D transformations (Z is mirrored). Also
+ // the transformation includes the step from unit device coordinates
+ // to discrete units ([-1.0 .. 1.0] -> [minDiscrete .. maxDiscrete]
+ basegfx::B3DHomMatrix aDeviceToView;
+
+ {
+ // step one:
+ //
+ // bring from [-1.0 .. 1.0] in X,Y and Z to [0.0 .. 1.0]. Also
+ // necessary to
+ // - flip Y due to screen orientation
+ // - flip Z due to Z-Buffer orientation from back to front
+
+ aDeviceToView.scale(0.5, -0.5, -0.5);
+ aDeviceToView.translate(0.5, 0.5, 0.5);
+ }
+
+ {
+ // step two:
+ //
+ // bring from [0.0 .. 1.0] in X,Y and Z to view coordinates
+ //
+ // #i102611#
+ // also: scale Z to [1.5 .. 65534.5]. Normally, a range of [0.0 .. 65535.0]
+ // could be used, but a 'unused' value is needed, so '0' is used what reduces
+ // the range to [1.0 .. 65535.0]. It has also shown that small numerical errors
+ // (smaller as basegfx::fTools::mfSmallValue, which is 0.000000001) happen.
+ // Instead of checking those by basegfx::fTools methods which would cost
+ // runtime, just add another 0.5 tolerance to the start and end of the Z-Buffer
+ // range, thus resulting in [1.5 .. 65534.5]
+ const double fMaxZDepth(65533.0);
+ aDeviceToView.translate(-rVisiblePart.getMinX(), -rVisiblePart.getMinY(), 0.0);
+
+ if(mnAntiAlialize)
+ aDeviceToView.scale(fFullViewSizeX * mnAntiAlialize, fFullViewSizeY * mnAntiAlialize, fMaxZDepth);
+ else
+ aDeviceToView.scale(fFullViewSizeX, fFullViewSizeY, fMaxZDepth);
+
+ aDeviceToView.translate(0.0, 0.0, 1.5);
+ }
+
+ // update local ViewInformation3D with own DeviceToView
+ const geometry::ViewInformation3D aNewViewInformation3D(
+ getViewInformation3D().getObjectTransformation(),
+ getViewInformation3D().getOrientation(),
+ getViewInformation3D().getProjection(),
+ aDeviceToView,
+ getViewInformation3D().getViewTime(),
+ getViewInformation3D().getExtendedInformationSequence());
+ updateViewInformation(aNewViewInformation3D);
+
+ // prepare inverse EyeToView transformation. This can be done in constructor
+ // since changes in object transformations when processing TransformPrimitive3Ds
+ // do not influence this prepared partial transformation
+ maInvEyeToView = getViewInformation3D().getDeviceToView() * getViewInformation3D().getProjection();
+ maInvEyeToView.invert();
+
+ // prepare maRasterRange
+ maRasterRange.reset();
+ maRasterRange.expand(basegfx::B2DPoint(0.0, nStartLine));
+ maRasterRange.expand(basegfx::B2DPoint(rBZPixelRaster.getWidth(), nStopLine));
+
+ // create the raster converter
+ mpZBufferRasterConverter3D.reset( new ZBufferRasterConverter3D(rBZPixelRaster, *this) );
+ }
+
+ ZBufferProcessor3D::~ZBufferProcessor3D()
+ {
+ mpZBufferRasterConverter3D.reset();
+
+ if(!maRasterPrimitive3Ds.empty())
+ {
+ OSL_FAIL("ZBufferProcessor3D: destructed, but there are unrendered transparent geometries. Use ZBufferProcessor3D::finish() to render these (!)");
+ }
+ }
+
+ void ZBufferProcessor3D::finish()
+ {
+ if(maRasterPrimitive3Ds.empty())
+ return;
+
+ // there are transparent rasterprimitives
+ const sal_uInt32 nSize(maRasterPrimitive3Ds.size());
+
+ if(nSize > 1)
+ {
+ // sort them from back to front
+ std::sort(maRasterPrimitive3Ds.begin(), maRasterPrimitive3Ds.end());
+ }
+
+ for(sal_uInt32 a(0); a < nSize; a++)
+ {
+ // paint each one by setting the remembered data and calling
+ // the render method
+ const RasterPrimitive3D& rCandidate = maRasterPrimitive3Ds[a];
+
+ mpGeoTexSvx = rCandidate.getGeoTexSvx();
+ mpTransparenceGeoTexSvx = rCandidate.getTransparenceGeoTexSvx();
+ mbModulate = rCandidate.getModulate();
+ mbFilter = rCandidate.getFilter();
+ mbSimpleTextureActive = rCandidate.getSimpleTextureActive();
+
+ if(rCandidate.getIsLine())
+ {
+ rasterconvertB3DPolygon(
+ rCandidate.getMaterial(),
+ rCandidate.getPolyPolygon().getB3DPolygon(0));
+ }
+ else
+ {
+ rasterconvertB3DPolyPolygon(
+ rCandidate.getMaterial(),
+ rCandidate.getPolyPolygon());
+ }
+ }
+
+ // delete them to signal the destructor that all is done and
+ // to allow asserting there
+ maRasterPrimitive3Ds.clear();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/texture/texture.cxx b/drawinglayer/source/texture/texture.cxx
new file mode 100644
index 000000000..b3477f642
--- /dev/null
+++ b/drawinglayer/source/texture/texture.cxx
@@ -0,0 +1,766 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <texture/texture.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/utils/gradienttools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+#include <comphelper/random.hxx>
+
+namespace drawinglayer::texture
+{
+ namespace
+ {
+ double getRandomColorRange()
+ {
+ return comphelper::rng::uniform_real_distribution(0.0, nextafter(1.0, DBL_MAX));
+ }
+ }
+
+ GeoTexSvx::GeoTexSvx()
+ {
+ }
+
+ GeoTexSvx::~GeoTexSvx()
+ {
+ }
+
+ bool GeoTexSvx::operator==(const GeoTexSvx& /*rGeoTexSvx*/) const
+ {
+ // default implementation says yes (no data -> no difference)
+ return true;
+ }
+
+ void GeoTexSvx::modifyBColor(const basegfx::B2DPoint& /*rUV*/, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ // base implementation creates random color (for testing only, may also be pure virtual)
+ rBColor.setRed(getRandomColorRange());
+ rBColor.setGreen(getRandomColorRange());
+ rBColor.setBlue(getRandomColorRange());
+ }
+
+ void GeoTexSvx::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const
+ {
+ // base implementation uses inverse of luminance of solved color (for testing only, may also be pure virtual)
+ basegfx::BColor aBaseColor;
+ modifyBColor(rUV, aBaseColor, rfOpacity);
+ rfOpacity = 1.0 - aBaseColor.luminance();
+ }
+
+
+ GeoTexSvxGradient::GeoTexSvxGradient(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rStart,
+ const basegfx::BColor& rEnd,
+ double fBorder)
+ : maDefinitionRange(rDefinitionRange),
+ maStart(rStart),
+ maEnd(rEnd),
+ mfBorder(fBorder)
+ {
+ }
+
+ GeoTexSvxGradient::~GeoTexSvxGradient()
+ {
+ }
+
+ bool GeoTexSvxGradient::operator==(const GeoTexSvx& rGeoTexSvx) const
+ {
+ const GeoTexSvxGradient* pCompare = dynamic_cast< const GeoTexSvxGradient* >(&rGeoTexSvx);
+
+ return (pCompare
+ && maGradientInfo == pCompare->maGradientInfo
+ && maDefinitionRange == pCompare->maDefinitionRange
+ && mfBorder == pCompare->mfBorder);
+ }
+
+
+ GeoTexSvxGradientLinear::GeoTexSvxGradientLinear(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::BColor& rStart,
+ const basegfx::BColor& rEnd,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fAngle)
+ : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder),
+ mfUnitMinX(0.0),
+ mfUnitWidth(1.0),
+ mfUnitMaxY(1.0)
+ {
+ maGradientInfo = basegfx::utils::createLinearODFGradientInfo(
+ rDefinitionRange,
+ nSteps,
+ fBorder,
+ fAngle);
+
+ if(rDefinitionRange != rOutputRange)
+ {
+ basegfx::B2DRange aInvOutputRange(rOutputRange);
+
+ aInvOutputRange.transform(maGradientInfo.getBackTextureTransform());
+ mfUnitMinX = aInvOutputRange.getMinX();
+ mfUnitWidth = aInvOutputRange.getWidth();
+ mfUnitMaxY = aInvOutputRange.getMaxY();
+ }
+ }
+
+ GeoTexSvxGradientLinear::~GeoTexSvxGradientLinear()
+ {
+ }
+
+ void GeoTexSvxGradientLinear::appendTransformationsAndColors(
+ std::vector< B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor)
+ {
+ rOuterColor = maStart;
+
+ if(!maGradientInfo.getSteps())
+ return;
+
+ const double fStripeWidth(1.0 / maGradientInfo.getSteps());
+ B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ basegfx::B2DHomMatrix aPattern;
+
+ // bring from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
+ aPattern.scale(0.5, 0.5);
+ aPattern.translate(0.5, 0.5);
+
+ // scale and translate in X
+ aPattern.scale(mfUnitWidth, 1.0);
+ aPattern.translate(mfUnitMinX, 0.0);
+
+ for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ {
+ const double fPos(fStripeWidth * a);
+ basegfx::B2DHomMatrix aNew(aPattern);
+
+ // scale and translate in Y
+ double fHeight(1.0 - fPos);
+
+ if(a + 1 == maGradientInfo.getSteps() && mfUnitMaxY > 1.0)
+ {
+ fHeight += mfUnitMaxY - 1.0;
+ }
+
+ aNew.scale(1.0, fHeight);
+ aNew.translate(0.0, fPos);
+
+ // set at target
+ aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew;
+
+ // interpolate and set color
+ aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
+
+ rEntries.push_back(aB2DHomMatrixAndBColor);
+ }
+ }
+
+ void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo));
+
+ rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ }
+
+ GeoTexSvxGradientAxial::GeoTexSvxGradientAxial(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::BColor& rStart,
+ const basegfx::BColor& rEnd,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fAngle)
+ : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder),
+ mfUnitMinX(0.0),
+ mfUnitWidth(1.0)
+ {
+ maGradientInfo = basegfx::utils::createAxialODFGradientInfo(
+ rDefinitionRange,
+ nSteps,
+ fBorder,
+ fAngle);
+
+ if(rDefinitionRange != rOutputRange)
+ {
+ basegfx::B2DRange aInvOutputRange(rOutputRange);
+
+ aInvOutputRange.transform(maGradientInfo.getBackTextureTransform());
+ mfUnitMinX = aInvOutputRange.getMinX();
+ mfUnitWidth = aInvOutputRange.getWidth();
+ }
+ }
+
+ GeoTexSvxGradientAxial::~GeoTexSvxGradientAxial()
+ {
+ }
+
+ void GeoTexSvxGradientAxial::appendTransformationsAndColors(
+ std::vector< B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor)
+ {
+ rOuterColor = maEnd;
+
+ if(!maGradientInfo.getSteps())
+ return;
+
+ const double fStripeWidth(1.0 / maGradientInfo.getSteps());
+ B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+
+ for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ {
+ const double fPos(fStripeWidth * a);
+ basegfx::B2DHomMatrix aNew;
+
+ // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
+ aNew.scale(0.5, 1.0);
+ aNew.translate(0.5, 0.0);
+
+ // scale/translate in X
+ aNew.scale(mfUnitWidth, 1.0);
+ aNew.translate(mfUnitMinX, 0.0);
+
+ // already centered in Y on X-Axis, just scale in Y
+ aNew.scale(1.0, 1.0 - fPos);
+
+ // set at target
+ aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew;
+
+ // interpolate and set color
+ aB2DHomMatrixAndBColor.maBColor = interpolate(maEnd, maStart, double(a) / double(maGradientInfo.getSteps() - 1));
+
+ rEntries.push_back(aB2DHomMatrixAndBColor);
+ }
+ }
+
+ void GeoTexSvxGradientAxial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV, maGradientInfo));
+
+ rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ }
+
+
+ GeoTexSvxGradientRadial::GeoTexSvxGradientRadial(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rStart,
+ const basegfx::BColor& rEnd,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fOffsetX,
+ double fOffsetY)
+ : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ {
+ maGradientInfo = basegfx::utils::createRadialODFGradientInfo(
+ rDefinitionRange,
+ basegfx::B2DVector(fOffsetX,fOffsetY),
+ nSteps,
+ fBorder);
+ }
+
+ GeoTexSvxGradientRadial::~GeoTexSvxGradientRadial()
+ {
+ }
+
+ void GeoTexSvxGradientRadial::appendTransformationsAndColors(
+ std::vector< B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor)
+ {
+ rOuterColor = maStart;
+
+ if(!maGradientInfo.getSteps())
+ return;
+
+ const double fStepSize(1.0 / maGradientInfo.getSteps());
+ B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+
+ for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ {
+ const double fSize(1.0 - (fStepSize * a));
+ aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize);
+ aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
+ rEntries.push_back(aB2DHomMatrixAndBColor);
+ }
+ }
+
+ void GeoTexSvxGradientRadial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo));
+
+ rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ }
+
+
+ GeoTexSvxGradientElliptical::GeoTexSvxGradientElliptical(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rStart,
+ const basegfx::BColor& rEnd,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fOffsetX,
+ double fOffsetY,
+ double fAngle)
+ : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ {
+ maGradientInfo = basegfx::utils::createEllipticalODFGradientInfo(
+ rDefinitionRange,
+ basegfx::B2DVector(fOffsetX,fOffsetY),
+ nSteps,
+ fBorder,
+ fAngle);
+ }
+
+ GeoTexSvxGradientElliptical::~GeoTexSvxGradientElliptical()
+ {
+ }
+
+ void GeoTexSvxGradientElliptical::appendTransformationsAndColors(
+ std::vector< B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor)
+ {
+ rOuterColor = maStart;
+
+ if(!maGradientInfo.getSteps())
+ return;
+
+ double fWidth(1.0);
+ double fHeight(1.0);
+ double fIncrementX(0.0);
+ double fIncrementY(0.0);
+
+ if(maGradientInfo.getAspectRatio() > 1.0)
+ {
+ fIncrementY = fHeight / maGradientInfo.getSteps();
+ fIncrementX = fIncrementY / maGradientInfo.getAspectRatio();
+ }
+ else
+ {
+ fIncrementX = fWidth / maGradientInfo.getSteps();
+ fIncrementY = fIncrementX * maGradientInfo.getAspectRatio();
+ }
+
+ B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+
+ for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ {
+ // next step
+ fWidth -= fIncrementX;
+ fHeight -= fIncrementY;
+
+ aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight);
+ aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
+ rEntries.push_back(aB2DHomMatrixAndBColor);
+ }
+ }
+
+ void GeoTexSvxGradientElliptical::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo));
+
+ rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ }
+
+
+ GeoTexSvxGradientSquare::GeoTexSvxGradientSquare(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rStart,
+ const basegfx::BColor& rEnd,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fOffsetX,
+ double fOffsetY,
+ double fAngle)
+ : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ {
+ maGradientInfo = basegfx::utils::createSquareODFGradientInfo(
+ rDefinitionRange,
+ basegfx::B2DVector(fOffsetX,fOffsetY),
+ nSteps,
+ fBorder,
+ fAngle);
+ }
+
+ GeoTexSvxGradientSquare::~GeoTexSvxGradientSquare()
+ {
+ }
+
+ void GeoTexSvxGradientSquare::appendTransformationsAndColors(
+ std::vector< B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor)
+ {
+ rOuterColor = maStart;
+
+ if(!maGradientInfo.getSteps())
+ return;
+
+ const double fStepSize(1.0 / maGradientInfo.getSteps());
+ B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+
+ for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ {
+ const double fSize(1.0 - (fStepSize * a));
+ aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize);
+ aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
+ rEntries.push_back(aB2DHomMatrixAndBColor);
+ }
+ }
+
+ void GeoTexSvxGradientSquare::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo));
+
+ rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ }
+
+
+ GeoTexSvxGradientRect::GeoTexSvxGradientRect(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rStart,
+ const basegfx::BColor& rEnd,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fOffsetX,
+ double fOffsetY,
+ double fAngle)
+ : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ {
+ maGradientInfo = basegfx::utils::createRectangularODFGradientInfo(
+ rDefinitionRange,
+ basegfx::B2DVector(fOffsetX,fOffsetY),
+ nSteps,
+ fBorder,
+ fAngle);
+ }
+
+ GeoTexSvxGradientRect::~GeoTexSvxGradientRect()
+ {
+ }
+
+ void GeoTexSvxGradientRect::appendTransformationsAndColors(
+ std::vector< B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor)
+ {
+ rOuterColor = maStart;
+
+ if(!maGradientInfo.getSteps())
+ return;
+
+ double fWidth(1.0);
+ double fHeight(1.0);
+ double fIncrementX(0.0);
+ double fIncrementY(0.0);
+
+ if(maGradientInfo.getAspectRatio() > 1.0)
+ {
+ fIncrementY = fHeight / maGradientInfo.getSteps();
+ fIncrementX = fIncrementY / maGradientInfo.getAspectRatio();
+ }
+ else
+ {
+ fIncrementX = fWidth / maGradientInfo.getSteps();
+ fIncrementY = fIncrementX * maGradientInfo.getAspectRatio();
+ }
+
+ B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+
+ for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ {
+ // next step
+ fWidth -= fIncrementX;
+ fHeight -= fIncrementY;
+
+ aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight);
+ aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
+ rEntries.push_back(aB2DHomMatrixAndBColor);
+ }
+ }
+
+ void GeoTexSvxGradientRect::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo));
+
+ rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ }
+
+
+ GeoTexSvxHatch::GeoTexSvxHatch(
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::B2DRange& rOutputRange,
+ double fDistance,
+ double fAngle)
+ : maOutputRange(rOutputRange),
+ mfDistance(0.1),
+ mfAngle(fAngle),
+ mnSteps(10),
+ mbDefinitionRangeEqualsOutputRange(rDefinitionRange == rOutputRange)
+ {
+ double fTargetSizeX(rDefinitionRange.getWidth());
+ double fTargetSizeY(rDefinitionRange.getHeight());
+ double fTargetOffsetX(rDefinitionRange.getMinX());
+ double fTargetOffsetY(rDefinitionRange.getMinY());
+
+ fAngle = -fAngle;
+
+ // add object expansion
+ if(0.0 != fAngle)
+ {
+ const double fAbsCos(fabs(cos(fAngle)));
+ const double fAbsSin(fabs(sin(fAngle)));
+ const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
+ const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
+ fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
+ fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
+ fTargetSizeX = fNewX;
+ fTargetSizeY = fNewY;
+ }
+
+ // add object scale before rotate
+ maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
+
+ // add texture rotate after scale to keep perpendicular angles
+ if(0.0 != fAngle)
+ {
+ basegfx::B2DPoint aCenter(0.5, 0.5);
+ aCenter *= maTextureTransform;
+
+ maTextureTransform = basegfx::utils::createRotateAroundPoint(aCenter, fAngle)
+ * maTextureTransform;
+ }
+
+ // add object translate
+ maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
+
+ // prepare height for texture
+ const double fSteps((0.0 != fDistance) ? fTargetSizeY / fDistance : 10.0);
+ mnSteps = basegfx::fround(fSteps + 0.5);
+ mfDistance = 1.0 / fSteps;
+ }
+
+ GeoTexSvxHatch::~GeoTexSvxHatch()
+ {
+ }
+
+ bool GeoTexSvxHatch::operator==(const GeoTexSvx& rGeoTexSvx) const
+ {
+ const GeoTexSvxHatch* pCompare = dynamic_cast< const GeoTexSvxHatch* >(&rGeoTexSvx);
+ return (pCompare
+ && maOutputRange == pCompare->maOutputRange
+ && maTextureTransform == pCompare->maTextureTransform
+ && mfDistance == pCompare->mfDistance
+ && mfAngle == pCompare->mfAngle
+ && mnSteps == pCompare->mnSteps);
+ }
+
+ void GeoTexSvxHatch::appendTransformations(std::vector< basegfx::B2DHomMatrix >& rMatrices)
+ {
+ if(mbDefinitionRangeEqualsOutputRange)
+ {
+ // simple hatch where the definition area equals the output area
+ for(sal_uInt32 a(1); a < mnSteps; a++)
+ {
+ // create matrix
+ const double fOffset(mfDistance * static_cast<double>(a));
+ basegfx::B2DHomMatrix aNew;
+ aNew.set(1, 2, fOffset);
+ rMatrices.push_back(maTextureTransform * aNew);
+ }
+ }
+ else
+ {
+ // output area is different from definition area, back-transform to get
+ // the output area in unit coordinates and fill this with hatch lines
+ // using the settings derived from the definition area
+ basegfx::B2DRange aBackUnitRange(maOutputRange);
+
+ aBackUnitRange.transform(getBackTextureTransform());
+
+ // calculate vertical start value and a security maximum integer value to avoid death loops
+ double fStart(basegfx::snapToNearestMultiple(aBackUnitRange.getMinY(), mfDistance));
+ const sal_uInt32 nNeededIntegerSteps(basegfx::fround((aBackUnitRange.getHeight() / mfDistance) + 0.5));
+ sal_uInt32 nMaxIntegerSteps(std::min(nNeededIntegerSteps, sal_uInt32(10000)));
+
+ while(fStart < aBackUnitRange.getMaxY() && nMaxIntegerSteps)
+ {
+ // create new transform for
+ basegfx::B2DHomMatrix aNew;
+
+ // adapt x scale and position
+ //aNew.scale(aBackUnitRange.getWidth(), 1.0);
+ //aNew.translate(aBackUnitRange.getMinX(), 0.0);
+ aNew.set(0, 0, aBackUnitRange.getWidth());
+ aNew.set(0, 2, aBackUnitRange.getMinX());
+
+ // adapt y position to current step
+ aNew.set(1, 2, fStart);
+ //aNew.translate(0.0, fStart);
+
+ // add new transformation
+ rMatrices.push_back(maTextureTransform * aNew);
+
+ // next step
+ fStart += mfDistance;
+ nMaxIntegerSteps--;
+ }
+ }
+ }
+
+ double GeoTexSvxHatch::getDistanceToHatch(const basegfx::B2DPoint& rUV) const
+ {
+ const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV);
+ return fmod(aCoor.getY(), mfDistance);
+ }
+
+ const basegfx::B2DHomMatrix& GeoTexSvxHatch::getBackTextureTransform() const
+ {
+ if(maBackTextureTransform.isIdentity())
+ {
+ const_cast< GeoTexSvxHatch* >(this)->maBackTextureTransform = maTextureTransform;
+ const_cast< GeoTexSvxHatch* >(this)->maBackTextureTransform.invert();
+ }
+
+ return maBackTextureTransform;
+ }
+
+
+ GeoTexSvxTiled::GeoTexSvxTiled(
+ const basegfx::B2DRange& rRange,
+ double fOffsetX,
+ double fOffsetY)
+ : maRange(rRange),
+ mfOffsetX(std::clamp(fOffsetX, 0.0, 1.0)),
+ mfOffsetY(std::clamp(fOffsetY, 0.0, 1.0))
+ {
+ if(!basegfx::fTools::equalZero(mfOffsetX))
+ {
+ mfOffsetY = 0.0;
+ }
+ }
+
+ GeoTexSvxTiled::~GeoTexSvxTiled()
+ {
+ }
+
+ bool GeoTexSvxTiled::operator==(const GeoTexSvx& rGeoTexSvx) const
+ {
+ const GeoTexSvxTiled* pCompare = dynamic_cast< const GeoTexSvxTiled* >(&rGeoTexSvx);
+
+ return (pCompare
+ && maRange == pCompare->maRange
+ && mfOffsetX == pCompare->mfOffsetX
+ && mfOffsetY == pCompare->mfOffsetY);
+ }
+
+ sal_uInt32 GeoTexSvxTiled::getNumberOfTiles() const
+ {
+ sal_Int32 nTiles = 0;
+ iterateTiles([&](double, double) { ++nTiles; });
+ return nTiles;
+ }
+
+ void GeoTexSvxTiled::appendTransformations(std::vector< basegfx::B2DHomMatrix >& rMatrices) const
+ {
+ const double fWidth(maRange.getWidth());
+ const double fHeight(maRange.getHeight());
+ iterateTiles([&](double fPosX, double fPosY) {
+ rMatrices.push_back(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fWidth,
+ fHeight,
+ fPosX,
+ fPosY));
+ });
+ }
+
+ void GeoTexSvxTiled::iterateTiles(std::function<void(double fPosX, double fPosY)> aFunc) const
+ {
+ const double fWidth(maRange.getWidth());
+
+ if(basegfx::fTools::equalZero(fWidth))
+ return;
+
+ const double fHeight(maRange.getHeight());
+
+ if(basegfx::fTools::equalZero(fHeight))
+ return;
+
+ double fStartX(maRange.getMinX());
+ double fStartY(maRange.getMinY());
+ sal_Int32 nPosX(0);
+ sal_Int32 nPosY(0);
+
+ if(basegfx::fTools::more(fStartX, 0.0))
+ {
+ const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartX / fWidth)) + 1);
+
+ nPosX -= nDiff;
+ fStartX -= nDiff * fWidth;
+ }
+
+ if(basegfx::fTools::less(fStartX + fWidth, 0.0))
+ {
+ const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartX / fWidth)));
+
+ nPosX += nDiff;
+ fStartX += nDiff * fWidth;
+ }
+
+ if(basegfx::fTools::more(fStartY, 0.0))
+ {
+ const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartY / fHeight)) + 1);
+
+ nPosY -= nDiff;
+ fStartY -= nDiff * fHeight;
+ }
+
+ if(basegfx::fTools::less(fStartY + fHeight, 0.0))
+ {
+ const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartY / fHeight)));
+
+ nPosY += nDiff;
+ fStartY += nDiff * fHeight;
+ }
+
+ if(!basegfx::fTools::equalZero(mfOffsetY))
+ {
+ for(double fPosX(fStartX); basegfx::fTools::less(fPosX, 1.0); fPosX += fWidth, nPosX++)
+ {
+ for(double fPosY((nPosX % 2) ? fStartY - fHeight + (mfOffsetY * fHeight) : fStartY);
+ basegfx::fTools::less(fPosY, 1.0); fPosY += fHeight)
+ aFunc(fPosX, fPosY);
+ }
+ }
+ else
+ {
+ for(double fPosY(fStartY); basegfx::fTools::less(fPosY, 1.0); fPosY += fHeight, nPosY++)
+ {
+ for(double fPosX((nPosY % 2) ? fStartX - fWidth + (mfOffsetX * fWidth) : fStartX);
+ basegfx::fTools::less(fPosX, 1.0); fPosX += fWidth)
+ aFunc(fPosX, fPosY);
+ }
+ }
+
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/texture/texture3d.cxx b/drawinglayer/source/texture/texture3d.cxx
new file mode 100644
index 000000000..0a8393799
--- /dev/null
+++ b/drawinglayer/source/texture/texture3d.cxx
@@ -0,0 +1,351 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <texture/texture3d.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <primitive3d/hatchtextureprimitive3d.hxx>
+#include <sal/log.hxx>
+
+namespace drawinglayer::texture
+{
+ GeoTexSvxMono::GeoTexSvxMono(
+ const basegfx::BColor& rSingleColor,
+ double fOpacity)
+ : maSingleColor(rSingleColor),
+ mfOpacity(fOpacity)
+ {
+ }
+
+ bool GeoTexSvxMono::operator==(const GeoTexSvx& rGeoTexSvx) const
+ {
+ const GeoTexSvxMono* pCompare = dynamic_cast< const GeoTexSvxMono* >(&rGeoTexSvx);
+
+ return (pCompare
+ && maSingleColor == pCompare->maSingleColor
+ && mfOpacity == pCompare->mfOpacity);
+ }
+
+ void GeoTexSvxMono::modifyBColor(const basegfx::B2DPoint& /*rUV*/, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
+ {
+ rBColor = maSingleColor;
+ }
+
+ void GeoTexSvxMono::modifyOpacity(const basegfx::B2DPoint& /*rUV*/, double& rfOpacity) const
+ {
+ rfOpacity = mfOpacity;
+ }
+
+
+ GeoTexSvxBitmapEx::GeoTexSvxBitmapEx(
+ const BitmapEx& rBitmapEx,
+ const basegfx::B2DRange& rRange)
+ : maBitmapEx(rBitmapEx),
+ maTopLeft(rRange.getMinimum()),
+ maSize(rRange.getRange()),
+ mfMulX(0.0),
+ mfMulY(0.0),
+ mbIsAlpha(maBitmapEx.IsAlpha())
+ {
+ if(vcl::bitmap::convertBitmap32To24Plus8(maBitmapEx,maBitmapEx))
+ mbIsAlpha = maBitmapEx.IsAlpha();
+ // #121194# Todo: use alpha channel, too (for 3d)
+ maBitmap = maBitmapEx.GetBitmap();
+
+ if(mbIsAlpha)
+ {
+ maTransparence = rBitmapEx.GetAlpha().GetBitmap();
+ mpReadTransparence = Bitmap::ScopedReadAccess(maTransparence);
+ }
+
+ if (!maBitmap.IsEmpty())
+ mpReadBitmap = Bitmap::ScopedReadAccess(maBitmap);
+ SAL_WARN_IF(!mpReadBitmap, "drawinglayer", "GeoTexSvxBitmapEx: Got no read access to Bitmap");
+ if (mpReadBitmap)
+ {
+ mfMulX = static_cast<double>(mpReadBitmap->Width()) / maSize.getX();
+ mfMulY = static_cast<double>(mpReadBitmap->Height()) / maSize.getY();
+ }
+
+ if(maSize.getX() <= 1.0)
+ {
+ maSize.setX(1.0);
+ }
+
+ if(maSize.getY() <= 1.0)
+ {
+ maSize.setY(1.0);
+ }
+ }
+
+ GeoTexSvxBitmapEx::~GeoTexSvxBitmapEx()
+ {
+ }
+
+ sal_uInt8 GeoTexSvxBitmapEx::impGetTransparence(sal_Int32 rX, sal_Int32 rY) const
+ {
+ if(mbIsAlpha)
+ {
+ OSL_ENSURE(mpReadTransparence, "OOps, transparence type Bitmap, but no read access created in the constructor (?)");
+ const BitmapColor aBitmapColor(mpReadTransparence->GetPixel(rY, rX));
+ return aBitmapColor.GetIndex();
+ }
+ return 0;
+ }
+
+ bool GeoTexSvxBitmapEx::impIsValid(const basegfx::B2DPoint& rUV, sal_Int32& rX, sal_Int32& rY) const
+ {
+ if(mpReadBitmap)
+ {
+ rX = static_cast<sal_Int32>((rUV.getX() - maTopLeft.getX()) * mfMulX);
+
+ if(rX >= 0 && rX < mpReadBitmap->Width())
+ {
+ rY = static_cast<sal_Int32>((rUV.getY() - maTopLeft.getY()) * mfMulY);
+
+ return (rY >= 0 && rY < mpReadBitmap->Height());
+ }
+ }
+
+ return false;
+ }
+
+ void GeoTexSvxBitmapEx::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const
+ {
+ sal_Int32 nX, nY;
+
+ if(impIsValid(rUV, nX, nY))
+ {
+ const double fConvertColor(1.0 / 255.0);
+ const BitmapColor aBMCol(mpReadBitmap->GetColor(nY, nX));
+ const basegfx::BColor aBSource(
+ static_cast<double>(aBMCol.GetRed()) * fConvertColor,
+ static_cast<double>(aBMCol.GetGreen()) * fConvertColor,
+ static_cast<double>(aBMCol.GetBlue()) * fConvertColor);
+
+ rBColor = aBSource;
+
+ if(mbIsAlpha)
+ {
+ // when we have a transparence, make use of it
+ const sal_uInt8 aLuminance(impGetTransparence(nX, nY));
+
+ rfOpacity = (static_cast<double>(0xff - aLuminance) * (1.0 / 255.0));
+ }
+ else
+ {
+ rfOpacity = 1.0;
+ }
+ }
+ else
+ {
+ rfOpacity = 0.0;
+ }
+ }
+
+ void GeoTexSvxBitmapEx::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const
+ {
+ sal_Int32 nX, nY;
+
+ if(impIsValid(rUV, nX, nY))
+ {
+ if(mbIsAlpha)
+ {
+ // this texture has an alpha part, use it
+ const sal_uInt8 aLuminance(impGetTransparence(nX, nY));
+ const double fNewOpacity(static_cast<double>(0xff - aLuminance) * (1.0 / 255.0));
+
+ rfOpacity = 1.0 - ((1.0 - fNewOpacity) * (1.0 - rfOpacity));
+ }
+ else
+ {
+ // this texture is a color bitmap used as transparence map
+ const BitmapColor aBMCol(mpReadBitmap->GetColor(nY, nX));
+ const Color aColor(aBMCol.GetRed(), aBMCol.GetGreen(), aBMCol.GetBlue());
+
+ rfOpacity = (static_cast<double>(0xff - aColor.GetLuminance()) * (1.0 / 255.0));
+ }
+ }
+ else
+ {
+ rfOpacity = 0.0;
+ }
+ }
+
+
+ basegfx::B2DPoint GeoTexSvxBitmapExTiled::impGetCorrected(const basegfx::B2DPoint& rUV) const
+ {
+ double fX(rUV.getX() - maTopLeft.getX());
+ double fY(rUV.getY() - maTopLeft.getY());
+
+ if(mbUseOffsetX)
+ {
+ const sal_Int32 nCol(static_cast< sal_Int32 >((fY < 0.0 ? maSize.getY() -fY : fY) / maSize.getY()));
+
+ if(nCol % 2)
+ {
+ fX += mfOffsetX * maSize.getX();
+ }
+ }
+ else if(mbUseOffsetY)
+ {
+ const sal_Int32 nRow(static_cast< sal_Int32 >((fX < 0.0 ? maSize.getX() -fX : fX) / maSize.getX()));
+
+ if(nRow % 2)
+ {
+ fY += mfOffsetY * maSize.getY();
+ }
+ }
+
+ fX = fmod(fX, maSize.getX());
+ fY = fmod(fY, maSize.getY());
+
+ if(fX < 0.0)
+ {
+ fX += maSize.getX();
+ }
+
+ if(fY < 0.0)
+ {
+ fY += maSize.getY();
+ }
+
+ return basegfx::B2DPoint(fX + maTopLeft.getX(), fY + maTopLeft.getY());
+ }
+
+ GeoTexSvxBitmapExTiled::GeoTexSvxBitmapExTiled(
+ const BitmapEx& rBitmapEx,
+ const basegfx::B2DRange& rRange,
+ double fOffsetX,
+ double fOffsetY)
+ : GeoTexSvxBitmapEx(rBitmapEx, rRange),
+ mfOffsetX(std::clamp(fOffsetX, 0.0, 1.0)),
+ mfOffsetY(std::clamp(fOffsetY, 0.0, 1.0)),
+ mbUseOffsetX(!basegfx::fTools::equalZero(mfOffsetX)),
+ mbUseOffsetY(!mbUseOffsetX && !basegfx::fTools::equalZero(mfOffsetY))
+ {
+ }
+
+ void GeoTexSvxBitmapExTiled::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const
+ {
+ if(mpReadBitmap)
+ {
+ GeoTexSvxBitmapEx::modifyBColor(impGetCorrected(rUV), rBColor, rfOpacity);
+ }
+ }
+
+ void GeoTexSvxBitmapExTiled::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const
+ {
+ if(mpReadBitmap)
+ {
+ GeoTexSvxBitmapEx::modifyOpacity(impGetCorrected(rUV), rfOpacity);
+ }
+ }
+
+
+ GeoTexSvxMultiHatch::GeoTexSvxMultiHatch(
+ const primitive3d::HatchTexturePrimitive3D& rPrimitive,
+ double fLogicPixelSize)
+ : mfLogicPixelSize(fLogicPixelSize)
+ {
+ const attribute::FillHatchAttribute& rHatch(rPrimitive.getHatch());
+ const basegfx::B2DRange aOutlineRange(0.0, 0.0, rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY());
+ const double fAngleA(rHatch.getAngle());
+ maColor = rHatch.getColor();
+ mbFillBackground = rHatch.isFillBackground();
+ mp0.reset( new GeoTexSvxHatch(
+ aOutlineRange,
+ aOutlineRange,
+ rHatch.getDistance(),
+ fAngleA) );
+
+ if(attribute::HatchStyle::Double == rHatch.getStyle() || attribute::HatchStyle::Triple == rHatch.getStyle())
+ {
+ mp1.reset( new GeoTexSvxHatch(
+ aOutlineRange,
+ aOutlineRange,
+ rHatch.getDistance(),
+ fAngleA + M_PI_2) );
+ }
+
+ if(attribute::HatchStyle::Triple == rHatch.getStyle())
+ {
+ mp2.reset( new GeoTexSvxHatch(
+ aOutlineRange,
+ aOutlineRange,
+ rHatch.getDistance(),
+ fAngleA + M_PI_4) );
+ }
+ }
+
+ GeoTexSvxMultiHatch::~GeoTexSvxMultiHatch()
+ {
+ }
+
+ bool GeoTexSvxMultiHatch::impIsOnHatch(const basegfx::B2DPoint& rUV) const
+ {
+ if(mp0->getDistanceToHatch(rUV) < mfLogicPixelSize)
+ {
+ return true;
+ }
+
+ if(mp1 && mp1->getDistanceToHatch(rUV) < mfLogicPixelSize)
+ {
+ return true;
+ }
+
+ if(mp2 && mp2->getDistanceToHatch(rUV) < mfLogicPixelSize)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ void GeoTexSvxMultiHatch::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const
+ {
+ if(impIsOnHatch(rUV))
+ {
+ rBColor = maColor;
+ }
+ else if(!mbFillBackground)
+ {
+ rfOpacity = 0.0;
+ }
+ }
+
+ void GeoTexSvxMultiHatch::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const
+ {
+ if(mbFillBackground || impIsOnHatch(rUV))
+ {
+ rfOpacity = 1.0;
+ }
+ else
+ {
+ rfOpacity = 0.0;
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/converters.cxx b/drawinglayer/source/tools/converters.cxx
new file mode 100644
index 000000000..c0a92a8f4
--- /dev/null
+++ b/drawinglayer/source/tools/converters.cxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <com/sun/star/geometry/RealRectangle2D.hpp>
+#include <tools/diagnose_ex.h>
+
+#include <drawinglayer/converters.hxx>
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/pngwrite.hxx>
+#endif
+
+namespace drawinglayer
+{
+
+ BitmapEx convertToBitmapEx(
+ drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
+ const geometry::ViewInformation2D& rViewInformation2D,
+ sal_uInt32 nDiscreteWidth,
+ sal_uInt32 nDiscreteHeight,
+ sal_uInt32 nMaxSquarePixels)
+ {
+ BitmapEx aRetval;
+
+ if(!rSeq.empty() && nDiscreteWidth && nDiscreteHeight)
+ {
+ // get destination size in pixels
+ const MapMode aMapModePixel(MapUnit::MapPixel);
+ const sal_uInt32 nViewVisibleArea(nDiscreteWidth * nDiscreteHeight);
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+
+ if(nViewVisibleArea > nMaxSquarePixels)
+ {
+ // reduce render size
+ double fReduceFactor = sqrt(static_cast<double>(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea));
+ nDiscreteWidth = basegfx::fround(static_cast<double>(nDiscreteWidth) * fReduceFactor);
+ nDiscreteHeight = basegfx::fround(static_cast<double>(nDiscreteHeight) * fReduceFactor);
+
+ const drawinglayer::primitive2d::Primitive2DReference aEmbed(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor),
+ std::move(rSeq)));
+
+ aSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed };
+ }
+ else
+ aSequence = std::move(rSeq);
+
+ const Point aEmptyPoint;
+ const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
+ ScopedVclPtrInstance< VirtualDevice > pContent;
+
+ // prepare vdev
+ pContent->SetOutputSizePixel(aSizePixel, false);
+ pContent->SetMapMode(aMapModePixel);
+
+ // set to all white
+ pContent->SetBackground(Wallpaper(COL_WHITE));
+ pContent->Erase();
+
+ // create pixel processor, also already takes care of AAing and
+ // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If
+ // not wanted, change after this call as needed
+ std::unique_ptr<processor2d::BaseProcessor2D> pContentProcessor = processor2d::createPixelProcessor2DFromOutputDevice(
+ *pContent,
+ rViewInformation2D);
+
+#ifdef DBG_UTIL
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+#endif
+ // render content
+ pContentProcessor->process(aSequence);
+
+ // get content
+ pContent->EnableMapMode(false);
+ const Bitmap aContent(pContent->GetBitmap(aEmptyPoint, aSizePixel));
+
+#ifdef DBG_UTIL
+ if(bDoSaveForVisualControl)
+ {
+ SvFileStream aNew(
+#ifdef _WIN32
+ "c:\\test_content.png"
+#else
+ "~/test_content.png"
+#endif
+ , StreamMode::WRITE|StreamMode::TRUNC);
+ BitmapEx aContentEx(aContent);
+ vcl::PNGWriter aPNGWriter(aContentEx);
+ aPNGWriter.Write(aNew);
+ }
+#endif
+ // prepare for mask creation
+ pContent->SetMapMode(aMapModePixel);
+
+ // set alpha to all white (fully transparent)
+ pContent->Erase();
+
+ // embed primitives to paint them black
+ const primitive2d::Primitive2DReference xRef(
+ new primitive2d::ModifiedColorPrimitive2D(
+ std::move(aSequence),
+ std::make_shared<basegfx::BColorModifier_replace>(
+ basegfx::BColor(0.0, 0.0, 0.0))));
+ const primitive2d::Primitive2DContainer xSeq { xRef };
+
+ // render
+ pContentProcessor->process(xSeq);
+ pContentProcessor.reset();
+
+ // get alpha channel from vdev
+ pContent->EnableMapMode(false);
+ const Bitmap aAlpha(pContent->GetBitmap(aEmptyPoint, aSizePixel));
+#ifdef DBG_UTIL
+ if(bDoSaveForVisualControl)
+ {
+ SvFileStream aNew(
+#ifdef _WIN32
+ "c:\\test_alpha.png"
+#else
+ "~/test_alpha.png"
+#endif
+ , StreamMode::WRITE|StreamMode::TRUNC);
+ BitmapEx aAlphaEx(aAlpha);
+ vcl::PNGWriter aPNGWriter(aAlphaEx);
+ aPNGWriter.Write(aNew);
+ }
+#endif
+
+ // create BitmapEx result
+ aRetval = BitmapEx(aContent, AlphaMask(aAlpha));
+#ifdef DBG_UTIL
+ if(bDoSaveForVisualControl)
+ {
+ SvFileStream aNew(
+#ifdef _WIN32
+ "c:\\test_combined.png"
+#else
+ "~/test_combined.png"
+#endif
+ , StreamMode::WRITE|StreamMode::TRUNC);
+ vcl::PNGWriter aPNGWriter(aRetval);
+ aPNGWriter.Write(aNew);
+ }
+#endif
+ }
+
+ return aRetval;
+ }
+
+ BitmapEx convertPrimitive2DContainerToBitmapEx(
+ primitive2d::Primitive2DContainer&& rSequence,
+ const basegfx::B2DRange& rTargetRange,
+ sal_uInt32 nMaximumQuadraticPixels,
+ const o3tl::Length eTargetUnit,
+ const std::optional<Size>& rTargetDPI)
+ {
+ if(rSequence.empty())
+ return BitmapEx();
+
+ try
+ {
+ css::geometry::RealRectangle2D aRealRect;
+ aRealRect.X1 = rTargetRange.getMinX();
+ aRealRect.Y1 = rTargetRange.getMinY();
+ aRealRect.X2 = rTargetRange.getMaxX();
+ aRealRect.Y2 = rTargetRange.getMaxY();
+
+ // get system DPI
+ Size aDPI(Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+ if (rTargetDPI.has_value())
+ {
+ aDPI = *rTargetDPI;
+ }
+
+ ::sal_uInt32 DPI_X = aDPI.getWidth();
+ ::sal_uInt32 DPI_Y = aDPI.getHeight();
+ const basegfx::B2DRange aRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2);
+ const double fWidth(aRange.getWidth());
+ const double fHeight(aRange.getHeight());
+
+ if(!(basegfx::fTools::more(fWidth, 0.0) && basegfx::fTools::more(fHeight, 0.0)))
+ return BitmapEx();
+
+ if(0 == DPI_X)
+ {
+ DPI_X = 75;
+ }
+
+ if(0 == DPI_Y)
+ {
+ DPI_Y = 75;
+ }
+
+ if(0 == nMaximumQuadraticPixels)
+ {
+ nMaximumQuadraticPixels = 500000;
+ }
+
+ const auto aViewInformation2D = geometry::createViewInformation2D({});
+ const sal_uInt32 nDiscreteWidth(basegfx::fround(o3tl::convert(fWidth, eTargetUnit, o3tl::Length::in) * DPI_X));
+ const sal_uInt32 nDiscreteHeight(basegfx::fround(o3tl::convert(fHeight, eTargetUnit, o3tl::Length::in) * DPI_Y));
+
+ basegfx::B2DHomMatrix aEmbedding(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -aRange.getMinX(),
+ -aRange.getMinY()));
+
+ aEmbedding.scale(
+ nDiscreteWidth / fWidth,
+ nDiscreteHeight / fHeight);
+
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(
+ aEmbedding,
+ std::move(rSequence)));
+ primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+
+ BitmapEx aBitmapEx(
+ convertToBitmapEx(
+ std::move(xEmbedSeq),
+ aViewInformation2D,
+ nDiscreteWidth,
+ nDiscreteHeight,
+ nMaximumQuadraticPixels));
+
+ if(aBitmapEx.IsEmpty())
+ return BitmapEx();
+ aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ aBitmapEx.SetPrefSize(Size(basegfx::fround(fWidth), basegfx::fround(fHeight)));
+
+ return aBitmapEx;
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!");
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e.what());
+ }
+
+ return BitmapEx();
+ }
+} // end of namespace drawinglayer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpbrush.cxx b/drawinglayer/source/tools/emfpbrush.cxx
new file mode 100644
index 000000000..c79b0ded0
--- /dev/null
+++ b/drawinglayer/source/tools/emfpbrush.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include "emfpbrush.hxx"
+#include "emfppath.hxx"
+
+namespace emfplushelper
+{
+ EMFPBrush::EMFPBrush()
+ : type(0)
+ , additionalFlags(0)
+ , wrapMode(0)
+ , firstPointX(0.0)
+ , firstPointY(0.0)
+ , aWidth(0.0)
+ , aHeight(0.0)
+ , hasTransformation(false)
+ , blendPoints(0)
+ , blendFactors(nullptr)
+ , colorblendPoints(0)
+ , surroundColorsNumber(0)
+ , hatchStyle(HatchStyleHorizontal)
+ {
+ }
+
+ EMFPBrush::~EMFPBrush()
+ {
+ }
+
+ static OUString BrushTypeToString(sal_uInt32 type)
+ {
+ switch (type)
+ {
+ case BrushTypeSolidColor: return "BrushTypeSolidColor";
+ case BrushTypeHatchFill: return "BrushTypeHatchFill";
+ case BrushTypeTextureFill: return "BrushTypeTextureFill";
+ case BrushTypePathGradient: return "BrushTypePathGradient";
+ case BrushTypeLinearGradient: return "BrushTypeLinearGradient";
+ }
+ return "";
+ }
+
+ void EMFPBrush::Read(SvStream& s, EmfPlusHelperData const & rR)
+ {
+ sal_uInt32 header;
+
+ s.ReadUInt32(header).ReadUInt32(type);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\tHeader: 0x" << std::hex << header);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\tType: " << BrushTypeToString(type) << "(0x" << type << ")" << std::dec);
+
+ switch (type)
+ {
+ case BrushTypeSolidColor:
+ {
+ sal_uInt32 color;
+ s.ReadUInt32(color);
+
+ solidColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tSolid color: 0x" << std::hex << color << std::dec);
+ break;
+ }
+ case BrushTypeHatchFill:
+ {
+ sal_uInt32 style;
+ sal_uInt32 foregroundColor;
+ sal_uInt32 backgroundColor;
+ s.ReadUInt32(style);
+ s.ReadUInt32(foregroundColor);
+ s.ReadUInt32(backgroundColor);
+
+ hatchStyle = static_cast<EmfPlusHatchStyle>(style);
+ solidColor = ::Color(ColorAlpha, (foregroundColor >> 24), (foregroundColor >> 16) & 0xff, (foregroundColor >> 8) & 0xff, foregroundColor & 0xff);
+ secondColor = ::Color(ColorAlpha, (backgroundColor >> 24), (backgroundColor >> 16) & 0xff, (backgroundColor >> 8) & 0xff, backgroundColor & 0xff);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tHatch style: 0x" << std::hex << style);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tForeground color: 0x" << solidColor.AsRGBHexString());
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tBackground color: 0x" << secondColor.AsRGBHexString());
+ break;
+ }
+ case BrushTypeTextureFill:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
+ break;
+ }
+ case BrushTypePathGradient:
+ {
+ s.ReadUInt32(additionalFlags).ReadInt32(wrapMode);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tAdditional flags: 0x" << std::hex << additionalFlags << std::dec);
+ sal_uInt32 color;
+ s.ReadUInt32(color);
+ solidColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tCenter color: 0x" << std::hex << color << std::dec);
+ s.ReadFloat(firstPointX).ReadFloat(firstPointY);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tCenter point: " << firstPointX << "," << firstPointY);
+ s.ReadUInt32(surroundColorsNumber);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t number of surround colors: " << surroundColorsNumber);
+
+ surroundColors.reset( new ::Color[surroundColorsNumber] );
+
+ for (sal_uInt32 i = 0; i < surroundColorsNumber; i++)
+ {
+ s.ReadUInt32(color);
+ surroundColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ if (i == 0)
+ secondColor = surroundColors[0];
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tSurround color[" << i << "]: 0x" << std::hex << color << std::dec);
+ }
+
+ if (additionalFlags & 0x01) // BrushDataPath
+ {
+ sal_Int32 pathLength;
+
+ s.ReadInt32(pathLength);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPath length: " << pathLength);
+
+ sal_uInt64 const pos = s.Tell();
+
+ sal_uInt32 pathHeader;
+ sal_Int32 pathPoints, pathFlags;
+ s.ReadUInt32(pathHeader).ReadInt32(pathPoints).ReadInt32(pathFlags);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPath (brush path gradient)");
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t\tHeader: 0x" << std::hex << pathHeader);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t\tPoints: " << std::dec << pathPoints);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t\tAdditional flags: 0x" << std::hex << pathFlags << std::dec);
+
+ path.reset( new EMFPPath(pathPoints) );
+ path->Read(s, pathFlags);
+
+ s.Seek(pos + pathLength);
+
+ const ::basegfx::B2DRectangle aBounds(::basegfx::utils::getRange(path->GetPolygon(rR, false)));
+ aWidth = aBounds.getWidth();
+ aHeight = aBounds.getHeight();
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPolygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " "
+ << aBounds.getWidth() << "x" << aBounds.getHeight());
+ }
+ else
+ {
+ sal_Int32 boundaryPointCount;
+ s.ReadInt32(boundaryPointCount);
+
+ sal_uInt64 const pos = s.Tell();
+ SAL_INFO("drawinglayer.emf", "EMF+\t use boundary, points: " << boundaryPointCount);
+ path.reset( new EMFPPath(boundaryPointCount) );
+ path->Read(s, 0x0);
+
+ s.Seek(pos + 8 * boundaryPointCount);
+
+ const ::basegfx::B2DRectangle aBounds(::basegfx::utils::getRange(path->GetPolygon(rR, false)));
+ aWidth = aBounds.getWidth();
+ aHeight = aBounds.getHeight();
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPolygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " "
+ << aBounds.getWidth() << "x" << aBounds.getHeight());
+ }
+
+ if (additionalFlags & 0x02) // BrushDataTransform
+ {
+ EmfPlusHelperData::readXForm(s, brush_transformation);
+ hasTransformation = true;
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation);
+ }
+
+ // BrushDataPresetColors and BrushDataBlendFactorsH
+ if ((additionalFlags & 0x04) && (additionalFlags & 0x08))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t Brush must not contain both BrushDataPresetColors and BrushDataBlendFactorsH");
+ return;
+ }
+ if (additionalFlags & 0x08) // BrushDataBlendFactorsH
+ {
+ s.ReadUInt32(blendPoints);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tuse blend, points: " << blendPoints);
+ blendPositions.reset( new float[2 * blendPoints] );
+ blendFactors = blendPositions.get() + blendPoints;
+
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
+ {
+ s.ReadFloat(blendPositions[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tposition[" << i << "]: " << blendPositions[i]);
+ }
+
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
+ {
+ s.ReadFloat(blendFactors[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]);
+ }
+ }
+
+ if (additionalFlags & 0x04) // BrushDataPresetColors
+ {
+ s.ReadUInt32(colorblendPoints);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints);
+ colorblendPositions.reset( new float[colorblendPoints] );
+ colorblendColors.reset( new ::Color[colorblendPoints] );
+
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
+ {
+ s.ReadFloat(colorblendPositions[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\tposition[" << i << "]: " << colorblendPositions[i]);
+ }
+
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
+ {
+ s.ReadUInt32(color);
+ colorblendColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tColor[" << i << "]: 0x" << std::hex << color << std::dec);
+ }
+ }
+
+ break;
+ }
+ case BrushTypeLinearGradient:
+ {
+ s.ReadUInt32(additionalFlags).ReadInt32(wrapMode);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tLinear gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec << ", wrapMode: " << wrapMode);
+ s.ReadFloat(firstPointX).ReadFloat(firstPointY).ReadFloat(aWidth).ReadFloat(aHeight);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFirst gradient point: " << firstPointX << ":" << firstPointY
+ << ", size " << aWidth << "x" << aHeight);
+ sal_uInt32 color;
+ s.ReadUInt32(color);
+ solidColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tfirst color: 0x" << std::hex << color << std::dec);
+ s.ReadUInt32(color);
+ secondColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tsecond color: 0x" << std::hex << color << std::dec);
+
+ // repeated colors, unknown meaning, see http://www.aces.uiuc.edu/~jhtodd/Metafile/MetafileRecords/ObjectBrush.html
+ s.ReadUInt32(color);
+ s.ReadUInt32(color);
+
+ if (additionalFlags & 0x02) //BrushDataTransform
+ {
+ EmfPlusHelperData::readXForm(s, brush_transformation);
+ hasTransformation = true;
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation);
+ }
+ // BrushDataPresetColors and BrushDataBlendFactorsH
+ if ((additionalFlags & 0x04) && (additionalFlags & 0x08))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t Brush must not contain both BrushDataPresetColors and BrushDataBlendFactorsH");
+ return;
+ }
+
+ if (additionalFlags & 0x08) // BrushDataBlendFactorsH
+ {
+ s.ReadUInt32(blendPoints);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse blend, points: " << blendPoints);
+ blendPositions.reset( new float[2 * blendPoints] );
+ blendFactors = blendPositions.get() + blendPoints;
+
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
+ {
+ s.ReadFloat(blendPositions[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPosition[" << i << "]: " << blendPositions[i]);
+ }
+
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
+ {
+ s.ReadFloat(blendFactors[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]);
+ }
+ }
+
+ if (additionalFlags & 0x04)
+ {
+ s.ReadUInt32(colorblendPoints);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints);
+ colorblendPositions.reset( new float[colorblendPoints] );
+ colorblendColors.reset( new ::Color[colorblendPoints] );
+
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
+ {
+ s.ReadFloat(colorblendPositions[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPosition[" << i << "]: " << colorblendPositions[i]);
+ }
+
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
+ {
+ s.ReadUInt32(color);
+ colorblendColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tColor[" << i << "]: 0x" << std::hex << color << std::dec);
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\tunhandled brush type: " << std::hex << type << std::dec);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpbrush.hxx b/drawinglayer/source/tools/emfpbrush.hxx
new file mode 100644
index 000000000..5f272a63a
--- /dev/null
+++ b/drawinglayer/source/tools/emfpbrush.hxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+#include <tools/color.hxx>
+
+namespace emfplushelper
+{
+ enum EmfPlusHatchStyle
+ {
+ HatchStyleHorizontal = 0x00000000,
+ HatchStyleVertical = 0x00000001,
+ HatchStyleForwardDiagonal = 0x00000002,
+ HatchStyleBackwardDiagonal = 0x00000003,
+ HatchStyleLargeGrid = 0x00000004,
+ HatchStyleDiagonalCross = 0x00000005,
+ HatchStyle05Percent = 0x00000006,
+ HatchStyle10Percent = 0x00000007,
+ HatchStyle20Percent = 0x00000008,
+ HatchStyle25Percent = 0x00000009,
+ HatchStyle30Percent = 0x0000000A,
+ HatchStyle40Percent = 0x0000000B,
+ HatchStyle50Percent = 0x0000000C,
+ HatchStyle60Percent = 0x0000000D,
+ HatchStyle70Percent = 0x0000000E,
+ HatchStyle75Percent = 0x0000000F,
+ HatchStyle80Percent = 0x00000010,
+ HatchStyle90Percent = 0x00000011,
+ HatchStyleLightDownwardDiagonal = 0x00000012,
+ HatchStyleLightUpwardDiagonal = 0x00000013,
+ HatchStyleDarkDownwardDiagonal = 0x00000014,
+ HatchStyleDarkUpwardDiagonal = 0x00000015,
+ HatchStyleWideDownwardDiagonal = 0x00000016,
+ HatchStyleWideUpwardDiagonal = 0x00000017,
+ HatchStyleLightVertical = 0x00000018,
+ HatchStyleLightHorizontal = 0x00000019,
+ HatchStyleNarrowVertical = 0x0000001A,
+ HatchStyleNarrowHorizontal = 0x0000001B,
+ HatchStyleDarkVertical = 0x0000001C,
+ HatchStyleDarkHorizontal = 0x0000001D,
+ HatchStyleDashedDownwardDiagonal = 0x0000001E,
+ HatchStyleDashedUpwardDiagonal = 0x0000001F,
+ HatchStyleDashedHorizontal = 0x00000020,
+ HatchStyleDashedVertical = 0x00000021,
+ HatchStyleSmallConfetti = 0x00000022,
+ HatchStyleLargeConfetti = 0x00000023,
+ HatchStyleZigZag = 0x00000024,
+ HatchStyleWave = 0x00000025,
+ HatchStyleDiagonalBrick = 0x00000026,
+ HatchStyleHorizontalBrick = 0x00000027,
+ HatchStyleWeave = 0x00000028,
+ HatchStylePlaid = 0x00000029,
+ HatchStyleDivot = 0x0000002A,
+ HatchStyleDottedGrid = 0x0000002B,
+ HatchStyleDottedDiamond = 0x0000002C,
+ HatchStyleShingle = 0x0000002D,
+ HatchStyleTrellis = 0x0000002E,
+ HatchStyleSphere = 0x0000002F,
+ HatchStyleSmallGrid = 0x00000030,
+ HatchStyleSmallCheckerBoard = 0x00000031,
+ HatchStyleLargeCheckerBoard = 0x00000032,
+ HatchStyleOutlinedDiamond = 0x00000033,
+ HatchStyleSolidDiamond = 0x00000034
+ };
+
+ enum EmfPlusBrushType
+ {
+ BrushTypeSolidColor = 0x00000000,
+ BrushTypeHatchFill = 0x00000001,
+ BrushTypeTextureFill = 0x00000002,
+ BrushTypePathGradient = 0x00000003,
+ BrushTypeLinearGradient = 0x00000004
+ };
+
+ struct EMFPPath;
+
+ struct EMFPBrush : public EMFPObject
+ {
+ ::Color solidColor;
+ sal_uInt32 type;
+ sal_uInt32 additionalFlags;
+
+ /* linear gradient */
+ sal_Int32 wrapMode;
+ float firstPointX, firstPointY, aWidth, aHeight;
+ ::Color secondColor; // first color is stored in solidColor;
+ basegfx::B2DHomMatrix brush_transformation;
+ bool hasTransformation;
+ sal_uInt32 blendPoints;
+ std::unique_ptr<float[]> blendPositions;
+ float* blendFactors;
+ sal_uInt32 colorblendPoints;
+ std::unique_ptr<float[]> colorblendPositions;
+ std::unique_ptr<::Color[]> colorblendColors;
+ sal_uInt32 surroundColorsNumber;
+ std::unique_ptr<::Color[]> surroundColors;
+ std::unique_ptr<EMFPPath> path;
+ EmfPlusHatchStyle hatchStyle;
+
+ EMFPBrush();
+ virtual ~EMFPBrush() override;
+
+ sal_uInt32 GetType() const { return type; }
+ const ::Color& GetColor() const { return solidColor; }
+
+ void Read(SvStream& s, EmfPlusHelperData const & rR);
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.cxx b/drawinglayer/source/tools/emfpcustomlinecap.cxx
new file mode 100644
index 000000000..e457a36cc
--- /dev/null
+++ b/drawinglayer/source/tools/emfpcustomlinecap.cxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include "emfpcustomlinecap.hxx"
+#include "emfppath.hxx"
+#include "emfppen.hxx"
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::basegfx;
+
+namespace emfplushelper
+{
+ const sal_uInt32 EmfPlusCustomLineCapDataTypeDefault = 0x00000000;
+ const sal_uInt32 EmfPlusCustomLineCapDataTypeAdjustableArrow = 0x00000001;
+ const sal_uInt32 EmfPlusCustomLineCapDataFillPath = 0x00000001;
+ const sal_uInt32 EmfPlusCustomLineCapDataLinePath = 0x00000002;
+
+ EMFPCustomLineCap::EMFPCustomLineCap()
+ : EMFPObject()
+ , type(0)
+ , strokeStartCap(0)
+ , strokeEndCap(0)
+ , strokeJoin(0)
+ , miterLimit(0.0)
+ , widthScale(0.0)
+ , mbIsFilled(false)
+ {
+ }
+
+ void EMFPCustomLineCap::ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill)
+ {
+ sal_Int32 pathLength;
+ s.ReadInt32(pathLength);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tpath length: " << pathLength);
+ sal_uInt32 pathHeader;
+ sal_Int32 pathPoints, pathFlags;
+ s.ReadUInt32(pathHeader).ReadInt32(pathPoints).ReadInt32(pathFlags);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tpath (custom cap line path)");
+ SAL_INFO("drawinglayer.emf", "EMF+\t\theader: 0x" << std::hex << pathHeader << " points: " << std::dec << pathPoints << " additional flags: 0x" << std::hex << pathFlags << std::dec);
+
+ EMFPPath path(pathPoints);
+ path.Read(s, pathFlags);
+ polygon = path.GetPolygon(rR, false);
+ // rotate polygon by 180 degrees
+ polygon.transform(basegfx::utils::createRotateB2DHomMatrix(M_PI));
+ mbIsFilled = bFill;
+ }
+
+ void EMFPCustomLineCap::Read(SvStream& s, EmfPlusHelperData const & rR)
+ {
+ sal_uInt32 header;
+ s.ReadUInt32(header).ReadUInt32(type);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tcustom cap");
+ SAL_INFO("drawinglayer.emf", "EMF+\t\theader: 0x" << std::hex << header << " type: " << type << std::dec);
+
+ if (type == EmfPlusCustomLineCapDataTypeDefault)
+ {
+ sal_uInt32 customLineCapDataFlags, baseCap;
+ float baseInset;
+ float fillHotSpotX, fillHotSpotY, strokeHotSpotX, strokeHotSpotY;
+
+ s.ReadUInt32(customLineCapDataFlags).ReadUInt32(baseCap).ReadFloat(baseInset)
+ .ReadUInt32(strokeStartCap).ReadUInt32(strokeEndCap).ReadUInt32(strokeJoin)
+ .ReadFloat(miterLimit).ReadFloat(widthScale)
+ .ReadFloat(fillHotSpotX).ReadFloat(fillHotSpotY).ReadFloat(strokeHotSpotX).ReadFloat(strokeHotSpotY);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex << customLineCapDataFlags);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseCap: 0x" << std::hex << baseCap);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseInset: " << baseInset);
+
+ if (customLineCapDataFlags & EmfPlusCustomLineCapDataFillPath)
+ {
+ ReadPath(s, rR, true);
+ }
+
+ if (customLineCapDataFlags & EmfPlusCustomLineCapDataLinePath)
+ {
+ ReadPath(s, rR, false);
+ }
+ }
+ else if (type == EmfPlusCustomLineCapDataTypeAdjustableArrow)
+ {
+ // TODO only reads the data, does not use them [I've had
+ // no test document to be able to implement it]
+
+ sal_Int32 fillState;
+ float width, height, middleInset, unusedHotSpot;
+
+ s.ReadFloat(width).ReadFloat(height).ReadFloat(middleInset).ReadInt32(fillState).ReadUInt32(strokeStartCap)
+ .ReadUInt32(strokeEndCap).ReadUInt32(strokeJoin).ReadFloat(miterLimit).ReadFloat(widthScale)
+ .ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)");
+ }
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tmiterLimit: " << miterLimit);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\twidthScale: " << widthScale);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.hxx b/drawinglayer/source/tools/emfpcustomlinecap.hxx
new file mode 100644
index 000000000..22ed6be6d
--- /dev/null
+++ b/drawinglayer/source/tools/emfpcustomlinecap.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+
+namespace emfplushelper
+{
+ struct EMFPCustomLineCap : public EMFPObject
+ {
+ sal_uInt32 type;
+ sal_uInt32 strokeStartCap, strokeEndCap, strokeJoin;
+ float miterLimit, widthScale;
+ basegfx::B2DPolyPolygon polygon;
+ bool mbIsFilled;
+
+ EMFPCustomLineCap();
+
+ void ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill);
+ void Read(SvStream& s, EmfPlusHelperData const & rR);
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpfont.cxx b/drawinglayer/source/tools/emfpfont.cxx
new file mode 100644
index 000000000..3fd6537e7
--- /dev/null
+++ b/drawinglayer/source/tools/emfpfont.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+#include "emfpfont.hxx"
+
+namespace emfplushelper
+{
+ static OUString FontStyleToString(sal_uInt32 style)
+ {
+ OUStringBuffer sStyle;
+
+ if (style & FontStyleBold)
+ sStyle = "\n\t\t\tFontStyleBold";
+
+ if (style & FontStyleItalic)
+ sStyle.append("\n\t\t\tFontStyleItalic");
+
+ if (style & FontStyleUnderline)
+ sStyle.append("\n\t\t\tFontStyleUnderline");
+
+ if (style & FontStyleStrikeout)
+ sStyle.append("\n\t\t\tFontStyleStrikeout");
+
+ return sStyle.makeStringAndClear();
+ }
+
+ void EMFPFont::Read(SvMemoryStream &s)
+ {
+ sal_uInt32 header;
+ sal_uInt32 reserved;
+ sal_uInt32 length;
+ s.ReadUInt32(header).ReadFloat(emSize).ReadUInt32(sizeUnit).ReadInt32(fontFlags).ReadUInt32(reserved).ReadUInt32(length);
+ SAL_WARN_IF((header >> 12) != 0xdbc01, "drawinglayer.emf", "Invalid header - not 0xdbc01");
+ SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << (header >> 12));
+ SAL_INFO("drawinglayer.emf", "EMF+\tVersion: 0x" << (header & 0x1fff));
+ SAL_INFO("drawinglayer.emf", "EMF+\tSize: " << std::dec << emSize);
+ SAL_INFO("drawinglayer.emf", "EMF+\tUnit: " << UnitTypeToString(sizeUnit) << " (0x" << std::hex << sizeUnit << ")" << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << FontStyleToString(fontFlags) << " (0x" << std::hex << fontFlags << ")");
+ SAL_INFO("drawinglayer.emf", "EMF+\tReserved: 0x" << reserved << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\tLength: " << length);
+
+ if (length <= 0 || length >= 0x4000)
+ return;
+
+ rtl_uString *pStr = rtl_uString_alloc(length);
+ sal_Unicode *chars = pStr->buffer;
+
+ for (sal_uInt32 i = 0; i < length; ++i)
+ {
+ s.ReadUtf16(chars[i]);
+ }
+
+ family = OUString(pStr, SAL_NO_ACQUIRE);
+ SAL_INFO("drawinglayer.emf", "EMF+\tFamily: " << family);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpfont.hxx b/drawinglayer/source/tools/emfpfont.hxx
new file mode 100644
index 000000000..3f9e24166
--- /dev/null
+++ b/drawinglayer/source/tools/emfpfont.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+
+namespace emfplushelper
+{
+ const sal_uInt32 FontStyleBold = 0x00000001;
+ const sal_uInt32 FontStyleItalic = 0x00000002;
+ const sal_uInt32 FontStyleUnderline = 0x00000004;
+ const sal_uInt32 FontStyleStrikeout = 0x00000008;
+
+
+ struct EMFPFont : public EMFPObject
+ {
+ float emSize;
+ sal_uInt32 sizeUnit;
+ sal_Int32 fontFlags;
+ OUString family;
+
+ void Read(SvMemoryStream &s);
+
+ bool Bold() const { return fontFlags & FontStyleBold; }
+ bool Italic() const { return fontFlags & FontStyleItalic; }
+ bool Underline() const { return fontFlags & FontStyleUnderline; }
+ bool Strikeout() const { return fontFlags & FontStyleStrikeout; }
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
new file mode 100644
index 000000000..aaa9b1bf7
--- /dev/null
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -0,0 +1,2268 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "emfpcustomlinecap.hxx"
+#include "emfphelperdata.hxx"
+#include "emfpbrush.hxx"
+#include "emfppen.hxx"
+#include "emfppath.hxx"
+#include "emfpregion.hxx"
+#include "emfpimage.hxx"
+#include "emfpimageattributes.hxx"
+#include "emfpfont.hxx"
+#include "emfpstringformat.hxx"
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <wmfemfhelper.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <algorithm>
+
+namespace emfplushelper
+{
+
+ enum
+ {
+ WrapModeTile = 0x00000000,
+ WrapModeTileFlipX = 0x00000001,
+ WrapModeTileFlipY = 0x00000002,
+ WrapModeTileFlipXY = 0x00000003,
+ WrapModeClamp = 0x00000004
+ };
+
+ const char* emfTypeToName(sal_uInt16 type)
+ {
+ switch (type)
+ {
+ case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
+ case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
+ case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment";
+ case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
+ case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
+ case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
+ case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
+ case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
+ case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
+ case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve";
+ case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
+ case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
+ case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
+ case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
+ case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
+ case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion";
+ case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
+ case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
+ case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
+ case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
+ case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
+ case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
+ case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
+ case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
+ case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
+ case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
+ case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast";
+ case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
+ case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
+ case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
+ case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
+ case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
+ case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer";
+ case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
+ case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
+ case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
+ case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
+ case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
+ case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
+ case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
+ case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
+ case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip";
+ case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
+ case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
+ case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
+ case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip";
+ case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
+ }
+ return "";
+ }
+
+ static OUString emfObjectToName(sal_uInt16 type)
+ {
+ switch (type)
+ {
+ case EmfPlusObjectTypeBrush: return "EmfPlusObjectTypeBrush";
+ case EmfPlusObjectTypePen: return "EmfPlusObjectTypePen";
+ case EmfPlusObjectTypePath: return "EmfPlusObjectTypePath";
+ case EmfPlusObjectTypeRegion: return "EmfPlusObjectTypeRegion";
+ case EmfPlusObjectTypeImage: return "EmfPlusObjectTypeImage";
+ case EmfPlusObjectTypeFont: return "EmfPlusObjectTypeFont";
+ case EmfPlusObjectTypeStringFormat: return "EmfPlusObjectTypeStringFormat";
+ case EmfPlusObjectTypeImageAttributes: return "EmfPlusObjectTypeImageAttributes";
+ case EmfPlusObjectTypeCustomLineCap: return "EmfPlusObjectTypeCustomLineCap";
+ }
+ return "";
+ }
+
+ static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
+ {
+ switch (nPixelOffset)
+ {
+ case PixelOffsetMode::PixelOffsetModeDefault: return "PixelOffsetModeDefault";
+ case PixelOffsetMode::PixelOffsetModeHighSpeed: return "PixelOffsetModeHighSpeed";
+ case PixelOffsetMode::PixelOffsetModeHighQuality: return "PixelOffsetModeHighQuality";
+ case PixelOffsetMode::PixelOffsetModeNone: return "PixelOffsetModeNone";
+ case PixelOffsetMode::PixelOffsetModeHalf: return "PixelOffsetModeHalf";
+ }
+ return "";
+ }
+
+ static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
+ {
+ switch (nSmoothMode)
+ {
+ case SmoothingMode::SmoothingModeDefault: return "SmoothingModeDefault";
+ case SmoothingMode::SmoothingModeHighSpeed: return "SmoothModeHighSpeed";
+ case SmoothingMode::SmoothingModeHighQuality: return "SmoothingModeHighQuality";
+ case SmoothingMode::SmoothingModeNone: return "SmoothingModeNone";
+ case SmoothingMode::SmoothingModeAntiAlias8x4: return "SmoothingModeAntiAlias8x4";
+ case SmoothingMode::SmoothingModeAntiAlias8x8: return "SmoothingModeAntiAlias8x8";
+ }
+ return "";
+ }
+
+ static OUString TextRenderingHintToString(sal_uInt16 nHint)
+ {
+ switch (nHint)
+ {
+ case TextRenderingHint::TextRenderingHintSystemDefault: return "TextRenderingHintSystemDefault";
+ case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return "TextRenderingHintSingleBitPerPixelGridFit";
+ case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return "TextRenderingHintSingleBitPerPixel";
+ case TextRenderingHint::TextRenderingHintAntialiasGridFit: return "TextRenderingHintAntialiasGridFit";
+ case TextRenderingHint::TextRenderingHintAntialias: return "TextRenderingHintAntialias";
+ case TextRenderingHint::TextRenderingHintClearTypeGridFit: return "TextRenderingHintClearTypeGridFit";
+ }
+ return "";
+ }
+
+ static OUString InterpolationModeToString(sal_uInt16 nMode)
+ {
+ switch (nMode)
+ {
+ case InterpolationMode::InterpolationModeDefault: return "InterpolationModeDefault";
+ case InterpolationMode::InterpolationModeLowQuality: return "InterpolationModeLowQuality";
+ case InterpolationMode::InterpolationModeHighQuality: return "InterpolationModeHighQuality";
+ case InterpolationMode::InterpolationModeBilinear: return "InterpolationModeBilinear";
+ case InterpolationMode::InterpolationModeBicubic: return "InterpolationModeBicubic";
+ case InterpolationMode::InterpolationModeNearestNeighbor: return "InterpolationModeNearestNeighbor";
+ case InterpolationMode::InterpolationModeHighQualityBilinear: return "InterpolationModeHighQualityBilinear";
+ case InterpolationMode::InterpolationModeHighQualityBicubic: return "InterpolationModeHighQualityBicubic";
+ }
+ return "";
+ }
+
+ OUString UnitTypeToString(sal_uInt16 nType)
+ {
+ switch (nType)
+ {
+ case UnitTypeWorld: return "UnitTypeWorld";
+ case UnitTypeDisplay: return "UnitTypeDisplay";
+ case UnitTypePixel: return "UnitTypePixel";
+ case UnitTypePoint: return "UnitTypePoint";
+ case UnitTypeInch: return "UnitTypeInch";
+ case UnitTypeDocument: return "UnitTypeDocument";
+ case UnitTypeMillimeter: return "UnitTypeMillimeter";
+ }
+ return "";
+ }
+
+ static bool IsBrush(sal_uInt16 flags)
+ {
+ return (!((flags >> 15) & 0x0001));
+ }
+
+ static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid)
+ {
+ if (IsBrush(flags))
+ return "EmfPlusBrush ID: " + OUString::number(brushid);
+ else
+ return "ARGB: 0x" + OUString::number(brushid, 16);
+ }
+
+ EMFPObject::~EMFPObject()
+ {
+ }
+
+ float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI)
+ {
+ switch (aUnitType)
+ {
+ case UnitTypePixel:
+ return 1.0f;
+
+ case UnitTypePoint:
+ return aDPI / 72.0;
+
+ case UnitTypeInch:
+ return aDPI;
+
+ case UnitTypeMillimeter:
+ return aDPI / 25.4;
+
+ case UnitTypeDocument:
+ return aDPI / 300.0;
+
+ case UnitTypeWorld:
+ case UnitTypeDisplay:
+ SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
+ return 1.0f;
+
+ default:
+ SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
+ return 1.0f;
+ }
+ }
+
+ void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
+ {
+ sal_uInt16 objecttype = flags & 0x7f00;
+ sal_uInt16 index = flags & 0xff;
+ SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")");
+ SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index);
+ SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00));
+
+ switch (objecttype)
+ {
+ case EmfPlusObjectTypeBrush:
+ {
+ EMFPBrush *brush = new EMFPBrush();
+ maEMFPObjects[index].reset(brush);
+ brush->Read(rObjectStream, *this);
+ break;
+ }
+ case EmfPlusObjectTypePen:
+ {
+ EMFPPen *pen = new EMFPPen();
+ maEMFPObjects[index].reset(pen);
+ pen->Read(rObjectStream, *this);
+ pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI);
+ break;
+ }
+ case EmfPlusObjectTypePath:
+ {
+ sal_uInt32 aVersion, aPathPointCount, aPathPointFlags;
+
+ rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << std::hex << aVersion);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tNumber of points: " << std::dec << aPathPointCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec);
+ EMFPPath *path = new EMFPPath(aPathPointCount);
+ maEMFPObjects[index].reset(path);
+ path->Read(rObjectStream, aPathPointFlags);
+ break;
+ }
+ case EmfPlusObjectTypeRegion:
+ {
+ EMFPRegion *region = new EMFPRegion();
+ maEMFPObjects[index].reset(region);
+ region->ReadRegion(rObjectStream, *this);
+ break;
+ }
+ case EmfPlusObjectTypeImage:
+ {
+ EMFPImage *image = new EMFPImage;
+ maEMFPObjects[index].reset(image);
+ image->type = 0;
+ image->width = 0;
+ image->height = 0;
+ image->stride = 0;
+ image->pixelFormat = 0;
+ image->Read(rObjectStream, dataSize, bUseWholeStream);
+ break;
+ }
+ case EmfPlusObjectTypeFont:
+ {
+ EMFPFont *font = new EMFPFont;
+ maEMFPObjects[index].reset(font);
+ font->emSize = 0;
+ font->sizeUnit = 0;
+ font->fontFlags = 0;
+ font->Read(rObjectStream);
+ // tdf#113624 Convert unit to Pixels
+ font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI);
+
+ break;
+ }
+ case EmfPlusObjectTypeStringFormat:
+ {
+ EMFPStringFormat *stringFormat = new EMFPStringFormat();
+ maEMFPObjects[index].reset(stringFormat);
+ stringFormat->Read(rObjectStream);
+ break;
+ }
+ case EmfPlusObjectTypeImageAttributes:
+ {
+ EMFPImageAttributes *imageAttributes = new EMFPImageAttributes();
+ maEMFPObjects[index].reset(imageAttributes);
+ imageAttributes->Read(rObjectStream);
+ break;
+ }
+ case EmfPlusObjectTypeCustomLineCap:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented");
+ break;
+ }
+ default:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
+ }
+ }
+ }
+
+ void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
+ {
+ if (flags & 0x800)
+ {
+ // specifies a location in the coordinate space that is relative to
+ // the location specified by the previous element in the array. In the case of the first element in
+ // PointData, a previous location at coordinates (0,0) is assumed.
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
+ }
+
+ if (flags & 0x4000)
+ {
+ sal_Int16 ix, iy;
+
+ s.ReadInt16(ix).ReadInt16(iy);
+
+ x = ix;
+ y = iy;
+ }
+ else
+ {
+ s.ReadFloat(x).ReadFloat(y);
+ }
+ }
+
+ void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
+ {
+ if (bCompressed)
+ {
+ sal_Int16 ix, iy, iw, ih;
+
+ s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);
+
+ x = ix;
+ y = iy;
+ width = iw;
+ height = ih;
+ }
+ else
+ {
+ s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
+ }
+ }
+
+ bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
+ {
+ rTarget.identity();
+
+ if (sizeof(float) != 4)
+ {
+ OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
+ return false;
+ }
+ else
+ {
+ float eM11(0.0);
+ float eM12(0.0);
+ float eM21(0.0);
+ float eM22(0.0);
+ float eDx(0.0);
+ float eDy(0.0);
+ rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
+ rTarget = basegfx::B2DHomMatrix(
+ eM11, eM21, eDx,
+ eM12, eM22, eDy);
+ }
+
+ return true;
+ }
+
+ void EmfPlusHelperData::mappingChanged()
+ {
+ if (mnPixX == 0 || mnPixY == 0)
+ {
+ SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0");
+ return;
+ }
+ // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes.
+ // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should
+ // be used in the future, this method will need to be called.
+ //
+ // Re-calculate maMapTransform to contain the complete former transformation so that
+ // it can be applied by a single matrix multiplication or be added to an encapsulated
+ // primitive later
+ //
+ // To evtl. correct and see where this came from, please compare with the implementations
+ // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions
+ maMapTransform = maWorldTransform;
+ maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
+ double(-mnFrameLeft), double(-mnFrameTop));
+ maMapTransform *= maBaseTransform;
+
+ // Used only for performance optimization, to do not calculate it every line draw
+ mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b());
+ mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d());
+ }
+
+ ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
+ {
+ // map in one step using complete MapTransform (see mappingChanged)
+ return maMapTransform * ::basegfx::B2DPoint(ix, iy);
+ }
+
+ Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const {
+ Color color;
+ if (flags & 0x8000) // we use a color
+ {
+ color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
+ (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
+ }
+ else // we use a brush
+ {
+ const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
+ if (brush)
+ {
+ color = brush->GetColor();
+ if (brush->type != BrushTypeSolidColor)
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported");
+ }
+ }
+ return color;
+ }
+
+ void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index)
+ {
+ GraphicStateMap::iterator iter = map.find( index );
+
+ if ( iter != map.end() )
+ {
+ map.erase( iter );
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased");
+ }
+
+ wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current();
+ // tdf#112500 We need to save world transform somehow, during graphic state push
+ state.setTransformation(maWorldTransform);
+ map[ index ] = state;
+ }
+
+ void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index)
+ {
+ GraphicStateMap::iterator iter = map.find(index);
+
+ if (iter != map.end())
+ {
+ wmfemfhelper::PropertyHolder state = iter->second;
+
+ maWorldTransform = state.getTransformation();
+ if (state.getClipPolyPolygonActive())
+ {
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Restore clipping region to saved in index: " << index);
+ wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
+ wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ mappingChanged();
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t\tStack index: " << index
+ << " found, maWorldTransform: " << maWorldTransform);
+ }
+ }
+
+ drawinglayer::attribute::LineStartEndAttribute
+ EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const
+ {
+ const double pw = mdExtractedYScale * aPenWidth;
+ if (aCap == LineCapTypeSquare)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeRound)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
+ {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236},
+ {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeTriangle)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeSquareAnchor)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeRoundAnchor)
+ {
+ const basegfx::B2DPolygon aCapPolygon
+ = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeDiamondAnchor)
+ {
+ basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5},
+ {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5},
+ {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeArrowAnchor)
+ {
+ basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ return drawinglayer::attribute::LineStartEndAttribute();
+ }
+
+ void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon,
+ sal_uInt32 penIndex)
+ {
+ const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
+ SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
+
+ if (!(pen && polygon.count()))
+ return;
+
+ const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
+ drawinglayer::attribute::LineAttribute lineAttribute(
+ pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin,
+ css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here
+ pen->fMiterMinimumAngle);
+
+ drawinglayer::attribute::LineStartEndAttribute aStart;
+ if (pen->penDataFlags & EmfPlusPenDataStartCap)
+ {
+ if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
+ && (pen->customStartCap->polygon.begin()->count() > 1))
+ aStart = drawinglayer::attribute::LineStartEndAttribute(
+ pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
+ * pen->customStartCap->widthScale * pen->penWidth,
+ pen->customStartCap->polygon, false);
+ else
+ aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
+ }
+
+ drawinglayer::attribute::LineStartEndAttribute aEnd;
+ if (pen->penDataFlags & EmfPlusPenDataEndCap)
+ {
+ if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
+ && (pen->customEndCap->polygon.begin()->count() > 1))
+ aEnd = drawinglayer::attribute::LineStartEndAttribute(
+ pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
+ * pen->customEndCap->widthScale * pen->penWidth,
+ pen->customEndCap->polygon, false);
+ else
+ aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
+ }
+
+ if (pen->GetColor().IsTransparent())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aContainer;
+ if (aStart.isDefault() && aEnd.isDefault())
+ aContainer.append(drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))));
+ else
+ {
+ aContainer.resize(polygon.count());
+ for (sal_uInt32 i = 0; i < polygon.count(); i++)
+ aContainer[i] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+ polygon.getB2DPolygon(i), lineAttribute,
+ pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
+ }
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0));
+ }
+ else
+ {
+ if (aStart.isDefault() && aEnd.isDefault())
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
+ else
+ for (sal_uInt32 i = 0; i < polygon.count(); i++)
+ {
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+ polygon.getB2DPolygon(i), lineAttribute,
+ pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
+ }
+ }
+ mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
+ mrPropertyHolders.Current().setLineColorActive(true);
+ mrPropertyHolders.Current().setFillColorActive(false);
+ }
+
+ void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color)
+ {
+ if (color.GetAlpha() == 0)
+ return;
+
+ if (!color.IsTransparent())
+ {
+ // not transparent
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ polygon,
+ color.getBColor()));
+ }
+ else
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ polygon,
+ color.getBColor()));
+
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitive },
+ (255 - color.GetAlpha()) / 255.0));
+ }
+ }
+
+ void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor)
+ {
+ if (!polygon.count())
+ return;
+
+ if (isColor) // use Color
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec);
+
+ // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background,
+ // ranging from 0 for completely transparent to 0xFF for completely opaque.
+ const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
+ EMFPPlusFillPolygonSolidColor(polygon, color);
+
+ mrPropertyHolders.Current().setFillColor(color.getBColor());
+ mrPropertyHolders.Current().setFillColorActive(true);
+ mrPropertyHolders.Current().setLineColorActive(false);
+ }
+ else // use Brush
+ {
+ EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");
+
+ // give up in case something wrong happened
+ if( !brush )
+ return;
+
+ mrPropertyHolders.Current().setFillColorActive(false);
+ mrPropertyHolders.Current().setLineColorActive(false);
+
+ if (brush->type == BrushTypeSolidColor)
+ {
+ Color fillColor = brush->solidColor;
+ EMFPPlusFillPolygonSolidColor(polygon, fillColor);
+ }
+ else if (brush->type == BrushTypeHatchFill)
+ {
+ // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
+ // for the others the hatch "background" color (secondColor in brush) is used.
+
+ bool isHatchBlend = true;
+ double blendFactor = 0.0;
+
+ switch (brush->hatchStyle)
+ {
+ case HatchStyle05Percent: blendFactor = 0.05; break;
+ case HatchStyle10Percent: blendFactor = 0.10; break;
+ case HatchStyle20Percent: blendFactor = 0.20; break;
+ case HatchStyle25Percent: blendFactor = 0.25; break;
+ case HatchStyle30Percent: blendFactor = 0.30; break;
+ case HatchStyle40Percent: blendFactor = 0.40; break;
+ case HatchStyle50Percent: blendFactor = 0.50; break;
+ case HatchStyle60Percent: blendFactor = 0.60; break;
+ case HatchStyle70Percent: blendFactor = 0.70; break;
+ case HatchStyle75Percent: blendFactor = 0.75; break;
+ case HatchStyle80Percent: blendFactor = 0.80; break;
+ case HatchStyle90Percent: blendFactor = 0.90; break;
+ default:
+ isHatchBlend = false;
+ break;
+ }
+ Color fillColor;
+ if (isHatchBlend)
+ {
+ fillColor = brush->solidColor;
+ fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
+ }
+ else
+ {
+ fillColor = brush->secondColor;
+ }
+ // temporal solution: create a solid colored polygon
+ // TODO create a 'real' hatching primitive
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ polygon,
+ fillColor.getBColor()));
+ }
+ else if (brush->type == BrushTypeTextureFill)
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
+ }
+ else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
+
+ {
+ if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: ");
+ }
+ ::basegfx::B2DHomMatrix aTextureTransformation;
+
+ if (brush->hasTransformation) {
+ aTextureTransformation = brush->brush_transformation;
+
+ // adjust aTextureTransformation for our world space:
+ // -> revert the mapping -> apply the transformation -> map back
+ basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform);
+ aInvertedMapTrasform.invert();
+ aTextureTransformation = maMapTransform * aTextureTransformation * aInvertedMapTrasform;
+ }
+
+ // select the stored colors
+ const basegfx::BColor aStartColor = brush->solidColor.getBColor();
+ const basegfx::BColor aEndColor = brush->secondColor.getBColor();
+ drawinglayer::primitive2d::SvgGradientEntryVector aVector;
+
+ if (brush->blendPositions)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend");
+
+ // store the blendpoints in the vector
+ for (sal_uInt32 i = 0; i < brush->blendPoints; i++)
+ {
+ const double aBlendPoint = brush->blendPositions[i];
+ basegfx::BColor aColor;
+ aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen()));
+ aColor.setBlue (aStartColor.getBlue() + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue()));
+ aColor.setRed (aStartColor.getRed() + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed()));
+ const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
+ aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
+ }
+ }
+ else if (brush->colorblendPositions)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend");
+
+ // store the colorBlends in the vector
+ for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++)
+ {
+ const double aBlendPoint = brush->colorblendPositions[i];
+ const basegfx::BColor aColor = brush->colorblendColors[i].getBColor();
+ aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0);
+ }
+ }
+ else // ok, no extra points: just start and end
+ {
+ aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
+ aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
+ }
+
+ // get the polygon range to be able to map the start/end/center point correctly
+ // therefore, create a mapping and invert it
+ basegfx::B2DRange aPolygonRange= polygon.getB2DRange();
+ basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aPolygonRange.getWidth(),aPolygonRange.getHeight(),
+ aPolygonRange.getMinX(), aPolygonRange.getMinY());
+ aPolygonTransformation.invert();
+
+ if (brush->type == BrushTypeLinearGradient)
+ {
+ // support for public enum EmfPlusWrapMode
+ basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0);
+ aStartPoint = aPolygonTransformation * aStartPoint;
+ basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0);
+ aEndPoint = aPolygonTransformation * aEndPoint;
+
+ // support for public enum EmfPlusWrapMode
+ drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
+ switch(brush->wrapMode)
+ {
+ case WrapModeTile:
+ case WrapModeTileFlipY:
+ {
+ aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat;
+ break;
+ }
+ case WrapModeTileFlipX:
+ case WrapModeTileFlipXY:
+ {
+ aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // create the same one used for SVG
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
+ aTextureTransformation,
+ polygon,
+ std::move(aVector),
+ aStartPoint,
+ aEndPoint,
+ false, // do not use UnitCoordinates
+ aSpreadMethod));
+ }
+ else // BrushTypePathGradient
+ { // TODO The PathGradient is not implemented, and Radial Gradient is used instead
+ basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY);
+ aCenterPoint = aPolygonTransformation * aCenterPoint;
+
+ // create the same one used for SVG
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
+ aTextureTransformation,
+ polygon,
+ std::move(aVector),
+ aCenterPoint,
+ 0.7, // relative radius little bigger to cover all elements
+ true, // use UnitCoordinates to stretch the gradient
+ drawinglayer::primitive2d::SpreadMethod::Pad,
+ nullptr));
+ }
+ }
+ }
+ }
+
+ EmfPlusHelperData::EmfPlusHelperData(
+ SvMemoryStream& rMS,
+ wmfemfhelper::TargetHolders& rTargetHolders,
+ wmfemfhelper::PropertyHolders& rPropertyHolders)
+ : mfPageScale(0.0),
+ mnOriginX(0),
+ mnOriginY(0),
+ mnHDPI(0),
+ mnVDPI(0),
+ mbSetTextContrast(false),
+ mnTextContrast(0),
+ mnFrameLeft(0),
+ mnFrameTop(0),
+ mnFrameRight(0),
+ mnFrameBottom(0),
+ mnPixX(0),
+ mnPixY(0),
+ mnMmX(0),
+ mnMmY(0),
+ mbMultipart(false),
+ mMFlags(0),
+ mrTargetHolders(rTargetHolders),
+ mrPropertyHolders(rPropertyHolders),
+ bIsGetDCProcessing(false)
+ {
+ rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
+ SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
+ rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
+ SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
+ readXForm(rMS, maBaseTransform);
+ SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform);
+ mappingChanged();
+ }
+
+ EmfPlusHelperData::~EmfPlusHelperData()
+ {
+ }
+
+ ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon)
+ {
+ basegfx::B2DPolyPolygon aClippedPolyPolygon;
+ switch (combineMode)
+ {
+ case EmfPlusCombineModeReplace:
+ {
+ aClippedPolyPolygon = rightPolygon;
+ break;
+ }
+ case EmfPlusCombineModeIntersect:
+ {
+ aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ leftPolygon, rightPolygon, true, false);
+ break;
+ }
+ case EmfPlusCombineModeUnion:
+ {
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon);
+ break;
+ }
+ case EmfPlusCombineModeXOR:
+ {
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon);
+ break;
+ }
+ case EmfPlusCombineModeExclude:
+ {
+ // Replaces the existing region with the part of itself that is not in the new region.
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon);
+ break;
+ }
+ case EmfPlusCombineModeComplement:
+ {
+ // Replaces the existing region with the part of the new region that is not in the existing region.
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon);
+ break;
+ }
+ }
+ return aClippedPolyPolygon;
+ }
+
+ void EmfPlusHelperData::processEmfPlusData(
+ SvMemoryStream& rMS,
+ const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/)
+ {
+ sal_uInt64 length = rMS.GetSize();
+
+ if (length < 12)
+ SAL_WARN("drawinglayer.emf", "length is less than required header size");
+
+ // 12 is minimal valid EMF+ record size; remaining bytes are padding
+ while (length >= 12)
+ {
+ sal_uInt16 type, flags;
+ sal_uInt32 size, dataSize;
+ sal_uInt64 next;
+
+ rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);
+
+ next = rMS.Tell() + (size - 12);
+
+ if (size < 12)
+ {
+ SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes");
+ break;
+ }
+ else if (size > length)
+ {
+ SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left");
+ break;
+ }
+
+ if (dataSize > (size - 12))
+ {
+ SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12");
+ break;
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size);
+ SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize);
+
+ if (bIsGetDCProcessing)
+ {
+ if (aGetDCState.getClipPolyPolygonActive())
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved");
+ wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
+ wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ bIsGetDCProcessing = false;
+ }
+ if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
+ {
+ if (!mbMultipart)
+ {
+ mbMultipart = true;
+ mMFlags = flags;
+ mMStream.Seek(0);
+ }
+
+ OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
+
+ // 1st 4 bytes are TotalObjectSize
+ mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
+ SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
+ }
+ else
+ {
+ if (mbMultipart)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags);
+ mMStream.Seek(0);
+ processObjectRecord(mMStream, mMFlags, 0, true);
+ }
+
+ mbMultipart = false;
+ }
+
+ if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
+ {
+ switch (type)
+ {
+ case EmfPlusRecordTypeHeader:
+ {
+ sal_uInt32 header, version;
+
+ rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
+ SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << header);
+ SAL_INFO("drawinglayer.emf", "EMF+\tVersion: " << std::dec << version);
+ SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI);
+ SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI);
+ SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
+ break;
+ }
+ case EmfPlusRecordTypeEndOfFile:
+ {
+ break;
+ }
+ case EmfPlusRecordTypeComment:
+ {
+#if OSL_DEBUG_LEVEL > 1
+ unsigned char data;
+ OUString hexdata;
+
+ SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec);
+
+ for (sal_uInt32 i=0; i<dataSize; i++)
+ {
+ rMS.ReadUChar(data);
+
+ if (i % 16 == 0)
+ hexdata += "\n";
+
+ OUString padding;
+ if ((data & 0xF0) == 0)
+ padding = "0";
+
+ hexdata += "0x" + padding + OUString::number(data, 16) + " ";
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata);
+#endif
+ break;
+ }
+ case EmfPlusRecordTypeGetDC:
+ {
+ bIsGetDCProcessing = true;
+ aGetDCState = mrPropertyHolders.Current();
+ SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
+ break;
+ }
+ case EmfPlusRecordTypeObject:
+ {
+ processObjectRecord(rMS, flags, dataSize);
+ break;
+ }
+ case EmfPlusRecordTypeFillPie:
+ case EmfPlusRecordTypeDrawPie:
+ case EmfPlusRecordTypeDrawArc:
+ {
+ float startAngle, sweepAngle;
+
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999;
+
+ if (type == EmfPlusRecordTypeFillPie)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor);
+ }
+ else if (type == EmfPlusRecordTypeDrawPie)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie");
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc");
+ }
+
+ rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+ SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+ startAngle = basegfx::deg2rad(startAngle);
+ sweepAngle = basegfx::deg2rad(sweepAngle);
+ float endAngle = startAngle + sweepAngle;
+ startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));
+
+ if (startAngle < 0.0)
+ {
+ startAngle += static_cast<float>(M_PI * 2.0);
+ }
+ endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0));
+
+ if (endAngle < 0.0)
+ {
+ endAngle += static_cast<float>(M_PI * 2.0);
+ }
+ if (sweepAngle < 0)
+ {
+ std::swap(endAngle, startAngle);
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " <<
+ basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
+ " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
+ const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh);
+ ::basegfx::B2DPolygon polygon(
+ ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint,
+ 0.5 * dw, 0.5 * dh,
+ startAngle, endAngle));
+ if (type != EmfPlusRecordTypeDrawArc)
+ {
+ polygon.append(centerPoint);
+ polygon.setClosed(true);
+ }
+ ::basegfx::B2DPolyPolygon polyPolygon(polygon);
+ polyPolygon.transform(maMapTransform);
+ if (type == EmfPlusRecordTypeFillPie)
+ EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
+ }
+ break;
+ case EmfPlusRecordTypeFillPath:
+ {
+ sal_uInt32 index = flags & 0xff;
+ sal_uInt32 brushIndexOrColor;
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index);
+
+ EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get());
+ if (path)
+ EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
+ else
+ SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path");
+ }
+ break;
+ case EmfPlusRecordTypeFillRegion:
+ {
+ sal_uInt32 index = flags & 0xff;
+ sal_uInt32 brushIndexOrColor;
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index);
+
+ EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
+ if (region)
+ EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
+ else
+ SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
+ }
+ break;
+ case EmfPlusRecordTypeDrawEllipse:
+ case EmfPlusRecordTypeFillEllipse:
+ {
+ // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
+ // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
+ // when it is later used.
+ sal_uInt32 brushIndexOrColor = 1234567;
+
+ if (type == EmfPlusRecordTypeFillEllipse)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+ SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+ ::basegfx::B2DPolyPolygon polyPolygon(
+ ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh),
+ 0.5 * dw, 0.5 * dh));
+ polyPolygon.transform(maMapTransform);
+ if (type == EmfPlusRecordTypeFillEllipse)
+ EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
+ }
+ break;
+ case EmfPlusRecordTypeFillRects:
+ case EmfPlusRecordTypeDrawRects:
+ {
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999;
+ sal_Int32 rectangles;
+ const bool isColor = (flags & 0x8000);
+ ::basegfx::B2DPolygon polygon;
+
+ if (EmfPlusRecordTypeFillRects == type)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t FillRects");
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects");
+ }
+
+ rMS.ReadInt32(rectangles);
+
+ for (int i = 0; i < rectangles; i++)
+ {
+ float x, y, width, height;
+ ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
+ polygon.clear();
+ polygon.append(Map(x, y));
+ polygon.append(Map(x + width, y));
+ polygon.append(Map(x + width, y + height));
+ polygon.append(Map(x, y + height));
+ polygon.setClosed(true);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
+
+ ::basegfx::B2DPolyPolygon polyPolygon(polygon);
+ if (type == EmfPlusRecordTypeFillRects)
+ EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
+ }
+ break;
+ }
+ case EmfPlusRecordTypeFillPolygon:
+ {
+ sal_uInt32 brushIndexOrColor, points;
+
+ rMS.ReadUInt32(brushIndexOrColor);
+ rMS.ReadUInt32(points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec);
+
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+
+ EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
+ break;
+ }
+ case EmfPlusRecordTypeDrawLines:
+ {
+ sal_uInt32 points;
+ rMS.ReadUInt32(points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+
+ // 0x2000 bit indicates whether to draw an extra line between the last point
+ // and the first point, to close the shape.
+ EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), flags);
+
+ break;
+ }
+ case EmfPlusRecordTypeDrawPath:
+ {
+ sal_uInt32 penIndex;
+ rMS.ReadUInt32(penIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Pen: " << penIndex);
+
+ EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
+ if (path)
+ EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex);
+ else
+ SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path");
+
+ break;
+ }
+ case EmfPlusRecordTypeDrawBeziers:
+ {
+ sal_uInt32 aCount;
+ float x1, y1, x2, y2, x3, y3, x4, y4;
+ ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint;
+ ::basegfx::B2DPolygon aPolygon;
+ rMS.ReadUInt32(aCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
+ SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
+ SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
+ "EMF+\t Bezier Draw not support number of points other than 4, 7, "
+ "10, 13, 16...");
+
+ if (aCount < 4)
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
+ "than 4 points. Number of points: "
+ << aCount);
+ break;
+ }
+
+ ReadPoint(rMS, x1, y1, flags);
+ // We need to add first starting point
+ aStartPoint = Map(x1, y1);
+ aPolygon.append(aStartPoint);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Bezier starting point: " << x1 << "," << y1);
+ for (sal_uInt32 i = 4; i <= aCount; i += 3)
+ {
+ ReadPoint(rMS, x2, y2, flags);
+ ReadPoint(rMS, x3, y3, flags);
+ ReadPoint(rMS, x4, y4, flags);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
+ << y3 << " " << x4 << "," << y4);
+
+ aControlPointA = Map(x2, y2);
+ aControlPointB = Map(x3, y3);
+ aEndPoint = Map(x4, y4);
+ aPolygon.appendBezierSegment(aControlPointA, aControlPointB, aEndPoint);
+ // The ending coordinate of one Bezier curve is the starting coordinate of the next.
+ aStartPoint = aEndPoint;
+ }
+ EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
+ break;
+ }
+ case EmfPlusRecordTypeDrawClosedCurve:
+ case EmfPlusRecordTypeFillClosedCurve:
+ {
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999, points;
+ float aTension;
+ if (type == EmfPlusRecordTypeFillClosedCurve)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate"));
+ }
+ rMS.ReadFloat(aTension);
+ rMS.ReadUInt32(points);
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t Tension: " << aTension << " Points: " << points);
+ SAL_WARN_IF(aTension != 0, "drawinglayer.emf",
+ "EMF+\t TODO Add support for tension different than 0");
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
+ << std::hex << brushIndexOrColor << std::dec);
+
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+ if (type == EmfPlusRecordTypeFillClosedCurve)
+ EMFPPlusFillPolygon(path.GetPolygon(*this, /* bMapIt */ true,
+ /*bAddLineToCloseShape */ true),
+ flags & 0x8000, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(path.GetPolygon(*this, /* bMapIt */ true,
+ /*bAddLineToCloseShape */ true),
+ flags & 0xff);
+ break;
+ }
+ case EmfPlusRecordTypeDrawImage:
+ case EmfPlusRecordTypeDrawImagePoints:
+ {
+ sal_uInt32 imageAttributesId;
+ sal_Int32 sourceUnit;
+ rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
+ : "DrawImagePoints")
+ << " image attributes Id: " << imageAttributesId
+ << " source unit: " << sourceUnit);
+ SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes");
+
+ // Source unit of measurement type must be 1 pixel
+ if (EMFPImage* image = sourceUnit == UnitTypePixel ?
+ dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) :
+ nullptr)
+ {
+ float sx, sy, sw, sh;
+ ReadRectangle(rMS, sx, sy, sw, sh);
+
+ ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t "
+ << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
+ : "DrawImagePoints")
+ << " source rectangle: " << sx << "," << sy << " " << sw << "x"
+ << sh);
+
+ float dx(0.), dy(0.), dw(0.), dh(0.);
+ double fShearX = 0.0;
+ double fShearY = 0.0;
+ if (type == EmfPlusRecordTypeDrawImagePoints)
+ {
+ sal_uInt32 aCount;
+ rMS.ReadUInt32(aCount);
+
+ // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
+ if (aCount != 3)
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected "
+ "3 points, received: "
+ << aCount);
+ break;
+ }
+ float x1, y1, x2, y2, x3, y3;
+
+ ReadPoint(rMS, x1, y1, flags); // upper-left point
+ ReadPoint(rMS, x2, y2, flags); // upper-right
+ ReadPoint(rMS, x3, y3, flags); // lower-left
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t destination points: "
+ << x1 << "," << y1 << " " << x2 << ","
+ << y2 << " " << x3 << "," << y3);
+ dx = x1;
+ dy = y2;
+ dw = x2 - x1;
+ dh = y3 - y1;
+ fShearX = x3 - x1;
+ fShearY = y2 - y1;
+ }
+ else if (type == EmfPlusRecordTypeDrawImage)
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
+ Size aSize;
+ if (image->type == ImageDataTypeBitmap)
+ {
+ aSize = image->graphic.GetBitmapEx().GetSizePixel();
+ SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width()
+ << "x"
+ << aSize.Height());
+ if (sx < 0)
+ {
+ // If src position is negative then we need shift image to right
+ dx = dx + ((-sx) / sw) * dw;
+ if (sx + sw <= aSize.Width())
+ dw = ((sw + sx) / sw) * dw;
+ else
+ dw = (aSize.Width() / sw) * dw;
+ }
+ else if (sx + sw > aSize.Width())
+ // If the src image is smaller that what we want to cut, then we need to scale down
+ dw = ((aSize.Width() - sx) / sw) * dw;
+
+ if (sy < 0)
+ {
+ dy = dy + ((-sy) / sh) * dh;
+ if (sy + sh <= aSize.Height())
+ dh = ((sh + sy) / sh) * dh;
+ else
+ dh = (aSize.Height() / sh) * dh;
+ }
+ else if (sy + sh > aSize.Height())
+ dh = ((aSize.Height() - sy) / sh) * dh;
+ }
+ else
+ SAL_INFO(
+ "drawinglayer.emf",
+ "EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile");
+ ::basegfx::B2DPoint aDstPoint(dx, dy);
+ ::basegfx::B2DSize aDstSize(dw, dh);
+
+ const basegfx::B2DHomMatrix aTransformMatrix
+ = maMapTransform
+ * basegfx::B2DHomMatrix(
+ /* Row 0, Column 0 */ aDstSize.getX(),
+ /* Row 0, Column 1 */ fShearX,
+ /* Row 0, Column 2 */ aDstPoint.getX(),
+ /* Row 1, Column 0 */ fShearY,
+ /* Row 1, Column 1 */ aDstSize.getY(),
+ /* Row 1, Column 2 */ aDstPoint.getY());
+
+ if (image->type == ImageDataTypeBitmap)
+ {
+ BitmapEx aBmp(image->graphic.GetBitmapEx());
+ aBmp.Crop(aSource);
+ aSize = aBmp.GetSizePixel();
+ if (aSize.Width() > 0 && aSize.Height() > 0)
+ {
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(aBmp), aTransformMatrix));
+ }
+ else
+ SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
+ }
+ else if (image->type == ImageDataTypeMetafile)
+ {
+ GDIMetaFile aGDI(image->graphic.GetGDIMetaFile());
+ aGDI.Clip(aSource);
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix,
+ aGDI));
+ }
+ }
+ else
+ {
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is "
+ "support by EMF+ specification for DrawImage(Points)");
+ }
+ break;
+ }
+ case EmfPlusRecordTypeDrawString:
+ {
+ sal_uInt32 brushId, formatId, stringLength;
+ rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
+ SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF));
+ SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId));
+ SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength);
+
+ // read the layout rectangle
+ float lx, ly, lw, lh;
+ rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
+ // parse the string
+ const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
+ // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
+ const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
+ // get the font from the flags
+ const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
+ if (!font)
+ {
+ break;
+ }
+ mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));
+
+ drawinglayer::attribute::FontAttribute fontAttribute(
+ font->family, // font family
+ "", // (no) font style
+ font->Bold() ? 8u : 1u, // weight: 8 = bold
+ font->family == "SYMBOL", // symbol
+ stringFormat && stringFormat->DirectionVertical(), // vertical
+ font->Italic(), // italic
+ false, // monospaced
+ false, // outline = false, no such thing in MS-EMFPLUS
+ stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left
+ false); // BiDiStrong
+
+ css::lang::Locale locale;
+ double stringAlignmentHorizontalOffset = 0.0;
+ double stringAlignmentVerticalOffset = font->emSize;
+ if (stringFormat)
+ {
+ LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language));
+ locale = aLanguageTag.getLocale();
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+
+ aTextLayouter.setFontAttribute(fontAttribute, font->emSize,
+ font->emSize, locale);
+
+ double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength);
+ SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf",
+ "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
+ if (stringFormat->stringAlignment == StringAlignmentNear)
+ // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
+ stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
+ else if (stringFormat->stringAlignment == StringAlignmentCenter)
+ // Alignment is centered between the origin and extent of the layout rectangle
+ stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth;
+ else if (stringFormat->stringAlignment == StringAlignmentFar)
+ // Alignment is to the right side of the layout rectangle
+ stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth;
+
+ if (stringFormat->lineAlign == StringAlignmentNear)
+ stringAlignmentVerticalOffset = font->emSize;
+ else if (stringFormat->lineAlign == StringAlignmentCenter)
+ stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize;
+ else if (stringFormat->lineAlign == StringAlignmentFar)
+ stringAlignmentVerticalOffset = lh;
+ }
+ else
+ {
+ // By default LeadingMargin is 1/6 inch
+ // TODO for typographic fonts set value to 0.
+ stringAlignmentHorizontalOffset = 16.0;
+
+ // use system default
+ locale = Application::GetSettings().GetLanguageTag().getLocale();
+ }
+
+ const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ ::basegfx::B2DSize(font->emSize, font->emSize),
+ ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset,
+ ly + stringAlignmentVerticalOffset));
+
+ Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
+ Color color;
+
+ if (mbSetTextContrast)
+ {
+ const auto gammaVal = mnTextContrast / 1000;
+ const basegfx::BColorModifier_gamma gamma(gammaVal);
+
+ // gamma correct transparency color
+ sal_uInt16 alpha = uncorrectedColor.GetAlpha();
+ alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;
+
+ basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
+ color.SetRed(modifiedColor.getRed() * 255);
+ color.SetGreen(modifiedColor.getGreen() * 255);
+ color.SetBlue(modifiedColor.getBlue() * 255);
+ color.SetAlpha(alpha);
+ }
+ else
+ {
+ color = uncorrectedColor;
+ }
+
+ mrPropertyHolders.Current().setTextColor(color.getBColor());
+ mrPropertyHolders.Current().setTextColorActive(true);
+
+ if (color.GetAlpha() > 0)
+ {
+ std::vector<double> emptyVector;
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
+ if (font->Underline() || font->Strikeout())
+ {
+ pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+ transformMatrix,
+ text,
+ 0, // text always starts at 0
+ stringLength,
+ std::move(emptyVector), // EMF-PLUS has no DX-array
+ fontAttribute,
+ locale,
+ color.getBColor(), // Font Color
+ COL_TRANSPARENT, // Fill Color
+ color.getBColor(), // OverlineColor
+ color.getBColor(), // TextlineColor
+ drawinglayer::primitive2d::TEXT_LINE_NONE,
+ font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
+ false,
+ font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
+ }
+ else
+ {
+ pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ transformMatrix,
+ text,
+ 0, // text always starts at 0
+ stringLength,
+ std::move(emptyVector), // EMF-PLUS has no DX-array
+ fontAttribute,
+ locale,
+ color.getBColor());
+ }
+ drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
+ if (color.IsTransparent())
+ {
+ aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
+ (255 - color.GetAlpha()) / 255.0);
+ }
+
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ maMapTransform,
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
+ }
+ break;
+ }
+ case EmfPlusRecordTypeSetPageTransform:
+ {
+ rMS.ReadFloat(mfPageScale);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags));
+
+ if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification.");
+ }
+ else
+ {
+ mnMmX *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
+ mnMmY *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
+ mappingChanged();
+ }
+ break;
+ }
+ case EmfPlusRecordTypeSetRenderingOrigin:
+ {
+ rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY);
+ SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY);
+ break;
+ }
+ case EmfPlusRecordTypeSetTextContrast:
+ {
+ const sal_uInt16 LOWERGAMMA = 1000;
+ const sal_uInt16 UPPERGAMMA = 2200;
+
+ mbSetTextContrast = true;
+ mnTextContrast = flags & 0xFFF;
+ SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA,
+ "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast);
+ mnTextContrast = std::min(mnTextContrast, UPPERGAMMA);
+ mnTextContrast = std::max(mnTextContrast, LOWERGAMMA);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma");
+ break;
+ }
+ case EmfPlusRecordTypeSetTextRenderingHint:
+ {
+ sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1;
+ SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint");
+ break;
+ }
+ case EmfPlusRecordTypeSetAntiAliasMode:
+ {
+ bool bUseAntiAlias = (flags & 0x0001);
+ sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1;
+ SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled"));
+ SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode");
+ break;
+ }
+ case EmfPlusRecordTypeSetInterpolationMode:
+ {
+ sal_uInt16 nInterpolationMode = flags & 0xFF;
+ SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode");
+ break;
+ }
+ case EmfPlusRecordTypeSetPixelOffsetMode:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode");
+ break;
+ }
+ case EmfPlusRecordTypeSetCompositingQuality:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality");
+ break;
+ }
+ case EmfPlusRecordTypeSave:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex);
+
+ GraphicStatePush(mGSStack, stackIndex);
+
+ break;
+ }
+ case EmfPlusRecordTypeRestore:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex);
+
+ GraphicStatePop(mGSStack, stackIndex);
+ break;
+ }
+ case EmfPlusRecordTypeBeginContainer:
+ {
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+
+ float sx, sy, sw, sh;
+ ReadRectangle(rMS, sx, sy, sw, sh);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh);
+
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags);
+
+ if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification.");
+ break;
+ }
+ const float aPageScaleX = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
+ const float aPageScaleY = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
+ GraphicStatePush(mGSContainerStack, stackIndex);
+ const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aPageScaleX * ( dw / sw ), aPageScaleY * ( dh / sh ),
+ aPageScaleX * ( dx - sx ), aPageScaleY * ( dy - sy) );
+ maWorldTransform *= transform;
+ mappingChanged();
+ break;
+ }
+ case EmfPlusRecordTypeBeginContainerNoParams:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex);
+
+ GraphicStatePush(mGSContainerStack, stackIndex);
+ break;
+ }
+ case EmfPlusRecordTypeEndContainer:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex);
+
+ GraphicStatePop(mGSContainerStack, stackIndex);
+ break;
+ }
+ case EmfPlusRecordTypeSetWorldTransform:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000));
+ readXForm(rMS, maWorldTransform);
+ mappingChanged();
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeResetWorldTransform:
+ {
+ maWorldTransform.identity();
+ SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform);
+ mappingChanged();
+ break;
+ }
+ case EmfPlusRecordTypeMultiplyWorldTransform:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000));
+ basegfx::B2DHomMatrix transform;
+ readXForm(rMS, transform);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Transform matrix: " << transform);
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ maWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= maWorldTransform;
+ maWorldTransform = transform;
+ }
+
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeTranslateWorldTransform:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000));
+
+ basegfx::B2DHomMatrix transform;
+ float eDx, eDy;
+ rMS.ReadFloat(eDx).ReadFloat(eDy);
+ transform.set(0, 2, eDx);
+ transform.set(1, 2, eDy);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Translate matrix: " << transform);
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ maWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= maWorldTransform;
+ maWorldTransform = transform;
+ }
+
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeScaleWorldTransform:
+ {
+ basegfx::B2DHomMatrix transform;
+ float eSx, eSy;
+ rMS.ReadFloat(eSx).ReadFloat(eSy);
+ transform.set(0, 0, eSx);
+ transform.set(1, 1, eSy);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx <<
+ " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000));
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ maWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= maWorldTransform;
+ maWorldTransform = transform;
+ }
+
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeRotateWorldTransform:
+ {
+ // Angle of rotation in degrees
+ float eAngle;
+ rMS.ReadFloat(eAngle);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle <<
+ ", post multiply: " << bool(flags & 0x2000));
+ // Skipping flags & 0x2000
+ // For rotation transformation there is no difference between post and pre multiply
+ maWorldTransform.rotate(basegfx::deg2rad(eAngle));
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeResetClip:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+ ResetClip");
+ // We don't need to read anything more, as Size needs to be set 0x0000000C
+ // and DataSize must be set to 0.
+
+ // Resets the current clipping region for the world space to infinity.
+ HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
+ break;
+ }
+ case EmfPlusRecordTypeSetClipRect:
+ case EmfPlusRecordTypeSetClipPath:
+ case EmfPlusRecordTypeSetClipRegion:
+ {
+ int combineMode = (flags >> 8) & 0xf;
+ ::basegfx::B2DPolyPolygon polyPolygon;
+ if (type == EmfPlusRecordTypeSetClipRect)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect");
+
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+ ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
+ ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
+
+ polyPolygon
+ = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
+ ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(),
+ mappedPoint2.getX(), mappedPoint2.getY())));
+ }
+ else if (type == EmfPlusRecordTypeSetClipPath)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff));
+
+ EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
+ if (!path)
+ {
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
+ break;
+ }
+ polyPolygon = path->GetPolygon(*this);
+ }
+ else if (type == EmfPlusRecordTypeSetClipRegion)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
+ EMFPRegion* region
+ = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
+ if (!region)
+ {
+ SAL_WARN(
+ "drawinglayer.emf",
+ "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
+ break;
+ }
+ polyPolygon = region->regionPolyPolygon;
+ }
+ SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode);
+ ::basegfx::B2DPolyPolygon aClippedPolyPolygon;
+ if (mrPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ aClippedPolyPolygon
+ = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
+ combineMode, polyPolygon);
+ }
+ else
+ {
+ //Combine with infinity
+ switch (combineMode)
+ {
+ case EmfPlusCombineModeReplace:
+ case EmfPlusCombineModeIntersect:
+ {
+ aClippedPolyPolygon = polyPolygon;
+ break;
+ }
+ case EmfPlusCombineModeUnion:
+ {
+ // Disable clipping as the clipping is infinity
+ aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
+ break;
+ }
+ case EmfPlusCombineModeXOR:
+ case EmfPlusCombineModeComplement:
+ {
+ //TODO It is not correct and it should be fixed
+ aClippedPolyPolygon = polyPolygon;
+ break;
+ }
+ case EmfPlusCombineModeExclude:
+ {
+ //TODO It is not correct and it should be fixed
+ aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
+ break;
+ }
+ }
+ }
+ HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders);
+ break;
+ }
+ case EmfPlusRecordTypeOffsetClip:
+ {
+ float dx, dy;
+ rMS.ReadFloat(dx).ReadFloat(dy);
+ SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy);
+
+ basegfx::B2DPolyPolygon aPolyPolygon(
+ mrPropertyHolders.Current().getClipPolyPolygon());
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t PolyPolygon before translate: " << aPolyPolygon);
+
+ basegfx::B2DPoint aOffset = Map(dx, dy);
+ basegfx::B2DHomMatrix transformMatrix;
+ transformMatrix.set(0, 2, aOffset.getX());
+ transformMatrix.set(1, 2, aOffset.getY());
+ aPolyPolygon.transform(transformMatrix);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t PolyPolygon after translate: " << aPolyPolygon <<
+ ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY());
+ HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders);
+ break;
+ }
+ case EmfPlusRecordTypeDrawDriverString:
+ {
+ sal_uInt32 brushIndexOrColor;
+ sal_uInt32 optionFlags;
+ sal_uInt32 hasMatrix;
+ sal_uInt32 glyphsCount;
+ rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount);
+
+ if ((optionFlags & 1) && glyphsCount > 0)
+ {
+ std::unique_ptr<float[]> charsPosX(new float[glyphsCount]);
+ std::unique_ptr<float[]> charsPosY(new float[glyphsCount]);
+ OUString text = read_uInt16s_ToOUString(rMS, glyphsCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text);
+
+ for (sal_uInt32 i = 0; i<glyphsCount; i++)
+ {
+ rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
+ }
+
+ basegfx::B2DHomMatrix transform;
+
+ if (hasMatrix)
+ {
+ readXForm(rMS, transform);
+ SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform);
+ }
+
+ // get the font from the flags
+ EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
+ if (!font)
+ {
+ break;
+ }
+ // done reading
+
+ drawinglayer::attribute::FontAttribute fontAttribute(
+ font->family, // font family
+ "", // (no) font style
+ font->Bold() ? 8u : 1u, // weight: 8 = bold
+ font->family == "SYMBOL", // symbol
+ optionFlags & 0x2, // vertical
+ font->Italic(), // italic
+ false, // monospaced
+ false, // outline = false, no such thing in MS-EMFPLUS
+ false, // right-to-left
+ false); // BiDiStrong
+
+ const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor);
+
+ // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D
+ // for all portions of text with the same charsPosY values
+ sal_uInt32 pos = 0;
+ while (pos < glyphsCount)
+ {
+ //determine the current length
+ sal_uInt32 aLength = 1;
+ while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon())
+ aLength++;
+
+ // generate the DX-Array
+ std::vector<double> aDXArray;
+ for (size_t i = 0; i < aLength - 1; i++)
+ {
+ aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]);
+ }
+ // last entry
+ aDXArray.push_back(0);
+
+ basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ ::basegfx::B2DSize(font->emSize, font->emSize),
+ ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos]));
+ if (hasMatrix)
+ transformMatrix *= transform;
+ if (color.GetAlpha() > 0)
+ {
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
+ if (font->Underline() || font->Strikeout())
+ {
+ pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+ transformMatrix,
+ text,
+ pos, // take character at current pos
+ aLength, // use determined length
+ std::move(aDXArray), // generated DXArray
+ fontAttribute,
+ Application::GetSettings().GetLanguageTag().getLocale(),
+ color.getBColor(),
+ COL_TRANSPARENT,
+ color.getBColor(),
+ color.getBColor(),
+ drawinglayer::primitive2d::TEXT_LINE_NONE,
+ font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
+ false,
+ font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
+ }
+ else
+ {
+ pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ transformMatrix,
+ text,
+ pos, // take character at current pos
+ aLength, // use determined length
+ std::move(aDXArray), // generated DXArray
+ fontAttribute,
+ Application::GetSettings().GetLanguageTag().getLocale(),
+ color.getBColor());
+ }
+ drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
+ if (color.IsTransparent())
+ {
+ aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
+ (255 - color.GetAlpha()) / 255.0);
+ }
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ maMapTransform,
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
+ }
+
+ // update pos
+ pos += aLength;
+ }
+ }
+ else
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
+ }
+ break;
+ }
+ default:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec);
+ }
+ }
+ }
+
+ rMS.Seek(next);
+
+ if (size <= length)
+ {
+ length -= size;
+ }
+ else
+ {
+ SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: "
+ "size " << size << " > length " << length);
+ length = 0;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfphelperdata.hxx b/drawinglayer/source/tools/emfphelperdata.hxx
new file mode 100644
index 000000000..ef870f31e
--- /dev/null
+++ b/drawinglayer/source/tools/emfphelperdata.hxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <wmfemfhelper.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/attribute/linestartendattribute.hxx>
+#include <tools/stream.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <map>
+
+// predefines
+class SvStream;
+namespace basegfx { class B2DPolyPolygon; }
+
+namespace emfplushelper
+{
+ // EMF+ commands
+ #define EmfPlusRecordTypeHeader 0x4001
+ #define EmfPlusRecordTypeEndOfFile 0x4002
+ #define EmfPlusRecordTypeComment 0x4003
+ #define EmfPlusRecordTypeGetDC 0x4004
+ //TODO EmfPlusRecordTypeMultiFormatStart 0x4005
+ //TODO EmfPlusRecordTypeMultiFormatSection 0x4006
+ //TODO EmfPlusRecordTypeMultiFormatEnd 0x4007
+ #define EmfPlusRecordTypeObject 0x4008
+ //TODO EmfPlusRecordTypeClear 0x4009
+ #define EmfPlusRecordTypeFillRects 0x400A
+ #define EmfPlusRecordTypeDrawRects 0x400B
+ #define EmfPlusRecordTypeFillPolygon 0x400C
+ #define EmfPlusRecordTypeDrawLines 0x400D
+ #define EmfPlusRecordTypeFillEllipse 0x400E
+ #define EmfPlusRecordTypeDrawEllipse 0x400F
+ #define EmfPlusRecordTypeFillPie 0x4010
+ #define EmfPlusRecordTypeDrawPie 0x4011
+ #define EmfPlusRecordTypeDrawArc 0x4012
+ #define EmfPlusRecordTypeFillRegion 0x4013
+ #define EmfPlusRecordTypeFillPath 0x4014
+ #define EmfPlusRecordTypeDrawPath 0x4015
+ #define EmfPlusRecordTypeFillClosedCurve 0x4016
+ #define EmfPlusRecordTypeDrawClosedCurve 0x4017
+ //TODO EmfPlusRecordTypeDrawCurve 0x4018
+ #define EmfPlusRecordTypeDrawBeziers 0x4019
+ #define EmfPlusRecordTypeDrawImage 0x401A
+ #define EmfPlusRecordTypeDrawImagePoints 0x401B
+ #define EmfPlusRecordTypeDrawString 0x401C
+ #define EmfPlusRecordTypeSetRenderingOrigin 0x401D
+ #define EmfPlusRecordTypeSetAntiAliasMode 0x401E
+ #define EmfPlusRecordTypeSetTextRenderingHint 0x401F
+ #define EmfPlusRecordTypeSetTextContrast 0x4020
+ #define EmfPlusRecordTypeSetInterpolationMode 0x4021
+ #define EmfPlusRecordTypeSetPixelOffsetMode 0x4022
+ //TODO EmfPlusRecordTypeSetCompositingMode 0x4023
+ #define EmfPlusRecordTypeSetCompositingQuality 0x4024
+ #define EmfPlusRecordTypeSave 0x4025
+ #define EmfPlusRecordTypeRestore 0x4026
+ #define EmfPlusRecordTypeBeginContainer 0x4027
+ #define EmfPlusRecordTypeBeginContainerNoParams 0x4028
+ #define EmfPlusRecordTypeEndContainer 0x4029
+ #define EmfPlusRecordTypeSetWorldTransform 0x402A
+ #define EmfPlusRecordTypeResetWorldTransform 0x402B
+ #define EmfPlusRecordTypeMultiplyWorldTransform 0x402C
+ #define EmfPlusRecordTypeTranslateWorldTransform 0x402D
+ #define EmfPlusRecordTypeScaleWorldTransform 0x402E
+ #define EmfPlusRecordTypeRotateWorldTransform 0x402F
+ #define EmfPlusRecordTypeSetPageTransform 0x4030
+ #define EmfPlusRecordTypeResetClip 0x4031
+ #define EmfPlusRecordTypeSetClipRect 0x4032
+ #define EmfPlusRecordTypeSetClipPath 0x4033
+ #define EmfPlusRecordTypeSetClipRegion 0x4034
+ #define EmfPlusRecordTypeOffsetClip 0x4035
+ #define EmfPlusRecordTypeDrawDriverString 0x4036
+ //TODO EmfPlusRecordTypeStrokeFillPath 0x4037
+ //TODO EmfPlusRecordTypeSerializableObject 0x4038
+ //TODO EmfPlusRecordTypeSetTSGraphics 0x4039
+ //TODO EmfPlusRecordTypeSetTSClip 0x403A
+
+ // EMF+object types
+ #define EmfPlusObjectTypeBrush 0x100
+ #define EmfPlusObjectTypePen 0x200
+ #define EmfPlusObjectTypePath 0x300
+ #define EmfPlusObjectTypeRegion 0x400
+ #define EmfPlusObjectTypeImage 0x500
+ #define EmfPlusObjectTypeFont 0x600
+ #define EmfPlusObjectTypeStringFormat 0x700
+ #define EmfPlusObjectTypeImageAttributes 0x800
+ #define EmfPlusObjectTypeCustomLineCap 0x900
+
+ enum PixelOffsetMode
+ {
+ PixelOffsetModeDefault = 0x00,
+ PixelOffsetModeHighSpeed = 0x01,
+ PixelOffsetModeHighQuality = 0x02,
+ PixelOffsetModeNone = 0x03,
+ PixelOffsetModeHalf = 0x04
+ };
+
+ enum SmoothingMode
+ {
+ SmoothingModeDefault = 0x00,
+ SmoothingModeHighSpeed = 0x01,
+ SmoothingModeHighQuality = 0x02,
+ SmoothingModeNone = 0x03,
+ SmoothingModeAntiAlias8x4 = 0x04,
+ SmoothingModeAntiAlias8x8 = 0x05
+ };
+
+ enum InterpolationMode
+ {
+ InterpolationModeDefault = 0x00,
+ InterpolationModeLowQuality = 0x01,
+ InterpolationModeHighQuality = 0x02,
+ InterpolationModeBilinear = 0x03,
+ InterpolationModeBicubic = 0x04,
+ InterpolationModeNearestNeighbor = 0x05,
+ InterpolationModeHighQualityBilinear = 0x06,
+ InterpolationModeHighQualityBicubic = 0x07
+ };
+
+ enum TextRenderingHint
+ {
+ TextRenderingHintSystemDefault = 0x00,
+ TextRenderingHintSingleBitPerPixelGridFit = 0x01,
+ TextRenderingHintSingleBitPerPixel = 0x02,
+ TextRenderingHintAntialiasGridFit = 0x03,
+ TextRenderingHintAntialias = 0x04,
+ TextRenderingHintClearTypeGridFit = 0x05
+ };
+
+ enum UnitType
+ {
+ UnitTypeWorld = 0x00,
+ UnitTypeDisplay = 0x01,
+ UnitTypePixel = 0x02,
+ UnitTypePoint = 0x03,
+ UnitTypeInch = 0x04,
+ UnitTypeDocument = 0x05,
+ UnitTypeMillimeter = 0x06
+ };
+
+ enum EmfPlusCombineMode
+ {
+ EmfPlusCombineModeReplace = 0x00000000,
+ EmfPlusCombineModeIntersect = 0x00000001,
+ EmfPlusCombineModeUnion = 0x00000002,
+ EmfPlusCombineModeXOR = 0x00000003,
+ EmfPlusCombineModeExclude = 0x00000004,
+ EmfPlusCombineModeComplement = 0x00000005
+ };
+
+ const char* emfTypeToName(sal_uInt16 type);
+ OUString UnitTypeToString(sal_uInt16 nType);
+
+ struct EMFPObject
+ {
+ virtual ~EMFPObject();
+ };
+
+ typedef std::map<int, wmfemfhelper::PropertyHolder> GraphicStateMap;
+
+ struct EmfPlusHelperData
+ {
+ private:
+ /* EMF+ */
+ basegfx::B2DHomMatrix maBaseTransform;
+ basegfx::B2DHomMatrix maWorldTransform;
+ basegfx::B2DHomMatrix maMapTransform;
+
+ std::unique_ptr<EMFPObject> maEMFPObjects[256];
+ float mfPageScale;
+ sal_Int32 mnOriginX;
+ sal_Int32 mnOriginY;
+ sal_uInt32 mnHDPI;
+ sal_uInt32 mnVDPI;
+ bool mbSetTextContrast;
+ sal_uInt16 mnTextContrast;
+
+ /* EMF+ emf header info */
+ sal_Int32 mnFrameLeft;
+ sal_Int32 mnFrameTop;
+ sal_Int32 mnFrameRight;
+ sal_Int32 mnFrameBottom;
+ sal_Int32 mnPixX;
+ sal_Int32 mnPixY;
+ sal_Int32 mnMmX;
+ sal_Int32 mnMmY;
+
+ /* multipart object data */
+ bool mbMultipart;
+ sal_uInt16 mMFlags;
+ SvMemoryStream mMStream;
+
+ /* emf+ graphic state stack */
+ GraphicStateMap mGSStack;
+ GraphicStateMap mGSContainerStack;
+
+ /* Performance optimizators */
+ /* Extracted Scale values from Transformation Matrix */
+ double mdExtractedXScale;
+ double mdExtractedYScale;
+
+ /// data holders
+ wmfemfhelper::TargetHolders& mrTargetHolders;
+ wmfemfhelper::PropertyHolders& mrPropertyHolders;
+ wmfemfhelper::PropertyHolder aGetDCState;
+ bool bIsGetDCProcessing;
+
+ // readers
+ void processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream = false);
+ static void ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags);
+
+ // internal mapper
+ void mappingChanged();
+
+ // stack actions
+ void GraphicStatePush(GraphicStateMap& map, sal_Int32 index);
+ void GraphicStatePop(GraphicStateMap& map, sal_Int32 index);
+
+ drawinglayer::attribute::LineStartEndAttribute CreateLineEnd(const sal_Int32 aCap,
+ const float aPenWidth) const;
+
+ // primitive creators
+ void EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex);
+ void EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor);
+ void EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color);
+
+ // helper functions
+ Color EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const;
+
+ public:
+ EmfPlusHelperData(
+ SvMemoryStream& rMS,
+ wmfemfhelper::TargetHolders& rTargetHolders,
+ wmfemfhelper::PropertyHolders& rPropertyHolders);
+ ~EmfPlusHelperData();
+
+ void processEmfPlusData(
+ SvMemoryStream& rMS,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation);
+
+ // mappers
+ ::basegfx::B2DPoint Map(double ix, double iy) const;
+
+ // readers
+ static void ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed = false);
+ static bool readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget);
+ static ::basegfx::B2DPolyPolygon combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon);
+
+ static float getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI);
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpimage.cxx b/drawinglayer/source/tools/emfpimage.cxx
new file mode 100644
index 000000000..67a0cef99
--- /dev/null
+++ b/drawinglayer/source/tools/emfpimage.cxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <vcl/graphicfilter.hxx>
+#include <sal/log.hxx>
+#include "emfpimage.hxx"
+
+namespace emfplushelper
+{
+ void EMFPImage::Read(SvMemoryStream &s, sal_uInt32 dataSize, bool bUseWholeStream)
+ {
+ sal_uInt32 header, bitmapType;
+ s.ReadUInt32(header).ReadUInt32(type);
+ SAL_INFO("drawinglayer.emf", "EMF+\timage\nEMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec);
+
+ if (ImageDataTypeBitmap == type)
+ {
+ // bitmap
+ s.ReadInt32(width).ReadInt32(height).ReadInt32(stride).ReadUInt32(pixelFormat).ReadUInt32(bitmapType);
+ SAL_INFO("drawinglayer.emf", "EMF+\tbitmap width: " << width << " height: " << height << " stride: " << stride << " pixelFormat: 0x" << std::hex << pixelFormat << " bitmapType: 0x" << bitmapType << std::dec);
+
+ if ((bitmapType != 0) || (width == 0))
+ {
+ // non native formats
+ GraphicFilter filter;
+ filter.ImportGraphic(graphic, u"", s);
+ SAL_INFO("drawinglayer.emf", "EMF+\tbitmap width: " << graphic.GetSizePixel().Width() << " height: " << graphic.GetSizePixel().Height());
+ }
+ }
+ else if (ImageDataTypeMetafile == type)
+ {
+ // metafile
+ sal_uInt32 mfType, mfSize;
+ s.ReadUInt32(mfType).ReadUInt32(mfSize);
+
+ if (bUseWholeStream)
+ dataSize = s.remainingSize();
+ else
+ dataSize -= 16;
+
+ SAL_INFO("drawinglayer.emf", "EMF+\tmetafile type: " << mfType << " dataSize: " << mfSize << " real size calculated from record dataSize: " << dataSize);
+
+ GraphicFilter filter;
+ // workaround buggy metafiles, which have wrong mfSize set (n#705956 for example)
+ SvMemoryStream mfStream(const_cast<char *>(static_cast<char const *>(s.GetData()) + s.Tell()), dataSize, StreamMode::READ);
+ filter.ImportGraphic(graphic, u"", mfStream);
+
+ // debug code - write the stream to debug file /tmp/emf-stream.emf
+#if OSL_DEBUG_LEVEL > 1
+ mfStream.Seek(0);
+ static sal_Int32 emfp_debug_stream_number = 0;
+ OUString emfp_debug_filename = "/tmp/emf-embedded-stream" +
+ OUString::number(emfp_debug_stream_number++) + ".emf";
+
+ SvFileStream file(emfp_debug_filename, StreamMode::WRITE | StreamMode::TRUNC);
+
+ mfStream.WriteStream(file);
+ file.Flush();
+ file.Close();
+#endif
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpimage.hxx b/drawinglayer/source/tools/emfpimage.hxx
new file mode 100644
index 000000000..75e42e7d2
--- /dev/null
+++ b/drawinglayer/source/tools/emfpimage.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+#include <vcl/graph.hxx>
+
+namespace emfplushelper
+{
+
+ typedef enum
+ {
+ ImageDataTypeUnknown = 0x00000000,
+ ImageDataTypeBitmap = 0x00000001,
+ ImageDataTypeMetafile = 0x00000002
+ } ImageDataType;
+
+ struct EMFPImage : public EMFPObject
+ {
+ sal_uInt32 type;
+ sal_Int32 width;
+ sal_Int32 height;
+ sal_Int32 stride;
+ sal_uInt32 pixelFormat;
+ Graphic graphic;
+
+ void Read(SvMemoryStream &s, sal_uInt32 dataSize, bool bUseWholeStream);
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpimageattributes.cxx b/drawinglayer/source/tools/emfpimageattributes.cxx
new file mode 100644
index 000000000..c13da361b
--- /dev/null
+++ b/drawinglayer/source/tools/emfpimageattributes.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+
+#include "emfpimageattributes.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::basegfx;
+
+namespace emfplushelper
+{
+EMFPImageAttributes::EMFPImageAttributes()
+ : EMFPObject()
+ , wrapMode(0)
+ , objectClamp(0)
+{
+}
+
+EMFPImageAttributes::~EMFPImageAttributes() {}
+
+void EMFPImageAttributes::Read(SvStream& s)
+{
+ sal_uInt32 graphicsVersion, reserved1, reserved2, tempClampColor;
+ sal_uInt8 clampColorBlue, clampColorGreen, clampColorRed, clampColorAlpha;
+
+ s.ReadUInt32(graphicsVersion)
+ .ReadUInt32(reserved1)
+ .ReadUInt32(wrapMode)
+ .ReadUInt32(tempClampColor)
+ .ReadUInt32(objectClamp)
+ .ReadUInt32(reserved2);
+
+ clampColorBlue = tempClampColor >> 24;
+ clampColorGreen = (tempClampColor & 0x00FFFFFF) >> 16;
+ clampColorRed = (tempClampColor & 0x0000FFFF) >> 8;
+ clampColorAlpha = tempClampColor & 0x000000FF;
+
+ clampColor.SetRed(clampColorRed);
+ clampColor.SetGreen(clampColorGreen);
+ clampColor.SetBlue(clampColorBlue);
+ clampColor.SetAlpha(255 - clampColorAlpha);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\timage attributes");
+ SAL_WARN_IF((reserved1 != 0) || (reserved2 != 0), "drawinglayer.emf",
+ "Reserved field(s) not zero - reserved1: " << std::hex << reserved1
+ << " reserved2: " << reserved2);
+ SAL_WARN_IF((objectClamp != EmpPlusRectClamp) && (objectClamp != EmpPlusBitmapClamp),
+ "drawinglayer.emf", "Invalid object clamp - set to" << std::hex << objectClamp);
+ SAL_INFO("drawinglayer.emf", "EMF+\t image graphics version: 0x"
+ << std::hex << graphicsVersion << " wrap mode: " << wrapMode
+ << " clamp color: " << clampColor
+ << " object clamp: " << objectClamp);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpimageattributes.hxx b/drawinglayer/source/tools/emfpimageattributes.hxx
new file mode 100644
index 000000000..a8ba37553
--- /dev/null
+++ b/drawinglayer/source/tools/emfpimageattributes.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+
+namespace emfplushelper
+{
+const sal_uInt32 EmpPlusRectClamp = 0x00000000;
+const sal_uInt32 EmpPlusBitmapClamp = 0x00000001;
+
+struct EMFPImageAttributes : public EMFPObject
+{
+ sal_uInt32 wrapMode;
+ Color clampColor;
+ sal_uInt32 objectClamp;
+
+ EMFPImageAttributes();
+
+ virtual ~EMFPImageAttributes() override;
+
+ void Read(SvStream& s);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfplushelper.cxx b/drawinglayer/source/tools/emfplushelper.cxx
new file mode 100644
index 000000000..87276d998
--- /dev/null
+++ b/drawinglayer/source/tools/emfplushelper.cxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <emfplushelper.hxx>
+#include "emfphelperdata.hxx"
+
+namespace emfplushelper
+{
+ EmfPlusHelper::EmfPlusHelper(
+ SvMemoryStream& rMS,
+ wmfemfhelper::TargetHolders& rTargetHolders,
+ wmfemfhelper::PropertyHolders& rPropertyHolders)
+ : mpD(new EmfPlusHelperData(rMS, rTargetHolders, rPropertyHolders))
+ {
+ }
+
+ EmfPlusHelper::~EmfPlusHelper()
+ {
+ }
+
+ void EmfPlusHelper::processEmfPlusData(
+ SvMemoryStream& rMS,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ mpD->processEmfPlusData(rMS, rViewInformation);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppath.cxx b/drawinglayer/source/tools/emfppath.cxx
new file mode 100644
index 000000000..4217e3616
--- /dev/null
+++ b/drawinglayer/source/tools/emfppath.cxx
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <sal/log.hxx>
+#include "emfppath.hxx"
+
+namespace
+{
+ const unsigned char nTopBitInt7 = 0x80;
+ const unsigned char nSignBitInt7 = 0x40;
+ // include the sign bit so if it's negative we get
+ // that "missing" bit pre-set to 1
+ const unsigned char nValueMaskInt7 = 0x7F;
+}
+
+namespace emfplushelper
+{
+ // see 2.2.2.21 EmfPlusInteger7
+ // 2.2.2.22 EmfPlusInteger15
+ // and 2.2.2.37 EmfPlusPointR Object
+ static sal_Int16 GetEmfPlusInteger(SvStream& s)
+ {
+ unsigned char u8(0);
+ s.ReadUChar(u8);
+
+ bool bIsEmfPlusInteger15 = u8 & nTopBitInt7;
+ bool bNegative = u8 & nSignBitInt7;
+ unsigned char val1 = u8 & nValueMaskInt7;
+ if (bNegative)
+ val1 |= nTopBitInt7;
+ if (!bIsEmfPlusInteger15)
+ {
+ return static_cast<signed char>(val1);
+ }
+
+ s.ReadUChar(u8);
+ sal_uInt16 nRet = (val1 << 8) | u8;
+ return static_cast<sal_Int16>(nRet);
+ }
+
+ EMFPPath::EMFPPath (sal_uInt32 _nPoints, bool bLines)
+ {
+ if (_nPoints > SAL_MAX_UINT32 / (2 * sizeof(float)))
+ {
+ _nPoints = SAL_MAX_UINT32 / (2 * sizeof(float));
+ }
+
+ nPoints = _nPoints;
+ pPoints.reset( new float [nPoints*2] );
+
+ if (!bLines)
+ pPointTypes.reset( new sal_uInt8 [_nPoints] );
+ }
+
+ EMFPPath::~EMFPPath ()
+ {
+ }
+
+ void EMFPPath::Read (SvStream& s, sal_uInt32 pathFlags)
+ {
+ for (sal_uInt32 i = 0; i < nPoints; i ++)
+ {
+ if (pathFlags & 0x800)
+ {
+ // EMFPlusPointR: points are stored in EMFPlusInteger7 or
+ // EMFPlusInteger15 objects, see section 2.2.2.21/22
+ // If 0x800 bit is set, the 0x4000 bit is undefined and must be ignored
+ sal_Int32 x = GetEmfPlusInteger(s);
+ sal_Int32 y = GetEmfPlusInteger(s);
+ pPoints [i*2] = x;
+ pPoints [i*2 + 1] = y;
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPointR [x,y]: " << x << ", " << y);
+ }
+ else if (pathFlags & 0x4000)
+ {
+ // EMFPlusPoint: stored in signed short 16bit integer format
+ sal_Int16 x, y;
+
+ s.ReadInt16( x ).ReadInt16( y );
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPoint [x,y]: " << x << ", " << y);
+ pPoints [i*2] = x;
+ pPoints [i*2 + 1] = y;
+ }
+ else
+ {
+ // EMFPlusPointF: stored in Single (float) format
+ s.ReadFloat( pPoints [i*2] ).ReadFloat( pPoints [i*2 + 1] );
+ SAL_INFO("drawinglayer.emf", "EMF+\t" << i << ". EMFPlusPointF [x,y]: " << pPoints [i * 2] << ", " << pPoints [i * 2 + 1]);
+ }
+ }
+
+ if (pPointTypes)
+ {
+ for (sal_uInt32 i = 0; i < nPoints; i++)
+ {
+ s.ReadUChar(pPointTypes[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\tpoint type: 0x" << std::hex << static_cast<int>(pPointTypes[i]) << std::dec);
+ }
+ }
+
+ aPolygon.clear();
+ }
+
+ ::basegfx::B2DPolyPolygon& EMFPPath::GetPolygon (EmfPlusHelperData const & rR, bool bMapIt, bool bAddLineToCloseShape)
+ {
+ ::basegfx::B2DPolygon polygon;
+ aPolygon.clear ();
+ sal_uInt32 last_normal = 0, p = 0;
+ ::basegfx::B2DPoint prev, mapped;
+ bool hasPrev = false;
+
+ for (sal_uInt32 i = 0; i < nPoints; i ++)
+ {
+ if (p && pPointTypes && (pPointTypes [i] == 0))
+ {
+ aPolygon.append (polygon);
+ last_normal = i;
+ p = 0;
+ polygon.clear ();
+ }
+
+ if (bMapIt)
+ mapped = rR.Map (pPoints [i*2], pPoints [i*2 + 1]);
+ else
+ mapped = ::basegfx::B2DPoint (pPoints [i*2], pPoints [i*2 + 1]);
+
+ if (pPointTypes)
+ {
+ if ((pPointTypes [i] & 0x07) == 3)
+ {
+ if (((i - last_normal )% 3) == 1)
+ {
+ polygon.setNextControlPoint (p - 1, mapped);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon append next: " << p - 1 << " mapped: " << mapped.getX () << "," << mapped.getY ());
+ continue;
+ }
+ else if (((i - last_normal) % 3) == 2)
+ {
+ prev = mapped;
+ hasPrev = true;
+ continue;
+ }
+ }
+ else
+ {
+ last_normal = i;
+ }
+ }
+
+ polygon.append (mapped);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\tPoint: " << pPoints [i*2] << "," << pPoints [i*2 + 1] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
+
+ if (hasPrev)
+ {
+ polygon.setPrevControlPoint (p, prev);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon append prev: " << p << " mapped: " << prev.getX () << "," << prev.getY ());
+ hasPrev = false;
+ }
+
+ p++;
+
+ if (pPointTypes && (pPointTypes [i] & 0x80)) // closed polygon
+ {
+ polygon.setClosed (true);
+ aPolygon.append (polygon);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\tClose polygon");
+ last_normal = i + 1;
+ p = 0;
+ polygon.clear ();
+ }
+ }
+
+ // Draw an extra line between the last point and the first point, to close the shape.
+ if (bAddLineToCloseShape)
+ {
+ polygon.setClosed (true);
+ }
+
+ if (polygon.count ())
+ {
+ aPolygon.append (polygon);
+
+#if OSL_DEBUG_LEVEL > 1
+ for (unsigned int i=0; i<aPolygon.count(); i++) {
+ polygon = aPolygon.getB2DPolygon(i);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon: " << i);
+ for (unsigned int j=0; j<polygon.count(); j++) {
+ ::basegfx::B2DPoint point = polygon.getB2DPoint(j);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tPoint: " << point.getX() << "," << point.getY());
+ if (polygon.isPrevControlPointUsed(j)) {
+ point = polygon.getPrevControlPoint(j);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tPrev: " << point.getX() << "," << point.getY());
+ }
+ if (polygon.isNextControlPointUsed(j)) {
+ point = polygon.getNextControlPoint(j);
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tNext: " << point.getX() << "," << point.getY());
+ }
+ }
+ }
+#endif
+ }
+
+ return aPolygon;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppath.hxx b/drawinglayer/source/tools/emfppath.hxx
new file mode 100644
index 000000000..8db095a21
--- /dev/null
+++ b/drawinglayer/source/tools/emfppath.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+
+namespace emfplushelper
+{
+ struct EMFPPath : public EMFPObject
+ {
+ ::basegfx::B2DPolyPolygon aPolygon;
+ sal_uInt32 nPoints;
+ std::unique_ptr<float[]> pPoints;
+ std::unique_ptr<sal_uInt8[]> pPointTypes;
+
+ EMFPPath(sal_uInt32 _nPoints, bool bLines = false);
+
+ virtual ~EMFPPath() override;
+
+ void Read(SvStream& s, sal_uInt32 pathFlags);
+
+ ::basegfx::B2DPolyPolygon& GetPolygon(EmfPlusHelperData const & rR, bool bMapIt = true, bool bAddLineToCloseShape = false);
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppen.cxx b/drawinglayer/source/tools/emfppen.cxx
new file mode 100644
index 000000000..adfee3bd3
--- /dev/null
+++ b/drawinglayer/source/tools/emfppen.cxx
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include "emfppen.hxx"
+#include "emfpcustomlinecap.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::basegfx;
+
+namespace emfplushelper
+{
+
+ EMFPPen::EMFPPen()
+ : penDataFlags(0)
+ , penUnit(0)
+ , penWidth(0.0)
+ , startCap(0)
+ , endCap(0)
+ , maLineJoin(basegfx::B2DLineJoin::Miter)
+ , fMiterMinimumAngle(basegfx::deg2rad(5.0))
+ , dashStyle(0)
+ , dashCap(0)
+ , dashOffset(0.0)
+ , alignment(0)
+ , customStartCapLen(0)
+ , customEndCapLen(0)
+ {
+ }
+
+ EMFPPen::~EMFPPen()
+ {
+ }
+
+ static OUString PenDataFlagsToString(sal_uInt32 flags)
+ {
+ rtl::OUStringBuffer sFlags;
+
+ if (flags & EmfPlusPenDataTransform)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataTransform");
+
+ if (flags & EmfPlusPenDataStartCap)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataStartCap");
+
+ if (flags & EmfPlusPenDataEndCap)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataEndCap");
+
+ if (flags & EmfPlusPenDataJoin)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataJoin");
+
+ if (flags & EmfPlusPenDataMiterLimit)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataMiterLimit");
+
+ if (flags & EmfPlusPenDataLineStyle)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataLineStyle");
+
+ if (flags & EmfPlusPenDataDashedLineCap)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineCap");
+
+ if (flags & EmfPlusPenDataDashedLineOffset)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineOffset");
+
+ if (flags & EmfPlusPenDataDashedLine)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLine");
+
+ if (flags & EmfPlusPenDataAlignment)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataAlignment");
+
+ if (flags & EmfPlusPenDataCompoundLine)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCompoundLine");
+
+ if (flags & EmfPlusPenDataCustomStartCap)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCustomStartCap");
+
+ if (flags & EmfPlusPenDataCustomEndCap)
+ sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCustomEndCap");
+
+ return sFlags.makeStringAndClear();
+ }
+
+ static OUString LineCapTypeToString(sal_uInt32 linecap)
+ {
+ switch (linecap)
+ {
+ case LineCapTypeFlat: return "LineCapTypeFlat";
+ case LineCapTypeSquare: return "LineCapTypeSquare";
+ case LineCapTypeRound: return "LineCapTypeRound";
+ case LineCapTypeTriangle: return "LineCapTypeTriangle";
+ case LineCapTypeNoAnchor: return "LineCapTypeNoAnchor";
+ case LineCapTypeSquareAnchor: return "LineCapTypeSquareAnchor";
+ case LineCapTypeRoundAnchor: return "LineCapTypeRoundAchor";
+ case LineCapTypeDiamondAnchor: return "LineCapTypeDiamondAnchor";
+ case LineCapTypeArrowAnchor: return "LineCapTypeArrowAnchor";
+ case LineCapTypeAnchorMask: return "LineCapTypeAnchorMask";
+ case LineCapTypeCustom: return "LineCapTypeCustom";
+ }
+ return "";
+ }
+
+ static OUString DashedLineCapTypeToString(sal_uInt32 dashedlinecaptype)
+ {
+ switch (dashedlinecaptype)
+ {
+ case DashedLineCapTypeFlat: return "DashedLineCapTypeFlat";
+ case DashedLineCapTypeRound: return "DashedLineCapTypeRound";
+ case DashedLineCapTypeTriangle: return "DashedLineCapTypeTriangle";
+ }
+ return "";
+ }
+
+ static OUString PenAlignmentToString(sal_uInt32 alignment)
+ {
+ switch (alignment)
+ {
+ case PenAlignmentCenter: return "PenAlignmentCenter";
+ case PenAlignmentInset: return "PenAlignmentInset";
+ case PenAlignmentLeft: return "PenAlignmentLeft";
+ case PenAlignmentOutset: return "PenAlignmentOutset";
+ case PenAlignmentRight: return "PenAlignmentRight";
+ }
+ return "";
+ }
+
+ drawinglayer::attribute::StrokeAttribute
+ EMFPPen::GetStrokeAttribute(const double aTransformation) const
+ {
+ if (penDataFlags & EmfPlusPenDataLineStyle // pen has a predefined line style
+ && dashStyle != EmfPlusLineStyleCustom)
+ {
+ const double pw = aTransformation * penWidth;
+ switch (dashStyle)
+ {
+ case EmfPlusLineStyleDash:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw });
+ case EmfPlusLineStyleDot:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ pw, pw });
+ case EmfPlusLineStyleDashDot:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw });
+ case EmfPlusLineStyleDashDotDot:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw, pw, pw });
+ }
+ }
+ else if (penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line
+ {
+ const double pw = aTransformation * penWidth;
+ // StrokeAttribute needs a double vector while the pen provides a float vector
+ std::vector<double> aPattern(dashPattern.size());
+ for (size_t i = 0; i < aPattern.size(); i++)
+ {
+ // convert from float to double and multiply with the adjusted pen width
+ aPattern[i] = pw * dashPattern[i];
+ }
+ return drawinglayer::attribute::StrokeAttribute(std::move(aPattern));
+ }
+ // EmfPlusLineStyleSolid: - do nothing special, use default stroke attribute
+ return drawinglayer::attribute::StrokeAttribute();
+ }
+
+ void EMFPPen::Read(SvStream& s, EmfPlusHelperData const & rR)
+ {
+ sal_Int32 lineJoin = EmfPlusLineJoinTypeMiter;
+ sal_uInt32 graphicsVersion, penType;
+ s.ReadUInt32(graphicsVersion).ReadUInt32(penType).ReadUInt32(penDataFlags).ReadUInt32(penUnit).ReadFloat(penWidth);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tGraphics version: 0x" << std::hex << graphicsVersion);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tType: " << penType);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tPen data flags: 0x" << penDataFlags << PenDataFlagsToString(penDataFlags));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tUnit: " << UnitTypeToString(penUnit));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tWidth: " << std::dec << penWidth);
+
+ // If a zero width is specified, a minimum value must be used, which is determined by the units
+ if (penWidth == 0.0)
+ { //TODO Check if these values is correct
+ penWidth = penUnit == 0 ? 0.18f
+ : 0.05f; // 0.05f is taken from old EMF+ implementation (case of Unit == Pixel etc.)
+ }
+
+ if (penDataFlags & EmfPlusPenDataTransform)
+ {
+ EmfPlusHelperData::readXForm(s, pen_transformation);
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataTransform: " << pen_transformation);
+ }
+
+ if (penDataFlags & EmfPlusPenDataStartCap)
+ {
+ s.ReadInt32(startCap);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tstartCap: " << LineCapTypeToString(startCap) << " (0x" << std::hex << startCap << ")");
+ }
+ else
+ {
+ startCap = 0;
+ }
+
+ if (penDataFlags & EmfPlusPenDataEndCap)
+ {
+ s.ReadInt32(endCap);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tendCap: " << LineCapTypeToString(endCap) << " (0x" << std::hex << startCap << ")");
+ }
+ else
+ {
+ endCap = 0;
+ }
+
+ if (penDataFlags & EmfPlusPenDataJoin)
+ {
+ s.ReadInt32(lineJoin);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t LineJoin: " << lineJoin);
+ switch (lineJoin)
+ {
+ case EmfPlusLineJoinTypeBevel:
+ maLineJoin = basegfx::B2DLineJoin::Bevel;
+ break;
+ case EmfPlusLineJoinTypeRound:
+ maLineJoin = basegfx::B2DLineJoin::Round;
+ break;
+ case EmfPlusLineJoinTypeMiter:
+ case EmfPlusLineJoinTypeMiterClipped:
+ default: // If nothing set, then apply Miter (based on MS Paint)
+ maLineJoin = basegfx::B2DLineJoin::Miter;
+ break;
+ }
+ }
+ else
+ maLineJoin = basegfx::B2DLineJoin::Miter;
+
+ if (penDataFlags & EmfPlusPenDataMiterLimit)
+ {
+ float miterLimit;
+ s.ReadFloat(miterLimit);
+
+ // EMF+ JoinTypeMiterClipped is working as our B2DLineJoin::Miter
+ // For EMF+ LineJoinTypeMiter we are simulating it by changing angle
+ if (lineJoin == EmfPlusLineJoinTypeMiter)
+ miterLimit = 3.0 * miterLimit;
+ // asin angle must be in range [-1, 1]
+ if (abs(miterLimit) > 1.0)
+ fMiterMinimumAngle = 2.0 * asin(1.0 / miterLimit);
+ else
+ // enable miter limit for all angles
+ fMiterMinimumAngle = basegfx::deg2rad(180.0);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t\t MiterLimit: " << std::dec << miterLimit
+ << ", Miter minimum angle (rad): " << fMiterMinimumAngle);
+ }
+ else
+ fMiterMinimumAngle = basegfx::deg2rad(5.0);
+
+
+ if (penDataFlags & EmfPlusPenDataLineStyle)
+ {
+ s.ReadInt32(dashStyle);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tdashStyle: " << DashedLineCapTypeToString(dashStyle) << " (0x" << std::hex << dashStyle << ")");
+ }
+ else
+ {
+ dashStyle = 0;
+ }
+
+ if (penDataFlags & EmfPlusPenDataDashedLineCap)
+ {
+ s.ReadInt32(dashCap);
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineCap: 0x" << std::hex << dashCap);
+ }
+ else
+ {
+ dashCap = 0;
+ }
+
+ if (penDataFlags & EmfPlusPenDataDashedLineOffset)
+ {
+ s.ReadFloat(dashOffset);
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineOffset: 0x" << std::hex << dashOffset);
+ }
+ else
+ {
+ dashOffset = 0;
+ }
+
+ if (penDataFlags & EmfPlusPenDataDashedLine)
+ {
+ dashStyle = EmfPlusLineStyleCustom;
+ sal_uInt32 dashPatternLen;
+
+ s.ReadUInt32(dashPatternLen);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\tdashPatternLen: " << dashPatternLen);
+
+ dashPattern.resize( dashPatternLen );
+
+ for (sal_uInt32 i = 0; i < dashPatternLen; i++)
+ {
+ s.ReadFloat(dashPattern[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tdashPattern[" << i << "]: " << dashPattern[i]);
+ }
+ }
+
+ if (penDataFlags & EmfPlusPenDataAlignment)
+ {
+ s.ReadInt32(alignment);
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataAlignment: " << PenAlignmentToString(alignment) << " (0x" << std::hex << alignment << ")");
+ }
+ else
+ {
+ alignment = 0;
+ }
+
+ if (penDataFlags & EmfPlusPenDataCompoundLine)
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataCompoundLine");
+ sal_uInt32 compoundArrayLen;
+ s.ReadUInt32(compoundArrayLen);
+
+ compoundArray.resize(compoundArrayLen);
+
+ for (sal_uInt32 i = 0; i < compoundArrayLen; i++)
+ {
+ s.ReadFloat(compoundArray[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tcompoundArray[" << i << "]: " << compoundArray[i]);
+ }
+ }
+
+ if (penDataFlags & EmfPlusPenDataCustomStartCap)
+ {
+ s.ReadUInt32(customStartCapLen);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomStartCapLen: " << customStartCapLen);
+ sal_uInt64 const pos = s.Tell();
+
+ customStartCap.reset( new EMFPCustomLineCap() );
+ customStartCap->Read(s, rR);
+
+ // maybe we don't read everything yet, play it safe ;-)
+ s.Seek(pos + customStartCapLen);
+ }
+ else
+ {
+ customStartCapLen = 0;
+ }
+
+ if (penDataFlags & EmfPlusPenDataCustomEndCap)
+ {
+ s.ReadUInt32(customEndCapLen);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomEndCapLen: " << customEndCapLen);
+ sal_uInt64 const pos = s.Tell();
+
+ customEndCap.reset( new EMFPCustomLineCap() );
+ customEndCap->Read(s, rR);
+
+ // maybe we don't read everything yet, play it safe ;-)
+ s.Seek(pos + customEndCapLen);
+ }
+ else
+ {
+ customEndCapLen = 0;
+ }
+
+ EMFPBrush::Read(s, rR);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppen.hxx b/drawinglayer/source/tools/emfppen.hxx
new file mode 100644
index 000000000..31812c8b0
--- /dev/null
+++ b/drawinglayer/source/tools/emfppen.hxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include "emfpbrush.hxx"
+#include <vector>
+
+namespace emfplushelper
+{
+ const sal_uInt32 EmfPlusLineCapTypeSquare = 0x00000001;
+ const sal_uInt32 EmfPlusLineCapTypeRound = 0x00000002;
+ const sal_uInt32 EmfPlusLineCapTypeTriangle = 0x00000003;
+
+ const sal_uInt32 EmfPlusLineJoinTypeMiter = 0x00000000;
+ const sal_uInt32 EmfPlusLineJoinTypeBevel = 0x00000001;
+ const sal_uInt32 EmfPlusLineJoinTypeRound = 0x00000002;
+ const sal_uInt32 EmfPlusLineJoinTypeMiterClipped = 0x00000003;
+
+ const sal_Int32 EmfPlusLineStyleSolid = 0x00000000;
+ const sal_Int32 EmfPlusLineStyleDash = 0x00000001;
+ const sal_Int32 EmfPlusLineStyleDot = 0x00000002;
+ const sal_Int32 EmfPlusLineStyleDashDot = 0x00000003;
+ const sal_Int32 EmfPlusLineStyleDashDotDot = 0x00000004;
+ const sal_Int32 EmfPlusLineStyleCustom = 0x00000005;
+
+ const sal_uInt32 EmfPlusPenDataTransform = 0x00000001;
+ const sal_uInt32 EmfPlusPenDataStartCap = 0x00000002;
+ const sal_uInt32 EmfPlusPenDataEndCap = 0x00000004;
+ const sal_uInt32 EmfPlusPenDataJoin = 0x00000008;
+ const sal_uInt32 EmfPlusPenDataMiterLimit = 0x00000010;
+ const sal_uInt32 EmfPlusPenDataLineStyle = 0x00000020;
+ const sal_uInt32 EmfPlusPenDataDashedLineCap = 0x00000040;
+ const sal_uInt32 EmfPlusPenDataDashedLineOffset = 0x00000080;
+ const sal_uInt32 EmfPlusPenDataDashedLine = 0x00000100;
+ const sal_uInt32 EmfPlusPenDataAlignment = 0x00000200;
+ const sal_uInt32 EmfPlusPenDataCompoundLine = 0x00000400;
+ const sal_uInt32 EmfPlusPenDataCustomStartCap = 0x00000800;
+ const sal_uInt32 EmfPlusPenDataCustomEndCap = 0x000001000;
+
+ enum LineCapType
+ {
+ LineCapTypeFlat = 0x00000000,
+ LineCapTypeSquare = 0x00000001,
+ LineCapTypeRound = 0x00000002,
+ LineCapTypeTriangle = 0x00000003,
+ LineCapTypeNoAnchor = 0x00000010,
+ LineCapTypeSquareAnchor = 0x00000011,
+ LineCapTypeRoundAnchor = 0x00000012,
+ LineCapTypeDiamondAnchor = 0x00000013,
+ LineCapTypeArrowAnchor = 0x00000014,
+ LineCapTypeAnchorMask = 0x000000F0,
+ LineCapTypeCustom = 0x000000FF
+ };
+
+ enum LineJoinType
+ {
+ LineJoinTypeMiter = 0x00000000,
+ LineJoinTypeBevel = 0x00000001,
+ LineJoinTypeRound = 0x00000002,
+ LineJoinTypeMiterClipped = 0x00000003
+ };
+
+ enum DashedLineCapType
+ {
+ DashedLineCapTypeFlat = 0x00000000,
+ DashedLineCapTypeRound = 0x00000002,
+ DashedLineCapTypeTriangle = 0x00000003
+ };
+
+ enum PenAlignment
+ {
+ PenAlignmentCenter = 0x00000000,
+ PenAlignmentInset = 0x00000001,
+ PenAlignmentLeft = 0x00000002,
+ PenAlignmentOutset = 0x00000003,
+ PenAlignmentRight = 0x00000004
+ };
+
+ struct EMFPCustomLineCap;
+
+ struct EMFPPen : public EMFPBrush
+ {
+ basegfx::B2DHomMatrix pen_transformation; //TODO: This isn't used
+ sal_uInt32 penDataFlags;
+ sal_uInt32 penUnit;
+ float penWidth;
+ sal_Int32 startCap;
+ sal_Int32 endCap;
+ basegfx::B2DLineJoin maLineJoin;
+ double fMiterMinimumAngle;
+ sal_Int32 dashStyle;
+ sal_Int32 dashCap;
+ float dashOffset;
+ std::vector<float> dashPattern;
+ sal_Int32 alignment;
+ std::vector<float> compoundArray;
+ sal_uInt32 customStartCapLen;
+ std::unique_ptr<EMFPCustomLineCap> customStartCap;
+ sal_uInt32 customEndCapLen;
+ std::unique_ptr<EMFPCustomLineCap> customEndCap;
+
+ EMFPPen();
+
+ virtual ~EMFPPen() override;
+
+ void Read(SvStream& s, EmfPlusHelperData const & rR);
+
+ drawinglayer::attribute::StrokeAttribute GetStrokeAttribute(const double aTransformation) const;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpregion.cxx b/drawinglayer/source/tools/emfpregion.cxx
new file mode 100644
index 000000000..9fcced337
--- /dev/null
+++ b/drawinglayer/source/tools/emfpregion.cxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <sal/log.hxx>
+#include "emfpregion.hxx"
+#include "emfppath.hxx"
+
+namespace emfplushelper
+{
+ EMFPRegion::EMFPRegion()
+ {
+ }
+
+ EMFPRegion::~EMFPRegion()
+ {
+ }
+
+ ::basegfx::B2DPolyPolygon EMFPRegion::ReadRegionNode(SvStream& s, EmfPlusHelperData& rR)
+ {
+ // Regions are specified as a binary tree of region nodes, and each node must either be a terminal node
+ // (RegionNodeDataTypeRect, RegionNodeDataTypePath, RegionNodeDataTypeEmpty, RegionNodeDataTypeInfinite)
+ // or specify one or two child nodes
+ // (RegionNodeDataTypeAnd, RegionNodeDataTypeOr, RegionNodeDataTypeXor,
+ // RegionNodeDataTypeExclude, RegionNodeDataTypeComplement).
+ sal_uInt32 dataType;
+ ::basegfx::B2DPolyPolygon polygon;
+ s.ReadUInt32(dataType);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Region node data type 0x" << std::hex << dataType << std::dec);
+
+ switch (dataType)
+ {
+ case RegionNodeDataTypeAnd: // CombineModeIntersect
+ case RegionNodeDataTypeOr: // CombineModeUnion
+ case RegionNodeDataTypeXor: // CombineModeXOR
+ case RegionNodeDataTypeExclude: // CombineModeExclude
+ case RegionNodeDataTypeComplement: // CombineModeComplement
+ {
+ ::basegfx::B2DPolyPolygon leftPolygon = ReadRegionNode(s, rR);
+ ::basegfx::B2DPolyPolygon rightPolygon = ReadRegionNode(s, rR);
+ polygon = EmfPlusHelperData::combineClip(leftPolygon, dataType, rightPolygon);
+ break;
+ }
+ case RegionNodeDataTypeRect:
+ {
+ float dx, dy, dw, dh;
+ s.ReadFloat(dx).ReadFloat(dy).ReadFloat(dw).ReadFloat(dh);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypeRect x:" << dx << ", y:" << dy <<
+ ", width:" << dw << ", height:" << dh);
+
+ const ::basegfx::B2DPoint mappedStartPoint(rR.Map(dx, dy));
+ const ::basegfx::B2DPoint mappedEndPoint(rR.Map(dx + dw, dy + dh));
+ polygon = ::basegfx::B2DPolyPolygon(
+ ::basegfx::utils::createPolygonFromRect(
+ ::basegfx::B2DRectangle(
+ mappedStartPoint.getX(),
+ mappedStartPoint.getY(),
+ mappedEndPoint.getX(),
+ mappedEndPoint.getY())));
+ break;
+ }
+ case RegionNodeDataTypePath:
+ {
+ sal_Int32 pathLength;
+ s.ReadInt32(pathLength);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypePath, Path Length: " << pathLength << " bytes");
+
+ sal_uInt32 header, pathFlags;
+ sal_Int32 points;
+
+ s.ReadUInt32(header).ReadInt32(points).ReadUInt32(pathFlags);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t header: 0x" << std::hex << header <<
+ " points: " << std::dec << points << " additional flags: 0x" << std::hex << pathFlags << std::dec);
+
+ EMFPPath path(points);
+ path.Read(s, pathFlags);
+ polygon = path.GetPolygon(rR);
+ break;
+ }
+ case RegionNodeDataTypeEmpty:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypeEmpty");
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO we need to set empty polygon here");
+ polygon = ::basegfx::B2DPolyPolygon();
+
+ break;
+ }
+ case RegionNodeDataTypeInfinite:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypeInfinite");
+ polygon = ::basegfx::B2DPolyPolygon();
+ break;
+ }
+ default:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t Unhandled region type: 0x" << std::hex << dataType << std::dec);
+ polygon = ::basegfx::B2DPolyPolygon();
+ }
+ }
+ return polygon;
+ }
+
+ void EMFPRegion::ReadRegion(SvStream& s, EmfPlusHelperData& rR)
+ {
+ sal_uInt32 header, count;
+ s.ReadUInt32(header).ReadUInt32(count);
+ // An array should be RegionNodeCount+1 of EmfPlusRegionNode objects.
+ SAL_INFO("drawinglayer.emf", "EMF+\t version: 0x" << std::hex << header << std::dec << ", region node count: " << count);
+
+ regionPolyPolygon = ReadRegionNode(s, rR);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpregion.hxx b/drawinglayer/source/tools/emfpregion.hxx
new file mode 100644
index 000000000..a234abbb5
--- /dev/null
+++ b/drawinglayer/source/tools/emfpregion.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+
+namespace emfplushelper
+{
+ typedef enum
+ {
+ RegionNodeDataTypeAnd = 0x00000001,
+ RegionNodeDataTypeOr = 0x00000002,
+ RegionNodeDataTypeXor = 0x00000003,
+ RegionNodeDataTypeExclude = 0x00000004,
+ RegionNodeDataTypeComplement = 0x00000005,
+ RegionNodeDataTypeRect = 0x10000000,
+ RegionNodeDataTypePath = 0x10000001,
+ RegionNodeDataTypeEmpty = 0x10000002,
+ RegionNodeDataTypeInfinite = 0x10000003
+ } RegionNodeDataType;
+
+ struct EMFPRegion : public EMFPObject
+ {
+ ::basegfx::B2DPolyPolygon regionPolyPolygon;
+
+ EMFPRegion();
+ virtual ~EMFPRegion() override;
+ void ReadRegion(SvStream& s, EmfPlusHelperData& rR);
+ ::basegfx::B2DPolyPolygon ReadRegionNode(SvStream& s, EmfPlusHelperData& rR);
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpstringformat.cxx b/drawinglayer/source/tools/emfpstringformat.cxx
new file mode 100644
index 000000000..0a053201b
--- /dev/null
+++ b/drawinglayer/source/tools/emfpstringformat.cxx
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+#include "emfpstringformat.hxx"
+
+namespace emfplushelper
+{
+ EMFPStringFormat::EMFPStringFormat()
+ : header(0)
+ , stringFormatFlags(0)
+ , language(0)
+ , stringAlignment(0)
+ , lineAlign(0)
+ , digitSubstitution(0)
+ , digitLanguage(0)
+ , firstTabOffset(0.0)
+ , hotkeyPrefix(0)
+ , leadingMargin(0.0)
+ , trailingMargin(0.0)
+ , tracking(1.0)
+ , trimming(0)
+ , tabStopCount(0)
+ , rangeCount(0)
+ {
+ }
+
+ static OUString StringFormatFlags(sal_uInt32 flag)
+ {
+ OUStringBuffer sFlags;
+
+ // These are extracted from enum in emfpstringformat.hxx
+ if (flag & StringFormatDirectionRightToLeft)
+ sFlags.append("StringFormatDirectionRightToLeft ");
+
+ if (flag & StringFormatDirectionVertical)
+ sFlags.append("StringFormatDirectionVertical ");
+
+ if (flag & StringFormatNoFitBlackBox)
+ sFlags.append("StringFormatNoFitBlackBox ");
+
+ if (flag & StringFormatDisplayFormatControl)
+ sFlags.append("StringFormatDisplayFormatControl ");
+
+ if (flag & StringFormatNoFontFallback)
+ sFlags.append("StringFormatNoFontFallback ");
+
+ if (flag & StringFormatMeasureTrailingSpaces)
+ sFlags.append("StringFormatMeasureTrailingSpaces ");
+
+ if (flag & StringFormatNoWrap)
+ sFlags.append("StringFormatNoWrap ");
+
+ if (flag & StringFormatLineLimit)
+ sFlags.append("StringFormatLineLimit ");
+
+ if (flag & StringFormatNoClip)
+ sFlags.append("StringFormatNoClip ");
+
+ if (flag & StringFormatBypassGDI)
+ sFlags.append("StringFormatBypassGDI ");
+
+ // There will be 1 extra space in the end. It could be truncated, but
+ // as it is for SAL_INFO() only, it would not be important
+ return sFlags.makeStringAndClear();
+ }
+
+ static OUString StringAlignmentString(sal_uInt32 nAlignment)
+ {
+ switch(nAlignment)
+ {
+ case StringAlignment::StringAlignmentNear:
+ return "StringAlignmentNear";
+ case StringAlignment::StringAlignmentCenter:
+ return "StringAlignmentCenter";
+ case StringAlignment::StringAlignmentFar:
+ return "StringAlignmentFar";
+ default:
+ assert(false && nAlignment && "invalid string alignment value");
+ return "INVALID";
+ }
+ }
+
+ static OUString DigitSubstitutionString(sal_uInt32 nSubst)
+ {
+ switch(nSubst)
+ {
+ case StringDigitSubstitution::StringDigitSubstitutionUser:
+ return "StringDigitSubstitutionUser";
+ case StringDigitSubstitution::StringDigitSubstitutionNone:
+ return "StringDigitSubstitutionNone";
+ case StringDigitSubstitution::StringDigitSubstitutionNational:
+ return "StringDigitSubstitutionNational";
+ case StringDigitSubstitution::StringDigitSubstitutionTraditional:
+ return "StringDigitSubstitutionTraditional";
+ default:
+ assert(false && nSubst && "invalid string digit substitution value");
+ return "INVALID";
+ }
+ }
+
+ static OUString HotkeyPrefixString(sal_uInt32 nHotkey)
+ {
+ switch(nHotkey)
+ {
+ case HotkeyPrefix::HotkeyPrefixNone:
+ return "HotkeyPrefixNone";
+ case HotkeyPrefix::HotkeyPrefixShow:
+ return "HotkeyPrefixShow";
+ case HotkeyPrefix::HotkeyPrefixHide:
+ return "HotkeyPrefixHide";
+ default:
+ assert(false && nHotkey && "invalid hotkey prefix value");
+ return "INVALID";
+ }
+ }
+
+ static OUString StringTrimmingString(sal_uInt32 nTrimming)
+ {
+ switch(nTrimming)
+ {
+ case StringTrimming::StringTrimmingNone:
+ return "StringTrimmingNone";
+ case StringTrimming::StringTrimmingCharacter:
+ return "StringTrimmingCharacter";
+ case StringTrimming::StringTrimmingWord:
+ return "StringTrimmingWord";
+ case StringTrimming::StringTrimmingEllipsisCharacter:
+ return "StringTrimmingEllipsisCharacter";
+ case StringTrimming::StringTrimmingEllipsisWord:
+ return "StringTrimmingEllipsisWord";
+ case StringTrimming::StringTrimmingEllipsisPath:
+ return "StringTrimmingEllipsisPath";
+ default:
+ assert(false && nTrimming && "invalid trim value");
+ return "INVALID";
+ }
+ }
+
+ void EMFPStringFormat::Read(SvMemoryStream &s)
+ {
+ s.ReadUInt32(header).ReadUInt32(stringFormatFlags).ReadUInt32(language);
+ s.ReadUInt32(stringAlignment).ReadUInt32(lineAlign).ReadUInt32(digitSubstitution).ReadUInt32(digitLanguage);
+ s.ReadFloat(firstTabOffset).ReadInt32(hotkeyPrefix).ReadFloat(leadingMargin).ReadFloat(trailingMargin).ReadFloat(tracking);
+ s.ReadInt32(trimming).ReadInt32(tabStopCount).ReadInt32(rangeCount);
+ // keep only the last 16 bits of language
+ language >>= 16;
+ digitLanguage >>= 16;
+ SAL_WARN_IF((header >> 12) != 0xdbc01, "drawinglayer.emf", "Invalid header - not 0xdbc01");
+ SAL_INFO("drawinglayer.emf", "EMF+\tString format");
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tHeader: 0x" << std::hex << (header >> 12));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << (header & 0x1fff) << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tStringFormatFlags: " << StringFormatFlags(stringFormatFlags));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tLanguage: sublangid: 0x" << std::hex << (language >> 10) << ", primarylangid: 0x" << (language & 0xF800));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tLineAlign: " << StringAlignmentString(lineAlign));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tDigitSubstitution: " << DigitSubstitutionString(digitSubstitution));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tDigitLanguage: sublangid: 0x" << std::hex << (digitLanguage >> 10) << ", primarylangid: 0x" << (digitLanguage & 0xF800));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tFirstTabOffset: " << firstTabOffset);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tHotkeyPrefix: " << HotkeyPrefixString(hotkeyPrefix));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tLeadingMargin: " << leadingMargin);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tTrailingMargin: " << trailingMargin);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tTracking: " << tracking);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tTrimming: " << StringTrimmingString(trimming));
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tTabStopCount: " << tabStopCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tRangeCount: " << rangeCount);
+
+ SAL_WARN_IF(digitSubstitution != StringDigitSubstitution::StringDigitSubstitutionNone,
+ "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:digitSubstitution");
+ SAL_WARN_IF(firstTabOffset != 0.0, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:firstTabOffset");
+ SAL_WARN_IF(hotkeyPrefix != HotkeyPrefix::HotkeyPrefixNone, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:hotkeyPrefix");
+ SAL_WARN_IF(tracking != 1.0, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:tracking");
+ SAL_WARN_IF(trimming != StringTrimming::StringTrimmingNone, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:trimming");
+ SAL_WARN_IF(tabStopCount, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:tabStopCount");
+ SAL_WARN_IF(rangeCount != 0, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:StringFormatData");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpstringformat.hxx b/drawinglayer/source/tools/emfpstringformat.hxx
new file mode 100644
index 000000000..b4d8cb380
--- /dev/null
+++ b/drawinglayer/source/tools/emfpstringformat.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "emfphelperdata.hxx"
+
+namespace emfplushelper
+{
+ const sal_uInt32 StringFormatDirectionRightToLeft = 0x00000001;
+ const sal_uInt32 StringFormatDirectionVertical = 0x00000002;
+ const sal_uInt32 StringFormatNoFitBlackBox = 0x00000004;
+ const sal_uInt32 StringFormatDisplayFormatControl = 0x00000020;
+ const sal_uInt32 StringFormatNoFontFallback = 0x00000400;
+ const sal_uInt32 StringFormatMeasureTrailingSpaces = 0x00000800;
+ const sal_uInt32 StringFormatNoWrap = 0x00001000;
+ const sal_uInt32 StringFormatLineLimit = 0x00002000;
+ const sal_uInt32 StringFormatNoClip = 0x00004000;
+ const sal_uInt32 StringFormatBypassGDI = 0x80000000;
+
+ enum StringAlignment
+ {
+ StringAlignmentNear = 0x00000000,
+ StringAlignmentCenter = 0x00000001,
+ StringAlignmentFar = 0x00000002
+ };
+
+ enum StringDigitSubstitution
+ {
+ StringDigitSubstitutionUser = 0x00000000,
+ StringDigitSubstitutionNone = 0x00000001,
+ StringDigitSubstitutionNational = 0x00000002,
+ StringDigitSubstitutionTraditional = 0x00000003
+ };
+
+ enum HotkeyPrefix
+ {
+ HotkeyPrefixNone = 0x00000000,
+ HotkeyPrefixShow = 0x00000001,
+ HotkeyPrefixHide = 0x00000002
+ };
+
+ enum StringTrimming
+ {
+ StringTrimmingNone = 0x00000000,
+ StringTrimmingCharacter = 0x00000001,
+ StringTrimmingWord = 0x00000002,
+ StringTrimmingEllipsisCharacter = 0x00000003,
+ StringTrimmingEllipsisWord = 0x00000004,
+ StringTrimmingEllipsisPath = 0x00000005
+ };
+
+ struct EMFPStringFormat : public EMFPObject
+ {
+ sal_uInt32 header;
+ sal_uInt32 stringFormatFlags;
+ sal_uInt32 language;
+ sal_uInt32 stringAlignment; // Horizontal alignment
+ sal_uInt32 lineAlign; // Vertical alignment
+ sal_uInt32 digitSubstitution;
+ sal_uInt32 digitLanguage;
+ float firstTabOffset;
+ sal_Int32 hotkeyPrefix;
+ float leadingMargin; // Length of the space to add to the starting position of a string.
+ float trailingMargin; // Length of the space to leave following a string.
+ float tracking;
+ sal_Int32 trimming;
+ sal_Int32 tabStopCount;
+ sal_Int32 rangeCount;
+
+ EMFPStringFormat();
+ void Read(SvMemoryStream &s);
+
+ // flags table from MS-EMFPLUS doc
+ bool DirectionRightToLeft() const { return stringFormatFlags & 0x00000001;}
+ bool DirectionVertical() const { return stringFormatFlags & 0x00000002;}
+ bool NoFitBlackBox() const { return stringFormatFlags & 0x00000004;}
+ bool DisplayFormatControl() const { return stringFormatFlags & 0x00000020;}
+ bool NoFontFallback() const { return stringFormatFlags & 0x00000400;}
+ bool MeasureTrailingSpaces() const { return stringFormatFlags & 0x00000800;}
+ bool NoWrap() const { return stringFormatFlags & 0x00001000;}
+ bool LineLimit() const { return stringFormatFlags & 0x00002000;}
+ bool NoClip() const { return stringFormatFlags & 0x00004000;}
+ bool BypassGDI() const { return stringFormatFlags & 0x80000000;}
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx
new file mode 100644
index 000000000..63562973d
--- /dev/null
+++ b/drawinglayer/source/tools/primitive2dxmldump.cxx
@@ -0,0 +1,1182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <drawinglayer/tools/primitive2dxmldump.hxx>
+
+#include <rtl/string.hxx>
+#include <tools/stream.hxx>
+#include <tools/XmlWriter.hxx>
+
+#include <math.h>
+#include <memory>
+#include <sal/log.hxx>
+
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <primitive2d/textlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <drawinglayer/primitive3d/baseprimitive3d.hxx>
+#include <drawinglayer/primitive3d/Tools.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/primitive3d/sdrextrudeprimitive3d.hxx>
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/fillhatchattribute.hxx>
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+
+using namespace drawinglayer::primitive2d;
+
+namespace drawinglayer
+{
+namespace
+{
+const size_t constMaxActionType = 513;
+
+OUString convertColorToString(const basegfx::BColor& rColor)
+{
+ OUString aRGBString = Color(rColor).AsRGBHexString();
+ return "#" + aRGBString;
+}
+
+void writeMatrix(::tools::XmlWriter& rWriter, const basegfx::B2DHomMatrix& rMatrix)
+{
+ rWriter.attribute("xy11", rMatrix.get(0, 0));
+ rWriter.attribute("xy12", rMatrix.get(0, 1));
+ rWriter.attribute("xy13", rMatrix.get(0, 2));
+ rWriter.attribute("xy21", rMatrix.get(1, 0));
+ rWriter.attribute("xy22", rMatrix.get(1, 1));
+ rWriter.attribute("xy23", rMatrix.get(1, 2));
+ rWriter.attribute("xy31", rMatrix.get(2, 0));
+ rWriter.attribute("xy32", rMatrix.get(2, 1));
+ rWriter.attribute("xy33", rMatrix.get(2, 2));
+}
+
+void writeMatrix3D(::tools::XmlWriter& rWriter, const basegfx::B3DHomMatrix& rMatrix)
+{
+ rWriter.attribute("xy11", rMatrix.get(0, 0));
+ rWriter.attribute("xy12", rMatrix.get(0, 1));
+ rWriter.attribute("xy13", rMatrix.get(0, 2));
+ rWriter.attribute("xy14", rMatrix.get(0, 3));
+ rWriter.attribute("xy21", rMatrix.get(1, 0));
+ rWriter.attribute("xy22", rMatrix.get(1, 1));
+ rWriter.attribute("xy23", rMatrix.get(1, 2));
+ rWriter.attribute("xy24", rMatrix.get(1, 3));
+ rWriter.attribute("xy31", rMatrix.get(2, 0));
+ rWriter.attribute("xy32", rMatrix.get(2, 1));
+ rWriter.attribute("xy33", rMatrix.get(2, 2));
+ rWriter.attribute("xy34", rMatrix.get(2, 3));
+ rWriter.attribute("xy41", rMatrix.get(3, 0));
+ rWriter.attribute("xy42", rMatrix.get(3, 1));
+ rWriter.attribute("xy43", rMatrix.get(3, 2));
+ rWriter.attribute("xy44", rMatrix.get(3, 3));
+}
+
+void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon& rB2DPolyPolygon)
+{
+ rWriter.startElement("polypolygon");
+ const basegfx::B2DRange aB2DRange(rB2DPolyPolygon.getB2DRange());
+ rWriter.attributeDouble("height", aB2DRange.getHeight());
+ rWriter.attributeDouble("width", aB2DRange.getWidth());
+ rWriter.attributeDouble("minx", aB2DRange.getMinX());
+ rWriter.attributeDouble("miny", aB2DRange.getMinY());
+ rWriter.attributeDouble("maxx", aB2DRange.getMaxX());
+ rWriter.attributeDouble("maxy", aB2DRange.getMaxY());
+ rWriter.attribute("path", basegfx::utils::exportToSvgD(rB2DPolyPolygon, true, true, false));
+
+ for (basegfx::B2DPolygon const& rPolygon : rB2DPolyPolygon)
+ {
+ rWriter.startElement("polygon");
+ for (sal_uInt32 i = 0; i < rPolygon.count(); ++i)
+ {
+ basegfx::B2DPoint const& rPoint = rPolygon.getB2DPoint(i);
+
+ rWriter.startElement("point");
+ rWriter.attribute("x", OUString::number(rPoint.getX()));
+ rWriter.attribute("y", OUString::number(rPoint.getY()));
+ rWriter.endElement();
+ }
+ rWriter.endElement();
+ }
+
+ rWriter.endElement();
+}
+
+void writeStrokeAttribute(::tools::XmlWriter& rWriter,
+ const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute)
+{
+ if (!rStrokeAttribute.getDotDashArray().empty())
+ {
+ rWriter.startElement("stroke");
+
+ OUString sDotDash;
+ for (double fDotDash : rStrokeAttribute.getDotDashArray())
+ {
+ sDotDash += OUString::number(lround(fDotDash)) + " ";
+ }
+ rWriter.attribute("dotDashArray", sDotDash);
+ rWriter.attribute("fullDotDashLength", rStrokeAttribute.getFullDotDashLen());
+ rWriter.endElement();
+ }
+}
+
+void writeLineAttribute(::tools::XmlWriter& rWriter,
+ const drawinglayer::attribute::LineAttribute& rLineAttribute)
+{
+ rWriter.startElement("line");
+ rWriter.attribute("color", convertColorToString(rLineAttribute.getColor()));
+ rWriter.attribute("width", rLineAttribute.getWidth());
+ switch (rLineAttribute.getLineJoin())
+ {
+ case basegfx::B2DLineJoin::NONE:
+ rWriter.attribute("linejoin", "NONE");
+ break;
+ case basegfx::B2DLineJoin::Bevel:
+ rWriter.attribute("linejoin", "Bevel");
+ break;
+ case basegfx::B2DLineJoin::Miter:
+ {
+ rWriter.attribute("linejoin", "Miter");
+ rWriter.attribute("miterangle",
+ basegfx::rad2deg(rLineAttribute.getMiterMinimumAngle()));
+ break;
+ }
+ case basegfx::B2DLineJoin::Round:
+ rWriter.attribute("linejoin", "Round");
+ break;
+ default:
+ rWriter.attribute("linejoin", "Unknown");
+ break;
+ }
+ switch (rLineAttribute.getLineCap())
+ {
+ case css::drawing::LineCap::LineCap_BUTT:
+ rWriter.attribute("linecap", "BUTT");
+ break;
+ case css::drawing::LineCap::LineCap_ROUND:
+ rWriter.attribute("linecap", "ROUND");
+ break;
+ case css::drawing::LineCap::LineCap_SQUARE:
+ rWriter.attribute("linecap", "SQUARE");
+ break;
+ default:
+ rWriter.attribute("linecap", "Unknown");
+ break;
+ }
+
+ rWriter.endElement();
+}
+
+void writeSdrLineAttribute(::tools::XmlWriter& rWriter,
+ const drawinglayer::attribute::SdrLineAttribute& rLineAttribute)
+{
+ if (rLineAttribute.isDefault())
+ return;
+
+ rWriter.startElement("line");
+ rWriter.attribute("color", convertColorToString(rLineAttribute.getColor()));
+ rWriter.attribute("width", rLineAttribute.getWidth());
+ rWriter.attribute("transparence", rLineAttribute.getTransparence());
+
+ switch (rLineAttribute.getJoin())
+ {
+ case basegfx::B2DLineJoin::NONE:
+ rWriter.attribute("linejoin", "NONE");
+ break;
+ case basegfx::B2DLineJoin::Bevel:
+ rWriter.attribute("linejoin", "Bevel");
+ break;
+ case basegfx::B2DLineJoin::Miter:
+ rWriter.attribute("linejoin", "Miter");
+ break;
+ case basegfx::B2DLineJoin::Round:
+ rWriter.attribute("linejoin", "Round");
+ break;
+ default:
+ rWriter.attribute("linejoin", "Unknown");
+ break;
+ }
+ switch (rLineAttribute.getCap())
+ {
+ case css::drawing::LineCap::LineCap_BUTT:
+ rWriter.attribute("linecap", "BUTT");
+ break;
+ case css::drawing::LineCap::LineCap_ROUND:
+ rWriter.attribute("linecap", "ROUND");
+ break;
+ case css::drawing::LineCap::LineCap_SQUARE:
+ rWriter.attribute("linecap", "SQUARE");
+ break;
+ default:
+ rWriter.attribute("linecap", "Unknown");
+ break;
+ }
+
+ if (!rLineAttribute.getDotDashArray().empty())
+ {
+ OUString sDotDash;
+ for (double fDotDash : rLineAttribute.getDotDashArray())
+ {
+ sDotDash += OUString::number(fDotDash) + " ";
+ }
+ rWriter.attribute("dotDashArray", sDotDash);
+ rWriter.attribute("fullDotDashLength", rLineAttribute.getFullDotDashLen());
+ }
+
+ rWriter.endElement();
+}
+
+void writeSdrFillAttribute(::tools::XmlWriter& rWriter,
+ const drawinglayer::attribute::SdrFillAttribute& rFillAttribute)
+{
+ if (rFillAttribute.isDefault())
+ return;
+
+ rWriter.startElement("fill");
+ rWriter.attribute("color", convertColorToString(rFillAttribute.getColor()));
+ rWriter.attribute("transparence", rFillAttribute.getTransparence());
+
+ auto const& rGradient = rFillAttribute.getGradient();
+ if (!rGradient.isDefault())
+ {
+ rWriter.startElement("gradient");
+ switch (rGradient.getStyle())
+ {
+ case drawinglayer::attribute::GradientStyle::Linear:
+ rWriter.attribute("style", "Linear");
+ break;
+ case drawinglayer::attribute::GradientStyle::Axial:
+ rWriter.attribute("style", "Axial");
+ break;
+ case drawinglayer::attribute::GradientStyle::Radial:
+ rWriter.attribute("style", "Radial");
+ break;
+ case drawinglayer::attribute::GradientStyle::Elliptical:
+ rWriter.attribute("style", "Elliptical");
+ break;
+ case drawinglayer::attribute::GradientStyle::Square:
+ rWriter.attribute("style", "Square");
+ break;
+ case drawinglayer::attribute::GradientStyle::Rect:
+ rWriter.attribute("style", "Rect");
+ break;
+ }
+ rWriter.attribute("border", rGradient.getBorder());
+ rWriter.attribute("offsetX", rGradient.getOffsetX());
+ rWriter.attribute("offsetY", rGradient.getOffsetY());
+ rWriter.attribute("angle", rGradient.getAngle());
+ rWriter.attribute("steps", rGradient.getSteps());
+ rWriter.attribute("startColor", convertColorToString(rGradient.getStartColor()));
+ rWriter.attribute("endColor", convertColorToString(rGradient.getEndColor()));
+ rWriter.endElement();
+ }
+
+ auto const& rHatch = rFillAttribute.getHatch();
+ if (!rHatch.isDefault())
+ {
+ rWriter.startElement("hatch");
+ switch (rHatch.getStyle())
+ {
+ case drawinglayer::attribute::HatchStyle::Single:
+ rWriter.attribute("style", "Single");
+ break;
+ case drawinglayer::attribute::HatchStyle::Double:
+ rWriter.attribute("style", "Double");
+ break;
+ case drawinglayer::attribute::HatchStyle::Triple:
+ rWriter.attribute("style", "Triple");
+ break;
+ }
+ rWriter.attribute("distance", rHatch.getDistance());
+ rWriter.attribute("angle", rHatch.getAngle());
+ rWriter.attribute("color", convertColorToString(rHatch.getColor()));
+ rWriter.attribute("minimalDescreteDistance", rHatch.getMinimalDiscreteDistance());
+ rWriter.attribute("isFillBackground", sal_Int32(rHatch.isFillBackground()));
+ rWriter.endElement();
+ }
+
+ auto const& rGraphic = rFillAttribute.getFillGraphic();
+ if (!rGraphic.isDefault())
+ {
+ rWriter.startElement("graphic");
+ // TODO
+ rWriter.endElement();
+ }
+
+ rWriter.endElement();
+}
+
+void writeShadeMode(::tools::XmlWriter& rWriter, const css::drawing::ShadeMode& rMode)
+{
+ switch (rMode)
+ {
+ case css::drawing::ShadeMode_FLAT:
+ rWriter.attribute("shadeMode", "Flat");
+ break;
+ case css::drawing::ShadeMode_SMOOTH:
+ rWriter.attribute("shadeMode", "Smooth");
+ break;
+ case css::drawing::ShadeMode_PHONG:
+ rWriter.attribute("shadeMode", "Phong");
+ break;
+ case css::drawing::ShadeMode_DRAFT:
+ rWriter.attribute("shadeMode", "Draft");
+ break;
+ default:
+ rWriter.attribute("shadeMode", "Undefined");
+ break;
+ }
+}
+
+void writeProjectionMode(::tools::XmlWriter& rWriter, const css::drawing::ProjectionMode& rMode)
+{
+ switch (rMode)
+ {
+ case css::drawing::ProjectionMode_PARALLEL:
+ rWriter.attribute("projectionMode", "Parallel");
+ break;
+ case css::drawing::ProjectionMode_PERSPECTIVE:
+ rWriter.attribute("projectionMode", "Perspective");
+ break;
+ default:
+ rWriter.attribute("projectionMode", "Undefined");
+ break;
+ }
+}
+
+void writeNormalsKind(::tools::XmlWriter& rWriter, const css::drawing::NormalsKind& rKind)
+{
+ switch (rKind)
+ {
+ case css::drawing::NormalsKind_SPECIFIC:
+ rWriter.attribute("normalsKind", "Specific");
+ break;
+ case css::drawing::NormalsKind_FLAT:
+ rWriter.attribute("normalsKind", "Flat");
+ break;
+ case css::drawing::NormalsKind_SPHERE:
+ rWriter.attribute("normalsKind", "Sphere");
+ break;
+ default:
+ rWriter.attribute("normalsKind", "Undefined");
+ break;
+ }
+}
+
+void writeTextureProjectionMode(::tools::XmlWriter& rWriter, const char* pElement,
+ const css::drawing::TextureProjectionMode& rMode)
+{
+ switch (rMode)
+ {
+ case css::drawing::TextureProjectionMode_OBJECTSPECIFIC:
+ rWriter.attribute(pElement, "Specific");
+ break;
+ case css::drawing::TextureProjectionMode_PARALLEL:
+ rWriter.attribute(pElement, "Parallel");
+ break;
+ case css::drawing::TextureProjectionMode_SPHERE:
+ rWriter.attribute(pElement, "Sphere");
+ break;
+ default:
+ rWriter.attribute(pElement, "Undefined");
+ break;
+ }
+}
+
+void writeTextureKind(::tools::XmlWriter& rWriter, const css::drawing::TextureKind2& rKind)
+{
+ switch (rKind)
+ {
+ case css::drawing::TextureKind2_LUMINANCE:
+ rWriter.attribute("textureKind", "Luminance");
+ break;
+ case css::drawing::TextureKind2_INTENSITY:
+ rWriter.attribute("textureKind", "Intensity");
+ break;
+ case css::drawing::TextureKind2_COLOR:
+ rWriter.attribute("textureKind", "Color");
+ break;
+ default:
+ rWriter.attribute("textureKind", "Undefined");
+ break;
+ }
+}
+
+void writeTextureMode(::tools::XmlWriter& rWriter, const css::drawing::TextureMode& rMode)
+{
+ switch (rMode)
+ {
+ case css::drawing::TextureMode_REPLACE:
+ rWriter.attribute("textureMode", "Replace");
+ break;
+ case css::drawing::TextureMode_MODULATE:
+ rWriter.attribute("textureMode", "Modulate");
+ break;
+ case css::drawing::TextureMode_BLEND:
+ rWriter.attribute("textureMode", "Blend");
+ break;
+ default:
+ rWriter.attribute("textureMode", "Undefined");
+ break;
+ }
+}
+
+void writeMaterialAttribute(::tools::XmlWriter& rWriter,
+ const drawinglayer::attribute::MaterialAttribute3D& rMaterial)
+{
+ rWriter.startElement("material");
+ rWriter.attribute("color", convertColorToString(rMaterial.getColor()));
+ rWriter.attribute("specular", convertColorToString(rMaterial.getSpecular()));
+ rWriter.attribute("emission", convertColorToString(rMaterial.getEmission()));
+ rWriter.attribute("specularIntensity", rMaterial.getSpecularIntensity());
+ rWriter.endElement();
+}
+
+void writeSpreadMethod(::tools::XmlWriter& rWriter,
+ const drawinglayer::primitive2d::SpreadMethod& rSpreadMethod)
+{
+ switch (rSpreadMethod)
+ {
+ case drawinglayer::primitive2d::SpreadMethod::Pad:
+ rWriter.attribute("spreadmethod", "pad");
+ break;
+ case drawinglayer::primitive2d::SpreadMethod::Reflect:
+ rWriter.attribute("spreadmethod", "reflect");
+ break;
+ case drawinglayer::primitive2d::SpreadMethod::Repeat:
+ rWriter.attribute("spreadmethod", "repeat");
+ break;
+ default:
+ rWriter.attribute("spreadmethod", "unknown");
+ }
+}
+
+} // end anonymous namespace
+
+Primitive2dXmlDump::Primitive2dXmlDump()
+ : maFilter(constMaxActionType, false)
+{
+}
+
+Primitive2dXmlDump::~Primitive2dXmlDump() = default;
+
+void Primitive2dXmlDump::dump(
+ const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence,
+ const OUString& rStreamName)
+{
+ std::unique_ptr<SvStream> pStream;
+
+ if (rStreamName.isEmpty())
+ pStream.reset(new SvMemoryStream());
+ else
+ pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
+
+ ::tools::XmlWriter aWriter(pStream.get());
+ aWriter.startDocument();
+ aWriter.startElement("primitive2D");
+
+ decomposeAndWrite(rPrimitive2DSequence, aWriter);
+
+ aWriter.endElement();
+ aWriter.endDocument();
+
+ pStream->Seek(STREAM_SEEK_TO_BEGIN);
+}
+
+namespace
+{
+class Primitive3DXmlDump
+{
+public:
+ void decomposeAndWrite(const drawinglayer::primitive3d::Primitive3DContainer& rSequence,
+ ::tools::XmlWriter& rWriter)
+ {
+ for (size_t i = 0; i < rSequence.size(); i++)
+ {
+ drawinglayer::primitive3d::Primitive3DReference xReference = rSequence[i];
+ const auto* pBasePrimitive
+ = static_cast<const drawinglayer::primitive3d::BasePrimitive3D*>(xReference.get());
+ sal_uInt32 nId = pBasePrimitive->getPrimitive3DID();
+ OUString sCurrentElementTag = drawinglayer::primitive3d::idToString(nId);
+ switch (nId)
+ {
+ case PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D:
+ {
+ const auto* pExtrudePrimitive3D
+ = static_cast<const drawinglayer::primitive3d::SdrExtrudePrimitive3D*>(
+ xReference.get());
+ rWriter.startElement("extrude3D");
+
+ rWriter.startElement("matrix3D");
+ writeMatrix3D(rWriter, pExtrudePrimitive3D->getTransform());
+ rWriter.endElement();
+
+ rWriter.attribute("textureSizeX", pExtrudePrimitive3D->getTextureSize().getX());
+ rWriter.attribute("textureSizeY", pExtrudePrimitive3D->getTextureSize().getY());
+ auto const& rLFSAttribute = pExtrudePrimitive3D->getSdrLFSAttribute();
+ writeSdrLineAttribute(rWriter, rLFSAttribute.getLine());
+ writeSdrFillAttribute(rWriter, rLFSAttribute.getFill());
+
+ rWriter.startElement("object3Dattributes");
+ {
+ auto const& r3DObjectAttributes
+ = pExtrudePrimitive3D->getSdr3DObjectAttribute();
+
+ writeNormalsKind(rWriter, r3DObjectAttributes.getNormalsKind());
+ writeTextureProjectionMode(rWriter, "textureProjectionX",
+ r3DObjectAttributes.getTextureProjectionX());
+ writeTextureProjectionMode(rWriter, "textureProjectionY",
+ r3DObjectAttributes.getTextureProjectionY());
+ writeTextureKind(rWriter, r3DObjectAttributes.getTextureKind());
+ writeTextureMode(rWriter, r3DObjectAttributes.getTextureMode());
+ writeMaterialAttribute(rWriter, r3DObjectAttributes.getMaterial());
+
+ rWriter.attribute("normalsInvert",
+ sal_Int32(r3DObjectAttributes.getNormalsInvert()));
+ rWriter.attribute("doubleSided",
+ sal_Int32(r3DObjectAttributes.getDoubleSided()));
+ rWriter.attribute("shadow3D", sal_Int32(r3DObjectAttributes.getShadow3D()));
+ rWriter.attribute("textureFilter",
+ sal_Int32(r3DObjectAttributes.getTextureFilter()));
+ rWriter.attribute("reducedGeometry",
+ sal_Int32(r3DObjectAttributes.getReducedLineGeometry()));
+ }
+ rWriter.endElement();
+
+ rWriter.attribute("depth", pExtrudePrimitive3D->getDepth());
+ rWriter.attribute("diagonal", pExtrudePrimitive3D->getDiagonal());
+ rWriter.attribute("backScale", pExtrudePrimitive3D->getBackScale());
+ rWriter.attribute("smoothNormals",
+ sal_Int32(pExtrudePrimitive3D->getSmoothNormals()));
+ rWriter.attribute("smoothLids",
+ sal_Int32(pExtrudePrimitive3D->getSmoothLids()));
+ rWriter.attribute("characterMode",
+ sal_Int32(pExtrudePrimitive3D->getCharacterMode()));
+ rWriter.attribute("closeFront",
+ sal_Int32(pExtrudePrimitive3D->getCloseFront()));
+ rWriter.attribute("closeBack", sal_Int32(pExtrudePrimitive3D->getCloseBack()));
+ writePolyPolygon(rWriter, pExtrudePrimitive3D->getPolyPolygon());
+ rWriter.endElement();
+ }
+ break;
+
+ default:
+ {
+ rWriter.startElement("unhandled");
+ rWriter.attribute("id",
+ OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8));
+ rWriter.attribute("idNumber", nId);
+
+ drawinglayer::geometry::ViewInformation3D aViewInformation3D;
+ drawinglayer::primitive3d::Primitive3DContainer aContainer;
+ aContainer = pBasePrimitive->get3DDecomposition(aViewInformation3D);
+ decomposeAndWrite(aContainer, rWriter);
+ rWriter.endElement();
+ }
+ break;
+ }
+ }
+ }
+};
+}
+xmlDocUniquePtr Primitive2dXmlDump::dumpAndParse(
+ const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence,
+ const OUString& rStreamName)
+{
+ std::unique_ptr<SvStream> pStream;
+
+ if (rStreamName.isEmpty())
+ pStream.reset(new SvMemoryStream());
+ else
+ pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
+
+ ::tools::XmlWriter aWriter(pStream.get());
+ aWriter.startDocument();
+ aWriter.startElement("primitive2D");
+
+ decomposeAndWrite(rPrimitive2DSequence, aWriter);
+
+ aWriter.endElement();
+ aWriter.endDocument();
+
+ pStream->Seek(STREAM_SEEK_TO_BEGIN);
+
+ std::size_t nSize = pStream->remainingSize();
+ std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nSize + 1]);
+ pStream->ReadBytes(pBuffer.get(), nSize);
+ pBuffer[nSize] = 0;
+ SAL_INFO("drawinglayer", "Parsed XML: " << pBuffer.get());
+
+ return xmlDocUniquePtr(xmlParseDoc(reinterpret_cast<xmlChar*>(pBuffer.get())));
+}
+
+void Primitive2dXmlDump::decomposeAndWrite(
+ const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence,
+ ::tools::XmlWriter& rWriter)
+{
+ for (size_t i = 0; i < rPrimitive2DSequence.size(); i++)
+ {
+ const BasePrimitive2D* pBasePrimitive = rPrimitive2DSequence[i].get();
+ sal_uInt32 nId = pBasePrimitive->getPrimitive2DID();
+ if (nId < maFilter.size() && maFilter[nId])
+ continue;
+
+ OUString sCurrentElementTag = drawinglayer::primitive2d::idToString(nId);
+
+ switch (nId)
+ {
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ {
+ const BitmapPrimitive2D& rBitmapPrimitive2D
+ = dynamic_cast<const BitmapPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("bitmap");
+ writeMatrix(rWriter, rBitmapPrimitive2D.getTransform());
+
+ const BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapPrimitive2D.getXBitmap()));
+ const Size& rSizePixel(aBitmapEx.GetSizePixel());
+
+ rWriter.attribute("height", rSizePixel.getHeight());
+ rWriter.attribute("width", rSizePixel.getWidth());
+ rWriter.attribute("checksum", OString(std::to_string(aBitmapEx.GetChecksum())));
+
+ for (tools::Long y = 0; y < rSizePixel.getHeight(); y++)
+ {
+ rWriter.startElement("data");
+ OUString aBitmapData = "";
+ for (tools::Long x = 0; x < rSizePixel.getHeight(); x++)
+ {
+ if (x != 0)
+ aBitmapData = aBitmapData + ",";
+ aBitmapData = aBitmapData + aBitmapEx.GetPixelColor(x, y).AsRGBHexString();
+ }
+ rWriter.attribute("row", aBitmapData);
+ rWriter.endElement();
+ }
+ rWriter.endElement();
+ }
+ break;
+ case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D:
+ {
+ const HiddenGeometryPrimitive2D& rHiddenGeometryPrimitive2D
+ = dynamic_cast<const HiddenGeometryPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("hiddengeometry");
+ decomposeAndWrite(rHiddenGeometryPrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ {
+ const TransformPrimitive2D& rTransformPrimitive2D
+ = dynamic_cast<const TransformPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("transform");
+ writeMatrix(rWriter, rTransformPrimitive2D.getTransformation());
+ decomposeAndWrite(rTransformPrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ {
+ const PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D
+ = dynamic_cast<const PolyPolygonColorPrimitive2D&>(*pBasePrimitive);
+
+ rWriter.startElement("polypolygoncolor");
+ rWriter.attribute("color",
+ convertColorToString(rPolyPolygonColorPrimitive2D.getBColor()));
+
+ const basegfx::B2DPolyPolygon& aB2DPolyPolygon(
+ rPolyPolygonColorPrimitive2D.getB2DPolyPolygon());
+ writePolyPolygon(rWriter, aB2DPolyPolygon);
+
+ rWriter.endElement();
+ }
+ break;
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ {
+ const PointArrayPrimitive2D& rPointArrayPrimitive2D
+ = dynamic_cast<const PointArrayPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("pointarray");
+
+ rWriter.attribute("color",
+ convertColorToString(rPointArrayPrimitive2D.getRGBColor()));
+
+ const std::vector<basegfx::B2DPoint> aPositions
+ = rPointArrayPrimitive2D.getPositions();
+ for (std::vector<basegfx::B2DPoint>::const_iterator iter = aPositions.begin();
+ iter != aPositions.end(); ++iter)
+ {
+ rWriter.startElement("point");
+ rWriter.attribute("x", OUString::number(iter->getX()));
+ rWriter.attribute("y", OUString::number(iter->getY()));
+ rWriter.endElement();
+ }
+
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D:
+ {
+ const PolygonStrokeArrowPrimitive2D& rPolygonStrokeArrowPrimitive2D
+ = dynamic_cast<const PolygonStrokeArrowPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("polygonstrokearrow");
+
+ rWriter.startElement("polygon");
+ rWriter.content(basegfx::utils::exportToSvgPoints(
+ rPolygonStrokeArrowPrimitive2D.getB2DPolygon()));
+ rWriter.endElement();
+
+ if (rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon().count())
+ {
+ rWriter.startElement("linestartattribute");
+ rWriter.attribute("width",
+ rPolygonStrokeArrowPrimitive2D.getStart().getWidth());
+ rWriter.attribute("centered",
+ static_cast<sal_Int32>(
+ rPolygonStrokeArrowPrimitive2D.getStart().isCentered()));
+ writePolyPolygon(rWriter,
+ rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon());
+ rWriter.endElement();
+ }
+
+ if (rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon().count())
+ {
+ rWriter.startElement("lineendattribute");
+ rWriter.attribute("width", rPolygonStrokeArrowPrimitive2D.getEnd().getWidth());
+ rWriter.attribute("centered",
+ static_cast<sal_Int32>(
+ rPolygonStrokeArrowPrimitive2D.getEnd().isCentered()));
+ writePolyPolygon(rWriter,
+ rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon());
+ rWriter.endElement();
+ }
+
+ writeLineAttribute(rWriter, rPolygonStrokeArrowPrimitive2D.getLineAttribute());
+ writeStrokeAttribute(rWriter, rPolygonStrokeArrowPrimitive2D.getStrokeAttribute());
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ {
+ const PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D
+ = dynamic_cast<const PolygonStrokePrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("polygonstroke");
+
+ rWriter.startElement("polygon");
+ rWriter.content(
+ basegfx::utils::exportToSvgPoints(rPolygonStrokePrimitive2D.getB2DPolygon()));
+ rWriter.endElement();
+
+ writeLineAttribute(rWriter, rPolygonStrokePrimitive2D.getLineAttribute());
+ writeStrokeAttribute(rWriter, rPolygonStrokePrimitive2D.getStrokeAttribute());
+ rWriter.endElement();
+ }
+ break;
+ case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D:
+ {
+ const PolyPolygonStrokePrimitive2D& rPolyPolygonStrokePrimitive2D
+ = dynamic_cast<const PolyPolygonStrokePrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("polypolygonstroke");
+
+ writeLineAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getLineAttribute());
+ writeStrokeAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getStrokeAttribute());
+ writePolyPolygon(rWriter, rPolyPolygonStrokePrimitive2D.getB2DPolyPolygon());
+
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ {
+ const PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D
+ = dynamic_cast<const PolygonHairlinePrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("polygonhairline");
+
+ rWriter.attribute("color",
+ convertColorToString(rPolygonHairlinePrimitive2D.getBColor()));
+
+ rWriter.startElement("polygon");
+ rWriter.content(
+ basegfx::utils::exportToSvgPoints(rPolygonHairlinePrimitive2D.getB2DPolygon()));
+ rWriter.endElement();
+
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
+ {
+ const TextDecoratedPortionPrimitive2D& rTextDecoratedPortionPrimitive2D
+ = dynamic_cast<const TextDecoratedPortionPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("textdecoratedportion");
+ writeMatrix(rWriter, rTextDecoratedPortionPrimitive2D.getTextTransform());
+
+ rWriter.attribute("text", rTextDecoratedPortionPrimitive2D.getText());
+ rWriter.attribute(
+ "fontcolor",
+ convertColorToString(rTextDecoratedPortionPrimitive2D.getFontColor()));
+
+ const drawinglayer::attribute::FontAttribute& aFontAttribute
+ = rTextDecoratedPortionPrimitive2D.getFontAttribute();
+ rWriter.attribute("familyname", aFontAttribute.getFamilyName());
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D:
+ {
+ const TextLinePrimitive2D& rTextLinePrimitive2D
+ = dynamic_cast<const TextLinePrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("textline");
+ writeMatrix(rWriter, rTextLinePrimitive2D.getObjectTransformation());
+
+ rWriter.attribute("width", rTextLinePrimitive2D.getWidth());
+ rWriter.attribute("offset", rTextLinePrimitive2D.getOffset());
+ rWriter.attribute("height", rTextLinePrimitive2D.getHeight());
+ rWriter.attribute("color",
+ convertColorToString(rTextLinePrimitive2D.getLineColor()));
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
+ {
+ const TextSimplePortionPrimitive2D& rTextSimplePortionPrimitive2D
+ = dynamic_cast<const TextSimplePortionPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("textsimpleportion");
+
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ if (rTextSimplePortionPrimitive2D.getTextTransform().decompose(aScale, aTranslate,
+ fRotate, fShearX))
+ {
+ rWriter.attribute("width", aScale.getX());
+ rWriter.attribute("height", aScale.getY());
+ }
+ rWriter.attribute("x", aTranslate.getX());
+ rWriter.attribute("y", aTranslate.getY());
+ OUString aText = rTextSimplePortionPrimitive2D.getText();
+ // TODO share code with sax_fastparser::FastSaxSerializer::write().
+ rWriter.attribute("text", aText.replaceAll("", "&#9;"));
+ rWriter.attribute("fontcolor", convertColorToString(
+ rTextSimplePortionPrimitive2D.getFontColor()));
+
+ const drawinglayer::attribute::FontAttribute& aFontAttribute
+ = rTextSimplePortionPrimitive2D.getFontAttribute();
+ rWriter.attribute("familyname", aFontAttribute.getFamilyName());
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_GROUPPRIMITIVE2D:
+ {
+ const GroupPrimitive2D& rGroupPrimitive2D
+ = dynamic_cast<const GroupPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("group");
+ decomposeAndWrite(rGroupPrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ {
+ const MaskPrimitive2D& rMaskPrimitive2D
+ = dynamic_cast<const MaskPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("mask");
+ writePolyPolygon(rWriter, rMaskPrimitive2D.getMask());
+ decomposeAndWrite(rMaskPrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+ {
+ const UnifiedTransparencePrimitive2D& rUnifiedTransparencePrimitive2D
+ = dynamic_cast<const UnifiedTransparencePrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("unifiedtransparence");
+ rWriter.attribute(
+ "transparence",
+ std::lround(100 * rUnifiedTransparencePrimitive2D.getTransparence()));
+ decomposeAndWrite(rUnifiedTransparencePrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
+ {
+ const ObjectInfoPrimitive2D& rObjectInfoPrimitive2D
+ = dynamic_cast<const ObjectInfoPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("objectinfo");
+
+ decomposeAndWrite(rObjectInfoPrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D:
+ {
+ const SvgRadialGradientPrimitive2D& rSvgRadialGradientPrimitive2D
+ = dynamic_cast<const SvgRadialGradientPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("svgradialgradient");
+ if (rSvgRadialGradientPrimitive2D.isFocalSet())
+ {
+ basegfx::B2DPoint aFocalAttribute = rSvgRadialGradientPrimitive2D.getFocal();
+ rWriter.attribute("focalx", aFocalAttribute.getX());
+ rWriter.attribute("focaly", aFocalAttribute.getY());
+ }
+
+ basegfx::B2DPoint aStartPoint = rSvgRadialGradientPrimitive2D.getStart();
+ rWriter.attribute("startx", aStartPoint.getX());
+ rWriter.attribute("starty", aStartPoint.getY());
+ rWriter.attribute("radius",
+ OString::number(rSvgRadialGradientPrimitive2D.getRadius()));
+ writeSpreadMethod(rWriter, rSvgRadialGradientPrimitive2D.getSpreadMethod());
+ rWriter.attributeDouble(
+ "opacity",
+ rSvgRadialGradientPrimitive2D.getGradientEntries().front().getOpacity());
+
+ rWriter.startElement("transform");
+ writeMatrix(rWriter, rSvgRadialGradientPrimitive2D.getGradientTransform());
+ rWriter.endElement();
+
+ writePolyPolygon(rWriter, rSvgRadialGradientPrimitive2D.getPolyPolygon());
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D:
+ {
+ const SvgLinearGradientPrimitive2D& rSvgLinearGradientPrimitive2D
+ = dynamic_cast<const SvgLinearGradientPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("svglineargradient");
+ basegfx::B2DPoint aStartAttribute = rSvgLinearGradientPrimitive2D.getStart();
+ basegfx::B2DPoint aEndAttribute = rSvgLinearGradientPrimitive2D.getEnd();
+
+ rWriter.attribute("startx", aStartAttribute.getX());
+ rWriter.attribute("starty", aStartAttribute.getY());
+ rWriter.attribute("endx", aEndAttribute.getX());
+ rWriter.attribute("endy", aEndAttribute.getY());
+ writeSpreadMethod(rWriter, rSvgLinearGradientPrimitive2D.getSpreadMethod());
+ rWriter.attributeDouble(
+ "opacity",
+ rSvgLinearGradientPrimitive2D.getGradientEntries().front().getOpacity());
+
+ rWriter.startElement("transform");
+ writeMatrix(rWriter, rSvgLinearGradientPrimitive2D.getGradientTransform());
+ rWriter.endElement();
+
+ writePolyPolygon(rWriter, rSvgLinearGradientPrimitive2D.getPolyPolygon());
+
+ rWriter.endElement();
+ }
+ break;
+
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D:
+ {
+ const MetafilePrimitive2D& rMetafilePrimitive2D
+ = dynamic_cast<const MetafilePrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("metafile");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ // since the graphic is not rendered in a document, we do not need a concrete view information
+ rMetafilePrimitive2D.get2DDecomposition(
+ aPrimitiveContainer, drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ }
+
+ break;
+
+ case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D:
+ {
+ // SdrRectanglePrimitive2D is private to us.
+ rWriter.startElement("sdrrectangle");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ break;
+ }
+
+ case PRIMITIVE2D_ID_SDRBLOCKTEXTPRIMITIVE2D:
+ {
+ // SdrBlockTextPrimitive2D is private to us.
+ rWriter.startElement("sdrblocktext");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ break;
+ }
+
+ case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D:
+ {
+ // TextHierarchyBlockPrimitive2D.
+ rWriter.startElement("texthierarchyblock");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ break;
+ }
+
+ case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D:
+ {
+ // TextHierarchyParagraphPrimitive2D.
+ rWriter.startElement("texthierarchyparagraph");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ break;
+ }
+
+ case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D:
+ {
+ // TextHierarchyLinePrimitive2D.
+ rWriter.startElement("texthierarchyline");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ break;
+ }
+
+ case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
+ {
+ // ShadowPrimitive2D.
+ rWriter.startElement("shadow");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ break;
+ }
+
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+ {
+ // ModifiedColorPrimitive2D.
+ rWriter.startElement("modifiedColor");
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ break;
+ }
+
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D:
+ {
+ const auto& rScenePrimitive2D
+ = dynamic_cast<const drawinglayer::primitive2d::ScenePrimitive2D&>(
+ *pBasePrimitive);
+ rWriter.startElement("scene");
+
+ auto const& rSceneAttribute = rScenePrimitive2D.getSdrSceneAttribute();
+
+ rWriter.attribute("shadowSlant", rSceneAttribute.getShadowSlant());
+ rWriter.attribute("isTwoSidedLighting",
+ sal_Int32(rSceneAttribute.getTwoSidedLighting()));
+ writeShadeMode(rWriter, rSceneAttribute.getShadeMode());
+ writeProjectionMode(rWriter, rSceneAttribute.getProjectionMode());
+
+ auto const& rLightingAttribute = rScenePrimitive2D.getSdrLightingAttribute();
+ rWriter.attribute("ambientLightColor",
+ convertColorToString(rLightingAttribute.getAmbientLightColor()));
+ rWriter.startElement("lights");
+ for (auto const& rLight : rLightingAttribute.getLightVector())
+ {
+ rWriter.startElement("light");
+ rWriter.attribute("color", convertColorToString(rLight.getColor()));
+ rWriter.attribute("directionVectorX", rLight.getDirection().getX());
+ rWriter.attribute("directionVectorY", rLight.getDirection().getY());
+ rWriter.attribute("specular", sal_Int32(rLight.getSpecular()));
+ rWriter.endElement();
+ }
+ rWriter.endElement();
+
+ Primitive3DXmlDump aPrimitive3DXmlDump;
+ aPrimitive3DXmlDump.decomposeAndWrite(rScenePrimitive2D.getChildren3D(), rWriter);
+
+ rWriter.endElement();
+ break;
+ }
+
+ default:
+ {
+ OString aName("unhandled");
+ switch (nId)
+ {
+ case PRIMITIVE2D_ID_RANGE_SVX | 14: // PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D
+ {
+ aName = "sdrCell";
+ break;
+ }
+ }
+ rWriter.startElement(aName);
+ rWriter.attribute("id",
+ OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8));
+ rWriter.attribute("idNumber", nId);
+
+ auto pBufferedDecomposition
+ = dynamic_cast<const BufferedDecompositionPrimitive2D*>(pBasePrimitive);
+ if (pBufferedDecomposition)
+ {
+ rWriter.attribute(
+ "transparenceForShadow",
+ OString::number(pBufferedDecomposition->getTransparenceForShadow()));
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
+ drawinglayer::geometry::ViewInformation2D());
+ decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.endElement();
+ }
+ break;
+ }
+ }
+}
+
+} // end namespace drawinglayer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/wmfemfhelper.cxx b/drawinglayer/source/tools/wmfemfhelper.cxx
new file mode 100644
index 000000000..8a5dbab74
--- /dev/null
+++ b/drawinglayer/source/tools/wmfemfhelper.cxx
@@ -0,0 +1,3036 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <wmfemfhelper.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/metaact.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <vcl/BitmapPalette.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <primitive2d/wallpaperprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <primitive2d/textlineprimitive2d.hxx>
+#include <primitive2d/textstrikeoutprimitive2d.hxx>
+#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+#include <sal/log.hxx>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/hatch.hxx>
+#include <vcl/outdev.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <emfplushelper.hxx>
+#include <numeric>
+#include <toolkit/helper/vclunohelper.hxx>
+
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ /** NonOverlappingFillGradientPrimitive2D class
+
+ This is a special version of the FillGradientPrimitive2D which decomposes
+ to a non-overlapping geometry version of the gradient. This needs to be
+ used to support the old XOR paint-'trick'.
+
+ It does not need an own identifier since a renderer who wants to interpret
+ it itself may do so. It just overrides the decomposition of the C++
+ implementation class to do an alternative decomposition.
+ */
+ class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D
+ {
+ protected:
+ /// local decomposition.
+ virtual void create2DDecomposition(Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& rViewInformation) const override;
+
+ public:
+ /// constructor
+ NonOverlappingFillGradientPrimitive2D(
+ const basegfx::B2DRange& rObjectRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : FillGradientPrimitive2D(rObjectRange, rFillGradient)
+ {
+ }
+ };
+
+ }
+
+ void NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if (!getFillGradient().isDefault())
+ {
+ createFill(rContainer, false);
+ }
+ }
+
+} // end of namespace
+
+namespace wmfemfhelper
+{
+ /** helper class for graphic context
+
+ This class allows to hold a complete representation of classic
+ VCL OutputDevice state. This data is needed for correct
+ interpretation of the MetaFile action flow.
+ */
+ PropertyHolder::PropertyHolder()
+ : maMapUnit(MapUnit::Map100thMM),
+ maTextColor(sal_uInt32(COL_BLACK)),
+ maRasterOp(RasterOp::OverPaint),
+ mnLayoutMode(vcl::text::ComplexTextLayoutFlags::Default),
+ maLanguageType(0),
+ mnPushFlags(vcl::PushFlags::NONE),
+ mbLineColor(false),
+ mbFillColor(false),
+ mbTextColor(true),
+ mbTextFillColor(false),
+ mbTextLineColor(false),
+ mbOverlineColor(false),
+ mbClipPolyPolygonActive(false)
+ {
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** stack for properties
+
+ This class builds a stack based on the PropertyHolder
+ class. It encapsulates the pointer/new/delete usage to
+ make it safe and implements the push/pop as needed by a
+ VCL Metafile interpreter. The critical part here are the
+ flag values VCL OutputDevice uses here; not all stuff is
+ pushed and thus needs to be copied at pop.
+ */
+ PropertyHolders::PropertyHolders()
+ {
+ maPropertyHolders.push_back(new PropertyHolder());
+ }
+
+ void PropertyHolders::PushDefault()
+ {
+ PropertyHolder* pNew = new PropertyHolder();
+ maPropertyHolders.push_back(pNew);
+ }
+
+ void PropertyHolders::Push(vcl::PushFlags nPushFlags)
+ {
+ if (bool(nPushFlags))
+ {
+ OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: PUSH with no property holders (!)");
+ if (!maPropertyHolders.empty())
+ {
+ PropertyHolder* pNew = new PropertyHolder(*maPropertyHolders.back());
+ pNew->setPushFlags(nPushFlags);
+ maPropertyHolders.push_back(pNew);
+ }
+ }
+ }
+
+ void PropertyHolders::Pop()
+ {
+ OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)");
+ const sal_uInt32 nSize(maPropertyHolders.size());
+
+ if (!nSize)
+ return;
+
+ const PropertyHolder* pTip = maPropertyHolders.back();
+ const vcl::PushFlags nPushFlags(pTip->getPushFlags());
+
+ if (nPushFlags != vcl::PushFlags::NONE)
+ {
+ if (nSize > 1)
+ {
+ // copy back content for all non-set flags
+ PropertyHolder* pLast = maPropertyHolders[nSize - 2];
+
+ if (vcl::PushFlags::ALL != nPushFlags)
+ {
+ if (!(nPushFlags & vcl::PushFlags::LINECOLOR))
+ {
+ pLast->setLineColor(pTip->getLineColor());
+ pLast->setLineColorActive(pTip->getLineColorActive());
+ }
+ if (!(nPushFlags & vcl::PushFlags::FILLCOLOR))
+ {
+ pLast->setFillColor(pTip->getFillColor());
+ pLast->setFillColorActive(pTip->getFillColorActive());
+ }
+ if (!(nPushFlags & vcl::PushFlags::FONT))
+ {
+ pLast->setFont(pTip->getFont());
+ }
+ if (!(nPushFlags & vcl::PushFlags::TEXTCOLOR))
+ {
+ pLast->setTextColor(pTip->getTextColor());
+ pLast->setTextColorActive(pTip->getTextColorActive());
+ }
+ if (!(nPushFlags & vcl::PushFlags::MAPMODE))
+ {
+ pLast->setTransformation(pTip->getTransformation());
+ pLast->setMapUnit(pTip->getMapUnit());
+ }
+ if (!(nPushFlags & vcl::PushFlags::CLIPREGION))
+ {
+ pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
+ pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
+ }
+ if (!(nPushFlags & vcl::PushFlags::RASTEROP))
+ {
+ pLast->setRasterOp(pTip->getRasterOp());
+ }
+ if (!(nPushFlags & vcl::PushFlags::TEXTFILLCOLOR))
+ {
+ pLast->setTextFillColor(pTip->getTextFillColor());
+ pLast->setTextFillColorActive(pTip->getTextFillColorActive());
+ }
+ if (!(nPushFlags & vcl::PushFlags::TEXTALIGN))
+ {
+ if (pLast->getFont().GetAlignment() != pTip->getFont().GetAlignment())
+ {
+ vcl::Font aFont(pLast->getFont());
+ aFont.SetAlignment(pTip->getFont().GetAlignment());
+ pLast->setFont(aFont);
+ }
+ }
+ if (!(nPushFlags & vcl::PushFlags::REFPOINT))
+ {
+ // not supported
+ }
+ if (!(nPushFlags & vcl::PushFlags::TEXTLINECOLOR))
+ {
+ pLast->setTextLineColor(pTip->getTextLineColor());
+ pLast->setTextLineColorActive(pTip->getTextLineColorActive());
+ }
+ if (!(nPushFlags & vcl::PushFlags::TEXTLAYOUTMODE))
+ {
+ pLast->setLayoutMode(pTip->getLayoutMode());
+ }
+ if (!(nPushFlags & vcl::PushFlags::TEXTLANGUAGE))
+ {
+ pLast->setLanguageType(pTip->getLanguageType());
+ }
+ if (!(nPushFlags & vcl::PushFlags::OVERLINECOLOR))
+ {
+ pLast->setOverlineColor(pTip->getOverlineColor());
+ pLast->setOverlineColorActive(pTip->getOverlineColorActive());
+ }
+ }
+ }
+ }
+
+ // execute the pop
+ delete maPropertyHolders.back();
+ maPropertyHolders.pop_back();
+ }
+
+ PropertyHolder& PropertyHolders::Current()
+ {
+ static PropertyHolder aDummy;
+ OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)");
+ return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back();
+ }
+
+ PropertyHolders::~PropertyHolders()
+ {
+ while (!maPropertyHolders.empty())
+ {
+ delete maPropertyHolders.back();
+ maPropertyHolders.pop_back();
+ }
+ }
+}
+
+namespace
+{
+ /** helper to convert a vcl::Region to a B2DPolyPolygon
+ when it does not yet contain one. In the future
+ this may be expanded to merge the polygons created
+ from rectangles or use a special algo to directly turn
+ the spans of regions to a single, already merged
+ PolyPolygon.
+ */
+ basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const vcl::Region& rRegion)
+ {
+ basegfx::B2DPolyPolygon aRetval;
+
+ if (!rRegion.IsEmpty())
+ {
+ aRetval = rRegion.GetAsB2DPolyPolygon();
+ }
+
+ return aRetval;
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** Helper class to buffer and hold a Primitive target vector. It
+ encapsulates the new/delete functionality and allows to work
+ on pointers of the implementation classes. All data will
+ be converted to uno sequences of uno references when accessing the
+ data.
+ */
+ TargetHolder::TargetHolder()
+ {
+ }
+
+ TargetHolder::~TargetHolder()
+ {
+ }
+
+ sal_uInt32 TargetHolder::size() const
+ {
+ return aTargets.size();
+ }
+
+ void TargetHolder::append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
+ {
+ if (pCandidate)
+ {
+ aTargets.push_back(pCandidate);
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer TargetHolder::getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer xRetval = std::move(aTargets);
+
+
+ if (!xRetval.empty() && rPropertyHolder.getClipPolyPolygonActive())
+ {
+ const basegfx::B2DPolyPolygon& rClipPolyPolygon = rPropertyHolder.getClipPolyPolygon();
+
+ if (rClipPolyPolygon.count())
+ {
+ drawinglayer::primitive2d::Primitive2DReference xMask(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ rClipPolyPolygon,
+ std::move(xRetval)));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask };
+ }
+ }
+
+ return xRetval;
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** Helper class which builds a stack on the TargetHolder class */
+ TargetHolders::TargetHolders()
+ {
+ maTargetHolders.push_back(new TargetHolder());
+ }
+
+ sal_uInt32 TargetHolders::size() const
+ {
+ return maTargetHolders.size();
+ }
+
+ void TargetHolders::Push()
+ {
+ maTargetHolders.push_back(new TargetHolder());
+ }
+
+ void TargetHolders::Pop()
+ {
+ OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)");
+ if (!maTargetHolders.empty())
+ {
+ delete maTargetHolders.back();
+ maTargetHolders.pop_back();
+ }
+ }
+
+ TargetHolder& TargetHolders::Current()
+ {
+ static TargetHolder aDummy;
+ OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)");
+ return maTargetHolders.empty() ? aDummy : *maTargetHolders.back();
+ }
+
+ TargetHolders::~TargetHolders()
+ {
+ while (!maTargetHolders.empty())
+ {
+ delete maTargetHolders.back();
+ maTargetHolders.pop_back();
+ }
+ }
+}
+
+namespace
+{
+ /** helper to convert a MapMode to a transformation */
+ basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
+ {
+ basegfx::B2DHomMatrix aMapping;
+ const Fraction aNoScale(1, 1);
+ const Point& rOrigin(rMapMode.GetOrigin());
+
+ if(0 != rOrigin.X() || 0 != rOrigin.Y())
+ {
+ aMapping.translate(rOrigin.X(), rOrigin.Y());
+ }
+
+ if(rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale)
+ {
+ aMapping.scale(
+ double(rMapMode.GetScaleX()),
+ double(rMapMode.GetScaleY()));
+ }
+
+ return aMapping;
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** helper to create a PointArrayPrimitive2D based on current context */
+ static void createPointArrayPrimitive(
+ std::vector< basegfx::B2DPoint >&& rPositions,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties,
+ const basegfx::BColor& rBColor)
+ {
+ if(rPositions.empty())
+ return;
+
+ if(rProperties.getTransformation().isIdentity())
+ {
+ rTarget.append(
+ new drawinglayer::primitive2d::PointArrayPrimitive2D(
+ std::move(rPositions),
+ rBColor));
+ }
+ else
+ {
+ for(basegfx::B2DPoint & aPosition : rPositions)
+ {
+ aPosition = rProperties.getTransformation() * aPosition;
+ }
+
+ rTarget.append(
+ new drawinglayer::primitive2d::PointArrayPrimitive2D(
+ std::move(rPositions),
+ rBColor));
+ }
+ }
+
+ /** helper to create a PolygonHairlinePrimitive2D based on current context */
+ static void createHairlinePrimitive(
+ const basegfx::B2DPolygon& rLinePolygon,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties)
+ {
+ if(rLinePolygon.count())
+ {
+ basegfx::B2DPolygon aLinePolygon(rLinePolygon);
+ aLinePolygon.transform(rProperties.getTransformation());
+ rTarget.append(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aLinePolygon,
+ rProperties.getLineColor()));
+ }
+ }
+
+ /** helper to create a PolyPolygonColorPrimitive2D based on current context */
+ static void createFillPrimitive(
+ const basegfx::B2DPolyPolygon& rFillPolyPolygon,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties)
+ {
+ if(rFillPolyPolygon.count())
+ {
+ basegfx::B2DPolyPolygon aFillPolyPolygon(rFillPolyPolygon);
+ aFillPolyPolygon.transform(rProperties.getTransformation());
+ rTarget.append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ aFillPolyPolygon,
+ rProperties.getFillColor()));
+ }
+ }
+
+ /** helper to create a PolygonStrokePrimitive2D based on current context */
+ static void createLinePrimitive(
+ const basegfx::B2DPolygon& rLinePolygon,
+ const LineInfo& rLineInfo,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties)
+ {
+ if(!rLinePolygon.count())
+ return;
+
+ const bool bDashDotUsed(LineStyle::Dash == rLineInfo.GetStyle());
+ const bool bWidthUsed(rLineInfo.GetWidth() > 1);
+
+ if(bDashDotUsed || bWidthUsed)
+ {
+ basegfx::B2DPolygon aLinePolygon(rLinePolygon);
+ aLinePolygon.transform(rProperties.getTransformation());
+ const drawinglayer::attribute::LineAttribute aLineAttribute(
+ rProperties.getLineColor(),
+ bWidthUsed ? rLineInfo.GetWidth() : 0.0,
+ rLineInfo.GetLineJoin(),
+ rLineInfo.GetLineCap());
+
+ if(bDashDotUsed)
+ {
+ std::vector< double > fDotDashArray = rLineInfo.GetDotDashArray();
+ const double fAccumulated(std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
+ const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
+ std::move(fDotDashArray),
+ fAccumulated);
+
+ rTarget.append(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ aLinePolygon,
+ aLineAttribute,
+ aStrokeAttribute));
+ }
+ else
+ {
+ rTarget.append(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ aLinePolygon,
+ aLineAttribute));
+ }
+ }
+ else
+ {
+ createHairlinePrimitive(rLinePolygon, rTarget, rProperties);
+ }
+ }
+
+ /** helper to create needed line and fill primitives based on current context */
+ static void createHairlineAndFillPrimitive(
+ const basegfx::B2DPolygon& rPolygon,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties)
+ {
+ if(rProperties.getFillColorActive())
+ {
+ createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon), rTarget, rProperties);
+ }
+
+ if(rProperties.getLineColorActive())
+ {
+ createHairlinePrimitive(rPolygon, rTarget, rProperties);
+ }
+ }
+
+ /** helper to create needed line and fill primitives based on current context */
+ static void createHairlineAndFillPrimitive(
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties)
+ {
+ if(rProperties.getFillColorActive())
+ {
+ createFillPrimitive(rPolyPolygon, rTarget, rProperties);
+ }
+
+ if(rProperties.getLineColorActive())
+ {
+ for(sal_uInt32 a(0); a < rPolyPolygon.count(); a++)
+ {
+ createHairlinePrimitive(rPolyPolygon.getB2DPolygon(a), rTarget, rProperties);
+ }
+ }
+ }
+
+ /** helper to create DiscreteBitmapPrimitive2D based on current context.
+ The DiscreteBitmapPrimitive2D is especially created for this usage
+ since no other usage defines a bitmap visualisation based on top-left
+ position and size in pixels. At the end it will create a view-dependent
+ transformed embedding of a BitmapPrimitive2D.
+ */
+ static void createBitmapExPrimitive(
+ const BitmapEx& rBitmapEx,
+ const Point& rPoint,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties)
+ {
+ if(!rBitmapEx.IsEmpty())
+ {
+ basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y());
+ aPoint = rProperties.getTransformation() * aPoint;
+
+ rTarget.append(
+ new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
+ rBitmapEx,
+ aPoint));
+ }
+ }
+
+ /** helper to create BitmapPrimitive2D based on current context */
+ static void createBitmapExPrimitive(
+ const BitmapEx& rBitmapEx,
+ const Point& rPoint,
+ const Size& rSize,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperties)
+ {
+ if(rBitmapEx.IsEmpty())
+ return;
+
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, rSize.Width());
+ aObjectTransform.set(1, 1, rSize.Height());
+ aObjectTransform.set(0, 2, rPoint.X());
+ aObjectTransform.set(1, 2, rPoint.Y());
+
+ aObjectTransform = rProperties.getTransformation() * aObjectTransform;
+
+ rTarget.append(
+ new drawinglayer::primitive2d::BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(rBitmapEx),
+ aObjectTransform));
+ }
+
+ /** helper to create a regular BotmapEx from a MaskAction (definitions
+ which use a bitmap without transparence but define one of the colors as
+ transparent)
+ */
+ static BitmapEx createMaskBmpEx(const Bitmap& rBitmap, const Color& rMaskColor)
+ {
+ const Color aWhite(COL_WHITE);
+ BitmapPalette aBiLevelPalette {
+ aWhite, rMaskColor
+ };
+
+ Bitmap aMask(rBitmap.CreateMask(aWhite));
+ Bitmap aSolid(rBitmap.GetSizePixel(), vcl::PixelFormat::N1_BPP, &aBiLevelPalette);
+
+ aSolid.Erase(rMaskColor);
+
+ return BitmapEx(aSolid, aMask);
+ }
+
+ /** helper to convert from a VCL Gradient definition to the corresponding
+ data for primitive representation
+ */
+ static drawinglayer::attribute::FillGradientAttribute createFillGradientAttribute(const Gradient& rGradient)
+ {
+ const Color aStartColor(rGradient.GetStartColor());
+ const sal_uInt16 nStartIntens(rGradient.GetStartIntensity());
+ basegfx::BColor aStart(aStartColor.getBColor());
+
+ if(nStartIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aStart = interpolate(aBlack, aStart, static_cast<double>(nStartIntens) * 0.01);
+ }
+
+ const Color aEndColor(rGradient.GetEndColor());
+ const sal_uInt16 nEndIntens(rGradient.GetEndIntensity());
+ basegfx::BColor aEnd(aEndColor.getBColor());
+
+ if(nEndIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01);
+ }
+
+ drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GradientStyle::Rect);
+
+ switch(rGradient.GetStyle())
+ {
+ case GradientStyle::Linear :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Linear;
+ break;
+ }
+ case GradientStyle::Axial :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Axial;
+ break;
+ }
+ case GradientStyle::Radial :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Radial;
+ break;
+ }
+ case GradientStyle::Elliptical :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Elliptical;
+ break;
+ }
+ case GradientStyle::Square :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Square;
+ break;
+ }
+ default : // GradientStyle::Rect
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Rect;
+ break;
+ }
+ }
+
+ return drawinglayer::attribute::FillGradientAttribute(
+ aGradientStyle,
+ static_cast<double>(rGradient.GetBorder()) * 0.01,
+ static_cast<double>(rGradient.GetOfsX()) * 0.01,
+ static_cast<double>(rGradient.GetOfsY()) * 0.01,
+ toRadians(rGradient.GetAngle()),
+ aStart,
+ aEnd,
+ rGradient.GetSteps());
+ }
+
+ /** helper to convert from a VCL Hatch definition to the corresponding
+ data for primitive representation
+ */
+ static drawinglayer::attribute::FillHatchAttribute createFillHatchAttribute(const Hatch& rHatch)
+ {
+ drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HatchStyle::Single);
+
+ switch(rHatch.GetStyle())
+ {
+ default : // case HatchStyle::Single :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Single;
+ break;
+ }
+ case HatchStyle::Double :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Double;
+ break;
+ }
+ case HatchStyle::Triple :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Triple;
+ break;
+ }
+ }
+
+ return drawinglayer::attribute::FillHatchAttribute(
+ aHatchStyle,
+ static_cast<double>(rHatch.GetDistance()),
+ toRadians(rHatch.GetAngle()),
+ rHatch.GetColor().getBColor(),
+ 3, // same default as VCL, a minimum of three discrete units (pixels) offset
+ false);
+ }
+
+ /** helper to take needed action on ClipRegion change. This method needs to be called
+ on any vcl::Region change, e.g. at the obvious actions doing this, but also at pop-calls
+ which change the vcl::Region of the current context. It takes care of creating the
+ current embedded context, set the new vcl::Region at the context and possibly prepare
+ a new target for including new geometry into the current region
+ */
+ void HandleNewClipRegion(
+ const basegfx::B2DPolyPolygon& rClipPolyPolygon,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders)
+ {
+ const bool bNewActive(rClipPolyPolygon.count());
+
+ // #i108636# The handling of new ClipPolyPolygons was not done as good as possible
+ // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set
+ // initially and then using a lot of push/pop actions, the pop always leads
+ // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon
+ // of the properties next on the stack.
+
+ // This ClipPolyPolygon is identical to the current one, so there is no need to
+ // create a MaskPrimitive2D containing the up-to-now created primitives, but
+ // this was done before. While this does not lead to wrong primitive
+ // representations of the metafile data, it creates unnecessarily expensive
+ // representations. Just detecting when no really 'new' ClipPolyPolygon gets set
+ // solves the problem.
+
+ if(!rPropertyHolders.Current().getClipPolyPolygonActive() && !bNewActive)
+ {
+ // no active ClipPolyPolygon exchanged by no new one, done
+ return;
+ }
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive() && bNewActive)
+ {
+ // active ClipPolyPolygon and new active ClipPolyPolygon
+ if(rPropertyHolders.Current().getClipPolyPolygon() == rClipPolyPolygon)
+ {
+ // new is the same as old, done
+ return;
+ }
+ }
+
+ // Here the old and the new are definitively different, maybe
+ // old one and/or new one is not active.
+
+ // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which
+ // belong to this active ClipPolyPolygon. These need to be embedded to a
+ // MaskPrimitive2D accordingly.
+ if(rPropertyHolders.Current().getClipPolyPolygonActive() && rTargetHolders.size() > 1)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aSubContent;
+
+ if(rPropertyHolders.Current().getClipPolyPolygon().count()
+ && rTargetHolders.Current().size())
+ {
+ aSubContent = rTargetHolders.Current().getPrimitive2DSequence(
+ rPropertyHolders.Current());
+ }
+
+ rTargetHolders.Pop();
+
+ if(!aSubContent.empty())
+ {
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::GroupPrimitive2D(
+ std::move(aSubContent)));
+ }
+ }
+
+ // apply new settings to current properties by setting
+ // the new region now
+ rPropertyHolders.Current().setClipPolyPolygonActive(bNewActive);
+
+ if(bNewActive)
+ {
+ rPropertyHolders.Current().setClipPolyPolygon(rClipPolyPolygon);
+
+ // prepare new content holder for new active region
+ rTargetHolders.Push();
+ }
+ }
+
+ /** helper to handle the change of RasterOp. It takes care of encapsulating all current
+ geometry to the current RasterOp (if changed) and needs to be called on any RasterOp
+ change. It will also start a new geometry target to embrace to the new RasterOp if
+ a changing RasterOp is used. Currently, RasterOp::Xor and RasterOp::Invert are supported using
+ InvertPrimitive2D, and RasterOp::N0 by using a ModifiedColorPrimitive2D to force to black paint
+ */
+ static void HandleNewRasterOp(
+ RasterOp aRasterOp,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders)
+ {
+ // check if currently active
+ if(rPropertyHolders.Current().isRasterOpActive() && rTargetHolders.size() > 1)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aSubContent;
+
+ if(rTargetHolders.Current().size())
+ {
+ aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ }
+
+ rTargetHolders.Pop();
+
+ if(!aSubContent.empty())
+ {
+ if(rPropertyHolders.Current().isRasterOpForceBlack())
+ {
+ // force content to black
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
+ std::move(aSubContent),
+ std::make_shared<basegfx::BColorModifier_replace>(
+ basegfx::BColor(0.0, 0.0, 0.0))));
+ }
+ else // if(rPropertyHolders.Current().isRasterOpInvert())
+ {
+ // invert content
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::InvertPrimitive2D(
+ std::move(aSubContent)));
+ }
+ }
+ }
+
+ // apply new settings
+ rPropertyHolders.Current().setRasterOp(aRasterOp);
+
+ // check if now active
+ if(rPropertyHolders.Current().isRasterOpActive())
+ {
+ // prepare new content holder for new invert
+ rTargetHolders.Push();
+ }
+ }
+
+ /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
+ It is a quite mighty action. This helper is for simple color filled background.
+ */
+ static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> CreateColorWallpaper(
+ const basegfx::B2DRange& rRange,
+ const basegfx::BColor& rColor,
+ PropertyHolder const & rPropertyHolder)
+ {
+ basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rRange));
+ aOutline.transform(rPropertyHolder.getTransformation());
+
+ return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aOutline),
+ rColor);
+ }
+
+ /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
+ It is a quite mighty action. This helper is for gradient filled background.
+ */
+ static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> CreateGradientWallpaper(
+ const basegfx::B2DRange& rRange,
+ const Gradient& rGradient,
+ PropertyHolder const & rPropertyHolder)
+ {
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient. Create filled rectangle
+ return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder);
+ }
+ else
+ {
+ // really a gradient
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pRetval(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ rRange,
+ aAttribute));
+
+ if(!rPropertyHolder.getTransformation().isIdentity())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval);
+ drawinglayer::primitive2d::Primitive2DContainer xSeq { xPrim };
+
+ pRetval = new drawinglayer::primitive2d::TransformPrimitive2D(
+ rPropertyHolder.getTransformation(),
+ std::move(xSeq));
+ }
+
+ return pRetval;
+ }
+ }
+
+ /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
+ It is a quite mighty action. This helper decides if color and/or gradient
+ background is needed for the wanted bitmap fill and then creates the needed
+ WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
+ takes over all needed logic of orientations and tiling.
+ */
+ static void CreateAndAppendBitmapWallpaper(
+ basegfx::B2DRange aWallpaperRange,
+ const Wallpaper& rWallpaper,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperty)
+ {
+ const BitmapEx aBitmapEx(rWallpaper.GetBitmap());
+ const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
+
+ // if bitmap visualisation is transparent, maybe background
+ // needs to be filled. Create background
+ if(aBitmapEx.IsAlpha()
+ || (WallpaperStyle::Tile != eWallpaperStyle && WallpaperStyle::Scale != eWallpaperStyle))
+ {
+ if(rWallpaper.IsGradient())
+ {
+ rTarget.append(
+ CreateGradientWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetGradient(),
+ rProperty));
+ }
+ else if(!rWallpaper.GetColor().IsTransparent())
+ {
+ rTarget.append(
+ CreateColorWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetColor().getBColor(),
+ rProperty));
+ }
+ }
+
+ // use wallpaper rect if set
+ if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty())
+ {
+ aWallpaperRange = vcl::unotools::b2DRectangleFromRectangle(rWallpaper.GetRect());
+ }
+
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBitmapWallpaperFill =
+ new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
+ aWallpaperRange,
+ aBitmapEx,
+ eWallpaperStyle);
+
+ if(rProperty.getTransformation().isIdentity())
+ {
+ // add directly
+ rTarget.append(pBitmapWallpaperFill);
+ }
+ else
+ {
+ // when a transformation is set, embed to it
+ const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill);
+
+ rTarget.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rProperty.getTransformation(),
+ drawinglayer::primitive2d::Primitive2DContainer { xPrim }));
+ }
+ }
+
+ /** helper to decide UnderlineAbove for text primitives */
+ static bool isUnderlineAbove(const vcl::Font& rFont)
+ {
+ if(!rFont.IsVertical())
+ {
+ return false;
+ }
+
+ // the underline is right for Japanese only
+ return (LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage());
+ }
+
+ static void createFontAttributeTransformAndAlignment(
+ drawinglayer::attribute::FontAttribute& rFontAttribute,
+ basegfx::B2DHomMatrix& rTextTransform,
+ basegfx::B2DVector& rAlignmentOffset,
+ PropertyHolder const & rProperty)
+ {
+ const vcl::Font& rFont = rProperty.getFont();
+ basegfx::B2DVector aFontScaling;
+
+ rFontAttribute = drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontScaling,
+ rFont,
+ bool(rProperty.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiRtl),
+ bool(rProperty.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiStrong));
+
+ // add FontScaling
+ rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());
+
+ // take text align into account
+ if(ALIGN_BASELINE != rFont.GetAlignment())
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rFont);
+
+ if(ALIGN_TOP == rFont.GetAlignment())
+ {
+ rAlignmentOffset.setY(aTextLayouterDevice.getFontAscent());
+ }
+ else // ALIGN_BOTTOM
+ {
+ rAlignmentOffset.setY(-aTextLayouterDevice.getFontDescent());
+ }
+
+ rTextTransform.translate(rAlignmentOffset.getX(), rAlignmentOffset.getY());
+ }
+
+ // add FontRotation (if used)
+ if(rFont.GetOrientation())
+ {
+ rTextTransform.rotate(-toRadians(rFont.GetOrientation()));
+ }
+ }
+
+ /** helper which takes complete care for creating the needed text primitives. It
+ takes care of decorated stuff and all the geometry adaptations needed
+ */
+ static void processMetaTextAction(
+ const Point& rTextStartPosition,
+ const OUString& rText,
+ sal_uInt16 nTextStart,
+ sal_uInt16 nTextLength,
+ std::vector< double >&& rDXArray,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperty)
+ {
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pResult;
+ const vcl::Font& rFont = rProperty.getFont();
+ basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
+
+ if(nTextLength)
+ {
+ drawinglayer::attribute::FontAttribute aFontAttribute;
+ basegfx::B2DHomMatrix aTextTransform;
+
+ // fill parameters derived from current font
+ createFontAttributeTransformAndAlignment(
+ aFontAttribute,
+ aTextTransform,
+ aAlignmentOffset,
+ rProperty);
+
+ // add TextStartPosition
+ aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
+
+ // prepare FontColor and Locale
+ const basegfx::BColor aFontColor(rProperty.getTextColor());
+ const Color aFillColor(rFont.GetFillColor());
+ const css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale());
+ const bool bWordLineMode(rFont.IsWordLineMode());
+
+ const bool bDecoratedIsNeeded(
+ LINESTYLE_NONE != rFont.GetOverline()
+ || LINESTYLE_NONE != rFont.GetUnderline()
+ || STRIKEOUT_NONE != rFont.GetStrikeout()
+ || FontEmphasisMark::NONE != (rFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ || FontRelief::NONE != rFont.GetRelief()
+ || rFont.IsShadow()
+ || bWordLineMode);
+
+ if(bDecoratedIsNeeded)
+ {
+ // prepare overline, underline and strikeout data
+ const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetOverline()));
+ const drawinglayer::primitive2d::TextLine eFontLineStyle(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetUnderline()));
+ const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));
+
+ // check UndelineAbove
+ const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && isUnderlineAbove(rFont));
+
+ // prepare emphasis mark data
+ drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);
+
+ switch(rFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ {
+ case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
+ case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
+ case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
+ case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
+ default: break;
+ }
+
+ const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & FontEmphasisMark::PosAbove);
+ const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & FontEmphasisMark::PosBelow);
+
+ // prepare font relief data
+ drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
+
+ switch(rFont.GetRelief())
+ {
+ case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
+ case FontRelief::Engraved : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
+ default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
+ }
+
+ // prepare shadow/outline data
+ const bool bShadow(rFont.IsShadow());
+
+ // TextDecoratedPortionPrimitive2D is needed, create one
+ pResult = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+
+ // attributes for TextSimplePortionPrimitive2D
+ aTextTransform,
+ rText,
+ nTextStart,
+ nTextLength,
+ std::move(rDXArray),
+ aFontAttribute,
+ aLocale,
+ aFontColor,
+ aFillColor,
+
+ // attributes for TextDecoratedPortionPrimitive2D
+ rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
+ rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
+ eFontOverline,
+ eFontLineStyle,
+ bUnderlineAbove,
+ eTextStrikeout,
+ bWordLineMode,
+ eTextEmphasisMark,
+ bEmphasisMarkAbove,
+ bEmphasisMarkBelow,
+ eTextRelief,
+ bShadow);
+ }
+ else
+ {
+ // TextSimplePortionPrimitive2D is enough
+ pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextTransform,
+ rText,
+ nTextStart,
+ nTextLength,
+ std::vector(rDXArray),
+ aFontAttribute,
+ aLocale,
+ aFontColor);
+ }
+ }
+
+ if(pResult && rProperty.getTextFillColorActive())
+ {
+ // text background is requested, add and encapsulate both to new primitive
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rFont);
+
+ // get text width
+ double fTextWidth(0.0);
+
+ if(rDXArray.empty())
+ {
+ fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength);
+ }
+ else
+ {
+ fTextWidth = rDXArray.back();
+ }
+
+ if(basegfx::fTools::more(fTextWidth, 0.0))
+ {
+ // build text range
+ const basegfx::B2DRange aTextRange(
+ 0.0, -aTextLayouterDevice.getFontAscent(),
+ fTextWidth, aTextLayouterDevice.getFontDescent());
+
+ // create Transform
+ basegfx::B2DHomMatrix aTextTransform;
+
+ aTextTransform.translate(aAlignmentOffset.getX(), aAlignmentOffset.getY());
+
+ if(rFont.GetOrientation())
+ {
+ aTextTransform.rotate(-toRadians(rFont.GetOrientation()));
+ }
+
+ aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
+
+ // prepare Primitive2DSequence, put text in foreground
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(2);
+ aSequence[1] = pResult;
+
+ // prepare filled polygon
+ basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aTextRange));
+ aOutline.transform(aTextTransform);
+
+ aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aOutline),
+ rProperty.getTextFillColor()));
+
+ // set as group at pResult
+ pResult = new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aSequence));
+ }
+ }
+
+ if(!pResult)
+ return;
+
+ // add created text primitive to target
+ if(rProperty.getTransformation().isIdentity())
+ {
+ rTarget.append(pResult);
+ }
+ else
+ {
+ // when a transformation is set, embed to it
+ const drawinglayer::primitive2d::Primitive2DReference aReference(pResult);
+
+ rTarget.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rProperty.getTransformation(),
+ drawinglayer::primitive2d::Primitive2DContainer { aReference }));
+ }
+ }
+
+ /** helper which takes complete care for creating the needed textLine primitives */
+ static void processMetaTextLineAction(
+ const MetaTextLineAction& rAction,
+ TargetHolder& rTarget,
+ PropertyHolder const & rProperty)
+ {
+ const double fLineWidth(fabs(static_cast<double>(rAction.GetWidth())));
+
+ if(fLineWidth <= 0.0)
+ return;
+
+ const drawinglayer::primitive2d::TextLine aOverlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rAction.GetOverline()));
+ const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rAction.GetUnderline()));
+ const drawinglayer::primitive2d::TextStrikeout aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction.GetStrikeout()));
+
+ const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aOverlineMode);
+ const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aUnderlineMode);
+ const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE != aTextStrikeout);
+
+ if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
+ return;
+
+ drawinglayer::primitive2d::Primitive2DContainer aTargets;
+ basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
+ drawinglayer::attribute::FontAttribute aFontAttribute;
+ basegfx::B2DHomMatrix aTextTransform;
+
+ // fill parameters derived from current font
+ createFontAttributeTransformAndAlignment(
+ aFontAttribute,
+ aTextTransform,
+ aAlignmentOffset,
+ rProperty);
+
+ // add TextStartPosition
+ aTextTransform.translate(rAction.GetStartPoint().X(), rAction.GetStartPoint().Y());
+
+ // prepare TextLayouter (used in most cases)
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFont(rProperty.getFont());
+
+ if(bOverlineUsed)
+ {
+ // create primitive geometry for overline
+ aTargets.push_back(
+ new drawinglayer::primitive2d::TextLinePrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ aTextLayouter.getOverlineOffset(),
+ aTextLayouter.getOverlineHeight(),
+ aOverlineMode,
+ rProperty.getOverlineColor()));
+ }
+
+ if(bUnderlineUsed)
+ {
+ // create primitive geometry for underline
+ aTargets.push_back(
+ new drawinglayer::primitive2d::TextLinePrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ aTextLayouter.getUnderlineOffset(),
+ aTextLayouter.getUnderlineHeight(),
+ aUnderlineMode,
+ rProperty.getTextLineColor()));
+ }
+
+ if(bStrikeoutUsed)
+ {
+ // create primitive geometry for strikeout
+ if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout
+ || drawinglayer::primitive2d::TEXT_STRIKEOUT_X == aTextStrikeout)
+ {
+ // strikeout with character
+ const sal_Unicode aStrikeoutChar(
+ drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X');
+ const css::lang::Locale aLocale(LanguageTag(
+ rProperty.getLanguageType()).getLocale());
+
+ aTargets.push_back(
+ new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ rProperty.getTextColor(),
+ aStrikeoutChar,
+ aFontAttribute,
+ aLocale));
+ }
+ else
+ {
+ // strikeout with geometry
+ aTargets.push_back(
+ new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ rProperty.getTextColor(),
+ aTextLayouter.getUnderlineHeight(),
+ aTextLayouter.getStrikeoutOffset(),
+ aTextStrikeout));
+ }
+ }
+
+ if(aTargets.empty())
+ return;
+
+ // add created text primitive to target
+ if(rProperty.getTransformation().isIdentity())
+ {
+ rTarget.append(std::move(aTargets));
+ }
+ else
+ {
+ // when a transformation is set, embed to it
+ rTarget.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rProperty.getTransformation(),
+ std::move(aTargets)));
+ }
+ }
+
+ /** This is the main interpreter method. It is designed to handle the given Metafile
+ completely inside the given context and target. It may use and modify the context and
+ target. This design allows to call itself recursively which adapted contexts and
+ targets as e.g. needed for the MetaActionType::FLOATTRANSPARENT where the content is expressed
+ as a metafile as sub-content.
+
+ This interpreter is as free of VCL functionality as possible. It uses VCL data classes
+ (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
+ as most other MetaFile interpreters/exporters do to hold and work with the current context.
+ This is necessary to be able to get away from the strong internal VCL-binding.
+
+ It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
+ where possible (which is not trivial with the possible line geometry definitions).
+
+ It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
+ ClipRegions with (where possible) high precision by using the best possible data quality
+ from the Region. The vcl::Region is unavoidable as data container, but nowadays allows the transport
+ of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
+ vcl::Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.
+
+ I have marked the single MetaActions with:
+
+ SIMPLE, DONE:
+ Simple, e.g nothing to do or value setting in the context
+
+ CHECKED, WORKS WELL:
+ Thoroughly tested with extra written test code which created a replacement
+ Metafile just to test this action in various combinations
+
+ NEEDS IMPLEMENTATION:
+ Not implemented and asserted, but also no usage found, neither in own Metafile
+ creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
+ bugdocs)
+
+ For more comments, see the single action implementations.
+ */
+ static void implInterpretMetafile(
+ const GDIMetaFile& rMetaFile,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ const size_t nCount(rMetaFile.GetActionSize());
+ std::unique_ptr<emfplushelper::EmfPlusHelper> aEMFPlus;
+
+ for(size_t nAction(0); nAction < nCount; nAction++)
+ {
+ MetaAction* pAction = rMetaFile.GetAction(nAction);
+
+ switch(pAction->GetType())
+ {
+ case MetaActionType::NONE :
+ {
+ /** SIMPLE, DONE */
+ break;
+ }
+ case MetaActionType::PIXEL :
+ {
+ /** CHECKED, WORKS WELL */
+ std::vector< basegfx::B2DPoint > aPositions;
+ Color aLastColor(COL_BLACK);
+
+ while(MetaActionType::PIXEL == pAction->GetType() && nAction < nCount)
+ {
+ const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
+
+ if(pA->GetColor() != aLastColor)
+ {
+ if(!aPositions.empty())
+ {
+ createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
+ aPositions.clear();
+ }
+
+ aLastColor = pA->GetColor();
+ }
+
+ const Point& rPoint = pA->GetPoint();
+ aPositions.emplace_back(rPoint.X(), rPoint.Y());
+ nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
+ }
+
+ nAction--;
+
+ if(!aPositions.empty())
+ {
+ createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
+ }
+
+ break;
+ }
+ case MetaActionType::POINT :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ std::vector< basegfx::B2DPoint > aPositions;
+
+ while(MetaActionType::POINT == pAction->GetType() && nAction < nCount)
+ {
+ const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
+ const Point& rPoint = pA->GetPoint();
+ aPositions.emplace_back(rPoint.X(), rPoint.Y());
+ nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
+ }
+
+ nAction--;
+
+ if(!aPositions.empty())
+ {
+ createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::LINE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ basegfx::B2DPolygon aLinePolygon;
+ LineInfo aLineInfo;
+
+ while(MetaActionType::LINE == pAction->GetType() && nAction < nCount)
+ {
+ const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
+ const Point& rStartPoint = pA->GetStartPoint();
+ const Point& rEndPoint = pA->GetEndPoint();
+ const basegfx::B2DPoint aStart(rStartPoint.X(), rStartPoint.Y());
+ const basegfx::B2DPoint aEnd(rEndPoint.X(), rEndPoint.Y());
+
+ if(aLinePolygon.count())
+ {
+ if(pA->GetLineInfo() == aLineInfo
+ && aStart == aLinePolygon.getB2DPoint(aLinePolygon.count() - 1))
+ {
+ aLinePolygon.append(aEnd);
+ }
+ else
+ {
+ createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
+ aLinePolygon.clear();
+ aLineInfo = pA->GetLineInfo();
+ aLinePolygon.append(aStart);
+ aLinePolygon.append(aEnd);
+ }
+ }
+ else
+ {
+ aLineInfo = pA->GetLineInfo();
+ aLinePolygon.append(aStart);
+ aLinePolygon.append(aEnd);
+ }
+
+ nAction++;
+ if (nAction < nCount)
+ pAction = rMetaFile.GetAction(nAction);
+ }
+
+ nAction--;
+ if (aLinePolygon.count())
+ createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::RECT :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
+ const tools::Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);
+
+ if(!aRange.isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::ROUNDRECT :
+ {
+ /** CHECKED, WORKS WELL */
+ /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
+ because the tools::Polygon operator creating the rounding does produce nonsense. I assume
+ this an error and create an unrounded rectangle in that case (implicit in
+ createPolygonFromRect)
+ */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
+ const tools::Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);
+
+ if(!aRange.isEmpty())
+ {
+ const sal_uInt32 nHor(pA->GetHorzRound());
+ const sal_uInt32 nVer(pA->GetVertRound());
+ basegfx::B2DPolygon aOutline;
+
+ if(nHor || nVer)
+ {
+ double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
+ double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
+ fRadiusX = std::clamp(fRadiusX, 0.0, 1.0);
+ fRadiusY = std::clamp(fRadiusY, 0.0, 1.0);
+
+ aOutline = basegfx::utils::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
+ }
+ else
+ {
+ aOutline = basegfx::utils::createPolygonFromRect(aRange);
+ }
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::ELLIPSE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
+ const tools::Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);
+
+ if(!aRange.isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromEllipse(
+ aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5));
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::ARC :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
+ const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Arc);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::PIE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPieAction* pA = static_cast<const MetaPieAction*>(pAction);
+ const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Pie);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::CHORD :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
+ const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Chord);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::POLYLINE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
+ createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::POLYGON :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
+ basegfx::B2DPolygon aOutline(pA->GetPolygon().getB2DPolygon());
+
+ // the metafile play interprets the polygons from MetaPolygonAction
+ // always as closed and always paints an edge from last to first point,
+ // so force to closed here to emulate that
+ if(aOutline.count() > 1 && !aOutline.isClosed())
+ {
+ aOutline.setClosed(true);
+ }
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::POLYPOLYGON :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
+ basegfx::B2DPolyPolygon aPolyPolygonOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
+
+ // the metafile play interprets the single polygons from MetaPolyPolygonAction
+ // always as closed and always paints an edge from last to first point,
+ // so force to closed here to emulate that
+ for(sal_uInt32 b(0); b < aPolyPolygonOutline.count(); b++)
+ {
+ basegfx::B2DPolygon aPolygonOutline(aPolyPolygonOutline.getB2DPolygon(b));
+
+ if(aPolygonOutline.count() > 1 && !aPolygonOutline.isClosed())
+ {
+ aPolygonOutline.setClosed(true);
+ aPolyPolygonOutline.setB2DPolygon(b, aPolygonOutline);
+ }
+ }
+
+ createHairlineAndFillPrimitive(aPolyPolygonOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::TEXT :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ std::vector< double > aDXArray{};
+ processMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ std::move(aDXArray),
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::TEXTARRAY :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ // prepare DXArray (if used)
+ std::vector< double > aDXArray;
+ const std::vector<sal_Int32> & rDXArray = pA->GetDXArray();
+
+ if(!rDXArray.empty())
+ {
+ aDXArray.reserve(nTextLength);
+
+ for(sal_uInt32 a(0); a < nTextLength; a++)
+ {
+ aDXArray.push_back(static_cast<double>(rDXArray[a]));
+ }
+ }
+
+ processMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ std::move(aDXArray),
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::STRETCHTEXT :
+ {
+ // #i108440# StarMath uses MetaStretchTextAction, thus support is needed.
+ // It looks as if it pretty never really uses a width different from
+ // the default text-layout width, but it's not possible to be sure.
+ // Implemented getting the DXArray and checking for scale at all. If
+ // scale is more than 3.5% different, scale the DXArray before usage.
+ // New status:
+
+ /** CHECKED, WORKS WELL */
+ const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
+
+ std::vector< double > aTextArray(
+ aTextLayouterDevice.getTextArray(
+ pA->GetText(),
+ nTextIndex,
+ nTextLength));
+
+ if(!aTextArray.empty())
+ {
+ const double fTextLength(aTextArray.back());
+
+ if(0.0 != fTextLength && pA->GetWidth())
+ {
+ const double fRelative(pA->GetWidth() / fTextLength);
+
+ if(fabs(fRelative - 1.0) >= 0.035)
+ {
+ // when derivation is more than 3,5% from default text size,
+ // scale the DXArray
+ for(double & a : aTextArray)
+ {
+ a *= fRelative;
+ }
+ }
+ }
+ }
+
+ processMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ std::move(aTextArray),
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::TEXTRECT :
+ {
+ /** CHECKED, WORKS WELL */
+ // OSL_FAIL("MetaActionType::TEXTRECT requested (!)");
+ const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
+ const tools::Rectangle& rRectangle = pA->GetRect();
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ if(!rRectangle.IsEmpty() && 0 != nStringLength)
+ {
+ // The problem with this action is that it describes unlayouted text
+ // and the layout capabilities are in EditEngine/Outliner in SVX. The
+ // same problem is true for VCL which internally has implementations
+ // to layout text in this case. There exists even a call
+ // OutputDevice::AddTextRectActions(...) to create the needed actions
+ // as 'sub-content' of a Metafile. Unfortunately i do not have an
+ // OutputDevice here since this interpreter tries to work without
+ // VCL AFAP.
+ // Since AddTextRectActions is the only way as long as we do not have
+ // a simple text layouter available, i will try to add it to the
+ // TextLayouterDevice isolation.
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
+ GDIMetaFile aGDIMetaFile;
+
+ aTextLayouterDevice.addTextRectActions(
+ rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile);
+
+ if(aGDIMetaFile.GetActionSize())
+ {
+ // create sub-content
+ drawinglayer::primitive2d::Primitive2DContainer xSubContent;
+ {
+ rTargetHolders.Push();
+
+ // for sub-Mteafile contents, do start with new, default render state
+ // #i124686# ...but copy font, this is already set accordingly
+ vcl::Font aTargetFont = rPropertyHolders.Current().getFont();
+ rPropertyHolders.PushDefault();
+ rPropertyHolders.Current().setFont(aTargetFont);
+
+ implInterpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation);
+ xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ rPropertyHolders.Pop();
+ rTargetHolders.Pop();
+ }
+
+ if(!xSubContent.empty())
+ {
+ // add with transformation
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rPropertyHolders.Current().getTransformation(),
+ std::move(xSubContent)));
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::BMP :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
+ const BitmapEx aBitmapEx(pA->GetBitmap());
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPSCALE :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+ const BitmapEx aBitmapEx(pA->GetBitmap());
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPSCALEPART :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
+ const Bitmap& rBitmap = pA->GetBitmap();
+
+ if(!rBitmap.IsEmpty())
+ {
+ Bitmap aCroppedBitmap(rBitmap);
+ const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+ if(!aCropRectangle.IsEmpty())
+ {
+ aCroppedBitmap.Crop(aCropRectangle);
+ }
+
+ const BitmapEx aCroppedBitmapEx(aCroppedBitmap);
+ createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::BMPEX :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
+ const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPEXSCALE :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPEXSCALEPART :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
+ const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ if(!rBitmapEx.IsEmpty())
+ {
+ BitmapEx aCroppedBitmapEx(rBitmapEx);
+ const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+ if(!aCropRectangle.IsEmpty())
+ {
+ aCroppedBitmapEx.Crop(aCropRectangle);
+ }
+
+ createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::MASK :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
+ /** Huh, no it isn't!? */
+ const MetaMaskAction* pA = static_cast<const MetaMaskAction*>(pAction);
+ const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::MASKSCALE :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
+ const MetaMaskScaleAction* pA = static_cast<const MetaMaskScaleAction*>(pAction);
+ const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::MASKSCALEPART :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
+ const MetaMaskScalePartAction* pA = static_cast<const MetaMaskScalePartAction*>(pAction);
+ const Bitmap& rBitmap = pA->GetBitmap();
+
+ if(!rBitmap.IsEmpty())
+ {
+ Bitmap aCroppedBitmap(rBitmap);
+ const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+ if(!aCropRectangle.IsEmpty())
+ {
+ aCroppedBitmap.Crop(aCropRectangle);
+ }
+
+ const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor()));
+ createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::GRADIENT :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
+ const tools::Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);
+
+ if(!aRange.isEmpty())
+ {
+ const Gradient& rGradient = pA->GetGradient();
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ basegfx::B2DPolyPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient. Create filled rectangle
+ createFillPrimitive(
+ aOutline,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+ else
+ {
+ // really a gradient
+ aRange.transform(rPropertyHolders.Current().getTransformation());
+ drawinglayer::primitive2d::Primitive2DContainer xGradient(1);
+
+ if(rPropertyHolders.Current().isRasterOpInvert())
+ {
+ // use a special version of FillGradientPrimitive2D which creates
+ // non-overlapping geometry on decomposition to make the old XOR
+ // paint 'trick' work.
+ xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
+ aRange,
+ aAttribute));
+ }
+ else
+ {
+ xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ aRange,
+ aAttribute));
+ }
+
+ // #i112300# clip against polygon representing the rectangle from
+ // the action. This is implicitly done using a temp Clipping in VCL
+ // when a MetaGradientAction is executed
+ aOutline.transform(rPropertyHolders.Current().getTransformation());
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aOutline,
+ std::move(xGradient)));
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::HATCH :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
+ basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aOutline.count())
+ {
+ const Hatch& rHatch = pA->GetHatch();
+ const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch));
+
+ aOutline.transform(rPropertyHolders.Current().getTransformation());
+
+ const basegfx::B2DRange aObjectRange(aOutline.getB2DRange());
+ const drawinglayer::primitive2d::Primitive2DReference aFillHatch(
+ new drawinglayer::primitive2d::FillHatchPrimitive2D(
+ aObjectRange,
+ basegfx::BColor(),
+ aAttribute));
+
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aOutline,
+ drawinglayer::primitive2d::Primitive2DContainer { aFillHatch }));
+ }
+
+ break;
+ }
+ case MetaActionType::WALLPAPER :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
+ tools::Rectangle aWallpaperRectangle(pA->GetRect());
+
+ if(!aWallpaperRectangle.IsEmpty())
+ {
+ const Wallpaper& rWallpaper = pA->GetWallpaper();
+ const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
+ basegfx::B2DRange aWallpaperRange = vcl::unotools::b2DRectangleFromRectangle(aWallpaperRectangle);
+
+ if(WallpaperStyle::NONE != eWallpaperStyle)
+ {
+ if(rWallpaper.IsBitmap())
+ {
+ // create bitmap background. Caution: This
+ // also will create gradient/color background(s)
+ // when the bitmap is transparent or not tiled
+ CreateAndAppendBitmapWallpaper(
+ aWallpaperRange,
+ rWallpaper,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+ else if(rWallpaper.IsGradient())
+ {
+ // create gradient background
+ rTargetHolders.Current().append(
+ CreateGradientWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetGradient(),
+ rPropertyHolders.Current()));
+ }
+ else if(!rWallpaper.GetColor().IsTransparent())
+ {
+ // create color background
+ rTargetHolders.Current().append(
+ CreateColorWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetColor().getBColor(),
+ rPropertyHolders.Current()));
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::CLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
+
+ if(pA->IsClipping())
+ {
+ // new clipping. Get tools::PolyPolygon and transform with current transformation
+ basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion()));
+
+ aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
+ HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ else
+ {
+ // end clipping
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+
+ break;
+ }
+ case MetaActionType::ISECTRECTCLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
+ const tools::Rectangle& rRectangle = pA->GetRect();
+
+ if(rRectangle.IsEmpty())
+ {
+ // intersect with empty rectangle will always give empty
+ // ClipPolyPolygon; start new clipping with empty PolyPolygon
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ else
+ {
+ // create transformed ClipRange
+ basegfx::B2DRange aClipRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);
+
+ aClipRange.transform(rPropertyHolders.Current().getTransformation());
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
+ {
+ // nothing to do, empty active clipPolyPolygon will stay
+ // empty when intersecting
+ }
+ else
+ {
+ // AND existing region and new ClipRange
+ const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
+ rPropertyHolders.Current().getClipPolyPolygon());
+ basegfx::B2DPolyPolygon aClippedPolyPolygon;
+
+ if(aOriginalPolyPolygon.count())
+ {
+ aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnRange(
+ aOriginalPolyPolygon,
+ aClipRange,
+ true,
+ false);
+ }
+
+ if(aClippedPolyPolygon != aOriginalPolyPolygon)
+ {
+ // start new clipping with intersected region
+ HandleNewClipRegion(
+ aClippedPolyPolygon,
+ rTargetHolders,
+ rPropertyHolders);
+ }
+ }
+ }
+ else
+ {
+ // start new clipping with ClipRange
+ const basegfx::B2DPolyPolygon aNewClipPolyPolygon(
+ basegfx::utils::createPolygonFromRect(aClipRange));
+
+ HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::ISECTREGIONCLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
+ const vcl::Region& rNewRegion = pA->GetRegion();
+
+ if(rNewRegion.IsEmpty())
+ {
+ // intersect with empty region will always give empty
+ // region; start new clipping with empty PolyPolygon
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ else
+ {
+ // get new ClipPolyPolygon, transform it with current transformation
+ basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion));
+ aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
+ {
+ // nothing to do, empty active clipPolyPolygon will stay empty
+ // when intersecting with any region
+ }
+ else
+ {
+ // AND existing and new region
+ const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
+ rPropertyHolders.Current().getClipPolyPolygon());
+ basegfx::B2DPolyPolygon aClippedPolyPolygon;
+
+ if(aOriginalPolyPolygon.count())
+ {
+ aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ aOriginalPolyPolygon, aNewClipPolyPolygon, true, false);
+ }
+
+ if(aClippedPolyPolygon != aOriginalPolyPolygon)
+ {
+ // start new clipping with intersected ClipPolyPolygon
+ HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+ }
+ else
+ {
+ // start new clipping with new ClipPolyPolygon
+ HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::MOVECLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
+ {
+ // nothing to do
+ }
+ else
+ {
+ const sal_Int32 nHor(pA->GetHorzMove());
+ const sal_Int32 nVer(pA->GetVertMove());
+
+ if(0 != nHor || 0 != nVer)
+ {
+ // prepare translation, add current transformation
+ basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove());
+ aVector *= rPropertyHolders.Current().getTransformation();
+ basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(aVector));
+
+ // transform existing region
+ basegfx::B2DPolyPolygon aClipPolyPolygon(
+ rPropertyHolders.Current().getClipPolyPolygon());
+
+ aClipPolyPolygon.transform(aTransform);
+ HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::LINECOLOR :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
+ // tdf#89901 do as OutDev does: COL_TRANSPARENT deactivates line draw
+ const bool bActive(pA->IsSetting() && COL_TRANSPARENT != pA->GetColor());
+
+ rPropertyHolders.Current().setLineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::FILLCOLOR :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
+ // tdf#89901 do as OutDev does: COL_TRANSPARENT deactivates polygon fill
+ const bool bActive(pA->IsSetting() && COL_TRANSPARENT != pA->GetColor());
+
+ rPropertyHolders.Current().setFillColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::TEXTCOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
+ const bool bActivate(COL_TRANSPARENT != pA->GetColor());
+
+ rPropertyHolders.Current().setTextColorActive(bActivate);
+ rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::TEXTFILLCOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pAction);
+ const bool bWithColorArgument(pA->IsSetting());
+
+ if(bWithColorArgument)
+ {
+ // emulate OutputDevice::SetTextFillColor(...) WITH argument
+ const Color& rFontFillColor = pA->GetColor();
+ rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
+ rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor);
+ }
+ else
+ {
+ // emulate SetFillColor() <- NO argument (!)
+ rPropertyHolders.Current().setTextFillColorActive(false);
+ }
+
+ break;
+ }
+ case MetaActionType::TEXTALIGN :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pAction);
+ const TextAlign aNewTextAlign = pA->GetTextAlign();
+
+ // TextAlign is applied to the current font (as in
+ // OutputDevice::SetTextAlign which would be used when
+ // playing the Metafile)
+ if(rPropertyHolders.Current().getFont().GetAlignment() != aNewTextAlign)
+ {
+ vcl::Font aNewFont(rPropertyHolders.Current().getFont());
+ aNewFont.SetAlignment(aNewTextAlign);
+ rPropertyHolders.Current().setFont(aNewFont);
+ }
+
+ break;
+ }
+ case MetaActionType::MAPMODE :
+ {
+ /** CHECKED, WORKS WELL */
+ // the most necessary MapMode to be interpreted is MapUnit::MapRelative,
+ // but also the others may occur. Even not yet supported ones
+ // may need to be added here later
+ const MetaMapModeAction* pA = static_cast<const MetaMapModeAction*>(pAction);
+ const MapMode& rMapMode = pA->GetMapMode();
+ basegfx::B2DHomMatrix aMapping;
+
+ if(MapUnit::MapRelative == rMapMode.GetMapUnit())
+ {
+ aMapping = getTransformFromMapMode(rMapMode);
+ }
+ else
+ {
+ const auto eFrom = MapToO3tlLength(rPropertyHolders.Current().getMapUnit()),
+ eTo = MapToO3tlLength(rMapMode.GetMapUnit());
+ if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
+ {
+ const double fConvert(o3tl::convert(1.0, eFrom, eTo));
+ aMapping.scale(fConvert, fConvert);
+ }
+ else
+ OSL_FAIL("implInterpretMetafile: MetaActionType::MAPMODE with unsupported MapUnit (!)");
+
+ aMapping = getTransformFromMapMode(rMapMode) * aMapping;
+ rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit());
+ }
+
+ if(!aMapping.isIdentity())
+ {
+ aMapping = aMapping * rPropertyHolders.Current().getTransformation();
+ rPropertyHolders.Current().setTransformation(aMapping);
+ }
+
+ break;
+ }
+ case MetaActionType::FONT :
+ {
+ /** SIMPLE, DONE */
+ const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
+ rPropertyHolders.Current().setFont(pA->GetFont());
+ Size aFontSize(pA->GetFont().GetFontSize());
+
+ if(0 == aFontSize.Height())
+ {
+ // this should not happen but i got Metafiles where this was the
+ // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
+ vcl::Font aCorrectedFont(pA->GetFont());
+
+ // guess 16 pixel (as in VCL)
+ aFontSize = Size(0, 16);
+
+ // convert to target MapUnit if not pixels
+ aFontSize = OutputDevice::LogicToLogic(
+ aFontSize, MapMode(MapUnit::MapPixel), MapMode(rPropertyHolders.Current().getMapUnit()));
+
+ aCorrectedFont.SetFontSize(aFontSize);
+ rPropertyHolders.Current().setFont(aCorrectedFont);
+ }
+
+ // older Metafiles have no MetaActionType::TEXTCOLOR which defines
+ // the FontColor now, so use the Font's color when not transparent
+ const Color& rFontColor = pA->GetFont().GetColor();
+ const bool bActivate(COL_TRANSPARENT != rFontColor);
+
+ if(bActivate)
+ {
+ rPropertyHolders.Current().setTextColor(rFontColor.getBColor());
+ }
+
+ // caution: do NOT deactivate here on transparent, see
+ // OutputDevice::SetFont(..) for more info
+ // rPropertyHolders.Current().setTextColorActive(bActivate);
+
+ // for fill color emulate a MetaTextFillColorAction with !transparent as bool,
+ // see OutputDevice::SetFont(..) the if(mpMetaFile) case
+ if(bActivate)
+ {
+ const Color& rFontFillColor = pA->GetFont().GetFillColor();
+ rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
+ rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor);
+ }
+ else
+ {
+ rPropertyHolders.Current().setTextFillColorActive(false);
+ }
+
+ break;
+ }
+ case MetaActionType::PUSH :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
+ rPropertyHolders.Push(pA->GetFlags());
+
+ break;
+ }
+ case MetaActionType::POP :
+ {
+ /** CHECKED, WORKS WELL */
+ const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & vcl::PushFlags::CLIPREGION);
+ const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & vcl::PushFlags::RASTEROP);
+
+ if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ // end evtl. clipping
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+
+ if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
+ {
+ // end evtl. RasterOp
+ HandleNewRasterOp(RasterOp::OverPaint, rTargetHolders, rPropertyHolders);
+ }
+
+ rPropertyHolders.Pop();
+
+ if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
+ {
+ // start evtl. RasterOp
+ HandleNewRasterOp(rPropertyHolders.Current().getRasterOp(), rTargetHolders, rPropertyHolders);
+ }
+
+ if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ // start evtl. clipping
+ HandleNewClipRegion(
+ rPropertyHolders.Current().getClipPolyPolygon(), rTargetHolders, rPropertyHolders);
+ }
+
+ break;
+ }
+ case MetaActionType::RASTEROP :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaRasterOpAction* pA = static_cast<const MetaRasterOpAction*>(pAction);
+ const RasterOp aRasterOp = pA->GetRasterOp();
+
+ HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders);
+
+ break;
+ }
+ case MetaActionType::Transparent :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction);
+ const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aOutline.count())
+ {
+ const sal_uInt16 nTransparence(pA->GetTransparence());
+
+ if(0 == nTransparence)
+ {
+ // not transparent
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ else if(nTransparence >= 100)
+ {
+ // fully or more than transparent
+ }
+ else
+ {
+ // transparent. Create new target
+ rTargetHolders.Push();
+
+ // create primitives there and get them
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ drawinglayer::primitive2d::Primitive2DContainer aSubContent(
+ rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
+
+ // back to old target
+ rTargetHolders.Pop();
+
+ if(!aSubContent.empty())
+ {
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aSubContent),
+ nTransparence * 0.01));
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::EPS :
+ {
+ /** CHECKED, WORKS WELL */
+ // To support this action, I have added a EpsPrimitive2D which will
+ // by default decompose to the Metafile replacement data. To support
+ // this EPS on screen, the renderer visualizing this has to support
+ // that primitive and visualize the Eps file (e.g. printing)
+ const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
+ const tools::Rectangle aRectangle(pA->GetPoint(), pA->GetSize());
+
+ if(!aRectangle.IsEmpty())
+ {
+ // create object transform
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, aRectangle.GetWidth());
+ aObjectTransform.set(1, 1, aRectangle.GetHeight());
+ aObjectTransform.set(0, 2, aRectangle.Left());
+ aObjectTransform.set(1, 2, aRectangle.Top());
+
+ // add current transformation
+ aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform;
+
+ // embed using EpsPrimitive
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::EpsPrimitive2D(
+ aObjectTransform,
+ pA->GetLink(),
+ pA->GetSubstitute()));
+ }
+
+ break;
+ }
+ case MetaActionType::REFPOINT :
+ {
+ /** SIMPLE, DONE */
+ // only used for hatch and line pattern offsets, pretty much no longer
+ // supported today
+ // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
+ break;
+ }
+ case MetaActionType::TEXTLINECOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setTextLineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::TEXTLINE :
+ {
+ /** CHECKED, WORKS WELL */
+ // actually creates overline, underline and strikeouts, so
+ // these should be isolated from TextDecoratedPortionPrimitive2D
+ // to own primitives. Done, available now.
+ //
+ // This Metaaction seems not to be used (was not used in any
+ // checked files). It's used in combination with the current
+ // Font.
+ const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
+
+ processMetaTextLineAction(
+ *pA,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::FLOATTRANSPARENT :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
+ const basegfx::B2DRange aTargetRange(
+ pA->GetPoint().X(),
+ pA->GetPoint().Y(),
+ pA->GetPoint().X() + pA->GetSize().Width(),
+ pA->GetPoint().Y() + pA->GetSize().Height());
+
+ if(!aTargetRange.isEmpty())
+ {
+ const GDIMetaFile& rContent = pA->GetGDIMetaFile();
+
+ if(rContent.GetActionSize())
+ {
+ // create the sub-content with no embedding specific to the
+ // sub-metafile, this seems not to be used.
+ drawinglayer::primitive2d::Primitive2DContainer xSubContent;
+ {
+ rTargetHolders.Push();
+ // #i# for sub-Mteafile contents, do start with new, default render state
+ rPropertyHolders.PushDefault();
+ implInterpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation);
+ xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ rPropertyHolders.Pop();
+ rTargetHolders.Pop();
+ }
+
+ if(!xSubContent.empty())
+ {
+ // prepare sub-content transform
+ basegfx::B2DHomMatrix aSubTransform;
+
+ // create SourceRange
+ const basegfx::B2DRange aSourceRange(
+ rContent.GetPrefMapMode().GetOrigin().X(),
+ rContent.GetPrefMapMode().GetOrigin().Y(),
+ rContent.GetPrefMapMode().GetOrigin().X() + rContent.GetPrefSize().Width(),
+ rContent.GetPrefMapMode().GetOrigin().Y() + rContent.GetPrefSize().Height());
+
+ // apply mapping if aTargetRange and aSourceRange are not equal
+ if(!aSourceRange.equal(aTargetRange))
+ {
+ aSubTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY());
+ aSubTransform.scale(
+ aTargetRange.getWidth() / (basegfx::fTools::equalZero(aSourceRange.getWidth()) ? 1.0 : aSourceRange.getWidth()),
+ aTargetRange.getHeight() / (basegfx::fTools::equalZero(aSourceRange.getHeight()) ? 1.0 : aSourceRange.getHeight()));
+ aSubTransform.translate(aTargetRange.getMinX(), aTargetRange.getMinY());
+ }
+
+ // apply general current transformation
+ aSubTransform = rPropertyHolders.Current().getTransformation() * aSubTransform;
+
+ // evtl. embed sub-content to its transformation
+ if(!aSubTransform.isIdentity())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aSubTransform,
+ std::move(xSubContent)));
+
+ xSubContent = drawinglayer::primitive2d::Primitive2DContainer { aEmbeddedTransform };
+ }
+
+ // check if gradient is a real gradient
+ const Gradient& rGradient = pA->GetGradient();
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient; create UnifiedTransparencePrimitive2D
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(xSubContent),
+ aAttribute.getStartColor().luminance()));
+ }
+ else
+ {
+ // really a gradient. Create gradient sub-content (with correct scaling)
+ basegfx::B2DRange aRange(aTargetRange);
+ aRange.transform(rPropertyHolders.Current().getTransformation());
+
+ // prepare gradient for transparent content
+ const drawinglayer::primitive2d::Primitive2DReference xTransparence(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ aRange,
+ aAttribute));
+
+ // create transparence primitive
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransparencePrimitive2D(
+ std::move(xSubContent),
+ drawinglayer::primitive2d::Primitive2DContainer { xTransparence }));
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::GRADIENTEX :
+ {
+ /** SIMPLE, DONE */
+ // This is only a data holder which is interpreted inside comment actions,
+ // see MetaActionType::COMMENT for more info
+ // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
+ break;
+ }
+ case MetaActionType::LAYOUTMODE :
+ {
+ /** SIMPLE, DONE */
+ const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
+ rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
+ break;
+ }
+ case MetaActionType::TEXTLANGUAGE :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
+ rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
+ break;
+ }
+ case MetaActionType::OVERLINECOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setOverlineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::COMMENT :
+ {
+ /** CHECKED, WORKS WELL */
+ // I already implemented
+ // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END
+ // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END,
+ // but opted to remove these again; it works well without them
+ // and makes the code less dependent from those Metafile Add-Ons
+ const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
+
+ if (pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
+ {
+ // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the
+ // pure recorded paint of the gradients uses the XOR paint functionality
+ // ('trick'). This is (and will be) problematic with AntiAliasing, so it's
+ // better to use this info
+ const MetaGradientExAction* pMetaGradientExAction = nullptr;
+ bool bDone(false);
+ size_t b(nAction + 1);
+
+ for(; !bDone && b < nCount; b++)
+ {
+ pAction = rMetaFile.GetAction(b);
+
+ if(MetaActionType::GRADIENTEX == pAction->GetType())
+ {
+ pMetaGradientExAction = static_cast<const MetaGradientExAction*>(pAction);
+ }
+ else if(MetaActionType::COMMENT == pAction->GetType())
+ {
+ if (static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END"))
+ {
+ bDone = true;
+ }
+ }
+ }
+
+ if(bDone && pMetaGradientExAction)
+ {
+ // consume actions and skip forward
+ nAction = b - 1;
+
+ // get geometry data
+ basegfx::B2DPolyPolygon aPolyPolygon(pMetaGradientExAction->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aPolyPolygon.count())
+ {
+ // transform geometry
+ aPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
+
+ // get and check if gradient is a real gradient
+ const Gradient& rGradient = pMetaGradientExAction->GetGradient();
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ aPolyPolygon,
+ aAttribute.getStartColor()));
+ }
+ else
+ {
+ // really a gradient
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
+ aPolyPolygon,
+ aAttribute));
+ }
+ }
+ }
+ }
+ else if (pA->GetComment().equalsIgnoreAsciiCase("EMF_PLUS_HEADER_INFO"))
+ {
+ if (aEMFPlus)
+ {
+ // error: should not yet exist
+ SAL_INFO("drawinglayer.emf", "Error: multiple EMF_PLUS_HEADER_INFO");
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pA->GetDataSize());
+ SvMemoryStream aMemoryStream(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(), StreamMode::READ);
+
+ aEMFPlus.reset(
+ new emfplushelper::EmfPlusHelper(
+ aMemoryStream,
+ rTargetHolders,
+ rPropertyHolders));
+ }
+ }
+ else if (pA->GetComment().equalsIgnoreAsciiCase("EMF_PLUS"))
+ {
+ if (!aEMFPlus)
+ {
+ // error: should exist
+ SAL_INFO("drawinglayer.emf", "Error: EMF_PLUS before EMF_PLUS_HEADER_INFO");
+ }
+ else
+ {
+ static int count = -1, limit = 0x7fffffff;
+
+ if (count == -1)
+ {
+ count = 0;
+
+ if (char *env = getenv("EMF_PLUS_LIMIT"))
+ {
+ limit = atoi(env);
+ SAL_INFO("drawinglayer.emf", "EMF+ records limit: " << limit);
+ }
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+ passed to canvas mtf renderer, size: " << pA->GetDataSize());
+
+ if (count < limit)
+ {
+ SvMemoryStream aMemoryStream(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(), StreamMode::READ);
+
+ aEMFPlus->processEmfPlusData(
+ aMemoryStream,
+ rViewInformation);
+ }
+
+ count++;
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ OSL_FAIL("Unknown MetaFile Action (!)");
+ break;
+ }
+ }
+ }
+ }
+}
+
+namespace wmfemfhelper
+{
+ drawinglayer::primitive2d::Primitive2DContainer interpretMetafile(
+ const GDIMetaFile& rMetaFile,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ // prepare target and properties; each will have one default entry
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ TargetHolders aTargetHolders;
+ PropertyHolders aPropertyHolders;
+
+ // set target MapUnit at Properties
+ aPropertyHolders.Current().setMapUnit(rMetaFile.GetPrefMapMode().GetMapUnit());
+
+ // interpret the Metafile
+ implInterpretMetafile(rMetaFile, aTargetHolders, aPropertyHolders, rViewInformation);
+
+ // get the content. There should be only one target, as in the start condition,
+ // but iterating will be the right thing to do when some push/pop is not closed
+ while (aTargetHolders.size() > 1)
+ {
+ xRetval.append(
+ aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
+ aTargetHolders.Pop();
+ }
+
+ xRetval.append(
+ aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
+
+ return xRetval;
+ }
+
+} // end of namespace wmfemfhelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */