/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <memory>
#include <unistd.h>
#include <osl/thread.h>
#include <unx/fontmanager.hxx>
#include <impfontcharmap.hxx>
#include <unotools/syslocaleoptions.hxx>
#include <unx/gendata.hxx>
#include <unx/helper.hxx>
#include <vcl/fontcharmap.hxx>
#include <tools/urlobj.hxx>
#include <o3tl/string_view.hxx>
#include <osl/file.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/strbuf.hxx>
#include <sal/macros.h>
#include <sal/log.hxx>
#include <i18nlangtag/applelangid.hxx>
#include <sft.hxx>
#if OSL_DEBUG_LEVEL > 1
#include <sys/times.h>
#include <stdio.h>
#endif
#include <algorithm>
#include <set>
#ifdef CALLGRIND_COMPILE
#include <valgrind/callgrind.h>
#endif
#include <com/sun/star/beans/XMaterialHolder.hpp>
using namespace vcl;
using namespace psp;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
/*
* static helpers
*/
static sal_uInt16 getUInt16BE(
const sal_uInt8*& pBuffer )
{
sal_uInt16 nRet =
static_cast <sal_uInt16>(pBuffer[1]) |
(
static_cast <sal_uInt16>(pBuffer[0]) << 8);
pBuffer+=2;
return nRet;
}
/*
* PrintFont implementations
*/
PrintFontManager::PrintFont::PrintFont()
: m_nDirectory(0)
, m_nCollectionEntry(0)
, m_nVariationEntry(0)
{
}
/*
* one instance only
*/
PrintFontManager& PrintFontManager::get()
{
GenericUnixSalData*
const pSalData(GetGenericUnixSalData());
assert(pSalData);
return *pSalData->GetPrintFontManager();
}
/*
* the PrintFontManager
*/
PrintFontManager::PrintFontManager()
: m_nNextFontID( 1 )
, m_nNextDirAtom( 1 )
, m_aFontInstallerTimer(
"PrintFontManager m_aFontInstallerTimer" )
{
m_aFontInstallerTimer.SetInvokeHandler(LINK(
this , PrintFontManager, autoInstallFon
tLangSupport));
m_aFontInstallerTimer.SetTimeout(5000);
}
PrintFontManager::~PrintFontManager()
{
m_aFontInstallerTimer.Stop();
deinitFontconfig();
}
OString PrintFontManager::getDirectory( int nAtom ) const
{
std::unordered_map< int , OString >::const_iterator it( m_aAtomToDir.find( nAtom ) );
return it != m_aAtomToDir.end() ? it->second : OString();
}
int PrintFontManager::getDirectoryAtom( const OString& rDirectory )
{
int nAtom = 0;
std::unordered_map< OString, int >::const_iterator it
( m_aDirToAtom.find( rDirectory ) );
if ( it != m_aDirToAtom.end() )
nAtom = it->second;
else
{
nAtom = m_nNextDirAtom++;
m_aDirToAtom[ rDirectory ] = nAtom;
m_aAtomToDir[ nAtom ] = rDirectory;
}
return nAtom;
}
std::vector<fontID> PrintFontManager::addFontFile( std::u16string_view rFileUrl )
{
rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
INetURLObject aPath( rFileUrl );
OString aName(OUStringToOString(aPath.GetLastName(INetURLObject::DecodeMechanism::WithCharset, aEncoding), aEncoding));
OString aDir( OUStringToOString(
INetURLObject::decode( aPath.GetPath(), INetURLObject::DecodeMechanism::WithCharset, aEncoding ), aEncoding ) );
int nDirID = getDirectoryAtom( aDir );
std::vector<fontID> aFontIds = findFontFileIDs( nDirID, aName );
if ( aFontIds.empty() )
{
addFontconfigFile(OUStringToOString(aPath.GetFull(), osl_getThreadTextEncoding()));
std::vector<PrintFont> aNewFonts = analyzeFontFile(nDirID, aName);
for (auto & font : aNewFonts)
{
fontID nFontId = m_nNextFontID++;
m_aFonts[nFontId] = std::move(font);
m_aFontFileToFontID[ aName ].insert( nFontId );
aFontIds.push_back(nFontId);
}
}
return aFontIds;
}
std::vector<PrintFontManager::PrintFont> PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile) const
{
std::vector<PrintFontManager::PrintFont> aNewFonts;
OString aDir( getDirectory( nDirID ) );
OString aFullPath = aDir + "/" + rFontFile;
bool bSupported;
int nFD;
int n;
if (sscanf(aFullPath.getStr(), "/:FD:/%d%n" , &nFD, &n) == 1 && aFullPath.getStr()[n] == '\0' )
{
// Hack, pathname that actually means we will use a pre-opened file descriptor
bSupported = true ;
}
else
{
// #i1872# reject unreadable files
if ( access( aFullPath.getStr(), R_OK ) )
return aNewFonts;
bSupported = false ;
OString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) );
if ( aExt.equalsIgnoreAsciiCase("ttf" )
|| aExt.equalsIgnoreAsciiCase("ttc" )
|| aExt.equalsIgnoreAsciiCase("tte" ) // #i33947# for Gaiji support
|| aExt.equalsIgnoreAsciiCase("otf" ) ) // check for TTF- and PS-OpenType too
bSupported = true ;
}
if (bSupported)
{
// get number of ttc entries
int nLength = CountTTCFonts( aFullPath.getStr() );
if (nLength > 0)
{
SAL_INFO("vcl.fonts" , "ttc: " << aFullPath << " contains " << nLength << " fonts" );
for ( int i = 0; i < nLength; i++ )
{
PrintFont aFont;
aFont.m_nDirectory = nDirID;
aFont.m_aFontFile = rFontFile;
aFont.m_nCollectionEntry = i;
if (analyzeSfntFile(aFont))
aNewFonts.push_back(aFont);
}
}
else
{
PrintFont aFont;
aFont.m_nDirectory = nDirID;
aFont.m_aFontFile = rFontFile;
aFont.m_nCollectionEntry = 0;
// need to read the font anyway to get aliases inside the font file
if (analyzeSfntFile(aFont))
aNewFonts.push_back(aFont);
}
}
return aNewFonts;
}
fontID PrintFontManager::findFontFileID(int nDirID, const OString& rFontFile, int nFaceIndex, int nVariationIndex) const
{
fontID nID = 0;
auto set_it = m_aFontFileToFontID.find( rFontFile );
if ( set_it == m_aFontFileToFontID.end() )
return nID;
for (fontID elem : set_it->second)
{
auto it = m_aFonts.find(elem);
if ( it == m_aFonts.end() )
continue ;
const PrintFont& rFont = (*it).second;
if (rFont.m_nDirectory == nDirID &&
rFont.m_aFontFile == rFontFile &&
rFont.m_nCollectionEntry == nFaceIndex &&
rFont.m_nVariationEntry == nVariationIndex)
{
nID = it->first;
if (nID)
break ;
}
}
return nID;
}
std::vector<fontID> PrintFontManager::findFontFileIDs( int nDirID, const OString& rFontFile ) const
{
std::vector<fontID> aIds;
auto set_it = m_aFontFileToFontID.find( rFontFile );
if ( set_it == m_aFontFileToFontID.end() )
return aIds;
for (auto const & elem : set_it->second)
{
auto it = m_aFonts.find(elem);
if ( it == m_aFonts.end() )
continue ;
const PrintFont& rFont = (*it).second;
if (rFont.m_nDirectory == nDirID &&
rFont.m_aFontFile == rFontFile)
aIds.push_back(it->first);
}
return aIds;
}
namespace {
OUString convertSfntName( const NameRecord& rNameRecord )
{
OUString aValue;
if (
( rNameRecord.platformID == 3 && ( rNameRecord.encodingID == 0 || rNameRecord.encodingID == 1 ) ) // MS, Unicode
||
( rNameRecord.platformID == 0 ) // Apple, Unicode
)
{
OUStringBuffer aName( rNameRecord.sptr.size()/2 );
const sal_uInt8* pNameBuffer = rNameRecord.sptr.data();
for (size_t n = 0; n < rNameRecord.sptr.size()/2; n++ )
aName.append( static_cast <sal_Unicode>(getUInt16BE( pNameBuffer )) );
aValue = aName.makeStringAndClear();
}
else if ( rNameRecord.platformID == 3 )
{
if ( rNameRecord.encodingID >= 2 && rNameRecord.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 = rNameRecord.sptr.data();
for (size_t n = 0; n < rNameRecord.sptr.size()/2; n++ )
{
sal_Unicode aCode = static_cast <sal_Unicode>(getUInt16BE( pNameBuffer ));
char aChar = aCode >> 8;
if ( aChar )
aName.append( aChar );
aChar = aCode & 0x00ff;
if ( aChar )
aName.append( aChar );
}
switch ( rNameRecord.encodingID )
{
case 2:
aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_932 );
break ;
case 3:
aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_936 );
break ;
case 4:
aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_950 );
break ;
case 5:
aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_949 );
break ;
case 6:
aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_1361 );
break ;
}
}
}
else if ( rNameRecord.platformID == 1 )
{
std::string_view aName(reinterpret_cast <const char *>(rNameRecord.sptr.data()), rNameRecord.sptr.size());
rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW;
switch (rNameRecord.encodingID)
{
case 0:
eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
break ;
case 1:
eEncoding = RTL_TEXTENCODING_APPLE_JAPANESE;
break ;
case 2:
eEncoding = RTL_TEXTENCODING_APPLE_CHINTRAD;
break ;
case 3:
eEncoding = RTL_TEXTENCODING_APPLE_KOREAN;
break ;
case 4:
eEncoding = RTL_TEXTENCODING_APPLE_ARABIC;
break ;
case 5:
eEncoding = RTL_TEXTENCODING_APPLE_HEBREW;
break ;
case 6:
eEncoding = RTL_TEXTENCODING_APPLE_GREEK;
break ;
case 7:
eEncoding = RTL_TEXTENCODING_APPLE_CYRILLIC;
break ;
case 9:
eEncoding = RTL_TEXTENCODING_APPLE_DEVANAGARI;
break ;
case 10:
eEncoding = RTL_TEXTENCODING_APPLE_GURMUKHI;
break ;
case 11:
eEncoding = RTL_TEXTENCODING_APPLE_GUJARATI;
break ;
case 21:
eEncoding = RTL_TEXTENCODING_APPLE_THAI;
break ;
case 25:
eEncoding = RTL_TEXTENCODING_APPLE_CHINSIMP;
break ;
case 29:
eEncoding = RTL_TEXTENCODING_APPLE_CENTEURO;
break ;
case 32: //Uninterpreted
eEncoding = RTL_TEXTENCODING_UTF8;
break ;
default :
if (o3tl::starts_with(aName, "Khmer OS" ) || // encoding '20' (Khmer) isn't implemented
o3tl::starts_with(aName, "YoavKtav" )) // tdf#152278
{
eEncoding = RTL_TEXTENCODING_UTF8;
}
SAL_WARN_IF(eEncoding == RTL_TEXTENCODING_DONTKNOW, "vcl.fonts" , "mac encoding " <<
rNameRecord.encodingID << " in font '" << aName << "'" <<
(rNameRecord.encodingID > 32 ? " is invalid" : " has unimplemented conversion" ));
break ;
}
if (eEncoding != RTL_TEXTENCODING_DONTKNOW)
aValue = OStringToOUString(aName, eEncoding);
}
return aValue;
}
OUString analyzeSfntFamilyName(void const * pTTFont)
{
OUString aFamily;
std::vector<NameRecord> aNameRecords;
GetTTNameRecords( static_cast <TrueTypeFont const *>(pTTFont), aNameRecords );
if ( !aNameRecords.empty() )
{
LanguageTag aUILang(SvtSysLocaleOptions().GetRealUILanguageTag());
LanguageType eLang = aUILang.getLanguageType();
int nLastMatch = -1;
for ( size_t i = 0; i < aNameRecords.size(); i++ )
{
if ( aNameRecords[i].nameID != 1 || aNameRecords[i].sptr.empty() )
continue ;
int nMatch = -1;
if ( aNameRecords[i].platformID == 0 ) // Unicode
nMatch = 4000;
else if ( aNameRecords[i].platformID == 3 )
{
// this bases on the LanguageType actually being a Win LCID
if (aNameRecords[i].languageID == eLang)
nMatch = 8000;
else if ( aNameRecords[i].languageID == LANGUAGE_ENGLISH_US )
nMatch = 2000;
else if ( aNameRecords[i].languageID == LANGUAGE_ENGLISH ||
aNameRecords[i].languageID == LANGUAGE_ENGLISH_UK )
nMatch = 1500;
else
nMatch = 1000;
}
else if (aNameRecords[i].platformID == 1)
{
AppleLanguageId aAppleId = static_cast <AppleLanguageId>(static_cast <sal_uInt16>(aNameRecords[i].languageID));
LanguageTag aApple(makeLanguageTagFromAppleLanguageId(aAppleId));
if (aApple == aUILang)
nMatch = 8000;
else if (aAppleId == AppleLanguageId::ENGLISH)
nMatch = 2000;
else
nMatch = 1000;
}
OUString aName = convertSfntName( aNameRecords[i] );
if (!(aName.isEmpty()) && nMatch >= nLastMatch)
{
nLastMatch = nMatch;
aFamily = aName;
}
}
}
return aFamily;
}
}
bool PrintFontManager::analyzeSfntFile( PrintFont& rFont ) const
{
bool bSuccess = false ;
rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
OString aFile = getFontFile( rFont );
TrueTypeFont* pTTFont = nullptr;
auto & rDFA = rFont.m_aFontAttributes;
rDFA.SetQuality(512);
auto const e = OpenTTFontFile( aFile.getStr(), rFont.m_nCollectionEntry, &pTTFont );
if ( e == SFErrCodes::Ok )
{
TTGlobalFontInfo aInfo;
GetTTGlobalFontInfo( pTTFont, & aInfo );
if (rDFA.GetFamilyName().isEmpty())
{
OUString aFamily = analyzeSfntFamilyName(pTTFont);
if (aFamily.isEmpty())
{
// poor font does not have a family name
// name it to file name minus the extension
sal_Int32 dotIndex = rFont.m_aFontFile.lastIndexOf('.' );
if ( dotIndex == -1 )
dotIndex = rFont.m_aFontFile.getLength();
aFamily = OStringToOUString(rFont.m_aFontFile.subView(0, dotIndex), aEncoding);
}
rDFA.SetFamilyName(aFamily);
}
if ( !aInfo.usubfamily.isEmpty() )
rDFA.SetStyleName(aInfo.usubfamily);
rDFA.SetFamilyType(matchFamilyName(rDFA.GetFamilyName()));
switch ( aInfo.weight )
{
case FW_THIN: rDFA.SetWeight(WEIGHT_THIN); break ;
case FW_EXTRALIGHT: rDFA.SetWeight(WEIGHT_ULTRALIGHT); break ;
case FW_LIGHT: rDFA.SetWeight(WEIGHT_LIGHT); break ;
case FW_MEDIUM: rDFA.SetWeight(WEIGHT_MEDIUM); break ;
case FW_SEMIBOLD: rDFA.SetWeight(WEIGHT_SEMIBOLD); break ;
case FW_BOLD: rDFA.SetWeight(WEIGHT_BOLD); break ;
case FW_EXTRABOLD: rDFA.SetWeight(WEIGHT_ULTRABOLD); break ;
case FW_BLACK: rDFA.SetWeight(WEIGHT_BLACK); break ;
case FW_NORMAL:
default : rDFA.SetWeight(WEIGHT_NORMAL); break ;
}
switch ( aInfo.width )
{
case FWIDTH_ULTRA_CONDENSED: rDFA.SetWidthType(WIDTH_ULTRA_CONDENSED); break ;
case FWIDTH_EXTRA_CONDENSED: rDFA.SetWidthType(WIDTH_EXTRA_CONDENSED); break ;
case FWIDTH_CONDENSED: rDFA.SetWidthType(WIDTH_CONDENSED); break ;
case FWIDTH_SEMI_CONDENSED: rDFA.SetWidthType(WIDTH_SEMI_CONDENSED); break ;
case FWIDTH_SEMI_EXPANDED: rDFA.SetWidthType(WIDTH_SEMI_EXPANDED); break ;
case FWIDTH_EXPANDED: rDFA.SetWidthType(WIDTH_EXPANDED); break ;
case FWIDTH_EXTRA_EXPANDED: rDFA.SetWidthType(WIDTH_EXTRA_EXPANDED); break ;
case FWIDTH_ULTRA_EXPANDED: rDFA.SetWidthType(WIDTH_ULTRA_EXPANDED); break ;
case FWIDTH_NORMAL:
default : rDFA.SetWidthType(WIDTH_NORMAL); break ;
}
rDFA.SetPitch(aInfo.pitch ? PITCH_FIXED : PITCH_VARIABLE);
rDFA.SetItalic(aInfo.italicAngle == 0 ? ITALIC_NONE : (aInfo.italicAngle < 0 ? ITALIC_NORMAL : 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) )
rDFA.SetItalic(ITALIC_NORMAL);
rDFA.SetMicrosoftSymbolEncoded(aInfo.microsoftSymbolEncoded);
CloseTTFont( pTTFont );
bSuccess = true ;
}
else
SAL_WARN("vcl.fonts" , "Could not OpenTTFont \" " << aFile << " \": " << int (e));
return bSuccess;
}
void PrintFontManager::initialize()
{
#ifdef CALLGRIND_COMPILE
CALLGRIND_TOGGLE_COLLECT();
CALLGRIND_ZERO_STATS();
#endif
// initialize can be called more than once, e.g.
// gtk-fontconfig-timestamp changes to reflect new font installed and
// PrintFontManager::initialize called again
{
m_nNextFontID = 1;
m_aFonts.clear();
}
#if OSL_DEBUG_LEVEL > 1
clock_t aStart;
clock_t aStep1;
clock_t aStep2;
struct tms tms;
aStart = times( &tms );
#endif
// first try fontconfig
initFontconfig();
// part one - look for downloadable fonts
rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
const OUString &rSalPrivatePath = psp::getFontPath();
// search for the fonts in SAL_PRIVATE_FONTPATH first; those are
// the fonts installed with the office
if ( !rSalPrivatePath.isEmpty() )
{
OString aPath = OUStringToOString( rSalPrivatePath, aEncoding );
sal_Int32 nIndex = 0;
do
{
OString aToken = aPath.getToken( 0, ';' , nIndex );
normPath( aToken );
if (!aToken.isEmpty())
addFontconfigDir(aToken);
} while ( nIndex >= 0 );
}
countFontconfigFonts();
#if OSL_DEBUG_LEVEL > 1
aStep1 = times( &tms );
aStep2 = times( &tms );
SAL_INFO("vcl.fonts" , "PrintFontManager::initialize: collected "
<< m_aFonts.size()
<< " fonts." );
double fTick = (double )sysconf( _SC_CLK_TCK );
SAL_INFO("vcl.fonts" , "Step 1 took "
<< ((double )(aStep1 - aStart)/fTick)
<< " seconds." );
SAL_INFO("vcl.fonts" , "Step 2 took "
<< ((double )(aStep2 - aStep1)/fTick)
<< " seconds." );
#endif
#ifdef CALLGRIND_COMPILE
CALLGRIND_DUMP_STATS();
CALLGRIND_TOGGLE_COLLECT();
#endif
}
void PrintFontManager::getFontList( ::std::vector< fontID >& rFontIDs )
{
rFontIDs.clear();
for (auto const & font : m_aFonts)
rFontIDs.push_back(font.first);
}
int PrintFontManager::getFontFaceNumber( fontID nFontID ) const
{
int nRet = 0;
const PrintFont* pFont = getFont( nFontID );
if (pFont)
{
nRet = pFont->m_nCollectionEntry;
if (nRet < 0)
nRet = 0;
}
return nRet;
}
int PrintFontManager::getFontFaceVariation( fontID nFontID ) const
{
int nRet = 0;
const PrintFont* pFont = getFont( nFontID );
if (pFont)
{
nRet = pFont->m_nVariationEntry;
if (nRet < 0)
nRet = 0;
}
return nRet;
}
FontFamily PrintFontManager::matchFamilyName( std::u16string_view rFamily )
{
struct family_t {
const char * mpName;
sal_uInt16 mnLength;
FontFamily meType;
};
#define InitializeClass( p, a ) p, sizeof (p) - 1, a
static 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 ) }
};
OString aFamily = OUStringToOString( rFamily, RTL_TEXTENCODING_ASCII_US );
sal_uInt32 nLower = 0;
sal_uInt32 nUpper = SAL_N_ELEMENTS(pFamilyMatch);
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_DONTKNOW;
}
OString PrintFontManager::getFontFile(const PrintFont& rFont) const
{
std::unordered_map< int , OString >::const_iterator it = m_aAtomToDir.find(rFont.m_nDirectory);
assert(it != m_aAtomToDir.end());
OString aPath = it->second + "/" + rFont.m_aFontFile;
return aPath;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=90 H=91 G=90
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland