/* -*- 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 "ios/common.h" #include "ios/salcoretextfontutils.hxx" #include "ios/salgdi.h" #include "sft.hxx" #include "ios/salinst.h" static bool GetDevFontAttributes( CTFontDescriptorRef font_descriptor, ImplDevFontAttributes& rDFA ) { // reset the attributes rDFA.meFamily = FAMILY_DONTKNOW; rDFA.mePitch = PITCH_VARIABLE; rDFA.meWidthType = WIDTH_NORMAL; rDFA.meWeight = WEIGHT_NORMAL; rDFA.meItalic = ITALIC_NONE; rDFA.mbSymbolFlag = false; rDFA.mbOrientation = true; rDFA.mbDevice = true; rDFA.mnQuality = 0; CFNumberRef format = (CFNumberRef)CTFontDescriptorCopyAttribute(font_descriptor, kCTFontFormatAttribute); int value = 0; CFNumberGetValue(format, kCFNumberIntType, &value); CFRelease(format); if (value == kCTFontFormatBitmap) { /* we don't want bitmap fonts */ return false; } rDFA.mbSubsettable = true; rDFA.mbEmbeddable = false; CFStringRef family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font_descriptor, kCTFontFamilyNameAttribute); rDFA.maName = GetOUString(family_name); CFRelease(family_name); CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font_descriptor, kCTFontTraitsAttribute); CFNumberRef symbolics = (CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait); value = 0; CFNumberGetValue(symbolics, kCFNumberIntType, &value); CFRelease(symbolics); if (value & kCTFontMonoSpaceTrait) { rDFA.mePitch = PITCH_FIXED; } if (value & kCTFontItalicTrait) { rDFA.meItalic = ITALIC_NORMAL; } if (value & kCTFontBoldTrait) { rDFA.meWeight = WEIGHT_BOLD; } if (value & kCTFontCondensedTrait) { rDFA.meWidthType = WIDTH_CONDENSED; } else if (value & kCTFontExpandedTrait) { rDFA.meWidthType = WIDTH_EXPANDED; } switch(value & kCTFontClassMaskTrait) { case kCTFontOldStyleSerifsClass: rDFA.meFamily = FAMILY_ROMAN; break; case kCTFontTransitionalSerifsClass: case kCTFontModernSerifsClass: case kCTFontClarendonSerifsClass: case kCTFontSlabSerifsClass: case kCTFontFreeformSerifsClass: break; case kCTFontSansSerifClass: rDFA.meFamily = FAMILY_SWISS; case kCTFontOrnamentalsClass: rDFA.meFamily = FAMILY_DECORATIVE; break; case kCTFontScriptsClass: rDFA.meFamily = FAMILY_SCRIPT; break; case kCTFontSymbolicClass: rDFA.mbSymbolFlag = true; break; } CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(traits, kCTFontWeightTrait); float fdval = 0.0; CFNumberGetValue(weight, kCFNumberFloatType, &fdval); if (fdval > 0.6) { rDFA.meWeight = WEIGHT_BLACK; } else if (fdval > 0.4) { rDFA.meWeight = WEIGHT_ULTRABOLD; } else if (fdval > 0.3) { rDFA.meWeight = WEIGHT_BOLD; } else if (fdval > 0.0) { rDFA.meWeight = WEIGHT_SEMIBOLD; } else if (fdval <= -0.8) { rDFA.meWeight = WEIGHT_ULTRALIGHT; } else if (fdval <= -0.4) { rDFA.meWeight = WEIGHT_LIGHT; } else if (fdval <= -0.3) { rDFA.meWeight = WEIGHT_SEMILIGHT; } else if (fdval <= -0.2) { rDFA.meWeight = WEIGHT_THIN; } else { rDFA.meWeight = 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.meItalic = ITALIC_OBLIQUE; } // heuristics to adjust font width if (font_name_lc.indexOf("narrow") != -1) { rDFA.meWidthType = 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.meFamily = FAMILY_SCRIPT; } else if ( (font_name_lc.indexOf("comic") != -1) || (font_name_lc.indexOf("outline") != -1) || (font_name_lc.indexOf("pinpoint") != -1) ) { rDFA.meFamily = FAMILY_DECORATIVE; } else if ( (font_name_lc.indexOf("sans") != -1) || (font_name_lc.indexOf("arial") != -1) ) { rDFA.meFamily = FAMILY_SWISS; } else if ( (font_name_lc.indexOf("roman") != -1) || (font_name_lc.indexOf("times") != -1) ) { rDFA.meFamily = FAMILY_ROMAN; } return true; } SystemFontList::SystemFontList() { CTFontCollectionRef font_collection = CTFontCollectionCreateFromAvailableFonts(NULL); if (font_collection) { CFArrayRef font_descriptors = CTFontCollectionCreateMatchingFontDescriptors(font_collection); for(int i = 0; i < CFArrayGetCount(font_descriptors); i++) { CTFontDescriptorRef font_descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(font_descriptors, i); CTFontRef font = CTFontCreateWithFontDescriptor(font_descriptor, 0, NULL); ImplDevFontAttributes devfont_attr; if (GetDevFontAttributes( font_descriptor, devfont_attr ) ) { ImplCoreTextFontData* font_data = new ImplCoreTextFontData(devfont_attr, font); if (font_data && font_data->GetCTFont()) { m_aFontContainer [ font_data->GetCTFont() ] = font_data; } } 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(); } ImplCoreTextFontData* 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() ); } } ImplCoreTextFontData::ImplCoreTextFontData( 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 ) { } ImplCoreTextFontData::~ImplCoreTextFontData() { if ( m_pCharMap ) { m_pCharMap->DeReference(); } if ( m_CTFontRef ) { CFRelease(m_CTFontRef); } } PhysicalFontFace* ImplCoreTextFontData::Clone() const { ImplCoreTextFontData* pClone = new ImplCoreTextFontData(*this); if ( m_pCharMap ) { m_pCharMap->AddReference(); } if ( m_CTFontRef ) { pClone->m_CTFontRef = (CTFontRef)CFRetain(m_CTFontRef); } return pClone; } ImplFontEntry* ImplCoreTextFontData::CreateFontInstance(FontSelectPattern& rFSD) const { return new ImplFontEntry(rFSD); } const ImplFontCharMap* ImplCoreTextFontData::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 ImplCoreTextFontData::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 ImplCoreTextFontData::GetRawFontData( std::vector& 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; } if (CFF_table) { 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 ImplCoreTextFontData::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 ImplCoreTextFontData::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 ImplCoreTextFontData::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: */