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