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, bool addEmbeddedFont( const css::uno::Reference< css::io::XInputStream >& stream,
const OUString& fontName, std::u16string_view extra, 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. 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: // during editing may be missing from the subsetted font:
CPPUNIT_ASSERT(aUrl.isEmpty()); 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: */ /* 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, void FontTable::addEmbeddedFont(const css::uno::Reference<css::io::XInputStream>& stream,
const OUString& fontName, std::u16string_view extra, 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) if (!m_pImpl->xEmbeddedFontHelper)
m_pImpl->xEmbeddedFontHelper.reset(new EmbeddedFontsHelper); 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 ) EmbeddedFontHandler::EmbeddedFontHandler(FontTable& rFontTable, OUString _fontName, std::u16string_view style )
@ -256,11 +258,6 @@ EmbeddedFontHandler::~EmbeddedFontHandler()
if( !m_inputStream.is()) if( !m_inputStream.is())
return; return;
if (m_bSubsetted && !m_fontTable.IsReadOnly())
{
return;
}
std::vector< unsigned char > key( 32 ); std::vector< unsigned char > key( 32 );
if( !m_fontKey.isEmpty()) if( !m_fontKey.isEmpty())
{ // key for unobfuscating { // key for unobfuscating
@ -280,7 +277,9 @@ EmbeddedFontHandler::~EmbeddedFontHandler()
key[ i + 16 ] = val; 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(); 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, void addEmbeddedFont(const css::uno::Reference<css::io::XInputStream>& stream,
const OUString& fontName, std::u16string_view extra, const OUString& fontName, std::u16string_view extra,
std::vector<unsigned char> const & key); std::vector<unsigned char> const & key,
bool bSubsetted);
bool IsReadOnly() const; bool IsReadOnly() const;
private: private:

View File

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