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