1*cdf0e10cSrcweir /************************************************************************* 2*cdf0e10cSrcweir * 3*cdf0e10cSrcweir * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4*cdf0e10cSrcweir * 5*cdf0e10cSrcweir * Copyright 2000, 2010 Oracle and/or its affiliates. 6*cdf0e10cSrcweir * 7*cdf0e10cSrcweir * OpenOffice.org - a multi-platform office productivity suite 8*cdf0e10cSrcweir * 9*cdf0e10cSrcweir * This file is part of OpenOffice.org. 10*cdf0e10cSrcweir * 11*cdf0e10cSrcweir * OpenOffice.org is free software: you can redistribute it and/or modify 12*cdf0e10cSrcweir * it under the terms of the GNU Lesser General Public License version 3 13*cdf0e10cSrcweir * only, as published by the Free Software Foundation. 14*cdf0e10cSrcweir * 15*cdf0e10cSrcweir * OpenOffice.org is distributed in the hope that it will be useful, 16*cdf0e10cSrcweir * but WITHOUT ANY WARRANTY; without even the implied warranty of 17*cdf0e10cSrcweir * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*cdf0e10cSrcweir * GNU Lesser General Public License version 3 for more details 19*cdf0e10cSrcweir * (a copy is included in the LICENSE file that accompanied this code). 20*cdf0e10cSrcweir * 21*cdf0e10cSrcweir * You should have received a copy of the GNU Lesser General Public License 22*cdf0e10cSrcweir * version 3 along with OpenOffice.org. If not, see 23*cdf0e10cSrcweir * <http://www.openoffice.org/license.html> 24*cdf0e10cSrcweir * for a copy of the LGPLv3 License. 25*cdf0e10cSrcweir * 26*cdf0e10cSrcweir ************************************************************************/ 27*cdf0e10cSrcweir 28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 29*cdf0e10cSrcweir #include "precompiled_drawinglayer.hxx" 30*cdf0e10cSrcweir 31*cdf0e10cSrcweir #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 32*cdf0e10cSrcweir #include <drawinglayer/primitive2d/textlayoutdevice.hxx> 33*cdf0e10cSrcweir #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 34*cdf0e10cSrcweir #include <drawinglayer/attribute/strokeattribute.hxx> 35*cdf0e10cSrcweir #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> 36*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx> 37*cdf0e10cSrcweir #include <comphelper/processfactory.hxx> 38*cdf0e10cSrcweir #include <com/sun/star/i18n/WordType.hpp> 39*cdf0e10cSrcweir #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx> 40*cdf0e10cSrcweir #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> 41*cdf0e10cSrcweir #include <com/sun/star/i18n/XBreakIterator.hpp> 42*cdf0e10cSrcweir #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 43*cdf0e10cSrcweir #include <drawinglayer/primitive2d/textlineprimitive2d.hxx> 44*cdf0e10cSrcweir #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx> 45*cdf0e10cSrcweir 46*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 47*cdf0e10cSrcweir 48*cdf0e10cSrcweir namespace drawinglayer 49*cdf0e10cSrcweir { 50*cdf0e10cSrcweir namespace primitive2d 51*cdf0e10cSrcweir { 52*cdf0e10cSrcweir void TextDecoratedPortionPrimitive2D::impCreateGeometryContent( 53*cdf0e10cSrcweir std::vector< Primitive2DReference >& rTarget, 54*cdf0e10cSrcweir basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans, 55*cdf0e10cSrcweir const String& rText, 56*cdf0e10cSrcweir xub_StrLen aTextPosition, 57*cdf0e10cSrcweir xub_StrLen aTextLength, 58*cdf0e10cSrcweir const ::std::vector< double >& rDXArray, 59*cdf0e10cSrcweir const attribute::FontAttribute& rFontAttribute) const 60*cdf0e10cSrcweir { 61*cdf0e10cSrcweir // create the SimpleTextPrimitive needed in any case 62*cdf0e10cSrcweir rTarget.push_back(Primitive2DReference( 63*cdf0e10cSrcweir new TextSimplePortionPrimitive2D( 64*cdf0e10cSrcweir rDecTrans.getB2DHomMatrix(), 65*cdf0e10cSrcweir rText, 66*cdf0e10cSrcweir aTextPosition, 67*cdf0e10cSrcweir aTextLength, 68*cdf0e10cSrcweir rDXArray, 69*cdf0e10cSrcweir rFontAttribute, 70*cdf0e10cSrcweir getLocale(), 71*cdf0e10cSrcweir getFontColor()))); 72*cdf0e10cSrcweir 73*cdf0e10cSrcweir // see if something else needs to be done 74*cdf0e10cSrcweir const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline()); 75*cdf0e10cSrcweir const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline()); 76*cdf0e10cSrcweir const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout()); 77*cdf0e10cSrcweir 78*cdf0e10cSrcweir if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed) 79*cdf0e10cSrcweir { 80*cdf0e10cSrcweir // common preparations 81*cdf0e10cSrcweir TextLayouterDevice aTextLayouter; 82*cdf0e10cSrcweir 83*cdf0e10cSrcweir // TextLayouterDevice is needed to get metrics for text decorations like 84*cdf0e10cSrcweir // underline/strikeout/emphasis marks from it. For setup, the font size is needed 85*cdf0e10cSrcweir aTextLayouter.setFontAttribute( 86*cdf0e10cSrcweir getFontAttribute(), 87*cdf0e10cSrcweir rDecTrans.getScale().getX(), 88*cdf0e10cSrcweir rDecTrans.getScale().getY(), 89*cdf0e10cSrcweir getLocale()); 90*cdf0e10cSrcweir 91*cdf0e10cSrcweir // get text width 92*cdf0e10cSrcweir double fTextWidth(0.0); 93*cdf0e10cSrcweir 94*cdf0e10cSrcweir if(rDXArray.empty()) 95*cdf0e10cSrcweir { 96*cdf0e10cSrcweir fTextWidth = aTextLayouter.getTextWidth(rText, aTextPosition, aTextLength); 97*cdf0e10cSrcweir } 98*cdf0e10cSrcweir else 99*cdf0e10cSrcweir { 100*cdf0e10cSrcweir fTextWidth = rDXArray.back() * rDecTrans.getScale().getX(); 101*cdf0e10cSrcweir const double fFontScaleX(rDecTrans.getScale().getX()); 102*cdf0e10cSrcweir 103*cdf0e10cSrcweir if(!basegfx::fTools::equal(fFontScaleX, 1.0) 104*cdf0e10cSrcweir && !basegfx::fTools::equalZero(fFontScaleX)) 105*cdf0e10cSrcweir { 106*cdf0e10cSrcweir // need to take FontScaling out of the DXArray 107*cdf0e10cSrcweir fTextWidth /= fFontScaleX; 108*cdf0e10cSrcweir } 109*cdf0e10cSrcweir } 110*cdf0e10cSrcweir 111*cdf0e10cSrcweir if(bOverlineUsed) 112*cdf0e10cSrcweir { 113*cdf0e10cSrcweir // create primitive geometry for overline 114*cdf0e10cSrcweir rTarget.push_back(Primitive2DReference( 115*cdf0e10cSrcweir new TextLinePrimitive2D( 116*cdf0e10cSrcweir rDecTrans.getB2DHomMatrix(), 117*cdf0e10cSrcweir fTextWidth, 118*cdf0e10cSrcweir aTextLayouter.getOverlineOffset(), 119*cdf0e10cSrcweir aTextLayouter.getOverlineHeight(), 120*cdf0e10cSrcweir getFontOverline(), 121*cdf0e10cSrcweir getOverlineColor()))); 122*cdf0e10cSrcweir } 123*cdf0e10cSrcweir 124*cdf0e10cSrcweir if(bUnderlineUsed) 125*cdf0e10cSrcweir { 126*cdf0e10cSrcweir // create primitive geometry for underline 127*cdf0e10cSrcweir rTarget.push_back(Primitive2DReference( 128*cdf0e10cSrcweir new TextLinePrimitive2D( 129*cdf0e10cSrcweir rDecTrans.getB2DHomMatrix(), 130*cdf0e10cSrcweir fTextWidth, 131*cdf0e10cSrcweir aTextLayouter.getUnderlineOffset(), 132*cdf0e10cSrcweir aTextLayouter.getUnderlineHeight(), 133*cdf0e10cSrcweir getFontUnderline(), 134*cdf0e10cSrcweir getTextlineColor()))); 135*cdf0e10cSrcweir } 136*cdf0e10cSrcweir 137*cdf0e10cSrcweir if(bStrikeoutUsed) 138*cdf0e10cSrcweir { 139*cdf0e10cSrcweir // create primitive geometry for strikeout 140*cdf0e10cSrcweir if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout()) 141*cdf0e10cSrcweir { 142*cdf0e10cSrcweir // strikeout with character 143*cdf0e10cSrcweir const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X'); 144*cdf0e10cSrcweir 145*cdf0e10cSrcweir rTarget.push_back(Primitive2DReference( 146*cdf0e10cSrcweir new TextCharacterStrikeoutPrimitive2D( 147*cdf0e10cSrcweir rDecTrans.getB2DHomMatrix(), 148*cdf0e10cSrcweir fTextWidth, 149*cdf0e10cSrcweir getFontColor(), 150*cdf0e10cSrcweir aStrikeoutChar, 151*cdf0e10cSrcweir getFontAttribute(), 152*cdf0e10cSrcweir getLocale()))); 153*cdf0e10cSrcweir } 154*cdf0e10cSrcweir else 155*cdf0e10cSrcweir { 156*cdf0e10cSrcweir // strikeout with geometry 157*cdf0e10cSrcweir rTarget.push_back(Primitive2DReference( 158*cdf0e10cSrcweir new TextGeometryStrikeoutPrimitive2D( 159*cdf0e10cSrcweir rDecTrans.getB2DHomMatrix(), 160*cdf0e10cSrcweir fTextWidth, 161*cdf0e10cSrcweir getFontColor(), 162*cdf0e10cSrcweir aTextLayouter.getUnderlineHeight(), 163*cdf0e10cSrcweir aTextLayouter.getStrikeoutOffset(), 164*cdf0e10cSrcweir getTextStrikeout()))); 165*cdf0e10cSrcweir } 166*cdf0e10cSrcweir } 167*cdf0e10cSrcweir } 168*cdf0e10cSrcweir 169*cdf0e10cSrcweir // TODO: Handle Font Emphasis Above/Below 170*cdf0e10cSrcweir } 171*cdf0e10cSrcweir 172*cdf0e10cSrcweir void TextDecoratedPortionPrimitive2D::impCorrectTextBoundary(::com::sun::star::i18n::Boundary& rNextWordBoundary) const 173*cdf0e10cSrcweir { 174*cdf0e10cSrcweir // truncate aNextWordBoundary to min/max possible values. This is necessary since the word start may be 175*cdf0e10cSrcweir // before/after getTextPosition() when a long string is the content and getTextPosition() 176*cdf0e10cSrcweir // is right inside a word. Same for end. 177*cdf0e10cSrcweir const sal_Int32 aMinPos(static_cast< sal_Int32 >(getTextPosition())); 178*cdf0e10cSrcweir const sal_Int32 aMaxPos(aMinPos + static_cast< sal_Int32 >(getTextLength())); 179*cdf0e10cSrcweir 180*cdf0e10cSrcweir if(rNextWordBoundary.startPos < aMinPos) 181*cdf0e10cSrcweir { 182*cdf0e10cSrcweir rNextWordBoundary.startPos = aMinPos; 183*cdf0e10cSrcweir } 184*cdf0e10cSrcweir else if(rNextWordBoundary.startPos > aMaxPos) 185*cdf0e10cSrcweir { 186*cdf0e10cSrcweir rNextWordBoundary.startPos = aMaxPos; 187*cdf0e10cSrcweir } 188*cdf0e10cSrcweir 189*cdf0e10cSrcweir if(rNextWordBoundary.endPos < aMinPos) 190*cdf0e10cSrcweir { 191*cdf0e10cSrcweir rNextWordBoundary.endPos = aMinPos; 192*cdf0e10cSrcweir } 193*cdf0e10cSrcweir else if(rNextWordBoundary.endPos > aMaxPos) 194*cdf0e10cSrcweir { 195*cdf0e10cSrcweir rNextWordBoundary.endPos = aMaxPos; 196*cdf0e10cSrcweir } 197*cdf0e10cSrcweir } 198*cdf0e10cSrcweir 199*cdf0e10cSrcweir void TextDecoratedPortionPrimitive2D::impSplitSingleWords( 200*cdf0e10cSrcweir std::vector< Primitive2DReference >& rTarget, 201*cdf0e10cSrcweir basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans) const 202*cdf0e10cSrcweir { 203*cdf0e10cSrcweir // break iterator support 204*cdf0e10cSrcweir // made static so it only needs to be fetched once, even with many single 205*cdf0e10cSrcweir // constructed VclMetafileProcessor2D. It's still incarnated on demand, 206*cdf0e10cSrcweir // but exists for OOo runtime now by purpose. 207*cdf0e10cSrcweir static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xLocalBreakIterator; 208*cdf0e10cSrcweir 209*cdf0e10cSrcweir if(!xLocalBreakIterator.is()) 210*cdf0e10cSrcweir { 211*cdf0e10cSrcweir ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); 212*cdf0e10cSrcweir xLocalBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY); 213*cdf0e10cSrcweir } 214*cdf0e10cSrcweir 215*cdf0e10cSrcweir if(xLocalBreakIterator.is() && getTextLength()) 216*cdf0e10cSrcweir { 217*cdf0e10cSrcweir // init word iterator, get first word and truncate to possibilities 218*cdf0e10cSrcweir ::com::sun::star::i18n::Boundary aNextWordBoundary(xLocalBreakIterator->getWordBoundary( 219*cdf0e10cSrcweir getText(), getTextPosition(), getLocale(), ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True)); 220*cdf0e10cSrcweir 221*cdf0e10cSrcweir if(aNextWordBoundary.endPos == getTextPosition()) 222*cdf0e10cSrcweir { 223*cdf0e10cSrcweir // backward hit, force next word 224*cdf0e10cSrcweir aNextWordBoundary = xLocalBreakIterator->getWordBoundary( 225*cdf0e10cSrcweir getText(), getTextPosition() + 1, getLocale(), ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True); 226*cdf0e10cSrcweir } 227*cdf0e10cSrcweir 228*cdf0e10cSrcweir impCorrectTextBoundary(aNextWordBoundary); 229*cdf0e10cSrcweir 230*cdf0e10cSrcweir // prepare new font attributes WITHOUT outline 231*cdf0e10cSrcweir const attribute::FontAttribute aNewFontAttribute( 232*cdf0e10cSrcweir getFontAttribute().getFamilyName(), 233*cdf0e10cSrcweir getFontAttribute().getStyleName(), 234*cdf0e10cSrcweir getFontAttribute().getWeight(), 235*cdf0e10cSrcweir getFontAttribute().getSymbol(), 236*cdf0e10cSrcweir getFontAttribute().getVertical(), 237*cdf0e10cSrcweir getFontAttribute().getItalic(), 238*cdf0e10cSrcweir false, // no outline anymore, handled locally 239*cdf0e10cSrcweir getFontAttribute().getRTL(), 240*cdf0e10cSrcweir getFontAttribute().getBiDiStrong()); 241*cdf0e10cSrcweir 242*cdf0e10cSrcweir if(aNextWordBoundary.startPos == getTextPosition() && aNextWordBoundary.endPos == getTextLength()) 243*cdf0e10cSrcweir { 244*cdf0e10cSrcweir // it IS only a single word, handle as one word 245*cdf0e10cSrcweir impCreateGeometryContent(rTarget, rDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute); 246*cdf0e10cSrcweir } 247*cdf0e10cSrcweir else 248*cdf0e10cSrcweir { 249*cdf0e10cSrcweir // prepare TextLayouter 250*cdf0e10cSrcweir const bool bNoDXArray(getDXArray().empty()); 251*cdf0e10cSrcweir TextLayouterDevice aTextLayouter; 252*cdf0e10cSrcweir 253*cdf0e10cSrcweir if(bNoDXArray) 254*cdf0e10cSrcweir { 255*cdf0e10cSrcweir // ..but only completely when no DXArray 256*cdf0e10cSrcweir aTextLayouter.setFontAttribute( 257*cdf0e10cSrcweir getFontAttribute(), 258*cdf0e10cSrcweir rDecTrans.getScale().getX(), 259*cdf0e10cSrcweir rDecTrans.getScale().getY(), 260*cdf0e10cSrcweir getLocale()); 261*cdf0e10cSrcweir } 262*cdf0e10cSrcweir 263*cdf0e10cSrcweir // do iterate over single words 264*cdf0e10cSrcweir while(aNextWordBoundary.startPos != aNextWordBoundary.endPos) 265*cdf0e10cSrcweir { 266*cdf0e10cSrcweir // prepare values for new portion 267*cdf0e10cSrcweir const xub_StrLen nNewTextStart(static_cast< xub_StrLen >(aNextWordBoundary.startPos)); 268*cdf0e10cSrcweir const xub_StrLen nNewTextEnd(static_cast< xub_StrLen >(aNextWordBoundary.endPos)); 269*cdf0e10cSrcweir 270*cdf0e10cSrcweir // prepare transform for the single word 271*cdf0e10cSrcweir basegfx::B2DHomMatrix aNewTransform; 272*cdf0e10cSrcweir ::std::vector< double > aNewDXArray; 273*cdf0e10cSrcweir const bool bNewStartIsNotOldStart(nNewTextStart > getTextPosition()); 274*cdf0e10cSrcweir 275*cdf0e10cSrcweir if(!bNoDXArray) 276*cdf0e10cSrcweir { 277*cdf0e10cSrcweir // prepare new DXArray for the single word 278*cdf0e10cSrcweir aNewDXArray = ::std::vector< double >( 279*cdf0e10cSrcweir getDXArray().begin() + static_cast< sal_uInt32 >(nNewTextStart - getTextPosition()), 280*cdf0e10cSrcweir getDXArray().begin() + static_cast< sal_uInt32 >(nNewTextEnd - getTextPosition())); 281*cdf0e10cSrcweir } 282*cdf0e10cSrcweir 283*cdf0e10cSrcweir if(bNewStartIsNotOldStart) 284*cdf0e10cSrcweir { 285*cdf0e10cSrcweir // needs to be moved to a new start position 286*cdf0e10cSrcweir double fOffset(0.0); 287*cdf0e10cSrcweir 288*cdf0e10cSrcweir if(bNoDXArray) 289*cdf0e10cSrcweir { 290*cdf0e10cSrcweir // evaluate using TextLayouter 291*cdf0e10cSrcweir fOffset = aTextLayouter.getTextWidth(getText(), getTextPosition(), nNewTextStart); 292*cdf0e10cSrcweir } 293*cdf0e10cSrcweir else 294*cdf0e10cSrcweir { 295*cdf0e10cSrcweir // get from DXArray 296*cdf0e10cSrcweir const sal_uInt32 nIndex(static_cast< sal_uInt32 >(nNewTextStart - getTextPosition())); 297*cdf0e10cSrcweir fOffset = getDXArray()[nIndex - 1]; 298*cdf0e10cSrcweir } 299*cdf0e10cSrcweir 300*cdf0e10cSrcweir // need offset without FontScale for building the new transformation. The 301*cdf0e10cSrcweir // new transformation will be multiplied with the current text transformation 302*cdf0e10cSrcweir // so FontScale would be double 303*cdf0e10cSrcweir double fOffsetNoScale(fOffset); 304*cdf0e10cSrcweir const double fFontScaleX(rDecTrans.getScale().getX()); 305*cdf0e10cSrcweir 306*cdf0e10cSrcweir if(!basegfx::fTools::equal(fFontScaleX, 1.0) 307*cdf0e10cSrcweir && !basegfx::fTools::equalZero(fFontScaleX)) 308*cdf0e10cSrcweir { 309*cdf0e10cSrcweir fOffsetNoScale /= fFontScaleX; 310*cdf0e10cSrcweir } 311*cdf0e10cSrcweir 312*cdf0e10cSrcweir // apply needed offset to transformation 313*cdf0e10cSrcweir aNewTransform.translate(fOffsetNoScale, 0.0); 314*cdf0e10cSrcweir 315*cdf0e10cSrcweir if(!bNoDXArray) 316*cdf0e10cSrcweir { 317*cdf0e10cSrcweir // DXArray values need to be corrected with the offset, too. Here, 318*cdf0e10cSrcweir // take the scaled offset since the DXArray is scaled 319*cdf0e10cSrcweir const sal_uInt32 nArraySize(aNewDXArray.size()); 320*cdf0e10cSrcweir 321*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nArraySize; a++) 322*cdf0e10cSrcweir { 323*cdf0e10cSrcweir aNewDXArray[a] -= fOffset; 324*cdf0e10cSrcweir } 325*cdf0e10cSrcweir } 326*cdf0e10cSrcweir } 327*cdf0e10cSrcweir 328*cdf0e10cSrcweir // add text transformation to new transformation 329*cdf0e10cSrcweir aNewTransform *= rDecTrans.getB2DHomMatrix(); 330*cdf0e10cSrcweir 331*cdf0e10cSrcweir // create geometry content for the single word. Do not forget 332*cdf0e10cSrcweir // to use the new transformation 333*cdf0e10cSrcweir basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(aNewTransform); 334*cdf0e10cSrcweir 335*cdf0e10cSrcweir impCreateGeometryContent(rTarget, aDecTrans, getText(), nNewTextStart, 336*cdf0e10cSrcweir nNewTextEnd - nNewTextStart, aNewDXArray, aNewFontAttribute); 337*cdf0e10cSrcweir 338*cdf0e10cSrcweir if(aNextWordBoundary.endPos >= getTextPosition() + getTextLength()) 339*cdf0e10cSrcweir { 340*cdf0e10cSrcweir // end reached 341*cdf0e10cSrcweir aNextWordBoundary.startPos = aNextWordBoundary.endPos; 342*cdf0e10cSrcweir } 343*cdf0e10cSrcweir else 344*cdf0e10cSrcweir { 345*cdf0e10cSrcweir // get new word portion 346*cdf0e10cSrcweir const sal_Int32 nLastEndPos(aNextWordBoundary.endPos); 347*cdf0e10cSrcweir 348*cdf0e10cSrcweir aNextWordBoundary = xLocalBreakIterator->getWordBoundary( 349*cdf0e10cSrcweir getText(), aNextWordBoundary.endPos, getLocale(), 350*cdf0e10cSrcweir ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True); 351*cdf0e10cSrcweir 352*cdf0e10cSrcweir if(nLastEndPos == aNextWordBoundary.endPos) 353*cdf0e10cSrcweir { 354*cdf0e10cSrcweir // backward hit, force next word 355*cdf0e10cSrcweir aNextWordBoundary = xLocalBreakIterator->getWordBoundary( 356*cdf0e10cSrcweir getText(), nLastEndPos + 1, getLocale(), 357*cdf0e10cSrcweir ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True); 358*cdf0e10cSrcweir } 359*cdf0e10cSrcweir 360*cdf0e10cSrcweir impCorrectTextBoundary(aNextWordBoundary); 361*cdf0e10cSrcweir } 362*cdf0e10cSrcweir } 363*cdf0e10cSrcweir } 364*cdf0e10cSrcweir } 365*cdf0e10cSrcweir } 366*cdf0e10cSrcweir 367*cdf0e10cSrcweir Primitive2DSequence TextDecoratedPortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 368*cdf0e10cSrcweir { 369*cdf0e10cSrcweir std::vector< Primitive2DReference > aNewPrimitives; 370*cdf0e10cSrcweir basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform()); 371*cdf0e10cSrcweir Primitive2DSequence aRetval; 372*cdf0e10cSrcweir 373*cdf0e10cSrcweir // create basic geometry such as SimpleTextPrimitive, Overline, Underline, 374*cdf0e10cSrcweir // Strikeout, etc... 375*cdf0e10cSrcweir if(getWordLineMode()) 376*cdf0e10cSrcweir { 377*cdf0e10cSrcweir // support for single word mode 378*cdf0e10cSrcweir impSplitSingleWords(aNewPrimitives, aDecTrans); 379*cdf0e10cSrcweir } 380*cdf0e10cSrcweir else 381*cdf0e10cSrcweir { 382*cdf0e10cSrcweir // prepare new font attributes WITHOUT outline 383*cdf0e10cSrcweir const attribute::FontAttribute aNewFontAttribute( 384*cdf0e10cSrcweir getFontAttribute().getFamilyName(), 385*cdf0e10cSrcweir getFontAttribute().getStyleName(), 386*cdf0e10cSrcweir getFontAttribute().getWeight(), 387*cdf0e10cSrcweir getFontAttribute().getSymbol(), 388*cdf0e10cSrcweir getFontAttribute().getVertical(), 389*cdf0e10cSrcweir getFontAttribute().getItalic(), 390*cdf0e10cSrcweir false, // no outline anymore, handled locally 391*cdf0e10cSrcweir getFontAttribute().getRTL(), 392*cdf0e10cSrcweir getFontAttribute().getBiDiStrong()); 393*cdf0e10cSrcweir 394*cdf0e10cSrcweir // handle as one word 395*cdf0e10cSrcweir impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute); 396*cdf0e10cSrcweir } 397*cdf0e10cSrcweir 398*cdf0e10cSrcweir // convert to Primitive2DSequence 399*cdf0e10cSrcweir const sal_uInt32 nMemberCount(aNewPrimitives.size()); 400*cdf0e10cSrcweir 401*cdf0e10cSrcweir if(nMemberCount) 402*cdf0e10cSrcweir { 403*cdf0e10cSrcweir aRetval.realloc(nMemberCount); 404*cdf0e10cSrcweir 405*cdf0e10cSrcweir for(sal_uInt32 a(0); a < nMemberCount; a++) 406*cdf0e10cSrcweir { 407*cdf0e10cSrcweir aRetval[a] = aNewPrimitives[a]; 408*cdf0e10cSrcweir } 409*cdf0e10cSrcweir } 410*cdf0e10cSrcweir 411*cdf0e10cSrcweir // Handle Shadow, Outline and TextRelief 412*cdf0e10cSrcweir if(aRetval.hasElements()) 413*cdf0e10cSrcweir { 414*cdf0e10cSrcweir // outline AND shadow depend on NO TextRelief (see dialog) 415*cdf0e10cSrcweir const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief()); 416*cdf0e10cSrcweir const bool bHasShadow(!bHasTextRelief && getShadow()); 417*cdf0e10cSrcweir const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline()); 418*cdf0e10cSrcweir 419*cdf0e10cSrcweir if(bHasShadow || bHasTextRelief || bHasOutline) 420*cdf0e10cSrcweir { 421*cdf0e10cSrcweir Primitive2DReference aShadow; 422*cdf0e10cSrcweir 423*cdf0e10cSrcweir if(bHasShadow) 424*cdf0e10cSrcweir { 425*cdf0e10cSrcweir // create shadow with current content (in aRetval). Text shadow 426*cdf0e10cSrcweir // is constant, relative to font size, rotated with the text and has a 427*cdf0e10cSrcweir // constant color. 428*cdf0e10cSrcweir // shadow parameter values 429*cdf0e10cSrcweir static double fFactor(1.0 / 24.0); 430*cdf0e10cSrcweir const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor); 431*cdf0e10cSrcweir static basegfx::BColor aShadowColor(0.3, 0.3, 0.3); 432*cdf0e10cSrcweir 433*cdf0e10cSrcweir // preapare shadow transform matrix 434*cdf0e10cSrcweir const basegfx::B2DHomMatrix aShadowTransform(basegfx::tools::createTranslateB2DHomMatrix( 435*cdf0e10cSrcweir fTextShadowOffset, fTextShadowOffset)); 436*cdf0e10cSrcweir 437*cdf0e10cSrcweir // create shadow primitive 438*cdf0e10cSrcweir aShadow = Primitive2DReference(new ShadowPrimitive2D( 439*cdf0e10cSrcweir aShadowTransform, 440*cdf0e10cSrcweir aShadowColor, 441*cdf0e10cSrcweir aRetval)); 442*cdf0e10cSrcweir } 443*cdf0e10cSrcweir 444*cdf0e10cSrcweir if(bHasTextRelief) 445*cdf0e10cSrcweir { 446*cdf0e10cSrcweir // create emboss using an own helper primitive since this will 447*cdf0e10cSrcweir // be view-dependent 448*cdf0e10cSrcweir const basegfx::BColor aBBlack(0.0, 0.0, 0.0); 449*cdf0e10cSrcweir const bool bDefaultTextColor(aBBlack == getFontColor()); 450*cdf0e10cSrcweir TextEffectStyle2D aTextEffectStyle2D(TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED); 451*cdf0e10cSrcweir 452*cdf0e10cSrcweir if(bDefaultTextColor) 453*cdf0e10cSrcweir { 454*cdf0e10cSrcweir if(TEXT_RELIEF_ENGRAVED == getTextRelief()) 455*cdf0e10cSrcweir { 456*cdf0e10cSrcweir aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED_DEFAULT; 457*cdf0e10cSrcweir } 458*cdf0e10cSrcweir else 459*cdf0e10cSrcweir { 460*cdf0e10cSrcweir aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED_DEFAULT; 461*cdf0e10cSrcweir } 462*cdf0e10cSrcweir } 463*cdf0e10cSrcweir else 464*cdf0e10cSrcweir { 465*cdf0e10cSrcweir if(TEXT_RELIEF_ENGRAVED == getTextRelief()) 466*cdf0e10cSrcweir { 467*cdf0e10cSrcweir aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED; 468*cdf0e10cSrcweir } 469*cdf0e10cSrcweir else 470*cdf0e10cSrcweir { 471*cdf0e10cSrcweir aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED; 472*cdf0e10cSrcweir } 473*cdf0e10cSrcweir } 474*cdf0e10cSrcweir 475*cdf0e10cSrcweir Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( 476*cdf0e10cSrcweir aRetval, 477*cdf0e10cSrcweir aDecTrans.getTranslate(), 478*cdf0e10cSrcweir aDecTrans.getRotate(), 479*cdf0e10cSrcweir aTextEffectStyle2D)); 480*cdf0e10cSrcweir aRetval = Primitive2DSequence(&aNewTextEffect, 1); 481*cdf0e10cSrcweir } 482*cdf0e10cSrcweir else if(bHasOutline) 483*cdf0e10cSrcweir { 484*cdf0e10cSrcweir // create outline using an own helper primitive since this will 485*cdf0e10cSrcweir // be view-dependent 486*cdf0e10cSrcweir Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( 487*cdf0e10cSrcweir aRetval, 488*cdf0e10cSrcweir aDecTrans.getTranslate(), 489*cdf0e10cSrcweir aDecTrans.getRotate(), 490*cdf0e10cSrcweir TEXTEFFECTSTYLE2D_OUTLINE)); 491*cdf0e10cSrcweir aRetval = Primitive2DSequence(&aNewTextEffect, 1); 492*cdf0e10cSrcweir } 493*cdf0e10cSrcweir 494*cdf0e10cSrcweir if(aShadow.is()) 495*cdf0e10cSrcweir { 496*cdf0e10cSrcweir // put shadow in front if there is one to paint timely before 497*cdf0e10cSrcweir // but placed behind content 498*cdf0e10cSrcweir const Primitive2DSequence aContent(aRetval); 499*cdf0e10cSrcweir aRetval = Primitive2DSequence(&aShadow, 1); 500*cdf0e10cSrcweir appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aContent); 501*cdf0e10cSrcweir } 502*cdf0e10cSrcweir } 503*cdf0e10cSrcweir } 504*cdf0e10cSrcweir 505*cdf0e10cSrcweir return aRetval; 506*cdf0e10cSrcweir } 507*cdf0e10cSrcweir 508*cdf0e10cSrcweir TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D( 509*cdf0e10cSrcweir 510*cdf0e10cSrcweir // TextSimplePortionPrimitive2D parameters 511*cdf0e10cSrcweir const basegfx::B2DHomMatrix& rNewTransform, 512*cdf0e10cSrcweir const String& rText, 513*cdf0e10cSrcweir xub_StrLen aTextPosition, 514*cdf0e10cSrcweir xub_StrLen aTextLength, 515*cdf0e10cSrcweir const ::std::vector< double >& rDXArray, 516*cdf0e10cSrcweir const attribute::FontAttribute& rFontAttribute, 517*cdf0e10cSrcweir const ::com::sun::star::lang::Locale& rLocale, 518*cdf0e10cSrcweir const basegfx::BColor& rFontColor, 519*cdf0e10cSrcweir 520*cdf0e10cSrcweir // local parameters 521*cdf0e10cSrcweir const basegfx::BColor& rOverlineColor, 522*cdf0e10cSrcweir const basegfx::BColor& rTextlineColor, 523*cdf0e10cSrcweir TextLine eFontOverline, 524*cdf0e10cSrcweir TextLine eFontUnderline, 525*cdf0e10cSrcweir bool bUnderlineAbove, 526*cdf0e10cSrcweir TextStrikeout eTextStrikeout, 527*cdf0e10cSrcweir bool bWordLineMode, 528*cdf0e10cSrcweir TextEmphasisMark eTextEmphasisMark, 529*cdf0e10cSrcweir bool bEmphasisMarkAbove, 530*cdf0e10cSrcweir bool bEmphasisMarkBelow, 531*cdf0e10cSrcweir TextRelief eTextRelief, 532*cdf0e10cSrcweir bool bShadow) 533*cdf0e10cSrcweir : TextSimplePortionPrimitive2D(rNewTransform, rText, aTextPosition, aTextLength, rDXArray, rFontAttribute, rLocale, rFontColor), 534*cdf0e10cSrcweir maOverlineColor(rOverlineColor), 535*cdf0e10cSrcweir maTextlineColor(rTextlineColor), 536*cdf0e10cSrcweir meFontOverline(eFontOverline), 537*cdf0e10cSrcweir meFontUnderline(eFontUnderline), 538*cdf0e10cSrcweir meTextStrikeout(eTextStrikeout), 539*cdf0e10cSrcweir meTextEmphasisMark(eTextEmphasisMark), 540*cdf0e10cSrcweir meTextRelief(eTextRelief), 541*cdf0e10cSrcweir mbUnderlineAbove(bUnderlineAbove), 542*cdf0e10cSrcweir mbWordLineMode(bWordLineMode), 543*cdf0e10cSrcweir mbEmphasisMarkAbove(bEmphasisMarkAbove), 544*cdf0e10cSrcweir mbEmphasisMarkBelow(bEmphasisMarkBelow), 545*cdf0e10cSrcweir mbShadow(bShadow) 546*cdf0e10cSrcweir { 547*cdf0e10cSrcweir } 548*cdf0e10cSrcweir 549*cdf0e10cSrcweir bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 550*cdf0e10cSrcweir { 551*cdf0e10cSrcweir if(TextSimplePortionPrimitive2D::operator==(rPrimitive)) 552*cdf0e10cSrcweir { 553*cdf0e10cSrcweir const TextDecoratedPortionPrimitive2D& rCompare = (TextDecoratedPortionPrimitive2D&)rPrimitive; 554*cdf0e10cSrcweir 555*cdf0e10cSrcweir return (getOverlineColor() == rCompare.getOverlineColor() 556*cdf0e10cSrcweir && getTextlineColor() == rCompare.getTextlineColor() 557*cdf0e10cSrcweir && getFontOverline() == rCompare.getFontOverline() 558*cdf0e10cSrcweir && getFontUnderline() == rCompare.getFontUnderline() 559*cdf0e10cSrcweir && getTextStrikeout() == rCompare.getTextStrikeout() 560*cdf0e10cSrcweir && getTextEmphasisMark() == rCompare.getTextEmphasisMark() 561*cdf0e10cSrcweir && getTextRelief() == rCompare.getTextRelief() 562*cdf0e10cSrcweir && getUnderlineAbove() == rCompare.getUnderlineAbove() 563*cdf0e10cSrcweir && getWordLineMode() == rCompare.getWordLineMode() 564*cdf0e10cSrcweir && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove() 565*cdf0e10cSrcweir && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow() 566*cdf0e10cSrcweir && getShadow() == rCompare.getShadow()); 567*cdf0e10cSrcweir } 568*cdf0e10cSrcweir 569*cdf0e10cSrcweir return false; 570*cdf0e10cSrcweir } 571*cdf0e10cSrcweir 572*cdf0e10cSrcweir // #i96475# 573*cdf0e10cSrcweir // Added missing implementation. Decorations may (will) stick out of the text's 574*cdf0e10cSrcweir // inking area, so add them if needed 575*cdf0e10cSrcweir basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const 576*cdf0e10cSrcweir { 577*cdf0e10cSrcweir const bool bDecoratedIsNeeded( 578*cdf0e10cSrcweir TEXT_LINE_NONE != getFontOverline() 579*cdf0e10cSrcweir || TEXT_LINE_NONE != getFontUnderline() 580*cdf0e10cSrcweir || TEXT_STRIKEOUT_NONE != getTextStrikeout() 581*cdf0e10cSrcweir || TEXT_EMPHASISMARK_NONE != getTextEmphasisMark() 582*cdf0e10cSrcweir || TEXT_RELIEF_NONE != getTextRelief() 583*cdf0e10cSrcweir || getShadow()); 584*cdf0e10cSrcweir 585*cdf0e10cSrcweir if(bDecoratedIsNeeded) 586*cdf0e10cSrcweir { 587*cdf0e10cSrcweir // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses 588*cdf0e10cSrcweir // the own local decomposition for computation and thus creates all necessary 589*cdf0e10cSrcweir // geometric objects 590*cdf0e10cSrcweir return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); 591*cdf0e10cSrcweir } 592*cdf0e10cSrcweir else 593*cdf0e10cSrcweir { 594*cdf0e10cSrcweir // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange 595*cdf0e10cSrcweir return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation); 596*cdf0e10cSrcweir } 597*cdf0e10cSrcweir } 598*cdf0e10cSrcweir 599*cdf0e10cSrcweir // provide unique ID 600*cdf0e10cSrcweir ImplPrimitrive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D) 601*cdf0e10cSrcweir 602*cdf0e10cSrcweir } // end of namespace primitive2d 603*cdf0e10cSrcweir } // end of namespace drawinglayer 604*cdf0e10cSrcweir 605*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 606*cdf0e10cSrcweir // eof 607