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