Don't write multiple copies of identical embedded font files
Before the change, ODF import created unique font name every time, using '?' as extra; that meant that every document with embedded fonts would create and register its fonts, even when it was already registered by a different document. On the other hand, OOXML import would rely on file name match, which might not guarantee that the files were actually identical. This change compares the existing file content, which would only happen when the files are of the same size. This also writes the file only after doing all the checks. Change-Id: I2377bd6c24291d9b4113607b4d48a223187b635c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189270 Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com> Tested-by: Jenkins
This commit is contained in:
@@ -28,16 +28,6 @@ class VCL_DLLPUBLIC EmbeddedFontsHelper
|
||||
private:
|
||||
std::vector<std::pair<OUString, OUString>> m_aAccumulatedFonts;
|
||||
|
||||
/**
|
||||
Returns a URL for a file where to store contents of a given temporary font.
|
||||
The file may or not may not exist yet, and will be cleaned up automatically as appropriate.
|
||||
Use activateTemporaryFont() to actually enable usage of the font.
|
||||
|
||||
@param fontName name of the font (e.g. 'Times New Roman')
|
||||
@param extra additional text to use for name (e.g. to distinguish regular from bold, italic,...), "?" for unique
|
||||
*/
|
||||
static OUString fileUrlForTemporaryFont(const OUString& fontName, std::u16string_view extra);
|
||||
|
||||
/**
|
||||
Adds the accumulated fonts to the list of known fonts. The fonts are used only until application
|
||||
exit.
|
||||
|
||||
@@ -70,6 +70,85 @@ void clearDir( const OUString& path )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a URL for a file where to store contents of a given temporary font.
|
||||
// The file may or not may not exist yet, and will be cleaned up automatically as appropriate.
|
||||
// Use activateFonts() to actually enable usage of the font.
|
||||
OUString fileUrlForTemporaryFont(std::u16string_view name)
|
||||
{
|
||||
OUString filename = OUString::Concat(name) + ".ttf"; // TODO is it always ttf?
|
||||
|
||||
if (!comphelper::OStorageHelper::IsValidZipEntryFileName(filename, false))
|
||||
{
|
||||
SAL_WARN("vcl.fonts", "Cannot use filename: " << filename << " for temporary font");
|
||||
static int uniqueCounter = 0;
|
||||
filename = "font" + OUString::number(uniqueCounter++) + ".ttf";
|
||||
}
|
||||
|
||||
OUString path = GetEmbeddedFontsRoot() + "fromdocs/";
|
||||
osl::Directory::createPath(path);
|
||||
return path
|
||||
+ rtl::Uri::encode(filename, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
|
||||
RTL_TEXTENCODING_UTF8);
|
||||
}
|
||||
|
||||
// Returns true when the file was written, or exactly same data was existing.
|
||||
//
|
||||
// @param name name of the font file
|
||||
// @param url returns URL of a new file, or empty string (when the same file already existed).
|
||||
bool writeFontBytesToFile(const std::vector<char>& bytes, std::u16string_view name, OUString& url)
|
||||
{
|
||||
url = fileUrlForTemporaryFont(name);
|
||||
std::optional<osl::File> file(url);
|
||||
auto rc = file->open(osl_File_OpenFlag_Create | osl_File_OpenFlag_Write);
|
||||
|
||||
// Check if existing file is the same
|
||||
for (int counter = 0; rc == osl::File::E_EXIST;)
|
||||
{
|
||||
if (file->open(osl_File_OpenFlag_Read | osl_File_OpenFlag_NoLock) == osl::File::E_None)
|
||||
{
|
||||
if (sal_uInt64 size; file->getSize(size) == osl::File::E_None && size == bytes.size())
|
||||
{
|
||||
std::vector<char> bytes2(bytes.size());
|
||||
sal_uInt64 readTotal = 0;
|
||||
while (readTotal < bytes.size())
|
||||
{
|
||||
sal_uInt64 read = 0;
|
||||
rc = file->read(bytes2.data() + readTotal, bytes.size() - readTotal, read);
|
||||
if (read == 0)
|
||||
break;
|
||||
readTotal += read;
|
||||
}
|
||||
if (rc == osl::File::E_None && bytes2 == bytes)
|
||||
{
|
||||
url.clear();
|
||||
return true; // OK, it's the same bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
url = fileUrlForTemporaryFont(Concat2View(name + OUString::number(counter++)));
|
||||
file.emplace(url);
|
||||
rc = file->open(osl_File_OpenFlag_Create | osl_File_OpenFlag_Write);
|
||||
}
|
||||
|
||||
if (rc != osl::File::E_None)
|
||||
return false;
|
||||
|
||||
sal_uInt64 writtenTotal = 0;
|
||||
while (writtenTotal < bytes.size())
|
||||
{
|
||||
sal_uInt64 written = 0;
|
||||
file->write(bytes.data() + writtenTotal, bytes.size() - writtenTotal, written);
|
||||
if (written == 0)
|
||||
{
|
||||
file->close();
|
||||
osl::File::remove(file->getURL());
|
||||
return false;
|
||||
}
|
||||
writtenTotal += written;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedFontsHelper::clearTemporaryFontFiles()
|
||||
@@ -83,18 +162,6 @@ bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStrea
|
||||
std::u16string_view extra, std::vector< unsigned char > const & key, bool eot,
|
||||
bool bSubsetted )
|
||||
{
|
||||
OUString fileUrl = EmbeddedFontsHelper::fileUrlForTemporaryFont( fontName, extra );
|
||||
osl::File file( fileUrl );
|
||||
switch( file.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_Write ))
|
||||
{
|
||||
case osl::File::E_None:
|
||||
break; // ok
|
||||
case osl::File::E_EXIST:
|
||||
return true; // Assume it's already been added correctly.
|
||||
default:
|
||||
SAL_WARN( "vcl.fonts", "Cannot open file for temporary font" );
|
||||
return false;
|
||||
}
|
||||
size_t keyPos = 0;
|
||||
std::vector< char > fontData;
|
||||
fontData.reserve( 1000000 );
|
||||
@@ -108,16 +175,6 @@ bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStrea
|
||||
++pos )
|
||||
bufferRange[ pos ] ^= key[ keyPos++ ];
|
||||
// if eot, don't write the file out yet, since we need to unpack it first.
|
||||
if( !eot && read > 0 )
|
||||
{
|
||||
sal_uInt64 writtenTotal = 0;
|
||||
while( writtenTotal < read )
|
||||
{
|
||||
sal_uInt64 written;
|
||||
file.write( buffer.getConstArray(), read, written );
|
||||
writtenTotal += written;
|
||||
}
|
||||
}
|
||||
fontData.insert( fontData.end(), buffer.getConstArray(), buffer.getConstArray() + read );
|
||||
if( read <= 0 )
|
||||
break;
|
||||
@@ -135,32 +192,16 @@ bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStrea
|
||||
if( uncompressError != libeot::EOT_SUCCESS )
|
||||
{
|
||||
SAL_WARN( "vcl.fonts", "Failed to uncompress font" );
|
||||
osl::File::remove( fileUrl );
|
||||
return false;
|
||||
}
|
||||
sal_uInt64 writtenTotal = 0;
|
||||
while( writtenTotal < uncompressedFontSize )
|
||||
{
|
||||
sal_uInt64 written;
|
||||
if( file.write( uncompressedFont.get() + writtenTotal, uncompressedFontSize - writtenTotal, written ) != osl::File::E_None )
|
||||
{
|
||||
SAL_WARN( "vcl.fonts", "Error writing temporary font file" );
|
||||
osl::File::remove( fileUrl );
|
||||
return false;
|
||||
}
|
||||
writtenTotal += written;
|
||||
}
|
||||
fontData.clear();
|
||||
fontData.insert(fontData.end(), reinterpret_cast<char*>(uncompressedFont.get()),
|
||||
reinterpret_cast<char*>(uncompressedFont.get() + uncompressedFontSize));
|
||||
sufficientFontRights = libeot::EOTcanLegallyEdit( &eotMetadata );
|
||||
libeot::EOTfreeMetadata( &eotMetadata );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( file.close() != osl::File::E_None )
|
||||
{
|
||||
SAL_WARN( "vcl.fonts", "Writing temporary font file failed" );
|
||||
osl::File::remove( fileUrl );
|
||||
return false;
|
||||
}
|
||||
if( !eot )
|
||||
{
|
||||
sufficientFontRights = sufficientTTFRights(fontData.data(), fontData.size(), FontRights::EditingAllowed);
|
||||
@@ -171,7 +212,6 @@ bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStrea
|
||||
// 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 );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -200,12 +240,15 @@ bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStrea
|
||||
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));
|
||||
OUString fileUrl;
|
||||
if (!writeFontBytesToFile(fontData, Concat2View(fontName + extra), fileUrl))
|
||||
return false;
|
||||
if (!fileUrl.isEmpty())
|
||||
m_aAccumulatedFonts.emplace_back(std::make_pair(fontName, fileUrl));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -236,29 +279,6 @@ void EmbeddedFontsHelper::activateFonts()
|
||||
m_aAccumulatedFonts.clear();
|
||||
}
|
||||
|
||||
OUString EmbeddedFontsHelper::fileUrlForTemporaryFont( const OUString& fontName, std::u16string_view extra )
|
||||
{
|
||||
OUString filename = fontName;
|
||||
static int uniqueCounter = 0;
|
||||
if( extra == u"?" )
|
||||
filename += OUString::number( uniqueCounter++ );
|
||||
else
|
||||
filename += extra;
|
||||
filename += ".ttf"; // TODO is it always ttf?
|
||||
|
||||
if (!::comphelper::OStorageHelper::IsValidZipEntryFileName(filename, false))
|
||||
{
|
||||
SAL_WARN( "vcl.fonts", "Cannot use filename: " << filename << " for temporary font");
|
||||
filename = "font" + OUString::number(uniqueCounter++) + ".ttf";
|
||||
}
|
||||
|
||||
OUString path = GetEmbeddedFontsRoot() + "fromdocs/";
|
||||
osl::Directory::createPath( path );
|
||||
return path
|
||||
+ rtl::Uri::encode(filename, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
|
||||
RTL_TEXTENCODING_UTF8);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -155,6 +155,13 @@ OUString XMLFontStyleContextFontFace::familyName() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
OUString XMLFontStyleContextFontFace::styleName() const
|
||||
{
|
||||
OUString ret;
|
||||
aStyleName >>= ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
XMLFontStyleContextFontFaceFormat::XMLFontStyleContextFontFaceFormat( SvXMLImport& rImport,
|
||||
XMLFontStyleContextFontFaceUri& _uri )
|
||||
@@ -289,7 +296,7 @@ void XMLFontStyleContextFontFaceUri::handleEmbeddedFont( const OUString& url, bo
|
||||
uno::Reference< io::XInputStream > inputStream;
|
||||
inputStream.set( storage->openStreamElement( url.copy( url.indexOf( '/' ) + 1 ), ::embed::ElementModes::READ ),
|
||||
UNO_QUERY_THROW );
|
||||
if (GetImport().addEmbeddedFont(inputStream, fontName, u"?", std::vector< unsigned char >(), eot))
|
||||
if (GetImport().addEmbeddedFont(inputStream, fontName, font.styleName(), std::vector< unsigned char >(), eot))
|
||||
GetImport().NotifyContainsEmbeddedFont();
|
||||
inputStream->closeInput();
|
||||
}
|
||||
@@ -301,7 +308,7 @@ void XMLFontStyleContextFontFaceUri::handleEmbeddedFont( const ::css::uno::Seque
|
||||
{
|
||||
const uno::Reference< io::XInputStream > xInput( new comphelper::SequenceInputStream( rData ) );
|
||||
const OUString fontName = font.familyName();
|
||||
if (GetImport().addEmbeddedFont(xInput, fontName, u"?", std::vector< unsigned char >(), eot))
|
||||
if (GetImport().addEmbeddedFont(xInput, fontName, font.styleName(), std::vector< unsigned char >(), eot))
|
||||
GetImport().NotifyContainsEmbeddedFont();
|
||||
xInput->closeInput();
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
sal_Int32 nCharsetIdx ) const;
|
||||
|
||||
OUString familyName() const;
|
||||
OUString styleName() const;
|
||||
|
||||
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
|
||||
sal_Int32 nElement,
|
||||
|
||||
Reference in New Issue
Block a user