1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_cppcanvas.hxx" 30 31 #include <canvas/debug.hxx> 32 #include <tools/diagnose_ex.h> 33 #include <canvas/verbosetrace.hxx> 34 35 #include <rtl/logfile.hxx> 36 37 #include <com/sun/star/rendering/PathCapType.hpp> 38 #include <com/sun/star/rendering/PathJoinType.hpp> 39 #include <com/sun/star/rendering/XCanvas.hpp> 40 #include <com/sun/star/rendering/XCanvasFont.hpp> 41 42 #include <basegfx/numeric/ftools.hxx> 43 #include <basegfx/matrix/b2dhommatrix.hxx> 44 #include <basegfx/range/b2drectangle.hxx> 45 #include <basegfx/vector/b2dsize.hxx> 46 #include <basegfx/polygon/b2dpolypolygontools.hxx> 47 #include <basegfx/polygon/b2dpolygontools.hxx> 48 #include <basegfx/matrix/b2dhommatrixtools.hxx> 49 50 #include <tools/gen.hxx> 51 #include <vcl/canvastools.hxx> 52 #include <vcl/virdev.hxx> 53 54 #include <basegfx/tools/canvastools.hxx> 55 #include <canvas/canvastools.hxx> 56 57 #include <boost/scoped_array.hpp> 58 #include <boost/bind.hpp> 59 #include <boost/utility.hpp> 60 61 #include "textaction.hxx" 62 #include "outdevstate.hxx" 63 #include "mtftools.hxx" 64 65 66 using namespace ::com::sun::star; 67 68 namespace cppcanvas 69 { 70 namespace internal 71 { 72 namespace 73 { 74 void init( rendering::RenderState& o_rRenderState, 75 const ::basegfx::B2DPoint& rStartPoint, 76 const OutDevState& rState, 77 const CanvasSharedPtr& rCanvas ) 78 { 79 tools::initRenderState(o_rRenderState,rState); 80 81 // #i36950# Offset clip back to origin (as it's also moved 82 // by rStartPoint) 83 // #i53964# Also take VCL font rotation into account, 84 // since this, opposed to the FontMatrix rotation 85 // elsewhere, _does_ get incorporated into the render 86 // state transform. 87 tools::modifyClip( o_rRenderState, 88 rState, 89 rCanvas, 90 rStartPoint, 91 NULL, 92 &rState.fontRotation ); 93 94 basegfx::B2DHomMatrix aLocalTransformation(basegfx::tools::createRotateB2DHomMatrix(rState.fontRotation)); 95 aLocalTransformation.translate( rStartPoint.getX(), 96 rStartPoint.getY() ); 97 ::canvas::tools::appendToRenderState( o_rRenderState, 98 aLocalTransformation ); 99 100 o_rRenderState.DeviceColor = rState.textColor; 101 } 102 103 void init( rendering::RenderState& o_rRenderState, 104 const ::basegfx::B2DPoint& rStartPoint, 105 const OutDevState& rState, 106 const CanvasSharedPtr& rCanvas, 107 const ::basegfx::B2DHomMatrix& rTextTransform ) 108 { 109 init( o_rRenderState, rStartPoint, rState, rCanvas ); 110 111 // TODO(F2): Also inversely-transform clip with 112 // rTextTransform (which is actually rather hard, as the 113 // text transform is _prepended_ to the render state)! 114 115 // prepend extra font transform to render state 116 // (prepend it, because it's interpreted in the unit 117 // rect coordinate space) 118 ::canvas::tools::prependToRenderState( o_rRenderState, 119 rTextTransform ); 120 } 121 122 void init( rendering::RenderState& o_rRenderState, 123 uno::Reference< rendering::XCanvasFont >& o_rFont, 124 const ::basegfx::B2DPoint& rStartPoint, 125 const OutDevState& rState, 126 const CanvasSharedPtr& rCanvas ) 127 { 128 // ensure that o_rFont is valid. It is possible that 129 // text actions are generated without previously 130 // setting a font. Then, just take a default font 131 if( !o_rFont.is() ) 132 { 133 // Use completely default FontRequest 134 const rendering::FontRequest aFontRequest; 135 136 geometry::Matrix2D aFontMatrix; 137 ::canvas::tools::setIdentityMatrix2D( aFontMatrix ); 138 139 o_rFont = rCanvas->getUNOCanvas()->createFont( 140 aFontRequest, 141 uno::Sequence< beans::PropertyValue >(), 142 aFontMatrix ); 143 } 144 145 init( o_rRenderState, 146 rStartPoint, 147 rState, 148 rCanvas ); 149 } 150 151 void init( rendering::RenderState& o_rRenderState, 152 uno::Reference< rendering::XCanvasFont >& o_rFont, 153 const ::basegfx::B2DPoint& rStartPoint, 154 const OutDevState& rState, 155 const CanvasSharedPtr& rCanvas, 156 const ::basegfx::B2DHomMatrix& rTextTransform ) 157 { 158 init( o_rRenderState, o_rFont, rStartPoint, rState, rCanvas ); 159 160 // TODO(F2): Also inversely-transform clip with 161 // rTextTransform (which is actually rather hard, as the 162 // text transform is _prepended_ to the render state)! 163 164 // prepend extra font transform to render state 165 // (prepend it, because it's interpreted in the unit 166 // rect coordinate space) 167 ::canvas::tools::prependToRenderState( o_rRenderState, 168 rTextTransform ); 169 } 170 171 ::basegfx::B2DPolyPolygon textLinesFromLogicalOffsets( const uno::Sequence< double >& rOffsets, 172 const tools::TextLineInfo& rTextLineInfo ) 173 { 174 return tools::createTextLinesPolyPolygon( 175 0.0, 176 // extract character cell furthest to the right 177 *(::std::max_element( 178 rOffsets.getConstArray(), 179 rOffsets.getConstArray() + rOffsets.getLength() )), 180 rTextLineInfo ); 181 } 182 183 uno::Sequence< double > setupDXArray( const sal_Int32* pCharWidths, 184 sal_Int32 nLen, 185 const OutDevState& rState ) 186 { 187 // convert character widths from logical units 188 uno::Sequence< double > aCharWidthSeq( nLen ); 189 double* pOutputWidths( aCharWidthSeq.getArray() ); 190 191 // #143885# maintain (nearly) full precision of DX 192 // array, by circumventing integer-based 193 // OutDev-mapping 194 const double nScale( rState.mapModeTransform.get(0,0) ); 195 for( int i = 0; i < nLen; ++i ) 196 { 197 // TODO(F2): use correct scale direction 198 *pOutputWidths++ = *pCharWidths++ * nScale; 199 } 200 201 return aCharWidthSeq; 202 } 203 204 uno::Sequence< double > setupDXArray( const ::String& rText, 205 sal_Int32 nStartPos, 206 sal_Int32 nLen, 207 VirtualDevice& rVDev, 208 const OutDevState& rState ) 209 { 210 // no external DX array given, create one from given 211 // string 212 ::boost::scoped_array< sal_Int32 > pCharWidths( new sal_Int32[nLen] ); 213 214 rVDev.GetTextArray( rText, pCharWidths.get(), 215 static_cast<sal_uInt16>(nStartPos), 216 static_cast<sal_uInt16>(nLen) ); 217 218 return setupDXArray( pCharWidths.get(), nLen, rState ); 219 } 220 221 ::basegfx::B2DPoint adaptStartPoint( const ::basegfx::B2DPoint& rStartPoint, 222 const OutDevState& rState, 223 const uno::Sequence< double >& rOffsets ) 224 { 225 ::basegfx::B2DPoint aLocalPoint( rStartPoint ); 226 227 if( rState.textAlignment ) 228 { 229 // text origin is right, not left. Modify start point 230 // accordingly, because XCanvas::drawTextLayout() 231 // always aligns left! 232 233 const double nOffset( rOffsets[ rOffsets.getLength()-1 ] ); 234 235 // correct start point for rotated text: rotate around 236 // former start point 237 aLocalPoint.setX( aLocalPoint.getX() + cos( rState.fontRotation )*nOffset ); 238 aLocalPoint.setY( aLocalPoint.getY() + sin( rState.fontRotation )*nOffset ); 239 } 240 241 return aLocalPoint; 242 } 243 244 /** Perform common setup for array text actions 245 246 This method creates the XTextLayout object and 247 initializes it, e.g. with the logical advancements. 248 */ 249 void initArrayAction( rendering::RenderState& o_rRenderState, 250 uno::Reference< rendering::XTextLayout >& o_rTextLayout, 251 const ::basegfx::B2DPoint& rStartPoint, 252 const ::rtl::OUString& rText, 253 sal_Int32 nStartPos, 254 sal_Int32 nLen, 255 const uno::Sequence< double >& rOffsets, 256 const CanvasSharedPtr& rCanvas, 257 const OutDevState& rState, 258 const ::basegfx::B2DHomMatrix* pTextTransform ) 259 { 260 ENSURE_OR_THROW( rOffsets.getLength(), 261 "::cppcanvas::internal::initArrayAction(): zero-length DX array" ); 262 263 const ::basegfx::B2DPoint aLocalStartPoint( 264 adaptStartPoint( rStartPoint, rState, rOffsets ) ); 265 266 uno::Reference< rendering::XCanvasFont > xFont( rState.xFont ); 267 268 if( pTextTransform ) 269 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform ); 270 else 271 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas ); 272 273 o_rTextLayout = xFont->createTextLayout( 274 rendering::StringContext( rText, nStartPos, nLen ), 275 rState.textDirection, 276 0 ); 277 278 ENSURE_OR_THROW( o_rTextLayout.is(), 279 "::cppcanvas::internal::initArrayAction(): Invalid font" ); 280 281 o_rTextLayout->applyLogicalAdvancements( rOffsets ); 282 } 283 284 double getLineWidth( ::VirtualDevice& rVDev, 285 const OutDevState& rState, 286 const rendering::StringContext& rStringContext ) 287 { 288 // TODO(F2): use correct scale direction 289 const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text, 290 static_cast<sal_uInt16>(rStringContext.StartPosition), 291 static_cast<sal_uInt16>(rStringContext.Length) ), 292 0 ); 293 294 return (rState.mapModeTransform * aSize).getX(); 295 } 296 297 uno::Sequence< double > 298 calcSubsetOffsets( rendering::RenderState& io_rRenderState, 299 double& o_rMinPos, 300 double& o_rMaxPos, 301 const uno::Reference< rendering::XTextLayout >& rOrigTextLayout, 302 const ::cppcanvas::internal::Action::Subset& rSubset ) 303 { 304 ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin, 305 "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" ); 306 307 uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() ); 308 const double* pOffsets( aOrigOffsets.getConstArray() ); 309 310 ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd, 311 "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" ); 312 313 // TODO(F3): It currently seems that for RTL text, the 314 // DX offsets are nevertheless increasing in logical 315 // text order (I'd expect they are decreasing, 316 // mimicking the fact that the text is output 317 // right-to-left). This breaks text effects for ALL 318 // RTL languages. 319 320 // determine leftmost position in given subset range - 321 // as the DX array contains the output positions 322 // starting with the second character (the first is 323 // assumed to have output position 0), correct begin 324 // iterator. 325 const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 : 326 *(::std::min_element( pOffsets+rSubset.mnSubsetBegin-1, 327 pOffsets+rSubset.mnSubsetEnd )) ); 328 329 // determine rightmost position in given subset range 330 // - as the DX array contains the output positions 331 // starting with the second character (the first is 332 // assumed to have output position 0), correct begin 333 // iterator. 334 const double nMaxPos( 335 *(::std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ? 336 0 : rSubset.mnSubsetBegin-1), 337 pOffsets + rSubset.mnSubsetEnd )) ); 338 339 340 // adapt render state, to move text output to given offset 341 // ------------------------------------------------------- 342 343 // TODO(F1): Strictly speaking, we also have to adapt 344 // the clip here, which normally should _not_ move 345 // with the output offset. Neglected for now, as it 346 // does not matter for drawing layer output 347 348 if( rSubset.mnSubsetBegin > 0 ) 349 { 350 ::basegfx::B2DHomMatrix aTranslation; 351 if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical ) 352 { 353 // vertical text -> offset in y direction 354 aTranslation.translate( 0.0, nMinPos ); 355 } 356 else 357 { 358 // horizontal text -> offset in x direction 359 aTranslation.translate( nMinPos, 0.0 ); 360 } 361 362 ::canvas::tools::appendToRenderState( io_rRenderState, 363 aTranslation ); 364 } 365 366 367 // reduce DX array to given substring 368 // ---------------------------------- 369 370 const sal_Int32 nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin ); 371 uno::Sequence< double > aAdaptedOffsets( nNewElements ); 372 double* pAdaptedOffsets( aAdaptedOffsets.getArray() ); 373 374 // move to new output position (subtract nMinPos, 375 // which is the new '0' position), copy only the range 376 // as given by rSubset. 377 ::std::transform( pOffsets + rSubset.mnSubsetBegin, 378 pOffsets + rSubset.mnSubsetEnd, 379 pAdaptedOffsets, 380 ::boost::bind( ::std::minus<double>(), 381 _1, 382 nMinPos ) ); 383 384 o_rMinPos = nMinPos; 385 o_rMaxPos = nMaxPos; 386 387 return aAdaptedOffsets; 388 } 389 390 uno::Reference< rendering::XTextLayout > 391 createSubsetLayout( const rendering::StringContext& rOrigContext, 392 const ::cppcanvas::internal::Action::Subset& rSubset, 393 const uno::Reference< rendering::XTextLayout >& rOrigTextLayout ) 394 { 395 // create temporary new text layout with subset string 396 // --------------------------------------------------- 397 398 const sal_Int32 nNewStartPos( rOrigContext.StartPosition + ::std::min( 399 rSubset.mnSubsetBegin, rOrigContext.Length-1 ) ); 400 const sal_Int32 nNewLength( ::std::max( 401 ::std::min( 402 rSubset.mnSubsetEnd - rSubset.mnSubsetBegin, 403 rOrigContext.Length ), 404 sal_Int32( 0 ) ) ); 405 406 const rendering::StringContext aContext( rOrigContext.Text, 407 nNewStartPos, 408 nNewLength ); 409 410 uno::Reference< rendering::XTextLayout > xTextLayout( 411 rOrigTextLayout->getFont()->createTextLayout( aContext, 412 rOrigTextLayout->getMainTextDirection(), 413 0 ), 414 uno::UNO_QUERY_THROW ); 415 416 return xTextLayout; 417 } 418 419 /** Setup subset text layout 420 421 @param io_rTextLayout 422 Must contain original (full set) text layout on input, 423 will contain subsetted text layout (or empty 424 reference, for empty subsets) on output. 425 426 @param io_rRenderState 427 Must contain original render state on input, will 428 contain shifted render state concatenated with 429 rTransformation on output. 430 431 @param rTransformation 432 Additional transformation, to be prepended to render 433 state 434 435 @param rSubset 436 Subset to prepare 437 */ 438 void createSubsetLayout( uno::Reference< rendering::XTextLayout >& io_rTextLayout, 439 rendering::RenderState& io_rRenderState, 440 double& o_rMinPos, 441 double& o_rMaxPos, 442 const ::basegfx::B2DHomMatrix& rTransformation, 443 const Action::Subset& rSubset ) 444 { 445 ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation); 446 447 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd ) 448 { 449 // empty range, empty layout 450 io_rTextLayout.clear(); 451 452 return; 453 } 454 455 ENSURE_OR_THROW( io_rTextLayout.is(), 456 "createSubsetLayout(): Invalid input layout" ); 457 458 const rendering::StringContext& rOrigContext( io_rTextLayout->getText() ); 459 460 if( rSubset.mnSubsetBegin == 0 && 461 rSubset.mnSubsetEnd == rOrigContext.Length ) 462 { 463 // full range, no need for subsetting 464 return; 465 } 466 467 uno::Reference< rendering::XTextLayout > xTextLayout( 468 createSubsetLayout( rOrigContext, rSubset, io_rTextLayout ) ); 469 470 if( xTextLayout.is() ) 471 { 472 xTextLayout->applyLogicalAdvancements( 473 calcSubsetOffsets( io_rRenderState, 474 o_rMinPos, 475 o_rMaxPos, 476 io_rTextLayout, 477 rSubset ) ); 478 } 479 480 io_rTextLayout = xTextLayout; 481 } 482 483 484 /** Interface for renderEffectText functor below. 485 486 This is interface is used from the renderEffectText() 487 method below, to call the client implementation. 488 */ 489 class TextRenderer 490 { 491 public: 492 virtual ~TextRenderer() {} 493 494 /// Render text with given RenderState 495 virtual bool operator()( const rendering::RenderState& rRenderState ) const = 0; 496 }; 497 498 /** Render effect text. 499 500 @param rRenderer 501 Functor object, will be called to render the actual 502 part of the text effect (the text itself and the means 503 to render it are unknown to this method) 504 */ 505 bool renderEffectText( const TextRenderer& rRenderer, 506 const rendering::RenderState& rRenderState, 507 const rendering::ViewState& /*rViewState*/, 508 const uno::Reference< rendering::XCanvas >& xCanvas, 509 const ::Color& rShadowColor, 510 const ::basegfx::B2DSize& rShadowOffset, 511 const ::Color& rReliefColor, 512 const ::basegfx::B2DSize& rReliefOffset ) 513 { 514 ::Color aEmptyColor( COL_AUTO ); 515 uno::Reference<rendering::XColorSpace> xColorSpace( 516 xCanvas->getDevice()->getDeviceColorSpace() ); 517 518 // draw shadow text, if enabled 519 if( rShadowColor != aEmptyColor ) 520 { 521 rendering::RenderState aShadowState( rRenderState ); 522 ::basegfx::B2DHomMatrix aTranslate; 523 524 aTranslate.translate( rShadowOffset.getX(), 525 rShadowOffset.getY() ); 526 527 ::canvas::tools::appendToRenderState(aShadowState, aTranslate); 528 529 aShadowState.DeviceColor = 530 ::vcl::unotools::colorToDoubleSequence( rShadowColor, 531 xColorSpace ); 532 533 rRenderer( aShadowState ); 534 } 535 536 // draw relief text, if enabled 537 if( rReliefColor != aEmptyColor ) 538 { 539 rendering::RenderState aReliefState( rRenderState ); 540 ::basegfx::B2DHomMatrix aTranslate; 541 542 aTranslate.translate( rReliefOffset.getX(), 543 rReliefOffset.getY() ); 544 545 ::canvas::tools::appendToRenderState(aReliefState, aTranslate); 546 547 aReliefState.DeviceColor = 548 ::vcl::unotools::colorToDoubleSequence( rReliefColor, 549 xColorSpace ); 550 551 rRenderer( aReliefState ); 552 } 553 554 // draw normal text 555 rRenderer( rRenderState ); 556 557 return true; 558 } 559 560 561 ::basegfx::B2DRange calcEffectTextBounds( const ::basegfx::B2DRange& rTextBounds, 562 const ::basegfx::B2DRange& rLineBounds, 563 const ::basegfx::B2DSize& rReliefOffset, 564 const ::basegfx::B2DSize& rShadowOffset, 565 const rendering::RenderState& rRenderState, 566 const rendering::ViewState& rViewState ) 567 { 568 ::basegfx::B2DRange aBounds( rTextBounds ); 569 570 // add extends of text lines 571 aBounds.expand( rLineBounds ); 572 573 // TODO(Q3): Provide this functionality at the B2DRange 574 ::basegfx::B2DRange aTotalBounds( aBounds ); 575 aTotalBounds.expand( 576 ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getX(), 577 aBounds.getMinY() + rReliefOffset.getY(), 578 aBounds.getMaxX() + rReliefOffset.getX(), 579 aBounds.getMaxY() + rReliefOffset.getY() ) ); 580 aTotalBounds.expand( 581 ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getX(), 582 aBounds.getMinY() + rShadowOffset.getY(), 583 aBounds.getMaxX() + rShadowOffset.getX(), 584 aBounds.getMaxY() + rShadowOffset.getY() ) ); 585 586 return tools::calcDevicePixelBounds( aTotalBounds, 587 rViewState, 588 rRenderState ); 589 } 590 591 void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize, 592 uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines, 593 const CanvasSharedPtr& rCanvas, 594 const uno::Sequence< double >& rOffsets, 595 const tools::TextLineInfo rLineInfo ) 596 { 597 const ::basegfx::B2DPolyPolygon aPoly( 598 textLinesFromLogicalOffsets( 599 rOffsets, 600 rLineInfo ) ); 601 602 o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange(); 603 604 o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 605 rCanvas->getUNOCanvas()->getDevice(), 606 aPoly ); 607 } 608 609 void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize, 610 uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines, 611 const CanvasSharedPtr& rCanvas, 612 double nLineWidth, 613 const tools::TextLineInfo rLineInfo ) 614 { 615 const ::basegfx::B2DPolyPolygon aPoly( 616 tools::createTextLinesPolyPolygon( 0.0, nLineWidth, 617 rLineInfo ) ); 618 619 o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange(); 620 621 o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 622 rCanvas->getUNOCanvas()->getDevice(), 623 aPoly ); 624 } 625 626 627 // ------------------------------------------------------------------------- 628 629 class TextAction : public Action, private ::boost::noncopyable 630 { 631 public: 632 TextAction( const ::basegfx::B2DPoint& rStartPoint, 633 const ::rtl::OUString& rString, 634 sal_Int32 nStartPos, 635 sal_Int32 nLen, 636 const CanvasSharedPtr& rCanvas, 637 const OutDevState& rState ); 638 639 TextAction( const ::basegfx::B2DPoint& rStartPoint, 640 const ::rtl::OUString& rString, 641 sal_Int32 nStartPos, 642 sal_Int32 nLen, 643 const CanvasSharedPtr& rCanvas, 644 const OutDevState& rState, 645 const ::basegfx::B2DHomMatrix& rTextTransform ); 646 647 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const; 648 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation, 649 const Subset& rSubset ) const; 650 651 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const; 652 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 653 const Subset& rSubset ) const; 654 655 virtual sal_Int32 getActionCount() const; 656 657 private: 658 // TODO(P2): This is potentially a real mass object 659 // (every character might be a separate TextAction), 660 // thus, make it as lightweight as possible. For 661 // example, share common RenderState among several 662 // TextActions, maybe using maOffsets for the 663 // translation. 664 665 uno::Reference< rendering::XCanvasFont > mxFont; 666 const rendering::StringContext maStringContext; 667 const CanvasSharedPtr mpCanvas; 668 rendering::RenderState maState; 669 const sal_Int8 maTextDirection; 670 }; 671 672 TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint, 673 const ::rtl::OUString& rString, 674 sal_Int32 nStartPos, 675 sal_Int32 nLen, 676 const CanvasSharedPtr& rCanvas, 677 const OutDevState& rState ) : 678 mxFont( rState.xFont ), 679 maStringContext( rString, nStartPos, nLen ), 680 mpCanvas( rCanvas ), 681 maState(), 682 maTextDirection( rState.textDirection ) 683 { 684 init( maState, mxFont, 685 rStartPoint, 686 rState, rCanvas ); 687 688 ENSURE_OR_THROW( mxFont.is(), 689 "::cppcanvas::internal::TextAction(): Invalid font" ); 690 } 691 692 TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint, 693 const ::rtl::OUString& rString, 694 sal_Int32 nStartPos, 695 sal_Int32 nLen, 696 const CanvasSharedPtr& rCanvas, 697 const OutDevState& rState, 698 const ::basegfx::B2DHomMatrix& rTextTransform ) : 699 mxFont( rState.xFont ), 700 maStringContext( rString, nStartPos, nLen ), 701 mpCanvas( rCanvas ), 702 maState(), 703 maTextDirection( rState.textDirection ) 704 { 705 init( maState, mxFont, 706 rStartPoint, 707 rState, rCanvas, rTextTransform ); 708 709 ENSURE_OR_THROW( mxFont.is(), 710 "::cppcanvas::internal::TextAction(): Invalid font" ); 711 } 712 713 bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const 714 { 715 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextAction::render()" ); 716 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextAction: 0x%X", this ); 717 718 rendering::RenderState aLocalState( maState ); 719 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 720 721 mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont, 722 mpCanvas->getViewState(), aLocalState, maTextDirection ); 723 724 return true; 725 } 726 727 bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation, 728 const Subset& /*rSubset*/ ) const 729 { 730 OSL_ENSURE( false, 731 "TextAction::render(): Subset not supported by this object" ); 732 733 // TODO(P1): Retrieve necessary font metric info for 734 // TextAction from XCanvas. Currently, the 735 // TextActionFactory does not generate this object for 736 // _subsettable_ text 737 return render( rTransformation ); 738 } 739 740 ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const 741 { 742 // create XTextLayout, to have the 743 // XTextLayout::queryTextBounds() method available 744 uno::Reference< rendering::XTextLayout > xTextLayout( 745 mxFont->createTextLayout( 746 maStringContext, 747 maTextDirection, 748 0 ) ); 749 750 rendering::RenderState aLocalState( maState ); 751 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 752 753 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D( 754 xTextLayout->queryTextBounds() ), 755 mpCanvas->getViewState(), 756 aLocalState ); 757 } 758 759 ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 760 const Subset& /*rSubset*/ ) const 761 { 762 OSL_ENSURE( false, 763 "TextAction::getBounds(): Subset not supported by this object" ); 764 765 // TODO(P1): Retrieve necessary font metric info for 766 // TextAction from XCanvas. Currently, the 767 // TextActionFactory does not generate this object for 768 // _subsettable_ text 769 return getBounds( rTransformation ); 770 } 771 772 sal_Int32 TextAction::getActionCount() const 773 { 774 // TODO(P1): Retrieve necessary font metric info for 775 // TextAction from XCanvas. Currently, the 776 // TextActionFactory does not generate this object for 777 // _subsettable_ text 778 return 1; 779 } 780 781 782 // ------------------------------------------------------------------------- 783 784 class EffectTextAction : 785 public Action, 786 public TextRenderer, 787 private ::boost::noncopyable 788 { 789 public: 790 EffectTextAction( const ::basegfx::B2DPoint& rStartPoint, 791 const ::basegfx::B2DSize& rReliefOffset, 792 const ::Color& rReliefColor, 793 const ::basegfx::B2DSize& rShadowOffset, 794 const ::Color& rShadowColor, 795 const ::rtl::OUString& rText, 796 sal_Int32 nStartPos, 797 sal_Int32 nLen, 798 VirtualDevice& rVDev, 799 const CanvasSharedPtr& rCanvas, 800 const OutDevState& rState ); 801 802 EffectTextAction( const ::basegfx::B2DPoint& rStartPoint, 803 const ::basegfx::B2DSize& rReliefOffset, 804 const ::Color& rReliefColor, 805 const ::basegfx::B2DSize& rShadowOffset, 806 const ::Color& rShadowColor, 807 const ::rtl::OUString& rText, 808 sal_Int32 nStartPos, 809 sal_Int32 nLen, 810 VirtualDevice& rVDev, 811 const CanvasSharedPtr& rCanvas, 812 const OutDevState& rState, 813 const ::basegfx::B2DHomMatrix& rTextTransform ); 814 815 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const; 816 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation, 817 const Subset& rSubset ) const; 818 819 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const; 820 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 821 const Subset& rSubset ) const; 822 823 virtual sal_Int32 getActionCount() const; 824 825 private: 826 /// Interface TextRenderer 827 virtual bool operator()( const rendering::RenderState& rRenderState ) const; 828 829 // TODO(P2): This is potentially a real mass object 830 // (every character might be a separate TextAction), 831 // thus, make it as lightweight as possible. For 832 // example, share common RenderState among several 833 // TextActions, maybe using maOffsets for the 834 // translation. 835 836 uno::Reference< rendering::XCanvasFont > mxFont; 837 const rendering::StringContext maStringContext; 838 const CanvasSharedPtr mpCanvas; 839 rendering::RenderState maState; 840 const tools::TextLineInfo maTextLineInfo; 841 ::basegfx::B2DSize maLinesOverallSize; 842 const double mnLineWidth; 843 uno::Reference< rendering::XPolyPolygon2D > mxTextLines; 844 const ::basegfx::B2DSize maReliefOffset; 845 const ::Color maReliefColor; 846 const ::basegfx::B2DSize maShadowOffset; 847 const ::Color maShadowColor; 848 const sal_Int8 maTextDirection; 849 }; 850 851 EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint, 852 const ::basegfx::B2DSize& rReliefOffset, 853 const ::Color& rReliefColor, 854 const ::basegfx::B2DSize& rShadowOffset, 855 const ::Color& rShadowColor, 856 const ::rtl::OUString& rText, 857 sal_Int32 nStartPos, 858 sal_Int32 nLen, 859 VirtualDevice& rVDev, 860 const CanvasSharedPtr& rCanvas, 861 const OutDevState& rState ) : 862 mxFont( rState.xFont ), 863 maStringContext( rText, nStartPos, nLen ), 864 mpCanvas( rCanvas ), 865 maState(), 866 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ), 867 maLinesOverallSize(), 868 mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ), 869 mxTextLines(), 870 maReliefOffset( rReliefOffset ), 871 maReliefColor( rReliefColor ), 872 maShadowOffset( rShadowOffset ), 873 maShadowColor( rShadowColor ), 874 maTextDirection( rState.textDirection ) 875 { 876 initEffectLinePolyPolygon( maLinesOverallSize, 877 mxTextLines, 878 rCanvas, 879 mnLineWidth, 880 maTextLineInfo ); 881 882 init( maState, mxFont, 883 rStartPoint, 884 rState, rCanvas ); 885 886 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(), 887 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" ); 888 } 889 890 EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint, 891 const ::basegfx::B2DSize& rReliefOffset, 892 const ::Color& rReliefColor, 893 const ::basegfx::B2DSize& rShadowOffset, 894 const ::Color& rShadowColor, 895 const ::rtl::OUString& rText, 896 sal_Int32 nStartPos, 897 sal_Int32 nLen, 898 VirtualDevice& rVDev, 899 const CanvasSharedPtr& rCanvas, 900 const OutDevState& rState, 901 const ::basegfx::B2DHomMatrix& rTextTransform ) : 902 mxFont( rState.xFont ), 903 maStringContext( rText, nStartPos, nLen ), 904 mpCanvas( rCanvas ), 905 maState(), 906 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ), 907 maLinesOverallSize(), 908 mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ), 909 mxTextLines(), 910 maReliefOffset( rReliefOffset ), 911 maReliefColor( rReliefColor ), 912 maShadowOffset( rShadowOffset ), 913 maShadowColor( rShadowColor ), 914 maTextDirection( rState.textDirection ) 915 { 916 initEffectLinePolyPolygon( maLinesOverallSize, 917 mxTextLines, 918 rCanvas, 919 mnLineWidth, 920 maTextLineInfo ); 921 922 init( maState, mxFont, 923 rStartPoint, 924 rState, rCanvas, rTextTransform ); 925 926 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(), 927 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" ); 928 } 929 930 bool EffectTextAction::operator()( const rendering::RenderState& rRenderState ) const 931 { 932 const rendering::ViewState& rViewState( mpCanvas->getViewState() ); 933 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() ); 934 935 rCanvas->fillPolyPolygon( mxTextLines, 936 rViewState, 937 rRenderState ); 938 939 rCanvas->drawText( maStringContext, mxFont, 940 rViewState, 941 rRenderState, 942 maTextDirection ); 943 944 return true; 945 } 946 947 bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const 948 { 949 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextAction::render()" ); 950 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextAction: 0x%X", this ); 951 952 rendering::RenderState aLocalState( maState ); 953 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 954 955 return renderEffectText( *this, 956 aLocalState, 957 mpCanvas->getViewState(), 958 mpCanvas->getUNOCanvas(), 959 maShadowColor, 960 maShadowOffset, 961 maReliefColor, 962 maReliefOffset ); 963 } 964 965 bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation, 966 const Subset& /*rSubset*/ ) const 967 { 968 OSL_ENSURE( false, 969 "EffectTextAction::render(): Subset not supported by this object" ); 970 971 // TODO(P1): Retrieve necessary font metric info for 972 // TextAction from XCanvas. Currently, the 973 // TextActionFactory does not generate this object for 974 // subsettable text 975 return render( rTransformation ); 976 } 977 978 ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const 979 { 980 // create XTextLayout, to have the 981 // XTextLayout::queryTextBounds() method available 982 uno::Reference< rendering::XTextLayout > xTextLayout( 983 mxFont->createTextLayout( 984 maStringContext, 985 maTextDirection, 986 0 ) ); 987 988 rendering::RenderState aLocalState( maState ); 989 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 990 991 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D( 992 xTextLayout->queryTextBounds() ), 993 ::basegfx::B2DRange( 0,0, 994 maLinesOverallSize.getX(), 995 maLinesOverallSize.getY() ), 996 maReliefOffset, 997 maShadowOffset, 998 aLocalState, 999 mpCanvas->getViewState() ); 1000 } 1001 1002 ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 1003 const Subset& /*rSubset*/ ) const 1004 { 1005 OSL_ENSURE( false, 1006 "EffectTextAction::getBounds(): Subset not supported by this object" ); 1007 1008 // TODO(P1): Retrieve necessary font metric info for 1009 // TextAction from XCanvas. Currently, the 1010 // TextActionFactory does not generate this object for 1011 // _subsettable_ text 1012 return getBounds( rTransformation ); 1013 } 1014 1015 sal_Int32 EffectTextAction::getActionCount() const 1016 { 1017 // TODO(P1): Retrieve necessary font metric info for 1018 // TextAction from XCanvas. Currently, the 1019 // TextActionFactory does not generate this object for 1020 // subsettable text 1021 return 1; 1022 } 1023 1024 1025 // ------------------------------------------------------------------------- 1026 1027 class TextArrayAction : public Action, private ::boost::noncopyable 1028 { 1029 public: 1030 TextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1031 const ::rtl::OUString& rString, 1032 sal_Int32 nStartPos, 1033 sal_Int32 nLen, 1034 const uno::Sequence< double >& rOffsets, 1035 const CanvasSharedPtr& rCanvas, 1036 const OutDevState& rState ); 1037 1038 TextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1039 const ::rtl::OUString& rString, 1040 sal_Int32 nStartPos, 1041 sal_Int32 nLen, 1042 const uno::Sequence< double >& rOffsets, 1043 const CanvasSharedPtr& rCanvas, 1044 const OutDevState& rState, 1045 const ::basegfx::B2DHomMatrix& rTextTransform ); 1046 1047 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const; 1048 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation, 1049 const Subset& rSubset ) const; 1050 1051 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const; 1052 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 1053 const Subset& rSubset ) const; 1054 1055 virtual sal_Int32 getActionCount() const; 1056 1057 private: 1058 // TODO(P2): This is potentially a real mass object 1059 // (every character might be a separate TextAction), 1060 // thus, make it as lightweight as possible. For 1061 // example, share common RenderState among several 1062 // TextActions, maybe using maOffsets for the 1063 // translation. 1064 1065 uno::Reference< rendering::XTextLayout > mxTextLayout; 1066 const CanvasSharedPtr mpCanvas; 1067 rendering::RenderState maState; 1068 }; 1069 1070 TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1071 const ::rtl::OUString& rString, 1072 sal_Int32 nStartPos, 1073 sal_Int32 nLen, 1074 const uno::Sequence< double >& rOffsets, 1075 const CanvasSharedPtr& rCanvas, 1076 const OutDevState& rState ) : 1077 mxTextLayout(), 1078 mpCanvas( rCanvas ), 1079 maState() 1080 { 1081 initArrayAction( maState, 1082 mxTextLayout, 1083 rStartPoint, 1084 rString, 1085 nStartPos, 1086 nLen, 1087 rOffsets, 1088 rCanvas, 1089 rState, NULL ); 1090 } 1091 1092 TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1093 const ::rtl::OUString& rString, 1094 sal_Int32 nStartPos, 1095 sal_Int32 nLen, 1096 const uno::Sequence< double >& rOffsets, 1097 const CanvasSharedPtr& rCanvas, 1098 const OutDevState& rState, 1099 const ::basegfx::B2DHomMatrix& rTextTransform ) : 1100 mxTextLayout(), 1101 mpCanvas( rCanvas ), 1102 maState() 1103 { 1104 initArrayAction( maState, 1105 mxTextLayout, 1106 rStartPoint, 1107 rString, 1108 nStartPos, 1109 nLen, 1110 rOffsets, 1111 rCanvas, 1112 rState, 1113 &rTextTransform ); 1114 } 1115 1116 bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const 1117 { 1118 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::render()" ); 1119 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this ); 1120 1121 rendering::RenderState aLocalState( maState ); 1122 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 1123 1124 #ifdef SPECIAL_DEBUG 1125 aLocalState.Clip.clear(); 1126 aLocalState.DeviceColor = 1127 ::vcl::unotools::colorToDoubleSequence( mpCanvas->getUNOCanvas()->getDevice(), 1128 ::Color( 0x80FF0000 ) ); 1129 1130 if( maState.Clip.is() ) 1131 mpCanvas->getUNOCanvas()->drawPolyPolygon( maState.Clip, 1132 mpCanvas->getViewState(), 1133 aLocalState ); 1134 1135 aLocalState.DeviceColor = maState.DeviceColor; 1136 #endif 1137 1138 mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout, 1139 mpCanvas->getViewState(), 1140 aLocalState ); 1141 1142 return true; 1143 } 1144 1145 bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation, 1146 const Subset& rSubset ) const 1147 { 1148 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::render( subset )" ); 1149 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this ); 1150 1151 rendering::RenderState aLocalState( maState ); 1152 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout ); 1153 1154 double nDummy0, nDummy1; 1155 createSubsetLayout( xTextLayout, 1156 aLocalState, 1157 nDummy0, 1158 nDummy1, 1159 rTransformation, 1160 rSubset ); 1161 1162 if( !xTextLayout.is() ) 1163 return true; // empty layout, render nothing 1164 1165 mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout, 1166 mpCanvas->getViewState(), 1167 aLocalState ); 1168 1169 return true; 1170 } 1171 1172 ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const 1173 { 1174 rendering::RenderState aLocalState( maState ); 1175 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 1176 1177 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D( 1178 mxTextLayout->queryTextBounds() ), 1179 mpCanvas->getViewState(), 1180 aLocalState ); 1181 } 1182 1183 ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 1184 const Subset& rSubset ) const 1185 { 1186 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::getBounds( subset )" ); 1187 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this ); 1188 1189 rendering::RenderState aLocalState( maState ); 1190 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout ); 1191 1192 double nDummy0, nDummy1; 1193 createSubsetLayout( xTextLayout, 1194 aLocalState, 1195 nDummy0, 1196 nDummy1, 1197 rTransformation, 1198 rSubset ); 1199 1200 if( !xTextLayout.is() ) 1201 return ::basegfx::B2DRange(); // empty layout, empty bounds 1202 1203 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D( 1204 xTextLayout->queryTextBounds() ), 1205 mpCanvas->getViewState(), 1206 aLocalState ); 1207 } 1208 1209 sal_Int32 TextArrayAction::getActionCount() const 1210 { 1211 const rendering::StringContext& rOrigContext( mxTextLayout->getText() ); 1212 1213 return rOrigContext.Length; 1214 } 1215 1216 1217 // ------------------------------------------------------------------------- 1218 1219 class EffectTextArrayAction : 1220 public Action, 1221 public TextRenderer, 1222 private ::boost::noncopyable 1223 { 1224 public: 1225 EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1226 const ::basegfx::B2DSize& rReliefOffset, 1227 const ::Color& rReliefColor, 1228 const ::basegfx::B2DSize& rShadowOffset, 1229 const ::Color& rShadowColor, 1230 const ::rtl::OUString& rText, 1231 sal_Int32 nStartPos, 1232 sal_Int32 nLen, 1233 const uno::Sequence< double >& rOffsets, 1234 VirtualDevice& rVDev, 1235 const CanvasSharedPtr& rCanvas, 1236 const OutDevState& rState ); 1237 EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1238 const ::basegfx::B2DSize& rReliefOffset, 1239 const ::Color& rReliefColor, 1240 const ::basegfx::B2DSize& rShadowOffset, 1241 const ::Color& rShadowColor, 1242 const ::rtl::OUString& rText, 1243 sal_Int32 nStartPos, 1244 sal_Int32 nLen, 1245 const uno::Sequence< double >& rOffsets, 1246 VirtualDevice& rVDev, 1247 const CanvasSharedPtr& rCanvas, 1248 const OutDevState& rState, 1249 const ::basegfx::B2DHomMatrix& rTextTransform ); 1250 1251 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const; 1252 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation, 1253 const Subset& rSubset ) const; 1254 1255 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const; 1256 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 1257 const Subset& rSubset ) const; 1258 1259 virtual sal_Int32 getActionCount() const; 1260 1261 private: 1262 // TextRenderer interface 1263 virtual bool operator()( const rendering::RenderState& rRenderState ) const; 1264 1265 // TODO(P2): This is potentially a real mass object 1266 // (every character might be a separate TextAction), 1267 // thus, make it as lightweight as possible. For 1268 // example, share common RenderState among several 1269 // TextActions, maybe using maOffsets for the 1270 // translation. 1271 1272 uno::Reference< rendering::XTextLayout > mxTextLayout; 1273 const CanvasSharedPtr mpCanvas; 1274 rendering::RenderState maState; 1275 const tools::TextLineInfo maTextLineInfo; 1276 ::basegfx::B2DSize maLinesOverallSize; 1277 uno::Reference< rendering::XPolyPolygon2D > mxTextLines; 1278 const ::basegfx::B2DSize maReliefOffset; 1279 const ::Color maReliefColor; 1280 const ::basegfx::B2DSize maShadowOffset; 1281 const ::Color maShadowColor; 1282 }; 1283 1284 EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1285 const ::basegfx::B2DSize& rReliefOffset, 1286 const ::Color& rReliefColor, 1287 const ::basegfx::B2DSize& rShadowOffset, 1288 const ::Color& rShadowColor, 1289 const ::rtl::OUString& rText, 1290 sal_Int32 nStartPos, 1291 sal_Int32 nLen, 1292 const uno::Sequence< double >& rOffsets, 1293 VirtualDevice& rVDev, 1294 const CanvasSharedPtr& rCanvas, 1295 const OutDevState& rState ) : 1296 mxTextLayout(), 1297 mpCanvas( rCanvas ), 1298 maState(), 1299 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ), 1300 maLinesOverallSize(), 1301 mxTextLines(), 1302 maReliefOffset( rReliefOffset ), 1303 maReliefColor( rReliefColor ), 1304 maShadowOffset( rShadowOffset ), 1305 maShadowColor( rShadowColor ) 1306 { 1307 initEffectLinePolyPolygon( maLinesOverallSize, 1308 mxTextLines, 1309 rCanvas, 1310 rOffsets, 1311 maTextLineInfo ); 1312 1313 initArrayAction( maState, 1314 mxTextLayout, 1315 rStartPoint, 1316 rText, 1317 nStartPos, 1318 nLen, 1319 rOffsets, 1320 rCanvas, 1321 rState, NULL ); 1322 } 1323 1324 EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint, 1325 const ::basegfx::B2DSize& rReliefOffset, 1326 const ::Color& rReliefColor, 1327 const ::basegfx::B2DSize& rShadowOffset, 1328 const ::Color& rShadowColor, 1329 const ::rtl::OUString& rText, 1330 sal_Int32 nStartPos, 1331 sal_Int32 nLen, 1332 const uno::Sequence< double >& rOffsets, 1333 VirtualDevice& rVDev, 1334 const CanvasSharedPtr& rCanvas, 1335 const OutDevState& rState, 1336 const ::basegfx::B2DHomMatrix& rTextTransform ) : 1337 mxTextLayout(), 1338 mpCanvas( rCanvas ), 1339 maState(), 1340 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ), 1341 maLinesOverallSize(), 1342 mxTextLines(), 1343 maReliefOffset( rReliefOffset ), 1344 maReliefColor( rReliefColor ), 1345 maShadowOffset( rShadowOffset ), 1346 maShadowColor( rShadowColor ) 1347 { 1348 initEffectLinePolyPolygon( maLinesOverallSize, 1349 mxTextLines, 1350 rCanvas, 1351 rOffsets, 1352 maTextLineInfo ); 1353 1354 initArrayAction( maState, 1355 mxTextLayout, 1356 rStartPoint, 1357 rText, 1358 nStartPos, 1359 nLen, 1360 rOffsets, 1361 rCanvas, 1362 rState, 1363 &rTextTransform ); 1364 } 1365 1366 bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState ) const 1367 { 1368 const rendering::ViewState& rViewState( mpCanvas->getViewState() ); 1369 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() ); 1370 1371 rCanvas->fillPolyPolygon( mxTextLines, 1372 rViewState, 1373 rRenderState ); 1374 1375 rCanvas->drawTextLayout( mxTextLayout, 1376 rViewState, 1377 rRenderState ); 1378 1379 return true; 1380 } 1381 1382 bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const 1383 { 1384 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" ); 1385 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this ); 1386 1387 rendering::RenderState aLocalState( maState ); 1388 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 1389 1390 return renderEffectText( *this, 1391 aLocalState, 1392 mpCanvas->getViewState(), 1393 mpCanvas->getUNOCanvas(), 1394 maShadowColor, 1395 maShadowOffset, 1396 maReliefColor, 1397 maReliefOffset ); 1398 } 1399 1400 class EffectTextArrayRenderHelper : public TextRenderer 1401 { 1402 public: 1403 EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas, 1404 const uno::Reference< rendering::XTextLayout >& rTextLayout, 1405 const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon, 1406 const rendering::ViewState& rViewState ) : 1407 mrCanvas( rCanvas ), 1408 mrTextLayout( rTextLayout ), 1409 mrLinePolygon( rLinePolygon ), 1410 mrViewState( rViewState ) 1411 { 1412 } 1413 1414 // TextRenderer interface 1415 virtual bool operator()( const rendering::RenderState& rRenderState ) const 1416 { 1417 mrCanvas->fillPolyPolygon( mrLinePolygon, 1418 mrViewState, 1419 rRenderState ); 1420 1421 mrCanvas->drawTextLayout( mrTextLayout, 1422 mrViewState, 1423 rRenderState ); 1424 1425 return true; 1426 } 1427 1428 private: 1429 const uno::Reference< rendering::XCanvas >& mrCanvas; 1430 const uno::Reference< rendering::XTextLayout >& mrTextLayout; 1431 const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon; 1432 const rendering::ViewState& mrViewState; 1433 }; 1434 1435 bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation, 1436 const Subset& rSubset ) const 1437 { 1438 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render( subset )" ); 1439 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this ); 1440 1441 rendering::RenderState aLocalState( maState ); 1442 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout ); 1443 const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() ); 1444 1445 double nMinPos(0.0); 1446 double nMaxPos(aTextBounds.X2 - aTextBounds.X1); 1447 1448 createSubsetLayout( xTextLayout, 1449 aLocalState, 1450 nMinPos, 1451 nMaxPos, 1452 rTransformation, 1453 rSubset ); 1454 1455 if( !xTextLayout.is() ) 1456 return true; // empty layout, render nothing 1457 1458 1459 // create and setup local line polygon 1460 // =================================== 1461 1462 uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() ); 1463 const rendering::ViewState& rViewState( mpCanvas->getViewState() ); 1464 1465 uno::Reference< rendering::XPolyPolygon2D > xTextLines( 1466 ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1467 xCanvas->getDevice(), 1468 tools::createTextLinesPolyPolygon( 1469 0.0, nMaxPos - nMinPos, 1470 maTextLineInfo ) ) ); 1471 1472 1473 // render everything 1474 // ================= 1475 1476 return renderEffectText( 1477 EffectTextArrayRenderHelper( xCanvas, 1478 xTextLayout, 1479 xTextLines, 1480 rViewState ), 1481 aLocalState, 1482 rViewState, 1483 xCanvas, 1484 maShadowColor, 1485 maShadowOffset, 1486 maReliefColor, 1487 maReliefOffset ); 1488 } 1489 1490 ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const 1491 { 1492 rendering::RenderState aLocalState( maState ); 1493 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 1494 1495 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D( 1496 mxTextLayout->queryTextBounds() ), 1497 ::basegfx::B2DRange( 0,0, 1498 maLinesOverallSize.getX(), 1499 maLinesOverallSize.getY() ), 1500 maReliefOffset, 1501 maShadowOffset, 1502 aLocalState, 1503 mpCanvas->getViewState() ); 1504 } 1505 1506 ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 1507 const Subset& rSubset ) const 1508 { 1509 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" ); 1510 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this ); 1511 1512 rendering::RenderState aLocalState( maState ); 1513 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout ); 1514 const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() ); 1515 1516 double nMinPos(0.0); 1517 double nMaxPos(aTextBounds.X2 - aTextBounds.X1); 1518 1519 createSubsetLayout( xTextLayout, 1520 aLocalState, 1521 nMinPos, 1522 nMaxPos, 1523 rTransformation, 1524 rSubset ); 1525 1526 if( !xTextLayout.is() ) 1527 return ::basegfx::B2DRange(); // empty layout, empty bounds 1528 1529 1530 // create and setup local line polygon 1531 // =================================== 1532 1533 const ::basegfx::B2DPolyPolygon aPoly( 1534 tools::createTextLinesPolyPolygon( 1535 0.0, nMaxPos - nMinPos, 1536 maTextLineInfo ) ); 1537 1538 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D( 1539 xTextLayout->queryTextBounds() ), 1540 ::basegfx::tools::getRange( aPoly ), 1541 maReliefOffset, 1542 maShadowOffset, 1543 aLocalState, 1544 mpCanvas->getViewState() ); 1545 } 1546 1547 sal_Int32 EffectTextArrayAction::getActionCount() const 1548 { 1549 const rendering::StringContext& rOrigContext( mxTextLayout->getText() ); 1550 1551 return rOrigContext.Length; 1552 } 1553 1554 1555 // ------------------------------------------------------------------------- 1556 1557 class OutlineAction : 1558 public Action, 1559 public TextRenderer, 1560 private ::boost::noncopyable 1561 { 1562 public: 1563 OutlineAction( const ::basegfx::B2DPoint& rStartPoint, 1564 const ::basegfx::B2DSize& rReliefOffset, 1565 const ::Color& rReliefColor, 1566 const ::basegfx::B2DSize& rShadowOffset, 1567 const ::Color& rShadowColor, 1568 const ::basegfx::B2DRectangle& rOutlineBounds, 1569 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly, 1570 const ::std::vector< sal_Int32 >& rPolygonGlyphMap, 1571 const uno::Sequence< double >& rOffsets, 1572 VirtualDevice& rVDev, 1573 const CanvasSharedPtr& rCanvas, 1574 const OutDevState& rState ); 1575 OutlineAction( const ::basegfx::B2DPoint& rStartPoint, 1576 const ::basegfx::B2DSize& rReliefOffset, 1577 const ::Color& rReliefColor, 1578 const ::basegfx::B2DSize& rShadowOffset, 1579 const ::Color& rShadowColor, 1580 const ::basegfx::B2DRectangle& rOutlineBounds, 1581 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly, 1582 const ::std::vector< sal_Int32 >& rPolygonGlyphMap, 1583 const uno::Sequence< double >& rOffsets, 1584 VirtualDevice& rVDev, 1585 const CanvasSharedPtr& rCanvas, 1586 const OutDevState& rState, 1587 const ::basegfx::B2DHomMatrix& rTextTransform ); 1588 1589 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const; 1590 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation, 1591 const Subset& rSubset ) const; 1592 1593 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const; 1594 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 1595 const Subset& rSubset ) const; 1596 1597 virtual sal_Int32 getActionCount() const; 1598 1599 private: 1600 // TextRenderer interface 1601 virtual bool operator()( const rendering::RenderState& rRenderState ) const; 1602 1603 // TODO(P2): This is potentially a real mass object 1604 // (every character might be a separate TextAction), 1605 // thus, make it as lightweight as possible. For 1606 // example, share common RenderState among several 1607 // TextActions, maybe using maOffsets for the 1608 // translation. 1609 1610 uno::Reference< rendering::XPolyPolygon2D > mxTextPoly; 1611 1612 /** This vector denotes the index of the start polygon 1613 for the respective glyph sequence. 1614 1615 To get a polygon index range for a given character 1616 index i, take [ maPolygonGlyphMap[i], 1617 maPolygonGlyphMap[i+1] ). Note that this is wrong 1618 for BiDi 1619 */ 1620 const ::std::vector< sal_Int32 > maPolygonGlyphMap; 1621 const uno::Sequence< double > maOffsets; 1622 const CanvasSharedPtr mpCanvas; 1623 rendering::RenderState maState; 1624 double mnOutlineWidth; 1625 const uno::Sequence< double > maFillColor; 1626 const tools::TextLineInfo maTextLineInfo; 1627 ::basegfx::B2DSize maLinesOverallSize; 1628 const ::basegfx::B2DRectangle maOutlineBounds; 1629 uno::Reference< rendering::XPolyPolygon2D > mxTextLines; 1630 const ::basegfx::B2DSize maReliefOffset; 1631 const ::Color maReliefColor; 1632 const ::basegfx::B2DSize maShadowOffset; 1633 const ::Color maShadowColor; 1634 }; 1635 1636 double calcOutlineWidth( const OutDevState& rState, 1637 VirtualDevice& rVDev ) 1638 { 1639 const ::basegfx::B2DSize aFontSize( 0, 1640 rVDev.GetFont().GetHeight() / 64.0 ); 1641 1642 const double nOutlineWidth( 1643 (rState.mapModeTransform * aFontSize).getY() ); 1644 1645 return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth; 1646 } 1647 1648 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint, 1649 const ::basegfx::B2DSize& rReliefOffset, 1650 const ::Color& rReliefColor, 1651 const ::basegfx::B2DSize& rShadowOffset, 1652 const ::Color& rShadowColor, 1653 const ::basegfx::B2DRectangle& rOutlineBounds, 1654 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly, 1655 const ::std::vector< sal_Int32 >& rPolygonGlyphMap, 1656 const uno::Sequence< double >& rOffsets, 1657 VirtualDevice& rVDev, 1658 const CanvasSharedPtr& rCanvas, 1659 const OutDevState& rState ) : 1660 mxTextPoly( rTextPoly ), 1661 maPolygonGlyphMap( rPolygonGlyphMap ), 1662 maOffsets( rOffsets ), 1663 mpCanvas( rCanvas ), 1664 maState(), 1665 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ), 1666 maFillColor( 1667 ::vcl::unotools::colorToDoubleSequence( 1668 ::Color( COL_WHITE ), 1669 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )), 1670 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ), 1671 maLinesOverallSize(), 1672 maOutlineBounds( rOutlineBounds ), 1673 mxTextLines(), 1674 maReliefOffset( rReliefOffset ), 1675 maReliefColor( rReliefColor ), 1676 maShadowOffset( rShadowOffset ), 1677 maShadowColor( rShadowColor ) 1678 { 1679 initEffectLinePolyPolygon( maLinesOverallSize, 1680 mxTextLines, 1681 rCanvas, 1682 rOffsets, 1683 maTextLineInfo ); 1684 1685 init( maState, 1686 rStartPoint, 1687 rState, 1688 rCanvas ); 1689 } 1690 1691 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint, 1692 const ::basegfx::B2DSize& rReliefOffset, 1693 const ::Color& rReliefColor, 1694 const ::basegfx::B2DSize& rShadowOffset, 1695 const ::Color& rShadowColor, 1696 const ::basegfx::B2DRectangle& rOutlineBounds, 1697 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly, 1698 const ::std::vector< sal_Int32 >& rPolygonGlyphMap, 1699 const uno::Sequence< double >& rOffsets, 1700 VirtualDevice& rVDev, 1701 const CanvasSharedPtr& rCanvas, 1702 const OutDevState& rState, 1703 const ::basegfx::B2DHomMatrix& rTextTransform ) : 1704 mxTextPoly( rTextPoly ), 1705 maPolygonGlyphMap( rPolygonGlyphMap ), 1706 maOffsets( rOffsets ), 1707 mpCanvas( rCanvas ), 1708 maState(), 1709 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ), 1710 maFillColor( 1711 ::vcl::unotools::colorToDoubleSequence( 1712 ::Color( COL_WHITE ), 1713 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )), 1714 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ), 1715 maLinesOverallSize(), 1716 maOutlineBounds( rOutlineBounds ), 1717 mxTextLines(), 1718 maReliefOffset( rReliefOffset ), 1719 maReliefColor( rReliefColor ), 1720 maShadowOffset( rShadowOffset ), 1721 maShadowColor( rShadowColor ) 1722 { 1723 initEffectLinePolyPolygon( maLinesOverallSize, 1724 mxTextLines, 1725 rCanvas, 1726 rOffsets, 1727 maTextLineInfo ); 1728 1729 init( maState, 1730 rStartPoint, 1731 rState, 1732 rCanvas, 1733 rTextTransform ); 1734 } 1735 1736 bool OutlineAction::operator()( const rendering::RenderState& rRenderState ) const 1737 { 1738 const rendering::ViewState& rViewState( mpCanvas->getViewState() ); 1739 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() ); 1740 1741 rendering::StrokeAttributes aStrokeAttributes; 1742 1743 aStrokeAttributes.StrokeWidth = mnOutlineWidth; 1744 aStrokeAttributes.MiterLimit = 1.0; 1745 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT; 1746 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT; 1747 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER; 1748 1749 rendering::RenderState aLocalState( rRenderState ); 1750 aLocalState.DeviceColor = maFillColor; 1751 1752 // TODO(P1): implement caching 1753 1754 // background of text 1755 rCanvas->fillPolyPolygon( mxTextPoly, 1756 rViewState, 1757 aLocalState ); 1758 1759 // border line of text 1760 rCanvas->strokePolyPolygon( mxTextPoly, 1761 rViewState, 1762 rRenderState, 1763 aStrokeAttributes ); 1764 1765 // underlines/strikethrough - background 1766 rCanvas->fillPolyPolygon( mxTextLines, 1767 rViewState, 1768 aLocalState ); 1769 // underlines/strikethrough - border 1770 rCanvas->strokePolyPolygon( mxTextLines, 1771 rViewState, 1772 rRenderState, 1773 aStrokeAttributes ); 1774 1775 return true; 1776 } 1777 1778 bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const 1779 { 1780 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" ); 1781 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this ); 1782 1783 rendering::RenderState aLocalState( maState ); 1784 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 1785 1786 return renderEffectText( *this, 1787 aLocalState, 1788 mpCanvas->getViewState(), 1789 mpCanvas->getUNOCanvas(), 1790 maShadowColor, 1791 maShadowOffset, 1792 maReliefColor, 1793 maReliefOffset ); 1794 } 1795 1796 class OutlineTextArrayRenderHelper : public TextRenderer 1797 { 1798 public: 1799 OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas, 1800 const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon, 1801 const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon, 1802 const rendering::ViewState& rViewState, 1803 double nOutlineWidth ) : 1804 maFillColor( 1805 ::vcl::unotools::colorToDoubleSequence( 1806 ::Color( COL_WHITE ), 1807 rCanvas->getDevice()->getDeviceColorSpace() )), 1808 mnOutlineWidth( nOutlineWidth ), 1809 mrCanvas( rCanvas ), 1810 mrTextPolygon( rTextPolygon ), 1811 mrLinePolygon( rLinePolygon ), 1812 mrViewState( rViewState ) 1813 { 1814 } 1815 1816 // TextRenderer interface 1817 virtual bool operator()( const rendering::RenderState& rRenderState ) const 1818 { 1819 rendering::StrokeAttributes aStrokeAttributes; 1820 1821 aStrokeAttributes.StrokeWidth = mnOutlineWidth; 1822 aStrokeAttributes.MiterLimit = 1.0; 1823 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT; 1824 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT; 1825 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER; 1826 1827 rendering::RenderState aLocalState( rRenderState ); 1828 aLocalState.DeviceColor = maFillColor; 1829 1830 // TODO(P1): implement caching 1831 1832 // background of text 1833 mrCanvas->fillPolyPolygon( mrTextPolygon, 1834 mrViewState, 1835 aLocalState ); 1836 1837 // border line of text 1838 mrCanvas->strokePolyPolygon( mrTextPolygon, 1839 mrViewState, 1840 rRenderState, 1841 aStrokeAttributes ); 1842 1843 // underlines/strikethrough - background 1844 mrCanvas->fillPolyPolygon( mrLinePolygon, 1845 mrViewState, 1846 aLocalState ); 1847 // underlines/strikethrough - border 1848 mrCanvas->strokePolyPolygon( mrLinePolygon, 1849 mrViewState, 1850 rRenderState, 1851 aStrokeAttributes ); 1852 1853 return true; 1854 } 1855 1856 private: 1857 const uno::Sequence< double > maFillColor; 1858 double mnOutlineWidth; 1859 const uno::Reference< rendering::XCanvas >& mrCanvas; 1860 const uno::Reference< rendering::XPolyPolygon2D >& mrTextPolygon; 1861 const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon; 1862 const rendering::ViewState& mrViewState; 1863 }; 1864 1865 bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation, 1866 const Subset& rSubset ) const 1867 { 1868 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::OutlineAction::render( subset )" ); 1869 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::OutlineAction: 0x%X", this ); 1870 1871 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd ) 1872 return true; // empty range, render nothing 1873 1874 #if 1 1875 // TODO(F3): Subsetting NYI for outline text! 1876 return render( rTransformation ); 1877 #else 1878 const rendering::StringContext rOrigContext( mxTextLayout->getText() ); 1879 1880 if( rSubset.mnSubsetBegin == 0 && 1881 rSubset.mnSubsetEnd == rOrigContext.Length ) 1882 { 1883 // full range, no need for subsetting 1884 return render( rTransformation ); 1885 } 1886 1887 rendering::RenderState aLocalState( maState ); 1888 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 1889 1890 1891 // create and setup local Text polygon 1892 // =================================== 1893 1894 uno::Reference< rendering::XPolyPolygon2D > xTextPolygon(); 1895 1896 // TODO(P3): Provide an API method for that! 1897 1898 if( !xTextLayout.is() ) 1899 return false; 1900 1901 // render everything 1902 // ================= 1903 1904 return renderEffectText( 1905 OutlineTextArrayRenderHelper( 1906 xCanvas, 1907 mnOutlineWidth, 1908 xTextLayout, 1909 xTextLines, 1910 rViewState ), 1911 aLocalState, 1912 rViewState, 1913 xCanvas, 1914 maShadowColor, 1915 maShadowOffset, 1916 maReliefColor, 1917 maReliefOffset ); 1918 #endif 1919 } 1920 1921 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const 1922 { 1923 rendering::RenderState aLocalState( maState ); 1924 ::canvas::tools::prependToRenderState(aLocalState, rTransformation); 1925 1926 return calcEffectTextBounds( maOutlineBounds, 1927 ::basegfx::B2DRange( 0,0, 1928 maLinesOverallSize.getX(), 1929 maLinesOverallSize.getY() ), 1930 maReliefOffset, 1931 maShadowOffset, 1932 aLocalState, 1933 mpCanvas->getViewState() ); 1934 } 1935 1936 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, 1937 const Subset& /*rSubset*/ ) const 1938 { 1939 OSL_ENSURE( false, 1940 "OutlineAction::getBounds(): Subset not yet supported by this object" ); 1941 1942 return getBounds( rTransformation ); 1943 } 1944 1945 sal_Int32 OutlineAction::getActionCount() const 1946 { 1947 // TODO(F3): Subsetting NYI for outline text! 1948 return maOffsets.getLength(); 1949 } 1950 1951 1952 // ====================================================================== 1953 // 1954 // Action factory methods 1955 // 1956 // ====================================================================== 1957 1958 /** Create an outline action 1959 1960 This method extracts the polygonal outline from the 1961 text, and creates a properly setup OutlineAction from 1962 it. 1963 */ 1964 ActionSharedPtr createOutline( const ::basegfx::B2DPoint& rStartPoint, 1965 const ::basegfx::B2DSize& rReliefOffset, 1966 const ::Color& rReliefColor, 1967 const ::basegfx::B2DSize& rShadowOffset, 1968 const ::Color& rShadowColor, 1969 const String& rText, 1970 sal_Int32 nStartPos, 1971 sal_Int32 nLen, 1972 const sal_Int32* pDXArray, 1973 VirtualDevice& rVDev, 1974 const CanvasSharedPtr& rCanvas, 1975 const OutDevState& rState, 1976 const Renderer::Parameters& rParms ) 1977 { 1978 // operate on raw DX array here (in logical coordinate 1979 // system), to have a higher resolution 1980 // PolyPolygon. That polygon is then converted to 1981 // device coordinate system. 1982 1983 // #i68512# Temporarily switch off font rotation 1984 // (which is already contained in the render state 1985 // transformation matrix - otherwise, glyph polygons 1986 // will be rotated twice) 1987 const ::Font aOrigFont( rVDev.GetFont() ); 1988 ::Font aUnrotatedFont( aOrigFont ); 1989 aUnrotatedFont.SetOrientation(0); 1990 rVDev.SetFont( aUnrotatedFont ); 1991 1992 // TODO(F3): Don't understand parameter semantics of 1993 // GetTextOutlines() 1994 ::basegfx::B2DPolyPolygon aResultingPolyPolygon; 1995 PolyPolyVector aVCLPolyPolyVector; 1996 const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText, 1997 static_cast<sal_uInt16>(nStartPos), 1998 static_cast<sal_uInt16>(nStartPos), 1999 static_cast<sal_uInt16>(nLen), 2000 sal_True, 0, pDXArray ) ); 2001 rVDev.SetFont(aOrigFont); 2002 2003 if( !bHaveOutlines ) 2004 return ActionSharedPtr(); 2005 2006 ::std::vector< sal_Int32 > aPolygonGlyphMap; 2007 2008 // first glyph starts at polygon index 0 2009 aPolygonGlyphMap.push_back( 0 ); 2010 2011 // remove offsetting from mapmode transformation 2012 // (outline polygons must stay at origin, only need to 2013 // be scaled) 2014 ::basegfx::B2DHomMatrix aMapModeTransform( 2015 rState.mapModeTransform ); 2016 aMapModeTransform.set(0,2, 0.0); 2017 aMapModeTransform.set(1,2, 0.0); 2018 2019 PolyPolyVector::const_iterator aIter( aVCLPolyPolyVector.begin() ); 2020 const PolyPolyVector::const_iterator aEnd( aVCLPolyPolyVector.end() ); 2021 for( ; aIter!= aEnd; ++aIter ) 2022 { 2023 ::basegfx::B2DPolyPolygon aPolyPolygon; 2024 2025 aPolyPolygon = aIter->getB2DPolyPolygon(); 2026 aPolyPolygon.transform( aMapModeTransform ); 2027 2028 // append result to collecting polypoly 2029 for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i ) 2030 { 2031 // #i47795# Ensure closed polygons (since 2032 // FreeType returns the glyph outlines 2033 // open) 2034 const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) ); 2035 const sal_uInt32 nCount( rPoly.count() ); 2036 if( nCount<3 || 2037 rPoly.isClosed() ) 2038 { 2039 // polygon either degenerate, or 2040 // already closed. 2041 aResultingPolyPolygon.append( rPoly ); 2042 } 2043 else 2044 { 2045 ::basegfx::B2DPolygon aPoly(rPoly); 2046 aPoly.setClosed(true); 2047 2048 aResultingPolyPolygon.append( aPoly ); 2049 } 2050 } 2051 2052 // TODO(F3): Depending on the semantics of 2053 // GetTextOutlines(), this here is wrong! 2054 2055 // calc next glyph index 2056 aPolygonGlyphMap.push_back( aResultingPolyPolygon.count() ); 2057 } 2058 2059 const uno::Sequence< double > aCharWidthSeq( 2060 pDXArray ? 2061 setupDXArray( pDXArray, nLen, rState ) : 2062 setupDXArray( rText, 2063 nStartPos, 2064 nLen, 2065 rVDev, 2066 rState )); 2067 const uno::Reference< rendering::XPolyPolygon2D > xTextPoly( 2068 ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 2069 rCanvas->getUNOCanvas()->getDevice(), 2070 aResultingPolyPolygon ) ); 2071 2072 if( rParms.maTextTransformation.is_initialized() ) 2073 { 2074 return ActionSharedPtr( 2075 new OutlineAction( 2076 rStartPoint, 2077 rReliefOffset, 2078 rReliefColor, 2079 rShadowOffset, 2080 rShadowColor, 2081 ::basegfx::tools::getRange(aResultingPolyPolygon), 2082 xTextPoly, 2083 aPolygonGlyphMap, 2084 aCharWidthSeq, 2085 rVDev, 2086 rCanvas, 2087 rState, 2088 *rParms.maTextTransformation ) ); 2089 } 2090 else 2091 { 2092 return ActionSharedPtr( 2093 new OutlineAction( 2094 rStartPoint, 2095 rReliefOffset, 2096 rReliefColor, 2097 rShadowOffset, 2098 rShadowColor, 2099 ::basegfx::tools::getRange(aResultingPolyPolygon), 2100 xTextPoly, 2101 aPolygonGlyphMap, 2102 aCharWidthSeq, 2103 rVDev, 2104 rCanvas, 2105 rState ) ); 2106 } 2107 } 2108 2109 } // namespace 2110 2111 2112 // --------------------------------------------------------------------------------- 2113 2114 ActionSharedPtr TextActionFactory::createTextAction( const ::Point& rStartPoint, 2115 const ::Size& rReliefOffset, 2116 const ::Color& rReliefColor, 2117 const ::Size& rShadowOffset, 2118 const ::Color& rShadowColor, 2119 const String& rText, 2120 sal_Int32 nStartPos, 2121 sal_Int32 nLen, 2122 const sal_Int32* pDXArray, 2123 VirtualDevice& rVDev, 2124 const CanvasSharedPtr& rCanvas, 2125 const OutDevState& rState, 2126 const Renderer::Parameters& rParms, 2127 bool bSubsettable ) 2128 { 2129 const ::Size aBaselineOffset( tools::getBaselineOffset( rState, 2130 rVDev ) ); 2131 // #143885# maintain (nearly) full precision positioning, 2132 // by circumventing integer-based OutDev-mapping 2133 const ::basegfx::B2DPoint aStartPoint( 2134 rState.mapModeTransform * 2135 ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(), 2136 rStartPoint.Y() + aBaselineOffset.Height()) ); 2137 2138 const ::basegfx::B2DSize aReliefOffset( 2139 rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rReliefOffset ) ); 2140 const ::basegfx::B2DSize aShadowOffset( 2141 rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rShadowOffset ) ); 2142 2143 if( rState.isTextOutlineModeSet ) 2144 { 2145 return createOutline( 2146 aStartPoint, 2147 aReliefOffset, 2148 rReliefColor, 2149 aShadowOffset, 2150 rShadowColor, 2151 rText, 2152 nStartPos, 2153 nLen, 2154 pDXArray, 2155 rVDev, 2156 rCanvas, 2157 rState, 2158 rParms ); 2159 } 2160 2161 // convert DX array to device coordinate system (and 2162 // create it in the first place, if pDXArray is NULL) 2163 const uno::Sequence< double > aCharWidths( 2164 pDXArray ? 2165 setupDXArray( pDXArray, nLen, rState ) : 2166 setupDXArray( rText, 2167 nStartPos, 2168 nLen, 2169 rVDev, 2170 rState )); 2171 2172 // determine type of text action to create 2173 // ======================================= 2174 2175 const ::Color aEmptyColor( COL_AUTO ); 2176 2177 // no DX array, and no need to subset - no need to store 2178 // DX array, then. 2179 if( !pDXArray && !bSubsettable ) 2180 { 2181 // effects, or not? 2182 if( !rState.textOverlineStyle && 2183 !rState.textUnderlineStyle && 2184 !rState.textStrikeoutStyle && 2185 rReliefColor == aEmptyColor && 2186 rShadowColor == aEmptyColor ) 2187 { 2188 // nope 2189 if( rParms.maTextTransformation.is_initialized() ) 2190 { 2191 return ActionSharedPtr( new TextAction( 2192 aStartPoint, 2193 rText, 2194 nStartPos, 2195 nLen, 2196 rCanvas, 2197 rState, 2198 *rParms.maTextTransformation ) ); 2199 } 2200 else 2201 { 2202 return ActionSharedPtr( new TextAction( 2203 aStartPoint, 2204 rText, 2205 nStartPos, 2206 nLen, 2207 rCanvas, 2208 rState ) ); 2209 } 2210 } 2211 else 2212 { 2213 // at least one of the effects requested 2214 if( rParms.maTextTransformation.is_initialized() ) 2215 return ActionSharedPtr( new EffectTextAction( 2216 aStartPoint, 2217 aReliefOffset, 2218 rReliefColor, 2219 aShadowOffset, 2220 rShadowColor, 2221 rText, 2222 nStartPos, 2223 nLen, 2224 rVDev, 2225 rCanvas, 2226 rState, 2227 *rParms.maTextTransformation ) ); 2228 else 2229 return ActionSharedPtr( new EffectTextAction( 2230 aStartPoint, 2231 aReliefOffset, 2232 rReliefColor, 2233 aShadowOffset, 2234 rShadowColor, 2235 rText, 2236 nStartPos, 2237 nLen, 2238 rVDev, 2239 rCanvas, 2240 rState ) ); 2241 } 2242 } 2243 else 2244 { 2245 // DX array necessary - any effects? 2246 if( !rState.textOverlineStyle && 2247 !rState.textUnderlineStyle && 2248 !rState.textStrikeoutStyle && 2249 rReliefColor == aEmptyColor && 2250 rShadowColor == aEmptyColor ) 2251 { 2252 // nope 2253 if( rParms.maTextTransformation.is_initialized() ) 2254 return ActionSharedPtr( new TextArrayAction( 2255 aStartPoint, 2256 rText, 2257 nStartPos, 2258 nLen, 2259 aCharWidths, 2260 rCanvas, 2261 rState, 2262 *rParms.maTextTransformation ) ); 2263 else 2264 return ActionSharedPtr( new TextArrayAction( 2265 aStartPoint, 2266 rText, 2267 nStartPos, 2268 nLen, 2269 aCharWidths, 2270 rCanvas, 2271 rState ) ); 2272 } 2273 else 2274 { 2275 // at least one of the effects requested 2276 if( rParms.maTextTransformation.is_initialized() ) 2277 return ActionSharedPtr( new EffectTextArrayAction( 2278 aStartPoint, 2279 aReliefOffset, 2280 rReliefColor, 2281 aShadowOffset, 2282 rShadowColor, 2283 rText, 2284 nStartPos, 2285 nLen, 2286 aCharWidths, 2287 rVDev, 2288 rCanvas, 2289 rState, 2290 *rParms.maTextTransformation ) ); 2291 else 2292 return ActionSharedPtr( new EffectTextArrayAction( 2293 aStartPoint, 2294 aReliefOffset, 2295 rReliefColor, 2296 aShadowOffset, 2297 rShadowColor, 2298 rText, 2299 nStartPos, 2300 nLen, 2301 aCharWidths, 2302 rVDev, 2303 rCanvas, 2304 rState ) ); 2305 } 2306 } 2307 #if defined __GNUC__ 2308 #if __GNUC__ == 4 && __GNUC_MINOR__ >= 1 2309 // Unreachable; to avoid bogus warning: 2310 return ActionSharedPtr(); 2311 #endif 2312 #endif 2313 } 2314 } 2315 } 2316