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 0 : basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale)
43 : {
44 : // copy input value
45 0 : basegfx::B2DVector aFontScale(rScale);
46 :
47 : // correct FontHeight settings
48 0 : 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 0 : 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 0 : rScale.setY(1.0);
65 : }
66 :
67 : // correct FontWidth settings
68 0 : if(basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY()))
69 : {
70 : // no FontScale, adapt scale
71 0 : 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 0 : rScale.setX(aFontScale.getX() / aFontScale.getY());
78 0 : aFontScale.setX(aFontScale.getY());
79 : }
80 :
81 0 : return aFontScale;
82 : }
83 : } // end of anonymous namespace
84 :
85 :
86 :
87 : namespace drawinglayer
88 : {
89 : namespace primitive2d
90 : {
91 0 : void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const
92 : {
93 0 : if(getTextLength())
94 : {
95 : // decompose object transformation to single values
96 0 : 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 0 : 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 0 : 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 0 : const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
116 :
117 : // prepare textlayoutdevice
118 0 : TextLayouterDevice aTextLayouter;
119 : aTextLayouter.setFontAttribute(
120 0 : getFontAttribute(),
121 : aFontScale.getX(),
122 : aFontScale.getY(),
123 0 : 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 0 : 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(sal_uInt32 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 0 : getText(),
152 0 : getTextPosition(),
153 0 : getTextLength(),
154 0 : getDXArray());
155 : }
156 :
157 : // create primitives for the outlines
158 0 : const sal_uInt32 nCount(rTarget.size());
159 :
160 0 : if(nCount)
161 : {
162 : // prepare object transformation for polygons
163 0 : rTransformation = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
164 0 : aScale, fShearX, fRotate, aTranslate);
165 0 : }
166 0 : }
167 : }
168 0 : }
169 :
170 0 : Primitive2DSequence TextSimplePortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
171 : {
172 0 : Primitive2DSequence aRetval;
173 :
174 0 : if(getTextLength())
175 : {
176 0 : basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
177 0 : basegfx::B2DHomMatrix aPolygonTransform;
178 :
179 : // get text outlines and their object transformation
180 0 : getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
181 :
182 : // create primitives for the outlines
183 0 : const sal_uInt32 nCount(aB2DPolyPolyVector.size());
184 :
185 0 : if(nCount)
186 : {
187 : // alloc space for the primitives
188 0 : aRetval.realloc(nCount);
189 :
190 : // color-filled polypolygons
191 0 : for(sal_uInt32 a(0L); a < nCount; a++)
192 : {
193 : // prepare polypolygon
194 0 : basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a];
195 0 : rPolyPolygon.transform(aPolygonTransform);
196 0 : aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor());
197 : }
198 :
199 0 : if(getFontAttribute().getOutline())
200 : {
201 : // decompose polygon transformation to single values
202 0 : basegfx::B2DVector aScale, aTranslate;
203 : double fRotate, fShearX;
204 0 : 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 0 : TEXTEFFECTSTYLE2D_OUTLINE));
212 :
213 0 : aRetval = Primitive2DSequence(&aNewTextEffect, 1);
214 : }
215 0 : }
216 : }
217 :
218 0 : return aRetval;
219 : }
220 :
221 0 : 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 : : BufferedDecompositionPrimitive2D(),
233 : maTextTransform(rNewTransform),
234 : maText(rText),
235 : mnTextPosition(nTextPosition),
236 : mnTextLength(nTextLength),
237 : maDXArray(rDXArray),
238 : maFontAttribute(rFontAttribute),
239 : maLocale(rLocale),
240 : maFontColor(rFontColor),
241 : maB2DRange(),
242 : mbFilled(bFilled),
243 0 : mnWidthToFill(nWidthToFill)
244 : {
245 : #if OSL_DEBUG_LEVEL > 0
246 : const sal_Int32 aStringLength(getText().getLength());
247 : OSL_ENSURE(aStringLength >= getTextPosition() && aStringLength >= getTextPosition() + getTextLength(),
248 : "TextSimplePortionPrimitive2D with text out of range (!)");
249 : #endif
250 0 : }
251 :
252 0 : bool LocalesAreEqual(const ::com::sun::star::lang::Locale& rA, const ::com::sun::star::lang::Locale& rB)
253 : {
254 0 : return (rA.Language == rB.Language
255 0 : && rA.Country == rB.Country
256 0 : && rA.Variant == rB.Variant);
257 : }
258 :
259 0 : bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
260 : {
261 0 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
262 : {
263 0 : const TextSimplePortionPrimitive2D& rCompare = (TextSimplePortionPrimitive2D&)rPrimitive;
264 :
265 0 : return (getTextTransform() == rCompare.getTextTransform()
266 0 : && getText() == rCompare.getText()
267 0 : && getTextPosition() == rCompare.getTextPosition()
268 0 : && getTextLength() == rCompare.getTextLength()
269 0 : && getDXArray() == rCompare.getDXArray()
270 0 : && getFontAttribute() == rCompare.getFontAttribute()
271 0 : && LocalesAreEqual(getLocale(), rCompare.getLocale())
272 0 : && getFontColor() == rCompare.getFontColor()
273 0 : && mbFilled == rCompare.mbFilled
274 0 : && mnWidthToFill == rCompare.mnWidthToFill);
275 : }
276 :
277 0 : return false;
278 : }
279 :
280 0 : basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
281 : {
282 0 : if(maB2DRange.isEmpty() && getTextLength())
283 : {
284 : // get TextBoundRect as base size
285 : // decompose object transformation to single values
286 0 : basegfx::B2DVector aScale, aTranslate;
287 : double fRotate, fShearX;
288 :
289 0 : if(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
290 : {
291 : // for the TextLayouterDevice, it is necessary to have a scaling representing
292 : // the font size. Since we want to extract polygons here, it is okay to
293 : // work just with scaling and to ignore shear, rotation and translation,
294 : // all that can be applied to the polygons later
295 0 : const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
296 :
297 : // prepare textlayoutdevice
298 0 : TextLayouterDevice aTextLayouter;
299 : aTextLayouter.setFontAttribute(
300 0 : getFontAttribute(),
301 : aFontScale.getX(),
302 : aFontScale.getY(),
303 0 : getLocale());
304 :
305 : // get basic text range
306 0 : basegfx::B2DRange aNewRange(aTextLayouter.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
307 :
308 : // #i104432#, #i102556# take empty results into account
309 0 : if(!aNewRange.isEmpty())
310 : {
311 : // prepare object transformation for range
312 : const basegfx::B2DHomMatrix aRangeTransformation(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
313 0 : aScale, fShearX, fRotate, aTranslate));
314 :
315 : // apply range transformation to it
316 0 : aNewRange.transform(aRangeTransformation);
317 :
318 : // assign to buffered value
319 0 : const_cast< TextSimplePortionPrimitive2D* >(this)->maB2DRange = aNewRange;
320 0 : }
321 0 : }
322 : }
323 :
324 0 : return maB2DRange;
325 : }
326 :
327 : // provide unique ID
328 0 : ImplPrimitive2DIDBlock(TextSimplePortionPrimitive2D, PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D)
329 :
330 : } // end of namespace primitive2d
331 : } // end of namespace drawinglayer
332 :
333 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|