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_canvas.hxx" 30 31 #include <canvas/debug.hxx> 32 #include <tools/diagnose_ex.h> 33 #include <canvas/canvastools.hxx> 34 35 #include <com/sun/star/rendering/CompositeOperation.hpp> 36 #include <com/sun/star/rendering/TextDirection.hpp> 37 38 #include <vcl/metric.hxx> 39 #include <vcl/virdev.hxx> 40 41 #include <basegfx/matrix/b2dhommatrix.hxx> 42 #include <basegfx/numeric/ftools.hxx> 43 #include <basegfx/tools/canvastools.hxx> 44 45 #include "impltools.hxx" 46 #include "textlayout.hxx" 47 48 #include <boost/scoped_array.hpp> 49 50 51 using namespace ::com::sun::star; 52 53 namespace vclcanvas 54 { 55 namespace 56 { 57 void setupLayoutMode( OutputDevice& rOutDev, 58 sal_Int8 nTextDirection ) 59 { 60 // TODO(P3): avoid if already correctly set 61 sal_uIntPtr nLayoutMode; 62 switch( nTextDirection ) 63 { 64 default: 65 nLayoutMode = 0; 66 break; 67 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT: 68 nLayoutMode = TEXT_LAYOUT_BIDI_LTR; 69 break; 70 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT: 71 nLayoutMode = TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG; 72 break; 73 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT: 74 nLayoutMode = TEXT_LAYOUT_BIDI_RTL; 75 break; 76 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT: 77 nLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG; 78 break; 79 } 80 81 // set calculated layout mode. Origin is always the left edge, 82 // as required at the API spec 83 rOutDev.SetLayoutMode( nLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT ); 84 } 85 } 86 87 TextLayout::TextLayout( const rendering::StringContext& aText, 88 sal_Int8 nDirection, 89 sal_Int64 nRandomSeed, 90 const CanvasFont::Reference& rFont, 91 const uno::Reference<rendering::XGraphicDevice>& xDevice, 92 const OutDevProviderSharedPtr& rOutDev ) : 93 TextLayout_Base( m_aMutex ), 94 maText( aText ), 95 maLogicalAdvancements(), 96 mpFont( rFont ), 97 mxDevice( xDevice ), 98 mpOutDevProvider( rOutDev ), 99 mnTextDirection( nDirection ) 100 { 101 (void)nRandomSeed; 102 } 103 104 void SAL_CALL TextLayout::disposing() 105 { 106 tools::LocalGuard aGuard; 107 108 mpOutDevProvider.reset(); 109 mxDevice.clear(); 110 mpFont.reset(); 111 } 112 113 // XTextLayout 114 uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( ) throw (uno::RuntimeException) 115 { 116 tools::LocalGuard aGuard; 117 118 OutputDevice& rOutDev = mpOutDevProvider->getOutDev(); 119 VirtualDevice aVDev( rOutDev ); 120 aVDev.SetFont( mpFont->getVCLFont() ); 121 122 setupLayoutMode( aVDev, mnTextDirection ); 123 124 const rendering::ViewState aViewState( 125 geometry::AffineMatrix2D(1,0,0, 0,1,0), 126 NULL); 127 128 rendering::RenderState aRenderState ( 129 geometry::AffineMatrix2D(1,0,0,0,1,0), 130 NULL, 131 uno::Sequence<double>(4), 132 rendering::CompositeOperation::SOURCE); 133 134 ::boost::scoped_array< sal_Int32 > aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]); 135 setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState); 136 137 uno::Sequence< uno::Reference< rendering::XPolyPolygon2D> > aOutlineSequence; 138 ::basegfx::B2DPolyPolygonVector aOutlines; 139 if (aVDev.GetTextOutlines( 140 aOutlines, 141 maText.Text, 142 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 143 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 144 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length), 145 sal_False, 146 0, 147 aOffsets.get())) 148 { 149 aOutlineSequence.realloc(aOutlines.size()); 150 sal_Int32 nIndex (0); 151 for (::basegfx::B2DPolyPolygonVector::const_iterator 152 iOutline(aOutlines.begin()), 153 iEnd(aOutlines.end()); 154 iOutline!=iEnd; 155 ++iOutline) 156 { 157 aOutlineSequence[nIndex++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 158 mxDevice, 159 *iOutline); 160 } 161 } 162 163 return aOutlineSequence; 164 } 165 166 uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( ) throw (uno::RuntimeException) 167 { 168 tools::LocalGuard aGuard; 169 170 171 OutputDevice& rOutDev = mpOutDevProvider->getOutDev(); 172 VirtualDevice aVDev( rOutDev ); 173 aVDev.SetFont( mpFont->getVCLFont() ); 174 175 setupLayoutMode( aVDev, mnTextDirection ); 176 177 const rendering::ViewState aViewState( 178 geometry::AffineMatrix2D(1,0,0, 0,1,0), 179 NULL); 180 181 rendering::RenderState aRenderState ( 182 geometry::AffineMatrix2D(1,0,0,0,1,0), 183 NULL, 184 uno::Sequence<double>(4), 185 rendering::CompositeOperation::SOURCE); 186 187 ::boost::scoped_array< sal_Int32 > aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]); 188 setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState); 189 190 MetricVector aMetricVector; 191 uno::Sequence<geometry::RealRectangle2D> aBoundingBoxes; 192 if (aVDev.GetGlyphBoundRects( 193 Point(0,0), 194 maText.Text, 195 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 196 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length), 197 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 198 aMetricVector)) 199 { 200 aBoundingBoxes.realloc(aMetricVector.size()); 201 sal_Int32 nIndex (0); 202 for (MetricVector::const_iterator 203 iMetric(aMetricVector.begin()), 204 iEnd(aMetricVector.end()); 205 iMetric!=iEnd; 206 ++iMetric) 207 { 208 aBoundingBoxes[nIndex++] = geometry::RealRectangle2D( 209 iMetric->getX(), 210 iMetric->getY(), 211 iMetric->getX() + iMetric->getWidth(), 212 iMetric->getY() + iMetric->getHeight()); 213 } 214 } 215 return aBoundingBoxes; 216 } 217 218 uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( ) throw (uno::RuntimeException) 219 { 220 tools::LocalGuard aGuard; 221 222 // TODO(F1) 223 return uno::Sequence< geometry::RealRectangle2D >(); 224 } 225 226 uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException) 227 { 228 tools::LocalGuard aGuard; 229 230 return maLogicalAdvancements; 231 } 232 233 void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements ) throw (lang::IllegalArgumentException, uno::RuntimeException) 234 { 235 tools::LocalGuard aGuard; 236 237 ENSURE_ARG_OR_THROW( aAdvancements.getLength() == maText.Length, 238 "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" ); 239 240 maLogicalAdvancements = aAdvancements; 241 } 242 243 geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( ) throw (uno::RuntimeException) 244 { 245 tools::LocalGuard aGuard; 246 247 if( !mpOutDevProvider ) 248 return geometry::RealRectangle2D(); 249 250 OutputDevice& rOutDev = mpOutDevProvider->getOutDev(); 251 252 VirtualDevice aVDev( rOutDev ); 253 aVDev.SetFont( mpFont->getVCLFont() ); 254 255 // need metrics for Y offset, the XCanvas always renders 256 // relative to baseline 257 const ::FontMetric& aMetric( aVDev.GetFontMetric() ); 258 259 setupLayoutMode( aVDev, mnTextDirection ); 260 261 const sal_Int32 nAboveBaseline( /*-aMetric.GetIntLeading()*/ - aMetric.GetAscent() ); 262 const sal_Int32 nBelowBaseline( aMetric.GetDescent() ); 263 264 if( maLogicalAdvancements.getLength() ) 265 { 266 return geometry::RealRectangle2D( 0, nAboveBaseline, 267 maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ], 268 nBelowBaseline ); 269 } 270 else 271 { 272 return geometry::RealRectangle2D( 0, nAboveBaseline, 273 aVDev.GetTextWidth( 274 maText.Text, 275 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 276 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ), 277 nBelowBaseline ); 278 } 279 } 280 281 double SAL_CALL TextLayout::justify( double nSize ) throw (lang::IllegalArgumentException, uno::RuntimeException) 282 { 283 tools::LocalGuard aGuard; 284 285 (void)nSize; 286 287 // TODO(F1) 288 return 0.0; 289 } 290 291 double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& aNextLayouts, 292 double nSize ) throw (lang::IllegalArgumentException, uno::RuntimeException) 293 { 294 tools::LocalGuard aGuard; 295 296 (void)aNextLayouts; 297 (void)nSize; 298 299 // TODO(F1) 300 return 0.0; 301 } 302 303 rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& aHitPoint ) throw (uno::RuntimeException) 304 { 305 tools::LocalGuard aGuard; 306 307 (void)aHitPoint; 308 309 // TODO(F1) 310 return rendering::TextHit(); 311 } 312 313 rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 nInsertionIndex, sal_Bool bExcludeLigatures ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 314 { 315 tools::LocalGuard aGuard; 316 317 (void)nInsertionIndex; 318 (void)bExcludeLigatures; 319 320 // TODO(F1) 321 return rendering::Caret(); 322 } 323 324 sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 nStartIndex, sal_Int32 nCaretAdvancement, sal_Bool bExcludeLigatures ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 325 { 326 tools::LocalGuard aGuard; 327 328 (void)nStartIndex; 329 (void)nCaretAdvancement; 330 (void)bExcludeLigatures; 331 332 // TODO(F1) 333 return 0; 334 } 335 336 uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 337 { 338 tools::LocalGuard aGuard; 339 340 (void)nStartIndex; 341 (void)nEndIndex; 342 343 // TODO(F1) 344 return uno::Reference< rendering::XPolyPolygon2D >(); 345 } 346 347 uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 348 { 349 tools::LocalGuard aGuard; 350 351 (void)nStartIndex; 352 (void)nEndIndex; 353 354 // TODO(F1) 355 return uno::Reference< rendering::XPolyPolygon2D >(); 356 } 357 358 double SAL_CALL TextLayout::getBaselineOffset( ) throw (uno::RuntimeException) 359 { 360 tools::LocalGuard aGuard; 361 362 // TODO(F1) 363 return 0.0; 364 } 365 366 sal_Int8 SAL_CALL TextLayout::getMainTextDirection( ) throw (uno::RuntimeException) 367 { 368 tools::LocalGuard aGuard; 369 370 return mnTextDirection; 371 } 372 373 uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( ) throw (uno::RuntimeException) 374 { 375 tools::LocalGuard aGuard; 376 377 return mpFont.getRef(); 378 } 379 380 rendering::StringContext SAL_CALL TextLayout::getText( ) throw (uno::RuntimeException) 381 { 382 tools::LocalGuard aGuard; 383 384 return maText; 385 } 386 387 bool TextLayout::draw( OutputDevice& rOutDev, 388 const Point& rOutpos, 389 const rendering::ViewState& viewState, 390 const rendering::RenderState& renderState ) const 391 { 392 tools::LocalGuard aGuard; 393 394 setupLayoutMode( rOutDev, mnTextDirection ); 395 396 if( maLogicalAdvancements.getLength() ) 397 { 398 // TODO(P2): cache that 399 ::boost::scoped_array< sal_Int32 > aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]); 400 setupTextOffsets( aOffsets.get(), maLogicalAdvancements, viewState, renderState ); 401 402 // TODO(F3): ensure correct length and termination for DX 403 // array (last entry _must_ contain the overall width) 404 405 rOutDev.DrawTextArray( rOutpos, 406 maText.Text, 407 aOffsets.get(), 408 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 409 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ); 410 } 411 else 412 { 413 rOutDev.DrawText( rOutpos, 414 maText.Text, 415 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 416 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ); 417 } 418 419 return true; 420 } 421 422 namespace 423 { 424 class OffsetTransformer 425 { 426 public: 427 OffsetTransformer( const ::basegfx::B2DHomMatrix& rMat ) : 428 maMatrix( rMat ) 429 { 430 } 431 432 sal_Int32 operator()( const double& rOffset ) 433 { 434 // This is an optimization of the normal rMat*[x,0] 435 // transformation of the advancement vector (in x 436 // direction), followed by a length calculation of the 437 // resulting vector: advancement' = 438 // ||rMat*[x,0]||. Since advancements are vectors, we 439 // can ignore translational components, thus if [x,0], 440 // it follows that rMat*[x,0]=[x',0] holds. Thus, we 441 // just have to calc the transformation of the x 442 // component. 443 444 // TODO(F2): Handle non-horizontal advancements! 445 return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset, 446 maMatrix.get(1,0)*rOffset) ); 447 } 448 449 private: 450 ::basegfx::B2DHomMatrix maMatrix; 451 }; 452 } 453 454 void TextLayout::setupTextOffsets( sal_Int32* outputOffsets, 455 const uno::Sequence< double >& inputOffsets, 456 const rendering::ViewState& viewState, 457 const rendering::RenderState& renderState ) const 458 { 459 ENSURE_OR_THROW( outputOffsets!=NULL, 460 "TextLayout::setupTextOffsets offsets NULL" ); 461 462 ::basegfx::B2DHomMatrix aMatrix; 463 464 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, 465 viewState, 466 renderState); 467 468 // fill integer offsets 469 ::std::transform( const_cast< uno::Sequence< double >& >(inputOffsets).getConstArray(), 470 const_cast< uno::Sequence< double >& >(inputOffsets).getConstArray()+inputOffsets.getLength(), 471 outputOffsets, 472 OffsetTransformer( aMatrix ) ); 473 } 474 475 476 #define IMPLEMENTATION_NAME "VCLCanvas::TextLayout" 477 #define SERVICE_NAME "com.sun.star.rendering.TextLayout" 478 479 ::rtl::OUString SAL_CALL TextLayout::getImplementationName() throw( uno::RuntimeException ) 480 { 481 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); 482 } 483 484 sal_Bool SAL_CALL TextLayout::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException ) 485 { 486 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) ); 487 } 488 489 uno::Sequence< ::rtl::OUString > SAL_CALL TextLayout::getSupportedServiceNames() throw( uno::RuntimeException ) 490 { 491 uno::Sequence< ::rtl::OUString > aRet(1); 492 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); 493 494 return aRet; 495 } 496 } 497