diff options
author | Khaled Hosny <khaledhosny@eglug.org> | 2013-06-01 17:12:29 +0200 |
---|---|---|
committer | Khaled Hosny <khaledhosny@eglug.org> | 2013-06-03 15:11:53 +0200 |
commit | ea8422d42e838e7ddfeb8d9f77f3ecedecb29ce8 (patch) | |
tree | 8f389c873fe7297fdf5f4dd59d631f95e583bcc6 /vcl | |
parent | cacc516693b879c08bd3b1963eeed9c38928615f (diff) |
Cherry-pick Core Text port from AOO
Manually picked from:
http://svn.apache.org/viewvc?view=revision&sortby=log&revision=1480384
Author: hdu
Date: Wed May 8 18:14:34 2013 UTC (3 weeks, 2 days ago)
Changed paths: 55
Log Message: #i122195# add VCL support for 64bit OSX>=10.7
Change-Id: Ia799d7fdeb257e9bfd311338dcfdf97caf9d191f
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/coretext/ctfonts.cxx | 648 | ||||
-rw-r--r-- | vcl/coretext/ctfonts.hxx | 90 | ||||
-rw-r--r-- | vcl/coretext/ctlayout.cxx | 500 | ||||
-rw-r--r-- | vcl/coretext/salgdi2.cxx | 918 | ||||
-rw-r--r-- | vcl/inc/aqua/salgdi.h | 2 | ||||
-rw-r--r-- | vcl/inc/coretext/salgdi2.h | 413 | ||||
-rw-r--r-- | vcl/inc/impfont.hxx | 3 | ||||
-rw-r--r-- | vcl/inc/quartz/salgdicommon.hxx | 5 | ||||
-rw-r--r-- | vcl/source/gdi/outdev3.cxx | 8 |
9 files changed, 2586 insertions, 1 deletions
diff --git a/vcl/coretext/ctfonts.cxx b/vcl/coretext/ctfonts.cxx new file mode 100644 index 000000000000..5eb2d3465ea5 --- /dev/null +++ b/vcl/coretext/ctfonts.cxx @@ -0,0 +1,648 @@ +/************************************************************** + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "impfont.hxx" +#include "outfont.hxx" +#include "sallayout.hxx" + +#include "aqua/salinst.h" +#include "aqua/saldata.hxx" +#include "coretext/salgdi2.h" +#include "ctfonts.hxx" + +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/matrix/b2dhommatrix.hxx" + +#ifndef DISABLE_CORETEXT_DYNLOAD +#include <dlfcn.h> +#endif + +// ======================================================================= + +// CoreText specific physically available font face +class CTFontData +: public ImplMacFontData +{ +public: + explicit CTFontData( const ImplDevFontAttributes&, sal_IntPtr nFontId ); + virtual ~CTFontData( void ); + virtual ImplFontData* Clone( void ) const; + + virtual ImplMacTextStyle* CreateMacTextStyle( const ImplFontSelectData& ) const; + virtual ImplFontEntry* CreateFontInstance( /*const*/ ImplFontSelectData& ) const; + virtual int GetFontTable( const char pTagName[5], unsigned char* ) const; +}; + +// ======================================================================= + +class CTFontList +: public SystemFontList +{ +public: + explicit CTFontList( void ); + virtual ~CTFontList( void ); + + bool Init( void ); + void AddFont( CTFontData* ); + + virtual void AnnounceFonts( ImplDevFontList& ) const; + virtual ImplMacFontData* GetFontDataFromId( sal_IntPtr ) const; + +private: + CTFontCollectionRef mpCTFontCollection; + CFArrayRef mpCTFontArray; + + typedef std::hash_map<sal_IntPtr,CTFontData*> CTFontContainer; + CTFontContainer maFontContainer; +}; + +// ======================================================================= + +CTTextStyle::CTTextStyle( const ImplFontSelectData& rFSD ) +: ImplMacTextStyle( rFSD ) +, mpStyleDict( NULL ) +{ + mpFontData = (CTFontData*)rFSD.mpFontData; + const ImplFontSelectData* const pReqFont = &rFSD; + + double fScaledFontHeight = pReqFont->mfExactHeight; +#if 0 // TODO: does CoreText need font size limiting??? + static const float fMaxFontHeight = 144.0; // TODO: is there a limit for CoreText? + if( fScaledFontHeight > fMaxFontHeight ) + { + mfFontScale = fScaledFontHeight / fMaxFontHeight; + fScaledFontHeight = fMaxFontHeight; + } +#endif + + // convert font rotation to radian + mfFontRotation = pReqFont->mnOrientation * (M_PI / 1800.0); + + // handle font stretching if any + const CGAffineTransform* pMatrix = NULL; + CGAffineTransform aMatrix; + if( (pReqFont->mnWidth != 0) && (pReqFont->mnWidth != pReqFont->mnHeight) ) + { + mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight; + aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F ); + pMatrix = &aMatrix; + } + + // create the style object for CoreText font attributes + static const CFIndex nMaxDictSize = 16; // TODO: does this really suffice? + mpStyleDict = CFDictionaryCreateMutable( NULL, nMaxDictSize, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); + + // set some default styles: no kerning, regular ligatures + static const CGFloat fValZero = 0.0; + CFNumberRef pCFFloatNumZero = CFNumberCreate( NULL, kCFNumberFloatType, &fValZero ); + CFDictionarySetValue( mpStyleDict, kCTKernAttributeName, pCFFloatNumZero ); + CFRelease( pCFFloatNumZero); + static const int nValOne = 1; + CFNumberRef pCFIntNumOne = CFNumberCreate( NULL, kCFNumberIntType, &nValOne ); + CFDictionarySetValue( mpStyleDict, kCTLigatureAttributeName, pCFIntNumOne ); + CFRelease( pCFIntNumOne); + CFBooleanRef pCFVertBool = pReqFont->mbVertical ? kCFBooleanTrue : kCFBooleanFalse; + CFDictionarySetValue( mpStyleDict, kCTVerticalFormsAttributeName, pCFVertBool ); + + CTFontDescriptorRef pFontDesc = (CTFontDescriptorRef)mpFontData->GetFontId(); + CTFontRef pNewCTFont = CTFontCreateWithFontDescriptor( pFontDesc, fScaledFontHeight, pMatrix ); + CFDictionarySetValue( mpStyleDict, kCTFontAttributeName, pNewCTFont ); + CFRelease( pNewCTFont); + +#if 0 // LastResort is implicit in CoreText's font cascading + const void* aGFBDescriptors[] = { CTFontDescriptorCreateWithNameAndSize( CFSTR("LastResort"), 0) }; // TODO: use the full GFB list + const int nGfbCount = sizeof(aGFBDescriptors) / sizeof(*aGFBDescriptors); + CFArrayRef pGfbList = CFArrayCreate( NULL, aGFBDescriptors, nGfbCount, &kCFTypeArrayCallBacks); + CFDictionaryAddValue( mpStyleDict, kCTFontCascadeListAttribute, pGfbList); + CFRelease( pGfbList); +#endif +} + +// ----------------------------------------------------------------------- + +CTTextStyle::~CTTextStyle( void ) +{ + if( mpStyleDict ) + CFRelease( mpStyleDict ); +} + +// ----------------------------------------------------------------------- + +void CTTextStyle::GetFontMetric( float fDPIY, ImplFontMetricData& rMetric ) const +{ + // get the matching CoreText font handle + // TODO: is it worth it to cache the CTFontRef in SetFont() and reuse it here? + CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ); + + const double fPixelSize = (mfFontScale * fDPIY); + rMetric.mnAscent = lrint( CTFontGetAscent( aCTFontRef ) * fPixelSize); + rMetric.mnDescent = lrint( CTFontGetDescent( aCTFontRef ) * fPixelSize); + rMetric.mnIntLeading = lrint( CTFontGetLeading( aCTFontRef ) * fPixelSize); + rMetric.mnExtLeading = 0; + // 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 + rMetric.mnWidth = lrint( CTFontGetSize( aCTFontRef ) * fPixelSize * mfFontStretch); + + // all CoreText fonts are scalable + rMetric.mbScalableFont = true; + // TODO: check if any kerning is supported + rMetric.mbKernableFont = true; +} + +// ----------------------------------------------------------------------- + +bool CTTextStyle::GetGlyphBoundRect( sal_GlyphId nGlyphId, Rectangle& rRect ) const +{ + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK; // NOTE: CoreText handles glyph fallback itself + CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ); + + const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; // TODO: horz/vert + const CGRect aCGRect = rCT.FontGetBoundingRectsForGlyphs( aCTFontRef, aFontOrientation, &nCGGlyph, NULL, 1 ); + + rRect.Left() = lrint( mfFontScale * aCGRect.origin.x ); + rRect.Top() = lrint( mfFontScale * aCGRect.origin.y ); + rRect.Right() = lrint( mfFontScale * (aCGRect.origin.x + aCGRect.size.width) ); + rRect.Bottom() = lrint( mfFontScale * (aCGRect.origin.y + aCGRect.size.height) ); + return true; +} + +// ----------------------------------------------------------------------- + +// callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline() +struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; }; + +static void MyCGPathApplierFunc( void* pData, const CGPathElement* pElement ) +{ + basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; + const int nPointCount = rPolygon.count(); + + switch( pElement->type ) + { + case kCGPathElementCloseSubpath: + case kCGPathElementMoveToPoint: + if( nPointCount > 0 ) { + static_cast<GgoData*>(pData)->mpPolyPoly->append( rPolygon ); + rPolygon.clear(); + } + // fall through for kCGPathElementMoveToPoint: + if( pElement->type != kCGPathElementMoveToPoint ) + break; + case kCGPathElementAddLineToPoint: + rPolygon.append( basegfx::B2DPoint( +pElement->points[0].x, -pElement->points[0].y ) ); + break; + case kCGPathElementAddCurveToPoint: + rPolygon.append( basegfx::B2DPoint( +pElement->points[2].x, -pElement->points[2].y ) ); + rPolygon.setNextControlPoint( nPointCount-1, basegfx::B2DPoint( pElement->points[0].x, -pElement->points[0].y ) ); + rPolygon.setPrevControlPoint( nPointCount+0, basegfx::B2DPoint( pElement->points[1].x, -pElement->points[1].y ) ); + break; + case kCGPathElementAddQuadCurveToPoint: { + const basegfx::B2DPoint aStartPt = rPolygon.getB2DPoint( nPointCount-1 ); + const basegfx::B2DPoint aCtrPt1( (aStartPt.getX() + 2* pElement->points[0].x) / 3.0, + (aStartPt.getY() - 2 * pElement->points[0].y) / 3.0 ); + const basegfx::B2DPoint aCtrPt2( (+2 * +pElement->points[0].x + pElement->points[1].x) / 3.0, + (-2 * pElement->points[0].y - pElement->points[1].y) / 3.0 ); + rPolygon.append( basegfx::B2DPoint( +pElement->points[1].x, -pElement->points[1].y ) ); + rPolygon.setNextControlPoint( nPointCount-1, aCtrPt1 ); + rPolygon.setPrevControlPoint( nPointCount+0, aCtrPt2 ); + } break; + } +} + +bool CTTextStyle::GetGlyphOutline( sal_GlyphId nGlyphId, basegfx::B2DPolyPolygon& rResult ) const +{ + rResult.clear(); + + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + // TODO: GF_FONTMASK if using non-native glyph fallback + CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK; + CTFontRef pCTFont = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ); + CGPathRef xPath = rCT.FontCreatePathForGlyph( pCTFont, nCGGlyph, NULL ); + + GgoData aGgoData; + aGgoData.mpPolyPoly = &rResult; + CGPathApply( xPath, (void*)&aGgoData, MyCGPathApplierFunc ); +#if 0 // TODO: does OSX ensure that the last polygon is always closed? + const CGPathElement aClosingElement = { kCGPathElementCloseSubpath, NULL }; + MyCGPathApplierFunc( (void*)&aGgoData, &aClosingElement ); +#endif + + // apply the font scale + if( mfFontScale != 1.0 ) { + basegfx::B2DHomMatrix aScale; + aScale.scale( +mfFontScale, +mfFontScale ); + rResult.transform( aScale ); + } + + return true; +} + +// ----------------------------------------------------------------------- + +void CTTextStyle::SetTextColor( const RGBAColor& rColor ) +{ +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + CGColorRef pCGColor = CGColorCreateGenericRGB( rColor.GetRed(), + rColor.GetGreen(), rColor.GetBlue(), rColor.GetAlpha() ); +#else // for builds on OSX 10.4 SDK + const CGColorSpaceRef pCGColorSpace = GetSalData()->mxRGBSpace; + CGColorRef pCGColor = CGColorCreate( pCGColorSpace, rColor.AsArray() ); +#endif + CFDictionarySetValue( mpStyleDict, kCTForegroundColorAttributeName, pCGColor ); + CFRelease( pCGColor); +} + +// ======================================================================= + +CTFontData::CTFontData( const ImplDevFontAttributes& rDFA, sal_IntPtr nFontId ) +: ImplMacFontData( rDFA, nFontId ) +{} + +// ----------------------------------------------------------------------- + +CTFontData::~CTFontData( void ) +{ + // TODO: any resources to release? +} + +// ----------------------------------------------------------------------- + +ImplFontData* CTFontData::Clone( void ) const +{ + return new CTFontData( *this); +} + +// ----------------------------------------------------------------------- + +ImplMacTextStyle* CTFontData::CreateMacTextStyle( const ImplFontSelectData& rFSD ) const +{ + return new CTTextStyle( rFSD); +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* CTFontData::CreateFontInstance( /*const*/ ImplFontSelectData& rFSD ) const +{ + return new ImplFontEntry( rFSD); +} + +// ----------------------------------------------------------------------- + +int CTFontData::GetFontTable( const char pTagName[5], unsigned char* pResultBuf ) const +{ + DBG_ASSERT( aTagName[4]=='\0', "CTFontData::GetFontTable with invalid tagname!\n" ); + + const CTFontTableTag nTagCode = (pTagName[0]<<24) + (pTagName[1]<<16) + (pTagName[2]<<8) + (pTagName[3]<<0); + + // get the raw table length + CTFontDescriptorRef pFontDesc = reinterpret_cast<CTFontDescriptorRef>( GetFontId()); + CTFontRef rCTFont = CTFontCreateWithFontDescriptor( pFontDesc, 0.0, NULL); + CFDataRef pDataRef = CTFontCopyTable( rCTFont, nTagCode, kCTFontTableOptionExcludeSynthetic); + CFRelease( rCTFont); + if( !pDataRef) + return 0; + + const CFIndex nByteLength = CFDataGetLength( pDataRef); + + // get the raw table data if requested + if( pResultBuf && (nByteLength > 0)) + { + const CFRange aFullRange = CFRangeMake( 0, nByteLength); + CFDataGetBytes( pDataRef, aFullRange, (UInt8*)pResultBuf); + } + + CFRelease( pDataRef); + + return (int)nByteLength; +} + +// ======================================================================= + +static void CTFontEnumCallBack( const void* pValue, void* pContext ) +{ + CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue); + + // all CoreText fonts are device fonts that can rotate just fine + ImplDevFontAttributes rDFA; + rDFA.mbOrientation = true; + rDFA.mbDevice = true; + rDFA.mnQuality = 0; + + // reset the font attributes + rDFA.meFamily = FAMILY_DONTKNOW; + rDFA.mePitch = PITCH_VARIABLE; + rDFA.meWidthType = WIDTH_NORMAL; + rDFA.meWeight = WEIGHT_NORMAL; + rDFA.meItalic = ITALIC_NONE; + rDFA.mbSymbolFlag = false; + + // all scalable fonts on this platform are subsettable + rDFA.mbEmbeddable = false; + rDFA.mbSubsettable = true; + + // get font name + // TODO: use kCTFontDisplayNameAttribute instead??? + CFStringRef pFamilyName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute ); + rDFA.maName = GetOUString( pFamilyName ); + // get font style + CFStringRef pStyleName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute ); + rDFA.maStyleName = GetOUString( pStyleName ); + + // get font-enabled status + int bFontEnabled = FALSE; + CFNumberRef pFontEnabled = (CFNumberRef)CTFontDescriptorCopyAttribute( pFD, kCTFontEnabledAttribute ); + CFNumberGetValue( pFontEnabled, kCFNumberIntType, &bFontEnabled ); + + // get font attributes + CFDictionaryRef pAttrDict = (CFDictionaryRef)CTFontDescriptorCopyAttribute( pFD, kCTFontTraitsAttribute ); + + // get symbolic trait + // TODO: use other traits such as MonoSpace/Condensed/Expanded or Vertical too + SInt64 nSymbolTrait = 0; + CFNumberRef pSymbolNum = NULL; + if( CFDictionaryGetValueIfPresent( pAttrDict, kCTFontSymbolicTrait, (const void**)&pSymbolNum ) ) { + CFNumberGetValue( pSymbolNum, kCFNumberSInt64Type, &nSymbolTrait ); + rDFA.mbSymbolFlag = ((nSymbolTrait & kCTFontClassMaskTrait) == kCTFontSymbolicClass); + } + + // get the font weight + double fWeight = 0; + CFNumberRef pWeightNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWeightTrait ); + CFNumberGetValue( pWeightNum, kCFNumberDoubleType, &fWeight ); + int nInt = WEIGHT_NORMAL; + if( fWeight > 0 ) { + nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_BLACK - WEIGHT_NORMAL)/0.68)); + if( nInt > WEIGHT_BLACK ) + nInt = WEIGHT_BLACK; + } else if( fWeight < 0 ) { + nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_NORMAL - WEIGHT_THIN)/0.9)); + if( nInt < WEIGHT_THIN ) + nInt = WEIGHT_THIN; + } + rDFA.meWeight = (FontWeight)nInt; + + // get the font slant + double fSlant = 0; + CFNumberRef pSlantNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait ); + CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant ); + if( fSlant >= 0.035 ) + rDFA.meItalic = ITALIC_NORMAL; + + // get width trait + double fWidth = 0; + CFNumberRef pWidthNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWidthTrait ); + CFNumberGetValue( pWidthNum, kCFNumberDoubleType, &fWidth ); + nInt = WIDTH_NORMAL; + if( fWidth > 0 ) { + nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_ULTRA_EXPANDED - WIDTH_NORMAL)/0.4)); + if( nInt > WIDTH_ULTRA_EXPANDED ) + nInt = WIDTH_ULTRA_EXPANDED; + } else if( fWidth < 0 ) { + nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_NORMAL - WIDTH_ULTRA_CONDENSED)/0.5)); + if( nInt < WIDTH_ULTRA_CONDENSED ) + nInt = WIDTH_ULTRA_CONDENSED; + } + rDFA.meWidthType = (FontWidth)nInt; + + // release the attribute dict that we had copied + CFRelease( pAttrDict ); + + // TODO? also use the HEAD table if available to get more attributes +// CFDataRef CTFontCopyTable( CTFontRef, kCTFontTableHead, /*kCTFontTableOptionNoOptions*/kCTFontTableOptionExcludeSynthetic ); + +#if (OSL_DEBUG_LEVEL >= 1) + // update font attributes using the font's postscript name + ImplDevFontAttributes rDFA2; + CTFontRef pFont = CTFontCreateWithFontDescriptor( pFD, 0.0, NULL ); + CFStringRef pPSName = CTFontCopyPostScriptName( pFont ); + const String aPSName = GetOUString( pPSName ); + + rDFA2.mbSymbolFlag = false; + rDFA2.mePitch = PITCH_VARIABLE; + rDFA2.meWidthType = WIDTH_NORMAL; + rDFA2.meWeight = WEIGHT_NORMAL; + rDFA2.meItalic = ITALIC_NONE; + + UpdateAttributesFromPSName( aPSName, rDFA2 ); + CFRelease( pPSName ); + CFRelease( pFont ); + + // show the font details and compare the CTFontDescriptor vs. PSName traits + char cMatch = (rDFA.mbSymbolFlag==rDFA2.mbSymbolFlag); + cMatch &= (rDFA.meWeight==rDFA2.meWeight); + cMatch &= ((rDFA.meItalic==ITALIC_NONE) == (rDFA2.meItalic==ITALIC_NONE)); + cMatch &= (rDFA.meWidthType==rDFA2.meWidthType); + cMatch = cMatch ? '.' : '#'; + + char aFN[256], aSN[256]; + CFStringGetCString( pFamilyName, aFN, sizeof(aFN), kCFStringEncodingUTF8 ); + CFStringGetCString( pStyleName, aSN, sizeof(aSN), kCFStringEncodingUTF8 ); + + const ByteString aPSCName( aPSName, RTL_TEXTENCODING_UTF8 ); + const char* aPN = aPSCName.GetBuffer(); + printf("\tCTFont_%d%x%d%d_%c_%d%x%d%d ena=%d s=%02d b=%+.2f i=%+.2f w=%+.2f (\"%s\", \"%s\", \"%s\")\n", + (int)rDFA.mbSymbolFlag,(int)rDFA.meWeight,(int)rDFA.meItalic,(int)rDFA.meWidthType, + cMatch, + (int)rDFA2.mbSymbolFlag,(int)rDFA2.meWeight,(int)rDFA2.meItalic,(int)rDFA2.meWidthType, + bFontEnabled, + (int)(nSymbolTrait>>kCTFontClassMaskShift),fWeight,fSlant,fWidth,aFN,aSN,aPN); +#endif // (OSL_DEBUG_LEVEL >= 1) + + if( bFontEnabled) + { + const sal_IntPtr nFontId = (sal_IntPtr)pValue; + CTFontData* pFontData = new CTFontData( rDFA, nFontId ); + CTFontList* pFontList = (CTFontList*)pContext; + pFontList->AddFont( pFontData ); + } +} + +// ======================================================================= + +CTFontList::CTFontList() +: mpCTFontCollection( NULL ) +, mpCTFontArray( NULL ) +{} + +// ----------------------------------------------------------------------- + +CTFontList::~CTFontList() +{ + CTFontContainer::const_iterator it = maFontContainer.begin(); + for(; it != maFontContainer.end(); ++it ) + delete (*it).second; + maFontContainer.clear(); + + if( mpCTFontArray ) + CFRelease( mpCTFontArray ); + if( mpCTFontCollection ) + CFRelease( mpCTFontCollection ); +} + +// ----------------------------------------------------------------------- + +void CTFontList::AddFont( CTFontData* pFontData ) +{ + sal_IntPtr nFontId = pFontData->GetFontId(); + maFontContainer[ nFontId ] = pFontData; +} + +// ----------------------------------------------------------------------- + +void CTFontList::AnnounceFonts( ImplDevFontList& rFontList ) const +{ + CTFontContainer::const_iterator it = maFontContainer.begin(); + for(; it != maFontContainer.end(); ++it ) + rFontList.Add( (*it).second->Clone() ); +} + +// ----------------------------------------------------------------------- + +ImplMacFontData* CTFontList::GetFontDataFromId( sal_IntPtr nFontId ) const +{ + CTFontContainer::const_iterator it = maFontContainer.find( nFontId ); + if( it == maFontContainer.end() ) + return NULL; + return (*it).second; +} + +// ----------------------------------------------------------------------- + +bool CTFontList::Init( void ) +{ +#ifndef DISABLE_CORETEXT_DYNLOAD + // check availability of the CoreText API + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + if( !rCT.IsActive() ) + return false; +#endif // DISABLE_CORETEXT_DYNLOAD + + // enumerate available system fonts + static const int nMaxDictEntries = 8; + CFMutableDictionaryRef pCFDict = CFDictionaryCreateMutable( NULL, + nMaxDictEntries, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); + CFDictionaryAddValue( pCFDict, kCTFontCollectionRemoveDuplicatesOption, kCFBooleanTrue ); + mpCTFontCollection = rCT.FontCollectionCreateFromAvailableFonts( pCFDict ); + CFRelease( pCFDict ); + + mpCTFontArray = rCT.FontCollectionCreateMatchingFontDescriptors( mpCTFontCollection ); + const int nFontCount = CFArrayGetCount( mpCTFontArray ); + const CFRange aFullRange = CFRangeMake( 0, nFontCount ); + CFArrayApplyFunction( mpCTFontArray, aFullRange, CTFontEnumCallBack, this ); + + return true; +} + +// ======================================================================= + +#ifndef DISABLE_CORETEXT_DYNLOAD + +DynCoreTextSyms::DynCoreTextSyms( void ) +{ + mbIsActive = false; + + // check if CoreText has been explicitely disabled + const char* pEnvStr = getenv( "SAL_DISABLE_CORETEXT"); + if( pEnvStr && (pEnvStr[0] != '0') ) + return; + + // check CoreText version + GetCoreTextVersion = (uint32_t(*)(void))dlsym( RTLD_DEFAULT, "CTGetCoreTextVersion"); + if( !GetCoreTextVersion) return; + + const uint32_t nCTVersion = GetCoreTextVersion(); + static const uint32_t mykCTVersionNumber10_5 = 0x00020000; + if( nCTVersion < mykCTVersionNumber10_5) + return; + + // load CoreText symbols dynamically + LineGetTrailingWhitespaceWidth = (double(*)(CTLineRef))dlsym( RTLD_DEFAULT, "CTLineGetTrailingWhitespaceWidth"); + if( !LineGetTrailingWhitespaceWidth) return; + + LineCreateJustifiedLine = (CTLineRef(*)(CTLineRef,CGFloat,double))dlsym( RTLD_DEFAULT, "CTLineCreateJustifiedLine"); + if( !LineCreateJustifiedLine) return; + + LineGetOffsetForStringIndex = (CGFloat(*)(CTLineRef,CFIndex,CGFloat*))dlsym( RTLD_DEFAULT, "CTLineGetOffsetForStringIndex"); + if( !LineGetOffsetForStringIndex) return; + + LineGetGlyphRuns = (CFArrayRef(*)(CTLineRef))dlsym( RTLD_DEFAULT, "CTLineGetGlyphRuns"); + if( !LineGetGlyphRuns) return; + + RunGetGlyphCount = (CFIndex(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetGlyphCount"); + if( !RunGetGlyphCount) return; + + RunGetGlyphsPtr = (const CGGlyph*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetGlyphsPtr"); + if( !RunGetGlyphsPtr) return; + + RunGetPositionsPtr = (const CGPoint*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetPositionsPtr"); + if( !RunGetPositionsPtr) return; + + RunGetAdvancesPtr = (const CGSize*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetAdvancesPtr"); + if( !RunGetAdvancesPtr) return; + + RunGetStringIndicesPtr = (const CFIndex*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetStringIndicesPtr"); + if( !RunGetStringIndicesPtr) return; + + FontCollectionCreateFromAvailableFonts = (CTFontCollectionRef(*)(CFDictionaryRef))dlsym( RTLD_DEFAULT, "CTFontCollectionCreateFromAvailableFonts"); + if( !FontCollectionCreateFromAvailableFonts) return; + + FontCollectionCreateMatchingFontDescriptors = (CFArrayRef(*)(CTFontCollectionRef))dlsym( RTLD_DEFAULT, "CTFontCollectionCreateMatchingFontDescriptors"); + if( !FontCollectionCreateMatchingFontDescriptors) return; + + FontCreatePathForGlyph = (CGPathRef(*)(CTFontRef,CGGlyph,const CGAffineTransform*))dlsym( RTLD_DEFAULT, "CTFontCreatePathForGlyph"); + if( !FontCreatePathForGlyph) return; + + FontGetBoundingRectsForGlyphs = (CGRect(*)(CTFontRef,CTFontOrientation,CGGlyph*,CGRect*,CFIndex))dlsym( RTLD_DEFAULT, "CTFontGetBoundingRectsForGlyphs"); + if( !FontGetBoundingRectsForGlyphs) return; + + mbIsActive = true; +} + +// ----------------------------------------------------------------------- + +const DynCoreTextSyms& DynCoreTextSyms::get( void ) +{ + static DynCoreTextSyms aCT; + return aCT; +} + +#endif // DISABLE_CORETEXT_DYNLOAD + +// ======================================================================= + +SystemFontList* GetCoretextFontList( void ) +{ + CTFontList* pList = new CTFontList(); + if( !pList->Init() ) { + delete pList; + return NULL; + } + + return pList; +} + +// ======================================================================= + diff --git a/vcl/coretext/ctfonts.hxx b/vcl/coretext/ctfonts.hxx new file mode 100644 index 000000000000..c62c1f963fca --- /dev/null +++ b/vcl/coretext/ctfonts.hxx @@ -0,0 +1,90 @@ +/************************************************************** + * + * 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. + * + *************************************************************/ + +#include "coretext/salgdi2.h" +#include "sallayout.hxx" + +#include <ApplicationServices/ApplicationServices.h> + +// ======================================================================= + +class CTTextStyle +: public ImplMacTextStyle +{ +public: + explicit CTTextStyle( const ImplFontSelectData& ); + virtual ~CTTextStyle( void ); + + virtual SalLayout* GetTextLayout( void ) const; + + virtual void GetFontMetric( float fDPIY, ImplFontMetricData& ) const; + virtual bool GetGlyphBoundRect( sal_GlyphId, Rectangle& ) const; + virtual bool GetGlyphOutline( sal_GlyphId, basegfx::B2DPolyPolygon& ) const; + + virtual void SetTextColor( const RGBAColor& ); + +private: + /// CoreText text style object + CFMutableDictionaryRef mpStyleDict; + + friend class CTLayout; + CFMutableDictionaryRef GetStyleDict( void ) const { return mpStyleDict; } +}; + +// ======================================================================= + +#ifndef DISABLE_CORETEXT_DYNLOAD +// the CoreText symbols may need to be loaded dynamically +// since platform targets like OSX 10.4 do not provide all required symbols +// TODO: avoid the dlsym stuff if the target platform is >= OSX10.5 + +class DynCoreTextSyms +{ +public: + // dynamic symbols to access the CoreText API + uint32_t (*GetCoreTextVersion)(void); + CTFontCollectionRef (*FontCollectionCreateFromAvailableFonts)(CFDictionaryRef); + CFArrayRef (*FontCollectionCreateMatchingFontDescriptors)(CTFontCollectionRef); + CGPathRef (*FontCreatePathForGlyph)(CTFontRef,CGGlyph,const CGAffineTransform*); + CGRect (*FontGetBoundingRectsForGlyphs)(CTFontRef,CTFontOrientation,CGGlyph*,CGRect*,CFIndex); + CTLineRef (*LineCreateJustifiedLine)(CTLineRef,CGFloat,double); + double (*LineGetTrailingWhitespaceWidth)(CTLineRef); + CGFloat (*LineGetOffsetForStringIndex)(CTLineRef,CFIndex,CGFloat*); + CFArrayRef (*LineGetGlyphRuns)(CTLineRef); + CFIndex (*RunGetGlyphCount)(CTRunRef); + const CGGlyph* (*RunGetGlyphsPtr)(CTRunRef); + const CGPoint* (*RunGetPositionsPtr)(CTRunRef); + const CGSize* (*RunGetAdvancesPtr)(CTRunRef); + const CFIndex * (*RunGetStringIndicesPtr)(CTRunRef); + + // singleton helpers + static const DynCoreTextSyms& get( void ); + bool IsActive( void ) const { return mbIsActive; } + +private: + explicit DynCoreTextSyms( void ); + bool mbIsActive; +}; + +#endif // DISABLE_CORETEXT_DYNLOAD + +// ======================================================================= + diff --git a/vcl/coretext/ctlayout.cxx b/vcl/coretext/ctlayout.cxx new file mode 100644 index 000000000000..d232910a41c3 --- /dev/null +++ b/vcl/coretext/ctlayout.cxx @@ -0,0 +1,500 @@ +/************************************************************** + * + * 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. + * + *************************************************************/ + +//#include "salgdi.hxx" +#include "tools/debug.hxx" + +#include "ctfonts.hxx" + +// ======================================================================= + +class CTLayout +: public SalLayout +{ +public: + explicit CTLayout( const CTTextStyle* ); + virtual ~CTLayout( void ); + + virtual bool LayoutText( ImplLayoutArgs& ); + virtual void AdjustLayout( ImplLayoutArgs& ); + virtual void DrawText( SalGraphics& ) const; + + virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, + sal_Int32* pGlyphAdvances, int* pCharIndexes ) const; + + virtual long GetTextWidth() const; + virtual long FillDXArray( sal_Int32* pDXArray ) const; + virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; + virtual void GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const; + virtual bool GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const; + virtual bool GetBoundRect( SalGraphics&, Rectangle& ) const; + + const ImplFontData* GetFallbackFontData( sal_GlyphId ) const; + + virtual void InitFont( void) const; + virtual void MoveGlyph( int nStart, long nNewXPos ); + virtual void DropGlyph( int nStart ); + virtual void Simplify( bool bIsBase ); + +private: + const CTTextStyle* const mpTextStyle; + + // CoreText specific objects + CFAttributedStringRef mpAttrString; + CTLineRef mpCTLine; + + int mnCharCount; // ==mnEndCharPos-mnMinCharPos + int mnTrailingSpaces; + + // to prevent overflows + // font requests get size limited by downscaling huge fonts + // in these cases the font scale becomes something bigger than 1.0 + float mfFontScale; // TODO: does CoreText have a font size limit? + + // cached details about the resulting layout + // mutable members since these details are all lazy initialized + mutable double mfCachedWidth; // cached value of resulting typographical width + mutable double mfTrailingSpaceWidth; // in Pixels + + // x-offset relative to layout origin + // currently only used in RTL-layouts + mutable long mnBaseAdv; +}; + +// ======================================================================= + +CTLayout::CTLayout( const CTTextStyle* pTextStyle ) +: mpTextStyle( pTextStyle ) +, mpAttrString( NULL ) +, mpCTLine( NULL ) +, mnCharCount( 0 ) +, mnTrailingSpaces( 0 ) +, mfFontScale( pTextStyle->mfFontScale ) +, mfCachedWidth( -1 ) +, mfTrailingSpaceWidth( 0 ) +, mnBaseAdv( 0 ) +{ + CFRetain( mpTextStyle->GetStyleDict() ); +} + +// ----------------------------------------------------------------------- + +CTLayout::~CTLayout() +{ + if( mpCTLine ) + CFRelease( mpCTLine ); + if( mpAttrString ) + CFRelease( mpAttrString ); + CFRelease( mpTextStyle->GetStyleDict() ); +} + +// ----------------------------------------------------------------------- + +bool CTLayout::LayoutText( ImplLayoutArgs& rArgs ) +{ + if( mpAttrString ) + CFRelease( mpAttrString ); + mpAttrString = NULL; + if( mpCTLine ) + CFRelease( mpCTLine ); + mpCTLine = NULL; + + SalLayout::AdjustLayout( rArgs ); + mnCharCount = mnEndCharPos - mnMinCharPos; + + // short circuit if there is nothing to do + if( mnCharCount <= 0 ) + return false; + + // create the CoreText line layout + CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos, mnCharCount, kCFAllocatorNull ); + mpAttrString = CFAttributedStringCreate( NULL, aCFText, mpTextStyle->GetStyleDict() ); + mpCTLine = CTLineCreateWithAttributedString( mpAttrString ); + CFRelease( aCFText); + + // get info about trailing whitespace to prepare for text justification in AdjustLayout() + mnTrailingSpaces = 0; + for( int i = mnEndCharPos; --i >= mnMinCharPos; ++mnTrailingSpaces ) + if( !IsSpacingGlyph( rArgs.mpStr[i] | GF_ISCHAR )) + break; + return true; +} + +// ----------------------------------------------------------------------- + +void CTLayout::AdjustLayout( ImplLayoutArgs& rArgs ) +{ + if( !mpCTLine) + return; + + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + // CoreText fills trailing space during justification so we have to + // take that into account when requesting CT to justify something + mfTrailingSpaceWidth = rCT.LineGetTrailingWhitespaceWidth( mpCTLine ); + const int nTrailingSpaceWidth = rint( mfFontScale * mfTrailingSpaceWidth ); + + int nOrigWidth = GetTextWidth(); + nOrigWidth -= nTrailingSpaceWidth; + int nPixelWidth = rArgs.mnLayoutWidth; + if( nPixelWidth ) + { + nPixelWidth -= nTrailingSpaceWidth; + if( nPixelWidth <= 0) + return; + } + else if( rArgs.mpDXArray ) + { + // for now we are only interested in the layout width + // TODO: use all mpDXArray elements for layouting + nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 - mnTrailingSpaces ]; + } + + // in RTL-layouts trailing spaces are leftmost + // TODO: use BiDi-algorithm to thoroughly check this assumption + if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) + mnBaseAdv = nTrailingSpaceWidth; + + // return early if there is nothing to do + if( nPixelWidth <= 0 ) + return; + + // HACK: justification requests which change the width by just one pixel are probably + // #i86038# introduced by lossy conversions between integer based coordinate system + if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) ) + return; + + CTLineRef pNewCTLine = rCT.LineCreateJustifiedLine( mpCTLine, 1.0, nPixelWidth / mfFontScale ); + if( !pNewCTLine ) { // CTLineCreateJustifiedLine can and does fail + // handle failure by keeping the unjustified layout + // TODO: a better solution such as + // - forcing glyph overlap + // - changing the font size + // - changing the CTM matrix + return; + } + CFRelease( mpCTLine ); + mpCTLine = pNewCTLine; + mfCachedWidth = -1; // TODO: can we set it directly to target width we requested? For now we re-measure + mfTrailingSpaceWidth = 0; +} + +// ----------------------------------------------------------------------- + +void CTLayout::DrawText( SalGraphics& rGraphics ) const +{ + AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics); + + // short circuit if there is nothing to do + if( (mnCharCount <= 0) + || !rAquaGraphics.CheckContext() ) + return; + + // the view is vertically flipped => flipped glyphs + // so apply a temporary transformation that it flips back + // also compensate if the font was size limited + CGContextSaveGState( rAquaGraphics.mrContext ); + CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale ); + CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText ); + + // Draw the text + const Point aVclPos = GetDrawPosition( Point(mnBaseAdv,0) ); + CGPoint aTextPos = { +aVclPos.X()/mfFontScale, -aVclPos.Y()/mfFontScale }; + + if( mpTextStyle->mfFontRotation != 0.0 ) + { + const CGFloat fRadians = mpTextStyle->mfFontRotation; + CGContextRotateCTM( rAquaGraphics.mrContext, +fRadians ); + + const CGAffineTransform aInvMatrix = CGAffineTransformMakeRotation( -fRadians ); + aTextPos = CGPointApplyAffineTransform( aTextPos, aInvMatrix ); + } + + CGContextSetTextPosition( rAquaGraphics.mrContext, aTextPos.x, aTextPos.y ); + CTLineDraw( mpCTLine, rAquaGraphics.mrContext ); + + // request an update of the changed window area + if( rAquaGraphics.IsWindowGraphics() ) + { + const CGRect aInkRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext ); + const CGRect aRefreshRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aInkRect ); + rAquaGraphics.RefreshRect( aRefreshRect ); + } + + // restore the original graphic context transformations + CGContextRestoreGState( rAquaGraphics.mrContext ); +} + +// ----------------------------------------------------------------------- + +int CTLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIDs, Point& rPos, int& nStart, + sal_Int32* pGlyphAdvances, int* pCharIndexes ) const +{ + if( !mpCTLine ) + return 0; + + if( nStart < 0 ) // first glyph requested? + nStart = 0; + nLen = 1; // TODO: handle nLen>1 below + + // prepare to iterate over the glyph runs + int nCount = 0; + int nSubIndex = nStart; + + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + typedef std::vector<CGGlyph> CGGlyphVector; + typedef std::vector<CGPoint> CGPointVector; + typedef std::vector<CGSize> CGSizeVector; + typedef std::vector<CFIndex> CFIndexVector; + CGGlyphVector aCGGlyphVec; + CGPointVector aCGPointVec; + CGSizeVector aCGSizeVec; + CFIndexVector aCFIndexVec; + + // TODO: iterate over cached layout + CFArrayRef aGlyphRuns = rCT.LineGetGlyphRuns( mpCTLine ); + const int nRunCount = CFArrayGetCount( aGlyphRuns ); + for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) { + CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex ); + const CFIndex nGlyphsInRun = rCT.RunGetGlyphCount( pGlyphRun ); + // skip to the first glyph run of interest + if( nSubIndex >= nGlyphsInRun ) { + nSubIndex -= nGlyphsInRun; + continue; + } + const CFRange aFullRange = CFRangeMake( 0, nGlyphsInRun ); + + // get glyph run details + const CGGlyph* pCGGlyphIdx = rCT.RunGetGlyphsPtr( pGlyphRun ); + if( !pCGGlyphIdx ) { + aCGGlyphVec.reserve( nGlyphsInRun ); + CTRunGetGlyphs( pGlyphRun, aFullRange, &aCGGlyphVec[0] ); + pCGGlyphIdx = &aCGGlyphVec[0]; + } + const CGPoint* pCGGlyphPos = rCT.RunGetPositionsPtr( pGlyphRun ); + if( !pCGGlyphPos ) { + aCGPointVec.reserve( nGlyphsInRun ); + CTRunGetPositions( pGlyphRun, aFullRange, &aCGPointVec[0] ); + pCGGlyphPos = &aCGPointVec[0]; + } + + const CGSize* pCGGlyphAdvs = NULL; + if( pGlyphAdvances) { + pCGGlyphAdvs = rCT.RunGetAdvancesPtr( pGlyphRun ); + if( !pCGGlyphAdvs) { + aCGSizeVec.reserve( nGlyphsInRun ); + CTRunGetAdvances( pGlyphRun, aFullRange, &aCGSizeVec[0] ); + pCGGlyphAdvs = &aCGSizeVec[0]; + } + } + + const CFIndex* pCGGlyphStrIdx = NULL; + if( pCharIndexes) { + pCGGlyphStrIdx = rCT.RunGetStringIndicesPtr( pGlyphRun ); + if( !pCGGlyphStrIdx) { + aCFIndexVec.reserve( nGlyphsInRun ); + CTRunGetStringIndices( pGlyphRun, aFullRange, &aCFIndexVec[0] ); + pCGGlyphStrIdx = &aCFIndexVec[0]; + } + } + + // get the details for each interesting glyph + // TODO: handle nLen>1 + for(; (--nLen >= 0) && (nSubIndex < nGlyphsInRun); ++nSubIndex, ++nStart ) + { + // convert glyph details for VCL + *(pGlyphIDs++) = pCGGlyphIdx[ nSubIndex ]; + if( pGlyphAdvances ) + *(pGlyphAdvances++) = pCGGlyphAdvs[ nSubIndex ].width; + if( pCharIndexes ) + *(pCharIndexes++) = pCGGlyphStrIdx[ nSubIndex] + mnMinCharPos; + if( !nCount++ ) { + const CGPoint& rCurPos = pCGGlyphPos[ nSubIndex ]; + rPos = GetDrawPosition( Point( mfFontScale * rCurPos.x, mfFontScale * rCurPos.y) ); + } + } + nSubIndex = 0; // prepare for the next glyph run + break; // TODO: handle nLen>1 + } + + return nCount; +} + +// ----------------------------------------------------------------------- + +long CTLayout::GetTextWidth() const +{ + if( (mnCharCount <= 0) || !mpCTLine ) + return 0; + + if( mfCachedWidth < 0.0 ) { + mfCachedWidth = CTLineGetTypographicBounds( mpCTLine, NULL, NULL, NULL); + mfTrailingSpaceWidth = CTLineGetTrailingWhitespaceWidth( mpCTLine); + } + + const long nScaledWidth = lrint( mfFontScale * (mfCachedWidth + mfTrailingSpaceWidth)); + return nScaledWidth; +} + +// ----------------------------------------------------------------------- + +long CTLayout::FillDXArray( sal_Int32* pDXArray ) const +{ + // short circuit requests which don't need full details + if( !pDXArray ) + return GetTextWidth(); + + // check assumptions + DBG_ASSERT( mfTrailingSpaceWidth==0.0, "CTLayout::FillDXArray() with fTSW!=0" ); + + long nPixWidth = GetTextWidth(); + if( pDXArray ) { + // initialize the result array + for( int i = 0; i < mnCharCount; ++i) + pDXArray[i] = 0; + // handle each glyph run + CFArrayRef aGlyphRuns = CTLineGetGlyphRuns( mpCTLine ); + const int nRunCount = CFArrayGetCount( aGlyphRuns ); + typedef std::vector<CGSize> CGSizeVector; + CGSizeVector aSizeVec; + typedef std::vector<CFIndex> CFIndexVector; + CFIndexVector aIndexVec; + for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) { + CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex ); + const CFIndex nGlyphCount = CTRunGetGlyphCount( pGlyphRun ); + const CFRange aFullRange = CFRangeMake( 0, nGlyphCount ); + aSizeVec.reserve( nGlyphCount ); + aIndexVec.reserve( nGlyphCount ); + CTRunGetAdvances( pGlyphRun, aFullRange, &aSizeVec[0] ); + CTRunGetStringIndices( pGlyphRun, aFullRange, &aIndexVec[0] ); + for( int i = 0; i != nGlyphCount; ++i ) { + const int nRelIdx = aIndexVec[i]; + pDXArray[ nRelIdx ] += aSizeVec[i].width; + } + } + } + + return nPixWidth; +} + +// ----------------------------------------------------------------------- + +int CTLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const +{ + if( !mpCTLine ) + return STRING_LEN; + + CTTypesetterRef aCTTypeSetter = CTTypesetterCreateWithAttributedString( mpAttrString ); + const double fCTMaxWidth = (double)nMaxWidth / (nFactor * mfFontScale); + CFIndex nIndex = CTTypesetterSuggestClusterBreak( aCTTypeSetter, 0, fCTMaxWidth ); + if( nIndex >= mnCharCount ) + return STRING_LEN; + + nIndex += mnMinCharPos; + return (int)nIndex; +} + +// ----------------------------------------------------------------------- + +void CTLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const +{ + DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)), + "CTLayout::GetCaretPositions() : invalid number of caret pairs requested"); + + // initialize the caret positions + for( int i = 0; i < nMaxIndex; ++i ) + pCaretXArray[ i ] = -1; + + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + for( int n = 0; n <= mnCharCount; ++n ) + { + // measure the characters cursor position + CGFloat fPos2 = -1; + const CGFloat fPos1 = rCT.LineGetOffsetForStringIndex( mpCTLine, n, &fPos2 ); + (void)fPos2; // TODO: split cursor at line direction change + // update previous trailing position + if( n > 0 ) + pCaretXArray[ 2*n-1 ] = lrint( fPos1 * mfFontScale ); + // update current leading position + if( 2*n >= nMaxIndex ) + break; + pCaretXArray[ 2*n+0 ] = lrint( fPos1 * mfFontScale ); + } +} + +// ----------------------------------------------------------------------- + +bool CTLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rVCLRect ) const +{ + AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics); + CGRect aMacRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext ); + CGPoint aMacPos = CGContextGetTextPosition( rAquaGraphics.mrContext ); + aMacRect.origin.x -= aMacPos.x; + aMacRect.origin.y -= aMacPos.y; + + const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) ); + + // CoreText top-bottom are vertically flipped from a VCL aspect + rVCLRect.Left() = aPos.X() + mfFontScale * aMacRect.origin.x; + rVCLRect.Right() = aPos.X() + mfFontScale * (aMacRect.origin.x + aMacRect.size.width); + rVCLRect.Bottom() = aPos.Y() - mfFontScale * aMacRect.origin.y; + rVCLRect.Top() = aPos.Y() - mfFontScale * (aMacRect.origin.y + aMacRect.size.height); + return true; +} + +// ======================================================================= + +// glyph fallback is supported directly by Aqua +// so methods used only by MultiSalLayout can be dummy implementated +bool CTLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; } +void CTLayout::InitFont() const {} +void CTLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {} +void CTLayout::DropGlyph( int /*nStart*/ ) {} +void CTLayout::Simplify( bool /*bIsBase*/ ) {} + +// get the ImplFontData for a glyph fallback font +// for a glyphid that was returned by CTLayout::GetNextGlyphs() +const ImplFontData* CTLayout::GetFallbackFontData( sal_GlyphId /*nGlyphId*/ ) const +{ +#if 0 + // check if any fallback fonts were needed + if( !mpFallbackInfo ) + return NULL; + // check if the current glyph needs a fallback font + int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; + if( !nFallbackLevel ) + return NULL; + pFallbackFont = mpFallbackInfo->GetFallbackFontData( nFallbackLevel ); +#else + // let CoreText's font cascading handle glyph fallback + const ImplFontData* pFallbackFont = NULL; +#endif + return pFallbackFont; +} + +// ======================================================================= + +SalLayout* CTTextStyle::GetTextLayout( void ) const +{ + return new CTLayout( this); +} + +// ======================================================================= + diff --git a/vcl/coretext/salgdi2.cxx b/vcl/coretext/salgdi2.cxx new file mode 100644 index 000000000000..c118440d4779 --- /dev/null +++ b/vcl/coretext/salgdi2.cxx @@ -0,0 +1,918 @@ +/* -*- 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 "sal/config.h" + +#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/atsui/salgdi.h" +#include "aqua/salframe.h" +#ifdef ENABLE_CORETEXT +#include "ctfonts.hxx" +#else +#include "atsfonts.hxx" +#endif + +#include "fontsubset.hxx" +#include "impfont.hxx" +#include "sallayout.hxx" +#include "sft.hxx" + + +using namespace vcl; + +// ======================================================================= + +SystemFontList::~SystemFontList( void ) +{} + + +// ATSUI is deprecated in 10.6 (or already 10.5?) +#if HAVE_GCC_PRAGMA_DIAGNOSTIC_MODIFY +#pragma GCC diagnostic warning "-Wdeprecated-declarations" +#endif + +// ======================================================================= + +ImplMacTextStyle::ImplMacTextStyle( const ImplFontSelectData& rReqFont ) +: mpFontData( (ImplMacFontData*)rReqFont.mpFontData ) +, mfFontScale( 1.0 ) +, mfFontStretch( 1.0 ) +, mfFontRotation( 0.0 ) +{} + +// ----------------------------------------------------------------------- + +ImplMacTextStyle::~ImplMacTextStyle( void ) +{} + +// ======================================================================= + +ImplMacFontData::ImplMacFontData( const ImplMacFontData& rSrc ) +: ImplFontData( rSrc ) +, mnFontId( rSrc.mnFontId ) +, mpCharMap( rSrc.mpCharMap ) +, mbOs2Read( rSrc.mbOs2Read ) +, mbHasOs2Table( rSrc.mbHasOs2Table ) +, mbCmapEncodingRead( rSrc.mbCmapEncodingRead ) +, mbHasCJKSupport( rSrc.mbHasCJKSupport ) +{ + if( mpCharMap ) + mpCharMap->AddReference(); +} + +// ----------------------------------------------------------------------- + +ImplMacFontData::ImplMacFontData( const ImplDevFontAttributes& rDFA, sal_IntPtr nFontId ) +: ImplFontData( 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; +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* ImplMacFontData::CreateFontInstance(FontSelectPattern& rFSD) const +{ + return new ImplFontEntry(rFSD); +} + +// ----------------------------------------------------------------------- + +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]);} + +#if MACOSX_SDK_VERSION >= 1070 +extern "C" { +extern ATSFontRef FMGetATSFontRefFromFont(FMFont iFont); +} +#endif + +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 + // allocate a buffer for the CMAP raw data + const int nBufSize = GetFontTable( "cmap", NULL ); + DBG_ASSERT( (nBufSize > 0), "ImplMacFontData::GetImplFontCharMap : GetFontTable1 failed!\n"); + if( nBufSize <= 0 ) + return mpCharMap; + + // get the CMAP raw data + ByteVector aBuffer( nBufSize ); + const int nRawLength = GetFontTable( "cmap", &aBuffer[0] ); + DBG_ASSERT( (nRawLength > 0), "ImplMacFontData::GetImplFontCharMap : GetFontTable2 failed!\n"); + if( nRawLength <= 0 ) + return mpCharMap; + DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::GetImplFontCharMap : ByteCount mismatch!\n"); + + // parse the CMAP + CmapResult aCmapResult; + if( ParseCMAP( &aBuffer[0], nRawLength, aCmapResult ) ) + { + // create the matching charmap + mpCharMap->DeReference(); + 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; + mbHasOs2Table = false; + + // prepare to get the OS/2 table raw data + const int nBufSize = GetFontTable( "OS/2", NULL ); + DBG_ASSERT( (nBufSize > 0), "ImplMacFontData::ReadOs2Table : GetFontTable1 failed!\n"); + if( nBufSize <= 0 ) + return; + + // get the OS/2 raw data + ByteVector aBuffer( nBufSize ); + const int nRawLength = GetFontTable( "cmap", &aBuffer[0] ); + DBG_ASSERT( (nRawLength > 0), "ImplMacFontData::ReadOs2Table : GetFontTable2 failed!\n"); + if( nRawLength <= 0 ) + 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; + + const int nBufSize = GetFontTable( "cmap", NULL ); + if( nBufSize <= 0 ) + return; + + // get the CMAP raw data + ByteVector aBuffer( nBufSize ); + const int nRawLength = GetFontTable( "cmap", &aBuffer[0] ); + if( nRawLength < 24 ) + return; + DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadMacCmapEncoding : ByteCount mismatch!\n"); + + const unsigned char* pCmap = &aBuffer[0]; + 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 ) + , mpMacTextStyle( NULL ) + , maTextColor( COL_BLACK ) + , mbNonAntialiasedText( false ) + , mbPrinter( false ) + , mbVirDev( false ) + , mbWindow( false ) +{} + +// ----------------------------------------------------------------------- + +AquaSalGraphics::~AquaSalGraphics() +{ + CGPathRelease( mxClipPath ); + delete mpMacTextStyle; + + 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 ) +{ + maTextColor = RGBAColor( nSalColor ); + if( mpMacTextStyle) + mpMacTextStyle->SetTextColor( maTextColor ); +} + +// ----------------------------------------------------------------------- + +void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int /*nFallbackLevel*/ ) +{ + mpMacTextStyle->GetFontMetric( mfFakeDPIScale, *pMetric ); +} + +// ----------------------------------------------------------------------- + +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??? + eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef, + eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, + &aATSFontContainer ); + 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 + + OUString aBrandStr( "$BRAND_BASE_DIR" ); + rtl_bootstrap_expandMacros( &aBrandStr.pData ); + OUString aBrandSysPath; + OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None ); + + OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 ); + aBrandFontDir.append( 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(); +#ifdef ENABLE_CORETEXT + SystemFontList* GetCoretextFontList(void); // forward declaration + if( !pSalData->mpFontList ) + pSalData->mpFontList = GetCoretextFontList(); +#else + SystemFontList* GetAtsFontList(void); // forward declaration + if( !pSalData->mpFontList ) + pSalData->mpFontList = GetAtsFontList(); +#endif + + // Copy all PhysicalFontFace objects contained in the SystemFontList + pSalData->mpFontList->AnnounceFonts( *pFontList ); +} + +void AquaSalGraphics::ClearDevFontCache() +{ + SalData* pSalData = GetSalData(); + delete pSalData->mpFontList; + pSalData->mpFontList = NULL; +} + +// ----------------------------------------------------------------------- + +bool AquaSalGraphics::AddTempDevFont( ImplDevFontList*, + const OUString& rFontFileURL, const OUString& /*rFontName*/ ) +{ + OUString aUSytemPath; + OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) ); + + FSRef aNewRef; + Boolean bIsDirectory = true; + OString aCFileName = 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??? + eStatus = ::ATSFontActivateFromFileReference( &aNewRef, + eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, + &oContainer ); + if( eStatus != noErr ) + return false; + + // TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed + // TODO: register new ImplMacFontdata in pFontList + return true; +} + +// ----------------------------------------------------------------------- + +sal_Bool AquaSalGraphics::GetGlyphOutline( sal_GlyphId nGlyphId, basegfx::B2DPolyPolygon& rPolyPoly ) +{ + const bool bRC = mpMacTextStyle->GetGlyphOutline( nGlyphId, rPolyPoly ); + return bRC; +} + +// ----------------------------------------------------------------------- + +sal_Bool AquaSalGraphics::GetGlyphBoundRect( sal_GlyphId nGlyphId, Rectangle& rRect ) +{ + const bool bRC = mpMacTextStyle->GetGlyphBoundRect( nGlyphId, rRect ); + return bRC; +} + +// ----------------------------------------------------------------------- + +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*/ ) +{ + // release the text style + delete mpMacTextStyle; + mpMacTextStyle = NULL; + + // handle NULL request meaning: release-font-resources request + if( !pReqFont ) + { + mpMacFontData = NULL; + return 0; + } + + // update the text style + mpMacFontData = static_cast<const ImplMacFontData*>( pReqFont->mpFontData ); + mpMacTextStyle = mpMacFontData->CreateMacTextStyle( *pReqFont ); + mpMacTextStyle->SetTextColor( maTextColor ); + +#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", + OUStringToOString( mpMacFontData->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), + OUStringToOString( mpMacFontData->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), + (int)nFontID, + OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), + OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), + pReqFont->GetWeight(), + pReqFont->GetSlant(), + pReqFont->mnHeight, + pReqFont->mnWidth, + pReqFont->mnOrientation); +#endif + + return 0; +} + +// ----------------------------------------------------------------------- + +SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int /*nFallbackLevel*/ ) +{ + SalLayout* pSalLayout = mpMacTextStyle->GetTextLayout(); + return pSalLayout; +} + +// ----------------------------------------------------------------------- + +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( const char aTag[5], ByteCount nOfs, ByteCount nLen, + const unsigned char* /*pData*/, unsigned char*& rpDest ) +{ + // write entry tag + rpDest[ 0] = aTag[0]; + rpDest[ 1] = aTag[1]; + rpDest[ 2] = aTag[2]; + rpDest[ 3] = aTag[3]; + // 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; +} + +// fake a TTF or CFF font as directly accessing font file is not possible +// when only the fontid is known. This approach also handles *.dfont fonts. +bool AquaSalGraphics::GetRawFontData( const PhysicalFontFace* pFontData, + ByteVector& rBuffer, bool* pJustCFF ) +{ + const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData); + + // short circuit for CFF-only fonts + const int nCffSize = pMacFont->GetFontTable( "CFF ", NULL); + if( pJustCFF != NULL ) + { + *pJustCFF = (nCffSize > 0); + if( *pJustCFF) + { + rBuffer.resize( nCffSize); + const int nCffRead = pMacFont->GetFontTable( "CFF ", &rBuffer[0]); + if( nCffRead != nCffSize) + return false; + return true; + } + } + + // get font table availability and size in bytes + const int nHeadSize = pMacFont->GetFontTable( "head", NULL); + if( nHeadSize <= 0) + return false; + const int nMaxpSize = pMacFont->GetFontTable( "maxp", NULL); + if( nMaxpSize <= 0) + return false; + const int nCmapSize = pMacFont->GetFontTable( "cmap", NULL); + if( nCmapSize <= 0) + return false; + const int nNameSize = pMacFont->GetFontTable( "name", NULL); + if( nNameSize <= 0) + return false; + const int nHheaSize = pMacFont->GetFontTable( "hhea", NULL); + if( nHheaSize <= 0) + return false; + const int nHmtxSize = pMacFont->GetFontTable( "hmtx", NULL); + if( nHmtxSize <= 0) + return false; + + // get the ttf-glyf outline tables + int nLocaSize = 0; + int nGlyfSize = 0; + if( nCffSize <= 0) + { + nLocaSize = pMacFont->GetFontTable( "loca", NULL); + if( nLocaSize <= 0) + return false; + nGlyfSize = pMacFont->GetFontTable( "glyf", NULL); + if( nGlyfSize <= 0) + return false; + } + + int nPrepSize = 0, nCvtSize = 0, nFpgmSize = 0; + if( nGlyfSize) // TODO: reduce PDF size by making hint subsetting optional + { + nPrepSize = pMacFont->GetFontTable( "prep", NULL); + nCvtSize = pMacFont->GetFontTable( "cvt ", NULL); + nFpgmSize = pMacFont->GetFontTable( "fpgm", NULL); + } + + // prepare a byte buffer for a fake font + int nTableCount = 7; + nTableCount += (nPrepSize>0) + (nCvtSize>0) + (nFpgmSize>0) + (nGlyfSize>0); + const ByteCount nFdirSize = 12 + 16*nTableCount; + ByteCount nTotalSize = nFdirSize; + nTotalSize += nHeadSize + nMaxpSize + nNameSize + nCmapSize; + if( nGlyfSize ) + nTotalSize += nLocaSize + nGlyfSize; + else + nTotalSize += nCffSize; + nTotalSize += nHheaSize + nHmtxSize; + nTotalSize += nPrepSize + nCvtSize + nFpgmSize; + rBuffer.resize( nTotalSize ); + + // 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 = nFdirSize; + unsigned char* pFakeEntry = &rBuffer[12]; + if( nCmapSize != pMacFont->GetFontTable( "cmap", &rBuffer[nOfs])) + return false; + FakeDirEntry( "cmap", nOfs, nCmapSize, &rBuffer[0], pFakeEntry ); + nOfs += nCmapSize; + if( nCvtSize ) { + if( nCvtSize != pMacFont->GetFontTable( "cvt ", &rBuffer[nOfs])) + return false; + FakeDirEntry( "cvt ", nOfs, nCvtSize, &rBuffer[0], pFakeEntry ); + nOfs += nCvtSize; + } + if( nFpgmSize ) { + if( nFpgmSize != pMacFont->GetFontTable( "fpgm", &rBuffer[nOfs])) + return false; + FakeDirEntry( "fpgm", nOfs, nFpgmSize, &rBuffer[0], pFakeEntry ); + nOfs += nFpgmSize; + } + if( nCffSize ) { + if( nCffSize != pMacFont->GetFontTable( "CFF ", &rBuffer[nOfs])) + return false; + FakeDirEntry( "CFF ", nOfs, nCffSize, &rBuffer[0], pFakeEntry ); + nOfs += nGlyfSize; + } else { + if( nGlyfSize != pMacFont->GetFontTable( "glyf", &rBuffer[nOfs])) + return false; + FakeDirEntry( "glyf", nOfs, nGlyfSize, &rBuffer[0], pFakeEntry ); + nOfs += nGlyfSize; + if( nLocaSize != pMacFont->GetFontTable( "loca", &rBuffer[nOfs])) + return false; + FakeDirEntry( "loca", nOfs, nLocaSize, &rBuffer[0], pFakeEntry ); + nOfs += nLocaSize; + } + if( nHeadSize != pMacFont->GetFontTable( "head", &rBuffer[nOfs])) + return false; + FakeDirEntry( "head", nOfs, nHeadSize, &rBuffer[0], pFakeEntry ); + nOfs += nHeadSize; + if( nHheaSize != pMacFont->GetFontTable( "hhea", &rBuffer[nOfs])) + return false; + FakeDirEntry( "hhea", nOfs, nHheaSize, &rBuffer[0], pFakeEntry ); + nOfs += nHheaSize; + if( nHmtxSize != pMacFont->GetFontTable( "hmtx", &rBuffer[nOfs])) + return false; + FakeDirEntry( "hmtx", nOfs, nHmtxSize, &rBuffer[0], pFakeEntry ); + nOfs += nHmtxSize; + if( nMaxpSize != pMacFont->GetFontTable( "maxp", &rBuffer[nOfs])) + return false; + FakeDirEntry( "maxp", nOfs, nMaxpSize, &rBuffer[0], pFakeEntry ); + nOfs += nMaxpSize; + if( nNameSize != pMacFont->GetFontTable( "name", &rBuffer[nOfs])) + return false; + FakeDirEntry( "name", nOfs, nNameSize, &rBuffer[0], pFakeEntry ); + nOfs += nNameSize; + if( nPrepSize ) { + if( nPrepSize != pMacFont->GetFontTable( "prep", &rBuffer[nOfs])) + return false; + FakeDirEntry( "prep", nOfs, nPrepSize, &rBuffer[0], pFakeEntry ); + nOfs += nPrepSize; + } + + DBG_ASSERT( (nOfs==nTotalSize), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalSize)"); + + return 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; + aSysFontData.nSize = sizeof( SystemFontData ); + +#ifndef ENABLE_CORETEXT + // NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks. + ATSUFontID fontId; + OSStatus err; + 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; + } +#endif + + aSysFontData.bAntialias = !mbNonAntialiasedText; + + return aSysFontData; +} + +// ----------------------------------------------------------------------- + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/aqua/salgdi.h b/vcl/inc/aqua/salgdi.h index 20d4588925de..d7ffe53dd206 100644 --- a/vcl/inc/aqua/salgdi.h +++ b/vcl/inc/aqua/salgdi.h @@ -8,7 +8,7 @@ */ #ifdef ENABLE_CORETEXT -# include "coretext/salgdi.h" +# include "coretext/salgdi2.h" #else # include "aqua/atsui/salgdi.h" #endif diff --git a/vcl/inc/coretext/salgdi2.h b/vcl/inc/coretext/salgdi2.h new file mode 100644 index 000000000000..86d5fc9f516f --- /dev/null +++ b/vcl/inc/coretext/salgdi2.h @@ -0,0 +1,413 @@ +/* -*- 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 . + */ + +#ifndef _SV_SALGDI_H +#define _SV_SALGDI_H + +#include <vector> + +#include "basegfx/polygon/b2dpolypolygon.hxx" + +#include "premac.h" +#include <ApplicationServices/ApplicationServices.h> +#include "postmac.h" + +#include "aqua/aquavcltypes.h" +#include <vcl/fontcapabilities.hxx> + +#include "outfont.hxx" +#include "aqua/salframe.h" +#include "salgdi.hxx" + +#include "quartz/salgdicommon.hxx" + +class AquaSalFrame; +class ImplDevFontAttributes; +class ImplMacTextStyle; + +struct CGRect; + +typedef sal_uInt32 sal_GlyphId; +typedef std::vector<unsigned char> ByteVector; + +#ifndef CGFLOAT_TYPE +typedef float CGFloat; +#endif + +// mac specific physically available font face +class ImplMacFontData : public PhysicalFontFace +{ +public: + ImplMacFontData( const ImplDevFontAttributes&, sal_IntPtr nFontID ); + + virtual ~ImplMacFontData(); + + virtual PhysicalFontFace* Clone() const = 0; + virtual ImplFontEntry* CreateFontInstance( FontSelectPattern& ) const; + virtual sal_IntPtr GetFontId() const; + + virtual ImplMacTextStyle* CreateMacTextStyle( const ImplFontSelectData& ) const = 0; + virtual int GetFontTable( const char pTagName[5], unsigned char* ) const = 0; + + const ImplFontCharMap* GetImplFontCharMap() const; + bool GetImplFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const; + bool HasChar( sal_uInt32 cChar ) const; + + void ReadOs2Table() const; + void ReadMacCmapEncoding() const; + bool HasCJKSupport() const; + +protected: + ImplMacFontData( const ImplMacFontData&); +private: + const sal_IntPtr mnFontId; + mutable const ImplFontCharMap* mpCharMap; + mutable vcl::FontCapabilities maFontCapabilities; + mutable bool mbOs2Read; // true if OS2-table related info is valid + mutable bool mbHasOs2Table; + mutable bool mbCmapEncodingRead; // true if cmap encoding of Mac font is read + mutable bool mbHasCJKSupport; // #i78970# CJK fonts need extra leading + mutable bool mbFontCapabilitiesRead; +}; + +// -------------------- +// - ImplMacTextStyle - +// -------------------- +class ImplMacTextStyle +{ +public: + explicit ImplMacTextStyle( const ImplFontSelectData& ); + virtual ~ImplMacTextStyle( void ); + + virtual SalLayout* GetTextLayout( void ) const = 0; + + virtual void GetFontMetric( float fPDIY, ImplFontMetricData& ) const = 0; + virtual bool GetGlyphBoundRect( sal_GlyphId, Rectangle& ) const = 0; + virtual bool GetGlyphOutline( sal_GlyphId, basegfx::B2DPolyPolygon& ) const = 0; + + virtual void SetTextColor( const RGBAColor& ) = 0; + +//###protected: + const ImplMacFontData* mpFontData; + /// workaround to prevent overflows for huge font sizes + float mfFontScale; + /// <1.0: font is squeezed, >1.0 font is stretched, else 1.0 + float mfFontStretch; + /// text rotation in radian + float mfFontRotation; +}; + +// ------------------ +// - SystemFontList - +// TODO: move into cross-platform headers +// ------------------ +class SystemFontList +{ +public: + virtual ~SystemFontList( void ); + + virtual void AnnounceFonts( ImplDevFontList& ) const = 0; + virtual ImplMacFontData* GetFontDataFromId( sal_IntPtr nFontId ) const = 0; +}; + +// ------------------- +// - AquaSalGraphics - +// ------------------- +class AquaSalGraphics : public SalGraphics +{ + friend class ATSLayout; + friend class CTLayout; +protected: + AquaSalFrame* mpFrame; + CGLayerRef mxLayer; // Quartz graphics layer + CGContextRef mrContext; // Quartz drawing context + class XorEmulation* mpXorEmulation; + int mnXorMode; // 0: off 1: on 2: invert only + int mnWidth; + int mnHeight; + int mnBitmapDepth; // zero unless bitmap + /// device resolution of this graphics + long mnRealDPIX; + long mnRealDPIY; + /// some graphics implementations (e.g. AquaSalInfoPrinter) scale + /// everything down by a factor (see SetupPrinterGraphics for details) + /// so we have to compensate for it with the inverse factor + double mfFakeDPIScale; + + /// path representing current clip region + CGMutablePathRef mxClipPath; + + /// Drawing colors + /// pen color RGBA + RGBAColor maLineColor; + /// brush color RGBA + RGBAColor maFillColor; + + // Device Font settings + const ImplMacFontData* mpMacFontData; + ImplMacTextStyle* mpMacTextStyle; + RGBAColor maTextColor; + /// allows text to be rendered without antialiasing + bool mbNonAntialiasedText; + + // Graphics types + + /// is this a printer graphics + bool mbPrinter; + /// is this a virtual device graphics + bool mbVirDev; + /// is this a window graphics + bool mbWindow; + +public: + AquaSalGraphics(); + virtual ~AquaSalGraphics(); + + bool IsPenVisible() const { return maLineColor.IsVisible(); } + bool IsBrushVisible() const { return maFillColor.IsVisible(); } + + void SetWindowGraphics( AquaSalFrame* pFrame ); + void SetPrinterGraphics( CGContextRef, long nRealDPIX, long nRealDPIY, double fFakeScale ); + void SetVirDevGraphics( CGLayerRef, CGContextRef, int nBitDepth = 0 ); + + void initResolution( NSWindow* ); + void copyResolution( AquaSalGraphics& ); + void updateResolution(); + + bool IsWindowGraphics() const { return mbWindow; } + AquaSalFrame* getGraphicsFrame() const { return mpFrame; } + void setGraphicsFrame( AquaSalFrame* pFrame ) { mpFrame = pFrame; } + + void ImplDrawPixel( long nX, long nY, const RGBAColor& ); // helper to draw single pixels + + bool CheckContext(); + CGContextRef GetContext(); + void UpdateWindow( NSRect& ); // delivered in NSView coordinates + void RefreshRect( const CGRect& ); + void RefreshRect( const NSRect& ); + void RefreshRect(float lX, float lY, float lWidth, float lHeight); + + void SetState(); + void UnsetState(); + // InvalidateContext does an UnsetState and sets mrContext to 0 + void InvalidateContext(); + + virtual bool setClipRegion( const Region& ); + + // draw --> LineColor and FillColor and RasterOp and ClipRegion + virtual void drawPixel( long nX, long nY ); + virtual void drawPixel( long nX, long nY, SalColor nSalColor ); + virtual void drawLine( long nX1, long nY1, long nX2, long nY2 ); + virtual void drawRect( long nX, long nY, long nWidth, long nHeight ); + virtual void drawPolyLine( sal_uLong nPoints, const SalPoint* pPtAry ); + virtual void drawPolygon( sal_uLong nPoints, const SalPoint* pPtAry ); + virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uLong* pPoints, PCONSTSALPOINT* pPtAry ); + virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); + virtual sal_Bool drawPolyLineBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); + virtual sal_Bool drawPolygonBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); + virtual sal_Bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const sal_uInt8* const* pFlgAry ); + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap eLineCap); + + // CopyArea --> No RasterOp, but ClipRegion + virtual void copyArea( long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth, + long nSrcHeight, sal_uInt16 nFlags ); + + // CopyBits and DrawBitmap --> RasterOp and ClipRegion + // CopyBits() --> pSrcGraphics == NULL, then CopyBits on same Graphics + virtual void copyBits( const SalTwoRect* pPosAry, SalGraphics* pSrcGraphics ); + virtual void drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap ); + virtual void drawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + SalColor nTransparentColor ); + virtual void drawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + const SalBitmap& rTransparentBitmap ); + virtual void drawMask( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + SalColor nMaskColor ); + + virtual SalBitmap* getBitmap( long nX, long nY, long nWidth, long nHeight ); + virtual SalColor getPixel( long nX, long nY ); + + // invert --> ClipRegion (only Windows or VirDevs) + virtual void invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags); + virtual void invert( sal_uLong nPoints, const SalPoint* pPtAry, SalInvert nFlags ); + + virtual sal_Bool drawEPS( long nX, long nY, long nWidth, long nHeight, void* pPtr, sal_uLong nSize ); + + virtual bool drawAlphaBitmap( const SalTwoRect&, + const SalBitmap& rSourceBitmap, + const SalBitmap& rAlphaBitmap ); + + virtual bool drawAlphaRect( long nX, long nY, long nWidth, + long nHeight, sal_uInt8 nTransparency ); + + CGPoint* makeCGptArray(sal_uLong nPoints, const SalPoint* pPtAry); + // native widget rendering methods that require mirroring + virtual sal_Bool hitTestNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + const Point& aPos, sal_Bool& rIsInside ); + virtual sal_Bool drawNativeControl( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, + ControlState nState, const ImplControlValue& aValue, + const OUString& aCaption ); + virtual sal_Bool getNativeControlRegion( ControlType nType, ControlPart nPart, const Rectangle& rControlRegion, ControlState nState, + const ImplControlValue& aValue, const OUString& aCaption, + Rectangle &rNativeBoundingRegion, Rectangle &rNativeContentRegion ); + + // get device resolution + virtual void GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY ); + // get the depth of the device + virtual sal_uInt16 GetBitCount() const; + // get the width of the device + virtual long GetGraphicsWidth() const; + + // set the clip region to empty + virtual void ResetClipRegion(); + + // set the line color to transparent (= don't draw lines) + virtual void SetLineColor(); + // set the line color to a specific color + virtual void SetLineColor( SalColor nSalColor ); + // set the fill color to transparent (= don't fill) + virtual void SetFillColor(); + // set the fill color to a specific color, shapes will be + // filled accordingly + virtual void SetFillColor( SalColor nSalColor ); + // enable/disable XOR drawing + virtual void SetXORMode( bool bSet, bool bInvertOnly ); + // set line color for raster operations + virtual void SetROPLineColor( SalROPColor nROPColor ); + // set fill color for raster operations + virtual void SetROPFillColor( SalROPColor nROPColor ); + // set the text color to a specific color + virtual void SetTextColor( SalColor nSalColor ); + // set the font + virtual sal_uInt16 SetFont( FontSelectPattern*, int nFallbackLevel ); + // get the current font's etrics + virtual void GetFontMetric( ImplFontMetricData*, int nFallbackLevel ); + // get kernign pairs of the current font + // return only PairCount if (pKernPairs == NULL) + virtual sal_uLong GetKernPairs( sal_uLong nPairs, ImplKernPairData* pKernPairs ); + // get the repertoire of the current font + virtual const ImplFontCharMap* GetImplFontCharMap() const; + virtual bool GetImplFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const; + // graphics must fill supplied font list + virtual void GetDevFontList( ImplDevFontList* ); + // graphics must drop any cached font info + virtual void ClearDevFontCache(); + // graphics should call ImplAddDevFontSubstitute on supplied + // OutputDevice for all its device specific preferred font substitutions + virtual void GetDevFontSubstList( OutputDevice* ); + virtual bool AddTempDevFont( ImplDevFontList*, const OUString& rFileURL, const OUString& rFontName ); + // CreateFontSubset: a method to get a subset of glyhps of a font + // inside a new valid font file + // returns TRUE if creation of subset was successful + // parameters: rToFile: contains a osl file URL to write the subset to + // pFont: describes from which font to create a subset + // pGlyphIDs: the glyph ids to be extracted + // pEncoding: the character code corresponding to each glyph + // pWidths: the advance widths of the correspoding glyphs (in PS font units) + // nGlyphs: the number of glyphs + // rInfo: additional outgoing information + // implementation note: encoding 0 with glyph id 0 should be added implicitly + // as "undefined character" + virtual sal_Bool CreateFontSubset( const OUString& rToFile, + const PhysicalFontFace* pFont, + sal_Int32* pGlyphIDs, + sal_uInt8* pEncoding, + sal_Int32* pWidths, + int nGlyphs, + FontSubsetInfo& rInfo // out parameter + ); + + // GetFontEncodingVector: a method to get the encoding map Unicode + // to font encoded character; this is only used for type1 fonts and + // may return NULL in case of unknown encoding vector + // if ppNonEncoded is set and non encoded characters (that is type1 + // glyphs with only a name) exist it is set to the corresponding + // map for non encoded glyphs; the encoding vector contains -1 + // as encoding for these cases + virtual const Ucs2SIntMap* GetFontEncodingVector( const PhysicalFontFace*, const Ucs2OStrMap** ppNonEncoded ); + + // GetEmbedFontData: gets the font data for a font marked + // embeddable by GetDevFontList or NULL in case of error + // parameters: pFont: describes the font in question + // pWidths: the widths of all glyphs from char code 0 to 255 + // pWidths MUST support at least 256 members; + // rInfo: additional outgoing information + // pDataLen: out parameter, contains the byte length of the returned buffer + virtual const void* GetEmbedFontData( const PhysicalFontFace*, + const sal_Ucs* pUnicodes, + sal_Int32* pWidths, + FontSubsetInfo& rInfo, + long* pDataLen ); + // frees the font data again + virtual void FreeEmbedFontData( const void* pData, long nDataLen ); + + virtual void GetGlyphWidths( const PhysicalFontFace*, + bool bVertical, + Int32Vector& rWidths, + Ucs2UIntMap& rUnicodeEnc ); + + virtual sal_Bool GetGlyphBoundRect( sal_GlyphId nIndex, Rectangle& ); + virtual sal_Bool GetGlyphOutline( sal_GlyphId nIndex, basegfx::B2DPolyPolygon& ); + + virtual SalLayout* GetTextLayout( ImplLayoutArgs&, int nFallbackLevel ); + virtual void DrawServerFontLayout( const ServerFontLayout& ); + virtual bool supportsOperation( OutDevSupportType ) const; + + // Query the platform layer for control support + virtual sal_Bool IsNativeControlSupported( ControlType nType, ControlPart nPart ); + + virtual SystemGraphicsData GetGraphicsData() const; + virtual SystemFontData GetSysFontData( int /* nFallbacklevel */ ) const; + +private: + // differences between VCL, Quartz and kHiThemeOrientation coordinate systems + // make some graphics seem to be vertically-mirrored from a VCL perspective + bool IsFlipped() const { return mbWindow; } + + void ApplyXorContext(); + void Pattern50Fill(); + UInt32 getState( ControlState nState ); + UInt32 getTrackState( ControlState nState ); + bool GetRawFontData( const PhysicalFontFace* pFontData, + std::vector<unsigned char>& rBuffer, + bool* pJustCFF ); +}; + +// --- some trivial inlines + +inline void AquaSalGraphics::RefreshRect( const CGRect& rRect ) +{ + RefreshRect( rRect.origin.x, rRect.origin.y, rRect.size.width, rRect.size.height ); +} + +inline void AquaSalGraphics::RefreshRect( const NSRect& rRect ) +{ + RefreshRect( rRect.origin.x, rRect.origin.y, rRect.size.width, rRect.size.height ); +} + +#endif // _SV_SALGDI_H + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/impfont.hxx b/vcl/inc/impfont.hxx index 1ac8bd65f020..26a9dd41aed9 100644 --- a/vcl/inc/impfont.hxx +++ b/vcl/inc/impfont.hxx @@ -27,6 +27,7 @@ #include <vcl/dllapi.h> #include <vcl/vclenum.hxx> #include <vcl/fntstyle.hxx> +#include <outfont.hxx> // ------------ // - Impl_Font - @@ -229,6 +230,8 @@ public: bool ParseCMAP( const unsigned char* pRawData, int nRawLength, CmapResult& ); +void UpdateAttributesFromPSName( const String& rPSName, ImplDevFontAttributes& ); + #endif // _SV_IMPFONT_HXX /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/quartz/salgdicommon.hxx b/vcl/inc/quartz/salgdicommon.hxx index bbc6f8f9a550..c8a18198ffe9 100644 --- a/vcl/inc/quartz/salgdicommon.hxx +++ b/vcl/inc/quartz/salgdicommon.hxx @@ -39,6 +39,11 @@ public: const CGFloat* AsArray() const { return m_fRGBA; } bool IsVisible() const { return m_fRGBA[3] > 0; } void SetAlpha( float fAlpha ) { m_fRGBA[3] = fAlpha; } + + CGFloat GetRed() const { return m_fRGBA[0]; } + CGFloat GetGreen() const { return m_fRGBA[1]; } + CGFloat GetBlue() const { return m_fRGBA[2]; } + CGFloat GetAlpha() const { return m_fRGBA[3]; } private: CGFloat m_fRGBA[4]; // red, green, blue, alpha }; diff --git a/vcl/source/gdi/outdev3.cxx b/vcl/source/gdi/outdev3.cxx index 359b82c2b5df..565a56c02730 100644 --- a/vcl/source/gdi/outdev3.cxx +++ b/vcl/source/gdi/outdev3.cxx @@ -5846,6 +5846,14 @@ SalLayout* OutputDevice::ImplLayout( const OUString& rOrigStr, sal_Int32 nMinInd ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray ); +#ifdef MACOSX + // CoreText layouts are immutable and already contain the text color + // so we need to provide the color already for the layout request + // even if this layout will never be drawn + if( mbInitTextColor ) + const_cast<OutputDevice&>(*this).ImplInitTextColor(); +#endif + // get matching layout object for base font SalLayout* pSalLayout = NULL; if( mpPDFWriter ) |