xref: /trunk/main/filter/source/flash/swfwriter2.cxx (revision c938ccda)
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 "swfwriter.hxx"
27 #include <vcl/virdev.hxx>
28 #include <basegfx/matrix/b2dhommatrixtools.hxx>
29 
30 #include <math.h>
31 
32 using namespace ::swf;
33 using namespace ::std;
34 using namespace ::com::sun::star::uno;
35 using namespace ::com::sun::star::io;
36 using ::rtl::OUString;
37 using ::rtl::OString;
38 
39 // -----------------------------------------------------------------------------
40 
getMaxBitsUnsigned(sal_uInt32 nValue)41 sal_uInt16 getMaxBitsUnsigned( sal_uInt32 nValue )
42 {
43 	sal_uInt16 nBits = 0;
44 
45 	while( nValue )
46 	{
47 		nBits++;
48 		nValue >>= 1;
49 	}
50 
51 	return nBits;
52 }
53 
54 // -----------------------------------------------------------------------------
55 
getMaxBitsSigned(sal_Int32 nValue)56 sal_uInt16 getMaxBitsSigned( sal_Int32 nValue )
57 {
58 	if( nValue < 0 )
59 		nValue *= -1;
60 
61 	return getMaxBitsUnsigned( static_cast< sal_uInt32 >(nValue) ) + 1;
62 }
63 
64 // -----------------------------------------------------------------------------
65 
BitStream()66 BitStream::BitStream()
67 {
68 	mnBitPos = 8;
69 	mnCurrentByte = 0;
70 }
71 
72 // -----------------------------------------------------------------------------
73 
writeUB(sal_uInt32 nValue,sal_uInt16 nBits)74 void BitStream::writeUB( sal_uInt32 nValue, sal_uInt16 nBits )
75 {
76 	while( nBits != 0 )
77 	{
78 		mnCurrentByte |= nValue << (32 - nBits) >> (32 - mnBitPos);
79 
80 		if ( nBits > mnBitPos )
81 		{
82 			nBits = nBits - mnBitPos;
83 			mnBitPos = 0;
84 		}
85 		else
86 		{
87 			mnBitPos = sal::static_int_cast<sal_uInt8>( mnBitPos - nBits );
88 			nBits = 0;
89 		}
90 
91 		if( 0 == mnBitPos )
92 			pad();
93 	}
94 }
95 
96 // -----------------------------------------------------------------------------
97 
writeSB(sal_Int32 nValue,sal_uInt16 nBits)98 void BitStream::writeSB( sal_Int32 nValue, sal_uInt16 nBits )
99 {
100 	writeUB( static_cast< sal_uInt32 >(nValue), nBits );
101 }
102 
103 // -----------------------------------------------------------------------------
104 
writeFB(sal_uInt32 nValue,sal_uInt16 nBits)105 void BitStream::writeFB( sal_uInt32 nValue, sal_uInt16 nBits )
106 {
107 	writeUB( nValue, nBits );
108 }
109 
110 // -----------------------------------------------------------------------------
111 
pad()112 void BitStream::pad()
113 {
114 	if( 8 != mnBitPos )
115 	{
116 		maData.push_back( mnCurrentByte );
117 		mnCurrentByte = 0;
118 		mnBitPos = 8;
119 	}
120 }
121 
122 // -----------------------------------------------------------------------------
123 
writeTo(SvStream & out)124 void BitStream::writeTo( SvStream& out )
125 {
126 	pad();
127 
128 	vector< sal_uInt8 >::iterator aIter( maData.begin() );
129 	const vector< sal_uInt8>::iterator aEnd( maData.end() );
130 	while(aIter != aEnd)
131 	{
132 		out << (*aIter++);
133 	}
134 }
135 
136 // -----------------------------------------------------------------------------
137 
getOffset() const138 sal_uInt32 BitStream::getOffset() const
139 {
140 	return maData.size();
141 }
142 
143 ////////////////////////////////////////////////////////////////////////////////
144 
Tag(sal_uInt8 nTagId)145 Tag::Tag( sal_uInt8 nTagId )
146 {
147 	mnTagId = nTagId;
148 }
149 
150 // -----------------------------------------------------------------------------
151 
write(SvStream & out)152 void Tag::write( SvStream &out )
153 {
154 	Seek( STREAM_SEEK_TO_END );
155 	sal_uInt32 nSz = Tell();
156 	Seek( STREAM_SEEK_TO_BEGIN );
157 
158 	if( mnTagId != 0xff )
159 	{
160 		bool bLarge = nSz > 62;
161 
162 		sal_uInt16 nCode = ( mnTagId << 6 ) | ( bLarge ? 0x3f : _uInt16(nSz) );
163 
164 		out << (sal_uInt8)nCode;
165 		out << (sal_uInt8)(nCode >> 8);
166 
167 		if( bLarge )
168 		{
169 			sal_uInt32 nTmp = nSz;
170 
171 			out << (sal_uInt8)nTmp;
172 			nTmp >>= 8;
173 			out << (sal_uInt8)nTmp;
174 			nTmp >>= 8;
175 			out << (sal_uInt8)nTmp;
176 			nTmp >>= 8;
177 			out << (sal_uInt8)nTmp;
178 		}
179 	}
180 
181 	out.Write( GetData(), nSz );
182 }
183 #if 0
184 // -----------------------------------------------------------------------------
185 
186 void Tag::addI32( sal_Int32 nValue )
187 {
188 	addUI32( static_cast<sal_uInt32>( nValue ) );
189 }
190 #endif
191 // -----------------------------------------------------------------------------
192 
addUI32(sal_uInt32 nValue)193 void Tag::addUI32( sal_uInt32 nValue )
194 {
195 	*this << nValue;
196 }
197 #if 0
198 // -----------------------------------------------------------------------------
199 
200 void Tag::addI16( sal_Int16 nValue )
201 {
202 	addUI16( static_cast<sal_uInt16>( nValue ) );
203 }
204 #endif
205 // -----------------------------------------------------------------------------
206 
addUI16(sal_uInt16 nValue)207 void Tag::addUI16( sal_uInt16 nValue )
208 {
209 	*this << (sal_uInt8)nValue;
210 	*this << (sal_uInt8)(nValue >> 8);
211 }
212 
213 // -----------------------------------------------------------------------------
214 
addUI8(sal_uInt8 nValue)215 void Tag::addUI8( sal_uInt8 nValue )
216 {
217 	*this << (sal_uInt8)nValue;
218 }
219 
220 // -----------------------------------------------------------------------------
221 
addBits(BitStream & rIn)222 void Tag::addBits( BitStream& rIn )
223 {
224 	rIn.writeTo( *this );
225 }
226 
227 // -----------------------------------------------------------------------------
228 
addRGBA(const Color & rColor)229 void Tag::addRGBA( const Color& rColor )
230 {
231 	addUI8( rColor.GetRed() );
232 	addUI8( rColor.GetGreen() );
233 	addUI8( rColor.GetBlue() );
234 	addUI8( 0xff - rColor.GetTransparency() );
235 }
236 
237 // -----------------------------------------------------------------------------
238 
addRGB(const Color & rColor)239 void Tag::addRGB( const Color& rColor )
240 {
241 	addUI8( rColor.GetRed() );
242 	addUI8( rColor.GetGreen() );
243 	addUI8( rColor.GetBlue() );
244 }
245 
246 // -----------------------------------------------------------------------------
247 
addRect(const Rectangle & rRect)248 void Tag::addRect( const Rectangle& rRect )
249 {
250 	writeRect( *this, rRect );
251 }
252 
253 // -----------------------------------------------------------------------------
254 
writeRect(SvStream & rOut,const Rectangle & rRect)255 void Tag::writeRect( SvStream& rOut, const Rectangle& rRect )
256 {
257 	BitStream aBits;
258 
259 	sal_Int32 minX, minY, maxX, maxY;
260 
261 	if( rRect.nLeft < rRect.nRight )
262 	{
263 		minX = rRect.nLeft; maxX = rRect.nRight;
264 	}
265 	else
266 	{
267 		maxX = rRect.nLeft; minX = rRect.nRight;
268 	}
269 
270 
271 	if( rRect.nTop < rRect.nBottom )
272 	{
273 		minY = rRect.nTop; maxY = rRect.nBottom;
274 	}
275 	else
276 	{
277 		maxY = rRect.nTop; minY = rRect.nBottom;
278 	}
279 
280 	// AS: Figure out the maximum number of bits required to represent any of the
281 	//  rectangle coordinates.  Since minX or minY could be negative, they could
282 	//  actually require more bits than maxX or maxY.
283 	// AS: Christian, can they be negative, or is that a wasted check?
284 	// CL: I think so, f.e. for shapes that have the top and/or left edge outside
285 	//         the page origin
286 	sal_uInt8 nBits1 = sal::static_int_cast<sal_uInt8>( max( getMaxBitsSigned( minX ), getMaxBitsSigned( minY ) ) );
287 	sal_uInt8 nBits2 = sal::static_int_cast<sal_uInt8>( max( getMaxBitsSigned( maxX ), getMaxBitsSigned( maxY ) ) );
288 	sal_uInt8 nBitsMax = max( nBits1, nBits2 );
289 
290 	aBits.writeUB( nBitsMax, 5 );
291 	aBits.writeSB( minX, nBitsMax );
292 	aBits.writeSB( maxX, nBitsMax );
293 	aBits.writeSB( minY, nBitsMax );
294 	aBits.writeSB( maxY, nBitsMax );
295 
296 	aBits.writeTo( rOut );
297 }
298 
299 // -----------------------------------------------------------------------------
300 
addMatrix(const::basegfx::B2DHomMatrix & rMatrix)301 void Tag::addMatrix( const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264#
302 {
303 	writeMatrix( *this, rMatrix );
304 }
305 
306 // -----------------------------------------------------------------------------
307 
writeMatrix(SvStream & rOut,const::basegfx::B2DHomMatrix & rMatrix)308 void Tag::writeMatrix( SvStream& rOut, const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264#
309 {
310 
311 	BitStream aBits;
312 
313 	const sal_uInt8 bHasScale = rMatrix.get(0, 0) != 1.0 || rMatrix.get(1, 1) != 1.0;
314 
315 	aBits.writeUB( bHasScale, 1 );
316 
317 	if( bHasScale )
318 	{
319 		sal_uInt8 nScaleBits = 31;
320 
321 		aBits.writeUB( nScaleBits, 5 );
322 		aBits.writeFB( getFixed( rMatrix.get(0, 0) ), nScaleBits );	// Scale X
323 		aBits.writeFB( getFixed( rMatrix.get(1, 1) ), nScaleBits ); // Scale Y
324 	}
325 
326 	const sal_uInt8 bHasRotate = rMatrix.get(0, 1) != 0.0 || rMatrix.get(1, 0) != 0.0;
327 
328 	aBits.writeUB( bHasRotate, 1 );
329 
330 	if( bHasRotate )
331 	{
332 		sal_uInt8 nRotateBits = 31;
333 
334 		aBits.writeUB( nRotateBits, 5 );
335 		aBits.writeFB( getFixed( rMatrix.get(0, 1) ), nRotateBits );	// RotateSkew0
336 		aBits.writeFB( getFixed( rMatrix.get(1, 0) ), nRotateBits );	// RotateSkew1
337 	}
338 
339 	sal_uInt8 nTranslateBits = 16;
340 
341 	aBits.writeUB( nTranslateBits, 5 );
342 	aBits.writeSB( (sal_Int16)rMatrix.get(0, 2), nTranslateBits );		// Translate X
343 	aBits.writeSB( (sal_Int16)rMatrix.get(1, 2), nTranslateBits );		// Translate Y
344 
345 	aBits.writeTo( rOut );
346 }
347 
348 // -----------------------------------------------------------------------------
349 
addString(const char * pString)350 void Tag::addString( const char* pString )
351 {
352 	if( pString )
353 	{
354 		while( *pString )
355 			addUI8( *pString++ );
356 	}
357 
358 	addUI8( 0 );
359 }
360 
361 // -----------------------------------------------------------------------------
362 
addStream(SvStream & rIn)363 void Tag::addStream( SvStream& rIn )
364 {
365 	*this << rIn;
366 }
367 
368 ////////////////////////////////////////////////////////////////////////////////
369 
Sprite(sal_uInt16 nId)370 Sprite::Sprite( sal_uInt16 nId )
371 : mnId( nId ), mnFrames(0)
372 {
373 }
374 
375 // -----------------------------------------------------------------------------
376 
~Sprite()377 Sprite::~Sprite()
378 {
379 	for(vector< Tag* >::iterator i = maTags.begin(); i != maTags.end(); i++)
380 		delete *i;
381 }
382 
383 // -----------------------------------------------------------------------------
384 
write(SvStream & out)385 void Sprite::write( SvStream& out )
386 {
387 	SvMemoryStream aTmp;
388 	for(vector< Tag* >::iterator i = maTags.begin(); i != maTags.end(); i++)
389 		(*i)->write( aTmp );
390 
391     if( !mnFrames )
392         mnFrames = 1;
393 
394 	aTmp.Seek(0);
395 
396 	Tag aTag( TAG_DEFINESPRITE );
397 	aTag.addUI16( mnId );
398 	aTag.addUI16( _uInt16( mnFrames ) );
399 	aTag.addStream( aTmp );
400 	aTag.write( out );
401 }
402 
403 // -----------------------------------------------------------------------------
404 
addTag(Tag * pNewTag)405 void Sprite::addTag( Tag* pNewTag )
406 {
407 	if( pNewTag )
408 	{
409 		if( pNewTag->getTagId() == TAG_SHOWFRAME )
410 			mnFrames++;
411 
412 		maTags.push_back( pNewTag );
413 	}
414 }
415 
416 /////////////////////////////////////////////////////////////////////////////////
417 
getFixed(double fValue)418 sal_uInt32 swf::getFixed( double fValue )
419 {
420 	sal_Int16 nUpper = (sal_Int16)floor(fValue);
421     sal_uInt16 nLower = (sal_uInt16)((fValue - floor(fValue))*0x10000);
422 
423 	sal_uInt32 temp = ((sal_Int32)nUpper)<<16;
424 	temp |= nLower;
425 
426 	return temp;
427 }
428 
429 /////////////////////////////////////////////////////////////////////////////////
430 
431 /** constructs a new flash font for the given VCL Font */
FlashFont(const Font & rFont,sal_uInt16 nId)432 FlashFont::FlashFont( const Font& rFont, sal_uInt16 nId )
433 : maFont( rFont ), mnNextIndex(0), mnId( nId )
434 {
435 }
436 
437 // -----------------------------------------------------------------------------
438 
~FlashFont()439 FlashFont::~FlashFont()
440 {
441 }
442 
443 // -----------------------------------------------------------------------------
444 
445 /** gets the glyph id for the given character. The glyphs are created on demand */
getGlyph(sal_uInt16 nChar,VirtualDevice * pVDev)446 sal_uInt16 FlashFont::getGlyph( sal_uInt16 nChar, VirtualDevice* pVDev )
447 {
448 	// see if we already created a glyph for this character
449 	std::map<sal_uInt16, sal_uInt16, ltuint16>::iterator aIter( maGlyphIndex.find(nChar) );
450 	if( aIter != maGlyphIndex.end() )
451 	{
452 		return aIter->second;
453 	}
454 
455 	// if not, we create one now
456 
457 	maGlyphIndex[nChar] = mnNextIndex;
458 
459 	Font aOldFont( pVDev->GetFont() );
460 	Font aNewFont( aOldFont );
461 	aNewFont.SetAlign( ALIGN_BASELINE );
462 	pVDev->SetFont( aNewFont );
463 	aOldFont.SetOrientation(0);
464 
465 	// let the virtual device convert the character to polygons
466 	PolyPolygon aPolyPoly;
467 	pVDev->GetTextOutline( aPolyPoly, nChar );
468 
469 	maGlyphOffsets.push_back( _uInt16( maGlyphData.getOffset() ) );
470 
471 	// Number of fill and line index bits set to 1
472 	maGlyphData.writeUB( 0x11, 8 );
473 
474 	const sal_uInt16 nCount = aPolyPoly.Count();
475 	sal_uInt16 i,n;
476 	for( i = 0; i < nCount; i++ )
477 	{
478 		Polygon& rPoly = aPolyPoly[ i ];
479 
480 		const sal_uInt16 nSize = rPoly.GetSize();
481 		if( nSize )
482 		{
483 			// convert polygon to flash EM_SQUARE (1024x1024)
484 			for( n = 0; n < nSize; n++ )
485 			{
486 				Point aPoint( rPoly[n] );
487 				aPoint.X() = static_cast<long>((double(aPoint.X()) * 1024.0 ) / double(aOldFont.GetHeight()));
488 				aPoint.Y() = static_cast<long>((double(aPoint.Y()) * 1024.0 ) / double(aOldFont.GetHeight()));
489 				rPoly[n] = aPoint;
490 			}
491 			Writer::Impl_addPolygon( maGlyphData, rPoly, true );
492 		}
493 	}
494 	Writer::Impl_addEndShapeRecord( maGlyphData );
495 
496 	maGlyphData.pad();
497 
498 	pVDev->SetFont( aOldFont );
499 
500 	return mnNextIndex++;
501 }
502 
503 // -----------------------------------------------------------------------------
504 
write(SvStream & out)505 void FlashFont::write( SvStream& out )
506 {
507 	Tag aTag( TAG_DEFINEFONT );
508 
509 	aTag.addUI16( mnId );
510 
511 	sal_uInt16 nGlyphs = _uInt16( maGlyphOffsets.size() );
512 	sal_uInt16 nOffset = nGlyphs * sizeof( sal_uInt16 );
513 
514 	for(vector< sal_uInt16 >::iterator i = maGlyphOffsets.begin(); i != maGlyphOffsets.end(); i++)
515 		aTag.addUI16( nOffset + (*i) );
516 
517 	aTag.addBits( maGlyphData );
518 
519 	aTag.write( out );
520 }
521 
522 ////////////////////////////////////////////////////////////////////////////////
523 
524 /** this c'tor creates a solid fill style */
FillStyle(const Color & rSolidColor)525 FillStyle::FillStyle( const Color& rSolidColor )
526 :	meType( solid ),
527 	maColor( rSolidColor )
528 {
529 }
530 
531 // -----------------------------------------------------------------------------
532 
533 /** this c'tor creates a tiled or clipped bitmap fill style */
FillStyle(sal_uInt16 nBitmapId,bool bClipped,const::basegfx::B2DHomMatrix & rMatrix)534 FillStyle::FillStyle( sal_uInt16 nBitmapId, bool bClipped, const ::basegfx::B2DHomMatrix& rMatrix ) // #i73264#
535 :	meType( bClipped ? clipped_bitmap : tiled_bitmap ),
536 	maMatrix( rMatrix ),
537 	mnBitmapId( nBitmapId )
538 {
539 }
540 
541 // -----------------------------------------------------------------------------
542 
Impl_getFillStyleType(const Gradient & rGradient)543 FillStyle::FillStyleType Impl_getFillStyleType( const Gradient& rGradient )
544 {
545 	switch( rGradient.GetStyle() )
546 	{
547 	case GradientStyle_ELLIPTICAL:
548 	case GradientStyle_RADIAL:
549 		return FillStyle::radial_gradient;
550 //	case GradientStyle_AXIAL:
551 //	case GradientStyle_SQUARE:
552 //	case GradientStyle_RECT:
553 //	case GradientStyle_LINEAR:
554 	default:
555 		return FillStyle::linear_gradient;
556 	}
557 }
558 
559 // -----------------------------------------------------------------------------
560 
561 /** this c'tor creates a linear or radial gradient fill style */
FillStyle(const Rectangle & rBoundRect,const Gradient & rGradient)562 FillStyle::FillStyle( const Rectangle& rBoundRect, const Gradient& rGradient )
563 :	meType( Impl_getFillStyleType( rGradient ) ),
564 	maGradient( rGradient ),
565 	maBoundRect( rBoundRect )
566 {
567 }
568 
569 // -----------------------------------------------------------------------------
570 
addTo(Tag * pTag) const571 void FillStyle::addTo( Tag* pTag ) const
572 {
573 	pTag->addUI8( sal::static_int_cast<sal_uInt8>( meType ) );
574 	switch( meType )
575 	{
576 	case solid:
577 		pTag->addRGBA( maColor );
578 		break;
579 	case linear_gradient:
580 	case radial_gradient:
581 		Impl_addGradient( pTag );
582 		break;
583 	case tiled_bitmap:
584 	case clipped_bitmap:
585 		pTag->addUI16( mnBitmapId );
586 		pTag->addMatrix( maMatrix );
587 		break;
588 	}
589 }
590 
591 // -----------------------------------------------------------------------------
592 
593 struct GradRecord
594 {
595 	sal_uInt8	mnRatio;
596 	Color		maColor;
597 
GradRecordGradRecord598 	GradRecord( sal_uInt8 nRatio, const Color& rColor ) : mnRatio( nRatio ), maColor( rColor ) {}
599 };
600 
601 // TODO: better emulation of our gradients
Impl_addGradient(Tag * pTag) const602 void FillStyle::Impl_addGradient( Tag* pTag ) const
603 {
604 	vector< struct GradRecord > aGradientRecords;
605     basegfx::B2DHomMatrix m(basegfx::tools::createRotateB2DHomMatrix((maGradient.GetAngle() - 900) * F_PI1800));
606 
607 	switch( maGradient.GetStyle() )
608 	{
609 	case GradientStyle_ELLIPTICAL:
610 	case GradientStyle_RADIAL:
611 		{
612 			aGradientRecords.push_back( GradRecord( 0x00, maGradient.GetEndColor() ) );
613 			aGradientRecords.push_back( GradRecord( 0xff, maGradient.GetStartColor() ) );
614 
615 			double tx = ( maGradient.GetOfsX() * 32768.0 ) / 100.0;
616 			double ty = ( maGradient.GetOfsY() * 32768.0 ) / 100.0;
617 			double scalex = (double)maBoundRect.GetWidth() / 32768.0;
618 			double scaley = (double)maBoundRect.GetHeight() / 32768.0;
619 
620 			m.scale( 1.2, 1.2 );
621 
622 			if( scalex > scaley )
623 			{
624 				double scale_move = scaley / scalex;
625 
626 				m.translate( tx, scale_move * ty );
627 
628 
629 				m.scale( scalex, scalex );
630 			}
631 			else
632 			{
633 				double scale_move = scalex / scaley;
634 
635 				m.translate( scale_move * tx, ty );
636 
637 
638 				m.scale( scaley, scaley );
639 			}
640 
641 		}
642 		break;
643 	case GradientStyle_AXIAL:
644 		{
645 			aGradientRecords.push_back( GradRecord( 0x00, maGradient.GetEndColor() ) );
646 			aGradientRecords.push_back( GradRecord( 0x80, maGradient.GetStartColor() ) );
647 			aGradientRecords.push_back( GradRecord( 0xff, maGradient.GetEndColor() ) );
648 			double tx = ( 32768.0 / 2.0 );
649 			double ty = ( 32768.0 / 2.0 );
650 			double scalex = (double)maBoundRect.GetWidth() / 32768.0;
651 			double scaley = (double)maBoundRect.GetHeight() / 32768.0;
652 
653 			m.translate( tx, ty );
654 			m.scale( scalex, scaley );
655 		}
656 		break;
657 	case GradientStyle_SQUARE:
658 	case GradientStyle_RECT:
659 	case GradientStyle_LINEAR:
660 		{
661 			aGradientRecords.push_back( GradRecord( 0x00, maGradient.GetStartColor() ) );
662 			aGradientRecords.push_back( GradRecord( 0xff, maGradient.GetEndColor() ) );
663 			double scalex = (double)maBoundRect.GetWidth() / 32768.0;
664 			double scaley = (double)maBoundRect.GetHeight() / 32768.0;
665 
666 			m.scale( scalex, scaley );
667 
668 			m.translate( maBoundRect.GetWidth() / 2.0, maBoundRect.GetHeight() / 2.0 );
669 		}
670 		break;
671 	case  GradientStyle_FORCE_EQUAL_SIZE: break;
672 	}
673 
674 	m.translate( maBoundRect.nLeft, maBoundRect.nTop );
675 
676 	pTag->addMatrix( m );
677 
678 	DBG_ASSERT( aGradientRecords.size() < 8, "Illegal FlashGradient!" );
679 
680 	pTag->addUI8( static_cast<sal_uInt8>( aGradientRecords.size() ) );
681 
682 	for(std::vector< GradRecord >::iterator i = aGradientRecords.begin(); i != aGradientRecords.end(); i++)
683 	{
684 		pTag->addUI8( (*i).mnRatio );
685 		pTag->addRGBA( (*i).maColor );
686 	}
687 }
688 
689