diff options
author | Tor Lillqvist <tml@collabora.com> | 2013-12-05 22:01:05 +0200 |
---|---|---|
committer | Tor Lillqvist <tml@collabora.com> | 2013-12-06 15:05:00 +0200 |
commit | c49721950cb3d897b35f08bf871239308680b18e (patch) | |
tree | 6a76e91557328b9195960d065065b1a6914bf9be /vcl/quartz/ctfonts.cxx | |
parent | 693eced961a3d3014d15e0a406f4e001ee817522 (diff) |
Re-organize OS X and iOS code in vcl a bit
Now with the ATSUI code gone is a good time for some
re-organisation. Get rid of "aqua" in file names and the separate
"coretext" folders. CoreText is all we use now for OS X (and has
always been so for iOS), so no need for a "coretext" folder, we can
keep the CoreText-using code under "quartz". Keep OS X -specific code
in "osx". Ditto for headers.
Keep "Aqua" as part of class names for now, though.
This is also preparation for planned further unification between OS X
and iOS code.
Change-Id: Ic60bd73fea4ab98183e7c8a09c7d3f66b9a34223
Diffstat (limited to 'vcl/quartz/ctfonts.cxx')
-rw-r--r-- | vcl/quartz/ctfonts.cxx | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/vcl/quartz/ctfonts.cxx b/vcl/quartz/ctfonts.cxx new file mode 100644 index 000000000000..d93d4b59b7d3 --- /dev/null +++ b/vcl/quartz/ctfonts.cxx @@ -0,0 +1,478 @@ +/* -*- 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 <boost/unordered_map.hpp> + +#include "impfont.hxx" +#include "outfont.hxx" +#include "sallayout.hxx" + +#ifdef MACOSX +#include "osx/salinst.h" +#include "osx/saldata.hxx" +#endif +#include "quartz/salgdi.h" +#include "quartz/utils.h" +#include "ctfonts.hxx" + +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/matrix/b2dhommatrix.hxx" + +// ======================================================================= + +inline double toRadian(int nDegree) +{ + return nDegree * (M_PI / 1800.0); +} + +CoreTextStyle::CoreTextStyle( const FontSelectPattern& rFSD ) +: mpFontData( (CoreTextFontData*)rFSD.mpFontData ) +, mfFontStretch( 1.0 ) +, mfFontRotation( 0.0 ) +, mpStyleDict( NULL ) +{ + mpFontData = (CoreTextFontData*)rFSD.mpFontData; + const FontSelectPattern* const pReqFont = &rFSD; + + double fScaledFontHeight = pReqFont->mfExactHeight; + + // convert font rotation to radian + mfFontRotation = toRadian(pReqFont->mnOrientation); + + // dummy matrix so we can use CGAffineTransformConcat() below + CGAffineTransform aMatrix = CGAffineTransformMakeTranslation(0, 0); + + // handle font stretching if any + if( (pReqFont->mnWidth != 0) && (pReqFont->mnWidth != pReqFont->mnHeight) ) + { + mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight; + aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMakeScale(mfFontStretch, 1.0F)); + } + + // create the style object for CoreText font attributes + static const CFIndex nMaxDictSize = 16; // TODO: does this really suffice? + mpStyleDict = CFDictionaryCreateMutable( NULL, nMaxDictSize, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); + + CFBooleanRef pCFVertBool = pReqFont->mbVertical ? kCFBooleanTrue : kCFBooleanFalse; + CFDictionarySetValue( mpStyleDict, kCTVerticalFormsAttributeName, pCFVertBool ); + + // fake bold + if ((pReqFont->GetWeight() >= WEIGHT_BOLD) && (mpFontData->GetWeight() < WEIGHT_SEMIBOLD)) + { + int nStroke = -10.0; + CFNumberRef rStroke = CFNumberCreate(NULL, kCFNumberSInt32Type, &nStroke); + CFDictionarySetValue(mpStyleDict, kCTStrokeWidthAttributeName, rStroke); + } + + // fake italic + if (((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE)) + && !((mpFontData->GetSlant() == ITALIC_NORMAL) || (mpFontData->GetSlant() == ITALIC_OBLIQUE))) + { + aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMake(1, 0, toRadian(120), 1, 0, 0)); + } + + CTFontDescriptorRef pFontDesc = (CTFontDescriptorRef)mpFontData->GetFontId(); + CTFontRef pNewCTFont = CTFontCreateWithFontDescriptor( pFontDesc, fScaledFontHeight, &aMatrix ); + 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 +} + +// ----------------------------------------------------------------------- + +CoreTextStyle::~CoreTextStyle( void ) +{ + if( mpStyleDict ) + CFRelease( mpStyleDict ); +} + +// ----------------------------------------------------------------------- + +void CoreTextStyle::GetFontMetric( float fPixelSize, 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 ); + + rMetric.mnAscent = lrint( CTFontGetAscent( aCTFontRef ) * fPixelSize); + rMetric.mnDescent = lrint( CTFontGetDescent( aCTFontRef ) * fPixelSize); + rMetric.mnExtLeading = lrint( CTFontGetLeading( aCTFontRef ) * fPixelSize); + rMetric.mnIntLeading = 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; + rMetric.mbKernableFont = true; +} + +// ----------------------------------------------------------------------- + +bool CoreTextStyle::GetGlyphBoundRect( sal_GlyphId nGlyphId, Rectangle& rRect ) const +{ + CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK; + // XXX: this is broken if the glyph came from fallback font + CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ); + + const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; // TODO: horz/vert + const CGRect aCGRect = CTFontGetBoundingRectsForGlyphs( aCTFontRef, aFontOrientation, &nCGGlyph, NULL, 1 ); + + rRect.Left() = lrint( aCGRect.origin.x ); + rRect.Top() = lrint( aCGRect.origin.y ); + rRect.Right() = lrint( aCGRect.origin.x + aCGRect.size.width ); + rRect.Bottom() = lrint( 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 CoreTextStyle::GetGlyphOutline( sal_GlyphId nGlyphId, basegfx::B2DPolyPolygon& rResult ) const +{ + rResult.clear(); + + CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK; + // XXX: this is broken if the glyph came from fallback font + CTFontRef pCTFont = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ); + CGPathRef xPath = CTFontCreatePathForGlyph( 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 + + return true; +} + +// ----------------------------------------------------------------------- + +void CoreTextStyle::SetTextColor( const RGBAColor& rColor ) +{ + CGFloat aColor[] = { rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue(), rColor.GetAlpha() }; + CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); + CGColorRef pCGColor = CGColorCreate( cs, aColor ); + CGColorSpaceRelease( cs ); + CFDictionarySetValue( mpStyleDict, kCTForegroundColorAttributeName, pCGColor ); + CFRelease( pCGColor); +} + +// ======================================================================= + +PhysicalFontFace* CoreTextFontData::Clone( void ) const +{ + return new CoreTextFontData( *this); +} + +// ----------------------------------------------------------------------- + +CoreTextStyle* CoreTextFontData::CreateTextStyle( const FontSelectPattern& rFSD ) const +{ + return new CoreTextStyle( rFSD); +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* CoreTextFontData::CreateFontInstance( /*const*/ FontSelectPattern& rFSD ) const +{ + return new ImplFontEntry( rFSD); +} + +// ----------------------------------------------------------------------- + +int CoreTextFontData::GetFontTable( const char pTagName[5], unsigned char* pResultBuf ) const +{ + DBG_ASSERT( pTagName[4]=='\0', "CoreTextFontData::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); +#if defined(MACOSX) && MACOSX_SDK_VERSION < 1080 + const uint32_t opts( kCTFontTableOptionExcludeSynthetic ); +#else + const uint32_t opts( kCTFontTableOptionNoOptions ); +#endif + CFDataRef pDataRef = CTFontCopyTable( rCTFont, nTagCode, opts); + 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; +} + +// ======================================================================= + +ImplDevFontAttributes DevFontFromCTFontDescriptor( CTFontDescriptorRef pFD, bool* bFontEnabled ) +{ + // 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.SetFamilyType( FAMILY_DONTKNOW ); + rDFA.SetPitch( PITCH_VARIABLE ); + rDFA.SetWidthType( WIDTH_NORMAL ); + rDFA.SetWeight( WEIGHT_NORMAL ); + rDFA.SetItalic( ITALIC_NONE ); + rDFA.SetSymbolFlag( false ); + + // all scalable fonts on this platform are subsettable + rDFA.mbEmbeddable = false; + rDFA.mbSubsettable = true; + + // get font name + CFStringRef pFamilyName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute ); + rDFA.SetFamilyName( GetOUString( pFamilyName ) ); + // get font style + CFStringRef pStyleName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute ); + rDFA.SetStyleName( GetOUString( pStyleName ) ); + + // get font-enabled status + if( bFontEnabled ) { + int bEnabled = FALSE; + CFNumberRef pEnabled = (CFNumberRef)CTFontDescriptorCopyAttribute( pFD, kCTFontEnabledAttribute ); + CFNumberGetValue( pEnabled, kCFNumberIntType, &bEnabled ); + *bFontEnabled = bEnabled; + } + + // 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.SetSymbolFlag( ((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.SetWeight( (FontWeight)nInt ); + + // get the font slant + double fSlant = 0; + CFNumberRef pSlantNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait ); + CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant ); + if( fSlant >= 0.035 ) + rDFA.SetItalic( 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.SetWidthType( (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 ); + + return rDFA; +} + +static void CTFontEnumCallBack( const void* pValue, void* pContext ) +{ + CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue); + + bool bFontEnabled; + ImplDevFontAttributes rDFA = DevFontFromCTFontDescriptor( pFD, &bFontEnabled ); + + if( bFontEnabled) + { + const sal_IntPtr nFontId = (sal_IntPtr)pValue; + CoreTextFontData* pFontData = new CoreTextFontData( rDFA, nFontId ); + SystemFontList* pFontList = (SystemFontList*)pContext; + pFontList->AddFont( pFontData ); + } +} + +// ======================================================================= + +SystemFontList::SystemFontList() +: mpCTFontCollection( NULL ) +, mpCTFontArray( NULL ) +{} + +// ----------------------------------------------------------------------- + +SystemFontList::~SystemFontList() +{ + 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 SystemFontList::AddFont( CoreTextFontData* pFontData ) +{ + sal_IntPtr nFontId = pFontData->GetFontId(); + maFontContainer[ nFontId ] = pFontData; +} + +// ----------------------------------------------------------------------- + +void SystemFontList::AnnounceFonts( ImplDevFontList& rFontList ) const +{ + CTFontContainer::const_iterator it = maFontContainer.begin(); + for(; it != maFontContainer.end(); ++it ) + rFontList.Add( (*it).second->Clone() ); +} + +// ----------------------------------------------------------------------- + +CoreTextFontData* SystemFontList::GetFontDataFromId( sal_IntPtr nFontId ) const +{ + CTFontContainer::const_iterator it = maFontContainer.find( nFontId ); + if( it == maFontContainer.end() ) + return NULL; + return (*it).second; +} + +// ----------------------------------------------------------------------- + +bool SystemFontList::Init( void ) +{ + // enumerate available system fonts + static const int nMaxDictEntries = 8; + CFMutableDictionaryRef pCFDict = CFDictionaryCreateMutable( NULL, + nMaxDictEntries, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); + CFDictionaryAddValue( pCFDict, kCTFontCollectionRemoveDuplicatesOption, kCFBooleanTrue ); + mpCTFontCollection = CTFontCollectionCreateFromAvailableFonts( pCFDict ); + CFRelease( pCFDict ); + + mpCTFontArray = CTFontCollectionCreateMatchingFontDescriptors( mpCTFontCollection ); + const int nFontCount = CFArrayGetCount( mpCTFontArray ); + const CFRange aFullRange = CFRangeMake( 0, nFontCount ); + CFArrayApplyFunction( mpCTFontArray, aFullRange, CTFontEnumCallBack, this ); + + return true; +} + +// ======================================================================= + +SystemFontList* GetCoretextFontList( void ) +{ + SystemFontList* pList = new SystemFontList(); + if( !pList->Init() ) { + delete pList; + return NULL; + } + + return pList; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |