office-gobmx/vcl/aqua/source/gdi/atsui/salgdi.cxx

1152 lines
42 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
#include "osl/file.hxx"
#include "osl/process.h"
#include "osl/mutex.hxx"
#include "rtl/bootstrap.h"
#include "rtl/strbuf.hxx"
#include "basegfx/range/b2drectangle.hxx"
#include "basegfx/polygon/b2dpolygon.hxx"
#include "basegfx/polygon/b2dpolygontools.hxx"
#include "basegfx/matrix/b2dhommatrix.hxx"
#include "basegfx/matrix/b2dhommatrixtools.hxx"
#include "vcl/sysdata.hxx"
#include "vcl/svapp.hxx"
#include "aqua/salconst.h"
#include "aqua/atsui/salgdi.h"
#include "aqua/salbmp.h"
#include "aqua/salframe.h"
#include "aqua/salcolorutils.hxx"
#include "aqua/atsui/salatsuifontutils.hxx"
#include "fontsubset.hxx"
#include "impfont.hxx"
#include "sallayout.hxx"
#include "sft.hxx"
using namespace vcl;
//typedef unsigned char Boolean; // copied from MacTypes.h, should be properly included
typedef std::vector<unsigned char> ByteVector;
// ATSUI is deprecated in 10.6 (or already 10.5?)
#if defined LIBO_WERROR && defined __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40201
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
#endif
#endif
// =======================================================================
ImplMacFontData::ImplMacFontData( const ImplDevFontAttributes& rDFA, ATSUFontID nFontId )
: PhysicalFontFace( rDFA, 0 )
, mnFontId( nFontId )
, mpCharMap( NULL )
, mbOs2Read( false )
, mbHasOs2Table( false )
, mbCmapEncodingRead( false )
, mbHasCJKSupport( false )
, mbFontCapabilitiesRead( false )
{}
// -----------------------------------------------------------------------
ImplMacFontData::~ImplMacFontData()
{
if( mpCharMap )
mpCharMap->DeReference();
}
// -----------------------------------------------------------------------
sal_IntPtr ImplMacFontData::GetFontId() const
{
return (sal_IntPtr)mnFontId;
}
// -----------------------------------------------------------------------
PhysicalFontFace* ImplMacFontData::Clone() const
{
ImplMacFontData* pClone = new ImplMacFontData(*this);
if( mpCharMap )
mpCharMap->AddReference();
return pClone;
}
// -----------------------------------------------------------------------
ImplFontEntry* ImplMacFontData::CreateFontInstance(FontSelectPattern& rFSD) const
{
return new ImplFontEntry(rFSD);
}
// -----------------------------------------------------------------------
inline FourCharCode GetTag(const char aTagName[5])
{
return (aTagName[0]<<24)+(aTagName[1]<<16)+(aTagName[2]<<8)+(aTagName[3]);
}
static unsigned GetUShort( const unsigned char* p ){return((p[0]<<8)+p[1]);}
static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
const ImplFontCharMap* ImplMacFontData::GetImplFontCharMap() const
{
// return the cached charmap
if( mpCharMap )
return mpCharMap;
// set the default charmap
mpCharMap = ImplFontCharMap::GetDefaultMap();
mpCharMap->AddReference();
// get the CMAP byte size
ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId );
ByteCount nBufSize = 0;
OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize );
DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable1 failed!\n");
if( eStatus != noErr )
return mpCharMap;
// allocate a buffer for the CMAP raw data
ByteVector aBuffer( nBufSize );
// get the CMAP raw data
ByteCount nRawLength = 0;
eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable2 failed!\n");
if( eStatus != noErr )
return mpCharMap;
DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::GetImplFontCharMap : ByteCount mismatch!\n");
// parse the CMAP
CmapResult aCmapResult;
if( !ParseCMAP( &aBuffer[0], nRawLength, aCmapResult ) )
return mpCharMap;
mpCharMap = new ImplFontCharMap( aCmapResult );
mpCharMap->AddReference();
return mpCharMap;
}
bool ImplMacFontData::GetImplFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
{
// read this only once per font
if( mbFontCapabilitiesRead )
{
rFontCapabilities = maFontCapabilities;
return !rFontCapabilities.maUnicodeRange.empty() || !rFontCapabilities.maCodePageRange.empty();
}
mbFontCapabilitiesRead = true;
// prepare to get the GSUB table raw data
ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId );
ByteCount nBufSize = 0;
OSStatus eStatus;
eStatus = ATSFontGetTable( rFont, GetTag("GSUB"), 0, 0, NULL, &nBufSize );
if( eStatus == noErr )
{
// allocate a buffer for the GSUB raw data
ByteVector aBuffer( nBufSize );
// get the GSUB raw data
ByteCount nRawLength = 0;
eStatus = ATSFontGetTable( rFont, GetTag("GSUB"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
if( eStatus == noErr )
{
const unsigned char* pGSUBTable = &aBuffer[0];
vcl::getTTScripts(maFontCapabilities.maGSUBScriptTags, pGSUBTable, nRawLength);
}
}
eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, 0, NULL, &nBufSize );
if( eStatus == noErr )
{
// allocate a buffer for the GSUB raw data
ByteVector aBuffer( nBufSize );
// get the OS/2 raw data
ByteCount nRawLength = 0;
eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
if( eStatus == noErr )
{
const unsigned char* pOS2Table = &aBuffer[0];
vcl::getTTCoverage(
maFontCapabilities.maUnicodeRange,
maFontCapabilities.maCodePageRange,
pOS2Table, nRawLength);
}
}
rFontCapabilities = maFontCapabilities;
return !rFontCapabilities.maUnicodeRange.empty() || !rFontCapabilities.maCodePageRange.empty();
}
// -----------------------------------------------------------------------
void ImplMacFontData::ReadOs2Table( void ) const
{
// read this only once per font
if( mbOs2Read )
return;
mbOs2Read = true;
// prepare to get the OS/2 table raw data
ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId );
ByteCount nBufSize = 0;
OSStatus eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, 0, NULL, &nBufSize );
DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable1 failed!\n");
if( eStatus != noErr )
return;
// allocate a buffer for the OS/2 raw data
ByteVector aBuffer( nBufSize );
// get the OS/2 raw data
ByteCount nRawLength = 0;
eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable2 failed!\n");
if( eStatus != noErr )
return;
DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadOs2Table : ByteCount mismatch!\n");
mbHasOs2Table = true;
// parse the OS/2 raw data
// TODO: also analyze panose info, etc.
// check if the fonts needs the "CJK extra leading" heuristic
const unsigned char* pOS2map = &aBuffer[0];
const sal_uInt32 nVersion = GetUShort( pOS2map );
if( nVersion >= 0x0001 )
{
sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 );
if( ulUnicodeRange2 & 0x2DF00000 )
mbHasCJKSupport = true;
}
}
void ImplMacFontData::ReadMacCmapEncoding( void ) const
{
// read this only once per font
if( mbCmapEncodingRead )
return;
mbCmapEncodingRead = true;
ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId );
ByteCount nBufSize = 0;
OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize );
DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable1 failed!\n");
if( eStatus != noErr )
return;
ByteVector aBuffer( nBufSize );
ByteCount nRawLength = 0;
eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable2 failed!\n");
if( eStatus != noErr )
return;
DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadMacCmapEncoding : ByteCount mismatch!\n");
const unsigned char* pCmap = &aBuffer[0];
if (nRawLength < 24 )
return;
if( GetUShort( pCmap ) != 0x0000 )
return;
// check if the fonts needs the "CJK extra leading" heuristic
int nSubTables = GetUShort( pCmap + 2 );
for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 )
{
int nPlatform = GetUShort( p );
if( nPlatform == kFontMacintoshPlatform ) {
int nEncoding = GetUShort (p + 2 );
if( nEncoding == kFontJapaneseScript ||
nEncoding == kFontTraditionalChineseScript ||
nEncoding == kFontKoreanScript ||
nEncoding == kFontSimpleChineseScript )
{
mbHasCJKSupport = true;
break;
}
}
}
}
// -----------------------------------------------------------------------
bool ImplMacFontData::HasCJKSupport( void ) const
{
ReadOs2Table();
if( !mbHasOs2Table )
ReadMacCmapEncoding();
return mbHasCJKSupport;
}
// =======================================================================
AquaSalGraphics::AquaSalGraphics()
: mpFrame( NULL )
, mxLayer( NULL )
, mrContext( NULL )
, mpXorEmulation( NULL )
, mnXorMode( 0 )
, mnWidth( 0 )
, mnHeight( 0 )
, mnBitmapDepth( 0 )
, mnRealDPIX( 0 )
, mnRealDPIY( 0 )
, mfFakeDPIScale( 1.0 )
, mxClipPath( NULL )
, maLineColor( COL_WHITE )
, maFillColor( COL_BLACK )
, mpMacFontData( NULL )
, mnATSUIRotation( 0 )
, mfFontScale( 1.0 )
, mfFontStretch( 1.0 )
, mbNonAntialiasedText( false )
, mbPrinter( false )
, mbVirDev( false )
, mbWindow( false )
{
// create the style object for font attributes
ATSUCreateStyle( &maATSUStyle );
}
// -----------------------------------------------------------------------
AquaSalGraphics::~AquaSalGraphics()
{
CGPathRelease( mxClipPath );
ATSUDisposeStyle( maATSUStyle );
if( mpXorEmulation )
delete mpXorEmulation;
if( mxLayer )
CGLayerRelease( mxLayer );
else if( mrContext && mbWindow )
{
// destroy backbuffer bitmap context that we created ourself
CGContextRelease( mrContext );
mrContext = NULL;
// memory is freed automatically by maOwnContextMemory
}
}
// =======================================================================
void AquaSalGraphics::SetTextColor( SalColor nSalColor )
{
RGBColor color;
color.red = (unsigned short) ( SALCOLOR_RED(nSalColor) * 65535.0 / 255.0 );
color.green = (unsigned short) ( SALCOLOR_GREEN(nSalColor) * 65535.0 / 255.0 );
color.blue = (unsigned short) ( SALCOLOR_BLUE(nSalColor) * 65535.0 / 255.0 );
ATSUAttributeTag aTag = kATSUColorTag;
ByteCount aValueSize = sizeof( color );
ATSUAttributeValuePtr aValue = &color;
OSStatus err = ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue );
DBG_ASSERT( (err==noErr), "AquaSalGraphics::SetTextColor() : Could not set font attributes!\n");
if( err != noErr )
return;
}
// -----------------------------------------------------------------------
void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel )
{
(void)nFallbackLevel; // glyph-fallback on ATSU is done differently -> no fallback level
// get the ATSU font metrics (in point units)
// of the font that has eventually been size-limited
ATSUFontID fontId;
OSStatus err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 );
DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n");
ATSFontMetrics aMetrics;
ATSFontRef rFont = FMGetATSFontRefFromFont( fontId );
err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics );
DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n");
if( err != noErr )
return;
// all ATS fonts are scalable fonts
pMetric->mbScalableFont = true;
// TODO: check if any kerning is possible
pMetric->mbKernableFont = true;
// convert into VCL font metrics (in unscaled pixel units)
Fixed ptSize;
err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0);
DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n");
const double fPointSize = Fix2X( ptSize );
// convert quartz units to pixel units
// please see the comment in AquaSalGraphics::SetFont() for details
const double fPixelSize = (mfFontScale * mfFakeDPIScale * fPointSize);
pMetric->mnAscent = static_cast<long>(+aMetrics.ascent * fPixelSize + 0.5);
pMetric->mnDescent = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5);
const long nExtDescent = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5);
pMetric->mnExtLeading = nExtDescent - pMetric->mnDescent;
pMetric->mnIntLeading = 0;
// ATSFontMetrics.avgAdvanceWidth is obsolete, so it is usually set to zero
// since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
// setting this width to the pixel height of the fontsize is good enough
// it also makes the calculation of the stretch factor simple
pMetric->mnWidth = static_cast<long>(mfFontStretch * fPixelSize + 0.5);
}
// -----------------------------------------------------------------------
sal_uLong AquaSalGraphics::GetKernPairs( sal_uLong, ImplKernPairData* )
{
return 0;
}
// -----------------------------------------------------------------------
static bool AddTempFontDir( const char* pDir )
{
FSRef aPathFSRef;
Boolean bIsDirectory = true;
OSStatus eStatus = FSPathMakeRef( reinterpret_cast<const UInt8*>(pDir), &aPathFSRef, &bIsDirectory );
DBG_ASSERTWARNING( (eStatus==noErr) && bIsDirectory, "vcl AddTempFontDir() with invalid directory name!" );
if( eStatus != noErr )
return false;
// TODO: deactivate ATSFontContainerRef when closing app
ATSFontContainerRef aATSFontContainer;
const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global???
#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef,
eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
&aATSFontContainer );
#else
FSSpec aPathFSSpec;
eStatus = ::FSGetCatalogInfo( &aPathFSRef, kFSCatInfoNone,
NULL, NULL, &aPathFSSpec, NULL );
if( eStatus != noErr )
return false;
eStatus = ::ATSFontActivateFromFileSpecification( &aPathFSSpec,
eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
&aATSFontContainer );
#endif
if( eStatus != noErr )
return false;
return true;
}
static bool AddLocalTempFontDirs( void )
{
static bool bFirst = true;
if( !bFirst )
return false;
bFirst = false;
// add private font files
rtl::OUString aBrandStr( RTL_CONSTASCII_USTRINGPARAM( "$BRAND_BASE_DIR" ) );
rtl_bootstrap_expandMacros( &aBrandStr.pData );
rtl::OUString aBrandSysPath;
OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None );
rtl::OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 );
aBrandFontDir.append( rtl::OUStringToOString( aBrandSysPath, RTL_TEXTENCODING_UTF8 ) );
aBrandFontDir.append( "/share/fonts/truetype/" );
return AddTempFontDir( aBrandFontDir.getStr() );
}
void AquaSalGraphics::GetDevFontList( ImplDevFontList* pFontList )
{
DBG_ASSERT( pFontList, "AquaSalGraphics::GetDevFontList(NULL) !");
AddLocalTempFontDirs();
// The idea is to cache the list of system fonts once it has been generated.
// SalData seems to be a good place for this caching. However we have to
// carefully make the access to the font list thread-safe. If we register
// a font-change event handler to update the font list in case fonts have
// changed on the system we have to lock access to the list. The right
// way to do that is the solar mutex since GetDevFontList is protected
// through it as should be all event handlers
SalData* pSalData = GetSalData();
if (pSalData->mpFontList == NULL)
pSalData->mpFontList = new SystemFontList();
// Copy all PhysicalFontFace objects contained in the SystemFontList
pSalData->mpFontList->AnnounceFonts( *pFontList );
}
// -----------------------------------------------------------------------
bool AquaSalGraphics::AddTempDevFont( ImplDevFontList*,
const rtl::OUString& rFontFileURL, const rtl::OUString& /*rFontName*/ )
{
::rtl::OUString aUSytemPath;
OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
FSRef aNewRef;
Boolean bIsDirectory = true;
::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, RTL_TEXTENCODING_UTF8 );
OSStatus eStatus = FSPathMakeRef( (UInt8*)aCFileName.getStr(), &aNewRef, &bIsDirectory );
DBG_ASSERT( (eStatus==noErr) && !bIsDirectory, "vcl AddTempDevFont() with invalid fontfile name!" );
if( eStatus != noErr )
return false;
ATSFontContainerRef oContainer;
const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global???
#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
eStatus = ::ATSFontActivateFromFileReference( &aNewRef,
eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
&oContainer );
#else
FSSpec aFontFSSpec;
eStatus = ::FSGetCatalogInfo( &aNewRef, kFSCatInfoNone,
NULL, NULL, &aFontFSSpec, NULL );
if( eStatus != noErr )
return false;
eStatus = ::ATSFontActivateFromFileSpecification( &aFontFSSpec,
eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
&oContainer );
#endif
if( eStatus != noErr )
return false;
// TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed
// TODO: register new ImplMacFontdata in pFontList
return true;
}
// -----------------------------------------------------------------------
// callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline()
struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData )
{
basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y );
rPolygon.append( aB2DPoint );
return noErr;
}
static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2,
const Float32Point* pPoint, void* pData )
{
basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
const sal_uInt32 nPointCount = rPolygon.count();
const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y );
rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 );
const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y );
rPolygon.append( aB2DEndPoint );
const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y );
rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 );
return noErr;
}
static OSStatus GgoClosePathProc( void* pData )
{
GgoData* pGgoData = static_cast<GgoData*>(pData);
basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon;
if( rPolygon.count() > 0 )
pGgoData->mpPolyPoly->append( rPolygon );
rPolygon.clear();
return noErr;
}
static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData )
{
GgoClosePathProc( pData );
OSStatus eStatus = GgoLineToProc( pPoint, pData );
return eStatus;
}
sal_Bool AquaSalGraphics::GetGlyphOutline( sal_GlyphId nGlyphId, basegfx::B2DPolyPolygon& rPolyPoly )
{
GgoData aGgoData;
aGgoData.mpPolyPoly = &rPolyPoly;
rPolyPoly.clear();
ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback when CWS pdffix02 is integrated
GlyphID aGlyphId = nGlyphId & GF_IDXMASK;
OSStatus eGgoStatus = noErr;
OSStatus eStatus = ATSUGlyphGetCubicPaths( rATSUStyle, aGlyphId,
GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc,
&aGgoData, &eGgoStatus );
if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved?
return false;
GgoClosePathProc( &aGgoData );
if( mfFontScale != 1.0 ) {
rPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(+mfFontScale, +mfFontScale));
}
return true;
}
// -----------------------------------------------------------------------
sal_Bool AquaSalGraphics::GetGlyphBoundRect( sal_GlyphId nGlyphId, Rectangle& rRect )
{
ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback
GlyphID aGlyphId = nGlyphId & GF_IDXMASK;
ATSGlyphScreenMetrics aGlyphMetrics;
OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle,
1, &aGlyphId, 0, FALSE, !mbNonAntialiasedText, &aGlyphMetrics );
if( eStatus != noErr )
return false;
const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale + 0.5);
const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale + 0.5);
const long nWidth = (long)(aGlyphMetrics.width * mfFontScale + 0.5);
const long nHeight = (long)(aGlyphMetrics.height * mfFontScale + 0.5);
Rectangle aRect(Point(nMinX, nMinY), Size(nWidth, nHeight));
if ( mnATSUIRotation == 0 )
rRect = aRect;
else
{
const double fRadians = mnATSUIRotation * (M_PI/0xB40000);
const double nSin = sin( fRadians );
const double nCos = cos( fRadians );
rRect.Left() = nCos*aRect.Left() + nSin*aRect.Top();
rRect.Top() = -nSin*aRect.Left() - nCos*aRect.Top();
rRect.Right() = nCos*aRect.Right() + nSin*aRect.Bottom();
rRect.Bottom() = -nSin*aRect.Right() - nCos*aRect.Bottom();
}
return true;
}
// -----------------------------------------------------------------------
void AquaSalGraphics::GetDevFontSubstList( OutputDevice* )
{
// nothing to do since there are no device-specific fonts on Aqua
}
// -----------------------------------------------------------------------
void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& )
{
}
// -----------------------------------------------------------------------
sal_uInt16 AquaSalGraphics::SetFont( FontSelectPattern* pReqFont, int /*nFallbackLevel*/ )
{
if( !pReqFont )
{
ATSUClearStyle( maATSUStyle );
mpMacFontData = NULL;
return 0;
}
// store the requested device font entry
const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>( pReqFont->mpFontData );
mpMacFontData = pMacFont;
// convert pixel units (as seen by upper layers) to typographic point units
double fScaledAtsHeight = pReqFont->mfExactHeight;
// avoid Fixed16.16 overflows by limiting the ATS font size
static const float fMaxAtsHeight = 144.0;
if( fScaledAtsHeight <= fMaxAtsHeight )
mfFontScale = 1.0;
else
{
mfFontScale = fScaledAtsHeight / fMaxAtsHeight;
fScaledAtsHeight = fMaxAtsHeight;
}
Fixed fFixedSize = FloatToFixed( fScaledAtsHeight );
// enable bold-emulation if needed
Boolean bFakeBold = FALSE;
if( (pReqFont->GetWeight() >= WEIGHT_BOLD)
&& (pMacFont->GetWeight() < WEIGHT_SEMIBOLD) )
bFakeBold = TRUE;
// enable italic-emulation if needed
Boolean bFakeItalic = FALSE;
if( ((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE))
&& !((pMacFont->GetSlant() == ITALIC_NORMAL) || (pMacFont->GetSlant() == ITALIC_OBLIQUE)) )
bFakeItalic = TRUE;
// enable/disable antialiased text
mbNonAntialiasedText = pReqFont->mbNonAntialiased;
UInt32 nStyleRenderingOptions = kATSStyleNoOptions;
if( pReqFont->mbNonAntialiased )
nStyleRenderingOptions |= kATSStyleNoAntiAliasing;
// set horizontal/vertical mode
ATSUVerticalCharacterType aVerticalCharacterType = kATSUStronglyHorizontal;
if( pReqFont->mbVertical )
aVerticalCharacterType = kATSUStronglyVertical;
// prepare ATS-fontid as type matching to the kATSUFontTag request
ATSUFontID nFontID = static_cast<ATSUFontID>(pMacFont->GetFontId());
// update ATSU style attributes with requested font parameters
// TODO: no need to set styles which are already defaulted
const ATSUAttributeTag aTag[] =
{
kATSUFontTag,
kATSUSizeTag,
kATSUQDBoldfaceTag,
kATSUQDItalicTag,
kATSUStyleRenderingOptionsTag,
kATSUVerticalCharacterTag
};
const ByteCount aValueSize[] =
{
sizeof(ATSUFontID),
sizeof(fFixedSize),
sizeof(bFakeBold),
sizeof(bFakeItalic),
sizeof(nStyleRenderingOptions),
sizeof(aVerticalCharacterType)
};
const ATSUAttributeValuePtr aValue[] =
{
&nFontID,
&fFixedSize,
&bFakeBold,
&bFakeItalic,
&nStyleRenderingOptions,
&aVerticalCharacterType
};
static const int nTagCount = SAL_N_ELEMENTS(aTag);
OSStatus eStatus = ATSUSetAttributes( maATSUStyle, nTagCount,
aTag, aValueSize, aValue );
// reset ATSUstyle if there was an error
if( eStatus != noErr )
{
DBG_WARNING( "AquaSalGraphics::SetFont() : Could not set font attributes!\n");
ATSUClearStyle( maATSUStyle );
mpMacFontData = NULL;
return 0;
}
// prepare font stretching
const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag;
if( (pReqFont->mnWidth == 0) || (pReqFont->mnWidth == pReqFont->mnHeight) )
{
mfFontStretch = 1.0;
ATSUClearAttributes( maATSUStyle, 1, &aMatrixTag );
}
else
{
mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F );
const ATSUAttributeValuePtr aAttr = &aMatrix;
const ByteCount aMatrixBytes = sizeof(aMatrix);
eStatus = ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr );
DBG_ASSERT( (eStatus==noErr), "AquaSalGraphics::SetFont() : Could not set font matrix\n");
}
// prepare font rotation
mnATSUIRotation = FloatToFixed( pReqFont->mnOrientation / 10.0 );
#if OSL_DEBUG_LEVEL > 3
fprintf( stderr, "SetFont to (\"%s\", \"%s\", fontid=%d) for (\"%s\" \"%s\" weight=%d, slant=%d size=%dx%d orientation=%d)\n",
::rtl::OUStringToOString( pMacFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(),
::rtl::OUStringToOString( pMacFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(),
(int)nFontID,
::rtl::OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(),
::rtl::OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(),
pReqFont->GetWeight(),
pReqFont->GetSlant(),
pReqFont->mnHeight,
pReqFont->mnWidth,
pReqFont->mnOrientation);
#endif
return 0;
}
// -----------------------------------------------------------------------
const ImplFontCharMap* AquaSalGraphics::GetImplFontCharMap() const
{
if( !mpMacFontData )
return ImplFontCharMap::GetDefaultMap();
return mpMacFontData->GetImplFontCharMap();
}
bool AquaSalGraphics::GetImplFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
{
if( !mpMacFontData )
return false;
return mpMacFontData->GetImplFontCapabilities(rFontCapabilities);
}
// -----------------------------------------------------------------------
// fake a SFNT font directory entry for a font table
// see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html#Directory
static void FakeDirEntry( FourCharCode eFCC, ByteCount nOfs, ByteCount nLen,
const unsigned char* /*pData*/, unsigned char*& rpDest )
{
// write entry tag
rpDest[ 0] = (char)(eFCC >> 24);
rpDest[ 1] = (char)(eFCC >> 16);
rpDest[ 2] = (char)(eFCC >> 8);
rpDest[ 3] = (char)(eFCC >> 0);
// TODO: get entry checksum and write it
// not too important since the subsetter doesn't care currently
// for( pData+nOfs ... pData+nOfs+nLen )
// write entry offset
rpDest[ 8] = (char)(nOfs >> 24);
rpDest[ 9] = (char)(nOfs >> 16);
rpDest[10] = (char)(nOfs >> 8);
rpDest[11] = (char)(nOfs >> 0);
// write entry length
rpDest[12] = (char)(nLen >> 24);
rpDest[13] = (char)(nLen >> 16);
rpDest[14] = (char)(nLen >> 8);
rpDest[15] = (char)(nLen >> 0);
// advance to next entry
rpDest += 16;
}
bool AquaSalGraphics::GetRawFontData( const PhysicalFontFace* pFontData,
ByteVector& rBuffer, bool* pJustCFF )
{
const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData);
const ATSUFontID nFontId = static_cast<ATSUFontID>(pMacFont->GetFontId());
ATSFontRef rFont = FMGetATSFontRefFromFont( nFontId );
ByteCount nCffLen = 0;
OSStatus eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, 0, NULL, &nCffLen);
if( pJustCFF != NULL )
{
*pJustCFF = (eStatus == noErr) && (nCffLen > 0);
if( *pJustCFF )
{
rBuffer.resize( nCffLen );
eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[0], &nCffLen);
if( (eStatus != noErr) || (nCffLen <= 0) )
return false;
return true;
}
}
// get font table availability and size in bytes
ByteCount nHeadLen = 0;
eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, 0, NULL, &nHeadLen);
if( (eStatus != noErr) || (nHeadLen <= 0) )
return false;
ByteCount nMaxpLen = 0;
eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, 0, NULL, &nMaxpLen);
if( (eStatus != noErr) || (nMaxpLen <= 0) )
return false;
ByteCount nCmapLen = 0;
eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nCmapLen);
if( (eStatus != noErr) || (nCmapLen <= 0) )
return false;
ByteCount nNameLen = 0;
eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, 0, NULL, &nNameLen);
if( (eStatus != noErr) || (nNameLen <= 0) )
return false;
ByteCount nHheaLen = 0;
eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, 0, NULL, &nHheaLen);
if( (eStatus != noErr) || (nHheaLen <= 0) )
return false;
ByteCount nHmtxLen = 0;
eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, 0, NULL, &nHmtxLen);
if( (eStatus != noErr) || (nHmtxLen <= 0) )
return false;
// get the glyph outline tables
ByteCount nLocaLen = 0;
ByteCount nGlyfLen = 0;
if( (eStatus != noErr) || (nCffLen <= 0) )
{
eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, 0, NULL, &nLocaLen);
if( (eStatus != noErr) || (nLocaLen <= 0) )
return false;
eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, 0, NULL, &nGlyfLen);
if( (eStatus != noErr) || (nGlyfLen <= 0) )
return false;
}
ByteCount nPrepLen=0, nCvtLen=0, nFpgmLen=0;
if( nGlyfLen ) // TODO: reduce PDF size by making hint subsetting optional
{
eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, 0, NULL, &nPrepLen);
eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, 0, NULL, &nCvtLen);
eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, 0, NULL, &nFpgmLen);
}
// prepare a byte buffer for a fake font
int nTableCount = 7;
nTableCount += (nPrepLen>0) + (nCvtLen>0) + (nFpgmLen>0) + (nGlyfLen>0);
const ByteCount nFdirLen = 12 + 16*nTableCount;
ByteCount nTotalLen = nFdirLen;
nTotalLen += nHeadLen + nMaxpLen + nNameLen + nCmapLen;
if( nGlyfLen )
nTotalLen += nLocaLen + nGlyfLen;
else
nTotalLen += nCffLen;
nTotalLen += nHheaLen + nHmtxLen;
nTotalLen += nPrepLen + nCvtLen + nFpgmLen;
rBuffer.resize( nTotalLen );
// fake a SFNT font directory header
if( nTableCount < 16 )
{
int nLog2 = 0;
while( (nTableCount >> nLog2) > 1 ) ++nLog2;
rBuffer[ 1] = 1; // Win-TTF style scaler
rBuffer[ 5] = nTableCount; // table count
rBuffer[ 7] = nLog2*16; // searchRange
rBuffer[ 9] = nLog2; // entrySelector
rBuffer[11] = (nTableCount-nLog2)*16; // rangeShift
}
// get font table raw data and update the fake directory entries
ByteCount nOfs = nFdirLen;
unsigned char* pFakeEntry = &rBuffer[12];
eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nCmapLen, (void*)&rBuffer[nOfs], &nCmapLen);
FakeDirEntry( GetTag("cmap"), nOfs, nCmapLen, &rBuffer[0], pFakeEntry );
nOfs += nCmapLen;
if( nCvtLen ) {
eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, nCvtLen, (void*)&rBuffer[nOfs], &nCvtLen);
FakeDirEntry( GetTag("cvt "), nOfs, nCvtLen, &rBuffer[0], pFakeEntry );
nOfs += nCvtLen;
}
if( nFpgmLen ) {
eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, nFpgmLen, (void*)&rBuffer[nOfs], &nFpgmLen);
FakeDirEntry( GetTag("fpgm"), nOfs, nFpgmLen, &rBuffer[0], pFakeEntry );
nOfs += nFpgmLen;
}
if( nCffLen ) {
eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[nOfs], &nCffLen);
FakeDirEntry( GetTag("CFF "), nOfs, nCffLen, &rBuffer[0], pFakeEntry );
nOfs += nGlyfLen;
} else {
eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, nGlyfLen, (void*)&rBuffer[nOfs], &nGlyfLen);
FakeDirEntry( GetTag("glyf"), nOfs, nGlyfLen, &rBuffer[0], pFakeEntry );
nOfs += nGlyfLen;
eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, nLocaLen, (void*)&rBuffer[nOfs], &nLocaLen);
FakeDirEntry( GetTag("loca"), nOfs, nLocaLen, &rBuffer[0], pFakeEntry );
nOfs += nLocaLen;
}
eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, nHeadLen, (void*)&rBuffer[nOfs], &nHeadLen);
FakeDirEntry( GetTag("head"), nOfs, nHeadLen, &rBuffer[0], pFakeEntry );
nOfs += nHeadLen;
eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, nHheaLen, (void*)&rBuffer[nOfs], &nHheaLen);
FakeDirEntry( GetTag("hhea"), nOfs, nHheaLen, &rBuffer[0], pFakeEntry );
nOfs += nHheaLen;
eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, nHmtxLen, (void*)&rBuffer[nOfs], &nHmtxLen);
FakeDirEntry( GetTag("hmtx"), nOfs, nHmtxLen, &rBuffer[0], pFakeEntry );
nOfs += nHmtxLen;
eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, nMaxpLen, (void*)&rBuffer[nOfs], &nMaxpLen);
FakeDirEntry( GetTag("maxp"), nOfs, nMaxpLen, &rBuffer[0], pFakeEntry );
nOfs += nMaxpLen;
eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, nNameLen, (void*)&rBuffer[nOfs], &nNameLen);
FakeDirEntry( GetTag("name"), nOfs, nNameLen, &rBuffer[0], pFakeEntry );
nOfs += nNameLen;
if( nPrepLen ) {
eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, nPrepLen, (void*)&rBuffer[nOfs], &nPrepLen);
FakeDirEntry( GetTag("prep"), nOfs, nPrepLen, &rBuffer[0], pFakeEntry );
nOfs += nPrepLen;
}
DBG_ASSERT( (nOfs==nTotalLen), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalLen)");
return sal_True;
}
// -----------------------------------------------------------------------
void AquaSalGraphics::GetGlyphWidths( const PhysicalFontFace* pFontData, bool bVertical,
Int32Vector& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc )
{
rGlyphWidths.clear();
rUnicodeEnc.clear();
if( pFontData->IsSubsettable() )
{
ByteVector aBuffer;
if( !GetRawFontData( pFontData, aBuffer, NULL ) )
return;
// TODO: modernize psprint's horrible fontsubset C-API
// this probably only makes sense after the switch to another SCM
// that can preserve change history after file renames
// use the font subsetter to get the widths
TrueTypeFont* pSftFont = NULL;
int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont);
if( nRC != SF_OK )
return;
const int nGlyphCount = ::GetTTGlyphCount( pSftFont );
if( nGlyphCount > 0 )
{
// get glyph metrics
rGlyphWidths.resize(nGlyphCount);
std::vector<sal_uInt16> aGlyphIds(nGlyphCount);
for( int i = 0; i < nGlyphCount; i++ )
aGlyphIds[i] = static_cast<sal_uInt16>(i);
const TTSimpleGlyphMetrics* pGlyphMetrics = ::GetTTSimpleGlyphMetrics(
pSftFont, &aGlyphIds[0], nGlyphCount, bVertical );
if( pGlyphMetrics )
{
for( int i = 0; i < nGlyphCount; ++i )
rGlyphWidths[i] = pGlyphMetrics[i].adv;
free( (void*)pGlyphMetrics );
}
const ImplFontCharMap* pMap = mpMacFontData->GetImplFontCharMap();
DBG_ASSERT( pMap && pMap->GetCharCount(), "no charmap" );
pMap->AddReference(); // TODO: add and use RAII object instead
// get unicode<->glyph encoding
// TODO? avoid sft mapping by using the pMap itself
int nCharCount = pMap->GetCharCount();
sal_uInt32 nChar = pMap->GetFirstChar();
for(; --nCharCount >= 0; nChar = pMap->GetNextChar( nChar ) )
{
if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars
break;
sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar);
sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar, bVertical );
if( nGlyph > 0 )
rUnicodeEnc[ nUcsChar ] = nGlyph;
}
pMap->DeReference(); // TODO: add and use RAII object instead
}
::CloseTTFont( pSftFont );
}
else if( pFontData->IsEmbeddable() )
{
// get individual character widths
OSL_FAIL("not implemented for non-subsettable fonts!\n");
}
}
// -----------------------------------------------------------------------
const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector(
const PhysicalFontFace*, const Ucs2OStrMap** /*ppNonEncoded*/ )
{
return NULL;
}
// -----------------------------------------------------------------------
const void* AquaSalGraphics::GetEmbedFontData( const PhysicalFontFace*,
const sal_Ucs* /*pUnicodes*/,
sal_Int32* /*pWidths*/,
FontSubsetInfo&,
long* /*pDataLen*/ )
{
return NULL;
}
// -----------------------------------------------------------------------
void AquaSalGraphics::FreeEmbedFontData( const void* pData, long /*nDataLen*/ )
{
// TODO: implementing this only makes sense when the implementation of
// AquaSalGraphics::GetEmbedFontData() returns non-NULL
(void)pData;
DBG_ASSERT( (pData!=NULL), "AquaSalGraphics::FreeEmbedFontData() is not implemented\n");
}
// -----------------------------------------------------------------------
SystemFontData AquaSalGraphics::GetSysFontData( int /* nFallbacklevel */ ) const
{
SystemFontData aSysFontData;
OSStatus err;
aSysFontData.nSize = sizeof( SystemFontData );
// NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks.
ATSUFontID fontId;
err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(fontId), &fontId, 0 );
if (err) fontId = 0;
aSysFontData.aATSUFontID = (void *) fontId;
Boolean bFbold;
err = ATSUGetAttribute( maATSUStyle, kATSUQDBoldfaceTag, sizeof(bFbold), &bFbold, 0 );
if (err) bFbold = FALSE;
aSysFontData.bFakeBold = (bool) bFbold;
Boolean bFItalic;
err = ATSUGetAttribute( maATSUStyle, kATSUQDItalicTag, sizeof(bFItalic), &bFItalic, 0 );
if (err) bFItalic = FALSE;
aSysFontData.bFakeItalic = (bool) bFItalic;
ATSUVerticalCharacterType aVerticalCharacterType;
err = ATSUGetAttribute( maATSUStyle, kATSUVerticalCharacterTag, sizeof(aVerticalCharacterType), &aVerticalCharacterType, 0 );
if (!err && aVerticalCharacterType == kATSUStronglyVertical) {
aSysFontData.bVerticalCharacterType = true;
} else {
aSysFontData.bVerticalCharacterType = false;
}
aSysFontData.bAntialias = !mbNonAntialiasedText;
return aSysFontData;
}
// -----------------------------------------------------------------------
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */