/src/libreoffice/svx/source/sdr/contact/viewobjectcontact.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <svx/sdr/contact/viewobjectcontact.hxx> |
21 | | #include <svx/sdr/contact/viewcontact.hxx> |
22 | | #include <svx/sdr/contact/objectcontact.hxx> |
23 | | #include <svx/sdr/contact/displayinfo.hxx> |
24 | | #include <svx/sdr/animation/animationstate.hxx> |
25 | | #include <svx/sdr/contact/viewobjectcontactredirector.hxx> |
26 | | #include <basegfx/color/bcolor.hxx> |
27 | | #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> |
28 | | #include <drawinglayer/primitive2d/animatedprimitive2d.hxx> |
29 | | #include <drawinglayer/processor2d/baseprocessor2d.hxx> |
30 | | #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> |
31 | | #include <drawinglayer/primitive2d/transformprimitive2d.hxx> |
32 | | #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> |
33 | | #include <svx/svdobj.hxx> |
34 | | #include <svx/svdomedia.hxx> |
35 | | #include <svx/svdmodel.hxx> |
36 | | #include <svx/svdpage.hxx> |
37 | | #include <svx/svdotext.hxx> |
38 | | #include <vcl/pdfwriter.hxx> |
39 | | #include <vcl/pdfextoutdevdata.hxx> |
40 | | |
41 | | using namespace com::sun::star; |
42 | | |
43 | | namespace { |
44 | | |
45 | | // animated extractor |
46 | | |
47 | | // Necessary to filter a sequence of animated primitives from |
48 | | // a sequence of primitives to find out if animated or not. The decision for |
49 | | // what to decompose is hard-coded and only done for knowingly animated primitives |
50 | | // to not decompose too deeply and unnecessarily. This implies that the list |
51 | | // which is view-specific needs to be expanded by hand when new animated objects |
52 | | // are added. This may eventually be changed to a dynamically configurable approach |
53 | | // if necessary. |
54 | | class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D |
55 | | { |
56 | | protected: |
57 | | // the found animated primitives |
58 | | drawinglayer::primitive2d::Primitive2DContainer maPrimitive2DSequence; |
59 | | |
60 | | // text animation allowed? |
61 | | bool mbTextAnimationAllowed : 1; |
62 | | |
63 | | // graphic animation allowed? |
64 | | bool mbGraphicAnimationAllowed : 1; |
65 | | |
66 | | // as tooling, the process() implementation takes over API handling and calls this |
67 | | // virtual render method when the primitive implementation is BasePrimitive2D-based. |
68 | | virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override; |
69 | | |
70 | | public: |
71 | | AnimatedExtractingProcessor2D( |
72 | | const drawinglayer::geometry::ViewInformation2D& rViewInformation, |
73 | | bool bTextAnimationAllowed, |
74 | | bool bGraphicAnimationAllowed); |
75 | | |
76 | | // data access |
77 | 1.35k | const drawinglayer::primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; } |
78 | 0 | drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence() { return std::move(maPrimitive2DSequence); } |
79 | | }; |
80 | | |
81 | | AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D( |
82 | | const drawinglayer::geometry::ViewInformation2D& rViewInformation, |
83 | | bool bTextAnimationAllowed, |
84 | | bool bGraphicAnimationAllowed) |
85 | 1.35k | : drawinglayer::processor2d::BaseProcessor2D(rViewInformation), |
86 | 1.35k | mbTextAnimationAllowed(bTextAnimationAllowed), |
87 | 1.35k | mbGraphicAnimationAllowed(bGraphicAnimationAllowed) |
88 | 1.35k | { |
89 | 1.35k | } |
90 | | |
91 | | void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) |
92 | 2.04k | { |
93 | | // known implementation, access directly |
94 | 2.04k | switch(rCandidate.getPrimitive2DID()) |
95 | 2.04k | { |
96 | | // add and accept animated primitives directly, no need to decompose |
97 | 0 | case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D : |
98 | 0 | case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D : |
99 | 0 | case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D : |
100 | 0 | case PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D : |
101 | 0 | { |
102 | 0 | const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate); |
103 | |
|
104 | 0 | if((rSwitchPrimitive.isTextAnimation() && mbTextAnimationAllowed) |
105 | 0 | || (rSwitchPrimitive.isGraphicAnimation() && mbGraphicAnimationAllowed)) |
106 | 0 | { |
107 | 0 | const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate)); |
108 | 0 | maPrimitive2DSequence.push_back(xReference); |
109 | 0 | } |
110 | 0 | break; |
111 | 0 | } |
112 | | |
113 | | // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D |
114 | | // which then produces the animation infos (all when used/needed) |
115 | 0 | case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D : |
116 | 97 | case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D : |
117 | | |
118 | | // decompose SdrObjects with evtl. animated text |
119 | 97 | case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D : |
120 | 97 | case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D : |
121 | 369 | case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D : |
122 | 369 | case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D : |
123 | 369 | case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D : |
124 | 369 | case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D : |
125 | 439 | case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D : |
126 | 439 | case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D : |
127 | | |
128 | | // #121194# With Graphic as Bitmap FillStyle, also check |
129 | | // for primitives filled with animated graphics |
130 | 439 | case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: |
131 | 590 | case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: |
132 | | |
133 | | // decompose evtl. animated text contained in MaskPrimitive2D |
134 | | // or group primitives |
135 | 590 | case PRIMITIVE2D_ID_MASKPRIMITIVE2D : |
136 | 935 | case PRIMITIVE2D_ID_GROUPPRIMITIVE2D : |
137 | 935 | { |
138 | 935 | process(rCandidate); |
139 | 935 | break; |
140 | 590 | } |
141 | | |
142 | 1.11k | default : |
143 | 1.11k | { |
144 | | // nothing to do for the rest |
145 | 1.11k | break; |
146 | 590 | } |
147 | 2.04k | } |
148 | 2.04k | } |
149 | | |
150 | | } // end of anonymous namespace |
151 | | |
152 | | namespace sdr::contact { |
153 | | |
154 | | ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact) |
155 | 76.9k | : mrObjectContact(rObjectContact), |
156 | 76.9k | mrViewContact(rViewContact), |
157 | 76.9k | mnActionChangedCount(0), |
158 | 76.9k | mbLazyInvalidate(false) |
159 | 76.9k | { |
160 | | // make the ViewContact remember me |
161 | 76.9k | mrViewContact.AddViewObjectContact(*this); |
162 | | |
163 | | // make the ObjectContact remember me |
164 | 76.9k | mrObjectContact.AddViewObjectContact(*this); |
165 | 76.9k | } |
166 | | |
167 | | ViewObjectContact::~ViewObjectContact() |
168 | 76.9k | { |
169 | | // if the object range is empty, then we have never had the primitive range change, so nothing to invalidate |
170 | 76.9k | if (!maObjectRange.isEmpty()) |
171 | 1.35k | { |
172 | | // invalidate in view |
173 | 1.35k | GetObjectContact().InvalidatePartOfView(maObjectRange); |
174 | 1.35k | } |
175 | | |
176 | | // delete PrimitiveAnimation |
177 | 76.9k | mpPrimitiveAnimation.reset(); |
178 | | |
179 | | // take care of remembered ObjectContact. Remove from |
180 | | // OC first. The VC removal (below) CAN trigger a StopGettingViewed() |
181 | | // which (depending of its implementation) may destroy other OCs. This |
182 | | // can trigger the deletion of the helper OC of a page visualising object |
183 | | // which IS the OC of this object. Eventually StopGettingViewed() needs |
184 | | // to get asynchron later |
185 | 76.9k | GetObjectContact().RemoveViewObjectContact(*this); |
186 | | |
187 | | // take care of remembered ViewContact |
188 | 76.9k | GetViewContact().RemoveViewObjectContact(*this); |
189 | 76.9k | } |
190 | | |
191 | | const basegfx::B2DRange& ViewObjectContact::getObjectRange() const |
192 | 0 | { |
193 | 0 | if(maObjectRange.isEmpty()) |
194 | 0 | { |
195 | 0 | const drawinglayer::geometry::ViewInformation2D& rViewInfo2D = GetObjectContact().getViewInformation2D(); |
196 | 0 | basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D); |
197 | 0 | if (!aTempRange.isEmpty()) |
198 | 0 | { |
199 | 0 | const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange; |
200 | 0 | } |
201 | 0 | else |
202 | 0 | { |
203 | | // if range is not computed (new or LazyInvalidate objects), force it |
204 | 0 | const DisplayInfo aDisplayInfo; |
205 | 0 | const drawinglayer::primitive2d::Primitive2DContainer& xSequence(getPrimitive2DSequence(aDisplayInfo)); |
206 | |
|
207 | 0 | if(!xSequence.empty()) |
208 | 0 | { |
209 | 0 | const_cast< ViewObjectContact* >(this)->maObjectRange = |
210 | 0 | xSequence.getB2DRange(rViewInfo2D); |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | |
|
215 | 0 | return maObjectRange; |
216 | 0 | } |
217 | | |
218 | | void ViewObjectContact::ActionChanged() |
219 | 0 | { |
220 | | // clear cached primitives |
221 | 0 | mxPrimitive2DSequence.clear(); |
222 | 0 | ++mnActionChangedCount; |
223 | |
|
224 | 0 | if(mbLazyInvalidate) |
225 | 0 | return; |
226 | | |
227 | | // set local flag |
228 | 0 | mbLazyInvalidate = true; |
229 | | |
230 | | // force ObjectRange |
231 | 0 | getObjectRange(); |
232 | |
|
233 | 0 | if(!getObjectRange().isEmpty()) |
234 | 0 | { |
235 | | // invalidate current valid range |
236 | 0 | GetObjectContact().InvalidatePartOfView(maObjectRange); |
237 | | |
238 | | // reset gridOffset, it needs to be recalculated |
239 | 0 | if (GetObjectContact().supportsGridOffsets()) |
240 | 0 | resetGridOffset(); |
241 | 0 | else |
242 | 0 | maObjectRange.reset(); |
243 | 0 | } |
244 | | |
245 | | // register at OC for lazy invalidate |
246 | 0 | GetObjectContact().setLazyInvalidate(*this); |
247 | 0 | } |
248 | | |
249 | | // IASS: helper for IASS invalidates |
250 | | void ViewObjectContact::ActionChangedIfDifferentPageView(const SdrPageView& rSdrPageView) |
251 | 0 | { |
252 | 0 | SdrPageView* pSdrPageView(GetObjectContact().TryToGetSdrPageView()); |
253 | | |
254 | | // if there is no SdrPageView or different from given one, force |
255 | | // invalidate/repaint |
256 | 0 | if (nullptr == pSdrPageView || pSdrPageView != &rSdrPageView) |
257 | 0 | ActionChanged(); |
258 | 0 | } |
259 | | |
260 | | void ViewObjectContact::triggerLazyInvalidate() |
261 | 0 | { |
262 | 0 | if(!mbLazyInvalidate) |
263 | 0 | return; |
264 | | |
265 | | // reset flag |
266 | 0 | mbLazyInvalidate = false; |
267 | | |
268 | | // force ObjectRange |
269 | 0 | getObjectRange(); |
270 | |
|
271 | 0 | if(!getObjectRange().isEmpty()) |
272 | 0 | { |
273 | | // invalidate current valid range |
274 | 0 | GetObjectContact().InvalidatePartOfView(maObjectRange); |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | | // Take some action when new objects are inserted |
279 | | void ViewObjectContact::ActionChildInserted(ViewContact& rChild) |
280 | 0 | { |
281 | | // force creation of the new VOC and trigger it's refresh, so it |
282 | | // will take part in LazyInvalidate immediately |
283 | 0 | rChild.GetViewObjectContact(GetObjectContact()).ActionChanged(); |
284 | | |
285 | | // forward action to ObjectContact |
286 | | // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact()); |
287 | | // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange()); |
288 | 0 | } |
289 | | |
290 | | // Check for animated primitives. |
291 | | void ViewObjectContact::checkForPrimitive2DAnimations() |
292 | 1.35k | { |
293 | | // Somewhat odd structure here, because we are trying to avoid deleting and re-allocating the mpPrimitiveAnimation |
294 | | // object, because we might be inside a call from the Scheduler, in which case we will be deleting an object |
295 | | // on the call stack. |
296 | | // Yes, this does still leave a possible situation where the user could turn animations off while we are |
297 | | // animating and we could crash. I don't have a better solution right now. |
298 | | // |
299 | 1.35k | drawinglayer::primitive2d::Primitive2DContainer aNewAnimatedPrimitives; |
300 | 1.35k | if(!mxPrimitive2DSequence.empty()) |
301 | 1.35k | { |
302 | 1.35k | const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed()); |
303 | 1.35k | const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed()); |
304 | | |
305 | 1.35k | if(bTextAnimationAllowed || bGraphicAnimationAllowed) |
306 | 1.35k | { |
307 | 1.35k | AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(), |
308 | 1.35k | bTextAnimationAllowed, bGraphicAnimationAllowed); |
309 | 1.35k | aAnimatedExtractor.process(mxPrimitive2DSequence); |
310 | | |
311 | 1.35k | if(!aAnimatedExtractor.getPrimitive2DSequence().empty()) |
312 | 0 | { |
313 | | // derived primitiveList is animated, setup new PrimitiveAnimation |
314 | 0 | aNewAnimatedPrimitives = aAnimatedExtractor.extractPrimitive2DSequence(); |
315 | 0 | } |
316 | 1.35k | } |
317 | 1.35k | } |
318 | 1.35k | if (!aNewAnimatedPrimitives.empty()) |
319 | 0 | { |
320 | | // derived primitiveList is animated, setup new PrimitiveAnimation |
321 | 0 | if (mpPrimitiveAnimation) |
322 | 0 | mpPrimitiveAnimation->SetPrimitives(std::move(aNewAnimatedPrimitives)); |
323 | 0 | else |
324 | 0 | mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, std::move(aNewAnimatedPrimitives)) ); |
325 | 0 | } |
326 | 1.35k | else |
327 | | // remove old one |
328 | 1.35k | mpPrimitiveAnimation.reset(); |
329 | 1.35k | } |
330 | | |
331 | | void ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const |
332 | 1.41k | { |
333 | | // get the view-independent Primitive from the viewContact |
334 | 1.41k | drawinglayer::primitive2d::Primitive2DContainer xRetval; |
335 | 1.41k | GetViewContact().getViewIndependentPrimitive2DContainer(xRetval); |
336 | | |
337 | 1.41k | if(!xRetval.empty()) |
338 | 1.11k | { |
339 | | // handle GluePoint |
340 | 1.11k | if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible()) |
341 | 0 | { |
342 | 0 | drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence()); |
343 | |
|
344 | 0 | if(!xGlue.empty()) |
345 | 0 | { |
346 | 0 | xRetval.append(std::move(xGlue)); |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | // handle ghosted |
351 | 1.11k | if(isPrimitiveGhosted(rDisplayInfo)) |
352 | 0 | { |
353 | 0 | basegfx::BColor aRGBWhite(1.0, 1.0, 1.0); |
354 | 0 | basegfx::BColorModifierSharedPtr aBColorModifier = |
355 | 0 | std::make_shared<basegfx::BColorModifier_interpolate>( |
356 | 0 | aRGBWhite, |
357 | 0 | 0.5); |
358 | 0 | xRetval = drawinglayer::primitive2d::Primitive2DContainer{ |
359 | 0 | new drawinglayer::primitive2d::ModifiedColorPrimitive2D( |
360 | 0 | std::move(xRetval), |
361 | 0 | std::move(aBColorModifier)) |
362 | 0 | }; |
363 | 0 | } |
364 | 1.11k | } |
365 | | |
366 | 1.41k | rVisitor.visit(xRetval); |
367 | 1.41k | } |
368 | | |
369 | | bool ViewObjectContact::isExportPDFTags() const |
370 | 1.58k | { |
371 | 1.58k | return GetObjectContact().isExportTaggedPDF(); |
372 | 1.58k | } |
373 | | |
374 | | /** Check if we need to embed to a StructureTagPrimitive2D, too. This |
375 | | was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before |
376 | | */ |
377 | | void ViewObjectContact::createStructureTag(drawinglayer::primitive2d::Primitive2DContainer & rNewPrimitiveSequence) const |
378 | 2.71k | { |
379 | 2.71k | SdrObject *const pSdrObj(mrViewContact.TryToGetSdrObject()); |
380 | | |
381 | | // Check if we need to embed to a StructureTagPrimitive2D, too. This |
382 | | // was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before |
383 | 2.71k | if (!rNewPrimitiveSequence.empty() && isExportPDFTags() |
384 | | // ISO 14289-1:2014, Clause: 7.3 |
385 | 0 | && (!pSdrObj || pSdrObj->getParentSdrObjectFromSdrObject() == nullptr)) |
386 | 0 | { |
387 | 0 | if (nullptr != pSdrObj && !pSdrObj->IsDecorative()) |
388 | 0 | { |
389 | 0 | vcl::pdf::StructElement eElement(vcl::pdf::StructElement::NonStructElement); |
390 | 0 | const SdrInventor nInventor(pSdrObj->GetObjInventor()); |
391 | 0 | const SdrObjKind nIdentifier(pSdrObj->GetObjIdentifier()); |
392 | 0 | const bool bIsTextObj(nullptr != DynCastSdrTextObj(pSdrObj)); |
393 | | |
394 | | // Note: SwFlyDrawObj/SwVirtFlyDrawObj have SdrInventor::Swg - these |
395 | | // are *not* handled here because not all of them are painted |
396 | | // completely with primitives, so a tag here does not encapsulate them. |
397 | | // The tag must be created by SwTaggedPDFHelper until this is fixed. |
398 | 0 | if ( nInventor == SdrInventor::Default ) |
399 | 0 | { |
400 | 0 | if ( nIdentifier == SdrObjKind::Group ) |
401 | 0 | eElement = vcl::pdf::StructElement::Figure; |
402 | 0 | else if (nIdentifier == SdrObjKind::Table) |
403 | 0 | eElement = vcl::pdf::StructElement::Table; |
404 | 0 | else if (nIdentifier == SdrObjKind::Media) |
405 | 0 | eElement = vcl::pdf::StructElement::Annot; |
406 | 0 | else if ( nIdentifier == SdrObjKind::TitleText ) |
407 | 0 | eElement = vcl::pdf::StructElement::Heading; |
408 | 0 | else if ( nIdentifier == SdrObjKind::OutlineText ) |
409 | 0 | eElement = vcl::pdf::StructElement::Division; |
410 | 0 | else if ( !bIsTextObj || !static_cast<const SdrTextObj&>(*pSdrObj).HasText() ) |
411 | 0 | eElement = vcl::pdf::StructElement::Figure; |
412 | 0 | else |
413 | 0 | eElement = vcl::pdf::StructElement::Division; |
414 | 0 | } |
415 | |
|
416 | 0 | if(vcl::pdf::StructElement::NonStructElement != eElement) |
417 | 0 | { |
418 | 0 | SdrPage* pSdrPage(pSdrObj->getSdrPageFromSdrObject()); |
419 | |
|
420 | 0 | if(pSdrPage) |
421 | 0 | { |
422 | 0 | const bool bBackground(pSdrPage->IsMasterPage()); |
423 | 0 | const bool bImage(SdrObjKind::Graphic == pSdrObj->GetObjIdentifier()); |
424 | | // note: there must be output device here, in PDF export |
425 | 0 | void const* pAnchorKey(nullptr); |
426 | 0 | if (auto const pUserCall = pSdrObj->GetUserCall()) |
427 | 0 | { |
428 | 0 | pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(*pSdrObj); |
429 | 0 | } |
430 | |
|
431 | 0 | ::std::vector<sal_Int32> annotIds; |
432 | 0 | if (eElement == vcl::pdf::StructElement::Annot |
433 | 0 | && !static_cast<SdrMediaObj*>(pSdrObj)->getURL().isEmpty()) |
434 | 0 | { |
435 | 0 | auto const pPDFExtOutDevData(GetObjectContact().GetPDFExtOutDevData()); |
436 | 0 | assert(pPDFExtOutDevData); |
437 | 0 | annotIds = pPDFExtOutDevData->GetScreenAnnotIds(pSdrObj); |
438 | 0 | } |
439 | |
|
440 | 0 | rNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { |
441 | 0 | new drawinglayer::primitive2d::StructureTagPrimitive2D( |
442 | 0 | eElement, |
443 | 0 | bBackground, |
444 | 0 | bImage, |
445 | 0 | false, // Decorative |
446 | 0 | std::move(rNewPrimitiveSequence), |
447 | 0 | pAnchorKey, |
448 | 0 | &annotIds) |
449 | 0 | }; |
450 | 0 | } |
451 | 0 | } |
452 | 0 | } |
453 | 0 | else |
454 | 0 | { |
455 | | // page backgrounds etc should be tagged as artifacts: |
456 | 0 | rNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { |
457 | 0 | new drawinglayer::primitive2d::StructureTagPrimitive2D( |
458 | | // lies to force silly VclMetafileProcessor2D to emit NonStructElement |
459 | 0 | vcl::pdf::StructElement::Division, |
460 | 0 | true, |
461 | 0 | true, |
462 | 0 | true, // Decorative |
463 | 0 | std::move(rNewPrimitiveSequence)) |
464 | 0 | }; |
465 | 0 | } |
466 | 0 | } |
467 | 2.71k | } |
468 | | |
469 | | drawinglayer::primitive2d::Primitive2DContainer const & ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const |
470 | 2.38k | { |
471 | | // only some of the top-level apps are any good at reliably invalidating us (e.g. writer is not) |
472 | 2.38k | SdrObject* pSdrObj(mrViewContact.TryToGetSdrObject()); |
473 | | |
474 | 2.38k | if (nullptr != pSdrObj && pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable()) |
475 | 773 | { |
476 | 773 | if (!mxPrimitive2DSequence.empty()) |
477 | 0 | return mxPrimitive2DSequence; |
478 | 773 | } |
479 | | |
480 | | // prepare new representation |
481 | 2.38k | drawinglayer::primitive2d::Primitive2DContainer xNewPrimitiveSequence; |
482 | | |
483 | | // take care of redirectors and create new list |
484 | 2.38k | ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector(); |
485 | | |
486 | 2.38k | if(pRedirector) |
487 | 2.29k | { |
488 | 2.29k | pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo, xNewPrimitiveSequence); |
489 | 2.29k | } |
490 | 86 | else |
491 | 86 | { |
492 | 86 | createPrimitive2DSequence(rDisplayInfo, xNewPrimitiveSequence); |
493 | 86 | } |
494 | | |
495 | | // check and eventually embed to GridOffset transform primitive (calc only) |
496 | 2.38k | if(!xNewPrimitiveSequence.empty() && GetObjectContact().supportsGridOffsets()) |
497 | 0 | { |
498 | 0 | const basegfx::B2DVector& rGridOffset(getGridOffset()); |
499 | |
|
500 | 0 | if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY()) |
501 | 0 | { |
502 | 0 | const basegfx::B2DHomMatrix aTranslateGridOffset( |
503 | 0 | basegfx::utils::createTranslateB2DHomMatrix( |
504 | 0 | rGridOffset)); |
505 | 0 | xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { |
506 | 0 | new drawinglayer::primitive2d::TransformPrimitive2D( |
507 | 0 | aTranslateGridOffset, |
508 | 0 | std::move(xNewPrimitiveSequence)) |
509 | 0 | }; |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | 2.38k | createStructureTag(xNewPrimitiveSequence); |
514 | | |
515 | | // Local up-to-date checks. New list different from local one? |
516 | | // This is the important point where it gets decided if the current or the new |
517 | | // representation gets used. This is important for performance, since the |
518 | | // current representation contains possible precious decompositions. That |
519 | | // comparisons triggers exactly if something in the object visualization |
520 | | // has changed. |
521 | | // Note: That is the main reason for BasePrimitive2D::operator== at all. I |
522 | | // have alternatively tried to invalidate the local representation on object |
523 | | // change, but that is simply not reliable. |
524 | | // Note2: I did that once in aw080, the lost CWS, and it worked well enough |
525 | | // so that I could remove *all* operator== from all derivations of |
526 | | // BasePrimitive2D, so it can be done again (with the needed resources) |
527 | 2.38k | if(mxPrimitive2DSequence != xNewPrimitiveSequence) |
528 | 1.35k | { |
529 | | // has changed, copy content |
530 | 1.35k | const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence); |
531 | | |
532 | | // check for animated stuff |
533 | 1.35k | const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations(); |
534 | | |
535 | | // always update object range when PrimitiveSequence changes |
536 | 1.35k | const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); |
537 | 1.35k | const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D); |
538 | 1.35k | } |
539 | | |
540 | | // return current Primitive2DContainer |
541 | 2.38k | return mxPrimitive2DSequence; |
542 | 2.38k | } |
543 | | |
544 | | bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const |
545 | 0 | { |
546 | | // default: always visible |
547 | 0 | return true; |
548 | 0 | } |
549 | | |
550 | | bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const |
551 | 1.11k | { |
552 | | // default: standard check |
553 | 1.11k | return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive()); |
554 | 1.11k | } |
555 | | |
556 | | void ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const |
557 | 60.7k | { |
558 | | // check model-view visibility |
559 | 60.7k | if(!isPrimitiveVisible(rDisplayInfo)) |
560 | 58.4k | return; |
561 | | |
562 | 2.28k | getPrimitive2DSequence(rDisplayInfo); |
563 | 2.28k | if(mxPrimitive2DSequence.empty()) |
564 | 1.02k | return; |
565 | | |
566 | | // get ranges |
567 | 1.26k | const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); |
568 | | // tdf#147164 cannot use maObjectRange here, it is unreliable |
569 | 1.26k | const basegfx::B2DRange aObjectRange(mxPrimitive2DSequence.getB2DRange(rViewInformation2D)); |
570 | 1.26k | const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport()); |
571 | | |
572 | | // check geometrical visibility |
573 | 1.26k | bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange); |
574 | 1.26k | if(!bVisible) |
575 | 123 | return; |
576 | | |
577 | | // temporarily take over the mxPrimitive2DSequence, in case it gets invalidated while we want to iterate over it |
578 | 1.13k | auto tmp = std::move(const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence); |
579 | 1.13k | int nPrevCount = mnActionChangedCount; |
580 | | |
581 | 1.13k | rVisitor.visit(tmp); |
582 | | |
583 | | // if we received ActionChanged() calls while walking the primitives, then leave it empty, otherwise move it back |
584 | 1.13k | if (mnActionChangedCount == nPrevCount) |
585 | 1.13k | const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence = std::move(tmp); |
586 | 1.13k | } |
587 | | |
588 | | void ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const |
589 | 2.52k | { |
590 | 2.52k | ViewContact& rViewContact = GetViewContact(); |
591 | 2.52k | const sal_uInt32 nSubHierarchyCount(rViewContact.GetObjectCount()); |
592 | | |
593 | 64.3k | for(sal_uInt32 a(0); a < nSubHierarchyCount; a++) |
594 | 61.8k | rViewContact.getPrimitive2DSequenceHierarchyOfIndex(a, rDisplayInfo, GetObjectContact(), rVisitor); |
595 | 2.52k | } |
596 | | |
597 | | // Support getting a GridOffset per object and view for non-linear ViewToDevice |
598 | | // transformation (calc). On-demand created by delegating to the ObjectContact |
599 | | // (->View) that has then all needed information |
600 | | const basegfx::B2DVector& ViewObjectContact::getGridOffset() const |
601 | 0 | { |
602 | 0 | static const basegfx::B2DVector EMPTY(0.0, 0.0); |
603 | 0 | if (!GetObjectContact().supportsGridOffsets()) |
604 | 0 | return EMPTY; |
605 | | |
606 | 0 | if (moGridOffset.has_value() && fabs(moGridOffset->getX()) > 1000.0) |
607 | 0 | { |
608 | | // Huge offsets are a hint for error -> usually the conditions for |
609 | | // calculation have changed. E.g. - I saw errors with +/-5740, that |
610 | | // was in the environment of massive external UNO API using LO as |
611 | | // target. |
612 | | // If conditions for this calculation change, it is usually required to call |
613 | | // - ViewObjectContact::resetGridOffset(), or |
614 | | // - ObjectContact::resetAllGridOffsets() or |
615 | | // - ScDrawView::resetGridOffsetsForAllSdrPageViews() |
616 | | // as it is done e.g. when zoom changes (see ScDrawView::RecalcScale()). |
617 | | // Theoretically these resets have to be done for any precondition |
618 | | // changed that is used in the calculation of that value (see |
619 | | // ScDrawView::calculateGridOffsetForSdrObject). |
620 | | // This is not complete and would be hard to do so. |
621 | | // Since it is just a buffered value and re-calculation is not |
622 | | // expensive (linear O(n)) we can just reset suspicious values here. |
623 | | // Hopefully - when that non-linear ViewTransformation problem for |
624 | | // the calc-view gets solved one day - all this can be removed |
625 | | // again. For now, let's just reset here and force re-calculation. |
626 | | // Add a SAL_WARN to inform about this. |
627 | 0 | SAL_WARN("svx", "Suspicious GridOffset value resetted (!)"); |
628 | 0 | moGridOffset.reset(); |
629 | 0 | } |
630 | | |
631 | 0 | if(!moGridOffset) |
632 | 0 | { |
633 | | // create on-demand |
634 | 0 | moGridOffset.emplace(); |
635 | 0 | GetObjectContact().calculateGridOffsetForViewObjectContact(*moGridOffset, *this); |
636 | 0 | } |
637 | |
|
638 | 0 | return *moGridOffset; |
639 | 0 | } |
640 | | |
641 | | void ViewObjectContact::resetGridOffset() |
642 | 0 | { |
643 | | // reset buffered GridOffset itself |
644 | 0 | moGridOffset.reset(); |
645 | | |
646 | | // also reset sequence to get a re-calculation when GridOffset changes |
647 | 0 | mxPrimitive2DSequence.clear(); |
648 | 0 | maObjectRange.reset(); |
649 | 0 | } |
650 | | |
651 | | } |
652 | | |
653 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |