Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / drawinglayer / source / primitive2d / textprimitive2d.cxx
blob94666e7cede4276cf6f66c96823b0110271956fe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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;
32 namespace
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)
42 // copy input value
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());
57 rScale.setY(-1.0);
59 else
61 // positive font height; adapt scale; scaling will be part of the polygons
62 rScale.setY(1.0);
65 // correct FontWidth settings
66 if(basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY()))
68 // no FontScale, adapt scale
69 rScale.setX(1.0);
71 else
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());
79 return aFontScale;
81 } // end of anonymous namespace
84 namespace drawinglayer
86 namespace primitive2d
88 void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const
90 if(getTextLength())
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
97 // be zero
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);
105 fRotate += F_PI;
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(
117 getFontAttribute(),
118 aFontScale.getX(),
119 aFontScale.getY(),
120 getLocale());
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)
132 a *= fDXArrayScale;
135 // get the text outlines
136 aTextLayouter.getTextOutlines(
137 rTarget,
138 getText(),
139 getTextPosition(),
140 getTextLength(),
141 aScaledDXArray);
143 else
145 // get the text outlines
146 aTextLayouter.getTextOutlines(
147 rTarget,
148 getText(),
149 getTextPosition(),
150 getTextLength(),
151 getDXArray());
154 // create primitives for the outlines
155 const sal_uInt32 nCount(rTarget.size());
157 if(nCount)
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
169 if(!getTextLength())
170 return;
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());
182 if(!nCount)
183 return;
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(
206 aRetval,
207 aTranslate,
208 fRotate,
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,
226 bool bFilled,
227 long nWidthToFill,
228 const Color& rTextFillColor)
229 : BufferedDecompositionPrimitive2D(),
230 maTextTransform(rNewTransform),
231 maText(rText),
232 mnTextPosition(nTextPosition),
233 mnTextLength(nTextLength),
234 maDXArray(rDXArray),
235 maFontAttribute(rFontAttribute),
236 maLocale(rLocale),
237 maFontColor(rFontColor),
238 mbFilled(bFilled),
239 mnWidthToFill(nWidthToFill),
240 maTextFillColor(rTextFillColor),
241 maB2DRange()
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 (!)");
247 #endif
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);
276 return false;
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(
299 getFontAttribute(),
300 aFontScale.getX(),
301 aFontScale.getY(),
302 getLocale());
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;
323 return maB2DRange;
326 // provide unique ID
327 ImplPrimitive2DIDBlock(TextSimplePortionPrimitive2D, PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D)
329 } // end of namespace primitive2d
330 } // end of namespace drawinglayer
332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */