xref: /trunk/main/drawinglayer/source/primitive2d/textprimitive2d.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_drawinglayer.hxx"
30 
31 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
33 #include <basegfx/polygon/b2dpolypolygon.hxx>
34 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
36 #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx>
37 #include <basegfx/matrix/b2dhommatrixtools.hxx>
38 
39 //////////////////////////////////////////////////////////////////////////////
40 
41 using namespace com::sun::star;
42 
43 //////////////////////////////////////////////////////////////////////////////
44 
45 namespace
46 {
47     // adapts fontScale for usage with TextLayouter. Input is rScale which is the extracted
48     // scale from a text transformation. A copy is modified so that it contains only positive
49     // scalings and XY-equal scalings to allow to get a non-X-scaled Vcl-Font for TextLayouter.
50     // rScale is adapted accordingly to contain the corrected scale which would need to be
51     // applied to e.g. outlines received from TextLayouter under usage of fontScale. This
52     // includes Y-Scale, X-Scale-correction and mirrorings.
53     basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale)
54     {
55         // copy input value
56         basegfx::B2DVector aFontScale(rScale);
57 
58         // correct FontHeight settings
59         if(basegfx::fTools::equalZero(aFontScale.getY()))
60         {
61             // no font height; choose one and adapt scale to get back to original scaling
62             static double fDefaultFontScale(100.0);
63             rScale.setY(1.0 / fDefaultFontScale);
64             aFontScale.setY(fDefaultFontScale);
65         }
66         else if(basegfx::fTools::less(aFontScale.getY(), 0.0))
67         {
68             // negative font height; invert and adapt scale to get back to original scaling
69             aFontScale.setY(-aFontScale.getY());
70             rScale.setY(-1.0);
71         }
72         else
73         {
74             // positive font height; adapt scale; scaling will be part of the polygons
75             rScale.setY(1.0);
76         }
77 
78         // correct FontWidth settings
79         if(basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY()))
80         {
81             // no FontScale, adapt scale
82             rScale.setX(1.0);
83         }
84         else
85         {
86             // If FontScale is used, force to no FontScale to get a non-scaled VCL font.
87             // Adapt scaling in X accordingly.
88             rScale.setX(aFontScale.getX() / aFontScale.getY());
89             aFontScale.setX(aFontScale.getY());
90         }
91 
92         return aFontScale;
93     }
94 } // end of anonymous namespace
95 
96 //////////////////////////////////////////////////////////////////////////////
97 
98 namespace drawinglayer
99 {
100     namespace primitive2d
101     {
102         void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const
103         {
104             if(getTextLength())
105             {
106                 // decompose object transformation to single values
107                 basegfx::B2DVector aScale, aTranslate;
108                 double fRotate, fShearX;
109 
110                 // if decomposition returns false, create no geometry since e.g. scaling may
111                 // be zero
112                 if(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
113                 {
114                     // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
115                     // be expressed as rotation by PI
116                     if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
117                     {
118                         aScale = basegfx::absolute(aScale);
119                         fRotate += F_PI;
120                     }
121 
122                     // for the TextLayouterDevice, it is necessary to have a scaling representing
123                     // the font size. Since we want to extract polygons here, it is okay to
124                     // work just with scaling and to ignore shear, rotation and translation,
125                     // all that can be applied to the polygons later
126                     const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
127 
128                     // prepare textlayoutdevice
129                     TextLayouterDevice aTextLayouter;
130                     aTextLayouter.setFontAttribute(
131                         getFontAttribute(),
132                         aFontScale.getX(),
133                         aFontScale.getY(),
134                         getLocale());
135 
136                     // When getting outlines from stretched text (aScale.getX() != 1.0) it
137                     // is necessary to inverse-scale the DXArray (if used) to not get the
138                     // outlines already aligned to given, but wrong DXArray
139                     if(getDXArray().size() && !basegfx::fTools::equal(aScale.getX(), 1.0))
140                     {
141                         ::std::vector< double > aScaledDXArray = getDXArray();
142                         const double fDXArrayScale(1.0 / aScale.getX());
143 
144                         for(sal_uInt32 a(0); a < aScaledDXArray.size(); a++)
145                         {
146                             aScaledDXArray[a] *= fDXArrayScale;
147                         }
148 
149                         // get the text outlines
150                         aTextLayouter.getTextOutlines(
151                             rTarget,
152                             getText(),
153                             getTextPosition(),
154                             getTextLength(),
155                             aScaledDXArray);
156                     }
157                     else
158                     {
159                         // get the text outlines
160                         aTextLayouter.getTextOutlines(
161                             rTarget,
162                             getText(),
163                             getTextPosition(),
164                             getTextLength(),
165                             getDXArray());
166                     }
167 
168                     // create primitives for the outlines
169                     const sal_uInt32 nCount(rTarget.size());
170 
171                     if(nCount)
172                     {
173                         // prepare object transformation for polygons
174                         rTransformation = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
175                             aScale, fShearX, fRotate, aTranslate);
176                     }
177                 }
178             }
179         }
180 
181         Primitive2DSequence TextSimplePortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
182         {
183             Primitive2DSequence aRetval;
184 
185             if(getTextLength())
186             {
187                 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
188                 basegfx::B2DHomMatrix aPolygonTransform;
189 
190                 // get text outlines and their object transformation
191                 getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
192 
193                 // create primitives for the outlines
194                 const sal_uInt32 nCount(aB2DPolyPolyVector.size());
195 
196                 if(nCount)
197                 {
198                     // alloc space for the primitives
199                     aRetval.realloc(nCount);
200 
201                     // color-filled polypolygons
202                     for(sal_uInt32 a(0L); a < nCount; a++)
203                     {
204                         // prepare polypolygon
205                         basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a];
206                         rPolyPolygon.transform(aPolygonTransform);
207                         aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor());
208                     }
209 
210                     if(getFontAttribute().getOutline())
211                     {
212                         // decompose polygon transformation to single values
213                         basegfx::B2DVector aScale, aTranslate;
214                         double fRotate, fShearX;
215                         aPolygonTransform.decompose(aScale, aTranslate, fRotate, fShearX);
216 
217                         // create outline text effect with current content and replace
218                         Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
219                             aRetval,
220                             aTranslate,
221                             fRotate,
222                             TEXTEFFECTSTYLE2D_OUTLINE));
223 
224                         aRetval = Primitive2DSequence(&aNewTextEffect, 1);
225                     }
226                 }
227             }
228 
229             return aRetval;
230         }
231 
232         TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
233             const basegfx::B2DHomMatrix& rNewTransform,
234             const String& rText,
235             xub_StrLen aTextPosition,
236             xub_StrLen aTextLength,
237             const ::std::vector< double >& rDXArray,
238             const attribute::FontAttribute& rFontAttribute,
239             const ::com::sun::star::lang::Locale& rLocale,
240             const basegfx::BColor& rFontColor)
241         :   BufferedDecompositionPrimitive2D(),
242             maTextTransform(rNewTransform),
243             maText(rText),
244             maTextPosition(aTextPosition),
245             maTextLength(aTextLength),
246             maDXArray(rDXArray),
247             maFontAttribute(rFontAttribute),
248             maLocale(rLocale),
249             maFontColor(rFontColor),
250             maB2DRange()
251         {
252 #ifdef DBG_UTIL
253             const xub_StrLen aStringLength(getText().Len());
254             OSL_ENSURE(aStringLength >= getTextPosition() && aStringLength >= getTextPosition() + getTextLength(),
255                 "TextSimplePortionPrimitive2D with text out of range (!)");
256 #endif
257         }
258 
259         bool LocalesAreEqual(const ::com::sun::star::lang::Locale& rA, const ::com::sun::star::lang::Locale& rB)
260         {
261             return (rA.Language == rB.Language
262                 && rA.Country == rB.Country
263                 && rA.Variant == rB.Variant);
264         }
265 
266         bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
267         {
268             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
269             {
270                 const TextSimplePortionPrimitive2D& rCompare = (TextSimplePortionPrimitive2D&)rPrimitive;
271 
272                 return (getTextTransform() == rCompare.getTextTransform()
273                     && getText() == rCompare.getText()
274                     && getTextPosition() == rCompare.getTextPosition()
275                     && getTextLength() == rCompare.getTextLength()
276                     && getDXArray() == rCompare.getDXArray()
277                     && getFontAttribute() == rCompare.getFontAttribute()
278                     && LocalesAreEqual(getLocale(), rCompare.getLocale())
279                     && getFontColor() == rCompare.getFontColor());
280             }
281 
282             return false;
283         }
284 
285         basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
286         {
287             if(maB2DRange.isEmpty() && getTextLength())
288             {
289                 // get TextBoundRect as base size
290                 // decompose object transformation to single values
291                 basegfx::B2DVector aScale, aTranslate;
292                 double fRotate, fShearX;
293 
294                 if(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
295                 {
296                     // for the TextLayouterDevice, it is necessary to have a scaling representing
297                     // the font size. Since we want to extract polygons here, it is okay to
298                     // work just with scaling and to ignore shear, rotation and translation,
299                     // all that can be applied to the polygons later
300                     const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
301 
302                     // prepare textlayoutdevice
303                     TextLayouterDevice aTextLayouter;
304                     aTextLayouter.setFontAttribute(
305                         getFontAttribute(),
306                         aFontScale.getX(),
307                         aFontScale.getY(),
308                         getLocale());
309 
310                     // get basic text range
311                     basegfx::B2DRange aNewRange(aTextLayouter.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
312 
313                     // #i104432#, #i102556# take empty results into account
314                     if(!aNewRange.isEmpty())
315                     {
316                         // prepare object transformation for range
317                         const basegfx::B2DHomMatrix aRangeTransformation(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
318                             aScale, fShearX, fRotate, aTranslate));
319 
320                         // apply range transformation to it
321                         aNewRange.transform(aRangeTransformation);
322 
323                         // assign to buffered value
324                         const_cast< TextSimplePortionPrimitive2D* >(this)->maB2DRange = aNewRange;
325                     }
326                 }
327             }
328 
329             return maB2DRange;
330         }
331 
332         // provide unique ID
333         ImplPrimitrive2DIDBlock(TextSimplePortionPrimitive2D, PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D)
334 
335     } // end of namespace primitive2d
336 } // end of namespace drawinglayer
337 
338 //////////////////////////////////////////////////////////////////////////////
339 // eof
340