diff options
-rw-r--r-- | vcl/aqua/source/gdi/ctfonts.cxx | 648 | ||||
-rw-r--r-- | vcl/aqua/source/gdi/ctfonts.hxx | 90 | ||||
-rw-r--r-- | vcl/aqua/source/gdi/ctlayout.cxx | 517 |
3 files changed, 1255 insertions, 0 deletions
diff --git a/vcl/aqua/source/gdi/ctfonts.cxx b/vcl/aqua/source/gdi/ctfonts.cxx new file mode 100644 index 000000000000..53a98e04fc06 --- /dev/null +++ b/vcl/aqua/source/gdi/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 "aqua/salgdi.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 aGlyphId, Rectangle& rRect ) const +{ + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + CGGlyph nCGGlyph = aGlyphId & 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 aGlyphId, basegfx::B2DPolyPolygon& rResult ) const +{ + rResult.clear(); + + const DynCoreTextSyms& rCT = DynCoreTextSyms::get(); + // TODO: GF_FONTMASK if using non-native glyph fallback + CGGlyph nCGGlyph = aGlyphId & 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( pTagName[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/aqua/source/gdi/ctfonts.hxx b/vcl/aqua/source/gdi/ctfonts.hxx new file mode 100644 index 000000000000..3ad46e9cb057 --- /dev/null +++ b/vcl/aqua/source/gdi/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 "aqua/salgdi.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/aqua/source/gdi/ctlayout.cxx b/vcl/aqua/source/gdi/ctlayout.cxx new file mode 100644 index 000000000000..1353fb839ae2 --- /dev/null +++ b/vcl/aqua/source/gdi/ctlayout.cxx @@ -0,0 +1,517 @@ +/************************************************************** + * + * 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* pOutGlyphIds, 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(); + 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; + + // if the text to be justified has whitespace in it then + // - Writer goes crazy with its HalfSpace magic + // - LayoutEngine handles spaces specially (in particular at the text start or end) + if( mnTrailingSpaces ) { + // adjust for Writer's SwFntObj::DrawText() Halfspace magic at the text end + std::vector<sal_Int32> aOrigDXAry; + aOrigDXAry.resize( mnCharCount); + FillDXArray( &aOrigDXAry[0] ); + int nLastCharSpace = rArgs.mpDXArray[ mnCharCount-1-mnTrailingSpaces ] + - aOrigDXAry[ mnCharCount-1-mnTrailingSpaces ]; + nPixelWidth -= nLastCharSpace; + if( nPixelWidth < 0 ) + return; + // recreate the CoreText line layout without trailing spaces + CFRelease( mpCTLine ); + CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos, + mnCharCount - mnTrailingSpaces, kCFAllocatorNull ); + CFAttributedStringRef pAttrStr = CFAttributedStringCreate( NULL, aCFText, mpTextStyle->GetStyleDict() ); + mpCTLine = CTLineCreateWithAttributedString( pAttrStr ); + CFRelease( aCFText); + CFRelease( pAttrStr ); + } + + 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* pOutGlyphIds, 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 + *(pOutGlyphIds++) = 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 ); + + const long nScaledWidth = lrint( mfFontScale * mfCachedWidth ); + return nScaledWidth; +} + +// ----------------------------------------------------------------------- + +long CTLayout::FillDXArray( sal_Int32* pDXArray ) const +{ + // short circuit requests which don't need full details + if( !pDXArray ) + return GetTextWidth(); + + 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 /*aGlyphId*/ ) 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 = (aGlyphId & 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); +} + +// ======================================================================= + |