1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <memory>
21 #include <unistd.h>
22 #include <osl/thread.h>
23 
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>
30 
31 #include <tools/urlobj.hxx>
32 
33 #include <osl/file.hxx>
34 
35 #include <rtl/ustrbuf.hxx>
36 #include <rtl/strbuf.hxx>
37 
38 #include <sal/macros.h>
39 #include <sal/log.hxx>
40 
41 #include <i18nlangtag/applelangid.hxx>
42 
43 #include <sft.hxx>
44 
45 #if OSL_DEBUG_LEVEL > 1
46 #include <sys/times.h>
47 #include <stdio.h>
48 #endif
49 
50 #include <algorithm>
51 #include <set>
52 
53 #ifdef CALLGRIND_COMPILE
54 #include <valgrind/callgrind.h>
55 #endif
56 
57 #include <com/sun/star/beans/XMaterialHolder.hpp>
58 
59 using namespace vcl;
60 using namespace utl;
61 using namespace psp;
62 using namespace osl;
63 using namespace com::sun::star::uno;
64 using namespace com::sun::star::beans;
65 using namespace com::sun::star::lang;
66 
67 /*
68  *  static helpers
69  */
70 
getUInt16BE(const sal_uInt8 * & pBuffer)71 static sal_uInt16 getUInt16BE( const sal_uInt8*& pBuffer )
72 {
73     sal_uInt16 nRet = static_cast<sal_uInt16>(pBuffer[1]) |
74         (static_cast<sal_uInt16>(pBuffer[0]) << 8);
75     pBuffer+=2;
76     return nRet;
77 }
78 
79 /*
80  *  PrintFont implementations
81  */
PrintFont()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)
89 ,   m_nAscend(0)
90 ,   m_nDescend(0)
91 ,   m_nLeading(0)
92 ,   m_nXMin(0)
93 ,   m_nYMin(0)
94 ,   m_nXMax(0)
95 ,   m_nYMax(0)
96 ,   m_nDirectory(0)
97 ,   m_nCollectionEntry(0)
98 ,   m_nVariationEntry(0)
99 {
100 }
101 
102 /*
103  *  one instance only
104  */
get()105 PrintFontManager& PrintFontManager::get()
106 {
107     GenericUnixSalData* const pSalData(GetGenericUnixSalData());
108     assert(pSalData);
109     return *pSalData->GetPrintFontManager();
110 }
111 
112 /*
113  *  the PrintFontManager
114  */
115 
PrintFontManager()116 PrintFontManager::PrintFontManager()
117     : m_nNextFontID( 1 )
118     , m_nNextDirAtom( 1 )
119 {
120     m_aFontInstallerTimer.SetInvokeHandler(LINK(this, PrintFontManager, autoInstallFontLangSupport));
121     m_aFontInstallerTimer.SetTimeout(5000);
122 }
123 
~PrintFontManager()124 PrintFontManager::~PrintFontManager()
125 {
126     m_aFontInstallerTimer.Stop();
127     deinitFontconfig();
128 }
129 
getDirectory(int nAtom) const130 OString PrintFontManager::getDirectory( int nAtom ) const
131 {
132     std::unordered_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) );
133     return it != m_aAtomToDir.end() ? it->second : OString();
134 }
135 
getDirectoryAtom(const OString & rDirectory)136 int PrintFontManager::getDirectoryAtom( const OString& rDirectory )
137 {
138     int nAtom = 0;
139     std::unordered_map< OString, int >::const_iterator it
140           ( m_aDirToAtom.find( rDirectory ) );
141     if( it != m_aDirToAtom.end() )
142         nAtom = it->second;
143     else
144     {
145         nAtom = m_nNextDirAtom++;
146         m_aDirToAtom[ rDirectory ] = nAtom;
147         m_aAtomToDir[ nAtom ] = rDirectory;
148     }
149     return nAtom;
150 }
151 
addFontFile(const OUString & rFileUrl)152 std::vector<fontID> PrintFontManager::addFontFile( const OUString& rFileUrl )
153 {
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 ) );
159 
160     int nDirID = getDirectoryAtom( aDir );
161     std::vector<fontID> aFontIds = findFontFileIDs( nDirID, aName );
162     if( aFontIds.empty() )
163     {
164         std::vector<std::unique_ptr<PrintFont>> aNewFonts = analyzeFontFile(nDirID, aName);
165         for (auto & font : aNewFonts)
166         {
167             fontID nFontId = m_nNextFontID++;
168             m_aFonts[nFontId] = std::move(font);
169             m_aFontFileToFontID[ aName ].insert( nFontId );
170             aFontIds.push_back(nFontId);
171         }
172     }
173     return aFontIds;
174 }
175 
analyzeFontFile(int nDirID,const OString & rFontFile,const char * pFormat) const176 std::vector<std::unique_ptr<PrintFontManager::PrintFont>> PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile, const char *pFormat ) const
177 {
178     std::vector<std::unique_ptr<PrintFontManager::PrintFont>> aNewFonts;
179 
180     OString aDir( getDirectory( nDirID ) );
181 
182     OString aFullPath = aDir + "/" + rFontFile;
183 
184     // #i1872# reject unreadable files
185     if( access( aFullPath.getStr(), R_OK ) )
186         return aNewFonts;
187 
188     bool bSupported = false;
189     if (pFormat)
190     {
191         if (!strcmp(pFormat, "TrueType") ||
192             !strcmp(pFormat, "CFF"))
193             bSupported = true;
194     }
195     if (!bSupported)
196     {
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
202             bSupported = true;
203     }
204 
205     if (bSupported)
206     {
207         // get number of ttc entries
208         int nLength = CountTTCFonts( aFullPath.getStr() );
209         if (nLength > 0)
210         {
211             SAL_INFO("vcl.fonts", "ttc: " << aFullPath << " contains " << nLength << " fonts");
212 
213             sal_uInt64 fileSize = 0;
214 
215             OUString aURL;
216             if (osl::File::getFileURLFromSystemPath(OStringToOUString(aFullPath, osl_getThreadTextEncoding()),
217                 aURL) == osl::File::E_None)
218             {
219                 osl::File aFile(aURL);
220                 if (aFile.open(osl_File_OpenFlag_Read | osl_File_OpenFlag_NoLock) == osl::File::E_None)
221                 {
222                     osl::DirectoryItem aItem;
223                     if (osl::DirectoryItem::get(aURL, aItem) == osl::File::E_None)
224                     {
225                         osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
226                         if (aItem.getFileStatus(aFileStatus) == osl::File::E_None)
227                             fileSize = aFileStatus.getFileSize();
228                     }
229                 }
230             }
231 
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;
240 
241             for( int i = 0; i < nLength; i++ )
242             {
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));
249             }
250         }
251         else
252         {
253             std::unique_ptr<PrintFont> xFont(new PrintFont);
254             xFont->m_nDirectory         = nDirID;
255             xFont->m_aFontFile          = rFontFile;
256             xFont->m_nCollectionEntry   = 0;
257 
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));
261         }
262     }
263     return aNewFonts;
264 }
265 
findFontFileID(int nDirID,const OString & rFontFile,int nFaceIndex,int nVariationIndex) const266 fontID PrintFontManager::findFontFileID(int nDirID, const OString& rFontFile, int nFaceIndex, int nVariationIndex) const
267 {
268     fontID nID = 0;
269 
270     std::unordered_map< OString, ::std::set< fontID > >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile );
271     if( set_it == m_aFontFileToFontID.end() )
272         return nID;
273 
274     for (auto const& elem : set_it->second)
275     {
276         auto it = m_aFonts.find(elem);
277         if( it == m_aFonts.end() )
278             continue;
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)
284         {
285             nID = it->first;
286             if (nID)
287                 break;
288         }
289     }
290 
291     return nID;
292 }
293 
findFontFileIDs(int nDirID,const OString & rFontFile) const294 std::vector<fontID> PrintFontManager::findFontFileIDs( int nDirID, const OString& rFontFile ) const
295 {
296     std::vector<fontID> aIds;
297 
298     std::unordered_map< OString, ::std::set< fontID > >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile );
299     if( set_it == m_aFontFileToFontID.end() )
300         return aIds;
301 
302     for (auto const& elem : set_it->second)
303     {
304         auto it = m_aFonts.find(elem);
305         if( it == m_aFonts.end() )
306             continue;
307         PrintFont* const pFont = (*it).second.get();
308         if (pFont->m_nDirectory == nDirID &&
309             pFont->m_aFontFile == rFontFile)
310             aIds.push_back(it->first);
311     }
312 
313     return aIds;
314 }
315 
convertSfntName(void * pRecord)316 OUString PrintFontManager::convertSfntName( void* pRecord )
317 {
318     NameRecord* pNameRecord = static_cast<NameRecord*>(pRecord);
319     OUString aValue;
320     if(
321        ( pNameRecord->platformID == 3 && ( pNameRecord->encodingID == 0 || pNameRecord->encodingID == 1 ) )  // MS, Unicode
322        ||
323        ( pNameRecord->platformID == 0 ) // Apple, Unicode
324        )
325     {
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();
331     }
332     else if( pNameRecord->platformID == 3 )
333     {
334         if( pNameRecord->encodingID >= 2 && pNameRecord->encodingID <= 6 )
335         {
336             /*
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
341              */
342             OStringBuffer aName;
343             const sal_uInt8* pNameBuffer = pNameRecord->sptr;
344             for(int n = 0; n < pNameRecord->slen/2; n++ )
345             {
346                 sal_Unicode aCode = static_cast<sal_Unicode>(getUInt16BE( pNameBuffer ));
347                 sal_Char aChar = aCode >> 8;
348                 if( aChar )
349                     aName.append( aChar );
350                 aChar = aCode & 0x00ff;
351                 if( aChar )
352                     aName.append( aChar );
353             }
354             switch( pNameRecord->encodingID )
355             {
356                 case 2:
357                     aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_932 );
358                     break;
359                 case 3:
360                     aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_936 );
361                     break;
362                 case 4:
363                     aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_950 );
364                     break;
365                 case 5:
366                     aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_949 );
367                     break;
368                 case 6:
369                     aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_1361 );
370                     break;
371             }
372         }
373     }
374     else if( pNameRecord->platformID == 1 )
375     {
376         OString aName(reinterpret_cast<char*>(pNameRecord->sptr), pNameRecord->slen);
377         rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW;
378         switch (pNameRecord->encodingID)
379         {
380             case 0:
381                 eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
382                 break;
383             case 1:
384                 eEncoding = RTL_TEXTENCODING_APPLE_JAPANESE;
385                 break;
386             case 2:
387                 eEncoding = RTL_TEXTENCODING_APPLE_CHINTRAD;
388                 break;
389             case 3:
390                 eEncoding = RTL_TEXTENCODING_APPLE_KOREAN;
391                 break;
392             case 4:
393                 eEncoding = RTL_TEXTENCODING_APPLE_ARABIC;
394                 break;
395             case 5:
396                 eEncoding = RTL_TEXTENCODING_APPLE_HEBREW;
397                 break;
398             case 6:
399                 eEncoding = RTL_TEXTENCODING_APPLE_GREEK;
400                 break;
401             case 7:
402                 eEncoding = RTL_TEXTENCODING_APPLE_CYRILLIC;
403                 break;
404             case 9:
405                 eEncoding = RTL_TEXTENCODING_APPLE_DEVANAGARI;
406                 break;
407             case 10:
408                 eEncoding = RTL_TEXTENCODING_APPLE_GURMUKHI;
409                 break;
410             case 11:
411                 eEncoding = RTL_TEXTENCODING_APPLE_GUJARATI;
412                 break;
413             case 21:
414                 eEncoding = RTL_TEXTENCODING_APPLE_THAI;
415                 break;
416             case 25:
417                 eEncoding = RTL_TEXTENCODING_APPLE_CHINSIMP;
418                 break;
419             case 29:
420                 eEncoding = RTL_TEXTENCODING_APPLE_CENTEURO;
421                 break;
422             case 32:    //Uninterpreted
423                 eEncoding = RTL_TEXTENCODING_UTF8;
424                 break;
425             default:
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);
429                 break;
430         }
431         if (eEncoding != RTL_TEXTENCODING_DONTKNOW)
432             aValue = OStringToOUString(aName, eEncoding);
433     }
434 
435     return aValue;
436 }
437 
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
442 namespace
443 {
isBadTNR(const OUString & rName,::std::set<OUString> & rSet)444     bool isBadTNR(const OUString &rName, ::std::set< OUString >& rSet)
445     {
446         bool bRet = false;
447         if ( rName == "Berling Antiqua" )
448         {
449             ::std::set< OUString >::iterator aEnd = rSet.end();
450             ::std::set< OUString >::iterator aI = rSet.find("Times New Roman");
451             if (aI != aEnd)
452             {
453                 bRet = true;
454                 rSet.erase(aI);
455             }
456         }
457         return bRet;
458     }
459 }
460 
analyzeSfntFamilyName(void const * pTTFont,::std::vector<OUString> & rNames)461 void PrintFontManager::analyzeSfntFamilyName( void const * pTTFont, ::std::vector< OUString >& rNames )
462 {
463     OUString aFamily;
464 
465     rNames.clear();
466     ::std::set< OUString > aSet;
467 
468     NameRecord* pNameRecords = nullptr;
469     int nNameRecords = GetTTNameRecords( static_cast<TrueTypeFont const *>(pTTFont), &pNameRecords );
470     if( nNameRecords && pNameRecords )
471     {
472         LanguageTag aSystem("");
473         LanguageType eLang = aSystem.getLanguageType();
474         int nLastMatch = -1;
475         for( int i = 0; i < nNameRecords; i++ )
476         {
477             if( pNameRecords[i].nameID != 1 || pNameRecords[i].sptr == nullptr )
478                 continue;
479             int nMatch = -1;
480             if( pNameRecords[i].platformID == 0 ) // Unicode
481                 nMatch = 4000;
482             else if( pNameRecords[i].platformID == 3 )
483             {
484                 // this bases on the LanguageType actually being a Win LCID
485                 if (pNameRecords[i].languageID == eLang)
486                     nMatch = 8000;
487                 else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH_US )
488                     nMatch = 2000;
489                 else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH ||
490                          pNameRecords[i].languageID == LANGUAGE_ENGLISH_UK )
491                     nMatch = 1500;
492                 else
493                     nMatch = 1000;
494             }
495             else if (pNameRecords[i].platformID == 1)
496             {
497                 AppleLanguageId aAppleId = static_cast<AppleLanguageId>(static_cast<sal_uInt16>(pNameRecords[i].languageID));
498                 LanguageTag aApple(makeLanguageTagFromAppleLanguageId(aAppleId));
499                 if (aApple == aSystem)
500                     nMatch = 8000;
501                 else if (aAppleId == AppleLanguageId::ENGLISH)
502                     nMatch = 2000;
503                 else
504                     nMatch = 1000;
505             }
506             OUString aName = convertSfntName( pNameRecords + i );
507             aSet.insert( aName );
508             if (aName.isEmpty())
509                 continue;
510             if( nMatch > nLastMatch || isBadTNR(aName, aSet) )
511             {
512                 nLastMatch = nMatch;
513                 aFamily = aName;
514             }
515         }
516     }
517     DisposeNameRecords( pNameRecords, nNameRecords );
518     if( !aFamily.isEmpty() )
519     {
520         rNames.push_back( aFamily );
521         for (auto const& elem : aSet)
522             if( elem != aFamily )
523                 rNames.push_back(elem);
524     }
525 }
526 
analyzeSfntFile(PrintFont * pFont) const527 bool PrintFontManager::analyzeSfntFile( PrintFont* pFont ) const
528 {
529     bool bSuccess = false;
530     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
531     OString aFile = getFontFile( pFont );
532     TrueTypeFont* pTTFont = nullptr;
533 
534     auto const e = OpenTTFontFile( aFile.getStr(), pFont->m_nCollectionEntry, &pTTFont );
535     if( e == SFErrCodes::Ok )
536     {
537         TTGlobalFontInfo aInfo;
538         GetTTGlobalFontInfo( pTTFont, & aInfo );
539 
540         ::std::vector< OUString > aNames;
541         analyzeSfntFamilyName( pTTFont, aNames );
542 
543         // set family name from XLFD if possible
544         if (pFont->m_aFamilyName.isEmpty())
545         {
546             if( !aNames.empty() )
547             {
548                 pFont->m_aFamilyName = aNames.front();
549                 aNames.erase(aNames.begin());
550             }
551             else
552             {
553                  sal_Int32   dotIndex;
554 
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();
560 
561                  pFont->m_aFamilyName = OStringToOUString(pFont->m_aFontFile.copy(0, dotIndex), aEncoding);
562             }
563         }
564         for (auto const& aAlias : aNames)
565         {
566             if (!aAlias.isEmpty())
567             {
568                 if (pFont->m_aFamilyName != aAlias)
569                 {
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);
573                 }
574             }
575         }
576 
577         if( aInfo.usubfamily )
578             pFont->m_aStyleName = OUString( aInfo.usubfamily );
579 
580         SAL_WARN_IF( !aInfo.psname, "vcl.fonts", "No PostScript name in font:" << aFile );
581 
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
585 
586         pFont->m_eFamilyStyle = matchFamilyName(pFont->m_aFamilyName);
587 
588         switch( aInfo.weight )
589         {
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;
598 
599             case FW_NORMAL:
600             default:        pFont->m_eWeight = WEIGHT_NORMAL; break;
601         }
602 
603         switch( aInfo.width )
604         {
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;
613 
614             case FWIDTH_NORMAL:
615             default:                        pFont->m_eWidth = WIDTH_NORMAL; break;
616         }
617 
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;
624 
625         pFont->m_aEncoding = aInfo.symbolEncoded ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UCS2;
626 
627         if( aInfo.ascender && aInfo.descender )
628         {
629             pFont->m_nLeading   = aInfo.linegap;
630             pFont->m_nAscend    = aInfo.ascender;
631             pFont->m_nDescend   = -aInfo.descender;
632         }
633         else if( aInfo.typoAscender && aInfo.typoDescender )
634         {
635             pFont->m_nLeading   = aInfo.typoLineGap;
636             pFont->m_nAscend    = aInfo.typoAscender;
637             pFont->m_nDescend   = -aInfo.typoDescender;
638         }
639         else if( aInfo.winAscent && aInfo.winDescent )
640         {
641             pFont->m_nAscend    = aInfo.winAscent;
642             pFont->m_nDescend   = aInfo.winDescent;
643             pFont->m_nLeading   = pFont->m_nAscend + pFont->m_nDescend - 1000;
644         }
645 
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;
653 
654         // get bounding box
655         pFont->m_nXMin = aInfo.xMin;
656         pFont->m_nYMin = aInfo.yMin;
657         pFont->m_nXMax = aInfo.xMax;
658         pFont->m_nYMax = aInfo.yMax;
659 
660         CloseTTFont( pTTFont );
661         bSuccess = true;
662     }
663     else
664         SAL_WARN("vcl.fonts", "Could not OpenTTFont \"" << aFile << "\": " << int(e));
665 
666     return bSuccess;
667 }
668 
initialize()669 void PrintFontManager::initialize()
670 {
671     #ifdef CALLGRIND_COMPILE
672     CALLGRIND_TOGGLE_COLLECT();
673     CALLGRIND_ZERO_STATS();
674     #endif
675 
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
679     {
680         m_nNextFontID = 1;
681         m_aFonts.clear();
682     }
683 
684 #if OSL_DEBUG_LEVEL > 1
685     clock_t aStart;
686     clock_t aStep1;
687     clock_t aStep2;
688 
689     struct tms tms;
690 
691     aStart = times( &tms );
692 #endif
693 
694     // first try fontconfig
695     initFontconfig();
696 
697     // part one - look for downloadable fonts
698     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
699     const OUString &rSalPrivatePath = psp::getFontPath();
700 
701     // search for the fonts in SAL_PRIVATE_FONTPATH first; those are
702     // the fonts installed with the office
703     if( !rSalPrivatePath.isEmpty() )
704     {
705         OString aPath = OUStringToOString( rSalPrivatePath, aEncoding );
706         sal_Int32 nIndex = 0;
707         do
708         {
709             OString aToken = aPath.getToken( 0, ';', nIndex );
710             normPath( aToken );
711             if (!aToken.isEmpty())
712                 addFontconfigDir(aToken);
713         } while( nIndex >= 0 );
714     }
715 
716     // protect against duplicate paths
717     std::unordered_map< OString, int > visited_dirs;
718 
719     // Don't search directories that fontconfig already did
720     countFontconfigFonts( visited_dirs );
721 
722 #if OSL_DEBUG_LEVEL > 1
723     aStep1 = times( &tms );
724 #endif
725 
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 );
732 #endif
733 
734     #ifdef CALLGRIND_COMPILE
735     CALLGRIND_DUMP_STATS();
736     CALLGRIND_TOGGLE_COLLECT();
737     #endif
738 }
739 
getFontList(::std::vector<fontID> & rFontIDs)740 void PrintFontManager::getFontList( ::std::vector< fontID >& rFontIDs )
741 {
742     rFontIDs.clear();
743 
744     for (auto const& font : m_aFonts)
745         rFontIDs.push_back(font.first);
746 }
747 
fillPrintFontInfo(PrintFont * pFont,FastPrintFontInfo & rInfo)748 void PrintFontManager::fillPrintFontInfo(PrintFont* pFont, FastPrintFontInfo& rInfo)
749 {
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;
758 
759     rInfo.m_aAliases.clear();
760     for (auto const& aAlias : pFont->m_aAliases)
761         rInfo.m_aAliases.push_back(aAlias);
762 }
763 
fillPrintFontInfo(PrintFont * pFont,PrintFontInfo & rInfo) const764 void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, PrintFontInfo& rInfo ) const
765 {
766     if (pFont->m_nAscend == 0 && pFont->m_nDescend == 0)
767     {
768         analyzeSfntFile(pFont);
769     }
770 
771     fillPrintFontInfo( pFont, static_cast< FastPrintFontInfo& >( rInfo ) );
772 
773     rInfo.m_nAscend         = pFont->m_nAscend;
774     rInfo.m_nDescend        = pFont->m_nDescend;
775 }
776 
getFontInfo(fontID nFontID,PrintFontInfo & rInfo) const777 bool PrintFontManager::getFontInfo( fontID nFontID, PrintFontInfo& rInfo ) const
778 {
779     PrintFont* pFont = getFont( nFontID );
780     if( pFont )
781     {
782         rInfo.m_nID = nFontID;
783         fillPrintFontInfo( pFont, rInfo );
784     }
785     return pFont != nullptr;
786 }
787 
getFontFastInfo(fontID nFontID,FastPrintFontInfo & rInfo) const788 bool PrintFontManager::getFontFastInfo( fontID nFontID, FastPrintFontInfo& rInfo ) const
789 {
790     PrintFont* pFont = getFont( nFontID );
791     if( pFont )
792     {
793         rInfo.m_nID = nFontID;
794         fillPrintFontInfo( pFont, rInfo );
795     }
796     return pFont != nullptr;
797 }
798 
getFontBoundingBox(fontID nFontID,int & xMin,int & yMin,int & xMax,int & yMax)799 void PrintFontManager::getFontBoundingBox( fontID nFontID, int& xMin, int& yMin, int& xMax, int& yMax )
800 {
801     PrintFont* pFont = getFont( nFontID );
802     if( pFont )
803     {
804         if( pFont->m_nXMin == 0 && pFont->m_nYMin == 0 && pFont->m_nXMax == 0 && pFont->m_nYMax == 0 )
805         {
806             analyzeSfntFile(pFont);
807         }
808         xMin = pFont->m_nXMin;
809         yMin = pFont->m_nYMin;
810         xMax = pFont->m_nXMax;
811         yMax = pFont->m_nYMax;
812     }
813 }
814 
getFontFaceNumber(fontID nFontID) const815 int PrintFontManager::getFontFaceNumber( fontID nFontID ) const
816 {
817     int nRet = 0;
818     PrintFont* pFont = getFont( nFontID );
819     if (pFont)
820     {
821         nRet = pFont->m_nCollectionEntry;
822         if (nRet < 0)
823             nRet = 0;
824     }
825     return nRet;
826 }
827 
getFontFaceVariation(fontID nFontID) const828 int PrintFontManager::getFontFaceVariation( fontID nFontID ) const
829 {
830     int nRet = 0;
831     PrintFont* pFont = getFont( nFontID );
832     if (pFont)
833     {
834         nRet = pFont->m_nVariationEntry;
835         if (nRet < 0)
836             nRet = 0;
837     }
838     return nRet;
839 }
840 
matchFamilyName(const OUString & rFamily)841 FontFamily PrintFontManager::matchFamilyName( const OUString& rFamily )
842 {
843     struct family_t {
844         const char*  mpName;
845         sal_uInt16 const   mnLength;
846         FontFamily const   meType;
847     };
848 
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 ) }
877     };
878 
879     OString aFamily = OUStringToOString( rFamily, RTL_TEXTENCODING_ASCII_US );
880     sal_uInt32 nLower = 0;
881     sal_uInt32 nUpper = SAL_N_ELEMENTS(pFamilyMatch);
882 
883     while( nLower < nUpper )
884     {
885         sal_uInt32 nCurrent = (nLower + nUpper) / 2;
886         const family_t* pHaystack = pFamilyMatch + nCurrent;
887         sal_Int32  nComparison =
888             rtl_str_compareIgnoreAsciiCase_WithLength
889             (
890              aFamily.getStr(), aFamily.getLength(),
891              pHaystack->mpName, pHaystack->mnLength
892              );
893 
894         if( nComparison < 0 )
895             nUpper = nCurrent;
896         else
897             if( nComparison > 0 )
898                 nLower = nCurrent + 1;
899             else
900                 return pHaystack->meType;
901     }
902 
903     return FAMILY_DONTKNOW;
904 }
905 
getFontFile(const PrintFont * pFont) const906 OString PrintFontManager::getFontFile(const PrintFont* pFont) const
907 {
908     OString aPath;
909 
910     if (pFont)
911     {
912         std::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find(pFont->m_nDirectory);
913         aPath = it->second + "/" + pFont->m_aFontFile;
914     }
915     return aPath;
916 }
917 
getPSName(fontID nFontID) const918 OUString PrintFontManager::getPSName( fontID nFontID ) const
919 {
920     PrintFont* pFont = getFont( nFontID );
921     if (pFont && pFont->m_aPSName.isEmpty())
922     {
923         analyzeSfntFile(pFont);
924     }
925 
926     return pFont ? pFont->m_aPSName : OUString();
927 }
928 
getFontAscend(fontID nFontID) const929 int PrintFontManager::getFontAscend( fontID nFontID ) const
930 {
931     PrintFont* pFont = getFont( nFontID );
932     if (pFont && pFont->m_nAscend == 0 && pFont->m_nDescend == 0)
933     {
934         analyzeSfntFile(pFont);
935     }
936     return pFont ? pFont->m_nAscend : 0;
937 }
938 
getFontDescend(fontID nFontID) const939 int PrintFontManager::getFontDescend( fontID nFontID ) const
940 {
941     PrintFont* pFont = getFont( nFontID );
942     if (pFont && pFont->m_nAscend == 0 && pFont->m_nDescend == 0)
943     {
944         analyzeSfntFile(pFont);
945     }
946     return pFont ? pFont->m_nDescend : 0;
947 }
948 
949 // TODO: move most of this stuff into the central font-subsetting code
createFontSubset(FontSubsetInfo & rInfo,fontID nFont,const OUString & rOutFile,const sal_GlyphId * pGlyphIds,const sal_uInt8 * pNewEncoding,sal_Int32 * pWidths,int nGlyphs)950 bool PrintFontManager::createFontSubset(
951                                         FontSubsetInfo& rInfo,
952                                         fontID nFont,
953                                         const OUString& rOutFile,
954                                         const sal_GlyphId* pGlyphIds,
955                                         const sal_uInt8* pNewEncoding,
956                                         sal_Int32* pWidths,
957                                         int nGlyphs
958                                         )
959 {
960     PrintFont* pFont = getFont( nFont );
961     if( !pFont )
962         return false;
963 
964     rInfo.m_nFontType = FontType::SFNT_TTF;
965 
966     // reshuffle array of requested glyphs to make sure glyph0==notdef
967     sal_uInt8  pEnc[256];
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 ) );
973     if( nGlyphs > 256 )
974         return false;
975     int nChar = 1;
976     for( int i = 0; i < nGlyphs; i++ )
977     {
978         if( pNewEncoding[i] == 0 )
979         {
980             pOldIndex[ 0 ] = i;
981         }
982         else
983         {
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;
990             nChar++;
991         }
992     }
993     nGlyphs = nChar; // either input value or increased by one
994 
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 );
998 
999     TrueTypeFont* pTTFont = nullptr; // TODO: rename to SfntFont
1000     if( OpenTTFontFile( aFromFile.getStr(), pFont->m_nCollectionEntry, &pTTFont ) != SFErrCodes::Ok )
1001         return false;
1002 
1003     // prepare system name for write access for subset file target
1004     OUString aSysPath;
1005     if( osl_File_E_None != osl_getSystemPathFromFileURL( rOutFile.pData, &aSysPath.pData ) )
1006         return false;
1007     const rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
1008     const OString aToFile( OUStringToOString( aSysPath, aEncoding ) );
1009 
1010     // do CFF subsetting if possible
1011     int nCffLength = 0;
1012     const sal_uInt8* pCffBytes = nullptr;
1013     if( GetSfntTable( pTTFont, O_CFF, &pCffBytes, &nCffLength ) )
1014     {
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];
1020 #endif
1021         // create subset file at requested path
1022         FILE* pOutFile = fopen( aToFile.getStr(), "wb" );
1023         if (!pOutFile)
1024         {
1025             CloseTTFont( pTTFont );
1026             return false;
1027         }
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 );
1034         fclose( pOutFile );
1035         // For OTC, values from hhea or OS2 are better
1036         psp::PrintFontInfo aFontInfo;
1037         if( getFontInfo( nFont, aFontInfo ) )
1038         {
1039             rInfo.m_nAscent     = aFontInfo.m_nAscend;
1040             rInfo.m_nDescent    = -aFontInfo.m_nDescend;
1041         }
1042         // cleanup before early return
1043         CloseTTFont( pTTFont );
1044         return bOK;
1045     }
1046 
1047     // do TTF->Type42 or Type3 subsetting
1048     // fill in font info
1049     psp::PrintFontInfo aFontInfo;
1050     if( ! getFontInfo( nFont, aFontInfo ) )
1051         return false;
1052 
1053     rInfo.m_nAscent     = aFontInfo.m_nAscend;
1054     rInfo.m_nDescent    = aFontInfo.m_nDescend;
1055     rInfo.m_aPSName     = getPSName( nFont );
1056 
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 ...
1061 
1062     // fill in glyph advance widths
1063     std::unique_ptr<sal_uInt16[]> pMetrics = GetTTSimpleGlyphMetrics( pTTFont,
1064                                                               pGID,
1065                                                               nGlyphs,
1066                                                               false/*bVertical*/ );
1067     if( pMetrics )
1068     {
1069         for( int i = 0; i < nGlyphs; i++ )
1070             pWidths[pOldIndex[i]] = pMetrics[i];
1071         pMetrics.reset();
1072     }
1073     else
1074     {
1075         CloseTTFont( pTTFont );
1076         return false;
1077     }
1078 
1079     bool bSuccess = ( SFErrCodes::Ok == CreateTTFromTTGlyphs( pTTFont,
1080                                                      aToFile.getStr(),
1081                                                      pGID,
1082                                                      pEnc,
1083                                                      nGlyphs ) );
1084     CloseTTFont( pTTFont );
1085 
1086     return bSuccess;
1087 }
1088 
getGlyphWidths(fontID nFont,bool bVertical,std::vector<sal_Int32> & rWidths,std::map<sal_Unicode,sal_uInt32> & rUnicodeEnc)1089 void PrintFontManager::getGlyphWidths( fontID nFont,
1090                                        bool bVertical,
1091                                        std::vector< sal_Int32 >& rWidths,
1092                                        std::map< sal_Unicode, sal_uInt32 >& rUnicodeEnc )
1093 {
1094     PrintFont* pFont = getFont( nFont );
1095     if (!pFont)
1096         return;
1097     TrueTypeFont* pTTFont = nullptr;
1098     OString aFromFile = getFontFile( pFont );
1099     if( OpenTTFontFile( aFromFile.getStr(), pFont->m_nCollectionEntry, &pTTFont ) != SFErrCodes::Ok )
1100         return;
1101     int nGlyphs = GetTTGlyphCount(pTTFont);
1102     if (nGlyphs > 0)
1103     {
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,
1109                                                                  aGlyphIds.data(),
1110                                                                  nGlyphs,
1111                                                                  bVertical);
1112         if (pMetrics)
1113         {
1114             for (int i = 0; i< nGlyphs; i++)
1115                 rWidths[i] = pMetrics[i];
1116             pMetrics.reset();
1117             rUnicodeEnc.clear();
1118         }
1119 
1120         // fill the unicode map
1121         // TODO: isn't this map already available elsewhere in the fontmanager?
1122         const sal_uInt8* pCmapData = nullptr;
1123         int nCmapSize = 0;
1124         if (GetSfntTable(pTTFont, O_cmap, &pCmapData, &nCmapSize))
1125         {
1126             CmapResult aCmapResult;
1127             if (ParseCMAP(pCmapData, nCmapSize, aCmapResult))
1128             {
1129                 FontCharMapRef xFontCharMap(new FontCharMap(aCmapResult));
1130                 for (sal_uInt32 cOld = 0;;)
1131                 {
1132                     // get next unicode covered by font
1133                     const sal_uInt32 c = xFontCharMap->GetNextChar(cOld);
1134                     if (c == cOld)
1135                         break;
1136                     cOld = c;
1137 #if 1 // TODO: remove when sal_Unicode covers all of unicode
1138                     if (c > sal_Unicode(~0))
1139                         break;
1140 #endif
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;
1145                 }
1146             }
1147         }
1148     }
1149     CloseTTFont(pTTFont);
1150 }
1151 
1152 /// used by online unit tests via dlopen.
1153 extern "C" {
unit_online_get_fonts(void)1154 SAL_DLLPUBLIC_EXPORT const char * unit_online_get_fonts(void)
1155 {
1156     std::vector< fontID > aFontIDs;
1157     PrintFontManager &rMgr = PrintFontManager::get();
1158     rMgr.getFontList(aFontIDs);
1159     OStringBuffer aBuf;
1160     aBuf.append( static_cast<sal_Int32>(aFontIDs.size()) );
1161     aBuf.append( " PS fonts.\n" );
1162     for( auto nId : aFontIDs )
1163     {
1164         const OUString& rName = rMgr.getPSName( nId );
1165         aBuf.append( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
1166         aBuf.append( "\n" );
1167     }
1168     static OString aResult = aBuf.makeStringAndClear();
1169     return aResult.getStr();
1170 }
1171 }
1172 
1173 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1174