| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 ); |
| |
| sal_uInt16 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, (sal_uInt16)nLen, sal_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( (sal_uInt16) 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, (sal_uInt16) 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 ) |
| { |
| sal_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<sal_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<sal_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( sal_uLong i = 0, nCount = rMtf.GetActionCount(); i < nCount; i++ ) |
| { |
| const MetaAction* pAction = rMtf.GetAction( i ); |
| const sal_uInt16 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( sal_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 sal_uInt8* 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; |
| } |