1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: sdrdecompositiontools.cxx,v $
11 * $Revision: 1.2.18.1 $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 #include "precompiled_svx.hxx"
33 #include <svx/sdr/primitive2d/sdrdecompositiontools.hxx>
34 #include <svx/sdr/attribute/sdrallattribute.hxx>
35 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
36 #include <drawinglayer/attribute/sdrattribute.hxx>
37 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/unifiedalphaprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/alphaprimitive2d.hxx>
40 #include <basegfx/polygon/b2dpolypolygontools.hxx>
41 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
42 #include <drawinglayer/attribute/strokeattribute.hxx>
43 #include <drawinglayer/attribute/linestartendattribute.hxx>
44 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
45 #include <drawinglayer/attribute/sdrfillbitmapattribute.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
48 #include <svx/sdr/attribute/sdrtextattribute.hxx>
49 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
50 #include <svx/svdotext.hxx>
51 #include <basegfx/polygon/b2dpolygontools.hxx>
52 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
53 #include <drawinglayer/animation/animationtiming.hxx>
54 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
55 #include <basegfx/tools/canvastools.hxx>
56 #include <drawinglayer/geometry/viewinformation2d.hxx>
57 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
59 //////////////////////////////////////////////////////////////////////////////
61 using namespace com::sun::star
;
63 //////////////////////////////////////////////////////////////////////////////
65 namespace drawinglayer
69 Primitive2DReference
createPolyPolygonFillPrimitive(
70 const basegfx::B2DPolyPolygon
& rUnitPolyPolygon
,
71 const basegfx::B2DHomMatrix
& rObjectTransform
,
72 const attribute::SdrFillAttribute
& rFill
,
73 const attribute::FillGradientAttribute
* pFillGradient
)
75 // prepare fully scaled polygon
76 basegfx::B2DPolyPolygon
aScaledPolyPolygon(rUnitPolyPolygon
);
77 aScaledPolyPolygon
.transform(rObjectTransform
);
78 BasePrimitive2D
* pNewFillPrimitive
= 0L;
80 if(rFill
.isGradient())
82 pNewFillPrimitive
= new PolyPolygonGradientPrimitive2D(aScaledPolyPolygon
, rFill
.getColor(), *rFill
.getGradient());
84 else if(rFill
.isHatch())
86 pNewFillPrimitive
= new PolyPolygonHatchPrimitive2D(aScaledPolyPolygon
, rFill
.getColor(), *rFill
.getHatch());
88 else if(rFill
.isBitmap())
90 const basegfx::B2DRange
aRange(basegfx::tools::getRange(aScaledPolyPolygon
));
91 pNewFillPrimitive
= new PolyPolygonBitmapPrimitive2D(aScaledPolyPolygon
, rFill
.getColor(), rFill
.getBitmap()->getFillBitmapAttribute(aRange
));
95 pNewFillPrimitive
= new PolyPolygonColorPrimitive2D(aScaledPolyPolygon
, rFill
.getColor());
98 if(0.0 != rFill
.getTransparence())
100 // create simpleTransparencePrimitive, add created fill primitive
101 const Primitive2DReference
xRefA(pNewFillPrimitive
);
102 const Primitive2DSequence
aContent(&xRefA
, 1L);
103 return Primitive2DReference(new UnifiedAlphaPrimitive2D(aContent
, rFill
.getTransparence()));
105 else if(pFillGradient
)
107 // create sequence with created fill primitive
108 const Primitive2DReference
xRefA(pNewFillPrimitive
);
109 const Primitive2DSequence
aContent(&xRefA
, 1L);
111 // create FillGradientPrimitive2D for transparence and add to new sequence
112 // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways
113 const basegfx::B2DRange
aRange(basegfx::tools::getRange(aScaledPolyPolygon
));
114 const Primitive2DReference
xRefB(new FillGradientPrimitive2D(aRange
, *pFillGradient
));
115 const Primitive2DSequence
aAlpha(&xRefB
, 1L);
117 // create AlphaPrimitive2D using alpha and content
118 return Primitive2DReference(new AlphaPrimitive2D(aContent
, aAlpha
));
122 // add to decomposition
123 return Primitive2DReference(pNewFillPrimitive
);
127 Primitive2DReference
createPolygonLinePrimitive(
128 const basegfx::B2DPolygon
& rUnitPolygon
,
129 const basegfx::B2DHomMatrix
& rObjectTransform
,
130 const attribute::SdrLineAttribute
& rLine
,
131 const attribute::SdrLineStartEndAttribute
* pStroke
)
133 // prepare fully scaled polygon
134 basegfx::B2DPolygon
aScaledPolygon(rUnitPolygon
);
135 aScaledPolygon
.transform(rObjectTransform
);
137 // create line and stroke attribute
138 const attribute::LineAttribute
aLineAttribute(rLine
.getColor(), rLine
.getWidth(), rLine
.getJoin());
139 const attribute::StrokeAttribute
aStrokeAttribute(rLine
.getDotDashArray(), rLine
.getFullDotDashLen());
140 BasePrimitive2D
* pNewLinePrimitive
= 0L;
142 if(!rUnitPolygon
.isClosed() && pStroke
)
144 attribute::LineStartEndAttribute
aStart(pStroke
->getStartWidth(), pStroke
->getStartPolyPolygon(), pStroke
->isStartCentered());
145 attribute::LineStartEndAttribute
aEnd(pStroke
->getEndWidth(), pStroke
->getEndPolyPolygon(), pStroke
->isEndCentered());
148 pNewLinePrimitive
= new PolygonStrokeArrowPrimitive2D(aScaledPolygon
, aLineAttribute
, aStrokeAttribute
, aStart
, aEnd
);
153 pNewLinePrimitive
= new PolygonStrokePrimitive2D(aScaledPolygon
, aLineAttribute
, aStrokeAttribute
);
156 if(0.0 != rLine
.getTransparence())
158 // create simpleTransparencePrimitive, add created fill primitive
159 const Primitive2DReference
xRefA(pNewLinePrimitive
);
160 const Primitive2DSequence
aContent(&xRefA
, 1L);
161 return Primitive2DReference(new UnifiedAlphaPrimitive2D(aContent
, rLine
.getTransparence()));
165 // add to decomposition
166 return Primitive2DReference(pNewLinePrimitive
);
170 Primitive2DReference
createTextPrimitive(
171 const basegfx::B2DPolyPolygon
& rUnitPolyPolygon
,
172 const basegfx::B2DHomMatrix
& rObjectTransform
,
173 const attribute::SdrTextAttribute
& rText
,
174 const attribute::SdrLineAttribute
* pStroke
,
178 basegfx::B2DHomMatrix
aAnchorTransform(rObjectTransform
);
179 SdrTextPrimitive2D
* pNew
= 0;
181 if(rText
.isContour())
184 if(pStroke
&& 0.0 != pStroke
->getWidth())
186 // take line width into account and shrink contour polygon accordingly
187 // decompose to get scale
188 basegfx::B2DVector aScale
, aTranslate
;
189 double fRotate
, fShearX
;
190 rObjectTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
192 // scale outline to object's size to allow growing with value relative to that size
193 // and also to keep aspect ratio
194 basegfx::B2DHomMatrix aScaleTransform
;
195 aScaleTransform
.set(0, 0, fabs(aScale
.getX()));
196 aScaleTransform
.set(1, 1, fabs(aScale
.getY()));
197 basegfx::B2DPolyPolygon
aScaledUnitPolyPolygon(rUnitPolyPolygon
);
198 aScaledUnitPolyPolygon
.transform(aScaleTransform
);
200 // grow the polygon. To shrink, use negative value (half width)
201 aScaledUnitPolyPolygon
= basegfx::tools::growInNormalDirection(aScaledUnitPolyPolygon
, -(pStroke
->getWidth() * 0.5));
203 // scale back to unit polygon
204 aScaleTransform
.set(0, 0, 0.0 != aScale
.getX() ? 1.0 / aScale
.getX() : 1.0);
205 aScaleTransform
.set(1, 1, 0.0 != aScale
.getY() ? 1.0 / aScale
.getY() : 1.0);
206 aScaledUnitPolyPolygon
.transform(aScaleTransform
);
208 // create with unit polygon
209 pNew
= new SdrContourTextPrimitive2D(
211 rText
.getOutlinerParaObject(),
212 aScaledUnitPolyPolygon
,
217 // create with unit polygon
218 pNew
= new SdrContourTextPrimitive2D(
220 rText
.getOutlinerParaObject(),
225 else if(rText
.getSdrFormTextAttribute())
227 // text on path, use scaled polygon
228 basegfx::B2DPolyPolygon
aScaledPolyPolygon(rUnitPolyPolygon
);
229 aScaledPolyPolygon
.transform(rObjectTransform
);
230 pNew
= new SdrPathTextPrimitive2D(
232 rText
.getOutlinerParaObject(),
234 *rText
.getSdrFormTextAttribute());
238 // rObjectTransform is the whole SdrObject transformation from unit rectangle
239 // to it's size and position. Decompose to allow working with single values.
240 basegfx::B2DVector aScale
, aTranslate
;
241 double fRotate
, fShearX
;
242 rObjectTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
245 const bool bMirrorX(basegfx::fTools::less(aScale
.getX(), 0.0));
246 const bool bMirrorY(basegfx::fTools::less(aScale
.getY(), 0.0));
247 aScale
= basegfx::absolute(aScale
);
249 // Get the real size, since polygon ountline and scale
250 // from the object transformation may vary (e.g. ellipse segments)
251 basegfx::B2DHomMatrix aJustScaleTransform
;
252 aJustScaleTransform
.set(0, 0, aScale
.getX());
253 aJustScaleTransform
.set(1, 1, aScale
.getY());
254 basegfx::B2DPolyPolygon
aScaledUnitPolyPolygon(rUnitPolyPolygon
);
255 aScaledUnitPolyPolygon
.transform(aJustScaleTransform
);
256 const basegfx::B2DRange
aSnapRange(basegfx::tools::getRange(aScaledUnitPolyPolygon
));
258 // create a range describing the wanted text position and size (aTextAnchorRange). This
259 // means to use the text distance values here
260 const basegfx::B2DPoint
aTopLeft(aSnapRange
.getMinX() + rText
.getTextLeftDistance(), aSnapRange
.getMinY() + rText
.getTextUpperDistance());
261 const basegfx::B2DPoint
aBottomRight(aSnapRange
.getMaxX() - rText
.getTextRightDistance(), aSnapRange
.getMaxY() - rText
.getTextLowerDistance());
262 basegfx::B2DRange aTextAnchorRange
;
263 aTextAnchorRange
.expand(aTopLeft
);
264 aTextAnchorRange
.expand(aBottomRight
);
266 // now create a transformation from this basic range (aTextAnchorRange)
267 aAnchorTransform
.identity();
268 aAnchorTransform
.scale(aTextAnchorRange
.getWidth(), aTextAnchorRange
.getHeight());
269 aAnchorTransform
.translate(aTextAnchorRange
.getMinX(), aTextAnchorRange
.getMinY());
272 aAnchorTransform
.scale(bMirrorX
? -1.0 : 1.0, bMirrorY
? -1.0 : 1.0);
274 // apply object's other transforms
275 aAnchorTransform
.shearX(fShearX
);
276 aAnchorTransform
.rotate(fRotate
);
277 aAnchorTransform
.translate(aTranslate
.getX(), aTranslate
.getY());
279 if(rText
.isFitToSize())
281 // streched text in range
282 pNew
= new SdrStretchTextPrimitive2D(
284 rText
.getOutlinerParaObject(),
286 rText
.isFixedCellHeight());
288 else if(rText
.isAutoFit())
290 // isotrophically scaled text in range
291 pNew
= new SdrAutoFitTextPrimitive2D(&rText
.getSdrText(), rText
.getOutlinerParaObject(), aAnchorTransform
, bWordWrap
);
293 else // text in range
295 // build new primitive
296 pNew
= new SdrBlockTextPrimitive2D(
298 rText
.getOutlinerParaObject(),
300 rText
.getSdrTextHorzAdjust(),
301 rText
.getSdrTextVertAdjust(),
302 rText
.isFixedCellHeight(),
309 OSL_ENSURE(pNew
!= 0, "createTextPrimitive: no text primitive created (!)");
313 // prepare animation and primitive list
314 drawinglayer::animation::AnimationEntryList aAnimationList
;
315 rText
.getBlinkTextTiming(aAnimationList
);
317 if(0.0 != aAnimationList
.getDuration())
319 // create content sequence
320 const Primitive2DReference
xRefA(pNew
);
321 const Primitive2DSequence
aContent(&xRefA
, 1L);
323 // create and add animated switch primitive
324 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList
, aContent
, true));
328 // add to decomposition
329 return Primitive2DReference(pNew
);
335 // suppress scroll when FontWork
336 if(!rText
.getSdrFormTextAttribute())
338 // get scroll direction
339 const SdrTextAniDirection
eDirection(rText
.getSdrText().GetObject().GetTextAniDirection());
340 const bool bHorizontal(SDRTEXTANI_LEFT
== eDirection
|| SDRTEXTANI_RIGHT
== eDirection
);
342 // decompose to get separated values for the scroll box
343 basegfx::B2DVector aScale
, aTranslate
;
344 double fRotate
, fShearX
;
345 aAnchorTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
347 // build transform from scaled only to full AnchorTransform and inverse
348 basegfx::B2DHomMatrix aSRT
;
349 aSRT
.shearX(fShearX
);
350 aSRT
.rotate(fRotate
);
351 aSRT
.translate(aTranslate
.getX(), aTranslate
.getY());
352 basegfx::B2DHomMatrix
aISRT(aSRT
);
355 // bring the primitive back to scaled only and get scaled range, create new clone for this
356 SdrTextPrimitive2D
* pNew2
= pNew
->createTransformedClone(aISRT
);
357 OSL_ENSURE(pNew2
, "createTextPrimitive: Could not create transformed clone of text primitive (!)");
361 // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay
362 // since the decompose is view-independent
363 const uno::Sequence
< beans::PropertyValue
> xViewParameters
;
364 geometry::ViewInformation2D
aViewInformation2D(xViewParameters
);
367 const basegfx::B2DRange
aScaledRange(pNew
->getB2DRange(aViewInformation2D
));
369 // create left outside and right outside transformations. Also take care
370 // of the clip rectangle
371 basegfx::B2DHomMatrix aLeft
, aRight
;
372 basegfx::B2DPoint
aClipTopLeft(0.0, 0.0);
373 basegfx::B2DPoint
aClipBottomRight(aScale
.getX(), aScale
.getY());
377 aClipTopLeft
.setY(aScaledRange
.getMinY());
378 aClipBottomRight
.setY(aScaledRange
.getMaxY());
379 aLeft
.translate(-aScaledRange
.getMaxX(), 0.0);
380 aRight
.translate(aScale
.getX() - aScaledRange
.getMinX(), 0.0);
384 aClipTopLeft
.setX(aScaledRange
.getMinX());
385 aClipBottomRight
.setX(aScaledRange
.getMaxX());
386 aLeft
.translate(0.0, -aScaledRange
.getMaxY());
387 aRight
.translate(0.0, aScale
.getY() - aScaledRange
.getMinY());
393 // prepare animation list
394 drawinglayer::animation::AnimationEntryList aAnimationList
;
398 rText
.getScrollTextTiming(aAnimationList
, aScale
.getX(), aScaledRange
.getWidth());
402 rText
.getScrollTextTiming(aAnimationList
, aScale
.getY(), aScaledRange
.getHeight());
405 if(0.0 != aAnimationList
.getDuration())
407 // create a new Primitive2DSequence containing the animated text in it's scaled only state.
408 // use the decomposition to force to simple text primitives, those will no longer
409 // need the outliner for formatting (alternatively it is also possible to just add
410 // pNew to aNewPrimitiveSequence)
411 Primitive2DSequence
aAnimSequence(pNew
->get2DDecomposition(aViewInformation2D
));
414 // create a new animatedInterpolatePrimitive and add it
415 std::vector
< basegfx::B2DHomMatrix
> aMatrixStack
;
416 aMatrixStack
.push_back(aLeft
);
417 aMatrixStack
.push_back(aRight
);
418 const Primitive2DReference
xRefA(new AnimatedInterpolatePrimitive2D(aMatrixStack
, aAnimationList
, aAnimSequence
, true));
419 const Primitive2DSequence
aContent(&xRefA
, 1L);
421 // scrolling needs an encapsulating clipping primitive
422 const basegfx::B2DRange
aClipRange(aClipTopLeft
, aClipBottomRight
);
423 basegfx::B2DPolygon
aClipPolygon(basegfx::tools::createPolygonFromRect(aClipRange
));
424 aClipPolygon
.transform(aSRT
);
425 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon
), aContent
));
429 // add to decomposition
430 return Primitive2DReference(pNew
);
435 if(rText
.isInEditMode())
438 // encapsulate with TextHierarchyEditPrimitive2D to allow renderers
439 // to suppress actively edited content if needed
440 const Primitive2DReference
xRefA(pNew
);
441 const Primitive2DSequence
aContent(&xRefA
, 1L);
443 // create and add TextHierarchyEditPrimitive2D primitive
444 return Primitive2DReference(new TextHierarchyEditPrimitive2D(aContent
));
448 // add to decomposition
449 return Primitive2DReference(pNew
);
453 Primitive2DSequence
createEmbeddedShadowPrimitive(
454 const Primitive2DSequence
& rContent
,
455 const attribute::SdrShadowAttribute
& rShadow
)
457 if(rContent
.hasElements())
459 Primitive2DSequence
aRetval(2);
460 basegfx::B2DHomMatrix aShadowOffset
;
462 // prepare shadow offset
463 aShadowOffset
.set(0, 2, rShadow
.getOffset().getX());
464 aShadowOffset
.set(1, 2, rShadow
.getOffset().getY());
466 // create shadow primitive and add content
467 aRetval
[0] = Primitive2DReference(
468 new ShadowPrimitive2D(
473 if(0.0 != rShadow
.getTransparence())
475 // create SimpleTransparencePrimitive2D
476 const Primitive2DSequence
aTempContent(&aRetval
[0], 1);
478 aRetval
[0] = Primitive2DReference(
479 new UnifiedAlphaPrimitive2D(
481 rShadow
.getTransparence()));
484 aRetval
[1] = Primitive2DReference(new GroupPrimitive2D(rContent
));
492 } // end of namespace primitive2d
493 } // end of namespace drawinglayer
495 //////////////////////////////////////////////////////////////////////////////