1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <i18nlangtag/mslangid.hxx> 21 22 #include <unotools/configmgr.hxx> 23 #include <vcl/virdev.hxx> 24 #include <vcl/print.hxx> 25 #include <vcl/sysdata.hxx> 26 #include <vcl/fontcharmap.hxx> 27 28 #include <sallayout.hxx> 29 #include <salgdi.hxx> 30 #include <svdata.hxx> 31 32 #include <outdev.h> 33 #include <window.h> 34 35 #include <PhysicalFontCollection.hxx> 36 37 #include <strings.hrc> 38 39 FontMetric OutputDevice::GetDevFont( int nDevFontIndex ) const 40 { 41 FontMetric aFontMetric; 42 43 ImplInitFontList(); 44 45 int nCount = GetDevFontCount(); 46 if( nDevFontIndex < nCount ) 47 { 48 const PhysicalFontFace& rData = *mpDeviceFontList->Get( nDevFontIndex ); 49 aFontMetric.SetFamilyName( rData.GetFamilyName() ); 50 aFontMetric.SetStyleName( rData.GetStyleName() ); 51 aFontMetric.SetCharSet( rData.GetCharSet() ); 52 aFontMetric.SetFamily( rData.GetFamilyType() ); 53 aFontMetric.SetPitch( rData.GetPitch() ); 54 aFontMetric.SetWeight( rData.GetWeight() ); 55 aFontMetric.SetItalic( rData.GetItalic() ); 56 aFontMetric.SetAlignment( TextAlign::ALIGN_TOP ); 57 aFontMetric.SetWidthType( rData.GetWidthType() ); 58 aFontMetric.SetQuality( rData.GetQuality() ); 59 aFontMetric.SetMapNames( rData.GetMapNames() ); 60 } 61 62 return aFontMetric; 63 } 64 65 int OutputDevice::GetDevFontCount() const 66 { 67 if( !mpDeviceFontList ) 68 { 69 if (!mpFontCollection) 70 { 71 return 0; 72 } 73 74 mpDeviceFontList = mpFontCollection->GetDeviceFontList(); 75 76 if (!mpDeviceFontList->Count()) 77 { 78 delete mpDeviceFontList; 79 mpDeviceFontList = nullptr; 80 81 return 0; 82 } 83 } 84 return mpDeviceFontList->Count(); 85 } 86 87 bool OutputDevice::IsFontAvailable( const OUString& rFontName ) const 88 { 89 PhysicalFontFamily* pFound = mpFontCollection->FindFontFamily( rFontName ); 90 return (pFound != nullptr); 91 } 92 93 int OutputDevice::GetDevFontSizeCount( const vcl::Font& rFont ) const 94 { 95 delete mpDeviceFontSizeList; 96 97 ImplInitFontList(); 98 mpDeviceFontSizeList = mpFontCollection->GetDeviceFontSizeList( rFont.GetFamilyName() ); 99 return mpDeviceFontSizeList->Count(); 100 } 101 102 Size OutputDevice::GetDevFontSize( const vcl::Font& rFont, int nSizeIndex ) const 103 { 104 // check range 105 int nCount = GetDevFontSizeCount( rFont ); 106 if ( nSizeIndex >= nCount ) 107 return Size(); 108 109 // when mapping is enabled round to .5 points 110 Size aSize( 0, mpDeviceFontSizeList->Get( nSizeIndex ) ); 111 if ( mbMap ) 112 { 113 aSize.Height() *= 10; 114 MapMode aMap( MapUnit::Map10thInch, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) ); 115 aSize = PixelToLogic( aSize, aMap ); 116 aSize.Height() += 5; 117 aSize.Height() /= 10; 118 long nRound = aSize.Height() % 5; 119 if ( nRound >= 3 ) 120 aSize.Height() += (5-nRound); 121 else 122 aSize.Height() -= nRound; 123 aSize.Height() *= 10; 124 aSize = LogicToPixel( aSize, aMap ); 125 aSize = PixelToLogic( aSize ); 126 aSize.Height() += 5; 127 aSize.Height() /= 10; 128 } 129 return aSize; 130 } 131 132 namespace 133 { 134 struct UpdateFontsGuard 135 { 136 UpdateFontsGuard() 137 { 138 OutputDevice::ImplClearAllFontData(true); 139 } 140 141 ~UpdateFontsGuard() 142 { 143 OutputDevice::ImplRefreshAllFontData(true); 144 } 145 }; 146 } 147 148 bool OutputDevice::AddTempDevFont( const OUString& rFileURL, const OUString& rFontName ) 149 { 150 UpdateFontsGuard aUpdateFontsGuard; 151 152 ImplInitFontList(); 153 154 if( !mpGraphics && !AcquireGraphics() ) 155 return false; 156 157 bool bRC = mpGraphics->AddTempDevFont( mpFontCollection, rFileURL, rFontName ); 158 if( !bRC ) 159 return false; 160 161 if( mpAlphaVDev ) 162 mpAlphaVDev->AddTempDevFont( rFileURL, rFontName ); 163 164 return true; 165 } 166 167 FontMetric OutputDevice::GetFontMetric() const 168 { 169 FontMetric aMetric; 170 if( mbNewFont && !ImplNewFont() ) 171 return aMetric; 172 173 LogicalFontInstance* pFontInstance = mpFontInstance; 174 ImplFontMetricDataRef xFontMetric = pFontInstance->mxFontMetric; 175 176 // prepare metric 177 aMetric.Font::operator=( maFont ); 178 179 // set aMetric with info from font 180 aMetric.SetFamilyName( maFont.GetFamilyName() ); 181 aMetric.SetStyleName( xFontMetric->GetStyleName() ); 182 aMetric.SetFontSize( PixelToLogic( Size( xFontMetric->GetWidth(), xFontMetric->GetAscent() + xFontMetric->GetDescent() - xFontMetric->GetInternalLeading() ) ) ); 183 aMetric.SetCharSet( xFontMetric->IsSymbolFont() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); 184 aMetric.SetFamily( xFontMetric->GetFamilyType() ); 185 aMetric.SetPitch( xFontMetric->GetPitch() ); 186 aMetric.SetWeight( xFontMetric->GetWeight() ); 187 aMetric.SetItalic( xFontMetric->GetItalic() ); 188 aMetric.SetAlignment( TextAlign::ALIGN_TOP ); 189 aMetric.SetWidthType( xFontMetric->GetWidthType() ); 190 if ( pFontInstance->mnOwnOrientation ) 191 aMetric.SetOrientation( pFontInstance->mnOwnOrientation ); 192 else 193 aMetric.SetOrientation( xFontMetric->GetOrientation() ); 194 195 // set remaining metric fields 196 aMetric.SetFullstopCenteredFlag( xFontMetric->IsFullstopCentered() ); 197 aMetric.SetBulletOffset( xFontMetric->GetBulletOffset() ); 198 aMetric.SetAscent( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + mnEmphasisAscent ) ); 199 aMetric.SetDescent( ImplDevicePixelToLogicHeight( xFontMetric->GetDescent() + mnEmphasisDescent ) ); 200 aMetric.SetInternalLeading( ImplDevicePixelToLogicHeight( xFontMetric->GetInternalLeading() + mnEmphasisAscent ) ); 201 // OutputDevice has its own external leading function due to #i60945# 202 aMetric.SetExternalLeading( ImplDevicePixelToLogicHeight( GetFontExtLeading() ) ); 203 aMetric.SetLineHeight( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + xFontMetric->GetDescent() + mnEmphasisAscent + mnEmphasisDescent ) ); 204 aMetric.SetSlant( ImplDevicePixelToLogicHeight( xFontMetric->GetSlant() ) ); 205 206 // get miscellaneous data 207 aMetric.SetQuality( xFontMetric->GetQuality() ); 208 aMetric.SetMapNames( xFontMetric->GetMapNames() ); 209 210 SAL_INFO("vcl.gdi.fontmetric", "OutputDevice::GetFontMetric:" << aMetric); 211 212 xFontMetric = nullptr; 213 214 return aMetric; 215 } 216 217 FontMetric OutputDevice::GetFontMetric( const vcl::Font& rFont ) const 218 { 219 // select font, query metrics, select original font again 220 vcl::Font aOldFont = GetFont(); 221 const_cast<OutputDevice*>(this)->SetFont( rFont ); 222 FontMetric aMetric( GetFontMetric() ); 223 const_cast<OutputDevice*>(this)->SetFont( aOldFont ); 224 return aMetric; 225 } 226 227 bool OutputDevice::GetFontCharMap( FontCharMapRef& rxFontCharMap ) const 228 { 229 // we need a graphics 230 if( !mpGraphics && !AcquireGraphics() ) 231 return false; 232 233 if( mbNewFont ) 234 ImplNewFont(); 235 if( mbInitFont ) 236 InitFont(); 237 if( !mpFontInstance ) 238 return false; 239 240 FontCharMapRef xFontCharMap ( mpGraphics->GetFontCharMap() ); 241 if (!xFontCharMap.is()) 242 { 243 FontCharMapRef xDefaultMap( new FontCharMap() ); 244 rxFontCharMap = xDefaultMap; 245 } 246 else 247 rxFontCharMap = xFontCharMap; 248 249 return !rxFontCharMap->IsDefaultMap(); 250 } 251 252 bool OutputDevice::GetFontCapabilities( vcl::FontCapabilities& rFontCapabilities ) const 253 { 254 // we need a graphics 255 if( !mpGraphics && !AcquireGraphics() ) 256 return false; 257 258 if( mbNewFont ) 259 ImplNewFont(); 260 if( mbInitFont ) 261 InitFont(); 262 if( !mpFontInstance ) 263 return false; 264 265 return mpGraphics->GetFontCapabilities(rFontCapabilities); 266 } 267 268 #if ENABLE_CAIRO_CANVAS 269 270 SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const 271 { 272 SystemFontData aSysFontData; 273 274 if (!mpGraphics) 275 (void) AcquireGraphics(); 276 277 if (mpGraphics) 278 aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel); 279 280 return aSysFontData; 281 } 282 283 #endif // ENABLE_CAIRO_CANVAS 284 285 void OutputDevice::ImplGetEmphasisMark( tools::PolyPolygon& rPolyPoly, bool& rPolyLine, 286 tools::Rectangle& rRect1, tools::Rectangle& rRect2, 287 long& rYOff, long& rWidth, 288 FontEmphasisMark eEmphasis, 289 long nHeight ) 290 { 291 static const PolyFlags aAccentPolyFlags[24] = 292 { 293 PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control, 294 PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control, 295 PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control, 296 PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control, 297 PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control, 298 PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control, 299 PolyFlags::Normal, PolyFlags::Normal, PolyFlags::Control, 300 PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control 301 }; 302 303 static const long aAccentPos[48] = 304 { 305 78, 0, 306 348, 79, 307 599, 235, 308 843, 469, 309 938, 574, 310 990, 669, 311 990, 773, 312 990, 843, 313 964, 895, 314 921, 947, 315 886, 982, 316 860, 999, 317 825, 999, 318 764, 999, 319 721, 964, 320 686, 895, 321 625, 791, 322 556, 660, 323 469, 504, 324 400, 400, 325 261, 252, 326 61, 61, 327 0, 27, 328 9, 0 329 }; 330 331 rWidth = 0; 332 rYOff = 0; 333 rPolyLine = false; 334 335 if ( !nHeight ) 336 return; 337 338 FontEmphasisMark nEmphasisStyle = eEmphasis & FontEmphasisMark::Style; 339 long nDotSize = 0; 340 switch ( nEmphasisStyle ) 341 { 342 case FontEmphasisMark::Dot: 343 // Dot has 55% of the height 344 nDotSize = (nHeight*550)/1000; 345 if ( !nDotSize ) 346 nDotSize = 1; 347 if ( nDotSize <= 2 ) 348 rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) ); 349 else 350 { 351 long nRad = nDotSize/2; 352 tools::Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); 353 rPolyPoly.Insert( aPoly ); 354 } 355 rYOff = ((nHeight*250)/1000)/2; // Center to the another EmphasisMarks 356 rWidth = nDotSize; 357 break; 358 359 case FontEmphasisMark::Circle: 360 // Dot has 80% of the height 361 nDotSize = (nHeight*800)/1000; 362 if ( !nDotSize ) 363 nDotSize = 1; 364 if ( nDotSize <= 2 ) 365 rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) ); 366 else 367 { 368 long nRad = nDotSize/2; 369 tools::Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); 370 rPolyPoly.Insert( aPoly ); 371 // BorderWidth is 15% 372 long nBorder = (nDotSize*150)/1000; 373 if ( nBorder <= 1 ) 374 rPolyLine = true; 375 else 376 { 377 tools::Polygon aPoly2( Point( nRad, nRad ), 378 nRad-nBorder, nRad-nBorder ); 379 rPolyPoly.Insert( aPoly2 ); 380 } 381 } 382 rWidth = nDotSize; 383 break; 384 385 case FontEmphasisMark::Disc: 386 // Dot has 80% of the height 387 nDotSize = (nHeight*800)/1000; 388 if ( !nDotSize ) 389 nDotSize = 1; 390 if ( nDotSize <= 2 ) 391 rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) ); 392 else 393 { 394 long nRad = nDotSize/2; 395 tools::Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); 396 rPolyPoly.Insert( aPoly ); 397 } 398 rWidth = nDotSize; 399 break; 400 401 case FontEmphasisMark::Accent: 402 // Dot has 80% of the height 403 nDotSize = (nHeight*800)/1000; 404 if ( !nDotSize ) 405 nDotSize = 1; 406 if ( nDotSize <= 2 ) 407 { 408 if ( nDotSize == 1 ) 409 { 410 rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) ); 411 rWidth = nDotSize; 412 } 413 else 414 { 415 rRect1 = tools::Rectangle( Point(), Size( 1, 1 ) ); 416 rRect2 = tools::Rectangle( Point( 1, 1 ), Size( 1, 1 ) ); 417 } 418 } 419 else 420 { 421 tools::Polygon aPoly( SAL_N_ELEMENTS( aAccentPos ) / 2, 422 reinterpret_cast<const Point*>(aAccentPos), 423 aAccentPolyFlags ); 424 double dScale = static_cast<double>(nDotSize)/1000.0; 425 aPoly.Scale( dScale, dScale ); 426 tools::Polygon aTemp; 427 aPoly.AdaptiveSubdivide( aTemp ); 428 tools::Rectangle aBoundRect = aTemp.GetBoundRect(); 429 rWidth = aBoundRect.GetWidth(); 430 nDotSize = aBoundRect.GetHeight(); 431 rPolyPoly.Insert( aTemp ); 432 } 433 break; 434 default: break; 435 } 436 437 // calculate position 438 long nOffY = 1+(mnDPIY/300); // one visible pixel space 439 long nSpaceY = nHeight-nDotSize; 440 if ( nSpaceY >= nOffY*2 ) 441 rYOff += nOffY; 442 if ( !(eEmphasis & FontEmphasisMark::PosBelow) ) 443 rYOff += nDotSize; 444 } 445 446 FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const vcl::Font& rFont ) 447 { 448 FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark(); 449 450 // If no Position is set, then calculate the default position, which 451 // depends on the language 452 if ( !(nEmphasisMark & (FontEmphasisMark::PosAbove | FontEmphasisMark::PosBelow)) ) 453 { 454 LanguageType eLang = rFont.GetLanguage(); 455 // In Chinese Simplified the EmphasisMarks are below/left 456 if (MsLangId::isSimplifiedChinese(eLang)) 457 nEmphasisMark |= FontEmphasisMark::PosBelow; 458 else 459 { 460 eLang = rFont.GetCJKContextLanguage(); 461 // In Chinese Simplified the EmphasisMarks are below/left 462 if (MsLangId::isSimplifiedChinese(eLang)) 463 nEmphasisMark |= FontEmphasisMark::PosBelow; 464 else 465 nEmphasisMark |= FontEmphasisMark::PosAbove; 466 } 467 } 468 469 return nEmphasisMark; 470 } 471 472 long OutputDevice::GetFontExtLeading() const 473 { 474 return mpFontInstance->mxFontMetric->GetExternalLeading(); 475 } 476 477 void OutputDevice::ImplClearFontData( const bool bNewFontLists ) 478 { 479 // the currently selected logical font is no longer needed 480 if ( mpFontInstance ) 481 { 482 mpFontInstance->Release(); 483 mpFontInstance = nullptr; 484 } 485 486 mbInitFont = true; 487 mbNewFont = true; 488 489 if ( bNewFontLists ) 490 { 491 if ( mpDeviceFontList ) 492 { 493 delete mpDeviceFontList; 494 mpDeviceFontList = nullptr; 495 } 496 if ( mpDeviceFontSizeList ) 497 { 498 delete mpDeviceFontSizeList; 499 mpDeviceFontSizeList = nullptr; 500 } 501 502 // release all physically selected fonts on this device 503 if( AcquireGraphics() ) 504 mpGraphics->ReleaseFonts(); 505 } 506 507 // if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter ) 508 { 509 ImplSVData* pSVData = ImplGetSVData(); 510 511 if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache ) 512 mpFontCache->Invalidate(); 513 514 if ( bNewFontLists ) 515 { 516 // we need a graphics 517 if ( AcquireGraphics() ) 518 { 519 if( mpFontCollection && mpFontCollection != pSVData->maGDIData.mpScreenFontList ) 520 mpFontCollection->Clear(); 521 522 if( mpPDFWriter ) 523 { 524 if( mpFontCollection && mpFontCollection != pSVData->maGDIData.mpScreenFontList ) 525 delete mpFontCollection; 526 if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache ) 527 delete mpFontCache; 528 mpFontCollection = nullptr; 529 mpFontCache = nullptr; 530 } 531 } 532 } 533 } 534 535 // also update child windows if needed 536 if ( GetOutDevType() == OUTDEV_WINDOW ) 537 { 538 vcl::Window* pChild = static_cast<vcl::Window*>(this)->mpWindowImpl->mpFirstChild; 539 while ( pChild ) 540 { 541 pChild->ImplClearFontData( true ); 542 pChild = pChild->mpWindowImpl->mpNext; 543 } 544 } 545 } 546 547 void OutputDevice::RefreshFontData( const bool bNewFontLists ) 548 { 549 ImplRefreshFontData( bNewFontLists ); 550 } 551 552 void OutputDevice::ImplRefreshFontData( const bool bNewFontLists ) 553 { 554 // if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter ) 555 { 556 ImplSVData* pSVData = ImplGetSVData(); 557 558 if ( bNewFontLists ) 559 { 560 // we need a graphics 561 if ( AcquireGraphics() ) 562 { 563 if( mpPDFWriter ) 564 { 565 mpFontCollection = pSVData->maGDIData.mpScreenFontList->Clone(); 566 mpFontCache = new ImplFontCache(); 567 } 568 else 569 { 570 mpGraphics->GetDevFontList( mpFontCollection ); 571 } 572 } 573 } 574 } 575 576 // also update child windows if needed 577 if ( GetOutDevType() == OUTDEV_WINDOW ) 578 { 579 vcl::Window* pChild = static_cast<vcl::Window*>(this)->mpWindowImpl->mpFirstChild; 580 while ( pChild ) 581 { 582 pChild->ImplRefreshFontData( true ); 583 pChild = pChild->mpWindowImpl->mpNext; 584 } 585 } 586 } 587 588 void OutputDevice::ImplUpdateFontData() 589 { 590 ImplClearFontData( true/*bNewFontLists*/ ); 591 ImplRefreshFontData( true/*bNewFontLists*/ ); 592 } 593 594 void OutputDevice::ImplClearAllFontData(bool bNewFontLists) 595 { 596 ImplSVData* pSVData = ImplGetSVData(); 597 598 ImplUpdateFontDataForAllFrames( &OutputDevice::ImplClearFontData, bNewFontLists ); 599 600 // clear global font lists to have them updated 601 pSVData->maGDIData.mpScreenFontCache->Invalidate(); 602 if ( bNewFontLists ) 603 { 604 pSVData->maGDIData.mpScreenFontList->Clear(); 605 vcl::Window * pFrame = pSVData->maWinData.mpFirstFrame; 606 if ( pFrame ) 607 { 608 if ( pFrame->AcquireGraphics() ) 609 { 610 OutputDevice *pDevice = pFrame; 611 pDevice->mpGraphics->ClearDevFontCache(); 612 pDevice->mpGraphics->GetDevFontList(pFrame->mpWindowImpl->mpFrameData->mpFontCollection); 613 } 614 } 615 } 616 } 617 618 void OutputDevice::ImplRefreshAllFontData(bool bNewFontLists) 619 { 620 ImplUpdateFontDataForAllFrames( &OutputDevice::ImplRefreshFontData, bNewFontLists ); 621 } 622 623 void OutputDevice::ImplUpdateAllFontData(bool bNewFontLists) 624 { 625 OutputDevice::ImplClearAllFontData(bNewFontLists); 626 OutputDevice::ImplRefreshAllFontData(bNewFontLists); 627 } 628 629 void OutputDevice::ImplUpdateFontDataForAllFrames( const FontUpdateHandler_t pHdl, const bool bNewFontLists ) 630 { 631 ImplSVData* const pSVData = ImplGetSVData(); 632 633 // update all windows 634 vcl::Window* pFrame = pSVData->maWinData.mpFirstFrame; 635 while ( pFrame ) 636 { 637 ( pFrame->*pHdl )( bNewFontLists ); 638 639 vcl::Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap; 640 while ( pSysWin ) 641 { 642 ( pSysWin->*pHdl )( bNewFontLists ); 643 pSysWin = pSysWin->mpWindowImpl->mpNextOverlap; 644 } 645 646 pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame; 647 } 648 649 // update all virtual devices 650 VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev; 651 while ( pVirDev ) 652 { 653 ( pVirDev->*pHdl )( bNewFontLists ); 654 pVirDev = pVirDev->mpNext; 655 } 656 657 // update all printers 658 Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter; 659 while ( pPrinter ) 660 { 661 ( pPrinter->*pHdl )( bNewFontLists ); 662 pPrinter = pPrinter->mpNext; 663 } 664 } 665 666 void OutputDevice::BeginFontSubstitution() 667 { 668 ImplSVData* pSVData = ImplGetSVData(); 669 pSVData->maGDIData.mbFontSubChanged = false; 670 } 671 672 void OutputDevice::EndFontSubstitution() 673 { 674 ImplSVData* pSVData = ImplGetSVData(); 675 if ( pSVData->maGDIData.mbFontSubChanged ) 676 { 677 ImplUpdateAllFontData( false ); 678 679 DataChangedEvent aDCEvt( DataChangedEventType::FONTSUBSTITUTION ); 680 Application::NotifyAllWindows( aDCEvt ); 681 pSVData->maGDIData.mbFontSubChanged = false; 682 } 683 } 684 685 void OutputDevice::AddFontSubstitute( const OUString& rFontName, 686 const OUString& rReplaceFontName, 687 AddFontSubstituteFlags nFlags ) 688 { 689 ImplDirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 690 if( !rpSubst ) 691 rpSubst = new ImplDirectFontSubstitution; 692 rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags ); 693 ImplGetSVData()->maGDIData.mbFontSubChanged = true; 694 } 695 696 void ImplDirectFontSubstitution::AddFontSubstitute( const OUString& rFontName, 697 const OUString& rSubstFontName, AddFontSubstituteFlags nFlags ) 698 { 699 maFontSubstList.emplace_back( rFontName, rSubstFontName, nFlags ); 700 } 701 702 ImplFontSubstEntry::ImplFontSubstEntry( const OUString& rFontName, 703 const OUString& rSubstFontName, AddFontSubstituteFlags nSubstFlags ) 704 : mnFlags( nSubstFlags ) 705 { 706 maSearchName = GetEnglishSearchFontName( rFontName ); 707 maSearchReplaceName = GetEnglishSearchFontName( rSubstFontName ); 708 } 709 710 void OutputDevice::RemoveFontsSubstitute() 711 { 712 ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 713 if( pSubst ) 714 pSubst->RemoveFontsSubstitute(); 715 } 716 717 void ImplDirectFontSubstitution::RemoveFontsSubstitute() 718 { 719 maFontSubstList.clear(); 720 } 721 722 bool ImplDirectFontSubstitution::FindFontSubstitute( OUString& rSubstName, 723 const OUString& rSearchName ) const 724 { 725 // TODO: get rid of O(N) searches 726 std::vector<ImplFontSubstEntry>::const_iterator it = std::find_if ( 727 maFontSubstList.begin(), maFontSubstList.end(), 728 [&] (const ImplFontSubstEntry& s) { return (s.mnFlags & AddFontSubstituteFlags::ALWAYS) 729 && (s.maSearchName == rSearchName); } ); 730 if (it != maFontSubstList.end()) 731 { 732 rSubstName = it->maSearchReplaceName; 733 return true; 734 } 735 return false; 736 } 737 738 void ImplFontSubstitute( OUString& rFontName ) 739 { 740 // must be canonicalised 741 assert( GetEnglishSearchFontName( rFontName ) == rFontName ); 742 743 OUString aSubstFontName; 744 745 // apply user-configurable font replacement (eg, from the list in Tools->Options) 746 const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 747 if( pSubst && pSubst->FindFontSubstitute( aSubstFontName, rFontName ) ) 748 { 749 rFontName = aSubstFontName; 750 return; 751 } 752 } 753 754 //hidpi TODO: This routine has hard-coded font-sizes that break places such as DialControl 755 vcl::Font OutputDevice::GetDefaultFont( DefaultFontType nType, LanguageType eLang, 756 GetDefaultFontFlags nFlags, const OutputDevice* pOutDev ) 757 { 758 if (!pOutDev && !utl::ConfigManager::IsFuzzing()) // default is NULL 759 pOutDev = Application::GetDefaultDevice(); 760 761 OUString aSearch; 762 if (!utl::ConfigManager::IsFuzzing()) 763 { 764 LanguageTag aLanguageTag( 765 ( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW ) ? 766 Application::GetSettings().GetUILanguageTag() : 767 LanguageTag( eLang )); 768 769 utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get(); 770 OUString aDefault = rDefaults.getDefaultFont( aLanguageTag, nType ); 771 772 if( !aDefault.isEmpty() ) 773 aSearch = aDefault; 774 else 775 aSearch = rDefaults.getUserInterfaceFont( aLanguageTag ); // use the UI font as a fallback 776 } 777 else 778 aSearch = "Liberation Serif"; 779 780 vcl::Font aFont; 781 aFont.SetPitch( PITCH_VARIABLE ); 782 783 switch ( nType ) 784 { 785 case DefaultFontType::SANS_UNICODE: 786 case DefaultFontType::UI_SANS: 787 aFont.SetFamily( FAMILY_SWISS ); 788 break; 789 790 case DefaultFontType::SANS: 791 case DefaultFontType::LATIN_HEADING: 792 case DefaultFontType::LATIN_SPREADSHEET: 793 case DefaultFontType::LATIN_DISPLAY: 794 aFont.SetFamily( FAMILY_SWISS ); 795 break; 796 797 case DefaultFontType::SERIF: 798 case DefaultFontType::LATIN_TEXT: 799 case DefaultFontType::LATIN_PRESENTATION: 800 aFont.SetFamily( FAMILY_ROMAN ); 801 break; 802 803 case DefaultFontType::FIXED: 804 case DefaultFontType::LATIN_FIXED: 805 case DefaultFontType::UI_FIXED: 806 aFont.SetPitch( PITCH_FIXED ); 807 aFont.SetFamily( FAMILY_MODERN ); 808 break; 809 810 case DefaultFontType::SYMBOL: 811 aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL ); 812 break; 813 814 case DefaultFontType::CJK_TEXT: 815 case DefaultFontType::CJK_PRESENTATION: 816 case DefaultFontType::CJK_SPREADSHEET: 817 case DefaultFontType::CJK_HEADING: 818 case DefaultFontType::CJK_DISPLAY: 819 aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later... 820 break; 821 822 case DefaultFontType::CTL_TEXT: 823 case DefaultFontType::CTL_PRESENTATION: 824 case DefaultFontType::CTL_SPREADSHEET: 825 case DefaultFontType::CTL_HEADING: 826 case DefaultFontType::CTL_DISPLAY: 827 aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later... 828 break; 829 } 830 831 if ( !aSearch.isEmpty() ) 832 { 833 aFont.SetFontHeight( 12 ); // corresponds to nDefaultHeight 834 aFont.SetWeight( WEIGHT_NORMAL ); 835 aFont.SetLanguage( eLang ); 836 837 if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW ) 838 aFont.SetCharSet( osl_getThreadTextEncoding() ); 839 840 // Should we only return available fonts on the given device 841 if ( pOutDev ) 842 { 843 pOutDev->ImplInitFontList(); 844 845 // Search Font in the FontList 846 OUString aName; 847 sal_Int32 nIndex = 0; 848 do 849 { 850 PhysicalFontFamily* pFontFamily = pOutDev->mpFontCollection->FindFontFamily( GetNextFontToken( aSearch, nIndex ) ); 851 if( pFontFamily ) 852 { 853 AddTokenFontName( aName, pFontFamily->GetFamilyName() ); 854 if( nFlags & GetDefaultFontFlags::OnlyOne ) 855 break; 856 } 857 } 858 while ( nIndex != -1 ); 859 aFont.SetFamilyName( aName ); 860 } 861 862 // No Name, than set all names 863 if ( aFont.GetFamilyName().isEmpty() ) 864 { 865 if ( nFlags & GetDefaultFontFlags::OnlyOne ) 866 { 867 if( !pOutDev ) 868 { 869 SAL_WARN_IF(!utl::ConfigManager::IsFuzzing(), "vcl.gdi", "No default window has been set for the application - we really shouldn't be able to get here"); 870 sal_Int32 nIndex = 0; 871 aFont.SetFamilyName( aSearch.getToken( 0, ';', nIndex ) ); 872 } 873 else 874 { 875 pOutDev->ImplInitFontList(); 876 877 aFont.SetFamilyName( aSearch ); 878 879 // convert to pixel height 880 Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetFontSize() ); 881 if ( !aSize.Height() ) 882 { 883 // use default pixel height only when logical height is zero 884 if ( aFont.GetFontHeight() ) 885 aSize.Height() = 1; 886 else 887 aSize.Height() = (12*pOutDev->mnDPIY)/72; 888 } 889 890 // use default width only when logical width is zero 891 if( (0 == aSize.Width()) && (0 != aFont.GetFontSize().Width()) ) 892 aSize.Width() = 1; 893 894 // get the name of the first available font 895 float fExactHeight = static_cast<float>(aSize.Height()); 896 LogicalFontInstance* pFontInstance = pOutDev->mpFontCache->GetFontInstance( pOutDev->mpFontCollection, aFont, aSize, fExactHeight ); 897 if (pFontInstance) 898 { 899 if( pFontInstance->maFontSelData.mpFontData ) 900 aFont.SetFamilyName( pFontInstance->maFontSelData.mpFontData->GetFamilyName() ); 901 else 902 aFont.SetFamilyName( pFontInstance->maFontSelData.maTargetName ); 903 pFontInstance->Release(); 904 } 905 } 906 } 907 else 908 aFont.SetFamilyName( aSearch ); 909 } 910 } 911 912 #if OSL_DEBUG_LEVEL > 2 913 const char* s = "SANS_UNKNOWN"; 914 switch ( nType ) 915 { 916 case DefaultFontType::SANS_UNICODE: s = "SANS_UNICODE"; break; 917 case DefaultFontType::UI_SANS: s = "UI_SANS"; break; 918 919 case DefaultFontType::SANS: s = "SANS"; break; 920 case DefaultFontType::LATIN_HEADING: s = "LATIN_HEADING"; break; 921 case DefaultFontType::LATIN_SPREADSHEET: s = "LATIN_SPREADSHEET"; break; 922 case DefaultFontType::LATIN_DISPLAY: s = "LATIN_DISPLAY"; break; 923 924 case DefaultFontType::SERIF: s = "SERIF"; break; 925 case DefaultFontType::LATIN_TEXT: s = "LATIN_TEXT"; break; 926 case DefaultFontType::LATIN_PRESENTATION: s = "LATIN_PRESENTATION"; break; 927 928 case DefaultFontType::FIXED: s = "FIXED"; break; 929 case DefaultFontType::LATIN_FIXED: s = "LATIN_FIXED"; break; 930 case DefaultFontType::UI_FIXED: s = "UI_FIXED"; break; 931 932 case DefaultFontType::SYMBOL: s = "SYMBOL"; break; 933 934 case DefaultFontType::CJK_TEXT: s = "CJK_TEXT"; break; 935 case DefaultFontType::CJK_PRESENTATION: s = "CJK_PRESENTATION"; break; 936 case DefaultFontType::CJK_SPREADSHEET: s = "CJK_SPREADSHEET"; break; 937 case DefaultFontType::CJK_HEADING: s = "CJK_HEADING"; break; 938 case DefaultFontType::CJK_DISPLAY: s = "CJK_DISPLAY"; break; 939 940 case DefaultFontType::CTL_TEXT: s = "CTL_TEXT"; break; 941 case DefaultFontType::CTL_PRESENTATION: s = "CTL_PRESENTATION"; break; 942 case DefaultFontType::CTL_SPREADSHEET: s = "CTL_SPREADSHEET"; break; 943 case DefaultFontType::CTL_HEADING: s = "CTL_HEADING"; break; 944 case DefaultFontType::CTL_DISPLAY: s = "CTL_DISPLAY"; break; 945 } 946 SAL_INFO("vcl.gdi", 947 "OutputDevice::GetDefaultFont() Type=" << s 948 << " lang=" << eLang 949 << " flags=" << static_cast<int>(nFlags) 950 << " family=\"" << aFont.GetFamilyName() << "\""); 951 #endif 952 953 return aFont; 954 } 955 956 void OutputDevice::ImplInitFontList() const 957 { 958 if( !mpFontCollection->Count() ) 959 { 960 if( mpGraphics || AcquireGraphics() ) 961 { 962 SAL_INFO( "vcl.gdi", "OutputDevice::ImplInitFontList()" ); 963 mpGraphics->GetDevFontList( mpFontCollection ); 964 965 // There is absolutely no way there should be no fonts available on the device 966 if( !mpFontCollection->Count() ) 967 { 968 OUString aError( "Application error: no fonts and no vcl resource found on your system" ); 969 OUString aResStr(VclResId(SV_ACCESSERROR_NO_FONTS)); 970 if (!aResStr.isEmpty()) 971 aError = aResStr; 972 Application::Abort(aError); 973 } 974 } 975 } 976 } 977 978 void OutputDevice::InitFont() const 979 { 980 DBG_TESTSOLARMUTEX(); 981 982 if (!mpFontInstance) 983 return; 984 985 if ( mbInitFont ) 986 { 987 // decide if antialiasing is appropriate 988 bool bNonAntialiased(GetAntialiasing() & AntialiasingFlags::DisableText); 989 if (!utl::ConfigManager::IsFuzzing()) 990 { 991 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); 992 bNonAntialiased |= bool(rStyleSettings.GetDisplayOptions() & DisplayOptions::AADisable); 993 bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontInstance->maFontSelData.mnHeight); 994 } 995 mpFontInstance->maFontSelData.mbNonAntialiased = bNonAntialiased; 996 997 // select font in the device layers 998 mpGraphics->SetFont( &(mpFontInstance->maFontSelData), 0 ); 999 mbInitFont = false; 1000 } 1001 } 1002 1003 bool OutputDevice::ImplNewFont() const 1004 { 1005 DBG_TESTSOLARMUTEX(); 1006 1007 // get correct font list on the PDF writer if necessary 1008 if( mpPDFWriter ) 1009 { 1010 const ImplSVData* pSVData = ImplGetSVData(); 1011 if( mpFontCollection == pSVData->maGDIData.mpScreenFontList 1012 || mpFontCache == pSVData->maGDIData.mpScreenFontCache ) 1013 const_cast<OutputDevice&>(*this).ImplUpdateFontData(); 1014 } 1015 1016 if ( !mbNewFont ) 1017 return true; 1018 1019 // we need a graphics 1020 if ( !mpGraphics && !AcquireGraphics() ) 1021 { 1022 SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no Graphics, no Font"); 1023 return false; 1024 } 1025 SalGraphics* pGraphics = mpGraphics; 1026 ImplInitFontList(); 1027 1028 // convert to pixel height 1029 // TODO: replace integer based aSize completely with subpixel accurate type 1030 float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetFontHeight()) ); 1031 Size aSize = ImplLogicToDevicePixel( maFont.GetFontSize() ); 1032 if ( !aSize.Height() ) 1033 { 1034 // use default pixel height only when logical height is zero 1035 if ( maFont.GetFontSize().Height() ) 1036 aSize.Height() = 1; 1037 else 1038 aSize.Height() = (12*mnDPIY)/72; 1039 fExactHeight = static_cast<float>(aSize.Height()); 1040 } 1041 1042 // select the default width only when logical width is zero 1043 if( (0 == aSize.Width()) && (0 != maFont.GetFontSize().Width()) ) 1044 aSize.Width() = 1; 1045 1046 // get font entry 1047 LogicalFontInstance* pOldFontInstance = mpFontInstance; 1048 mpFontInstance = mpFontCache->GetFontInstance( mpFontCollection, maFont, aSize, fExactHeight ); 1049 if( pOldFontInstance ) 1050 pOldFontInstance->Release(); 1051 1052 LogicalFontInstance* pFontInstance = mpFontInstance; 1053 1054 if (!pFontInstance) 1055 { 1056 SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no LogicalFontInstance, no Font"); 1057 return false; 1058 } 1059 1060 // mark when lower layers need to get involved 1061 mbNewFont = false; 1062 if( pFontInstance != pOldFontInstance ) 1063 mbInitFont = true; 1064 1065 // select font when it has not been initialized yet 1066 if ( !pFontInstance->mbInit ) 1067 { 1068 InitFont(); 1069 1070 // get metric data from device layers 1071 if ( pGraphics ) 1072 { 1073 pFontInstance->mbInit = true; 1074 1075 pFontInstance->mxFontMetric->SetOrientation( sal::static_int_cast<short>(pFontInstance->maFontSelData.mnOrientation) ); 1076 pGraphics->GetFontMetric( pFontInstance->mxFontMetric, 0 ); 1077 1078 pFontInstance->mxFontMetric->ImplInitTextLineSize( this ); 1079 pFontInstance->mxFontMetric->ImplInitAboveTextLineSize(); 1080 pFontInstance->mxFontMetric->ImplInitFlags( this ); 1081 1082 pFontInstance->mnLineHeight = pFontInstance->mxFontMetric->GetAscent() + pFontInstance->mxFontMetric->GetDescent(); 1083 1084 SetFontOrientation( pFontInstance ); 1085 } 1086 } 1087 1088 // calculate EmphasisArea 1089 mnEmphasisAscent = 0; 1090 mnEmphasisDescent = 0; 1091 if ( maFont.GetEmphasisMark() & FontEmphasisMark::Style ) 1092 { 1093 FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont ); 1094 long nEmphasisHeight = (pFontInstance->mnLineHeight*250)/1000; 1095 if ( nEmphasisHeight < 1 ) 1096 nEmphasisHeight = 1; 1097 if ( nEmphasisMark & FontEmphasisMark::PosBelow ) 1098 mnEmphasisDescent = nEmphasisHeight; 1099 else 1100 mnEmphasisAscent = nEmphasisHeight; 1101 } 1102 1103 // calculate text offset depending on TextAlignment 1104 TextAlign eAlign = maFont.GetAlignment(); 1105 if ( eAlign == ALIGN_BASELINE ) 1106 { 1107 mnTextOffX = 0; 1108 mnTextOffY = 0; 1109 } 1110 else if ( eAlign == ALIGN_TOP ) 1111 { 1112 mnTextOffX = 0; 1113 mnTextOffY = +pFontInstance->mxFontMetric->GetAscent() + mnEmphasisAscent; 1114 if ( pFontInstance->mnOrientation ) 1115 { 1116 Point aOriginPt(0, 0); 1117 aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation ); 1118 } 1119 } 1120 else // eAlign == ALIGN_BOTTOM 1121 { 1122 mnTextOffX = 0; 1123 mnTextOffY = -pFontInstance->mxFontMetric->GetDescent() + mnEmphasisDescent; 1124 if ( pFontInstance->mnOrientation ) 1125 { 1126 Point aOriginPt(0, 0); 1127 aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation ); 1128 } 1129 } 1130 1131 mbTextLines = ((maFont.GetUnderline() != LINESTYLE_NONE) && (maFont.GetUnderline() != LINESTYLE_DONTKNOW)) || 1132 ((maFont.GetOverline() != LINESTYLE_NONE) && (maFont.GetOverline() != LINESTYLE_DONTKNOW)) || 1133 ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW)); 1134 mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() || 1135 (maFont.GetRelief() != FontRelief::NONE); 1136 1137 1138 // #95414# fix for OLE objects which use scale factors very creatively 1139 if( mbMap && !aSize.Width() ) 1140 { 1141 int nOrigWidth = pFontInstance->mxFontMetric->GetWidth(); 1142 float fStretch = static_cast<float>(maMapRes.mnMapScNumX) * maMapRes.mnMapScDenomY; 1143 fStretch /= static_cast<float>(maMapRes.mnMapScNumY) * maMapRes.mnMapScDenomX; 1144 int nNewWidth = static_cast<int>(nOrigWidth * fStretch + 0.5); 1145 if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) ) 1146 { 1147 Size aOrigSize = maFont.GetFontSize(); 1148 const_cast<vcl::Font&>(maFont).SetFontSize( Size( nNewWidth, aSize.Height() ) ); 1149 mbMap = false; 1150 mbNewFont = true; 1151 ImplNewFont(); // recurse once using stretched width 1152 mbMap = true; 1153 const_cast<vcl::Font&>(maFont).SetFontSize( aOrigSize ); 1154 } 1155 } 1156 1157 return true; 1158 } 1159 1160 void OutputDevice::SetFontOrientation( LogicalFontInstance* const pFontInstance ) const 1161 { 1162 if( pFontInstance->maFontSelData.mnOrientation && !pFontInstance->mxFontMetric->GetOrientation() ) 1163 { 1164 pFontInstance->mnOwnOrientation = sal::static_int_cast<short>(pFontInstance->maFontSelData.mnOrientation); 1165 pFontInstance->mnOrientation = pFontInstance->mnOwnOrientation; 1166 } 1167 else 1168 { 1169 pFontInstance->mnOrientation = pFontInstance->mxFontMetric->GetOrientation(); 1170 } 1171 } 1172 1173 void OutputDevice::ImplDrawEmphasisMark( long nBaseX, long nX, long nY, 1174 const tools::PolyPolygon& rPolyPoly, bool bPolyLine, 1175 const tools::Rectangle& rRect1, const tools::Rectangle& rRect2 ) 1176 { 1177 if( IsRTLEnabled() ) 1178 nX = nBaseX - (nX - nBaseX - 1); 1179 1180 nX -= mnOutOffX; 1181 nY -= mnOutOffY; 1182 1183 if ( rPolyPoly.Count() ) 1184 { 1185 if ( bPolyLine ) 1186 { 1187 tools::Polygon aPoly = rPolyPoly.GetObject( 0 ); 1188 aPoly.Move( nX, nY ); 1189 DrawPolyLine( aPoly ); 1190 } 1191 else 1192 { 1193 tools::PolyPolygon aPolyPoly = rPolyPoly; 1194 aPolyPoly.Move( nX, nY ); 1195 DrawPolyPolygon( aPolyPoly ); 1196 } 1197 } 1198 1199 if ( !rRect1.IsEmpty() ) 1200 { 1201 tools::Rectangle aRect( Point( nX+rRect1.Left(), 1202 nY+rRect1.Top() ), rRect1.GetSize() ); 1203 DrawRect( aRect ); 1204 } 1205 1206 if ( !rRect2.IsEmpty() ) 1207 { 1208 tools::Rectangle aRect( Point( nX+rRect2.Left(), 1209 nY+rRect2.Top() ), rRect2.GetSize() ); 1210 1211 DrawRect( aRect ); 1212 } 1213 } 1214 1215 void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout ) 1216 { 1217 Color aOldLineColor = GetLineColor(); 1218 Color aOldFillColor = GetFillColor(); 1219 bool bOldMap = mbMap; 1220 GDIMetaFile* pOldMetaFile = mpMetaFile; 1221 mpMetaFile = nullptr; 1222 EnableMapMode( false ); 1223 1224 FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont ); 1225 tools::PolyPolygon aPolyPoly; 1226 tools::Rectangle aRect1; 1227 tools::Rectangle aRect2; 1228 long nEmphasisYOff; 1229 long nEmphasisWidth; 1230 long nEmphasisHeight; 1231 bool bPolyLine; 1232 1233 if ( nEmphasisMark & FontEmphasisMark::PosBelow ) 1234 nEmphasisHeight = mnEmphasisDescent; 1235 else 1236 nEmphasisHeight = mnEmphasisAscent; 1237 1238 ImplGetEmphasisMark( aPolyPoly, bPolyLine, 1239 aRect1, aRect2, 1240 nEmphasisYOff, nEmphasisWidth, 1241 nEmphasisMark, 1242 nEmphasisHeight ); 1243 1244 if ( bPolyLine ) 1245 { 1246 SetLineColor( GetTextColor() ); 1247 SetFillColor(); 1248 } 1249 else 1250 { 1251 SetLineColor(); 1252 SetFillColor( GetTextColor() ); 1253 } 1254 1255 Point aOffset = Point(0,0); 1256 1257 if ( nEmphasisMark & FontEmphasisMark::PosBelow ) 1258 aOffset.Y() += mpFontInstance->mxFontMetric->GetDescent() + nEmphasisYOff; 1259 else 1260 aOffset.Y() -= mpFontInstance->mxFontMetric->GetAscent() + nEmphasisYOff; 1261 1262 long nEmphasisWidth2 = nEmphasisWidth / 2; 1263 long nEmphasisHeight2 = nEmphasisHeight / 2; 1264 aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 ); 1265 1266 Point aOutPoint; 1267 tools::Rectangle aRectangle; 1268 const GlyphItem* pGlyph; 1269 int nStart = 0; 1270 while (rSalLayout.GetNextGlyphs(1, &pGlyph, aOutPoint, nStart)) 1271 { 1272 if (!mpGraphics->GetGlyphBoundRect(*pGlyph, aRectangle ) ) 1273 continue; 1274 1275 if (!pGlyph->IsSpacing()) 1276 { 1277 Point aAdjPoint = aOffset; 1278 aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2; 1279 if ( mpFontInstance->mnOrientation ) 1280 { 1281 Point aOriginPt(0, 0); 1282 aOriginPt.RotateAround( aAdjPoint.X(), aAdjPoint.Y(), mpFontInstance->mnOrientation ); 1283 } 1284 aOutPoint += aAdjPoint; 1285 aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 ); 1286 ImplDrawEmphasisMark( rSalLayout.DrawBase().X(), 1287 aOutPoint.X(), aOutPoint.Y(), 1288 aPolyPoly, bPolyLine, aRect1, aRect2 ); 1289 } 1290 } 1291 1292 SetLineColor( aOldLineColor ); 1293 SetFillColor( aOldFillColor ); 1294 EnableMapMode( bOldMap ); 1295 mpMetaFile = pOldMetaFile; 1296 } 1297 1298 std::unique_ptr<SalLayout> OutputDevice::getFallbackFont( 1299 FontSelectPattern &rFontSelData, int nFallbackLevel, 1300 ImplLayoutArgs& rLayoutArgs) const 1301 { 1302 // we need a graphics 1303 if (!mpGraphics && !AcquireGraphics()) 1304 return nullptr; 1305 1306 assert(mpGraphics != nullptr); 1307 mpGraphics->SetFont( &rFontSelData, nFallbackLevel ); 1308 1309 rLayoutArgs.ResetPos(); 1310 std::unique_ptr<SalLayout> pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel ); 1311 1312 if (!pFallback) 1313 return nullptr; 1314 1315 if (!pFallback->LayoutText(rLayoutArgs)) 1316 { 1317 // there is no need for a font that couldn't resolve anything 1318 return nullptr; 1319 } 1320 1321 pFallback->AdjustLayout( rLayoutArgs ); 1322 1323 return pFallback; 1324 } 1325 1326 std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( std::unique_ptr<SalLayout> pSalLayout, ImplLayoutArgs& rLayoutArgs ) const 1327 { 1328 // This function relies on a valid mpFontInstance, if it doesn't exist bail out 1329 // - we'd have crashed later on anyway. At least here we can catch the error in debug 1330 // mode. 1331 if ( !mpFontInstance ) 1332 { 1333 SAL_WARN ("vcl.gdi", "No font entry set in OutputDevice"); 1334 assert(mpFontInstance); 1335 return nullptr; 1336 } 1337 1338 // prepare multi level glyph fallback 1339 std::unique_ptr<MultiSalLayout> pMultiSalLayout; 1340 ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns; 1341 rLayoutArgs.PrepareFallback(); 1342 rLayoutArgs.mnFlags |= SalLayoutFlags::ForFallback; 1343 1344 // get list of code units that need glyph fallback 1345 int nCharPos = -1; 1346 bool bRTL = false; 1347 OUStringBuffer aMissingCodeBuf; 1348 while (rLayoutArgs.GetNextPos( &nCharPos, &bRTL)) 1349 aMissingCodeBuf.append(rLayoutArgs.mrStr[nCharPos]); 1350 rLayoutArgs.ResetPos(); 1351 OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear(); 1352 1353 FontSelectPattern aFontSelData = mpFontInstance->maFontSelData; 1354 1355 // try if fallback fonts support the missing code units 1356 for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel ) 1357 { 1358 // find a font family suited for glyph fallback 1359 // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontInstance 1360 // if the system-specific glyph fallback is active 1361 aFontSelData.mpFontInstance = mpFontInstance; // reset the fontinstance to base-level 1362 1363 LogicalFontInstance* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontCollection, 1364 aFontSelData, nFallbackLevel, aMissingCodes ); 1365 if( !pFallbackFont ) 1366 break; 1367 1368 aFontSelData.mpFontInstance = pFallbackFont; 1369 aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData; 1370 if( nFallbackLevel < MAX_FALLBACK-1) 1371 { 1372 // ignore fallback font if it is the same as the original font 1373 // unless we are looking for a substitution for 0x202F, in which 1374 // case we'll just use a normal space 1375 if( mpFontInstance->maFontSelData.mpFontData == aFontSelData.mpFontData && 1376 aMissingCodes.indexOf(0x202F) == -1 ) 1377 { 1378 pFallbackFont->Release(); 1379 continue; 1380 } 1381 } 1382 1383 // create and add glyph fallback layout to multilayout 1384 std::unique_ptr<SalLayout> pFallback = getFallbackFont(aFontSelData, 1385 nFallbackLevel, rLayoutArgs); 1386 if (pFallback) 1387 { 1388 if( !pMultiSalLayout ) 1389 pMultiSalLayout.reset( new MultiSalLayout( std::move(pSalLayout) ) ); 1390 pMultiSalLayout->AddFallback( std::move(pFallback), 1391 rLayoutArgs.maRuns, aFontSelData.mpFontData ); 1392 if (nFallbackLevel == MAX_FALLBACK-1) 1393 pMultiSalLayout->SetIncomplete(true); 1394 } 1395 1396 pFallbackFont->Release(); 1397 1398 // break when this fallback was sufficient 1399 if( !rLayoutArgs.PrepareFallback() ) 1400 break; 1401 } 1402 1403 if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) ) 1404 pSalLayout = std::move(pMultiSalLayout); 1405 1406 // restore orig font settings 1407 pSalLayout->InitFont(); 1408 rLayoutArgs.maRuns = aLayoutRuns; 1409 1410 return pSalLayout; 1411 } 1412 1413 long OutputDevice::GetMinKashida() const 1414 { 1415 if( mbNewFont && !ImplNewFont() ) 1416 return 0; 1417 1418 return ImplDevicePixelToLogicWidth( mpFontInstance->mxFontMetric->GetMinKashida() ); 1419 } 1420 1421 sal_Int32 OutputDevice::ValidateKashidas ( const OUString& rTxt, 1422 sal_Int32 nIdx, sal_Int32 nLen, 1423 sal_Int32 nKashCount, 1424 const sal_Int32* pKashidaPos, 1425 sal_Int32* pKashidaPosDropped ) const 1426 { 1427 // do layout 1428 std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rTxt, nIdx, nLen ); 1429 if( !pSalLayout ) 1430 return 0; 1431 sal_Int32 nDropped = 0; 1432 for( int i = 0; i < nKashCount; ++i ) 1433 { 1434 if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] )) 1435 { 1436 pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ]; 1437 ++nDropped; 1438 } 1439 } 1440 return nDropped; 1441 } 1442 1443 bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, 1444 int nIndex, int nLen, MetricVector& rVector ) 1445 { 1446 rVector.clear(); 1447 1448 if( nIndex >= rStr.getLength() ) 1449 return false; 1450 1451 if( nLen < 0 || nIndex + nLen >= rStr.getLength() ) 1452 { 1453 nLen = rStr.getLength() - nIndex; 1454 } 1455 1456 tools::Rectangle aRect; 1457 for( int i = 0; i < nLen; i++ ) 1458 { 1459 if( !GetTextBoundRect( aRect, rStr, nIndex, nIndex + i, 1 ) ) 1460 break; 1461 aRect.Move( rOrigin.X(), rOrigin.Y() ); 1462 rVector.push_back( aRect ); 1463 } 1464 1465 return (nLen == static_cast<int>(rVector.size())); 1466 } 1467 1468 sal_Int32 OutputDevice::HasGlyphs( const vcl::Font& rTempFont, const OUString& rStr, 1469 sal_Int32 nIndex, sal_Int32 nLen ) const 1470 { 1471 if( nIndex >= rStr.getLength() ) 1472 return nIndex; 1473 sal_Int32 nEnd; 1474 if( nLen == -1 ) 1475 nEnd = rStr.getLength(); 1476 else 1477 nEnd = std::min( rStr.getLength(), nIndex + nLen ); 1478 1479 SAL_WARN_IF( nIndex >= nEnd, "vcl.gdi", "StartPos >= EndPos?" ); 1480 SAL_WARN_IF( nEnd > rStr.getLength(), "vcl.gdi", "String too short" ); 1481 1482 // to get the map temporarily set font 1483 const vcl::Font aOrigFont = GetFont(); 1484 const_cast<OutputDevice&>(*this).SetFont( rTempFont ); 1485 FontCharMapRef xFontCharMap ( new FontCharMap() ); 1486 bool bRet = GetFontCharMap( xFontCharMap ); 1487 const_cast<OutputDevice&>(*this).SetFont( aOrigFont ); 1488 1489 // if fontmap is unknown assume it doesn't have the glyphs 1490 if( !bRet ) 1491 return nIndex; 1492 1493 for( sal_Int32 i = nIndex; nIndex < nEnd; ++i, ++nIndex ) 1494 if( ! xFontCharMap->HasChar( rStr[i] ) ) 1495 return nIndex; 1496 1497 return -1; 1498 } 1499 1500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1501
