diff options
Diffstat (limited to 'vcl/unx/generic/gdi/salgdi3.cxx')
-rw-r--r-- | vcl/unx/generic/gdi/salgdi3.cxx | 1695 |
1 files changed, 1695 insertions, 0 deletions
diff --git a/vcl/unx/generic/gdi/salgdi3.cxx b/vcl/unx/generic/gdi/salgdi3.cxx new file mode 100644 index 000000000000..c60bf49fff0e --- /dev/null +++ b/vcl/unx/generic/gdi/salgdi3.cxx @@ -0,0 +1,1695 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "sal/alloca.h" +#include "sal/types.h" + +#include "rtl/tencinfo.h" + +#include "osl/file.hxx" + +#include "tools/string.hxx" +#include "tools/debug.hxx" +#include "tools/stream.hxx" + +#include "basegfx/polygon/b2dpolypolygon.hxx" + +#include "i18npool/mslangid.hxx" + +#include <boost/unordered_set.hpp> + +#include <vcl/sysdata.hxx> +#include "printergfx.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/jobdata.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/svapp.hxx" + +#include "unx/salunx.h" +#include "unx/saldata.hxx" +#include "unx/saldisp.hxx" +#include "unx/salgdi.h" +#include "unx/pspgraphics.h" +#include "unx/salvd.h" + +#include "salcvt.hxx" +#include "gcach_xpeer.hxx" +#include "xrender_peer.hxx" +#include "impfont.hxx" +#include "salframe.hxx" +#include "outdev.h" + + + +#ifdef ENABLE_GRAPHITE +#include <graphite_layout.hxx> +#include <graphite_serverfont.hxx> +#endif + +struct cairo_surface_t; +struct cairo_t; +struct cairo_font_face_t; +typedef void* FT_Face; +struct cairo_matrix_t { + double xx; double yx; + double xy; double yy; + double x0; double y0; +}; +struct cairo_glyph_t +{ + unsigned long index; + double x; + double y; +}; +struct BOX +{ + short x1, x2, y1, y2; +}; +struct _XRegion +{ + long size; + long numRects; + BOX *rects; + BOX extents; +}; +using ::rtl::OUString; +// =========================================================================== + +// PspKernInfo allows on-demand-querying of psprint provided kerning info (#i29881#) +class PspKernInfo : public ExtraKernInfo +{ +public: + PspKernInfo( int nFontId ) : ExtraKernInfo(nFontId) {} +protected: + virtual void Initialize() const; +}; + +//-------------------------------------------------------------------------- + +void PspKernInfo::Initialize() const +{ + mbInitialized = true; + + // get the kerning pairs from psprint + const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + typedef std::list< psp::KernPair > PspKernPairs; + const PspKernPairs& rKernPairs = rMgr.getKernPairs( mnFontId ); + if( rKernPairs.empty() ) + return; + + PspKernPairs::const_iterator it = rKernPairs.begin(); + for(; it != rKernPairs.end(); ++it ) + { + ImplKernPairData aKernPair = { it->first, it->second, it->kern_x }; + maUnicodeKernPairs.insert( aKernPair ); + } +} + +// ---------------------------------------------------------------------------- +// +// X11SalGraphics +// +// ---------------------------------------------------------------------------- + +GC +X11SalGraphics::GetFontGC() +{ + Display *pDisplay = GetXDisplay(); + + if( !pFontGC_ ) + { + XGCValues values; + values.subwindow_mode = ClipByChildren; + values.fill_rule = EvenOddRule; // Pict import/ Gradient + values.graphics_exposures = False; + values.foreground = nTextPixel_; + pFontGC_ = XCreateGC( pDisplay, hDrawable_, + GCSubwindowMode | GCFillRule + | GCGraphicsExposures | GCForeground, + &values ); + } + if( !bFontGC_ ) + { + XSetForeground( pDisplay, pFontGC_, nTextPixel_ ); + SetClipRegion( pFontGC_ ); + bFontGC_ = sal_True; + } + + return pFontGC_; +} + +//-------------------------------------------------------------------------- + +bool X11SalGraphics::setFont( const ImplFontSelectData *pEntry, int nFallbackLevel ) +{ + // release all no longer needed font resources + for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i ) + { + if( mpServerFont[i] != NULL ) + { + // old server side font is no longer referenced + GlyphCache::GetInstance().UncacheFont( *mpServerFont[i] ); + mpServerFont[i] = NULL; + } + } + + // return early if there is no new font + if( !pEntry ) + return false; + + bFontVertical_ = pEntry->mbVertical; + + // return early if this is not a valid font for this graphics + if( !pEntry->mpFontData ) + return false; + + // handle the request for a non-native X11-font => use the GlyphCache + ServerFont* pServerFont = GlyphCache::GetInstance().CacheFont( *pEntry ); + if( pServerFont != NULL ) + { + // ignore fonts with e.g. corrupted font files + if( !pServerFont->TestFont() ) + { + GlyphCache::GetInstance().UncacheFont( *pServerFont ); + return false; + } + + // register to use the font + mpServerFont[ nFallbackLevel ] = pServerFont; + + // apply font specific-hint settings if needed + // TODO: also disable it for reference devices + if( !bPrinter_ ) + { + ImplServerFontEntry* pSFE = static_cast<ImplServerFontEntry*>( pEntry->mpFontEntry ); + pSFE->HandleFontOptions(); + } + + return true; + } + + return false; +} + +ImplFontOptions* GetFCFontOptions( const ImplFontAttributes& rFontAttributes, int nSize); + +void ImplServerFontEntry::HandleFontOptions( void ) +{ + if( !mpServerFont ) + return; + if( !mbGotFontOptions ) + { + // get and cache the font options + mbGotFontOptions = true; + mpFontOptions.reset(GetFCFontOptions( *maFontSelData.mpFontData, + maFontSelData.mnHeight )); + } + // apply the font options + mpServerFont->SetFontOptions( mpFontOptions ); +} + +//-------------------------------------------------------------------------- + +namespace { + +class CairoWrapper +{ +private: + oslModule mpCairoLib; + + cairo_surface_t* (*mp_xlib_surface_create_with_xrender_format)(Display *, Drawable , Screen *, XRenderPictFormat *, int , int ); + void (*mp_surface_destroy)(cairo_surface_t *); + cairo_t* (*mp_create)(cairo_surface_t *); + void (*mp_destroy)(cairo_t*); + void (*mp_clip)(cairo_t*); + void (*mp_rectangle)(cairo_t*, double, double, double, double); + cairo_font_face_t * (*mp_ft_font_face_create_for_ft_face)(FT_Face, int); + cairo_font_face_t * (*mp_ft_font_face_create_for_pattern)(void*); + void (*mp_set_font_face)(cairo_t *, cairo_font_face_t *); + void (*mp_font_face_destroy)(cairo_font_face_t *); + void (*mp_matrix_init_identity)(cairo_matrix_t *); + void (*mp_matrix_scale)(cairo_matrix_t *, double, double); + void (*mp_matrix_rotate)(cairo_matrix_t *, double); + void (*mp_set_font_matrix)(cairo_t *, const cairo_matrix_t *); + void (*mp_show_glyphs)(cairo_t *, const cairo_glyph_t *, int ); + void (*mp_set_source_rgb)(cairo_t *, double , double , double ); + void (*mp_set_font_options)(cairo_t *, const void *); + void (*mp_ft_font_options_substitute)(const void*, void*); + + bool canEmbolden() const { return mp_ft_font_face_create_for_pattern != NULL; } + + CairoWrapper(); +public: + static CairoWrapper& get(); + bool isValid() const { return (mpCairoLib != NULL); } + bool isCairoRenderable(const ServerFont& rFont); + + cairo_surface_t* xlib_surface_create_with_xrender_format(Display *pDisplay, Drawable drawable, Screen *pScreen, XRenderPictFormat *pFormat, int width, int height) + { return (*mp_xlib_surface_create_with_xrender_format)(pDisplay, drawable, pScreen, pFormat, width, height); } + void surface_destroy(cairo_surface_t *surface) { (*mp_surface_destroy)(surface); } + cairo_t* create(cairo_surface_t *surface) { return (*mp_create)(surface); } + void destroy(cairo_t *cr) { (*mp_destroy)(cr); } + void clip(cairo_t *cr) { (*mp_clip)(cr); } + void rectangle(cairo_t *cr, double x, double y, double width, double height) + { (*mp_rectangle)(cr, x, y, width, height); } + cairo_font_face_t* ft_font_face_create_for_ft_face(FT_Face face, int load_flags) + { return (*mp_ft_font_face_create_for_ft_face)(face, load_flags); } + cairo_font_face_t* ft_font_face_create_for_pattern(void *pattern) + { + return mp_ft_font_face_create_for_pattern + ? (*mp_ft_font_face_create_for_pattern)(pattern) + : NULL; + } + void set_font_face(cairo_t *cr, cairo_font_face_t *font_face) + { (*mp_set_font_face)(cr, font_face); } + void font_face_destroy(cairo_font_face_t *font_face) + { (*mp_font_face_destroy)(font_face); } + void matrix_init_identity(cairo_matrix_t *matrix) + { (*mp_matrix_init_identity)(matrix); } + void matrix_scale(cairo_matrix_t *matrix, double sx, double sy) + { (*mp_matrix_scale)(matrix, sx, sy); } + void matrix_rotate(cairo_matrix_t *matrix, double radians) + { (*mp_matrix_rotate)(matrix, radians); } + void set_font_matrix(cairo_t *cr, const cairo_matrix_t *matrix) + { (*mp_set_font_matrix)(cr, matrix); } + void show_glyphs(cairo_t *cr, const cairo_glyph_t *glyphs, int no_glyphs) + { (*mp_show_glyphs)(cr, glyphs, no_glyphs); } + void set_source_rgb(cairo_t *cr, double red, double green, double blue) + { (*mp_set_source_rgb)(cr, red, green, blue); } + void set_font_options(cairo_t *cr, const void *options) + { (*mp_set_font_options)(cr, options); } + void ft_font_options_substitute(const void *options, void *pattern) + { (*mp_ft_font_options_substitute)(options, pattern); } +}; + +static CairoWrapper* pCairoInstance = NULL; + +CairoWrapper& CairoWrapper::get() +{ + if( ! pCairoInstance ) + pCairoInstance = new CairoWrapper(); + return *pCairoInstance; +} + +CairoWrapper::CairoWrapper() +: mpCairoLib( NULL ) +{ + static const char* pDisableCairoText = getenv( "SAL_DISABLE_CAIROTEXT" ); + if( pDisableCairoText && (pDisableCairoText[0] != '0') ) + return; + + int nDummy; + if( !XQueryExtension( GetX11SalData()->GetDisplay()->GetDisplay(), "RENDER", &nDummy, &nDummy, &nDummy ) ) + return; + +#ifdef MACOSX + OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libcairo.2.dylib" )); +#else + OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libcairo.so.2" )); +#endif + mpCairoLib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_DEFAULT ); + if( !mpCairoLib ) + return; + +#ifdef DEBUG + // check cairo version + int (*p_version)(); + p_version = (int(*)()) osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_version" ); + const int nVersion = p_version ? (*p_version)() : 0; + fprintf( stderr, "CAIRO version=%d\n", nVersion ); +#endif + + mp_xlib_surface_create_with_xrender_format = (cairo_surface_t* (*)(Display *, Drawable , Screen *, XRenderPictFormat *, int , int )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_xlib_surface_create_with_xrender_format" ); + mp_surface_destroy = (void(*)(cairo_surface_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_surface_destroy" ); + mp_create = (cairo_t*(*)(cairo_surface_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_create" ); + mp_destroy = (void(*)(cairo_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_destroy" ); + mp_clip = (void(*)(cairo_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_clip" ); + mp_rectangle = (void(*)(cairo_t*, double, double, double, double)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_rectangle" ); + mp_ft_font_face_create_for_ft_face = (cairo_font_face_t * (*)(FT_Face, int)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_face_create_for_ft_face" ); + mp_ft_font_face_create_for_pattern = (cairo_font_face_t * (*)(void*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_face_create_for_pattern" ); + mp_set_font_face = (void (*)(cairo_t *, cairo_font_face_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_face" ); + mp_font_face_destroy = (void (*)(cairo_font_face_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_font_face_destroy" ); + mp_matrix_init_identity = (void (*)(cairo_matrix_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_init_identity" ); + mp_matrix_scale = (void (*)(cairo_matrix_t *, double, double)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_scale" ); + mp_matrix_rotate = (void (*)(cairo_matrix_t *, double)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_rotate" ); + mp_set_font_matrix = (void (*)(cairo_t *, const cairo_matrix_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_matrix" ); + mp_show_glyphs = (void (*)(cairo_t *, const cairo_glyph_t *, int )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_show_glyphs" ); + mp_set_source_rgb = (void (*)(cairo_t *, double , double , double )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_source_rgb" ); + mp_set_font_options = (void (*)(cairo_t *, const void *options )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_options" ); + mp_ft_font_options_substitute = (void (*)(const void *, void *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_options_substitute" ); + + if( !( + mp_xlib_surface_create_with_xrender_format && + mp_surface_destroy && + mp_create && + mp_destroy && + mp_clip && + mp_rectangle && + mp_ft_font_face_create_for_ft_face && + mp_set_font_face && + mp_font_face_destroy && + mp_matrix_init_identity && + mp_matrix_scale && + mp_matrix_rotate && + mp_set_font_matrix && + mp_show_glyphs && + mp_set_source_rgb && + mp_set_font_options && + mp_ft_font_options_substitute + ) ) + { + osl_unloadModule( mpCairoLib ); + mpCairoLib = NULL; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "not all needed symbols were found\n" ); +#endif + } +} + +bool CairoWrapper::isCairoRenderable(const ServerFont& rFont) +{ + return rFont.GetFtFace() && isValid() && rFont.GetAntialiasAdvice() && + (rFont.NeedsArtificialBold() ? canEmbolden() : true); +} + +} //namespace + +CairoFontsCache::LRUFonts CairoFontsCache::maLRUFonts; +int CairoFontsCache::mnRefCount = 0; + +CairoFontsCache::CairoFontsCache() +{ + ++mnRefCount; +} + +CairoFontsCache::~CairoFontsCache() +{ + --mnRefCount; + if (!mnRefCount && !maLRUFonts.empty()) + { + CairoWrapper &rCairo = CairoWrapper::get(); + LRUFonts::iterator aEnd = maLRUFonts.end(); + for (LRUFonts::iterator aI = maLRUFonts.begin(); aI != aEnd; ++aI) + rCairo.font_face_destroy((cairo_font_face_t*)aI->first); + } +} + +void CairoFontsCache::CacheFont(void *pFont, const CairoFontsCache::CacheId &rId) +{ + maLRUFonts.push_front( std::pair<void*, CairoFontsCache::CacheId>(pFont, rId) ); + if (maLRUFonts.size() > 8) + { + CairoWrapper &rCairo = CairoWrapper::get(); + rCairo.font_face_destroy((cairo_font_face_t*)maLRUFonts.back().first); + maLRUFonts.pop_back(); + } +} + +void* CairoFontsCache::FindCachedFont(const CairoFontsCache::CacheId &rId) +{ + LRUFonts::iterator aEnd = maLRUFonts.end(); + for (LRUFonts::iterator aI = maLRUFonts.begin(); aI != aEnd; ++aI) + if (aI->second == rId) + return aI->first; + return NULL; +} + +void X11SalGraphics::DrawCairoAAFontString( const ServerFontLayout& rLayout ) +{ + std::vector<cairo_glyph_t> cairo_glyphs; + cairo_glyphs.reserve( 256 ); + + Point aPos; + sal_GlyphId aGlyphId; + for( int nStart = 0; rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); ) + { + cairo_glyph_t aGlyph; + aGlyph.index = aGlyphId & GF_IDXMASK; + aGlyph.x = aPos.X(); + aGlyph.y = aPos.Y(); + cairo_glyphs.push_back(aGlyph); + } + + if (cairo_glyphs.empty()) + return; + + // find a XRenderPictFormat compatible with the Drawable + XRenderPictFormat* pVisualFormat = GetXRenderFormat(); + DBG_ASSERT( pVisualFormat!=NULL, "no matching XRenderPictFormat for text" ); + if( !pVisualFormat ) + return; + + CairoWrapper &rCairo = CairoWrapper::get(); + + Display* pDisplay = GetXDisplay(); + + cairo_surface_t *surface = rCairo.xlib_surface_create_with_xrender_format (pDisplay, + hDrawable_, ScreenOfDisplay(pDisplay, m_nScreen), pVisualFormat, SAL_MAX_INT16, SAL_MAX_INT16); + + /* + * It might be ideal to cache surface and cairo context between calls and + * only destroy it when the drawable changes, but to do that we need to at + * least change the SalFrame etc impls to dtor the SalGraphics *before* the + * destruction of the windows they reference + */ + cairo_t *cr = rCairo.create(surface); + rCairo.surface_destroy(surface); + + if (const void *pOptions = Application::GetSettings().GetStyleSettings().GetCairoFontOptions()) + rCairo.set_font_options( cr, pOptions); + + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + { + for (long i = 0; i < mpClipRegion->numRects; ++i) + { + rCairo.rectangle(cr, + mpClipRegion->rects[i].x1, + mpClipRegion->rects[i].y1, + mpClipRegion->rects[i].x2 - mpClipRegion->rects[i].x1, + mpClipRegion->rects[i].y2 - mpClipRegion->rects[i].y1); + } + rCairo.clip(cr); + } + + rCairo.set_source_rgb(cr, + SALCOLOR_RED(nTextColor_)/255.0, + SALCOLOR_GREEN(nTextColor_)/255.0, + SALCOLOR_BLUE(nTextColor_)/255.0); + + ServerFont& rFont = rLayout.GetServerFont(); + + cairo_font_face_t* font_face = NULL; + + void* pFace = rFont.GetFtFace(); + CairoFontsCache::CacheId aId; + aId.mpFace = pFace; + aId.mpOptions = rFont.GetFontOptions().get(); + aId.mbEmbolden = rFont.NeedsArtificialBold(); + font_face = (cairo_font_face_t*)m_aCairoFontsCache.FindCachedFont(aId); + if (!font_face) + { + const ImplFontOptions *pOptions = rFont.GetFontOptions().get(); + void *pPattern = pOptions ? pOptions->GetPattern(pFace, aId.mbEmbolden) : NULL; + if (pPattern) + font_face = rCairo.ft_font_face_create_for_pattern(pPattern); + if (!font_face) + font_face = rCairo.ft_font_face_create_for_ft_face(pFace, rFont.GetLoadFlags()); + m_aCairoFontsCache.CacheFont(font_face, aId); + } + + rCairo.set_font_face(cr, font_face); + + cairo_matrix_t m; + const ImplFontSelectData& rFSD = rFont.GetFontSelData(); + int nWidth = rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight; + + rCairo.matrix_init_identity(&m); + + if (rLayout.GetOrientation()) + rCairo.matrix_rotate(&m, (3600 - rLayout.GetOrientation()) * M_PI / 1800.0); + + rCairo.matrix_scale(&m, nWidth, rFSD.mnHeight); + if (rFont.NeedsArtificialItalic()) + m.xy = -m.xx * 0x6000L / 0x10000L; + + rCairo.set_font_matrix(cr, &m); + rCairo.show_glyphs(cr, &cairo_glyphs[0], cairo_glyphs.size()); + rCairo.destroy(cr); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::DrawServerAAFontString( const ServerFontLayout& rLayout ) +{ + // get xrender target for this drawable + Picture aDstPic = GetXRenderPicture(); + if( !aDstPic ) + return; + + // get a XRenderPicture for the font foreground + // TODO: move into own method + XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); + XRenderPictFormat* pVisualFormat = (XRenderPictFormat*)GetXRenderFormat(); + DBG_ASSERT( pVisualFormat, "we already have a render picture, but XRenderPictFormat==NULL???"); + const int nVisualDepth = pVisualFormat->depth; + SalDisplay::RenderEntry& rEntry = GetDisplay()->GetRenderEntries( m_nScreen )[ nVisualDepth ]; + if( !rEntry.m_aPicture ) + { + // create and cache XRenderPicture for the font foreground + Display* pDisplay = GetXDisplay(); +#ifdef DEBUG + int iDummy; + unsigned uDummy; + XLIB_Window wDummy; + unsigned int nDrawDepth; + ::XGetGeometry( pDisplay, hDrawable_, &wDummy, &iDummy, &iDummy, + &uDummy, &uDummy, &uDummy, &nDrawDepth ); + DBG_ASSERT( static_cast<unsigned>(nVisualDepth) == nDrawDepth, "depth messed up for XRender" ); +#endif + + rEntry.m_aPixmap = ::XCreatePixmap( pDisplay, hDrawable_, 1, 1, nVisualDepth ); + + XRenderPictureAttributes aAttr; + aAttr.repeat = true; + rEntry.m_aPicture = rRenderPeer.CreatePicture ( rEntry.m_aPixmap, pVisualFormat, CPRepeat, &aAttr ); + } + + // set font foreground color and opacity + XRenderColor aRenderColor = GetXRenderColor( nTextColor_ ); + rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 ); + + // set clipping + // TODO: move into GetXRenderPicture()? + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + rRenderPeer.SetPictureClipRegion( aDstPic, mpClipRegion ); + + ServerFont& rFont = rLayout.GetServerFont(); + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + GlyphSet aGlyphSet = rGlyphPeer.GetGlyphSet( rFont, m_nScreen ); + + Point aPos; + static const int MAXGLYPHS = 160; + sal_GlyphId aGlyphAry[ MAXGLYPHS ]; + int nMaxGlyphs = rLayout.GetOrientation() ? 1 : MAXGLYPHS; + for( int nStart = 0;;) + { + int nGlyphs = rLayout.GetNextGlyphs( nMaxGlyphs, aGlyphAry, aPos, nStart ); + if( !nGlyphs ) + break; + + // #i51924# avoid 32->16bit coordinate truncation problem in X11 + // TODO: reevaluate once displays with >30000 pixels are available + if( aPos.X() >= 30000 || aPos.Y() >= 30000 ) + continue; + + unsigned int aRenderAry[ MAXGLYPHS ]; + for( int i = 0; i < nGlyphs; ++i ) + aRenderAry[ i ] = rGlyphPeer.GetGlyphId( rFont, aGlyphAry[i] ); + rRenderPeer.CompositeString32( rEntry.m_aPicture, aDstPic, + aGlyphSet, aPos.X(), aPos.Y(), aRenderAry, nGlyphs ); + } +} + +//-------------------------------------------------------------------------- + +bool X11SalGraphics::DrawServerAAForcedString( const ServerFontLayout& rLayout ) +{ + ServerFont& rFont = rLayout.GetServerFont(); + + // prepare glyphs and get extent of operation + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + int nXmin = 0; + int nXmax = 0; + int nYmin = 0; + int nYmax = 0; + int nStart = 0; + Point aPos; + sal_GlyphId nGlyph; + for( bool bFirst=true; rLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) + { + const RawBitmap* const pRawBitmap = rGlyphPeer.GetRawBitmap( rFont, nGlyph ); + if( !pRawBitmap ) + continue; + + const int nX1 = aPos.X() + pRawBitmap->mnXOffset; + const int nY1 = aPos.Y() + pRawBitmap->mnYOffset; + const int nX2 = nX1 + pRawBitmap->mnWidth; + const int nY2 = nY1 + pRawBitmap->mnHeight; + + if( bFirst ) + { + bFirst = false; + nXmin = nX1; + nXmax = nX2; + nYmin = nY1; + nYmax = nY2; + } + else + { + if( nXmin > nX1 ) nXmin = nX1; + if( nXmax < nX2 ) nXmax = nX2; + if( nYmin > nY1 ) nYmin = nY1; + if( nYmax < nY2 ) nYmax = nY2; + } + } + + // get XImage + GetDisplay()->GetXLib()->PushXErrorLevel( true ); + Display* pDisplay = GetXDisplay(); + + XRectangle aXRect; + long nWidth = 1, nHeight = 1; + if( m_pFrame ) + nWidth = m_pFrame->maGeometry.nWidth, nHeight = m_pFrame->maGeometry.nHeight; + else if( m_pVDev ) + nWidth = m_pVDev->GetWidth(), nHeight = m_pVDev->GetHeight(); + + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + { + // get bounding box + XClipBox( mpClipRegion, &aXRect ); + // clip with window + if( aXRect.x < 0 ) aXRect.x = 0; + + if( aXRect.y < 0 ) aXRect.y = 0; + if( aXRect.width+aXRect.x > nWidth ) aXRect.width = nWidth-aXRect.x; + if( aXRect.height+aXRect.y > nHeight ) aXRect.height = nHeight-aXRect.y; + } + else + { + aXRect.x = 0; + aXRect.y = 0; + aXRect.width = nWidth; + aXRect.height = nHeight; + } + if( m_pFrame ) + { + // clip with screen + int nScreenX = m_pFrame->maGeometry.nX+aXRect.x; + int nScreenY = m_pFrame->maGeometry.nY+aXRect.y; + const Size& rScreenSize = GetDisplay()->getDataForScreen( m_nScreen ).m_aSize; + int nScreenW = rScreenSize.Width(); + int nScreenH = rScreenSize.Height(); + if( nScreenX < 0 ) + aXRect.x -= nScreenX, aXRect.width += nScreenX; + if( nScreenX+aXRect.width > nScreenW ) + aXRect.width = nScreenW-nScreenX; + if( nScreenY < 0 ) + aXRect.y -= nScreenY, aXRect.height += nScreenY; + if( nScreenY+aXRect.height > nScreenH ) + aXRect.height = nScreenH-nScreenY; + } + + + if( nXmin < aXRect.x ) nXmin = aXRect.x; + if( nYmin < aXRect.y ) nYmin = aXRect.y; + if( nXmax >= aXRect.x+aXRect.width ) nXmax = aXRect.x + aXRect.width - 1; + if( nYmax >= aXRect.y+aXRect.height ) nYmax = aXRect.y + aXRect.height - 1; + + if( nXmin > nXmax ) + return false; + if( nYmin > nYmax ) + return false; + + XImage* pImg = XGetImage( pDisplay, hDrawable_, + nXmin, nYmin, + (nXmax-nXmin+1), (nYmax-nYmin+1), + ~0, ZPixmap ); + if( pImg == NULL ) + { + if( m_pFrame ) + { + // the reason we did not get an image could be that the frame + // geometry changed in the meantime; lets get the current geometry + // and clip against the current window size as well as the screen + // with the current frame position + const Size& rScreenSize = GetDisplay()->getDataForScreen(m_nScreen).m_aSize; + int nScreenW = rScreenSize.Width(); + int nScreenH = rScreenSize.Height(); + XLIB_Window aRoot = None; + int x = 0, y = 0; + unsigned int w = 0, h = 0, bw = 0, d; + XGetGeometry( pDisplay, hDrawable_, &aRoot, &x, &y, &w, &h, &bw, &d ); + XTranslateCoordinates( pDisplay, hDrawable_, aRoot, 0, 0, &x, &y, &aRoot ); + if( nXmin + x < 0 ) // clip on left screen edge + nXmin += x-nXmin; + if( nYmin + y < 0 ) // clip on top screen edge + nYmin += y-nYmin; + if( nXmax >= int(w) ) // clip on right window egde + nXmax = w-1; + if( nYmax >= int(h) ) // clip on bottom window edge + nYmax = h-1; + if( nXmax + x >= nScreenW ) // clip on right screen edge + nXmax -= (nXmax + x - nScreenW)+1; + if( nYmax + y >= nScreenH ) // clip on bottom screen edge + nYmax -= (nYmax + y - nScreenH)+1; + if( nXmax >= nXmin && nYmax >= nYmin ) + { + // try again to get the image + pImg = XGetImage( pDisplay, hDrawable_, + nXmin, nYmin, + (nXmax-nXmin+1), (nYmax-nYmin+1), + ~0, ZPixmap ); + } + } + if( pImg == NULL ) + { + GetDisplay()->GetXLib()->PopXErrorLevel(); + return false; + } + } + + // prepare context + GC nGC = GetFontGC(); + XGCValues aGCVal; + XGetGCValues( pDisplay, nGC, GCForeground, &aGCVal ); + + unsigned long nOrigColor = XGetPixel( pImg, 0, 0 ); + XPutPixel( pImg, 0, 0, aGCVal.foreground ); + unsigned char aColor[4]; + aColor[0] = pImg->data[0]; + aColor[1] = pImg->data[1]; + aColor[2] = pImg->data[2]; + aColor[3] = pImg->data[3]; + XPutPixel( pImg, 0, 0, nOrigColor ); + + // work on XImage + const int bpp = pImg->bits_per_pixel >> 3; + for( nStart = 0; rLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) + { + const RawBitmap* const pRawBitmap = rGlyphPeer.GetRawBitmap( rFont, nGlyph ); + if( !pRawBitmap ) + continue; + + const int nX1 = aPos.X() + pRawBitmap->mnXOffset; + const int nY1 = aPos.Y() + pRawBitmap->mnYOffset; + + if( (nX1 <= nXmax) && (int(nX1 + pRawBitmap->mnWidth) > nXmin) + && (nY1 <= nYmax) && (int(nY1 + pRawBitmap->mnHeight) > nYmin) ) + { + const unsigned char* p10 = pRawBitmap->mpBits; + unsigned char* p20 = (unsigned char*)pImg->data; // dest left limit + p20 += (nY1 - nYmin) * pImg->bytes_per_line; + unsigned char* p21 = p20 + (nX1 - nXmin + pImg->xoffset) * bpp; + int y = pRawBitmap->mnHeight; + if( y > nYmax - nY1 ) + y = nYmax - nY1 + 1; + while( --y >= 0 ) + { + if( p20 >= (unsigned char*)pImg->data ) + { + unsigned char* const p22 = p20 + pImg->width * bpp; // dest right limit + unsigned char* pDst = p21; + const unsigned char* pSrc = p10; + for( int x = pRawBitmap->mnWidth; (--x >= 0) && (p22 > pDst); ++pSrc ) + { + if( (*pSrc == 0) || (p20 > pDst) ) // keep background + pDst += bpp; + else if( *pSrc == 0xFF ) // paint foreground + { + const unsigned char* pColor = aColor; + for( int z = bpp; --z >= 0; ++pColor, ++pDst ) + *pDst = *pColor; + } + else // blend fg into bg + { + const unsigned char* pColor = aColor; + for( int z = bpp; --z >= 0; ++pColor, ++pDst ) + // theoretically it should be *257) >> 16 + // but the error is <0.4% worst case and we are in + // the innermost loop of very perf-sensitive code + + *pDst += (*pSrc * ((int)*pColor - *pDst)) >> 8; + } + } + } + p10 += pRawBitmap->mnScanlineSize; + p20 += pImg->bytes_per_line; + p21 += pImg->bytes_per_line; + } + } + } + + // put XImage + XPutImage( pDisplay, hDrawable_, nGC, pImg, + 0, 0, nXmin, nYmin, (nXmax - nXmin + 1), (nYmax - nYmin + 1) ); + XDestroyImage( pImg ); + + GetDisplay()->GetXLib()->PopXErrorLevel(); + return true; +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::DrawServerSimpleFontString( const ServerFontLayout& rSalLayout ) +{ + ServerFont& rFont = rSalLayout.GetServerFont(); + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + + Display* pDisplay = GetXDisplay(); + GC nGC = GetFontGC(); + + XGCValues aGCVal; + aGCVal.fill_style = FillStippled; + aGCVal.line_width = 0; + GC tmpGC = XCreateGC( pDisplay, hDrawable_, GCFillStyle|GCLineWidth, &aGCVal ); + XCopyGC( pDisplay, nGC, (1<<GCLastBit)-(1+GCFillStyle+GCLineWidth), tmpGC ); + + Point aPos; + sal_GlyphId nGlyph; + for( int nStart = 0; rSalLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) + { + // #i51924# avoid 32->16bit coordinate truncation problem in X11 + // TODO: reevaluate once displays with >30000 pixels are available + if( aPos.X() >= 30000 || aPos.Y() >= 30000 ) + continue; + + Pixmap aStipple = rGlyphPeer.GetPixmap( rFont, nGlyph, m_nScreen ); + const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyph ); + + if( aStipple != None ) + { + const int nDestX = aPos.X() + rGM.GetOffset().X(); + const int nDestY = aPos.Y() + rGM.GetOffset().Y(); + + aGCVal.stipple = aStipple; + aGCVal.ts_x_origin = nDestX; + aGCVal.ts_y_origin = nDestY; + XChangeGC( pDisplay, tmpGC, GCStipple|GCTileStipXOrigin|GCTileStipYOrigin, &aGCVal ); + + const int nWidth = rGM.GetSize().Width(); + const int nHeight = rGM.GetSize().Height(); + XFillRectangle( pDisplay, hDrawable_, tmpGC, nDestX, nDestY, nWidth, nHeight ); + } + } + + XFreeGC( pDisplay, tmpGC ); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::DrawServerFontLayout( const ServerFontLayout& rLayout ) +{ + // draw complex text + ServerFont& rFont = rLayout.GetServerFont(); + const bool bVertical = rFont.GetFontSelData().mbVertical; + + if( !bVertical && CairoWrapper::get().isCairoRenderable(rFont) ) + DrawCairoAAFontString( rLayout ); + else + { + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + if( rGlyphPeer.GetGlyphSet( rFont, m_nScreen ) ) + DrawServerAAFontString( rLayout ); + else if( !rGlyphPeer.ForcedAntialiasing( rFont, m_nScreen ) ) + DrawServerSimpleFontString( rLayout ); + else + DrawServerAAForcedString( rLayout ); + } +} + +//-------------------------------------------------------------------------- + +const ImplFontCharMap* X11SalGraphics::GetImplFontCharMap() const +{ + if( !mpServerFont[0] ) + return NULL; + + const ImplFontCharMap* pIFCMap = mpServerFont[0]->GetImplFontCharMap(); + return pIFCMap; +} + +bool X11SalGraphics::GetImplFontCapabilities(vcl::FontCapabilities &rGetImplFontCapabilities) const +{ + if (!mpServerFont[0]) + return false; + return mpServerFont[0]->GetFontCapabilities(rGetImplFontCapabilities); +} + +// ---------------------------------------------------------------------------- +// +// SalGraphics +// +// ---------------------------------------------------------------------------- + +sal_uInt16 X11SalGraphics::SetFont( ImplFontSelectData *pEntry, int nFallbackLevel ) +{ + sal_uInt16 nRetVal = 0; + if( !setFont( pEntry, nFallbackLevel ) ) + nRetVal |= SAL_SETFONT_BADFONT; + if( bPrinter_ || (mpServerFont[ nFallbackLevel ] != NULL) ) + nRetVal |= SAL_SETFONT_USEDRAWTEXTARRAY; + return nRetVal; +} + +// ---------------------------------------------------------------------------- + +void +X11SalGraphics::SetTextColor( SalColor nSalColor ) +{ + if( nTextColor_ != nSalColor ) + { + nTextColor_ = nSalColor; + nTextPixel_ = GetPixel( nSalColor ); + bFontGC_ = sal_False; + } +} + +// ---------------------------------------------------------------------------- + +bool X11SalGraphics::AddTempDevFont( ImplDevFontList* pFontList, + const String& rFileURL, const String& rFontName ) +{ + // inform PSP font manager + rtl::OUString aUSystemPath; + OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFileURL, aUSystemPath ) ); + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + OString aOFileName( OUStringToOString( aUSystemPath, aEncoding ) ); + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + int nFontId = rMgr.addFontFile( aOFileName, 0 ); + if( !nFontId ) + return false; + + // prepare font data + psp::FastPrintFontInfo aInfo; + rMgr.getFontFastInfo( nFontId, aInfo ); + aInfo.m_aFamilyName = rFontName; + + // inform glyph cache of new font + ImplDevFontAttributes aDFA = PspGraphics::Info2DevFontAttributes( aInfo ); + aDFA.mnQuality += 5800; + + int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID ); + if( nFaceNum < 0 ) + nFaceNum = 0; + + GlyphCache& rGC = X11GlyphCache::GetInstance(); + const rtl::OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID ); + rGC.AddFontFile( rFileName, nFaceNum, aInfo.m_nID, aDFA ); + + // announce new font to device's font list + rGC.AnnounceFonts( pFontList ); + return true; +} + +// ---------------------------------------------------------------------------- + +void RegisterFontSubstitutors( ImplDevFontList* ); + +void X11SalGraphics::GetDevFontList( ImplDevFontList *pList ) +{ + // prepare the GlyphCache using psprint's font infos + X11GlyphCache& rGC = X11GlyphCache::GetInstance(); + + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + ::std::list< psp::fontID > aList; + ::std::list< psp::fontID >::iterator it; + psp::FastPrintFontInfo aInfo; + rMgr.getFontList( aList ); + for( it = aList.begin(); it != aList.end(); ++it ) + { + if( !rMgr.getFontFastInfo( *it, aInfo ) ) + continue; + + // the GlyphCache must not bother with builtin fonts because + // it cannot access or use them anyway + if( aInfo.m_eType == psp::fonttype::Builtin ) + continue; + + // normalize face number to the GlyphCache + int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID ); + if( nFaceNum < 0 ) + nFaceNum = 0; + + // for fonts where extra kerning info can be provided on demand + // an ExtraKernInfo object is supplied + const ExtraKernInfo* pExtraKernInfo = NULL; + if( aInfo.m_eType == psp::fonttype::Type1 ) + pExtraKernInfo = new PspKernInfo( *it ); + + // inform GlyphCache about this font provided by the PsPrint subsystem + ImplDevFontAttributes aDFA = PspGraphics::Info2DevFontAttributes( aInfo ); + aDFA.mnQuality += 4096; + const rtl::OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID ); + rGC.AddFontFile( rFileName, nFaceNum, aInfo.m_nID, aDFA, pExtraKernInfo ); + } + + // announce glyphcache fonts + rGC.AnnounceFonts( pList ); + + // register platform specific font substitutions if available + if( rMgr.hasFontconfig() ) + RegisterFontSubstitutors( pList ); + + ImplGetSVData()->maGDIData.mbNativeFontConfig = rMgr.hasFontconfig(); +} + +// ---------------------------------------------------------------------------- + +void X11SalGraphics::GetDevFontSubstList( OutputDevice* ) +{ + // no device specific font substitutions on X11 needed +} + +// ---------------------------------------------------------------------------- + +void cairosubcallback( void* pPattern ) +{ + CairoWrapper& rCairo = CairoWrapper::get(); + if( !rCairo.isValid() ) + return; + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const void* pFontOptions = rStyleSettings.GetCairoFontOptions(); + if( !pFontOptions ) + return; + rCairo.ft_font_options_substitute( pFontOptions, pPattern ); +} + +ImplFontOptions* GetFCFontOptions( const ImplFontAttributes& rFontAttributes, int nSize) +{ + // TODO: get rid of these insane enum-conversions + // e.g. by using the classic vclenum values inside VCL + + psp::FastPrintFontInfo aInfo; + // set family name + aInfo.m_aFamilyName = rFontAttributes.GetFamilyName(); + // set italic + switch( rFontAttributes.GetSlant() ) + { + case ITALIC_NONE: + aInfo.m_eItalic = psp::italic::Upright; + break; + case ITALIC_NORMAL: + aInfo.m_eItalic = psp::italic::Italic; + break; + case ITALIC_OBLIQUE: + aInfo.m_eItalic = psp::italic::Oblique; + break; + default: + aInfo.m_eItalic = psp::italic::Unknown; + break; + } + // set weight + switch( rFontAttributes.GetWeight() ) + { + case WEIGHT_THIN: + aInfo.m_eWeight = psp::weight::Thin; + break; + case WEIGHT_ULTRALIGHT: + aInfo.m_eWeight = psp::weight::UltraLight; + break; + case WEIGHT_LIGHT: + aInfo.m_eWeight = psp::weight::Light; + break; + case WEIGHT_SEMILIGHT: + aInfo.m_eWeight = psp::weight::SemiLight; + break; + case WEIGHT_NORMAL: + aInfo.m_eWeight = psp::weight::Normal; + break; + case WEIGHT_MEDIUM: + aInfo.m_eWeight = psp::weight::Medium; + break; + case WEIGHT_SEMIBOLD: + aInfo.m_eWeight = psp::weight::SemiBold; + break; + case WEIGHT_BOLD: + aInfo.m_eWeight = psp::weight::Bold; + break; + case WEIGHT_ULTRABOLD: + aInfo.m_eWeight = psp::weight::UltraBold; + break; + case WEIGHT_BLACK: + aInfo.m_eWeight = psp::weight::Black; + break; + default: + aInfo.m_eWeight = psp::weight::Unknown; + break; + } + // set width + switch( rFontAttributes.GetWidthType() ) + { + case WIDTH_ULTRA_CONDENSED: + aInfo.m_eWidth = psp::width::UltraCondensed; + break; + case WIDTH_EXTRA_CONDENSED: + aInfo.m_eWidth = psp::width::ExtraCondensed; + break; + case WIDTH_CONDENSED: + aInfo.m_eWidth = psp::width::Condensed; + break; + case WIDTH_SEMI_CONDENSED: + aInfo.m_eWidth = psp::width::SemiCondensed; + break; + case WIDTH_NORMAL: + aInfo.m_eWidth = psp::width::Normal; + break; + case WIDTH_SEMI_EXPANDED: + aInfo.m_eWidth = psp::width::SemiExpanded; + break; + case WIDTH_EXPANDED: + aInfo.m_eWidth = psp::width::Expanded; + break; + case WIDTH_EXTRA_EXPANDED: + aInfo.m_eWidth = psp::width::ExtraExpanded; + break; + case WIDTH_ULTRA_EXPANDED: + aInfo.m_eWidth = psp::width::UltraExpanded; + break; + default: + aInfo.m_eWidth = psp::width::Unknown; + break; + } + + const psp::PrintFontManager& rPFM = psp::PrintFontManager::get(); + return rPFM.getFontOptions(aInfo, nSize, cairosubcallback); +} + +// ---------------------------------------------------------------------------- + +void +X11SalGraphics::GetFontMetric( ImplFontMetricData *pMetric, int nFallbackLevel ) +{ + if( nFallbackLevel >= MAX_FALLBACK ) + return; + + if( mpServerFont[nFallbackLevel] != NULL ) + { + long rDummyFactor; + mpServerFont[nFallbackLevel]->FetchFontMetric( *pMetric, rDummyFactor ); + } +} + +// --------------------------------------------------------------------------- + +sal_uLong +X11SalGraphics::GetKernPairs( sal_uLong nPairs, ImplKernPairData *pKernPairs ) +{ + if( ! bPrinter_ ) + { + if( mpServerFont[0] != NULL ) + { + ImplKernPairData* pTmpKernPairs; + sal_uLong nGotPairs = mpServerFont[0]->GetKernPairs( &pTmpKernPairs ); + for( unsigned int i = 0; i < nPairs && i < nGotPairs; ++i ) + pKernPairs[ i ] = pTmpKernPairs[ i ]; + delete[] pTmpKernPairs; + return nGotPairs; + } + } + return 0; +} + +// --------------------------------------------------------------------------- + +sal_Bool X11SalGraphics::GetGlyphBoundRect( long nGlyphIndex, Rectangle& rRect ) +{ + int nLevel = nGlyphIndex >> GF_FONTSHIFT; + if( nLevel < 0 || nLevel >= MAX_FALLBACK ) + return sal_False; + + ServerFont* pSF = mpServerFont[ nLevel ]; + if( !pSF ) + return sal_False; + + nGlyphIndex &= ~GF_FONTMASK; + const GlyphMetric& rGM = pSF->GetGlyphMetric( nGlyphIndex ); + rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() ); + return sal_True; +} + +// --------------------------------------------------------------------------- + +sal_Bool X11SalGraphics::GetGlyphOutline( long nGlyphIndex, + ::basegfx::B2DPolyPolygon& rPolyPoly ) +{ + int nLevel = nGlyphIndex >> GF_FONTSHIFT; + if( nLevel >= MAX_FALLBACK ) + return sal_False; + + ServerFont* pSF = mpServerFont[ nLevel ]; + if( !pSF ) + return sal_False; + + nGlyphIndex &= ~GF_FONTMASK; + if( pSF->GetGlyphOutline( nGlyphIndex, rPolyPoly ) ) + return sal_True; + + return sal_False; +} + +//-------------------------------------------------------------------------- + +SalLayout* X11SalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) +{ + SalLayout* pLayout = NULL; + + if( mpServerFont[ nFallbackLevel ] + && !(rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) ) + { +#ifdef ENABLE_GRAPHITE + // Is this a Graphite font? + if (!bDisableGraphite_ && + GraphiteServerFontLayout::IsGraphiteEnabledFont(mpServerFont[nFallbackLevel])) + { + pLayout = new GraphiteServerFontLayout(*mpServerFont[nFallbackLevel]); + } + else +#endif + pLayout = new ServerFontLayout( *mpServerFont[ nFallbackLevel ] ); + } + + return pLayout; +} + +//-------------------------------------------------------------------------- + +SystemFontData X11SalGraphics::GetSysFontData( int nFallbacklevel ) const +{ + SystemFontData aSysFontData; + aSysFontData.nSize = sizeof( SystemFontData ); + aSysFontData.nFontId = 0; + + if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1; + if (nFallbacklevel < 0 ) nFallbacklevel = 0; + + if (mpServerFont[nFallbacklevel] != NULL) + { + ServerFont* rFont = mpServerFont[nFallbacklevel]; + aSysFontData.nFontId = rFont->GetFtFace(); + aSysFontData.nFontFlags = rFont->GetLoadFlags(); + aSysFontData.bFakeBold = rFont->NeedsArtificialBold(); + aSysFontData.bFakeItalic = rFont->NeedsArtificialItalic(); + aSysFontData.bAntialias = rFont->GetAntialiasAdvice(); + aSysFontData.bVerticalCharacterType = rFont->GetFontSelData().mbVertical; + } + + return aSysFontData; +} + +//-------------------------------------------------------------------------- + +sal_Bool X11SalGraphics::CreateFontSubset( + const rtl::OUString& rToFile, + const ImplFontData* pFont, + sal_Int32* pGlyphIDs, + sal_uInt8* pEncoding, + sal_Int32* pWidths, + int nGlyphCount, + FontSubsetInfo& rInfo + ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + bool bSuccess = rMgr.createFontSubset( rInfo, + aFont, + rToFile, + pGlyphIDs, + pEncoding, + pWidths, + nGlyphCount ); + return bSuccess; +} + +//-------------------------------------------------------------------------- + +const void* X11SalGraphics::GetEmbedFontData( const ImplFontData* pFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + return PspGraphics::DoGetEmbedFontData( aFont, pUnicodes, pWidths, rInfo, pDataLen ); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::FreeEmbedFontData( const void* pData, long nLen ) +{ + PspGraphics::DoFreeEmbedFontData( pData, nLen ); +} + +//-------------------------------------------------------------------------- + +const Ucs2SIntMap* X11SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + return PspGraphics::DoGetFontEncodingVector( aFont, pNonEncoded ); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::GetGlyphWidths( const ImplFontData* pFont, + bool bVertical, + Int32Vector& rWidths, + Ucs2UIntMap& rUnicodeEnc ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + PspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc ); +} + +// =========================================================================== +// platform specific font substitution hooks + +class FcPreMatchSubstititution +: public ImplPreMatchFontSubstitution +{ +public: + bool FindFontSubstitute( ImplFontSelectData& ) const; + +private: + typedef ::boost::unordered_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash > + CachedFontMapType; + mutable CachedFontMapType maCachedFontMap; +}; + +class FcGlyphFallbackSubstititution +: public ImplGlyphFallbackFontSubstitution +{ + // TODO: add a cache +public: + bool FindFontSubstitute( ImplFontSelectData&, OUString& rMissingCodes ) const; +}; + +void RegisterFontSubstitutors( ImplDevFontList* pList ) +{ + // init font substitution defaults + int nDisableBits = 0; +#ifdef SOLARIS + nDisableBits = 1; // disable "font fallback" here on default +#endif + // apply the environment variable if any + const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" ); + if( pEnvStr ) + { + if( (*pEnvStr >= '0') && (*pEnvStr <= '9') ) + nDisableBits = (*pEnvStr - '0'); + else + nDisableBits = ~0U; // no specific bits set: disable all + } + + // register font fallback substitutions (unless disabled by bit0) + if( (nDisableBits & 1) == 0 ) + { + static FcPreMatchSubstititution aSubstPreMatch; + pList->SetPreMatchHook( &aSubstPreMatch ); + } + + // register glyph fallback substitutions (unless disabled by bit1) + if( (nDisableBits & 2) == 0 ) + { + static FcGlyphFallbackSubstititution aSubstFallback; + pList->SetFallbackHook( &aSubstFallback ); + } +} + +// ----------------------------------------------------------------------- + +static ImplFontSelectData GetFcSubstitute(const ImplFontSelectData &rFontSelData, OUString& rMissingCodes ) +{ + ImplFontSelectData aRet(rFontSelData); + + const rtl::OString aLangAttrib = MsLangId::convertLanguageToIsoByteString( rFontSelData.meLanguage ); + + psp::italic::type eItalic = psp::italic::Unknown; + if( rFontSelData.GetSlant() != ITALIC_DONTKNOW ) + { + switch( rFontSelData.GetSlant() ) + { + case ITALIC_NONE: eItalic = psp::italic::Upright; break; + case ITALIC_NORMAL: eItalic = psp::italic::Italic; break; + case ITALIC_OBLIQUE: eItalic = psp::italic::Oblique; break; + default: + break; + } + } + + psp::weight::type eWeight = psp::weight::Unknown; + if( rFontSelData.GetWeight() != WEIGHT_DONTKNOW ) + { + switch( rFontSelData.GetWeight() ) + { + case WEIGHT_THIN: eWeight = psp::weight::Thin; break; + case WEIGHT_ULTRALIGHT: eWeight = psp::weight::UltraLight; break; + case WEIGHT_LIGHT: eWeight = psp::weight::Light; break; + case WEIGHT_SEMILIGHT: eWeight = psp::weight::SemiLight; break; + case WEIGHT_NORMAL: eWeight = psp::weight::Normal; break; + case WEIGHT_MEDIUM: eWeight = psp::weight::Medium; break; + case WEIGHT_SEMIBOLD: eWeight = psp::weight::SemiBold; break; + case WEIGHT_BOLD: eWeight = psp::weight::Bold; break; + case WEIGHT_ULTRABOLD: eWeight = psp::weight::UltraBold; break; + case WEIGHT_BLACK: eWeight = psp::weight::Black; break; + default: + break; + } + } + + psp::width::type eWidth = psp::width::Unknown; + if( rFontSelData.GetWidthType() != WIDTH_DONTKNOW ) + { + switch( rFontSelData.GetWidthType() ) + { + case WIDTH_ULTRA_CONDENSED: eWidth = psp::width::UltraCondensed; break; + case WIDTH_EXTRA_CONDENSED: eWidth = psp::width::ExtraCondensed; break; + case WIDTH_CONDENSED: eWidth = psp::width::Condensed; break; + case WIDTH_SEMI_CONDENSED: eWidth = psp::width::SemiCondensed; break; + case WIDTH_NORMAL: eWidth = psp::width::Normal; break; + case WIDTH_SEMI_EXPANDED: eWidth = psp::width::SemiExpanded; break; + case WIDTH_EXPANDED: eWidth = psp::width::Expanded; break; + case WIDTH_EXTRA_EXPANDED: eWidth = psp::width::ExtraExpanded; break; + case WIDTH_ULTRA_EXPANDED: eWidth = psp::width::UltraExpanded; break; + default: + break; + } + } + + psp::pitch::type ePitch = psp::pitch::Unknown; + if( rFontSelData.GetPitch() != PITCH_DONTKNOW ) + { + switch( rFontSelData.GetPitch() ) + { + case PITCH_FIXED: ePitch=psp::pitch::Fixed; break; + case PITCH_VARIABLE: ePitch=psp::pitch::Variable; break; + default: + break; + } + } + + const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + aRet.maSearchName = rMgr.Substitute( rFontSelData.maTargetName, rMissingCodes, aLangAttrib, eItalic, eWeight, eWidth, ePitch); + + switch (eItalic) + { + case psp::italic::Upright: aRet.meItalic = ITALIC_NONE; break; + case psp::italic::Italic: aRet.meItalic = ITALIC_NORMAL; break; + case psp::italic::Oblique: aRet.meItalic = ITALIC_OBLIQUE; break; + default: + break; + } + + switch (eWeight) + { + case psp::weight::Thin: aRet.meWeight = WEIGHT_THIN; break; + case psp::weight::UltraLight: aRet.meWeight = WEIGHT_ULTRALIGHT; break; + case psp::weight::Light: aRet.meWeight = WEIGHT_LIGHT; break; + case psp::weight::SemiLight: aRet.meWeight = WEIGHT_SEMILIGHT; break; + case psp::weight::Normal: aRet.meWeight = WEIGHT_NORMAL; break; + case psp::weight::Medium: aRet.meWeight = WEIGHT_MEDIUM; break; + case psp::weight::SemiBold: aRet.meWeight = WEIGHT_SEMIBOLD; break; + case psp::weight::Bold: aRet.meWeight = WEIGHT_BOLD; break; + case psp::weight::UltraBold: aRet.meWeight = WEIGHT_ULTRABOLD; break; + case psp::weight::Black: aRet.meWeight = WEIGHT_BLACK; break; + default: + break; + } + + switch (eWidth) + { + case psp::width::UltraCondensed: aRet.meWidthType = WIDTH_ULTRA_CONDENSED; break; + case psp::width::ExtraCondensed: aRet.meWidthType = WIDTH_EXTRA_CONDENSED; break; + case psp::width::Condensed: aRet.meWidthType = WIDTH_CONDENSED; break; + case psp::width::SemiCondensed: aRet.meWidthType = WIDTH_SEMI_CONDENSED; break; + case psp::width::Normal: aRet.meWidthType = WIDTH_NORMAL; break; + case psp::width::SemiExpanded: aRet.meWidthType = WIDTH_SEMI_EXPANDED; break; + case psp::width::Expanded: aRet.meWidthType = WIDTH_EXPANDED; break; + case psp::width::ExtraExpanded: aRet.meWidthType = WIDTH_EXTRA_EXPANDED; break; + case psp::width::UltraExpanded: aRet.meWidthType = WIDTH_ULTRA_EXPANDED; break; + default: + break; + } + + switch (ePitch) + { + case psp::pitch::Fixed: aRet.mePitch = PITCH_FIXED; break; + case psp::pitch::Variable: aRet.mePitch = PITCH_VARIABLE; break; + default: + break; + } + + return aRet; +} + +namespace +{ + bool uselessmatch(const ImplFontSelectData &rOrig, const ImplFontSelectData &rNew) + { + return + ( + rOrig.maTargetName == rNew.maSearchName && + rOrig.meWeight == rNew.meWeight && + rOrig.meItalic == rNew.meItalic && + rOrig.mePitch == rNew.mePitch && + rOrig.meWidthType == rNew.meWidthType + ); + } +} + +//-------------------------------------------------------------------------- + +bool FcPreMatchSubstititution::FindFontSubstitute( ImplFontSelectData &rFontSelData ) const +{ + // We dont' actually want to talk to Fontconfig at all for symbol fonts + if( rFontSelData.IsSymbolFont() ) + return false; + // StarSymbol is a unicode font, but it still deserves the symbol flag + if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) + || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) + return false; + + CachedFontMapType::const_iterator itr = maCachedFontMap.find(rFontSelData.maTargetName); + if (itr != maCachedFontMap.end()) + { + // Cached substitution pair + rFontSelData.maSearchName = itr->second; + return true; + } + + rtl::OUString aDummy; + const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, aDummy ); + + maCachedFontMap.insert( + CachedFontMapType::value_type(rFontSelData.maTargetName, aOut.maSearchName)); + + if( !aOut.maSearchName.Len() ) + return false; + + const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); + +#ifdef DEBUG + const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); + const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); + printf( "FcPreMatchSubstititution \"%s\" bipw=%d%d%d%d -> ", + aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, + rFontSelData.mePitch, rFontSelData.meWidthType ); + if( !bHaveSubstitute ) + printf( "no substitute available\n" ); + else + printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), + aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); +#endif + + if( bHaveSubstitute ) + rFontSelData = aOut; + + return bHaveSubstitute; +} + +// ----------------------------------------------------------------------- + +bool FcGlyphFallbackSubstititution::FindFontSubstitute( ImplFontSelectData& rFontSelData, + rtl::OUString& rMissingCodes ) const +{ + // We dont' actually want to talk to Fontconfig at all for symbol fonts + if( rFontSelData.IsSymbolFont() ) + return false; + // StarSymbol is a unicode font, but it still deserves the symbol flag + if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) + || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) + return false; + + const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, rMissingCodes ); + // TODO: cache the unicode + srcfont specific result + // FC doing it would be preferable because it knows the invariables + // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans + // whereas we would have to check for every size or attribute + if( !aOut.maSearchName.Len() ) + return false; + + const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); + +#ifdef DEBUG + const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); + const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); + printf( "FcGFSubstititution \"%s\" bipw=%d%d%d%d ->", + aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, + rFontSelData.mePitch, rFontSelData.meWidthType ); + if( !bHaveSubstitute ) + printf( "no substitute available\n" ); + else + printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), + aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); +#endif + + if( bHaveSubstitute ) + rFontSelData = aOut; + + return bHaveSubstitute; +} + +// =========================================================================== + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |