Line data Source code
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 :
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>
27 :
28 :
29 :
30 : using namespace com::sun::star;
31 :
32 :
33 :
34 : namespace
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.
42 24912 : basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale)
43 : {
44 : // copy input value
45 24912 : basegfx::B2DVector aFontScale(rScale);
46 :
47 : // correct FontHeight settings
48 24912 : if(basegfx::fTools::equalZero(aFontScale.getY()))
49 : {
50 : // no font height; choose one and adapt scale to get back to original scaling
51 : static double fDefaultFontScale(100.0);
52 0 : rScale.setY(1.0 / fDefaultFontScale);
53 0 : aFontScale.setY(fDefaultFontScale);
54 : }
55 24912 : 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 0 : aFontScale.setY(-aFontScale.getY());
59 0 : rScale.setY(-1.0);
60 : }
61 : else
62 : {
63 : // positive font height; adapt scale; scaling will be part of the polygons
64 24912 : rScale.setY(1.0);
65 : }
66 :
67 : // correct FontWidth settings
68 24912 : if(basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY()))
69 : {
70 : // no FontScale, adapt scale
71 23231 : 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 1681 : rScale.setX(aFontScale.getX() / aFontScale.getY());
78 1681 : aFontScale.setX(aFontScale.getY());
79 : }
80 :
81 24912 : return aFontScale;
82 : }
83 : } // end of anonymous namespace
84 :
85 :
86 :
87 : namespace drawinglayer
88 : {
89 : namespace primitive2d
90 : {
91 923 : void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const
92 : {
93 923 : if(getTextLength())
94 : {
95 : // decompose object transformation to single values
96 1846 : basegfx::B2DVector aScale, aTranslate;
97 : double fRotate, fShearX;
98 :
99 : // if decomposition returns false, create no geometry since e.g. scaling may
100 : // be zero
101 923 : if(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
102 : {
103 : // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
104 : // be expressed as rotation by PI
105 923 : if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
106 : {
107 0 : aScale = basegfx::absolute(aScale);
108 0 : fRotate += F_PI;
109 : }
110 :
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 923 : const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
116 :
117 : // prepare textlayoutdevice
118 1846 : TextLayouterDevice aTextLayouter;
119 : aTextLayouter.setFontAttribute(
120 923 : getFontAttribute(),
121 : aFontScale.getX(),
122 : aFontScale.getY(),
123 1846 : getLocale());
124 :
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 923 : if(getDXArray().size() && !basegfx::fTools::equal(aScale.getX(), 1.0))
129 : {
130 0 : ::std::vector< double > aScaledDXArray = getDXArray();
131 0 : const double fDXArrayScale(1.0 / aScale.getX());
132 :
133 0 : for(size_t a(0); a < aScaledDXArray.size(); a++)
134 : {
135 0 : aScaledDXArray[a] *= fDXArrayScale;
136 : }
137 :
138 : // get the text outlines
139 : aTextLayouter.getTextOutlines(
140 : rTarget,
141 0 : getText(),
142 0 : getTextPosition(),
143 0 : getTextLength(),
144 0 : aScaledDXArray);
145 : }
146 : else
147 : {
148 : // get the text outlines
149 : aTextLayouter.getTextOutlines(
150 : rTarget,
151 923 : getText(),
152 923 : getTextPosition(),
153 923 : getTextLength(),
154 3692 : getDXArray());
155 : }
156 :
157 : // create primitives for the outlines
158 923 : const sal_uInt32 nCount(rTarget.size());
159 :
160 923 : if(nCount)
161 : {
162 : // prepare object transformation for polygons
163 1846 : rTransformation = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
164 923 : aScale, fShearX, fRotate, aTranslate);
165 923 : }
166 923 : }
167 : }
168 923 : }
169 :
170 923 : Primitive2DSequence TextSimplePortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
171 : {
172 923 : Primitive2DSequence aRetval;
173 :
174 923 : if(getTextLength())
175 : {
176 923 : basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
177 1846 : basegfx::B2DHomMatrix aPolygonTransform;
178 :
179 : // get text outlines and their object transformation
180 923 : getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
181 :
182 : // create primitives for the outlines
183 923 : const sal_uInt32 nCount(aB2DPolyPolyVector.size());
184 :
185 923 : if(nCount)
186 : {
187 : // alloc space for the primitives
188 923 : aRetval.realloc(nCount);
189 :
190 : // color-filled polypolygons
191 3351 : for(sal_uInt32 a(0L); a < nCount; a++)
192 : {
193 : // prepare polypolygon
194 2428 : basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a];
195 2428 : rPolyPolygon.transform(aPolygonTransform);
196 2428 : aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor());
197 : }
198 :
199 923 : if(getFontAttribute().getOutline())
200 : {
201 : // decompose polygon transformation to single values
202 94 : basegfx::B2DVector aScale, aTranslate;
203 : double fRotate, fShearX;
204 47 : aPolygonTransform.decompose(aScale, aTranslate, fRotate, fShearX);
205 :
206 : // create outline text effect with current content and replace
207 : Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
208 : aRetval,
209 : aTranslate,
210 : fRotate,
211 94 : TEXTEFFECTSTYLE2D_OUTLINE));
212 :
213 94 : aRetval = Primitive2DSequence(&aNewTextEffect, 1);
214 : }
215 923 : }
216 : }
217 :
218 923 : return aRetval;
219 : }
220 :
221 26785 : TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
222 : const basegfx::B2DHomMatrix& rNewTransform,
223 : const OUString& rText,
224 : sal_Int32 nTextPosition,
225 : sal_Int32 nTextLength,
226 : const ::std::vector< double >& rDXArray,
227 : const attribute::FontAttribute& rFontAttribute,
228 : const ::com::sun::star::lang::Locale& rLocale,
229 : const basegfx::BColor& rFontColor,
230 : bool bFilled,
231 : long nWidthToFill,
232 : const Color& rFillColor)
233 : : BufferedDecompositionPrimitive2D(),
234 : maTextTransform(rNewTransform),
235 : maText(rText),
236 : mnTextPosition(nTextPosition),
237 : mnTextLength(nTextLength),
238 : maDXArray(rDXArray),
239 : maFontAttribute(rFontAttribute),
240 : maLocale(rLocale),
241 : maFontColor(rFontColor),
242 : maB2DRange(),
243 : mbFilled(bFilled),
244 : mnWidthToFill(nWidthToFill),
245 26785 : maTextFillColor(rFillColor)
246 : {
247 : #if OSL_DEBUG_LEVEL > 0
248 : const sal_Int32 aStringLength(getText().getLength());
249 : OSL_ENSURE(aStringLength >= getTextPosition() && aStringLength >= getTextPosition() + getTextLength(),
250 : "TextSimplePortionPrimitive2D with text out of range (!)");
251 : #endif
252 26785 : }
253 :
254 68 : bool LocalesAreEqual(const ::com::sun::star::lang::Locale& rA, const ::com::sun::star::lang::Locale& rB)
255 : {
256 68 : return (rA.Language == rB.Language
257 68 : && rA.Country == rB.Country
258 136 : && rA.Variant == rB.Variant);
259 : }
260 :
261 68 : bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
262 : {
263 68 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
264 : {
265 68 : const TextSimplePortionPrimitive2D& rCompare = static_cast<const TextSimplePortionPrimitive2D&>(rPrimitive);
266 :
267 68 : return (getTextTransform() == rCompare.getTextTransform()
268 68 : && getText() == rCompare.getText()
269 68 : && getTextPosition() == rCompare.getTextPosition()
270 68 : && getTextLength() == rCompare.getTextLength()
271 68 : && getDXArray() == rCompare.getDXArray()
272 68 : && getFontAttribute() == rCompare.getFontAttribute()
273 68 : && LocalesAreEqual(getLocale(), rCompare.getLocale())
274 68 : && getFontColor() == rCompare.getFontColor()
275 68 : && mbFilled == rCompare.mbFilled
276 136 : && mnWidthToFill == rCompare.mnWidthToFill);
277 : }
278 :
279 0 : return false;
280 : }
281 :
282 122936 : basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
283 : {
284 122936 : if(maB2DRange.isEmpty() && getTextLength())
285 : {
286 : // get TextBoundRect as base size
287 : // decompose object transformation to single values
288 47978 : basegfx::B2DVector aScale, aTranslate;
289 : double fRotate, fShearX;
290 :
291 23989 : if(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
292 : {
293 : // for the TextLayouterDevice, it is necessary to have a scaling representing
294 : // the font size. Since we want to extract polygons here, it is okay to
295 : // work just with scaling and to ignore shear, rotation and translation,
296 : // all that can be applied to the polygons later
297 23989 : const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
298 :
299 : // prepare textlayoutdevice
300 47978 : TextLayouterDevice aTextLayouter;
301 : aTextLayouter.setFontAttribute(
302 23989 : getFontAttribute(),
303 : aFontScale.getX(),
304 : aFontScale.getY(),
305 47978 : getLocale());
306 :
307 : // get basic text range
308 23989 : basegfx::B2DRange aNewRange(aTextLayouter.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
309 :
310 : // #i104432#, #i102556# take empty results into account
311 23989 : if(!aNewRange.isEmpty())
312 : {
313 : // prepare object transformation for range
314 : const basegfx::B2DHomMatrix aRangeTransformation(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
315 22866 : aScale, fShearX, fRotate, aTranslate));
316 :
317 : // apply range transformation to it
318 22866 : aNewRange.transform(aRangeTransformation);
319 :
320 : // assign to buffered value
321 22866 : const_cast< TextSimplePortionPrimitive2D* >(this)->maB2DRange = aNewRange;
322 23989 : }
323 23989 : }
324 : }
325 :
326 122936 : return maB2DRange;
327 : }
328 :
329 : // provide unique ID
330 14273 : ImplPrimitive2DIDBlock(TextSimplePortionPrimitive2D, PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D)
331 :
332 : } // end of namespace primitive2d
333 : } // end of namespace drawinglayer
334 :
335 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|