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/PolyPolygonColorPrimitive2D.hxx>
24 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
25 #include <primitive2d/texteffectprimitive2d.hxx>
26 #include <basegfx/matrix/b2dhommatrixtools.hxx>
28 #include <osl/diagnose.h>
30 using namespace com::sun::star
;
32 namespace drawinglayer::primitive2d
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 const 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 void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(
86 basegfx::B2DPolyPolygonVector
& rTarget
, basegfx::B2DHomMatrix
& rTransformation
) const
91 // decompose object transformation to single values
92 basegfx::B2DVector aScale
, aTranslate
;
93 double fRotate
, fShearX
;
95 // if decomposition returns false, create no geometry since e.g. scaling may
97 if (!(getTextTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
)
98 && aScale
.getX() != 0.0))
101 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
102 // be expressed as rotation by PI
103 if (basegfx::fTools::less(aScale
.getX(), 0.0) && basegfx::fTools::less(aScale
.getY(), 0.0))
105 aScale
= basegfx::absolute(aScale
);
109 // for the TextLayouterDevice, it is necessary to have a scaling representing
110 // the font size. Since we want to extract polygons here, it is okay to
111 // work just with scaling and to ignore shear, rotation and translation,
112 // all that can be applied to the polygons later
113 const basegfx::B2DVector
aFontScale(getCorrectedScaleAndFontScale(aScale
));
115 // prepare textlayoutdevice
116 TextLayouterDevice aTextLayouter
;
117 aTextLayouter
.setFontAttribute(getFontAttribute(), aFontScale
.getX(), aFontScale
.getY(),
120 // When getting outlines from stretched text (aScale.getX() != 1.0) it
121 // is necessary to inverse-scale the DXArray (if used) to not get the
122 // outlines already aligned to given, but wrong DXArray
123 if (!getDXArray().empty() && !basegfx::fTools::equal(aScale
.getX(), 1.0))
125 std::vector
<double> aScaledDXArray
= getDXArray();
126 const double fDXArrayScale(1.0 / aScale
.getX());
128 for (double& a
: aScaledDXArray
)
133 // get the text outlines
134 aTextLayouter
.getTextOutlines(rTarget
, getText(), getTextPosition(), getTextLength(),
135 aScaledDXArray
, getKashidaArray());
139 // get the text outlines
140 aTextLayouter
.getTextOutlines(rTarget
, getText(), getTextPosition(), getTextLength(),
141 getDXArray(), getKashidaArray());
144 // create primitives for the outlines
145 const sal_uInt32
nCount(rTarget
.size());
149 // prepare object transformation for polygons
150 rTransformation
= basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
151 aScale
, fShearX
, fRotate
, aTranslate
);
155 void TextSimplePortionPrimitive2D::create2DDecomposition(
156 Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
158 if (!getTextLength())
161 Primitive2DContainer aRetval
;
162 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector
;
163 basegfx::B2DHomMatrix aPolygonTransform
;
165 // get text outlines and their object transformation
166 getTextOutlinesAndTransformation(aB2DPolyPolyVector
, aPolygonTransform
);
168 // create primitives for the outlines
169 const sal_uInt32
nCount(aB2DPolyPolyVector
.size());
174 // alloc space for the primitives
175 aRetval
.resize(nCount
);
177 // color-filled polypolygons
178 for (sal_uInt32
a(0); a
< nCount
; a
++)
180 // prepare polypolygon
181 basegfx::B2DPolyPolygon
& rPolyPolygon
= aB2DPolyPolyVector
[a
];
182 rPolyPolygon
.transform(aPolygonTransform
);
183 aRetval
[a
] = new PolyPolygonColorPrimitive2D(rPolyPolygon
, getFontColor());
186 if (getFontAttribute().getOutline())
188 // decompose polygon transformation to single values
189 basegfx::B2DVector aScale
, aTranslate
;
190 double fRotate
, fShearX
;
191 aPolygonTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
193 // create outline text effect with current content and replace
194 Primitive2DReference
aNewTextEffect(new TextEffectPrimitive2D(
195 std::move(aRetval
), aTranslate
, fRotate
, TextEffectStyle2D::Outline
));
197 aRetval
= Primitive2DContainer
{ aNewTextEffect
};
200 rContainer
.append(std::move(aRetval
));
203 TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
204 basegfx::B2DHomMatrix rNewTransform
, OUString rText
, sal_Int32 nTextPosition
,
205 sal_Int32 nTextLength
, std::vector
<double>&& rDXArray
, std::vector
<sal_Bool
>&& rKashidaArray
,
206 attribute::FontAttribute aFontAttribute
, css::lang::Locale aLocale
,
207 const basegfx::BColor
& rFontColor
, bool bFilled
, tools::Long nWidthToFill
,
208 const Color
& rTextFillColor
)
209 : maTextTransform(std::move(rNewTransform
))
210 , maText(std::move(rText
))
211 , mnTextPosition(nTextPosition
)
212 , mnTextLength(nTextLength
)
213 , maDXArray(std::move(rDXArray
))
214 , maKashidaArray(std::move(rKashidaArray
))
215 , maFontAttribute(std::move(aFontAttribute
))
216 , maLocale(std::move(aLocale
))
217 , maFontColor(rFontColor
)
219 , mnWidthToFill(nWidthToFill
)
220 , maTextFillColor(rTextFillColor
)
222 #if OSL_DEBUG_LEVEL > 0
223 const sal_Int32
aStringLength(getText().getLength());
224 OSL_ENSURE(aStringLength
>= getTextPosition()
225 && aStringLength
>= getTextPosition() + getTextLength(),
226 "TextSimplePortionPrimitive2D with text out of range (!)");
230 bool LocalesAreEqual(const css::lang::Locale
& rA
, const css::lang::Locale
& rB
)
232 return (rA
.Language
== rB
.Language
&& rA
.Country
== rB
.Country
&& rA
.Variant
== rB
.Variant
);
235 bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
237 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
239 const TextSimplePortionPrimitive2D
& rCompare
240 = static_cast<const TextSimplePortionPrimitive2D
&>(rPrimitive
);
242 return (getTextTransform() == rCompare
.getTextTransform() && getText() == rCompare
.getText()
243 && getTextPosition() == rCompare
.getTextPosition()
244 && getTextLength() == rCompare
.getTextLength()
245 && getDXArray() == rCompare
.getDXArray()
246 && getKashidaArray() == rCompare
.getKashidaArray()
247 && getFontAttribute() == rCompare
.getFontAttribute()
248 && LocalesAreEqual(getLocale(), rCompare
.getLocale())
249 && getFontColor() == rCompare
.getFontColor() && mbFilled
== rCompare
.mbFilled
250 && mnWidthToFill
== rCompare
.mnWidthToFill
251 && maTextFillColor
== rCompare
.maTextFillColor
);
257 basegfx::B2DRange
TextSimplePortionPrimitive2D::getB2DRange(
258 const geometry::ViewInformation2D
& /*rViewInformation*/) const
260 if (maB2DRange
.isEmpty() && getTextLength())
262 // get TextBoundRect as base size
263 // decompose object transformation to single values
264 basegfx::B2DVector aScale
, aTranslate
;
265 double fRotate
, fShearX
;
267 if (getTextTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
))
269 // for the TextLayouterDevice, it is necessary to have a scaling representing
270 // the font size. Since we want to extract polygons here, it is okay to
271 // work just with scaling and to ignore shear, rotation and translation,
272 // all that can be applied to the polygons later
273 const basegfx::B2DVector
aFontScale(getCorrectedScaleAndFontScale(aScale
));
275 // prepare textlayoutdevice
276 TextLayouterDevice aTextLayouter
;
277 aTextLayouter
.setFontAttribute(getFontAttribute(), aFontScale
.getX(), aFontScale
.getY(),
280 // get basic text range
281 basegfx::B2DRange
aNewRange(
282 aTextLayouter
.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
284 // #i104432#, #i102556# take empty results into account
285 if (!aNewRange
.isEmpty())
287 // prepare object transformation for range
288 const basegfx::B2DHomMatrix
aRangeTransformation(
289 basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
290 aScale
, fShearX
, fRotate
, aTranslate
));
292 // apply range transformation to it
293 aNewRange
.transform(aRangeTransformation
);
295 // assign to buffered value
296 const_cast<TextSimplePortionPrimitive2D
*>(this)->maB2DRange
= aNewRange
;
305 sal_uInt32
TextSimplePortionPrimitive2D::getPrimitive2DID() const
307 return PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
;
310 } // end of namespace
312 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */