Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / unx / generic / fontmanager / fontmanager.cxx
blobbaaae5211fbfae1ca5ae0794cfb7cd0ef245cc90
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <memory>
21 #include <unistd.h>
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>
43 #include <sft.hxx>
45 #if OSL_DEBUG_LEVEL > 1
46 #include <sys/times.h>
47 #include <stdio.h>
48 #endif
50 #include <algorithm>
51 #include <set>
53 #ifdef CALLGRIND_COMPILE
54 #include <valgrind/callgrind.h>
55 #endif
57 #include <com/sun/star/beans/XMaterialHolder.hpp>
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;
68 * static helpers
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);
75 pBuffer+=2;
76 return nRet;
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)
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)
103 * one instance only
105 PrintFontManager& PrintFontManager::get()
107 GenericUnixSalData* const pSalData(GetGenericUnixSalData());
108 assert(pSalData);
109 return *pSalData->GetPrintFontManager();
113 * the PrintFontManager
116 PrintFontManager::PrintFontManager()
117 : m_nNextFontID( 1 )
118 , m_nNextDirAtom( 1 )
120 m_aFontInstallerTimer.SetInvokeHandler(LINK(this, PrintFontManager, autoInstallFontLangSupport));
121 m_aFontInstallerTimer.SetTimeout(5000);
124 PrintFontManager::~PrintFontManager()
126 m_aFontInstallerTimer.Stop();
127 deinitFontconfig();
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 )
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
145 nAtom = m_nNextDirAtom++;
146 m_aDirToAtom[ rDirectory ] = nAtom;
147 m_aAtomToDir[ nAtom ] = rDirectory;
149 return nAtom;
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);
173 return aFontIds;
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 ) )
186 return aNewFonts;
188 bool bSupported = false;
189 if (pFormat)
191 if (!strcmp(pFormat, "TrueType") ||
192 !strcmp(pFormat, "CFF"))
193 bSupported = true;
195 if (!bSupported)
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;
205 if (bSupported)
207 // get number of ttc entries
208 int nLength = CountTTCFonts( aFullPath.getStr() );
209 if (nLength > 0)
211 SAL_INFO("vcl.fonts", "ttc: " << aFullPath << " contains " << nLength << " fonts");
213 sal_uInt64 fileSize = 0;
215 OUString aURL;
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));
251 else
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));
263 return aNewFonts;
266 fontID PrintFontManager::findFontFileID(int nDirID, const OString& rFontFile, int nFaceIndex, int nVariationIndex) const
268 fontID nID = 0;
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;
274 for (auto const& elem : set_it->second)
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)
285 nID = it->first;
286 if (nID)
287 break;
291 return nID;
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() )
300 return aIds;
302 for (auto const& elem : set_it->second)
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);
313 return aIds;
316 OUString PrintFontManager::convertSfntName( void* pRecord )
318 NameRecord* pNameRecord = static_cast<NameRecord*>(pRecord);
319 OUString aValue;
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
342 OStringBuffer aName;
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;
348 if( aChar )
349 aName.append( aChar );
350 aChar = aCode & 0x00ff;
351 if( aChar )
352 aName.append( aChar );
354 switch( pNameRecord->encodingID )
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;
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)
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;
431 if (eEncoding != RTL_TEXTENCODING_DONTKNOW)
432 aValue = OStringToOUString(aName, eEncoding);
435 return aValue;
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
444 bool isBadTNR(const OUString &rName, ::std::set< OUString >& rSet)
446 bool bRet = false;
447 if ( rName == "Berling Antiqua" )
449 ::std::set< OUString >::iterator aEnd = rSet.end();
450 ::std::set< OUString >::iterator aI = rSet.find("Times New Roman");
451 if (aI != aEnd)
453 bRet = true;
454 rSet.erase(aI);
457 return bRet;
461 void PrintFontManager::analyzeSfntFamilyName( void const * pTTFont, ::std::vector< OUString >& rNames )
463 OUString aFamily;
465 rNames.clear();
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();
474 int nLastMatch = -1;
475 for( int i = 0; i < nNameRecords; i++ )
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 )
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;
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)
500 nMatch = 8000;
501 else if (aAppleId == AppleLanguageId::ENGLISH)
502 nMatch = 2000;
503 else
504 nMatch = 1000;
506 OUString aName = convertSfntName( pNameRecords + i );
507 aSet.insert( aName );
508 if (aName.isEmpty())
509 continue;
510 if( nMatch > nLastMatch || isBadTNR(aName, aSet) )
512 nLastMatch = nMatch;
513 aFamily = aName;
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());
551 else
553 sal_Int32 dotIndex;
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;
599 case FW_NORMAL:
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;
614 case FWIDTH_NORMAL:
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;
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;
660 CloseTTFont( pTTFont );
661 bSuccess = true;
663 else
664 SAL_WARN("vcl.fonts", "Could not OpenTTFont \"" << aFile << "\": " << int(e));
666 return bSuccess;
669 void PrintFontManager::initialize()
671 #ifdef CALLGRIND_COMPILE
672 CALLGRIND_TOGGLE_COLLECT();
673 CALLGRIND_ZERO_STATS();
674 #endif
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
680 m_nNextFontID = 1;
681 m_aFonts.clear();
684 #if OSL_DEBUG_LEVEL > 1
685 clock_t aStart;
686 clock_t aStep1;
687 clock_t aStep2;
689 struct tms tms;
691 aStart = times( &tms );
692 #endif
694 // first try fontconfig
695 initFontconfig();
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 );
710 normPath( aToken );
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 );
724 #endif
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
734 #ifdef CALLGRIND_COMPILE
735 CALLGRIND_DUMP_STATS();
736 CALLGRIND_TOGGLE_COLLECT();
737 #endif
740 void PrintFontManager::getFontList( ::std::vector< fontID >& rFontIDs )
742 rFontIDs.clear();
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 );
780 if( pFont )
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 );
791 if( pFont )
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 );
802 if( pFont )
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
817 int nRet = 0;
818 PrintFont* pFont = getFont( nFontID );
819 if (pFont)
821 nRet = pFont->m_nCollectionEntry;
822 if (nRet < 0)
823 nRet = 0;
825 return nRet;
828 int PrintFontManager::getFontFaceVariation( fontID nFontID ) const
830 int nRet = 0;
831 PrintFont* pFont = getFont( nFontID );
832 if (pFont)
834 nRet = pFont->m_nVariationEntry;
835 if (nRet < 0)
836 nRet = 0;
838 return nRet;
841 FontFamily PrintFontManager::matchFamilyName( const OUString& rFamily )
843 struct family_t {
844 const char* mpName;
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 )
895 nUpper = nCurrent;
896 else
897 if( nComparison > 0 )
898 nLower = nCurrent + 1;
899 else
900 return pHaystack->meType;
903 return FAMILY_DONTKNOW;
906 OString PrintFontManager::getFontFile(const PrintFont* pFont) const
908 OString aPath;
910 if (pFont)
912 std::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find(pFont->m_nDirectory);
913 aPath = it->second + "/" + pFont->m_aFontFile;
915 return aPath;
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,
952 fontID nFont,
953 const OUString& rOutFile,
954 const sal_GlyphId* pGlyphIds,
955 const sal_uInt8* pNewEncoding,
956 sal_Int32* pWidths,
957 int nGlyphs
960 PrintFont* pFont = getFont( nFont );
961 if( !pFont )
962 return false;
964 rInfo.m_nFontType = FontType::SFNT_TTF;
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++ )
978 if( pNewEncoding[i] == 0 )
980 pOldIndex[ 0 ] = i;
982 else
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++;
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 )
1001 return false;
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 ) );
1010 // do CFF subsetting if possible
1011 int nCffLength = 0;
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];
1020 #endif
1021 // create subset file at requested path
1022 FILE* pOutFile = fopen( aToFile.getStr(), "wb" );
1023 if (!pOutFile)
1025 CloseTTFont( pTTFont );
1026 return false;
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 ) )
1039 rInfo.m_nAscent = aFontInfo.m_nAscend;
1040 rInfo.m_nDescent = -aFontInfo.m_nDescend;
1042 // cleanup before early return
1043 CloseTTFont( pTTFont );
1044 return bOK;
1047 // do TTF->Type42 or Type3 subsetting
1048 // fill in font info
1049 psp::PrintFontInfo aFontInfo;
1050 if( ! getFontInfo( nFont, aFontInfo ) )
1051 return false;
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,
1064 pGID,
1065 nGlyphs,
1066 false/*bVertical*/ );
1067 if( pMetrics )
1069 for( int i = 0; i < nGlyphs; i++ )
1070 pWidths[pOldIndex[i]] = pMetrics[i];
1071 pMetrics.reset();
1073 else
1075 CloseTTFont( pTTFont );
1076 return false;
1079 bool bSuccess = ( SFErrCodes::Ok == CreateTTFromTTGlyphs( pTTFont,
1080 aToFile.getStr(),
1081 pGID,
1082 pEnc,
1083 nGlyphs ) );
1084 CloseTTFont( pTTFont );
1086 return bSuccess;
1089 void PrintFontManager::getGlyphWidths( fontID nFont,
1090 bool bVertical,
1091 std::vector< sal_Int32 >& rWidths,
1092 std::map< sal_Unicode, sal_uInt32 >& rUnicodeEnc )
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)
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)
1114 for (int i = 0; i< nGlyphs; i++)
1115 rWidths[i] = pMetrics[i];
1116 pMetrics.reset();
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;
1123 int nCmapSize = 0;
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);
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;
1149 CloseTTFont(pTTFont);
1152 /// used by online unit tests via dlopen.
1153 extern "C" {
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);
1159 OStringBuffer aBuf;
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: */