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