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