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/textdecoratedprimitive2d.hxx> 28 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 29 #include <drawinglayer/attribute/strokeattribute.hxx> 30 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> 31 #include <basegfx/matrix/b2dhommatrixtools.hxx> 32 #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx> 33 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> 34 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 35 #include <drawinglayer/primitive2d/textlineprimitive2d.hxx> 36 #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx> 37 #include <drawinglayer/primitive2d/textbreakuphelper.hxx> 38 39 ////////////////////////////////////////////////////////////////////////////// 40 41 namespace drawinglayer 42 { 43 namespace primitive2d 44 { impCreateGeometryContent(std::vector<Primitive2DReference> & rTarget,basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose & rDecTrans,const String & rText,xub_StrLen aTextPosition,xub_StrLen aTextLength,const::std::vector<double> & rDXArray,const attribute::FontAttribute & rFontAttribute) const45 void TextDecoratedPortionPrimitive2D::impCreateGeometryContent( 46 std::vector< Primitive2DReference >& rTarget, 47 basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans, 48 const String& rText, 49 xub_StrLen aTextPosition, 50 xub_StrLen aTextLength, 51 const ::std::vector< double >& rDXArray, 52 const attribute::FontAttribute& rFontAttribute) const 53 { 54 // create the SimpleTextPrimitive needed in any case 55 rTarget.push_back(Primitive2DReference( 56 new TextSimplePortionPrimitive2D( 57 rDecTrans.getB2DHomMatrix(), 58 rText, 59 aTextPosition, 60 aTextLength, 61 rDXArray, 62 rFontAttribute, 63 getLocale(), 64 getFontColor()))); 65 66 // see if something else needs to be done 67 const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline()); 68 const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline()); 69 const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout()); 70 71 if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed) 72 { 73 // common preparations 74 TextLayouterDevice aTextLayouter; 75 76 // TextLayouterDevice is needed to get metrics for text decorations like 77 // underline/strikeout/emphasis marks from it. For setup, the font size is needed 78 aTextLayouter.setFontAttribute( 79 getFontAttribute(), 80 rDecTrans.getScale().getX(), 81 rDecTrans.getScale().getY(), 82 getLocale()); 83 84 // get text width 85 double fTextWidth(0.0); 86 87 if(rDXArray.empty()) 88 { 89 fTextWidth = aTextLayouter.getTextWidth(rText, aTextPosition, aTextLength); 90 } 91 else 92 { 93 fTextWidth = rDXArray.back() * rDecTrans.getScale().getX(); 94 const double fFontScaleX(rDecTrans.getScale().getX()); 95 96 if(!basegfx::fTools::equal(fFontScaleX, 1.0) 97 && !basegfx::fTools::equalZero(fFontScaleX)) 98 { 99 // need to take FontScaling out of the DXArray 100 fTextWidth /= fFontScaleX; 101 } 102 } 103 104 if(bOverlineUsed) 105 { 106 // create primitive geometry for overline 107 rTarget.push_back(Primitive2DReference( 108 new TextLinePrimitive2D( 109 rDecTrans.getB2DHomMatrix(), 110 fTextWidth, 111 aTextLayouter.getOverlineOffset(), 112 aTextLayouter.getOverlineHeight(), 113 getFontOverline(), 114 getOverlineColor()))); 115 } 116 117 if(bUnderlineUsed) 118 { 119 // create primitive geometry for underline 120 rTarget.push_back(Primitive2DReference( 121 new TextLinePrimitive2D( 122 rDecTrans.getB2DHomMatrix(), 123 fTextWidth, 124 aTextLayouter.getUnderlineOffset(), 125 aTextLayouter.getUnderlineHeight(), 126 getFontUnderline(), 127 getTextlineColor()))); 128 } 129 130 if(bStrikeoutUsed) 131 { 132 // create primitive geometry for strikeout 133 if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout()) 134 { 135 // strikeout with character 136 const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X'); 137 138 rTarget.push_back(Primitive2DReference( 139 new TextCharacterStrikeoutPrimitive2D( 140 rDecTrans.getB2DHomMatrix(), 141 fTextWidth, 142 getFontColor(), 143 aStrikeoutChar, 144 getFontAttribute(), 145 getLocale()))); 146 } 147 else 148 { 149 // strikeout with geometry 150 rTarget.push_back(Primitive2DReference( 151 new TextGeometryStrikeoutPrimitive2D( 152 rDecTrans.getB2DHomMatrix(), 153 fTextWidth, 154 getFontColor(), 155 aTextLayouter.getUnderlineHeight(), 156 aTextLayouter.getStrikeoutOffset(), 157 getTextStrikeout()))); 158 } 159 } 160 } 161 162 // TODO: Handle Font Emphasis Above/Below 163 } 164 create2DDecomposition(const geometry::ViewInformation2D &) const165 Primitive2DSequence TextDecoratedPortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 166 { 167 if(getWordLineMode()) 168 { 169 // support for single word mode; split to single word primitives 170 // using TextBreakupHelper 171 const TextBreakupHelper aTextBreakupHelper(*this); 172 const Primitive2DSequence aBroken(aTextBreakupHelper.getResult(BreakupUnit_word)); 173 174 if(aBroken.hasElements()) 175 { 176 // was indeed split to several words, use as result 177 return aBroken; 178 } 179 else 180 { 181 // no split, was already a single word. Continue to 182 // decompse local entity 183 } 184 } 185 std::vector< Primitive2DReference > aNewPrimitives; 186 basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform()); 187 Primitive2DSequence aRetval; 188 189 // create basic geometry such as SimpleTextPrimitive, Overline, Underline, 190 // Strikeout, etc... 191 // prepare new font attributes WITHOUT outline 192 const attribute::FontAttribute aNewFontAttribute( 193 getFontAttribute().getFamilyName(), 194 getFontAttribute().getStyleName(), 195 getFontAttribute().getWeight(), 196 getFontAttribute().getSymbol(), 197 getFontAttribute().getVertical(), 198 getFontAttribute().getItalic(), 199 false, // no outline anymore, handled locally 200 getFontAttribute().getRTL(), 201 getFontAttribute().getBiDiStrong()); 202 203 // handle as one word 204 impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute); 205 206 // convert to Primitive2DSequence 207 const sal_uInt32 nMemberCount(aNewPrimitives.size()); 208 209 if(nMemberCount) 210 { 211 aRetval.realloc(nMemberCount); 212 213 for(sal_uInt32 a(0); a < nMemberCount; a++) 214 { 215 aRetval[a] = aNewPrimitives[a]; 216 } 217 } 218 219 // Handle Shadow, Outline and TextRelief 220 if(aRetval.hasElements()) 221 { 222 // outline AND shadow depend on NO TextRelief (see dialog) 223 const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief()); 224 const bool bHasShadow(!bHasTextRelief && getShadow()); 225 const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline()); 226 227 if(bHasShadow || bHasTextRelief || bHasOutline) 228 { 229 Primitive2DReference aShadow; 230 231 if(bHasShadow) 232 { 233 // create shadow with current content (in aRetval). Text shadow 234 // is constant, relative to font size, rotated with the text and has a 235 // constant color. 236 // shadow parameter values 237 static double fFactor(1.0 / 24.0); 238 const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor); 239 static basegfx::BColor aShadowColor(0.3, 0.3, 0.3); 240 241 // preapare shadow transform matrix 242 const basegfx::B2DHomMatrix aShadowTransform(basegfx::tools::createTranslateB2DHomMatrix( 243 fTextShadowOffset, fTextShadowOffset)); 244 245 // create shadow primitive 246 aShadow = Primitive2DReference(new ShadowPrimitive2D( 247 aShadowTransform, 248 aShadowColor, 249 aRetval)); 250 } 251 252 if(bHasTextRelief) 253 { 254 // create emboss using an own helper primitive since this will 255 // be view-dependent 256 const basegfx::BColor aBBlack(0.0, 0.0, 0.0); 257 const bool bDefaultTextColor(aBBlack == getFontColor()); 258 TextEffectStyle2D aTextEffectStyle2D(TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED); 259 260 if(bDefaultTextColor) 261 { 262 if(TEXT_RELIEF_ENGRAVED == getTextRelief()) 263 { 264 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED_DEFAULT; 265 } 266 else 267 { 268 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED_DEFAULT; 269 } 270 } 271 else 272 { 273 if(TEXT_RELIEF_ENGRAVED == getTextRelief()) 274 { 275 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED; 276 } 277 else 278 { 279 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED; 280 } 281 } 282 283 Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( 284 aRetval, 285 aDecTrans.getTranslate(), 286 aDecTrans.getRotate(), 287 aTextEffectStyle2D)); 288 aRetval = Primitive2DSequence(&aNewTextEffect, 1); 289 } 290 else if(bHasOutline) 291 { 292 // create outline using an own helper primitive since this will 293 // be view-dependent 294 Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( 295 aRetval, 296 aDecTrans.getTranslate(), 297 aDecTrans.getRotate(), 298 TEXTEFFECTSTYLE2D_OUTLINE)); 299 aRetval = Primitive2DSequence(&aNewTextEffect, 1); 300 } 301 302 if(aShadow.is()) 303 { 304 // put shadow in front if there is one to paint timely before 305 // but placed behind content 306 const Primitive2DSequence aContent(aRetval); 307 aRetval = Primitive2DSequence(&aShadow, 1); 308 appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aContent); 309 } 310 } 311 } 312 313 return aRetval; 314 } 315 TextDecoratedPortionPrimitive2D(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,const basegfx::BColor & rOverlineColor,const basegfx::BColor & rTextlineColor,TextLine eFontOverline,TextLine eFontUnderline,bool bUnderlineAbove,TextStrikeout eTextStrikeout,bool bWordLineMode,TextEmphasisMark eTextEmphasisMark,bool bEmphasisMarkAbove,bool bEmphasisMarkBelow,TextRelief eTextRelief,bool bShadow)316 TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D( 317 318 // TextSimplePortionPrimitive2D parameters 319 const basegfx::B2DHomMatrix& rNewTransform, 320 const String& rText, 321 xub_StrLen aTextPosition, 322 xub_StrLen aTextLength, 323 const ::std::vector< double >& rDXArray, 324 const attribute::FontAttribute& rFontAttribute, 325 const ::com::sun::star::lang::Locale& rLocale, 326 const basegfx::BColor& rFontColor, 327 328 // local parameters 329 const basegfx::BColor& rOverlineColor, 330 const basegfx::BColor& rTextlineColor, 331 TextLine eFontOverline, 332 TextLine eFontUnderline, 333 bool bUnderlineAbove, 334 TextStrikeout eTextStrikeout, 335 bool bWordLineMode, 336 TextEmphasisMark eTextEmphasisMark, 337 bool bEmphasisMarkAbove, 338 bool bEmphasisMarkBelow, 339 TextRelief eTextRelief, 340 bool bShadow) 341 : TextSimplePortionPrimitive2D(rNewTransform, rText, aTextPosition, aTextLength, rDXArray, rFontAttribute, rLocale, rFontColor), 342 maOverlineColor(rOverlineColor), 343 maTextlineColor(rTextlineColor), 344 meFontOverline(eFontOverline), 345 meFontUnderline(eFontUnderline), 346 meTextStrikeout(eTextStrikeout), 347 meTextEmphasisMark(eTextEmphasisMark), 348 meTextRelief(eTextRelief), 349 mbUnderlineAbove(bUnderlineAbove), 350 mbWordLineMode(bWordLineMode), 351 mbEmphasisMarkAbove(bEmphasisMarkAbove), 352 mbEmphasisMarkBelow(bEmphasisMarkBelow), 353 mbShadow(bShadow) 354 { 355 } 356 decoratedIsNeeded() const357 bool TextDecoratedPortionPrimitive2D::decoratedIsNeeded() const 358 { 359 return (TEXT_LINE_NONE != getFontOverline() 360 || TEXT_LINE_NONE != getFontUnderline() 361 || TEXT_STRIKEOUT_NONE != getTextStrikeout() 362 || TEXT_EMPHASISMARK_NONE != getTextEmphasisMark() 363 || TEXT_RELIEF_NONE != getTextRelief() 364 || getShadow()); 365 } 366 operator ==(const BasePrimitive2D & rPrimitive) const367 bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 368 { 369 if(TextSimplePortionPrimitive2D::operator==(rPrimitive)) 370 { 371 const TextDecoratedPortionPrimitive2D& rCompare = (TextDecoratedPortionPrimitive2D&)rPrimitive; 372 373 return (getOverlineColor() == rCompare.getOverlineColor() 374 && getTextlineColor() == rCompare.getTextlineColor() 375 && getFontOverline() == rCompare.getFontOverline() 376 && getFontUnderline() == rCompare.getFontUnderline() 377 && getTextStrikeout() == rCompare.getTextStrikeout() 378 && getTextEmphasisMark() == rCompare.getTextEmphasisMark() 379 && getTextRelief() == rCompare.getTextRelief() 380 && getUnderlineAbove() == rCompare.getUnderlineAbove() 381 && getWordLineMode() == rCompare.getWordLineMode() 382 && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove() 383 && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow() 384 && getShadow() == rCompare.getShadow()); 385 } 386 387 return false; 388 } 389 390 // #i96475# 391 // Added missing implementation. Decorations may (will) stick out of the text's 392 // inking area, so add them if needed getB2DRange(const geometry::ViewInformation2D & rViewInformation) const393 basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const 394 { 395 if(decoratedIsNeeded()) 396 { 397 // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses 398 // the own local decomposition for computation and thus creates all necessary 399 // geometric objects 400 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); 401 } 402 else 403 { 404 // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange 405 return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation); 406 } 407 } 408 409 // provide unique ID 410 ImplPrimitrive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D) 411 412 } // end of namespace primitive2d 413 } // end of namespace drawinglayer 414 415 ////////////////////////////////////////////////////////////////////////////// 416 // eof 417