summaryrefslogtreecommitdiff
path: root/filter/source/flash/swfwriter1.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'filter/source/flash/swfwriter1.cxx')
-rw-r--r--filter/source/flash/swfwriter1.cxx2126
1 files changed, 2126 insertions, 0 deletions
diff --git a/filter/source/flash/swfwriter1.cxx b/filter/source/flash/swfwriter1.cxx
new file mode 100644
index 000000000000..cce0314436f8
--- /dev/null
+++ b/filter/source/flash/swfwriter1.cxx
@@ -0,0 +1,2126 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_filter.hxx"
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/processfactory.hxx>
+#include "swfwriter.hxx"
+#include <vcl/metaact.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/bmpacc.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/metric.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svtools/filter.hxx>
+#include <vcl/graphictools.hxx>
+
+#ifndef _ZLIB_H
+#ifdef SYSTEM_ZLIB
+#include <zlib.h>
+#else
+#include <external/zlib/zlib.h>
+#endif
+#endif
+
+#include <vcl/salbtype.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+using namespace ::swf;
+using namespace ::std;
+using namespace ::rtl;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::beans;
+
+extern sal_uInt16 getMaxBitsUnsigned( sal_uInt32 nValue );
+extern sal_uInt16 getMaxBitsSigned( sal_Int32 nValue );
+
+static MapMode aTWIPSMode( MAP_TWIP );
+static MapMode a100thmmMode( MAP_100TH_MM );
+
+// -----------------------------------------------------------------------------
+
+Point Writer::map( const Point& rPoint ) const
+{
+ const MapMode& aSourceMapMode = mpVDev->GetMapMode();
+
+ Point retPoint = mpVDev->LogicToLogic( rPoint, &aSourceMapMode, &aTWIPSMode );
+
+ // AS: Produces a 'possible loss of data' warning that we can't fix without
+ // hurting code readability.
+ retPoint.X() = (long)( retPoint.X() * mnDocXScale );
+ retPoint.Y() = (long)( retPoint.Y() * mnDocYScale );
+
+ return retPoint;
+}
+
+// -----------------------------------------------------------------------------
+
+Size Writer::map( const Size& rSize ) const
+{
+ const MapMode& aSourceMapMode = mpVDev->GetMapMode();
+
+ Size retSize = mpVDev->LogicToLogic( rSize, &aSourceMapMode, &aTWIPSMode );
+
+ // AS: Produces a 'possible loss of data' warning that we can't fix without
+ // hurting code readability.
+ retSize.Width() = (long)( retSize.Width() * mnDocXScale );
+ retSize.Height() = (long)( retSize.Height() * mnDocYScale );
+
+ return retSize;
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::map( PolyPolygon& rPolyPolygon ) const
+{
+ const sal_uInt16 nPolyCount = rPolyPolygon.Count();
+ if( nPolyCount )
+ {
+ sal_uInt16 nPoly, nPoint, nPointCount;
+ for( nPoly = 0; nPoly < nPolyCount; nPoly++ )
+ {
+ Polygon& rPoly = rPolyPolygon[nPoly];
+ nPointCount = rPoly.GetSize();
+
+ for( nPoint = 0; nPoint < nPointCount; nPoint++ )
+ {
+ rPoly[nPoint] = map( rPoly[nPoint] );
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+sal_Int32 Writer::mapRelative( sal_Int32 n100thMM ) const
+{
+ MapMode aSourceMapMode( mpVDev->GetMapMode() );
+ aSourceMapMode.SetOrigin( Point() );
+
+ sal_Int32 nTwips = mpVDev->LogicToLogic( Point( n100thMM, n100thMM ), &aSourceMapMode, &aTWIPSMode ).X();
+ return nTwips;
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+*/
+void Writer::Impl_addPolygon( BitStream& rBits, const Polygon& rPoly, sal_Bool bFilled )
+{
+ Point aLastPoint( rPoly[0] );
+
+ Impl_addShapeRecordChange( rBits, _Int16(aLastPoint.X()),_Int16(aLastPoint.Y()), bFilled );
+
+ USHORT i = 0, nSize = rPoly.GetSize();
+
+ double d = 16.0f;
+
+ // points
+ while( ( i + 1 ) < nSize )
+ {
+ if( ( i + 3 ) < nSize )
+ {
+ PolyFlags P1( rPoly.GetFlags( i ) );
+ PolyFlags P4( rPoly.GetFlags( i + 3 ) );
+
+ if( ( POLY_NORMAL == P1 || POLY_SMOOTH == P1 || POLY_SYMMTR == P1 ) &&
+ ( POLY_CONTROL == rPoly.GetFlags( i + 1 ) ) &&
+ ( POLY_CONTROL == rPoly.GetFlags( i + 2 ) ) &&
+ ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) )
+ {
+ Impl_quadBezierApprox( rBits, aLastPoint, d*d,
+ rPoly.GetPoint( i ).X(), rPoly.GetPoint( i ).Y(),
+ rPoly.GetPoint( i+1 ).X(), rPoly.GetPoint( i+1 ).Y(),
+ rPoly.GetPoint( i+2 ).X(), rPoly.GetPoint( i+2 ).Y(),
+ rPoly.GetPoint( i+3 ).X(), rPoly.GetPoint( i+3 ).Y() );
+ i += 3;
+ continue;
+ }
+ }
+
+ ++i;
+
+ const Point aPolyPoint( rPoly[ i ] );
+ if( aPolyPoint != aLastPoint )
+ {
+ Impl_addStraightEdgeRecord( rBits, _Int16(aPolyPoint.X() - aLastPoint.X()),_Int16(aPolyPoint.Y() - aLastPoint.Y()));
+ aLastPoint = aPolyPoint;
+ }
+ }
+
+ if( bFilled && (rPoly[0] != rPoly[nSize-1]))
+ {
+ const Point aPolyPoint( rPoly[ 0 ] );
+ if( aPolyPoint != aLastPoint )
+ {
+ Impl_addStraightEdgeRecord( rBits, _Int16(aPolyPoint.X() - aLastPoint.X()),_Int16(aPolyPoint.Y() - aLastPoint.Y()));
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+/** exports a style change record with a move to (x,y) and depending on bFilled a line style 1 or fill style 1
+*/
+void Writer::Impl_addShapeRecordChange( BitStream& rBits, sal_Int16 dx, sal_Int16 dy, sal_Bool bFilled )
+{
+ rBits.writeUB( 0, 1 ); // TypeFlag
+ rBits.writeUB( 0, 1 ); // StateNewStyles
+ rBits.writeUB( !bFilled, 1 ); // StateLineStyle
+ rBits.writeUB( 0, 1 ); // StateFillStyle0
+ rBits.writeUB( bFilled, 1 ); // StateFillStyle1
+ rBits.writeUB( 1, 1 ); // StateMoveTo
+
+ sal_uInt16 nMoveBits = max( getMaxBitsSigned( dx ), getMaxBitsSigned( dy ) );
+
+ rBits.writeUB( nMoveBits, 5 ); // Number of bits per value
+ // TODO: Optimize horizontal and vertical lines
+ rBits.writeSB( dx, nMoveBits ); // DeltaX
+ rBits.writeSB( dy, nMoveBits ); // DeltaY
+
+ rBits.writeUB( 1, 1 ); // set FillStyle1 or LineStyle to 1
+}
+
+// -----------------------------------------------------------------------------
+
+/** exports a straight edge record
+*/
+void Writer::Impl_addStraightEdgeRecord( BitStream& rBits, sal_Int16 dx, sal_Int16 dy )
+{
+ rBits.writeUB( 1, 1 ); // TypeFlag
+ rBits.writeUB( 1, 1 ); // StraightFlag
+
+ sal_uInt16 nBits = max( getMaxBitsSigned( dx ), getMaxBitsSigned( dy ) );
+
+ rBits.writeUB( nBits - 2, 4 ); // Number of bits per value
+
+ if( (dx != 0) && (dy != 0) )
+ {
+ rBits.writeUB( 1, 1 ); // GeneralLineFlag
+ rBits.writeSB( dx, nBits ); // DeltaX
+ rBits.writeSB( dy, nBits ); // DeltaY
+ }
+ else
+ {
+ rBits.writeUB( 0, 1 );
+ rBits.writeUB( ( dx == 0 ), 1 );
+ if( dx == 0 )
+ {
+ rBits.writeSB( dy, nBits ); // DeltaY
+ }
+ else
+ {
+ rBits.writeSB( dx, nBits ); // DeltaX
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+/** exports a curved edge record
+*/
+void Writer::Impl_addCurvedEdgeRecord( BitStream& rBits, sal_Int16 control_dx, sal_Int16 control_dy, sal_Int16 anchor_dx, sal_Int16 anchor_dy )
+{
+ rBits.writeUB( 1, 1 ); // TypeFlag
+ rBits.writeUB( 0, 1 ); // CurvedFlag
+
+ sal_uInt8 nBits = static_cast<sal_uInt8>(
+ max( getMaxBitsSigned( control_dx ),
+ max( getMaxBitsSigned( control_dy ),
+ max( getMaxBitsSigned( anchor_dx ),
+ max( getMaxBitsSigned( anchor_dy ), (sal_uInt16)3 ) ) ) ) );
+
+ rBits.writeUB( nBits - 2, 4 ); // Number of bits per value
+
+ rBits.writeSB( control_dx, nBits ); // DeltaX
+ rBits.writeSB( control_dy, nBits ); // DeltaY
+ rBits.writeSB( anchor_dx, nBits ); // DeltaX
+ rBits.writeSB( anchor_dy, nBits ); // DeltaY
+}
+
+// -----------------------------------------------------------------------------
+
+/** exports a end shape record
+*/
+void Writer::Impl_addEndShapeRecord( BitStream& rBits )
+{
+ rBits.writeUB( 0, 6 );
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writePolygon( const Polygon& rPoly, sal_Bool bFilled )
+{
+ PolyPolygon aPolyPoly( rPoly );
+ Impl_writePolyPolygon( aPolyPoly, bFilled );
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writePolygon( const Polygon& rPoly, sal_Bool bFilled, const Color& rFillColor, const Color& rLineColor )
+{
+ PolyPolygon aPolyPoly( rPoly );
+ Impl_writePolyPolygon( aPolyPoly, bFilled, rFillColor, rLineColor );
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writePolyPolygon( const PolyPolygon& rPolyPoly, sal_Bool bFilled, sal_uInt8 nTransparence /* = 0 */ )
+{
+ Color aLineColor( mpVDev->GetLineColor() );
+ if( 0 == aLineColor.GetTransparency() )
+ aLineColor.SetTransparency( nTransparence );
+ Color aFillColor( mpVDev->GetFillColor() );
+ if( 0 == aFillColor.GetTransparency() )
+ aFillColor.SetTransparency( nTransparence );
+ Impl_writePolyPolygon(rPolyPoly, bFilled, aFillColor, aLineColor );
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writePolyPolygon( const PolyPolygon& rPolyPoly, sal_Bool bFilled, const Color& rFillColor, const Color& rLineColor )
+{
+ PolyPolygon aPolyPoly( rPolyPoly );
+
+ if( aPolyPoly.Count() )
+ {
+ map( aPolyPoly );
+
+ if( mpClipPolyPolygon )
+ rPolyPoly.GetIntersection( *mpClipPolyPolygon, aPolyPoly );
+
+ sal_uInt16 nID;
+ if( bFilled )
+ {
+ Color aFillColor( rFillColor );
+ if( 0 != mnGlobalTransparency )
+ aFillColor.SetTransparency( mnGlobalTransparency );
+
+ FillStyle aStyle( aFillColor );
+ nID = defineShape( aPolyPoly, aStyle );
+ }
+ else
+ {
+ Color aLineColor( rLineColor );
+ if( 0 != mnGlobalTransparency )
+ aLineColor.SetTransparency( mnGlobalTransparency );
+
+ nID = defineShape( aPolyPoly, 1, aLineColor );
+ }
+ maShapeIds.push_back( nID );
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+/** a gradient is a transition from one color to another, rendered inside a given polypolygon */
+void Writer::Impl_writeGradientEx( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
+{
+ if( rPolyPoly.Count() )
+ {
+ PolyPolygon aPolyPolygon( rPolyPoly );
+ map( aPolyPolygon );
+
+ if( (rGradient.GetStyle() == GRADIENT_LINEAR && rGradient.GetAngle() == 900) || (rGradient.GetStyle() == GRADIENT_RADIAL) )
+ {
+ const Rectangle aBoundRect( aPolyPolygon.GetBoundRect() );
+
+ FillStyle aFillStyle( aBoundRect, rGradient );
+
+ sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
+ maShapeIds.push_back( nShapeId );
+ }
+ else
+ {
+ setClipping( &aPolyPolygon );
+
+ // render the gradient filling to simple polygons
+ {
+ GDIMetaFile aTmpMtf;
+ mpVDev->AddGradientActions( aPolyPolygon.GetBoundRect(), rGradient, aTmpMtf );
+ Impl_writeActions( aTmpMtf );
+ }
+
+ setClipping( NULL );
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::setClipping( const PolyPolygon* pClipPolyPolygon )
+{
+ mpClipPolyPolygon = pClipPolyPolygon;
+}
+
+// -----------------------------------------------------------------------------
+
+// AS: Just comparing fonts straight up is too literal. There are some
+// differences in font that actually require different glyphs to be defined,
+// and some that don't. This function is meant to capture all the differences
+// that we care about.
+bool compare_fonts_for_me(const Font& rFont1, const Font& rFont2)
+{
+ return rFont1.GetName() == rFont2.GetName() &&
+ rFont1.GetWeight() == rFont2.GetWeight() &&
+ rFont1.GetItalic() == rFont2.GetItalic() &&
+ rFont1.IsOutline() == rFont2.IsOutline() &&
+ rFont1.IsShadow() == rFont2.IsShadow() &&
+ rFont1.GetRelief() == rFont2.GetRelief();
+}
+
+// -----------------------------------------------------------------------------
+
+FlashFont& Writer::Impl_getFont( const Font& rFont )
+{
+ FontMap::iterator aIter( maFonts.begin() );
+ const FontMap::iterator aEnd( maFonts.end() );
+
+ while( aIter != aEnd )
+ {
+ const Font tempFont = (*aIter)->getFont();
+ if( compare_fonts_for_me(tempFont, rFont) )
+ {
+ return **aIter;
+ }
+
+ aIter++;
+ }
+
+ FlashFont* pFont = new FlashFont( rFont, createID() );
+ maFonts.push_back( pFont );
+ return *pFont;
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth )
+{
+ const FontMetric aMetric( mpVDev->GetFontMetric() );
+
+ bool bTextSpecial = aMetric.IsShadow() || aMetric.IsOutline() || (aMetric.GetRelief() != RELIEF_NONE);
+
+ if( !bTextSpecial )
+ {
+ Impl_writeText( rPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ }
+ else
+ {
+ if( aMetric.GetRelief() != RELIEF_NONE )
+ {
+ Color aReliefColor( COL_LIGHTGRAY );
+ Color aTextColor( mpVDev->GetTextColor() );
+
+ if ( aTextColor.GetColor() == COL_BLACK )
+ aTextColor = Color( COL_WHITE );
+
+ if ( aTextColor.GetColor() == COL_WHITE )
+ aReliefColor = Color( COL_BLACK );
+
+
+ Point aPos( rPos );
+ Point aOffset( 6,6 );
+
+ if ( aMetric.GetRelief() == RELIEF_ENGRAVED )
+ {
+ aPos -= aOffset;
+ }
+ else
+ {
+ aPos += aOffset;
+ }
+
+ Impl_writeText( aPos, rText, pDXArray, nWidth, aReliefColor );
+ Impl_writeText( rPos, rText, pDXArray, nWidth, aTextColor );
+ }
+ else
+ {
+ if( aMetric.IsShadow() )
+ {
+ long nOff = 1 + ((aMetric.GetLineHeight()-24)/24);
+ if ( aMetric.IsOutline() )
+ nOff += 6;
+
+ Color aTextColor( mpVDev->GetTextColor() );
+ Color aShadowColor = Color( COL_BLACK );
+
+ if ( (aTextColor.GetColor() == COL_BLACK) || (aTextColor.GetLuminance() < 8) )
+ aShadowColor = Color( COL_LIGHTGRAY );
+
+ Point aPos( rPos );
+ aPos += Point( nOff, nOff );
+ Impl_writeText( aPos, rText, pDXArray, nWidth, aShadowColor );
+
+ if( !aMetric.IsOutline() )
+ {
+ Impl_writeText( rPos, rText, pDXArray, nWidth, aTextColor );
+ }
+ }
+
+ if( aMetric.IsOutline() )
+ {
+ Point aPos = rPos + Point( -6, -6 );
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ aPos = rPos + Point(+6,+6);
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ aPos = rPos + Point(-6,+0);
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ aPos = rPos + Point(-6,+6);
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ aPos = rPos + Point(+0,+6);
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ aPos = rPos + Point(+0,-6);
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ aPos = rPos + Point(+6,-1);
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+ aPos = rPos + Point(+6,+0);
+ Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
+
+ Impl_writeText( rPos, rText, pDXArray, nWidth, Color( COL_WHITE ) );
+ }
+ }
+ }
+}
+
+void Writer::Impl_writeText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth, Color aTextColor )
+{
+ sal_uInt32 nLen = rText.Len();
+
+ if( !nLen )
+ return;
+
+ const bool bRTL = (mpVDev->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL) != 0;
+
+ sal_Int16 nScriptType = ScriptType::LATIN;
+ Reference < XBreakIterator > xBI( Impl_GetBreakIterator() );
+ if( xBI.is() )
+ {
+ const OUString oText( rText );
+ nScriptType = xBI->getScriptType( oText, 0 );
+ }
+
+ // if the text is either right to left or complex or asian, we
+ // ask the output device for a polygon representation.
+ // On complex and asian text, each unicode character can have
+ // different glyph representation, based on context. Also positioning
+ // is not trivial so we let the output device do it for us.
+ if( bRTL || (nScriptType != ScriptType::LATIN) )
+ {
+ // todo: optimize me as this will generate a huge amount of duplicate polygons
+ PolyPolygon aPolyPoygon;
+ mpVDev->GetTextOutline( aPolyPoygon, rText, 0, 0, (USHORT)nLen, TRUE, nWidth, pDXArray );
+ aPolyPoygon.Translate( rPos );
+ Impl_writePolyPolygon( aPolyPoygon, sal_True, aTextColor, aTextColor );
+ }
+ else
+ {
+ Size aNormSize;
+ sal_Int32* pOwnArray;
+ sal_Int32* pDX;
+
+ // get text sizes
+ if( pDXArray )
+ {
+ pOwnArray = NULL;
+ aNormSize = Size( mpVDev->GetTextWidth( rText ), 0 );
+ pDX = (sal_Int32*) pDXArray;
+ }
+ else
+ {
+ pOwnArray = new sal_Int32[ nLen ];
+ aNormSize = Size( mpVDev->GetTextArray( rText, pOwnArray ), 0 );
+ pDX = pOwnArray;
+ }
+
+ if( nLen > 1 )
+ {
+ aNormSize.Width() = pDX[ nLen - 2 ] + mpVDev->GetTextWidth( rText.GetChar( (USHORT) nLen - 1 ) );
+
+ if( nWidth && aNormSize.Width() && ( nWidth != aNormSize.Width() ) )
+ {
+ const double fFactor = (double) nWidth / aNormSize.Width();
+
+ sal_uInt32 i;
+ for( i = 0; i < ( nLen - 1 ); i++ )
+ pDX[ i ] = FRound( pDX[ i ] * fFactor );
+ }
+ }
+
+ Font aOldFont( mpVDev->GetFont() );
+ Point aBaseLinePos( rPos );
+
+ Font aFont(aOldFont);
+ short nOrientation = aFont.GetOrientation();
+ aFont.SetOrientation( 0 );
+ aFont.SetUnderline(UNDERLINE_NONE);
+ aFont.SetStrikeout(STRIKEOUT_NONE);
+ mpVDev->SetFont( aFont );
+
+ const FontMetric aMetric( mpVDev->GetFontMetric() );
+
+ FlashFont& rFlashFont = Impl_getFont( aFont );
+
+ // always adjust text position to match baseline alignment
+ switch( aOldFont.GetAlign() )
+ {
+ case( ALIGN_TOP ):
+ aBaseLinePos.Y() += aMetric.GetAscent();
+ break;
+
+ case( ALIGN_BOTTOM ):
+ aBaseLinePos.Y() -= aMetric.GetDescent();
+ break;
+
+ default:
+ break;
+ }
+
+ // get mapped text position
+ const Point aPt( map( aBaseLinePos ) );
+
+ // write text element
+
+/* test code to create a bound rect, not realy working for rotated text
+ Size aTextSize( map( Size( mpVDev->GetTextWidth( rText ), mpVDev->GetTextHeight() ) ) );
+ Point aMetricPoint( map( Point( aMetric.GetLeading(), aMetric.GetAscent() ) ) );
+
+ Point aTmpPoint( map( Point( - aMetric.GetLeading(), - aMetric.GetAscent() ) ) ); ;
+ Rectangle aTmpRectangle(aTmpPoint, aTextSize );
+ Polygon aPoly( aTmpRectangle );
+
+ aPoly.Rotate( aTmpPoint, (USHORT) nOrientation );
+
+ Rectangle aTextBoundRect( aPoly.GetBoundRect() );
+
+ aPoly.Move( aPt.X(), aPt.Y() - map( Size( 0, aMetric.GetDescent() ) ).Height() );
+
+*/
+
+#if 0 // makes the calculated bound rect visible for debuging
+{
+ Polygon aTmpPoly( aPoly );
+ sal_uInt16 nID = FlashGeometryExporter::writePolygonShape( aMovieStream, aTmpPoly, false, Color(COL_MAGENTA), Color(COL_MAGENTA), mpClipPolyPolygon );
+ ImplPlaceObject( nID );
+}
+#endif
+
+ // CL: This is still a hack until we figure out how to calculate a correct bound rect
+ // for rotatet text
+ Rectangle textBounds( 0, 0, static_cast<long>(mnDocWidth*mnDocXScale), static_cast<long>(mnDocHeight*mnDocYScale) );
+ double scale = 1.0;
+
+ // scale width if we have a stretched text
+ if( 0 != aFont.GetSize().Width() )
+ {
+ Font aTmpFont( aFont );
+ aTmpFont.SetWidth(0);
+ mpVDev->SetFont( aTmpFont );
+
+ const FontMetric aMetric2( mpVDev->GetFontMetric() );
+ mpVDev->SetFont( aFont );
+
+ const long n1 = aFont.GetSize().Width();
+ const long n2 = aMetric2.GetSize().Width();
+ scale = (double)n1 / (double)n2;
+ }
+
+ basegfx::B2DHomMatrix m(basegfx::tools::createRotateB2DHomMatrix(static_cast<double>(nOrientation) * F_PI1800));
+ m.translate( double(aPt.X() / scale), double(aPt.Y()) );
+ m.scale( scale, scale );
+
+ sal_Int16 nHeight = _Int16( map( Size( 0, aFont.GetHeight() ) ).Height() );
+
+ startTag( TAG_DEFINETEXT );
+
+ sal_uInt16 nTextId = createID();
+
+ mpTag->addUI16( nTextId );
+ mpTag->addRect( textBounds );
+ mpTag->addMatrix( m );
+
+ sal_uInt8 nGlyphBits = 16;
+ sal_uInt8 nAdvanceBits = 16;
+
+ mpTag->addUI8( nGlyphBits );
+ mpTag->addUI8( nAdvanceBits );
+
+ // text style change record
+ mpTag->addUI8( 0x8c );
+ mpTag->addUI16( rFlashFont.getID() );
+ mpTag->addRGB( aTextColor );
+ mpTag->addUI16( _uInt16( nHeight ) );
+
+ DBG_ASSERT( nLen <= 127, "TODO: handle text with more than 127 characters" );
+
+ // Glyph record
+ mpTag->addUI8( (sal_uInt8) nLen );
+
+ BitStream aBits;
+
+ sal_Int32 nLastDX = 0;
+ sal_Int32 nAdvance;
+ sal_uInt32 i;
+ for( i = 0; i < nLen; i++ )
+ {
+ if( i < (nLen-1) )
+ {
+ nAdvance = pDX[i] - nLastDX;
+ nLastDX = pDX[i];
+ }
+ else
+ {
+ nAdvance = 0;
+ }
+
+ aBits.writeUB( rFlashFont.getGlyph(rText.GetChar(_uInt16(i)),mpVDev), nGlyphBits );
+ aBits.writeSB( _Int16(map( Size( (long)( nAdvance / scale ), 0 ) ).Width() ), nAdvanceBits );
+ }
+
+ mpTag->addBits( aBits );
+ mpTag->addUI8( 0 );
+
+ endTag();
+
+ maShapeIds.push_back( nTextId );
+
+ // AS: Write strikeout and underline, if neccessary. This code was originally taken from the SVG
+ // export facility, although the positioning had to be tweaked a little. I can't explain the
+ // numbers, but the flash lines up very well with the original OOo document. All of this should
+ // probably be converted to polygons as part of the meta file, though, as we don't handle any
+ // fancy lines (like dashes).
+ if( ( aOldFont.GetStrikeout() != STRIKEOUT_NONE ) || ( aOldFont.GetUnderline() != UNDERLINE_NONE ) )
+ {
+ Polygon aPoly( 4 );
+ const long nLineHeight = Max( (long) FRound( aMetric.GetLineHeight() * 0.05 ), (long) 1 );
+
+ if( aOldFont.GetStrikeout() != STRIKEOUT_NONE )
+ {
+ aPoly[ 0 ].X() = aBaseLinePos.X();
+ aPoly[ 0 ].Y() = aBaseLinePos.Y() - FRound( aMetric.GetAscent() * 0.26 ) - nLineHeight;
+ aPoly[ 1 ].X() = aPoly[ 0 ].X() + aNormSize.Width() - 1;
+ aPoly[ 1 ].Y() = aPoly[ 0 ].Y();
+ aPoly[ 2 ].X() = aPoly[ 1 ].X();
+ aPoly[ 2 ].Y() = aPoly[ 1 ].Y() + nLineHeight - 1;
+ aPoly[ 3 ].X() = aPoly[ 0 ].X();
+ aPoly[ 3 ].Y() = aPoly[ 2 ].Y();
+
+ Impl_writePolygon( aPoly, sal_True, aTextColor, aTextColor );
+ }
+
+ // AS: The factor of 1.5 on the nLineHeight is a magic number. I'm not sure why it works,
+ // but it looks good to me.
+ if( aOldFont.GetUnderline() != UNDERLINE_NONE )
+ {
+ aPoly[ 0 ].X() = aBaseLinePos.X();
+ aPoly[ 0 ].Y() = static_cast<long>(aBaseLinePos.Y() + 1.5*nLineHeight);
+ aPoly[ 1 ].X() = aPoly[ 0 ].X() + aNormSize.Width() - 1;
+ aPoly[ 1 ].Y() = aPoly[ 0 ].Y();
+ aPoly[ 2 ].X() = aPoly[ 1 ].X();
+ aPoly[ 2 ].Y() = aPoly[ 1 ].Y() + nLineHeight - 1;
+ aPoly[ 3 ].X() = aPoly[ 0 ].X();
+ aPoly[ 3 ].Y() = aPoly[ 2 ].Y();
+
+ Impl_writePolygon( aPoly, sal_True, aTextColor, aTextColor );
+ }
+ }
+
+ mpVDev->SetFont( aOldFont );
+ delete[] pOwnArray;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// AS: Because JPEGs require the alpha channel provided seperately (JPEG does not
+// natively support alpha channel, but SWF lets you provide it seperately), we
+// extract the alpha channel into a seperate array here.
+void getBitmapData( const BitmapEx& aBmpEx, sal_uInt8*& tgadata, sal_uInt8*& tgaAlphadata, sal_uInt32& nWidth, sal_uInt32& nHeight )
+{
+ if( !aBmpEx.IsEmpty() )
+ {
+ Bitmap aBmp( aBmpEx.GetBitmap() );
+ BitmapReadAccess* pRAcc = aBmp.AcquireReadAccess();
+
+ if( pRAcc )
+ {
+ AlphaMask aAlpha;
+ nWidth = pRAcc->Width();
+ nHeight = pRAcc->Height();
+ tgadata = new sal_uInt8[nWidth*nHeight*4];
+ tgaAlphadata = new sal_uInt8[nWidth*nHeight];
+ sal_uInt8* p = tgadata, *pAlpha = tgaAlphadata;
+
+
+ if( aBmpEx.IsAlpha() )
+ aAlpha = aBmpEx.GetAlpha();
+ else if( aBmpEx.IsTransparent() )
+ aAlpha = aBmpEx.GetMask();
+ else
+ {
+ sal_uInt8 cAlphaVal = 0;
+ aAlpha = AlphaMask( aBmp.GetSizePixel(), &cAlphaVal );
+ }
+
+ BitmapReadAccess* pAAcc = aAlpha.AcquireReadAccess();
+
+ if( pAAcc )
+ {
+ for( sal_uInt32 nY = 0; nY < nHeight; nY++ )
+ {
+ for( sal_uInt32 nX = 0; nX < nWidth; nX++ )
+ {
+ const sal_uInt8 nAlpha = pAAcc->GetPixel( nY, nX ).GetIndex();
+ const BitmapColor aPixelColor( pRAcc->GetColor( nY, nX ) );
+
+ if( nAlpha == 0xff )
+ {
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
+ else
+ {
+ *p++ = 0xff-nAlpha;
+ *p++ = aPixelColor.GetRed();
+ *p++ = aPixelColor.GetGreen();
+ *p++ = aPixelColor.GetBlue();
+ }
+ *pAlpha++ = 0xff - nAlpha;
+ }
+ }
+
+ aAlpha.ReleaseAccess( pAAcc );
+ }
+
+ aBmp.ReleaseAccess( pRAcc );
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+sal_uInt16 Writer::defineBitmap( const BitmapEx &bmpSource, sal_Int32 nJPEGQualityLevel )
+{
+ ULONG bmpChecksum = bmpSource.GetChecksum();
+
+ ChecksumCache::iterator it = mBitmapCache.find(bmpChecksum);
+
+ // AS: We already exported this bitmap, so just return its ID.
+ if (mBitmapCache.end() != it)
+ return it->second;
+
+ sal_uInt16 nBitmapId = createID();
+ mBitmapCache[bmpChecksum] = nBitmapId;
+
+ // AS: OK, we have a good image, so now we decide whether or not to JPEG it or
+ // or Lossless compress it.
+
+ //Figure out lossless size
+ sal_uInt8 *pImageData, *pAlphaData;
+ sal_uInt32 width, height;
+
+ getBitmapData( bmpSource, pImageData, pAlphaData, width, height );
+ sal_uInt32 raw_size = width * height * 4;
+ uLongf compressed_size = raw_size + (sal_uInt32)(raw_size/100) + 12;
+ sal_uInt8 *pCompressed = new sal_uInt8[ compressed_size ];
+
+#ifdef DBG_UTIL
+ if(compress2(pCompressed, &compressed_size, pImageData, raw_size, Z_BEST_COMPRESSION) != Z_OK)
+ {
+ DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
+ }
+#else
+ compress2(pCompressed, &compressed_size, pImageData, raw_size, Z_BEST_COMPRESSION);
+#endif
+
+ // AS: SWF files let you provide an Alpha mask for JPEG images, but we have
+ // to ZLIB compress the alpha channel seperately.
+ uLong alpha_compressed_size = 0;
+ sal_uInt8 *pAlphaCompressed = NULL;
+ if (bmpSource.IsAlpha() || bmpSource.IsTransparent())
+ {
+ alpha_compressed_size = uLongf(width * height + (sal_uInt32)(raw_size/100) + 12);
+ pAlphaCompressed = new sal_uInt8[ compressed_size ];
+
+#ifdef DBG_UTIL
+ if(compress2(pAlphaCompressed, &alpha_compressed_size, pAlphaData, width * height, Z_BEST_COMPRESSION) != Z_OK)
+ {
+ DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
+ }
+#else
+ compress2(pAlphaCompressed, &alpha_compressed_size, pAlphaData, width * height, Z_BEST_COMPRESSION);
+#endif
+ }
+
+ //Figure out JPEG size
+ const sal_uInt8* pJpgData = NULL;;
+ sal_uInt32 nJpgDataLength = 0xffffffff;
+
+ Graphic aGraphic( bmpSource );
+ SvMemoryStream aDstStm( 65535, 65535 );
+
+ GraphicFilter aFilter;
+
+ Sequence< PropertyValue > aFilterData(nJPEGQualityLevel != -1);
+ if( nJPEGQualityLevel != -1 )
+ {
+ aFilterData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("Quality"));
+ aFilterData[0].Value <<= nJPEGQualityLevel;
+ }
+
+#if 0
+ // Debug code to see what we export to swf
+ {
+ SvFileStream aDstStm( String( RTL_CONSTASCII_USTRINGPARAM("e:\\test.jpg") ), STREAM_READ | STREAM_WRITE | STREAM_TRUNC );
+ aFilter.ExportGraphic( aGraphic, String(), aDstStm,
+ aFilter.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME ) ) ), &aFilterData );
+ }
+#endif
+
+ if( aFilter.ExportGraphic( aGraphic, String(), aDstStm,
+ aFilter.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME ) ) ), &aFilterData ) == ERRCODE_NONE )
+ {
+ pJpgData = reinterpret_cast<const sal_uInt8*>(aDstStm.GetData());
+ nJpgDataLength = aDstStm.Seek( STREAM_SEEK_TO_END );
+ }
+
+ // AS: Ok, now go ahead and use whichever is smaller. If JPEG is smaller, then
+ // we have to export as TAG_DEFINEBITSJPEG3 in the case that there is alpha
+ // channel data.
+ if ( pJpgData && ( nJpgDataLength + alpha_compressed_size < compressed_size) )
+ Impl_writeJPEG(nBitmapId, pJpgData, nJpgDataLength, pAlphaCompressed, alpha_compressed_size );
+ else
+ Impl_writeBmp( nBitmapId, width, height, pCompressed, compressed_size );
+
+ delete[] pCompressed;
+ delete[] pAlphaCompressed;
+ delete[] pImageData;
+ delete[] pAlphaData;
+
+ return nBitmapId;
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeImage( const BitmapEx& rBmpEx, const Point& rPt, const Size& rSz, const Point& /* rSrcPt */, const Size& /* rSrcSz */, const Rectangle& rClipRect, bool bNeedToMapClipRect )
+{
+ if( !!rBmpEx )
+ {
+ BitmapEx bmpSource( rBmpEx );
+
+ Rectangle originalPixelRect = Rectangle(Point(), bmpSource.GetSizePixel());
+
+ Point srcPt( map(rPt) );
+ Size srcSize( map(rSz) );
+ Rectangle destRect( srcPt, srcSize );
+
+ // AS: Christian, my scaling factors are different than yours, and work better for me.
+ // However, I can't explain why exactly. I got some of this by trial and error.
+ double XScale = static_cast<double>(originalPixelRect.GetWidth())/destRect.GetWidth();
+ double YScale = static_cast<double>(originalPixelRect.GetHeight())/destRect.GetHeight();
+
+ // AS: If rClipRect has a value set, then we need to crop the bmp appropriately.
+ // If a map event already occurred in the metafile, then we do not need to map
+ // the clip rect as it's already been done.
+ if (!rClipRect.IsEmpty())
+ {
+ // AS: Christian, I also don't understand why bNeedToMapClipRect is necessary, but it
+ // works like a charm. Usually, the map event in the meta file does not cause the
+ // clipping rectangle to get mapped. However, sometimes there are multiple layers
+ // of mapping which eventually do cause the clipping rect to be mapped.
+ Size clipSize( bNeedToMapClipRect ? map(rClipRect.GetSize()) : rClipRect.GetSize() );
+ Rectangle clipRect = Rectangle(Point(), clipSize);
+ destRect.Intersection( clipRect );
+
+ Rectangle cropRect(destRect);
+
+ // AS: The bmp origion is always 0,0 so we have to adjust before we crop.
+ cropRect.Move(-srcPt.X(), -srcPt.Y());
+ // AS: Rectangle has no scale function (?!) so I do it manually...
+ Rectangle cropPixelRect(static_cast<long>(cropRect.Left()*XScale),
+ static_cast<long>(cropRect.Top()*YScale),
+ static_cast<long>(cropRect.Right()*XScale),
+ static_cast<long>(cropRect.Bottom()*YScale));
+
+ bmpSource.Crop(cropPixelRect);
+ }
+
+ if( !!bmpSource )
+ {
+ // #105949# fix images that are under 16 pixels width or height by
+ // expanding them. Some swf players can't display such small
+ // bitmaps
+ const Size& rSizePixel = bmpSource.GetSizePixel();
+ if( (rSizePixel.Width() < 16) || (rSizePixel.Height() < 16) )
+ {
+ const sal_uInt32 nDX = rSizePixel.Width() < 16 ? 16 - rSizePixel.Width() : 0;
+ const sal_uInt32 nDY = rSizePixel.Height() < 16 ? 16 - rSizePixel.Height() : 0;
+ bmpSource.Expand( nDX, nDY );
+ }
+
+ sal_Int32 nJPEGQuality = mnJPEGCompressMode;
+
+ Size szDestPixel = mpVDev->LogicToPixel(srcSize, aTWIPSMode);
+
+ double pixXScale = static_cast<double>(szDestPixel.Width()) / originalPixelRect.GetWidth();
+ double pixYScale = static_cast<double>(szDestPixel.Height()) / originalPixelRect.GetHeight();
+
+ // AS: If the image has been scaled down, then scale down the quality
+ // that we use for JPEG compression.
+ if (pixXScale < 1.0 && pixYScale < 1.0)
+ {
+
+ double qualityScale = (pixXScale + pixYScale)/2;
+
+ nJPEGQuality = (sal_Int32)( nJPEGQuality * qualityScale );
+
+ if (nJPEGQuality < 10)
+ nJPEGQuality += 3;
+ }
+
+ sal_uInt16 nBitmapId = defineBitmap(bmpSource, nJPEGQuality);
+
+ Polygon aPoly( destRect );
+
+ // AS: Since images are being cropped now, no translation is normally necessary.
+ // However, some things like graphical bullet points are still get translated.
+ ::basegfx::B2DHomMatrix m; // #i73264#
+ m.scale(1.0/XScale, 1.0/YScale );
+ if (destRect.Left() || destRect.Top())
+ m.translate(destRect.Left(), destRect.Top());
+
+ FillStyle aFillStyle( nBitmapId, true, m );
+
+ sal_uInt16 nShapeId = defineShape( aPoly, aFillStyle );
+
+ maShapeIds.push_back( nShapeId );
+ }
+ }
+}
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeBmp( sal_uInt16 nBitmapId, sal_uInt32 width, sal_uInt32 height, sal_uInt8 *pCompressed, sal_uInt32 compressed_size )
+{
+ startTag( TAG_DEFINEBITSLOSSLESS2 );
+
+ mpTag->addUI16( nBitmapId );
+ mpTag->addUI8( 5 );
+ mpTag->addUI16( _uInt16(width) );
+ mpTag->addUI16( _uInt16(height) );
+
+ mpTag->Write( pCompressed, compressed_size );
+
+ endTag();
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeJPEG(sal_uInt16 nBitmapId, const sal_uInt8* pJpgData, sal_uInt32 nJpgDataLength, sal_uInt8 *pAlphaCompressed, sal_uInt32 alpha_compressed_size )
+{
+ // AS: Go through the actuall JPEG bits, seperating out the
+ // header fields from the actual image fields. Fields are
+ // identifed by 0xFFXX where XX is the field type. Both
+ // the header and the image need start and stop (D8 and D9),
+ // so that's why you see those written to both. I don't
+ // really know what the rest of these are, I got it to work
+ // kind of by trial and error and by comparing with known
+ // good SWF files.
+ sal_uInt8 cType = 0x01;
+ const sal_uInt8* pJpgSearch = pJpgData;
+
+ int nLength = 0;
+
+ SvMemoryStream EncodingTableStream;
+ SvMemoryStream ImageBitsStream;
+ for (;pJpgSearch < pJpgData + nJpgDataLength; pJpgSearch += nLength)
+ {
+
+#ifdef DBG_UTIL
+ if (0xFF != *pJpgSearch)
+ {
+ DBG_ERROR( "Expected JPEG marker." ); ((void)0);
+ }
+#endif
+
+ cType = *(pJpgSearch + 1);
+
+ if (0xD8 == cType || 0xD9 == cType)
+ {
+ nLength = 2;
+ }
+ else if (0xDA == cType)
+ {
+ //AS: This is the actual image data, and runs to the
+ // end of the file (as best I know), minus 2 bytes
+ // for the closing 0xFFD9.
+ nLength = nJpgDataLength - (pJpgSearch - pJpgData) - 2;
+ }
+ else
+ {
+ // AS: Lengths are big endian.
+
+ // Beware. pJpgSearch is not necessarily word-aligned,
+ // so we access it byte-wise.
+
+ // AS: Add 2 to the length to include the 0xFFXX itself.
+ nLength = 2 + (pJpgSearch[2]<<8) + pJpgSearch[3];
+ }
+
+ // AS: I'm refering to libjpeg for a list of what these
+ // markers are. See jdmarker.c for a list.
+ // AS: I'm ignoring application specific markers 0xE1...0xEF
+ // and comments 0xFE. I don't know what
+ // 0xF0 or 0xFD are for, and they don't come up.
+ // Additionally, 0xDE and 0xDF aren't clear to me.
+ switch(cType)
+ {
+ case 0xD8:
+ case 0xD9:
+ EncodingTableStream.Write( pJpgSearch, nLength );
+ ImageBitsStream.Write( pJpgSearch, nLength );
+ break;
+
+ case 0x01:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xC4:
+ EncodingTableStream.Write( pJpgSearch, nLength );
+ break;
+
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+// case 0xC8: Apparently reserved for JPEG extensions?
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xDA:
+ case 0xE0:
+ ImageBitsStream.Write( pJpgSearch, nLength );
+ break;
+
+ default:
+ DBG_ERROR( "JPEG marker I didn't handle!" );
+
+ }
+ }
+
+ EncodingTableStream.Seek( STREAM_SEEK_TO_END );
+ sal_uInt32 nEncodingTableSize = EncodingTableStream.Tell();
+ EncodingTableStream.Seek( STREAM_SEEK_TO_BEGIN );
+
+ ImageBitsStream.Seek( STREAM_SEEK_TO_END );
+ sal_uInt32 nImageBitsSize = ImageBitsStream.Tell();
+ ImageBitsStream.Seek( STREAM_SEEK_TO_BEGIN );
+
+ // AS: If we need alpha support, use TAG_DEFINEBITSJPEG3.
+ if (alpha_compressed_size > 0)
+ {
+ startTag( TAG_DEFINEBITSJPEG3 );
+
+ mpTag->addUI16( nBitmapId );
+
+ mpTag->addUI32( nEncodingTableSize + nImageBitsSize );
+
+ mpTag->Write(EncodingTableStream.GetData(), nEncodingTableSize);
+ mpTag->Write(ImageBitsStream.GetData(), nImageBitsSize);
+
+ mpTag->Write( pAlphaCompressed, alpha_compressed_size );
+
+ endTag();
+ }
+ else
+ {
+ startTag( TAG_DEFINEBITSJPEG2 );
+
+ mpTag->addUI16( nBitmapId );
+
+ mpTag->Write(EncodingTableStream.GetData(), nEncodingTableSize);
+ mpTag->Write(ImageBitsStream.GetData(), nImageBitsSize);
+
+ endTag();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeLine( const Point& rPt1, const Point& rPt2, const Color* pLineColor )
+{
+ Color aOldColor( mpVDev->GetLineColor() );
+ if( pLineColor )
+ mpVDev->SetLineColor( *pLineColor );
+
+ const Point aPtAry[2] = { rPt1, rPt2 };
+ Polygon aPoly( 2, aPtAry );
+ Impl_writePolyPolygon( aPoly, false );
+
+ mpVDev->SetLineColor( aOldColor );
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeRect( const Rectangle& rRect, long nRadX, long nRadY )
+{
+ if( (rRect.nTop == rRect.nBottom) || (rRect.nLeft == rRect.nRight) )
+ {
+ Color aColor( mpVDev->GetFillColor() );
+ Impl_writeLine( rRect.TopLeft(), rRect.BottomRight(), &aColor );
+ }
+ else
+ {
+ Polygon aPoly( rRect, nRadX, nRadY );
+ Impl_writePolyPolygon( aPoly, true );
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeEllipse( const Point& rCenter, long nRadX, long nRadY )
+{
+ Polygon aPoly( rCenter, nRadX, nRadY );
+ Impl_writePolyPolygon( aPoly, false );
+}
+
+
+/** writes the stroke defined by SvtGraphicStroke and returns true or it returns
+ false if it can't handle this stroke.
+*/
+bool Writer::Impl_writeStroke( SvtGraphicStroke& rStroke )
+{
+ Polygon aPolygon;
+ rStroke.getPath( aPolygon );
+ PolyPolygon aPolyPolygon( aPolygon );
+
+ Rectangle aOldRect( aPolyPolygon.GetBoundRect() );
+
+ map( aPolyPolygon );
+
+ Rectangle aNewRect( aPolyPolygon.GetBoundRect() );
+
+ // as log as not LINESTYLE2 and DefineShape4 is used (which
+ // added support for LineJoin), only round LineJoins are
+ // supported. Fallback to META_POLYLINE_ACTION and META_LINE_ACTION
+ if(SvtGraphicStroke::joinRound != rStroke.getJoinType())
+ return false;
+
+ PolyPolygon aStartArrow;
+ rStroke.getStartArrow( aStartArrow );
+ if( 0 != aStartArrow.Count() )
+ return false; // todo: Implement line ends
+
+ PolyPolygon aEndArrow;
+ rStroke.getEndArrow( aEndArrow );
+ if( 0 != aEndArrow.Count() )
+ return false; // todo: Implement line ends
+
+ SvtGraphicStroke::DashArray aDashArray;
+ rStroke.getDashArray( aDashArray );
+ if( 0 != aDashArray.size() )
+ return false; // todo: implement dashes
+
+ Color aColor( mpVDev->GetLineColor() );
+
+ if( 0.0 != rStroke.getTransparency() )
+ aColor.SetTransparency( sal::static_int_cast<UINT8>( MinMax( (long int)( rStroke.getTransparency() * 0xff ), 0, 0xff ) ) );
+
+ sal_uInt16 nShapeId = defineShape( aPolyPolygon, sal::static_int_cast<sal_uInt16>( mapRelative( (sal_Int32)( rStroke.getStrokeWidth() ) ) ), aColor );
+ maShapeIds.push_back( nShapeId );
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+/** writes the filling defined by SvtGraphicFill and returns true or it returns
+ false if it can't handle this filling.
+*/
+bool Writer::Impl_writeFilling( SvtGraphicFill& rFilling )
+{
+ PolyPolygon aPolyPolygon;
+ rFilling.getPath( aPolyPolygon );
+
+ Rectangle aOldRect( aPolyPolygon.GetBoundRect() );
+
+ map( aPolyPolygon );
+
+ Rectangle aNewRect( aPolyPolygon.GetBoundRect() );
+
+ switch( rFilling.getFillType() )
+ {
+ case SvtGraphicFill::fillSolid:
+ {
+ Color aColor( rFilling.getFillColor() );
+
+ if( 0.0 != rFilling.getTransparency() )
+ aColor.SetTransparency( sal::static_int_cast<UINT8>( MinMax( (long int)( rFilling.getTransparency() * 0xff ) , 0, 0xff ) ) );
+
+ FillStyle aFillStyle( aColor );
+
+ sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
+ maShapeIds.push_back( nShapeId );
+ }
+ break;
+ case SvtGraphicFill::fillGradient:
+ return false;
+ case SvtGraphicFill::fillHatch:
+ return false;
+ case SvtGraphicFill::fillTexture:
+ {
+ Graphic aGraphic;
+ rFilling.getGraphic( aGraphic );
+
+ // CL->AS: Should we also scale down the quality here depending on image scale?
+ sal_uInt16 nBitmapId = defineBitmap( aGraphic.GetBitmapEx(), mnJPEGCompressMode );
+
+ ::basegfx::B2DHomMatrix aMatrix; // #i73264#
+
+ SvtGraphicFill::Transform aTransform;
+
+ rFilling.getTransform( aTransform );
+
+ sal_uInt16 a,b;
+ for( a = 0; a < 2; a++ )
+ {
+ for( b = 0; b < 3; b++ )
+ {
+ aMatrix.set(a, b, aTransform.matrix[a*3+b]);
+ }
+ }
+ aMatrix.set(2, 0, 0.0);
+ aMatrix.set(2, 1, 0.0);
+ aMatrix.set(2, 2, 1.0);
+
+ // scale bitmap
+ Rectangle originalPixelRect = Rectangle(Point(), aGraphic.GetBitmapEx().GetSizePixel());
+
+ double XScale = (double)aNewRect.GetWidth()/aOldRect.GetWidth();
+ double YScale = (double)aNewRect.GetHeight()/aOldRect.GetHeight();
+
+ aMatrix.scale( XScale, YScale );
+
+ FillStyle aFillStyle( nBitmapId, !rFilling.IsTiling(), aMatrix );
+
+ sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
+ maShapeIds.push_back( nShapeId );
+ }
+ break;
+ }
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+/* CL: The idea was to export page fields as text fields that get theire
+ string from a variable set with actionscript by each page. This didn't
+ work out since the formating is always wrong when text follows the
+ page number field since pages greater one may require more space than
+ page 1
+*/
+#if 0
+bool Writer::Impl_writePageField( Rectangle& rTextBounds )
+{
+ startTag( TAG_DEFINEEDITTEXT );
+
+ sal_uInt16 nTextId = createID();
+
+ mpTag->addUI16( nTextId );
+ mpTag->addRect( rTextBounds );
+
+ BitStream aBits;
+ aBits.writeUB( 1, 1 ); // HasText
+ aBits.writeUB( 0, 1 ); // WordWrap
+ aBits.writeUB( 0, 1 ); // MultiLine
+ aBits.writeUB( 0, 1 ); // Password
+ aBits.writeUB( 1, 1 ); // HasTextColor
+ aBits.writeUB( 0, 1 ); // HasMaxLength
+ aBits.writeUB( 0, 1 ); // HasFont
+ aBits.writeUB( 0, 1 ); // Reserved
+ aBits.writeUB( 0, 1 ); // AutoSize
+ aBits.writeUB( 0, 1 ); // HasLayout
+ aBits.writeUB( 1, 1 ); // NoSelect
+ aBits.writeUB( 1, 1 ); // Border
+ aBits.writeUB( 0, 1 ); // Reserved
+ aBits.writeUB( 0, 1 ); // HTML
+ aBits.writeUB( 0, 1 ); // UseOutlines
+ mpTag->addBits( aBits );
+
+ Color aColor( COL_BLACK );
+ mpTag->addRGB( aColor );
+ mpTag->addString( "PageNumber" );
+ mpTag->addString( "XXX" );
+
+ endTag();
+
+ maShapeIds.push_back( nTextId );
+
+ return true;
+}
+#endif
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon)
+{
+ if(rLinePolygon.count())
+ {
+ basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
+ basegfx::B2DPolyPolygon aFillPolyPolygon;
+
+ rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);
+
+ if(aLinePolyPolygon.count())
+ {
+ for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
+ {
+ const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
+ Impl_writePolygon(Polygon(aCandidate), sal_False );
+ }
+ }
+
+ if(aFillPolyPolygon.count())
+ {
+ const Color aOldLineColor(mpVDev->GetLineColor());
+ const Color aOldFillColor(mpVDev->GetFillColor());
+
+ mpVDev->SetLineColor();
+ mpVDev->SetFillColor(aOldLineColor);
+
+ for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
+ {
+ const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a));
+ Impl_writePolyPolygon(PolyPolygon(Polygon(aPolygon)), sal_True );
+ }
+
+ mpVDev->SetLineColor(aOldLineColor);
+ mpVDev->SetFillColor(aOldFillColor);
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_writeActions( const GDIMetaFile& rMtf )
+{
+ Rectangle clipRect;
+ int bMap = 0;
+ for( ULONG i = 0, nCount = rMtf.GetActionCount(); i < nCount; i++ )
+ {
+ const MetaAction* pAction = rMtf.GetAction( i );
+ const USHORT nType = pAction->GetType();
+
+ switch( nType )
+ {
+ case( META_PIXEL_ACTION ):
+ {
+ const MetaPixelAction* pA = (const MetaPixelAction*) pAction;
+
+ Impl_writeLine( pA->GetPoint(), pA->GetPoint(), &pA->GetColor() );
+ }
+ break;
+
+ case( META_POINT_ACTION ):
+ {
+ const MetaPointAction* pA = (const MetaPointAction*) pAction;
+
+ Impl_writeLine( pA->GetPoint(), pA->GetPoint() );
+ }
+ break;
+
+ case( META_LINE_ACTION ):
+ {
+ const MetaLineAction* pA = (const MetaLineAction*) pAction;
+
+ if(pA->GetLineInfo().IsDefault())
+ {
+ Impl_writeLine( pA->GetStartPoint(), pA->GetEndPoint() );
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
+ aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
+ Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
+ }
+ }
+ break;
+
+ case( META_RECT_ACTION ):
+ {
+ Impl_writeRect( ( (const MetaRectAction*) pAction )->GetRect(), 0, 0 );
+ }
+ break;
+
+ case( META_ROUNDRECT_ACTION ):
+ {
+ const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction;
+
+ Impl_writeRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
+ }
+ break;
+
+ case( META_ELLIPSE_ACTION ):
+ {
+ const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction;
+ const Rectangle& rRect = pA->GetRect();
+
+ Impl_writeEllipse( rRect.Center(), rRect.GetWidth() >> 1, rRect.GetHeight() >> 1 );
+ }
+ break;
+
+ case( META_ARC_ACTION ):
+ case( META_PIE_ACTION ):
+ case( META_CHORD_ACTION ):
+ case( META_POLYGON_ACTION ):
+ {
+ Polygon aPoly;
+
+ switch( nType )
+ {
+ case( META_ARC_ACTION ):
+ {
+ const MetaArcAction* pA = (const MetaArcAction*) pAction;
+ aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC );
+ }
+ break;
+
+ case( META_PIE_ACTION ):
+ {
+ const MetaPieAction* pA = (const MetaPieAction*) pAction;
+ aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE );
+ }
+ break;
+
+ case( META_CHORD_ACTION ):
+ {
+ const MetaChordAction* pA = (const MetaChordAction*) pAction;
+ aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD );
+ }
+ break;
+
+ case( META_POLYGON_ACTION ):
+ aPoly = ( (const MetaPolygonAction*) pAction )->GetPolygon();
+ break;
+ }
+
+ if( aPoly.GetSize() )
+ {
+ Impl_writePolygon( aPoly, sal_True );
+ }
+ }
+ break;
+
+ case( META_POLYLINE_ACTION ):
+ {
+ const MetaPolyLineAction* pA = (const MetaPolyLineAction*) pAction;
+ const Polygon& rPoly = pA->GetPolygon();
+
+ if( rPoly.GetSize() )
+ {
+ if(pA->GetLineInfo().IsDefault())
+ {
+ Impl_writePolygon( rPoly, sal_False );
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
+ }
+ }
+ }
+ break;
+
+ case( META_POLYPOLYGON_ACTION ):
+ {
+ const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*) pAction;
+ const PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
+
+ if( rPolyPoly.Count() )
+ Impl_writePolyPolygon( rPolyPoly, sal_True );
+ }
+ break;
+
+ case( META_GRADIENT_ACTION ):
+ {
+ const MetaGradientAction* pA = (const MetaGradientAction*) pAction;
+
+ Polygon aPoly( pA->GetRect() );
+ Impl_writeGradientEx( aPoly, pA->GetGradient() );
+ }
+ break;
+
+ case( META_GRADIENTEX_ACTION ):
+ {
+ const MetaGradientExAction* pA = (const MetaGradientExAction*) pAction;
+ Impl_writeGradientEx( pA->GetPolyPolygon(), pA->GetGradient() );
+ }
+ break;
+
+ case META_HATCH_ACTION:
+ {
+ const MetaHatchAction* pA = (const MetaHatchAction*) pAction;
+ GDIMetaFile aTmpMtf;
+
+ mpVDev->AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
+ Impl_writeActions( aTmpMtf );
+ }
+ break;
+
+ case( META_TRANSPARENT_ACTION ):
+ {
+ const MetaTransparentAction* pA = (const MetaTransparentAction*) pAction;
+ const PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
+
+ if( rPolyPoly.Count() )
+ {
+ // convert transparence from percent into 0x00 - 0xff
+ sal_uInt8 nTransparence = (sal_uInt8) MinMax( FRound( pA->GetTransparence() * 2.55 ), 0, 255 );
+ Impl_writePolyPolygon( rPolyPoly, sal_True, nTransparence );
+ }
+ }
+ break;
+
+ case( META_FLOATTRANSPARENT_ACTION ):
+ {
+ const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction;
+ GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
+ Point aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
+ const Size aSrcSize( aTmpMtf.GetPrefSize() );
+ const Point aDestPt( pA->GetPoint() );
+ const Size aDestSize( pA->GetSize() );
+ const double fScaleX = aSrcSize.Width() ? (double) aDestSize.Width() / aSrcSize.Width() : 1.0;
+ const double fScaleY = aSrcSize.Height() ? (double) aDestSize.Height() / aSrcSize.Height() : 1.0;
+ long nMoveX, nMoveY;
+
+ if( fScaleX != 1.0 || fScaleY != 1.0 )
+ {
+ aTmpMtf.Scale( fScaleX, fScaleY );
+ aSrcPt.X() = FRound( aSrcPt.X() * fScaleX );
+ aSrcPt.Y() = FRound( aSrcPt.Y() * fScaleY );
+ }
+
+ nMoveX = aDestPt.X() - aSrcPt.X(), nMoveY = aDestPt.Y() - aSrcPt.Y();
+
+ if( nMoveX || nMoveY )
+ aTmpMtf.Move( nMoveX, nMoveY );
+
+ const Gradient& rGradient = pA->GetGradient();
+ sal_uInt32 nLuminance = ((sal_Int32)rGradient.GetStartColor().GetLuminance() + (sal_Int32)rGradient.GetEndColor().GetLuminance() ) >> 1;
+
+ sal_uInt8 nOldGlobalTransparency = mnGlobalTransparency;
+ mnGlobalTransparency = (sal_uInt8)MinMax( nLuminance, 0, 0xff );
+
+ mpVDev->Push();
+ Impl_writeActions( aTmpMtf );
+ mpVDev->Pop();
+
+ mnGlobalTransparency = nOldGlobalTransparency;
+ }
+ break;
+
+ case( META_EPS_ACTION ):
+ {
+ const MetaEPSAction* pA = (const MetaEPSAction*) pAction;
+ const GDIMetaFile aGDIMetaFile( pA->GetSubstitute() );
+ sal_Bool bFound = sal_False;
+
+ for( ULONG j = 0, nC = aGDIMetaFile.GetActionCount(); ( j < nC ) && !bFound; j++ )
+ {
+ const MetaAction* pSubstAct = aGDIMetaFile.GetAction( j );
+
+ if( pSubstAct->GetType() == META_BMPSCALE_ACTION )
+ {
+ bFound = sal_True;
+ const MetaBmpScaleAction* pBmpScaleAction = (const MetaBmpScaleAction*) pSubstAct;
+ Impl_writeImage( pBmpScaleAction->GetBitmap(),
+ pA->GetPoint(), pA->GetSize(),
+ Point(), pBmpScaleAction->GetBitmap().GetSizePixel(), clipRect, 1 == bMap );
+ }
+ }
+ }
+ break;
+
+ case( META_COMMENT_ACTION ):
+ {
+ const MetaCommentAction* pA = (const MetaCommentAction*) pAction;
+ const BYTE* pData = pA->GetData();
+ String aSkipComment;
+
+ if( pA->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
+ {
+ const MetaGradientExAction* pGradAction = NULL;
+ sal_Bool bDone = sal_False;
+
+ while( !bDone && ( ++i < nCount ) )
+ {
+ pAction = rMtf.GetAction( i );
+
+ if( pAction->GetType() == META_GRADIENTEX_ACTION )
+ pGradAction = (const MetaGradientExAction*) pAction;
+ else if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
+ ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) )
+ {
+ bDone = sal_True;
+ }
+ }
+
+ if( pGradAction )
+ Impl_writeGradientEx( pGradAction->GetPolyPolygon(), pGradAction->GetGradient());
+ }
+ else if( pA->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_BEGIN" ) == COMPARE_EQUAL &&
+ pData )
+ {
+
+ // this comment encapsulates all high level information for a filling that caused
+ // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
+
+ SvtGraphicFill aFilling;
+ SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
+
+ // read the fill info
+ aMemStm >> aFilling;
+
+ // if impl_writeFilling can handle this high level filling, it returns true and we
+ // skip all meta actions until "XPATHFILL_SEQ_END"
+ if( Impl_writeFilling( aFilling ) )
+ {
+ bool bDone = sal_False;
+
+ while( !bDone && ( ++i < nCount ) )
+ {
+ pAction = rMtf.GetAction( i );
+
+ if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
+ ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_END" ) == COMPARE_EQUAL ) )
+ {
+ bDone = sal_True;
+ }
+ }
+ }
+ }
+ else if( pA->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_BEGIN" ) == COMPARE_EQUAL &&
+ pData )
+ {
+
+ // this comment encapsulates all high level information for a filling that caused
+ // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
+
+ SvtGraphicStroke aStroke;
+ SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
+
+ // read the fill info
+ aMemStm >> aStroke;
+
+ // if impl_writeStroke can handle this high level stroke, it returns true and we
+ // skip all meta actions until "XPATHSTROKE_SEQ_END"
+ if( Impl_writeStroke( aStroke ) )
+ {
+ bool bDone = sal_False;
+
+ while( !bDone && ( ++i < nCount ) )
+ {
+ pAction = rMtf.GetAction( i );
+
+ if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
+ ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_END" ) == COMPARE_EQUAL ) )
+ {
+ bDone = sal_True;
+ }
+ }
+ }
+ }
+#if 0
+ else if( pA->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_BEGIN;PageField" ) == COMPARE_EQUAL )
+ {
+ bool bDone = sal_False;
+
+ while( !bDone && ( ++i < nCount ) )
+ {
+ pAction = rMtf.GetAction( i );
+
+ if( pAction->GetType() == META_TEXTARRAY_ACTION )
+ {
+ const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction;
+ Rectangle aRect( pA->GetPoint(), Size( 100, 100 ) );
+ Impl_writePageField( aRect );
+ }
+
+ if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
+ ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_END" ) == COMPARE_EQUAL ) )
+ {
+ bDone = sal_True;
+ }
+ }
+ }
+#endif
+ }
+ break;
+
+ case( META_BMPSCALE_ACTION ):
+ {
+ const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction;
+
+ Impl_writeImage( pA->GetBitmap(),
+ pA->GetPoint(), pA->GetSize(),
+ Point(), pA->GetBitmap().GetSizePixel(), clipRect, 1 == bMap );
+ }
+ break;
+
+ case( META_BMP_ACTION ):
+ {
+ const MetaBmpAction* pA = (const MetaBmpAction*) pAction;
+ Impl_writeImage( pA->GetBitmap(),
+ pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmap().GetSizePixel()),
+ Point(), pA->GetBitmap().GetSizePixel(), clipRect, 1 ==bMap );
+ }
+ break;
+
+ case( META_BMPSCALEPART_ACTION ):
+ {
+ const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*) pAction;
+ Impl_writeImage( pA->GetBitmap(),
+ pA->GetDestPoint(), pA->GetDestSize(),
+ pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap );
+ }
+ break;
+
+ case( META_BMPEX_ACTION ):
+ {
+ const MetaBmpExAction* pA = (const MetaBmpExAction*) pAction;
+ Impl_writeImage( pA->GetBitmapEx(),
+ pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmapEx().GetSizePixel() ),
+ Point(), pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap );
+ }
+ break;
+
+ case( META_BMPEXSCALE_ACTION ):
+ {
+ const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction;
+ Impl_writeImage( pA->GetBitmapEx(),
+ pA->GetPoint(), pA->GetSize(),
+ Point(), pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap );
+ }
+ break;
+
+ case( META_BMPEXSCALEPART_ACTION ):
+ {
+ const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pAction;
+ Impl_writeImage( pA->GetBitmapEx(),
+ pA->GetDestPoint(), pA->GetDestSize(),
+ pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap );
+ }
+ break;
+
+ case( META_TEXT_ACTION ):
+ {
+ const MetaTextAction* pA = (const MetaTextAction*) pAction;
+ Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), NULL, 0);
+ }
+ break;
+
+ case( META_TEXTRECT_ACTION ):
+ {
+ const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction;
+ Impl_writeText( pA->GetRect().TopLeft(), pA->GetText(), NULL, 0 );
+ }
+ break;
+
+ case( META_TEXTARRAY_ACTION ):
+ {
+ const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction;
+ Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), pA->GetDXArray(), 0 );
+ }
+ break;
+
+ case( META_STRETCHTEXT_ACTION ):
+ {
+ const MetaStretchTextAction* pA = (const MetaStretchTextAction*) pAction;
+ Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), NULL, pA->GetWidth() );
+ }
+ break;
+
+ case( META_ISECTRECTCLIPREGION_ACTION ):
+ {
+ const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*) pAction;
+ clipRect = pA->GetRect();
+ }
+ case( META_CLIPREGION_ACTION ):
+ case( META_ISECTREGIONCLIPREGION_ACTION ):
+ case( META_MOVECLIPREGION_ACTION ):
+ {
+ ( (MetaAction*) pAction )->Execute( mpVDev );
+// mbClipAttrChanged = sal_True;
+ }
+ break;
+
+ case( META_MAPMODE_ACTION ):
+ {
+// const MetaMapModeAction *pA = (const MetaMapModeAction*) pAction;
+// MapMode mm = pA->GetMapMode();
+// MapUnit mu = mm.GetMapUnit();
+//
+// Point pt = mm.GetOrigin();
+// Fraction fx = mm.GetScaleX();
+// Fraction fy = mm.GetScaleY();
+
+ bMap++;
+ }
+ case( META_REFPOINT_ACTION ):
+ case( META_LINECOLOR_ACTION ):
+ case( META_FILLCOLOR_ACTION ):
+ case( META_TEXTLINECOLOR_ACTION ):
+ case( META_TEXTFILLCOLOR_ACTION ):
+ case( META_TEXTCOLOR_ACTION ):
+ case( META_TEXTALIGN_ACTION ):
+ case( META_FONT_ACTION ):
+ case( META_PUSH_ACTION ):
+ case( META_POP_ACTION ):
+ case( META_LAYOUTMODE_ACTION ):
+ {
+ ( (MetaAction*) pAction )->Execute( mpVDev );
+ }
+ break;
+
+ case( META_RASTEROP_ACTION ):
+ case( META_MASK_ACTION ):
+ case( META_MASKSCALE_ACTION ):
+ case( META_MASKSCALEPART_ACTION ):
+ case( META_WALLPAPER_ACTION ):
+ case( META_TEXTLINE_ACTION ):
+ {
+ // !!! >>> we don't want to support these actions
+ }
+ break;
+
+ default:
+ //DBG_ERROR( "FlashActionWriter::ImplWriteActions: unsupported MetaAction #" );
+ break;
+ }
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+
+
+void Writer::Impl_addStraightLine( BitStream& rBits, Point& rLastPoint,
+ const double P2x, const double P2y )
+{
+ Point aPoint( FRound(P2x), FRound(P2y) );
+
+ Impl_addStraightEdgeRecord( rBits, _Int16(aPoint.X() - rLastPoint.X()),_Int16(aPoint.Y() - rLastPoint.Y()));
+ rLastPoint = aPoint;
+
+}
+
+// -----------------------------------------------------------------------------
+
+void Writer::Impl_addQuadBezier( BitStream& rBits, Point& rLastPoint,
+ const double P2x, const double P2y,
+ const double P3x, const double P3y )
+{
+
+ Point aControlPoint( FRound(P2x), FRound(P2y) );
+ Point aAnchorPoint( FRound(P3x), FRound(P3y) );
+
+ Impl_addCurvedEdgeRecord( rBits,
+ _Int16(aControlPoint.X() - rLastPoint.X()),_Int16(aControlPoint.Y() - rLastPoint.Y()),
+ _Int16(aAnchorPoint.X() - aControlPoint.X()),_Int16(aAnchorPoint.Y() - aControlPoint.Y()) );
+ rLastPoint = aAnchorPoint;
+}
+
+// -----------------------------------------------------------------------------
+
+/* Approximate given cubic bezier curve by quadratic bezier segments */
+void Writer::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 )
+{
+ // Check for degenerate case, where the given cubic bezier curve
+ // is already quadratic: P4 == 3P3 - 3P2 + P1
+ if( P4x == 3.0*P3x - 3.0*P2x + P1x &&
+ P4y == 3.0*P3y - 3.0*P2y + P1y )
+ {
+ Impl_addQuadBezier( rBits, rLastPoint,
+ 3.0/2.0*P2x - 1.0/2.0*P1x, 3.0/2.0*P2y - 1.0/2.0*P1y,
+ P4x, P4y);
+ }
+ else
+ {
+ // Create quadratic segment for given cubic:
+ // Start and end point must coincide, determine quadratic control
+ // point in such a way that it lies on the intersection of the
+ // tangents at start and end point, resp. Thus, both cubic and
+ // quadratic curve segments will match in 0th and 1st derivative
+ // at the start and end points
+
+ // Intersection of P2P1 and P4P3
+ // (P2y-P4y)(P3x-P4x)-(P2x-P4x)(P3y-P4y)
+ // lambda = -------------------------------------
+ // (P1x-P2x)(P3y-P4y)-(P1y-P2y)(P3x-P4x)
+ //
+ // Intersection point IP is now
+ // IP = P2 + lambda(P1-P2)
+ //
+ const double nominator( (P2y-P4y)*(P3x-P4x) - (P2x-P4x)*(P3y-P4y) );
+ const double denominator( (P1x-P2x)*(P3y-P4y) - (P1y-P2y)*(P3x-P4x) );
+ const double lambda( nominator / denominator );
+
+ const double IPx( P2x + lambda*( P1x - P2x) );
+ const double IPy( P2y + lambda*( P1y - P2y) );
+
+ // Introduce some alias names: quadratic start point is P1, end
+ // point is P4, control point is IP
+ const double QP1x( P1x );
+ const double QP1y( P1y );
+ const double QP2x( IPx );
+ const double QP2y( IPy );
+ const double QP3x( P4x );
+ const double QP3y( P4y );
+
+ // Adapted bezier flatness test (lecture notes from R. Schaback,
+ // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
+ //
+ // ||C(t) - Q(t)|| <= max ||c_j - q_j||
+ // 0<=j<=n
+ //
+ // In this case, we don't need the distance from the cubic bezier
+ // to a straight line, but to a quadratic bezier. The c_j's are
+ // the cubic bezier's bernstein coefficients, the q_j's the
+ // quadratic bezier's. We have the c_j's given, the q_j's can be
+ // calculated from QPi like this (sorry, mixed index notation, we
+ // use [1,n], formulas use [0,n-1]):
+ //
+ // q_0 = QP1 = P1
+ // q_1 = 1/3 QP1 + 2/3 QP2
+ // q_2 = 2/3 QP2 + 1/3 QP3
+ // q_3 = QP3 = P4
+ //
+ // We can drop case 0 and 3, since there the curves coincide
+ // (distance is zero)
+
+ // calculate argument of max for j=1 and j=2
+ const double fJ1x( P2x - 1.0/3.0*QP1x - 2.0/3.0*QP2x );
+ const double fJ1y( P2y - 1.0/3.0*QP1y - 2.0/3.0*QP2y );
+ const double fJ2x( P3x - 2.0/3.0*QP2x - 1.0/3.0*QP3x );
+ const double fJ2y( P3y - 2.0/3.0*QP2y - 1.0/3.0*QP3y );
+
+ // stop if distance from cubic curve is guaranteed to be bounded by d
+ // Should denominator be 0: then P1P2 and P3P4 are parallel (P1P2^T R[90,P3P4] = 0.0),
+ // meaning that either we have a straight line or an inflexion point (see else block below)
+ if( 0.0 != denominator &&
+ ::std::max( fJ1x*fJ1x + fJ1y*fJ1y,
+ fJ2x*fJ2x + fJ2y*fJ2y) < d2 )
+ {
+ // requested resolution reached.
+ // Add end points to output file.
+ // order is preserved, since this is so to say depth first traversal.
+ Impl_addQuadBezier( rBits, rLastPoint,
+ QP2x, QP2y,
+ QP3x, QP3y);
+ }
+ else
+ {
+ // Maybe subdivide further
+
+ // This is for robustness reasons, since the line intersection
+ // method below gets instable if the curve gets closer to a
+ // straight line. If the given cubic bezier does not deviate by
+ // more than d/4 from a straight line, either:
+ // - take the line (that's what we do here)
+ // - express the line by a quadratic bezier
+
+ // Perform bezier flatness test (lecture notes from R. Schaback,
+ // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
+ //
+ // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
+ // 0<=j<=n
+ //
+ // What is calculated here is an upper bound to the distance from
+ // a line through b_0 and b_3 (P1 and P4 in our notation) and the
+ // curve. We can drop 0 and n from the running indices, since the
+ // argument of max becomes zero for those cases.
+ const double fJ1x2( P2x - P1x - 1.0/3.0*(P4x - P1x) );
+ const double fJ1y2( P2y - P1y - 1.0/3.0*(P4y - P1y) );
+ const double fJ2x2( P3x - P1x - 2.0/3.0*(P4x - P1x) );
+ const double fJ2y2( P3y - P1y - 2.0/3.0*(P4y - P1y) );
+
+ // stop if distance from line is guaranteed to be bounded by d/4
+ if( ::std::max( fJ1x2*fJ1x2 + fJ1y2*fJ1y2,
+ fJ2x2*fJ2x2 + fJ2y2*fJ2y2) < d2/16.0 )
+ {
+ // do not subdivide further, add straight line instead
+ Impl_addStraightLine( rBits, rLastPoint, P4x, P4y);
+ }
+ else
+ {
+ // deCasteljau bezier arc, split at t=0.5
+ // Foley/vanDam, p. 508
+ const double L1x( P1x ), L1y( P1y );
+ const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 );
+ const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 );
+ const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 );
+ const double R4x( P4x ), R4y( P4y );
+ const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 );
+ const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 );
+ const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 );
+ const double L4x( R1x ), L4y( R1y );
+
+ // subdivide further
+ Impl_quadBezierApprox(rBits, rLastPoint, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y);
+ Impl_quadBezierApprox(rBits, rLastPoint, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y);
+ }
+ }
+ }
+}
+
+Reference < XBreakIterator > Writer::Impl_GetBreakIterator()
+{
+ if ( !mxBreakIterator.is() )
+ {
+ Reference< XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
+ mxBreakIterator.set( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
+ }
+ return mxBreakIterator;
+}