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>
29 using namespace com::sun::star
;
34 // adapts fontScale for usage with TextLayouter. Input is rScale which is the extracted
35 // scale from a text transformation. A copy is modified so that it contains only positive
36 // scalings and XY-equal scalings to allow to get a non-X-scaled Vcl-Font for TextLayouter.
37 // rScale is adapted accordingly to contain the corrected scale which would need to be
38 // applied to e.g. outlines received from TextLayouter under usage of fontScale. This
39 // includes Y-Scale, X-Scale-correction and mirrorings.
40 basegfx::B2DVector
getCorrectedScaleAndFontScale(basegfx::B2DVector
& rScale
)
43 basegfx::B2DVector
aFontScale(rScale
);
45 // correct FontHeight settings
46 if(basegfx::fTools::equalZero(aFontScale
.getY()))
48 // no font height; choose one and adapt scale to get back to original scaling
49 static const double fDefaultFontScale(100.0);
50 rScale
.setY(1.0 / fDefaultFontScale
);
51 aFontScale
.setY(fDefaultFontScale
);
53 else if(basegfx::fTools::less(aFontScale
.getY(), 0.0))
55 // negative font height; invert and adapt scale to get back to original scaling
56 aFontScale
.setY(-aFontScale
.getY());
61 // positive font height; adapt scale; scaling will be part of the polygons
65 // correct FontWidth settings
66 if(basegfx::fTools::equal(aFontScale
.getX(), aFontScale
.getY()))
68 // no FontScale, adapt scale
73 // If FontScale is used, force to no FontScale to get a non-scaled VCL font.
74 // Adapt scaling in X accordingly.
75 rScale
.setX(aFontScale
.getX() / aFontScale
.getY());
76 aFontScale
.setX(aFontScale
.getY());
81 } // end of anonymous namespace
84 namespace drawinglayer
88 void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector
& rTarget
, basegfx::B2DHomMatrix
& rTransformation
) const
92 // decompose object transformation to single values
93 basegfx::B2DVector aScale
, aTranslate
;
94 double fRotate
, fShearX
;
96 // if decomposition returns false, create no geometry since e.g. scaling may
98 if (getTextTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
) && aScale
.getX() != 0.0)
100 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
101 // be expressed as rotation by PI
102 if(basegfx::fTools::less(aScale
.getX(), 0.0) && basegfx::fTools::less(aScale
.getY(), 0.0))
104 aScale
= basegfx::absolute(aScale
);
108 // for the TextLayouterDevice, it is necessary to have a scaling representing
109 // the font size. Since we want to extract polygons here, it is okay to
110 // work just with scaling and to ignore shear, rotation and translation,
111 // all that can be applied to the polygons later
112 const basegfx::B2DVector
aFontScale(getCorrectedScaleAndFontScale(aScale
));
114 // prepare textlayoutdevice
115 TextLayouterDevice aTextLayouter
;
116 aTextLayouter
.setFontAttribute(
122 // When getting outlines from stretched text (aScale.getX() != 1.0) it
123 // is necessary to inverse-scale the DXArray (if used) to not get the
124 // outlines already aligned to given, but wrong DXArray
125 if(!getDXArray().empty() && !basegfx::fTools::equal(aScale
.getX(), 1.0))
127 std::vector
< double > aScaledDXArray
= getDXArray();
128 const double fDXArrayScale(1.0 / aScale
.getX());
130 for(double & a
: aScaledDXArray
)
135 // get the text outlines
136 aTextLayouter
.getTextOutlines(
145 // get the text outlines
146 aTextLayouter
.getTextOutlines(
154 // create primitives for the outlines
155 const sal_uInt32
nCount(rTarget
.size());
159 // prepare object transformation for polygons
160 rTransformation
= basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
161 aScale
, fShearX
, fRotate
, aTranslate
);
167 void TextSimplePortionPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
172 Primitive2DContainer aRetval
;
173 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector
;
174 basegfx::B2DHomMatrix aPolygonTransform
;
176 // get text outlines and their object transformation
177 getTextOutlinesAndTransformation(aB2DPolyPolyVector
, aPolygonTransform
);
179 // create primitives for the outlines
180 const sal_uInt32
nCount(aB2DPolyPolyVector
.size());
185 // alloc space for the primitives
186 aRetval
.resize(nCount
);
188 // color-filled polypolygons
189 for(sal_uInt32
a(0); a
< nCount
; a
++)
191 // prepare polypolygon
192 basegfx::B2DPolyPolygon
& rPolyPolygon
= aB2DPolyPolyVector
[a
];
193 rPolyPolygon
.transform(aPolygonTransform
);
194 aRetval
[a
] = new PolyPolygonColorPrimitive2D(rPolyPolygon
, getFontColor());
197 if(getFontAttribute().getOutline())
199 // decompose polygon transformation to single values
200 basegfx::B2DVector aScale
, aTranslate
;
201 double fRotate
, fShearX
;
202 aPolygonTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
204 // create outline text effect with current content and replace
205 Primitive2DReference
aNewTextEffect(new TextEffectPrimitive2D(
209 TextEffectStyle2D::Outline
));
211 aRetval
= Primitive2DContainer
{ aNewTextEffect
};
214 rContainer
.insert(rContainer
.end(), aRetval
.begin(), aRetval
.end());
217 TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
218 const basegfx::B2DHomMatrix
& rNewTransform
,
219 const OUString
& rText
,
220 sal_Int32 nTextPosition
,
221 sal_Int32 nTextLength
,
222 const std::vector
< double >& rDXArray
,
223 const attribute::FontAttribute
& rFontAttribute
,
224 const css::lang::Locale
& rLocale
,
225 const basegfx::BColor
& rFontColor
,
228 const Color
& rTextFillColor
)
229 : BufferedDecompositionPrimitive2D(),
230 maTextTransform(rNewTransform
),
232 mnTextPosition(nTextPosition
),
233 mnTextLength(nTextLength
),
235 maFontAttribute(rFontAttribute
),
237 maFontColor(rFontColor
),
239 mnWidthToFill(nWidthToFill
),
240 maTextFillColor(rTextFillColor
),
243 #if OSL_DEBUG_LEVEL > 0
244 const sal_Int32
aStringLength(getText().getLength());
245 OSL_ENSURE(aStringLength
>= getTextPosition() && aStringLength
>= getTextPosition() + getTextLength(),
246 "TextSimplePortionPrimitive2D with text out of range (!)");
250 bool LocalesAreEqual(const css::lang::Locale
& rA
, const css::lang::Locale
& rB
)
252 return (rA
.Language
== rB
.Language
253 && rA
.Country
== rB
.Country
254 && rA
.Variant
== rB
.Variant
);
257 bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
259 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
261 const TextSimplePortionPrimitive2D
& rCompare
= static_cast<const TextSimplePortionPrimitive2D
&>(rPrimitive
);
263 return (getTextTransform() == rCompare
.getTextTransform()
264 && getText() == rCompare
.getText()
265 && getTextPosition() == rCompare
.getTextPosition()
266 && getTextLength() == rCompare
.getTextLength()
267 && getDXArray() == rCompare
.getDXArray()
268 && getFontAttribute() == rCompare
.getFontAttribute()
269 && LocalesAreEqual(getLocale(), rCompare
.getLocale())
270 && getFontColor() == rCompare
.getFontColor()
271 && mbFilled
== rCompare
.mbFilled
272 && mnWidthToFill
== rCompare
.mnWidthToFill
273 && maTextFillColor
== rCompare
.maTextFillColor
);
279 basegfx::B2DRange
TextSimplePortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
281 if(maB2DRange
.isEmpty() && getTextLength())
283 // get TextBoundRect as base size
284 // decompose object transformation to single values
285 basegfx::B2DVector aScale
, aTranslate
;
286 double fRotate
, fShearX
;
288 if(getTextTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
))
290 // for the TextLayouterDevice, it is necessary to have a scaling representing
291 // the font size. Since we want to extract polygons here, it is okay to
292 // work just with scaling and to ignore shear, rotation and translation,
293 // all that can be applied to the polygons later
294 const basegfx::B2DVector
aFontScale(getCorrectedScaleAndFontScale(aScale
));
296 // prepare textlayoutdevice
297 TextLayouterDevice aTextLayouter
;
298 aTextLayouter
.setFontAttribute(
304 // get basic text range
305 basegfx::B2DRange
aNewRange(aTextLayouter
.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
307 // #i104432#, #i102556# take empty results into account
308 if(!aNewRange
.isEmpty())
310 // prepare object transformation for range
311 const basegfx::B2DHomMatrix
aRangeTransformation(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
312 aScale
, fShearX
, fRotate
, aTranslate
));
314 // apply range transformation to it
315 aNewRange
.transform(aRangeTransformation
);
317 // assign to buffered value
318 const_cast< TextSimplePortionPrimitive2D
* >(this)->maB2DRange
= aNewRange
;
327 ImplPrimitive2DIDBlock(TextSimplePortionPrimitive2D
, PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
)
329 } // end of namespace primitive2d
330 } // end of namespace drawinglayer
332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */