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_vcl.hxx" 26 27 #include <string.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <math.h> 31 #include <unistd.h> 32 #include <fcntl.h> 33 #include <sys/mman.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 37 #include "sal/alloca.h" 38 #include "sal/types.h" 39 40 #include "rtl/tencinfo.h" 41 42 #include "osl/file.hxx" 43 44 #include "tools/string.hxx" 45 #include "tools/debug.hxx" 46 #include "tools/stream.hxx" 47 48 #include "basegfx/polygon/b2dpolypolygon.hxx" 49 50 #include "i18npool/mslangid.hxx" 51 52 #include <vcl/sysdata.hxx> 53 #include "printergfx.hxx" 54 #include "vcl/fontmanager.hxx" 55 #include "vcl/jobdata.hxx" 56 #include "vcl/printerinfomanager.hxx" 57 #include "vcl/svapp.hxx" 58 59 #include "unx/salunx.h" 60 #include "unx/saldata.hxx" 61 #include "unx/saldisp.hxx" 62 #include "unx/salgdi.h" 63 #include "unx/pspgraphics.h" 64 #include "unx/salvd.h" 65 66 #include "salcvt.hxx" 67 #include "gcach_xpeer.hxx" 68 #include "xrender_peer.hxx" 69 #include "impfont.hxx" 70 #include "salframe.hxx" 71 #include "outdev.h" 72 73 74 #include <hash_set> 75 76 #ifdef ENABLE_GRAPHITE 77 #include <graphite_layout.hxx> 78 #include <graphite_serverfont.hxx> 79 #endif 80 81 struct cairo_surface_t; 82 struct cairo_t; 83 struct cairo_font_face_t; 84 typedef void* FT_Face; 85 struct cairo_matrix_t { 86 double xx; double yx; 87 double xy; double yy; 88 double x0; double y0; 89 }; 90 struct cairo_glyph_t 91 { 92 unsigned long index; 93 double x; 94 double y; 95 }; 96 struct BOX 97 { 98 short x1, x2, y1, y2; 99 }; 100 struct _XRegion 101 { 102 long size; 103 long numRects; 104 BOX *rects; 105 BOX extents; 106 }; 107 using namespace rtl; 108 109 // =========================================================================== 110 111 // PspKernInfo allows on-demand-querying of psprint provided kerning info (#i29881#) 112 class PspKernInfo : public ExtraKernInfo 113 { 114 public: 115 PspKernInfo( int nFontId ) : ExtraKernInfo(nFontId) {} 116 protected: 117 virtual void Initialize() const; 118 }; 119 120 //-------------------------------------------------------------------------- 121 122 void PspKernInfo::Initialize() const 123 { 124 mbInitialized = true; 125 126 // get the kerning pairs from psprint 127 const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); 128 typedef std::list< psp::KernPair > PspKernPairs; 129 const PspKernPairs& rKernPairs = rMgr.getKernPairs( mnFontId ); 130 if( rKernPairs.empty() ) 131 return; 132 133 // feed psprint's kerning list into a lookup-friendly container 134 maUnicodeKernPairs.rehash( rKernPairs.size() ); 135 PspKernPairs::const_iterator it = rKernPairs.begin(); 136 for(; it != rKernPairs.end(); ++it ) 137 { 138 ImplKernPairData aKernPair = { it->first, it->second, it->kern_x }; 139 maUnicodeKernPairs.insert( aKernPair ); 140 } 141 } 142 143 // ---------------------------------------------------------------------------- 144 // 145 // X11SalGraphics 146 // 147 // ---------------------------------------------------------------------------- 148 149 GC 150 X11SalGraphics::GetFontGC() 151 { 152 Display *pDisplay = GetXDisplay(); 153 154 if( !pFontGC_ ) 155 { 156 XGCValues values; 157 values.subwindow_mode = ClipByChildren; 158 values.fill_rule = EvenOddRule; // Pict import/ Gradient 159 values.graphics_exposures = False; 160 values.foreground = nTextPixel_; 161 pFontGC_ = XCreateGC( pDisplay, hDrawable_, 162 GCSubwindowMode | GCFillRule 163 | GCGraphicsExposures | GCForeground, 164 &values ); 165 } 166 if( !bFontGC_ ) 167 { 168 XSetForeground( pDisplay, pFontGC_, nTextPixel_ ); 169 SetClipRegion( pFontGC_ ); 170 bFontGC_ = sal_True; 171 } 172 173 return pFontGC_; 174 } 175 176 //-------------------------------------------------------------------------- 177 178 bool X11SalGraphics::setFont( const ImplFontSelectData *pEntry, int nFallbackLevel ) 179 { 180 #ifdef HDU_DEBUG 181 ByteString aReqName( "NULL" ); 182 if( pEntry ) 183 aReqName = ByteString( pEntry->maName, RTL_TEXTENCODING_UTF8 ); 184 ByteString aUseName( "NULL" ); 185 if( pEntry && pEntry->mpFontData ) 186 aUseName = ByteString( pEntry->mpFontData->GetFamilyName(), RTL_TEXTENCODING_UTF8 ); 187 fprintf( stderr, "SetFont(lvl=%d,\"%s\", %d*%d, naa=%d,b=%d,i=%d) => \"%s\"\n", 188 nFallbackLevel, aReqName.GetBuffer(), 189 !pEntry?-1:pEntry->mnWidth, !pEntry?-1:pEntry->mnHeight, 190 !pEntry?-1:pEntry->mbNonAntialiased, 191 !pEntry?-1:pEntry->meWeight, !pEntry?-1:pEntry->meItalic, 192 aUseName.GetBuffer() ); 193 #endif 194 195 // release all no longer needed font resources 196 for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i ) 197 { 198 if( mpServerFont[i] != NULL ) 199 { 200 // old server side font is no longer referenced 201 GlyphCache::GetInstance().UncacheFont( *mpServerFont[i] ); 202 mpServerFont[i] = NULL; 203 } 204 } 205 206 // return early if there is no new font 207 if( !pEntry ) 208 return false; 209 210 bFontVertical_ = pEntry->mbVertical; 211 212 // return early if this is not a valid font for this graphics 213 if( !pEntry->mpFontData ) 214 return false; 215 216 // handle the request for a non-native X11-font => use the GlyphCache 217 ServerFont* pServerFont = GlyphCache::GetInstance().CacheFont( *pEntry ); 218 if( pServerFont != NULL ) 219 { 220 // ignore fonts with e.g. corrupted font files 221 if( !pServerFont->TestFont() ) 222 { 223 GlyphCache::GetInstance().UncacheFont( *pServerFont ); 224 return false; 225 } 226 227 // register to use the font 228 mpServerFont[ nFallbackLevel ] = pServerFont; 229 230 // apply font specific-hint settings if needed 231 // TODO: also disable it for reference devices 232 if( !bPrinter_ ) 233 { 234 ImplServerFontEntry* pSFE = static_cast<ImplServerFontEntry*>( pEntry->mpFontEntry ); 235 pSFE->HandleFontOptions(); 236 } 237 238 return true; 239 } 240 241 return false; 242 } 243 244 void ImplServerFontEntry::HandleFontOptions( void ) 245 { 246 bool GetFCFontOptions( const ImplFontAttributes&, int nSize, ImplFontOptions& ); 247 248 if( !mpServerFont ) 249 return; 250 if( !mbGotFontOptions ) 251 { 252 // get and cache the font options 253 mbGotFontOptions = true; 254 mbValidFontOptions = GetFCFontOptions( *maFontSelData.mpFontData, 255 maFontSelData.mnHeight, maFontOptions ); 256 } 257 // apply the font options 258 if( mbValidFontOptions ) 259 mpServerFont->SetFontOptions( maFontOptions ); 260 } 261 262 //-------------------------------------------------------------------------- 263 264 namespace { 265 266 class CairoWrapper 267 { 268 private: 269 oslModule mpCairoLib; 270 271 cairo_surface_t* (*mp_xlib_surface_create_with_xrender_format)(Display *, Drawable , Screen *, XRenderPictFormat *, int , int ); 272 void (*mp_surface_destroy)(cairo_surface_t *); 273 cairo_t* (*mp_create)(cairo_surface_t *); 274 void (*mp_destroy)(cairo_t*); 275 void (*mp_clip)(cairo_t*); 276 void (*mp_rectangle)(cairo_t*, double, double, double, double); 277 cairo_font_face_t * (*mp_ft_font_face_create_for_ft_face)(FT_Face, int); 278 void (*mp_set_font_face)(cairo_t *, cairo_font_face_t *); 279 void (*mp_font_face_destroy)(cairo_font_face_t *); 280 void (*mp_matrix_init_identity)(cairo_matrix_t *); 281 void (*mp_matrix_scale)(cairo_matrix_t *, double, double); 282 void (*mp_matrix_rotate)(cairo_matrix_t *, double); 283 void (*mp_set_font_matrix)(cairo_t *, const cairo_matrix_t *); 284 void (*mp_show_glyphs)(cairo_t *, const cairo_glyph_t *, int ); 285 void (*mp_set_source_rgb)(cairo_t *, double , double , double ); 286 void (*mp_set_font_options)(cairo_t *, const void *); 287 void (*mp_ft_font_options_substitute)(const void*, void*); 288 289 bool canEmbolden() const { return false; } 290 291 CairoWrapper(); 292 public: 293 static CairoWrapper& get(); 294 bool isValid() const { return (mpCairoLib != NULL); } 295 bool isCairoRenderable(const ServerFont& rFont); 296 297 cairo_surface_t* xlib_surface_create_with_xrender_format(Display *pDisplay, Drawable drawable, Screen *pScreen, XRenderPictFormat *pFormat, int width, int height) 298 { return (*mp_xlib_surface_create_with_xrender_format)(pDisplay, drawable, pScreen, pFormat, width, height); } 299 void surface_destroy(cairo_surface_t *surface) { (*mp_surface_destroy)(surface); } 300 cairo_t* create(cairo_surface_t *surface) { return (*mp_create)(surface); } 301 void destroy(cairo_t *cr) { (*mp_destroy)(cr); } 302 void clip(cairo_t *cr) { (*mp_clip)(cr); } 303 void rectangle(cairo_t *cr, double x, double y, double width, double height) 304 { (*mp_rectangle)(cr, x, y, width, height); } 305 cairo_font_face_t* ft_font_face_create_for_ft_face(FT_Face face, int load_flags) 306 { return (*mp_ft_font_face_create_for_ft_face)(face, load_flags); } 307 void set_font_face(cairo_t *cr, cairo_font_face_t *font_face) 308 { (*mp_set_font_face)(cr, font_face); } 309 void font_face_destroy(cairo_font_face_t *font_face) 310 { (*mp_font_face_destroy)(font_face); } 311 void matrix_init_identity(cairo_matrix_t *matrix) 312 { (*mp_matrix_init_identity)(matrix); } 313 void matrix_scale(cairo_matrix_t *matrix, double sx, double sy) 314 { (*mp_matrix_scale)(matrix, sx, sy); } 315 void matrix_rotate(cairo_matrix_t *matrix, double radians) 316 { (*mp_matrix_rotate)(matrix, radians); } 317 void set_font_matrix(cairo_t *cr, const cairo_matrix_t *matrix) 318 { (*mp_set_font_matrix)(cr, matrix); } 319 void show_glyphs(cairo_t *cr, const cairo_glyph_t *glyphs, int no_glyphs) 320 { (*mp_show_glyphs)(cr, glyphs, no_glyphs); } 321 void set_source_rgb(cairo_t *cr, double red, double green, double blue) 322 { (*mp_set_source_rgb)(cr, red, green, blue); } 323 void set_font_options(cairo_t *cr, const void *options) 324 { (*mp_set_font_options)(cr, options); } 325 void ft_font_options_substitute(const void *options, void *pattern) 326 { (*mp_ft_font_options_substitute)(options, pattern); } 327 }; 328 329 static CairoWrapper* pCairoInstance = NULL; 330 331 CairoWrapper& CairoWrapper::get() 332 { 333 if( ! pCairoInstance ) 334 pCairoInstance = new CairoWrapper(); 335 return *pCairoInstance; 336 } 337 338 CairoWrapper::CairoWrapper() 339 : mpCairoLib( NULL ) 340 { 341 static const char* pDisableCairoText = getenv( "SAL_DISABLE_CAIROTEXT" ); 342 if( pDisableCairoText && (pDisableCairoText[0] != '0') ) 343 return; 344 345 int nDummy; 346 if( !XQueryExtension( GetX11SalData()->GetDisplay()->GetDisplay(), "RENDER", &nDummy, &nDummy, &nDummy ) ) 347 return; 348 349 mpCairoLib = osl_loadAsciiModule( "libcairo.so.2", SAL_LOADMODULE_DEFAULT ); 350 if( !mpCairoLib ) 351 return; 352 353 #ifdef DEBUG 354 // check cairo version 355 int (*p_version)(); 356 p_version = (int(*)()) osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_version" ); 357 const int nVersion = p_version ? (*p_version)() : 0; 358 fprintf( stderr, "CAIRO version=%d\n", nVersion ); 359 #endif 360 361 mp_xlib_surface_create_with_xrender_format = (cairo_surface_t* (*)(Display *, Drawable , Screen *, XRenderPictFormat *, int , int )) 362 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_xlib_surface_create_with_xrender_format" ); 363 mp_surface_destroy = (void(*)(cairo_surface_t*)) 364 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_surface_destroy" ); 365 mp_create = (cairo_t*(*)(cairo_surface_t*)) 366 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_create" ); 367 mp_destroy = (void(*)(cairo_t*)) 368 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_destroy" ); 369 mp_clip = (void(*)(cairo_t*)) 370 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_clip" ); 371 mp_rectangle = (void(*)(cairo_t*, double, double, double, double)) 372 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_rectangle" ); 373 mp_ft_font_face_create_for_ft_face = (cairo_font_face_t * (*)(FT_Face, int)) 374 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_face_create_for_ft_face" ); 375 mp_set_font_face = (void (*)(cairo_t *, cairo_font_face_t *)) 376 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_face" ); 377 mp_font_face_destroy = (void (*)(cairo_font_face_t *)) 378 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_font_face_destroy" ); 379 mp_matrix_init_identity = (void (*)(cairo_matrix_t *)) 380 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_init_identity" ); 381 mp_matrix_scale = (void (*)(cairo_matrix_t *, double, double)) 382 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_scale" ); 383 mp_matrix_rotate = (void (*)(cairo_matrix_t *, double)) 384 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_rotate" ); 385 mp_set_font_matrix = (void (*)(cairo_t *, const cairo_matrix_t *)) 386 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_matrix" ); 387 mp_show_glyphs = (void (*)(cairo_t *, const cairo_glyph_t *, int )) 388 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_show_glyphs" ); 389 mp_set_source_rgb = (void (*)(cairo_t *, double , double , double )) 390 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_source_rgb" ); 391 mp_set_font_options = (void (*)(cairo_t *, const void *options )) 392 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_options" ); 393 mp_ft_font_options_substitute = (void (*)(const void *, void *)) 394 osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_options_substitute" ); 395 396 if( !( 397 mp_xlib_surface_create_with_xrender_format && 398 mp_surface_destroy && 399 mp_create && 400 mp_destroy && 401 mp_clip && 402 mp_rectangle && 403 mp_ft_font_face_create_for_ft_face && 404 mp_set_font_face && 405 mp_font_face_destroy && 406 mp_matrix_init_identity && 407 mp_matrix_scale && 408 mp_matrix_rotate && 409 mp_set_font_matrix && 410 mp_show_glyphs && 411 mp_set_source_rgb && 412 mp_set_font_options && 413 mp_ft_font_options_substitute 414 ) ) 415 { 416 osl_unloadModule( mpCairoLib ); 417 mpCairoLib = NULL; 418 #if OSL_DEBUG_LEVEL > 1 419 fprintf( stderr, "not all needed symbols were found\n" ); 420 #endif 421 } 422 } 423 424 bool CairoWrapper::isCairoRenderable(const ServerFont& rFont) 425 { 426 return rFont.GetFtFace() && isValid() && rFont.GetAntialiasAdvice() && 427 (rFont.NeedsArtificialBold() ? canEmbolden() : true); 428 } 429 430 } //namespace 431 432 CairoFontsCache::LRUFonts CairoFontsCache::maLRUFonts; 433 int CairoFontsCache::mnRefCount = 0; 434 435 CairoFontsCache::CairoFontsCache() 436 { 437 ++mnRefCount; 438 } 439 440 CairoFontsCache::~CairoFontsCache() 441 { 442 --mnRefCount; 443 if (!mnRefCount && !maLRUFonts.empty()) 444 { 445 CairoWrapper &rCairo = CairoWrapper::get(); 446 LRUFonts::iterator aEnd = maLRUFonts.end(); 447 for (LRUFonts::iterator aI = maLRUFonts.begin(); aI != aEnd; ++aI) 448 rCairo.font_face_destroy((cairo_font_face_t*)aI->first); 449 } 450 } 451 452 void CairoFontsCache::CacheFont(void *pFont, void* pId) 453 { 454 maLRUFonts.push_front( std::pair<void*, void *>(pFont, pId) ); 455 if (maLRUFonts.size() > 8) 456 { 457 CairoWrapper &rCairo = CairoWrapper::get(); 458 rCairo.font_face_destroy((cairo_font_face_t*)maLRUFonts.back().first); 459 maLRUFonts.pop_back(); 460 } 461 } 462 463 void* CairoFontsCache::FindCachedFont(void *pId) 464 { 465 LRUFonts::iterator aEnd = maLRUFonts.end(); 466 for (LRUFonts::iterator aI = maLRUFonts.begin(); aI != aEnd; ++aI) 467 if (aI->second == pId) 468 return aI->first; 469 return NULL; 470 } 471 472 void X11SalGraphics::DrawCairoAAFontString( const ServerFontLayout& rLayout ) 473 { 474 std::vector<cairo_glyph_t> cairo_glyphs; 475 cairo_glyphs.reserve( 256 ); 476 477 Point aPos; 478 sal_GlyphId aGlyphId; 479 for( int nStart = 0; rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); ) 480 { 481 cairo_glyph_t aGlyph; 482 aGlyph.index = aGlyphId & GF_IDXMASK; 483 aGlyph.x = aPos.X(); 484 aGlyph.y = aPos.Y(); 485 cairo_glyphs.push_back(aGlyph); 486 } 487 488 if (cairo_glyphs.empty()) 489 return; 490 491 // find a XRenderPictFormat compatible with the Drawable 492 XRenderPictFormat* pVisualFormat = static_cast<XRenderPictFormat*>(GetXRenderFormat()); 493 if( !pVisualFormat ) 494 { 495 Visual* pVisual = GetDisplay()->GetVisual( m_nScreen ).GetVisual(); 496 pVisualFormat = XRenderPeer::GetInstance().FindVisualFormat( pVisual ); 497 // cache the XRenderPictFormat 498 SetXRenderFormat( static_cast<void*>(pVisualFormat) ); 499 } 500 501 DBG_ASSERT( pVisualFormat!=NULL, "no matching XRenderPictFormat for text" ); 502 if( !pVisualFormat ) 503 return; 504 505 CairoWrapper &rCairo = CairoWrapper::get(); 506 507 Display* pDisplay = GetXDisplay(); 508 509 cairo_surface_t *surface = rCairo.xlib_surface_create_with_xrender_format (pDisplay, 510 hDrawable_, ScreenOfDisplay(pDisplay, m_nScreen), pVisualFormat, SAL_MAX_INT16, SAL_MAX_INT16); 511 512 /* 513 * It might be ideal to cache surface and cairo context between calls and 514 * only destroy it when the drawable changes, but to do that we need to at 515 * least change the SalFrame etc impls to dtor the SalGraphics *before* the 516 * destruction of the windows they reference 517 */ 518 cairo_t *cr = rCairo.create(surface); 519 rCairo.surface_destroy(surface); 520 521 if (const void *pOptions = Application::GetSettings().GetStyleSettings().GetCairoFontOptions()) 522 rCairo.set_font_options( cr, pOptions); 523 524 if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) 525 { 526 for (long i = 0; i < mpClipRegion->numRects; ++i) 527 { 528 rCairo.rectangle(cr, 529 mpClipRegion->rects[i].x1, 530 mpClipRegion->rects[i].y1, 531 mpClipRegion->rects[i].x2 - mpClipRegion->rects[i].x1, 532 mpClipRegion->rects[i].y2 - mpClipRegion->rects[i].y1); 533 } 534 rCairo.clip(cr); 535 } 536 537 rCairo.set_source_rgb(cr, 538 SALCOLOR_RED(nTextColor_)/255.0, 539 SALCOLOR_GREEN(nTextColor_)/255.0, 540 SALCOLOR_BLUE(nTextColor_)/255.0); 541 542 ServerFont& rFont = rLayout.GetServerFont(); 543 544 cairo_font_face_t* font_face = NULL; 545 546 void *pId = rFont.GetFtFace(); 547 font_face = (cairo_font_face_t*)m_aCairoFontsCache.FindCachedFont(pId); 548 if (!font_face) 549 { 550 font_face = rCairo.ft_font_face_create_for_ft_face(pId, rFont.GetLoadFlags()); 551 m_aCairoFontsCache.CacheFont(font_face, pId); 552 } 553 554 rCairo.set_font_face(cr, font_face); 555 556 cairo_matrix_t m; 557 const ImplFontSelectData& rFSD = rFont.GetFontSelData(); 558 int nWidth = rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight; 559 560 rCairo.matrix_init_identity(&m); 561 562 if (rLayout.GetOrientation()) 563 rCairo.matrix_rotate(&m, (3600 - rLayout.GetOrientation()) * M_PI / 1800.0); 564 565 rCairo.matrix_scale(&m, nWidth, rFSD.mnHeight); 566 if (rFont.NeedsArtificialItalic()) 567 m.xy = -m.xx * 0x6000L / 0x10000L; 568 569 rCairo.set_font_matrix(cr, &m); 570 rCairo.show_glyphs(cr, &cairo_glyphs[0], cairo_glyphs.size()); 571 rCairo.destroy(cr); 572 } 573 574 //-------------------------------------------------------------------------- 575 576 void X11SalGraphics::DrawServerAAFontString( const ServerFontLayout& rLayout ) 577 { 578 // get xrender target for this drawable 579 Picture aDstPic = GetXRenderPicture(); 580 if( !aDstPic ) 581 return; 582 583 // get a XRenderPicture for the font foreground 584 // TODO: move into own method 585 XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); 586 XRenderPictFormat* pVisualFormat = (XRenderPictFormat*)GetXRenderFormat(); 587 DBG_ASSERT( pVisualFormat, "we already have a render picture, but XRenderPictFormat==NULL???"); 588 const int nVisualDepth = pVisualFormat->depth; 589 SalDisplay::RenderEntry& rEntry = GetDisplay()->GetRenderEntries( m_nScreen )[ nVisualDepth ]; 590 if( !rEntry.m_aPicture ) 591 { 592 // create and cache XRenderPicture for the font foreground 593 Display* pDisplay = GetXDisplay(); 594 #ifdef DEBUG 595 int iDummy; 596 unsigned uDummy; 597 XLIB_Window wDummy; 598 unsigned int nDrawDepth; 599 ::XGetGeometry( pDisplay, hDrawable_, &wDummy, &iDummy, &iDummy, 600 &uDummy, &uDummy, &uDummy, &nDrawDepth ); 601 DBG_ASSERT( static_cast<unsigned>(nVisualDepth) == nDrawDepth, "depth messed up for XRender" ); 602 #endif 603 604 rEntry.m_aPixmap = ::XCreatePixmap( pDisplay, hDrawable_, 1, 1, nVisualDepth ); 605 606 XRenderPictureAttributes aAttr; 607 aAttr.repeat = true; 608 rEntry.m_aPicture = rRenderPeer.CreatePicture ( rEntry.m_aPixmap, pVisualFormat, CPRepeat, &aAttr ); 609 } 610 611 // set font foreground color and opacity 612 XRenderColor aRenderColor = GetXRenderColor( nTextColor_ ); 613 rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 ); 614 615 // set clipping 616 // TODO: move into GetXRenderPicture()? 617 if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) 618 rRenderPeer.SetPictureClipRegion( aDstPic, mpClipRegion ); 619 620 ServerFont& rFont = rLayout.GetServerFont(); 621 X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); 622 GlyphSet aGlyphSet = rGlyphPeer.GetGlyphSet( rFont, m_nScreen ); 623 624 Point aPos; 625 static const int MAXGLYPHS = 160; 626 sal_GlyphId aGlyphAry[ MAXGLYPHS ]; 627 int nMaxGlyphs = rLayout.GetOrientation() ? 1 : MAXGLYPHS; 628 for( int nStart = 0;;) 629 { 630 int nGlyphs = rLayout.GetNextGlyphs( nMaxGlyphs, aGlyphAry, aPos, nStart ); 631 if( !nGlyphs ) 632 break; 633 634 // #i51924# avoid 32->16bit coordinate truncation problem in X11 635 // TODO: reevaluate once displays with >30000 pixels are available 636 if( aPos.X() >= 30000 || aPos.Y() >= 30000 ) 637 continue; 638 639 unsigned int aRenderAry[ MAXGLYPHS ]; 640 for( int i = 0; i < nGlyphs; ++i ) 641 aRenderAry[ i ] = rGlyphPeer.GetXRGlyph( rFont, aGlyphAry[i] ); 642 rRenderPeer.CompositeString32( rEntry.m_aPicture, aDstPic, 643 aGlyphSet, aPos.X(), aPos.Y(), aRenderAry, nGlyphs ); 644 } 645 } 646 647 //-------------------------------------------------------------------------- 648 649 bool X11SalGraphics::DrawServerAAForcedString( const ServerFontLayout& rLayout ) 650 { 651 ServerFont& rFont = rLayout.GetServerFont(); 652 653 // prepare glyphs and get extent of operation 654 X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); 655 int nXmin = 0; 656 int nXmax = 0; 657 int nYmin = 0; 658 int nYmax = 0; 659 int nStart = 0; 660 Point aPos; 661 sal_GlyphId nGlyph; 662 for( bool bFirst=true; rLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) 663 { 664 const RawBitmap* const pRawBitmap = rGlyphPeer.GetRawBitmap( rFont, nGlyph ); 665 if( !pRawBitmap ) 666 continue; 667 668 const int nX1 = aPos.X() + pRawBitmap->mnXOffset; 669 const int nY1 = aPos.Y() + pRawBitmap->mnYOffset; 670 const int nX2 = nX1 + pRawBitmap->mnWidth; 671 const int nY2 = nY1 + pRawBitmap->mnHeight; 672 673 if( bFirst ) 674 { 675 bFirst = false; 676 nXmin = nX1; 677 nXmax = nX2; 678 nYmin = nY1; 679 nYmax = nY2; 680 } 681 else 682 { 683 if( nXmin > nX1 ) nXmin = nX1; 684 if( nXmax < nX2 ) nXmax = nX2; 685 if( nYmin > nY1 ) nYmin = nY1; 686 if( nYmax < nY2 ) nYmax = nY2; 687 } 688 } 689 690 // get XImage 691 GetDisplay()->GetXLib()->PushXErrorLevel( true ); 692 Display* pDisplay = GetXDisplay(); 693 694 XRectangle aXRect; 695 long nWidth = 1, nHeight = 1; 696 if( m_pFrame ) 697 nWidth = m_pFrame->maGeometry.nWidth, nHeight = m_pFrame->maGeometry.nHeight; 698 else if( m_pVDev ) 699 nWidth = m_pVDev->GetWidth(), nHeight = m_pVDev->GetHeight(); 700 701 if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) 702 { 703 // get bounding box 704 XClipBox( mpClipRegion, &aXRect ); 705 // clip with window 706 if( aXRect.x < 0 ) aXRect.x = 0; 707 708 if( aXRect.y < 0 ) aXRect.y = 0; 709 if( aXRect.width+aXRect.x > nWidth ) aXRect.width = nWidth-aXRect.x; 710 if( aXRect.height+aXRect.y > nHeight ) aXRect.height = nHeight-aXRect.y; 711 } 712 else 713 { 714 aXRect.x = 0; 715 aXRect.y = 0; 716 aXRect.width = nWidth; 717 aXRect.height = nHeight; 718 } 719 if( m_pFrame ) 720 { 721 // clip with screen 722 int nScreenX = m_pFrame->maGeometry.nX+aXRect.x; 723 int nScreenY = m_pFrame->maGeometry.nY+aXRect.y; 724 const Size& rScreenSize = GetDisplay()->getDataForScreen( m_nScreen ).m_aSize; 725 int nScreenW = rScreenSize.Width(); 726 int nScreenH = rScreenSize.Height(); 727 if( nScreenX < 0 ) 728 aXRect.x -= nScreenX, aXRect.width += nScreenX; 729 if( nScreenX+aXRect.width > nScreenW ) 730 aXRect.width = nScreenW-nScreenX; 731 if( nScreenY < 0 ) 732 aXRect.y -= nScreenY, aXRect.height += nScreenY; 733 if( nScreenY+aXRect.height > nScreenH ) 734 aXRect.height = nScreenH-nScreenY; 735 } 736 737 738 if( nXmin < aXRect.x ) nXmin = aXRect.x; 739 if( nYmin < aXRect.y ) nYmin = aXRect.y; 740 if( nXmax >= aXRect.x+aXRect.width ) nXmax = aXRect.x + aXRect.width - 1; 741 if( nYmax >= aXRect.y+aXRect.height ) nYmax = aXRect.y + aXRect.height - 1; 742 743 if( nXmin > nXmax ) 744 return false; 745 if( nYmin > nYmax ) 746 return false; 747 748 XImage* pImg = XGetImage( pDisplay, hDrawable_, 749 nXmin, nYmin, 750 (nXmax-nXmin+1), (nYmax-nYmin+1), 751 ~0, ZPixmap ); 752 if( pImg == NULL ) 753 { 754 if( m_pFrame ) 755 { 756 // the reason we did not get an image could be that the frame 757 // geometry changed in the meantime; lets get the current geometry 758 // and clip against the current window size as well as the screen 759 // with the current frame position 760 const Size& rScreenSize = GetDisplay()->getDataForScreen(m_nScreen).m_aSize; 761 int nScreenW = rScreenSize.Width(); 762 int nScreenH = rScreenSize.Height(); 763 XLIB_Window aRoot = None; 764 int x = 0, y = 0; 765 unsigned int w = 0, h = 0, bw = 0, d; 766 XGetGeometry( pDisplay, hDrawable_, &aRoot, &x, &y, &w, &h, &bw, &d ); 767 XTranslateCoordinates( pDisplay, hDrawable_, aRoot, 0, 0, &x, &y, &aRoot ); 768 if( nXmin + x < 0 ) // clip on left screen edge 769 nXmin += x-nXmin; 770 if( nYmin + y < 0 ) // clip on top screen edge 771 nYmin += y-nYmin; 772 if( nXmax >= int(w) ) // clip on right window egde 773 nXmax = w-1; 774 if( nYmax >= int(h) ) // clip on bottom window edge 775 nYmax = h-1; 776 if( nXmax + x >= nScreenW ) // clip on right screen edge 777 nXmax -= (nXmax + x - nScreenW)+1; 778 if( nYmax + y >= nScreenH ) // clip on bottom screen edge 779 nYmax -= (nYmax + y - nScreenH)+1; 780 if( nXmax >= nXmin && nYmax >= nYmin ) 781 { 782 // try again to get the image 783 pImg = XGetImage( pDisplay, hDrawable_, 784 nXmin, nYmin, 785 (nXmax-nXmin+1), (nYmax-nYmin+1), 786 ~0, ZPixmap ); 787 } 788 } 789 if( pImg == NULL ) 790 { 791 GetDisplay()->GetXLib()->PopXErrorLevel(); 792 return false; 793 } 794 } 795 796 // prepare context 797 GC nGC = GetFontGC(); 798 XGCValues aGCVal; 799 XGetGCValues( pDisplay, nGC, GCForeground, &aGCVal ); 800 801 unsigned long nOrigColor = XGetPixel( pImg, 0, 0 ); 802 XPutPixel( pImg, 0, 0, aGCVal.foreground ); 803 unsigned char aColor[4]; 804 aColor[0] = pImg->data[0]; 805 aColor[1] = pImg->data[1]; 806 aColor[2] = pImg->data[2]; 807 aColor[3] = pImg->data[3]; 808 XPutPixel( pImg, 0, 0, nOrigColor ); 809 810 // work on XImage 811 const int bpp = pImg->bits_per_pixel >> 3; 812 for( nStart = 0; rLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) 813 { 814 const RawBitmap* const pRawBitmap = rGlyphPeer.GetRawBitmap( rFont, nGlyph ); 815 if( !pRawBitmap ) 816 continue; 817 818 const int nX1 = aPos.X() + pRawBitmap->mnXOffset; 819 const int nY1 = aPos.Y() + pRawBitmap->mnYOffset; 820 821 if( (nX1 <= nXmax) && (int(nX1 + pRawBitmap->mnWidth) > nXmin) 822 && (nY1 <= nYmax) && (int(nY1 + pRawBitmap->mnHeight) > nYmin) ) 823 { 824 const unsigned char* p10 = pRawBitmap->mpBits; 825 unsigned char* p20 = (unsigned char*)pImg->data; // dest left limit 826 p20 += (nY1 - nYmin) * pImg->bytes_per_line; 827 unsigned char* p21 = p20 + (nX1 - nXmin + pImg->xoffset) * bpp; 828 int y = pRawBitmap->mnHeight; 829 if( y > nYmax - nY1 ) 830 y = nYmax - nY1 + 1; 831 while( --y >= 0 ) 832 { 833 if( p20 >= (unsigned char*)pImg->data ) 834 { 835 unsigned char* const p22 = p20 + pImg->width * bpp; // dest right limit 836 unsigned char* pDst = p21; 837 const unsigned char* pSrc = p10; 838 for( int x = pRawBitmap->mnWidth; (--x >= 0) && (p22 > pDst); ++pSrc ) 839 { 840 if( (*pSrc == 0) || (p20 > pDst) ) // keep background 841 pDst += bpp; 842 else if( *pSrc == 0xFF ) // paint foreground 843 { 844 const unsigned char* pColor = aColor; 845 for( int z = bpp; --z >= 0; ++pColor, ++pDst ) 846 *pDst = *pColor; 847 } 848 else // blend fg into bg 849 { 850 const unsigned char* pColor = aColor; 851 for( int z = bpp; --z >= 0; ++pColor, ++pDst ) 852 // theoretically it should be *257) >> 16 853 // but the error is <0.4% worst case and we are in 854 // the innermost loop of very perf-sensitive code 855 856 *pDst += (*pSrc * ((int)*pColor - *pDst)) >> 8; 857 } 858 } 859 } 860 p10 += pRawBitmap->mnScanlineSize; 861 p20 += pImg->bytes_per_line; 862 p21 += pImg->bytes_per_line; 863 } 864 } 865 } 866 867 // put XImage 868 XPutImage( pDisplay, hDrawable_, nGC, pImg, 869 0, 0, nXmin, nYmin, (nXmax - nXmin + 1), (nYmax - nYmin + 1) ); 870 XDestroyImage( pImg ); 871 872 GetDisplay()->GetXLib()->PopXErrorLevel(); 873 return true; 874 } 875 876 //-------------------------------------------------------------------------- 877 878 void X11SalGraphics::DrawServerSimpleFontString( const ServerFontLayout& rSalLayout ) 879 { 880 ServerFont& rFont = rSalLayout.GetServerFont(); 881 X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); 882 883 Display* pDisplay = GetXDisplay(); 884 GC nGC = GetFontGC(); 885 886 XGCValues aGCVal; 887 aGCVal.fill_style = FillStippled; 888 aGCVal.line_width = 0; 889 GC tmpGC = XCreateGC( pDisplay, hDrawable_, GCFillStyle|GCLineWidth, &aGCVal ); 890 XCopyGC( pDisplay, nGC, (1<<GCLastBit)-(1+GCFillStyle+GCLineWidth), tmpGC ); 891 892 Point aPos; 893 sal_GlyphId nGlyph; 894 for( int nStart = 0; rSalLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) 895 { 896 // #i51924# avoid 32->16bit coordinate truncation problem in X11 897 // TODO: reevaluate once displays with >30000 pixels are available 898 if( aPos.X() >= 30000 || aPos.Y() >= 30000 ) 899 continue; 900 901 Pixmap aStipple = rGlyphPeer.GetPixmap( rFont, nGlyph, m_nScreen ); 902 const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyph ); 903 904 if( aStipple != None ) 905 { 906 const int nDestX = aPos.X() + rGM.GetOffset().X(); 907 const int nDestY = aPos.Y() + rGM.GetOffset().Y(); 908 909 aGCVal.stipple = aStipple; 910 aGCVal.ts_x_origin = nDestX; 911 aGCVal.ts_y_origin = nDestY; 912 XChangeGC( pDisplay, tmpGC, GCStipple|GCTileStipXOrigin|GCTileStipYOrigin, &aGCVal ); 913 914 const int nWidth = rGM.GetSize().Width(); 915 const int nHeight = rGM.GetSize().Height(); 916 XFillRectangle( pDisplay, hDrawable_, tmpGC, nDestX, nDestY, nWidth, nHeight ); 917 } 918 } 919 920 XFreeGC( pDisplay, tmpGC ); 921 } 922 923 //-------------------------------------------------------------------------- 924 925 void X11SalGraphics::DrawServerFontLayout( const ServerFontLayout& rLayout ) 926 { 927 // draw complex text 928 ServerFont& rFont = rLayout.GetServerFont(); 929 const bool bVertical = rFont.GetFontSelData().mbVertical; 930 931 if( !bVertical && CairoWrapper::get().isCairoRenderable(rFont) ) 932 DrawCairoAAFontString( rLayout ); 933 else 934 { 935 X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); 936 if( rGlyphPeer.GetGlyphSet( rFont, m_nScreen ) ) 937 DrawServerAAFontString( rLayout ); 938 else if( !rGlyphPeer.ForcedAntialiasing( rFont, m_nScreen ) ) 939 DrawServerSimpleFontString( rLayout ); 940 else 941 DrawServerAAForcedString( rLayout ); 942 } 943 } 944 945 //-------------------------------------------------------------------------- 946 947 const ImplFontCharMap* X11SalGraphics::GetImplFontCharMap() const 948 { 949 if( !mpServerFont[0] ) 950 return NULL; 951 952 const ImplFontCharMap* pIFCMap = mpServerFont[0]->GetImplFontCharMap(); 953 return pIFCMap; 954 } 955 956 // ---------------------------------------------------------------------------- 957 // 958 // SalGraphics 959 // 960 // ---------------------------------------------------------------------------- 961 962 sal_uInt16 X11SalGraphics::SetFont( ImplFontSelectData *pEntry, int nFallbackLevel ) 963 { 964 sal_uInt16 nRetVal = 0; 965 if( !setFont( pEntry, nFallbackLevel ) ) 966 nRetVal |= SAL_SETFONT_BADFONT; 967 if( bPrinter_ || (mpServerFont[ nFallbackLevel ] != NULL) ) 968 nRetVal |= SAL_SETFONT_USEDRAWTEXTARRAY; 969 return nRetVal; 970 } 971 972 // ---------------------------------------------------------------------------- 973 974 void 975 X11SalGraphics::SetTextColor( SalColor nSalColor ) 976 { 977 if( nTextColor_ != nSalColor ) 978 { 979 nTextColor_ = nSalColor; 980 nTextPixel_ = GetPixel( nSalColor ); 981 bFontGC_ = sal_False; 982 } 983 } 984 985 // ---------------------------------------------------------------------------- 986 987 bool X11SalGraphics::AddTempDevFont( ImplDevFontList* pFontList, 988 const String& rFileURL, const String& rFontName ) 989 { 990 // inform PSP font manager 991 rtl::OUString aUSystemPath; 992 OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFileURL, aUSystemPath ) ); 993 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); 994 OString aOFileName( OUStringToOString( aUSystemPath, aEncoding ) ); 995 psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); 996 int nFontId = rMgr.addFontFile( aOFileName, 0 ); 997 if( !nFontId ) 998 return false; 999 1000 // prepare font data 1001 psp::FastPrintFontInfo aInfo; 1002 rMgr.getFontFastInfo( nFontId, aInfo ); 1003 aInfo.m_aFamilyName = rFontName; 1004 1005 // inform glyph cache of new font 1006 ImplDevFontAttributes aDFA = PspGraphics::Info2DevFontAttributes( aInfo ); 1007 aDFA.mnQuality += 5800; 1008 1009 int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID ); 1010 if( nFaceNum < 0 ) 1011 nFaceNum = 0; 1012 1013 GlyphCache& rGC = X11GlyphCache::GetInstance(); 1014 const rtl::OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID ); 1015 rGC.AddFontFile( rFileName, nFaceNum, aInfo.m_nID, aDFA ); 1016 1017 // announce new font to device's font list 1018 rGC.AnnounceFonts( pFontList ); 1019 return true; 1020 } 1021 1022 // ---------------------------------------------------------------------------- 1023 1024 void RegisterFontSubstitutors( ImplDevFontList* ); 1025 1026 void X11SalGraphics::GetDevFontList( ImplDevFontList *pList ) 1027 { 1028 // prepare the GlyphCache using psprint's font infos 1029 X11GlyphCache& rGC = X11GlyphCache::GetInstance(); 1030 1031 psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); 1032 ::std::list< psp::fontID > aList; 1033 ::std::list< psp::fontID >::iterator it; 1034 psp::FastPrintFontInfo aInfo; 1035 rMgr.getFontList( aList ); 1036 for( it = aList.begin(); it != aList.end(); ++it ) 1037 { 1038 if( !rMgr.getFontFastInfo( *it, aInfo ) ) 1039 continue; 1040 1041 // the GlyphCache must not bother with builtin fonts because 1042 // it cannot access or use them anyway 1043 if( aInfo.m_eType == psp::fonttype::Builtin ) 1044 continue; 1045 1046 // normalize face number to the GlyphCache 1047 int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID ); 1048 if( nFaceNum < 0 ) 1049 nFaceNum = 0; 1050 1051 // for fonts where extra kerning info can be provided on demand 1052 // an ExtraKernInfo object is supplied 1053 const ExtraKernInfo* pExtraKernInfo = NULL; 1054 if( aInfo.m_eType == psp::fonttype::Type1 ) 1055 pExtraKernInfo = new PspKernInfo( *it ); 1056 1057 // inform GlyphCache about this font provided by the PsPrint subsystem 1058 ImplDevFontAttributes aDFA = PspGraphics::Info2DevFontAttributes( aInfo ); 1059 aDFA.mnQuality += 4096; 1060 const rtl::OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID ); 1061 rGC.AddFontFile( rFileName, nFaceNum, aInfo.m_nID, aDFA, pExtraKernInfo ); 1062 } 1063 1064 // announce glyphcache fonts 1065 rGC.AnnounceFonts( pList ); 1066 1067 // register platform specific font substitutions if available 1068 if( rMgr.hasFontconfig() ) 1069 RegisterFontSubstitutors( pList ); 1070 1071 ImplGetSVData()->maGDIData.mbNativeFontConfig = rMgr.hasFontconfig(); 1072 } 1073 1074 // ---------------------------------------------------------------------------- 1075 1076 void X11SalGraphics::GetDevFontSubstList( OutputDevice* ) 1077 { 1078 // no device specific font substitutions on X11 needed 1079 } 1080 1081 // ---------------------------------------------------------------------------- 1082 1083 void cairosubcallback( void* pPattern ) 1084 { 1085 CairoWrapper& rCairo = CairoWrapper::get(); 1086 if( !rCairo.isValid() ) 1087 return; 1088 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); 1089 const void* pFontOptions = rStyleSettings.GetCairoFontOptions(); 1090 if( !pFontOptions ) 1091 return; 1092 rCairo.ft_font_options_substitute( pFontOptions, pPattern ); 1093 } 1094 1095 bool GetFCFontOptions( const ImplFontAttributes& rFontAttributes, int nSize, 1096 ImplFontOptions& rFontOptions) 1097 { 1098 // TODO: get rid of these insane enum-conversions 1099 // e.g. by using the classic vclenum values inside VCL 1100 1101 psp::FastPrintFontInfo aInfo; 1102 // set family name 1103 aInfo.m_aFamilyName = rFontAttributes.GetFamilyName(); 1104 // set italic 1105 switch( rFontAttributes.GetSlant() ) 1106 { 1107 case ITALIC_NONE: 1108 aInfo.m_eItalic = psp::italic::Upright; 1109 break; 1110 case ITALIC_NORMAL: 1111 aInfo.m_eItalic = psp::italic::Italic; 1112 break; 1113 case ITALIC_OBLIQUE: 1114 aInfo.m_eItalic = psp::italic::Oblique; 1115 break; 1116 default: 1117 aInfo.m_eItalic = psp::italic::Unknown; 1118 break; 1119 } 1120 // set weight 1121 switch( rFontAttributes.GetWeight() ) 1122 { 1123 case WEIGHT_THIN: 1124 aInfo.m_eWeight = psp::weight::Thin; 1125 break; 1126 case WEIGHT_ULTRALIGHT: 1127 aInfo.m_eWeight = psp::weight::UltraLight; 1128 break; 1129 case WEIGHT_LIGHT: 1130 aInfo.m_eWeight = psp::weight::Light; 1131 break; 1132 case WEIGHT_SEMILIGHT: 1133 aInfo.m_eWeight = psp::weight::SemiLight; 1134 break; 1135 case WEIGHT_NORMAL: 1136 aInfo.m_eWeight = psp::weight::Normal; 1137 break; 1138 case WEIGHT_MEDIUM: 1139 aInfo.m_eWeight = psp::weight::Medium; 1140 break; 1141 case WEIGHT_SEMIBOLD: 1142 aInfo.m_eWeight = psp::weight::SemiBold; 1143 break; 1144 case WEIGHT_BOLD: 1145 aInfo.m_eWeight = psp::weight::Bold; 1146 break; 1147 case WEIGHT_ULTRABOLD: 1148 aInfo.m_eWeight = psp::weight::UltraBold; 1149 break; 1150 case WEIGHT_BLACK: 1151 aInfo.m_eWeight = psp::weight::Black; 1152 break; 1153 default: 1154 aInfo.m_eWeight = psp::weight::Unknown; 1155 break; 1156 } 1157 // set width 1158 switch( rFontAttributes.GetWidthType() ) 1159 { 1160 case WIDTH_ULTRA_CONDENSED: 1161 aInfo.m_eWidth = psp::width::UltraCondensed; 1162 break; 1163 case WIDTH_EXTRA_CONDENSED: 1164 aInfo.m_eWidth = psp::width::ExtraCondensed; 1165 break; 1166 case WIDTH_CONDENSED: 1167 aInfo.m_eWidth = psp::width::Condensed; 1168 break; 1169 case WIDTH_SEMI_CONDENSED: 1170 aInfo.m_eWidth = psp::width::SemiCondensed; 1171 break; 1172 case WIDTH_NORMAL: 1173 aInfo.m_eWidth = psp::width::Normal; 1174 break; 1175 case WIDTH_SEMI_EXPANDED: 1176 aInfo.m_eWidth = psp::width::SemiExpanded; 1177 break; 1178 case WIDTH_EXPANDED: 1179 aInfo.m_eWidth = psp::width::Expanded; 1180 break; 1181 case WIDTH_EXTRA_EXPANDED: 1182 aInfo.m_eWidth = psp::width::ExtraExpanded; 1183 break; 1184 case WIDTH_ULTRA_EXPANDED: 1185 aInfo.m_eWidth = psp::width::UltraExpanded; 1186 break; 1187 default: 1188 aInfo.m_eWidth = psp::width::Unknown; 1189 break; 1190 } 1191 1192 const psp::PrintFontManager& rPFM = psp::PrintFontManager::get(); 1193 bool bOK = rPFM.getFontOptions( aInfo, nSize, cairosubcallback, rFontOptions); 1194 return bOK; 1195 } 1196 1197 // ---------------------------------------------------------------------------- 1198 1199 void 1200 X11SalGraphics::GetFontMetric( ImplFontMetricData *pMetric, int nFallbackLevel ) 1201 { 1202 if( nFallbackLevel >= MAX_FALLBACK ) 1203 return; 1204 1205 if( mpServerFont[nFallbackLevel] != NULL ) 1206 { 1207 long rDummyFactor; 1208 mpServerFont[nFallbackLevel]->FetchFontMetric( *pMetric, rDummyFactor ); 1209 } 1210 } 1211 1212 // --------------------------------------------------------------------------- 1213 1214 sal_uLong 1215 X11SalGraphics::GetKernPairs( sal_uLong nPairs, ImplKernPairData *pKernPairs ) 1216 { 1217 if( ! bPrinter_ ) 1218 { 1219 if( mpServerFont[0] != NULL ) 1220 { 1221 ImplKernPairData* pTmpKernPairs; 1222 sal_uLong nGotPairs = mpServerFont[0]->GetKernPairs( &pTmpKernPairs ); 1223 for( unsigned int i = 0; i < nPairs && i < nGotPairs; ++i ) 1224 pKernPairs[ i ] = pTmpKernPairs[ i ]; 1225 delete[] pTmpKernPairs; 1226 return nGotPairs; 1227 } 1228 } 1229 return 0; 1230 } 1231 1232 // --------------------------------------------------------------------------- 1233 1234 bool X11SalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect ) 1235 { 1236 const int nLevel = aGlyphId >> GF_FONTSHIFT; 1237 if( nLevel >= MAX_FALLBACK ) 1238 return false; 1239 1240 ServerFont* pSF = mpServerFont[ nLevel ]; 1241 if( !pSF ) 1242 return false; 1243 1244 aGlyphId &= ~GF_FONTMASK; 1245 const GlyphMetric& rGM = pSF->GetGlyphMetric( aGlyphId ); 1246 rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() ); 1247 return true; 1248 } 1249 1250 // --------------------------------------------------------------------------- 1251 1252 bool X11SalGraphics::GetGlyphOutline( sal_GlyphId aGlyphId, 1253 ::basegfx::B2DPolyPolygon& rPolyPoly ) 1254 { 1255 const int nLevel = aGlyphId >> GF_FONTSHIFT; 1256 if( nLevel >= MAX_FALLBACK ) 1257 return false; 1258 1259 ServerFont* pSF = mpServerFont[ nLevel ]; 1260 if( !pSF ) 1261 return false; 1262 1263 aGlyphId &= ~GF_FONTMASK; 1264 bool bOK = pSF->GetGlyphOutline( aGlyphId, rPolyPoly ); 1265 return bOK; 1266 } 1267 1268 //-------------------------------------------------------------------------- 1269 1270 SalLayout* X11SalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) 1271 { 1272 SalLayout* pLayout = NULL; 1273 1274 if( mpServerFont[ nFallbackLevel ] 1275 && !(rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) ) 1276 { 1277 #ifdef ENABLE_GRAPHITE 1278 // Is this a Graphite font? 1279 if (!bDisableGraphite_ && 1280 GraphiteFontAdaptor::IsGraphiteEnabledFont(*mpServerFont[nFallbackLevel])) 1281 { 1282 sal_Int32 xdpi, ydpi; 1283 1284 xdpi = GetDisplay()->GetResolution().A(); 1285 ydpi = GetDisplay()->GetResolution().B(); 1286 1287 GraphiteFontAdaptor * pGrfont = new GraphiteFontAdaptor( *mpServerFont[nFallbackLevel], xdpi, ydpi); 1288 if (!pGrfont) return NULL; 1289 pLayout = new GraphiteServerFontLayout(pGrfont); 1290 } 1291 else 1292 #endif 1293 pLayout = new ServerFontLayout( *mpServerFont[ nFallbackLevel ] ); 1294 } 1295 1296 return pLayout; 1297 } 1298 1299 //-------------------------------------------------------------------------- 1300 1301 SystemFontData X11SalGraphics::GetSysFontData( int nFallbacklevel ) const 1302 { 1303 SystemFontData aSysFontData; 1304 aSysFontData.nSize = sizeof( SystemFontData ); 1305 aSysFontData.nFontId = 0; 1306 1307 if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1; 1308 if (nFallbacklevel < 0 ) nFallbacklevel = 0; 1309 1310 if (mpServerFont[nFallbacklevel] != NULL) 1311 { 1312 ServerFont* rFont = mpServerFont[nFallbacklevel]; 1313 aSysFontData.nFontId = rFont->GetFtFace(); 1314 aSysFontData.nFontFlags = rFont->GetLoadFlags(); 1315 aSysFontData.bFakeBold = rFont->NeedsArtificialBold(); 1316 aSysFontData.bFakeItalic = rFont->NeedsArtificialItalic(); 1317 aSysFontData.bAntialias = rFont->GetAntialiasAdvice(); 1318 aSysFontData.bVerticalCharacterType = rFont->GetFontSelData().mbVertical; 1319 } 1320 1321 return aSysFontData; 1322 } 1323 1324 //-------------------------------------------------------------------------- 1325 1326 sal_Bool X11SalGraphics::CreateFontSubset( 1327 const rtl::OUString& rToFile, 1328 const ImplFontData* pFont, 1329 sal_GlyphId* pGlyphIds, 1330 sal_uInt8* pEncoding, 1331 sal_Int32* pWidths, 1332 int nGlyphCount, 1333 FontSubsetInfo& rInfo 1334 ) 1335 { 1336 // in this context the pFont->GetFontId() is a valid PSP 1337 // font since they are the only ones left after the PDF 1338 // export has filtered its list of subsettable fonts (for 1339 // which this method was created). The correct way would 1340 // be to have the GlyphCache search for the ImplFontData pFont 1341 psp::fontID aFont = pFont->GetFontId(); 1342 1343 psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); 1344 bool bSuccess = rMgr.createFontSubset( rInfo, 1345 aFont, 1346 rToFile, 1347 pGlyphIds, 1348 pEncoding, 1349 pWidths, 1350 nGlyphCount ); 1351 return bSuccess; 1352 } 1353 1354 //-------------------------------------------------------------------------- 1355 1356 const void* X11SalGraphics::GetEmbedFontData( const ImplFontData* pFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen ) 1357 { 1358 // in this context the pFont->GetFontId() is a valid PSP 1359 // font since they are the only ones left after the PDF 1360 // export has filtered its list of subsettable fonts (for 1361 // which this method was created). The correct way would 1362 // be to have the GlyphCache search for the ImplFontData pFont 1363 psp::fontID aFont = pFont->GetFontId(); 1364 return PspGraphics::DoGetEmbedFontData( aFont, pUnicodes, pWidths, rInfo, pDataLen ); 1365 } 1366 1367 //-------------------------------------------------------------------------- 1368 1369 void X11SalGraphics::FreeEmbedFontData( const void* pData, long nLen ) 1370 { 1371 PspGraphics::DoFreeEmbedFontData( pData, nLen ); 1372 } 1373 1374 //-------------------------------------------------------------------------- 1375 1376 const Ucs2SIntMap* X11SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded ) 1377 { 1378 // in this context the pFont->GetFontId() is a valid PSP 1379 // font since they are the only ones left after the PDF 1380 // export has filtered its list of subsettable fonts (for 1381 // which this method was created). The correct way would 1382 // be to have the GlyphCache search for the ImplFontData pFont 1383 psp::fontID aFont = pFont->GetFontId(); 1384 return PspGraphics::DoGetFontEncodingVector( aFont, pNonEncoded ); 1385 } 1386 1387 //-------------------------------------------------------------------------- 1388 1389 void X11SalGraphics::GetGlyphWidths( const ImplFontData* pFont, 1390 bool bVertical, 1391 Int32Vector& rWidths, 1392 Ucs2UIntMap& rUnicodeEnc ) 1393 { 1394 // in this context the pFont->GetFontId() is a valid PSP 1395 // font since they are the only ones left after the PDF 1396 // export has filtered its list of subsettable fonts (for 1397 // which this method was created). The correct way would 1398 // be to have the GlyphCache search for the ImplFontData pFont 1399 psp::fontID aFont = pFont->GetFontId(); 1400 PspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc ); 1401 } 1402 1403 // =========================================================================== 1404 // platform specific font substitution hooks 1405 1406 class FcPreMatchSubstititution 1407 : public ImplPreMatchFontSubstitution 1408 { 1409 public: 1410 bool FindFontSubstitute( ImplFontSelectData& ) const; 1411 }; 1412 1413 class FcGlyphFallbackSubstititution 1414 : public ImplGlyphFallbackFontSubstitution 1415 { 1416 // TODO: add a cache 1417 public: 1418 bool FindFontSubstitute( ImplFontSelectData&, OUString& rMissingCodes ) const; 1419 }; 1420 1421 void RegisterFontSubstitutors( ImplDevFontList* pList ) 1422 { 1423 // init font substitution defaults 1424 int nDisableBits = 0; 1425 #ifdef SOLARIS 1426 nDisableBits = 1; // disable "font fallback" here on default 1427 #endif 1428 // apply the environment variable if any 1429 const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" ); 1430 if( pEnvStr ) 1431 { 1432 if( (*pEnvStr >= '0') && (*pEnvStr <= '9') ) 1433 nDisableBits = (*pEnvStr - '0'); 1434 else 1435 nDisableBits = ~0U; // no specific bits set: disable all 1436 } 1437 1438 // register font fallback substitutions (unless disabled by bit0) 1439 if( (nDisableBits & 1) == 0 ) 1440 { 1441 static FcPreMatchSubstititution aSubstPreMatch; 1442 pList->SetPreMatchHook( &aSubstPreMatch ); 1443 } 1444 1445 // register glyph fallback substitutions (unless disabled by bit1) 1446 if( (nDisableBits & 2) == 0 ) 1447 { 1448 static FcGlyphFallbackSubstititution aSubstFallback; 1449 pList->SetFallbackHook( &aSubstFallback ); 1450 } 1451 } 1452 1453 // ----------------------------------------------------------------------- 1454 1455 static ImplFontSelectData GetFcSubstitute(const ImplFontSelectData &rFontSelData, OUString& rMissingCodes ) 1456 { 1457 ImplFontSelectData aRet(rFontSelData); 1458 1459 const rtl::OString aLangAttrib = MsLangId::convertLanguageToIsoByteString( rFontSelData.meLanguage ); 1460 1461 psp::italic::type eItalic = psp::italic::Unknown; 1462 if( rFontSelData.GetSlant() != ITALIC_DONTKNOW ) 1463 { 1464 switch( rFontSelData.GetSlant() ) 1465 { 1466 case ITALIC_NONE: eItalic = psp::italic::Upright; break; 1467 case ITALIC_NORMAL: eItalic = psp::italic::Italic; break; 1468 case ITALIC_OBLIQUE: eItalic = psp::italic::Oblique; break; 1469 default: 1470 break; 1471 } 1472 } 1473 1474 psp::weight::type eWeight = psp::weight::Unknown; 1475 if( rFontSelData.GetWeight() != WEIGHT_DONTKNOW ) 1476 { 1477 switch( rFontSelData.GetWeight() ) 1478 { 1479 case WEIGHT_THIN: eWeight = psp::weight::Thin; break; 1480 case WEIGHT_ULTRALIGHT: eWeight = psp::weight::UltraLight; break; 1481 case WEIGHT_LIGHT: eWeight = psp::weight::Light; break; 1482 case WEIGHT_SEMILIGHT: eWeight = psp::weight::SemiLight; break; 1483 case WEIGHT_NORMAL: eWeight = psp::weight::Normal; break; 1484 case WEIGHT_MEDIUM: eWeight = psp::weight::Medium; break; 1485 case WEIGHT_SEMIBOLD: eWeight = psp::weight::SemiBold; break; 1486 case WEIGHT_BOLD: eWeight = psp::weight::Bold; break; 1487 case WEIGHT_ULTRABOLD: eWeight = psp::weight::UltraBold; break; 1488 case WEIGHT_BLACK: eWeight = psp::weight::Black; break; 1489 default: 1490 break; 1491 } 1492 } 1493 1494 psp::width::type eWidth = psp::width::Unknown; 1495 if( rFontSelData.GetWidthType() != WIDTH_DONTKNOW ) 1496 { 1497 switch( rFontSelData.GetWidthType() ) 1498 { 1499 case WIDTH_ULTRA_CONDENSED: eWidth = psp::width::UltraCondensed; break; 1500 case WIDTH_EXTRA_CONDENSED: eWidth = psp::width::ExtraCondensed; break; 1501 case WIDTH_CONDENSED: eWidth = psp::width::Condensed; break; 1502 case WIDTH_SEMI_CONDENSED: eWidth = psp::width::SemiCondensed; break; 1503 case WIDTH_NORMAL: eWidth = psp::width::Normal; break; 1504 case WIDTH_SEMI_EXPANDED: eWidth = psp::width::SemiExpanded; break; 1505 case WIDTH_EXPANDED: eWidth = psp::width::Expanded; break; 1506 case WIDTH_EXTRA_EXPANDED: eWidth = psp::width::ExtraExpanded; break; 1507 case WIDTH_ULTRA_EXPANDED: eWidth = psp::width::UltraExpanded; break; 1508 default: 1509 break; 1510 } 1511 } 1512 1513 psp::pitch::type ePitch = psp::pitch::Unknown; 1514 if( rFontSelData.GetPitch() != PITCH_DONTKNOW ) 1515 { 1516 switch( rFontSelData.GetPitch() ) 1517 { 1518 case PITCH_FIXED: ePitch=psp::pitch::Fixed; break; 1519 case PITCH_VARIABLE: ePitch=psp::pitch::Variable; break; 1520 default: 1521 break; 1522 } 1523 } 1524 1525 const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); 1526 aRet.maSearchName = rMgr.Substitute( rFontSelData.maTargetName, rMissingCodes, aLangAttrib, eItalic, eWeight, eWidth, ePitch); 1527 1528 switch (eItalic) 1529 { 1530 case psp::italic::Upright: aRet.meItalic = ITALIC_NONE; break; 1531 case psp::italic::Italic: aRet.meItalic = ITALIC_NORMAL; break; 1532 case psp::italic::Oblique: aRet.meItalic = ITALIC_OBLIQUE; break; 1533 default: 1534 break; 1535 } 1536 1537 switch (eWeight) 1538 { 1539 case psp::weight::Thin: aRet.meWeight = WEIGHT_THIN; break; 1540 case psp::weight::UltraLight: aRet.meWeight = WEIGHT_ULTRALIGHT; break; 1541 case psp::weight::Light: aRet.meWeight = WEIGHT_LIGHT; break; 1542 case psp::weight::SemiLight: aRet.meWeight = WEIGHT_SEMILIGHT; break; 1543 case psp::weight::Normal: aRet.meWeight = WEIGHT_NORMAL; break; 1544 case psp::weight::Medium: aRet.meWeight = WEIGHT_MEDIUM; break; 1545 case psp::weight::SemiBold: aRet.meWeight = WEIGHT_SEMIBOLD; break; 1546 case psp::weight::Bold: aRet.meWeight = WEIGHT_BOLD; break; 1547 case psp::weight::UltraBold: aRet.meWeight = WEIGHT_ULTRABOLD; break; 1548 case psp::weight::Black: aRet.meWeight = WEIGHT_BLACK; break; 1549 default: 1550 break; 1551 } 1552 1553 switch (eWidth) 1554 { 1555 case psp::width::UltraCondensed: aRet.meWidthType = WIDTH_ULTRA_CONDENSED; break; 1556 case psp::width::ExtraCondensed: aRet.meWidthType = WIDTH_EXTRA_CONDENSED; break; 1557 case psp::width::Condensed: aRet.meWidthType = WIDTH_CONDENSED; break; 1558 case psp::width::SemiCondensed: aRet.meWidthType = WIDTH_SEMI_CONDENSED; break; 1559 case psp::width::Normal: aRet.meWidthType = WIDTH_NORMAL; break; 1560 case psp::width::SemiExpanded: aRet.meWidthType = WIDTH_SEMI_EXPANDED; break; 1561 case psp::width::Expanded: aRet.meWidthType = WIDTH_EXPANDED; break; 1562 case psp::width::ExtraExpanded: aRet.meWidthType = WIDTH_EXTRA_EXPANDED; break; 1563 case psp::width::UltraExpanded: aRet.meWidthType = WIDTH_ULTRA_EXPANDED; break; 1564 default: 1565 break; 1566 } 1567 1568 switch (ePitch) 1569 { 1570 case psp::pitch::Fixed: aRet.mePitch = PITCH_FIXED; break; 1571 case psp::pitch::Variable: aRet.mePitch = PITCH_VARIABLE; break; 1572 default: 1573 break; 1574 } 1575 1576 return aRet; 1577 } 1578 1579 namespace 1580 { 1581 bool uselessmatch(const ImplFontSelectData &rOrig, const ImplFontSelectData &rNew) 1582 { 1583 return 1584 ( 1585 rOrig.maTargetName == rNew.maSearchName && 1586 rOrig.meWeight == rNew.meWeight && 1587 rOrig.meItalic == rNew.meItalic && 1588 rOrig.mePitch == rNew.mePitch && 1589 rOrig.meWidthType == rNew.meWidthType 1590 ); 1591 } 1592 } 1593 1594 //-------------------------------------------------------------------------- 1595 1596 bool FcPreMatchSubstititution::FindFontSubstitute( ImplFontSelectData &rFontSelData ) const 1597 { 1598 // We dont' actually want to talk to Fontconfig at all for symbol fonts 1599 if( rFontSelData.IsSymbolFont() ) 1600 return false; 1601 // StarSymbol is a unicode font, but it still deserves the symbol flag 1602 if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) 1603 || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) 1604 return false; 1605 1606 rtl::OUString aDummy; 1607 const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, aDummy ); 1608 // TODO: cache the font substitution suggestion 1609 // FC doing it would be preferable because it knows the invariables 1610 // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans 1611 // whereas we would have to check for every size or attribute 1612 if( !aOut.maSearchName.Len() ) 1613 return false; 1614 1615 const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); 1616 1617 #ifdef DEBUG 1618 const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 1619 const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); 1620 printf( "FcPreMatchSubstititution \"%s\" bipw=%d%d%d%d -> ", 1621 aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, 1622 rFontSelData.mePitch, rFontSelData.meWidthType ); 1623 if( !bHaveSubstitute ) 1624 printf( "no substitute available\n" ); 1625 else 1626 printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), 1627 aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); 1628 #endif 1629 1630 if( bHaveSubstitute ) 1631 rFontSelData = aOut; 1632 1633 return bHaveSubstitute; 1634 } 1635 1636 // ----------------------------------------------------------------------- 1637 1638 bool FcGlyphFallbackSubstititution::FindFontSubstitute( ImplFontSelectData& rFontSelData, 1639 rtl::OUString& rMissingCodes ) const 1640 { 1641 // We dont' actually want to talk to Fontconfig at all for symbol fonts 1642 if( rFontSelData.IsSymbolFont() ) 1643 return false; 1644 // StarSymbol is a unicode font, but it still deserves the symbol flag 1645 if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) 1646 || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) 1647 return false; 1648 1649 const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, rMissingCodes ); 1650 // TODO: cache the unicode + srcfont specific result 1651 // FC doing it would be preferable because it knows the invariables 1652 // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans 1653 // whereas we would have to check for every size or attribute 1654 if( !aOut.maSearchName.Len() ) 1655 return false; 1656 1657 const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); 1658 1659 #ifdef DEBUG 1660 const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 1661 const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); 1662 printf( "FcGFSubstititution \"%s\" bipw=%d%d%d%d ->", 1663 aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, 1664 rFontSelData.mePitch, rFontSelData.meWidthType ); 1665 if( !bHaveSubstitute ) 1666 printf( "no substitute available\n" ); 1667 else 1668 printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), 1669 aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); 1670 #endif 1671 1672 if( bHaveSubstitute ) 1673 rFontSelData = aOut; 1674 1675 return bHaveSubstitute; 1676 } 1677 1678 // =========================================================================== 1679 1680