nss: upgrade to release 3.73
[LibreOffice.git] / xmloff / source / style / XMLFontAutoStylePool.cxx
blob248bb2bc8dcf534555e9cd5ba9a25bfbc494f205
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <o3tl/sorted_vector.hxx>
21 #include <tools/fontenum.hxx>
22 #include <xmloff/xmlnamespace.hxx>
23 #include <xmloff/xmltoken.hxx>
24 #include <xmloff/xmluconv.hxx>
25 #include "fonthdl.hxx"
26 #include <xmloff/xmlexp.hxx>
27 #include <xmloff/XMLFontAutoStylePool.hxx>
28 #include <vcl/embeddedfontshelper.hxx>
29 #include <osl/file.hxx>
30 #include <sal/log.hxx>
31 #include <tools/diagnose_ex.h>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/embed/ElementModes.hpp>
35 #include <com/sun/star/embed/XTransactedObject.hpp>
36 #include <com/sun/star/embed/XStorage.hpp>
37 #include <com/sun/star/frame/XModel.hpp>
38 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
39 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
40 #include <com/sun/star/style/XStyle.hpp>
41 #include <com/sun/star/container/XNameAccess.hpp>
43 #include <XMLBase64Export.hxx>
44 #include <AutoStyleEntry.hxx>
45 #include <comphelper/hash.hxx>
47 using namespace ::com::sun::star;
48 using namespace ::com::sun::star::uno;
49 using namespace ::xmloff::token;
51 namespace {
53 class XMLFontAutoStylePoolEntry_Impl
55 OUString sName;
56 OUString sFamilyName;
57 OUString sStyleName;
58 FontFamily nFamily;
59 FontPitch nPitch;
60 rtl_TextEncoding eEnc;
62 public:
64 inline XMLFontAutoStylePoolEntry_Impl(
65 const OUString& rName,
66 const OUString& rFamilyName,
67 const OUString& rStyleName,
68 FontFamily nFamily,
69 FontPitch nPitch,
70 rtl_TextEncoding eEnc );
72 inline XMLFontAutoStylePoolEntry_Impl(
73 const OUString& rFamilyName,
74 const OUString& rStyleName,
75 FontFamily nFamily,
76 FontPitch nPitch,
77 rtl_TextEncoding eEnc );
79 const OUString& GetName() const { return sName; }
80 const OUString& GetFamilyName() const { return sFamilyName; }
81 const OUString& GetStyleName() const { return sStyleName; }
82 FontFamily GetFamily() const { return nFamily; }
83 FontPitch GetPitch() const { return nPitch; }
84 rtl_TextEncoding GetEncoding() const { return eEnc; }
89 inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
90 const OUString& rName,
91 const OUString& rFamilyName,
92 const OUString& rStyleName,
93 FontFamily nFam,
94 FontPitch nP,
95 rtl_TextEncoding eE ) :
96 sName( rName ),
97 sFamilyName( rFamilyName ),
98 sStyleName( rStyleName ),
99 nFamily( nFam ),
100 nPitch( nP ),
101 eEnc( eE )
105 inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
106 const OUString& rFamilyName,
107 const OUString& rStyleName,
108 FontFamily nFam,
109 FontPitch nP,
110 rtl_TextEncoding eE ) :
111 sFamilyName( rFamilyName ),
112 sStyleName( rStyleName ),
113 nFamily( nFam ),
114 nPitch( nP ),
115 eEnc( eE )
119 namespace {
121 struct XMLFontAutoStylePoolEntryCmp_Impl {
122 bool operator()(
123 std::unique_ptr<XMLFontAutoStylePoolEntry_Impl> const& r1,
124 std::unique_ptr<XMLFontAutoStylePoolEntry_Impl> const& r2 ) const
126 bool bEnc1(r1->GetEncoding() != RTL_TEXTENCODING_SYMBOL);
127 bool bEnc2(r2->GetEncoding() != RTL_TEXTENCODING_SYMBOL);
128 if( bEnc1 != bEnc2 )
129 return bEnc1 < bEnc2;
130 else if( r1->GetPitch() != r2->GetPitch() )
131 return r1->GetPitch() < r2->GetPitch();
132 else if( r1->GetFamily() != r2->GetFamily() )
133 return r1->GetFamily() < r2->GetFamily();
134 else
136 sal_Int32 nCmp = r1->GetFamilyName().compareTo( r2->GetFamilyName() );
137 if( 0 == nCmp )
138 return r1->GetStyleName().compareTo( r2->GetStyleName() ) < 0;
139 else
140 return nCmp < 0;
147 class XMLFontAutoStylePool_Impl : public o3tl::sorted_vector<std::unique_ptr<XMLFontAutoStylePoolEntry_Impl>, XMLFontAutoStylePoolEntryCmp_Impl>
151 XMLFontAutoStylePool::XMLFontAutoStylePool(SvXMLExport& rExp, bool bTryToEmbedFonts) :
152 rExport( rExp ),
153 m_pFontAutoStylePool( new XMLFontAutoStylePool_Impl ),
154 m_bTryToEmbedFonts( bTryToEmbedFonts ),
155 m_bEmbedUsedOnly(false),
156 m_bEmbedLatinScript(true),
157 m_bEmbedAsianScript(true),
158 m_bEmbedComplexScript(true)
162 XMLFontAutoStylePool::~XMLFontAutoStylePool()
166 OUString XMLFontAutoStylePool::Add(
167 const OUString& rFamilyName,
168 const OUString& rStyleName,
169 FontFamily nFamily,
170 FontPitch nPitch,
171 rtl_TextEncoding eEnc )
173 OUString sPoolName;
174 XMLFontAutoStylePoolEntry_Impl aTmp( rFamilyName, rStyleName, nFamily,
175 nPitch, eEnc );
176 XMLFontAutoStylePool_Impl::const_iterator it = m_pFontAutoStylePool->find( &aTmp );
177 if( it != m_pFontAutoStylePool->end() )
179 sPoolName = (*it)->GetName();
181 else
183 OUString sName;
184 sal_Int32 nLen = rFamilyName.indexOf( ';' );
185 if( -1 == nLen )
187 sName = rFamilyName;
189 else if( nLen > 0 )
191 sName = rFamilyName.copy( 0, nLen );
192 sName = sName.trim();
195 if( sName.isEmpty() )
196 sName = "F";
198 if( m_aNames.find(sName) != m_aNames.end() )
200 sal_Int32 nCount = 1;
201 OUString sPrefix( sName );
202 sName = sPrefix + OUString::number( nCount );
203 while( m_aNames.find(sName) != m_aNames.end() )
205 sName = sPrefix + OUString::number( ++nCount );
209 std::unique_ptr<XMLFontAutoStylePoolEntry_Impl> pEntry(
210 new XMLFontAutoStylePoolEntry_Impl( sName, rFamilyName, rStyleName,
211 nFamily, nPitch, eEnc ));
212 m_pFontAutoStylePool->insert( std::move(pEntry) );
213 m_aNames.insert(sName);
216 return sPoolName;
219 OUString XMLFontAutoStylePool::Find(
220 const OUString& rFamilyName,
221 const OUString& rStyleName,
222 FontFamily nFamily,
223 FontPitch nPitch,
224 rtl_TextEncoding eEnc ) const
226 OUString sName;
227 XMLFontAutoStylePoolEntry_Impl aTmp( rFamilyName, rStyleName, nFamily,
228 nPitch, eEnc );
229 XMLFontAutoStylePool_Impl::const_iterator it = m_pFontAutoStylePool->find( &aTmp );
230 if( it != m_pFontAutoStylePool->end() )
232 sName = (*it)->GetName();
235 return sName;
238 namespace
241 OUString lcl_checkFontFile( const OUString &fileUrl )
243 osl::DirectoryItem aDirItem;
244 if( osl::DirectoryItem::get( fileUrl, aDirItem ) == osl::File::E_None )
246 osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
247 if( aDirItem.getFileStatus( aStatus ) == osl::File::E_None )
249 if( !aStatus.isDirectory() )
250 return fileUrl;
253 return OUString();
256 /// Contains information about a single variant of an embedded font.
257 struct EmbeddedFontInfo
259 OUString aURL;
260 FontWeight eWeight = WEIGHT_NORMAL;
261 FontItalic eItalic = ITALIC_NONE;
264 /// Converts FontWeight to CSS-compatible string representation.
265 OUString FontWeightToString(FontWeight eWeight)
267 OUString aRet;
269 switch (eWeight)
271 case WEIGHT_BOLD:
272 aRet = "bold";
273 break;
274 default:
275 aRet = "normal";
276 break;
279 return aRet;
282 /// Converts FontItalic to CSS-compatible string representation.
283 OUString FontItalicToString(FontItalic eWeight)
285 OUString aRet;
287 switch (eWeight)
289 case ITALIC_NORMAL:
290 aRet = "italic";
291 break;
292 default:
293 aRet = "normal";
294 break;
297 return aRet;
302 std::unordered_set<OUString> XMLFontAutoStylePool::getUsedFontList()
304 std::unordered_set<OUString> aReturnSet;
306 uno::Reference<style::XStyleFamiliesSupplier> xFamiliesSupp(GetExport().GetModel(), UNO_QUERY);
307 if (!xFamiliesSupp.is())
308 return aReturnSet;
310 // Check styles first
311 uno::Reference<container::XNameAccess> xFamilies(xFamiliesSupp->getStyleFamilies());
312 if (xFamilies.is())
314 const uno::Sequence<OUString> aFamilyNames = xFamilies->getElementNames();
315 for (OUString const & sFamilyName : aFamilyNames)
317 uno::Reference<container::XNameAccess> xStyleContainer;
318 xFamilies->getByName(sFamilyName) >>= xStyleContainer;
320 if (xStyleContainer.is())
322 const uno::Sequence<OUString> aStyleNames = xStyleContainer->getElementNames();
323 for (OUString const & rName : aStyleNames)
325 uno::Reference<style::XStyle> xStyle;
326 xStyleContainer->getByName(rName) >>= xStyle;
327 if (xStyle->isInUse())
329 uno::Reference<beans::XPropertySet> xPropertySet(xStyle, UNO_QUERY);
330 if (xPropertySet.is())
332 uno::Reference<beans::XPropertySetInfo> xInfo(xPropertySet->getPropertySetInfo());
333 if (m_bEmbedLatinScript && xInfo->hasPropertyByName("CharFontName"))
335 OUString sCharFontName;
336 Any aFontAny = xPropertySet->getPropertyValue("CharFontName");
337 aFontAny >>= sCharFontName;
338 if (!sCharFontName.isEmpty())
339 aReturnSet.insert(sCharFontName);
341 if (m_bEmbedAsianScript && xInfo->hasPropertyByName("CharFontNameAsian"))
343 OUString sCharFontNameAsian;
344 Any aFontAny = xPropertySet->getPropertyValue("CharFontNameAsian");
345 aFontAny >>= sCharFontNameAsian;
346 if (!sCharFontNameAsian.isEmpty())
347 aReturnSet.insert(sCharFontNameAsian);
349 if (m_bEmbedComplexScript && xInfo->hasPropertyByName("CharFontNameComplex"))
351 OUString sCharFontNameComplex;
352 Any aFontAny = xPropertySet->getPropertyValue("CharFontNameComplex");
353 aFontAny >>= sCharFontNameComplex;
354 if (!sCharFontNameComplex.isEmpty())
355 aReturnSet.insert(sCharFontNameComplex);
364 // make sure auto-styles are collected
365 GetExport().collectAutoStyles();
367 // Check auto-styles for fonts
368 std::vector<xmloff::AutoStyleEntry> aAutoStyleEntries = GetExport().GetAutoStylePool()->GetAutoStyleEntries();
369 for (auto const & rAutoStyleEntry : aAutoStyleEntries)
371 for (auto const & rPair : rAutoStyleEntry.m_aXmlProperties)
373 if (rPair.first == "font-name" ||
374 rPair.first == "font-weight-asian" ||
375 rPair.first == "font-weight-complex")
377 if (rPair.second.has<OUString>())
379 OUString sFontName = rPair.second.get<OUString>();
380 if (!sFontName.isEmpty())
381 aReturnSet.insert(sFontName);
387 return aReturnSet;
390 void XMLFontAutoStylePool::exportXML()
392 SvXMLElementExport aElem(GetExport(), XML_NAMESPACE_OFFICE,
393 XML_FONT_FACE_DECLS,
394 true, true);
395 Any aAny;
396 OUString sTmp;
397 XMLFontFamilyNamePropHdl aFamilyNameHdl;
398 XMLFontFamilyPropHdl aFamilyHdl;
399 XMLFontPitchPropHdl aPitchHdl;
400 XMLFontEncodingPropHdl aEncHdl;
401 const SvXMLUnitConverter& rUnitConv = GetExport().GetMM100UnitConverter();
403 std::map<OUString, OUString> fontFilesMap; // our url to document url
405 std::unordered_set<OUString> aUsedFontNames;
406 if (m_bEmbedUsedOnly)
407 aUsedFontNames = getUsedFontList();
409 for (const auto& pEntry : *m_pFontAutoStylePool)
411 GetExport().AddAttribute(XML_NAMESPACE_STYLE, XML_NAME, pEntry->GetName());
413 aAny <<= pEntry->GetFamilyName();
414 if (aFamilyNameHdl.exportXML(sTmp, aAny, rUnitConv))
415 GetExport().AddAttribute(XML_NAMESPACE_SVG,
416 XML_FONT_FAMILY, sTmp);
418 const OUString& rStyleName = pEntry->GetStyleName();
419 if (!rStyleName.isEmpty())
420 GetExport().AddAttribute(XML_NAMESPACE_STYLE,
421 XML_FONT_ADORNMENTS,
422 rStyleName);
424 aAny <<= static_cast<sal_Int16>(pEntry->GetFamily());
425 if (aFamilyHdl.exportXML(sTmp, aAny, rUnitConv))
427 GetExport().AddAttribute(XML_NAMESPACE_STYLE,
428 XML_FONT_FAMILY_GENERIC, sTmp);
430 aAny <<= static_cast<sal_Int16>(pEntry->GetPitch());
431 if (aPitchHdl.exportXML(sTmp, aAny, rUnitConv))
433 GetExport().AddAttribute(XML_NAMESPACE_STYLE,
434 XML_FONT_PITCH, sTmp);
437 aAny <<= static_cast<sal_Int16>(pEntry->GetEncoding());
438 if (aEncHdl.exportXML( sTmp, aAny, rUnitConv))
440 GetExport().AddAttribute(XML_NAMESPACE_STYLE,
441 XML_FONT_CHARSET, sTmp);
444 SvXMLElementExport aElement(GetExport(), XML_NAMESPACE_STYLE,
445 XML_FONT_FACE, true, true);
447 if (m_bTryToEmbedFonts)
449 const bool bExportFlat(GetExport().getExportFlags() & SvXMLExportFlags::EMBEDDED);
450 std::vector<EmbeddedFontInfo> aEmbeddedFonts;
451 static const std::vector<std::pair<FontWeight, FontItalic>> aCombinations =
453 { WEIGHT_NORMAL, ITALIC_NONE },
454 { WEIGHT_BOLD, ITALIC_NONE },
455 { WEIGHT_NORMAL, ITALIC_NORMAL },
456 { WEIGHT_BOLD, ITALIC_NORMAL },
459 for (auto const & aCombinationPair : aCombinations)
461 // Embed font if at least viewing is allowed (in which case the opening app must check
462 // the font license rights too and open either read-only or not use the font for editing).
463 OUString sFileUrl = EmbeddedFontsHelper::fontFileUrl(
464 pEntry->GetFamilyName(), pEntry->GetFamily(),
465 aCombinationPair.second, aCombinationPair.first, pEntry->GetPitch(),
466 EmbeddedFontsHelper::FontRights::ViewingAllowed);
467 if (sFileUrl.isEmpty())
468 continue;
470 // When embedded only is not set or font is used
471 if (!m_bEmbedUsedOnly ||
472 aUsedFontNames.find(pEntry->GetFamilyName()) != aUsedFontNames.end())
474 if (!fontFilesMap.count(sFileUrl))
476 const OUString docUrl = bExportFlat ?
477 lcl_checkFontFile(sFileUrl) : embedFontFile(sFileUrl, pEntry->GetFamilyName());
478 if (!docUrl.isEmpty())
479 fontFilesMap[sFileUrl] = docUrl;
480 else
481 continue; // --> failed to embed
483 EmbeddedFontInfo aEmbeddedFont;
484 aEmbeddedFont.aURL = sFileUrl;
485 aEmbeddedFont.eWeight = aCombinationPair.first;
486 aEmbeddedFont.eItalic = aCombinationPair.second;
487 aEmbeddedFonts.push_back(aEmbeddedFont);
490 if (!aEmbeddedFonts.empty())
492 SvXMLElementExport fontFaceSrc(GetExport(), XML_NAMESPACE_SVG, XML_FONT_FACE_SRC, true, true);
493 for (EmbeddedFontInfo const & rEmbeddedFont : aEmbeddedFonts)
495 if (fontFilesMap.count(rEmbeddedFont.aURL))
497 if (!bExportFlat)
499 GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_HREF,
500 fontFilesMap[rEmbeddedFont.aURL]);
501 GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, "simple");
504 // Help consumers of our output by telling them which
505 // font file is which one.
506 GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_FONT_STYLE,
507 FontItalicToString(rEmbeddedFont.eItalic));
508 GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_FONT_WEIGHT,
509 FontWeightToString(rEmbeddedFont.eWeight));
511 SvXMLElementExport fontFaceUri(GetExport(), XML_NAMESPACE_SVG,
512 XML_FONT_FACE_URI, true, true);
514 if (bExportFlat)
516 const uno::Reference<ucb::XSimpleFileAccess> xFileAccess(
517 ucb::SimpleFileAccess::create(GetExport().getComponentContext()));
520 const uno::Reference<io::XInputStream> xInput(xFileAccess->openFileRead(fontFilesMap[rEmbeddedFont.aURL]));
521 XMLBase64Export aBase64Exp(GetExport());
522 aBase64Exp.exportOfficeBinaryDataElement(xInput);
524 catch (const uno::Exception &)
526 // opening the file failed, ignore
530 GetExport().AddAttribute(XML_NAMESPACE_SVG, XML_STRING, "truetype");
531 SvXMLElementExport fontFaceFormat(GetExport(), XML_NAMESPACE_SVG,
532 XML_FONT_FACE_FORMAT, true, true);
540 static OUString getFreeFontName(uno::Reference<embed::XStorage> const & rxStorage, OUString const & rFamilyName)
542 OUString sName;
543 int nIndex = 1;
546 sName = "Font_" +
547 rFamilyName.replaceAll(" ", "_") + "_" +
548 OUString::number(nIndex) + ".ttf";
549 nIndex++;
550 } while (rxStorage->hasByName(sName));
552 return sName;
555 static OString convertToHashString(std::vector<unsigned char> const & rHash)
557 std::stringstream aStringStream;
558 for (auto const & rByte : rHash)
560 aStringStream << std::setw(2) << std::setfill('0') << std::hex << int(rByte);
563 return aStringStream.str().c_str();
566 static OString getFileHash(OUString const & rFileUrl)
568 OString aHash;
569 osl::File aFile(rFileUrl);
570 if (aFile.open(osl_File_OpenFlag_Read) != osl::File::E_None)
571 return aHash;
573 comphelper::Hash aHashEngine(comphelper::HashType::SHA512);
574 for (;;)
576 sal_Int8 aBuffer[4096];
577 sal_uInt64 nReadSize;
578 sal_Bool bEof;
579 if (aFile.isEndOfFile(&bEof) != osl::File::E_None)
581 SAL_WARN("xmloff", "Error reading font file " << rFileUrl);
582 return aHash;
584 if (bEof)
585 break;
586 if (aFile.read(aBuffer, 4096, nReadSize) != osl::File::E_None)
588 SAL_WARN("xmloff", "Error reading font file " << rFileUrl);
589 return aHash;
591 if (nReadSize == 0)
592 break;
593 aHashEngine.update(reinterpret_cast<unsigned char*>(aBuffer), nReadSize);
595 return convertToHashString(aHashEngine.finalize());
598 OUString XMLFontAutoStylePool::embedFontFile(OUString const & fileUrl, OUString const & rFamilyName)
602 OString sHashString = getFileHash(fileUrl);
603 if (m_aEmbeddedFontFiles.find(sHashString) != m_aEmbeddedFontFiles.end())
604 return m_aEmbeddedFontFiles.at(sHashString);
606 osl::File file( fileUrl );
607 if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None )
608 return OUString();
610 if ( !GetExport().GetTargetStorage().is() )
611 return OUString();
613 uno::Reference< embed::XStorage > storage;
614 storage.set( GetExport().GetTargetStorage()->openStorageElement( "Fonts",
615 ::embed::ElementModes::WRITE ), uno::UNO_SET_THROW );
617 OUString name = getFreeFontName(storage, rFamilyName);
619 uno::Reference< io::XOutputStream > outputStream;
620 outputStream.set( storage->openStreamElement( name, ::embed::ElementModes::WRITE ), UNO_QUERY_THROW );
621 uno::Reference < beans::XPropertySet > propertySet( outputStream, uno::UNO_QUERY );
622 assert( propertySet.is());
623 propertySet->setPropertyValue( "MediaType", uno::makeAny( OUString( "application/x-font-ttf" ))); // TODO
624 for(;;)
626 sal_Int8 buffer[ 4096 ];
627 sal_uInt64 readSize;
628 sal_Bool eof;
629 if( file.isEndOfFile( &eof ) != osl::File::E_None )
631 SAL_WARN( "xmloff", "Error reading font file " << fileUrl );
632 outputStream->closeOutput();
633 return OUString();
635 if( eof )
636 break;
637 if( file.read( buffer, 4096, readSize ) != osl::File::E_None )
639 SAL_WARN( "xmloff", "Error reading font file " << fileUrl );
640 outputStream->closeOutput();
641 return OUString();
643 if( readSize == 0 )
644 break;
645 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
646 outputStream->writeBytes(uno::Sequence<sal_Int8>(buffer, readSize));
648 outputStream->closeOutput();
649 if( storage.is() )
651 Reference< embed::XTransactedObject > transaction( storage, UNO_QUERY );
652 if( transaction.is())
654 transaction->commit();
655 OUString sInternalName = "Fonts/" + name;
656 m_aEmbeddedFontFiles.emplace(sHashString, sInternalName);
657 return sInternalName;
660 } catch( const Exception& )
662 TOOLS_WARN_EXCEPTION( "xmloff", "Exception when embedding a font file" );
664 return OUString();
668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */