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.GetGlyphId( 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 sal_Bool X11SalGraphics::GetGlyphBoundRect( long nGlyphIndex, Rectangle& rRect ) 1235 { 1236 int nLevel = nGlyphIndex >> GF_FONTSHIFT; 1237 if( nLevel >= MAX_FALLBACK ) 1238 return sal_False; 1239 1240 ServerFont* pSF = mpServerFont[ nLevel ]; 1241 if( !pSF ) 1242 return sal_False; 1243 1244 nGlyphIndex &= ~GF_FONTMASK; 1245 const GlyphMetric& rGM = pSF->GetGlyphMetric( nGlyphIndex ); 1246 rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() ); 1247 return sal_True; 1248 } 1249 1250 // --------------------------------------------------------------------------- 1251 1252 sal_Bool X11SalGraphics::GetGlyphOutline( long nGlyphIndex, 1253 ::basegfx::B2DPolyPolygon& rPolyPoly ) 1254 { 1255 int nLevel = nGlyphIndex >> GF_FONTSHIFT; 1256 if( nLevel >= MAX_FALLBACK ) 1257 return sal_False; 1258 1259 ServerFont* pSF = mpServerFont[ nLevel ]; 1260 if( !pSF ) 1261 return sal_False; 1262 1263 nGlyphIndex &= ~GF_FONTMASK; 1264 if( pSF->GetGlyphOutline( nGlyphIndex, rPolyPoly ) ) 1265 return sal_True; 1266 1267 return sal_False; 1268 } 1269 1270 //-------------------------------------------------------------------------- 1271 1272 SalLayout* X11SalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) 1273 { 1274 SalLayout* pLayout = NULL; 1275 1276 if( mpServerFont[ nFallbackLevel ] 1277 && !(rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) ) 1278 { 1279 #ifdef ENABLE_GRAPHITE 1280 // Is this a Graphite font? 1281 if (!bDisableGraphite_ && 1282 GraphiteFontAdaptor::IsGraphiteEnabledFont(*mpServerFont[nFallbackLevel])) 1283 { 1284 sal_Int32 xdpi, ydpi; 1285 1286 xdpi = GetDisplay()->GetResolution().A(); 1287 ydpi = GetDisplay()->GetResolution().B(); 1288 1289 GraphiteFontAdaptor * pGrfont = new GraphiteFontAdaptor( *mpServerFont[nFallbackLevel], xdpi, ydpi); 1290 if (!pGrfont) return NULL; 1291 pLayout = new GraphiteServerFontLayout(pGrfont); 1292 } 1293 else 1294 #endif 1295 pLayout = new ServerFontLayout( *mpServerFont[ nFallbackLevel ] ); 1296 } 1297 1298 return pLayout; 1299 } 1300 1301 //-------------------------------------------------------------------------- 1302 1303 SystemFontData X11SalGraphics::GetSysFontData( int nFallbacklevel ) const 1304 { 1305 SystemFontData aSysFontData; 1306 aSysFontData.nSize = sizeof( SystemFontData ); 1307 aSysFontData.nFontId = 0; 1308 1309 if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1; 1310 if (nFallbacklevel < 0 ) nFallbacklevel = 0; 1311 1312 if (mpServerFont[nFallbacklevel] != NULL) 1313 { 1314 ServerFont* rFont = mpServerFont[nFallbacklevel]; 1315 aSysFontData.nFontId = rFont->GetFtFace(); 1316 aSysFontData.nFontFlags = rFont->GetLoadFlags(); 1317 aSysFontData.bFakeBold = rFont->NeedsArtificialBold(); 1318 aSysFontData.bFakeItalic = rFont->NeedsArtificialItalic(); 1319 aSysFontData.bAntialias = rFont->GetAntialiasAdvice(); 1320 aSysFontData.bVerticalCharacterType = rFont->GetFontSelData().mbVertical; 1321 } 1322 1323 return aSysFontData; 1324 } 1325 1326 //-------------------------------------------------------------------------- 1327 1328 sal_Bool X11SalGraphics::CreateFontSubset( 1329 const rtl::OUString& rToFile, 1330 const ImplFontData* pFont, 1331 sal_Int32* pGlyphIDs, 1332 sal_uInt8* pEncoding, 1333 sal_Int32* pWidths, 1334 int nGlyphCount, 1335 FontSubsetInfo& rInfo 1336 ) 1337 { 1338 // in this context the pFont->GetFontId() is a valid PSP 1339 // font since they are the only ones left after the PDF 1340 // export has filtered its list of subsettable fonts (for 1341 // which this method was created). The correct way would 1342 // be to have the GlyphCache search for the ImplFontData pFont 1343 psp::fontID aFont = pFont->GetFontId(); 1344 1345 psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); 1346 bool bSuccess = rMgr.createFontSubset( rInfo, 1347 aFont, 1348 rToFile, 1349 pGlyphIDs, 1350 pEncoding, 1351 pWidths, 1352 nGlyphCount ); 1353 return bSuccess; 1354 } 1355 1356 //-------------------------------------------------------------------------- 1357 1358 const void* X11SalGraphics::GetEmbedFontData( const ImplFontData* pFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen ) 1359 { 1360 // in this context the pFont->GetFontId() is a valid PSP 1361 // font since they are the only ones left after the PDF 1362 // export has filtered its list of subsettable fonts (for 1363 // which this method was created). The correct way would 1364 // be to have the GlyphCache search for the ImplFontData pFont 1365 psp::fontID aFont = pFont->GetFontId(); 1366 return PspGraphics::DoGetEmbedFontData( aFont, pUnicodes, pWidths, rInfo, pDataLen ); 1367 } 1368 1369 //-------------------------------------------------------------------------- 1370 1371 void X11SalGraphics::FreeEmbedFontData( const void* pData, long nLen ) 1372 { 1373 PspGraphics::DoFreeEmbedFontData( pData, nLen ); 1374 } 1375 1376 //-------------------------------------------------------------------------- 1377 1378 const Ucs2SIntMap* X11SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded ) 1379 { 1380 // in this context the pFont->GetFontId() is a valid PSP 1381 // font since they are the only ones left after the PDF 1382 // export has filtered its list of subsettable fonts (for 1383 // which this method was created). The correct way would 1384 // be to have the GlyphCache search for the ImplFontData pFont 1385 psp::fontID aFont = pFont->GetFontId(); 1386 return PspGraphics::DoGetFontEncodingVector( aFont, pNonEncoded ); 1387 } 1388 1389 //-------------------------------------------------------------------------- 1390 1391 void X11SalGraphics::GetGlyphWidths( const ImplFontData* pFont, 1392 bool bVertical, 1393 Int32Vector& rWidths, 1394 Ucs2UIntMap& rUnicodeEnc ) 1395 { 1396 // in this context the pFont->GetFontId() is a valid PSP 1397 // font since they are the only ones left after the PDF 1398 // export has filtered its list of subsettable fonts (for 1399 // which this method was created). The correct way would 1400 // be to have the GlyphCache search for the ImplFontData pFont 1401 psp::fontID aFont = pFont->GetFontId(); 1402 PspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc ); 1403 } 1404 1405 // =========================================================================== 1406 // platform specific font substitution hooks 1407 1408 class FcPreMatchSubstititution 1409 : public ImplPreMatchFontSubstitution 1410 { 1411 public: 1412 bool FindFontSubstitute( ImplFontSelectData& ) const; 1413 }; 1414 1415 class FcGlyphFallbackSubstititution 1416 : public ImplGlyphFallbackFontSubstitution 1417 { 1418 // TODO: add a cache 1419 public: 1420 bool FindFontSubstitute( ImplFontSelectData&, OUString& rMissingCodes ) const; 1421 }; 1422 1423 void RegisterFontSubstitutors( ImplDevFontList* pList ) 1424 { 1425 // init font substitution defaults 1426 int nDisableBits = 0; 1427 #ifdef SOLARIS 1428 nDisableBits = 1; // disable "font fallback" here on default 1429 #endif 1430 // apply the environment variable if any 1431 const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" ); 1432 if( pEnvStr ) 1433 { 1434 if( (*pEnvStr >= '0') && (*pEnvStr <= '9') ) 1435 nDisableBits = (*pEnvStr - '0'); 1436 else 1437 nDisableBits = ~0U; // no specific bits set: disable all 1438 } 1439 1440 // register font fallback substitutions (unless disabled by bit0) 1441 if( (nDisableBits & 1) == 0 ) 1442 { 1443 static FcPreMatchSubstititution aSubstPreMatch; 1444 pList->SetPreMatchHook( &aSubstPreMatch ); 1445 } 1446 1447 // register glyph fallback substitutions (unless disabled by bit1) 1448 if( (nDisableBits & 2) == 0 ) 1449 { 1450 static FcGlyphFallbackSubstititution aSubstFallback; 1451 pList->SetFallbackHook( &aSubstFallback ); 1452 } 1453 } 1454 1455 // ----------------------------------------------------------------------- 1456 1457 static ImplFontSelectData GetFcSubstitute(const ImplFontSelectData &rFontSelData, OUString& rMissingCodes ) 1458 { 1459 ImplFontSelectData aRet(rFontSelData); 1460 1461 const rtl::OString aLangAttrib = MsLangId::convertLanguageToIsoByteString( rFontSelData.meLanguage ); 1462 1463 psp::italic::type eItalic = psp::italic::Unknown; 1464 if( rFontSelData.GetSlant() != ITALIC_DONTKNOW ) 1465 { 1466 switch( rFontSelData.GetSlant() ) 1467 { 1468 case ITALIC_NONE: eItalic = psp::italic::Upright; break; 1469 case ITALIC_NORMAL: eItalic = psp::italic::Italic; break; 1470 case ITALIC_OBLIQUE: eItalic = psp::italic::Oblique; break; 1471 default: 1472 break; 1473 } 1474 } 1475 1476 psp::weight::type eWeight = psp::weight::Unknown; 1477 if( rFontSelData.GetWeight() != WEIGHT_DONTKNOW ) 1478 { 1479 switch( rFontSelData.GetWeight() ) 1480 { 1481 case WEIGHT_THIN: eWeight = psp::weight::Thin; break; 1482 case WEIGHT_ULTRALIGHT: eWeight = psp::weight::UltraLight; break; 1483 case WEIGHT_LIGHT: eWeight = psp::weight::Light; break; 1484 case WEIGHT_SEMILIGHT: eWeight = psp::weight::SemiLight; break; 1485 case WEIGHT_NORMAL: eWeight = psp::weight::Normal; break; 1486 case WEIGHT_MEDIUM: eWeight = psp::weight::Medium; break; 1487 case WEIGHT_SEMIBOLD: eWeight = psp::weight::SemiBold; break; 1488 case WEIGHT_BOLD: eWeight = psp::weight::Bold; break; 1489 case WEIGHT_ULTRABOLD: eWeight = psp::weight::UltraBold; break; 1490 case WEIGHT_BLACK: eWeight = psp::weight::Black; break; 1491 default: 1492 break; 1493 } 1494 } 1495 1496 psp::width::type eWidth = psp::width::Unknown; 1497 if( rFontSelData.GetWidthType() != WIDTH_DONTKNOW ) 1498 { 1499 switch( rFontSelData.GetWidthType() ) 1500 { 1501 case WIDTH_ULTRA_CONDENSED: eWidth = psp::width::UltraCondensed; break; 1502 case WIDTH_EXTRA_CONDENSED: eWidth = psp::width::ExtraCondensed; break; 1503 case WIDTH_CONDENSED: eWidth = psp::width::Condensed; break; 1504 case WIDTH_SEMI_CONDENSED: eWidth = psp::width::SemiCondensed; break; 1505 case WIDTH_NORMAL: eWidth = psp::width::Normal; break; 1506 case WIDTH_SEMI_EXPANDED: eWidth = psp::width::SemiExpanded; break; 1507 case WIDTH_EXPANDED: eWidth = psp::width::Expanded; break; 1508 case WIDTH_EXTRA_EXPANDED: eWidth = psp::width::ExtraExpanded; break; 1509 case WIDTH_ULTRA_EXPANDED: eWidth = psp::width::UltraExpanded; break; 1510 default: 1511 break; 1512 } 1513 } 1514 1515 psp::pitch::type ePitch = psp::pitch::Unknown; 1516 if( rFontSelData.GetPitch() != PITCH_DONTKNOW ) 1517 { 1518 switch( rFontSelData.GetPitch() ) 1519 { 1520 case PITCH_FIXED: ePitch=psp::pitch::Fixed; break; 1521 case PITCH_VARIABLE: ePitch=psp::pitch::Variable; break; 1522 default: 1523 break; 1524 } 1525 } 1526 1527 const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); 1528 aRet.maSearchName = rMgr.Substitute( rFontSelData.maTargetName, rMissingCodes, aLangAttrib, eItalic, eWeight, eWidth, ePitch); 1529 1530 switch (eItalic) 1531 { 1532 case psp::italic::Upright: aRet.meItalic = ITALIC_NONE; break; 1533 case psp::italic::Italic: aRet.meItalic = ITALIC_NORMAL; break; 1534 case psp::italic::Oblique: aRet.meItalic = ITALIC_OBLIQUE; break; 1535 default: 1536 break; 1537 } 1538 1539 switch (eWeight) 1540 { 1541 case psp::weight::Thin: aRet.meWeight = WEIGHT_THIN; break; 1542 case psp::weight::UltraLight: aRet.meWeight = WEIGHT_ULTRALIGHT; break; 1543 case psp::weight::Light: aRet.meWeight = WEIGHT_LIGHT; break; 1544 case psp::weight::SemiLight: aRet.meWeight = WEIGHT_SEMILIGHT; break; 1545 case psp::weight::Normal: aRet.meWeight = WEIGHT_NORMAL; break; 1546 case psp::weight::Medium: aRet.meWeight = WEIGHT_MEDIUM; break; 1547 case psp::weight::SemiBold: aRet.meWeight = WEIGHT_SEMIBOLD; break; 1548 case psp::weight::Bold: aRet.meWeight = WEIGHT_BOLD; break; 1549 case psp::weight::UltraBold: aRet.meWeight = WEIGHT_ULTRABOLD; break; 1550 case psp::weight::Black: aRet.meWeight = WEIGHT_BLACK; break; 1551 default: 1552 break; 1553 } 1554 1555 switch (eWidth) 1556 { 1557 case psp::width::UltraCondensed: aRet.meWidthType = WIDTH_ULTRA_CONDENSED; break; 1558 case psp::width::ExtraCondensed: aRet.meWidthType = WIDTH_EXTRA_CONDENSED; break; 1559 case psp::width::Condensed: aRet.meWidthType = WIDTH_CONDENSED; break; 1560 case psp::width::SemiCondensed: aRet.meWidthType = WIDTH_SEMI_CONDENSED; break; 1561 case psp::width::Normal: aRet.meWidthType = WIDTH_NORMAL; break; 1562 case psp::width::SemiExpanded: aRet.meWidthType = WIDTH_SEMI_EXPANDED; break; 1563 case psp::width::Expanded: aRet.meWidthType = WIDTH_EXPANDED; break; 1564 case psp::width::ExtraExpanded: aRet.meWidthType = WIDTH_EXTRA_EXPANDED; break; 1565 case psp::width::UltraExpanded: aRet.meWidthType = WIDTH_ULTRA_EXPANDED; break; 1566 default: 1567 break; 1568 } 1569 1570 switch (ePitch) 1571 { 1572 case psp::pitch::Fixed: aRet.mePitch = PITCH_FIXED; break; 1573 case psp::pitch::Variable: aRet.mePitch = PITCH_VARIABLE; break; 1574 default: 1575 break; 1576 } 1577 1578 return aRet; 1579 } 1580 1581 namespace 1582 { 1583 bool uselessmatch(const ImplFontSelectData &rOrig, const ImplFontSelectData &rNew) 1584 { 1585 return 1586 ( 1587 rOrig.maTargetName == rNew.maSearchName && 1588 rOrig.meWeight == rNew.meWeight && 1589 rOrig.meItalic == rNew.meItalic && 1590 rOrig.mePitch == rNew.mePitch && 1591 rOrig.meWidthType == rNew.meWidthType 1592 ); 1593 } 1594 } 1595 1596 //-------------------------------------------------------------------------- 1597 1598 bool FcPreMatchSubstititution::FindFontSubstitute( ImplFontSelectData &rFontSelData ) const 1599 { 1600 // We dont' actually want to talk to Fontconfig at all for symbol fonts 1601 if( rFontSelData.IsSymbolFont() ) 1602 return false; 1603 // StarSymbol is a unicode font, but it still deserves the symbol flag 1604 if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) 1605 || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) 1606 return false; 1607 1608 rtl::OUString aDummy; 1609 const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, aDummy ); 1610 // TODO: cache the font substitution suggestion 1611 // FC doing it would be preferable because it knows the invariables 1612 // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans 1613 // whereas we would have to check for every size or attribute 1614 if( !aOut.maSearchName.Len() ) 1615 return false; 1616 1617 const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); 1618 1619 #ifdef DEBUG 1620 const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 1621 const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); 1622 printf( "FcPreMatchSubstititution \"%s\" bipw=%d%d%d%d -> ", 1623 aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, 1624 rFontSelData.mePitch, rFontSelData.meWidthType ); 1625 if( !bHaveSubstitute ) 1626 printf( "no substitute available\n" ); 1627 else 1628 printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), 1629 aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); 1630 #endif 1631 1632 if( bHaveSubstitute ) 1633 rFontSelData = aOut; 1634 1635 return bHaveSubstitute; 1636 } 1637 1638 // ----------------------------------------------------------------------- 1639 1640 bool FcGlyphFallbackSubstititution::FindFontSubstitute( ImplFontSelectData& rFontSelData, 1641 rtl::OUString& rMissingCodes ) const 1642 { 1643 // We dont' actually want to talk to Fontconfig at all for symbol fonts 1644 if( rFontSelData.IsSymbolFont() ) 1645 return false; 1646 // StarSymbol is a unicode font, but it still deserves the symbol flag 1647 if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) 1648 || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) 1649 return false; 1650 1651 const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, rMissingCodes ); 1652 // TODO: cache the unicode + srcfont specific result 1653 // FC doing it would be preferable because it knows the invariables 1654 // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans 1655 // whereas we would have to check for every size or attribute 1656 if( !aOut.maSearchName.Len() ) 1657 return false; 1658 1659 const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); 1660 1661 #ifdef DEBUG 1662 const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 1663 const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); 1664 printf( "FcGFSubstititution \"%s\" bipw=%d%d%d%d ->", 1665 aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, 1666 rFontSelData.mePitch, rFontSelData.meWidthType ); 1667 if( !bHaveSubstitute ) 1668 printf( "no substitute available\n" ); 1669 else 1670 printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), 1671 aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); 1672 #endif 1673 1674 if( bHaveSubstitute ) 1675 rFontSelData = aOut; 1676 1677 return bHaveSubstitute; 1678 } 1679 1680 // =========================================================================== 1681 1682