xref: /AOO41X/main/filter/source/flash/swfwriter1.cxx (revision ddde725d65c83fe3ba1186d46f6e3e08f12ba47e)
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 realy 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 neccessary.  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 seperately (JPEG does not
761 //  natively support alpha channel, but SWF lets you provide it seperately), we
762 //  extract the alpha channel into a seperate 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 seperately.
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, seperating 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 refering 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 formating 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