office-gobmx/vcl/quartz/SystemFontList.cxx
Patrick Luby a4e9584c55 tdf#163000 don't add any fonts with an 'hvgl' font table
macOS Sequoia added a new PingFangUI.ttc font file which
contains all of the PingFang font families. However, any
fonts loaded from this font file result in the following
failures:

- Skia renders font with wrong glyphs
- Export to PDF contain a damaged embedded font

Despite the fact that the fonts in this new font file have
a TrueType font type, they are missing a 'glyf' font table
and, instead, have a new, undefined 'hvgl' font table. See
the following link for more details about the new 'hvgl'
font table:

https://gitlab.freedesktop.org/freetype/freetype/-/issues/1281

Change-Id: I18170b1b226de86f79402ad0e45df8620c693f83
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174305
Reviewed-by: Patrick Luby <guibomacdev@gmail.com>
Tested-by: Jenkins
Reviewed-by: Jonathan Clark <jonathan@libreoffice.org>
Reviewed-by: خالد حسني <khaled@libreoffice.org>
2024-10-02 14:54:59 +02:00

350 lines
12 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <sal/log.hxx>
#include <tools/long.hxx>
#include <quartz/SystemFontList.hxx>
#include <impfont.hxx>
#ifdef MACOSX
#include <osx/saldata.hxx>
#include <osx/salinst.h>
#endif
#include <fontattributes.hxx>
#include <font/PhysicalFontCollection.hxx>
#include <quartz/CoreTextFontFace.hxx>
#include <quartz/salgdi.h>
#include <quartz/utils.h>
#include <sallayout.hxx>
FontAttributes DevFontFromCTFontDescriptor( CTFontDescriptorRef pFD, bool* bFontEnabled )
{
// all CoreText fonts are device fonts that can rotate just fine
FontAttributes rDFA;
rDFA.SetQuality( 0 );
// reset the font attributes
rDFA.SetFamilyType( FAMILY_DONTKNOW );
rDFA.SetPitch( PITCH_VARIABLE );
rDFA.SetWidthType( WIDTH_NORMAL );
rDFA.SetWeight( WEIGHT_NORMAL );
rDFA.SetItalic( ITALIC_NONE );
rDFA.SetMicrosoftSymbolEncoded( false );
// get font name
CFStringRef pLang = nullptr;
CFStringRef pFamilyName = static_cast<CFStringRef>(
CTFontDescriptorCopyLocalizedAttribute( pFD, kCTFontFamilyNameAttribute, &pLang ));
if ( !pLang )
{
if( pFamilyName )
{
CFRelease( pFamilyName );
}
pFamilyName = static_cast<CFStringRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute ));
}
rDFA.SetFamilyName( GetOUString( pFamilyName ) );
// get font style
CFStringRef pStyleName = static_cast<CFStringRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute ));
rDFA.SetStyleName( GetOUString( pStyleName ) );
// get font-enabled status
if( bFontEnabled )
{
int bEnabled = TRUE; // by default (and when we're on macOS < 10.6) it's "enabled"
CFNumberRef pEnabled = static_cast<CFNumberRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontEnabledAttribute ));
CFNumberGetValue( pEnabled, kCFNumberIntType, &bEnabled );
*bFontEnabled = bEnabled;
}
// get font attributes
CFDictionaryRef pAttrDict = static_cast<CFDictionaryRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontTraitsAttribute ));
if (bFontEnabled && *bFontEnabled)
{
// Ignore font formats not supported.
int nFormat;
CFNumberRef pFormat = static_cast<CFNumberRef>(CTFontDescriptorCopyAttribute(pFD, kCTFontFormatAttribute));
CFNumberGetValue(pFormat, kCFNumberIntType, &nFormat);
if (nFormat == kCTFontFormatUnrecognized || nFormat == kCTFontFormatPostScript || nFormat == kCTFontFormatBitmap)
{
SAL_INFO("vcl.fonts", "Ignoring font with unsupported format: " << rDFA.GetFamilyName());
*bFontEnabled = false;
}
CFRelease(pFormat);
}
// get symbolic trait
// TODO: use other traits such as MonoSpace/Condensed/Expanded or Vertical too
SInt64 nSymbolTrait = 0;
CFNumberRef pSymbolNum = nullptr;
if( CFDictionaryGetValueIfPresent( pAttrDict, kCTFontSymbolicTrait, reinterpret_cast<const void**>(&pSymbolNum) ) )
{
CFNumberGetValue( pSymbolNum, kCFNumberSInt64Type, &nSymbolTrait );
if (nSymbolTrait & kCTFontMonoSpaceTrait)
rDFA.SetPitch(PITCH_FIXED);
}
// get the font weight
double fWeight = 0;
CFNumberRef pWeightNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontWeightTrait ));
// tdf#140401 check if attribute is a nullptr
if( pWeightNum )
CFNumberGetValue( pWeightNum, kCFNumberDoubleType, &fWeight );
int nInt = WEIGHT_NORMAL;
// Special case fixes
// tdf#67744: Courier Std Medium is always bold. We get a kCTFontWeightTrait of 0.23 which
// surely must be wrong.
if (rDFA.GetFamilyName() == "Courier Std" &&
(rDFA.GetStyleName() == "Medium" || rDFA.GetStyleName() == "Medium Oblique") &&
fWeight > 0.2)
{
fWeight = 0;
}
// tdf#68889: Ditto for Gill Sans MT Pro. Here I can kinda understand it, maybe the
// kCTFontWeightTrait is intended to give a subjective "optical" impression of how the font
// looks, and Gill Sans MT Pro Medium is kinda heavy. But with the way LibreOffice uses fonts,
// we still should think of it as being "medium" weight.
if (rDFA.GetFamilyName() == "Gill Sans MT Pro" &&
(rDFA.GetStyleName() == "Medium" || rDFA.GetStyleName() == "Medium Italic") &&
fWeight > 0.2)
{
fWeight = 0;
}
if( fWeight > 0 )
{
nInt = rint(int(WEIGHT_NORMAL) + fWeight * ((WEIGHT_BLACK - WEIGHT_NORMAL)/0.68));
if( nInt > WEIGHT_BLACK )
{
nInt = WEIGHT_BLACK;
}
}
else if( fWeight < 0 )
{
nInt = rint(int(WEIGHT_NORMAL) + fWeight * ((WEIGHT_NORMAL - WEIGHT_THIN)/0.8));
if( nInt < WEIGHT_THIN )
{
nInt = WEIGHT_THIN;
}
}
rDFA.SetWeight( static_cast<FontWeight>(nInt) );
// get the font slant
double fSlant = 0;
CFNumberRef pSlantNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait ));
// tdf#140401 check if attribute is a nullptr
if( pSlantNum )
CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant );
if( fSlant >= 0.035 )
{
rDFA.SetItalic( ITALIC_NORMAL );
}
// get width trait
double fWidth = 0;
CFNumberRef pWidthNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontWidthTrait ));
// tdf#140401 check if attribute is a nullptr
if( pWidthNum )
CFNumberGetValue( pWidthNum, kCFNumberDoubleType, &fWidth );
nInt = WIDTH_NORMAL;
if( fWidth > 0 )
{
nInt = rint( int(WIDTH_NORMAL) + fWidth * ((WIDTH_ULTRA_EXPANDED - WIDTH_NORMAL)/0.4));
if( nInt > WIDTH_ULTRA_EXPANDED )
{
nInt = WIDTH_ULTRA_EXPANDED;
}
}
else if( fWidth < 0 )
{
nInt = rint( int(WIDTH_NORMAL) + fWidth * ((WIDTH_NORMAL - WIDTH_ULTRA_CONDENSED)/0.5));
if( nInt < WIDTH_ULTRA_CONDENSED )
{
nInt = WIDTH_ULTRA_CONDENSED;
}
}
rDFA.SetWidthType( static_cast<FontWidth>(nInt) );
// release the attribute dict that we had copied
CFRelease( pAttrDict );
// TODO? also use the HEAD table if available to get more attributes
// CFDataRef CTFontCopyTable( CTFontRef, kCTFontTableHead, /*kCTFontTableOptionNoOptions*/kCTFontTableOptionExcludeSynthetic );
return rDFA;
}
static void fontEnumCallBack( const void* pValue, void* pContext )
{
CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue);
// tdf#163000 don't add any fonts with an 'hvgl' font table
// macOS Sequoia added a new PingFangUI.ttc font file which
// contains all of the PingFang font families. However, any
// fonts loaded from this font file result in the following
// failures:
// - Skia renders font with wrong glyphs
// - Export to PDF contain a damaged embedded font
// Despite the fact that the fonts in this new font file have
// a TrueType font type, they are missing a 'glyf' font table
// and, instead, have a new, undefined 'hvgl' font table. See
// the following link for more details about the new 'hvgl'
// font table:
// https://gitlab.freedesktop.org/freetype/freetype/-/issues/1281
CFNumberRef pFontFormat = static_cast<CFNumberRef>(CTFontDescriptorCopyAttribute(pFD, kCTFontFormatAttribute));
if (pFontFormat)
{
bool bSkipFont = false;
int nFontFormat;
// At least for the PingFangUI.ttc font file, the font format is
// different on macOS and iOS
if (CFNumberGetValue(pFontFormat, kCFNumberIntType, &nFontFormat) && (nFontFormat == kCTFontFormatOpenTypeTrueType || nFontFormat == kCTFontFormatTrueType))
{
CTFontRef pFont = CTFontCreateWithFontDescriptor(pFD, 0.0, nullptr);
if (pFont)
{
CFArrayRef pFontTableTags = CTFontCopyAvailableTables(pFont, kCTFontTableOptionNoOptions);
if (pFontTableTags)
{
bool bGlyfTableFound = false;
bool bHvglTableFound = false;
CFIndex nFontTableTagCount = CFArrayGetCount(pFontTableTags);
for (CFIndex i = 0; i < nFontTableTagCount; i++)
{
CTFontTableTag nTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(pFontTableTags, i));
if (nTag == kCTFontTableGlyf)
{
bGlyfTableFound = true;
break;
}
else if (nTag == 'hvgl')
{
bHvglTableFound = true;
}
}
bSkipFont = !bGlyfTableFound && bHvglTableFound;
CFRelease(pFontTableTags);
}
CFRelease(pFont);
}
}
CFRelease(pFontFormat);
if (bSkipFont)
return;
}
bool bFontEnabled;
FontAttributes rDFA = DevFontFromCTFontDescriptor( pFD, &bFontEnabled );
if( bFontEnabled)
{
rtl::Reference<CoreTextFontFace> pFontData = new CoreTextFontFace( rDFA, pFD );
SystemFontList* pFontList = static_cast<SystemFontList*>(pContext);
pFontList->AddFont( pFontData.get() );
}
}
SystemFontList::SystemFontList()
: mpCTFontCollection( nullptr )
, mpCTFontArray( nullptr )
{}
SystemFontList::~SystemFontList()
{
maFontContainer.clear();
if( mpCTFontArray )
{
CFRelease( mpCTFontArray );
}
if( mpCTFontCollection )
{
CFRelease( mpCTFontCollection );
}
}
void SystemFontList::AddFont( CoreTextFontFace* pFontData )
{
sal_IntPtr nFontId = pFontData->GetFontId();
maFontContainer[ nFontId ] = pFontData;
}
void SystemFontList::AnnounceFonts( vcl::font::PhysicalFontCollection& rFontCollection ) const
{
for(const auto& rEntry : maFontContainer )
{
rFontCollection.Add( rEntry.second.get() );
}
}
CoreTextFontFace* SystemFontList::GetFontDataFromId( sal_IntPtr nFontId ) const
{
auto it = maFontContainer.find( nFontId );
if( it == maFontContainer.end() )
{
return nullptr;
}
return (*it).second.get();
}
bool SystemFontList::Init()
{
// enumerate available system fonts
static const int nMaxDictEntries = 8;
CFMutableDictionaryRef pCFDict = CFDictionaryCreateMutable( nullptr,
nMaxDictEntries,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
CFDictionaryAddValue( pCFDict, kCTFontCollectionRemoveDuplicatesOption, kCFBooleanTrue );
mpCTFontCollection = CTFontCollectionCreateFromAvailableFonts( pCFDict );
CFRelease( pCFDict );
mpCTFontArray = CTFontCollectionCreateMatchingFontDescriptors( mpCTFontCollection );
const int nFontCount = CFArrayGetCount( mpCTFontArray );
const CFRange aFullRange = CFRangeMake( 0, nFontCount );
CFArrayApplyFunction( mpCTFontArray, aFullRange, fontEnumCallBack, this );
return true;
}
std::unique_ptr<SystemFontList> GetCoretextFontList()
{
std::unique_ptr<SystemFontList> pList(new SystemFontList());
if( !pList->Init() )
{
return nullptr;
}
return pList;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */