blob: 4f11a4c51f00db29b98c93f6ddf38d85b50711b5 [file] [log] [blame]
/**************************************************************
*
* 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 = GetDisplay()->GetResolution().A();
sal_Int32 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;
}
// ===========================================================================