/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the 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 <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"


#include <hash_set>

#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 namespace rtl;

// ===========================================================================

// 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;

    // feed psprint's kerning list into a lookup-friendly container
    maUnicodeKernPairs.rehash( rKernPairs.size() );
    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 )
{
#ifdef HDU_DEBUG
    ByteString aReqName( "NULL" );
    if( pEntry )
        aReqName = ByteString( pEntry->maName, RTL_TEXTENCODING_UTF8 );
    ByteString aUseName( "NULL" );
    if( pEntry && pEntry->mpFontData )
        aUseName = ByteString( pEntry->mpFontData->GetFamilyName(), RTL_TEXTENCODING_UTF8 );
    fprintf( stderr, "SetFont(lvl=%d,\"%s\", %d*%d, naa=%d,b=%d,i=%d) => \"%s\"\n",
        nFallbackLevel, aReqName.GetBuffer(),
	!pEntry?-1:pEntry->mnWidth, !pEntry?-1:pEntry->mnHeight,
        !pEntry?-1:pEntry->mbNonAntialiased,
	!pEntry?-1:pEntry->meWeight, !pEntry?-1:pEntry->meItalic,
        aUseName.GetBuffer() );
#endif

    // 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;
}

void ImplServerFontEntry::HandleFontOptions( void )
{
	bool GetFCFontOptions( const ImplFontAttributes&, int nSize, ImplFontOptions& );

	if( !mpServerFont )
		return;
	if( !mbGotFontOptions )
	{
		// get and cache the font options
		mbGotFontOptions = true;
		mbValidFontOptions = GetFCFontOptions( *maFontSelData.mpFontData,
			maFontSelData.mnHeight, maFontOptions );
	}
	// apply the font options
	if( mbValidFontOptions )
		mpServerFont->SetFontOptions( maFontOptions );
}

//--------------------------------------------------------------------------

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);
    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 false; }

    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); }
    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;

    mpCairoLib = osl_loadAsciiModule( "libcairo.so.2", 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_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, void* pId)
{
    maLRUFonts.push_front( std::pair<void*, void *>(pFont, pId) );
    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(void *pId)
{
    LRUFonts::iterator aEnd = maLRUFonts.end();
    for (LRUFonts::iterator aI = maLRUFonts.begin(); aI != aEnd; ++aI)
        if (aI->second == pId)
            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 = static_cast<XRenderPictFormat*>(GetXRenderFormat());
    if( !pVisualFormat )
    {
        Visual* pVisual = GetDisplay()->GetVisual( m_nScreen ).GetVisual();
        pVisualFormat = XRenderPeer::GetInstance().FindVisualFormat( pVisual );
        // cache the XRenderPictFormat
        SetXRenderFormat( static_cast<void*>(pVisualFormat) );
    }

    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 *pId = rFont.GetFtFace();
    font_face = (cairo_font_face_t*)m_aCairoFontsCache.FindCachedFont(pId);
    if (!font_face)
    {
        font_face = rCairo.ft_font_face_create_for_ft_face(pId, rFont.GetLoadFlags());
        m_aCairoFontsCache.CacheFont(font_face, pId);
    }

    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.GetXRGlyph( 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;
}

// ----------------------------------------------------------------------------
//
// 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 );
}

bool GetFCFontOptions( const ImplFontAttributes& rFontAttributes, int nSize,
	ImplFontOptions& rFontOptions)
{
    // 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();
    bool bOK = rPFM.getFontOptions( aInfo, nSize, cairosubcallback, rFontOptions);
    return bOK;
}

// ----------------------------------------------------------------------------

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;
}

// ---------------------------------------------------------------------------

bool X11SalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect )
{
    const int nLevel = aGlyphId >> GF_FONTSHIFT;
    if( nLevel >= MAX_FALLBACK )
        return false;

    ServerFont* pSF = mpServerFont[ nLevel ];
    if( !pSF )
        return false;

    aGlyphId &= ~GF_FONTMASK;
    const GlyphMetric& rGM = pSF->GetGlyphMetric( aGlyphId );
    rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() );
    return true;
}

// ---------------------------------------------------------------------------

bool X11SalGraphics::GetGlyphOutline( sal_GlyphId aGlyphId,
    ::basegfx::B2DPolyPolygon& rPolyPoly )
{
    const int nLevel = aGlyphId >> GF_FONTSHIFT;
    if( nLevel >= MAX_FALLBACK )
        return false;

    ServerFont* pSF = mpServerFont[ nLevel ];
    if( !pSF )
        return false;

    aGlyphId &= ~GF_FONTMASK;
    bool bOK = pSF->GetGlyphOutline( aGlyphId, rPolyPoly );
    return bOK;
}

//--------------------------------------------------------------------------

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_ &&
            GraphiteFontAdaptor::IsGraphiteEnabledFont(*mpServerFont[nFallbackLevel]))
        {
            sal_Int32 xdpi, ydpi;

            xdpi = GetDisplay()->GetResolution().A();
            ydpi = GetDisplay()->GetResolution().B();

            GraphiteFontAdaptor * pGrfont = new GraphiteFontAdaptor( *mpServerFont[nFallbackLevel], xdpi, ydpi);
            if (!pGrfont) return NULL;
            pLayout = new GraphiteServerFontLayout(pGrfont);
        }
        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_GlyphId* 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;
};

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;

    rtl::OUString aDummy;
    const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, aDummy );
    // TODO: cache the font substitution suggestion
    // 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( "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;
}

// ===========================================================================