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 <math.h> 32 33 #include <canvas/debug.hxx> 34 #include <canvas/verbosetrace.hxx> 35 #include <tools/diagnose_ex.h> 36 37 #include <vcl/metric.hxx> 38 #include <vcl/virdev.hxx> 39 40 #ifdef WNT 41 #include <tools/prewin.h> 42 #include <windows.h> 43 #include <tools/postwin.h> 44 #ifdef max 45 #undef max 46 #endif 47 #ifdef min 48 #undef min 49 #endif 50 #endif 51 #include <vcl/sysdata.hxx> 52 53 #include <basegfx/matrix/b2dhommatrix.hxx> 54 #include <basegfx/numeric/ftools.hxx> 55 56 #include <boost/scoped_array.hpp> 57 58 #include "cairo_textlayout.hxx" 59 #include "cairo_spritecanvas.hxx" 60 61 #ifdef CAIRO_HAS_QUARTZ_SURFACE 62 # include "cairo_quartz_cairo.hxx" 63 #elif defined CAIRO_HAS_WIN32_SURFACE 64 # include "cairo_win32_cairo.hxx" 65 # include <cairo-win32.h> 66 #elif defined CAIRO_HAS_XLIB_SURFACE 67 # include "cairo_xlib_cairo.hxx" 68 # include <cairo-ft.h> 69 #else 70 # error Native API needed. 71 #endif 72 73 using namespace ::cairo; 74 using namespace ::com::sun::star; 75 76 namespace cairocanvas 77 { 78 namespace 79 { 80 void setupLayoutMode( OutputDevice& rOutDev, 81 sal_Int8 nTextDirection ) 82 { 83 // TODO(P3): avoid if already correctly set 84 sal_uLong nLayoutMode; 85 switch( nTextDirection ) 86 { 87 default: 88 nLayoutMode = 0; 89 break; 90 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT: 91 nLayoutMode = TEXT_LAYOUT_BIDI_LTR; 92 break; 93 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT: 94 nLayoutMode = TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG; 95 break; 96 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT: 97 nLayoutMode = TEXT_LAYOUT_BIDI_RTL; 98 break; 99 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT: 100 nLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG; 101 break; 102 } 103 104 // set calculated layout mode. Origin is always the left edge, 105 // as required at the API spec 106 rOutDev.SetLayoutMode( nLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT ); 107 } 108 109 bool compareFallbacks(const SystemGlyphData&rA, const SystemGlyphData &rB) 110 { 111 return rA.fallbacklevel < rB.fallbacklevel; 112 } 113 } 114 115 TextLayout::TextLayout( const rendering::StringContext& aText, 116 sal_Int8 nDirection, 117 sal_Int64 /*nRandomSeed*/, 118 const CanvasFont::Reference& rFont, 119 const SurfaceProviderRef& rRefDevice ) : 120 TextLayout_Base( m_aMutex ), 121 maText( aText ), 122 maLogicalAdvancements(), 123 mpFont( rFont ), 124 mpRefDevice( rRefDevice ), 125 mnTextDirection( nDirection ) 126 { 127 } 128 129 TextLayout::~TextLayout() 130 { 131 } 132 133 void SAL_CALL TextLayout::disposing() 134 { 135 ::osl::MutexGuard aGuard( m_aMutex ); 136 137 mpFont.reset(); 138 mpRefDevice.clear(); 139 } 140 141 // XTextLayout 142 uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( ) throw (uno::RuntimeException) 143 { 144 ::osl::MutexGuard aGuard( m_aMutex ); 145 146 // TODO 147 return uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > >(); 148 } 149 150 uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( ) throw (uno::RuntimeException) 151 { 152 ::osl::MutexGuard aGuard( m_aMutex ); 153 154 // TODO 155 return uno::Sequence< geometry::RealRectangle2D >(); 156 } 157 158 uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( ) throw (uno::RuntimeException) 159 { 160 ::osl::MutexGuard aGuard( m_aMutex ); 161 162 // TODO 163 return uno::Sequence< geometry::RealRectangle2D >(); 164 } 165 166 uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException) 167 { 168 ::osl::MutexGuard aGuard( m_aMutex ); 169 170 return maLogicalAdvancements; 171 } 172 173 void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements ) throw (lang::IllegalArgumentException, uno::RuntimeException) 174 { 175 ::osl::MutexGuard aGuard( m_aMutex ); 176 177 if( aAdvancements.getLength() != maText.Length ) 178 { 179 OSL_TRACE( "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" ); 180 throw lang::IllegalArgumentException(); 181 } 182 183 maLogicalAdvancements = aAdvancements; 184 } 185 186 geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( ) throw (uno::RuntimeException) 187 { 188 ::osl::MutexGuard aGuard( m_aMutex ); 189 190 OutputDevice* pOutDev = mpRefDevice->getOutputDevice(); 191 if( !pOutDev ) 192 return geometry::RealRectangle2D(); 193 194 VirtualDevice aVDev( *pOutDev ); 195 aVDev.SetFont( mpFont->getVCLFont() ); 196 197 // need metrics for Y offset, the XCanvas always renders 198 // relative to baseline 199 const ::FontMetric& aMetric( aVDev.GetFontMetric() ); 200 201 setupLayoutMode( aVDev, mnTextDirection ); 202 203 const sal_Int32 nAboveBaseline( -aMetric.GetIntLeading() - aMetric.GetAscent() ); 204 const sal_Int32 nBelowBaseline( aMetric.GetDescent() ); 205 206 if( maLogicalAdvancements.getLength() ) 207 { 208 return geometry::RealRectangle2D( 0, nAboveBaseline, 209 maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ], 210 nBelowBaseline ); 211 } 212 else 213 { 214 return geometry::RealRectangle2D( 0, nAboveBaseline, 215 aVDev.GetTextWidth( 216 maText.Text, 217 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 218 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ), 219 nBelowBaseline ); 220 } 221 } 222 223 double SAL_CALL TextLayout::justify( double /*nSize*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException) 224 { 225 ::osl::MutexGuard aGuard( m_aMutex ); 226 227 // TODO 228 return 0.0; 229 } 230 231 double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& /*aNextLayouts*/, 232 double /*nSize*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException) 233 { 234 ::osl::MutexGuard aGuard( m_aMutex ); 235 236 // TODO 237 return 0.0; 238 } 239 240 rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& /*aHitPoint*/ ) throw (uno::RuntimeException) 241 { 242 ::osl::MutexGuard aGuard( m_aMutex ); 243 244 // TODO 245 return rendering::TextHit(); 246 } 247 248 rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 /*nInsertionIndex*/, 249 sal_Bool /*bExcludeLigatures*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 250 { 251 ::osl::MutexGuard aGuard( m_aMutex ); 252 253 // TODO 254 return rendering::Caret(); 255 } 256 257 sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 /*nStartIndex*/, 258 sal_Int32 /*nCaretAdvancement*/, 259 sal_Bool /*bExcludeLigatures*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 260 { 261 ::osl::MutexGuard aGuard( m_aMutex ); 262 263 // TODO 264 return 0; 265 } 266 267 uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 /*nStartIndex*/, 268 sal_Int32 /*nEndIndex*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 269 { 270 ::osl::MutexGuard aGuard( m_aMutex ); 271 272 // TODO 273 return uno::Reference< rendering::XPolyPolygon2D >(); 274 } 275 276 uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 /*nStartIndex*/, 277 sal_Int32 /*nEndIndex*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 278 { 279 ::osl::MutexGuard aGuard( m_aMutex ); 280 281 // TODO 282 return uno::Reference< rendering::XPolyPolygon2D >(); 283 } 284 285 double SAL_CALL TextLayout::getBaselineOffset( ) throw (uno::RuntimeException) 286 { 287 ::osl::MutexGuard aGuard( m_aMutex ); 288 289 // TODO 290 return 0.0; 291 } 292 293 sal_Int8 SAL_CALL TextLayout::getMainTextDirection( ) throw (uno::RuntimeException) 294 { 295 ::osl::MutexGuard aGuard( m_aMutex ); 296 297 return mnTextDirection; 298 } 299 300 uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( ) throw (uno::RuntimeException) 301 { 302 ::osl::MutexGuard aGuard( m_aMutex ); 303 304 return mpFont.getRef(); 305 } 306 307 rendering::StringContext SAL_CALL TextLayout::getText( ) throw (uno::RuntimeException) 308 { 309 ::osl::MutexGuard aGuard( m_aMutex ); 310 311 return maText; 312 } 313 314 void TextLayout::useFont( Cairo* pCairo ) 315 { 316 rendering::FontRequest aFontRequest = mpFont->getFontRequest(); 317 rendering::FontInfo aFontInfo = aFontRequest.FontDescription; 318 319 cairo_select_font_face( pCairo, ::rtl::OUStringToOString( aFontInfo.FamilyName, RTL_TEXTENCODING_UTF8 ), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL ); 320 cairo_set_font_size( pCairo, aFontRequest.CellSize ); 321 } 322 323 /** TextLayout:draw 324 * 325 * This function uses the "toy" api of the cairo library 326 * 327 **/ 328 bool TextLayout::draw( Cairo* pCairo ) 329 { 330 ::osl::MutexGuard aGuard( m_aMutex ); 331 332 ::rtl::OUString aSubText = maText.Text.copy( maText.StartPosition, maText.Length ); 333 ::rtl::OString aUTF8String = ::rtl::OUStringToOString( aSubText, RTL_TEXTENCODING_UTF8 ); 334 335 cairo_save( pCairo ); 336 /* move to 0, 0 as cairo_show_text advances current point and current point is not restored by cairo_restore. 337 before we were depending on unmodified current point which I believed was preserved by save/restore */ 338 cairo_move_to( pCairo, 0, 0 ); 339 useFont( pCairo ); 340 cairo_show_text( pCairo, aUTF8String ); 341 cairo_restore( pCairo ); 342 343 return true; 344 } 345 346 347 /** 348 * TextLayout::isCairoRenderable 349 * 350 * Features currenly not supported by Cairo (VCL rendering is used as fallback): 351 * - vertical glyphs 352 * 353 * @return true, if text/font can be rendered with cairo 354 **/ 355 bool TextLayout::isCairoRenderable(SystemFontData aSysFontData) const 356 { 357 #if defined UNX && !defined QUARTZ 358 // is font usable? 359 if (!aSysFontData.nFontId) return false; 360 #endif 361 362 // vertical glyph rendering is not supported in cairo for now 363 if (aSysFontData.bVerticalCharacterType) { 364 OSL_TRACE(":cairocanvas::TextLayout::isCairoRenderable(): ***************** VERTICAL CHARACTER STYLE!!! ****************"); 365 return false; 366 } 367 368 return true; 369 } 370 371 /** 372 * TextLayout::draw 373 * 374 * Cairo-based text rendering. Draw text directly on the cairo surface with cairo fonts. 375 * Avoid using VCL VirtualDevices for that, bypassing VCL DrawText functions, when possible 376 * 377 * Note: some text effects are not rendered due to lacking generic canvas or cairo canvas 378 * implementation. See issues 92657, 92658, 92659, 92660, 97529 379 * 380 * @return true, if successful 381 **/ 382 bool TextLayout::draw( SurfaceSharedPtr& pSurface, 383 OutputDevice& rOutDev, 384 const Point& rOutpos, 385 const rendering::ViewState& viewState, 386 const rendering::RenderState& renderState ) const 387 { 388 ::osl::MutexGuard aGuard( m_aMutex ); 389 SystemTextLayoutData aSysLayoutData; 390 #if (defined CAIRO_HAS_WIN32_SURFACE) && (OSL_DEBUG_LEVEL > 1) 391 LOGFONTW logfont; 392 #endif 393 setupLayoutMode( rOutDev, mnTextDirection ); 394 395 // TODO(P2): cache that 396 ::boost::scoped_array< sal_Int32 > aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]); 397 398 if( maLogicalAdvancements.getLength() ) 399 { 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 406 aSysLayoutData = rOutDev.GetSysTextLayoutData(rOutpos, maText.Text, 407 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 408 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length), 409 maLogicalAdvancements.getLength() ? aOffsets.get() : NULL); 410 411 // Sort them so that all glyphs on the same glyph fallback level are consecutive 412 std::sort(aSysLayoutData.rGlyphData.begin(), aSysLayoutData.rGlyphData.end(), compareFallbacks); 413 bool bCairoRenderable = true; 414 415 //Pull all the fonts we need to render the text 416 typedef std::pair<SystemFontData,int> FontLevel; 417 typedef std::vector<FontLevel> FontLevelVector; 418 FontLevelVector aFontData; 419 SystemGlyphDataVector::const_iterator aIter=aSysLayoutData.rGlyphData.begin(); 420 const SystemGlyphDataVector::const_iterator aEnd=aSysLayoutData.rGlyphData.end(); 421 for( ; aIter != aEnd; ++aIter ) 422 { 423 if( aFontData.empty() || aIter->fallbacklevel != aFontData.back().second ) 424 { 425 aFontData.push_back(FontLevel(rOutDev.GetSysFontData(aIter->fallbacklevel), 426 aIter->fallbacklevel)); 427 if( !isCairoRenderable(aFontData.back().first) ) 428 { 429 bCairoRenderable = false; 430 OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): VCL FALLBACK %s%s%s%s - %s", 431 maLogicalAdvancements.getLength() ? "ADV " : "", 432 aFontData.back().first.bAntialias ? "AA " : "", 433 aFontData.back().first.bFakeBold ? "FB " : "", 434 aFontData.back().first.bFakeItalic ? "FI " : "", 435 ::rtl::OUStringToOString( maText.Text.copy( maText.StartPosition, maText.Length ), 436 RTL_TEXTENCODING_UTF8 ).getStr()); 437 break; 438 } 439 } 440 } 441 442 // The ::GetSysTextLayoutData(), i.e. layouting of text to glyphs can change the font being used. 443 // The fallback checks need to be done after final font is known. 444 if (!bCairoRenderable) // VCL FALLBACKS 445 { 446 if (maLogicalAdvancements.getLength()) // VCL FALLBACK - with glyph advances 447 { 448 rOutDev.DrawTextArray( rOutpos, maText.Text, aOffsets.get(), 449 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 450 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ); 451 return true; 452 } 453 else // VCL FALLBACK - without advances 454 { 455 rOutDev.DrawText( rOutpos, maText.Text, 456 ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition), 457 ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ); 458 return true; 459 } 460 } 461 462 if (aSysLayoutData.rGlyphData.empty()) return false; //??? false? 463 464 /** 465 * Setup platform independent glyph vector into cairo-based glyphs vector. 466 **/ 467 468 // Loop through the fonts used and render the matching glyphs for each 469 FontLevelVector::const_iterator aFontDataIter = aFontData.begin(); 470 const FontLevelVector::const_iterator aFontDataEnd = aFontData.end(); 471 for( ; aFontDataIter != aFontDataEnd; ++aFontDataIter ) 472 { 473 const SystemFontData &rSysFontData = aFontDataIter->first; 474 475 // setup glyphs 476 std::vector<cairo_glyph_t> cairo_glyphs; 477 cairo_glyphs.reserve( 256 ); 478 479 SystemGlyphDataVector::const_iterator aIter=aSysLayoutData.rGlyphData.begin(); 480 const SystemGlyphDataVector::const_iterator aEnd=aSysLayoutData.rGlyphData.end(); 481 for( ; aIter != aEnd; ++aIter ) 482 { 483 SystemGlyphData systemGlyph = *aIter; 484 if( systemGlyph.fallbacklevel != aFontDataIter->second ) 485 continue; 486 487 cairo_glyph_t aGlyph; 488 aGlyph.index = systemGlyph.index; 489 #ifdef CAIRO_HAS_WIN32_SURFACE 490 // Cairo requires standard glyph indexes (ETO_GLYPH_INDEX), while vcl/win/* uses ucs4 chars. 491 // Convert to standard indexes 492 aGlyph.index = cairo::ucs4toindex((unsigned int) aGlyph.index, rSysFontData.hFont); 493 #endif 494 aGlyph.x = systemGlyph.x; 495 aGlyph.y = systemGlyph.y; 496 cairo_glyphs.push_back(aGlyph); 497 } 498 499 if (cairo_glyphs.empty()) 500 continue; 501 502 /** 503 * Setup font 504 **/ 505 cairo_font_face_t* font_face = NULL; 506 507 #ifdef CAIRO_HAS_QUARTZ_SURFACE 508 // TODO: use cairo_quartz_font_face_create_for_cgfont(cgFont) 509 // when CGFont (Mac OS X 10.5 API) is provided by the AQUA VCL backend. 510 font_face = cairo_quartz_font_face_create_for_atsu_font_id((ATSUFontID) rSysFontData.aATSUFontID); 511 512 #elif defined CAIRO_HAS_WIN32_SURFACE 513 #if (OSL_DEBUG_LEVEL > 1) 514 GetObjectW( rSysFontData.hFont, sizeof(logfont), &logfont ); 515 #endif 516 // Note: cairo library uses logfont fallbacks when lfEscapement, lfOrientation and lfWidth are not zero. 517 // VCL always has non-zero value for lfWidth 518 font_face = cairo_win32_font_face_create_for_hfont(rSysFontData.hFont); 519 520 #elif defined CAIRO_HAS_XLIB_SURFACE 521 font_face = cairo_ft_font_face_create_for_ft_face((FT_Face)rSysFontData.nFontId, 522 rSysFontData.nFontFlags); 523 #else 524 # error Native API needed. 525 #endif 526 527 CairoSharedPtr pSCairo = pSurface->getCairo(); 528 529 cairo_set_font_face( pSCairo.get(), font_face); 530 531 // create default font options. cairo_get_font_options() does not retrieve the surface defaults, 532 // only what has been set before with cairo_set_font_options() 533 cairo_font_options_t* options = cairo_font_options_create(); 534 if (rSysFontData.bAntialias) { 535 // CAIRO_ANTIALIAS_GRAY provides more similar result to VCL Canvas, 536 // so we're not using CAIRO_ANTIALIAS_SUBPIXEL 537 cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); 538 } 539 cairo_set_font_options( pSCairo.get(), options); 540 541 // Font color 542 Color mTextColor = rOutDev.GetTextColor(); 543 cairo_set_source_rgb(pSCairo.get(), 544 mTextColor.GetRed()/255.0, 545 mTextColor.GetGreen()/255.0, 546 mTextColor.GetBlue()/255.0); 547 548 // Font rotation and scaling 549 cairo_matrix_t m; 550 Font aFont = rOutDev.GetFont(); 551 FontMetric aMetric( rOutDev.GetFontMetric(aFont) ); 552 long nWidth = 0; 553 554 // width calculation is deep magic and platform/font dependant. 555 // width == 0 means no scaling, and usually width == height means the same. 556 // Other values mean horizontal scaling (narrow or stretching) 557 // see issue #101566 558 559 //proper scale calculation across platforms 560 if (aFont.GetWidth() == 0) { 561 nWidth = aFont.GetHeight(); 562 } else { 563 // any scaling needs to be relative to the platform-dependent definition 564 // of height of the font 565 nWidth = aFont.GetWidth() * aFont.GetHeight() / aMetric.GetHeight(); 566 } 567 568 cairo_matrix_init_identity(&m); 569 570 if (aSysLayoutData.orientation) cairo_matrix_rotate(&m, (3600 - aSysLayoutData.orientation) * M_PI / 1800.0); 571 572 cairo_matrix_scale(&m, nWidth, aFont.GetHeight()); 573 574 //faux italics 575 if (rSysFontData.bFakeItalic) m.xy = -m.xx * 0x6000L / 0x10000L; 576 577 cairo_set_font_matrix(pSCairo.get(), &m); 578 579 OSL_TRACE("\r\n:cairocanvas::TextLayout::draw(S,O,p,v,r): Size:(%d,%d), W:%d->%d, Pos (%d,%d), G(%d,%d,%d) %s%s%s%s || Name:%s - %s", 580 aFont.GetWidth(), 581 aFont.GetHeight(), 582 aMetric.GetWidth(), 583 nWidth, 584 (int) rOutpos.X(), 585 (int) rOutpos.Y(), 586 cairo_glyphs[0].index, cairo_glyphs[1].index, cairo_glyphs[2].index, 587 maLogicalAdvancements.getLength() ? "ADV " : "", 588 rSysFontData.bAntialias ? "AA " : "", 589 rSysFontData.bFakeBold ? "FB " : "", 590 rSysFontData.bFakeItalic ? "FI " : "", 591 #if (defined CAIRO_HAS_WIN32_SURFACE) && (OSL_DEBUG_LEVEL > 1) 592 ::rtl::OUStringToOString( reinterpret_cast<const sal_Unicode*> (logfont.lfFaceName), RTL_TEXTENCODING_UTF8 ).getStr(), 593 #else 594 ::rtl::OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr(), 595 #endif 596 ::rtl::OUStringToOString( maText.Text.copy( maText.StartPosition, maText.Length ), 597 RTL_TEXTENCODING_UTF8 ).getStr() 598 ); 599 600 cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size()); 601 602 //faux bold 603 if (rSysFontData.bFakeBold) { 604 double bold_dx = 0.5 * sqrt( 0.7 * aFont.GetHeight() ); 605 int total_steps = 2 * ((int) (bold_dx + 0.5)); 606 607 // loop to draw the text for every half pixel of displacement 608 for (int nSteps = 0; nSteps < total_steps; nSteps++) { 609 for(int nGlyphIdx = 0; nGlyphIdx < (int) cairo_glyphs.size(); nGlyphIdx++) { 610 cairo_glyphs[nGlyphIdx].x += bold_dx * nSteps / total_steps; 611 } 612 cairo_show_glyphs(pSCairo.get(), &cairo_glyphs[0], cairo_glyphs.size()); 613 } 614 OSL_TRACE(":cairocanvas::TextLayout::draw(S,O,p,v,r): FAKEBOLD - dx:%d", (int) bold_dx); 615 } 616 617 cairo_restore( pSCairo.get() ); 618 cairo_font_face_destroy(font_face); 619 } 620 return true; 621 } 622 623 624 namespace 625 { 626 class OffsetTransformer 627 { 628 public: 629 OffsetTransformer( const ::basegfx::B2DHomMatrix& rMat ) : 630 maMatrix( rMat ) 631 { 632 } 633 634 sal_Int32 operator()( const double& rOffset ) 635 { 636 // This is an optimization of the normal rMat*[x,0] 637 // transformation of the advancement vector (in x 638 // direction), followed by a length calculation of the 639 // resulting vector: advancement' = 640 // ||rMat*[x,0]||. Since advancements are vectors, we 641 // can ignore translational components, thus if [x,0], 642 // it follows that rMat*[x,0]=[x',0] holds. Thus, we 643 // just have to calc the transformation of the x 644 // component. 645 646 // TODO(F2): Handle non-horizontal advancements! 647 return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset, 648 maMatrix.get(1,0)*rOffset) ); 649 } 650 651 private: 652 ::basegfx::B2DHomMatrix maMatrix; 653 }; 654 } 655 656 void TextLayout::setupTextOffsets( sal_Int32* outputOffsets, 657 const uno::Sequence< double >& inputOffsets, 658 const rendering::ViewState& viewState, 659 const rendering::RenderState& renderState ) const 660 { 661 ENSURE_OR_THROW( outputOffsets!=NULL, 662 "TextLayout::setupTextOffsets offsets NULL" ); 663 664 ::basegfx::B2DHomMatrix aMatrix; 665 666 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, 667 viewState, 668 renderState); 669 670 // fill integer offsets 671 ::std::transform( const_cast< uno::Sequence< double >& >(inputOffsets).getConstArray(), 672 const_cast< uno::Sequence< double >& >(inputOffsets).getConstArray()+inputOffsets.getLength(), 673 outputOffsets, 674 OffsetTransformer( aMatrix ) ); 675 } 676 677 #define SERVICE_NAME "com.sun.star.rendering.TextLayout" 678 #define IMPLEMENTATION_NAME "CairoCanvas::TextLayout" 679 680 ::rtl::OUString SAL_CALL TextLayout::getImplementationName() throw( uno::RuntimeException ) 681 { 682 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); 683 } 684 685 sal_Bool SAL_CALL TextLayout::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException ) 686 { 687 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) ); 688 } 689 690 uno::Sequence< ::rtl::OUString > SAL_CALL TextLayout::getSupportedServiceNames() throw( uno::RuntimeException ) 691 { 692 uno::Sequence< ::rtl::OUString > aRet(1); 693 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); 694 695 return aRet; 696 } 697 } 698