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 "swfwriter.hxx"
21 : #include <vcl/virdev.hxx>
22 : #include <vcl/gdimtf.hxx>
23 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
24 :
25 : using namespace ::swf;
26 : using namespace ::std;
27 : using namespace ::com::sun::star::uno;
28 : using namespace ::com::sun::star::io;
29 :
30 :
31 :
32 0 : static MapMode aTWIPSMode( MAP_TWIP );
33 0 : static MapMode a100thmmMode( MAP_100TH_MM );
34 :
35 0 : static sal_Int32 map100thmm( sal_Int32 n100thMM )
36 : {
37 0 : Point aPoint( n100thMM, n100thMM );
38 0 : sal_Int32 nX = OutputDevice::LogicToLogic( aPoint, a100thmmMode, aTWIPSMode ).X();
39 0 : return nX;
40 : }
41 :
42 :
43 :
44 0 : Writer::Writer( sal_Int32 nTWIPWidthOutput, sal_Int32 nTWIPHeightOutput, sal_Int32 nDocWidthInput, sal_Int32 nDocHeightInput, sal_Int32 nJPEGcompressMode )
45 : : mpClipPolyPolygon( NULL ),
46 : mpTag( NULL ),
47 : mpSprite( NULL ),
48 : mnNextId( 1 ),
49 : mnGlobalTransparency(0),
50 0 : mnJPEGCompressMode(nJPEGcompressMode)
51 : {
52 0 : mpVDev = new VirtualDevice;
53 0 : mpVDev->EnableOutput( false );
54 :
55 0 : maMovieTempFile.EnableKillingFile();
56 0 : maFontsTempFile.EnableKillingFile();
57 :
58 0 : mpMovieStream = maMovieTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC );
59 0 : mpFontsStream = maFontsTempFile.GetStream( STREAM_WRITE|STREAM_TRUNC );
60 :
61 0 : mnFrames = 0;
62 :
63 0 : mnDocWidth = map100thmm( nDocWidthInput );
64 0 : mnDocHeight = map100thmm( nDocHeightInput );
65 :
66 0 : mnDocXScale = (double)nTWIPWidthOutput / mnDocWidth;
67 0 : mnDocYScale = (double)nTWIPHeightOutput / mnDocHeight;
68 :
69 : // define an invisible button with the size of a page
70 0 : Rectangle aRect( 0, 0, (long)( mnDocWidth * mnDocXScale ), (long)( mnDocHeight * mnDocYScale ) );
71 0 : Polygon aPoly( aRect );
72 0 : FillStyle aFill = FillStyle( Color(COL_WHITE) );
73 0 : mnWhiteBackgroundShapeId = defineShape( aPoly, aFill );
74 :
75 0 : ::basegfx::B2DHomMatrix m; // #i73264#
76 0 : mnPageButtonId = createID();
77 0 : startTag( TAG_DEFINEBUTTON );
78 0 : mpTag->addUI16( mnPageButtonId ); // character id for button
79 :
80 : // button records
81 0 : mpTag->addUI8( 0x08 ); // only hit state
82 0 : mpTag->addUI16( mnWhiteBackgroundShapeId ); // shape id of background rectangle
83 0 : mpTag->addUI16( 0 ); // depth for button DANGER!
84 0 : mpTag->addMatrix( m ); // identity matrix
85 0 : mpTag->addUI8( 0 ); // empty color transform
86 :
87 : // mpTag->addUI8( 0 ); // end of button records
88 :
89 : // action records
90 0 : mpTag->addUI8( 0x06 ); // ActionPlay
91 0 : mpTag->addUI8( 0 ); // end of action records
92 :
93 0 : endTag();
94 :
95 : // place a shape that clips shapes depth 2-3 to document boundaries
96 : // placeShape( mnWhiteBackgroundShapeId, 1, 0, 0, 4 );
97 0 : }
98 :
99 :
100 :
101 0 : Writer::~Writer()
102 : {
103 0 : delete mpVDev;
104 0 : delete mpSprite;
105 0 : delete mpTag;
106 0 : }
107 :
108 :
109 :
110 0 : void ImplCopySvStreamToXOutputStream( SvStream& rIn, Reference< XOutputStream > &xOut )
111 : {
112 0 : sal_uInt32 nBufferSize = 64*1024;
113 :
114 0 : rIn.Seek( STREAM_SEEK_TO_END );
115 0 : sal_uInt32 nSize = rIn.Tell();
116 0 : rIn.Seek( STREAM_SEEK_TO_BEGIN );
117 :
118 0 : Sequence< sal_Int8 > aBuffer( min( nBufferSize, nSize ) );
119 :
120 0 : while( nSize )
121 : {
122 0 : if( nSize < nBufferSize )
123 : {
124 0 : nBufferSize = nSize;
125 0 : aBuffer.realloc( nSize );
126 : }
127 :
128 0 : sal_uInt32 nRead = rIn.Read( aBuffer.getArray(), nBufferSize );
129 : DBG_ASSERT( nRead == nBufferSize, "ImplCopySvStreamToXOutputStream failed!" );
130 0 : xOut->writeBytes( aBuffer );
131 :
132 0 : if( nRead == 0 )
133 0 : break;
134 :
135 0 : nSize -= nRead;
136 0 : }
137 0 : }
138 :
139 :
140 :
141 0 : void Writer::storeTo( Reference< XOutputStream > &xOutStream )
142 : {
143 0 : for(FontMap::iterator i = maFonts.begin(); i != maFonts.end(); ++i)
144 : {
145 0 : FlashFont* pFont = (*i);
146 0 : pFont->write( *mpFontsStream );
147 0 : delete pFont;
148 : }
149 :
150 : // Endtag
151 0 : mpMovieStream->WriteUInt16( (sal_uInt16)0 );
152 :
153 0 : Tag aHeader( 0xff );
154 :
155 0 : aHeader.addUI8( 'F' );
156 0 : aHeader.addUI8( 'W' );
157 0 : aHeader.addUI8( 'S' );
158 0 : aHeader.addUI8( 5 );
159 :
160 0 : sal_uInt32 nSizePos = aHeader.Tell();
161 :
162 0 : aHeader.WriteUInt32( (sal_uInt32)0 );
163 :
164 0 : Rectangle aDocRect( 0, 0, static_cast<long>(mnDocWidth*mnDocXScale), static_cast<long>(mnDocHeight*mnDocYScale) );
165 :
166 0 : aHeader.addRect( aDocRect );
167 :
168 : // frame delay in 8.8 fixed number of frames per second
169 0 : aHeader.addUI8( 0 );
170 0 : aHeader.addUI8( 12 );
171 :
172 0 : aHeader.addUI16( _uInt16(mnFrames) );
173 :
174 0 : const sal_uInt32 nSize = aHeader.Tell() + mpFontsStream->Tell() + mpMovieStream->Tell();
175 :
176 0 : aHeader.Seek( nSizePos );
177 0 : aHeader.WriteUInt32( (sal_uInt32)nSize );
178 :
179 0 : ImplCopySvStreamToXOutputStream( aHeader, xOutStream );
180 0 : ImplCopySvStreamToXOutputStream( *mpFontsStream, xOutStream );
181 0 : ImplCopySvStreamToXOutputStream( *mpMovieStream, xOutStream );
182 0 : }
183 :
184 :
185 :
186 0 : sal_uInt16 Writer::startSprite()
187 : {
188 0 : sal_uInt16 nShapeId = createID();
189 0 : mvSpriteStack.push(mpSprite);
190 0 : mpSprite = new Sprite( nShapeId );
191 0 : return nShapeId;
192 : }
193 :
194 :
195 :
196 0 : void Writer::endSprite()
197 : {
198 0 : if( mpSprite )
199 : {
200 0 : startTag( TAG_END );
201 0 : endTag();
202 :
203 0 : mpSprite->write( *mpMovieStream );
204 0 : delete mpSprite;
205 :
206 0 : if (!mvSpriteStack.empty())
207 : {
208 0 : mpSprite = mvSpriteStack.top();
209 0 : mvSpriteStack.pop();
210 : }
211 : else
212 0 : mpSprite = NULL;
213 : }
214 0 : }
215 :
216 :
217 :
218 0 : void Writer::placeShape( sal_uInt16 nID, sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y, sal_uInt16 nClip, const char* pName )
219 : {
220 0 : startTag( TAG_PLACEOBJECT2 );
221 :
222 0 : BitStream aBits;
223 :
224 0 : aBits.writeUB( sal_uInt32(nClip != 0), 1 ); // Has Clip Actions?
225 0 : aBits.writeUB( 0, 1 ); // reserved
226 0 : aBits.writeUB( sal_uInt32(pName != NULL), 1 ); // has a name
227 0 : aBits.writeUB( 0, 1 ); // no ratio
228 0 : aBits.writeUB( 0, 1 ); // no color transform
229 0 : aBits.writeUB( 1, 1 ); // has a matrix
230 0 : aBits.writeUB( 1, 1 ); // places a character
231 0 : aBits.writeUB( 0, 1 ); // does not define a character to be moved
232 :
233 0 : mpTag->addBits( aBits );
234 0 : mpTag->addUI16( nDepth ); // depth
235 0 : mpTag->addUI16( nID ); // character Id
236 :
237 : // #i73264#
238 : const basegfx::B2DHomMatrix aMatrix(basegfx::tools::createTranslateB2DHomMatrix(
239 0 : _Int16(static_cast<long>(map100thmm(x)*mnDocXScale)),
240 0 : _Int16(static_cast<long>(map100thmm(y)*mnDocYScale))));
241 0 : mpTag->addMatrix( aMatrix ); // transformation matrix
242 :
243 0 : if( pName )
244 0 : mpTag->addString( pName );
245 :
246 0 : if( nClip != 0 )
247 0 : mpTag->addUI16( nClip );
248 :
249 0 : endTag();
250 0 : }
251 :
252 : #ifdef THEFUTURE
253 :
254 :
255 : void Writer::moveShape( sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y )
256 : {
257 : startTag( TAG_PLACEOBJECT2 );
258 :
259 : BitStream aBits;
260 : aBits.writeUB( 0, 1 ); // Has no Clip Actions
261 : aBits.writeUB( 0, 1 ); // reserved
262 : aBits.writeUB( 0, 1 ); // has no name
263 : aBits.writeUB( 0, 1 ); // no ratio
264 : aBits.writeUB( 0, 1 ); // no color transform
265 : aBits.writeUB( 1, 1 ); // has a matrix
266 : aBits.writeUB( 0, 1 ); // places a character
267 : aBits.writeUB( 1, 1 ); // defines a character to be moved
268 :
269 : mpTag->addBits( aBits );
270 : mpTag->addUI16( nDepth ); // depth
271 :
272 : // #i73264#
273 : const basegfx::B2DHomMatrix aMatrix(basegfx::tools::createTranslateB2DHomMatrix(
274 : _Int16(static_cast<long>(map100thmm(x)*mnDocXScale)),
275 : _Int16(static_cast<long>(map100thmm(y)*mnDocYScale))));
276 : mpTag->addMatrix( aMatrix ); // transformation matrix
277 :
278 : endTag();
279 : }
280 : #endif
281 :
282 :
283 :
284 0 : void Writer::removeShape( sal_uInt16 nDepth )
285 : {
286 0 : startTag( TAG_REMOVEOBJECT2 );
287 0 : mpTag->addUI16( nDepth ); // depth
288 0 : endTag();
289 0 : }
290 :
291 :
292 :
293 0 : void Writer::startTag( sal_uInt8 nTagId )
294 : {
295 : DBG_ASSERT( mpTag == NULL, "Last tag was not ended");
296 :
297 0 : mpTag = new Tag( nTagId );
298 0 : }
299 :
300 :
301 :
302 0 : void Writer::endTag()
303 : {
304 0 : sal_uInt8 nTag = mpTag->getTagId();
305 :
306 0 : if( mpSprite && ( (nTag == TAG_END) || (nTag == TAG_SHOWFRAME) || (nTag == TAG_DOACTION) || (nTag == TAG_STARTSOUND) || (nTag == TAG_PLACEOBJECT) || (nTag == TAG_PLACEOBJECT2) || (nTag == TAG_REMOVEOBJECT2) || (nTag == TAG_FRAMELABEL) ) )
307 : {
308 0 : mpSprite->addTag( mpTag );
309 0 : mpTag = NULL;
310 : }
311 : else
312 : {
313 0 : mpTag->write( *mpMovieStream );
314 0 : delete mpTag;
315 0 : mpTag = NULL;
316 : }
317 0 : }
318 :
319 :
320 :
321 0 : sal_uInt16 Writer::createID()
322 : {
323 0 : return mnNextId++;
324 : }
325 :
326 :
327 :
328 0 : void Writer::showFrame()
329 : {
330 0 : startTag( TAG_SHOWFRAME );
331 0 : endTag();
332 :
333 0 : if(NULL == mpSprite)
334 0 : mnFrames++;
335 0 : }
336 :
337 :
338 :
339 0 : sal_uInt16 Writer::defineShape( const GDIMetaFile& rMtf, sal_Int16 x, sal_Int16 y )
340 : {
341 0 : mpVDev->SetMapMode( rMtf.GetPrefMapMode() );
342 0 : Impl_writeActions( rMtf );
343 :
344 0 : sal_uInt16 nId = 0;
345 : {
346 0 : CharacterIdVector::iterator aIter( maShapeIds.begin() );
347 0 : const CharacterIdVector::iterator aEnd( maShapeIds.end() );
348 :
349 0 : sal_Bool bHaveShapes = aIter != aEnd;
350 :
351 0 : if (bHaveShapes)
352 : {
353 0 : nId = startSprite();
354 :
355 0 : sal_uInt16 iDepth = 1;
356 0 : for(; aIter != aEnd; ++aIter)
357 : {
358 0 : placeShape( *aIter, iDepth++, x, y );
359 : }
360 :
361 0 : endSprite();
362 : }
363 : }
364 :
365 0 : maShapeIds.clear();
366 :
367 0 : return nId;
368 : }
369 :
370 :
371 :
372 0 : sal_uInt16 Writer::defineShape( const Polygon& rPoly, const FillStyle& rFillStyle )
373 : {
374 0 : const PolyPolygon aPolyPoly( rPoly );
375 0 : return defineShape( aPolyPoly, rFillStyle );
376 : }
377 :
378 :
379 :
380 0 : sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, const FillStyle& rFillStyle )
381 : {
382 0 : sal_uInt16 nShapeId = createID();
383 :
384 : // start a DefineShape3 tag
385 0 : startTag( TAG_DEFINESHAPE3 );
386 :
387 0 : mpTag->addUI16( nShapeId );
388 0 : mpTag->addRect( rPolyPoly.GetBoundRect() );
389 :
390 :
391 : // FILLSTYLEARRAY
392 0 : mpTag->addUI8( 1 ); // FillStyleCount
393 :
394 : // FILLSTYLE
395 0 : rFillStyle.addTo( mpTag );
396 :
397 : // LINESTYLEARRAY
398 0 : mpTag->addUI8( 0 ); // LineStyleCount
399 :
400 : // Number of fill and line index bits to 1
401 0 : mpTag->addUI8( 0x11 );
402 :
403 0 : BitStream aBits;
404 :
405 0 : const sal_uInt16 nCount = rPolyPoly.Count();
406 : sal_uInt16 i;
407 0 : for( i = 0; i < nCount; i++ )
408 : {
409 0 : const Polygon& rPoly = rPolyPoly[ i ];
410 0 : if( rPoly.GetSize() )
411 0 : Impl_addPolygon( aBits, rPoly, true );
412 : }
413 :
414 0 : Impl_addEndShapeRecord( aBits );
415 :
416 0 : mpTag->addBits( aBits );
417 0 : endTag();
418 :
419 0 : return nShapeId;
420 : }
421 :
422 :
423 :
424 0 : sal_uInt16 Writer::defineShape( const PolyPolygon& rPolyPoly, sal_uInt16 nLineWidth, const Color& rLineColor )
425 : {
426 0 : sal_uInt16 nShapeId = createID();
427 :
428 : // start a DefineShape3 tag
429 0 : startTag( TAG_DEFINESHAPE3 );
430 :
431 0 : mpTag->addUI16( nShapeId );
432 0 : mpTag->addRect( rPolyPoly.GetBoundRect() );
433 :
434 :
435 : // FILLSTYLEARRAY
436 0 : mpTag->addUI8( 0 ); // FillStyleCount
437 :
438 : // LINESTYLEARRAY
439 0 : mpTag->addUI8( 1 ); // LineStyleCount
440 :
441 : // LINESTYLE
442 0 : mpTag->addUI16( nLineWidth ); // Width of line in twips
443 0 : mpTag->addRGBA( rLineColor ); // Color
444 :
445 : // Number of fill and line index bits to 1
446 0 : mpTag->addUI8( 0x11 );
447 :
448 0 : BitStream aBits;
449 :
450 0 : const sal_uInt16 nCount = rPolyPoly.Count();
451 : sal_uInt16 i;
452 0 : for( i = 0; i < nCount; i++ )
453 : {
454 0 : const Polygon& rPoly = rPolyPoly[ i ];
455 0 : if( rPoly.GetSize() )
456 0 : Impl_addPolygon( aBits, rPoly, false );
457 : }
458 :
459 0 : Impl_addEndShapeRecord( aBits );
460 :
461 0 : mpTag->addBits( aBits );
462 0 : endTag();
463 :
464 0 : return nShapeId;
465 : }
466 :
467 :
468 :
469 :
470 0 : void Writer::stop()
471 : {
472 0 : startTag( TAG_DOACTION );
473 0 : mpTag->addUI8( 0x07 );
474 0 : mpTag->addUI8( 0 );
475 0 : endTag();
476 0 : }
477 :
478 :
479 :
480 0 : void Writer::waitOnClick( sal_uInt16 nDepth )
481 : {
482 0 : placeShape( _uInt16( mnPageButtonId ), nDepth, 0, 0 );
483 0 : stop();
484 0 : showFrame();
485 0 : removeShape( nDepth );
486 0 : }
487 :
488 :
489 :
490 : /** inserts a doaction tag with an ActionGotoFrame */
491 0 : void Writer::gotoFrame( sal_uInt16 nFrame )
492 : {
493 0 : startTag( TAG_DOACTION );
494 0 : mpTag->addUI8( 0x81 );
495 0 : mpTag->addUI16( 2 );
496 0 : mpTag->addUI16( nFrame );
497 0 : mpTag->addUI8( 0 );
498 0 : endTag();
499 0 : }
500 :
501 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|