blob: 3b271df97a6277987cc8495d1f89d243eecf9e86 [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.
*
*************************************************************/
#define INCL_GRE_STRINGS
#define INCL_GPI
#define INCL_DOS
#include <string.h>
//#include <stdlib.h>
//#include <math.h>
#include <svpm.h>
#include <vcl/sysdata.hxx>
#include "tools/svwin.h"
#include "os2/saldata.hxx"
#include "os2/salgdi.h"
#include "vcl/svapp.hxx"
#include "outfont.hxx"
#include "vcl/font.hxx"
#include "fontsubset.hxx"
#include "sallayout.hxx"
#include "rtl/logfile.hxx"
#include "rtl/tencinfo.h"
#include "rtl/textcvt.h"
#include "rtl/bootstrap.hxx"
#include "osl/module.h"
#include "osl/file.hxx"
#include "osl/thread.hxx"
#include "osl/process.h"
#include "tools/poly.hxx"
#include "tools/debug.hxx"
#include "tools/stream.hxx"
#include "basegfx/polygon/b2dpolygon.hxx"
#include "basegfx/polygon/b2dpolypolygon.hxx"
#include "basegfx/matrix/b2dhommatrix.hxx"
#ifndef __H_FT2LIB
#include <os2/wingdi.h>
#include <ft2lib.h>
#endif
#include "sft.hxx"
#ifdef GCP_KERN_HACK
#include <algorithm>
#endif
using namespace vcl;
// -----------
// - Inlines -
// -----------
inline W32FIXED FixedFromDouble( double d )
{
const long l = (long) ( d * 65536. );
return *(W32FIXED*) &l;
}
// -----------------------------------------------------------------------
inline int IntTimes256FromFixed(W32FIXED f)
{
int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
return nFixedTimes256;
}
// -----------
// - Defines -
// -----------
// this is a special codepage code, used to identify OS/2 symbol font.
#define SYMBOL_CHARSET 65400
// =======================================================================
UniString ImplSalGetUniString( const sal_Char* pStr, xub_StrLen nLen = STRING_LEN)
{
return UniString( pStr, nLen, gsl_getSystemTextEncoding(),
RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT );
}
// =======================================================================
static USHORT ImplSalToCharSet( CharSet eCharSet )
{
// !!! Fuer DBCS-Systeme muss dieser Code auskommentiert werden und 0
// !!! zurueckgegeben werden, solange die DBCS-Charsets nicht
// !!! durchgereicht werden
switch ( eCharSet )
{
case RTL_TEXTENCODING_IBM_437:
return 437;
case RTL_TEXTENCODING_IBM_850:
return 850;
case RTL_TEXTENCODING_IBM_860:
return 860;
case RTL_TEXTENCODING_IBM_861:
return 861;
case RTL_TEXTENCODING_IBM_863:
return 863;
case RTL_TEXTENCODING_IBM_865:
return 865;
case RTL_TEXTENCODING_MS_1252:
return 1004;
case RTL_TEXTENCODING_SYMBOL:
return 65400;
}
return 0;
}
// -----------------------------------------------------------------------
static CharSet ImplCharSetToSal( USHORT usCodePage )
{
switch ( usCodePage )
{
case 437:
return RTL_TEXTENCODING_IBM_437;
case 850:
return RTL_TEXTENCODING_IBM_850;
case 860:
return RTL_TEXTENCODING_IBM_860;
case 861:
return RTL_TEXTENCODING_IBM_861;
case 863:
return RTL_TEXTENCODING_IBM_863;
case 865:
return RTL_TEXTENCODING_IBM_865;
case 1004:
return RTL_TEXTENCODING_MS_1252;
case 65400:
return RTL_TEXTENCODING_SYMBOL;
}
return RTL_TEXTENCODING_DONTKNOW;
}
// -----------------------------------------------------------------------
static FontFamily ImplFamilyToSal( PM_BYTE bFamilyType )
{
switch ( bFamilyType )
{
case 4:
return FAMILY_DECORATIVE;
case 3:
return FAMILY_SCRIPT;
}
return FAMILY_DONTKNOW;
}
// -----------------------------------------------------------------------
static FontWeight ImplWeightToSal( USHORT nWeight )
{
// Falls sich jemand an die alte Doku gehalten hat
if ( nWeight > 999 )
nWeight /= 1000;
switch ( nWeight )
{
case 1:
return WEIGHT_THIN;
case 2:
return WEIGHT_ULTRALIGHT;
case 3:
return WEIGHT_LIGHT;
case 4:
return WEIGHT_SEMILIGHT;
case 5:
return WEIGHT_NORMAL;
case 6:
return WEIGHT_SEMIBOLD;
case 7:
return WEIGHT_BOLD;
case 8:
return WEIGHT_ULTRABOLD;
case 9:
return WEIGHT_BLACK;
}
return WEIGHT_DONTKNOW;
}
// -----------------------------------------------------------------------
static UniString ImpStyleNameToSal( const char* pFamilyName,
const char* pFaceName,
USHORT nLen )
{
if ( !nLen )
nLen = strlen(pFamilyName);
// strip FamilyName from FaceName
if ( strncmp( pFamilyName, pFaceName, nLen ) == 0 )
{
USHORT nFaceLen = (USHORT)strlen( pFaceName+nLen );
// Ist Facename laenger, schneiden wir den FamilyName ab
if ( nFaceLen > 1 )
return UniString( pFaceName+(nLen+1), gsl_getSystemTextEncoding());
else
return UniString();
}
else
return UniString( pFaceName, gsl_getSystemTextEncoding());
}
// -----------------------------------------------------------------------
inline FontPitch ImplLogPitchToSal( PM_BYTE fsType )
{
if ( fsType & FM_TYPE_FIXED )
return PITCH_FIXED;
else
return PITCH_VARIABLE;
}
// -----------------------------------------------------------------------
inline PM_BYTE ImplPitchToWin( FontPitch ePitch )
{
if ( ePitch == PITCH_FIXED )
return FM_TYPE_FIXED;
//else if ( ePitch == PITCH_VARIABLE )
return 0;
}
// -----------------------------------------------------------------------
static ImplDevFontAttributes Os2Font2DevFontAttributes( const PFONTMETRICS pFontMetric)
{
ImplDevFontAttributes aDFA;
// get font face attributes
aDFA.meFamily = ImplFamilyToSal( pFontMetric->panose.bFamilyType);
aDFA.meWidthType = WIDTH_DONTKNOW;
aDFA.meWeight = ImplWeightToSal( pFontMetric->usWeightClass);
aDFA.meItalic = (pFontMetric->fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE;
aDFA.mePitch = ImplLogPitchToSal( pFontMetric->fsType );
aDFA.mbSymbolFlag = (pFontMetric->usCodePage == SYMBOL_CHARSET);
// get the font face name
// the maName field stores the font name without the style, so under OS/2
// we must use the family name
aDFA.maName = UniString( pFontMetric->szFamilyname, gsl_getSystemTextEncoding());
aDFA.maStyleName = ImpStyleNameToSal( pFontMetric->szFamilyname,
pFontMetric->szFacename,
strlen( pFontMetric->szFamilyname) );
// get device specific font attributes
aDFA.mbOrientation = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0;
aDFA.mbDevice = (pFontMetric->fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE;
aDFA.mbEmbeddable = false;
aDFA.mbSubsettable = false;
DWORD fontType = Ft2QueryFontType( 0, pFontMetric->szFamilyname);
if( fontType == FT2_FONTTYPE_TRUETYPE && !aDFA.mbDevice)
aDFA.mbSubsettable = true;
// for now we can only embed Type1 fonts
if( fontType == FT2_FONTTYPE_TYPE1 )
aDFA.mbEmbeddable = true;
// heuristics for font quality
// - standard-type1 > opentypeTT > truetype > non-standard-type1 > raster
// - subsetting > embedding > none
aDFA.mnQuality = 0;
if( fontType == FT2_FONTTYPE_TRUETYPE )
aDFA.mnQuality += 50;
if( aDFA.mbSubsettable )
aDFA.mnQuality += 200;
else if( aDFA.mbEmbeddable )
aDFA.mnQuality += 100;
// #i38665# prefer Type1 versions of the standard postscript fonts
if( aDFA.mbEmbeddable )
{
if( aDFA.maName.EqualsAscii( "AvantGarde" )
|| aDFA.maName.EqualsAscii( "Bookman" )
|| aDFA.maName.EqualsAscii( "Courier" )
|| aDFA.maName.EqualsAscii( "Helvetica" )
|| aDFA.maName.EqualsAscii( "NewCenturySchlbk" )
|| aDFA.maName.EqualsAscii( "Palatino" )
|| aDFA.maName.EqualsAscii( "Symbol" )
|| aDFA.maName.EqualsAscii( "Times" )
|| aDFA.maName.EqualsAscii( "ZapfChancery" )
|| aDFA.maName.EqualsAscii( "ZapfDingbats" ) )
aDFA.mnQuality += 500;
}
// TODO: add alias names
return aDFA;
}
// =======================================================================
// raw font data with a scoped lifetime
class RawFontData
{
public:
explicit RawFontData( HDC, DWORD nTableTag=0 );
~RawFontData() { delete[] mpRawBytes; }
const unsigned char* get() const { return mpRawBytes; }
const unsigned char* steal() { unsigned char* p = mpRawBytes; mpRawBytes = NULL; return p; }
const int size() const { return mnByteCount; }
private:
unsigned char* mpRawBytes;
int mnByteCount;
};
RawFontData::RawFontData( HPS hPS, DWORD nTableTag )
: mpRawBytes( NULL )
, mnByteCount( 0 )
{
// get required size in bytes
mnByteCount = ::Ft2GetFontData( hPS, nTableTag, 0, NULL, 0 );
if( mnByteCount == FT2_ERROR )
return;
else if( !mnByteCount )
return;
// allocate the array
mpRawBytes = new unsigned char[ mnByteCount ];
// get raw data in chunks small enough for GetFontData()
int nRawDataOfs = 0;
DWORD nMaxChunkSize = 0x100000;
for(;;)
{
// calculate remaining raw data to get
DWORD nFDGet = mnByteCount - nRawDataOfs;
if( nFDGet <= 0 )
break;
// #i56745# limit GetFontData requests
if( nFDGet > nMaxChunkSize )
nFDGet = nMaxChunkSize;
const DWORD nFDGot = ::Ft2GetFontData( hPS, nTableTag, nRawDataOfs,
(void*)(mpRawBytes + nRawDataOfs), nFDGet );
if( !nFDGot )
break;
else if( nFDGot != FT2_ERROR )
nRawDataOfs += nFDGot;
else
{
// was the chunk too big? reduce it
nMaxChunkSize /= 2;
if( nMaxChunkSize < 0x10000 )
break;
}
}
// cleanup if the raw data is incomplete
if( nRawDataOfs != mnByteCount )
{
delete[] mpRawBytes;
mpRawBytes = NULL;
}
}
// -----------------------------------------------------------------------
// =======================================================================
ImplOs2FontData::ImplOs2FontData( PFONTMETRICS _pFontMetric,
int nHeight, PM_BYTE nPitchAndFamily )
: ImplFontData( Os2Font2DevFontAttributes(_pFontMetric), 0 ),
pFontMetric( _pFontMetric ),
meOs2CharSet( _pFontMetric->usCodePage),
mnPitchAndFamily( nPitchAndFamily ),
mpFontCharSets( NULL ),
mpUnicodeMap( NULL ),
mbDisableGlyphApi( false ),
mbHasKoreanRange( false ),
mbHasCJKSupport( false ),
mbAliasSymbolsLow( false ),
mbAliasSymbolsHigh( false ),
mnId( 0 )
{
SetBitmapSize( 0, nHeight );
}
// -----------------------------------------------------------------------
ImplOs2FontData::~ImplOs2FontData()
{
delete[] mpFontCharSets;
if( mpUnicodeMap )
mpUnicodeMap->DeReference();
}
// -----------------------------------------------------------------------
sal_IntPtr ImplOs2FontData::GetFontId() const
{
return mnId;
}
// -----------------------------------------------------------------------
void ImplOs2FontData::UpdateFromHPS( HPS hPS ) const
{
// short circuit if already initialized
if( mpUnicodeMap != NULL )
return;
ReadCmapTable( hPS );
ReadOs2Table( hPS );
// even if the font works some fonts have problems with the glyph API
// => the heuristic below tries to figure out which fonts have the problem
DWORD fontType = Ft2QueryFontType( 0, pFontMetric->szFacename);
if( fontType != FT2_FONTTYPE_TRUETYPE
&& (pFontMetric->fsDefn & FM_DEFN_GENERIC) == 0)
mbDisableGlyphApi = true;
}
// -----------------------------------------------------------------------
bool ImplOs2FontData::HasGSUBstitutions( HPS hPS ) const
{
if( !mbGsubRead )
ReadGsubTable( hPS );
return !maGsubTable.empty();
}
// -----------------------------------------------------------------------
bool ImplOs2FontData::IsGSUBstituted( sal_Ucs cChar ) const
{
return( maGsubTable.find( cChar ) != maGsubTable.end() );
}
// -----------------------------------------------------------------------
const ImplFontCharMap* ImplOs2FontData::GetImplFontCharMap() const
{
mpUnicodeMap->AddReference();
return mpUnicodeMap;
}
// -----------------------------------------------------------------------
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 inline DWORD CalcTag( const char p[4]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }
void ImplOs2FontData::ReadOs2Table( HPS hPS ) const
{
const DWORD Os2Tag = CalcTag( "OS/2" );
DWORD nLength = Ft2GetFontData( hPS, Os2Tag, 0, NULL, 0 );
if( (nLength == FT2_ERROR) || !nLength )
return;
std::vector<unsigned char> aOS2map( nLength );
unsigned char* pOS2map = &aOS2map[0];
DWORD nRC = Ft2GetFontData( hPS, Os2Tag, 0, pOS2map, nLength );
sal_uInt32 nVersion = GetUShort( pOS2map );
if ( nVersion >= 0x0001 && nLength >= 58 )
{
// We need at least version 0x0001 (TrueType rev 1.66)
// to have access to the needed struct members.
sal_uInt32 ulUnicodeRange1 = GetUInt( pOS2map + 42 );
sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 );
#if 0
sal_uInt32 ulUnicodeRange3 = GetUInt( pOS2map + 50 );
sal_uInt32 ulUnicodeRange4 = GetUInt( pOS2map + 54 );
#endif
// Check for CJK capabilities of the current font
mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000);
mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000)
| (ulUnicodeRange2 & 0x01100000);
mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000);
}
}
// -----------------------------------------------------------------------
void ImplOs2FontData::ReadGsubTable( HPS hPS ) const
{
mbGsubRead = true;
// check the existence of a GSUB table
const DWORD GsubTag = CalcTag( "GSUB" );
DWORD nRC = Ft2GetFontData( hPS, GsubTag, 0, NULL, 0 );
if( (nRC == FT2_ERROR) || !nRC )
return;
// parse the GSUB table through sft
// TODO: parse it directly
// sft needs the full font file data => get it
const RawFontData aRawFontData( hPS );
if( !aRawFontData.get() )
return;
// open font file
sal_uInt32 nFaceNum = 0;
if( !*aRawFontData.get() ) // TTC candidate
nFaceNum = ~0U; // indicate "TTC font extracts only"
TrueTypeFont* pTTFont = NULL;
::OpenTTFontBuffer( (void*)aRawFontData.get(), aRawFontData.size(), nFaceNum, &pTTFont );
if( !pTTFont )
return;
// add vertically substituted characters to list
static const sal_Unicode aGSUBCandidates[] = {
0x0020, 0x0080, // ASCII
0x2000, 0x2600, // misc
0x3000, 0x3100, // CJK punctutation
0x3300, 0x3400, // squared words
0xFF00, 0xFFF0, // halfwidth|fullwidth forms
0 };
for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 )
for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar )
if( ::MapChar( pTTFont, cChar, 0 ) != ::MapChar( pTTFont, cChar, 1 ) )
maGsubTable.insert( cChar ); // insert GSUBbed unicodes
CloseTTFont( pTTFont );
}
// -----------------------------------------------------------------------
void ImplOs2FontData::ReadCmapTable( HPS hPS ) const
{
if( mpUnicodeMap != NULL )
return;
bool bIsSymbolFont = (meOs2CharSet == SYMBOL_CHARSET);
// get the CMAP table from the font which is selected into the DC
const DWORD nCmapTag = CalcTag( "cmap" );
const RawFontData aRawFontData( hPS, nCmapTag );
// parse the CMAP table if available
if( aRawFontData.get() ) {
CmapResult aResult;
ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult );
mbDisableGlyphApi |= aResult.mbRecoded;
aResult.mbSymbolic = bIsSymbolFont;
if( aResult.mnRangeCount > 0 )
mpUnicodeMap = new ImplFontCharMap( aResult );
}
if( !mpUnicodeMap )
mpUnicodeMap = ImplFontCharMap::GetDefaultMap( bIsSymbolFont );
mpUnicodeMap->AddReference();
}
// =======================================================================
void Os2SalGraphics::SetTextColor( SalColor nSalColor )
{
CHARBUNDLE cb;
cb.lColor = MAKE_SALCOLOR( SALCOLOR_RED( nSalColor ),
SALCOLOR_GREEN( nSalColor ),
SALCOLOR_BLUE( nSalColor ) );
// set default color attributes
Ft2SetAttrs( mhPS,
PRIM_CHAR,
CBB_COLOR,
0,
&cb );
}
// -----------------------------------------------------------------------
USHORT Os2SalGraphics::ImplDoSetFont( ImplFontSelectData* i_pFont, float& o_rFontScale, int nFallbackLevel)
{
#if OSL_DEBUG_LEVEL>10
debug_printf( "Os2SalGraphics::ImplDoSetFont mbPrinter=%d\n", mbPrinter);
#endif
ImplOs2FontData* pFontData = (ImplOs2FontData*)i_pFont->mpFontData;
PFONTMETRICS pFontMetric = NULL;
FATTRS aFAttrs;
PM_BOOL bOutline = FALSE;
APIRET rc;
memset( &aFAttrs, 0, sizeof( FATTRS ) );
aFAttrs.usRecordLength = sizeof( FATTRS );
aFAttrs.lMaxBaselineExt = i_pFont->mnHeight;
aFAttrs.lAveCharWidth = i_pFont->mnWidth;
// do we have a pointer to the FONTMETRICS of the selected font? -> use it!
if ( pFontData )
{
pFontMetric = pFontData->GetFontMetrics();
bOutline = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0;
// use match&registry fields to get correct match
aFAttrs.lMatch = pFontMetric->lMatch;
aFAttrs.idRegistry = pFontMetric->idRegistry;
aFAttrs.usCodePage = pFontMetric->usCodePage;
if ( bOutline )
{
aFAttrs.fsFontUse |= FATTR_FONTUSE_OUTLINE;
if ( i_pFont->mnOrientation )
aFAttrs.fsFontUse |= FATTR_FONTUSE_TRANSFORMABLE;
}
else
{
aFAttrs.lMaxBaselineExt = pFontMetric->lMaxBaselineExt;
aFAttrs.lAveCharWidth = pFontMetric->lAveCharWidth;
}
}
#if OSL_DEBUG_LEVEL>1
if (pFontMetric->szFacename[0] == 'D') {
rc = 0; // debugger breakpoint
}
#endif
// use family name for outline fonts
if ( mbPrinter ) {
// use font face name for printers because otherwise ft2lib will fail
// to select the correct font for GPI (ticket#117)
strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) );
} else if ( !pFontMetric) {
// use OOo name if fontmetrics not available!
ByteString aName( i_pFont->maName.GetToken( 0 ), gsl_getSystemTextEncoding());
strncpy( (char*)(aFAttrs.szFacename), aName.GetBuffer(), sizeof( aFAttrs.szFacename ) );
} else if ( bOutline) {
// use fontmetric family name for outline fonts
strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFamilyname, sizeof( aFAttrs.szFacename ) );
} else {
// use real font face name for bitmaps (WarpSans only)
strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) );
}
if ( i_pFont->meItalic != ITALIC_NONE )
aFAttrs.fsSelection |= FATTR_SEL_ITALIC;
if ( i_pFont->meWeight > WEIGHT_MEDIUM )
aFAttrs.fsSelection |= FATTR_SEL_BOLD;
#if OSL_DEBUG_LEVEL>1
if (pFontMetric->szFacename[0] == 'A') {
debug_printf( "Os2SalGraphics::SetFont hps %x lMatch '%d'\n", mhPS, pFontMetric->lMatch);
debug_printf( "Os2SalGraphics::SetFont hps %x fontmetrics facename '%s'\n", mhPS, pFontMetric->szFacename);
debug_printf( "Os2SalGraphics::SetFont hps %x fattrs facename '%s'\n", mhPS, aFAttrs.szFacename);
debug_printf( "Os2SalGraphics::SetFont hps %x fattrs height '%d'\n", mhPS, aFAttrs.lMaxBaselineExt);
debug_printf( "Os2SalGraphics::SetFont hps %x fattrs width '%d'\n", mhPS, aFAttrs.lAveCharWidth);
}
#endif
// set default font
rc = Ft2SetCharSet( mhPS, LCID_DEFAULT);
// delete selected font
rc = Ft2DeleteSetId( mhPS, nFallbackLevel + LCID_BASE);
// create new logical font
if ( (rc=Ft2CreateLogFont( mhPS, NULL, nFallbackLevel + LCID_BASE, &aFAttrs)) == GPI_ERROR ) {
#if OSL_DEBUG_LEVEL>1
ERRORID nLastError = WinGetLastError( GetSalData()->mhAB );
debug_printf( "Os2SalGraphics::SetFont hps %x Ft2CreateLogFont failed err %x\n", mhPS, nLastError );
#endif
return SAL_SETFONT_REMOVEANDMATCHNEW;
}
#if OSL_DEBUG_LEVEL>1
// select new font
rc = Ft2SetCharSet( mhPS, nFallbackLevel + LCID_BASE);
// query fontmetric of new font
FONTMETRICS aOS2Metric = {0};
rc = Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
if (pFontMetric->szFacename[0] == 'D') {
debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs facename '%s'\n", aOS2Metric.szFacename);
debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs height '%d'\n", aOS2Metric.lMaxBaselineExt);
debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs width '%d'\n", aOS2Metric.lAveCharWidth);
}
#endif
// apply font sizing, rotation
CHARBUNDLE aBundle;
ULONG nAttrsDefault = 0;
ULONG nAttrs = CBB_SET;
aBundle.usSet = nFallbackLevel + LCID_BASE;
if ( bOutline )
{
nAttrs |= CBB_BOX;
aBundle.sizfxCell.cy = MAKEFIXED( i_pFont->mnHeight, 0 );
if ( !i_pFont->mnWidth )
{
LONG nXFontRes;
LONG nYFontRes;
LONG nHeight;
// Auf die Aufloesung achten, damit das Ergebnis auch auf
// Drucken mit 180*360 DPI stimmt. Ausserdem muss gerundet
// werden, da auf meinem OS2 beispielsweise als
// Bildschirmaufloesung 3618*3622 PixelPerMeter zurueck-
// gegeben wird
GetResolution( nXFontRes, nYFontRes );
nHeight = i_pFont->mnHeight;
nHeight *= nXFontRes;
nHeight += nYFontRes/2;
nHeight /= nYFontRes;
aBundle.sizfxCell.cx = MAKEFIXED( nHeight, 0 );
}
else
aBundle.sizfxCell.cx = MAKEFIXED( i_pFont->mnWidth, 0 );
}
// set orientation for outlinefonts
if ( i_pFont->mnOrientation )
{
if ( bOutline )
{
nAttrs |= CBB_ANGLE;
double alpha = (double)(i_pFont->mnOrientation);
alpha *= 0.0017453292; // *PI / 1800
mnOrientationY = (long) (1000.0 * sin( alpha ));
mnOrientationX = (long) (1000.0 * cos( alpha ));
aBundle.ptlAngle.x = mnOrientationX;
aBundle.ptlAngle.y = mnOrientationY;
}
else
{
mnOrientationX = 1;
mnOrientationY = 0;
nAttrs |= CBB_ANGLE;
aBundle.ptlAngle.x = 1;
aBundle.ptlAngle.y = 0;
}
}
else
{
mnOrientationX = 1;
mnOrientationY = 0;
nAttrs |= CBB_ANGLE;
aBundle.ptlAngle.x = 1;
aBundle.ptlAngle.y = 0;
}
rc = Ft2SetAttrs( mhPS, PRIM_CHAR, nAttrs, nAttrsDefault, &aBundle );
#if OSL_DEBUG_LEVEL>1
rc = Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
if (pFontMetric->szFacename[0] == 'D') {
debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs facename '%s'\n", aOS2Metric.szFacename);
debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs height '%d'\n", aOS2Metric.lMaxBaselineExt);
debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs width '%d'\n", aOS2Metric.lAveCharWidth);
}
#endif
return 0;
}
USHORT Os2SalGraphics::SetFont( ImplFontSelectData* pFont, int nFallbackLevel )
{
// return early if there is no new font
if( !pFont )
{
#if 0
// deselect still active font
if( mhDefFont )
Ft2SetCharSet( mhPS, mhDefFont );
// release no longer referenced font handles
for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
{
if( mhFonts[i] )
Ft2DeleteSetId( mhPS, mhFonts[i] );
mhFonts[ i ] = 0;
}
#endif
mhDefFont = 0;
return 0;
}
#if OSL_DEBUG_LEVEL>10
debug_printf( "Os2SalGraphics::SetFont\n");
#endif
DBG_ASSERT( pFont->mpFontData, "WinSalGraphics mpFontData==NULL");
mpOs2FontEntry[ nFallbackLevel ] = reinterpret_cast<ImplOs2FontEntry*>( pFont->mpFontEntry );
mpOs2FontData[ nFallbackLevel ] = static_cast<const ImplOs2FontData*>( pFont->mpFontData );
ImplDoSetFont( pFont, mfFontScale, nFallbackLevel);
if( !mhDefFont )
{
// keep default font
mhDefFont = nFallbackLevel + LCID_BASE;
}
else
{
// release no longer referenced font handles
for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
{
if( mhFonts[i] )
{
#if 0
Ft2DeleteSetId( mhPS, mhFonts[i] );
#endif
mhFonts[i] = 0;
}
}
}
// store new font in correct layer
mhFonts[ nFallbackLevel ] = nFallbackLevel + LCID_BASE;
// now the font is live => update font face
if( mpOs2FontData[ nFallbackLevel ] )
mpOs2FontData[ nFallbackLevel ]->UpdateFromHPS( mhPS );
if( !nFallbackLevel )
{
mbFontKernInit = TRUE;
if ( mpFontKernPairs )
{
delete[] mpFontKernPairs;
mpFontKernPairs = NULL;
}
mnFontKernPairCount = 0;
}
// some printers have higher internal resolution, so their
// text output would be different from what we calculated
// => suggest DrawTextArray to workaround this problem
if ( mbPrinter )
return SAL_SETFONT_USEDRAWTEXTARRAY;
else
return 0;
}
// -----------------------------------------------------------------------
void Os2SalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel )
{
FONTMETRICS aOS2Metric;
Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
// @TODO sync , int nFallbackLevel in 340
#if OSL_DEBUG_LEVEL>1
debug_printf( "Os2SalGraphics::GetFontMetric hps %x\n", mhPS);
if (aOS2Metric.szFacename[0] == 'A') {
debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics facename '%s'\n", mhPS, aOS2Metric.szFacename);
debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics lMatch '%d'\n", mhPS, aOS2Metric.lMatch);
}
#endif
pMetric->maName = UniString( aOS2Metric.szFamilyname, gsl_getSystemTextEncoding());
pMetric->maStyleName = ImpStyleNameToSal( aOS2Metric.szFamilyname,
aOS2Metric.szFacename,
strlen( aOS2Metric.szFamilyname ) );
// device independent font attributes
pMetric->meFamily = ImplFamilyToSal( aOS2Metric.panose.bFamilyType);
pMetric->mbSymbolFlag = (aOS2Metric.usCodePage == SYMBOL_CHARSET);
pMetric->meWeight = ImplWeightToSal( aOS2Metric.usWeightClass );
pMetric->mePitch = ImplLogPitchToSal( aOS2Metric.fsType );
pMetric->meItalic = (aOS2Metric.fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE;
pMetric->mnSlant = 0;
// device dependend font attributes
pMetric->mbDevice = (aOS2Metric.fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE;
pMetric->mbScalableFont = (aOS2Metric.fsDefn & FM_DEFN_OUTLINE) ? true : false;
if( pMetric->mbScalableFont )
{
// check if there are kern pairs
// TODO: does this work with GPOS kerning?
pMetric->mbKernableFont = (aOS2Metric.sKerningPairs > 0);
}
else
{
// bitmap fonts cannot be rotated directly
pMetric->mnOrientation = 0;
// bitmap fonts have no kerning
pMetric->mbKernableFont = false;
}
// transformation dependend font metrics
if ( aOS2Metric.fsDefn & FM_DEFN_OUTLINE )
{
pMetric->mnWidth = aOS2Metric.lEmInc;
}
else
{
pMetric->mnWidth = aOS2Metric.lAveCharWidth;
pMetric->mnOrientation = 0;
}
pMetric->mnIntLeading = aOS2Metric.lInternalLeading;
pMetric->mnExtLeading = aOS2Metric.lExternalLeading;
pMetric->mnAscent = aOS2Metric.lMaxAscender;
pMetric->mnDescent = aOS2Metric.lMaxDescender;
// #107888# improved metric compatibility for Asian fonts...
// TODO: assess workaround below for CWS >= extleading
// TODO: evaluate use of aWinMetric.sTypo* members for CJK
if( mpOs2FontData[0] && mpOs2FontData[0]->SupportsCJK() )
{
pMetric->mnIntLeading += pMetric->mnExtLeading;
// #109280# The line height for Asian fonts is too small.
// Therefore we add half of the external leading to the
// ascent, the other half is added to the descent.
const long nHalfTmpExtLeading = pMetric->mnExtLeading / 2;
const long nOtherHalfTmpExtLeading = pMetric->mnExtLeading - nHalfTmpExtLeading;
// #110641# external leading for Asian fonts.
// The factor 0.3 has been confirmed with experiments.
long nCJKExtLeading = static_cast<long>(0.30 * (pMetric->mnAscent + pMetric->mnDescent));
nCJKExtLeading -= pMetric->mnExtLeading;
pMetric->mnExtLeading = (nCJKExtLeading > 0) ? nCJKExtLeading : 0;
pMetric->mnAscent += nHalfTmpExtLeading;
pMetric->mnDescent += nOtherHalfTmpExtLeading;
// #109280# HACK korean only: increase descent for wavelines and impr
// YD win9x only
}
}
// -----------------------------------------------------------------------
ULONG Os2SalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs )
{
DBG_ASSERT( sizeof( KERNINGPAIRS ) == sizeof( ImplKernPairData ),
"Os2SalGraphics::GetKernPairs(): KERNINGPAIRS != ImplKernPairData" );
if ( mbFontKernInit )
{
if( mpFontKernPairs )
{
delete[] mpFontKernPairs;
mpFontKernPairs = NULL;
}
mnFontKernPairCount = 0;
{
KERNINGPAIRS* pPairs = NULL;
FONTMETRICS aOS2Metric;
Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
int nCount = aOS2Metric.sKerningPairs;
if( nCount )
{
#ifdef GCP_KERN_HACK
pPairs = new KERNINGPAIRS[ nCount+1 ];
mpFontKernPairs = pPairs;
mnFontKernPairCount = nCount;
Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs );
#else // GCP_KERN_HACK
pPairs = (KERNINGPAIRS*)pKernPairs;
nCount = (nCount < nPairs) ? nCount : nPairs;
Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs );
return nCount;
#endif // GCP_KERN_HACK
}
}
mbFontKernInit = FALSE;
std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData );
}
if( !pKernPairs )
return mnFontKernPairCount;
else if( mpFontKernPairs )
{
if ( nPairs < mnFontKernPairCount )
nPairs = mnFontKernPairCount;
memcpy( pKernPairs, mpFontKernPairs,
nPairs*sizeof( ImplKernPairData ) );
return nPairs;
}
return 0;
}
// -----------------------------------------------------------------------
static const ImplFontCharMap* pOs2DefaultImplFontCharMap = NULL;
static const sal_uInt32 pOs2DefaultRangeCodes[] = {0x0020,0x00FF};
const ImplFontCharMap* Os2SalGraphics::GetImplFontCharMap() const
{
if( !mpOs2FontData[0] )
return ImplFontCharMap::GetDefaultMap();
return mpOs2FontData[0]->GetImplFontCharMap();
}
// -----------------------------------------------------------------------
bool Os2SalGraphics::AddTempDevFont( ImplDevFontList* pFontList,
const String& rFontFileURL, const String& rFontName )
{
#if OSL_DEBUG_LEVEL>0
debug_printf("Os2SalGraphics::AddTempDevFont\n");
#endif
return false;
}
// -----------------------------------------------------------------------
void Os2SalGraphics::GetDevFontList( ImplDevFontList* pList )
{
PFONTMETRICS pFontMetrics;
ULONG nFontMetricCount;
SalData* pSalData;
#if OSL_DEBUG_LEVEL>0
debug_printf("Os2SalGraphics::GetDevFontList mbPrinter=%d\n", mbPrinter);
#endif
// install OpenSymbol
HMODULE hMod;
ULONG ObjNum, Offset, rc;
CHAR Buff[2*_MAX_PATH];
char drive[_MAX_DRIVE], dir[_MAX_DIR];
char fname[_MAX_FNAME], ext[_MAX_EXT];
// get module handle (and name)
rc = DosQueryModFromEIP( &hMod, &ObjNum, sizeof( Buff), Buff,
&Offset, (ULONG)ImplSalGetUniString);
DosQueryModuleName(hMod, sizeof(Buff), Buff);
// replace module path with font path
char* slash = strrchr( Buff, '\\');
*slash = '\0';
slash = strrchr( Buff, '\\');
*slash = '\0';
strcat( Buff, "\\FONTS\\OPENS___.TTF");
rc = GpiLoadPublicFonts( GetSalData()->mhAB, Buff);
if ( !mbPrinter )
{
// Bei Bildschirm-Devices cachen wir die Liste global, da
// dies im unabhaengigen Teil auch so gemacht wird und wir
// ansonsten auf geloeschten Systemdaten arbeiten koennten
pSalData = GetSalData();
nFontMetricCount = pSalData->mnFontMetricCount;
pFontMetrics = pSalData->mpFontMetrics;
// Bei Bildschirm-Devices holen wir uns die Fontliste jedesmal neu
if ( pFontMetrics )
{
delete pFontMetrics;
pFontMetrics = NULL;
nFontMetricCount = 0;
}
}
else
{
nFontMetricCount = mnFontMetricCount;
pFontMetrics = mpFontMetrics;
}
// do we have to create the cached font list first?
if ( !pFontMetrics )
{
// query the number of fonts available
LONG nTemp = 0;
nFontMetricCount = Ft2QueryFonts( mhPS,
QF_PUBLIC | QF_PRIVATE | QF_NO_DEVICE,
NULL, &nTemp,
sizeof( FONTMETRICS ), NULL );
// procede only if at least one is available!
if ( nFontMetricCount )
{
// allocate memory for font list
pFontMetrics = new FONTMETRICS[nFontMetricCount];
// query font list
Ft2QueryFonts( mhPS,
QF_PUBLIC | QF_PRIVATE | QF_NO_DEVICE,
NULL,
(PLONG)&nFontMetricCount,
(LONG) sizeof( FONTMETRICS ),
pFontMetrics );
}
if ( !mbPrinter )
{
pSalData->mnFontMetricCount = nFontMetricCount;
pSalData->mpFontMetrics = pFontMetrics;
}
else
{
mnFontMetricCount = nFontMetricCount;
mpFontMetrics = pFontMetrics;
}
}
// copy data from the font list
for( ULONG i = 0; i < nFontMetricCount; i++ )
{
PFONTMETRICS pFontMetric = &pFontMetrics[i];
#if OSL_DEBUG_LEVEL>2
debug_printf("Os2SalGraphics::GetDevFontList #%d,'%s'\n", i, pFontMetric->szFacename);
#endif
// skip font starting with '@', this is an alias internally
// used by truetype engine.
if (pFontMetric->szFacename[0] == '@')
continue;
// skip bitmap fonts (but keep WarpSans)
if ( (pFontMetric->fsDefn & FM_DEFN_OUTLINE) == 0
&& strncmp( pFontMetric->szFacename, "WarpSans", 8) )
// Font nicht aufnehmen
continue;
// replace '-' in facename with ' ' (for ft2lib)
char* dash = pFontMetric->szFacename;
while( (dash=strchr( dash, '-')))
*dash++ = ' ';
// create new font list element
ImplOs2FontData* pData = new ImplOs2FontData( pFontMetric, 0, 0 );
// ticket#80: font id field is used for pdf font cache code.
pData->SetFontId( i);
// add font list element to font list
pList->Add( pData );
}
}
// ----------------------------------------------------------------------------
void Os2SalGraphics::GetDevFontSubstList( OutputDevice* pOutDev )
{
}
// -----------------------------------------------------------------------
bool Os2SalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect )
{
// use unity matrix
MAT2 aMat;
aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
UINT nGGOFlags = GGO_METRICS;
if( !(aGlyphId & GF_ISCHAR) )
nGGOFlags |= GGO_GLYPH_INDEX;
aGlyphId &= GF_IDXMASK;
GLYPHMETRICS aGM;
DWORD nSize = FT2_ERROR;
nSize = Ft2GetGlyphOutline( mhPS, aGlyphId, nGGOFlags, &aGM, 0, NULL, &aMat );
if( nSize == FT2_ERROR )
return false;
rRect = Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
rRect.Left() = static_cast<int>( mfFontScale * rRect.Left() );
rRect.Right() = static_cast<int>( mfFontScale * rRect.Right() );
rRect.Top() = static_cast<int>( mfFontScale * rRect.Top() );
rRect.Bottom() = static_cast<int>( mfFontScale * rRect.Bottom() );
return true;
}
// -----------------------------------------------------------------------
bool Os2SalGraphics::GetGlyphOutline( sal_GlyphId aGlyphId, ::basegfx::B2DPolyPolygon& rB2DPolyPoly )
{
#if OSL_DEBUG_LEVEL>0
debug_printf("Os2SalGraphics::GetGlyphOutline\n");
#endif
rB2DPolyPoly.clear();
PM_BOOL bRet = FALSE;
// use unity matrix
MAT2 aMat;
aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
UINT nGGOFlags = GGO_NATIVE;
if( !(aGlyphId & GF_ISCHAR) )
nGGOFlags |= GGO_GLYPH_INDEX;
aGlyphId &= GF_IDXMASK;
GLYPHMETRICS aGlyphMetrics;
DWORD nSize1 = FT2_ERROR;
nSize1 = Ft2GetGlyphOutline( mhPS, aGlyphId, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat );
if( !nSize1 ) // blank glyphs are ok
bRet = TRUE;
else if( nSize1 != FT2_ERROR )
{
PM_BYTE* pData = new PM_BYTE[ nSize1 ];
ULONG nTotalCount = 0;
DWORD nSize2;
nSize2 = Ft2GetGlyphOutline( mhPS, aGlyphId, nGGOFlags,
&aGlyphMetrics, nSize1, pData, &aMat );
if( nSize1 == nSize2 )
{
bRet = TRUE;
int nPtSize = 512;
Point* pPoints = new Point[ nPtSize ];
sal_uInt8* pFlags = new sal_uInt8[ nPtSize ];
TTPOLYGONHEADER* pHeader = (TTPOLYGONHEADER*)pData;
while( (PM_BYTE*)pHeader < pData+nSize2 )
{
// only outline data is interesting
if( pHeader->dwType != TT_POLYGON_TYPE )
break;
// get start point; next start points are end points
// of previous segment
int nPnt = 0;
long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
pPoints[ nPnt ] = Point( nX, nY );
pFlags[ nPnt++ ] = POLY_NORMAL;
bool bHasOfflinePoints = false;
TTPOLYCURVE* pCurve = (TTPOLYCURVE*)( pHeader + 1 );
pHeader = (TTPOLYGONHEADER*)( (PM_BYTE*)pHeader + pHeader->cb );
while( (PM_BYTE*)pCurve < (PM_BYTE*)pHeader )
{
int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
if( nPtSize < nNeededSize )
{
Point* pOldPoints = pPoints;
sal_uInt8* pOldFlags = pFlags;
nPtSize = 2 * nNeededSize;
pPoints = new Point[ nPtSize ];
pFlags = new sal_uInt8[ nPtSize ];
for( int i = 0; i < nPnt; ++i )
{
pPoints[ i ] = pOldPoints[ i ];
pFlags[ i ] = pOldFlags[ i ];
}
delete[] pOldPoints;
delete[] pOldFlags;
}
int i = 0;
if( TT_PRIM_LINE == pCurve->wType )
{
while( i < pCurve->cpfx )
{
nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
++i;
pPoints[ nPnt ] = Point( nX, nY );
pFlags[ nPnt ] = POLY_NORMAL;
++nPnt;
}
}
else if( TT_PRIM_QSPLINE == pCurve->wType )
{
bHasOfflinePoints = true;
while( i < pCurve->cpfx )
{
// get control point of quadratic bezier spline
nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
++i;
Point aControlP( nX, nY );
// calculate first cubic control point
// P0 = 1/3 * (PBeg + 2 * PQControl)
nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
pFlags[ nPnt+0 ] = POLY_CONTROL;
// calculate endpoint of segment
nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
if ( i+1 >= pCurve->cpfx )
{
// endpoint is either last point in segment => advance
++i;
}
else
{
// or endpoint is the middle of two control points
nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
nX = (nX + 1) / 2;
nY = (nY + 1) / 2;
// no need to advance, because the current point
// is the control point in next bezier spline
}
pPoints[ nPnt+2 ] = Point( nX, nY );
pFlags[ nPnt+2 ] = POLY_NORMAL;
// calculate second cubic control point
// P1 = 1/3 * (PEnd + 2 * PQControl)
nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
pFlags[ nPnt+1 ] = POLY_CONTROL;
nPnt += 3;
}
}
// next curve segment
pCurve = (TTPOLYCURVE*)&pCurve->apfx[ i ];
}
// end point is start point for closed contour
// disabled, because Polygon class closes the contour itself
// pPoints[nPnt++] = pPoints[0];
// #i35928#
// Added again, but add only when not yet closed
if(pPoints[nPnt - 1] != pPoints[0])
{
if( bHasOfflinePoints )
pFlags[nPnt] = pFlags[0];
pPoints[nPnt++] = pPoints[0];
}
// convert y-coordinates W32 -> VCL
for( int i = 0; i < nPnt; ++i )
pPoints[i].Y() = -pPoints[i].Y();
// insert into polypolygon
Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : NULL) );
// convert to B2DPolyPolygon
// TODO: get rid of the intermediate PolyPolygon
rB2DPolyPoly.append( aPoly.getB2DPolygon() );
}
delete[] pPoints;
delete[] pFlags;
}
delete[] pData;
}
// rescaling needed for the PolyPolygon conversion
if( rB2DPolyPoly.count() )
{
::basegfx::B2DHomMatrix aMatrix;
aMatrix.scale( 1.0/256, 1.0/256 );
aMatrix.scale( mfFontScale, mfFontScale );
rB2DPolyPoly.transform( aMatrix );
}
return bRet;
}
// -----------------------------------------------------------------------
// TODO: Replace this class with boost::scoped_array
class ScopedCharArray
{
public:
inline explicit ScopedCharArray(char * pArray): m_pArray(pArray) {}
inline ~ScopedCharArray() { delete[] m_pArray; }
inline char * get() const { return m_pArray; }
private:
char * m_pArray;
};
class ScopedFont
{
public:
explicit ScopedFont(Os2SalGraphics & rData);
~ScopedFont();
private:
Os2SalGraphics & m_rData;
ULONG m_hOrigFont;
};
ScopedFont::ScopedFont(Os2SalGraphics & rData): m_rData(rData)
{
#if 0
m_hOrigFont = m_rData.mhFonts[0];
m_rData.mhFonts[0] = 0; // avoid deletion of current font
#endif
}
ScopedFont::~ScopedFont()
{
#if 0
if( m_hOrigFont )
{
// restore original font, destroy temporary font
HFONT hTempFont = m_rData.mhFonts[0];
m_rData.mhFonts[0] = m_hOrigFont;
SelectObject( m_rData.mhDC, m_hOrigFont );
DeleteObject( hTempFont );
}
#endif
}
class ScopedTrueTypeFont
{
public:
inline ScopedTrueTypeFont(): m_pFont(0) {}
~ScopedTrueTypeFont();
int open(void * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum);
inline TrueTypeFont * get() const { return m_pFont; }
private:
TrueTypeFont * m_pFont;
};
ScopedTrueTypeFont::~ScopedTrueTypeFont()
{
if (m_pFont != 0)
CloseTTFont(m_pFont);
}
int ScopedTrueTypeFont::open(void * pBuffer, sal_uInt32 nLen,
sal_uInt32 nFaceNum)
{
OSL_ENSURE(m_pFont == 0, "already open");
return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont);
}
sal_Bool Os2SalGraphics::CreateFontSubset( const rtl::OUString& rToFile,
const ImplFontData* pFont, sal_GlyphId* pGlyphIds, sal_uInt8* pEncoding,
sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
{
// TODO: use more of the central font-subsetting code, move stuff there if needed
// create matching ImplFontSelectData
// we need just enough to get to the font file data
// use height=1000 for easier debugging (to match psprint's font units)
ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
// TODO: much better solution: move SetFont and restoration of old font to caller
ScopedFont aOldFont(*this);
SetFont( &aIFSD, 0 );
ImplOs2FontData* pWinFontData = (ImplOs2FontData*)aIFSD.mpFontData;
pWinFontData->UpdateFromHPS( mhPS );
const ImplFontCharMap* pImplFontCharMap = pWinFontData->GetImplFontCharMap();
#if OSL_DEBUG_LEVEL > 100
// get font metrics
TEXTMETRICA aWinMetric;
if( !::GetTextMetricsA( mhDC, &aWinMetric ) )
return FALSE;
DBG_ASSERT( !(aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "cannot subset device font" );
DBG_ASSERT( aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE, "can only subset TT font" );
#endif
rtl::OUString aSysPath;
if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
return FALSE;
const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
const ByteString aToFile( aSysPath.getStr(), (xub_StrLen)aSysPath.getLength(), aThreadEncoding );
// check if the font has a CFF-table
const DWORD nCffTag = CalcTag( "CFF " );
const RawFontData aRawCffData( mhPS, nCffTag );
if( aRawCffData.get() )
{
sal_GlyphId aRealGlyphIds[ 256 ];
for( int i = 0; i < nGlyphCount; ++i )
{
// TODO: remap notdef glyph if needed
// TODO: use GDI's GetGlyphIndices instead? Does it handle GSUB properly?
sal_uInt32 aGlyphId = pGlyphIds[i] & GF_IDXMASK;
if( pGlyphIds[i] & GF_ISCHAR ) // remaining pseudo-glyphs need to be translated
aGlyphId = pImplFontCharMap->GetGlyphIndex( aGlyphId );
if( (pGlyphIds[i] & (GF_ROTMASK|GF_GSUB)) != 0) // TODO: vertical substitution
{/*####*/}
aRealGlyphIds[i] = aGlyphId;
}
// provide a font subset from the CFF-table
FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
rInfo.LoadFont( FontSubsetInfo::CFF_FONT, aRawCffData.get(), aRawCffData.size() );
bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL,
aRealGlyphIds, pEncoding, nGlyphCount, pGlyphWidths );
fclose( pOutFile );
return bRC;
}
// get raw font file data
const RawFontData xRawFontData( mhPS, NULL );
if( !xRawFontData.get() )
return FALSE;
// open font file
sal_uInt32 nFaceNum = 0;
if( !*xRawFontData.get() ) // TTC candidate
nFaceNum = ~0U; // indicate "TTC font extracts only"
ScopedTrueTypeFont aSftTTF;
int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
if( nRC != SF_OK )
return FALSE;
TTGlobalFontInfo aTTInfo;
::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF;
rInfo.m_aPSName = ImplSalGetUniString( aTTInfo.psname );
rInfo.m_nAscent = +aTTInfo.winAscent;
rInfo.m_nDescent = -aTTInfo.winDescent;
rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
Point( aTTInfo.xMax, aTTInfo.yMax ) );
rInfo.m_nCapHeight = aTTInfo.yMax; // Well ...
// subset TTF-glyphs and get their properties
// take care that subset fonts require the NotDef glyph in pos 0
int nOrigCount = nGlyphCount;
USHORT aShortIDs[ 256 ];
sal_uInt8 aTempEncs[ 256 ];
int nNotDef=-1, i;
for( i = 0; i < nGlyphCount; ++i )
{
aTempEncs[i] = pEncoding[i];
sal_GlyphId aGlyphId = pGlyphIds[i] & GF_IDXMASK;
if( pGlyphIds[i] & GF_ISCHAR )
{
sal_Unicode cChar = static_cast<sal_Unicode>(aGlyphId); // TODO: sal_UCS4
const bool bVertical = ((pGlyphIds[i] & (GF_ROTMASK|GF_GSUB)) != 0);
aGlyphId = ::MapChar( aSftTTF.get(), cChar, bVertical );
if( (aGlyphId == 0) && pFont->IsSymbolFont() )
{
// #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
cChar = (cChar & 0xF000) ? (cChar & 0x00FF) : (cChar | 0xF000);
aGlyphId = ::MapChar( aSftTTF.get(), cChar, bVertical );
}
}
aShortIDs[i] = static_cast<USHORT>( aGlyphId );
if( !aGlyphId )
if( nNotDef < 0 )
nNotDef = i; // first NotDef glyph found
}
if( nNotDef != 0 )
{
// add fake NotDef glyph if needed
if( nNotDef < 0 )
nNotDef = nGlyphCount++;
// NotDef glyph must be in pos 0 => swap glyphids
aShortIDs[ nNotDef ] = aShortIDs[0];
aTempEncs[ nNotDef ] = aTempEncs[0];
aShortIDs[0] = 0;
aTempEncs[0] = 0;
}
DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" );
// fill pWidth array
TTSimpleGlyphMetrics* pMetrics =
::GetTTSimpleGlyphMetrics( aSftTTF.get(), aShortIDs, nGlyphCount, aIFSD.mbVertical );
if( !pMetrics )
return FALSE;
sal_uInt16 nNotDefAdv = pMetrics[0].adv;
pMetrics[0].adv = pMetrics[nNotDef].adv;
pMetrics[nNotDef].adv = nNotDefAdv;
for( i = 0; i < nOrigCount; ++i )
pGlyphWidths[i] = pMetrics[i].adv;
free( pMetrics );
// write subset into destination file
nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.GetBuffer(), aShortIDs,
aTempEncs, nGlyphCount, 0, NULL, 0 );
return (nRC == SF_OK);
}
//--------------------------------------------------------------------------
const void* Os2SalGraphics::GetEmbedFontData( const ImplFontData* pFont,
const sal_Ucs* pUnicodes, sal_Int32* pCharWidths,
FontSubsetInfo& rInfo, long* pDataLen )
{
// create matching ImplFontSelectData
// we need just enough to get to the font file data
ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
// TODO: much better solution: move SetFont and restoration of old font to caller
ScopedFont aOldFont(*this);
SetFont( &aIFSD, 0 );
// get the raw font file data
RawFontData aRawFontData( mhPS );
*pDataLen = aRawFontData.size();
if( !aRawFontData.get() )
return NULL;
// get important font properties
FONTMETRICS aOS2Metric;
if (Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ) == GPI_ERROR)
*pDataLen = 0;
rInfo.m_nFontType = FontSubsetInfo::ANY_TYPE1;
rInfo.m_aPSName = ImplSalGetUniString( aOS2Metric.szFacename );
rInfo.m_nAscent = +aOS2Metric.lMaxAscender;
rInfo.m_nDescent = -aOS2Metric.lMaxDescender;
rInfo.m_aFontBBox = Rectangle( Point( 0, -aOS2Metric.lMaxDescender ),
Point( aOS2Metric.lMaxCharInc, aOS2Metric.lMaxAscender+aOS2Metric.lExternalLeading ) );
rInfo.m_nCapHeight = aOS2Metric.lMaxAscender; // Well ...
// get individual character widths
for( int i = 0; i < 256; ++i )
{
LONG nCharWidth = 0;
const sal_Ucs cChar = pUnicodes[i];
if( !Ft2QueryStringWidthW( mhPS, (LPWSTR)&cChar, 1, &nCharWidth ) )
*pDataLen = 0;
pCharWidths[i] = nCharWidth;
}
if( !*pDataLen )
return NULL;
const unsigned char* pData = aRawFontData.steal();
return (void*)pData;
}
//--------------------------------------------------------------------------
void Os2SalGraphics::FreeEmbedFontData( const void* pData, long /*nLen*/ )
{
delete[] reinterpret_cast<char*>(const_cast<void*>(pData));
}
const Ucs2SIntMap* Os2SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded )
{
// TODO: even for builtin fonts we get here... why?
if( !pFont->IsEmbeddable() )
return NULL;
// fill the encoding vector
// currently no nonencoded vector
if( pNonEncoded )
*pNonEncoded = NULL;
const ImplOs2FontData* pWinFontData = static_cast<const ImplOs2FontData*>(pFont);
const Ucs2SIntMap* pEncoding = pWinFontData->GetEncodingVector();
if( pEncoding == NULL )
{
Ucs2SIntMap* pNewEncoding = new Ucs2SIntMap;
#if 0
// TODO: get correct encoding vector
GLYPHSET aGlyphSet;
aGlyphSet.cbThis = sizeof(aGlyphSet);
DWORD aW = ::GetFontUnicodeRanges( mhPS, &aGlyphSet);
#else
for( sal_Unicode i = 32; i < 256; ++i )
(*pNewEncoding)[i] = i;
#endif
pWinFontData->SetEncodingVector( pNewEncoding );
pEncoding = pNewEncoding;
}
return pEncoding;
}
//--------------------------------------------------------------------------
void Os2SalGraphics::GetGlyphWidths( const ImplFontData* pFont,
bool bVertical,
Int32Vector& rWidths,
Ucs2UIntMap& rUnicodeEnc )
{
// create matching ImplFontSelectData
// we need just enough to get to the font file data
ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
// TODO: much better solution: move SetFont and restoration of old font to caller
ScopedFont aOldFont(*this);
float fScale = 0.0;
ImplDoSetFont( &aIFSD, fScale, 0);
if( pFont->IsSubsettable() )
{
// get raw font file data
const RawFontData xRawFontData( mhPS );
if( !xRawFontData.get() )
return;
// open font file
sal_uInt32 nFaceNum = 0;
if( !*xRawFontData.get() ) // TTC candidate
nFaceNum = ~0U; // indicate "TTC font extracts only"
ScopedTrueTypeFont aSftTTF;
int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
if( nRC != SF_OK )
return;
int nGlyphs = GetTTGlyphCount( aSftTTF.get() );
if( nGlyphs > 0 )
{
rWidths.resize(nGlyphs);
std::vector<sal_uInt16> aGlyphIds(nGlyphs);
for( int i = 0; i < nGlyphs; i++ )
aGlyphIds[i] = sal_uInt16(i);
TTSimpleGlyphMetrics* pMetrics = ::GetTTSimpleGlyphMetrics( aSftTTF.get(),
&aGlyphIds[0],
nGlyphs,
bVertical ? 1 : 0 );
if( pMetrics )
{
for( int i = 0; i< nGlyphs; i++ )
rWidths[i] = pMetrics[i].adv;
free( pMetrics );
rUnicodeEnc.clear();
}
const ImplOs2FontData* pWinFont = static_cast<const ImplOs2FontData*>(pFont);
const ImplFontCharMap* pMap = pWinFont->GetImplFontCharMap();
DBG_ASSERT( pMap && pMap->GetCharCount(), "no map" );
int nCharCount = pMap->GetCharCount();
sal_uInt32 nChar = pMap->GetFirstChar();
for( int i = 0; i < nCharCount; i++ )
{
if( nChar < 0x00010000 )
{
sal_uInt16 nGlyph = ::MapChar( aSftTTF.get(),
static_cast<sal_uInt16>(nChar),
bVertical ? 1 : 0 );
if( nGlyph )
rUnicodeEnc[ static_cast<sal_Unicode>(nChar) ] = nGlyph;
}
nChar = pMap->GetNextChar( nChar );
}
}
}
else if( pFont->IsEmbeddable() )
{
// get individual character widths
rWidths.clear();
rUnicodeEnc.clear();
rWidths.reserve( 224 );
for( sal_Unicode i = 32; i < 256; ++i )
{
int nCharWidth = 0;
if( Ft2QueryStringWidthW( mhPS, (LPWSTR)&i, 1, (LONG*)&nCharWidth ) )
{
rUnicodeEnc[ i ] = rWidths.size();
rWidths.push_back( nCharWidth );
}
}
}
}
//--------------------------------------------------------------------------
void Os2SalGraphics::DrawServerFontLayout( const ServerFontLayout& )
{}
//--------------------------------------------------------------------------
SystemFontData Os2SalGraphics::GetSysFontData( int nFallbacklevel ) const
{
SystemFontData aSysFontData;
if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1;
if (nFallbacklevel < 0 ) nFallbacklevel = 0;
aSysFontData.nSize = sizeof( SystemFontData );
aSysFontData.hFont = mhFonts[nFallbacklevel];
aSysFontData.bFakeBold = false;
aSysFontData.bFakeItalic = false;
aSysFontData.bAntialias = true;
aSysFontData.bVerticalCharacterType = false;
return aSysFontData;
}
//--------------------------------------------------------------------------