xmloff: prevent embedding same font files with checksumming

When embedding fonts to ODF documents we can get into a situation
where the same font is embedded multiple times and this wastes
space. To prevent this we calculate the checksum of the font first
and track the fonts that are embedded. When the font has the same
checksum, the path to already embedded font is returned.

Change-Id: I087c42d9a63b1697b9b4ea985dbce2fda760a996
Reviewed-on: https://gerrit.libreoffice.org/56507
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
This commit is contained in:
Tomaž Vajngerl 2018-06-26 20:06:40 +02:00 committed by Tomaž Vajngerl
parent eeef4ab2f4
commit b262e6e5da
2 changed files with 75 additions and 10 deletions

View File

@ -26,6 +26,7 @@
#include <tools/fontenum.hxx>
#include <salhelper/simplereferenceobject.hxx>
#include <set>
#include <unordered_map>
#include <memory>
class XMLFontAutoStylePool_Impl;
@ -33,13 +34,15 @@ class SvXMLExport;
class XMLOFF_DLLPUBLIC XMLFontAutoStylePool : public salhelper::SimpleReferenceObject
{
private:
SvXMLExport& rExport;
std::unique_ptr<XMLFontAutoStylePool_Impl> m_pFontAutoStylePool;
std::set<OUString> m_aNames;
bool m_bTryToEmbedFonts;
std::unordered_map<OString, OUString> m_aEmbeddedFontFiles;
OUString embedFontFile( const OUString& fontUrl );
OUString embedFontFile(OUString const & rFileUrl, OUString const & rFamilyName);
protected:

View File

@ -34,6 +34,7 @@
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
#include <XMLBase64Export.hxx>
#include <comphelper/hash.hxx>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
@ -367,7 +368,7 @@ void XMLFontAutoStylePool::exportXML()
if (!fontFilesMap.count(sFileUrl))
{
const OUString docUrl = bExportFlat ?
lcl_checkFontFile(sFileUrl) : embedFontFile(sFileUrl);
lcl_checkFontFile(sFileUrl) : embedFontFile(sFileUrl, pEntry->GetFamilyName());
if (!docUrl.isEmpty())
fontFilesMap[sFileUrl] = docUrl;
else
@ -429,10 +430,72 @@ void XMLFontAutoStylePool::exportXML()
}
}
OUString XMLFontAutoStylePool::embedFontFile( const OUString& fileUrl )
OUString getFreeFontName(uno::Reference<embed::XStorage> const & rxStorage, OUString const & rFamilyName)
{
OUString sName;
int nIndex = 1;
do
{
sName = "Font_" +
rFamilyName.replaceAll(" ", "_") + "_" +
OUString::number(nIndex) + ".ttf";
nIndex++;
} while (rxStorage->hasByName(sName));
return sName;
}
OString convertToHashString(std::vector<unsigned char> const & rHash)
{
std::stringstream aStringStream;
for (auto const & rByte : rHash)
{
aStringStream << std::setw(2) << std::setfill('0') << std::hex << int(rByte);
}
return OString(aStringStream.str().c_str());
}
OString getFileHash(OUString const & rFileUrl)
{
OString aHash;
osl::File aFile(rFileUrl);
if (aFile.open(osl_File_OpenFlag_Read) != osl::File::E_None)
return aHash;
comphelper::Hash aHashEngine(comphelper::HashType::SHA512);
for (;;)
{
sal_Int8 aBuffer[4096];
sal_uInt64 nReadSize;
sal_Bool bEof;
if (aFile.isEndOfFile(&bEof) != osl::File::E_None)
{
SAL_WARN("xmloff", "Error reading font file " << rFileUrl);
return aHash;
}
if (bEof)
break;
if (aFile.read(aBuffer, 4096, nReadSize) != osl::File::E_None)
{
SAL_WARN("xmloff", "Error reading font file " << rFileUrl);
return aHash;
}
if (nReadSize == 0)
break;
aHashEngine.update(reinterpret_cast<unsigned char*>(aBuffer), nReadSize);
}
return convertToHashString(aHashEngine.finalize());
}
OUString XMLFontAutoStylePool::embedFontFile(OUString const & fileUrl, OUString const & rFamilyName)
{
try
{
OString sHashString = getFileHash(fileUrl);
if (m_aEmbeddedFontFiles.find(sHashString) != m_aEmbeddedFontFiles.end())
return m_aEmbeddedFontFiles.at(sHashString);
osl::File file( fileUrl );
if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None )
return OUString();
@ -443,12 +506,9 @@ OUString XMLFontAutoStylePool::embedFontFile( const OUString& fileUrl )
uno::Reference< embed::XStorage > storage;
storage.set( GetExport().GetTargetStorage()->openStorageElement( "Fonts",
::embed::ElementModes::WRITE ), uno::UNO_QUERY_THROW );
int index = 0;
OUString name;
do
{
name = "font" + OUString::number( ++index ) + ".ttf";
} while( storage->hasByName( name ) );
OUString name = getFreeFontName(storage, rFamilyName);
uno::Reference< io::XOutputStream > outputStream;
outputStream.set( storage->openStreamElement( name, ::embed::ElementModes::WRITE ), UNO_QUERY_THROW );
uno::Reference < beans::XPropertySet > propertySet( outputStream, uno::UNO_QUERY );
@ -484,7 +544,9 @@ OUString XMLFontAutoStylePool::embedFontFile( const OUString& fileUrl )
if( transaction.is())
{
transaction->commit();
return "Fonts/" + name;
OUString sInternalName = "Fonts/" + name;
m_aEmbeddedFontFiles.emplace(sHashString, sInternalName);
return sInternalName;
}
}
} catch( const Exception& e )