/************************************************************** * * 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 #include #include #include #include #include "unotools/atom.hxx" #include "fontcache.hxx" #include "fontsubset.hxx" #include "impfont.hxx" #include "svdata.hxx" #include "salinst.hxx" #include "vcl/fontmanager.hxx" #include "vcl/strhelper.hxx" #include "vcl/ppdparser.hxx" #include "tools/urlobj.hxx" #include "tools/stream.hxx" #include "tools/debug.hxx" #include "tools/config.hxx" #include "osl/file.hxx" #include "osl/process.h" #include "rtl/tencinfo.h" #include "rtl/ustrbuf.hxx" #include "rtl/strbuf.hxx" #include "i18npool/mslangid.hxx" #include "parseAFM.hxx" #include "sft.hxx" #if OSL_DEBUG_LEVEL > 1 #include #include #endif #include "sal/alloca.h" #include #include #include #include "adobeenc.tab" // get encoding table for AFM metrics #ifdef CALLGRIND_COMPILE #include #endif #include "comphelper/processfactory.hxx" #include "com/sun/star/beans/XMaterialHolder.hpp" #include "com/sun/star/beans/NamedValue.hpp" #define PRINTER_METRICDIR "fontmetric" namespace { namespace css = com::sun::star; } using namespace vcl; using namespace utl; using namespace psp; using namespace osl; using namespace rtl; using namespace com::sun::star::uno; using namespace com::sun::star::beans; using namespace com::sun::star::lang; /* * static helpers */ inline sal_uInt16 getUInt16BE( const sal_uInt8*& pBuffer ) { sal_uInt16 nRet = (sal_uInt16)pBuffer[1] | (((sal_uInt16)pBuffer[0]) << 8); pBuffer+=2; return nRet; } inline sal_uInt32 getUInt32BE( const sal_uInt8*& pBuffer ) { sal_uInt32 nRet = (((sal_uInt32)pBuffer[0]) << 24) | (((sal_uInt32)pBuffer[1]) << 16) | (((sal_uInt32)pBuffer[2]) << 8) | (((sal_uInt32)pBuffer[3]) ); pBuffer += 4; return nRet; } static italic::type parseItalic( const ByteString& rItalic ) { italic::type eItalic = italic::Unknown; if( rItalic.EqualsIgnoreCaseAscii( "i" ) ) eItalic = italic::Italic; else if( rItalic.EqualsIgnoreCaseAscii( "o" ) ) eItalic = italic::Oblique; else eItalic = italic::Upright; return eItalic; } // ------------------------------------------------------------------------- static weight::type parseWeight( const ByteString& rWeight ) { weight::type eWeight = weight::Unknown; if( rWeight.Search( "bold" ) != STRING_NOTFOUND ) { if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi eWeight = weight::SemiBold; else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND ) eWeight = weight::UltraBold; else eWeight = weight::Bold; } else if( rWeight.Search( "heavy" ) != STRING_NOTFOUND ) eWeight = weight::Bold; else if( rWeight.Search( "light" ) != STRING_NOTFOUND ) { if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi eWeight = weight::SemiLight; else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND ) eWeight = weight::UltraLight; else eWeight = weight::Light; } else if( rWeight.Search( "black" ) != STRING_NOTFOUND ) eWeight = weight::Black; else if( rWeight.Equals( "demi" ) ) eWeight = weight::SemiBold; else if( rWeight.Equals( "book" ) || rWeight.Equals( "semicondensed" ) ) eWeight = weight::Light; else if( rWeight.Equals( "medium" ) || rWeight.Equals( "roman" ) ) eWeight = weight::Medium; else eWeight = weight::Normal; return eWeight; } // ------------------------------------------------------------------------- static width::type parseWidth( const ByteString& rWidth ) { width::type eWidth = width::Unknown; if( rWidth.Equals( "bold" ) || rWidth.Equals( "semiexpanded" ) ) eWidth = width::SemiExpanded; else if( rWidth.Equals( "condensed" ) || rWidth.Equals( "narrow" ) ) eWidth = width::Condensed; else if( rWidth.Equals( "double wide" ) || rWidth.Equals( "extraexpanded" ) || rWidth.Equals( "ultraexpanded" ) ) eWidth = width::UltraExpanded; else if( rWidth.Equals( "expanded" ) || rWidth.Equals( "wide" ) ) eWidth = width::Expanded; else if( rWidth.Equals( "extracondensed" ) ) eWidth = width::ExtraCondensed; else if( rWidth.Equals( "semicondensed" ) ) eWidth = width::SemiCondensed; else if( rWidth.Equals( "ultracondensed" ) ) eWidth = width::UltraCondensed; else eWidth = width::Normal; return eWidth; } // ------------------------------------------------------------------------- bool PrintFontManager::XLFDEntry::operator<(const PrintFontManager::XLFDEntry& rRight) const { sal_Int32 nCmp = 0; if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) ) { nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer, aFamily.pData->length, rRight.aFamily.pData->buffer, rRight.aFamily.pData->length ); if( nCmp != 0 ) return nCmp < 0; } if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) ) { nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer, aFoundry.pData->length, rRight.aFoundry.pData->buffer, rRight.aFoundry.pData->length ); if( nCmp != 0 ) return nCmp < 0; } if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) ) { if( eItalic != rRight.eItalic ) return (int)eItalic < (int)rRight.eItalic; } if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) ) { if( eWeight != rRight.eWeight ) return (int)eWeight < (int)rRight.eWeight; } if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) ) { if( eWidth != rRight.eWidth ) return (int)eWidth < (int)rRight.eWidth; } if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) ) { if( ePitch != rRight.ePitch ) return (int)ePitch < (int)rRight.ePitch; } if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) ) { nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer, aAddStyle.pData->length, rRight.aAddStyle.pData->buffer, rRight.aAddStyle.pData->length ); if( nCmp != 0 ) return nCmp < 0; } if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) ) { if( aEncoding != rRight.aEncoding ) return aEncoding < rRight.aEncoding; } return false; } bool PrintFontManager::XLFDEntry::operator==(const PrintFontManager::XLFDEntry& rRight) const { sal_Int32 nCmp = 0; if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) ) { nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer, aFamily.pData->length, rRight.aFamily.pData->buffer, rRight.aFamily.pData->length ); if( nCmp != 0 ) return false; } if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) ) { nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer, aFoundry.pData->length, rRight.aFoundry.pData->buffer, rRight.aFoundry.pData->length ); if( nCmp != 0 ) return false; } if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) ) { if( eItalic != rRight.eItalic ) return false; } if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) ) { if( eWeight != rRight.eWeight ) return false; } if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) ) { if( eWidth != rRight.eWidth ) return false; } if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) ) { if( ePitch != rRight.ePitch ) return false; } if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) ) { nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer, aAddStyle.pData->length, rRight.aAddStyle.pData->buffer, rRight.aAddStyle.pData->length ); if( nCmp != 0 ) return false; } if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) ) { if( aEncoding != rRight.aEncoding ) return false; } return true; } /* * PrintFont implementations */ PrintFontManager::PrintFont::PrintFont( fonttype::type eType ) : m_eType( eType ), m_nFamilyName( 0 ), m_nPSName( 0 ), m_eItalic( italic::Unknown ), m_eWidth( width::Unknown ), m_eWeight( weight::Unknown ), m_ePitch( pitch::Unknown ), m_aEncoding( RTL_TEXTENCODING_DONTKNOW ), m_bFontEncodingOnly( false ), m_pMetrics( NULL ), m_nAscend( 0 ), m_nDescend( 0 ), m_nLeading( 0 ), m_nXMin( 0 ), m_nYMin( 0 ), m_nXMax( 0 ), m_nYMax( 0 ), m_bHaveVerticalSubstitutedGlyphs( false ), m_bUserOverride( false ) { } // ------------------------------------------------------------------------- PrintFontManager::PrintFont::~PrintFont() { if( m_pMetrics ) delete m_pMetrics; } // ------------------------------------------------------------------------- PrintFontManager::Type1FontFile::~Type1FontFile() { } // ------------------------------------------------------------------------- PrintFontManager::TrueTypeFontFile::TrueTypeFontFile() : PrintFont( fonttype::TrueType ) , m_nDirectory( 0 ) , m_nCollectionEntry(-1) , m_nTypeFlags( TYPEFLAG_INVALID ) {} // ------------------------------------------------------------------------- PrintFontManager::TrueTypeFontFile::~TrueTypeFontFile() { } // ------------------------------------------------------------------------- PrintFontManager::BuiltinFont::~BuiltinFont() { } // ------------------------------------------------------------------------- bool PrintFontManager::Type1FontFile::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider ) { return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false ); } // ------------------------------------------------------------------------- bool PrintFontManager::BuiltinFont::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider ) { return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false ); } // ------------------------------------------------------------------------- bool PrintFontManager::TrueTypeFontFile::queryMetricPage( int nPage, MultiAtomProvider* pProvider ) { bool bSuccess = false; ByteString aFile( PrintFontManager::get().getFontFile( this ) ); TrueTypeFont* pTTFont = NULL; if( OpenTTFontFile( aFile.GetBuffer(), m_nCollectionEntry < 0 ? 0 : m_nCollectionEntry, &pTTFont ) == SF_OK ) { if( ! m_pMetrics ) { m_pMetrics = new PrintFontMetrics; memset (m_pMetrics->m_aPages, 0, sizeof(m_pMetrics->m_aPages)); } m_pMetrics->m_aPages[ nPage/8 ] |= (1 << ( nPage & 7 )); int i; sal_uInt16 table[256], table_vert[256]; for( i = 0; i < 256; i++ ) table[ i ] = 256*nPage + i; int nCharacters = nPage < 255 ? 256 : 254; MapString( pTTFont, table, nCharacters, NULL, 0 ); TTSimpleGlyphMetrics* pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 0 ); if( pMetrics ) { for( i = 0; i < nCharacters; i++ ) { if( table[i] ) { CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i ]; rChar.width = pMetrics[ i ].adv; rChar.height = m_aGlobalMetricX.height; } } free( pMetrics ); } for( i = 0; i < 256; i++ ) table_vert[ i ] = 256*nPage + i; MapString( pTTFont, table_vert, nCharacters, NULL, 1 ); pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 1 ); if( pMetrics ) { for( i = 0; i < nCharacters; i++ ) { if( table_vert[i] ) { CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i + ( 1 << 16 ) ]; rChar.width = m_aGlobalMetricY.width; rChar.height = pMetrics[ i ].adv; if( table_vert[i] != table[i] ) m_pMetrics->m_bVerticalSubstitutions[ nPage*256 + i ] = 1; } } free( pMetrics ); } if( ! m_pMetrics->m_bKernPairsQueried ) { m_pMetrics->m_bKernPairsQueried = true; // this is really a hack // in future MapString/KernGlyphs should be used // but vcl is not in a state where that could be used // so currently we get kernpairs by accessing the raw data struct _TrueTypeFont* pImplTTFont = (struct _TrueTypeFont*)pTTFont; //----------------------------------------------------------------- // Kerning: KT_MICROSOFT //----------------------------------------------------------------- if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_MICROSOFT ) { // create a glyph -> character mapping ::std::hash_map< sal_uInt16, sal_Unicode > aGlyphMap; ::std::hash_map< sal_uInt16, sal_Unicode >::iterator left, right; for( i = 21; i < 0xfffd; i++ ) { sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only if( nGlyph != 0 ) aGlyphMap[ nGlyph ] = (sal_Unicode)i; } KernPair aPair; for( i = 0; i < (int)pImplTTFont->nkern; i++ ) { const sal_uInt8* pTable = pImplTTFont->kerntables[i]; /*sal_uInt16 nVersion =*/ getUInt16BE( pTable ); /*sal_uInt16 nLength =*/ getUInt16BE( pTable ); sal_uInt16 nCoverage = getUInt16BE( pTable ); aPair.kern_x = 0; aPair.kern_y = 0; switch( nCoverage >> 8 ) { case 0: { sal_uInt16 nPairs = getUInt16BE( pTable ); pTable += 6; for( int n = 0; n < nPairs; n++ ) { sal_uInt16 nLeftGlyph = getUInt16BE( pTable ); sal_uInt16 nRightGlyph = getUInt16BE( pTable ); sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable ); left = aGlyphMap.find( nLeftGlyph ); right = aGlyphMap.find( nRightGlyph ); if( left != aGlyphMap.end() && right != aGlyphMap.end() ) { aPair.first = left->second; aPair.second = right->second; switch( nCoverage & 1 ) { case 1: aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aXKernPairs.push_back( aPair ); break; case 0: aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aYKernPairs.push_back( aPair ); break; } } } } break; case 2: { const sal_uInt8* pSubTable = pTable; /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable ); sal_uInt16 nOfLeft = getUInt16BE( pTable ); sal_uInt16 nOfRight = getUInt16BE( pTable ); /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable ); const sal_uInt8* pTmp = pSubTable + nOfLeft; sal_uInt16 nFirstLeft = getUInt16BE( pTmp ); sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1; pTmp = pSubTable + nOfRight; sal_uInt16 nFirstRight = getUInt16BE( pTmp ); sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1; // int nPairs = (int)(nLastLeft-nFirstLeft+1)*(int)(nLastRight-nFirstRight+1); for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ ) { for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ ) { sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp ); switch( nCoverage & 1 ) { case 1: aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aXKernPairs.push_back( aPair ); break; case 0: aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aYKernPairs.push_back( aPair ); break; } } } } break; } } } //----------------------------------------------------------------- // Kerning: KT_APPLE_NEW //----------------------------------------------------------------- if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_APPLE_NEW ) { // create a glyph -> character mapping ::std::hash_map< sal_uInt16, sal_Unicode > aGlyphMap; ::std::hash_map< sal_uInt16, sal_Unicode >::iterator left, right; for( i = 21; i < 0xfffd; i++ ) { sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only if( nGlyph != 0 ) aGlyphMap[ nGlyph ] = (sal_Unicode)i; } // Loop through each of the 'kern' subtables KernPair aPair; for( i = 0; (unsigned int)i < pImplTTFont->nkern; i++ ) { const sal_uInt8* pTable = pImplTTFont->kerntables[i]; /*sal_uInt32 nLength =*/ getUInt32BE( pTable ); sal_uInt16 nCoverage = getUInt16BE( pTable ); /*sal_uInt16 nTupleIndex =*/ getUInt16BE( pTable ); // Get kerning type // sal_Bool bKernVertical = nCoverage & 0x8000; // sal_Bool bKernCrossStream = nCoverage & 0x4000; // sal_Bool bKernVariation = nCoverage & 0x2000; // Kerning sub-table format, 0 through 3 sal_uInt8 nSubTableFormat = nCoverage & 0x00FF; aPair.kern_x = 0; aPair.kern_y = 0; switch( nSubTableFormat ) { case 0: { // Grab the # of kern pairs but skip over the: // searchRange // entrySelector // rangeShift sal_uInt16 nPairs = getUInt16BE( pTable ); pTable += 6; for( int n = 0; n < nPairs; n++ ) { sal_uInt16 nLeftGlyph = getUInt16BE( pTable ); sal_uInt16 nRightGlyph = getUInt16BE( pTable ); sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable ); left = aGlyphMap.find( nLeftGlyph ); right = aGlyphMap.find( nRightGlyph ); if( left != aGlyphMap.end() && right != aGlyphMap.end() ) { aPair.first = left->second; aPair.second = right->second; // Only support horizontal kerning for now aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; aPair.kern_y = 0; m_pMetrics->m_aXKernPairs.push_back( aPair ); /* switch( nCoverage & 1 ) { case 1: aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aXKernPairs.push_back( aPair ); break; case 0: aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aYKernPairs.push_back( aPair ); break; } */ } } } break; case 2: { const sal_uInt8* pSubTable = pTable; /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable ); sal_uInt16 nOfLeft = getUInt16BE( pTable ); sal_uInt16 nOfRight = getUInt16BE( pTable ); /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable ); const sal_uInt8* pTmp = pSubTable + nOfLeft; sal_uInt16 nFirstLeft = getUInt16BE( pTmp ); sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1; pTmp = pSubTable + nOfRight; sal_uInt16 nFirstRight = getUInt16BE( pTmp ); sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1; for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ ) { for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ ) { sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp ); switch( nCoverage & 1 ) { case 1: aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aXKernPairs.push_back( aPair ); break; case 0: aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; m_pMetrics->m_aYKernPairs.push_back( aPair ); break; } } } } break; default: fprintf( stderr, "Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat ); break; } } } #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "found %d/%d kern pairs for %s\n", m_pMetrics->m_aXKernPairs.size(), m_pMetrics->m_aYKernPairs.size(), OUStringToOString( pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ), RTL_TEXTENCODING_MS_1252 ).getStr() ); #else (void) pProvider; /* avoid warnings */ #endif } CloseTTFont( pTTFont ); bSuccess = true; } return bSuccess; } // ------------------------------------------------------------------------- /* #i73387# There seem to be fonts with a rather unwell chosen family name * consider e.g. "Helvetica Narrow" which defines its family as "Helvetica" * It can really only be distinguished by its PSName and FullName. Both of * which are not user presentable in OOo. So replace it by something sensible. * * If other fonts feature this behaviour, insert them to the map. */ static bool familyNameOverride( const OUString& i_rPSname, OUString& o_rFamilyName ) { static std::hash_map< OUString, OUString, OUStringHash > aPSNameToFamily( 16 ); if( aPSNameToFamily.empty() ) // initialization { aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow" ) ) ] = OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Bold" ) ) ] = OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-BoldOblique" ) ) ] = OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Oblique" ) ) ] = OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); } std::hash_map::const_iterator it = aPSNameToFamily.find( i_rPSname ); bool bReplaced = (it != aPSNameToFamily.end() ); if( bReplaced ) o_rFamilyName = it->second; return bReplaced; }; bool PrintFontManager::PrintFont::readAfmMetrics( const OString& rFileName, MultiAtomProvider* pProvider, bool bFillEncodingvector, bool bOnlyGlobalAttributes ) { PrintFontManager& rManager( PrintFontManager::get() ); int i; FontInfo* pInfo = NULL; parseFile( rFileName.getStr(), &pInfo, P_ALL ); if( ! pInfo || ! pInfo->numOfChars ) { if( pInfo ) freeFontInfo( pInfo ); return false; } m_aEncodingVector.clear(); // fill in global info // PSName OUString aPSName( OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ) ); m_nPSName = pProvider->getAtom( ATOM_PSNAME, aPSName, sal_True ); // family name (if not already set) OUString aFamily; if( ! m_nFamilyName ) { aFamily = OStringToOUString( pInfo->gfi->familyName, RTL_TEXTENCODING_ISO_8859_1 ); if( ! aFamily.getLength() ) { aFamily = OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ); sal_Int32 nIndex = 0; aFamily = aFamily.getToken( 0, '-', nIndex ); } familyNameOverride( aPSName, aFamily ); m_nFamilyName = pProvider->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ); } else aFamily = pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ); // style name: if fullname begins with family name // interpret the rest of fullname as style if( ! m_aStyleName.getLength() && pInfo->gfi->fullName && *pInfo->gfi->fullName ) { OUString aFullName( OStringToOUString( pInfo->gfi->fullName, RTL_TEXTENCODING_ISO_8859_1 ) ); if( aFullName.indexOf( aFamily ) == 0 ) m_aStyleName = WhitespaceToSpace( aFullName.copy( aFamily.getLength() ) ); } // italic if( pInfo->gfi->italicAngle > 0 ) m_eItalic = italic::Oblique; else if( pInfo->gfi->italicAngle < 0 ) m_eItalic = italic::Italic; else m_eItalic = italic::Upright; // weight ByteString aLowerWeight( pInfo->gfi->weight ); aLowerWeight.ToLowerAscii(); m_eWeight = parseWeight( aLowerWeight ); // pitch m_ePitch = pInfo->gfi->isFixedPitch ? pitch::Fixed : pitch::Variable; // encoding - only set if unknown int nAdobeEncoding = 0; if( pInfo->gfi->encodingScheme ) { if( !strcmp( pInfo->gfi->encodingScheme, "AdobeStandardEncoding" ) ) nAdobeEncoding = 1; else if( !strcmp( pInfo->gfi->encodingScheme, "ISO10646-1" ) ) { nAdobeEncoding = 1; m_aEncoding = RTL_TEXTENCODING_UNICODE; } else if( !strcmp( pInfo->gfi->encodingScheme, "Symbol") ) nAdobeEncoding = 2; else if( !strcmp( pInfo->gfi->encodingScheme, "FontSpecific") ) nAdobeEncoding = 3; if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) m_aEncoding = nAdobeEncoding == 1 ? RTL_TEXTENCODING_ADOBE_STANDARD : RTL_TEXTENCODING_SYMBOL; } else if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) m_aEncoding = RTL_TEXTENCODING_ADOBE_STANDARD; // try to parse the font name and decide whether it might be a // japanese font. Who invented this PITA ? OUString aPSNameLastToken( aPSName.copy( aPSName.lastIndexOf( '-' )+1 ) ); if( ! aPSNameLastToken.compareToAscii( "H" ) || ! aPSNameLastToken.compareToAscii( "V" ) ) { static const char* pEncs[] = { "EUC", "RKSJ", "SJ" }; static const rtl_TextEncoding aEncs[] = { RTL_TEXTENCODING_EUC_JP, RTL_TEXTENCODING_SHIFT_JIS, RTL_TEXTENCODING_JIS_X_0208 }; for( unsigned int enc = 0; enc < sizeof( aEncs )/sizeof(aEncs[0]) && m_aEncoding == RTL_TEXTENCODING_DONTKNOW; enc++ ) { sal_Int32 nIndex = 0, nOffset = 1; do { OUString aToken( aPSName.getToken( nOffset, '-', nIndex ) ); if( nIndex == -1 ) break; nOffset = 0; if( ! aToken.compareToAscii( pEncs[enc] ) ) { m_aEncoding = aEncs[ enc ]; m_bFontEncodingOnly = true; } } while( nIndex != -1 ); } // default is jis if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) m_aEncoding = RTL_TEXTENCODING_JIS_X_0208; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "Encoding %d for %s\n", m_aEncoding, pInfo->gfi->fontName ); #endif } // hack for GB encoded builtin fonts posing as FontSpecific if( m_eType == fonttype::Builtin && ( nAdobeEncoding == 3 || nAdobeEncoding == 0 ) ) { int nLen = aFamily.getLength(); if( nLen > 2 && aFamily.getStr()[ nLen-2 ] == 'G' && aFamily.getStr()[ nLen-1 ] == 'B' && pInfo->numOfChars > 255 ) { m_aEncoding = RTL_TEXTENCODING_GBK; m_bFontEncodingOnly = true; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "found builtin font %s with GBK encoding\n", pInfo->gfi->fontName ); #endif } } // #i37313# check if Fontspecific is not rather some character encoding if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL ) { bool bYFound = false; bool bQFound = false; CharMetricInfo* pChar = pInfo->cmi; for( int j = 0; j < pInfo->numOfChars && ! (bYFound && bQFound); j++ ) { if( pChar[j].name ) { if( pChar[j].name[0] == 'Y' && pChar[j].name[1] == 0 ) bYFound = true; else if( pChar[j].name[0] == 'Q' && pChar[j].name[1] == 0 ) bQFound = true; } } if( bQFound && bYFound ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "setting FontSpecific font %s (file %s) to unicode\n", pInfo->gfi->fontName, rFileName.getStr() ); #endif nAdobeEncoding = 4; m_aEncoding = RTL_TEXTENCODING_UNICODE; bFillEncodingvector = false; // will be filled anyway, don't do the work twice } } // ascend m_nAscend = pInfo->gfi->fontBBox.ury; // descend // descends have opposite sign of our definition m_nDescend = -pInfo->gfi->fontBBox.lly; // fallback to ascender, descender // interesting: the BBox seems to describe Ascender and Descender better // as we understand it if( m_nAscend == 0 ) m_nAscend = pInfo->gfi->ascender; if( m_nDescend == 0) m_nDescend = -pInfo->gfi->descender; m_nLeading = m_nAscend + m_nDescend - 1000; if( m_pMetrics ) delete m_pMetrics; m_pMetrics = new PrintFontMetrics; // mark all pages as queried (or clear if only global font info queiried) memset( m_pMetrics->m_aPages, bOnlyGlobalAttributes ? 0 : 0xff, sizeof( m_pMetrics->m_aPages ) ); m_aGlobalMetricX.width = m_aGlobalMetricY.width = pInfo->gfi->charwidth ? pInfo->gfi->charwidth : pInfo->gfi->fontBBox.urx; m_aGlobalMetricX.height = m_aGlobalMetricY.height = pInfo->gfi->capHeight ? pInfo->gfi->capHeight : pInfo->gfi->fontBBox.ury; m_nXMin = pInfo->gfi->fontBBox.llx; m_nYMin = pInfo->gfi->fontBBox.lly; m_nXMax = pInfo->gfi->fontBBox.urx; m_nYMax = pInfo->gfi->fontBBox.ury; if( bFillEncodingvector || !bOnlyGlobalAttributes ) { // fill in character metrics // first transform the character codes to unicode // note: this only works with single byte encodings sal_Unicode* pUnicodes = (sal_Unicode*)alloca( pInfo->numOfChars * sizeof(sal_Unicode)); CharMetricInfo* pChar = pInfo->cmi; for( i = 0; i < pInfo->numOfChars; i++, pChar++ ) { if( nAdobeEncoding == 4 ) { if( pChar->name ) { pUnicodes[i] = 0; std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) { if( *it != 0 ) { m_aEncodingVector[ *it ] = pChar->code; if( pChar->code == -1 ) m_aNonEncoded[ *it ] = pChar->name; if( ! pUnicodes[i] ) // map the first pUnicodes[i] = *it; } } } } else if( pChar->code != -1 ) { if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL ) { pUnicodes[i] = pChar->code + 0xf000; if( bFillEncodingvector ) m_aEncodingVector[ pUnicodes[i] ] = pChar->code; continue; } if( m_aEncoding == RTL_TEXTENCODING_UNICODE ) { pUnicodes[i] = (sal_Unicode)pChar->code; continue; } ByteString aTranslate; if( pChar->code & 0xff000000 ) aTranslate += (char)(pChar->code >> 24 ); if( pChar->code & 0xffff0000 ) aTranslate += (char)((pChar->code & 0x00ff0000) >> 16 ); if( pChar->code & 0xffffff00 ) aTranslate += (char)((pChar->code & 0x0000ff00) >> 8 ); aTranslate += (char)(pChar->code & 0xff); String aUni( aTranslate, m_aEncoding ); pUnicodes[i] = *aUni.GetBuffer(); } else pUnicodes[i] = 0; } // now fill in the character metrics // parseAFM.cxx effectively only supports direction 0 (horizontal) pChar = pInfo->cmi; CharacterMetric aMetric; for( i = 0; i < pInfo->numOfChars; i++, pChar++ ) { if( pChar->code == -1 && ! pChar->name ) continue; if( bFillEncodingvector && pChar->name ) { std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) { if( *it != 0 ) { m_aEncodingVector[ *it ] = pChar->code; if( pChar->code == -1 ) m_aNonEncoded[ *it ] = pChar->name; } } } aMetric.width = pChar->wx ? pChar->wx : pChar->charBBox.urx; aMetric.height = pChar->wy ? pChar->wy : pChar->charBBox.ury - pChar->charBBox.lly; if( aMetric.width == 0 && aMetric.height == 0 ) // guess something for e.g. space aMetric.width = m_aGlobalMetricX.width/4; if( ( nAdobeEncoding == 0 ) || ( ( nAdobeEncoding == 3 ) && ( m_aEncoding != RTL_TEXTENCODING_SYMBOL ) ) ) { if( pChar->code != -1 ) { m_pMetrics->m_aMetrics[ pUnicodes[i] ] = aMetric; if( bFillEncodingvector ) m_aEncodingVector[ pUnicodes[i] ] = pChar->code; } else if( pChar->name ) { std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) { if( *it != 0 ) m_pMetrics->m_aMetrics[ *it ] = aMetric; } } } else if( nAdobeEncoding == 1 || nAdobeEncoding == 2 || nAdobeEncoding == 4) { if( pChar->name ) { std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) { if( *it != 0 ) m_pMetrics->m_aMetrics[ *it ] = aMetric; } } else if( pChar->code != -1 ) { ::std::pair< ::std::hash_multimap< sal_uInt8, sal_Unicode >::const_iterator, ::std::hash_multimap< sal_uInt8, sal_Unicode >::const_iterator > aCodes = rManager.getUnicodeFromAdobeCode( pChar->code ); while( aCodes.first != aCodes.second ) { if( (*aCodes.first).second != 0 ) { m_pMetrics->m_aMetrics[ (*aCodes.first).second ] = aMetric; if( bFillEncodingvector ) m_aEncodingVector[ (*aCodes.first).second ] = pChar->code; } ++aCodes.first; } } } else if( nAdobeEncoding == 3 ) { if( pChar->code != -1 ) { sal_Unicode code = 0xf000 + pChar->code; m_pMetrics->m_aMetrics[ code ] = aMetric; // maybe should try to find the name in the convtabs ? if( bFillEncodingvector ) m_aEncodingVector[ code ] = pChar->code; } } } m_pMetrics->m_aXKernPairs.clear(); m_pMetrics->m_aYKernPairs.clear(); // now fill in the kern pairs // parseAFM.cxx effectively only supports direction 0 (horizontal) PairKernData* pKern = pInfo->pkd; KernPair aPair; for( i = 0; i < pInfo->numOfPairs; i++, pKern++ ) { // #i37703# broken kern table if( ! pKern->name1 || ! pKern->name2 ) continue; aPair.first = 0; aPair.second = 0; // currently we have to find the adobe character names // in the already parsed character metrics to find // the corresponding UCS2 code which is a bit dangerous // since the character names are not required // in the metric descriptions pChar = pInfo->cmi; for( int j = 0; j < pInfo->numOfChars && ( aPair.first == 0 || aPair.second == 0 ); j++, pChar++ ) { if( pChar->code != -1 ) { if( ! strcmp( pKern->name1, pChar->name ? pChar->name : "" ) ) aPair.first = pUnicodes[ j ]; if( ! strcmp( pKern->name2, pChar->name ? pChar->name : "" ) ) aPair.second = pUnicodes[ j ]; } } if( aPair.first && aPair.second ) { aPair.kern_x = pKern->xamt; aPair.kern_y = pKern->yamt; m_pMetrics->m_aXKernPairs.push_back( aPair ); } } m_pMetrics->m_bKernPairsQueried = true; } freeFontInfo( pInfo ); return true; } // ------------------------------------------------------------------------- OString PrintFontManager::s_aEmptyOString; /* * one instance only */ PrintFontManager& PrintFontManager::get() { static PrintFontManager* theManager = NULL; if( ! theManager ) { theManager = new PrintFontManager(); theManager->initialize(); } return *theManager; } // ------------------------------------------------------------------------- /* * the PrintFontManager */ PrintFontManager::PrintFontManager() : m_nNextFontID( 1 ), m_pAtoms( new MultiAtomProvider() ), m_nNextDirAtom( 1 ), m_pFontCache( NULL ), m_bFontconfigSuccess( false ) { for( unsigned int i = 0; i < sizeof( aAdobeCodes )/sizeof( aAdobeCodes[0] ); i++ ) { m_aUnicodeToAdobename.insert( ::std::hash_multimap< sal_Unicode, ::rtl::OString >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].pAdobename ) ); m_aAdobenameToUnicode.insert( ::std::hash_multimap< ::rtl::OString, sal_Unicode, ::rtl::OStringHash >::value_type( aAdobeCodes[i].pAdobename, aAdobeCodes[i].aUnicode ) ); if( aAdobeCodes[i].aAdobeStandardCode ) { m_aUnicodeToAdobecode.insert( ::std::hash_multimap< sal_Unicode, sal_uInt8 >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].aAdobeStandardCode ) ); m_aAdobecodeToUnicode.insert( ::std::hash_multimap< sal_uInt8, sal_Unicode >::value_type( aAdobeCodes[i].aAdobeStandardCode, aAdobeCodes[i].aUnicode ) ); } #if 0 m_aUnicodeToAdobename[ aAdobeCodes[i].aUnicode ] = aAdobeCodes[i].pAdobename; m_aAdobenameToUnicode[ aAdobeCodes[i].pAdobename ] = aAdobeCodes[i].aUnicode; if( aAdobeCodes[i].aAdobeStandardCode ) { m_aUnicodeToAdobecode[ aAdobeCodes[i].aUnicode ] = aAdobeCodes[i].aAdobeStandardCode; m_aAdobecodeToUnicode[ aAdobeCodes[i].aAdobeStandardCode ] = aAdobeCodes[i].aUnicode; } #endif } } // ------------------------------------------------------------------------- PrintFontManager::~PrintFontManager() { deinitFontconfig(); for( ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) delete (*it).second; delete m_pAtoms; if( m_pFontCache ) delete m_pFontCache; } // ------------------------------------------------------------------------- const OString& PrintFontManager::getDirectory( int nAtom ) const { ::std::hash_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) ); return it != m_aAtomToDir.end() ? it->second : s_aEmptyOString; } // ------------------------------------------------------------------------- int PrintFontManager::getDirectoryAtom( const OString& rDirectory, bool bCreate ) { int nAtom = 0; ::std::hash_map< OString, int, OStringHash >::const_iterator it ( m_aDirToAtom.find( rDirectory ) ); if( it != m_aDirToAtom.end() ) nAtom = it->second; else if( bCreate ) { nAtom = m_nNextDirAtom++; m_aDirToAtom[ rDirectory ] = nAtom; m_aAtomToDir[ nAtom ] = rDirectory; } return nAtom; } // ------------------------------------------------------------------------- int PrintFontManager::addFontFile( const ::rtl::OString& rFileName, int /*nFaceNum*/ ) { rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); INetURLObject aPath( OStringToOUString( rFileName, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); OString aName( OUStringToOString( aPath.GetName(), aEncoding ) ); OString aDir( OUStringToOString( aPath.GetPath(), aEncoding ) ); int nDirID = getDirectoryAtom( aDir, true ); fontID nFontId = findFontFileID( nDirID, aName ); if( !nFontId ) { ::std::list< PrintFont* > aNewFonts; if( analyzeFontFile( nDirID, aName, ::std::list(), aNewFonts ) ) { for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); it != aNewFonts.end(); ++it ) { m_aFonts[ nFontId = m_nNextFontID++ ] = *it; m_aFontFileToFontID[ aName ].insert( nFontId ); m_pFontCache->updateFontCacheEntry( *it, true ); } } } return nFontId; } // ------------------------------------------------------------------------- bool PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile, const ::std::list& rXLFDs, ::std::list< PrintFontManager::PrintFont* >& rNewFonts ) const { rNewFonts.clear(); OString aDir( getDirectory( nDirID ) ); OString aFullPath( aDir ); aFullPath += "/"; aFullPath += rFontFile; // #i1872# reject unreadable files if( access( aFullPath.getStr(), R_OK ) ) return false; ByteString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) ); if( aExt.EqualsIgnoreCaseAscii( "pfb" ) || aExt.EqualsIgnoreCaseAscii( "pfa" ) ) { // check for corresponding afm metric // first look for an adjacent file static const char* pSuffix[] = { ".afm", ".AFM" }; for( unsigned int i = 0; i < sizeof(pSuffix)/sizeof(pSuffix[0]); i++ ) { ByteString aName( rFontFile ); aName.Erase( aName.Len()-4 ); aName.Append( pSuffix[i] ); ByteString aFilePath( aDir ); aFilePath.Append( '/' ); aFilePath.Append( aName ); ByteString aAfmFile; if( access( aFilePath.GetBuffer(), R_OK ) ) { // try in subdirectory afm instead aFilePath = aDir; aFilePath.Append( "/afm/" ); aFilePath.Append( aName ); if( ! access( aFilePath.GetBuffer(), R_OK ) ) { aAfmFile = "afm/"; aAfmFile += aName; } } else aAfmFile = aName; if( aAfmFile.Len() ) { Type1FontFile* pFont = new Type1FontFile(); pFont->m_nDirectory = nDirID; pFont->m_aFontFile = rFontFile; pFont->m_aMetricFile = aAfmFile; if( ! pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ) ) { delete pFont; pFont = NULL; } if( pFont && rXLFDs.size() ) getFontAttributesFromXLFD( pFont, rXLFDs ); if( pFont ) rNewFonts.push_back( pFont ); break; } } } else if( aExt.EqualsIgnoreCaseAscii( "afm" ) ) { ByteString aFilePath( aDir ); aFilePath.Append( '/' ); aFilePath.Append( ByteString( rFontFile ) ); BuiltinFont* pFont = new BuiltinFont(); pFont->m_nDirectory = nDirID; pFont->m_aMetricFile = rFontFile; if( pFont->readAfmMetrics( aFilePath, m_pAtoms, false, true ) ) rNewFonts.push_back( pFont ); else delete pFont; } else if( aExt.EqualsIgnoreCaseAscii( "ttf" ) || aExt.EqualsIgnoreCaseAscii( "tte" ) // #i33947# for Gaiji support || aExt.EqualsIgnoreCaseAscii( "otf" ) ) // check for TTF- and PS-OpenType too { TrueTypeFontFile* pFont = new TrueTypeFontFile(); pFont->m_nDirectory = nDirID; pFont->m_aFontFile = rFontFile; pFont->m_nCollectionEntry = -1; if( rXLFDs.size() ) getFontAttributesFromXLFD( pFont, rXLFDs ); // need to read the font anyway to get aliases inside the font file if( ! analyzeTrueTypeFile( pFont ) ) { delete pFont; pFont = NULL; } else rNewFonts.push_back( pFont ); } else if( aExt.EqualsIgnoreCaseAscii( "ttc" ) ) { // get number of ttc entries int nLength = CountTTCFonts( aFullPath.getStr() ); if( nLength ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "%s contains %d fonts\n", aFullPath.getStr(), nLength ); #endif for( int i = 0; i < nLength; i++ ) { TrueTypeFontFile* pFont = new TrueTypeFontFile(); pFont->m_nDirectory = nDirID; pFont->m_aFontFile = rFontFile; pFont->m_nCollectionEntry = i; if( nLength == 1 ) getFontAttributesFromXLFD( pFont, rXLFDs ); if( ! analyzeTrueTypeFile( pFont ) ) { delete pFont; pFont = NULL; } else rNewFonts.push_back( pFont ); } } #if OSL_DEBUG_LEVEL > 1 else fprintf( stderr, "CountTTCFonts( \"%s/%s\" ) failed\n", getDirectory(nDirID).getStr(), rFontFile.getStr() ); #endif } return ! rNewFonts.empty(); } // ------------------------------------------------------------------------- fontID PrintFontManager::findFontBuiltinID( int nPSNameAtom ) const { fontID nID = 0; ::std::hash_map< fontID, PrintFont* >::const_iterator it; for( it = m_aFonts.begin(); nID == 0 && it != m_aFonts.end(); ++it ) { if( it->second->m_eType == fonttype::Builtin && it->second->m_nPSName == nPSNameAtom ) nID = it->first; } return nID; } // ------------------------------------------------------------------------- fontID PrintFontManager::findFontFileID( int nDirID, const OString& rFontFile ) const { fontID nID = 0; ::std::hash_map< OString, ::std::set< fontID >, OStringHash >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile ); if( set_it != m_aFontFileToFontID.end() ) { for( ::std::set< fontID >::const_iterator font_it = set_it->second.begin(); font_it != set_it->second.end() && ! nID; ++font_it ) { ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.find( *font_it ); if( it != m_aFonts.end() ) { switch( it->second->m_eType ) { case fonttype::Type1: { Type1FontFile* const pFont = static_cast< Type1FontFile* const >((*it).second); if( pFont->m_nDirectory == nDirID && pFont->m_aFontFile == rFontFile ) nID = it->first; } break; case fonttype::TrueType: { TrueTypeFontFile* const pFont = static_cast< TrueTypeFontFile* const >((*it).second); if( pFont->m_nDirectory == nDirID && pFont->m_aFontFile == rFontFile ) nID = it->first; } break; case fonttype::Builtin: if( static_cast((*it).second)->m_nDirectory == nDirID && static_cast((*it).second)->m_aMetricFile == rFontFile ) nID = it->first; break; default: break; } } } } return nID; } // ------------------------------------------------------------------------- bool PrintFontManager::parseXLFD( const OString& rXLFD, XLFDEntry& rEntry ) { sal_Int32 nIndex = 0; OString aFoundry = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ) ); if( nIndex < 0 ) return false; OString aFamilyXLFD = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ) ); if( nIndex < 0 ) return false; OString aWeight = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); if( nIndex < 0 ) return false; OString aSlant = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); if( nIndex < 0 ) return false; OString aWidth = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); if( nIndex < 0 ) return false; OString aAddStyle = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); if( nIndex < 0 ) return false; OString aPitch = rXLFD.getToken( 4, '-', nIndex ).toAsciiLowerCase(); if( nIndex < 0 ) return false; OString aRegEnc = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ).toAsciiLowerCase() ); if( nIndex < 0 ) return false; OString aEnc = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase() ); // capitalize words sal_Int32 nFamIndex = 0; OStringBuffer aFamilyName; while( nFamIndex >= 0 ) { OString aToken = aFamilyXLFD.getToken( 0, ' ', nFamIndex ); sal_Char aFirst = aToken.toChar(); if( aFirst >= 'a' && aFirst <= 'z' ) aFirst = aFirst - 'a' + 'A'; OStringBuffer aNewToken( aToken.getLength() ); aNewToken.append( aToken ); aNewToken.setCharAt( 0, aFirst ); if( aFamilyName.getLength() > 0 ) aFamilyName.append( ' ' ); aFamilyName.append( aNewToken.makeStringAndClear() ); } rEntry.aFoundry = aFoundry; rEntry.aFamily = aFamilyName.makeStringAndClear(); rEntry.aAddStyle = aAddStyle; // evaluate weight rEntry.eWeight = parseWeight( aWeight ); // evaluate slant rEntry.eItalic = parseItalic( aSlant ); // evaluate width rEntry.eWidth = parseWidth( aWidth ); // evaluate pitch if( aPitch.toChar() == 'c' || aPitch.toChar() == 'm' ) rEntry.ePitch = pitch::Fixed; else rEntry.ePitch = pitch::Variable; OString aToken = aEnc.toAsciiLowerCase(); // get encoding if( aAddStyle.indexOf( "symbol" ) != -1 ) rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL; else { if( aToken.equals( "symbol" ) ) rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL; else { OStringBuffer aCharset( aRegEnc.getLength() + aEnc.getLength() + 1 ); aCharset.append( aRegEnc ); aCharset.append( '-' ); aCharset.append( aEnc ); rEntry.aEncoding = rtl_getTextEncodingFromUnixCharset( aCharset.getStr() ); } } // set correct mask flags rEntry.nMask = 0; if( rEntry.aFoundry != "*" ) rEntry.nMask |= XLFDEntry::MaskFoundry; if( rEntry.aFamily != "*" ) rEntry.nMask |= XLFDEntry::MaskFamily; if( rEntry.aAddStyle != "*" ) rEntry.nMask |= XLFDEntry::MaskAddStyle; if( aWeight != "*" ) rEntry.nMask |= XLFDEntry::MaskWeight; if( aSlant != "*" ) rEntry.nMask |= XLFDEntry::MaskItalic; if( aWidth != "*" ) rEntry.nMask |= XLFDEntry::MaskWidth; if( aPitch != "*" ) rEntry.nMask |= XLFDEntry::MaskPitch; if( aRegEnc != "*" && aEnc != "*" ) rEntry.nMask |= XLFDEntry::MaskEncoding; return true; } // ------------------------------------------------------------------------- void PrintFontManager::parseXLFD_appendAliases( const std::list< OString >& rXLFDs, std::list< XLFDEntry >& rEntries ) const { for( std::list< OString >::const_iterator it = rXLFDs.begin(); it != rXLFDs.end(); ++it ) { XLFDEntry aEntry; if( ! parseXLFD(*it, aEntry) ) continue; rEntries.push_back( aEntry ); std::map< XLFDEntry, std::list< XLFDEntry > >::const_iterator alias_it = m_aXLFD_Aliases.find( aEntry ); if( alias_it != m_aXLFD_Aliases.end() ) { rEntries.insert( rEntries.end(), alias_it->second.begin(), alias_it->second.end() ); } } } // ------------------------------------------------------------------------- void PrintFontManager::getFontAttributesFromXLFD( PrintFont* pFont, const std::list< OString >& rXLFDs ) const { bool bFamilyName = false; std::list< XLFDEntry > aXLFDs; parseXLFD_appendAliases( rXLFDs, aXLFDs ); for( std::list< XLFDEntry >::const_iterator it = aXLFDs.begin(); it != aXLFDs.end(); ++it ) { // set family name or alias int nFam = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( it->aFamily, it->aAddStyle.indexOf( "utf8" ) != -1 ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1 ), sal_True ); if( ! bFamilyName ) { bFamilyName = true; pFont->m_nFamilyName = nFam; switch( pFont->m_eType ) { case fonttype::Type1: static_cast(pFont)->m_aXLFD = rXLFDs.front(); break; case fonttype::TrueType: static_cast(pFont)->m_aXLFD = rXLFDs.front(); break; default: break; } } else { // make sure that aliases are unique if( nFam != pFont->m_nFamilyName ) { std::list< int >::const_iterator al_it; for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nFam; ++al_it ) ; if( al_it == pFont->m_aAliases.end() ) pFont->m_aAliases.push_back( nFam ); } // for the rest of the attributes there can only be one value; // we'll trust the first one continue; } // fill in weight pFont->m_eWeight = it->eWeight; // fill in slant pFont->m_eItalic = it->eItalic; // fill in width pFont->m_eWidth = it->eWidth; // fill in pitch pFont->m_ePitch = it->ePitch; // fill in encoding pFont->m_aEncoding = it->aEncoding; } // handle iso8859-1 as ms1252 to fill the "gap" starting at 0x80 if( pFont->m_aEncoding == RTL_TEXTENCODING_ISO_8859_1 ) pFont->m_aEncoding = RTL_TEXTENCODING_MS_1252; if( rXLFDs.begin() != rXLFDs.end() ) { switch( pFont->m_eType ) { case fonttype::Type1: static_cast(pFont)->m_aXLFD = rXLFDs.front(); break; case fonttype::TrueType: static_cast(pFont)->m_aXLFD = rXLFDs.front(); break; default: break; } } } // ------------------------------------------------------------------------- OString PrintFontManager::getXLFD( PrintFont* pFont ) const { if( pFont->m_eType == fonttype::Type1 ) { if( static_cast(pFont)->m_aXLFD.getLength() ) return static_cast(pFont)->m_aXLFD; } if( pFont->m_eType == fonttype::TrueType ) { if( static_cast(pFont)->m_aXLFD.getLength() ) return static_cast(pFont)->m_aXLFD; } OStringBuffer aXLFD( 128 ); aXLFD.append( "-misc-" ); ByteString aFamily( String( m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ) ), RTL_TEXTENCODING_UTF8 ); aFamily.SearchAndReplaceAll( '-',' ' ); aFamily.SearchAndReplaceAll( '?',' ' ); aFamily.SearchAndReplaceAll( '*',' ' ); aXLFD.append( OString( aFamily ) ); aXLFD.append( '-' ); switch( pFont->m_eWeight ) { case weight::Thin: aXLFD.append("thin");break; case weight::UltraLight: aXLFD.append("ultralight");break; case weight::Light: aXLFD.append("light");break; case weight::SemiLight: aXLFD.append("semilight");break; case weight::Normal: aXLFD.append("normal");break; case weight::Medium: aXLFD.append("medium");break; case weight::SemiBold: aXLFD.append("semibold");break; case weight::Bold: aXLFD.append("bold");break; case weight::UltraBold: aXLFD.append("ultrabold");break; case weight::Black: aXLFD.append("black");break; default: break; } aXLFD.append('-'); switch( pFont->m_eItalic ) { case italic::Upright: aXLFD.append('r');break; case italic::Oblique: aXLFD.append('o');break; case italic::Italic: aXLFD.append('i');break; default: break; } aXLFD.append('-'); switch( pFont->m_eWidth ) { case width::UltraCondensed: aXLFD.append("ultracondensed");break; case width::ExtraCondensed: aXLFD.append("extracondensed");break; case width::Condensed: aXLFD.append("condensed");break; case width::SemiCondensed: aXLFD.append("semicondensed");break; case width::Normal: aXLFD.append("normal");break; case width::SemiExpanded: aXLFD.append("semiexpanded");break; case width::Expanded: aXLFD.append("expanded");break; case width::ExtraExpanded: aXLFD.append("extraexpanded");break; case width::UltraExpanded: aXLFD.append("ultraexpanded");break; default: break; } aXLFD.append("-utf8-0-0-0-0-"); aXLFD.append( pFont->m_ePitch == pitch::Fixed ? "m" : "p" ); aXLFD.append("-0-"); const char* pEnc = rtl_getBestUnixCharsetFromTextEncoding( pFont->m_aEncoding ); if( ! pEnc ) { if( pFont->m_aEncoding == RTL_TEXTENCODING_ADOBE_STANDARD ) pEnc = "adobe-standard"; else pEnc = "iso8859-1"; } aXLFD .append( pEnc ); return aXLFD.makeStringAndClear(); } // ------------------------------------------------------------------------- OUString PrintFontManager::convertTrueTypeName( void* pRecord ) const { NameRecord* pNameRecord = (NameRecord*)pRecord; OUString aValue; if( ( pNameRecord->platformID == 3 && ( pNameRecord->encodingID == 0 || pNameRecord->encodingID == 1 ) ) // MS, Unicode || ( pNameRecord->platformID == 0 ) // Apple, Unicode ) { OUStringBuffer aName( pNameRecord->slen/2 ); const sal_uInt8* pNameBuffer = pNameRecord->sptr; for(int n = 0; n < pNameRecord->slen/2; n++ ) aName.append( (sal_Unicode)getUInt16BE( pNameBuffer ) ); aValue = aName.makeStringAndClear(); } else if( pNameRecord->platformID == 3 ) { if( pNameRecord->encodingID >= 2 && pNameRecord->encodingID <= 6 ) { /* * and now for a special kind of madness: * some fonts encode their byte value string as BE uint16 * (leading to stray zero bytes in the string) * while others code two bytes as a uint16 and swap to BE */ OStringBuffer aName; const sal_uInt8* pNameBuffer = pNameRecord->sptr; for(int n = 0; n < pNameRecord->slen/2; n++ ) { sal_Unicode aCode = (sal_Unicode)getUInt16BE( pNameBuffer ); sal_Char aChar = aCode >> 8; if( aChar ) aName.append( aChar ); aChar = aCode & 0x00ff; if( aChar ) aName.append( aChar ); } switch( pNameRecord->encodingID ) { case 2: aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_932 ); break; case 3: aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_936 ); break; case 4: aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_950 ); break; case 5: aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_949 ); break; case 6: aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_1361 ); break; } } } return aValue; } // ------------------------------------------------------------------------- void PrintFontManager::analyzeTrueTypeFamilyName( void* pTTFont, ::std::list< OUString >& rNames ) const { OUString aFamily; rNames.clear(); ::std::set< OUString > aSet; NameRecord* pNameRecords = NULL; int nNameRecords = GetTTNameRecords( (TrueTypeFont*)pTTFont, &pNameRecords ); if( nNameRecords && pNameRecords ) { LanguageType aLang = MsLangId::getSystemLanguage(); int nLastMatch = -1; for( int i = 0; i < nNameRecords; i++ ) { if( pNameRecords[i].nameID != 1 || pNameRecords[i].sptr == NULL ) continue; int nMatch = -1; if( pNameRecords[i].platformID == 0 ) // Unicode nMatch = 4000; else if( pNameRecords[i].platformID == 3 ) { // this bases on the LanguageType actually being a Win LCID if( pNameRecords[i].languageID == aLang ) nMatch = 8000; else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH_US ) nMatch = 2000; else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH || pNameRecords[i].languageID == LANGUAGE_ENGLISH_UK ) nMatch = 1500; else nMatch = 1000; } OUString aName = convertTrueTypeName( pNameRecords + i ); aSet.insert( aName ); if( nMatch > nLastMatch ) { nLastMatch = nMatch; aFamily = aName; } } DisposeNameRecords( pNameRecords, nNameRecords ); } if( aFamily.getLength() ) { rNames.push_front( aFamily ); for( ::std::set< OUString >::const_iterator it = aSet.begin(); it != aSet.end(); ++it ) if( *it != aFamily ) rNames.push_back( *it ); } return; } // ------------------------------------------------------------------------- bool PrintFontManager::analyzeTrueTypeFile( PrintFont* pFont ) const { bool bSuccess = false; rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); ByteString aFile = getFontFile( pFont ); TrueTypeFont* pTTFont = NULL; TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) { TTGlobalFontInfo aInfo; GetTTGlobalFontInfo( pTTFont, & aInfo ); ::std::list< OUString > aNames; analyzeTrueTypeFamilyName( pTTFont, aNames ); // set family name from XLFD if possible if( ! pFont->m_nFamilyName ) { if( aNames.begin() != aNames.end() ) { pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, aNames.front(), sal_True ); aNames.pop_front(); } else { sal_Int32 dotIndex; // poor font does not have a family name // name it to file name minus the extension dotIndex = pTTFontFile->m_aFontFile.lastIndexOf( '.' ); if ( dotIndex == -1 ) dotIndex = pTTFontFile->m_aFontFile.getLength(); pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( pTTFontFile->m_aFontFile.copy( 0, dotIndex ), aEncoding ), sal_True ); } } for( ::std::list< OUString >::iterator it = aNames.begin(); it != aNames.end(); ++it ) { if( it->getLength() ) { int nAlias = m_pAtoms->getAtom( ATOM_FAMILYNAME, *it, sal_True ); if( nAlias != pFont->m_nFamilyName ) { std::list< int >::const_iterator al_it; for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nAlias; ++al_it ) ; if( al_it == pFont->m_aAliases.end() ) pFont->m_aAliases.push_back( nAlias ); } } } if( aInfo.usubfamily ) pFont->m_aStyleName = OUString( aInfo.usubfamily ); pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, String( ByteString( aInfo.psname ), aEncoding ), sal_True ); switch( aInfo.weight ) { case FW_THIN: pFont->m_eWeight = weight::Thin; break; case FW_EXTRALIGHT: pFont->m_eWeight = weight::UltraLight; break; case FW_LIGHT: pFont->m_eWeight = weight::Light; break; case FW_MEDIUM: pFont->m_eWeight = weight::Medium; break; case FW_SEMIBOLD: pFont->m_eWeight = weight::SemiBold; break; case FW_BOLD: pFont->m_eWeight = weight::Bold; break; case FW_EXTRABOLD: pFont->m_eWeight = weight::UltraBold; break; case FW_BLACK: pFont->m_eWeight = weight::Black; break; case FW_NORMAL: default: pFont->m_eWeight = weight::Normal; break; } switch( aInfo.width ) { case FWIDTH_ULTRA_CONDENSED: pFont->m_eWidth = width::UltraCondensed; break; case FWIDTH_EXTRA_CONDENSED: pFont->m_eWidth = width::ExtraCondensed; break; case FWIDTH_CONDENSED: pFont->m_eWidth = width::Condensed; break; case FWIDTH_SEMI_CONDENSED: pFont->m_eWidth = width::SemiCondensed; break; case FWIDTH_SEMI_EXPANDED: pFont->m_eWidth = width::SemiExpanded; break; case FWIDTH_EXPANDED: pFont->m_eWidth = width::Expanded; break; case FWIDTH_EXTRA_EXPANDED: pFont->m_eWidth = width::ExtraExpanded; break; case FWIDTH_ULTRA_EXPANDED: pFont->m_eWidth = width::UltraExpanded; break; case FWIDTH_NORMAL: default: pFont->m_eWidth = width::Normal; break; } pFont->m_ePitch = aInfo.pitch ? pitch::Fixed : pitch::Variable; pFont->m_eItalic = aInfo.italicAngle == 0 ? italic::Upright : ( aInfo.italicAngle < 0 ? italic::Italic : italic::Oblique ); // #104264# there are fonts that set italic angle 0 although they are // italic; use macstyle bit here if( aInfo.italicAngle == 0 && (aInfo.macStyle & 2) ) pFont->m_eItalic = italic::Italic; pFont->m_aEncoding = aInfo.symbolEncoded ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UCS2; pFont->m_aGlobalMetricY.width = pFont->m_aGlobalMetricX.width = aInfo.xMax - aInfo.xMin; pFont->m_aGlobalMetricY.height = pFont->m_aGlobalMetricX.height = aInfo.yMax - aInfo.yMin; if( aInfo.winAscent && aInfo.winDescent ) { pFont->m_nAscend = aInfo.winAscent; pFont->m_nDescend = aInfo.winDescent; pFont->m_nLeading = pFont->m_nAscend + pFont->m_nDescend - 1000; } else if( aInfo.typoAscender && aInfo.typoDescender ) { pFont->m_nLeading = aInfo.typoLineGap; pFont->m_nAscend = aInfo.typoAscender; pFont->m_nDescend = -aInfo.typoDescender; } else { pFont->m_nLeading = aInfo.linegap; pFont->m_nAscend = aInfo.ascender; pFont->m_nDescend = -aInfo.descender; } // last try: font bounding box if( pFont->m_nAscend == 0 ) pFont->m_nAscend = aInfo.yMax; if( pFont->m_nDescend == 0 ) pFont->m_nDescend = -aInfo.yMin; if( pFont->m_nLeading == 0 ) pFont->m_nLeading = 15 * (pFont->m_nAscend+pFont->m_nDescend) / 100; if( pFont->m_nAscend ) pFont->m_aGlobalMetricX.height = pFont->m_aGlobalMetricY.height = pFont->m_nAscend + pFont->m_nDescend; // get bounding box pFont->m_nXMin = aInfo.xMin; pFont->m_nYMin = aInfo.yMin; pFont->m_nXMax = aInfo.xMax; pFont->m_nYMax = aInfo.yMax; // get type flags pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags; // get vertical substitutions flag pFont->m_bHaveVerticalSubstitutedGlyphs = DoesVerticalSubstitution( pTTFont, 1 ); CloseTTFont( pTTFont ); bSuccess = true; } #if OSL_DEBUG_LEVEL > 1 else fprintf( stderr, "could not OpenTTFont \"%s\"\n", aFile.GetBuffer() ); #endif return bSuccess; } // ------------------------------------------------------------------------- void PrintFontManager::initFontsAlias() { m_aXLFD_Aliases.clear(); rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); for( std::list< OString >::const_iterator dir_it = m_aFontDirectories.begin(); dir_it != m_aFontDirectories.end(); ++dir_it ) { OStringBuffer aDirName(512); aDirName.append( *dir_it ); aDirName.append( "/fonts.alias" ); SvFileStream aStream( OStringToOUString( aDirName.makeStringAndClear(), aEnc ), STREAM_READ ); if( ! aStream.IsOpen() ) continue; do { ByteString aLine; aStream.ReadLine( aLine ); // get the alias and the pattern it gets translated to ByteString aAlias = GetCommandLineToken( 0, aLine ); ByteString aMap = GetCommandLineToken( 1, aLine ); // remove eventual quotes aAlias.EraseLeadingChars( '"' ); aAlias.EraseTrailingChars( '"' ); aMap.EraseLeadingChars( '"' ); aMap.EraseTrailingChars( '"' ); XLFDEntry aAliasEntry, aMapEntry; parseXLFD( aAlias, aAliasEntry ); parseXLFD( aMap, aMapEntry ); if( aAliasEntry.nMask && aMapEntry.nMask ) m_aXLFD_Aliases[ aMapEntry ].push_back( aAliasEntry ); } while( ! aStream.IsEof() ); } } // code stolen from vcl's RegisterFontSubstitutors() // TODO: use that method once psprint gets merged into vcl static bool AreFCSubstitutionsEnabled() { // init font substitution defaults int nDisableBits = 0; #ifdef SOLARIS // TODO: check the OS version and fc-data maintenance level nDisableBits = 1; // disable "font fallback" here on default #endif // apply the environment variable if any const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" ); if( pEnvStr ) { // if( (*pEnvStr >= '0') && (*pEnvStr <= '9') ) nDisableBits = (*pEnvStr - '0'); else nDisableBits = ~0U; // no specific bits set: disable all } return ((nDisableBits & 3) == 0); } void PrintFontManager::initialize() { #ifdef CALLGRIND_COMPILE CALLGRIND_TOGGLE_COLLECT(); CALLGRIND_ZERO_STATS(); #endif long aDirEntBuffer[ (sizeof(struct dirent)+_PC_NAME_MAX)+1 ]; if( ! m_pFontCache ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "creating font cache ... " ); clock_t aStart; struct tms tms; aStart = times( &tms ); #endif m_pFontCache = new FontCache(); #if OSL_DEBUG_LEVEL > 1 clock_t aStop = times( &tms ); fprintf( stderr, "done in %lf s\n", (double)(aStop - aStart)/(double)sysconf( _SC_CLK_TCK ) ); #endif } // initialize may be called twice in the future { for( ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) delete (*it).second; m_nNextFontID = 1; m_aFonts.clear(); m_aFontDirectories.clear(); m_aPrivateFontDirectories.clear(); m_aOverrideFonts.clear(); } #if OSL_DEBUG_LEVEL > 1 clock_t aStart; clock_t aStep1; clock_t aStep2; clock_t aStep3; int nBuiltinFonts = 0; int nCached = 0; struct tms tms; aStart = times( &tms ); #endif // first try fontconfig m_bFontconfigSuccess = initFontconfig(); // part one - look for downloadable fonts rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); const ::rtl::OUString &rSalPrivatePath = psp::getFontPath(); // search for the fonts in SAL_PRIVATE_FONTPATH first; those are // the fonts installed with the office if( rSalPrivatePath.getLength() ) { OString aPath = rtl::OUStringToOString( rSalPrivatePath, aEncoding ); const bool bAreFCSubstitutionsEnabled = AreFCSubstitutionsEnabled(); sal_Int32 nIndex = 0; do { OString aToken = aPath.getToken( 0, ';', nIndex ); normPath( aToken ); // if registering an app-specific fontdir with fontconfig fails // and fontconfig-based substitutions are enabled // then trying to use these app-specific fonts doesn't make sense if( m_bFontconfigSuccess && !addFontconfigDir( aToken ) ) if( bAreFCSubstitutionsEnabled ) continue; m_aFontDirectories.push_back( aToken ); m_aPrivateFontDirectories.push_back( getDirectoryAtom( aToken, true ) ); } while( nIndex >= 0 ); } // protect against duplicate paths std::hash_map< OString, int, OStringHash > visited_dirs; // now that all global and local font dirs are known to fontconfig // check that there are fonts actually managed by fontconfig // also don't search directories that fontconfig already did if( m_bFontconfigSuccess ) m_bFontconfigSuccess = (countFontconfigFonts( visited_dirs ) > 0); // don't search through many directories fontconfig already told us about if( ! m_bFontconfigSuccess ) ImplGetSVData()->mpDefInst->FillFontPathList( m_aFontDirectories ); // fill XLFD aliases from fonts.alias files initFontsAlias(); // search for font files in each path std::list< OString >::iterator dir_it; for( dir_it = m_aFontDirectories.begin(); dir_it != m_aFontDirectories.end(); ++dir_it ) { OString aPath( *dir_it ); // see if we were here already if( visited_dirs.find( aPath ) != visited_dirs.end() ) continue; visited_dirs[ aPath ] = 1; // there may be ":unscaled" directories (see XFree86) // it should be safe to ignore them since they should not // contain any of our recognizeable fonts // ask the font cache whether it handles this directory std::list< PrintFont* > aCacheFonts; if( m_pFontCache->listDirectory( aPath, aCacheFonts ) ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "adding cache directory: %s\n", aPath.getStr() ); #endif for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it ) { fontID aFont = m_nNextFontID++; m_aFonts[ aFont ] = *it; if( (*it)->m_eType == fonttype::Type1 ) m_aFontFileToFontID[ static_cast(*it)->m_aFontFile ].insert( aFont ); else if( (*it)->m_eType == fonttype::TrueType ) m_aFontFileToFontID[ static_cast(*it)->m_aFontFile ].insert( aFont ); else if( (*it)->m_eType == fonttype::Builtin ) m_aFontFileToFontID[ static_cast(*it)->m_aMetricFile ].insert( aFont ); #if OSL_DEBUG_LEVEL > 1 if( (*it)->m_eType == fonttype::Builtin ) nBuiltinFonts++; nCached++; #if OSL_DEBUG_LEVEL > 2 fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont, OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), getFontFileSysPath( aFont ).getStr() ); #endif #endif } if( ! m_pFontCache->scanAdditionalFiles( aPath ) ) continue; } DIR* pDIR = opendir( aPath.getStr() ); struct dirent* pEntry = (struct dirent*)aDirEntBuffer; if( pDIR ) { // read fonts.dir if possible ::std::hash_map< OString, ::std::list, OStringHash > aFontsDir; int nDirID = getDirectoryAtom( aPath, true ); // #i38367# no fonts.dir in our own directories anymore std::list< int >::const_iterator priv_dir; for( priv_dir = m_aPrivateFontDirectories.begin(); priv_dir != m_aPrivateFontDirectories.end() && *priv_dir != nDirID; ++priv_dir ) ; if( priv_dir == m_aPrivateFontDirectories.end() ) { ByteString aGccDummy( aPath ); String aFontsDirPath( aGccDummy, aEncoding ); aFontsDirPath.AppendAscii( "/fonts.dir" ); SvFileStream aStream( aFontsDirPath, STREAM_READ ); if( aStream.IsOpen() ) { ByteString aLine; while( ! aStream.IsEof() ) { aStream.ReadLine( aLine ); ByteString aFileName( GetCommandLineToken( 0, aLine ) ); ByteString aXLFD( aLine.Copy( aFileName.Len() ) ); if( aFileName.Len() && aXLFD.Len() ) aFontsDir[ aFileName ].push_back(aXLFD); } } } int nDirFonts = 0; while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pEntry ) && pEntry ) { OString aFileName( pEntry->d_name ); // ignore .afm files here if( aFileName.getLength() > 3 && aFileName.lastIndexOf( ".afm" ) == aFileName.getLength()-4 ) continue; struct stat aStat; ByteString aFilePath( aPath ); aFilePath.Append( '/' ); aFilePath.Append( ByteString( aFileName ) ); if( ! stat( aFilePath.GetBuffer(), &aStat ) && S_ISREG( aStat.st_mode ) ) { if( findFontFileID( nDirID, aFileName ) == 0 ) { ::std::list aXLFDs; ::std::hash_map< OString, ::std::list, OStringHash >::const_iterator it = aFontsDir.find( aFileName ); if( it != aFontsDir.end() ) aXLFDs = (*it).second; // fill in font attributes from XLFD rather // than reading every file ::std::list< PrintFont* > aNewFonts; if( analyzeFontFile( nDirID, aFileName, aXLFDs, aNewFonts ) ) { for( ::std::list< PrintFont* >::iterator font_it = aNewFonts.begin(); font_it != aNewFonts.end(); ++font_it ) { fontID aFont = m_nNextFontID++; m_aFonts[ aFont ] = *font_it; m_aFontFileToFontID[ aFileName ].insert( aFont ); m_pFontCache->updateFontCacheEntry( *font_it, false ); nDirFonts++; #if OSL_DEBUG_LEVEL > 2 fprintf( stderr, "adding font %d: \"%s\" from %s\n", aFont, OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), getFontFileSysPath( aFont ).getStr() ); #endif } } } } } closedir( pDIR ); m_pFontCache->updateDirTimestamp( nDirID ); if( ! nDirFonts ) m_pFontCache->markEmptyDir( nDirID ); } } #if OSL_DEBUG_LEVEL > 1 aStep1 = times( &tms ); #endif // part two - look for metrics for builtin printer fonts std::list< OUString > aMetricDirs; psp::getPrinterPathList( aMetricDirs, PRINTER_METRICDIR ); std::list< OString > aEmptyFontsDir; for( std::list< OUString >::const_iterator met_dir_it = aMetricDirs.begin(); met_dir_it != aMetricDirs.end(); ++met_dir_it ) { OString aDir = OUStringToOString( *met_dir_it, aEncoding ); // ask the font cache whether it handles this directory std::list< PrintFont* > aCacheFonts; if( m_pFontCache->listDirectory( aDir, aCacheFonts ) ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "adding cache directory: %s\n", aDir.getStr() ); #endif for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it ) { fontID aFont = m_nNextFontID++; m_aFonts[ aFont ] = *it; if( (*it)->m_eType == fonttype::Type1 ) m_aFontFileToFontID[ static_cast(*it)->m_aFontFile ].insert( aFont ); else if( (*it)->m_eType == fonttype::TrueType ) m_aFontFileToFontID[ static_cast(*it)->m_aFontFile ].insert( aFont ); else if( (*it)->m_eType == fonttype::Builtin ) m_aFontFileToFontID[ static_cast(*it)->m_aMetricFile ].insert( aFont ); #if OSL_DEBUG_LEVEL > 1 if( (*it)->m_eType == fonttype::Builtin ) nBuiltinFonts++; nCached++; #if OSL_DEBUG_LEVEL > 2 fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont, OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), getFontFileSysPath( aFont ).getStr() ); #endif #endif } continue; } DIR* pDIR = opendir( aDir.getStr() ); if( pDIR ) { struct dirent* pDirEntry = (struct dirent*)aDirEntBuffer; int nDirID = getDirectoryAtom( aDir, true ); int nDirFonts = 0; while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pDirEntry ) && pDirEntry ) { ByteString aFile( aDir ); aFile += '/'; aFile += pDirEntry->d_name; struct stat aStat; if( ! stat( aFile.GetBuffer(), &aStat ) && S_ISREG( aStat.st_mode ) ) { OString aFileName( pDirEntry->d_name, strlen( pDirEntry->d_name ) ); OString aExt( aFileName.copy( aFileName.lastIndexOf( '.' )+1 ) ); if( aExt.equalsIgnoreAsciiCase( "afm" ) ) { ::std::list< PrintFont* > aNewFonts; analyzeFontFile( nDirID, aFileName, aEmptyFontsDir, aNewFonts ); for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); it != aNewFonts.end(); ++it ) { if( findFontBuiltinID( (*it)->m_nPSName ) == 0 ) { m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID ); m_aFonts[ m_nNextFontID++ ] = *it; m_pFontCache->updateFontCacheEntry( *it, false ); #if OSL_DEBUG_LEVEL > 2 nBuiltinFonts++; #endif } else delete *it; } } } } closedir( pDIR ); if( ! nDirFonts ) m_pFontCache->markEmptyDir( nDirID ); } } #if OSL_DEBUG_LEVEL > 1 aStep2 = times( &tms ); #endif // part three - fill in family styles ::std::hash_map< fontID, PrintFont* >::iterator font_it; for (font_it = m_aFonts.begin(); font_it != m_aFonts.end(); ++font_it) { ::std::hash_map< int, family::type >::const_iterator it = m_aFamilyTypes.find( font_it->second->m_nFamilyName ); if (it != m_aFamilyTypes.end()) continue; const ::rtl::OUString& rFamily = m_pAtoms->getString( ATOM_FAMILYNAME, font_it->second->m_nFamilyName); family::type eType = matchFamilyName( rFamily ); m_aFamilyTypes[ font_it->second->m_nFamilyName ] = eType; } #if OSL_DEBUG_LEVEL > 1 aStep3 = times( &tms ); fprintf( stderr, "PrintFontManager::initialize: collected %d fonts (%d builtin, %d cached)\n", m_aFonts.size(), nBuiltinFonts, nCached ); double fTick = (double)sysconf( _SC_CLK_TCK ); fprintf( stderr, "Step 1 took %lf seconds\n", (double)(aStep1 - aStart)/fTick ); fprintf( stderr, "Step 2 took %lf seconds\n", (double)(aStep2 - aStep1)/fTick ); fprintf( stderr, "Step 3 took %lf seconds\n", (double)(aStep3 - aStep2)/fTick ); #endif m_pFontCache->flush(); #ifdef CALLGRIND_COMPILE CALLGRIND_DUMP_STATS(); CALLGRIND_TOGGLE_COLLECT(); #endif } // ------------------------------------------------------------------------- inline bool equalPitch (psp::pitch::type from, psp::pitch::type to) { return from == to; } inline bool equalWeight (psp::weight::type from, psp::weight::type to) { return from > to ? (from - to) <= 3 : (to - from) <= 3; } inline bool equalItalic (psp::italic::type from, psp::italic::type to) { if ( (from == psp::italic::Italic) || (from == psp::italic::Oblique) ) return (to == psp::italic::Italic) || (to == psp::italic::Oblique); return to == from; } inline bool equalEncoding (rtl_TextEncoding from, rtl_TextEncoding to) { if ((from == RTL_TEXTENCODING_ISO_8859_1) || (from == RTL_TEXTENCODING_MS_1252)) return (to == RTL_TEXTENCODING_ISO_8859_1) || (to == RTL_TEXTENCODING_MS_1252); return from == to; } namespace { struct BuiltinFontIdentifier { OUString aFamily; italic::type eItalic; weight::type eWeight; pitch::type ePitch; rtl_TextEncoding aEncoding; BuiltinFontIdentifier( const OUString& rFam, italic::type eIt, weight::type eWg, pitch::type ePt, rtl_TextEncoding enc ) : aFamily( rFam ), eItalic( eIt ), eWeight( eWg ), ePitch( ePt ), aEncoding( enc ) {} bool operator==( const BuiltinFontIdentifier& rRight ) const { return equalItalic( eItalic, rRight.eItalic ) && equalWeight( eWeight, rRight.eWeight ) && equalPitch( ePitch, rRight.ePitch ) && equalEncoding( aEncoding, rRight.aEncoding ) && aFamily.equalsIgnoreAsciiCase( rRight.aFamily ); } }; struct BuiltinFontIdentifierHash { size_t operator()( const BuiltinFontIdentifier& rFont ) const { return rFont.aFamily.hashCode() ^ rFont.eItalic ^ rFont.eWeight ^ rFont.ePitch ^ rFont.aEncoding; } }; } void PrintFontManager::getFontList( ::std::list< fontID >& rFontIDs, const PPDParser* pParser, bool bUseOverrideMetrics ) { rFontIDs.clear(); std::hash_map< fontID, PrintFont* >::const_iterator it; /* * Note: there are two easy steps making this faster: * first: insert the printer builtins first, then the not builtins, * if they do not match. * drawback: this would change the sequence of fonts; this could have * subtle, unknown consequences in vcl font matching * second: instead of comparing attributes to see whether a softfont * is duplicate to a builtin one could simply compare the PSName (which is * supposed to be unique), which at this point is just an int. * drawback: this could change which fonts are listed; especially TrueType * fonts often have a rather dubious PSName, so this could change the * font list not so subtle. * Until getFontList for a printer becomes a performance issue (which is * currently not the case), best stay with the current algorithm. */ // fill sets of printer supported fonts if( pParser ) { std::set aBuiltinPSNames; std::hash_set< BuiltinFontIdentifier, BuiltinFontIdentifierHash > aBuiltinFonts; std::map aOverridePSNames; if( bUseOverrideMetrics ) { readOverrideMetrics(); for( std::vector::const_iterator over = m_aOverrideFonts.begin(); over != m_aOverrideFonts.end(); ++over ) { std::hash_map::const_iterator font_it = m_aFonts.find( *over ); DBG_ASSERT( font_it != m_aFonts.end(), "override to nonexistant font" ); if( font_it != m_aFonts.end() ) aOverridePSNames[ font_it->second->m_nPSName ] = *over; } } int nFonts = pParser->getFonts(); for( int i = 0; i < nFonts; i++ ) aBuiltinPSNames.insert( m_pAtoms->getAtom( ATOM_PSNAME, pParser->getFont( i ) ) ); for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) { PrintFont* pFont = it->second; if( it->second->m_eType == fonttype::Builtin && aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() ) { bool bInsert = true; if( bUseOverrideMetrics ) { // in override case only use the override fonts, not their counterparts std::map::const_iterator over = aOverridePSNames.find( pFont->m_nPSName ); if( over != aOverridePSNames.end() && over->second != it->first ) bInsert = false; } else { // do not insert override fonts in non override case if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() ) bInsert = false; } if( bInsert ) { aBuiltinFonts.insert( BuiltinFontIdentifier( m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ), pFont->m_eItalic, pFont->m_eWeight, pFont->m_ePitch, pFont->m_aEncoding ) ); } } } for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) { PrintFont* pFont = it->second; if( it->second->m_eType == fonttype::Builtin ) { if( aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() ) { bool bInsert = true; if( bUseOverrideMetrics ) { // in override case only use the override fonts, not their counterparts std::map::const_iterator over = aOverridePSNames.find( pFont->m_nPSName ); if( over != aOverridePSNames.end() && over->second != it->first ) bInsert = false; } else { // do not insert override fonts in non override case if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() ) bInsert = false; } if( bInsert ) rFontIDs.push_back( it->first ); } } else if( aBuiltinFonts.find( BuiltinFontIdentifier( m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ), pFont->m_eItalic, pFont->m_eWeight, pFont->m_ePitch, pFont->m_aEncoding ) ) == aBuiltinFonts.end() ) { rFontIDs.push_back( it->first ); } } } else // no specific printer { for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) rFontIDs.push_back( it->first ); } } // ------------------------------------------------------------------------- void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, FastPrintFontInfo& rInfo ) const { ::std::hash_map< int, family::type >::const_iterator style_it = m_aFamilyTypes.find( pFont->m_nFamilyName ); rInfo.m_eType = pFont->m_eType; rInfo.m_aFamilyName = m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ); rInfo.m_aStyleName = pFont->m_aStyleName; rInfo.m_eFamilyStyle = style_it != m_aFamilyTypes.end() ? style_it->second : family::Unknown; rInfo.m_eItalic = pFont->m_eItalic; rInfo.m_eWidth = pFont->m_eWidth; rInfo.m_eWeight = pFont->m_eWeight; rInfo.m_ePitch = pFont->m_ePitch; rInfo.m_aEncoding = pFont->m_aEncoding; rInfo.m_bEmbeddable = (pFont->m_eType == fonttype::Type1); rInfo.m_bSubsettable = (pFont->m_eType == fonttype::TrueType); // TODO: rename to SfntType rInfo.m_aAliases.clear(); for( ::std::list< int >::iterator it = pFont->m_aAliases.begin(); it != pFont->m_aAliases.end(); ++it ) rInfo.m_aAliases.push_back( m_pAtoms->getString( ATOM_FAMILYNAME, *it ) ); } // ------------------------------------------------------------------------- void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, PrintFontInfo& rInfo ) const { if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() ) { // might be a truetype font not analyzed or type1 without metrics read if( pFont->m_eType == fonttype::Type1 ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); else if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } fillPrintFontInfo( pFont, static_cast< FastPrintFontInfo& >( rInfo ) ); rInfo.m_nAscend = pFont->m_nAscend; rInfo.m_nDescend = pFont->m_nDescend; rInfo.m_nLeading = pFont->m_nLeading; rInfo.m_nWidth = pFont->m_aGlobalMetricX.width < pFont->m_aGlobalMetricY.width ? pFont->m_aGlobalMetricY.width : pFont->m_aGlobalMetricX.width; } // ------------------------------------------------------------------------- void PrintFontManager::getFontListWithInfo( ::std::list< PrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics ) { rFonts.clear(); ::std::list< fontID > aFontList; getFontList( aFontList, pParser, bUseOverrideMetrics ); ::std::list< fontID >::iterator it; for( it = aFontList.begin(); it != aFontList.end(); ++it ) { PrintFontInfo aInfo; aInfo.m_nID = *it; fillPrintFontInfo( getFont( *it ), aInfo ); rFonts.push_back( aInfo ); } } // ------------------------------------------------------------------------- void PrintFontManager::getFontListWithFastInfo( ::std::list< FastPrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics ) { rFonts.clear(); ::std::list< fontID > aFontList; getFontList( aFontList, pParser, bUseOverrideMetrics ); ::std::list< fontID >::iterator it; for( it = aFontList.begin(); it != aFontList.end(); ++it ) { FastPrintFontInfo aInfo; aInfo.m_nID = *it; fillPrintFontInfo( getFont( *it ), aInfo ); rFonts.push_back( aInfo ); } } // ------------------------------------------------------------------------- bool PrintFontManager::getFontInfo( fontID nFontID, PrintFontInfo& rInfo ) const { PrintFont* pFont = getFont( nFontID ); if( pFont ) { rInfo.m_nID = nFontID; fillPrintFontInfo( pFont, rInfo ); } return pFont ? true : false; } // ------------------------------------------------------------------------- bool PrintFontManager::getFontFastInfo( fontID nFontID, FastPrintFontInfo& rInfo ) const { PrintFont* pFont = getFont( nFontID ); if( pFont ) { rInfo.m_nID = nFontID; fillPrintFontInfo( pFont, rInfo ); } return pFont ? true : false; } // ------------------------------------------------------------------------- bool PrintFontManager::getFontBoundingBox( fontID nFontID, int& xMin, int& yMin, int& xMax, int& yMax ) { bool bSuccess = false; PrintFont* pFont = getFont( nFontID ); if( pFont ) { if( pFont->m_nXMin == 0 && pFont->m_nYMin == 0 && pFont->m_nXMax == 0 && pFont->m_nYMax == 0 ) { // might be a truetype font not analyzed or type1 without metrics read if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); else if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } bSuccess = true; xMin = pFont->m_nXMin; yMin = pFont->m_nYMin; xMax = pFont->m_nXMax; yMax = pFont->m_nYMax; } return bSuccess; } // ------------------------------------------------------------------------- int PrintFontManager::getFontFaceNumber( fontID nFontID ) const { int nRet = -1; PrintFont* pFont = getFont( nFontID ); if( pFont && pFont->m_eType == fonttype::TrueType ) nRet = static_cast< TrueTypeFontFile* >(pFont)->m_nCollectionEntry; return nRet; } // ------------------------------------------------------------------------- family::type PrintFontManager::matchFamilyName( const ::rtl::OUString& rFamily ) const { typedef struct { const char* mpName; sal_uInt16 mnLength; family::type meType; } family_t; #define InitializeClass( p, a ) p, sizeof(p) - 1, a const family_t pFamilyMatch[] = { { InitializeClass( "arial", family::Swiss ) }, { InitializeClass( "arioso", family::Script ) }, { InitializeClass( "avant garde", family::Swiss ) }, { InitializeClass( "avantgarde", family::Swiss ) }, { InitializeClass( "bembo", family::Roman ) }, { InitializeClass( "bookman", family::Roman ) }, { InitializeClass( "conga", family::Roman ) }, { InitializeClass( "courier", family::Modern ) }, { InitializeClass( "curl", family::Script ) }, { InitializeClass( "fixed", family::Modern ) }, { InitializeClass( "gill", family::Swiss ) }, { InitializeClass( "helmet", family::Modern ) }, { InitializeClass( "helvetica", family::Swiss ) }, { InitializeClass( "international", family::Modern ) }, { InitializeClass( "lucida", family::Swiss ) }, { InitializeClass( "new century schoolbook", family::Roman ) }, { InitializeClass( "palatino", family::Roman ) }, { InitializeClass( "roman", family::Roman ) }, { InitializeClass( "sans serif", family::Swiss ) }, { InitializeClass( "sansserif", family::Swiss ) }, { InitializeClass( "serf", family::Roman ) }, { InitializeClass( "serif", family::Roman ) }, { InitializeClass( "times", family::Roman ) }, { InitializeClass( "utopia", family::Roman ) }, { InitializeClass( "zapf chancery", family::Script ) }, { InitializeClass( "zapfchancery", family::Script ) } }; rtl::OString aFamily = rtl::OUStringToOString( rFamily, RTL_TEXTENCODING_ASCII_US ); sal_uInt32 nLower = 0; sal_uInt32 nUpper = sizeof(pFamilyMatch) / sizeof(pFamilyMatch[0]); while( nLower < nUpper ) { sal_uInt32 nCurrent = (nLower + nUpper) / 2; const family_t* pHaystack = pFamilyMatch + nCurrent; sal_Int32 nComparison = rtl_str_compareIgnoreAsciiCase_WithLength ( aFamily.getStr(), aFamily.getLength(), pHaystack->mpName, pHaystack->mnLength ); if( nComparison < 0 ) nUpper = nCurrent; else if( nComparison > 0 ) nLower = nCurrent + 1; else return pHaystack->meType; } return family::Unknown; } // ------------------------------------------------------------------------- family::type PrintFontManager::getFontFamilyType( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); if( !pFont ) return family::Unknown; ::std::hash_map< int, family::type >::const_iterator it = m_aFamilyTypes.find( pFont->m_nFamilyName ); return (it != m_aFamilyTypes.end()) ? it->second : family::Unknown; } // ------------------------------------------------------------------------- const ::rtl::OUString& PrintFontManager::getFontFamily( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); return m_pAtoms->getString( ATOM_FAMILYNAME, pFont ? pFont->m_nFamilyName : INVALID_ATOM ); } // ------------------------------------------------------------------------- OString PrintFontManager::getAfmFile( PrintFont* pFont ) const { OString aMetricPath; if( pFont ) { switch( pFont->m_eType ) { case fonttype::Type1: { Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont); aMetricPath = getDirectory( pPSFont->m_nDirectory ); aMetricPath += "/"; aMetricPath += pPSFont->m_aMetricFile; } break; case fonttype::Builtin: { BuiltinFont* pBuiltinFont = static_cast< BuiltinFont* >(pFont); aMetricPath = getDirectory( pBuiltinFont->m_nDirectory ); aMetricPath += "/"; aMetricPath += pBuiltinFont->m_aMetricFile; } break; default: break; } } return aMetricPath; } // ------------------------------------------------------------------------- OString PrintFontManager::getFontFile( PrintFont* pFont ) const { OString aPath; if( pFont && pFont->m_eType == fonttype::Type1 ) { Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont); ::std::hash_map< int, OString >::const_iterator it = m_aAtomToDir.find( pPSFont->m_nDirectory ); aPath = it->second; aPath += "/"; aPath += pPSFont->m_aFontFile; } else if( pFont && pFont->m_eType == fonttype::TrueType ) { TrueTypeFontFile* pTTFont = static_cast< TrueTypeFontFile* >(pFont); ::std::hash_map< int, OString >::const_iterator it = m_aAtomToDir.find( pTTFont->m_nDirectory ); aPath = it->second; aPath += "/"; aPath += pTTFont->m_aFontFile; } return aPath; } // ------------------------------------------------------------------------- const ::rtl::OUString& PrintFontManager::getPSName( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); if( pFont && pFont->m_nPSName == 0 ) { if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } return m_pAtoms->getString( ATOM_PSNAME, pFont ? pFont->m_nPSName : INVALID_ATOM ); } // ------------------------------------------------------------------------- const CharacterMetric& PrintFontManager::getGlobalFontMetric( fontID nFontID, bool bHorizontal ) const { static CharacterMetric aMetric; PrintFont* pFont = getFont( nFontID ); return pFont ? ( bHorizontal ? pFont->m_aGlobalMetricX : pFont->m_aGlobalMetricY ) : aMetric; } // ------------------------------------------------------------------------- int PrintFontManager::getFontAscend( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) { // might be a truetype font not yet analyzed if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); } return pFont->m_nAscend; } // ------------------------------------------------------------------------- int PrintFontManager::getFontDescend( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) { // might be a truetype font not yet analyzed if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); } return pFont->m_nDescend; } // ------------------------------------------------------------------------- int PrintFontManager::getFontLeading( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) { // might be a truetype font not yet analyzed if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } return pFont->m_nLeading; } // ------------------------------------------------------------------------- bool PrintFontManager::hasVerticalSubstitutions( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) { // might be a truetype font not yet analyzed if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } return pFont->m_bHaveVerticalSubstitutedGlyphs; } // ------------------------------------------------------------------------- void PrintFontManager::hasVerticalSubstitutions( fontID nFontID, const sal_Unicode* pCharacters, int nCharacters, bool* pHasSubst ) const { PrintFont* pFont = getFont( nFontID ); if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) { // might be a truetype font not yet analyzed if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } if( ! pFont->m_bHaveVerticalSubstitutedGlyphs ) memset( pHasSubst, 0, sizeof(bool)*nCharacters ); else { for( int i = 0; i < nCharacters; i++ ) { sal_Unicode code = pCharacters[i]; if( ! pFont->m_pMetrics || ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) ) pFont->queryMetricPage( code >> 8, m_pAtoms ); ::std::hash_map< sal_Unicode, bool >::const_iterator it = pFont->m_pMetrics->m_bVerticalSubstitutions.find( code ); pHasSubst[i] = it != pFont->m_pMetrics->m_bVerticalSubstitutions.end(); } } } // ------------------------------------------------------------------------- OUString PrintFontManager::getFontXLFD( fontID nFontID ) const { PrintFont* pFont = getFont( nFontID ); OUString aRet; if( pFont ) { ByteString aXLFD( getXLFD( pFont ) ); rtl_TextEncoding aEncoding = aXLFD.GetToken( 6, '-' ).Search( "utf8" ) != STRING_NOTFOUND ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1; aRet = OStringToOUString( aXLFD, aEncoding ); } return aRet; } // ------------------------------------------------------------------------- const ::std::list< KernPair >& PrintFontManager::getKernPairs( fontID nFontID, bool bVertical ) const { static ::std::list< KernPair > aEmpty; PrintFont* pFont = getFont( nFontID ); if( ! pFont ) return aEmpty; if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried ) pFont->queryMetricPage( 0, m_pAtoms ); if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried ) return aEmpty; return bVertical ? pFont->m_pMetrics->m_aYKernPairs : pFont->m_pMetrics->m_aXKernPairs; } // ------------------------------------------------------------------------- bool PrintFontManager::isFontDownloadingAllowed( fontID nFont ) const { static const char* pEnable = getenv( "PSPRINT_ENABLE_TTF_COPYRIGHTAWARENESS" ); bool bRet = true; if( pEnable && *pEnable ) { PrintFont* pFont = getFont( nFont ); if( pFont && pFont->m_eType == fonttype::TrueType ) { TrueTypeFontFile* pTTFontFile = static_cast(pFont); if( pTTFontFile->m_nTypeFlags & TYPEFLAG_INVALID ) { TrueTypeFont* pTTFont = NULL; ByteString aFile = getFontFile( pFont ); if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) { // get type flags TTGlobalFontInfo aInfo; GetTTGlobalFontInfo( pTTFont, & aInfo ); pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags; CloseTTFont( pTTFont ); } } unsigned int nCopyrightFlags = pTTFontFile->m_nTypeFlags & TYPEFLAG_COPYRIGHT_MASK; // font embedding is allowed if either // no restriction at all (bit 1 clear) // printing allowed (bit 1 set, bit 2 set ) bRet = ! ( nCopyrightFlags & 0x02 ) || ( nCopyrightFlags & 0x04 ); } } return bRet; } // ------------------------------------------------------------------------- bool PrintFontManager::getMetrics( fontID nFontID, const sal_Unicode* pString, int nLen, CharacterMetric* pArray, bool bVertical ) const { PrintFont* pFont = getFont( nFontID ); if( ! pFont ) return false; if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() ) { // might be a font not yet analyzed if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); else if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } for( int i = 0; i < nLen; i++ ) { if( ! pFont->m_pMetrics || ! ( pFont->m_pMetrics->m_aPages[ pString[i] >> 11 ] & ( 1 << ( ( pString[i] >> 8 ) & 7 ) ) ) ) pFont->queryMetricPage( pString[i] >> 8, m_pAtoms ); pArray[i].width = pArray[i].height = -1; if( pFont->m_pMetrics ) { int effectiveCode = pString[i]; effectiveCode |= bVertical ? 1 << 16 : 0; ::std::hash_map< int, CharacterMetric >::const_iterator it = pFont->m_pMetrics->m_aMetrics.find( effectiveCode ); // if no vertical metrics are available assume rotated horizontal metrics if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) ) it = pFont->m_pMetrics->m_aMetrics.find( pString[i] ); // the character metrics are in it->second if( it != pFont->m_pMetrics->m_aMetrics.end() ) pArray[ i ] = it->second; } } return true; } // ------------------------------------------------------------------------- bool PrintFontManager::getMetrics( fontID nFontID, sal_Unicode minCharacter, sal_Unicode maxCharacter, CharacterMetric* pArray, bool bVertical ) const { PrintFont* pFont = getFont( nFontID ); if( ! pFont ) return false; if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() ) { // might be a font not yet analyzed if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); else if( pFont->m_eType == fonttype::TrueType ) analyzeTrueTypeFile( pFont ); } sal_Unicode code = minCharacter; do { if( ! pFont->m_pMetrics || ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) ) pFont->queryMetricPage( code >> 8, m_pAtoms ); pArray[ code - minCharacter ].width = -1; pArray[ code - minCharacter ].height = -1; if( pFont->m_pMetrics ) { int effectiveCode = code; effectiveCode |= bVertical ? 1 << 16 : 0; ::std::hash_map< int, CharacterMetric >::const_iterator it = pFont->m_pMetrics->m_aMetrics.find( effectiveCode ); // if no vertical metrics are available assume rotated horizontal metrics if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) ) it = pFont->m_pMetrics->m_aMetrics.find( code ); // the character metrics are in it->second if( it != pFont->m_pMetrics->m_aMetrics.end() ) pArray[ code - minCharacter ] = it->second; } } while( code++ != maxCharacter ); return true; } // ------------------------------------------------------------------------- static bool createWriteablePath( const ByteString& rPath ) { bool bSuccess = false; if( access( rPath.GetBuffer(), W_OK ) ) { int nPos = rPath.SearchBackward( '/' ); if( nPos != STRING_NOTFOUND ) while( nPos > 0 && rPath.GetChar( nPos ) == '/' ) nPos--; if( nPos != STRING_NOTFOUND && nPos != 0 && createWriteablePath( rPath.Copy( 0, nPos+1 ) ) ) { bSuccess = mkdir( rPath.GetBuffer(), 0777 ) ? false : true; } } else bSuccess = true; return bSuccess; } // ------------------------------------------------------------------------- int PrintFontManager::importFonts( const ::std::list< OString >& rFiles, bool bLinkOnly, ImportFontCallback* pCallback ) { int nSuccess = 0; // find a directory with write access rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); bool bCanWrite = false; int nDirID = 0; INetURLObject aDir; for( ::std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin(); ! bCanWrite && dir_it != m_aPrivateFontDirectories.end(); ++dir_it ) { // check if we can create files in that directory ByteString aDirPath = getDirectory( *dir_it ); if( createWriteablePath( aDirPath ) ) { aDir = INetURLObject( OStringToOUString( aDirPath, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); nDirID = *dir_it; bCanWrite = true; } } if( bCanWrite ) { for( ::std::list< OString >::const_iterator font_it = rFiles.begin(); font_it != rFiles.end(); ++font_it ) { INetURLObject aFrom( OStringToOUString( *font_it, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); INetURLObject aTo( aDir ); aTo.Append( aFrom.GetName() ); if( pCallback ) pCallback->progress( aTo.PathToFileName() ); if( pCallback && pCallback->isCanceled() ) break; if( ! access( ByteString( String(aTo.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) { if( ! ( pCallback ? pCallback->queryOverwriteFile( aTo.PathToFileName() ) : false ) ) continue; } // look for afm if necessary OUString aAfmCopied; FileBase::RC nError; if( aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfa" ) || aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfb" ) ) { INetURLObject aFromAfm( aFrom ); aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) { aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) ); if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) { aFromAfm.removeSegment(); aFromAfm.Append( String( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); aFromAfm.Append( aTo.GetName() ); aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) { aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) ); if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) { // give up if( pCallback ) pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::NoAfmMetric ); continue; } } } } INetURLObject aToAfm( aTo ); aToAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); OUString aFromPath, aToPath; if( bLinkOnly ) { ByteString aLinkFromPath( String(aFromAfm.PathToFileName()), aEncoding ); ByteString aLinkToPath( String(aToAfm.PathToFileName()), aEncoding ); nError = (FileBase::RC)symlink( aLinkFromPath.GetBuffer(), aLinkToPath.GetBuffer() ); } else nError = File::copy( aFromAfm.GetMainURL(INetURLObject::DECODE_TO_IURI), aToAfm.GetMainURL(INetURLObject::DECODE_TO_IURI) ); if( nError ) { if( pCallback ) pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::AfmCopyFailed ); continue; } aAfmCopied = aToPath; } if( bLinkOnly ) { ByteString aFromPath( String(aFrom.PathToFileName()), aEncoding ); ByteString aToPath( String(aTo.PathToFileName()), aEncoding ); nError = (FileBase::RC)symlink( aFromPath.GetBuffer(), aToPath.GetBuffer() ); } else nError = File::copy( aFrom.GetMainURL(INetURLObject::DECODE_TO_IURI), aTo.GetMainURL(INetURLObject::DECODE_TO_IURI) ); // copy font file if( nError ) { if( aAfmCopied.getLength() ) File::remove( aAfmCopied ); if( pCallback ) pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::FontCopyFailed ); continue; } ::std::list< PrintFont* > aNewFonts; ::std::list< PrintFont* >::iterator it; if( analyzeFontFile( nDirID, OUStringToOString( aTo.GetName(), aEncoding ), ::std::list(), aNewFonts ) ) { // remove all fonts for the same file // discarding their font ids ::std::hash_map< fontID, PrintFont* >::iterator current, next; current = m_aFonts.begin(); OString aFileName( OUStringToOString( aTo.GetName(), aEncoding ) ); while( current != m_aFonts.end() ) { bool bRemove = false; switch( current->second->m_eType ) { case fonttype::Type1: if( static_cast(current->second)->m_aFontFile == aFileName ) bRemove = true; break; case fonttype::TrueType: if( static_cast(current->second)->m_aFontFile == aFileName ) bRemove = true; break; default: break; } if( bRemove ) { next = current; ++next; m_aFontFileToFontID[ aFileName ].erase( current->first ); delete current->second; m_aFonts.erase( current ); current = next; } else ++current; } DBG_ASSERT( !findFontFileID( nDirID, aFileName ), "not all fonts removed for file" ); nSuccess++; for( it = aNewFonts.begin(); it != aNewFonts.end(); ++it ) { m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID ); m_aFonts[ m_nNextFontID++ ] = *it; m_pFontCache->updateFontCacheEntry( *it, false ); } } } m_pFontCache->updateDirTimestamp( nDirID ); m_pFontCache->flush(); } else if( pCallback ) pCallback->importFontsFailed( ImportFontCallback::NoWritableDirectory ); return nSuccess; } // ------------------------------------------------------------------------- bool PrintFontManager::checkImportPossible() const { bool bSuccess = false; // find a directory with write access ByteString aDir; for( std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin(); dir_it != m_aPrivateFontDirectories.end(); ++dir_it ) { aDir = getDirectory( *dir_it ); if( createWriteablePath( aDir ) ) { bSuccess = true; break; } } #if OSL_DEBUG_LEVEL > 1 if( bSuccess ) fprintf( stderr, "found writable %s\n", aDir.GetBuffer() ); #endif return bSuccess; } // ------------------------------------------------------------------------- bool PrintFontManager::checkChangeFontPropertiesPossible( fontID /*nFontID*/ ) const { // since font properties are changed in the font cache file only nowadays // they can always be changed return true; } // ------------------------------------------------------------------------- bool PrintFontManager::changeFontProperties( fontID nFontID, const ::rtl::OUString& rXLFD ) { ByteString aXLFD( OUStringToOString( rXLFD, RTL_TEXTENCODING_UTF8 ) ); ByteString aAddStyle = aXLFD.GetToken( '-', 6 ); if( aAddStyle.Search( "utf8" ) == STRING_NOTFOUND ) { aAddStyle.Append( aAddStyle.Len() ? ";utf8" : "utf8" ); aXLFD.SetToken( 6, ';', aAddStyle ); } PrintFont* pFont = getFont( nFontID ); std::list< OString > aDummyList; aDummyList.push_back( aXLFD ); getFontAttributesFromXLFD( pFont, aDummyList ); pFont->m_bUserOverride = true; m_pFontCache->updateFontCacheEntry( pFont, true ); return true; } // ------------------------------------------------------------------------- bool PrintFontManager:: getImportableFontProperties( const OString& rFile, ::std::list< FastPrintFontInfo >& rFontProps ) { rFontProps.clear(); int nIndex = rFile.lastIndexOf( '/' ); OString aDir, aFile( rFile.copy( nIndex+1 ) ); if( nIndex != -1 ) aDir = rFile.copy( 0, nIndex ); int nDirID = getDirectoryAtom( aDir, true ); ::std::list< PrintFont* > aFonts; bool bRet = analyzeFontFile( nDirID, aFile, ::std::list(), aFonts ); while( aFonts.begin() != aFonts.end() ) { PrintFont* pFont = aFonts.front(); aFonts.pop_front(); FastPrintFontInfo aInfo; fillPrintFontInfo( pFont, aInfo ); rFontProps.push_back( aInfo ); delete pFont; } return bRet; } // ------------------------------------------------------------------------- bool PrintFontManager::getFileDuplicates( fontID nFont, ::std::list< fontID >& rFonts ) const { bool bRet = false; rFonts.clear(); PrintFont* pSearchFont = getFont( nFont ); if( ! pSearchFont || pSearchFont->m_eType != fonttype::TrueType || static_cast(pSearchFont)->m_nCollectionEntry == -1 ) return false; OString aFile( getFontFileSysPath( nFont ) ); if( ! aFile.getLength() ) return false; for( ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) { if( nFont != it->first ) { OString aCompFile( getFontFile( it->second ) ); if( aCompFile == aFile ) { rFonts.push_back( it->first ); bRet = true; } } } return bRet; } // ------------------------------------------------------------------------- bool PrintFontManager::removeFonts( const ::std::list< fontID >& rFonts ) { bool bRet = true; ::std::list< fontID > aDuplicates; for( ::std::list< fontID >::const_iterator it = rFonts.begin(); it != rFonts.end(); ++it ) { ::std::hash_map< fontID, PrintFont* >::const_iterator haveFont = m_aFonts.find( *it ); if( haveFont == m_aFonts.end() ) continue; PrintFont* pFont = haveFont->second; bool bRemoveDuplicates = getFileDuplicates( *it, aDuplicates ); ByteString aFile( getFontFile( pFont ) ); if( aFile.Len() ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "try unlink( \"%s\" ) ... ", aFile.GetBuffer() ); #endif if( unlink( aFile.GetBuffer() ) ) { bRet = false; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "failed\n" ); #endif continue; } #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "succeeded\n" ); #endif OString aAfm( getAfmFile( pFont ) ); if( aAfm.getLength() ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "unlink( \"%s\" )\n", aAfm.getStr() ); #endif unlink( aAfm.getStr() ); } m_aFonts.erase( *it ); delete pFont; if( bRemoveDuplicates ) { for( ::std::list< fontID >::iterator dup = aDuplicates.begin(); dup != aDuplicates.end(); ++dup ) { m_aFontFileToFontID[ aFile ].erase( *dup ); PrintFont* pDup = m_aFonts[ *dup ]; m_aFonts.erase( *dup ); delete pDup; } } } } return bRet; } // ------------------------------------------------------------------------- bool PrintFontManager::isPrivateFontFile( fontID nFont ) const { bool bRet = false; int nDirID = -1; PrintFont* pFont = getFont( nFont ); if( pFont ) { switch( pFont->m_eType ) { case fonttype::Type1: nDirID = static_cast< Type1FontFile* >(pFont)->m_nDirectory;break; case fonttype::TrueType: nDirID = static_cast< TrueTypeFontFile* >(pFont)->m_nDirectory;break; default: break; } } if( nDirID != -1 ) { for( ::std::list< int >::const_iterator it = m_aPrivateFontDirectories.begin(); it != m_aPrivateFontDirectories.end(); ++it ) { if( nDirID == *it ) { bRet = true; break; } } } return bRet; } // ------------------------------------------------------------------------- bool PrintFontManager::getAlternativeFamilyNames( fontID nFont, ::std::list< OUString >& rNames ) const { rNames.clear(); PrintFont* pFont = getFont( nFont ); if( pFont && pFont->m_eType == fonttype::TrueType ) { TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); ByteString aFile( getFontFile( pFont ) ); TrueTypeFont* pTTFont; if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) { NameRecord* pNameRecords = NULL; int nNameRecords = GetTTNameRecords( pTTFont, &pNameRecords ); for( int i = 0; i < nNameRecords; i++ ) { if( pNameRecords[i].nameID != 1 ) // family name continue; OUString aFamily( convertTrueTypeName( pNameRecords+i ) ); if( aFamily.getLength() && m_pAtoms->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ) != pFont->m_nFamilyName ) { rNames.push_back( aFamily ); } } if( nNameRecords ) DisposeNameRecords( pNameRecords, nNameRecords ); CloseTTFont( pTTFont ); } } return rNames.begin() != rNames.end(); } // ------------------------------------------------------------------------- // TODO: move most of this stuff into the central font-subsetting code bool PrintFontManager::createFontSubset( FontSubsetInfo& rInfo, fontID nFont, const OUString& rOutFile, sal_GlyphId* pGlyphIds, sal_uInt8* pNewEncoding, sal_Int32* pWidths, int nGlyphs, bool bVertical ) { PrintFont* pFont = getFont( nFont ); if( !pFont ) return false; switch( pFont->m_eType ) { case psp::fonttype::TrueType: rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; break; case psp::fonttype::Type1: rInfo.m_nFontType = FontSubsetInfo::ANY_TYPE1; break; default: return false; } // TODO: remove when Type1 subsetting gets implemented if( pFont->m_eType != fonttype::TrueType ) return false; // reshuffle array of requested glyphs to make sure glyph0==notdef sal_uInt8 pEnc[256]; sal_uInt16 pGID[256]; sal_uInt8 pOldIndex[256]; memset( pEnc, 0, sizeof( pEnc ) ); memset( pGID, 0, sizeof( pGID ) ); memset( pOldIndex, 0, sizeof( pOldIndex ) ); if( nGlyphs > 256 ) return false; int nChar = 1; for( int i = 0; i < nGlyphs; i++ ) { if( pNewEncoding[i] == 0 ) { pOldIndex[ 0 ] = i; } else { DBG_ASSERT( !(pGlyphIds[i] & 0x007f0000), "overlong glyph id" ); DBG_ASSERT( (int)pNewEncoding[i] < nGlyphs, "encoding wrong" ); DBG_ASSERT( pEnc[pNewEncoding[i]] == 0 && pGID[pNewEncoding[i]] == 0, "duplicate encoded glyph" ); pEnc[ pNewEncoding[i] ] = pNewEncoding[i]; pGID[ pNewEncoding[i] ] = (sal_uInt16)pGlyphIds[ i ]; pOldIndex[ pNewEncoding[i] ] = i; nChar++; } } nGlyphs = nChar; // either input value or increased by one // prepare system name for read access for subset source file // TODO: since this file is usually already mmapped there is no need to open it again const ByteString aFromFile = getFontFile( pFont ); TrueTypeFont* pTTFont = NULL; // TODO: rename to SfntFont TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK ) return false; // prepare system name for write access for subset file target OUString aSysPath; if( osl_File_E_None != osl_getSystemPathFromFileURL( rOutFile.pData, &aSysPath.pData ) ) return false; const rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); const ByteString aToFile( OUStringToOString( aSysPath, aEncoding ) ); // do CFF subsetting if possible int nCffLength = 0; const sal_uInt8* pCffBytes = NULL; if( GetSfntTable( pTTFont, O_CFF, &pCffBytes, &nCffLength ) ) { rInfo.LoadFont( FontSubsetInfo::CFF_FONT, pCffBytes, nCffLength ); #if 1 // TODO: remove 16bit->long conversion when related methods handle non-16bit glyphids sal_GlyphId aRequestedGlyphIds[256]; for( int i = 0; i < nGlyphs; ++i ) aRequestedGlyphIds[i] = pGID[i]; #endif // create subset file at requested path FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); // create font subset const char* pGlyphSetName = NULL; // TODO: better name? const bool bOK = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, pGlyphSetName, aRequestedGlyphIds, pEnc, nGlyphs, pWidths ); fclose( pOutFile ); // cleanup before early return CloseTTFont( pTTFont ); return bOK; } // do TTF->Type42 or Type3 subsetting // fill in font info psp::PrintFontInfo aFontInfo; if( ! getFontInfo( nFont, aFontInfo ) ) return false; rInfo.m_nAscent = aFontInfo.m_nAscend; rInfo.m_nDescent = aFontInfo.m_nDescend; rInfo.m_aPSName = getPSName( nFont ); int xMin, yMin, xMax, yMax; getFontBoundingBox( nFont, xMin, yMin, xMax, yMax ); rInfo.m_aFontBBox = Rectangle( Point( xMin, yMin ), Size( xMax-xMin, yMax-yMin ) ); rInfo.m_nCapHeight = yMax; // Well ... // fill in glyph advance widths TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont, pGID, nGlyphs, bVertical ? 1 : 0 ); if( pMetrics ) { for( int i = 0; i < nGlyphs; i++ ) pWidths[pOldIndex[i]] = pMetrics[i].adv; free( pMetrics ); } else { CloseTTFont( pTTFont ); return false; } bool bSuccess = ( SF_OK == CreateTTFromTTGlyphs( pTTFont, aToFile.GetBuffer(), pGID, pEnc, nGlyphs, 0, NULL, 0 ) ); CloseTTFont( pTTFont ); return bSuccess; } void PrintFontManager::getGlyphWidths( fontID nFont, bool bVertical, std::vector< sal_Int32 >& rWidths, std::map< sal_Unicode, sal_uInt32 >& rUnicodeEnc ) { PrintFont* pFont = getFont( nFont ); if( !pFont || (pFont->m_eType != fonttype::TrueType && pFont->m_eType != fonttype::Type1) ) return; if( pFont->m_eType == fonttype::TrueType ) { TrueTypeFont* pTTFont = NULL; TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); ByteString aFromFile = getFontFile( pFont ); if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK ) return; int nGlyphs = GetTTGlyphCount( pTTFont ); if( nGlyphs > 0 ) { rWidths.resize(nGlyphs); std::vector aGlyphIds(nGlyphs); for( int i = 0; i < nGlyphs; i++ ) aGlyphIds[i] = sal_uInt16(i); TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont, &aGlyphIds[0], nGlyphs, bVertical ? 1 : 0 ); if( pMetrics ) { for( int i = 0; i< nGlyphs; i++ ) rWidths[i] = pMetrics[i].adv; free( pMetrics ); rUnicodeEnc.clear(); } // fill the unicode map // TODO: isn't this map already available elsewhere in the fontmanager? const sal_uInt8* pCmapData = NULL; int nCmapSize = 0; if( GetSfntTable( pTTFont, O_cmap, &pCmapData, &nCmapSize ) ) { CmapResult aCmapResult; if( ParseCMAP( pCmapData, nCmapSize, aCmapResult ) ) { const ImplFontCharMap aCharMap( aCmapResult ); for( sal_uInt32 cOld = 0;;) { // get next unicode covered by font const sal_uInt32 c = aCharMap.GetNextChar( cOld ); if( c == cOld ) break; cOld = c; #if 1 // TODO: remove when sal_Unicode covers all of unicode if( c > (sal_Unicode)~0 ) break; #endif // get the matching glyph index const sal_GlyphId aGlyphId = aCharMap.GetGlyphIndex( c ); // update the requested map rUnicodeEnc[ (sal_Unicode)c ] = aGlyphId; } } } } CloseTTFont( pTTFont ); } else if( pFont->m_eType == fonttype::Type1 ) { if( ! pFont->m_aEncodingVector.size() ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true ); if( pFont->m_pMetrics ) { rUnicodeEnc.clear(); rWidths.clear(); rWidths.reserve( pFont->m_pMetrics->m_aMetrics.size() ); for( std::hash_map< int, CharacterMetric >::const_iterator it = pFont->m_pMetrics->m_aMetrics.begin(); it != pFont->m_pMetrics->m_aMetrics.end(); ++it ) { if( (it->first & 0x00010000) == 0 || bVertical ) { rUnicodeEnc[ sal_Unicode(it->first & 0x0000ffff) ] = sal_uInt32(rWidths.size()); rWidths.push_back( it->second.width ); } } } } } // ------------------------------------------------------------------------- const std::map< sal_Unicode, sal_Int32 >* PrintFontManager::getEncodingMap( fontID nFont, const std::map< sal_Unicode, rtl::OString >** pNonEncoded ) const { PrintFont* pFont = getFont( nFont ); if( !pFont || (pFont->m_eType != fonttype::Type1 && pFont->m_eType != fonttype::Builtin) ) return NULL; if( ! pFont->m_aEncodingVector.size() ) pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true ); if( pNonEncoded ) *pNonEncoded = pFont->m_aNonEncoded.size() ? &pFont->m_aNonEncoded : NULL; return pFont->m_aEncodingVector.size() ? &pFont->m_aEncodingVector : NULL; } // ------------------------------------------------------------------------- std::list< OString > PrintFontManager::getAdobeNameFromUnicode( sal_Unicode aChar ) const { std::pair< std::hash_multimap< sal_Unicode, rtl::OString >::const_iterator, std::hash_multimap< sal_Unicode, rtl::OString >::const_iterator > range = m_aUnicodeToAdobename.equal_range( aChar ); std::list< OString > aRet; for( ; range.first != range.second; ++range.first ) aRet.push_back( range.first->second ); if( aRet.begin() == aRet.end() && aChar != 0 ) { sal_Char aBuf[8]; sal_Int32 nChars = snprintf( (char*)aBuf, sizeof( aBuf ), "uni%.4hX", aChar ); aRet.push_back( OString( aBuf, nChars ) ); } return aRet; } // ------------------------------------------------------------------------- std::list< sal_Unicode > PrintFontManager::getUnicodeFromAdobeName( const rtl::OString& rName ) const { std::pair< std::hash_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator, std::hash_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator > range = m_aAdobenameToUnicode.equal_range( rName ); std::list< sal_Unicode > aRet; for( ; range.first != range.second; ++range.first ) aRet.push_back( range.first->second ); if( aRet.begin() == aRet.end() ) { if( rName.getLength() == 7 && rName.indexOf( "uni" ) == 0 ) { sal_Unicode aCode = (sal_Unicode)rName.copy( 3 ).toInt32( 16 ); aRet.push_back( aCode ); } } return aRet; } // ------------------------------------------------------------------------- namespace { OUString getString( const Any& rAny ) { OUString aStr; rAny >>= aStr; return aStr; } bool getBool( const Any& rAny ) { sal_Bool bBool = sal_False; rAny >>= bBool; return static_cast(bBool); } sal_Int32 getInt( const Any& rAny ) { sal_Int32 n = 0; rAny >>= n; return n; } } bool PrintFontManager::readOverrideMetrics() { if( ! m_aOverrideFonts.empty() ) return false; css::uno::Reference< XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); if( !xFact.is() ) return false; css::uno::Reference< XMaterialHolder > xMat( xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.psprint.CompatMetricOverride" ) ) ), UNO_QUERY ); if( !xMat.is() ) return false; Any aAny( xMat->getMaterial() ); Sequence< Any > aOverrideFonts; if( ! (aAny >>= aOverrideFonts ) ) return false; sal_Int32 nFonts = aOverrideFonts.getLength(); for( sal_Int32 i = 0; i < nFonts; i++ ) { Sequence< NamedValue > aMetrics; if( ! (aOverrideFonts.getConstArray()[i] >>= aMetrics) ) continue; BuiltinFont* pFont = new BuiltinFont(); pFont->m_nDirectory = 0; pFont->m_bUserOverride = false; pFont->m_pMetrics = new PrintFontMetrics; memset( pFont->m_pMetrics->m_aPages, 0xff, sizeof( pFont->m_pMetrics->m_aPages ) ); pFont->m_pMetrics->m_bKernPairsQueried = true; sal_Int32 nProps = aMetrics.getLength(); const NamedValue* pProps = aMetrics.getConstArray(); for( sal_Int32 n = 0; n < nProps; n++ ) { if( pProps[n].Name.equalsAscii( "FamilyName" ) ) pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, getString(pProps[n].Value), sal_True ); else if( pProps[n].Name.equalsAscii( "PSName" ) ) pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, getString(pProps[n].Value), sal_True ); else if( pProps[n].Name.equalsAscii( "StyleName" ) ) pFont->m_aStyleName = getString(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "Italic" ) ) pFont->m_eItalic = static_cast(getInt(pProps[n].Value)); else if( pProps[n].Name.equalsAscii( "Width" ) ) pFont->m_eWidth = static_cast(getInt(pProps[n].Value)); else if( pProps[n].Name.equalsAscii( "Weight" ) ) pFont->m_eWeight = static_cast(getInt(pProps[n].Value)); else if( pProps[n].Name.equalsAscii( "Pitch" ) ) pFont->m_ePitch = static_cast(getInt(pProps[n].Value)); else if( pProps[n].Name.equalsAscii( "Encoding" ) ) pFont->m_aEncoding = static_cast(getInt(pProps[n].Value)); else if( pProps[n].Name.equalsAscii( "FontEncodingOnly" ) ) pFont->m_bFontEncodingOnly = getBool(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "GlobalMetricXWidth" ) ) pFont->m_aGlobalMetricX.width = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "GlobalMetricXHeight" ) ) pFont->m_aGlobalMetricX.height = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "GlobalMetricYWidth" ) ) pFont->m_aGlobalMetricY.width = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "GlobalMetricYHeight" ) ) pFont->m_aGlobalMetricY.height = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "Ascend" ) ) pFont->m_nAscend = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "Descend" ) ) pFont->m_nDescend = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "Leading" ) ) pFont->m_nLeading = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "XMin" ) ) pFont->m_nXMin = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "YMin" ) ) pFont->m_nYMin = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "XMax" ) ) pFont->m_nXMax = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "YMax" ) ) pFont->m_nYMax = getInt(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "VerticalSubstitutes" ) ) pFont->m_bHaveVerticalSubstitutedGlyphs = getBool(pProps[n].Value); else if( pProps[n].Name.equalsAscii( "EncodingVector" ) ) { Sequence< NamedValue > aEncoding; pProps[n].Value >>= aEncoding; sal_Int32 nEnc = aEncoding.getLength(); const NamedValue* pEnc = aEncoding.getConstArray(); for( sal_Int32 m = 0; m < nEnc; m++ ) { sal_Unicode cCode = *pEnc[m].Name.getStr(); sal_Int32 nGlyph = getInt(pEnc[m].Value); pFont->m_aEncodingVector[ cCode ] = nGlyph; } } else if( pProps[n].Name.equalsAscii( "NonEncoded" ) ) { Sequence< NamedValue > aEncoding; pProps[n].Value >>= aEncoding; sal_Int32 nEnc = aEncoding.getLength(); const NamedValue* pEnc = aEncoding.getConstArray(); for( sal_Int32 m = 0; m < nEnc; m++ ) { sal_Unicode cCode = *pEnc[m].Name.getStr(); OUString aGlyphName( getString(pEnc[m].Value) ); pFont->m_aNonEncoded[ cCode ] = OUStringToOString(aGlyphName,RTL_TEXTENCODING_ASCII_US); } } else if( pProps[n].Name.equalsAscii( "CharacterMetrics" ) ) { // fill pFont->m_pMetrics->m_aMetrics // expect triples of int: int -> CharacterMetric.{ width, height } Sequence< sal_Int32 > aSeq; pProps[n].Value >>= aSeq; sal_Int32 nInts = aSeq.getLength(); const sal_Int32* pInts = aSeq.getConstArray(); for( sal_Int32 m = 0; m < nInts; m+=3 ) { pFont->m_pMetrics->m_aMetrics[ pInts[m] ].width = static_cast(pInts[m+1]); pFont->m_pMetrics->m_aMetrics[ pInts[m] ].height = static_cast(pInts[m+2]); } } else if( pProps[n].Name.equalsAscii( "XKernPairs" ) ) { // fill pFont->m_pMetrics->m_aXKernPairs // expection name: value: ((height << 16)| width) Sequence< NamedValue > aKern; pProps[n].Value >>= aKern; KernPair aPair; const NamedValue* pVals = aKern.getConstArray(); int nPairs = aKern.getLength(); for( int m = 0; m < nPairs; m++ ) { if( pVals[m].Name.getLength() == 2 ) { aPair.first = pVals[m].Name.getStr()[0]; aPair.second = pVals[m].Name.getStr()[1]; sal_Int32 nKern = getInt( pVals[m].Value ); aPair.kern_x = static_cast(nKern & 0xffff); aPair.kern_y = static_cast((sal_uInt32(nKern) >> 16) & 0xffff); pFont->m_pMetrics->m_aXKernPairs.push_back( aPair ); } } } } // sanity check if( pFont->m_nPSName && pFont->m_nFamilyName && ! pFont->m_pMetrics->m_aMetrics.empty() ) { m_aOverrideFonts.push_back( m_nNextFontID ); m_aFonts[ m_nNextFontID++ ] = pFont; } else { DBG_ASSERT( 0, "override font failed" ); delete pFont; } } return true; }