LibreOffice Module drawinglayer (master) 1
textprimitive2d.cxx
Go to the documentation of this file.
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
27#include <utility>
28#include <osl/diagnose.h>
29
30using namespace com::sun::star;
31
33{
34namespace
35{
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.
42basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale)
43{
44 // copy input value
45 basegfx::B2DVector aFontScale(rScale);
46
47 // correct FontHeight settings
48 if (basegfx::fTools::equalZero(aFontScale.getY()))
49 {
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);
54 }
55 else if (basegfx::fTools::less(aFontScale.getY(), 0.0))
56 {
57 // negative font height; invert and adapt scale to get back to original scaling
58 aFontScale.setY(-aFontScale.getY());
59 rScale.setY(-1.0);
60 }
61 else
62 {
63 // positive font height; adapt scale; scaling will be part of the polygons
64 rScale.setY(1.0);
65 }
66
67 // correct FontWidth settings
68 if (basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY()))
69 {
70 // no FontScale, adapt scale
71 rScale.setX(1.0);
72 }
73 else
74 {
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());
79 }
80
81 return aFontScale;
82}
83} // end of anonymous namespace
84
86 basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const
87{
88 if (!getTextLength())
89 return;
90
91 // decompose object transformation to single values
92 basegfx::B2DVector aScale, aTranslate;
93 double fRotate, fShearX;
94
95 // if decomposition returns false, create no geometry since e.g. scaling may
96 // be zero
97 if (!(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX)
98 && aScale.getX() != 0.0))
99 return;
100
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))
104 {
105 aScale = basegfx::absolute(aScale);
106 fRotate += M_PI;
107 }
108
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));
114
115 // prepare textlayoutdevice
116 TextLayouterDevice aTextLayouter;
117 aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(),
118 getLocale());
119
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))
124 {
125 std::vector<double> aScaledDXArray = getDXArray();
126 const double fDXArrayScale(1.0 / aScale.getX());
127
128 for (double& a : aScaledDXArray)
129 {
130 a *= fDXArrayScale;
131 }
132
133 // get the text outlines
135 aScaledDXArray, getKashidaArray());
136 }
137 else
138 {
139 // get the text outlines
142 }
143
144 // create primitives for the outlines
145 const sal_uInt32 nCount(rTarget.size());
146
147 if (nCount)
148 {
149 // prepare object transformation for polygons
151 aScale, fShearX, fRotate, aTranslate);
152 }
153}
154
156 Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
157{
158 if (!getTextLength())
159 return;
160
161 Primitive2DContainer aRetval;
162 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
163 basegfx::B2DHomMatrix aPolygonTransform;
164
165 // get text outlines and their object transformation
166 getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
167
168 // create primitives for the outlines
169 const sal_uInt32 nCount(aB2DPolyPolyVector.size());
170
171 if (!nCount)
172 return;
173
174 // alloc space for the primitives
175 aRetval.resize(nCount);
176
177 // color-filled polypolygons
178 for (sal_uInt32 a(0); a < nCount; a++)
179 {
180 // prepare polypolygon
181 basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a];
182 rPolyPolygon.transform(aPolygonTransform);
183 aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor());
184 }
185
186 if (getFontAttribute().getOutline())
187 {
188 // decompose polygon transformation to single values
189 basegfx::B2DVector aScale, aTranslate;
190 double fRotate, fShearX;
191 aPolygonTransform.decompose(aScale, aTranslate, fRotate, fShearX);
192
193 // create outline text effect with current content and replace
195 std::move(aRetval), aTranslate, fRotate, TextEffectStyle2D::Outline));
196
197 aRetval = Primitive2DContainer{ aNewTextEffect };
198 }
199
200 rContainer.append(std::move(aRetval));
201}
202
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)
218 , mbFilled(bFilled)
219 , mnWidthToFill(nWidthToFill)
220 , maTextFillColor(rTextFillColor)
221{
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 (!)");
227#endif
228}
229
230bool LocalesAreEqual(const css::lang::Locale& rA, const css::lang::Locale& rB)
231{
232 return (rA.Language == rB.Language && rA.Country == rB.Country && rA.Variant == rB.Variant);
233}
234
236{
237 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
238 {
239 const TextSimplePortionPrimitive2D& rCompare
240 = static_cast<const TextSimplePortionPrimitive2D&>(rPrimitive);
241
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);
252 }
253
254 return false;
255}
256
258 const geometry::ViewInformation2D& /*rViewInformation*/) const
259{
261 {
262 // get TextBoundRect as base size
263 // decompose object transformation to single values
264 basegfx::B2DVector aScale, aTranslate;
265 double fRotate, fShearX;
266
267 if (getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
268 {
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));
274
275 // prepare textlayoutdevice
276 TextLayouterDevice aTextLayouter;
277 aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(),
278 getLocale());
279
280 // get basic text range
281 basegfx::B2DRange aNewRange(
283
284 // #i104432#, #i102556# take empty results into account
285 if (!aNewRange.isEmpty())
286 {
287 // prepare object transformation for range
288 const basegfx::B2DHomMatrix aRangeTransformation(
290 aScale, fShearX, fRotate, aTranslate));
291
292 // apply range transformation to it
293 aNewRange.transform(aRangeTransformation);
294
295 // assign to buffered value
296 const_cast<TextSimplePortionPrimitive2D*>(this)->maB2DRange = aNewRange;
297 }
298 }
299 }
300
301 return maB2DRange;
302}
303
304// provide unique ID
306{
308}
309
310} // end of namespace
311
312/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Text maText
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void transform(const basegfx::B2DHomMatrix &rMatrix)
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
bool isEmpty() const
TYPE getX() const
void setY(TYPE fY)
TYPE getY() const
void setX(TYPE fX)
void setFontAttribute(const attribute::FontAttribute &rFontAttribute, double fFontScaleX, double fFontScaleY, const css::lang::Locale &rLocale)
basegfx::B2DRange getTextBoundRect(const OUString &rText, sal_uInt32 nIndex, sal_uInt32 nLength) const
void getTextOutlines(basegfx::B2DPolyPolygonVector &, const OUString &rText, sal_uInt32 nIndex, sal_uInt32 nLength, const ::std::vector< double > &rDXArray, const ::std::vector< sal_Bool > &rKashidaArray) const
basegfx::B2DRange maB2DRange
#i96669# internal: add simple range buffering for this primitive
TextSimplePortionPrimitive2D(basegfx::B2DHomMatrix aNewTransform, OUString aText, sal_Int32 nTextPosition, sal_Int32 nTextLength, std::vector< double > &&rDXArray, std::vector< sal_Bool > &&rKashidaArray, attribute::FontAttribute aFontAttribute, css::lang::Locale aLocale, const basegfx::BColor &rFontColor, bool bFilled=false, tools::Long nWidthToFill=0, const Color &rTextFillColor=COL_TRANSPARENT)
constructor
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
const ::std::vector< sal_Bool > & getKashidaArray() const
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition.
void getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector &rTarget, basegfx::B2DHomMatrix &rTransformation) const
get text outlines as polygons and their according ObjectTransformation.
const ::std::vector< double > & getDXArray() const
const attribute::FontAttribute & getFontAttribute() const
const basegfx::B2DHomMatrix & getTextTransform() const
data read access
int nCount
#define PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
FilterGroup & rTarget
uno_Any a
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
bool less(const T &rfValA, const T &rfValB)
double getLength(const B2DPolygon &rCandidate)
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
B2DTuple absolute(const B2DTuple &rTup)
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
rtl::OString decompose(rtl::OString const &type, sal_Int32 *rank=nullptr, std::vector< rtl::OString > *arguments=nullptr)
bool LocalesAreEqual(const css::lang::Locale &rA, const css::lang::Locale &rB)
small helper to have a compare operator for Locale
long Long
const ::Color maTextFillColor