Kill the old Unix layout engines

Change-Id: I9e1667faf9644dfab025c82cb11a6490d1e8f998
Reviewed-on: https://gerrit.libreoffice.org/31221
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Khaled Hosny <khaledhosny@eglug.org>
This commit is contained in:
Khaled Hosny
2016-11-26 05:58:33 +00:00
parent 8b920435c0
commit 47ea13ef8d
9 changed files with 21 additions and 1005 deletions
-1
View File
@@ -543,7 +543,6 @@ vcl_headless_freetype_code=\
vcl/headless/svpglyphcache \
vcl/unx/generic/gdi/cairotextrender \
vcl/unx/generic/glyphs/freetype_glyphcache \
vcl/unx/generic/glyphs/gcach_layout \
vcl/unx/generic/glyphs/glyphcache \
vcl/unx/generic/fontmanager/fontsubst \
vcl/unx/generic/fontmanager/fontcache \
-1
View File
@@ -34,7 +34,6 @@ class PhysicalFontCollection;
namespace psp { struct JobData; class PrinterGfx; }
class FreetypeFont;
class ServerFontLayout;
class FontAttributes;
class SalInfoPrinter;
class GlyphCache;
-43
View File
@@ -43,8 +43,6 @@ class GlyphData;
class FontConfigFontOptions;
class PhysicalFontCollection;
class FreetypeFont;
class ServerFontLayout;
class ServerFontLayoutEngine;
class SvpGcpHelper;
namespace basegfx { class B2DPolyPolygon; }
@@ -178,7 +176,6 @@ public:
private:
friend class GlyphCache;
friend class ServerFontLayout;
friend class FreetypeFontInstance;
friend class X11SalGraphics;
friend class CairoTextRender;
@@ -193,9 +190,6 @@ private:
void ReleaseFromGarbageCollect();
void ApplyGlyphTransform( int nGlyphFlags, FT_GlyphRec_* ) const;
void ApplyGSUB( const FontSelectPattern& );
ServerFontLayoutEngine* GetLayoutEngine();
typedef std::unordered_map<int,GlyphData> GlyphList;
mutable GlyphList maGlyphList;
@@ -233,7 +227,6 @@ private:
typedef std::unordered_map<int,int> GlyphSubstitution;
GlyphSubstitution maGlyphSubstitution;
ServerFontLayoutEngine* mpLayoutEngine;
hb_font_t* mpHbFont;
};
@@ -250,42 +243,6 @@ private:
FreetypeFont* mpFreetypeFont;
};
class VCL_DLLPUBLIC ServerFontLayout : public GenericSalLayout
{
public:
ServerFontLayout( FreetypeFont& );
virtual bool LayoutText( ImplLayoutArgs& ) override;
virtual void AdjustLayout( ImplLayoutArgs& ) override;
virtual void DrawText( SalGraphics& ) const override;
void SetNeedFallback(
ImplLayoutArgs& rArgs,
sal_Int32 nIndex,
bool bRightToLeft);
FreetypeFont& GetFreetypeFont() const { return mrFreetypeFont; }
virtual std::shared_ptr<vcl::TextLayoutCache>
CreateTextLayoutCache(OUString const&) const override;
private:
FreetypeFont& mrFreetypeFont;
css::uno::Reference<css::i18n::XBreakIterator> mxBreak;
ServerFontLayout( const ServerFontLayout& ) = delete;
ServerFontLayout& operator=( const ServerFontLayout& ) = delete;
};
class ServerFontLayoutEngine
{
public:
virtual ~ServerFontLayoutEngine() {}
virtual bool Layout(ServerFontLayout&, ImplLayoutArgs&) = 0;
};
#endif // INCLUDED_VCL_INC_GENERIC_GLYPHCACHE_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
-2
View File
@@ -54,7 +54,6 @@ class X11OpenGLSalGraphicsImpl;
class X11OpenGLSalVirtualDevice;
class FreetypeFont;
class ImplLayoutArgs;
class ServerFontLayout;
class PhysicalFontCollection;
class PhysicalFontFace;
class SalGraphicsImpl;
@@ -66,7 +65,6 @@ namespace basegfx {
class VCLPLUG_GEN_PUBLIC X11SalGraphics : public SalGraphics
{
friend class ServerFontLayout;
friend class X11SalGraphicsImpl;
friend class X11OpenGLSalGraphicsImpl;
friend class X11CairoTextRender;
+7 -11
View File
@@ -120,17 +120,13 @@ void FontCfgWrapper::addFontSet( FcSetName eSetName )
FcResult eOutRes = FcPatternGetBool( pPattern, FC_OUTLINE, 0, &bOutline );
if( (eOutRes != FcResultMatch) || (bOutline == FcFalse) )
continue;
if (SalLayout::UseCommonLayout())
{
// Ignore Type 1 fonts; CommonSalLayout does not support them.
FcChar8* pFormat = nullptr;
FcResult eFormatRes = FcPatternGetString(pPattern, FC_FONTFORMAT, 0, &pFormat);
if ((eFormatRes == FcResultMatch) &&
(strcmp(reinterpret_cast<char*>(pFormat), "Type 1") == 0))
{
continue;
}
}
// Ignore Type 1 fonts, too.
FcChar8* pFormat = nullptr;
FcResult eFormatRes = FcPatternGetString(pPattern, FC_FONTFORMAT, 0, &pFormat);
if ((eFormatRes == FcResultMatch) && (strcmp(reinterpret_cast<char*>(pFormat), "Type 1") == 0))
continue;
FcPatternReference( pPattern );
FcFontSetAdd( m_pOutlineSet, pPattern );
}
+9 -33
View File
@@ -185,9 +185,6 @@ void CairoTextRender::DrawServerFontLayout( const GenericSalLayout& rLayout, con
case GF_ROTL: // left
glyph_extrarotation.push_back(1);
break;
case GF_ROTR: // right
glyph_extrarotation.push_back(-1);
break;
default:
glyph_extrarotation.push_back(0);
break;
@@ -293,35 +290,17 @@ void CairoTextRender::DrawServerFontLayout( const GenericSalLayout& rLayout, con
//like them
double xdiff = 0.0;
double ydiff = 0.0;
if (nGlyphRotation == 1)
if (nGlyphRotation)
{
if (SalLayout::UseCommonLayout())
{
// The y is the origin point position, but Cairo will draw
// the glyph *above* that point, we need to move it down to
// the glyph’s baseline.
cairo_text_extents_t aExt;
cairo_glyph_extents(cr, &cairo_glyphs[nStartIndex], nLen, &aExt);
double nDescender = std::fmax(aExt.height + aExt.y_bearing, 0);
ydiff = (aExt.x_advance - nDescender) / nHeight;
}
else
{
ydiff = font_extents.ascent/nHeight;
}
// The y is the origin point position, but Cairo will draw
// the glyph *above* that point, we need to move it down to
// the glyph’s baseline.
cairo_text_extents_t aExt;
cairo_glyph_extents(cr, &cairo_glyphs[nStartIndex], nLen, &aExt);
double nDescender = std::fmax(aExt.height + aExt.y_bearing, 0);
ydiff = (aExt.x_advance - nDescender) / nHeight;
xdiff = -font_extents.descent/nHeight;
}
else if (nGlyphRotation == -1)
{
cairo_text_extents_t text_extents;
cairo_glyph_extents(cr, &cairo_glyphs[nStartIndex], nLen,
&text_extents);
xdiff = -text_extents.x_advance/nHeight;
//to restore an apparent bug in the original X11 impl, replace
//nHeight with nWidth below
xdiff += font_extents.descent/nHeight;
}
cairo_matrix_translate(&m, xdiff, ydiff);
}
@@ -506,10 +485,7 @@ SalLayout* CairoTextRender::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackL
if( mpFreetypeFont[ nFallbackLevel ]
&& !(rArgs.mnFlags & SalLayoutFlags::DisableGlyphProcessing) )
{
if (SalLayout::UseCommonLayout())
pLayout = new CommonSalLayout(*mpFreetypeFont[nFallbackLevel]);
else
pLayout = new ServerFontLayout( *mpFreetypeFont[ nFallbackLevel ] );
pLayout = new CommonSalLayout(*mpFreetypeFont[nFallbackLevel]);
}
return pLayout;
@@ -225,7 +225,6 @@ void FreetypeFontInfo::ReleaseFaceFT()
static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);}
//static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
static const sal_uInt32 T_true = 0x74727565; /* 'true' */
static const sal_uInt32 T_ttcf = 0x74746366; /* 'ttcf' */
@@ -398,7 +397,6 @@ FreetypeFont::FreetypeFont( const FontSelectPattern& rFSD, FreetypeFontInfo* pFI
mbArtItalic( false ),
mbArtBold( false ),
mbUseGamma( false ),
mpLayoutEngine( nullptr ),
mpHbFont( nullptr )
{
// TODO: move update of mpFontInstance into FontEntry class when
@@ -445,9 +443,6 @@ FreetypeFont::FreetypeFont( const FontSelectPattern& rFSD, FreetypeFontInfo* pFI
mbFaceOk = true;
if (!SalLayout::UseCommonLayout())
ApplyGSUB( rFSD );
// TODO: query GASP table for load flags
mnLoadFlags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM;
@@ -562,8 +557,6 @@ int FreetypeFont::GetFontFaceIndex() const
FreetypeFont::~FreetypeFont()
{
delete mpLayoutEngine;
if( maSizeFT )
FT_Done_Size( maSizeFT );
@@ -1213,232 +1206,6 @@ bool FreetypeFont::GetGlyphOutline( sal_GlyphId aGlyphId,
return true;
}
void FreetypeFont::ApplyGSUB( const FontSelectPattern& rFSD )
{
#define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
std::vector<sal_uInt32> aReqFeatureTagList;
if( rFSD.mbVertical )
aReqFeatureTagList.push_back( MKTAG("vert") );
// TODO: request more features depending on script and language system
if( aReqFeatureTagList.empty()) // nothing to do
return;
// load GSUB table into memory
sal_uLong nLength = 0;
const FT_Byte* const pGsubBase = mpFontInfo->GetTable( "GSUB", &nLength );
if( !pGsubBase )
return;
// parse GSUB header
const FT_Byte* pGsubHeader = pGsubBase;
const sal_uInt16 nOfsScriptList = GetUShort( pGsubHeader+4 );
const sal_uInt16 nOfsFeatureTable = GetUShort( pGsubHeader+6 );
const sal_uInt16 nOfsLookupList = GetUShort( pGsubHeader+8 );
std::vector<sal_uInt16> aFeatureIndexList;
// parse Script Table
const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
const sal_uInt16 nCntScript = GetUShort( pScriptHeader+0 );
pScriptHeader += 2;
for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
{
const sal_uInt16 nOfsScriptTable= GetUShort( pScriptHeader+4 );
pScriptHeader += 6;
const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
const sal_uInt16 nDefaultLangsysOfs = GetUShort( pScriptTable+0 );
const sal_uInt16 nCntLangSystem = GetUShort( pScriptTable+2 );
pScriptTable += 4;
sal_uInt16 nLangsysOffset = 0;
if (nCntLangSystem != 0)
{
nLangsysOffset = GetUShort( pScriptTable+4 );
}
if (nDefaultLangsysOfs != 0 && nDefaultLangsysOfs != nLangsysOffset)
{
const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
pLangSys += 6;
aFeatureIndexList.push_back( nReqFeatureIdx );
for( sal_uInt16 i = 0; i < nCntFeature; ++i )
{
const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
pLangSys += 2;
aFeatureIndexList.push_back( nFeatureIndex );
}
}
if( nLangsysOffset != 0 )
{
const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
pLangSys += 6;
aFeatureIndexList.push_back( nReqFeatureIdx );
for( sal_uInt16 i = 0; i < nCntFeature; ++i )
{
const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
pLangSys += 2;
aFeatureIndexList.push_back( nFeatureIndex );
}
}
}
if( aFeatureIndexList.empty() )
return;
std::vector<sal_uInt16> aLookupIndexList;
std::vector<sal_uInt16> aLookupOffsetList;
// parse Feature Table
const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
const sal_uInt16 nCntFeature = GetUShort( pFeatureHeader );
pFeatureHeader += 2;
for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
{
const sal_uLong nTag = GetUInt( pFeatureHeader+0 ); // e.g. locl/vert/trad/smpl/liga/fina/...
const sal_uInt16 nOffset= GetUShort( pFeatureHeader+4 );
pFeatureHeader += 6;
// short circuit some feature lookups
if( aFeatureIndexList[0] != nFeatureIndex ) // required feature?
{
const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
if( !nRequested ) // ignore features that are not requested
continue;
const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
if( !nAvailable ) // some fonts don't provide features they request!
continue;
}
const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
pFeatureTable += 2; // ignore FeatureParams
const sal_uInt16 nCntLookups = GetUShort( pFeatureTable+0 );
pFeatureTable += 2;
for( sal_uInt16 i = 0; i < nCntLookups; ++i )
{
const sal_uInt16 nLookupIndex = GetUShort( pFeatureTable );
pFeatureTable += 2;
aLookupIndexList.push_back( nLookupIndex );
}
if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
aLookupIndexList.push_back( 0 );
}
// parse Lookup List
const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
const sal_uInt16 nCntLookupTable = GetUShort( pLookupHeader );
pLookupHeader += 2;
for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
{
const sal_uInt16 nOffset = GetUShort( pLookupHeader );
pLookupHeader += 2;
if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
aLookupOffsetList.push_back( nOffset );
}
std::vector<sal_uInt16>::const_iterator lookup_it = aLookupOffsetList.begin();
for(; lookup_it != aLookupOffsetList.end(); ++lookup_it )
{
const sal_uInt16 nOfsLookupTable = *lookup_it;
const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
const sal_uInt16 eLookupType = GetUShort( pLookupTable+0 );
const sal_uInt16 nCntLookupSubtable = GetUShort( pLookupTable+4 );
pLookupTable += 6;
// TODO: switch( eLookupType )
if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
continue;
for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
{
const sal_uInt16 nOfsSubLookupTable = GetUShort( pLookupTable );
pLookupTable += 2;
const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
const sal_uInt16 nFmtSubstitution = GetUShort( pSubLookup+0 );
const sal_uInt16 nOfsCoverage = GetUShort( pSubLookup+2 );
pSubLookup += 4;
typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
std::vector<GlyphSubst> aSubstVector;
const FT_Byte* pCoverage = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
const sal_uInt16 nFmtCoverage = GetUShort( pCoverage+0 );
pCoverage += 2;
switch( nFmtCoverage )
{
case 1: // Coverage Format 1
{
const sal_uInt16 nCntGlyph = GetUShort( pCoverage );
pCoverage += 2;
aSubstVector.reserve( nCntGlyph );
for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
{
const sal_uInt16 nGlyphId = GetUShort( pCoverage );
pCoverage += 2;
aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
}
}
break;
case 2: // Coverage Format 2
{
const sal_uInt16 nCntRange = GetUShort( pCoverage );
pCoverage += 2;
for( int i = nCntRange; --i >= 0; )
{
const sal_uInt32 nGlyph0 = GetUShort( pCoverage+0 );
const sal_uInt32 nGlyph1 = GetUShort( pCoverage+2 );
const sal_uInt16 nCovIdx = GetUShort( pCoverage+4 );
pCoverage += 6;
for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
}
}
break;
}
std::vector<GlyphSubst>::iterator it( aSubstVector.begin() );
switch( nFmtSubstitution )
{
case 1: // Single Substitution Format 1
{
const sal_uInt16 nDeltaGlyphId = GetUShort( pSubLookup );
for(; it != aSubstVector.end(); ++it )
(*it).second = (*it).first + nDeltaGlyphId;
}
break;
case 2: // Single Substitution Format 2
{
const sal_uInt16 nCntGlyph = GetUShort( pSubLookup );
pSubLookup += 2;
for( int i = nCntGlyph; (it != aSubstVector.end()) && (--i>=0); ++it )
{
const sal_uInt16 nGlyphId = GetUShort( pSubLookup );
pSubLookup += 2;
(*it).second = nGlyphId;
}
}
break;
}
SAL_WARN_IF( (it != aSubstVector.end()), "vcl", "lookup<->coverage table mismatch" );
// now apply the glyph substitutions that have been collected in this subtable
for( it = aSubstVector.begin(); it != aSubstVector.end(); ++it )
maGlyphSubstitution[ (*it).first ] = (*it).second;
}
}
}
const unsigned char* FreetypeFont::GetTable(const char* pName, sal_uLong* pLength)
{
return mpFontInfo->GetTable( pName, pLength );
-621
View File
@@ -1,621 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include "unx/freetype_glyphcache.hxx"
#include <sallayout.hxx>
#include <salgdi.hxx>
#include <scrptrun.h>
#include <unx/genpspgraphics.h>
#include <i18nlangtag/mslangid.hxx>
#include <vcl/svapp.hxx>
#include <vcl/unohelp.hxx>
#include <rtl/instance.hxx>
#include <hb-icu.h>
#include <hb-ot.h>
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
#ifndef HB_VERSION_ATLEAST
#define HB_VERSION_ATLEAST(a,b,c) 0
#endif
// layout implementation for FreetypeFont
ServerFontLayout::ServerFontLayout( FreetypeFont& rFont )
: mrFreetypeFont( rFont )
{
}
void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
{
rSalGraphics.DrawServerFontLayout( *this, mrFreetypeFont );
}
bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
{
return mrFreetypeFont.GetLayoutEngine()->Layout(*this, rArgs);
}
void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
{
GenericSalLayout::AdjustLayout( rArgs );
// apply asian kerning if the glyphs are not already formatted
if( (rArgs.mnFlags & SalLayoutFlags::KerningAsian)
&& !(rArgs.mnFlags & SalLayoutFlags::Vertical) )
if( (rArgs.mpDXArray != nullptr) || (rArgs.mnLayoutWidth != 0) )
ApplyAsianKerning(rArgs.mrStr);
// insert kashidas where requested by the formatting array
if( (rArgs.mnFlags & SalLayoutFlags::KashidaJustification) && rArgs.mpDXArray )
{
int nKashidaIndex = mrFreetypeFont.GetGlyphIndex( 0x0640 );
if( nKashidaIndex != 0 )
{
const GlyphMetric& rGM = mrFreetypeFont.GetGlyphMetric( nKashidaIndex );
KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
// TODO: kashida-GSUB/GPOS
}
}
}
void ServerFontLayout::SetNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos,
bool bRightToLeft)
{
if (nCharPos < 0)
return;
using namespace ::com::sun::star;
if (!mxBreak.is())
mxBreak = vcl::unohelper::CreateBreakIterator();
lang::Locale aLocale(rArgs.maLanguageTag.getLocale());
//if position nCharPos is missing in the font, grab the entire grapheme and
//mark all glyphs as missing so the whole thing is rendered with the same
//font
sal_Int32 nDone;
sal_Int32 nGraphemeStartPos =
mxBreak->previousCharacters(rArgs.mrStr, nCharPos+1, aLocale,
i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
sal_Int32 nGraphemeEndPos =
mxBreak->nextCharacters(rArgs.mrStr, nCharPos, aLocale,
i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
rArgs.NeedFallback(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft);
}
std::ostream &operator <<(std::ostream& s, FreetypeFont* pFont)
{
#ifndef SAL_LOG_INFO
(void) pFont;
#else
FT_Face aFace = pFont->GetFtFace();
const char* pName = FT_Get_Postscript_Name(aFace);
if (pName)
s << pName;
else
s << pFont->GetFontFileName();
#endif
return s;
}
static hb_blob_t *getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
{
char pTagName[5];
pTagName[0] = (char)(nTableTag >> 24);
pTagName[1] = (char)(nTableTag >> 16);
pTagName[2] = (char)(nTableTag >> 8);
pTagName[3] = (char)(nTableTag);
pTagName[4] = 0;
FreetypeFont* pFont = static_cast<FreetypeFont*>(pUserData);
SAL_INFO("vcl.harfbuzz", "getFontTable(" << pFont << ", " << pTagName << ")");
sal_uLong nLength;
const unsigned char* pBuffer = pFont->GetTable(pTagName, &nLength);
hb_blob_t* pBlob = nullptr;
if (pBuffer != nullptr)
pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY, const_cast<unsigned char *>(pBuffer), nullptr);
return pBlob;
}
static hb_bool_t getFontGlyph(hb_font_t* /*font*/, void* pFontData,
hb_codepoint_t ch, hb_codepoint_t vs,
hb_codepoint_t* nGlyphIndex,
void* /*pUserData*/)
{
FreetypeFont* pFont = static_cast<FreetypeFont*>(pFontData);
*nGlyphIndex = pFont->GetRawGlyphIndex(ch, vs);
// tdf#89231 if the font is missing non-breaking space, then use a normal space
if (*nGlyphIndex == 0 && ch == 0x202F && !vs)
*nGlyphIndex = pFont->GetRawGlyphIndex(' ');
return *nGlyphIndex != 0;
}
static hb_position_t getGlyphAdvanceH(hb_font_t* /*font*/, void* pFontData,
hb_codepoint_t nGlyphIndex,
void* /*pUserData*/)
{
FreetypeFont* pFont = static_cast<FreetypeFont*>(pFontData);
const GlyphMetric& rGM = pFont->GetGlyphMetric(nGlyphIndex);
return rGM.GetCharWidth() << 6;
}
#if !HB_VERSION_ATLEAST(1, 1, 2)
static hb_position_t getGlyphAdvanceV(hb_font_t* /*font*/, void* /*pFontData*/,
hb_codepoint_t /*nGlyphIndex*/,
void* /*pUserData*/)
{
// XXX: vertical metrics
return 0;
}
static hb_bool_t getGlyphOriginH(hb_font_t* /*font*/, void* /*pFontData*/,
hb_codepoint_t /*nGlyphIndex*/,
hb_position_t* /*x*/, hb_position_t* /*y*/,
void* /*pUserData*/)
{
// the horizontal origin is always (0, 0)
return true;
}
static hb_bool_t getGlyphOriginV(hb_font_t* /*font*/, void* /*pFontData*/,
hb_codepoint_t /*nGlyphIndex*/,
hb_position_t* /*x*/, hb_position_t* /*y*/,
void* /*pUserData*/)
{
// XXX: vertical origin
return true;
}
#endif
static hb_position_t getGlyphKerningH(hb_font_t* /*font*/, void* pFontData,
hb_codepoint_t nGlyphIndex1, hb_codepoint_t nGlyphIndex2,
void* /*pUserData*/)
{
// This callback is for old style 'kern' table, GPOS kerning is handled by HarfBuzz directly
FreetypeFont* pFont = static_cast<FreetypeFont*>(pFontData);
FT_Face aFace = pFont->GetFtFace();
SAL_INFO("vcl.harfbuzz", "getGlyphKerningH(" << pFont << ", " << nGlyphIndex1 << ", " << nGlyphIndex2 << ")");
FT_Error error;
FT_Vector kerning;
hb_position_t ret;
error = FT_Get_Kerning(aFace, nGlyphIndex1, nGlyphIndex2, FT_KERNING_DEFAULT, &kerning);
if (error)
ret = 0;
else
ret = kerning.x;
return ret;
}
#if !HB_VERSION_ATLEAST(1, 1, 2)
static hb_position_t getGlyphKerningV(hb_font_t* /*font*/, void* /*pFontData*/,
hb_codepoint_t /*nGlyphIndex1*/, hb_codepoint_t /*nGlyphIndex2*/,
void* /*pUserData*/)
{
// XXX vertical kerning
return 0;
}
#endif
static hb_bool_t getGlyphExtents(hb_font_t* /*font*/, void* pFontData,
hb_codepoint_t nGlyphIndex,
hb_glyph_extents_t* pExtents,
void* /*pUserData*/)
{
FreetypeFont* pFont = static_cast<FreetypeFont*>(pFontData);
FT_Face aFace = pFont->GetFtFace();
SAL_INFO("vcl.harfbuzz", "getGlyphExtents(" << pFont << ", " << nGlyphIndex << ")");
FT_Error error;
error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
if (!error)
{
pExtents->x_bearing = aFace->glyph->metrics.horiBearingX;
pExtents->y_bearing = aFace->glyph->metrics.horiBearingY;
pExtents->width = aFace->glyph->metrics.width;
pExtents->height = -aFace->glyph->metrics.height;
}
return !error;
}
static hb_bool_t getGlyphContourPoint(hb_font_t* /*font*/, void* pFontData,
hb_codepoint_t nGlyphIndex, unsigned int nPointIndex,
hb_position_t *x, hb_position_t *y,
void* /*pUserData*/)
{
bool ret = false;
FreetypeFont* pFont = static_cast<FreetypeFont*>(pFontData);
FT_Face aFace = pFont->GetFtFace();
SAL_INFO("vcl.harfbuzz", "getGlyphContourPoint(" << pFont << ", " << nGlyphIndex << ", " << nPointIndex << ")");
FT_Error error;
error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
if (!error)
{
if (aFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
{
if (nPointIndex < (unsigned int) aFace->glyph->outline.n_points)
{
*x = aFace->glyph->outline.points[nPointIndex].x;
*y = aFace->glyph->outline.points[nPointIndex].y;
ret = true;
}
}
}
return ret;
}
static hb_font_funcs_t* getFontFuncs()
{
static hb_font_funcs_t* funcs = hb_font_funcs_create();
hb_font_funcs_set_glyph_func (funcs, getFontGlyph, nullptr, nullptr);
hb_font_funcs_set_glyph_h_advance_func (funcs, getGlyphAdvanceH, nullptr, nullptr);
hb_font_funcs_set_glyph_h_kerning_func (funcs, getGlyphKerningH, nullptr, nullptr);
hb_font_funcs_set_glyph_extents_func (funcs, getGlyphExtents, nullptr, nullptr);
hb_font_funcs_set_glyph_contour_point_func (funcs, getGlyphContourPoint, nullptr, nullptr);
#if !HB_VERSION_ATLEAST(1, 1, 2)
hb_font_funcs_set_glyph_v_advance_func (funcs, getGlyphAdvanceV, nullptr, nullptr);
hb_font_funcs_set_glyph_h_origin_func (funcs, getGlyphOriginH, nullptr, nullptr);
hb_font_funcs_set_glyph_v_origin_func (funcs, getGlyphOriginV, nullptr, nullptr);
hb_font_funcs_set_glyph_v_kerning_func (funcs, getGlyphKerningV, nullptr, nullptr);
#endif
return funcs;
}
#if !HB_VERSION_ATLEAST(1, 1, 0)
// Disabled Unicode compatibility decomposition, see fdo#66715
static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t* /*ufuncs*/,
hb_codepoint_t /*u*/,
hb_codepoint_t* /*decomposed*/,
void* /*user_data*/)
{
return 0;
}
static hb_unicode_funcs_t* getUnicodeFuncs()
{
static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, unicodeDecomposeCompatibility, nullptr, nullptr);
return ufuncs;
}
#endif
class HbLayoutEngine : public ServerFontLayoutEngine
{
private:
hb_script_t maHbScript;
hb_face_t* mpHbFace;
int mnUnitsPerEM;
public:
explicit HbLayoutEngine(FreetypeFont&);
virtual ~HbLayoutEngine() override;
virtual bool Layout(ServerFontLayout&, ImplLayoutArgs&) override;
};
HbLayoutEngine::HbLayoutEngine(FreetypeFont& rFreetypeFont)
: maHbScript(HB_SCRIPT_INVALID),
mpHbFace(nullptr),
mnUnitsPerEM(0)
{
FT_Face aFtFace = rFreetypeFont.GetFtFace();
mnUnitsPerEM = rFreetypeFont.GetEmUnits();
mpHbFace = hb_face_create_for_tables(getFontTable, &rFreetypeFont, nullptr);
hb_face_set_index(mpHbFace, aFtFace->face_index);
hb_face_set_upem(mpHbFace, mnUnitsPerEM);
}
HbLayoutEngine::~HbLayoutEngine()
{
hb_face_destroy(mpHbFace);
}
struct HbScriptRun
{
int32_t mnMin;
int32_t mnEnd;
hb_script_t maScript;
HbScriptRun(int32_t nMin, int32_t nEnd, UScriptCode aScript)
: mnMin(nMin), mnEnd(nEnd),
maScript(hb_icu_script_to_script(aScript))
{}
};
typedef std::vector<HbScriptRun> HbScriptRuns;
namespace vcl {
struct Run
{
int32_t nStart;
int32_t nEnd;
UScriptCode nCode;
Run(int32_t nStart_, int32_t nEnd_, UScriptCode nCode_)
: nStart(nStart_), nEnd(nEnd_), nCode(nCode_)
{}
};
class TextLayoutCache
{
public:
std::vector<vcl::Run> runs;
TextLayoutCache(sal_Unicode const* pStr, sal_Int32 const nEnd)
{
vcl::ScriptRun aScriptRun(
reinterpret_cast<const UChar *>(pStr),
nEnd);
while (aScriptRun.next())
{
runs.push_back(Run(aScriptRun.getScriptStart(),
aScriptRun.getScriptEnd(), aScriptRun.getScriptCode()));
}
}
};
}
std::shared_ptr<vcl::TextLayoutCache> ServerFontLayout::CreateTextLayoutCache(
OUString const& rString) const
{
return std::make_shared<vcl::TextLayoutCache>(rString.getStr(), rString.getLength());
}
bool HbLayoutEngine::Layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
{
FreetypeFont& rFont = rLayout.GetFreetypeFont();
FT_Face aFtFace = rFont.GetFtFace();
SAL_INFO("vcl.harfbuzz", "layout(" << this << ",rArgs=" << rArgs << ")");
static hb_font_funcs_t* pHbFontFuncs = getFontFuncs();
hb_font_t *pHbFont = hb_font_create(mpHbFace);
hb_font_set_funcs(pHbFont, pHbFontFuncs, &rFont, nullptr);
hb_font_set_scale(pHbFont,
((uint64_t) aFtFace->size->metrics.x_scale * (uint64_t) mnUnitsPerEM) >> 16,
((uint64_t) aFtFace->size->metrics.y_scale * (uint64_t) mnUnitsPerEM) >> 16);
hb_font_set_ppem(pHbFont, aFtFace->size->metrics.x_ppem, aFtFace->size->metrics.y_ppem);
// allocate temporary arrays, note: round to even
int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos) | 15) + 1;
int32_t nVirtAdv = int32_t(aFtFace->size->metrics.height*rFont.GetStretch())>>6;
rLayout.Reserve(nGlyphCapacity);
const int nLength = rArgs.mrStr.getLength();
const sal_Unicode *pStr = rArgs.mrStr.getStr();
std::unique_ptr<vcl::TextLayoutCache> pNewScriptRun;
vcl::TextLayoutCache const* pTextLayout;
if (rArgs.m_pTextLayoutCache)
{
pTextLayout = rArgs.m_pTextLayoutCache; // use cache!
}
else
{
pNewScriptRun.reset(new vcl::TextLayoutCache(pStr, rArgs.mnEndCharPos));
pTextLayout = pNewScriptRun.get();
}
Point aCurrPos(0, 0);
while (true)
{
int nBidiMinRunPos, nBidiEndRunPos;
bool bRightToLeft;
if (!rArgs.GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
break;
// Find script subruns.
int nCurrentPos = nBidiMinRunPos;
HbScriptRuns aScriptSubRuns;
size_t k = 0;
for (; k < pTextLayout->runs.size(); ++k)
{
vcl::Run const& rRun(pTextLayout->runs[k]);
if (rRun.nStart <= nCurrentPos && nCurrentPos < rRun.nEnd)
{
break;
}
}
while (nCurrentPos < nBidiEndRunPos && k < pTextLayout->runs.size())
{
int32_t nMinRunPos = nCurrentPos;
int32_t nEndRunPos = std::min(pTextLayout->runs[k].nEnd, nBidiEndRunPos);
HbScriptRun aRun(nMinRunPos, nEndRunPos, pTextLayout->runs[k].nCode);
aScriptSubRuns.push_back(aRun);
nCurrentPos = nEndRunPos;
++k;
}
// RTL subruns should be reversed to ensure that final glyph order is
// correct.
if (bRightToLeft)
std::reverse(aScriptSubRuns.begin(), aScriptSubRuns.end());
for (HbScriptRuns::iterator it = aScriptSubRuns.begin(); it != aScriptSubRuns.end(); ++it)
{
int nMinRunPos = it->mnMin;
int nEndRunPos = it->mnEnd;
int nRunLen = nEndRunPos - nMinRunPos;
maHbScript = it->maScript;
// hb_language_from_string() accept ISO639-3 language tag except for Chinese.
LanguageTag &rTag = rArgs.maLanguageTag;
OString sLanguage = OUStringToOString( MsLangId::isChinese(rTag.getLanguageType()) ? rTag.getBcp47():rTag.getLanguage() , RTL_TEXTENCODING_UTF8 );
int nHbFlags = HB_BUFFER_FLAGS_DEFAULT;
if (nMinRunPos == 0)
nHbFlags |= HB_BUFFER_FLAG_BOT; /* Beginning-of-text */
if (nEndRunPos == nLength)
nHbFlags |= HB_BUFFER_FLAG_EOT; /* End-of-text */
hb_buffer_t *pHbBuffer = hb_buffer_create();
#if !HB_VERSION_ATLEAST(1, 1, 0)
static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs();
hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs);
#endif
hb_buffer_set_direction(pHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR);
hb_buffer_set_script(pHbBuffer, maHbScript);
hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), -1));
hb_buffer_set_flags(pHbBuffer, (hb_buffer_flags_t) nHbFlags);
hb_buffer_add_utf16(
pHbBuffer, reinterpret_cast<uint16_t const *>(pStr), nLength,
nMinRunPos, nRunLen);
hb_shape(pHbFont, pHbBuffer, nullptr, 0);
int nRunGlyphCount = hb_buffer_get_length(pHbBuffer);
hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer, nullptr);
hb_glyph_position_t *pHbPositions = hb_buffer_get_glyph_positions(pHbBuffer, nullptr);
for (int i = 0; i < nRunGlyphCount; ++i) {
int32_t nGlyphIndex = pHbGlyphInfos[i].codepoint;
int32_t nCharPos = pHbGlyphInfos[i].cluster;
// if needed request glyph fallback by updating LayoutArgs
if (!nGlyphIndex)
{
rLayout.SetNeedFallback(rArgs, nCharPos, bRightToLeft);
if (SalLayoutFlags::ForFallback & rArgs.mnFlags)
continue;
}
// apply vertical flags and glyph substitution
// XXX: Use HB_DIRECTION_TTB above and apply whatever flags magic
// FixupGlyphIndex() is doing, minus the GSUB part.
sal_Int32 indexUtf16 = nCharPos;
sal_UCS4 aChar = rArgs.mrStr.iterateCodePoints(&indexUtf16, 0);
nGlyphIndex = rFont.FixupGlyphIndex(nGlyphIndex, aChar);
bool bInCluster = false;
if (i > 0 && pHbGlyphInfos[i].cluster == pHbGlyphInfos[i - 1].cluster)
bInCluster = true;
long nGlyphFlags = 0;
if (bRightToLeft)
nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
if (bInCluster)
nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
// The whole IS_DIACRITIC concept is a stupid hack that was
// introduced ages ago to work around the utter brokenness of the
// way justification adjustments are applied (the DXArray fiasco).
// Since it is such a stupid hack, there is no sane way to directly
// map to concepts of the "outside" world, so we do some rather
// ugly hacks:
// * If the font has a GDEF table, we check for glyphs with mark
// glyph class which is sensible, except that some fonts
// (fdo#70968) assign mark class to spacing marks (which is wrong
// but usually harmless), so we try to sniff what HarfBuzz thinks
// about this glyph by checking if it gives it a zero advance
// width.
// * If the font has no GDEF table, we just check if the glyph has
// zero advance width, but this is stupid and can be wrong. A
// better way would to check the character's Unicode combining
// class, but unfortunately HarfBuzz gives combining marks the
// cluster value of its base character, so nCharPos will be
// pointing to the wrong character (but HarfBuzz might change
// this in the future).
// Newer versions of HarfBuzz can control this behaviour with
// hb_buffer_set_cluster_level().
bool bDiacritic = false;
if (hb_ot_layout_has_glyph_classes(mpHbFace))
{
// the font has GDEF table
bool bMark = hb_ot_layout_get_glyph_class(mpHbFace, nGlyphIndex) == HB_OT_LAYOUT_GLYPH_CLASS_MARK;
if (bMark && pHbPositions[i].x_advance == 0)
bDiacritic = true;
}
else
{
// the font lacks GDEF table
if (pHbPositions[i].x_advance == 0)
bDiacritic = true;
}
if (bDiacritic)
nGlyphFlags |= GlyphItem::IS_DIACRITIC;
int32_t nXOffset = pHbPositions[i].x_offset >> 6;
int32_t nYOffset = pHbPositions[i].y_offset >> 6;
int32_t nXAdvance = pHbPositions[i].x_advance >> 6;
int32_t nYAdvance = pHbPositions[i].y_advance >> 6;
if ( nGlyphIndex & GF_ROTMASK )
nXAdvance = nVirtAdv;
Point aNewPos = Point(aCurrPos.X() + nXOffset, -(aCurrPos.Y() + nYOffset));
const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nXAdvance, nXOffset);
rLayout.AppendGlyph(aGI);
aCurrPos.X() += nXAdvance;
aCurrPos.Y() += nYAdvance;
}
hb_buffer_destroy(pHbBuffer);
}
}
hb_font_destroy(pHbFont);
// sort glyphs in visual order
// and then in logical order (e.g. diacritics after cluster start)
// XXX: why?
rLayout.SortGlyphItems();
// determine need for kashida justification
if((rArgs.mpDXArray || rArgs.mnLayoutWidth)
&& ((maHbScript == HB_SCRIPT_ARABIC) || (maHbScript == HB_SCRIPT_SYRIAC)))
rArgs.mnFlags |= SalLayoutFlags::KashidaJustification;
return true;
}
ServerFontLayoutEngine* FreetypeFont::GetLayoutEngine()
{
if (!mpLayoutEngine) {
mpLayoutEngine = new HbLayoutEngine(*this);
}
return mpLayoutEngine;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+5 -60
View File
@@ -640,48 +640,6 @@ bool PspFontLayout::LayoutText( ImplLayoutArgs& rArgs )
return (aOldGlyphId != GF_DROPPED);
}
class PspServerFontLayout : public ServerFontLayout
{
public:
PspServerFontLayout( psp::PrinterGfx&, FreetypeFont& rFont, const ImplLayoutArgs& rArgs );
virtual void InitFont() const override;
const sal_Unicode* getTextPtr() const { return maText.getStr() - mnMinCharPos; }
int getMinCharPos() const { return mnMinCharPos; }
int getMaxCharPos() const { return mnMinCharPos+maText.getLength()-1; }
private:
::psp::PrinterGfx& mrPrinterGfx;
sal_IntPtr mnFontID;
int mnFontHeight;
int mnFontWidth;
bool mbVertical;
bool mbArtItalic;
bool mbArtBold;
OUString maText;
int mnMinCharPos;
};
PspServerFontLayout::PspServerFontLayout( ::psp::PrinterGfx& rGfx, FreetypeFont& rFont, const ImplLayoutArgs& rArgs )
: ServerFontLayout( rFont ),
mrPrinterGfx( rGfx )
{
mnFontID = mrPrinterGfx.GetFontID();
mnFontHeight = mrPrinterGfx.GetFontHeight();
mnFontWidth = mrPrinterGfx.GetFontWidth();
mbVertical = mrPrinterGfx.GetFontVertical();
mbArtItalic = mrPrinterGfx.GetArtificialItalic();
mbArtBold = mrPrinterGfx.GetArtificialBold();
const sal_Unicode *pStr = rArgs.mrStr.getStr();
maText = OUString( pStr + rArgs.mnMinCharPos, rArgs.mnEndCharPos - rArgs.mnMinCharPos+1 );
mnMinCharPos = rArgs.mnMinCharPos;
}
void PspServerFontLayout::InitFont() const
{
mrPrinterGfx.SetFont( mnFontID, mnFontHeight, mnFontWidth,
mnOrientation, mbVertical, mbArtItalic, mbArtBold );
}
class PspCommonSalLayout : public CommonSalLayout
{
public:
@@ -718,7 +676,7 @@ void PspCommonSalLayout::InitFont() const
mnOrientation, mbVertical, mbArtItalic, mbArtBold);
}
static void DrawPrinterLayout( const SalLayout& rLayout, ::psp::PrinterGfx& rGfx, bool bIsPspServerFontLayout )
static void DrawPrinterLayout( const SalLayout& rLayout, ::psp::PrinterGfx& rGfx)
{
const int nMaxGlyphs = 1;
sal_GlyphId aGlyphAry[ nMaxGlyphs ];
@@ -732,16 +690,6 @@ static void DrawPrinterLayout( const SalLayout& rLayout, ::psp::PrinterGfx& rGfx
const sal_Unicode* pText = nullptr;
int nMinCharPos = 0;
int nMaxCharPos = 0;
if (bIsPspServerFontLayout)
{
const PspServerFontLayout * pPspLayout = dynamic_cast<const PspServerFontLayout*>(&rLayout);
if (pPspLayout)
{
pText = pPspLayout->getTextPtr();
nMinCharPos = pPspLayout->getMinCharPos();
nMaxCharPos = pPspLayout->getMaxCharPos();
}
}
for( int nStart = 0;; )
{
int nGlyphCount = rLayout.GetNextGlyphs( nMaxGlyphs, aGlyphAry, aPos, nStart, aWidthAry, pText ? aCharPosAry : nullptr );
@@ -773,18 +721,18 @@ void PspFontLayout::InitFont() const
void PspFontLayout::DrawText( SalGraphics& ) const
{
DrawPrinterLayout( *this, mrPrinterGfx, false );
DrawPrinterLayout(*this, mrPrinterGfx);
}
void GenPspGraphics::DrawServerFontLayout( const GenericSalLayout& rLayout, const FreetypeFont& /*unused*/ )
{
// print complex text
DrawPrinterLayout( rLayout, *m_pPrinterGfx, true );
DrawPrinterLayout(rLayout, *m_pPrinterGfx);
}
void GenPspGraphics::DrawSalLayout(const CommonSalLayout& rLayout)
{
DrawPrinterLayout(rLayout, *m_pPrinterGfx, false);
DrawPrinterLayout(rLayout, *m_pPrinterGfx);
}
const FontCharMapRef GenPspGraphics::GetFontCharMap() const
@@ -1012,10 +960,7 @@ SalLayout* GenPspGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLe
if( m_pFreetypeFont[ nFallbackLevel ]
&& !(rArgs.mnFlags & SalLayoutFlags::DisableGlyphProcessing) )
{
if (SalLayout::UseCommonLayout())
pLayout = new PspCommonSalLayout(*m_pPrinterGfx, *m_pFreetypeFont[nFallbackLevel]);
else
pLayout = new PspServerFontLayout( *m_pPrinterGfx, *m_pFreetypeFont[nFallbackLevel], rArgs );
pLayout = new PspCommonSalLayout(*m_pPrinterGfx, *m_pFreetypeFont[nFallbackLevel]);
}
else
pLayout = new PspFontLayout( *m_pPrinterGfx );