Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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>
30 :
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>
35 :
36 : #include "XMLBase64Export.hxx"
37 :
38 : using namespace ::com::sun::star;
39 : using namespace ::com::sun::star::uno;
40 : using namespace ::xmloff::token;
41 :
42 13143 : class XMLFontAutoStylePoolEntry_Impl
43 : {
44 : OUString sName;
45 : OUString sFamilyName;
46 : OUString sStyleName;
47 : FontFamily nFamily;
48 : FontPitch nPitch;
49 : rtl_TextEncoding eEnc;
50 :
51 : public:
52 :
53 : inline XMLFontAutoStylePoolEntry_Impl(
54 : const OUString& rName,
55 : const OUString& rFamilyName,
56 : const OUString& rStyleName,
57 : FontFamily nFamily,
58 : FontPitch nPitch,
59 : rtl_TextEncoding eEnc );
60 :
61 : inline XMLFontAutoStylePoolEntry_Impl(
62 : const OUString& rFamilyName,
63 : const OUString& rStyleName,
64 : FontFamily nFamily,
65 : FontPitch nPitch,
66 : rtl_TextEncoding eEnc );
67 :
68 8284 : const OUString& GetName() const { return sName; }
69 42328 : const OUString& GetFamilyName() const { return sFamilyName; }
70 29764 : const OUString& GetStyleName() const { return sStyleName; }
71 74912 : FontFamily GetFamily() const { return nFamily; }
72 75684 : FontPitch GetPitch() const { return nPitch; }
73 67770 : rtl_TextEncoding GetEncoding() const { return eEnc; }
74 : };
75 :
76 :
77 1226 : inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
78 : const OUString& rName,
79 : const OUString& rFamilyName,
80 : const OUString& rStyleName,
81 : FontFamily nFam,
82 : FontPitch nP,
83 : rtl_TextEncoding eE ) :
84 : sName( rName ),
85 : sFamilyName( rFamilyName ),
86 : sStyleName( rStyleName ),
87 : nFamily( nFam ),
88 : nPitch( nP ),
89 1226 : eEnc( eE )
90 : {
91 1226 : }
92 :
93 11917 : inline XMLFontAutoStylePoolEntry_Impl::XMLFontAutoStylePoolEntry_Impl(
94 : const OUString& rFamilyName,
95 : const OUString& rStyleName,
96 : FontFamily nFam,
97 : FontPitch nP,
98 : rtl_TextEncoding eE ) :
99 : sFamilyName( rFamilyName ),
100 : sStyleName( rStyleName ),
101 : nFamily( nFam ),
102 : nPitch( nP ),
103 11917 : eEnc( eE )
104 : {
105 11917 : }
106 :
107 : struct XMLFontAutoStylePoolEntryCmp_Impl {
108 33270 : bool operator()(
109 : XMLFontAutoStylePoolEntry_Impl* const& r1,
110 : XMLFontAutoStylePoolEntry_Impl* const& r2 ) const
111 : {
112 33270 : bool nEnc1(r1->GetEncoding() != RTL_TEXTENCODING_SYMBOL);
113 33270 : bool nEnc2(r2->GetEncoding() != RTL_TEXTENCODING_SYMBOL);
114 33270 : if( nEnc1 != nEnc2 )
115 309 : return nEnc1 < nEnc2;
116 32961 : else if( r1->GetPitch() != r2->GetPitch() )
117 4266 : return r1->GetPitch() < r2->GetPitch();
118 28695 : else if( r1->GetFamily() != r2->GetFamily() )
119 8146 : return r1->GetFamily() < r2->GetFamily();
120 : else
121 : {
122 20549 : sal_Int32 nCmp = r1->GetFamilyName().compareTo( r2->GetFamilyName() );
123 20549 : if( 0 == nCmp )
124 14267 : return r1->GetStyleName().compareTo( r2->GetStyleName() ) < 0;
125 : else
126 6282 : return nCmp < 0;
127 : }
128 : }
129 : };
130 :
131 583 : class XMLFontAutoStylePool_Impl : public o3tl::sorted_vector<XMLFontAutoStylePoolEntry_Impl*, XMLFontAutoStylePoolEntryCmp_Impl>
132 : {
133 : public:
134 583 : ~XMLFontAutoStylePool_Impl() { DeleteAndDestroyAll(); }
135 : };
136 :
137 583 : XMLFontAutoStylePool::XMLFontAutoStylePool( SvXMLExport& rExp, bool _tryToEmbedFonts ) :
138 : rExport( rExp ),
139 583 : pPool( new XMLFontAutoStylePool_Impl ),
140 1166 : tryToEmbedFonts( _tryToEmbedFonts )
141 : {
142 583 : }
143 :
144 1547 : XMLFontAutoStylePool::~XMLFontAutoStylePool()
145 : {
146 583 : delete pPool;
147 964 : }
148 :
149 6588 : OUString XMLFontAutoStylePool::Add(
150 : const OUString& rFamilyName,
151 : const OUString& rStyleName,
152 : FontFamily nFamily,
153 : FontPitch nPitch,
154 : rtl_TextEncoding eEnc )
155 : {
156 6588 : OUString sPoolName;
157 : XMLFontAutoStylePoolEntry_Impl aTmp( rFamilyName, rStyleName, nFamily,
158 13176 : nPitch, eEnc );
159 6588 : XMLFontAutoStylePool_Impl::const_iterator it = pPool->find( &aTmp );
160 6588 : if( it != pPool->end() )
161 : {
162 5362 : sPoolName = (*it)->GetName();
163 : }
164 : else
165 : {
166 1226 : OUString sName;
167 1226 : sal_Int32 nLen = rFamilyName.indexOf( ';', 0 );
168 1226 : if( -1 == nLen )
169 : {
170 1226 : sName = rFamilyName;
171 : }
172 0 : else if( nLen > 0 )
173 : {
174 0 : sName = rFamilyName.copy( 0, nLen );
175 0 : sName = sName.trim();
176 : }
177 :
178 1226 : if( sName.isEmpty() )
179 8 : sName = "F";
180 :
181 1226 : if( m_aNames.find(sName) != m_aNames.end() )
182 : {
183 225 : sal_Int32 nCount = 1;
184 225 : OUString sPrefix( sName );
185 225 : sName += OUString::number( nCount );
186 480 : while( m_aNames.find(sName) != m_aNames.end() )
187 : {
188 30 : sName = sPrefix;
189 30 : sName += OUString::number( ++nCount );
190 225 : }
191 : }
192 :
193 : XMLFontAutoStylePoolEntry_Impl *pEntry =
194 : new XMLFontAutoStylePoolEntry_Impl( sName, rFamilyName, rStyleName,
195 1226 : nFamily, nPitch, eEnc );
196 1226 : pPool->insert( pEntry );
197 1226 : m_aNames.insert(sName);
198 : }
199 :
200 13176 : return sPoolName;
201 : }
202 :
203 5329 : OUString XMLFontAutoStylePool::Find(
204 : const OUString& rFamilyName,
205 : const OUString& rStyleName,
206 : FontFamily nFamily,
207 : FontPitch nPitch,
208 : rtl_TextEncoding eEnc ) const
209 : {
210 5329 : OUString sName;
211 : XMLFontAutoStylePoolEntry_Impl aTmp( rFamilyName, rStyleName, nFamily,
212 10658 : nPitch, eEnc );
213 5329 : XMLFontAutoStylePool_Impl::const_iterator it = pPool->find( &aTmp );
214 5329 : if( it != pPool->end() )
215 : {
216 1692 : sName = (*it)->GetName();
217 : }
218 :
219 10658 : return sName;
220 : }
221 :
222 : namespace
223 : {
224 :
225 0 : OUString lcl_checkFontFile( const OUString &fileUrl )
226 : {
227 0 : osl::DirectoryItem aDirItem;
228 0 : if( osl::DirectoryItem::get( fileUrl, aDirItem ) == osl::File::E_None )
229 : {
230 0 : osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
231 0 : if( aDirItem.getFileStatus( aStatus ) == osl::File::E_None )
232 : {
233 0 : if( !aStatus.isDirectory() )
234 0 : return fileUrl;
235 0 : }
236 : }
237 0 : return OUString();
238 : }
239 :
240 : }
241 :
242 326 : void XMLFontAutoStylePool::exportXML()
243 : {
244 326 : SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_OFFICE,
245 : XML_FONT_FACE_DECLS,
246 326 : true, true );
247 652 : Any aAny;
248 652 : OUString sTmp;
249 652 : XMLFontFamilyNamePropHdl aFamilyNameHdl;
250 652 : XMLFontFamilyPropHdl aFamilyHdl;
251 652 : XMLFontPitchPropHdl aPitchHdl;
252 652 : XMLFontEncodingPropHdl aEncHdl;
253 326 : const SvXMLUnitConverter& rUnitConv = GetExport().GetMM100UnitConverter();
254 :
255 652 : std::map< OUString, OUString > fontFilesMap; // our url to document url
256 326 : sal_uInt32 nCount = pPool->size();
257 1556 : for( sal_uInt32 i=0; i<nCount; i++ )
258 : {
259 1230 : const XMLFontAutoStylePoolEntry_Impl *pEntry = (*pPool)[ i ];
260 :
261 1230 : GetExport().AddAttribute( XML_NAMESPACE_STYLE,
262 2460 : XML_NAME, pEntry->GetName() );
263 :
264 1230 : aAny <<= pEntry->GetFamilyName();
265 1230 : if( aFamilyNameHdl.exportXML( sTmp, aAny, rUnitConv ) )
266 1230 : GetExport().AddAttribute( XML_NAMESPACE_SVG,
267 1230 : XML_FONT_FAMILY, sTmp );
268 :
269 1230 : const OUString& rStyleName = pEntry->GetStyleName();
270 1230 : if( !rStyleName.isEmpty() )
271 18 : GetExport().AddAttribute( XML_NAMESPACE_STYLE,
272 : XML_FONT_ADORNMENTS,
273 18 : rStyleName );
274 :
275 1230 : aAny <<= (sal_Int16)pEntry->GetFamily();
276 1230 : if( aFamilyHdl.exportXML( sTmp, aAny, rUnitConv ) )
277 1173 : GetExport().AddAttribute( XML_NAMESPACE_STYLE,
278 1173 : XML_FONT_FAMILY_GENERIC, sTmp );
279 :
280 1230 : aAny <<= (sal_Int16)pEntry->GetPitch();
281 1230 : if( aPitchHdl.exportXML( sTmp, aAny, rUnitConv ) )
282 1070 : GetExport().AddAttribute( XML_NAMESPACE_STYLE,
283 1070 : XML_FONT_PITCH, sTmp );
284 :
285 1230 : aAny <<= (sal_Int16)pEntry->GetEncoding();
286 1230 : if( aEncHdl.exportXML( sTmp, aAny, rUnitConv ) )
287 4 : GetExport().AddAttribute( XML_NAMESPACE_STYLE,
288 4 : XML_FONT_CHARSET, sTmp );
289 :
290 1230 : SvXMLElementExport aElement( GetExport(), XML_NAMESPACE_STYLE,
291 : XML_FONT_FACE,
292 1230 : true, true );
293 :
294 1230 : if( tryToEmbedFonts )
295 : {
296 0 : const bool bExportFlat( GetExport().getExportFlags() & SvXMLExportFlags::EMBEDDED );
297 0 : 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 0 : for( unsigned int j = 0;
302 0 : j < SAL_N_ELEMENTS( weight );
303 : ++j )
304 : {
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 0 : OUString fileUrl = EmbeddedFontsHelper::fontFileUrl( pEntry->GetFamilyName(), pEntry->GetFamily(),
308 0 : italic[ j ], weight[ j ], pEntry->GetPitch(), pEntry->GetEncoding(),
309 0 : EmbeddedFontsHelper::ViewingAllowed );
310 0 : if( fileUrl.isEmpty())
311 0 : continue;
312 0 : if( !fontFilesMap.count( fileUrl ))
313 : {
314 0 : const OUString docUrl = bExportFlat ? lcl_checkFontFile( fileUrl ) : embedFontFile( fileUrl );
315 0 : if( !docUrl.isEmpty())
316 0 : fontFilesMap[ fileUrl ] = docUrl;
317 : else
318 0 : continue; // --> failed to embed
319 : }
320 0 : fileUrls.push_back( fileUrl );
321 0 : }
322 0 : if( !fileUrls.empty())
323 : {
324 0 : SvXMLElementExport fontFaceSrc( GetExport(), XML_NAMESPACE_SVG,
325 0 : XML_FONT_FACE_SRC, true, true );
326 0 : for( std::vector< OUString >::const_iterator it = fileUrls.begin();
327 0 : it != fileUrls.end();
328 : ++it )
329 : {
330 0 : if( fontFilesMap.count( *it ))
331 : {
332 0 : if( !bExportFlat )
333 : {
334 0 : GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, fontFilesMap[ *it ] );
335 0 : GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, "simple" );
336 : }
337 0 : SvXMLElementExport fontFaceUri( GetExport(), XML_NAMESPACE_SVG,
338 0 : XML_FONT_FACE_URI, true, true );
339 :
340 0 : if( bExportFlat )
341 : {
342 0 : const uno::Reference< ucb::XSimpleFileAccess > xFileAccess( ucb::SimpleFileAccess::create( GetExport().getComponentContext() ) );
343 : try
344 : {
345 0 : const uno::Reference< io::XInputStream > xInput( xFileAccess->openFileRead( fontFilesMap[ *it ] ) );
346 0 : XMLBase64Export aBase64Exp( GetExport() );
347 0 : aBase64Exp.exportOfficeBinaryDataElement( xInput );
348 : }
349 0 : catch( const uno::Exception & )
350 : {
351 : // opening the file failed, ignore
352 0 : }
353 : }
354 :
355 0 : GetExport().AddAttribute( XML_NAMESPACE_SVG, XML_STRING, "truetype" );
356 0 : SvXMLElementExport fontFaceFormat( GetExport(), XML_NAMESPACE_SVG,
357 0 : XML_FONT_FACE_FORMAT, true, true );
358 : }
359 0 : }
360 0 : }
361 : }
362 1556 : }
363 326 : }
364 :
365 0 : OUString XMLFontAutoStylePool::embedFontFile( const OUString& fileUrl )
366 : {
367 : try
368 : {
369 0 : osl::File file( fileUrl );
370 0 : if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None )
371 0 : return OUString();
372 :
373 0 : if ( !GetExport().GetTargetStorage().is() )
374 0 : return OUString();
375 :
376 0 : uno::Reference< embed::XStorage > storage;
377 0 : storage.set( GetExport().GetTargetStorage()->openStorageElement( OUString( "Fonts" ),
378 0 : ::embed::ElementModes::WRITE ), uno::UNO_QUERY_THROW );
379 0 : int index = 0;
380 0 : OUString name;
381 0 : do
382 : {
383 0 : name = "font" + OUString::number( ++index ) + ".ttf";
384 0 : } while( storage->hasByName( name ) );
385 0 : uno::Reference< io::XOutputStream > outputStream;
386 0 : outputStream.set( storage->openStreamElement( name, ::embed::ElementModes::WRITE ), UNO_QUERY_THROW );
387 0 : uno::Reference < beans::XPropertySet > propertySet( outputStream, uno::UNO_QUERY );
388 : assert( propertySet.is());
389 0 : propertySet->setPropertyValue( "MediaType", uno::makeAny( OUString( "application/x-font-ttf" ))); // TODO
390 : for(;;)
391 : {
392 : char buffer[ 4096 ];
393 : sal_uInt64 readSize;
394 : sal_Bool eof;
395 0 : if( file.isEndOfFile( &eof ) != osl::File::E_None )
396 : {
397 : SAL_WARN( "xmloff", "Error reading font file " << fileUrl );
398 0 : outputStream->closeOutput();
399 0 : return OUString();
400 : }
401 0 : if( eof )
402 0 : break;
403 0 : if( file.read( buffer, 4096, readSize ) != osl::File::E_None )
404 : {
405 : SAL_WARN( "xmloff", "Error reading font file " << fileUrl );
406 0 : outputStream->closeOutput();
407 0 : return OUString();
408 : }
409 0 : if( readSize == 0 )
410 0 : break;
411 0 : outputStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), readSize ));
412 0 : }
413 0 : outputStream->closeOutput();
414 0 : if( storage.is() )
415 : {
416 0 : Reference< embed::XTransactedObject > transaction( storage, UNO_QUERY );
417 0 : if( transaction.is())
418 : {
419 0 : transaction->commit();
420 0 : return "Fonts/" + name;
421 0 : }
422 0 : }
423 0 : } catch( const Exception& e )
424 : {
425 : SAL_WARN( "xmloff", "Exception when embedding a font file:" << e.Message );
426 : }
427 0 : return OUString();
428 : }
429 :
430 :
431 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|