summaryrefslogtreecommitdiffstats
path: root/vcl/aqua/source/gdi/atsui/salatsuifontutils.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/aqua/source/gdi/atsui/salatsuifontutils.cxx')
-rw-r--r--vcl/aqua/source/gdi/atsui/salatsuifontutils.cxx493
1 files changed, 493 insertions, 0 deletions
diff --git a/vcl/aqua/source/gdi/atsui/salatsuifontutils.cxx b/vcl/aqua/source/gdi/atsui/salatsuifontutils.cxx
new file mode 100644
index 000000000000..c56ac0d901ee
--- /dev/null
+++ b/vcl/aqua/source/gdi/atsui/salatsuifontutils.cxx
@@ -0,0 +1,493 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <boost/assert.hpp>
+#include <vector>
+#include <set>
+
+#include "vcl/svapp.hxx"
+
+#include "aqua/atsui/salgdi.h"
+#include "aqua/saldata.hxx"
+#include "aqua/atsui/salatsuifontutils.hxx"
+
+// ATSUI is deprecated in 10.6 (or already 10.5?)
+#if defined LIBO_WERROR && defined __GNUC__
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION >= 40201
+#pragma GCC diagnostic warning "-Wdeprecated-declarations"
+#endif
+#endif
+
+// we have to get the font attributes from the name table
+// since neither head's macStyle nor OS/2's panose are easily available
+// during font enumeration. macStyle bits would be not sufficient anyway
+// and SFNT fonts on Mac usually do not contain an OS/2 table.
+static void UpdateAttributesFromPSName( const String& rPSName, ImplDevFontAttributes& rDFA )
+{
+ rtl::OString aPSName( rtl::OUStringToOString( rPSName, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase() );
+
+ // TODO: use a multi-string ignore-case matcher once it becomes available
+ if( (aPSName.indexOf("regular") != -1)
+ || (aPSName.indexOf("normal") != -1)
+ || (aPSName.indexOf("roman") != -1)
+ || (aPSName.indexOf("medium") != -1)
+ || (aPSName.indexOf("plain") != -1)
+ || (aPSName.indexOf("standard") != -1)
+ || (aPSName.indexOf("std") != -1) )
+ {
+ rDFA.meWidthType = WIDTH_NORMAL;
+ rDFA.meWeight = WEIGHT_NORMAL;
+ rDFA.meItalic = ITALIC_NONE;
+ }
+
+ // heuristics for font weight
+ if (aPSName.indexOf("extrablack") != -1)
+ rDFA.meWeight = WEIGHT_BLACK;
+ else if (aPSName.indexOf("black") != -1)
+ rDFA.meWeight = WEIGHT_BLACK;
+ //else if (aPSName.indexOf("book") != -1)
+ // rDFA.meWeight = WEIGHT_SEMIBOLD;
+ else if( (aPSName.indexOf("semibold") != -1)
+ || (aPSName.indexOf("smbd") != -1))
+ rDFA.meWeight = WEIGHT_SEMIBOLD;
+ else if (aPSName.indexOf("ultrabold") != -1)
+ rDFA.meWeight = WEIGHT_ULTRABOLD;
+ else if (aPSName.indexOf("extrabold") != -1)
+ rDFA.meWeight = WEIGHT_BLACK;
+ else if( (aPSName.indexOf("bold") != -1)
+ || (aPSName.indexOf("-bd") != -1))
+ rDFA.meWeight = WEIGHT_BOLD;
+ else if (aPSName.indexOf("extralight") != -1)
+ rDFA.meWeight = WEIGHT_ULTRALIGHT;
+ else if (aPSName.indexOf("ultralight") != -1)
+ rDFA.meWeight = WEIGHT_ULTRALIGHT;
+ else if (aPSName.indexOf("light") != -1)
+ rDFA.meWeight = WEIGHT_LIGHT;
+ else if (aPSName.indexOf("thin") != -1)
+ rDFA.meWeight = WEIGHT_THIN;
+ else if (aPSName.indexOf("-w3") != -1)
+ rDFA.meWeight = WEIGHT_LIGHT;
+ else if (aPSName.indexOf("-w4") != -1)
+ rDFA.meWeight = WEIGHT_SEMILIGHT;
+ else if (aPSName.indexOf("-w5") != -1)
+ rDFA.meWeight = WEIGHT_NORMAL;
+ else if (aPSName.indexOf("-w6") != -1)
+ rDFA.meWeight = WEIGHT_SEMIBOLD;
+ else if (aPSName.indexOf("-w7") != -1)
+ rDFA.meWeight = WEIGHT_BOLD;
+ else if (aPSName.indexOf("-w8") != -1)
+ rDFA.meWeight = WEIGHT_ULTRABOLD;
+ else if (aPSName.indexOf("-w9") != -1)
+ rDFA.meWeight = WEIGHT_BLACK;
+
+ // heuristics for font slant
+ if( (aPSName.indexOf("italic") != -1)
+ || (aPSName.indexOf(" ital") != -1)
+ || (aPSName.indexOf("cursive") != -1)
+ || (aPSName.indexOf("-it") != -1)
+ || (aPSName.indexOf("lightit") != -1)
+ || (aPSName.indexOf("mediumit") != -1)
+ || (aPSName.indexOf("boldit") != -1)
+ || (aPSName.indexOf("cnit") != -1)
+ || (aPSName.indexOf("bdcn") != -1)
+ || (aPSName.indexOf("bdit") != -1)
+ || (aPSName.indexOf("condit") != -1)
+ || (aPSName.indexOf("bookit") != -1)
+ || (aPSName.indexOf("blackit") != -1) )
+ rDFA.meItalic = ITALIC_NORMAL;
+ if( (aPSName.indexOf("oblique") != -1)
+ || (aPSName.indexOf("inclined") != -1)
+ || (aPSName.indexOf("slanted") != -1) )
+ rDFA.meItalic = ITALIC_OBLIQUE;
+
+ // heuristics for font width
+ if( (aPSName.indexOf("condensed") != -1)
+ || (aPSName.indexOf("-cond") != -1)
+ || (aPSName.indexOf("boldcond") != -1)
+ || (aPSName.indexOf("boldcn") != -1)
+ || (aPSName.indexOf("cnit") != -1) )
+ rDFA.meWidthType = WIDTH_CONDENSED;
+ else if (aPSName.indexOf("narrow") != -1)
+ rDFA.meWidthType = WIDTH_SEMI_CONDENSED;
+ else if (aPSName.indexOf("expanded") != -1)
+ rDFA.meWidthType = WIDTH_EXPANDED;
+ else if (aPSName.indexOf("wide") != -1)
+ rDFA.meWidthType = WIDTH_EXPANDED;
+
+ // heuristics for font pitch
+ if( (aPSName.indexOf("mono") != -1)
+ || (aPSName.indexOf("courier") != -1)
+ || (aPSName.indexOf("monaco") != -1)
+ || (aPSName.indexOf("typewriter") != -1) )
+ rDFA.mePitch = PITCH_FIXED;
+
+ // heuristics for font family type
+ if( (aPSName.indexOf("script") != -1)
+ || (aPSName.indexOf("chancery") != -1)
+ || (aPSName.indexOf("zapfino") != -1))
+ rDFA.meFamily = FAMILY_SCRIPT;
+ else if( (aPSName.indexOf("comic") != -1)
+ || (aPSName.indexOf("outline") != -1)
+ || (aPSName.indexOf("pinpoint") != -1) )
+ rDFA.meFamily = FAMILY_DECORATIVE;
+ else if( (aPSName.indexOf("sans") != -1)
+ || (aPSName.indexOf("arial") != -1) )
+ rDFA.meFamily = FAMILY_SWISS;
+ else if( (aPSName.indexOf("roman") != -1)
+ || (aPSName.indexOf("times") != -1) )
+ rDFA.meFamily = FAMILY_ROMAN;
+
+ // heuristics for codepoint semantic
+ if( (aPSName.indexOf("symbol") != -1)
+ || (aPSName.indexOf("dings") != -1)
+ || (aPSName.indexOf("dingbats") != -1)
+ || (aPSName.indexOf("ornaments") != -1)
+ || (aPSName.indexOf("embellishments") != -1) )
+ rDFA.mbSymbolFlag = true;
+
+ // #i100020# special heuristic for names with single-char styles
+ // NOTE: we are checking name that hasn't been lower-cased
+ if( rPSName.Len() > 3 )
+ {
+ int i = rPSName.Len();
+ sal_Unicode c = rPSName.GetChar( --i );
+ if( c == 'C' ) { // "capitals"
+ rDFA.meFamily = FAMILY_DECORATIVE;
+ c = rPSName.GetChar( --i );
+ }
+ if( c == 'O' ) { // CFF-based OpenType
+ c = rPSName.GetChar( --i );
+ }
+ if( c == 'I' ) { // "italic"
+ rDFA.meItalic = ITALIC_NORMAL;
+ c = rPSName.GetChar( --i );
+ }
+ if( c == 'B' ) // "bold"
+ rDFA.meWeight = WEIGHT_BOLD;
+ if( c == 'C' ) // "capitals"
+ rDFA.meFamily = FAMILY_DECORATIVE;
+ // TODO: check that all single-char styles have been resolved?
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static bool GetDevFontAttributes( ATSUFontID nFontID, ImplDevFontAttributes& rDFA )
+{
+ // all ATSU fonts are device fonts that can be directly rotated
+ rDFA.mbOrientation = true;
+ rDFA.mbDevice = true;
+ rDFA.mnQuality = 0;
+
+ // 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;
+
+ // ignore bitmap fonts
+ ATSFontRef rATSFontRef = FMGetATSFontRefFromFont( nFontID );
+ ByteCount nHeadLen = 0;
+ OSStatus rc = ATSFontGetTable( rATSFontRef, 0x68656164/*head*/, 0, 0, NULL, &nHeadLen );
+ if( (rc != noErr) || (nHeadLen <= 0) )
+ return false;
+
+ // all scalable fonts on this platform are subsettable
+ rDFA.mbSubsettable = true;
+ rDFA.mbEmbeddable = false;
+
+ // prepare iterating over all name strings of the font
+ ItemCount nFontNameCount = 0;
+ rc = ATSUCountFontNames( nFontID, &nFontNameCount );
+ if( rc != noErr )
+ return false;
+ int nBestNameValue = 0;
+ int nBestStyleValue = 0;
+ FontLanguageCode eBestLangCode = 0;
+ const FontLanguageCode eUILangCode = Application::GetSettings().GetUILanguage();
+ typedef std::vector<char> NameBuffer;
+ NameBuffer aNameBuffer( 256 );
+
+ // iterate over all available name strings of the font
+ for( ItemCount nNameIndex = 0; nNameIndex < nFontNameCount; ++nNameIndex )
+ {
+ ByteCount nNameLength = 0;
+
+ FontNameCode eFontNameCode;
+ FontPlatformCode eFontNamePlatform;
+ FontScriptCode eFontNameScript;
+ FontLanguageCode eFontNameLanguage;
+ rc = ATSUGetIndFontName( nFontID, nNameIndex, 0, NULL,
+ &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
+ if( rc != noErr )
+ continue;
+
+ // ignore non-interesting name entries
+ if( (eFontNameCode != kFontFamilyName)
+ && (eFontNameCode != kFontStyleName)
+ && (eFontNameCode != kFontPostscriptName) )
+ continue;
+
+ // heuristic to find the most common font name
+ // prefering default language names or even better the names matching to the UI language
+ int nNameValue = (eFontNameLanguage==eUILangCode) ? 0 : ((eFontNameLanguage==0) ? -10 : -20);
+ rtl_TextEncoding eEncoding = RTL_TEXTENCODING_UNICODE;
+ const int nPlatformEncoding = ((int)eFontNamePlatform << 8) + (int)eFontNameScript;
+ switch( nPlatformEncoding )
+ {
+ case 0x000: nNameValue += 23; break; // Unicode 1.0
+ case 0x001: nNameValue += 24; break; // Unicode 1.1
+ case 0x002: nNameValue += 25; break; // iso10646_1993
+ case 0x003: nNameValue += 26; break; // UCS-2
+ case 0x301: nNameValue += 27; break; // Win UCS-2
+ case 0x004: // UCS-4
+ case 0x30A: nNameValue += 0; // Win-UCS-4
+ eEncoding = RTL_TEXTENCODING_UCS4;
+ break;
+ case 0x100: nNameValue += 21; // Mac Roman
+ eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
+ break;
+ case 0x300: nNameValue = 0; // Win Symbol encoded name!
+ rDFA.mbSymbolFlag = true; // (often seen for symbol fonts)
+ break;
+ default: nNameValue = 0; // ignore other encodings
+ break;
+ }
+
+ // ignore name entries with no useful encoding
+ if( nNameValue <= 0 )
+ continue;
+ if( nNameLength >= aNameBuffer.size() )
+ continue;
+
+ // get the encoded name
+ aNameBuffer.reserve( nNameLength+1 ); // extra byte helps for debugging
+ rc = ATSUGetIndFontName( nFontID, nNameIndex, nNameLength, &aNameBuffer[0],
+ &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
+ if( rc != noErr )
+ continue;
+
+ // convert to unicode name
+ UniString aUtf16Name;
+ if( eEncoding == RTL_TEXTENCODING_UNICODE ) // we are just interested in UTF16 encoded names
+ aUtf16Name = UniString( (const sal_Unicode*)&aNameBuffer[0], nNameLength/2 );
+ else if( eEncoding == RTL_TEXTENCODING_UCS4 )
+ aUtf16Name = UniString(); // TODO
+ else // assume the non-unicode encoded names are byte encoded
+ aUtf16Name = UniString( &aNameBuffer[0], nNameLength, eEncoding );
+
+ // ignore empty strings
+ if( aUtf16Name.Len() <= 0 )
+ continue;
+
+ // handle the name depending on its namecode
+ switch( eFontNameCode )
+ {
+ case kFontFamilyName:
+ // ignore font names starting with '.'
+ if( aUtf16Name.GetChar(0) == '.' )
+ nNameValue = 0;
+ else if( rDFA.maName.Len() )
+ {
+ // even if a family name is not the one we are looking for
+ // it is still useful as a font name alternative
+ if( rDFA.maMapNames.Len() )
+ rDFA.maMapNames += ';';
+ rDFA.maMapNames += (nBestNameValue < nNameValue) ? rDFA.maName : aUtf16Name;
+ }
+ if( nBestNameValue < nNameValue )
+ {
+ // get the best family name
+ nBestNameValue = nNameValue;
+ eBestLangCode = eFontNameLanguage;
+ rDFA.maName = aUtf16Name;
+ }
+ break;
+ case kFontStyleName:
+ // get a style name matching to the family name
+ if( nBestStyleValue < nNameValue )
+ {
+ nBestStyleValue = nNameValue;
+ rDFA.maStyleName = aUtf16Name;
+ }
+ break;
+ case kFontPostscriptName:
+ // use the postscript name to get some useful info
+ UpdateAttributesFromPSName( aUtf16Name, rDFA );
+ break;
+ default:
+ // TODO: use other name entries too?
+ break;
+ }
+ }
+
+ bool bRet = (rDFA.maName.Len() > 0);
+ return bRet;
+}
+
+// =======================================================================
+
+SystemFontList::SystemFontList()
+{
+ // count available system fonts
+ ItemCount nATSUICompatibleFontsAvailable = 0;
+ if( ATSUFontCount(&nATSUICompatibleFontsAvailable) != noErr )
+ return;
+ if( nATSUICompatibleFontsAvailable <= 0 )
+ return;
+
+ // enumerate available system fonts
+ typedef std::vector<ATSUFontID> AtsFontIDVector;
+ AtsFontIDVector aFontIDVector( nATSUICompatibleFontsAvailable );
+ ItemCount nFontItemsCount = 0;
+ if( ATSUGetFontIDs( &aFontIDVector[0], aFontIDVector.capacity(), &nFontItemsCount ) != noErr )
+ return;
+
+ BOOST_ASSERT(nATSUICompatibleFontsAvailable == nFontItemsCount && "Strange I would expect them to be equal");
+
+ // prepare use of the available fonts
+ AtsFontIDVector::const_iterator it = aFontIDVector.begin();
+ for(; it != aFontIDVector.end(); ++it )
+ {
+ const ATSUFontID nFontID = *it;
+ ImplDevFontAttributes aDevFontAttr;
+ if( !GetDevFontAttributes( nFontID, aDevFontAttr ) )
+ continue;
+ ImplMacFontData* pFontData = new ImplMacFontData( aDevFontAttr, nFontID );
+ maFontContainer[ nFontID ] = pFontData;
+ }
+
+ InitGlyphFallbacks();
+}
+
+// -----------------------------------------------------------------------
+
+SystemFontList::~SystemFontList()
+{
+ MacFontContainer::const_iterator it = maFontContainer.begin();
+ for(; it != maFontContainer.end(); ++it )
+ delete (*it).second;
+ maFontContainer.clear();
+
+ ATSUDisposeFontFallbacks( maFontFallbacks );
+}
+
+// -----------------------------------------------------------------------
+
+void SystemFontList::AnnounceFonts( ImplDevFontList& rFontList ) const
+{
+ MacFontContainer::const_iterator it = maFontContainer.begin();
+ for(; it != maFontContainer.end(); ++it )
+ rFontList.Add( (*it).second->Clone() );
+}
+
+// -----------------------------------------------------------------------
+
+// not all fonts are suitable for glyph fallback => sort them
+struct GfbCompare{ bool operator()(const ImplMacFontData*, const ImplMacFontData*); };
+
+inline bool GfbCompare::operator()( const ImplMacFontData* pA, const ImplMacFontData* pB )
+{
+ // use symbol fonts only as last resort
+ bool bPreferA = !pA->IsSymbolFont();
+ bool bPreferB = !pB->IsSymbolFont();
+ if( bPreferA != bPreferB )
+ return bPreferA;
+ // prefer scalable fonts
+ bPreferA = pA->IsScalable();
+ bPreferB = pB->IsScalable();
+ if( bPreferA != bPreferB )
+ return bPreferA;
+ // prefer non-slanted fonts
+ bPreferA = (pA->GetSlant() == ITALIC_NONE);
+ bPreferB = (pB->GetSlant() == ITALIC_NONE);
+ if( bPreferA != bPreferB )
+ return bPreferA;
+ // prefer normal weight fonts
+ bPreferA = (pA->GetWeight() == WEIGHT_NORMAL);
+ bPreferB = (pB->GetWeight() == WEIGHT_NORMAL);
+ if( bPreferA != bPreferB )
+ return bPreferA;
+ // prefer normal width fonts
+ bPreferA = (pA->GetWidthType() == WIDTH_NORMAL);
+ bPreferB = (pB->GetWidthType() == WIDTH_NORMAL);
+ if( bPreferA != bPreferB )
+ return bPreferA;
+ return false;
+}
+
+void SystemFontList::InitGlyphFallbacks()
+{
+ // sort fonts for "glyph fallback"
+ typedef std::multiset<const ImplMacFontData*,GfbCompare> FallbackSet;
+ FallbackSet aFallbackSet;
+ MacFontContainer::const_iterator it = maFontContainer.begin();
+ for(; it != maFontContainer.end(); ++it )
+ {
+ const ImplMacFontData* pIFD = (*it).second;
+ // TODO: subsettable/embeddable glyph fallback only for PDF export?
+ if( pIFD->IsSubsettable() || pIFD->IsEmbeddable() )
+ aFallbackSet.insert( pIFD );
+ }
+
+ // tell ATSU about font preferences for "glyph fallback"
+ typedef std::vector<ATSUFontID> AtsFontIDVector;
+ AtsFontIDVector aFallbackVector;
+ aFallbackVector.reserve( maFontContainer.size() );
+ FallbackSet::const_iterator itFData = aFallbackSet.begin();
+ for(; itFData != aFallbackSet.end(); ++itFData )
+ {
+ const ImplMacFontData* pFontData = (*itFData);
+ ATSUFontID nFontID = (ATSUFontID)pFontData->GetFontId();
+ aFallbackVector.push_back( nFontID );
+ }
+
+ ATSUCreateFontFallbacks( &maFontFallbacks );
+ ATSUSetObjFontFallbacks( maFontFallbacks,
+ aFallbackVector.size(), &aFallbackVector[0], kATSUSequentialFallbacksPreferred );
+}
+
+// -----------------------------------------------------------------------
+
+ImplMacFontData* SystemFontList::GetFontDataFromId( ATSUFontID nFontId ) const
+{
+ MacFontContainer::const_iterator it = maFontContainer.find( nFontId );
+ if( it == maFontContainer.end() )
+ return NULL;
+ return (*it).second;
+}
+
+// -----------------------------------------------------------------------
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */