/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
if( file.close() != osl::File::E_None )
{
SAL_WARN( "vcl.fonts", "Writing temporary font file failed" );
osl::File::remove( fileUrl ); returnfalse;
} if( !eot )
{
sufficientFontRights = sufficientTTFRights(fontData.data(), fontData.size(), FontRights::EditingAllowed);
} if( !sufficientFontRights )
{ // It would be actually better to open the document in read-only mode in this case, // warn the user about this, and provide a button to drop the font(s) in order // to switch to editing.
SAL_INFO( "vcl.fonts", "Ignoring embedded font that is not usable for editing" );
osl::File::remove( fileUrl ); returnfalse;
}
if (bSubsetted)
{
TrueTypeFont* font;
sal_uInt32 nGlyphs = 0; if (OpenTTFontBuffer(fontData.data(), fontData.size(), 0, &font) == SFErrCodes::Ok)
{
sal_uInt32 nGlyphCount = font->glyphCount(); for (sal_uInt32 i = 0; i < nGlyphCount; ++i)
{
sal_uInt32 nOffset = font->glyphOffset(i);
sal_uInt32 nNextOffset = font->glyphOffset(i + 1); if (nOffset == nNextOffset)
{ // GetTTGlyphComponents() says this is an empty glyph, ignore it. continue;
}
++nGlyphs;
}
CloseTTFont(font);
} // Check if it has reasonable amount of glyphs, set the limit to the number of glyphs in the // English alphabet (not differentiating lowercase and uppercase). if (nGlyphs < 26)
{
SAL_INFO("vcl.fonts", "Ignoring embedded font that only provides " << nGlyphs << " non-empty glyphs");
osl::File::remove(fileUrl); returnfalse;
}
}
// Check if it's (legally) allowed to embed the font file into a document // (ttf has a flag allowing this). PhysicalFontFace::IsEmbeddable() appears // to have a different meaning (guessing from code, IsSubsettable() might // possibly mean it's ttf, while IsEmbeddable() might mean it's type1). // So just try to open the data as ttf and see. bool EmbeddedFontsHelper::sufficientTTFRights( constvoid* data, tools::Long size, FontRights rights )
{
TrueTypeFont* font; if( OpenTTFontBuffer( data, size, 0 /*TODO*/, &font ) == SFErrCodes::Ok )
{
TTGlobalFontInfo info;
GetTTGlobalFontInfo( font, &info );
CloseTTFont( font ); // https://www.microsoft.com/typography/otspec/os2.htm#fst int copyright = info.typeFlags; switch( rights )
{ case FontRights::ViewingAllowed: // Embedding not restricted completely. return ( copyright & 0x02 ) != 0x02; case FontRights::EditingAllowed: // Font is installable or editable. return copyright == 0 || ( copyright & 0x08 );
}
} returntrue; // no known restriction
}
OUString EmbeddedFontsHelper::fontFileUrl( std::u16string_view familyName, FontFamily family, FontItalic italic,
FontWeight weight, FontPitch pitch, FontRights rights )
{
OUString path = u"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}"_ustr;
rtl::Bootstrap::expandMacros( path );
path += "/user/temp/embeddedfonts/fromsystem/";
osl::Directory::createPath( path );
OUString filename = OUString::Concat(familyName) + "_" + OUString::number( family ) + "_" + OUString::number( italic )
+ "_" + OUString::number( weight ) + "_" + OUString::number( pitch )
+ ".ttf"; // TODO is it always ttf?
OUString url = path + filename; if( osl::File( url ).open( osl_File_OpenFlag_Read ) == osl::File::E_None ) // = exists()
{ // File with contents of the font file already exists, assume it's been created by a previous call. return url;
} bool ok = false;
SalGraphics* graphics = Application::GetDefaultDevice()->GetGraphics();
vcl::font::PhysicalFontCollection fonts;
graphics->GetDevFontList( &fonts );
std::unique_ptr< vcl::font::PhysicalFontFaceCollection > fontInfo( fonts.GetFontFaceCollection());
vcl::font::PhysicalFontFace* selected = nullptr;
// Maybe we don't find the perfect match for the font. E.G. we have fonts with the same family name // but not same bold or italic etc... // In this case we add all the fonts having the family name of the used font: // - we store all these fonts in familyNameFonts during loop // - if we haven't found the perfect match we store all fonts in familyNameFonts typedef std::vector<vcl::font::PhysicalFontFace*> FontList;
FontList familyNameFonts;
for( int i = 0;
i < fontInfo->Count();
++i )
{
vcl::font::PhysicalFontFace* f = fontInfo->Get( i ); if( f->GetFamilyName() == familyName )
{ // Ignore comparing text encodings, at least for now. They cannot be trivially compared // (e.g. UCS2 and UTF8 are technically the same characters, just have different encoding, // and just having a unicode font doesn't say what glyphs it actually contains). // It is possible that it still may be needed to do at least some checks here // for some encodings (can one font have more font files for more encodings?). if(( family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
&& ( italic == ITALIC_DONTKNOW || f->GetItalic() == italic )
&& ( weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
&& ( pitch == PITCH_DONTKNOW || f->GetPitch() == pitch ))
{ // Exact match, return it immediately.
selected = f; break;
} if(( f->GetFamilyType() == FAMILY_DONTKNOW || family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
&& ( f->GetItalic() == ITALIC_DONTKNOW || italic == ITALIC_DONTKNOW || f->GetItalic() == italic )
&& ( f->GetWeight() == WEIGHT_DONTKNOW || weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
&& ( f->GetPitch() == PITCH_DONTKNOW || pitch == PITCH_DONTKNOW || f->GetPitch() == pitch ))
{ // Some fonts specify 'DONTKNOW' for some things, still a good match, if we don't find a better one.
selected = f;
} // adding "not perfect match" to familyNameFonts vector
familyNameFonts.push_back(f);
}
}
// if we have found a perfect match we will add only "selected", otherwise all familyNameFonts
FontList fontsToAdd = (selected ? FontList(1, selected) : std::move(familyNameFonts));
for (vcl::font::PhysicalFontFace* f : fontsToAdd)
{ if (!selected) { // recalculate file not for "not perfect match"
filename = OUString::Concat(familyName) + "_" + OUString::number(f->GetFamilyType()) + "_" +
OUString::number(f->GetItalic()) + "_" + OUString::number(f->GetWeight()) + "_" +
OUString::number(f->GetPitch()) + ".ttf"; // TODO is it always ttf?
url = path + filename; if (osl::File(url).open(osl_File_OpenFlag_Read) == osl::File::E_None) // = exists()
{ // File with contents of the font file already exists, assume it's been created by a previous call. continue;
}
} auto aFontData(f->GetRawFontData(0)); if (!aFontData.empty())
{ auto data = aFontData.data(); auto size = aFontData.size(); if( sufficientTTFRights( data, size, rights ))
{
osl::File file( url ); if( file.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) == osl::File::E_None )
{
sal_uInt64 written = 0;
sal_uInt64 totalSize = size; bool error = false; while( written < totalSize && !error)
{
sal_uInt64 nowWritten; switch( file.write( data + written, size - written, nowWritten ))
{ case osl::File::E_None:
written += nowWritten; break; case osl::File::E_AGAIN: case osl::File::E_INTR: break; default:
error = true; break;
}
}
file.close(); if( error )
osl::File::remove( url ); else
ok = true;
}
}
}
} return ok ? url : u""_ustr;
}
bool EmbeddedFontsHelper::isCommonFont(std::u16string_view aFontName)
{ static constexpr auto aCommonFontsList = frozen::make_unordered_set<std::u16string_view>({ // LO Common
u"Liberation Sans",
u"Liberation Serif",
u"Liberation Sans Narrow",
u"Liberation Mono",
u"Caladea",
u"Carlito", // MSO
u"Times New Roman",
u"Arial",
u"Arial Narrow",
u"Courier New",
u"Cambria",
u"Calibri",
});
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.