1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
21 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
22 #include <basegfx/polygon/b2dpolypolygon.hxx>
23 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
25 #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx>
26 #include <basegfx/matrix/b2dhommatrixtools.hxx>
28 //////////////////////////////////////////////////////////////////////////////
30 using namespace com::sun::star
;
32 //////////////////////////////////////////////////////////////////////////////
36 // adapts fontScale for usage with TextLayouter. Input is rScale which is the extracted
37 // scale from a text transformation. A copy is modified so that it contains only positive
38 // scalings and XY-equal scalings to allow to get a non-X-scaled Vcl-Font for TextLayouter.
39 // rScale is adapted accordingly to contain the corrected scale which would need to be
40 // applied to e.g. outlines received from TextLayouter under usage of fontScale. This
41 // includes Y-Scale, X-Scale-correction and mirrorings.
42 basegfx::B2DVector
getCorrectedScaleAndFontScale(basegfx::B2DVector
& rScale
)
45 basegfx::B2DVector
aFontScale(rScale
);
47 // correct FontHeight settings
48 if(basegfx::fTools::equalZero(aFontScale
.getY()))
50 // no font height; choose one and adapt scale to get back to original scaling
51 static double fDefaultFontScale(100.0);
52 rScale
.setY(1.0 / fDefaultFontScale
);
53 aFontScale
.setY(fDefaultFontScale
);
55 else if(basegfx::fTools::less(aFontScale
.getY(), 0.0))
57 // negative font height; invert and adapt scale to get back to original scaling
58 aFontScale
.setY(-aFontScale
.getY());
63 // positive font height; adapt scale; scaling will be part of the polygons
67 // correct FontWidth settings
68 if(basegfx::fTools::equal(aFontScale
.getX(), aFontScale
.getY()))
70 // no FontScale, adapt scale
75 // If FontScale is used, force to no FontScale to get a non-scaled VCL font.
76 // Adapt scaling in X accordingly.
77 rScale
.setX(aFontScale
.getX() / aFontScale
.getY());
78 aFontScale
.setX(aFontScale
.getY());
83 } // end of anonymous namespace
85 //////////////////////////////////////////////////////////////////////////////
87 namespace drawinglayer
91 void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector
& rTarget
, basegfx::B2DHomMatrix
& rTransformation
) const
95 // decompose object transformation to single values
96 basegfx::B2DVector aScale
, aTranslate
;
97 double fRotate
, fShearX
;
99 // if decomposition returns false, create no geometry since e.g. scaling may
101 if(getTextTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
))
103 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
104 // be expressed as rotation by PI
105 if(basegfx::fTools::less(aScale
.getX(), 0.0) && basegfx::fTools::less(aScale
.getY(), 0.0))
107 aScale
= basegfx::absolute(aScale
);
111 // for the TextLayouterDevice, it is necessary to have a scaling representing
112 // the font size. Since we want to extract polygons here, it is okay to
113 // work just with scaling and to ignore shear, rotation and translation,
114 // all that can be applied to the polygons later
115 const basegfx::B2DVector
aFontScale(getCorrectedScaleAndFontScale(aScale
));
117 // prepare textlayoutdevice
118 TextLayouterDevice aTextLayouter
;
119 aTextLayouter
.setFontAttribute(
125 // When getting outlines from stretched text (aScale.getX() != 1.0) it
126 // is necessary to inverse-scale the DXArray (if used) to not get the
127 // outlines already aligned to given, but wrong DXArray
128 if(getDXArray().size() && !basegfx::fTools::equal(aScale
.getX(), 1.0))
130 ::std::vector
< double > aScaledDXArray
= getDXArray();
131 const double fDXArrayScale(1.0 / aScale
.getX());
133 for(sal_uInt32
a(0); a
< aScaledDXArray
.size(); a
++)
135 aScaledDXArray
[a
] *= fDXArrayScale
;
138 // get the text outlines
139 aTextLayouter
.getTextOutlines(
148 // get the text outlines
149 aTextLayouter
.getTextOutlines(
157 // create primitives for the outlines
158 const sal_uInt32
nCount(rTarget
.size());
162 // prepare object transformation for polygons
163 rTransformation
= basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
164 aScale
, fShearX
, fRotate
, aTranslate
);
170 Primitive2DSequence
TextSimplePortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
172 Primitive2DSequence aRetval
;
176 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector
;
177 basegfx::B2DHomMatrix aPolygonTransform
;
179 // get text outlines and their object transformation
180 getTextOutlinesAndTransformation(aB2DPolyPolyVector
, aPolygonTransform
);
182 // create primitives for the outlines
183 const sal_uInt32
nCount(aB2DPolyPolyVector
.size());
187 // alloc space for the primitives
188 aRetval
.realloc(nCount
);
190 // color-filled polypolygons
191 for(sal_uInt32
a(0L); a
< nCount
; a
++)
193 // prepare polypolygon
194 basegfx::B2DPolyPolygon
& rPolyPolygon
= aB2DPolyPolyVector
[a
];
195 rPolyPolygon
.transform(aPolygonTransform
);
196 aRetval
[a
] = new PolyPolygonColorPrimitive2D(rPolyPolygon
, getFontColor());
199 if(getFontAttribute().getOutline())
201 // decompose polygon transformation to single values
202 basegfx::B2DVector aScale
, aTranslate
;
203 double fRotate
, fShearX
;
204 aPolygonTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
206 // create outline text effect with current content and replace
207 Primitive2DReference
aNewTextEffect(new TextEffectPrimitive2D(
211 TEXTEFFECTSTYLE2D_OUTLINE
));
213 aRetval
= Primitive2DSequence(&aNewTextEffect
, 1);
221 TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
222 const basegfx::B2DHomMatrix
& rNewTransform
,
224 xub_StrLen aTextPosition
,
225 xub_StrLen aTextLength
,
226 const ::std::vector
< double >& rDXArray
,
227 const attribute::FontAttribute
& rFontAttribute
,
228 const ::com::sun::star::lang::Locale
& rLocale
,
229 const basegfx::BColor
& rFontColor
,
232 : BufferedDecompositionPrimitive2D(),
233 maTextTransform(rNewTransform
),
235 maTextPosition(aTextPosition
),
236 maTextLength(aTextLength
),
238 maFontAttribute(rFontAttribute
),
240 maFontColor(rFontColor
),
243 mnWidthToFill(nWidthToFill
)
245 #if OSL_DEBUG_LEVEL > 0
246 const xub_StrLen
aStringLength(getText().Len());
247 OSL_ENSURE(aStringLength
>= getTextPosition() && aStringLength
>= getTextPosition() + getTextLength(),
248 "TextSimplePortionPrimitive2D with text out of range (!)");
252 bool LocalesAreEqual(const ::com::sun::star::lang::Locale
& rA
, const ::com::sun::star::lang::Locale
& rB
)
254 return (rA
.Language
== rB
.Language
255 && rA
.Country
== rB
.Country
256 && rA
.Variant
== rB
.Variant
);
259 bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
261 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
263 const TextSimplePortionPrimitive2D
& rCompare
= (TextSimplePortionPrimitive2D
&)rPrimitive
;
265 return (getTextTransform() == rCompare
.getTextTransform()
266 && getText() == rCompare
.getText()
267 && getTextPosition() == rCompare
.getTextPosition()
268 && getTextLength() == rCompare
.getTextLength()
269 && getDXArray() == rCompare
.getDXArray()
270 && getFontAttribute() == rCompare
.getFontAttribute()
271 && LocalesAreEqual(getLocale(), rCompare
.getLocale())
272 && getFontColor() == rCompare
.getFontColor()
273 && mbFilled
== rCompare
.mbFilled
274 && mnWidthToFill
== rCompare
.mnWidthToFill
);
280 basegfx::B2DRange
TextSimplePortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
282 if(maB2DRange
.isEmpty() && getTextLength())
284 // get TextBoundRect as base size
285 // decompose object transformation to single values
286 basegfx::B2DVector aScale
, aTranslate
;
287 double fRotate
, fShearX
;
289 if(getTextTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
))
291 // for the TextLayouterDevice, it is necessary to have a scaling representing
292 // the font size. Since we want to extract polygons here, it is okay to
293 // work just with scaling and to ignore shear, rotation and translation,
294 // all that can be applied to the polygons later
295 const basegfx::B2DVector
aFontScale(getCorrectedScaleAndFontScale(aScale
));
297 // prepare textlayoutdevice
298 TextLayouterDevice aTextLayouter
;
299 aTextLayouter
.setFontAttribute(
305 // get basic text range
306 basegfx::B2DRange
aNewRange(aTextLayouter
.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
308 // #i104432#, #i102556# take empty results into account
309 if(!aNewRange
.isEmpty())
311 // prepare object transformation for range
312 const basegfx::B2DHomMatrix
aRangeTransformation(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
313 aScale
, fShearX
, fRotate
, aTranslate
));
315 // apply range transformation to it
316 aNewRange
.transform(aRangeTransformation
);
318 // assign to buffered value
319 const_cast< TextSimplePortionPrimitive2D
* >(this)->maB2DRange
= aNewRange
;
328 ImplPrimitive2DIDBlock(TextSimplePortionPrimitive2D
, PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
)
330 } // end of namespace primitive2d
331 } // end of namespace drawinglayer
333 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */