tdf#162002 DOCX import, font embed: only discard subset fonts with few glyphs

Commit a9f3c11375525a7708378dd3648febc40db1ad20 (tdf#162002 DOCX import:
ignore subsetted embedded fonts for editing, 2024-07-12) decided to
ignore all subsetted fonts for editing, improve this a little so we only
ignore subsetted fonts when they can't even provide an English alphabet
in any form (lowercase, uppercase).

This avoids the possible problem that a font is marked as subsetted but
it's good enough in practice and we would still throw it away for
editing.

Change-Id: I0bc0e14ffc0c039f029220991bd16d9e3254f059
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170570
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
This commit is contained in:
Miklos Vajna 2024-07-16 14:49:23 +02:00
parent 828b26bdc2
commit 09da7fd9ce
6 changed files with 62 additions and 11 deletions

View File

@ -66,7 +66,8 @@ public:
*/
bool addEmbeddedFont( const css::uno::Reference< css::io::XInputStream >& stream,
const OUString& fontName, std::u16string_view extra,
std::vector< unsigned char > const & key, bool eot = false);
std::vector< unsigned char > const & key, bool eot = false,
bool bSubsetted = false);
/**
Returns a URL for a file where to store contents of a given temporary font.

View File

@ -39,6 +39,24 @@ CPPUNIT_TEST_FIXTURE(Test, testSubsettedEmbeddedFont)
// during editing may be missing from the subsetted font:
CPPUNIT_ASSERT(aUrl.isEmpty());
}
CPPUNIT_TEST_FIXTURE(Test, testSubsettedFullEmbeddedFont)
{
#if !defined(MACOSX) // FIXME fails on macOS
// Given a document with an embedded font (marked as subsetted, but otherwise full in practice),
// loaded for editing:
loadFromFile(u"subsetted-full-embedded-font.docx");
// When checking if the font is available:
OUString aUrl = EmbeddedFontsHelper::fontFileUrl(
u"IBM Plex Serif Light", FAMILY_ROMAN, ITALIC_NONE, WEIGHT_NORMAL, PITCH_VARIABLE,
EmbeddedFontsHelper::FontRights::ViewingAllowed);
// Then make sure the subsetted font is available, given that it has the reasonable amount of
// glyphs:
CPPUNIT_ASSERT(!aUrl.isEmpty());
#endif
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -236,11 +236,13 @@ bool FontTable::IsReadOnly() const
void FontTable::addEmbeddedFont(const css::uno::Reference<css::io::XInputStream>& stream,
const OUString& fontName, std::u16string_view extra,
std::vector<unsigned char> const & key)
std::vector<unsigned char> const & key,
bool bSubsetted)
{
if (!m_pImpl->xEmbeddedFontHelper)
m_pImpl->xEmbeddedFontHelper.reset(new EmbeddedFontsHelper);
m_pImpl->xEmbeddedFontHelper->addEmbeddedFont(stream, fontName, extra, key);
m_pImpl->xEmbeddedFontHelper->addEmbeddedFont(stream, fontName, extra, key,
/*eot=*/false, bSubsetted);
}
EmbeddedFontHandler::EmbeddedFontHandler(FontTable& rFontTable, OUString _fontName, std::u16string_view style )
@ -256,11 +258,6 @@ EmbeddedFontHandler::~EmbeddedFontHandler()
if( !m_inputStream.is())
return;
if (m_bSubsetted && !m_fontTable.IsReadOnly())
{
return;
}
std::vector< unsigned char > key( 32 );
if( !m_fontKey.isEmpty())
{ // key for unobfuscating
@ -280,7 +277,9 @@ EmbeddedFontHandler::~EmbeddedFontHandler()
key[ i + 16 ] = val;
}
}
m_fontTable.addEmbeddedFont( m_inputStream, m_fontName, m_style, key );
// Ignore the "subsetted" flag if we're not editing anyway.
bool bSubsetted = m_bSubsetted && !m_fontTable.IsReadOnly();
m_fontTable.addEmbeddedFont( m_inputStream, m_fontName, m_style, key, bSubsetted );
m_inputStream->closeInput();
}

View File

@ -53,7 +53,8 @@ class FontTable : public LoggedProperties, public LoggedTable
void addEmbeddedFont(const css::uno::Reference<css::io::XInputStream>& stream,
const OUString& fontName, std::u16string_view extra,
std::vector<unsigned char> const & key);
std::vector<unsigned char> const & key,
bool bSubsetted);
bool IsReadOnly() const;
private:

View File

@ -66,7 +66,8 @@ void EmbeddedFontsHelper::clearTemporaryFontFiles()
}
bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStream >& stream, const OUString& fontName,
std::u16string_view extra, std::vector< unsigned char > const & key, bool eot )
std::u16string_view extra, std::vector< unsigned char > const & key, bool eot,
bool bSubsetted )
{
OUString fileUrl = EmbeddedFontsHelper::fileUrlForTemporaryFont( fontName, extra );
osl::File file( fileUrl );
@ -159,6 +160,37 @@ bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStrea
osl::File::remove( fileUrl );
return false;
}
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);
return false;
}
}
m_aAccumulatedFonts.emplace_back(std::make_pair(fontName, fileUrl));
return true;
}