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