diff options
Diffstat (limited to 'vcl/coretext/salcoretextfontutils.cxx')
-rw-r--r-- | vcl/coretext/salcoretextfontutils.cxx | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/vcl/coretext/salcoretextfontutils.cxx b/vcl/coretext/salcoretextfontutils.cxx new file mode 100644 index 000000000000..a9c0871e0157 --- /dev/null +++ b/vcl/coretext/salcoretextfontutils.cxx @@ -0,0 +1,629 @@ +/* -*- 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 "coretext/common.h" + +#include "coretext/salcoretextfontutils.hxx" +#include "coretext/salgdi.h" + +#include "sft.hxx" + +#ifdef MACOSX +#include "aqua/salinst.h" +#else // IOS +#include "ios/salinst.h" +#endif + +static bool GetDevFontAttributes( CTFontDescriptorRef font_descriptor, ImplDevFontAttributes& rDFA ) +{ + int value = 0; + + // reset the attributes + rDFA.SetFamilyType( FAMILY_DONTKNOW ); + rDFA.SetPitch( PITCH_VARIABLE ); + rDFA.SetWidthType( WIDTH_NORMAL ); + rDFA.SetWeight( WEIGHT_NORMAL ); + rDFA.SetItalic( ITALIC_NONE ); + rDFA.SetSymbolFlag( false ); + rDFA.mbOrientation = true; + rDFA.mbDevice = true; + rDFA.mnQuality = 0; + +#if defined(MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + CTFontRef font = CTFontCreateWithFontDescriptor(font_descriptor, 0.0, NULL); + CFDataRef rHeadTable = CTFontCopyTable(font, kCTFontTableHead, kCTFontTableOptionNoOptions); + CFRelease(font); + if(!rHeadTable || CFDataGetLength(rHeadTable) == 0) + { + SafeCFRelease(rHeadTable); + return false; + } + CFRelease(rHeadTable); +#else + CFNumberRef format = (CFNumberRef)CTFontDescriptorCopyAttribute(font_descriptor, kCTFontFormatAttribute); + CFNumberGetValue(format, kCFNumberIntType, &value); + CFRelease(format); + + if(value == kCTFontFormatBitmap) + { + /* we don't want bitmap fonts */ + return false; + } +#endif + rDFA.mbSubsettable = true; + rDFA.mbEmbeddable = false; + + CFStringRef family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font_descriptor, kCTFontFamilyNameAttribute); + rDFA.SetFamilyName( GetOUString(family_name) ); + CFRelease(family_name); + + CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font_descriptor, kCTFontTraitsAttribute); + CFNumberRef symbolics = (CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + CFNumberGetValue(symbolics, kCFNumberIntType, &value); + CFRelease(symbolics); + + if(value & kCTFontMonoSpaceTrait) + { + rDFA.SetPitch( PITCH_FIXED ); + } + + if(value & kCTFontItalicTrait) + { + rDFA.SetItalic( ITALIC_NORMAL ); + } + + if(value & kCTFontBoldTrait) + { + rDFA.SetWeight( WEIGHT_BOLD ); + } + + if(value & kCTFontCondensedTrait) + { + rDFA.SetWidthType( WIDTH_CONDENSED ); + } + else if(value & kCTFontExpandedTrait) + { + rDFA.SetWidthType( WIDTH_EXPANDED ); + } + switch(value & kCTFontClassMaskTrait) + { + case kCTFontOldStyleSerifsClass: + rDFA.SetFamilyType( FAMILY_ROMAN ); + break; + case kCTFontTransitionalSerifsClass: + case kCTFontModernSerifsClass: + case kCTFontClarendonSerifsClass: + case kCTFontSlabSerifsClass: + case kCTFontFreeformSerifsClass: + break; + case kCTFontSansSerifClass: + rDFA.SetFamilyType( FAMILY_SWISS ); + case kCTFontOrnamentalsClass: + rDFA.SetFamilyType( FAMILY_DECORATIVE ); + break; + case kCTFontScriptsClass: + rDFA.SetFamilyType( FAMILY_SCRIPT ); + break; + case kCTFontSymbolicClass: + rDFA.SetSymbolFlag( true ); + break; + } + + CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(traits, kCTFontWeightTrait); + float fdval = 0.0; + CFNumberGetValue(weight, kCFNumberFloatType, &fdval); + if(fdval > 0.6) + { + rDFA.SetWeight( WEIGHT_BLACK ); + } + else if(fdval > 0.4) + { + rDFA.SetWeight( WEIGHT_ULTRABOLD ); + } + else if (fdval > 0.3) + { + rDFA.SetWeight( WEIGHT_BOLD ); + } + else if (fdval > 0.0) + { + rDFA.SetWeight( WEIGHT_SEMIBOLD ); + } + else if (fdval <= -0.8) + { + rDFA.SetWeight( WEIGHT_ULTRALIGHT ); + } + else if (fdval <= -0.4) + { + rDFA.SetWeight( WEIGHT_LIGHT ); + } + else if (fdval <= -0.3) + { + rDFA.SetWeight( WEIGHT_SEMILIGHT ); + } + else if (fdval <= -0.2) + { + rDFA.SetWeight( WEIGHT_THIN ); + } + else + { + rDFA.SetWeight( WEIGHT_NORMAL ); + } + + CFStringRef string_ref = (CFStringRef)CTFontDescriptorCopyAttribute(font_descriptor, kCTFontStyleNameAttribute); + rtl::OUString font_name = GetOUString(string_ref); + rtl::OUString font_name_lc(font_name.toAsciiLowerCase()); + CFRelease(string_ref); + + // heuristics to adjust font slant + if( (font_name_lc.indexOf("oblique") != -1) || + (font_name_lc.indexOf("inclined") != -1) || + (font_name_lc.indexOf("slanted") != -1) ) + { + rDFA.SetItalic( ITALIC_OBLIQUE ); + } + + // heuristics to adjust font width + if (font_name_lc.indexOf("narrow") != -1) + { + rDFA.SetWidthType( WIDTH_SEMI_CONDENSED ); + } + + // heuristics for font family type + if( (font_name_lc.indexOf("script") != -1) || + (font_name_lc.indexOf("chancery") != -1) || + (font_name_lc.indexOf("zapfino") != -1)) + { + rDFA.SetFamilyType( FAMILY_SCRIPT ); + } + else if( (font_name_lc.indexOf("comic") != -1) || + (font_name_lc.indexOf("outline") != -1) || + (font_name_lc.indexOf("pinpoint") != -1) ) + { + rDFA.SetFamilyType( FAMILY_DECORATIVE ); + } + else if( (font_name_lc.indexOf("sans") != -1) || + (font_name_lc.indexOf("arial") != -1) ) + { + rDFA.SetFamilyType( FAMILY_SWISS ); + } + else if( (font_name_lc.indexOf("roman") != -1) || + (font_name_lc.indexOf("times") != -1) ) + { + rDFA.SetFamilyType( FAMILY_ROMAN ); + } + return true; +} + +SystemFontList::SystemFontList() +{ + CTFontCollectionRef font_collection = CTFontCollectionCreateFromAvailableFonts(NULL); + if(font_collection) + { + CFArrayRef font_descriptors = CTFontCollectionCreateMatchingFontDescriptors(font_collection); + + if(font_descriptors) + { + for(int i = 0; i < CFArrayGetCount(font_descriptors); i++) + { + CTFontDescriptorRef font_descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(font_descriptors, i); + CTFontRef font = CTFontCreateWithFontDescriptor(font_descriptor, 0, NULL); + if(font) + { + ImplDevFontAttributes devfont_attr; + if(GetDevFontAttributes( font_descriptor, devfont_attr ) ) + { + CoreTextPhysicalFontFace* font_face = new CoreTextPhysicalFontFace(devfont_attr, font); + if(font_face && font_face->GetCTFont()) + { + m_aFontContainer [ font_face->GetCTFont() ] = font_face; + } + } + CFRelease(font); + } + } + CFRelease(font_descriptors); + } + CFRelease(font_collection); + } + +} + +SystemFontList::~SystemFontList() +{ + CoreTextFontContainer::const_iterator it = m_aFontContainer.begin(); + for(; it != m_aFontContainer.end(); ++it ) + delete (*it).second; + m_aFontContainer.clear(); +} + +CoreTextPhysicalFontFace* SystemFontList::GetFontDataFromRef( CTFontRef font ) const +{ + CoreTextFontContainer::const_iterator it = m_aFontContainer.find( font ); + return it == m_aFontContainer.end() ? NULL : (*it).second; +} + + +void SystemFontList::AnnounceFonts( ImplDevFontList& rFontList ) const +{ + CoreTextFontContainer::const_iterator it = m_aFontContainer.begin(); + for(; it != m_aFontContainer.end(); ++it ) + { + rFontList.Add( (*it).second->Clone() ); + } +} + +CoreTextPhysicalFontFace::CoreTextPhysicalFontFace( const ImplDevFontAttributes& rDFA, CTFontRef font ) +: PhysicalFontFace( rDFA, 0 ) +, m_CTFontRef((CTFontRef)CFRetain(font)) +, m_pCharMap( NULL ) +, m_bHasOs2Table( false ) +, m_bOs2TableRead( false ) +, m_bCmapTableRead( false ) +, m_bHasCJKSupport( false ) +, m_bFontCapabilitiesRead( false ) +{ + SAL_INFO( "vcl.coretext.font", "retain " << font << " as " << m_CTFontRef ); +} + +CoreTextPhysicalFontFace::~CoreTextPhysicalFontFace() +{ + if( m_pCharMap ) + { + m_pCharMap->DeReference(); + } + SAL_INFO( "vcl.coretext.font", "release " << m_CTFontRef ); + SafeCFRelease(m_CTFontRef); +} + +PhysicalFontFace* CoreTextPhysicalFontFace::Clone() const +{ + CoreTextPhysicalFontFace* pClone = new CoreTextPhysicalFontFace(*this); + if( m_pCharMap ) + { + m_pCharMap->AddReference(); + } + if( m_CTFontRef ) + { + pClone->m_CTFontRef = (CTFontRef)CFRetain(m_CTFontRef); + SAL_INFO( "vcl.coretext.font", "clone " << m_CTFontRef << " into " << pClone->m_CTFontRef ); + } + return pClone; +} + +ImplFontEntry* CoreTextPhysicalFontFace::CreateFontInstance(FontSelectPattern& rFSD) const +{ + return new ImplFontEntry(rFSD); +} + +const ImplFontCharMap* CoreTextPhysicalFontFace::GetImplFontCharMap() +{ + // return the cached charmap + if( m_pCharMap ) + { + return m_pCharMap; + } + // set the default charmap + m_pCharMap = ImplFontCharMap::GetDefaultMap(); + m_pCharMap->AddReference(); + + // get the CMAP byte size + CFDataRef rCmapTable = CTFontCopyTable( m_CTFontRef, kCTFontTableCmap, kCTFontTableOptionNoOptions); + if(!rCmapTable) + { + return m_pCharMap; + } + if(!m_bCmapTableRead) + { + m_bCmapTableRead = true; + DetermineCJKSupport_cmap(rCmapTable); + } + // parse the CMAP + CmapResult aCmapResult; + if(ParseCMAP( CFDataGetBytePtr(rCmapTable), CFDataGetLength(rCmapTable), aCmapResult ) ) + { + m_pCharMap = new ImplFontCharMap( aCmapResult ); + m_pCharMap->AddReference(); + } + CFRelease(rCmapTable); + return m_pCharMap; +} + +bool CoreTextPhysicalFontFace::GetImplFontCapabilities(vcl::FontCapabilities &rFontCapabilities) +{ + // read this only once per font + if( m_bFontCapabilitiesRead ) + { + rFontCapabilities = m_aFontCapabilities; + return !rFontCapabilities.maUnicodeRange.empty() || !rFontCapabilities.maCodePageRange.empty(); + } + m_bFontCapabilitiesRead = true; + + // get the GSUB table raw data + CFDataRef rGSUBTable = CTFontCopyTable( m_CTFontRef, kCTFontTableGSUB, kCTFontTableOptionNoOptions); + if(rGSUBTable) + { + + vcl::getTTScripts(m_aFontCapabilities.maGSUBScriptTags, + CFDataGetBytePtr(rGSUBTable), CFDataGetLength(rGSUBTable)); + CFRelease(rGSUBTable); + } + CFDataRef OS2_Table = CTFontCopyTable( m_CTFontRef, kCTFontTableOS2, kCTFontTableOptionNoOptions); + if(OS2_Table) + { + vcl::getTTCoverage( + m_aFontCapabilities.maUnicodeRange, + m_aFontCapabilities.maCodePageRange, + CFDataGetBytePtr(OS2_Table), CFDataGetLength(OS2_Table)); + /* while we are at it let's solve HasCJK for the same price */ + if(!m_bOs2TableRead ) + { + m_bOs2TableRead = true; + m_bHasOs2Table = true; + DetermineCJKSupport_OS2(OS2_Table); + } + CFRelease(OS2_Table); + } + rFontCapabilities = m_aFontCapabilities; + return !rFontCapabilities.maUnicodeRange.empty() || !rFontCapabilities.maCodePageRange.empty(); +} + +struct font_table +{ + unsigned char* table; + unsigned char* dir_entry; + unsigned char* cursor; +}; + +void addTable(struct font_table* table, CTFontTableTag tag, CFDataRef data) +{ + if(data && CFDataGetLength(data) > 0) + { + *(uint32_t*)table->dir_entry = CFSwapInt32HostToBig(tag); + table->dir_entry += 4; + *(uint32_t*)table->dir_entry = 0; /* TODO: checksum */ + table->dir_entry += 4; + *(uint32_t*)table->dir_entry = CFSwapInt32HostToBig((uint32_t)((uintptr_t)table->cursor - (uintptr_t)table)); + table->dir_entry += 4; + *(uint32_t*)table->dir_entry = CFSwapInt32HostToBig(CFDataGetLength(data)); + table->dir_entry += 4; + + memcpy(table->cursor, CFDataGetBytePtr(data), CFDataGetLength(data)); + table->cursor += CFDataGetLength(data); + } +} + +bool CoreTextPhysicalFontFace::GetRawFontData( std::vector<unsigned char>& rBuffer, bool* pJustCFF ) const +{ + bool rc; + int table_count = 0; + + CFDataRef CFF_table = CTFontCopyTable( m_CTFontRef, kCTFontTableCFF, kCTFontTableOptionNoOptions); + if(pJustCFF) + { + if(CFF_table) + { + *pJustCFF = CFDataGetLength(CFF_table) ? true : false; + CFRelease(CFF_table); + return true; + } + else + { + return false; + } + } + size_t total_len = 0; + CFDataRef head_table = CTFontCopyTable( m_CTFontRef, kCTFontTableHead, kCTFontTableOptionNoOptions); + CFDataRef maxp_table = CTFontCopyTable( m_CTFontRef, kCTFontTableMaxp, kCTFontTableOptionNoOptions); + CFDataRef cmap_table = CTFontCopyTable( m_CTFontRef, kCTFontTableHead, kCTFontTableOptionNoOptions); + CFDataRef name_table = CTFontCopyTable( m_CTFontRef, kCTFontTableName, kCTFontTableOptionNoOptions); + CFDataRef hhea_table = CTFontCopyTable( m_CTFontRef, kCTFontTableHhea, kCTFontTableOptionNoOptions); + CFDataRef hmtx_table = CTFontCopyTable( m_CTFontRef, kCTFontTableHmtx, kCTFontTableOptionNoOptions); + rc = false; + if(head_table && maxp_table && cmap_table && name_table && hhea_table && hmtx_table) + { + if(CFDataGetLength(head_table) && + CFDataGetLength(maxp_table) && + CFDataGetLength(name_table) && + CFDataGetLength(hhea_table) && + CFDataGetLength(hmtx_table)) + { + table_count += 6; + total_len = CFDataGetLength(head_table) + + CFDataGetLength(maxp_table) + + CFDataGetLength(name_table) + + CFDataGetLength(hhea_table) + + CFDataGetLength(hmtx_table); + rc = true; + } + } + + CFDataRef loca_table = NULL; + CFDataRef glyf_table = NULL; + CFDataRef prep_table = NULL; + CFDataRef cvt_table = NULL; + CFDataRef fpgm_table = NULL; + if(rc) + { + if(!CFF_table || CFDataGetLength(CFF_table) == 0) + { + loca_table = CTFontCopyTable( m_CTFontRef, kCTFontTableLoca, kCTFontTableOptionNoOptions); + glyf_table = CTFontCopyTable( m_CTFontRef, kCTFontTableGlyf, kCTFontTableOptionNoOptions); + if(!loca_table || !glyf_table || !CFDataGetLength(loca_table) || !CFDataGetLength(glyf_table)) + { + rc = false; + } + else + { + table_count += 2; + total_len += CFDataGetLength(loca_table) + CFDataGetLength(glyf_table); + prep_table = CTFontCopyTable( m_CTFontRef, kCTFontTablePrep, kCTFontTableOptionNoOptions); + cvt_table = CTFontCopyTable( m_CTFontRef, kCTFontTableCvt, kCTFontTableOptionNoOptions); + fpgm_table = CTFontCopyTable( m_CTFontRef, kCTFontTableFpgm, kCTFontTableOptionNoOptions); + if(prep_table || CFDataGetLength(prep_table) > 0) + { + table_count += 1; + total_len += CFDataGetLength(prep_table); + } + if(cvt_table || CFDataGetLength(cvt_table) > 0) + { + table_count += 1; + total_len += CFDataGetLength(cvt_table); + } + if(fpgm_table || CFDataGetLength(fpgm_table) > 0) + { + table_count += 1; + total_len += CFDataGetLength(fpgm_table); + } + } + } + else + { + table_count += 1; + total_len += CFDataGetLength(CFF_table); + } + } + if(rc) + { + total_len += 12 + 16 * table_count; + rBuffer.resize(total_len); + struct font_table table; + unsigned char* cursor = &rBuffer[0]; + int nLog2 = 0; + + while( (table_count >> nLog2) > 1 ) ++nLog2; + + table.table = cursor; + *(uint16_t*)cursor = CFSwapInt16HostToBig(1); + cursor += 2; + *(uint16_t*)cursor = 0; + cursor += 2; + *(uint16_t*)cursor = CFSwapInt16HostToBig(table_count); + cursor += 2; + *(uint16_t*)cursor = CFSwapInt16HostToBig(nLog2 * 16); + cursor += 2; + *(uint16_t*)cursor = CFSwapInt16HostToBig(nLog2); + cursor += 2; + *(uint16_t*)cursor = CFSwapInt16HostToBig((table_count - nLog2) * 16); // rangeShift + cursor += 2; + table.dir_entry = cursor; + cursor += (16 * table_count); + table.cursor = cursor; + addTable(&table, kCTFontTableCmap, cmap_table); + addTable(&table, kCTFontTableCvt, cvt_table); + addTable(&table, kCTFontTableFpgm, fpgm_table); + addTable(&table, kCTFontTableCFF, CFF_table); + addTable(&table, kCTFontTableGlyf, glyf_table); + addTable(&table, kCTFontTableLoca, loca_table); + addTable(&table, kCTFontTableHead, head_table); + addTable(&table, kCTFontTableHhea, hhea_table); + addTable(&table, kCTFontTableHmtx, hmtx_table); + addTable(&table, kCTFontTableMaxp, maxp_table); + addTable(&table, kCTFontTableName, name_table); + addTable(&table, kCTFontTablePrep, prep_table); + } + SafeCFRelease(cmap_table); + SafeCFRelease(cvt_table); + SafeCFRelease(fpgm_table); + SafeCFRelease(CFF_table); + SafeCFRelease(glyf_table); + SafeCFRelease(loca_table); + SafeCFRelease(head_table); + SafeCFRelease(hhea_table); + SafeCFRelease(hmtx_table); + SafeCFRelease(maxp_table); + SafeCFRelease(name_table); + SafeCFRelease(prep_table); + + return rc; +} + +void CoreTextPhysicalFontFace::DetermineCJKSupport_OS2(CFDataRef rOS2Table) +{ + if(CFDataGetLength(rOS2Table) >= 48) + { + const unsigned short* pOS2buffer = (const unsigned short*)CFDataGetBytePtr(rOS2Table); + const unsigned short version = CFSwapInt16BigToHost(pOS2buffer[0]); + if( version >= 1) + { + const unsigned short unicode_range = CFSwapInt16BigToHost(pOS2buffer[23]); + if( unicode_range & 0x2DF0) + { + m_bHasCJKSupport = true; + } + } + } +} + +void CoreTextPhysicalFontFace::DetermineCJKSupport_cmap(CFDataRef rCmapTable) +{ + int table_len = CFDataGetLength(rCmapTable) / 2; + if(table_len >= 12) + { + const unsigned short* pCmap = (const unsigned short*)CFDataGetBytePtr(rCmapTable); + if(pCmap[0] == 0) + { + short nb_sub_tables = CFSwapInt16BigToHost(pCmap[1]); + for(int i = 2; --nb_sub_tables >= 0 && i < table_len; i += 4) + { + short platform = CFSwapInt16BigToHost(pCmap[i]); + if( platform == kFontMacintoshPlatform ) + { + short encoding = CFSwapInt16BigToHost(pCmap[i+1]); + if( encoding == kFontJapaneseScript || + encoding == kFontTraditionalChineseScript || + encoding == kFontKoreanScript || + encoding == kFontSimpleChineseScript ) + { + m_bHasCJKSupport = true; + break; + } + } + } + } + } +} + +bool CoreTextPhysicalFontFace::HasCJKSupport( void ) +{ + // read this only once per font + if(!m_bOs2TableRead ) + { + m_bOs2TableRead = true; + CFDataRef rOS2Table = CTFontCopyTable( m_CTFontRef, kCTFontTableOS2, kCTFontTableOptionNoOptions); + if(rOS2Table) + { + m_bHasOs2Table = true; + DetermineCJKSupport_OS2(rOS2Table); + CFRelease(rOS2Table); + } + } + if( !m_bCmapTableRead && !m_bHasOs2Table && !m_bHasCJKSupport ) + { + m_bCmapTableRead = true; + CFDataRef rCmapTable = CTFontCopyTable( m_CTFontRef, kCTFontTableCmap, kCTFontTableOptionNoOptions); + if(rCmapTable) + { + DetermineCJKSupport_cmap(rCmapTable); + CFRelease(rCmapTable); + } + } + return m_bHasCJKSupport; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |