1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_filter.hxx"
26 #include <com/sun/star/container/XNamed.hpp>
27 #include <com/sun/star/beans/PropertyValue.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
30 #ifndef _COM_SUN_STAR_PRESENTATION_PRESENTATIONPAGE_HPP_
31 #include <com/sun/star/presentation/XPresentationPage.hpp>
32 #endif
33 #include <com/sun/star/container/XIndexAccess.hpp>
34 #include <com/sun/star/document/XFilter.hpp>
35 #include <com/sun/star/text/XText.hpp>
36 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
37 #include <com/sun/star/frame/XModel.hpp>
38 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
39 #include <rtl/ustrbuf.hxx>
40 #include <rtl/string.hxx>
41 #include <osl/diagnose.h>
42 
43 #include <vector>
44 
45 #include "exporter.hxx"
46 #include "Base64Codec.hxx"
47 #include "zip.hxx"
48 #include "tempfile.hxx"
49 
50 using rtl::OUString;
51 using rtl::OString;
52 using namespace ::com::sun::star::uno;
53 using namespace ::com::sun::star::drawing;
54 using namespace ::com::sun::star::container;
55 using namespace ::com::sun::star::document;
56 using namespace ::com::sun::star::io;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::text;
59 using namespace ::std;
60 
61 using com::sun::star::beans::PropertyValue;
62 using com::sun::star::beans::XPropertySet;
63 using com::sun::star::presentation::XPresentationPage;
64 using com::sun::star::task::XStatusIndicator;
65 
66 // -----------------------------------------------------------------------------
67 
PlaceWareExporter(const Reference<XMultiServiceFactory> & rxMSF)68 PlaceWareExporter::PlaceWareExporter(const Reference< XMultiServiceFactory > &rxMSF)
69 :	mxMSF( rxMSF )
70 {
71 }
72 
73 // -----------------------------------------------------------------------------
74 
~PlaceWareExporter()75 PlaceWareExporter::~PlaceWareExporter()
76 {
77 }
78 
79 // -----------------------------------------------------------------------------
80 class PageEntry
81 {
82 private:
83 	TempFile maTempFile;
84 	rtl::OUString maName;
85 	rtl::OUString maTitle;
86 	rtl::OUString maNotes;
87 	rtl::OUString maURL;
88 
89 public:
90 	PageEntry();
91 	~PageEntry();
92 
getTempURL()93 	OUString getTempURL() { return maTempFile.getFileURL(); }
94 
setName(const rtl::OUString & rName)95 	void setName( const rtl::OUString& rName ) { maName = rName; }
getName() const96 	const rtl::OUString& getName() const { return maName; }
97 
setTitle(const rtl::OUString & rTitle)98 	void setTitle( const rtl::OUString& rTitle ) { maTitle = rTitle; }
getTitle() const99 	const rtl::OUString& getTitle() const { return maTitle; }
100 
setNotes(const rtl::OUString & rNotes)101 	void setNotes( const rtl::OUString& rNotes ) { maNotes = rNotes; }
getNotes() const102 	const rtl::OUString& getNotes() const { return maNotes; }
103 
setURL(const rtl::OUString & rURL)104 	void setURL( const rtl::OUString& rURL ) { maURL = rURL; }
getURL() const105 	const rtl::OUString& getURL() const { return maURL; }
106 };
107 
PageEntry()108 PageEntry::PageEntry()
109 : maTempFile( TempFile::createTempFileURL() )
110 {
111 }
112 
~PageEntry()113 PageEntry::~PageEntry()
114 {
115 }
116 
117 
encodeFile(osl::File & rSourceFile,Reference<XOutputStream> & xOutputStream)118 static void encodeFile( osl::File& rSourceFile, Reference< XOutputStream >& xOutputStream ) throw( ::com::sun::star::uno::Exception )
119 {
120 	if( xOutputStream.is() )
121 	{
122 		sal_uInt64 nTemp( 0 );
123 
124 		osl::File::RC nRC = rSourceFile.setPos( osl_Pos_End, 0  );
125 		if( osl::File::E_None == nRC )
126 		{
127 			nRC = rSourceFile.getPos( nTemp );
128 			if( osl::File::E_None == nRC )
129 			{
130 				nRC = rSourceFile.setPos( osl_Pos_Absolut, 0 );
131 			}
132 		}
133 
134 		sal_Int32 nLen = static_cast<sal_Int32>(nTemp);
135 
136 		if( osl::File::E_None != nRC )
137 			throw IOException();
138 
139 		sal_Int32 nBufferSize = 3*1024;	// !!! buffer size must be a factor of 3 for base64 to work
140 		Sequence< sal_Int8 > aInBuffer( nBufferSize < nLen ? nBufferSize : nLen );
141 		void* pInBuffer = aInBuffer.getArray();
142 
143 		Sequence< sal_Int8 > aOutBuffer;
144 		sal_Int32 nRead;
145 		while( nLen )
146 		{
147 			nRC = rSourceFile.read( pInBuffer, aInBuffer.getLength(), nTemp );
148 
149 			if( (nRC != osl::File::E_None) || (0 == nTemp) )
150 				throw IOException();
151 
152 			nRead = static_cast<sal_Int32>( nTemp );
153 
154 			if( nRead < aInBuffer.getLength() )
155 			{
156 				aInBuffer.realloc( nRead );
157 				pInBuffer = aInBuffer.getArray();
158 			}
159 
160 			nLen -= nRead;
161 
162 			rtl::OUStringBuffer aStrBuffer;
163 			Base64Codec::encodeBase64( aStrBuffer, aInBuffer );
164 
165 			sal_Int32 nCount = aStrBuffer.getLength();
166 
167 			if( aOutBuffer.getLength() != nCount )
168 				aOutBuffer.realloc( nCount );
169 
170 			sal_Int8* pBytes = aOutBuffer.getArray();
171 			const sal_Unicode* pUnicode = aStrBuffer.getStr();
172 
173 			while( nCount-- )
174 			{
175 				// since base64 is always ascii, we can cast safely
176 				*pBytes++ = static_cast<sal_Int8>(*pUnicode++);
177 			}
178 
179 			xOutputStream->writeBytes( aOutBuffer );
180 		}
181 	}
182 }
183 
convertString(OUString aInput)184 static OString convertString( OUString aInput )
185 {
186 	OString aRet( aInput.getStr(), aInput.getLength(), RTL_TEXTENCODING_ASCII_US );
187 	aRet = aRet.replace( '\r', ' ' );
188 	aRet = aRet.replace( '\n', ' ' );
189 
190 	return aRet;
191 }
192 
createSlideFile(Reference<XComponent> xDoc,ZipFile & rZipFile,const rtl::OUString & rURL,vector<PageEntry * > & rPageEntries)193 static void createSlideFile( Reference< XComponent > xDoc, ZipFile& rZipFile, const rtl::OUString& rURL, vector< PageEntry* >& rPageEntries  ) throw( ::com::sun::star::uno::Exception )
194 {
195 	OString aInfo;
196 
197 	const OString aNewLine( "\r\n" );
198 	OUString aTemp;
199 
200 	Reference< XDocumentPropertiesSupplier > xDPS( xDoc, UNO_QUERY );
201 	Reference< XDocumentProperties > xDocProps( xDPS->getDocumentProperties() );
202 
203     aTemp = xDocProps->getTitle();
204 	if( 0 == aTemp.getLength() )
205 	{
206 		sal_Int32 nPos1 = rURL.lastIndexOf( (sal_Unicode)'/' );
207 		if( -1 != nPos1 )
208 		{
209 			sal_Int32 nPos2 = rURL.lastIndexOf( (sal_Unicode)'.' );
210 			if( nPos2 > nPos1 )
211 			{
212 				aTemp = rURL.copy( nPos1 + 1, nPos2 - nPos1 - 1 );
213 			}
214 			else
215 			{
216 				aTemp = rURL.copy( nPos1 + 1 );
217 			}
218 		}
219 		else
220 		{
221 			aTemp = rURL;
222 		}
223 	}
224 
225 	aInfo += OString( "SlideSetName: " );
226 	aInfo += convertString( aTemp );
227 	aInfo += aNewLine;
228 
229     aTemp = xDocProps->getAuthor();
230 
231 	if( aTemp.getLength() )
232 	{
233 		aInfo += OString( "PresenterName: " );
234 		aInfo += convertString( aTemp );
235 		aInfo += aNewLine;
236 	}
237 
238 	vector< PageEntry* >::iterator aIter( rPageEntries.begin() );
239 	vector< PageEntry* >::iterator aEnd( rPageEntries.end() );
240 	while( aIter != aEnd )
241 	{
242 		PageEntry* pEntry = (*aIter++);
243 
244 		aInfo += OString( "slide: " );
245 		if( pEntry->getTitle().getLength() )
246 		{
247 			aInfo += convertString( pEntry->getTitle() );
248 		}
249 		else
250 		{
251 			aInfo += convertString( pEntry->getName() );
252 		}
253 		aInfo += aNewLine;
254 
255 		aInfo += OString( "type: gif");
256 		aInfo += aNewLine;
257 
258 		aInfo += OString( "url: " );
259 		aInfo += convertString( pEntry->getURL() );
260 		aInfo += aNewLine;
261 
262 
263 		if( pEntry->getNotes().getLength() )
264 		{
265 			aInfo += OString( "notes: " );
266 			aInfo += convertString( pEntry->getNotes() );
267 			aInfo += aNewLine;
268 		}
269 	}
270 
271 	TempFile aInfoFile( TempFile::createTempFileURL() );
272 
273 	osl::File::RC nRC;
274 	sal_uInt64 nTemp;
275 
276 	nRC = aInfoFile.open( OpenFlag_Write );
277 	if( osl::File::E_None == nRC )
278 	{
279 		nRC = aInfoFile.write( aInfo.getStr(), aInfo.getLength(), nTemp );
280 		if( osl::File::E_None == nRC )
281 		{
282 			nRC = aInfoFile.setPos( osl_Pos_Absolut, 0 );
283 			if( osl::File::E_None == nRC )
284 			{
285 				nRC = aInfoFile.close();
286 			}
287 		}
288 	}
289 
290 	if( (osl::File::E_None != nRC) || !rZipFile.addFile( aInfoFile, OString( RTL_CONSTASCII_STRINGPARAM("slides.txt") ) ))
291 		throw IOException();
292 }
293 
294 //#define PLACEWARE_DEBUG 1
295 
doExport(Reference<XComponent> xDoc,Reference<XOutputStream> xOutputStream,const rtl::OUString & rURL,Reference<XInterface>,Reference<XStatusIndicator> & xStatusIndicator)296 sal_Bool PlaceWareExporter::doExport( Reference< XComponent > xDoc, Reference < XOutputStream > xOutputStream,
297 										const rtl::OUString& rURL, Reference < XInterface > /* xHandler */, Reference < XStatusIndicator >& xStatusIndicator )
298 {
299 	sal_Bool bRet = sal_False;
300 
301 	mxGraphicExporter = Reference< XExporter >::query( mxMSF->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.drawing.GraphicExportFilter") ) ) );
302 	Reference< XDrawPagesSupplier > xDrawPagesSupplier(xDoc, UNO_QUERY);
303 	if(!xDrawPagesSupplier.is())
304 		return sal_False;
305 
306 	Reference< XIndexAccess > xDrawPages( xDrawPagesSupplier->getDrawPages(), UNO_QUERY );
307 	if(!xDrawPages.is())
308 		return sal_False;
309 
310 	if(xStatusIndicator.is())
311 	{
312 		xStatusIndicator->start(OUString(  RTL_CONSTASCII_USTRINGPARAM( "PlaceWare:" )),xDrawPages->getCount());
313 	}
314 
315 	Reference< XDrawPage > xDrawPage;
316 
317 	osl::File::RC nRC;
318 
319 #ifndef PLACEWARE_DEBUG
320 	TempFile aTempFile( TempFile::createTempFileURL() );
321 	nRC = aTempFile.open( osl_File_OpenFlag_Write|osl_File_OpenFlag_Read );
322 	OUString aURL( aTempFile.getFileURL() );
323 #else
324 	OUString aURL( RTL_CONSTASCII_USTRINGPARAM("file:///e:/test.zip") );
325 	osl::File::remove( aURL );
326 	osl::File aTempFile( aURL );
327 	nRC = aTempFile.open( osl_File_OpenFlag_Create|osl_File_OpenFlag_Write|osl_File_OpenFlag_Read );
328 #endif
329 
330 	if( osl::File::E_None != nRC )
331 		return sal_False;
332 
333 	vector< PageEntry* > aPageEntries;
334 
335     // Create new package...
336     try
337     {
338 		ZipFile aZipFile(aTempFile);
339 
340 		// export slides as gifs and collect information for slides
341 
342 		const sal_Int32 nPageCount = xDrawPages->getCount();
343 		sal_Int32 nPage;
344 
345 		for( nPage = 0; nPage < nPageCount; nPage++)
346 		{
347 			xDrawPages->getByIndex(nPage) >>= xDrawPage;
348 
349 			if( !xDrawPage.is() )
350 				continue;
351 
352 			PageEntry* pEntry = exportPage( xDrawPage );
353 			aPageEntries.push_back( pEntry );
354 
355 			OUString aName( RTL_CONSTASCII_USTRINGPARAM("i") );
356 			aName += OUString::valueOf( nPage );
357 			aName += OUString( RTL_CONSTASCII_USTRINGPARAM(".gif") );
358 			pEntry->setURL( aName );
359 
360 			if(xStatusIndicator.is())
361 			{
362 				xStatusIndicator->setValue( nPage + 1 );
363 			}
364 		}
365 
366 		// create the slide.txt file
367 
368 		createSlideFile( xDoc, aZipFile, rURL, aPageEntries );
369 
370 		// add gifs to zip
371 		vector< PageEntry* >::iterator aIter( aPageEntries.begin() );
372 		vector< PageEntry* >::iterator aEnd( aPageEntries.end() );
373 		while( aIter != aEnd )
374 		{
375 			PageEntry* pEntry = (*aIter++);
376 
377 			osl::File aFile(pEntry->getTempURL() );
378 			const OUString aTemp( pEntry->getURL() );
379 
380 			if( (osl::File::E_None != nRC) || !aZipFile.addFile( aFile, OString( aTemp.getStr(), aTemp.getLength(), RTL_TEXTENCODING_ASCII_US ) ) )
381 				throw IOException();
382 		}
383 
384 		if(!aZipFile.close())
385 			throw IOException();
386 
387 		encodeFile( aTempFile, xOutputStream );
388 
389 		bRet = sal_True;
390     }
391     catch ( RuntimeException const & )
392     {
393     }
394     catch ( Exception const & )
395     {
396     }
397 
398 	vector< PageEntry* >::iterator aIter( aPageEntries.begin() );
399 	vector< PageEntry* >::iterator aEnd( aPageEntries.end() );
400 	while( aIter != aEnd )
401 	{
402 		delete (*aIter++);
403 	}
404 
405 	if( xStatusIndicator.is() )
406 		xStatusIndicator->end();
407 
408 	return bRet;
409 }
410 
411 // -----------------------------------------------------------------------------
412 
exportPage(Reference<XDrawPage> & xDrawPage)413 PageEntry* PlaceWareExporter::exportPage( Reference< XDrawPage >&xDrawPage )
414 {
415 	Reference< XComponent > xComp( xDrawPage, UNO_QUERY );
416 
417 	PageEntry* pEntry = new PageEntry();
418 
419 	// get page name
420 	Reference< XNamed > xNamed( xDrawPage, UNO_QUERY );
421 	if( xNamed.is() )
422 		pEntry->setName( xNamed->getName() );
423 
424 	// get title text from title presentation shape if available
425 	const OUString szTitleTextShape( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.presentation.TitleTextShape") );
426 	const OUString szIsEmptyPresObj( RTL_CONSTASCII_USTRINGPARAM("IsEmptyPresentationObject") );
427 
428 	sal_Int32 nShapeCount = xDrawPage->getCount();
429 	sal_Int32 nShape;
430 	for( nShape = 0; nShape < nShapeCount; nShape++ )
431 	{
432 		Reference< XShape > xShape;
433 		xDrawPage->getByIndex( nShape ) >>= xShape;
434 
435 		if( xShape.is() && xShape->getShapeType() == szTitleTextShape )
436 		{
437 			Reference< XPropertySet > xPropSet( xShape, UNO_QUERY );
438 			if( xPropSet.is() )
439 			{
440 				sal_Bool bIsEmpty = true;
441 				xPropSet->getPropertyValue( szIsEmptyPresObj ) >>= bIsEmpty;
442 
443 				if( !bIsEmpty )
444 				{
445 					Reference< XText > xText( xShape, UNO_QUERY );
446 					if( xText.is() )
447 					{
448 						pEntry->setTitle( xText->getString() );
449 					}
450 				}
451 			}
452 			break;
453 		}
454 	}
455 
456 	// get notes text if available
457 	Reference< XPresentationPage > xPresPage( xDrawPage, UNO_QUERY );
458 	if( xPresPage.is() )
459 	{
460 		Reference< XDrawPage > xNotesPage( xPresPage->getNotesPage() );
461 
462 		const OUString szNotesShape( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.presentation.NotesShape") );
463 
464 		nShapeCount = xNotesPage->getCount();
465 		for( nShape = 0; nShape < nShapeCount; nShape++ )
466 		{
467 			Reference< XShape > xShape;
468 			xNotesPage->getByIndex( nShape ) >>= xShape;
469 
470 			if( xShape.is() && (xShape->getShapeType() == szNotesShape) )
471 			{
472 				Reference< XPropertySet > xPropSet( xShape, UNO_QUERY );
473 				if( xPropSet.is() )
474 				{
475 					sal_Bool bIsEmpty = true;
476 					xPropSet->getPropertyValue( szIsEmptyPresObj ) >>= bIsEmpty;
477 
478 					if( !bIsEmpty )
479 					{
480 						Reference< XText > xText( xShape, UNO_QUERY );
481 						if( xText.is() )
482 						{
483 							pEntry->setNotes( xText->getString() );
484 						}
485 					}
486 				}
487 				break;
488 			}
489 		}
490 	}
491 
492 	// create the gif
493 	Reference< XFilter > xFilter( mxGraphicExporter, UNO_QUERY );
494 
495 	Sequence< PropertyValue > aFilterData( 2 );
496 	aFilterData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("Width") );
497 	aFilterData[0].Value <<= (sal_Int32)704;
498 	aFilterData[1].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("Translucent") );
499 	aFilterData[1].Value <<= (sal_Bool)sal_False;
500 
501 	Sequence< PropertyValue > aDescriptor( 3 );
502 	aDescriptor[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("FilterName") );
503 	aDescriptor[0].Value <<= OUString( RTL_CONSTASCII_USTRINGPARAM("GIF") );
504 	aDescriptor[1].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("URL") );
505 	aDescriptor[1].Value <<= OUString( pEntry->getTempURL() );
506 	aDescriptor[2].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("FilterData") );
507 	aDescriptor[2].Value <<= aFilterData;
508 	mxGraphicExporter->setSourceDocument( xComp );
509 	xFilter->filter( aDescriptor );
510 
511 	return pEntry;
512 }
513