LibreOffice Module svx (master) 1
viewobjectcontact.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
33#include <svx/svdobj.hxx>
34#include <svx/svdmodel.hxx>
35#include <svx/svdpage.hxx>
36#include <svx/svdotext.hxx>
37#include <vcl/pdfwriter.hxx>
38
39using namespace com::sun::star;
40
41namespace {
42
43// animated extractor
44
45// Necessary to filter a sequence of animated primitives from
46// a sequence of primitives to find out if animated or not. The decision for
47// what to decompose is hard-coded and only done for knowingly animated primitives
48// to not decompose too deeply and unnecessarily. This implies that the list
49// which is view-specific needs to be expanded by hand when new animated objects
50// are added. This may eventually be changed to a dynamically configurable approach
51// if necessary.
52class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D
53{
54protected:
55 // the found animated primitives
57
58 // text animation allowed?
59 bool mbTextAnimationAllowed : 1;
60
61 // graphic animation allowed?
62 bool mbGraphicAnimationAllowed : 1;
63
64 // as tooling, the process() implementation takes over API handling and calls this
65 // virtual render method when the primitive implementation is BasePrimitive2D-based.
66 virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override;
67
68public:
69 AnimatedExtractingProcessor2D(
70 const drawinglayer::geometry::ViewInformation2D& rViewInformation,
71 bool bTextAnimationAllowed,
72 bool bGraphicAnimationAllowed);
73
74 // data access
75 const drawinglayer::primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
76 drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence() { return std::move(maPrimitive2DSequence); }
77};
78
79AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
80 const drawinglayer::geometry::ViewInformation2D& rViewInformation,
81 bool bTextAnimationAllowed,
82 bool bGraphicAnimationAllowed)
83: drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
84 mbTextAnimationAllowed(bTextAnimationAllowed),
85 mbGraphicAnimationAllowed(bGraphicAnimationAllowed)
86{
87}
88
89void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate)
90{
91 // known implementation, access directly
92 switch(rCandidate.getPrimitive2DID())
93 {
94 // add and accept animated primitives directly, no need to decompose
98 {
100
101 if((rSwitchPrimitive.isTextAnimation() && mbTextAnimationAllowed)
102 || (rSwitchPrimitive.isGraphicAnimation() && mbGraphicAnimationAllowed))
103 {
105 maPrimitive2DSequence.push_back(xReference);
106 }
107 break;
108 }
109
110 // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D
111 // which then produces the animation infos (all when used/needed)
114
115 // decompose SdrObjects with evtl. animated text
124
125 // #121194# With Graphic as Bitmap FillStyle, also check
126 // for primitives filled with animated graphics
130
131 // decompose evtl. animated text contained in MaskPrimitive2D
132 // or group primitives
135 {
136 process(rCandidate);
137 break;
138 }
139
140 default :
141 {
142 // nothing to do for the rest
143 break;
144 }
145 }
146}
147
148} // end of anonymous namespace
149
150namespace sdr::contact {
151
153: mrObjectContact(rObjectContact),
154 mrViewContact(rViewContact),
155 maGridOffset(0.0, 0.0),
156 mnActionChangedCount(0),
157 mbLazyInvalidate(false)
158{
159 // make the ViewContact remember me
161
162 // make the ObjectContact remember me
164}
165
167{
168 // invalidate in view
169 if(!getObjectRange().isEmpty())
170 {
172 }
173
174 // delete PrimitiveAnimation
175 mpPrimitiveAnimation.reset();
176
177 // take care of remembered ObjectContact. Remove from
178 // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
179 // which (depending of its implementation) may destroy other OCs. This
180 // can trigger the deletion of the helper OC of a page visualising object
181 // which IS the OC of this object. Eventually StopGettingViewed() needs
182 // to get asynchron later
184
185 // take care of remembered ViewContact
187}
188
190{
192 {
194 basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D);
195 if (!aTempRange.isEmpty())
196 {
197 const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange;
198 }
199 else
200 {
201 // if range is not computed (new or LazyInvalidate objects), force it
202 const DisplayInfo aDisplayInfo;
204
205 if(!xSequence.empty())
206 {
207 const_cast< ViewObjectContact* >(this)->maObjectRange =
208 xSequence.getB2DRange(rViewInfo2D);
209 }
210 }
211 }
212
213 return maObjectRange;
214}
215
217{
218 // clear cached primitives
219 mxPrimitive2DSequence.clear();
221
223 return;
224
225 // set local flag
226 mbLazyInvalidate = true;
227
228 // force ObjectRange
230
231 if(!getObjectRange().isEmpty())
232 {
233 // invalidate current valid range
235
236 // reset gridOffset, it needs to be recalculated
237 if (GetObjectContact().supportsGridOffsets())
239 else
241 }
242
243 // register at OC for lazy invalidate
245}
246
248{
250 return;
251
252 // reset flag
253 mbLazyInvalidate = false;
254
255 // force ObjectRange
257
258 if(!getObjectRange().isEmpty())
259 {
260 // invalidate current valid range
262 }
263}
264
265// Take some action when new objects are inserted
267{
268 // force creation of the new VOC and trigger it's refresh, so it
269 // will take part in LazyInvalidate immediately
271
272 // forward action to ObjectContact
273 // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
274 // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
275}
276
278{
279 // remove old one
280 mpPrimitiveAnimation.reset();
281
282 // check for animated primitives
283 if(mxPrimitive2DSequence.empty())
284 return;
285
286 const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
287 const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
288
289 if(bTextAnimationAllowed || bGraphicAnimationAllowed)
290 {
291 AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
292 bTextAnimationAllowed, bGraphicAnimationAllowed);
293 aAnimatedExtractor.process(mxPrimitive2DSequence);
294
295 if(!aAnimatedExtractor.getPrimitive2DSequence().empty())
296 {
297 // derived primitiveList is animated, setup new PrimitiveAnimation
298 mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.extractPrimitive2DSequence()) );
299 }
300 }
301}
302
304{
305 // get the view-independent Primitive from the viewContact
308
309 if(!xRetval.empty())
310 {
311 // handle GluePoint
312 if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible())
313 {
314 const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
315
316 if(!xGlue.empty())
317 {
318 xRetval.append(xGlue);
319 }
320 }
321
322 // handle ghosted
323 if(isPrimitiveGhosted(rDisplayInfo))
324 {
325 const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
326 const basegfx::BColorModifierSharedPtr aBColorModifier =
327 std::make_shared<basegfx::BColorModifier_interpolate>(
328 aRGBWhite,
329 0.5);
332 std::move(xRetval),
333 aBColorModifier));
334
336 }
337 }
338
339 rVisitor.visit(xRetval);
340}
341
343{
344 // only some of the top-level apps are any good at reliably invalidating us (e.g. writer is not)
346
347 if (nullptr != pSdrObj && pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable())
348 {
349 if (!mxPrimitive2DSequence.empty())
351 }
352
353 // prepare new representation
355
356 // take care of redirectors and create new list
358
359 if(pRedirector)
360 {
361 pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo, xNewPrimitiveSequence);
362 }
363 else
364 {
365 createPrimitive2DSequence(rDisplayInfo, xNewPrimitiveSequence);
366 }
367
368 // check and eventually embed to GridOffset transform primitive (calc only)
369 if(!xNewPrimitiveSequence.empty() && GetObjectContact().supportsGridOffsets())
370 {
371 const basegfx::B2DVector& rGridOffset(getGridOffset());
372
373 if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
374 {
375 const basegfx::B2DHomMatrix aTranslateGridOffset(
377 rGridOffset));
380 aTranslateGridOffset,
381 std::move(xNewPrimitiveSequence)));
382 xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed };
383 }
384 }
385
386 // Check if we need to embed to a StructureTagPrimitive2D, too. This
387 // was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before
388 if (!xNewPrimitiveSequence.empty() && GetObjectContact().isExportTaggedPDF())
389 {
390 if (nullptr != pSdrObj)
391 {
393 const SdrInventor nInventor(pSdrObj->GetObjInventor());
394 const SdrObjKind nIdentifier(pSdrObj->GetObjIdentifier());
395 const bool bIsTextObj(nullptr != DynCastSdrTextObj(pSdrObj));
396
397 // Note: SwFlyDrawObj/SwVirtFlyDrawObj have SdrInventor::Swg - these
398 // are *not* handled here because not all of them are painted
399 // completely with primitives, so a tag here does not encapsulate them.
400 // The tag must be created by SwTaggedPDFHelper until this is fixed.
401 if ( nInventor == SdrInventor::Default )
402 {
403 if ( nIdentifier == SdrObjKind::Group )
404 eElement = vcl::PDFWriter::Section;
405 else if (nIdentifier == SdrObjKind::Table)
406 eElement = vcl::PDFWriter::Table;
407 else if ( nIdentifier == SdrObjKind::TitleText )
408 eElement = vcl::PDFWriter::Heading;
409 else if ( nIdentifier == SdrObjKind::OutlineText )
410 eElement = vcl::PDFWriter::Division;
411 else if ( !bIsTextObj || !static_cast<const SdrTextObj&>(*pSdrObj).HasText() )
412 eElement = vcl::PDFWriter::Figure;
413 else
414 eElement = vcl::PDFWriter::Division;
415 }
416
418 {
419 SdrPage* pSdrPage(pSdrObj->getSdrPageFromSdrObject());
420
421 if(pSdrPage)
422 {
423 const bool bBackground(pSdrPage->IsMasterPage());
424 const bool bImage(SdrObjKind::Graphic == pSdrObj->GetObjIdentifier());
425 // note: there must be output device here, in PDF export
426 sal_Int32 nAnchorId(-1);
427 if (auto const pUserCall = pSdrObj->GetUserCall())
428 {
429 nAnchorId = pUserCall->GetPDFAnchorStructureElementId(*pSdrObj);
430 }
431
434 eElement,
435 bBackground,
436 bImage,
437 std::move(xNewPrimitiveSequence),
438 nAnchorId));
439 xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { xReference };
440 }
441 }
442 }
443 else
444 {
445 // page backgrounds etc should be tagged as artifacts:
448 // lies to force silly VclMetafileProcessor2D to emit NonStructElement
450 true,
451 true,
452 std::move(xNewPrimitiveSequence))
453 };
454 }
455 }
456
457 // Local up-to-date checks. New list different from local one?
458 // This is the important point where it gets decided if the current or the new
459 // representation gets used. This is important for performance, since the
460 // current representation contains possible precious decompositions. That
461 // comparisons triggers exactly if something in the object visualization
462 // has changed.
463 // Note: That is the main reason for BasePrimitive2D::operator== at all. I
464 // have alternatively tried to invalidate the local representation on object
465 // change, but that is simply not reliable.
466 // Note2: I did that once in aw080, the lost CWS, and it worked well enough
467 // so that I could remove *all* operator== from all derivations of
468 // BasePrimitive2D, so it can be done again (with the needed resources)
469 if(mxPrimitive2DSequence != xNewPrimitiveSequence)
470 {
471 // has changed, copy content
472 const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence);
473
474 // check for animated stuff
475 const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
476
477 // always update object range when PrimitiveSequence changes
478 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
479 const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D);
480 }
481
482 // return current Primitive2DContainer
484}
485
486bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
487{
488 // default: always visible
489 return true;
490}
491
493{
494 // default: standard check
495 return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
496}
497
499{
500 // check model-view visibility
501 if(!isPrimitiveVisible(rDisplayInfo))
502 return;
503
504 getPrimitive2DSequence(rDisplayInfo);
505 if(mxPrimitive2DSequence.empty())
506 return;
507
508 // get ranges
509 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
510 // tdf#147164 cannot use maObjectRange here, it is unreliable
511 const basegfx::B2DRange aObjectRange(mxPrimitive2DSequence.getB2DRange(rViewInformation2D));
512 const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport());
513
514 // check geometrical visibility
515 bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange);
516 if(!bVisible)
517 return;
518
519 // temporarily take over the mxPrimitive2DSequence, in case it gets invalidated while we want to iterate over it
520 auto tmp = std::move(const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence);
521 int nPrevCount = mnActionChangedCount;
522
523 rVisitor.visit(tmp);
524
525 // if we received ActionChanged() calls while walking the primitives, then leave it empty, otherwise move it back
526 if (mnActionChangedCount == nPrevCount)
527 const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence = std::move(tmp);
528}
529
531{
532 const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
533
534 for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
535 {
536 const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
537
538 rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor);
539 }
540}
541
542// Support getting a GridOffset per object and view for non-linear ViewToDevice
543// transformation (calc). On-demand created by delegating to the ObjectContact
544// (->View) that has then all needed information
546{
547 if(0.0 == maGridOffset.getX() && 0.0 == maGridOffset.getY() && GetObjectContact().supportsGridOffsets())
548 {
549 // create on-demand
551 }
552
553 return maGridOffset;
554}
555
557{
558 // reset buffered GridOffset itself
559 maGridOffset.setX(0.0);
560 maGridOffset.setY(0.0);
561
562 // also reset sequence to get a re-calculation when GridOffset changes
563 mxPrimitive2DSequence.clear();
565}
566
567}
568
569/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsVOCInvalidationIsReliable() const
Definition: svdmodel.hxx:614
Abstract DrawObject.
Definition: svdobj.hxx:260
SdrObjUserCall * GetUserCall() const
Definition: svdobj.hxx:836
virtual SdrInventor GetObjInventor() const
Definition: svdobj.cxx:621
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:289
virtual SdrObjKind GetObjIdentifier() const
Definition: svdobj.cxx:626
SdrPage * getSdrPageFromSdrObject() const
Definition: svdobj.cxx:279
A SdrPage contains exactly one SdrObjList and a description of the physical page dimensions (size / m...
Definition: svdpage.hxx:377
bool IsMasterPage() const
Definition: svdpage.hxx:462
virtual bool HasText() const override
Definition: svdotxat.cxx:420
bool isEmpty() const
bool overlaps(const Range2D &rRange) const
TYPE getX() const
void setY(TYPE fY)
TYPE getY() const
void setX(TYPE fX)
const basegfx::B2DRange & getViewport() const
virtual sal_uInt32 getPrimitive2DID() const=0
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
void append(const Primitive2DReference &)
virtual void visit(const Primitive2DReference &)=0
virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D &rCandidate)
bool IsGhostedDrawModeActive() const
Definition: displayinfo.hxx:93
virtual void InvalidatePartOfView(const basegfx::B2DRange &rRange) const
void RemoveViewObjectContact(ViewObjectContact &rVOContact)
virtual void calculateGridOffsetForViewOjectContact(basegfx::B2DVector &rTarget, const ViewObjectContact &rClient) const
virtual void setLazyInvalidate(ViewObjectContact &rVOC)
const drawinglayer::geometry::ViewInformation2D & getViewInformation2D() const
ViewObjectContactRedirector * GetViewObjectContactRedirector() const
void AddViewObjectContact(ViewObjectContact &rVOContact)
virtual SdrObject * TryToGetSdrObject() const
void RemoveViewObjectContact(ViewObjectContact &rVOContact)
Definition: viewcontact.cxx:99
void AddViewObjectContact(ViewObjectContact &rVOContact)
Definition: viewcontact.cxx:93
ViewObjectContact & GetViewObjectContact(ObjectContact &rObjectContact)
Definition: viewcontact.cxx:65
virtual basegfx::B2DRange getRange(const drawinglayer::geometry::ViewInformation2D &rViewInfo2D) const
void getViewIndependentPrimitive2DContainer(drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
virtual void createRedirectedPrimitive2DSequence(const sdr::contact::ViewObjectContact &rOriginal, const sdr::contact::DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor)
const basegfx::B2DVector & getGridOffset() const
drawinglayer::primitive2d::Primitive2DContainer const & getPrimitive2DSequence(const DisplayInfo &rDisplayInfo) const
ViewContact & GetViewContact() const
ViewObjectContact(ObjectContact &rObjectContact, ViewContact &rViewContact)
virtual bool isPrimitiveVisible(const DisplayInfo &rDisplayInfo) const
drawinglayer::primitive2d::Primitive2DContainer mxPrimitive2DSequence
virtual bool isPrimitiveGhosted(const DisplayInfo &rDisplayInfo) const
ObjectContact & GetObjectContact() const
void ActionChildInserted(ViewContact &rChild)
virtual void createPrimitive2DSequence(const DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
void getPrimitive2DSequenceSubHierarchy(DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
std::unique_ptr< sdr::animation::PrimitiveAnimation > mpPrimitiveAnimation
virtual void getPrimitive2DSequenceHierarchy(DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
const basegfx::B2DRange & getObjectRange() const
#define PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D
#define PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D
#define PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D
#define PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
#define PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D
#define PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D
#define PRIMITIVE2D_ID_GROUPPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
#define PRIMITIVE2D_ID_MASKPRIMITIVE2D
uno_Any a
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
std::shared_ptr< BColorModifier > BColorModifierSharedPtr
SdrTextObj * DynCastSdrTextObj(SdrObject *pObj)
Definition: svdobj.cxx:3176
SdrInventor
Definition: svdobj.hxx:98
SdrObjKind
Definition: svdobjkind.hxx:25
@ Group
abstract object (SdrObject)
@ Table
media shape
@ Graphic
OutlineText, special text object for StarDraw.
@ OutlineText
TitleText, special text object for StarDraw.
@ TitleText
text object
#define PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D
bool bVisible