xref: /trunk/main/filter/source/flash/swfwriter1.cxx (revision 06fb39a1)
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 
27 #include <com/sun/star/i18n/ScriptType.hpp>
28 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
29 #include <comphelper/processfactory.hxx>
30 #include "swfwriter.hxx"
31 #include <vcl/metaact.hxx>
32 #include <vcl/gdimtf.hxx>
33 #include <vcl/bmpacc.hxx>
34 #include <vcl/virdev.hxx>
35 #include <vcl/metric.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
37 #include <svtools/filter.hxx>
38 #include <vcl/graphictools.hxx>
39 
40 #ifndef _ZLIB_H
41 #ifdef SYSTEM_ZLIB
42 #include <zlib.h>
43 #else
44 #include <external/zlib/zlib.h>
45 #endif
46 #endif
47 
48 #include <vcl/salbtype.hxx>
49 #include <basegfx/polygon/b2dpolygon.hxx>
50 #include <basegfx/polygon/b2dpolypolygon.hxx>
51 
52 using namespace ::swf;
53 using namespace ::std;
54 using namespace ::rtl;
55 using namespace ::com::sun::star::i18n;
56 using namespace ::com::sun::star::uno;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::io;
59 using namespace ::com::sun::star::beans;
60 
61 extern sal_uInt16 getMaxBitsUnsigned( sal_uInt32 nValue );
62 extern sal_uInt16 getMaxBitsSigned( sal_Int32 nValue );
63 
64 static MapMode aTWIPSMode( MAP_TWIP );
65 static MapMode a100thmmMode( MAP_100TH_MM );
66 
67 // -----------------------------------------------------------------------------
68 
map(const Point & rPoint) const69 Point Writer::map( const Point& rPoint ) const
70 {
71 	const MapMode& aSourceMapMode = mpVDev->GetMapMode();
72 
73 	Point retPoint = mpVDev->LogicToLogic( rPoint,  &aSourceMapMode, &aTWIPSMode );
74 
75 	// AS: Produces a 'possible loss of data' warning that we can't fix without
76 	//  hurting code readability.
77 	retPoint.X() = (long)( retPoint.X() * mnDocXScale );
78 	retPoint.Y() = (long)( retPoint.Y() * mnDocYScale );
79 
80 	return retPoint;
81 }
82 
83 // -----------------------------------------------------------------------------
84 
map(const Size & rSize) const85 Size Writer::map( const Size& rSize ) const
86 {
87 	const MapMode& aSourceMapMode = mpVDev->GetMapMode();
88 
89 	Size retSize = mpVDev->LogicToLogic( rSize,  &aSourceMapMode, &aTWIPSMode );
90 
91 	// AS: Produces a 'possible loss of data' warning that we can't fix without
92 	//  hurting code readability.
93 	retSize.Width() = (long)( retSize.Width() * mnDocXScale );
94 	retSize.Height() = (long)( retSize.Height() * mnDocYScale );
95 
96 	return retSize;
97 }
98 
99 // -----------------------------------------------------------------------------
100 
map(PolyPolygon & rPolyPolygon) const101 void Writer::map( PolyPolygon& rPolyPolygon ) const
102 {
103 	const sal_uInt16 nPolyCount = rPolyPolygon.Count();
104 	if( nPolyCount )
105 	{
106 		sal_uInt16 nPoly, nPoint, nPointCount;
107 		for( nPoly = 0; nPoly < nPolyCount; nPoly++ )
108 		{
109 			Polygon& rPoly = rPolyPolygon[nPoly];
110 			nPointCount = rPoly.GetSize();
111 
112 			for( nPoint = 0; nPoint < nPointCount; nPoint++ )
113 			{
114 				rPoly[nPoint] = map( rPoly[nPoint] );
115 			}
116 		}
117 	}
118 }
119 
120 // -----------------------------------------------------------------------------
121 
mapRelative(sal_Int32 n100thMM) const122 sal_Int32 Writer::mapRelative( sal_Int32 n100thMM ) const
123 {
124 	MapMode aSourceMapMode( mpVDev->GetMapMode() );
125 	aSourceMapMode.SetOrigin( Point() );
126 
127 	sal_Int32 nTwips = mpVDev->LogicToLogic( Point( n100thMM, n100thMM ),  &aSourceMapMode, &aTWIPSMode ).X();
128 	return nTwips;
129 }
130 
131 // -----------------------------------------------------------------------------
132 
133 /**
134 */
Impl_addPolygon(BitStream & rBits,const Polygon & rPoly,sal_Bool bFilled)135 void Writer::Impl_addPolygon( BitStream& rBits, const Polygon& rPoly, sal_Bool bFilled )
136 {
137 	Point aLastPoint( rPoly[0] );
138 
139 	Impl_addShapeRecordChange( rBits, _Int16(aLastPoint.X()),_Int16(aLastPoint.Y()), bFilled );
140 
141 	sal_uInt16 i = 0, nSize = rPoly.GetSize();
142 
143 	double d = 16.0f;
144 
145 	// points
146 	while( ( i + 1 ) < nSize )
147 	{
148 		if( ( i + 3 ) < nSize )
149         {
150             PolyFlags P1( rPoly.GetFlags( i ) );
151             PolyFlags P4( rPoly.GetFlags( i + 3 ) );
152 
153             if( ( POLY_NORMAL == P1 || POLY_SMOOTH == P1 || POLY_SYMMTR == P1 ) &&
154                 ( POLY_CONTROL == rPoly.GetFlags( i + 1 ) ) &&
155                 ( POLY_CONTROL == rPoly.GetFlags( i + 2 ) ) &&
156                 ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) )
157             {
158                 Impl_quadBezierApprox( rBits, aLastPoint, d*d,
159                                       rPoly.GetPoint( i ).X(),   rPoly.GetPoint( i ).Y(),
160                                       rPoly.GetPoint( i+1 ).X(), rPoly.GetPoint( i+1 ).Y(),
161                                       rPoly.GetPoint( i+2 ).X(), rPoly.GetPoint( i+2 ).Y(),
162                                       rPoly.GetPoint( i+3 ).X(), rPoly.GetPoint( i+3 ).Y() );
163                 i += 3;
164                 continue;
165             }
166         }
167 
168 		++i;
169 
170 		const Point aPolyPoint( rPoly[ i ] );
171 		if( aPolyPoint != aLastPoint )
172 		{
173 			Impl_addStraightEdgeRecord( rBits, _Int16(aPolyPoint.X() - aLastPoint.X()),_Int16(aPolyPoint.Y() - aLastPoint.Y()));
174 			aLastPoint = aPolyPoint;
175 		}
176 	}
177 
178 	if( bFilled && (rPoly[0] != rPoly[nSize-1]))
179 	{
180 		const Point aPolyPoint( rPoly[ 0 ] );
181 		if( aPolyPoint != aLastPoint )
182 		{
183 			Impl_addStraightEdgeRecord( rBits, _Int16(aPolyPoint.X() - aLastPoint.X()),_Int16(aPolyPoint.Y() - aLastPoint.Y()));
184 		}
185 	}
186 }
187 
188 // -----------------------------------------------------------------------------
189 
190 /** exports a style change record with a move to (x,y) and depending on bFilled a line style 1 or fill style 1
191 */
Impl_addShapeRecordChange(BitStream & rBits,sal_Int16 dx,sal_Int16 dy,sal_Bool bFilled)192 void Writer::Impl_addShapeRecordChange( BitStream& rBits, sal_Int16 dx, sal_Int16 dy, sal_Bool bFilled )
193 {
194 	rBits.writeUB( 0, 1 );			// TypeFlag
195 	rBits.writeUB( 0, 1 );			// StateNewStyles
196 	rBits.writeUB( !bFilled, 1 );	// StateLineStyle
197 	rBits.writeUB( 0, 1 );			// StateFillStyle0
198 	rBits.writeUB( bFilled, 1 );		// StateFillStyle1
199 	rBits.writeUB( 1, 1 );			// StateMoveTo
200 
201 	sal_uInt16 nMoveBits = max( getMaxBitsSigned( dx ), getMaxBitsSigned( dy ) );
202 
203 	rBits.writeUB( nMoveBits, 5 );	// Number of bits per value
204 									// TODO: Optimize horizontal and vertical lines
205 	rBits.writeSB( dx, nMoveBits );	// DeltaX
206 	rBits.writeSB( dy, nMoveBits );	// DeltaY
207 
208 	rBits.writeUB( 1, 1 );			// set FillStyle1 or LineStyle to 1
209 }
210 
211 // -----------------------------------------------------------------------------
212 
213 /** exports a straight edge record
214 */
Impl_addStraightEdgeRecord(BitStream & rBits,sal_Int16 dx,sal_Int16 dy)215 void Writer::Impl_addStraightEdgeRecord( BitStream& rBits, sal_Int16 dx, sal_Int16 dy )
216 {
217 	rBits.writeUB( 1, 1 );			// TypeFlag
218 	rBits.writeUB( 1, 1 );			// StraightFlag
219 
220 	sal_uInt16 nBits = max( getMaxBitsSigned( dx ), getMaxBitsSigned( dy ) );
221 
222 	rBits.writeUB( nBits - 2, 4 );	// Number of bits per value
223 
224 	if( (dx != 0) && (dy != 0) )
225 	{
226 		rBits.writeUB( 1, 1 );			// GeneralLineFlag
227 		rBits.writeSB( dx, nBits );		// DeltaX
228 		rBits.writeSB( dy, nBits );		// DeltaY
229 	}
230 	else
231 	{
232 		rBits.writeUB( 0, 1 );
233 		rBits.writeUB( ( dx == 0 ), 1 );
234 		if( dx == 0 )
235 		{
236 			rBits.writeSB( dy, nBits );		// DeltaY
237 		}
238 		else
239 		{
240 			rBits.writeSB( dx, nBits );		// DeltaX
241 		}
242 	}
243 }
244 
245 // -----------------------------------------------------------------------------
246 
247 /** exports a curved edge record
248 */
Impl_addCurvedEdgeRecord(BitStream & rBits,sal_Int16 control_dx,sal_Int16 control_dy,sal_Int16 anchor_dx,sal_Int16 anchor_dy)249 void Writer::Impl_addCurvedEdgeRecord( BitStream& rBits, sal_Int16 control_dx, sal_Int16 control_dy, sal_Int16 anchor_dx, sal_Int16 anchor_dy )
250 {
251 	rBits.writeUB( 1, 1 );			// TypeFlag
252 	rBits.writeUB( 0, 1 );			// CurvedFlag
253 
254 	sal_uInt8 nBits = static_cast<sal_uInt8>(
255 		max( getMaxBitsSigned( control_dx ),
256 			max( getMaxBitsSigned( control_dy ),
257 				max( getMaxBitsSigned( anchor_dx ),
258 					max( getMaxBitsSigned( anchor_dy ), (sal_uInt16)3 ) ) ) ) );
259 
260 	rBits.writeUB( nBits - 2, 4 );	// Number of bits per value
261 
262 	rBits.writeSB( control_dx, nBits );		// DeltaX
263 	rBits.writeSB( control_dy, nBits );		// DeltaY
264 	rBits.writeSB( anchor_dx, nBits );		// DeltaX
265 	rBits.writeSB( anchor_dy, nBits );		// DeltaY
266 }
267 
268 // -----------------------------------------------------------------------------
269 
270 /** exports a end shape record
271 */
Impl_addEndShapeRecord(BitStream & rBits)272 void Writer::Impl_addEndShapeRecord( BitStream& rBits )
273 {
274 	rBits.writeUB( 0, 6 );
275 }
276 
277 // -----------------------------------------------------------------------------
278 
Impl_writePolygon(const Polygon & rPoly,sal_Bool bFilled)279 void Writer::Impl_writePolygon( const Polygon& rPoly, sal_Bool bFilled )
280 {
281 	PolyPolygon aPolyPoly( rPoly );
282 	Impl_writePolyPolygon( aPolyPoly, bFilled );
283 }
284 
285 // -----------------------------------------------------------------------------
286 
Impl_writePolygon(const Polygon & rPoly,sal_Bool bFilled,const Color & rFillColor,const Color & rLineColor)287 void Writer::Impl_writePolygon( const Polygon& rPoly, sal_Bool bFilled, const Color& rFillColor, const Color& rLineColor )
288 {
289 	PolyPolygon aPolyPoly( rPoly );
290 	Impl_writePolyPolygon( aPolyPoly, bFilled, rFillColor, rLineColor );
291 }
292 
293 // -----------------------------------------------------------------------------
294 
Impl_writePolyPolygon(const PolyPolygon & rPolyPoly,sal_Bool bFilled,sal_uInt8 nTransparence)295 void Writer::Impl_writePolyPolygon( const PolyPolygon& rPolyPoly, sal_Bool bFilled, sal_uInt8 nTransparence /* = 0 */ )
296 {
297 	Color aLineColor( mpVDev->GetLineColor() );
298 	if( 0 == aLineColor.GetTransparency() )
299 		aLineColor.SetTransparency( nTransparence );
300 	Color aFillColor( mpVDev->GetFillColor() );
301 	if( 0 == aFillColor.GetTransparency() )
302 		aFillColor.SetTransparency( nTransparence );
303 	Impl_writePolyPolygon(rPolyPoly, bFilled, aFillColor, aLineColor );
304 }
305 
306 // -----------------------------------------------------------------------------
307 
Impl_writePolyPolygon(const PolyPolygon & rPolyPoly,sal_Bool bFilled,const Color & rFillColor,const Color & rLineColor)308 void Writer::Impl_writePolyPolygon( const PolyPolygon& rPolyPoly, sal_Bool bFilled, const Color& rFillColor, const Color& rLineColor )
309 {
310 	PolyPolygon aPolyPoly( rPolyPoly );
311 
312 	if( aPolyPoly.Count() )
313 	{
314 		map( aPolyPoly );
315 
316 		if( mpClipPolyPolygon )
317 			rPolyPoly.GetIntersection( *mpClipPolyPolygon, aPolyPoly );
318 
319 		sal_uInt16 nID;
320 		if( bFilled )
321 		{
322 			Color aFillColor( rFillColor );
323 			if( 0 != mnGlobalTransparency )
324 				aFillColor.SetTransparency( mnGlobalTransparency );
325 
326 			FillStyle aStyle( aFillColor );
327 			nID = defineShape( aPolyPoly, aStyle );
328 		}
329 		else
330 		{
331 			Color aLineColor( rLineColor );
332 			if( 0 != mnGlobalTransparency )
333 				aLineColor.SetTransparency( mnGlobalTransparency );
334 
335 			nID = defineShape( aPolyPoly, 1, aLineColor );
336 		}
337 		maShapeIds.push_back( nID );
338 	}
339 }
340 
341 // -----------------------------------------------------------------------------
342 
343 /** a gradient is a transition from one color to another, rendered inside a given polypolygon */
Impl_writeGradientEx(const PolyPolygon & rPolyPoly,const Gradient & rGradient)344 void Writer::Impl_writeGradientEx( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
345 {
346 	if( rPolyPoly.Count() )
347 	{
348 		PolyPolygon aPolyPolygon( rPolyPoly );
349 		map( aPolyPolygon );
350 
351 		if( (rGradient.GetStyle() == GRADIENT_LINEAR && rGradient.GetAngle() == 900) || (rGradient.GetStyle() == GRADIENT_RADIAL)  )
352 		{
353 			const Rectangle aBoundRect( aPolyPolygon.GetBoundRect() );
354 
355 			FillStyle aFillStyle( aBoundRect, rGradient );
356 
357 			sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
358 			maShapeIds.push_back( nShapeId );
359 		}
360 		else
361 		{
362 			setClipping( &aPolyPolygon );
363 
364 			// render the gradient filling to simple polygons
365 			{
366 				GDIMetaFile	aTmpMtf;
367 				mpVDev->AddGradientActions( aPolyPolygon.GetBoundRect(), rGradient, aTmpMtf );
368 				Impl_writeActions( aTmpMtf );
369 			}
370 
371 			setClipping( NULL );
372 		}
373 	}
374 }
375 
376 // -----------------------------------------------------------------------------
377 
setClipping(const PolyPolygon * pClipPolyPolygon)378 void Writer::setClipping( const PolyPolygon* pClipPolyPolygon )
379 {
380 	mpClipPolyPolygon = pClipPolyPolygon;
381 }
382 
383 // -----------------------------------------------------------------------------
384 
385 // AS: Just comparing fonts straight up is too literal.  There are some
386 //  differences in font that actually require different glyphs to be defined,
387 //  and some that don't.  This function is meant to capture all the differences
388 //  that we care about.
compare_fonts_for_me(const Font & rFont1,const Font & rFont2)389 bool compare_fonts_for_me(const Font& rFont1, const Font& rFont2)
390 {
391 	return rFont1.GetName() == rFont2.GetName() &&
392 			rFont1.GetWeight() == rFont2.GetWeight() &&
393 			rFont1.GetItalic() == rFont2.GetItalic() &&
394 			rFont1.IsOutline() == rFont2.IsOutline() &&
395 			rFont1.IsShadow() == rFont2.IsShadow() &&
396 			rFont1.GetRelief() == rFont2.GetRelief();
397 }
398 
399 // -----------------------------------------------------------------------------
400 
Impl_getFont(const Font & rFont)401 FlashFont& Writer::Impl_getFont( const Font& rFont )
402 {
403 	FontMap::iterator aIter( maFonts.begin() );
404 	const FontMap::iterator aEnd( maFonts.end() );
405 
406 	while( aIter != aEnd )
407 	{
408 		const Font tempFont = (*aIter)->getFont();
409 		if( compare_fonts_for_me(tempFont, rFont) )
410 		{
411 			return **aIter;
412 		}
413 
414 		aIter++;
415 	}
416 
417 	FlashFont* pFont = new FlashFont( rFont, createID() );
418 	maFonts.push_back( pFont );
419 	return *pFont;
420 }
421 
422 // -----------------------------------------------------------------------------
423 
Impl_writeText(const Point & rPos,const String & rText,const sal_Int32 * pDXArray,long nWidth)424 void Writer::Impl_writeText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth )
425 {
426 	const FontMetric aMetric( mpVDev->GetFontMetric() );
427 
428 	bool bTextSpecial = aMetric.IsShadow() || aMetric.IsOutline() || (aMetric.GetRelief() != RELIEF_NONE);
429 
430 	if( !bTextSpecial )
431 	{
432 		Impl_writeText( rPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
433 	}
434 	else
435 	{
436 		if( aMetric.GetRelief() != RELIEF_NONE )
437 		{
438 	        Color aReliefColor( COL_LIGHTGRAY );
439 			Color aTextColor( mpVDev->GetTextColor() );
440 
441 			if ( aTextColor.GetColor() == COL_BLACK )
442 				aTextColor = Color( COL_WHITE );
443 
444 	        if ( aTextColor.GetColor() == COL_WHITE )
445 	            aReliefColor = Color( COL_BLACK );
446 
447 
448 			Point aPos( rPos );
449 			Point aOffset( 6,6 );
450 
451 			if ( aMetric.GetRelief() == RELIEF_ENGRAVED )
452 			{
453 				aPos -= aOffset;
454 			}
455 			else
456 			{
457 				aPos += aOffset;
458 			}
459 
460 			Impl_writeText( aPos, rText, pDXArray, nWidth, aReliefColor );
461 			Impl_writeText( rPos, rText, pDXArray, nWidth, aTextColor );
462 		}
463 		else
464 		{
465 			if( aMetric.IsShadow() )
466 			{
467 				long nOff = 1 + ((aMetric.GetLineHeight()-24)/24);
468 				if ( aMetric.IsOutline() )
469 					nOff += 6;
470 
471 				Color aTextColor( mpVDev->GetTextColor() );
472 				Color aShadowColor = Color( COL_BLACK );
473 
474 				if ( (aTextColor.GetColor() == COL_BLACK) || (aTextColor.GetLuminance() < 8) )
475 					aShadowColor = Color( COL_LIGHTGRAY );
476 
477 				Point aPos( rPos );
478 				aPos += Point( nOff, nOff );
479 				Impl_writeText( aPos, rText, pDXArray, nWidth, aShadowColor );
480 
481 				if( !aMetric.IsOutline() )
482 				{
483 					Impl_writeText( rPos, rText, pDXArray, nWidth, aTextColor );
484 				}
485 			}
486 
487 			if( aMetric.IsOutline() )
488 			{
489 				Point aPos = rPos + Point( -6, -6 );
490 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
491 				aPos = rPos + Point(+6,+6);
492 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
493 				aPos = rPos + Point(-6,+0);
494 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
495 				aPos = rPos + Point(-6,+6);
496 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
497 				aPos = rPos + Point(+0,+6);
498 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
499 				aPos = rPos + Point(+0,-6);
500 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
501 				aPos = rPos + Point(+6,-1);
502 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
503 				aPos = rPos + Point(+6,+0);
504 				Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
505 
506 				Impl_writeText( rPos, rText, pDXArray, nWidth, Color( COL_WHITE ) );
507 			}
508 		}
509 	}
510 }
511 
Impl_writeText(const Point & rPos,const String & rText,const sal_Int32 * pDXArray,long nWidth,Color aTextColor)512 void Writer::Impl_writeText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth, Color aTextColor )
513 {
514 	sal_uInt32 nLen = rText.Len();
515 
516 	if( !nLen )
517 		return;
518 
519 	const bool bRTL = (mpVDev->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL) != 0;
520 
521 	sal_Int16 nScriptType = ScriptType::LATIN;
522 	Reference < XBreakIterator > xBI( Impl_GetBreakIterator() );
523 	if( xBI.is() )
524 	{
525 		const OUString oText( rText );
526 		nScriptType = xBI->getScriptType( oText, 0 );
527 	}
528 
529 	// if the text is either right to left or complex or asian, we
530 	// ask the output device for a polygon representation.
531 	// On complex and asian text, each unicode character can have
532 	// different glyph representation, based on context. Also positioning
533 	// is not trivial so we let the output device do it for us.
534 	if( bRTL || (nScriptType != ScriptType::LATIN) )
535 	{
536 		// todo: optimize me as this will generate a huge amount of duplicate polygons
537 		PolyPolygon aPolyPoygon;
538 		mpVDev->GetTextOutline( aPolyPoygon, rText, 0, 0, (sal_uInt16)nLen, sal_True, nWidth, pDXArray );
539 		aPolyPoygon.Translate( rPos );
540 		Impl_writePolyPolygon( aPolyPoygon, sal_True, aTextColor, aTextColor );
541 	}
542 	else
543 	{
544 		Size	aNormSize;
545 		sal_Int32* pOwnArray;
546 		sal_Int32* pDX;
547 
548 		// get text sizes
549 		if( pDXArray )
550 		{
551 			pOwnArray = NULL;
552 			aNormSize = Size( mpVDev->GetTextWidth( rText ), 0 );
553 			pDX = (sal_Int32*) pDXArray;
554 		}
555 		else
556 		{
557 			pOwnArray = new sal_Int32[ nLen ];
558 			aNormSize = Size( mpVDev->GetTextArray( rText, pOwnArray ), 0 );
559 			pDX = pOwnArray;
560 		}
561 
562 		if( nLen > 1 )
563 		{
564 			aNormSize.Width() = pDX[ nLen - 2 ] + mpVDev->GetTextWidth( rText.GetChar(  (sal_uInt16) nLen - 1 ) );
565 
566 			if( nWidth && aNormSize.Width() && ( nWidth != aNormSize.Width() ) )
567 			{
568 				const double fFactor = (double) nWidth / aNormSize.Width();
569 
570 				sal_uInt32 i;
571 				for( i = 0; i < ( nLen - 1 ); i++ )
572 					pDX[ i ] = FRound( pDX[ i ] * fFactor );
573 			}
574 		}
575 
576 		Font aOldFont( mpVDev->GetFont() );
577 		Point				aBaseLinePos( rPos );
578 
579 		Font aFont(aOldFont);
580 		short nOrientation = aFont.GetOrientation();
581 		aFont.SetOrientation( 0 );
582 		aFont.SetUnderline(UNDERLINE_NONE);
583 		aFont.SetStrikeout(STRIKEOUT_NONE);
584 		mpVDev->SetFont( aFont );
585 
586 		const FontMetric	aMetric( mpVDev->GetFontMetric() );
587 
588 		FlashFont&			rFlashFont = Impl_getFont( aFont );
589 
590 		// always adjust text position to match baseline alignment
591 		switch( aOldFont.GetAlign() )
592 		{
593 			case( ALIGN_TOP ):
594 				aBaseLinePos.Y() += aMetric.GetAscent();
595 			break;
596 
597 			case( ALIGN_BOTTOM ):
598 				aBaseLinePos.Y() -= aMetric.GetDescent();
599 			break;
600 
601 			default:
602 			break;
603 		}
604 
605 		// get mapped text position
606 		const Point aPt( map( aBaseLinePos ) );
607 
608 		// write text element
609 
610 /* test code to create a bound rect, not really working for rotated text
611 			Size		aTextSize( map( Size( mpVDev->GetTextWidth( rText ), mpVDev->GetTextHeight() ) ) );
612 			Point		aMetricPoint( map( Point( aMetric.GetLeading(), aMetric.GetAscent() )  ) );
613 
614 			Point		aTmpPoint( map( Point( - aMetric.GetLeading(), - aMetric.GetAscent() )  ) ); ;
615 			Rectangle	aTmpRectangle(aTmpPoint, aTextSize );
616 			Polygon 	aPoly( aTmpRectangle );
617 
618 			aPoly.Rotate( aTmpPoint, (sal_uInt16) nOrientation );
619 
620 			Rectangle   aTextBoundRect( aPoly.GetBoundRect() );
621 
622 			aPoly.Move( aPt.X(), aPt.Y() - map( Size( 0, aMetric.GetDescent() ) ).Height() );
623 
624 */
625 
626 #if	0 // makes the calculated bound rect visible for debuging
627 {
628 		Polygon aTmpPoly( aPoly );
629 		sal_uInt16 nID = FlashGeometryExporter::writePolygonShape( aMovieStream, aTmpPoly, false, Color(COL_MAGENTA), Color(COL_MAGENTA), mpClipPolyPolygon  );
630 		ImplPlaceObject( nID );
631 }
632 #endif
633 
634 		// CL: This is still a hack until we figure out how to calculate a correct bound rect
635 		//	   for rotatet text
636 		Rectangle textBounds( 0, 0, static_cast<long>(mnDocWidth*mnDocXScale), static_cast<long>(mnDocHeight*mnDocYScale) );
637 		double scale = 1.0;
638 
639 		// scale width if we have a stretched text
640 		if( 0 != aFont.GetSize().Width() )
641 		{
642 			Font aTmpFont( aFont );
643 			aTmpFont.SetWidth(0);
644 			mpVDev->SetFont( aTmpFont );
645 
646 			const FontMetric aMetric2( mpVDev->GetFontMetric() );
647 			mpVDev->SetFont( aFont );
648 
649 			const long n1 = aFont.GetSize().Width();
650 			const long n2 = aMetric2.GetSize().Width();
651 			scale =  (double)n1 / (double)n2;
652 		}
653 
654         basegfx::B2DHomMatrix m(basegfx::tools::createRotateB2DHomMatrix(static_cast<double>(nOrientation) * F_PI1800));
655 		m.translate( double(aPt.X() / scale), double(aPt.Y()) );
656 		m.scale( scale, scale );
657 
658 		sal_Int16 nHeight = _Int16( map( Size( 0, aFont.GetHeight() ) ).Height() );
659 
660 		startTag( TAG_DEFINETEXT );
661 
662 		sal_uInt16 nTextId = createID();
663 
664 		mpTag->addUI16( nTextId );
665 		mpTag->addRect( textBounds );
666 		mpTag->addMatrix( m );
667 
668 		sal_uInt8 nGlyphBits = 16;
669 		sal_uInt8 nAdvanceBits = 16;
670 
671 		mpTag->addUI8( nGlyphBits );
672 		mpTag->addUI8( nAdvanceBits );
673 
674 		// text style change record
675 		mpTag->addUI8( 0x8c );
676 		mpTag->addUI16( rFlashFont.getID() );
677 		mpTag->addRGB( aTextColor );
678 		mpTag->addUI16( _uInt16( nHeight ) );
679 
680 		DBG_ASSERT( nLen <= 127, "TODO: handle text with more than 127 characters" );
681 
682 		// Glyph record
683 		mpTag->addUI8( (sal_uInt8) nLen );
684 
685 		BitStream aBits;
686 
687 		sal_Int32 nLastDX = 0;
688 		sal_Int32 nAdvance;
689 		sal_uInt32 i;
690 		for( i = 0; i < nLen; i++  )
691 		{
692 			if( i < (nLen-1) )
693 			{
694 				nAdvance = pDX[i] - nLastDX;
695 				nLastDX = pDX[i];
696 			}
697 			else
698 			{
699 				nAdvance = 0;
700 			}
701 
702 			aBits.writeUB( rFlashFont.getGlyph(rText.GetChar(_uInt16(i)),mpVDev), nGlyphBits );
703 			aBits.writeSB( _Int16(map( Size( (long)( nAdvance / scale ), 0 ) ).Width() ), nAdvanceBits );
704 		}
705 
706 		mpTag->addBits( aBits );
707 		mpTag->addUI8( 0 );
708 
709 		endTag();
710 
711 		maShapeIds.push_back( nTextId );
712 
713 		// AS: Write strikeout and underline, if necessary.  This code was originally taken from the SVG
714 		//  export facility, although the positioning had to be tweaked a little.  I can't explain the
715 		//  numbers, but the flash lines up very well with the original OOo document.  All of this should
716 		//  probably be converted to polygons as part of the meta file, though, as we don't handle any
717 		//  fancy lines (like dashes).
718 		if( ( aOldFont.GetStrikeout() != STRIKEOUT_NONE ) || ( aOldFont.GetUnderline() != UNDERLINE_NONE ) )
719 		{
720 			Polygon		aPoly( 4 );
721 			const long	nLineHeight = Max( (long) FRound( aMetric.GetLineHeight() * 0.05 ), (long) 1 );
722 
723 			if( aOldFont.GetStrikeout() != STRIKEOUT_NONE )
724 			{
725 				aPoly[ 0 ].X() = aBaseLinePos.X();
726 				aPoly[ 0 ].Y() = aBaseLinePos.Y() - FRound( aMetric.GetAscent() * 0.26 ) - nLineHeight;
727 				aPoly[ 1 ].X() = aPoly[ 0 ].X() + aNormSize.Width() - 1;
728 				aPoly[ 1 ].Y() = aPoly[ 0 ].Y();
729 				aPoly[ 2 ].X() = aPoly[ 1 ].X();
730 				aPoly[ 2 ].Y() = aPoly[ 1 ].Y() + nLineHeight - 1;
731 				aPoly[ 3 ].X() = aPoly[ 0 ].X();
732 				aPoly[ 3 ].Y() = aPoly[ 2 ].Y();
733 
734 				Impl_writePolygon( aPoly, sal_True, aTextColor, aTextColor );
735 			}
736 
737 			// AS: The factor of 1.5 on the nLineHeight is a magic number.  I'm not sure why it works,
738 			//  but it looks good to me.
739 			if( aOldFont.GetUnderline() != UNDERLINE_NONE )
740 			{
741 				aPoly[ 0 ].X() = aBaseLinePos.X();
742 				aPoly[ 0 ].Y() = static_cast<long>(aBaseLinePos.Y() + 1.5*nLineHeight);
743 				aPoly[ 1 ].X() = aPoly[ 0 ].X() + aNormSize.Width() - 1;
744 				aPoly[ 1 ].Y() = aPoly[ 0 ].Y();
745 				aPoly[ 2 ].X() = aPoly[ 1 ].X();
746 				aPoly[ 2 ].Y() = aPoly[ 1 ].Y() + nLineHeight - 1;
747 				aPoly[ 3 ].X() = aPoly[ 0 ].X();
748 				aPoly[ 3 ].Y() = aPoly[ 2 ].Y();
749 
750 				Impl_writePolygon( aPoly, sal_True, aTextColor, aTextColor );
751 			}
752 		}
753 
754 		mpVDev->SetFont( aOldFont );
755 		delete[] pOwnArray;
756 	}
757 }
758 
759 // -----------------------------------------------------------------------------
760 // AS: Because JPEGs require the alpha channel provided separately (JPEG does not
761 //  natively support alpha channel, but SWF lets you provide it separately), we
762 //  extract the alpha channel into a separate array here.
getBitmapData(const BitmapEx & aBmpEx,sal_uInt8 * & tgadata,sal_uInt8 * & tgaAlphadata,sal_uInt32 & nWidth,sal_uInt32 & nHeight)763 void getBitmapData( const BitmapEx& aBmpEx, sal_uInt8*& tgadata, sal_uInt8*& tgaAlphadata, sal_uInt32& nWidth, sal_uInt32& nHeight )
764 {
765     if( !aBmpEx.IsEmpty() )
766     {
767         Bitmap              aBmp( aBmpEx.GetBitmap() );
768         BitmapReadAccess*   pRAcc = aBmp.AcquireReadAccess();
769 
770         if( pRAcc )
771         {
772             AlphaMask   aAlpha;
773             nWidth = pRAcc->Width();
774 			nHeight = pRAcc->Height();
775 			tgadata = new sal_uInt8[nWidth*nHeight*4];
776 			tgaAlphadata = new sal_uInt8[nWidth*nHeight];
777 			sal_uInt8* p = tgadata, *pAlpha = tgaAlphadata;
778 
779 
780             if( aBmpEx.IsAlpha() )
781                 aAlpha = aBmpEx.GetAlpha();
782             else if( aBmpEx.IsTransparent() )
783                 aAlpha = aBmpEx.GetMask();
784             else
785             {
786                 sal_uInt8 cAlphaVal = 0;
787                 aAlpha = AlphaMask( aBmp.GetSizePixel(), &cAlphaVal );
788             }
789 
790             BitmapReadAccess* pAAcc = aAlpha.AcquireReadAccess();
791 
792             if( pAAcc )
793             {
794                 for( sal_uInt32 nY = 0; nY < nHeight; nY++ )
795                 {
796                     for( sal_uInt32 nX = 0; nX < nWidth; nX++ )
797                     {
798                         const sal_uInt8     nAlpha = pAAcc->GetPixel( nY, nX ).GetIndex();
799                         const BitmapColor   aPixelColor( pRAcc->GetColor( nY, nX ) );
800 
801 						if( nAlpha == 0xff )
802 						{
803 							*p++ = 0;
804 							*p++ = 0;
805 							*p++ = 0;
806 							*p++ = 0;
807 						}
808 						else
809 						{
810 							*p++ = 0xff-nAlpha;
811 							*p++ = aPixelColor.GetRed();
812 							*p++ = aPixelColor.GetGreen();
813 							*p++ = aPixelColor.GetBlue();
814 						}
815 						*pAlpha++ = 0xff - nAlpha;
816                     }
817                 }
818 
819                 aAlpha.ReleaseAccess( pAAcc );
820             }
821 
822             aBmp.ReleaseAccess( pRAcc );
823         }
824     }
825 }
826 
827 // -----------------------------------------------------------------------------
defineBitmap(const BitmapEx & bmpSource,sal_Int32 nJPEGQualityLevel)828 sal_uInt16 Writer::defineBitmap( const BitmapEx &bmpSource, sal_Int32 nJPEGQualityLevel )
829 {
830 	sal_uLong bmpChecksum = bmpSource.GetChecksum();
831 
832 	ChecksumCache::iterator it = mBitmapCache.find(bmpChecksum);
833 
834 	// AS: We already exported this bitmap, so just return its ID.
835 	if (mBitmapCache.end() != it)
836 		return it->second;
837 
838 	sal_uInt16 nBitmapId = createID();
839 	mBitmapCache[bmpChecksum] = nBitmapId;
840 
841 	// AS: OK, we have a good image, so now we decide whether or not to JPEG it or
842 	//  or Lossless compress it.
843 
844 	//Figure out lossless size
845 	sal_uInt8 *pImageData, *pAlphaData;
846 	sal_uInt32 width, height;
847 
848 	getBitmapData( bmpSource, pImageData, pAlphaData, width, height );
849 	sal_uInt32 raw_size = width * height * 4;
850 	uLongf compressed_size = raw_size + (sal_uInt32)(raw_size/100) + 12;
851 	sal_uInt8 *pCompressed = new sal_uInt8[ compressed_size ];
852 
853 #ifdef DBG_UTIL
854 	if(compress2(pCompressed, &compressed_size, pImageData, raw_size, Z_BEST_COMPRESSION) != Z_OK)
855     {
856 		DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
857     }
858 #else
859 	compress2(pCompressed, &compressed_size, pImageData, raw_size, Z_BEST_COMPRESSION);
860 #endif
861 
862 	// AS: SWF files let you provide an Alpha mask for JPEG images, but we have
863 	//  to ZLIB compress the alpha channel separately.
864 	uLong alpha_compressed_size = 0;
865 	sal_uInt8 *pAlphaCompressed = NULL;
866 	if (bmpSource.IsAlpha() || bmpSource.IsTransparent())
867 	{
868 		alpha_compressed_size = uLongf(width * height + (sal_uInt32)(raw_size/100) + 12);
869 		pAlphaCompressed = new sal_uInt8[ compressed_size ];
870 
871 #ifdef DBG_UTIL
872 		if(compress2(pAlphaCompressed, &alpha_compressed_size, pAlphaData, width * height, Z_BEST_COMPRESSION) != Z_OK)
873         {
874 			DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
875         }
876 #else
877 		compress2(pAlphaCompressed, &alpha_compressed_size, pAlphaData, width * height, Z_BEST_COMPRESSION);
878 #endif
879 	}
880 
881 	//Figure out JPEG size
882 	const sal_uInt8* pJpgData = NULL;;
883 	sal_uInt32 nJpgDataLength = 0xffffffff;
884 
885 	Graphic aGraphic( bmpSource );
886 	SvMemoryStream aDstStm( 65535, 65535 );
887 
888 	GraphicFilter aFilter;
889 
890 	Sequence< PropertyValue > aFilterData(nJPEGQualityLevel != -1);
891 	if( nJPEGQualityLevel != -1 )
892 	{
893 		aFilterData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("Quality"));
894 		aFilterData[0].Value <<= nJPEGQualityLevel;
895 	}
896 
897 #if 0
898 	// Debug code to see what we export to swf
899 	{
900 		SvFileStream aDstStm( String( RTL_CONSTASCII_USTRINGPARAM("e:\\test.jpg") ), STREAM_READ | STREAM_WRITE | STREAM_TRUNC );
901 		aFilter.ExportGraphic( aGraphic, String(), aDstStm,
902 								aFilter.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME ) ) ), &aFilterData );
903 	}
904 #endif
905 
906 	if( aFilter.ExportGraphic( aGraphic, String(), aDstStm,
907 								aFilter.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME ) ) ), &aFilterData ) == ERRCODE_NONE )
908 	{
909 		pJpgData = reinterpret_cast<const sal_uInt8*>(aDstStm.GetData());
910 		nJpgDataLength = aDstStm.Seek( STREAM_SEEK_TO_END );
911 	}
912 
913 	// AS: Ok, now go ahead and use whichever is smaller.  If JPEG is smaller, then
914 	//  we have to export as TAG_DEFINEBITSJPEG3 in the case that there is alpha
915 	//  channel data.
916 	if ( pJpgData && ( nJpgDataLength + alpha_compressed_size < compressed_size) )
917 		Impl_writeJPEG(nBitmapId, pJpgData, nJpgDataLength, pAlphaCompressed, alpha_compressed_size );
918 	else
919 		Impl_writeBmp( nBitmapId, width, height, pCompressed, compressed_size );
920 
921 	delete[] pCompressed;
922 	delete[] pAlphaCompressed;
923 	delete[] pImageData;
924 	delete[] pAlphaData;
925 
926 	return nBitmapId;
927 }
928 
929 // -----------------------------------------------------------------------------
930 
Impl_writeImage(const BitmapEx & rBmpEx,const Point & rPt,const Size & rSz,const Point &,const Size &,const Rectangle & rClipRect,bool bNeedToMapClipRect)931 void Writer::Impl_writeImage( const BitmapEx& rBmpEx, const Point& rPt, const Size& rSz, const Point& /* rSrcPt */, const Size& /* rSrcSz */, const Rectangle& rClipRect, bool bNeedToMapClipRect )
932 {
933 	if( !!rBmpEx )
934 	{
935 		BitmapEx bmpSource( rBmpEx );
936 
937 		Rectangle originalPixelRect = Rectangle(Point(), bmpSource.GetSizePixel());
938 
939 		Point srcPt( map(rPt) );
940 		Size srcSize( map(rSz) );
941 		Rectangle destRect( srcPt, srcSize );
942 
943 		// AS: Christian, my scaling factors are different than yours, and work better for me.
944 		//  However, I can't explain why exactly.  I got some of this by trial and error.
945 		double XScale = static_cast<double>(originalPixelRect.GetWidth())/destRect.GetWidth();
946 		double YScale = static_cast<double>(originalPixelRect.GetHeight())/destRect.GetHeight();
947 
948 		// AS: If rClipRect has a value set, then we need to crop the bmp appropriately.
949 		//  If a map event already occurred in the metafile, then we do not need to map
950 		//  the clip rect as it's already been done.
951 		if (!rClipRect.IsEmpty())
952 		{
953 			// AS: Christian, I also don't understand why bNeedToMapClipRect is necessary, but it
954 			//  works like a charm.  Usually, the map event in the meta file does not cause the
955 			//  clipping rectangle to get mapped.  However, sometimes there are multiple layers
956 			//  of mapping which eventually do cause the clipping rect to be mapped.
957 			Size clipSize( bNeedToMapClipRect ? map(rClipRect.GetSize()) : rClipRect.GetSize() );
958 			Rectangle clipRect = Rectangle(Point(), clipSize);
959 			destRect.Intersection( clipRect );
960 
961 			Rectangle cropRect(destRect);
962 
963 			// AS: The bmp origion is always 0,0 so we have to adjust before we crop.
964 			cropRect.Move(-srcPt.X(), -srcPt.Y());
965 			// AS: Rectangle has no scale function (?!) so I do it manually...
966 			Rectangle cropPixelRect(static_cast<long>(cropRect.Left()*XScale),
967 									static_cast<long>(cropRect.Top()*YScale),
968 									static_cast<long>(cropRect.Right()*XScale),
969 									static_cast<long>(cropRect.Bottom()*YScale));
970 
971 			bmpSource.Crop(cropPixelRect);
972 		}
973 
974 		if( !!bmpSource )
975 		{
976 			// #105949# fix images that are under 16 pixels width or height by
977 			//			expanding them. Some swf players can't display such small
978 			//		    bitmaps
979 			const Size& rSizePixel = bmpSource.GetSizePixel();
980 			if( (rSizePixel.Width() < 16) || (rSizePixel.Height() < 16) )
981 			{
982 				const sal_uInt32 nDX = rSizePixel.Width() < 16 ? 16 - rSizePixel.Width() : 0;
983 				const sal_uInt32 nDY = rSizePixel.Height() < 16 ? 16 - rSizePixel.Height() : 0;
984 				bmpSource.Expand( nDX, nDY );
985 			}
986 
987 			sal_Int32 nJPEGQuality = mnJPEGCompressMode;
988 
989 			Size szDestPixel = mpVDev->LogicToPixel(srcSize, aTWIPSMode);
990 
991 			double pixXScale = static_cast<double>(szDestPixel.Width()) / originalPixelRect.GetWidth();
992 			double pixYScale = static_cast<double>(szDestPixel.Height()) / originalPixelRect.GetHeight();
993 
994 			// AS: If the image has been scaled down, then scale down the quality
995 			//   that we use for JPEG compression.
996 			if (pixXScale < 1.0 && pixYScale < 1.0)
997 			{
998 
999 				double qualityScale = (pixXScale + pixYScale)/2;
1000 
1001 				nJPEGQuality = (sal_Int32)( nJPEGQuality * qualityScale );
1002 
1003 				if (nJPEGQuality < 10)
1004 					nJPEGQuality += 3;
1005 			}
1006 
1007 			sal_uInt16 nBitmapId = defineBitmap(bmpSource, nJPEGQuality);
1008 
1009 			Polygon aPoly( destRect );
1010 
1011 			// AS: Since images are being cropped now, no translation is normally necessary.
1012 			//  However, some things like graphical bullet points are still get translated.
1013 			::basegfx::B2DHomMatrix m; // #i73264#
1014 			m.scale(1.0/XScale, 1.0/YScale );
1015 			if (destRect.Left() || destRect.Top())
1016 				m.translate(destRect.Left(), destRect.Top());
1017 
1018 			FillStyle aFillStyle( nBitmapId, true, m );
1019 
1020 			sal_uInt16 nShapeId = defineShape( aPoly, aFillStyle );
1021 
1022 			maShapeIds.push_back( nShapeId );
1023 		}
1024 	}
1025 }
1026 // -----------------------------------------------------------------------------
1027 
Impl_writeBmp(sal_uInt16 nBitmapId,sal_uInt32 width,sal_uInt32 height,sal_uInt8 * pCompressed,sal_uInt32 compressed_size)1028 void Writer::Impl_writeBmp( sal_uInt16 nBitmapId, sal_uInt32 width, sal_uInt32 height, sal_uInt8 *pCompressed, sal_uInt32 compressed_size )
1029 {
1030 	startTag( TAG_DEFINEBITSLOSSLESS2 );
1031 
1032 	mpTag->addUI16( nBitmapId );
1033 	mpTag->addUI8( 5 );
1034 	mpTag->addUI16( _uInt16(width) );
1035 	mpTag->addUI16( _uInt16(height) );
1036 
1037 	mpTag->Write( pCompressed, compressed_size );
1038 
1039 	endTag();
1040 }
1041 
1042 // -----------------------------------------------------------------------------
1043 
Impl_writeJPEG(sal_uInt16 nBitmapId,const sal_uInt8 * pJpgData,sal_uInt32 nJpgDataLength,sal_uInt8 * pAlphaCompressed,sal_uInt32 alpha_compressed_size)1044 void Writer::Impl_writeJPEG(sal_uInt16 nBitmapId, const sal_uInt8* pJpgData, sal_uInt32 nJpgDataLength, sal_uInt8 *pAlphaCompressed, sal_uInt32 alpha_compressed_size )
1045 {
1046 	// AS: Go through the actuall JPEG bits, separating out the
1047 	//  header fields from the actual image fields.  Fields are
1048 	//  identifed by 0xFFXX where XX is the field type.  Both
1049 	//  the header and the image need start and stop (D8 and D9),
1050 	//  so that's why you see those written to both.  I don't
1051 	//  really know what the rest of these are, I got it to work
1052 	//  kind of by trial and error and by comparing with known
1053 	//  good SWF files.
1054 	sal_uInt8 cType = 0x01;
1055 	const sal_uInt8* pJpgSearch = pJpgData;
1056 
1057 	int nLength = 0;
1058 
1059 	SvMemoryStream EncodingTableStream;
1060 	SvMemoryStream ImageBitsStream;
1061 	for (;pJpgSearch < pJpgData + nJpgDataLength; pJpgSearch += nLength)
1062 	{
1063 
1064 #ifdef DBG_UTIL
1065 		if (0xFF != *pJpgSearch)
1066         {
1067 			DBG_ERROR( "Expected JPEG marker." ); ((void)0);
1068         }
1069 #endif
1070 
1071 		cType = *(pJpgSearch + 1);
1072 
1073 		if (0xD8 == cType || 0xD9 == cType)
1074 		{
1075 			nLength = 2;
1076 		}
1077 		else if (0xDA == cType)
1078 		{
1079 			//AS: This is the actual image data, and runs to the
1080 			// end of the file (as best I know), minus 2 bytes
1081 			// for the closing 0xFFD9.
1082 			nLength = nJpgDataLength - (pJpgSearch - pJpgData) - 2;
1083 		}
1084 		else
1085 		{
1086 			// AS: Lengths are big endian.
1087 
1088             // Beware. pJpgSearch is not necessarily word-aligned,
1089             // so we access it byte-wise.
1090 
1091 			// AS: Add 2 to the length to include the 0xFFXX itself.
1092 			nLength = 2 + (pJpgSearch[2]<<8) + pJpgSearch[3];
1093 		}
1094 
1095 		// AS: I'm referring to libjpeg for a list of what these
1096 		//  markers are.  See jdmarker.c for a list.
1097 		// AS: I'm ignoring application specific markers 0xE1...0xEF
1098 		//  and comments 0xFE.  I don't know what
1099 		//  0xF0 or 0xFD are for, and they don't come up.
1100 		//  Additionally, 0xDE and 0xDF aren't clear to me.
1101 		switch(cType)
1102 		{
1103 		case 0xD8:
1104 		case 0xD9:
1105 			EncodingTableStream.Write( pJpgSearch, nLength );
1106 			ImageBitsStream.Write( pJpgSearch, nLength );
1107 			break;
1108 
1109 		case 0x01:
1110 		case 0xDB:
1111 		case 0xDC:
1112 		case 0xDD:
1113 		case 0xC4:
1114 			EncodingTableStream.Write( pJpgSearch, nLength );
1115 			break;
1116 
1117 		case 0xC0:
1118 		case 0xC1:
1119 		case 0xC2:
1120 		case 0xC3:
1121 		case 0xC5:
1122 		case 0xC6:
1123 		case 0xC7:
1124 //		case 0xC8: Apparently reserved for JPEG extensions?
1125 		case 0xC9:
1126 		case 0xCA:
1127 		case 0xCB:
1128 		case 0xCD:
1129 		case 0xCE:
1130 		case 0xCF:
1131 		case 0xDA:
1132 		case 0xE0:
1133 			ImageBitsStream.Write( pJpgSearch, nLength );
1134 			break;
1135 
1136 		default:
1137 			DBG_ERROR( "JPEG marker I didn't handle!" );
1138 
1139 		}
1140 	}
1141 
1142 	EncodingTableStream.Seek( STREAM_SEEK_TO_END );
1143 	sal_uInt32 nEncodingTableSize = EncodingTableStream.Tell();
1144 	EncodingTableStream.Seek( STREAM_SEEK_TO_BEGIN );
1145 
1146 	ImageBitsStream.Seek( STREAM_SEEK_TO_END );
1147 	sal_uInt32 nImageBitsSize = ImageBitsStream.Tell();
1148 	ImageBitsStream.Seek( STREAM_SEEK_TO_BEGIN );
1149 
1150 	// AS: If we need alpha support, use TAG_DEFINEBITSJPEG3.
1151 	if (alpha_compressed_size > 0)
1152 	{
1153 		startTag( TAG_DEFINEBITSJPEG3 );
1154 
1155 		mpTag->addUI16( nBitmapId );
1156 
1157 		mpTag->addUI32( nEncodingTableSize + nImageBitsSize );
1158 
1159 		mpTag->Write(EncodingTableStream.GetData(), nEncodingTableSize);
1160 		mpTag->Write(ImageBitsStream.GetData(), nImageBitsSize);
1161 
1162 		mpTag->Write( pAlphaCompressed, alpha_compressed_size );
1163 
1164 		endTag();
1165 	}
1166 	else
1167 	{
1168 		startTag( TAG_DEFINEBITSJPEG2 );
1169 
1170 		mpTag->addUI16( nBitmapId );
1171 
1172 		mpTag->Write(EncodingTableStream.GetData(), nEncodingTableSize);
1173 		mpTag->Write(ImageBitsStream.GetData(), nImageBitsSize);
1174 
1175 		endTag();
1176 	}
1177 }
1178 
1179 // -----------------------------------------------------------------------------
1180 
Impl_writeLine(const Point & rPt1,const Point & rPt2,const Color * pLineColor)1181 void Writer::Impl_writeLine( const Point& rPt1, const Point& rPt2, const Color* pLineColor )
1182 {
1183 	Color aOldColor( mpVDev->GetLineColor() );
1184 	if( pLineColor )
1185 		mpVDev->SetLineColor( *pLineColor );
1186 
1187 	const Point aPtAry[2] = { rPt1, rPt2 };
1188 	Polygon aPoly( 2, aPtAry );
1189 	Impl_writePolyPolygon( aPoly, false );
1190 
1191 	mpVDev->SetLineColor( aOldColor );
1192 }
1193 
1194 // -----------------------------------------------------------------------------
1195 
Impl_writeRect(const Rectangle & rRect,long nRadX,long nRadY)1196 void Writer::Impl_writeRect( const Rectangle& rRect, long nRadX, long nRadY )
1197 {
1198 	if( (rRect.nTop == rRect.nBottom) || (rRect.nLeft == rRect.nRight) )
1199 	{
1200 		Color aColor( mpVDev->GetFillColor() );
1201 		Impl_writeLine( rRect.TopLeft(), rRect.BottomRight(), &aColor );
1202 	}
1203 	else
1204 	{
1205 		Polygon aPoly( rRect, nRadX, nRadY );
1206 		Impl_writePolyPolygon( aPoly, true );
1207 	}
1208 }
1209 
1210 // -----------------------------------------------------------------------------
1211 
Impl_writeEllipse(const Point & rCenter,long nRadX,long nRadY)1212 void Writer::Impl_writeEllipse( const Point& rCenter, long nRadX, long nRadY )
1213 {
1214 	Polygon aPoly( rCenter, nRadX, nRadY );
1215 	Impl_writePolyPolygon( aPoly, false );
1216 }
1217 
1218 
1219 /** writes the stroke defined by SvtGraphicStroke and returns true or it returns
1220 	false if it can't handle this stroke.
1221 */
Impl_writeStroke(SvtGraphicStroke & rStroke)1222 bool Writer::Impl_writeStroke( SvtGraphicStroke& rStroke )
1223 {
1224 	Polygon aPolygon;
1225 	rStroke.getPath( aPolygon );
1226 	PolyPolygon aPolyPolygon( aPolygon );
1227 
1228 	Rectangle aOldRect( aPolyPolygon.GetBoundRect() );
1229 
1230     map( aPolyPolygon );
1231 
1232 	Rectangle aNewRect( aPolyPolygon.GetBoundRect() );
1233 
1234     // as log as not LINESTYLE2 and DefineShape4 is used (which
1235     // added support for LineJoin), only round LineJoins are
1236     // supported. Fallback to META_POLYLINE_ACTION and META_LINE_ACTION
1237     if(SvtGraphicStroke::joinRound != rStroke.getJoinType())
1238         return false;
1239 
1240 	PolyPolygon aStartArrow;
1241 	rStroke.getStartArrow( aStartArrow );
1242 	if( 0 != aStartArrow.Count() )
1243 		return false;		// todo: Implement line ends
1244 
1245 	PolyPolygon aEndArrow;
1246 	rStroke.getEndArrow( aEndArrow );
1247 	if( 0 != aEndArrow.Count() )
1248 		return false;		// todo: Implement line ends
1249 
1250 	SvtGraphicStroke::DashArray aDashArray;
1251 	rStroke.getDashArray( aDashArray );
1252 	if( 0 != aDashArray.size() )
1253 		return false;		// todo: implement dashes
1254 
1255 	Color aColor( mpVDev->GetLineColor() );
1256 
1257 	if( 0.0 != rStroke.getTransparency() )
1258 		aColor.SetTransparency( sal::static_int_cast<sal_uInt8>( MinMax( (long int)( rStroke.getTransparency() * 0xff ), 0, 0xff ) ) );
1259 
1260 	sal_uInt16 nShapeId = defineShape( aPolyPolygon, sal::static_int_cast<sal_uInt16>( mapRelative( (sal_Int32)( rStroke.getStrokeWidth() ) ) ), aColor );
1261 	maShapeIds.push_back( nShapeId );
1262 	return true;
1263 }
1264 
1265 // -----------------------------------------------------------------------------
1266 
1267 /** writes the filling defined by SvtGraphicFill and returns true or it returns
1268 	false if it can't handle this filling.
1269 */
Impl_writeFilling(SvtGraphicFill & rFilling)1270 bool Writer::Impl_writeFilling( SvtGraphicFill& rFilling )
1271 {
1272 	PolyPolygon aPolyPolygon;
1273 	rFilling.getPath( aPolyPolygon );
1274 
1275 	Rectangle aOldRect( aPolyPolygon.GetBoundRect() );
1276 
1277     map( aPolyPolygon );
1278 
1279 	Rectangle aNewRect( aPolyPolygon.GetBoundRect() );
1280 
1281 	switch( rFilling.getFillType() )
1282 	{
1283 	case SvtGraphicFill::fillSolid:
1284 		{
1285 			Color aColor( rFilling.getFillColor() );
1286 
1287 			if( 0.0 != rFilling.getTransparency() )
1288 				aColor.SetTransparency( sal::static_int_cast<sal_uInt8>( MinMax( (long int)( rFilling.getTransparency() * 0xff ) , 0, 0xff ) ) );
1289 
1290 			FillStyle aFillStyle( aColor );
1291 
1292 			sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
1293 			maShapeIds.push_back( nShapeId );
1294 		}
1295 		break;
1296 	case SvtGraphicFill::fillGradient:
1297 		return false;
1298 	case SvtGraphicFill::fillHatch:
1299 		return false;
1300 	case SvtGraphicFill::fillTexture:
1301 		{
1302 			Graphic aGraphic;
1303 			rFilling.getGraphic( aGraphic );
1304 
1305 			// CL->AS: Should we also scale down the quality here depending on image scale?
1306 			sal_uInt16 nBitmapId = defineBitmap( aGraphic.GetBitmapEx(), mnJPEGCompressMode );
1307 
1308 			::basegfx::B2DHomMatrix aMatrix; // #i73264#
1309 
1310 			SvtGraphicFill::Transform aTransform;
1311 
1312 			rFilling.getTransform( aTransform );
1313 
1314 			sal_uInt16 a,b;
1315 			for( a = 0; a < 2; a++ )
1316 			{
1317 				for( b = 0; b < 3; b++ )
1318 				{
1319 					aMatrix.set(a, b, aTransform.matrix[a*3+b]);
1320 				}
1321 			}
1322             aMatrix.set(2, 0, 0.0);
1323 			aMatrix.set(2, 1, 0.0);
1324 			aMatrix.set(2, 2, 1.0);
1325 
1326             // scale bitmap
1327             Rectangle originalPixelRect = Rectangle(Point(), aGraphic.GetBitmapEx().GetSizePixel());
1328 
1329             double XScale = (double)aNewRect.GetWidth()/aOldRect.GetWidth();
1330             double YScale = (double)aNewRect.GetHeight()/aOldRect.GetHeight();
1331 
1332  			aMatrix.scale( XScale, YScale );
1333 
1334 			FillStyle aFillStyle( nBitmapId, !rFilling.IsTiling(), aMatrix );
1335 
1336 			sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
1337 			maShapeIds.push_back( nShapeId );
1338 		}
1339 		break;
1340 	}
1341 	return true;
1342 }
1343 
1344 // -----------------------------------------------------------------------------
1345 
1346 /* CL: The idea was to export page fields as text fields that get theire
1347    string from a variable set with actionscript by each page. This didn't
1348    work out since the formatting is always wrong when text follows the
1349    page number field since pages greater one may require more space than
1350    page 1
1351 */
1352 #if 0
1353 bool Writer::Impl_writePageField( Rectangle& rTextBounds )
1354 {
1355 	startTag( TAG_DEFINEEDITTEXT );
1356 
1357 	sal_uInt16 nTextId = createID();
1358 
1359 	mpTag->addUI16( nTextId );
1360 	mpTag->addRect( rTextBounds );
1361 
1362 	BitStream aBits;
1363 	aBits.writeUB( 1, 1 );					// HasText
1364 	aBits.writeUB( 0, 1 );					// WordWrap
1365 	aBits.writeUB( 0, 1 );					// MultiLine
1366 	aBits.writeUB( 0, 1 );					// Password
1367 	aBits.writeUB( 1, 1 );					// HasTextColor
1368 	aBits.writeUB( 0, 1 );					// HasMaxLength
1369 	aBits.writeUB( 0, 1 );					// HasFont
1370 	aBits.writeUB( 0, 1 );					// Reserved
1371 	aBits.writeUB( 0, 1 );					// AutoSize
1372 	aBits.writeUB( 0, 1 );					// HasLayout
1373 	aBits.writeUB( 1, 1 );					// NoSelect
1374 	aBits.writeUB( 1, 1 );					// Border
1375 	aBits.writeUB( 0, 1 );					// Reserved
1376 	aBits.writeUB( 0, 1 );					// HTML
1377 	aBits.writeUB( 0, 1 );					// UseOutlines
1378 	mpTag->addBits( aBits );
1379 
1380 	Color aColor( COL_BLACK );
1381 	mpTag->addRGB( aColor );
1382 	mpTag->addString( "PageNumber" );
1383 	mpTag->addString( "XXX" );
1384 
1385 	endTag();
1386 
1387 	maShapeIds.push_back( nTextId );
1388 
1389 	return true;
1390 }
1391 #endif
1392 
1393 // -----------------------------------------------------------------------------
1394 
Impl_handleLineInfoPolyPolygons(const LineInfo & rInfo,const basegfx::B2DPolygon & rLinePolygon)1395 void Writer::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon)
1396 {
1397 	if(rLinePolygon.count())
1398 	{
1399 		basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
1400 		basegfx::B2DPolyPolygon aFillPolyPolygon;
1401 
1402 		rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);
1403 
1404 		if(aLinePolyPolygon.count())
1405 		{
1406 			for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
1407 			{
1408 				const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
1409 				Impl_writePolygon(Polygon(aCandidate), sal_False );
1410 			}
1411 		}
1412 
1413 		if(aFillPolyPolygon.count())
1414 		{
1415 			const Color aOldLineColor(mpVDev->GetLineColor());
1416 			const Color aOldFillColor(mpVDev->GetFillColor());
1417 
1418 			mpVDev->SetLineColor();
1419 			mpVDev->SetFillColor(aOldLineColor);
1420 
1421 			for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
1422 			{
1423 				const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a));
1424     			Impl_writePolyPolygon(PolyPolygon(Polygon(aPolygon)), sal_True );
1425 			}
1426 
1427 			mpVDev->SetLineColor(aOldLineColor);
1428 			mpVDev->SetFillColor(aOldFillColor);
1429 		}
1430 	}
1431 }
1432 
1433 // -----------------------------------------------------------------------------
1434 
Impl_writeActions(const GDIMetaFile & rMtf)1435 void Writer::Impl_writeActions( const GDIMetaFile& rMtf )
1436 {
1437 	Rectangle clipRect;
1438 	int bMap = 0;
1439 	for( sal_uLong i = 0, nCount = rMtf.GetActionCount(); i < nCount; i++ )
1440 	{
1441 		const MetaAction*	pAction = rMtf.GetAction( i );
1442 		const sal_uInt16		nType = pAction->GetType();
1443 
1444 		switch( nType )
1445 		{
1446 			case( META_PIXEL_ACTION	):
1447 			{
1448 				const MetaPixelAction* pA = (const MetaPixelAction*) pAction;
1449 
1450 				Impl_writeLine( pA->GetPoint(), pA->GetPoint(), &pA->GetColor() );
1451 			}
1452 			break;
1453 
1454 			case( META_POINT_ACTION	):
1455 			{
1456 				const MetaPointAction* pA = (const MetaPointAction*) pAction;
1457 
1458 				Impl_writeLine( pA->GetPoint(), pA->GetPoint() );
1459 			}
1460 			break;
1461 
1462 			case( META_LINE_ACTION ):
1463 			{
1464 				const MetaLineAction* pA = (const MetaLineAction*) pAction;
1465 
1466                 if(pA->GetLineInfo().IsDefault())
1467                 {
1468     				Impl_writeLine( pA->GetStartPoint(), pA->GetEndPoint() );
1469                 }
1470                 else
1471                 {
1472                     // LineInfo used; handle Dash/Dot and fat lines
1473                     basegfx::B2DPolygon aPolygon;
1474                     aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
1475                     aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
1476                     Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
1477                 }
1478 			}
1479 			break;
1480 
1481 			case( META_RECT_ACTION ):
1482 			{
1483 				Impl_writeRect( ( (const MetaRectAction*) pAction )->GetRect(), 0, 0 );
1484 			}
1485 			break;
1486 
1487 			case( META_ROUNDRECT_ACTION	):
1488 			{
1489 				const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction;
1490 
1491 				Impl_writeRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
1492 			}
1493 			break;
1494 
1495 			case( META_ELLIPSE_ACTION ):
1496 			{
1497 				const MetaEllipseAction*	pA = (const MetaEllipseAction*) pAction;
1498 				const Rectangle&			rRect = pA->GetRect();
1499 
1500 				Impl_writeEllipse( rRect.Center(), rRect.GetWidth() >> 1, rRect.GetHeight() >> 1 );
1501 			}
1502 			break;
1503 
1504 			case( META_ARC_ACTION ):
1505 			case( META_PIE_ACTION ):
1506 			case( META_CHORD_ACTION	):
1507 			case( META_POLYGON_ACTION ):
1508 			{
1509 				Polygon aPoly;
1510 
1511 				switch( nType )
1512 				{
1513 					case( META_ARC_ACTION ):
1514 					{
1515 						const MetaArcAction* pA = (const MetaArcAction*) pAction;
1516 						aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC );
1517 					}
1518 					break;
1519 
1520 					case( META_PIE_ACTION ):
1521 					{
1522 						const MetaPieAction* pA = (const MetaPieAction*) pAction;
1523 						aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE );
1524 					}
1525 					break;
1526 
1527 					case( META_CHORD_ACTION	):
1528 					{
1529 						const MetaChordAction* pA = (const MetaChordAction*) pAction;
1530 						aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD );
1531 					}
1532 					break;
1533 
1534 					case( META_POLYGON_ACTION ):
1535 						aPoly = ( (const MetaPolygonAction*) pAction )->GetPolygon();
1536 					break;
1537 				}
1538 
1539 				if( aPoly.GetSize() )
1540 				{
1541 					Impl_writePolygon( aPoly, sal_True );
1542 				}
1543 			}
1544 			break;
1545 
1546 			case( META_POLYLINE_ACTION ):
1547 			{
1548 				const MetaPolyLineAction*	pA = (const MetaPolyLineAction*) pAction;
1549 				const Polygon&				rPoly = pA->GetPolygon();
1550 
1551 			    if( rPoly.GetSize() )
1552                 {
1553                     if(pA->GetLineInfo().IsDefault())
1554                     {
1555                         Impl_writePolygon( rPoly, sal_False );
1556                     }
1557                     else
1558                     {
1559                         // LineInfo used; handle Dash/Dot and fat lines
1560                         Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
1561                     }
1562                 }
1563 			}
1564 			break;
1565 
1566 			case( META_POLYPOLYGON_ACTION ):
1567 			{
1568 				const MetaPolyPolygonAction*	pA = (const MetaPolyPolygonAction*) pAction;
1569 				const PolyPolygon&				rPolyPoly = pA->GetPolyPolygon();
1570 
1571 				if( rPolyPoly.Count() )
1572 					Impl_writePolyPolygon( rPolyPoly, sal_True );
1573 			}
1574 			break;
1575 
1576 			case( META_GRADIENT_ACTION ):
1577 			{
1578 				const MetaGradientAction*	pA = (const MetaGradientAction*) pAction;
1579 
1580 				Polygon aPoly( pA->GetRect() );
1581 				Impl_writeGradientEx( aPoly, pA->GetGradient() );
1582 			}
1583 			break;
1584 
1585 			case( META_GRADIENTEX_ACTION ):
1586 			{
1587 				const MetaGradientExAction*	pA = (const MetaGradientExAction*) pAction;
1588 				Impl_writeGradientEx( pA->GetPolyPolygon(), pA->GetGradient() );
1589 			}
1590 			break;
1591 
1592 			case META_HATCH_ACTION:
1593 			{
1594 				const MetaHatchAction*	pA = (const MetaHatchAction*) pAction;
1595 				GDIMetaFile				aTmpMtf;
1596 
1597 				mpVDev->AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
1598 				Impl_writeActions( aTmpMtf );
1599 			}
1600 			break;
1601 
1602 			case( META_TRANSPARENT_ACTION ):
1603 			{
1604 				const MetaTransparentAction*	pA = (const MetaTransparentAction*) pAction;
1605 				const PolyPolygon&				rPolyPoly = pA->GetPolyPolygon();
1606 
1607 				if( rPolyPoly.Count() )
1608 				{
1609 					// convert transparence from percent into 0x00 - 0xff
1610 					sal_uInt8 nTransparence = (sal_uInt8) MinMax( FRound( pA->GetTransparence() * 2.55 ), 0, 255 );
1611 					Impl_writePolyPolygon( rPolyPoly, sal_True, nTransparence );
1612 				}
1613 			}
1614 			break;
1615 
1616 			case( META_FLOATTRANSPARENT_ACTION ):
1617 			{
1618 				const MetaFloatTransparentAction*	pA = (const MetaFloatTransparentAction*) pAction;
1619 				GDIMetaFile							aTmpMtf( pA->GetGDIMetaFile() );
1620 				Point								aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
1621 				const Size							aSrcSize( aTmpMtf.GetPrefSize() );
1622 				const Point							aDestPt( pA->GetPoint() );
1623 				const Size							aDestSize( pA->GetSize() );
1624 				const double						fScaleX = aSrcSize.Width() ? (double) aDestSize.Width() / aSrcSize.Width() : 1.0;
1625 				const double						fScaleY = aSrcSize.Height() ? (double) aDestSize.Height() / aSrcSize.Height() : 1.0;
1626 				long								nMoveX, nMoveY;
1627 
1628 				if( fScaleX != 1.0 || fScaleY != 1.0 )
1629 				{
1630 					aTmpMtf.Scale( fScaleX, fScaleY );
1631 					aSrcPt.X() = FRound( aSrcPt.X() * fScaleX );
1632 					aSrcPt.Y() = FRound( aSrcPt.Y() * fScaleY );
1633 				}
1634 
1635 				nMoveX = aDestPt.X() - aSrcPt.X(), nMoveY = aDestPt.Y() - aSrcPt.Y();
1636 
1637 				if( nMoveX || nMoveY )
1638 					aTmpMtf.Move( nMoveX, nMoveY );
1639 
1640 				const Gradient& rGradient = pA->GetGradient();
1641 				sal_uInt32 nLuminance = ((sal_Int32)rGradient.GetStartColor().GetLuminance() + (sal_Int32)rGradient.GetEndColor().GetLuminance() ) >> 1;
1642 
1643 				sal_uInt8 nOldGlobalTransparency = mnGlobalTransparency;
1644 				mnGlobalTransparency = (sal_uInt8)MinMax( nLuminance, 0, 0xff );
1645 
1646 				mpVDev->Push();
1647 				Impl_writeActions( aTmpMtf );
1648 				mpVDev->Pop();
1649 
1650 				mnGlobalTransparency = nOldGlobalTransparency;
1651 			}
1652 			break;
1653 
1654 			case( META_EPS_ACTION ):
1655 			{
1656 				const MetaEPSAction*	pA = (const MetaEPSAction*) pAction;
1657 				const GDIMetaFile		aGDIMetaFile( pA->GetSubstitute() );
1658 				sal_Bool				bFound = sal_False;
1659 
1660 				for( sal_uLong j = 0, nC = aGDIMetaFile.GetActionCount(); ( j < nC ) && !bFound; j++ )
1661 				{
1662 					const MetaAction* pSubstAct = aGDIMetaFile.GetAction( j );
1663 
1664 					if( pSubstAct->GetType() == META_BMPSCALE_ACTION )
1665 					{
1666 						bFound = sal_True;
1667 						const MetaBmpScaleAction* pBmpScaleAction = (const MetaBmpScaleAction*) pSubstAct;
1668 						Impl_writeImage( pBmpScaleAction->GetBitmap(),
1669 									  pA->GetPoint(), pA->GetSize(),
1670 									  Point(), pBmpScaleAction->GetBitmap().GetSizePixel(), clipRect, 1 == bMap  );
1671 					}
1672 				}
1673 			}
1674 			break;
1675 
1676 			case( META_COMMENT_ACTION ):
1677 			{
1678 				const MetaCommentAction*	pA = (const MetaCommentAction*) pAction;
1679                 const sal_uInt8* 				pData = pA->GetData();
1680 				String						aSkipComment;
1681 
1682 				if( pA->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
1683 				{
1684 					const MetaGradientExAction*	pGradAction = NULL;
1685 					sal_Bool					bDone = sal_False;
1686 
1687 					while( !bDone && ( ++i < nCount ) )
1688 					{
1689 						pAction = rMtf.GetAction( i );
1690 
1691 						if( pAction->GetType() == META_GRADIENTEX_ACTION )
1692 							pGradAction = (const MetaGradientExAction*) pAction;
1693 						else if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1694 								 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) )
1695 						{
1696 							bDone = sal_True;
1697 						}
1698 					}
1699 
1700 					if( pGradAction )
1701 						Impl_writeGradientEx( pGradAction->GetPolyPolygon(), pGradAction->GetGradient());
1702 				}
1703 				else if( pA->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_BEGIN" ) == COMPARE_EQUAL &&
1704                          pData )
1705 				{
1706 
1707 					// this comment encapsulates all high level information for a filling that caused
1708 					// the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1709 
1710 					SvtGraphicFill 	aFilling;
1711 					SvMemoryStream	aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
1712 
1713                     // read the fill info
1714                     aMemStm >> aFilling;
1715 
1716 					// if impl_writeFilling can handle this high level filling, it returns true and we
1717 					// skip all meta actions until "XPATHFILL_SEQ_END"
1718 					if( Impl_writeFilling( aFilling ) )
1719 					{
1720 						bool bDone = sal_False;
1721 
1722 						while( !bDone && ( ++i < nCount ) )
1723 						{
1724 							pAction = rMtf.GetAction( i );
1725 
1726 							if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1727 									 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_END" ) == COMPARE_EQUAL ) )
1728 							{
1729 								bDone = sal_True;
1730 							}
1731 						}
1732 					}
1733 				}
1734 				else if( pA->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_BEGIN" ) == COMPARE_EQUAL &&
1735                          pData )
1736 				{
1737 
1738 					// this comment encapsulates all high level information for a filling that caused
1739 					// the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1740 
1741 					SvtGraphicStroke aStroke;
1742 					SvMemoryStream	aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
1743 
1744                     // read the fill info
1745                     aMemStm >> aStroke;
1746 
1747 					// if impl_writeStroke can handle this high level stroke, it returns true and we
1748 					// skip all meta actions until "XPATHSTROKE_SEQ_END"
1749 					if( Impl_writeStroke( aStroke ) )
1750 					{
1751 						bool bDone = sal_False;
1752 
1753 						while( !bDone && ( ++i < nCount ) )
1754 						{
1755 							pAction = rMtf.GetAction( i );
1756 
1757 							if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1758 									 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_END" ) == COMPARE_EQUAL ) )
1759 							{
1760 								bDone = sal_True;
1761 							}
1762 						}
1763 					}
1764 				}
1765 #if 0
1766 				else if( pA->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_BEGIN;PageField" ) == COMPARE_EQUAL )
1767 				{
1768 					bool bDone = sal_False;
1769 
1770 					while( !bDone && ( ++i < nCount ) )
1771 					{
1772 						pAction = rMtf.GetAction( i );
1773 
1774 						if( pAction->GetType() == META_TEXTARRAY_ACTION )
1775 						{
1776 							const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction;
1777 							Rectangle aRect( pA->GetPoint(), Size( 100, 100 ) );
1778 							Impl_writePageField( aRect );
1779 						}
1780 
1781 						if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1782 								 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_END" ) == COMPARE_EQUAL ) )
1783 						{
1784 							bDone = sal_True;
1785 						}
1786 					}
1787 				}
1788 #endif
1789 			}
1790 			break;
1791 
1792 			case( META_BMPSCALE_ACTION ):
1793 			{
1794 				const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction;
1795 
1796 				Impl_writeImage( pA->GetBitmap(),
1797 						  pA->GetPoint(), pA->GetSize(),
1798 						  Point(), pA->GetBitmap().GetSizePixel(), clipRect, 1 == bMap );
1799 			}
1800 			break;
1801 
1802 			case( META_BMP_ACTION ):
1803 			{
1804 				const MetaBmpAction* pA = (const MetaBmpAction*) pAction;
1805 				Impl_writeImage( pA->GetBitmap(),
1806 						  pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmap().GetSizePixel()),
1807 						  Point(), pA->GetBitmap().GetSizePixel(), clipRect, 1 ==bMap );
1808 			}
1809 			break;
1810 
1811 			case( META_BMPSCALEPART_ACTION ):
1812 			{
1813 				const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*) pAction;
1814 				Impl_writeImage( pA->GetBitmap(),
1815 						  pA->GetDestPoint(), pA->GetDestSize(),
1816 						  pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap );
1817 			}
1818 			break;
1819 
1820 			case( META_BMPEX_ACTION	):
1821 			{
1822 				const MetaBmpExAction*	pA = (const MetaBmpExAction*) pAction;
1823 				Impl_writeImage( pA->GetBitmapEx(),
1824 						  pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmapEx().GetSizePixel() ),
1825 						  Point(), pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap );
1826 			}
1827 			break;
1828 
1829 			case( META_BMPEXSCALE_ACTION ):
1830 			{
1831 				const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction;
1832 				Impl_writeImage( pA->GetBitmapEx(),
1833 						  pA->GetPoint(), pA->GetSize(),
1834 						  Point(), pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap );
1835 			}
1836 			break;
1837 
1838 			case( META_BMPEXSCALEPART_ACTION ):
1839 			{
1840 				const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pAction;
1841 				Impl_writeImage( pA->GetBitmapEx(),
1842 						  pA->GetDestPoint(), pA->GetDestSize(),
1843 						  pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap );
1844 			}
1845 			break;
1846 
1847 			case( META_TEXT_ACTION ):
1848 			{
1849 				const MetaTextAction* pA = (const MetaTextAction*) pAction;
1850 				Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), NULL, 0);
1851 			}
1852 			break;
1853 
1854 			case( META_TEXTRECT_ACTION ):
1855 			{
1856 				const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction;
1857 				Impl_writeText( pA->GetRect().TopLeft(), pA->GetText(), NULL, 0  );
1858 			}
1859 			break;
1860 
1861 			case( META_TEXTARRAY_ACTION	):
1862 			{
1863 				const MetaTextArrayAction*	pA = (const MetaTextArrayAction*) pAction;
1864 				Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), pA->GetDXArray(), 0 );
1865 			}
1866 			break;
1867 
1868 			case( META_STRETCHTEXT_ACTION ):
1869 			{
1870 				const MetaStretchTextAction* pA = (const MetaStretchTextAction*) pAction;
1871 				Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), NULL, pA->GetWidth() );
1872 			}
1873 			break;
1874 
1875 			case( META_ISECTRECTCLIPREGION_ACTION ):
1876 			{
1877 				const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*) pAction;
1878 				clipRect = pA->GetRect();
1879 			}
1880 			case( META_CLIPREGION_ACTION ):
1881 			case( META_ISECTREGIONCLIPREGION_ACTION	):
1882 			case( META_MOVECLIPREGION_ACTION ):
1883 			{
1884 				( (MetaAction*) pAction )->Execute( mpVDev );
1885 //				mbClipAttrChanged = sal_True;
1886 			}
1887 			break;
1888 
1889 			case( META_MAPMODE_ACTION ):
1890 			{
1891 //				const MetaMapModeAction *pA = (const MetaMapModeAction*) pAction;
1892 //				MapMode mm = pA->GetMapMode();
1893 //				MapUnit mu = mm.GetMapUnit();
1894 //
1895 //				Point pt = mm.GetOrigin();
1896 //				Fraction fx = mm.GetScaleX();
1897 //				Fraction fy = mm.GetScaleY();
1898 
1899 				bMap++;
1900 			}
1901 			case( META_REFPOINT_ACTION ):
1902 			case( META_LINECOLOR_ACTION	):
1903 			case( META_FILLCOLOR_ACTION	):
1904 			case( META_TEXTLINECOLOR_ACTION ):
1905 			case( META_TEXTFILLCOLOR_ACTION	):
1906 			case( META_TEXTCOLOR_ACTION	):
1907 			case( META_TEXTALIGN_ACTION	):
1908 			case( META_FONT_ACTION ):
1909 			case( META_PUSH_ACTION ):
1910 			case( META_POP_ACTION ):
1911 			case( META_LAYOUTMODE_ACTION ):
1912 			{
1913 				( (MetaAction*) pAction )->Execute( mpVDev );
1914 			}
1915 			break;
1916 
1917 			case( META_RASTEROP_ACTION ):
1918 			case( META_MASK_ACTION ):
1919 			case( META_MASKSCALE_ACTION	):
1920 			case( META_MASKSCALEPART_ACTION	):
1921 			case( META_WALLPAPER_ACTION	):
1922 			case( META_TEXTLINE_ACTION ):
1923 			{
1924 				// !!! >>> we don't want to support these actions
1925 			}
1926 			break;
1927 
1928 			default:
1929 				//DBG_ERROR( "FlashActionWriter::ImplWriteActions: unsupported MetaAction #" );
1930 			break;
1931 		}
1932 	}
1933 }
1934 
1935 
1936 /////////////////////////////////////////////////////////////////////////
1937 
1938 
Impl_addStraightLine(BitStream & rBits,Point & rLastPoint,const double P2x,const double P2y)1939 void Writer::Impl_addStraightLine( BitStream& rBits, Point& rLastPoint,
1940                                    const double P2x, const double P2y )
1941 {
1942 	Point aPoint( FRound(P2x), FRound(P2y) );
1943 
1944 	Impl_addStraightEdgeRecord( rBits, _Int16(aPoint.X() - rLastPoint.X()),_Int16(aPoint.Y() - rLastPoint.Y()));
1945 	rLastPoint = aPoint;
1946 
1947 }
1948 
1949 // -----------------------------------------------------------------------------
1950 
Impl_addQuadBezier(BitStream & rBits,Point & rLastPoint,const double P2x,const double P2y,const double P3x,const double P3y)1951 void Writer::Impl_addQuadBezier( BitStream& rBits, Point& rLastPoint,
1952 								 const double P2x, const double P2y,
1953                                  const double P3x, const double P3y )
1954 {
1955 
1956 	Point aControlPoint( FRound(P2x), FRound(P2y) );
1957 	Point aAnchorPoint( FRound(P3x), FRound(P3y) );
1958 
1959 	Impl_addCurvedEdgeRecord( rBits,
1960 								_Int16(aControlPoint.X() - rLastPoint.X()),_Int16(aControlPoint.Y() - rLastPoint.Y()),
1961 								_Int16(aAnchorPoint.X() - aControlPoint.X()),_Int16(aAnchorPoint.Y() - aControlPoint.Y()) );
1962 	rLastPoint = aAnchorPoint;
1963 }
1964 
1965 // -----------------------------------------------------------------------------
1966 
1967 /* Approximate given cubic bezier curve by quadratic bezier segments */
Impl_quadBezierApprox(BitStream & rBits,Point & rLastPoint,const double d2,const double P1x,const double P1y,const double P2x,const double P2y,const double P3x,const double P3y,const double P4x,const double P4y)1968 void Writer::Impl_quadBezierApprox( BitStream& rBits,
1969 								   Point& rLastPoint,
1970                                    const double d2,
1971                                    const double P1x, const double P1y,
1972                                    const double P2x, const double P2y,
1973                                    const double P3x, const double P3y,
1974                                    const double P4x, const double P4y )
1975 {
1976     // Check for degenerate case, where the given cubic bezier curve
1977     // is already quadratic: P4 == 3P3 - 3P2 + P1
1978     if( P4x == 3.0*P3x - 3.0*P2x + P1x &&
1979         P4y == 3.0*P3y - 3.0*P2y + P1y )
1980     {
1981         Impl_addQuadBezier( rBits, rLastPoint,
1982                            3.0/2.0*P2x - 1.0/2.0*P1x, 3.0/2.0*P2y - 1.0/2.0*P1y,
1983                            P4x, P4y);
1984     }
1985     else
1986     {
1987         // Create quadratic segment for given cubic:
1988         // Start and end point must coincide, determine quadratic control
1989         // point in such a way that it lies on the intersection of the
1990         // tangents at start and end point, resp. Thus, both cubic and
1991         // quadratic curve segments will match in 0th and 1st derivative
1992         // at the start and end points
1993 
1994         // Intersection of P2P1 and P4P3
1995         //           (P2y-P4y)(P3x-P4x)-(P2x-P4x)(P3y-P4y)
1996         //  lambda = -------------------------------------
1997         //           (P1x-P2x)(P3y-P4y)-(P1y-P2y)(P3x-P4x)
1998         //
1999         // Intersection point IP is now
2000         // IP = P2 + lambda(P1-P2)
2001         //
2002         const double nominator( (P2y-P4y)*(P3x-P4x) - (P2x-P4x)*(P3y-P4y) );
2003         const double denominator( (P1x-P2x)*(P3y-P4y) - (P1y-P2y)*(P3x-P4x) );
2004         const double lambda( nominator / denominator );
2005 
2006         const double IPx( P2x + lambda*( P1x - P2x) );
2007         const double IPy( P2y + lambda*( P1y - P2y) );
2008 
2009         // Introduce some alias names: quadratic start point is P1, end
2010         // point is P4, control point is IP
2011         const double QP1x( P1x );
2012         const double QP1y( P1y );
2013         const double QP2x( IPx );
2014         const double QP2y( IPy );
2015         const double QP3x( P4x );
2016         const double QP3y( P4y );
2017 
2018         // Adapted bezier flatness test (lecture notes from R. Schaback,
2019         // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
2020         //
2021         // ||C(t) - Q(t)|| <= max     ||c_j - q_j||
2022         //                    0<=j<=n
2023         //
2024         // In this case, we don't need the distance from the cubic bezier
2025         // to a straight line, but to a quadratic bezier. The c_j's are
2026         // the cubic bezier's bernstein coefficients, the q_j's the
2027         // quadratic bezier's. We have the c_j's given, the q_j's can be
2028         // calculated from QPi like this (sorry, mixed index notation, we
2029         // use [1,n], formulas use [0,n-1]):
2030         //
2031         // q_0 = QP1 = P1
2032         // q_1 = 1/3 QP1 + 2/3 QP2
2033         // q_2 = 2/3 QP2 + 1/3 QP3
2034         // q_3 = QP3 = P4
2035         //
2036         // We can drop case 0 and 3, since there the curves coincide
2037         // (distance is zero)
2038 
2039         // calculate argument of max for j=1 and j=2
2040         const double fJ1x( P2x - 1.0/3.0*QP1x - 2.0/3.0*QP2x );
2041         const double fJ1y( P2y - 1.0/3.0*QP1y - 2.0/3.0*QP2y );
2042         const double fJ2x( P3x - 2.0/3.0*QP2x - 1.0/3.0*QP3x );
2043         const double fJ2y( P3y - 2.0/3.0*QP2y - 1.0/3.0*QP3y );
2044 
2045         // stop if distance from cubic curve is guaranteed to be bounded by d
2046         // Should denominator be 0: then P1P2 and P3P4 are parallel (P1P2^T R[90,P3P4] = 0.0),
2047         // meaning that either we have a straight line or an inflexion point (see else block below)
2048         if( 0.0 != denominator &&
2049             ::std::max( fJ1x*fJ1x + fJ1y*fJ1y,
2050                         fJ2x*fJ2x + fJ2y*fJ2y) < d2 )
2051         {
2052             // requested resolution reached.
2053             // Add end points to output file.
2054             // order is preserved, since this is so to say depth first traversal.
2055             Impl_addQuadBezier( rBits, rLastPoint,
2056                                 QP2x, QP2y,
2057                                 QP3x, QP3y);
2058         }
2059         else
2060         {
2061             // Maybe subdivide further
2062 
2063             // This is for robustness reasons, since the line intersection
2064             // method below gets instable if the curve gets closer to a
2065             // straight line. If the given cubic bezier does not deviate by
2066             // more than d/4 from a straight line, either:
2067             //  - take the line (that's what we do here)
2068             //  - express the line by a quadratic bezier
2069 
2070             // Perform bezier flatness test (lecture notes from R. Schaback,
2071             // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
2072             //
2073             // ||P(t) - L(t)|| <= max     ||b_j - b_0 - j/n(b_n - b_0)||
2074             //                    0<=j<=n
2075             //
2076             // What is calculated here is an upper bound to the distance from
2077             // a line through b_0 and b_3 (P1 and P4 in our notation) and the
2078             // curve. We can drop 0 and n from the running indices, since the
2079             // argument of max becomes zero for those cases.
2080             const double fJ1x2( P2x - P1x - 1.0/3.0*(P4x - P1x) );
2081             const double fJ1y2( P2y - P1y - 1.0/3.0*(P4y - P1y) );
2082             const double fJ2x2( P3x - P1x - 2.0/3.0*(P4x - P1x) );
2083             const double fJ2y2( P3y - P1y - 2.0/3.0*(P4y - P1y) );
2084 
2085             // stop if distance from line is guaranteed to be bounded by d/4
2086             if( ::std::max( fJ1x2*fJ1x2 + fJ1y2*fJ1y2,
2087                             fJ2x2*fJ2x2 + fJ2y2*fJ2y2) < d2/16.0 )
2088             {
2089                 // do not subdivide further, add straight line instead
2090                 Impl_addStraightLine( rBits, rLastPoint, P4x, P4y);
2091             }
2092             else
2093             {
2094                 // deCasteljau bezier arc, split at t=0.5
2095                 // Foley/vanDam, p. 508
2096                 const double L1x( P1x ), 		   	 L1y( P1y );
2097                 const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 );
2098                 const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 );
2099                 const double L3x( (L2x + Hx)*0.5 ),  L3y( (L2y + Hy)*0.5 );
2100                 const double R4x( P4x ), 		   	 R4y( P4y );
2101                 const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 );
2102                 const double R2x( (Hx + R3x)*0.5 ),  R2y( (Hy + R3y)*0.5 );
2103                 const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 );
2104                 const double L4x( R1x ), 		     L4y( R1y );
2105 
2106                 // subdivide further
2107                 Impl_quadBezierApprox(rBits, rLastPoint, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y);
2108                 Impl_quadBezierApprox(rBits, rLastPoint, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y);
2109             }
2110         }
2111     }
2112 }
2113 
Impl_GetBreakIterator()2114 Reference < XBreakIterator > Writer::Impl_GetBreakIterator()
2115 {
2116 	if ( !mxBreakIterator.is() )
2117 	{
2118 		Reference< XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
2119 		mxBreakIterator.set( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
2120 	}
2121 	return mxBreakIterator;
2122 }
2123