1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <osl/thread.h>
24 #include <unx/fontmanager.hxx>
25 #include <fontsubset.hxx>
26 #include <impfontcharmap.hxx>
27 #include <unx/gendata.hxx>
28 #include <unx/helper.hxx>
29 #include <vcl/fontcharmap.hxx>
31 #include <tools/urlobj.hxx>
33 #include <osl/file.hxx>
35 #include <rtl/ustrbuf.hxx>
36 #include <rtl/strbuf.hxx>
38 #include <sal/macros.h>
39 #include <sal/log.hxx>
41 #include <i18nlangtag/applelangid.hxx>
45 #if OSL_DEBUG_LEVEL > 1
46 #include <sys/times.h>
53 #ifdef CALLGRIND_COMPILE
54 #include <valgrind/callgrind.h>
57 #include <com/sun/star/beans/XMaterialHolder.hpp>
63 using namespace com::sun::star::uno
;
64 using namespace com::sun::star::beans
;
65 using namespace com::sun::star::lang
;
71 static sal_uInt16
getUInt16BE( const sal_uInt8
*& pBuffer
)
73 sal_uInt16 nRet
= static_cast<sal_uInt16
>(pBuffer
[1]) |
74 (static_cast<sal_uInt16
>(pBuffer
[0]) << 8);
80 * PrintFont implementations
82 PrintFontManager::PrintFont::PrintFont()
83 : m_eFamilyStyle(FAMILY_DONTKNOW
)
84 , m_eItalic(ITALIC_DONTKNOW
)
85 , m_eWidth(WIDTH_DONTKNOW
)
86 , m_eWeight(WEIGHT_DONTKNOW
)
87 , m_ePitch(PITCH_DONTKNOW
)
88 , m_aEncoding(RTL_TEXTENCODING_DONTKNOW
)
97 , m_nCollectionEntry(0)
98 , m_nVariationEntry(0)
105 PrintFontManager
& PrintFontManager::get()
107 GenericUnixSalData
* const pSalData(GetGenericUnixSalData());
109 return *pSalData
->GetPrintFontManager();
113 * the PrintFontManager
116 PrintFontManager::PrintFontManager()
118 , m_nNextDirAtom( 1 )
120 m_aFontInstallerTimer
.SetInvokeHandler(LINK(this, PrintFontManager
, autoInstallFontLangSupport
));
121 m_aFontInstallerTimer
.SetTimeout(5000);
124 PrintFontManager::~PrintFontManager()
126 m_aFontInstallerTimer
.Stop();
130 OString
PrintFontManager::getDirectory( int nAtom
) const
132 std::unordered_map
< int, OString
>::const_iterator
it( m_aAtomToDir
.find( nAtom
) );
133 return it
!= m_aAtomToDir
.end() ? it
->second
: OString();
136 int PrintFontManager::getDirectoryAtom( const OString
& rDirectory
)
139 std::unordered_map
< OString
, int >::const_iterator it
140 ( m_aDirToAtom
.find( rDirectory
) );
141 if( it
!= m_aDirToAtom
.end() )
145 nAtom
= m_nNextDirAtom
++;
146 m_aDirToAtom
[ rDirectory
] = nAtom
;
147 m_aAtomToDir
[ nAtom
] = rDirectory
;
152 std::vector
<fontID
> PrintFontManager::addFontFile( const OUString
& rFileUrl
)
154 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
155 INetURLObject
aPath( rFileUrl
);
156 OString
aName(OUStringToOString(aPath
.GetLastName(INetURLObject::DecodeMechanism::WithCharset
, aEncoding
), aEncoding
));
157 OString
aDir( OUStringToOString(
158 INetURLObject::decode( aPath
.GetPath(), INetURLObject::DecodeMechanism::WithCharset
, aEncoding
), aEncoding
) );
160 int nDirID
= getDirectoryAtom( aDir
);
161 std::vector
<fontID
> aFontIds
= findFontFileIDs( nDirID
, aName
);
162 if( aFontIds
.empty() )
164 std::vector
<std::unique_ptr
<PrintFont
>> aNewFonts
= analyzeFontFile(nDirID
, aName
);
165 for (auto & font
: aNewFonts
)
167 fontID nFontId
= m_nNextFontID
++;
168 m_aFonts
[nFontId
] = std::move(font
);
169 m_aFontFileToFontID
[ aName
].insert( nFontId
);
170 aFontIds
.push_back(nFontId
);
176 std::vector
<std::unique_ptr
<PrintFontManager::PrintFont
>> PrintFontManager::analyzeFontFile( int nDirID
, const OString
& rFontFile
, const char *pFormat
) const
178 std::vector
<std::unique_ptr
<PrintFontManager::PrintFont
>> aNewFonts
;
180 OString
aDir( getDirectory( nDirID
) );
182 OString aFullPath
= aDir
+ "/" + rFontFile
;
184 // #i1872# reject unreadable files
185 if( access( aFullPath
.getStr(), R_OK
) )
188 bool bSupported
= false;
191 if (!strcmp(pFormat
, "TrueType") ||
192 !strcmp(pFormat
, "CFF"))
197 OString
aExt( rFontFile
.copy( rFontFile
.lastIndexOf( '.' )+1 ) );
198 if( aExt
.equalsIgnoreAsciiCase("ttf")
199 || aExt
.equalsIgnoreAsciiCase("ttc")
200 || aExt
.equalsIgnoreAsciiCase("tte") // #i33947# for Gaiji support
201 || aExt
.equalsIgnoreAsciiCase("otf") ) // check for TTF- and PS-OpenType too
207 // get number of ttc entries
208 int nLength
= CountTTCFonts( aFullPath
.getStr() );
211 SAL_INFO("vcl.fonts", "ttc: " << aFullPath
<< " contains " << nLength
<< " fonts");
213 sal_uInt64 fileSize
= 0;
216 if (osl::File::getFileURLFromSystemPath(OStringToOUString(aFullPath
, osl_getThreadTextEncoding()),
217 aURL
) == osl::File::E_None
)
219 osl::File
aFile(aURL
);
220 if (aFile
.open(osl_File_OpenFlag_Read
| osl_File_OpenFlag_NoLock
) == osl::File::E_None
)
222 osl::DirectoryItem aItem
;
223 if (osl::DirectoryItem::get(aURL
, aItem
) == osl::File::E_None
)
225 osl::FileStatus
aFileStatus( osl_FileStatus_Mask_FileSize
);
226 if (aItem
.getFileStatus(aFileStatus
) == osl::File::E_None
)
227 fileSize
= aFileStatus
.getFileSize();
232 //Feel free to calc the exact max possible number of fonts a file
233 //could contain given its physical size. But this will clamp it to
234 //a sane starting point
235 //http://processingjs.nihongoresources.com/the_smallest_font/
236 //https://github.com/grzegorzrolek/null-ttf
237 const int nMaxFontsPossible
= fileSize
/ 528;
238 if (nLength
> nMaxFontsPossible
)
239 nLength
= nMaxFontsPossible
;
241 for( int i
= 0; i
< nLength
; i
++ )
243 std::unique_ptr
<PrintFont
> xFont(new PrintFont
);
244 xFont
->m_nDirectory
= nDirID
;
245 xFont
->m_aFontFile
= rFontFile
;
246 xFont
->m_nCollectionEntry
= i
;
247 if (analyzeSfntFile(xFont
.get()))
248 aNewFonts
.push_back(std::move(xFont
));
253 std::unique_ptr
<PrintFont
> xFont(new PrintFont
);
254 xFont
->m_nDirectory
= nDirID
;
255 xFont
->m_aFontFile
= rFontFile
;
256 xFont
->m_nCollectionEntry
= 0;
258 // need to read the font anyway to get aliases inside the font file
259 if (analyzeSfntFile(xFont
.get()))
260 aNewFonts
.push_back(std::move(xFont
));
266 fontID
PrintFontManager::findFontFileID(int nDirID
, const OString
& rFontFile
, int nFaceIndex
, int nVariationIndex
) const
270 std::unordered_map
< OString
, ::std::set
< fontID
> >::const_iterator set_it
= m_aFontFileToFontID
.find( rFontFile
);
271 if( set_it
== m_aFontFileToFontID
.end() )
274 for (auto const& elem
: set_it
->second
)
276 auto it
= m_aFonts
.find(elem
);
277 if( it
== m_aFonts
.end() )
279 PrintFont
* const pFont
= (*it
).second
.get();
280 if (pFont
->m_nDirectory
== nDirID
&&
281 pFont
->m_aFontFile
== rFontFile
&&
282 pFont
->m_nCollectionEntry
== nFaceIndex
&&
283 pFont
->m_nVariationEntry
== nVariationIndex
)
294 std::vector
<fontID
> PrintFontManager::findFontFileIDs( int nDirID
, const OString
& rFontFile
) const
296 std::vector
<fontID
> aIds
;
298 std::unordered_map
< OString
, ::std::set
< fontID
> >::const_iterator set_it
= m_aFontFileToFontID
.find( rFontFile
);
299 if( set_it
== m_aFontFileToFontID
.end() )
302 for (auto const& elem
: set_it
->second
)
304 auto it
= m_aFonts
.find(elem
);
305 if( it
== m_aFonts
.end() )
307 PrintFont
* const pFont
= (*it
).second
.get();
308 if (pFont
->m_nDirectory
== nDirID
&&
309 pFont
->m_aFontFile
== rFontFile
)
310 aIds
.push_back(it
->first
);
316 OUString
PrintFontManager::convertSfntName( void* pRecord
)
318 NameRecord
* pNameRecord
= static_cast<NameRecord
*>(pRecord
);
321 ( pNameRecord
->platformID
== 3 && ( pNameRecord
->encodingID
== 0 || pNameRecord
->encodingID
== 1 ) ) // MS, Unicode
323 ( pNameRecord
->platformID
== 0 ) // Apple, Unicode
326 OUStringBuffer
aName( pNameRecord
->slen
/2 );
327 const sal_uInt8
* pNameBuffer
= pNameRecord
->sptr
;
328 for(int n
= 0; n
< pNameRecord
->slen
/2; n
++ )
329 aName
.append( static_cast<sal_Unicode
>(getUInt16BE( pNameBuffer
)) );
330 aValue
= aName
.makeStringAndClear();
332 else if( pNameRecord
->platformID
== 3 )
334 if( pNameRecord
->encodingID
>= 2 && pNameRecord
->encodingID
<= 6 )
337 * and now for a special kind of madness:
338 * some fonts encode their byte value string as BE uint16
339 * (leading to stray zero bytes in the string)
340 * while others code two bytes as a uint16 and swap to BE
343 const sal_uInt8
* pNameBuffer
= pNameRecord
->sptr
;
344 for(int n
= 0; n
< pNameRecord
->slen
/2; n
++ )
346 sal_Unicode aCode
= static_cast<sal_Unicode
>(getUInt16BE( pNameBuffer
));
347 sal_Char aChar
= aCode
>> 8;
349 aName
.append( aChar
);
350 aChar
= aCode
& 0x00ff;
352 aName
.append( aChar
);
354 switch( pNameRecord
->encodingID
)
357 aValue
= OStringToOUString( aName
.makeStringAndClear(), RTL_TEXTENCODING_MS_932
);
360 aValue
= OStringToOUString( aName
.makeStringAndClear(), RTL_TEXTENCODING_MS_936
);
363 aValue
= OStringToOUString( aName
.makeStringAndClear(), RTL_TEXTENCODING_MS_950
);
366 aValue
= OStringToOUString( aName
.makeStringAndClear(), RTL_TEXTENCODING_MS_949
);
369 aValue
= OStringToOUString( aName
.makeStringAndClear(), RTL_TEXTENCODING_MS_1361
);
374 else if( pNameRecord
->platformID
== 1 )
376 OString
aName(reinterpret_cast<char*>(pNameRecord
->sptr
), pNameRecord
->slen
);
377 rtl_TextEncoding eEncoding
= RTL_TEXTENCODING_DONTKNOW
;
378 switch (pNameRecord
->encodingID
)
381 eEncoding
= RTL_TEXTENCODING_APPLE_ROMAN
;
384 eEncoding
= RTL_TEXTENCODING_APPLE_JAPANESE
;
387 eEncoding
= RTL_TEXTENCODING_APPLE_CHINTRAD
;
390 eEncoding
= RTL_TEXTENCODING_APPLE_KOREAN
;
393 eEncoding
= RTL_TEXTENCODING_APPLE_ARABIC
;
396 eEncoding
= RTL_TEXTENCODING_APPLE_HEBREW
;
399 eEncoding
= RTL_TEXTENCODING_APPLE_GREEK
;
402 eEncoding
= RTL_TEXTENCODING_APPLE_CYRILLIC
;
405 eEncoding
= RTL_TEXTENCODING_APPLE_DEVANAGARI
;
408 eEncoding
= RTL_TEXTENCODING_APPLE_GURMUKHI
;
411 eEncoding
= RTL_TEXTENCODING_APPLE_GUJARATI
;
414 eEncoding
= RTL_TEXTENCODING_APPLE_THAI
;
417 eEncoding
= RTL_TEXTENCODING_APPLE_CHINSIMP
;
420 eEncoding
= RTL_TEXTENCODING_APPLE_CENTEURO
;
422 case 32: //Uninterpreted
423 eEncoding
= RTL_TEXTENCODING_UTF8
;
426 if (aName
.startsWith("Khmer OS"))
427 eEncoding
= RTL_TEXTENCODING_UTF8
;
428 SAL_WARN_IF(eEncoding
== RTL_TEXTENCODING_DONTKNOW
, "vcl.fonts", "Unimplemented mac encoding " << pNameRecord
->encodingID
<< " to unicode conversion for fontname " << aName
);
431 if (eEncoding
!= RTL_TEXTENCODING_DONTKNOW
)
432 aValue
= OStringToOUString(aName
, eEncoding
);
438 //fdo#33349.There exists an archaic Berling Antiqua font which has a "Times New
439 //Roman" name field in it. We don't want the "Times New Roman" name to take
440 //precedence in this case. We take Berling Antiqua as a higher priority name,
441 //and erase the "Times New Roman" name
444 bool isBadTNR(const OUString
&rName
, ::std::set
< OUString
>& rSet
)
447 if ( rName
== "Berling Antiqua" )
449 ::std::set
< OUString
>::iterator aEnd
= rSet
.end();
450 ::std::set
< OUString
>::iterator aI
= rSet
.find("Times New Roman");
461 void PrintFontManager::analyzeSfntFamilyName( void const * pTTFont
, ::std::vector
< OUString
>& rNames
)
466 ::std::set
< OUString
> aSet
;
468 NameRecord
* pNameRecords
= nullptr;
469 int nNameRecords
= GetTTNameRecords( static_cast<TrueTypeFont
const *>(pTTFont
), &pNameRecords
);
470 if( nNameRecords
&& pNameRecords
)
472 LanguageTag
aSystem("");
473 LanguageType eLang
= aSystem
.getLanguageType();
475 for( int i
= 0; i
< nNameRecords
; i
++ )
477 if( pNameRecords
[i
].nameID
!= 1 || pNameRecords
[i
].sptr
== nullptr )
480 if( pNameRecords
[i
].platformID
== 0 ) // Unicode
482 else if( pNameRecords
[i
].platformID
== 3 )
484 // this bases on the LanguageType actually being a Win LCID
485 if (pNameRecords
[i
].languageID
== eLang
)
487 else if( pNameRecords
[i
].languageID
== LANGUAGE_ENGLISH_US
)
489 else if( pNameRecords
[i
].languageID
== LANGUAGE_ENGLISH
||
490 pNameRecords
[i
].languageID
== LANGUAGE_ENGLISH_UK
)
495 else if (pNameRecords
[i
].platformID
== 1)
497 AppleLanguageId aAppleId
= static_cast<AppleLanguageId
>(static_cast<sal_uInt16
>(pNameRecords
[i
].languageID
));
498 LanguageTag
aApple(makeLanguageTagFromAppleLanguageId(aAppleId
));
499 if (aApple
== aSystem
)
501 else if (aAppleId
== AppleLanguageId::ENGLISH
)
506 OUString aName
= convertSfntName( pNameRecords
+ i
);
507 aSet
.insert( aName
);
510 if( nMatch
> nLastMatch
|| isBadTNR(aName
, aSet
) )
517 DisposeNameRecords( pNameRecords
, nNameRecords
);
518 if( !aFamily
.isEmpty() )
520 rNames
.push_back( aFamily
);
521 for (auto const& elem
: aSet
)
522 if( elem
!= aFamily
)
523 rNames
.push_back(elem
);
527 bool PrintFontManager::analyzeSfntFile( PrintFont
* pFont
) const
529 bool bSuccess
= false;
530 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
531 OString aFile
= getFontFile( pFont
);
532 TrueTypeFont
* pTTFont
= nullptr;
534 auto const e
= OpenTTFontFile( aFile
.getStr(), pFont
->m_nCollectionEntry
, &pTTFont
);
535 if( e
== SFErrCodes::Ok
)
537 TTGlobalFontInfo aInfo
;
538 GetTTGlobalFontInfo( pTTFont
, & aInfo
);
540 ::std::vector
< OUString
> aNames
;
541 analyzeSfntFamilyName( pTTFont
, aNames
);
543 // set family name from XLFD if possible
544 if (pFont
->m_aFamilyName
.isEmpty())
546 if( !aNames
.empty() )
548 pFont
->m_aFamilyName
= aNames
.front();
549 aNames
.erase(aNames
.begin());
555 // poor font does not have a family name
556 // name it to file name minus the extension
557 dotIndex
= pFont
->m_aFontFile
.lastIndexOf( '.' );
558 if ( dotIndex
== -1 )
559 dotIndex
= pFont
->m_aFontFile
.getLength();
561 pFont
->m_aFamilyName
= OStringToOUString(pFont
->m_aFontFile
.copy(0, dotIndex
), aEncoding
);
564 for (auto const& aAlias
: aNames
)
566 if (!aAlias
.isEmpty())
568 if (pFont
->m_aFamilyName
!= aAlias
)
570 auto al_it
= std::find(pFont
->m_aAliases
.begin(), pFont
->m_aAliases
.end(), aAlias
);
571 if( al_it
== pFont
->m_aAliases
.end() )
572 pFont
->m_aAliases
.push_back(aAlias
);
577 if( aInfo
.usubfamily
)
578 pFont
->m_aStyleName
= OUString( aInfo
.usubfamily
);
580 SAL_WARN_IF( !aInfo
.psname
, "vcl.fonts", "No PostScript name in font:" << aFile
);
582 pFont
->m_aPSName
= aInfo
.psname
?
583 OUString(aInfo
.psname
, rtl_str_getLength(aInfo
.psname
), aEncoding
) :
584 pFont
->m_aFamilyName
; // poor font does not have a postscript name
586 pFont
->m_eFamilyStyle
= matchFamilyName(pFont
->m_aFamilyName
);
588 switch( aInfo
.weight
)
590 case FW_THIN
: pFont
->m_eWeight
= WEIGHT_THIN
; break;
591 case FW_EXTRALIGHT
: pFont
->m_eWeight
= WEIGHT_ULTRALIGHT
; break;
592 case FW_LIGHT
: pFont
->m_eWeight
= WEIGHT_LIGHT
; break;
593 case FW_MEDIUM
: pFont
->m_eWeight
= WEIGHT_MEDIUM
; break;
594 case FW_SEMIBOLD
: pFont
->m_eWeight
= WEIGHT_SEMIBOLD
; break;
595 case FW_BOLD
: pFont
->m_eWeight
= WEIGHT_BOLD
; break;
596 case FW_EXTRABOLD
: pFont
->m_eWeight
= WEIGHT_ULTRABOLD
; break;
597 case FW_BLACK
: pFont
->m_eWeight
= WEIGHT_BLACK
; break;
600 default: pFont
->m_eWeight
= WEIGHT_NORMAL
; break;
603 switch( aInfo
.width
)
605 case FWIDTH_ULTRA_CONDENSED
: pFont
->m_eWidth
= WIDTH_ULTRA_CONDENSED
; break;
606 case FWIDTH_EXTRA_CONDENSED
: pFont
->m_eWidth
= WIDTH_EXTRA_CONDENSED
; break;
607 case FWIDTH_CONDENSED
: pFont
->m_eWidth
= WIDTH_CONDENSED
; break;
608 case FWIDTH_SEMI_CONDENSED
: pFont
->m_eWidth
= WIDTH_SEMI_CONDENSED
; break;
609 case FWIDTH_SEMI_EXPANDED
: pFont
->m_eWidth
= WIDTH_SEMI_EXPANDED
; break;
610 case FWIDTH_EXPANDED
: pFont
->m_eWidth
= WIDTH_EXPANDED
; break;
611 case FWIDTH_EXTRA_EXPANDED
: pFont
->m_eWidth
= WIDTH_EXTRA_EXPANDED
; break;
612 case FWIDTH_ULTRA_EXPANDED
: pFont
->m_eWidth
= WIDTH_ULTRA_EXPANDED
; break;
615 default: pFont
->m_eWidth
= WIDTH_NORMAL
; break;
618 pFont
->m_ePitch
= aInfo
.pitch
? PITCH_FIXED
: PITCH_VARIABLE
;
619 pFont
->m_eItalic
= aInfo
.italicAngle
== 0 ? ITALIC_NONE
: ( aInfo
.italicAngle
< 0 ? ITALIC_NORMAL
: ITALIC_OBLIQUE
);
620 // #104264# there are fonts that set italic angle 0 although they are
621 // italic; use macstyle bit here
622 if( aInfo
.italicAngle
== 0 && (aInfo
.macStyle
& 2) )
623 pFont
->m_eItalic
= ITALIC_NORMAL
;
625 pFont
->m_aEncoding
= aInfo
.symbolEncoded
? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UCS2
;
627 if( aInfo
.ascender
&& aInfo
.descender
)
629 pFont
->m_nLeading
= aInfo
.linegap
;
630 pFont
->m_nAscend
= aInfo
.ascender
;
631 pFont
->m_nDescend
= -aInfo
.descender
;
633 else if( aInfo
.typoAscender
&& aInfo
.typoDescender
)
635 pFont
->m_nLeading
= aInfo
.typoLineGap
;
636 pFont
->m_nAscend
= aInfo
.typoAscender
;
637 pFont
->m_nDescend
= -aInfo
.typoDescender
;
639 else if( aInfo
.winAscent
&& aInfo
.winDescent
)
641 pFont
->m_nAscend
= aInfo
.winAscent
;
642 pFont
->m_nDescend
= aInfo
.winDescent
;
643 pFont
->m_nLeading
= pFont
->m_nAscend
+ pFont
->m_nDescend
- 1000;
646 // last try: font bounding box
647 if( pFont
->m_nAscend
== 0 )
648 pFont
->m_nAscend
= aInfo
.yMax
;
649 if( pFont
->m_nDescend
== 0 )
650 pFont
->m_nDescend
= -aInfo
.yMin
;
651 if( pFont
->m_nLeading
== 0 )
652 pFont
->m_nLeading
= 15 * (pFont
->m_nAscend
+pFont
->m_nDescend
) / 100;
655 pFont
->m_nXMin
= aInfo
.xMin
;
656 pFont
->m_nYMin
= aInfo
.yMin
;
657 pFont
->m_nXMax
= aInfo
.xMax
;
658 pFont
->m_nYMax
= aInfo
.yMax
;
660 CloseTTFont( pTTFont
);
664 SAL_WARN("vcl.fonts", "Could not OpenTTFont \"" << aFile
<< "\": " << int(e
));
669 void PrintFontManager::initialize()
671 #ifdef CALLGRIND_COMPILE
672 CALLGRIND_TOGGLE_COLLECT();
673 CALLGRIND_ZERO_STATS();
676 // initialize can be called more than once, e.g.
677 // gtk-fontconfig-timestamp changes to reflect new font installed and
678 // PrintFontManager::initialize called again
684 #if OSL_DEBUG_LEVEL > 1
691 aStart
= times( &tms
);
694 // first try fontconfig
697 // part one - look for downloadable fonts
698 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
699 const OUString
&rSalPrivatePath
= psp::getFontPath();
701 // search for the fonts in SAL_PRIVATE_FONTPATH first; those are
702 // the fonts installed with the office
703 if( !rSalPrivatePath
.isEmpty() )
705 OString aPath
= OUStringToOString( rSalPrivatePath
, aEncoding
);
706 sal_Int32 nIndex
= 0;
709 OString aToken
= aPath
.getToken( 0, ';', nIndex
);
711 if (!aToken
.isEmpty())
712 addFontconfigDir(aToken
);
713 } while( nIndex
>= 0 );
716 // protect against duplicate paths
717 std::unordered_map
< OString
, int > visited_dirs
;
719 // Don't search directories that fontconfig already did
720 countFontconfigFonts( visited_dirs
);
722 #if OSL_DEBUG_LEVEL > 1
723 aStep1
= times( &tms
);
726 #if OSL_DEBUG_LEVEL > 1
727 aStep2
= times( &tms
);
728 fprintf( stderr
, "PrintFontManager::initialize: collected %" SAL_PRI_SIZET
"u fonts\n", m_aFonts
.size() );
729 double fTick
= (double)sysconf( _SC_CLK_TCK
);
730 fprintf( stderr
, "Step 1 took %lf seconds\n", (double)(aStep1
- aStart
)/fTick
);
731 fprintf( stderr
, "Step 2 took %lf seconds\n", (double)(aStep2
- aStep1
)/fTick
);
734 #ifdef CALLGRIND_COMPILE
735 CALLGRIND_DUMP_STATS();
736 CALLGRIND_TOGGLE_COLLECT();
740 void PrintFontManager::getFontList( ::std::vector
< fontID
>& rFontIDs
)
744 for (auto const& font
: m_aFonts
)
745 rFontIDs
.push_back(font
.first
);
748 void PrintFontManager::fillPrintFontInfo(PrintFont
* pFont
, FastPrintFontInfo
& rInfo
)
750 rInfo
.m_aFamilyName
= pFont
->m_aFamilyName
;
751 rInfo
.m_aStyleName
= pFont
->m_aStyleName
;
752 rInfo
.m_eFamilyStyle
= pFont
->m_eFamilyStyle
;
753 rInfo
.m_eItalic
= pFont
->m_eItalic
;
754 rInfo
.m_eWidth
= pFont
->m_eWidth
;
755 rInfo
.m_eWeight
= pFont
->m_eWeight
;
756 rInfo
.m_ePitch
= pFont
->m_ePitch
;
757 rInfo
.m_aEncoding
= pFont
->m_aEncoding
;
759 rInfo
.m_aAliases
.clear();
760 for (auto const& aAlias
: pFont
->m_aAliases
)
761 rInfo
.m_aAliases
.push_back(aAlias
);
764 void PrintFontManager::fillPrintFontInfo( PrintFont
* pFont
, PrintFontInfo
& rInfo
) const
766 if (pFont
->m_nAscend
== 0 && pFont
->m_nDescend
== 0)
768 analyzeSfntFile(pFont
);
771 fillPrintFontInfo( pFont
, static_cast< FastPrintFontInfo
& >( rInfo
) );
773 rInfo
.m_nAscend
= pFont
->m_nAscend
;
774 rInfo
.m_nDescend
= pFont
->m_nDescend
;
777 bool PrintFontManager::getFontInfo( fontID nFontID
, PrintFontInfo
& rInfo
) const
779 PrintFont
* pFont
= getFont( nFontID
);
782 rInfo
.m_nID
= nFontID
;
783 fillPrintFontInfo( pFont
, rInfo
);
785 return pFont
!= nullptr;
788 bool PrintFontManager::getFontFastInfo( fontID nFontID
, FastPrintFontInfo
& rInfo
) const
790 PrintFont
* pFont
= getFont( nFontID
);
793 rInfo
.m_nID
= nFontID
;
794 fillPrintFontInfo( pFont
, rInfo
);
796 return pFont
!= nullptr;
799 void PrintFontManager::getFontBoundingBox( fontID nFontID
, int& xMin
, int& yMin
, int& xMax
, int& yMax
)
801 PrintFont
* pFont
= getFont( nFontID
);
804 if( pFont
->m_nXMin
== 0 && pFont
->m_nYMin
== 0 && pFont
->m_nXMax
== 0 && pFont
->m_nYMax
== 0 )
806 analyzeSfntFile(pFont
);
808 xMin
= pFont
->m_nXMin
;
809 yMin
= pFont
->m_nYMin
;
810 xMax
= pFont
->m_nXMax
;
811 yMax
= pFont
->m_nYMax
;
815 int PrintFontManager::getFontFaceNumber( fontID nFontID
) const
818 PrintFont
* pFont
= getFont( nFontID
);
821 nRet
= pFont
->m_nCollectionEntry
;
828 int PrintFontManager::getFontFaceVariation( fontID nFontID
) const
831 PrintFont
* pFont
= getFont( nFontID
);
834 nRet
= pFont
->m_nVariationEntry
;
841 FontFamily
PrintFontManager::matchFamilyName( const OUString
& rFamily
)
845 sal_uInt16
const mnLength
;
846 FontFamily
const meType
;
849 #define InitializeClass( p, a ) p, sizeof(p) - 1, a
850 static const family_t pFamilyMatch
[] = {
851 { InitializeClass( "arial", FAMILY_SWISS
) },
852 { InitializeClass( "arioso", FAMILY_SCRIPT
) },
853 { InitializeClass( "avant garde", FAMILY_SWISS
) },
854 { InitializeClass( "avantgarde", FAMILY_SWISS
) },
855 { InitializeClass( "bembo", FAMILY_ROMAN
) },
856 { InitializeClass( "bookman", FAMILY_ROMAN
) },
857 { InitializeClass( "conga", FAMILY_ROMAN
) },
858 { InitializeClass( "courier", FAMILY_MODERN
) },
859 { InitializeClass( "curl", FAMILY_SCRIPT
) },
860 { InitializeClass( "fixed", FAMILY_MODERN
) },
861 { InitializeClass( "gill", FAMILY_SWISS
) },
862 { InitializeClass( "helmet", FAMILY_MODERN
) },
863 { InitializeClass( "helvetica", FAMILY_SWISS
) },
864 { InitializeClass( "international", FAMILY_MODERN
) },
865 { InitializeClass( "lucida", FAMILY_SWISS
) },
866 { InitializeClass( "new century schoolbook", FAMILY_ROMAN
) },
867 { InitializeClass( "palatino", FAMILY_ROMAN
) },
868 { InitializeClass( "roman", FAMILY_ROMAN
) },
869 { InitializeClass( "sans serif", FAMILY_SWISS
) },
870 { InitializeClass( "sansserif", FAMILY_SWISS
) },
871 { InitializeClass( "serf", FAMILY_ROMAN
) },
872 { InitializeClass( "serif", FAMILY_ROMAN
) },
873 { InitializeClass( "times", FAMILY_ROMAN
) },
874 { InitializeClass( "utopia", FAMILY_ROMAN
) },
875 { InitializeClass( "zapf chancery", FAMILY_SCRIPT
) },
876 { InitializeClass( "zapfchancery", FAMILY_SCRIPT
) }
879 OString aFamily
= OUStringToOString( rFamily
, RTL_TEXTENCODING_ASCII_US
);
880 sal_uInt32 nLower
= 0;
881 sal_uInt32 nUpper
= SAL_N_ELEMENTS(pFamilyMatch
);
883 while( nLower
< nUpper
)
885 sal_uInt32 nCurrent
= (nLower
+ nUpper
) / 2;
886 const family_t
* pHaystack
= pFamilyMatch
+ nCurrent
;
887 sal_Int32 nComparison
=
888 rtl_str_compareIgnoreAsciiCase_WithLength
890 aFamily
.getStr(), aFamily
.getLength(),
891 pHaystack
->mpName
, pHaystack
->mnLength
894 if( nComparison
< 0 )
897 if( nComparison
> 0 )
898 nLower
= nCurrent
+ 1;
900 return pHaystack
->meType
;
903 return FAMILY_DONTKNOW
;
906 OString
PrintFontManager::getFontFile(const PrintFont
* pFont
) const
912 std::unordered_map
< int, OString
>::const_iterator it
= m_aAtomToDir
.find(pFont
->m_nDirectory
);
913 aPath
= it
->second
+ "/" + pFont
->m_aFontFile
;
918 OUString
PrintFontManager::getPSName( fontID nFontID
) const
920 PrintFont
* pFont
= getFont( nFontID
);
921 if (pFont
&& pFont
->m_aPSName
.isEmpty())
923 analyzeSfntFile(pFont
);
926 return pFont
? pFont
->m_aPSName
: OUString();
929 int PrintFontManager::getFontAscend( fontID nFontID
) const
931 PrintFont
* pFont
= getFont( nFontID
);
932 if (pFont
&& pFont
->m_nAscend
== 0 && pFont
->m_nDescend
== 0)
934 analyzeSfntFile(pFont
);
936 return pFont
? pFont
->m_nAscend
: 0;
939 int PrintFontManager::getFontDescend( fontID nFontID
) const
941 PrintFont
* pFont
= getFont( nFontID
);
942 if (pFont
&& pFont
->m_nAscend
== 0 && pFont
->m_nDescend
== 0)
944 analyzeSfntFile(pFont
);
946 return pFont
? pFont
->m_nDescend
: 0;
949 // TODO: move most of this stuff into the central font-subsetting code
950 bool PrintFontManager::createFontSubset(
951 FontSubsetInfo
& rInfo
,
953 const OUString
& rOutFile
,
954 const sal_GlyphId
* pGlyphIds
,
955 const sal_uInt8
* pNewEncoding
,
960 PrintFont
* pFont
= getFont( nFont
);
964 rInfo
.m_nFontType
= FontType::SFNT_TTF
;
966 // reshuffle array of requested glyphs to make sure glyph0==notdef
968 sal_uInt16 pGID
[256];
969 sal_uInt8 pOldIndex
[256];
970 memset( pEnc
, 0, sizeof( pEnc
) );
971 memset( pGID
, 0, sizeof( pGID
) );
972 memset( pOldIndex
, 0, sizeof( pOldIndex
) );
976 for( int i
= 0; i
< nGlyphs
; i
++ )
978 if( pNewEncoding
[i
] == 0 )
984 SAL_WARN_IF( (pGlyphIds
[i
] & 0x007f0000), "vcl.fonts", "overlong glyph id" );
985 SAL_WARN_IF( static_cast<int>(pNewEncoding
[i
]) >= nGlyphs
, "vcl.fonts", "encoding wrong" );
986 SAL_WARN_IF( pEnc
[pNewEncoding
[i
]] != 0 || pGID
[pNewEncoding
[i
]] != 0, "vcl.fonts", "duplicate encoded glyph" );
987 pEnc
[ pNewEncoding
[i
] ] = pNewEncoding
[i
];
988 pGID
[ pNewEncoding
[i
] ] = static_cast<sal_uInt16
>(pGlyphIds
[ i
]);
989 pOldIndex
[ pNewEncoding
[i
] ] = i
;
993 nGlyphs
= nChar
; // either input value or increased by one
995 // prepare system name for read access for subset source file
996 // TODO: since this file is usually already mmapped there is no need to open it again
997 const OString aFromFile
= getFontFile( pFont
);
999 TrueTypeFont
* pTTFont
= nullptr; // TODO: rename to SfntFont
1000 if( OpenTTFontFile( aFromFile
.getStr(), pFont
->m_nCollectionEntry
, &pTTFont
) != SFErrCodes::Ok
)
1003 // prepare system name for write access for subset file target
1005 if( osl_File_E_None
!= osl_getSystemPathFromFileURL( rOutFile
.pData
, &aSysPath
.pData
) )
1007 const rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
1008 const OString
aToFile( OUStringToOString( aSysPath
, aEncoding
) );
1010 // do CFF subsetting if possible
1012 const sal_uInt8
* pCffBytes
= nullptr;
1013 if( GetSfntTable( pTTFont
, O_CFF
, &pCffBytes
, &nCffLength
) )
1015 rInfo
.LoadFont( FontType::CFF_FONT
, pCffBytes
, nCffLength
);
1016 #if 1 // TODO: remove 16bit->long conversion when related methods handle non-16bit glyphids
1017 sal_GlyphId aRequestedGlyphIds
[256];
1018 for( int i
= 0; i
< nGlyphs
; ++i
)
1019 aRequestedGlyphIds
[i
] = pGID
[i
];
1021 // create subset file at requested path
1022 FILE* pOutFile
= fopen( aToFile
.getStr(), "wb" );
1025 CloseTTFont( pTTFont
);
1028 // create font subset
1029 const char* const pGlyphSetName
= nullptr; // TODO: better name?
1030 const bool bOK
= rInfo
.CreateFontSubset(
1031 FontType::TYPE1_PFB
,
1032 pOutFile
, pGlyphSetName
,
1033 aRequestedGlyphIds
, pEnc
, nGlyphs
, pWidths
);
1035 // For OTC, values from hhea or OS2 are better
1036 psp::PrintFontInfo aFontInfo
;
1037 if( getFontInfo( nFont
, aFontInfo
) )
1039 rInfo
.m_nAscent
= aFontInfo
.m_nAscend
;
1040 rInfo
.m_nDescent
= -aFontInfo
.m_nDescend
;
1042 // cleanup before early return
1043 CloseTTFont( pTTFont
);
1047 // do TTF->Type42 or Type3 subsetting
1048 // fill in font info
1049 psp::PrintFontInfo aFontInfo
;
1050 if( ! getFontInfo( nFont
, aFontInfo
) )
1053 rInfo
.m_nAscent
= aFontInfo
.m_nAscend
;
1054 rInfo
.m_nDescent
= aFontInfo
.m_nDescend
;
1055 rInfo
.m_aPSName
= getPSName( nFont
);
1057 int xMin
, yMin
, xMax
, yMax
;
1058 getFontBoundingBox( nFont
, xMin
, yMin
, xMax
, yMax
);
1059 rInfo
.m_aFontBBox
= tools::Rectangle( Point( xMin
, yMin
), Size( xMax
-xMin
, yMax
-yMin
) );
1060 rInfo
.m_nCapHeight
= yMax
; // Well ...
1062 // fill in glyph advance widths
1063 std::unique_ptr
<sal_uInt16
[]> pMetrics
= GetTTSimpleGlyphMetrics( pTTFont
,
1066 false/*bVertical*/ );
1069 for( int i
= 0; i
< nGlyphs
; i
++ )
1070 pWidths
[pOldIndex
[i
]] = pMetrics
[i
];
1075 CloseTTFont( pTTFont
);
1079 bool bSuccess
= ( SFErrCodes::Ok
== CreateTTFromTTGlyphs( pTTFont
,
1084 CloseTTFont( pTTFont
);
1089 void PrintFontManager::getGlyphWidths( fontID nFont
,
1091 std::vector
< sal_Int32
>& rWidths
,
1092 std::map
< sal_Unicode
, sal_uInt32
>& rUnicodeEnc
)
1094 PrintFont
* pFont
= getFont( nFont
);
1097 TrueTypeFont
* pTTFont
= nullptr;
1098 OString aFromFile
= getFontFile( pFont
);
1099 if( OpenTTFontFile( aFromFile
.getStr(), pFont
->m_nCollectionEntry
, &pTTFont
) != SFErrCodes::Ok
)
1101 int nGlyphs
= GetTTGlyphCount(pTTFont
);
1104 rWidths
.resize(nGlyphs
);
1105 std::vector
<sal_uInt16
> aGlyphIds(nGlyphs
);
1106 for (int i
= 0; i
< nGlyphs
; i
++)
1107 aGlyphIds
[i
] = sal_uInt16(i
);
1108 std::unique_ptr
<sal_uInt16
[]> pMetrics
= GetTTSimpleGlyphMetrics(pTTFont
,
1114 for (int i
= 0; i
< nGlyphs
; i
++)
1115 rWidths
[i
] = pMetrics
[i
];
1117 rUnicodeEnc
.clear();
1120 // fill the unicode map
1121 // TODO: isn't this map already available elsewhere in the fontmanager?
1122 const sal_uInt8
* pCmapData
= nullptr;
1124 if (GetSfntTable(pTTFont
, O_cmap
, &pCmapData
, &nCmapSize
))
1126 CmapResult aCmapResult
;
1127 if (ParseCMAP(pCmapData
, nCmapSize
, aCmapResult
))
1129 FontCharMapRef
xFontCharMap(new FontCharMap(aCmapResult
));
1130 for (sal_uInt32 cOld
= 0;;)
1132 // get next unicode covered by font
1133 const sal_uInt32 c
= xFontCharMap
->GetNextChar(cOld
);
1137 #if 1 // TODO: remove when sal_Unicode covers all of unicode
1138 if (c
> sal_Unicode(~0))
1141 // get the matching glyph index
1142 const sal_GlyphId aGlyphId
= xFontCharMap
->GetGlyphIndex(c
);
1143 // update the requested map
1144 rUnicodeEnc
[static_cast<sal_Unicode
>(c
)] = aGlyphId
;
1149 CloseTTFont(pTTFont
);
1152 /// used by online unit tests via dlopen.
1154 SAL_DLLPUBLIC_EXPORT
const char * unit_online_get_fonts(void)
1156 std::vector
< fontID
> aFontIDs
;
1157 PrintFontManager
&rMgr
= PrintFontManager::get();
1158 rMgr
.getFontList(aFontIDs
);
1160 aBuf
.append( static_cast<sal_Int32
>(aFontIDs
.size()) );
1161 aBuf
.append( " PS fonts.\n" );
1162 for( auto nId
: aFontIDs
)
1164 const OUString
& rName
= rMgr
.getPSName( nId
);
1165 aBuf
.append( OUStringToOString( rName
, RTL_TEXTENCODING_UTF8
) );
1166 aBuf
.append( "\n" );
1168 static OString aResult
= aBuf
.makeStringAndClear();
1169 return aResult
.getStr();
1173 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */