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