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>
36 using namespace ::com::sun::star
;
37 using namespace ::com::sun::star::uno
;
38 using namespace ::xmloff::token
;
40 class XMLFontAutoStylePoolEntry_Impl
47 rtl_TextEncoding eEnc
;
51 inline XMLFontAutoStylePoolEntry_Impl(
52 const OUString
& rName
,
53 const OUString
& rFamilyName
,
54 const OUString
& rStyleName
,
57 rtl_TextEncoding eEnc
);
59 inline XMLFontAutoStylePoolEntry_Impl(
60 const OUString
& rFamilyName
,
61 const OUString
& rStyleName
,
64 rtl_TextEncoding eEnc
);
66 const OUString
& GetName() const { return sName
; }
67 const OUString
& GetFamilyName() const { return sFamilyName
; }
68 const OUString
& GetStyleName() const { return sStyleName
; }
69 FontFamily
GetFamily() const { return nFamily
; }
70 FontPitch
GetPitch() const { return nPitch
; }
71 rtl_TextEncoding
GetEncoding() const { return eEnc
; }
75 inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
76 const OUString
& rName
,
77 const OUString
& rFamilyName
,
78 const OUString
& rStyleName
,
81 rtl_TextEncoding eE
) :
83 sFamilyName( rFamilyName
),
84 sStyleName( rStyleName
),
91 inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
92 const OUString
& rFamilyName
,
93 const OUString
& rStyleName
,
96 rtl_TextEncoding eE
) :
97 sFamilyName( rFamilyName
),
98 sStyleName( rStyleName
),
105 struct XMLFontAutoStylePoolEntryCmp_Impl
{
107 XMLFontAutoStylePoolEntry_Impl
* const& r1
,
108 XMLFontAutoStylePoolEntry_Impl
* const& r2
) const
110 sal_Int8
nEnc1(r1
->GetEncoding() != RTL_TEXTENCODING_SYMBOL
);
111 sal_Int8
nEnc2(r2
->GetEncoding() != RTL_TEXTENCODING_SYMBOL
);
113 return nEnc1
< nEnc2
;
114 else if( r1
->GetPitch() != r2
->GetPitch() )
115 return r1
->GetPitch() < r2
->GetPitch();
116 else if( r1
->GetFamily() != r2
->GetFamily() )
117 return r1
->GetFamily() < r2
->GetFamily();
120 sal_Int32 nCmp
= r1
->GetFamilyName().compareTo( r2
->GetFamilyName() );
122 return r1
->GetStyleName().compareTo( r2
->GetStyleName() ) < 0;
129 class XMLFontAutoStylePool_Impl
: public o3tl::sorted_vector
<XMLFontAutoStylePoolEntry_Impl
*, XMLFontAutoStylePoolEntryCmp_Impl
>
132 ~XMLFontAutoStylePool_Impl() { DeleteAndDestroyAll(); }
135 XMLFontAutoStylePool::XMLFontAutoStylePool( SvXMLExport
& rExp
, bool _tryToEmbedFonts
) :
137 pPool( new XMLFontAutoStylePool_Impl
),
138 tryToEmbedFonts( _tryToEmbedFonts
)
142 XMLFontAutoStylePool::~XMLFontAutoStylePool()
147 OUString
XMLFontAutoStylePool::Add(
148 const OUString
& rFamilyName
,
149 const OUString
& rStyleName
,
152 rtl_TextEncoding eEnc
)
155 XMLFontAutoStylePoolEntry_Impl
aTmp( rFamilyName
, rStyleName
, nFamily
,
157 XMLFontAutoStylePool_Impl::const_iterator it
= pPool
->find( &aTmp
);
158 if( it
!= pPool
->end() )
160 sPoolName
= (*it
)->GetName();
165 sal_Int32 nLen
= rFamilyName
.indexOf( sal_Unicode(';'), 0 );
172 sName
= rFamilyName
.copy( 0, nLen
);
173 sName
= sName
.trim();
176 if( sName
.isEmpty() )
177 sName
= OUString::valueOf( sal_Unicode( 'F' ) );
179 if( m_aNames
.find(sName
) != m_aNames
.end() )
181 sal_Int32 nCount
= 1;
182 OUString
sPrefix( sName
);
183 sName
+= OUString::valueOf( nCount
);
184 while( m_aNames
.find(sName
) != m_aNames
.end() )
187 sName
+= OUString::valueOf( ++nCount
);
191 XMLFontAutoStylePoolEntry_Impl
*pEntry
=
192 new XMLFontAutoStylePoolEntry_Impl( sName
, rFamilyName
, rStyleName
,
193 nFamily
, nPitch
, eEnc
);
194 pPool
->insert( pEntry
);
195 m_aNames
.insert(sName
);
201 OUString
XMLFontAutoStylePool::Find(
202 const OUString
& rFamilyName
,
203 const OUString
& rStyleName
,
206 rtl_TextEncoding eEnc
) const
209 XMLFontAutoStylePoolEntry_Impl
aTmp( rFamilyName
, rStyleName
, nFamily
,
211 XMLFontAutoStylePool_Impl::const_iterator it
= pPool
->find( &aTmp
);
212 if( it
!= pPool
->end() )
214 sName
= (*it
)->GetName();
221 void XMLFontAutoStylePool::exportXML()
223 SvXMLElementExport
aElem( GetExport(), XML_NAMESPACE_OFFICE
,
225 sal_True
, sal_True
);
228 XMLFontFamilyNamePropHdl aFamilyNameHdl
;
229 XMLFontFamilyPropHdl aFamilyHdl
;
230 XMLFontPitchPropHdl aPitchHdl
;
231 XMLFontEncodingPropHdl aEncHdl
;
232 const SvXMLUnitConverter
& rUnitConv
= GetExport().GetMM100UnitConverter();
234 std::map
< OUString
, OUString
> fontFilesMap
; // our url to document url
235 sal_uInt32 nCount
= pPool
->size();
236 for( sal_uInt32 i
=0; i
<nCount
; i
++ )
238 const XMLFontAutoStylePoolEntry_Impl
*pEntry
= (*pPool
)[ i
];
240 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
241 XML_NAME
, pEntry
->GetName() );
243 aAny
<<= pEntry
->GetFamilyName();
244 if( aFamilyNameHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
245 GetExport().AddAttribute( XML_NAMESPACE_SVG
,
246 XML_FONT_FAMILY
, sTmp
);
248 const OUString
& rStyleName
= pEntry
->GetStyleName();
249 if( !rStyleName
.isEmpty() )
250 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
254 aAny
<<= (sal_Int16
)pEntry
->GetFamily();
255 if( aFamilyHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
256 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
257 XML_FONT_FAMILY_GENERIC
, sTmp
);
259 aAny
<<= (sal_Int16
)pEntry
->GetPitch();
260 if( aPitchHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
261 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
262 XML_FONT_PITCH
, sTmp
);
264 aAny
<<= (sal_Int16
)pEntry
->GetEncoding();
265 if( aEncHdl
.exportXML( sTmp
, aAny
, rUnitConv
) )
266 GetExport().AddAttribute( XML_NAMESPACE_STYLE
,
267 XML_FONT_CHARSET
, sTmp
);
269 SvXMLElementExport
aElement( GetExport(), XML_NAMESPACE_STYLE
,
271 sal_True
, sal_True
);
273 if( tryToEmbedFonts
)
275 std::vector
< OUString
> fileUrls
;
276 static const FontWeight weight
[] = { WEIGHT_NORMAL
, WEIGHT_BOLD
, WEIGHT_NORMAL
, WEIGHT_BOLD
};
277 static const FontItalic italic
[] = { ITALIC_NONE
, ITALIC_NONE
, ITALIC_NORMAL
, ITALIC_NORMAL
};
278 assert( SAL_N_ELEMENTS( weight
) == SAL_N_ELEMENTS( italic
));
279 for( unsigned int j
= 0;
280 j
< SAL_N_ELEMENTS( weight
);
283 // Embed font if at least viewing is allowed (in which case the opening app must check
284 // the font license rights too and open either read-only or not use the font for editing).
285 OUString fileUrl
= EmbeddedFontsHelper::fontFileUrl( pEntry
->GetFamilyName(), pEntry
->GetFamily(),
286 italic
[ j
], weight
[ j
], pEntry
->GetPitch(), pEntry
->GetEncoding(),
287 EmbeddedFontsHelper::ViewingAllowed
);
288 if( fileUrl
.isEmpty())
290 if( !fontFilesMap
.count( fileUrl
))
292 OUString docUrl
= embedFontFile( fileUrl
);
293 if( !docUrl
.isEmpty())
294 fontFilesMap
[ fileUrl
] = docUrl
;
296 continue; // --> failed to embed
298 fileUrls
.push_back( fileUrl
);
300 if( !fileUrls
.empty())
302 SvXMLElementExport
fontFaceSrc( GetExport(), XML_NAMESPACE_SVG
,
303 XML_FONT_FACE_SRC
, true, true );
304 for( std::vector
< OUString
>::const_iterator it
= fileUrls
.begin();
305 it
!= fileUrls
.end();
308 if( fontFilesMap
.count( *it
))
310 GetExport().AddAttribute( XML_NAMESPACE_XLINK
, XML_HREF
, fontFilesMap
[ *it
] );
311 GetExport().AddAttribute( XML_NAMESPACE_XLINK
, XML_TYPE
, "simple" );
312 SvXMLElementExport
fontFaceUri( GetExport(), XML_NAMESPACE_SVG
,
313 XML_FONT_FACE_URI
, true, true );
321 OUString
XMLFontAutoStylePool::embedFontFile( const OUString
& fileUrl
)
325 osl::File
file( fileUrl
);
326 if( file
.open( osl_File_OpenFlag_Read
) != osl::File::E_None
)
328 uno::Reference
< embed::XStorage
> storage
;
329 storage
.set( GetExport().GetTargetStorage()->openStorageElement( OUString( "Fonts" ),
330 ::embed::ElementModes::WRITE
), uno::UNO_QUERY_THROW
);
335 name
= "font" + OUString::number( ++index
) + ".ttf";
336 } while( storage
->hasByName( name
) );
337 uno::Reference
< io::XOutputStream
> outputStream
;
338 outputStream
.set( storage
->openStreamElement( name
, ::embed::ElementModes::WRITE
), UNO_QUERY_THROW
);
339 uno::Reference
< beans::XPropertySet
> propertySet( outputStream
, uno::UNO_QUERY
);
340 assert( propertySet
.is());
341 propertySet
->setPropertyValue( "MediaType", uno::makeAny( OUString( "application/x-font-ttf" ))); // TODO
347 if( file
.isEndOfFile( &eof
) != osl::File::E_None
)
349 SAL_WARN( "xmloff", "Error reading font file " << fileUrl
);
350 outputStream
->closeOutput();
355 if( file
.read( buffer
, 4096, readSize
) != osl::File::E_None
)
357 SAL_WARN( "xmloff", "Error reading font file " << fileUrl
);
358 outputStream
->closeOutput();
363 outputStream
->writeBytes( uno::Sequence
< sal_Int8
>( reinterpret_cast< const sal_Int8
* >( buffer
), readSize
));
365 outputStream
->closeOutput();
368 Reference
< embed::XTransactedObject
> transaction( storage
, UNO_QUERY
);
369 if( transaction
.is())
371 transaction
->commit();
372 return "Fonts/" + name
;
375 } catch( const Exception
& e
)
377 SAL_WARN( "xmloff", "Exception when embedding a font file:" << e
.Message
);
383 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */