1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/xmlnmspe.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>
31 #include <com/sun/star/embed/ElementModes.hpp>
32 #include <com/sun/star/embed/XTransactedObject.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
36 #include "XMLBase64Export.hxx"
38 using namespace ::com::sun::star
;
39 using namespace ::com::sun::star::uno
;
40 using namespace ::xmloff::token
;
42 class XMLFontAutoStylePoolEntry_Impl
49 rtl_TextEncoding eEnc
;
53 inline XMLFontAutoStylePoolEntry_Impl(
54 const OUString
& rName
,
55 const OUString
& rFamilyName
,
56 const OUString
& rStyleName
,
59 rtl_TextEncoding eEnc
);
61 inline XMLFontAutoStylePoolEntry_Impl(
62 const OUString
& rFamilyName
,
63 const OUString
& rStyleName
,
66 rtl_TextEncoding eEnc
);
68 const OUString
& GetName() const { return sName
; }
69 const OUString
& GetFamilyName() const { return sFamilyName
; }
70 const OUString
& GetStyleName() const { return sStyleName
; }
71 FontFamily
GetFamily() const { return nFamily
; }
72 FontPitch
GetPitch() const { return nPitch
; }
73 rtl_TextEncoding
GetEncoding() const { return eEnc
; }
77 inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
78 const OUString
& rName
,
79 const OUString
& rFamilyName
,
80 const OUString
& rStyleName
,
83 rtl_TextEncoding eE
) :
85 sFamilyName( rFamilyName
),
86 sStyleName( rStyleName
),
93 inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
94 const OUString
& rFamilyName
,
95 const OUString
& rStyleName
,
98 rtl_TextEncoding eE
) :
99 sFamilyName( rFamilyName
),
100 sStyleName( rStyleName
),
107 struct XMLFontAutoStylePoolEntryCmp_Impl
{
109 XMLFontAutoStylePoolEntry_Impl
* const& r1
,
110 XMLFontAutoStylePoolEntry_Impl
* const& r2
) const
112 bool nEnc1(r1
->GetEncoding() != RTL_TEXTENCODING_SYMBOL
);
113 bool nEnc2(r2
->GetEncoding() != RTL_TEXTENCODING_SYMBOL
);
115 return nEnc1
< nEnc2
;
116 else if( r1
->GetPitch() != r2
->GetPitch() )
117 return r1
->GetPitch() < r2
->GetPitch();
118 else if( r1
->GetFamily() != r2
->GetFamily() )
119 return r1
->GetFamily() < r2
->GetFamily();
122 sal_Int32 nCmp
= r1
->GetFamilyName().compareTo( r2
->GetFamilyName() );
124 return r1
->GetStyleName().compareTo( r2
->GetStyleName() ) < 0;
131 class XMLFontAutoStylePool_Impl
: public o3tl::sorted_vector
<XMLFontAutoStylePoolEntry_Impl
*, XMLFontAutoStylePoolEntryCmp_Impl
>
134 ~XMLFontAutoStylePool_Impl() { DeleteAndDestroyAll(); }
137 XMLFontAutoStylePool::XMLFontAutoStylePool( SvXMLExport
& rExp
, bool _tryToEmbedFonts
) :
139 pPool( new XMLFontAutoStylePool_Impl
),
140 tryToEmbedFonts( _tryToEmbedFonts
)
144 XMLFontAutoStylePool::~XMLFontAutoStylePool()
149 OUString
XMLFontAutoStylePool::Add(
150 const OUString
& rFamilyName
,
151 const OUString
& rStyleName
,
154 rtl_TextEncoding eEnc
)
157 XMLFontAutoStylePoolEntry_Impl
aTmp( rFamilyName
, rStyleName
, nFamily
,
159 XMLFontAutoStylePool_Impl::const_iterator it
= pPool
->find( &aTmp
);
160 if( it
!= pPool
->end() )
162 sPoolName
= (*it
)->GetName();
167 sal_Int32 nLen
= rFamilyName
.indexOf( ';', 0 );
174 sName
= rFamilyName
.copy( 0, nLen
);
175 sName
= sName
.trim();
178 if( sName
.isEmpty() )
181 if( m_aNames
.find(sName
) != m_aNames
.end() )
183 sal_Int32 nCount
= 1;
184 OUString
sPrefix( sName
);
185 sName
+= OUString::number( nCount
);
186 while( m_aNames
.find(sName
) != m_aNames
.end() )
189 sName
+= OUString::number( ++nCount
);
193 XMLFontAutoStylePoolEntry_Impl
*pEntry
=
194 new XMLFontAutoStylePoolEntry_Impl( sName
, rFamilyName
, rStyleName
,
195 nFamily
, nPitch
, eEnc
);
196 pPool
->insert( pEntry
);
197 m_aNames
.insert(sName
);
203 OUString
XMLFontAutoStylePool::Find(
204 const OUString
& rFamilyName
,
205 const OUString
& rStyleName
,
208 rtl_TextEncoding eEnc
) const
211 XMLFontAutoStylePoolEntry_Impl
aTmp( rFamilyName
, rStyleName
, nFamily
,
213 XMLFontAutoStylePool_Impl::const_iterator it
= pPool
->find( &aTmp
);
214 if( it
!= pPool
->end() )
216 sName
= (*it
)->GetName();
225 OUString
lcl_checkFontFile( const OUString
&fileUrl
)
227 osl::DirectoryItem aDirItem
;
228 if( osl::DirectoryItem::get( fileUrl
, aDirItem
) == osl::File::E_None
)
230 osl::FileStatus
aStatus( osl_FileStatus_Mask_Type
);
231 if( aDirItem
.getFileStatus( aStatus
) == osl::File::E_None
)
233 if( !aStatus
.isDirectory() )
242 void XMLFontAutoStylePool::exportXML()
244 SvXMLElementExport
aElem( GetExport(), XML_NAMESPACE_OFFICE
,
249 XMLFontFamilyNamePropHdl aFamilyNameHdl
;
250 XMLFontFamilyPropHdl aFamilyHdl
;
251 XMLFontPitchPropHdl aPitchHdl
;
252 XMLFontEncodingPropHdl aEncHdl
;
253 const SvXMLUnitConverter
& rUnitConv
= GetExport().GetMM100UnitConverter();
255 std::map
< OUString
, OUString
> fontFilesMap
; // our url to document url
256 sal_uInt32 nCount
= pPool
->size();
257 for( sal_uInt32 i
=0; i
<nCount
; i
++ )
259 const XMLFontAutoStylePoolEntry_Impl
*pEntry
= (*pPool
)[ i
];
261 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
262 XML_NAME
, pEntry
->GetName() );
264 aAny
<<= pEntry
->GetFamilyName();
265 if( aFamilyNameHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
266 GetExport().AddAttribute( XML_NAMESPACE_SVG
,
267 XML_FONT_FAMILY
, sTmp
);
269 const OUString
& rStyleName
= pEntry
->GetStyleName();
270 if( !rStyleName
.isEmpty() )
271 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
275 aAny
<<= (sal_Int16
)pEntry
->GetFamily();
276 if( aFamilyHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
277 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
278 XML_FONT_FAMILY_GENERIC
, sTmp
);
280 aAny
<<= (sal_Int16
)pEntry
->GetPitch();
281 if( aPitchHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
282 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
283 XML_FONT_PITCH
, sTmp
);
285 aAny
<<= (sal_Int16
)pEntry
->GetEncoding();
286 if( aEncHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
287 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
288 XML_FONT_CHARSET
, sTmp
);
290 SvXMLElementExport
aElement( GetExport(), XML_NAMESPACE_STYLE
,
294 if( tryToEmbedFonts
)
296 const bool bExportFlat( GetExport().getExportFlags() & SvXMLExportFlags::EMBEDDED
);
297 std::vector
< OUString
> fileUrls
;
298 static const FontWeight weight
[] = { WEIGHT_NORMAL
, WEIGHT_BOLD
, WEIGHT_NORMAL
, WEIGHT_BOLD
};
299 static const FontItalic italic
[] = { ITALIC_NONE
, ITALIC_NONE
, ITALIC_NORMAL
, ITALIC_NORMAL
};
300 assert( SAL_N_ELEMENTS( weight
) == SAL_N_ELEMENTS( italic
));
301 for( unsigned int j
= 0;
302 j
< SAL_N_ELEMENTS( weight
);
305 // Embed font if at least viewing is allowed (in which case the opening app must check
306 // the font license rights too and open either read-only or not use the font for editing).
307 OUString fileUrl
= EmbeddedFontsHelper::fontFileUrl( pEntry
->GetFamilyName(), pEntry
->GetFamily(),
308 italic
[ j
], weight
[ j
], pEntry
->GetPitch(), pEntry
->GetEncoding(),
309 EmbeddedFontsHelper::ViewingAllowed
);
310 if( fileUrl
.isEmpty())
312 if( !fontFilesMap
.count( fileUrl
))
314 const OUString docUrl
= bExportFlat
? lcl_checkFontFile( fileUrl
) : embedFontFile( fileUrl
);
315 if( !docUrl
.isEmpty())
316 fontFilesMap
[ fileUrl
] = docUrl
;
318 continue; // --> failed to embed
320 fileUrls
.push_back( fileUrl
);
322 if( !fileUrls
.empty())
324 SvXMLElementExport
fontFaceSrc( GetExport(), XML_NAMESPACE_SVG
,
325 XML_FONT_FACE_SRC
, true, true );
326 for( std::vector
< OUString
>::const_iterator it
= fileUrls
.begin();
327 it
!= fileUrls
.end();
330 if( fontFilesMap
.count( *it
))
334 GetExport().AddAttribute( XML_NAMESPACE_XLINK
, XML_HREF
, fontFilesMap
[ *it
] );
335 GetExport().AddAttribute( XML_NAMESPACE_XLINK
, XML_TYPE
, "simple" );
337 SvXMLElementExport
fontFaceUri( GetExport(), XML_NAMESPACE_SVG
,
338 XML_FONT_FACE_URI
, true, true );
342 const uno::Reference
< ucb::XSimpleFileAccess
> xFileAccess( ucb::SimpleFileAccess::create( GetExport().getComponentContext() ) );
345 const uno::Reference
< io::XInputStream
> xInput( xFileAccess
->openFileRead( fontFilesMap
[ *it
] ) );
346 XMLBase64Export
aBase64Exp( GetExport() );
347 aBase64Exp
.exportOfficeBinaryDataElement( xInput
);
349 catch( const uno::Exception
& )
351 // opening the file failed, ignore
355 GetExport().AddAttribute( XML_NAMESPACE_SVG
, XML_STRING
, "truetype" );
356 SvXMLElementExport
fontFaceFormat( GetExport(), XML_NAMESPACE_SVG
,
357 XML_FONT_FACE_FORMAT
, true, true );
365 OUString
XMLFontAutoStylePool::embedFontFile( const OUString
& fileUrl
)
369 osl::File
file( fileUrl
);
370 if( file
.open( osl_File_OpenFlag_Read
) != osl::File::E_None
)
373 if ( !GetExport().GetTargetStorage().is() )
376 uno::Reference
< embed::XStorage
> storage
;
377 storage
.set( GetExport().GetTargetStorage()->openStorageElement( OUString( "Fonts" ),
378 ::embed::ElementModes::WRITE
), uno::UNO_QUERY_THROW
);
383 name
= "font" + OUString::number( ++index
) + ".ttf";
384 } while( storage
->hasByName( name
) );
385 uno::Reference
< io::XOutputStream
> outputStream
;
386 outputStream
.set( storage
->openStreamElement( name
, ::embed::ElementModes::WRITE
), UNO_QUERY_THROW
);
387 uno::Reference
< beans::XPropertySet
> propertySet( outputStream
, uno::UNO_QUERY
);
388 assert( propertySet
.is());
389 propertySet
->setPropertyValue( "MediaType", uno::makeAny( OUString( "application/x-font-ttf" ))); // TODO
395 if( file
.isEndOfFile( &eof
) != osl::File::E_None
)
397 SAL_WARN( "xmloff", "Error reading font file " << fileUrl
);
398 outputStream
->closeOutput();
403 if( file
.read( buffer
, 4096, readSize
) != osl::File::E_None
)
405 SAL_WARN( "xmloff", "Error reading font file " << fileUrl
);
406 outputStream
->closeOutput();
411 outputStream
->writeBytes( uno::Sequence
< sal_Int8
>( reinterpret_cast< const sal_Int8
* >( buffer
), readSize
));
413 outputStream
->closeOutput();
416 Reference
< embed::XTransactedObject
> transaction( storage
, UNO_QUERY
);
417 if( transaction
.is())
419 transaction
->commit();
420 return "Fonts/" + name
;
423 } catch( const Exception
& e
)
425 SAL_WARN( "xmloff", "Exception when embedding a font file:" << e
.Message
);
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */