1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: swfwriter1.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_filter.hxx"
34 #include <com/sun/star/i18n/ScriptType.hpp>
35 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
37 #include <comphelper/processfactory.hxx>
38 #include "swfwriter.hxx"
39 #include <vcl/metaact.hxx>
40 #include <vcl/gdimtf.hxx>
41 #include <vcl/bmpacc.hxx>
42 #include <vcl/virdev.hxx>
43 #include <vcl/metric.hxx>
46 #include <svtools/filter.hxx>
47 #include <vcl/graphictools.hxx>
53 #include <external/zlib/zlib.h>
56 #include <vcl/salbtype.hxx>
58 using namespace ::swf
;
59 using namespace ::std
;
60 using namespace ::rtl
;
61 using namespace ::com::sun::star::i18n
;
62 using namespace ::com::sun::star::uno
;
63 using namespace ::com::sun::star::lang
;
64 using namespace ::com::sun::star::io
;
65 using namespace ::com::sun::star::beans
;
67 extern sal_uInt16
getMaxBitsUnsigned( sal_uInt32 nValue
);
68 extern sal_uInt16
getMaxBitsSigned( sal_Int32 nValue
);
70 static MapMode
aTWIPSMode( MAP_TWIP
);
71 static MapMode
a100thmmMode( MAP_100TH_MM
);
73 // -----------------------------------------------------------------------------
75 Point
Writer::map( const Point
& rPoint
) const
77 const MapMode
& aSourceMapMode
= mpVDev
->GetMapMode();
79 Point retPoint
= mpVDev
->LogicToLogic( rPoint
, &aSourceMapMode
, &aTWIPSMode
);
81 // AS: Produces a 'possible loss of data' warning that we can't fix without
82 // hurting code readability.
83 retPoint
.X() = (long)( retPoint
.X() * mnDocXScale
);
84 retPoint
.Y() = (long)( retPoint
.Y() * mnDocYScale
);
89 // -----------------------------------------------------------------------------
91 Size
Writer::map( const Size
& rSize
) const
93 const MapMode
& aSourceMapMode
= mpVDev
->GetMapMode();
95 Size retSize
= mpVDev
->LogicToLogic( rSize
, &aSourceMapMode
, &aTWIPSMode
);
97 // AS: Produces a 'possible loss of data' warning that we can't fix without
98 // hurting code readability.
99 retSize
.Width() = (long)( retSize
.Width() * mnDocXScale
);
100 retSize
.Height() = (long)( retSize
.Height() * mnDocYScale
);
105 // -----------------------------------------------------------------------------
107 void Writer::map( PolyPolygon
& rPolyPolygon
) const
109 const sal_uInt16 nPolyCount
= rPolyPolygon
.Count();
112 sal_uInt16 nPoly
, nPoint
, nPointCount
;
113 for( nPoly
= 0; nPoly
< nPolyCount
; nPoly
++ )
115 Polygon
& rPoly
= rPolyPolygon
[nPoly
];
116 nPointCount
= rPoly
.GetSize();
118 for( nPoint
= 0; nPoint
< nPointCount
; nPoint
++ )
120 rPoly
[nPoint
] = map( rPoly
[nPoint
] );
126 // -----------------------------------------------------------------------------
128 sal_Int32
Writer::mapRelative( sal_Int32 n100thMM
) const
130 MapMode
aSourceMapMode( mpVDev
->GetMapMode() );
131 aSourceMapMode
.SetOrigin( Point() );
133 sal_Int32 nTwips
= mpVDev
->LogicToLogic( Point( n100thMM
, n100thMM
), &aSourceMapMode
, &aTWIPSMode
).X();
137 // -----------------------------------------------------------------------------
141 void Writer::Impl_addPolygon( BitStream
& rBits
, const Polygon
& rPoly
, sal_Bool bFilled
)
143 Point
aLastPoint( rPoly
[0] );
145 Impl_addShapeRecordChange( rBits
, _Int16(aLastPoint
.X()),_Int16(aLastPoint
.Y()), bFilled
);
147 USHORT i
= 0, nSize
= rPoly
.GetSize();
152 while( ( i
+ 1 ) < nSize
)
154 if( ( i
+ 3 ) < nSize
)
156 PolyFlags
P1( rPoly
.GetFlags( i
) );
157 PolyFlags
P4( rPoly
.GetFlags( i
+ 3 ) );
159 if( ( POLY_NORMAL
== P1
|| POLY_SMOOTH
== P1
|| POLY_SYMMTR
== P1
) &&
160 ( POLY_CONTROL
== rPoly
.GetFlags( i
+ 1 ) ) &&
161 ( POLY_CONTROL
== rPoly
.GetFlags( i
+ 2 ) ) &&
162 ( POLY_NORMAL
== P4
|| POLY_SMOOTH
== P4
|| POLY_SYMMTR
== P4
) )
164 Impl_quadBezierApprox( rBits
, aLastPoint
, d
*d
,
165 rPoly
.GetPoint( i
).X(), rPoly
.GetPoint( i
).Y(),
166 rPoly
.GetPoint( i
+1 ).X(), rPoly
.GetPoint( i
+1 ).Y(),
167 rPoly
.GetPoint( i
+2 ).X(), rPoly
.GetPoint( i
+2 ).Y(),
168 rPoly
.GetPoint( i
+3 ).X(), rPoly
.GetPoint( i
+3 ).Y() );
176 const Point
aPolyPoint( rPoly
[ i
] );
177 if( aPolyPoint
!= aLastPoint
)
179 Impl_addStraightEdgeRecord( rBits
, _Int16(aPolyPoint
.X() - aLastPoint
.X()),_Int16(aPolyPoint
.Y() - aLastPoint
.Y()));
180 aLastPoint
= aPolyPoint
;
184 if( bFilled
&& (rPoly
[0] != rPoly
[nSize
-1]))
186 const Point
aPolyPoint( rPoly
[ 0 ] );
187 if( aPolyPoint
!= aLastPoint
)
189 Impl_addStraightEdgeRecord( rBits
, _Int16(aPolyPoint
.X() - aLastPoint
.X()),_Int16(aPolyPoint
.Y() - aLastPoint
.Y()));
194 // -----------------------------------------------------------------------------
196 /** exports a style change record with a move to (x,y) and depending on bFilled a line style 1 or fill style 1
198 void Writer::Impl_addShapeRecordChange( BitStream
& rBits
, sal_Int16 dx
, sal_Int16 dy
, sal_Bool bFilled
)
200 rBits
.writeUB( 0, 1 ); // TypeFlag
201 rBits
.writeUB( 0, 1 ); // StateNewStyles
202 rBits
.writeUB( !bFilled
, 1 ); // StateLineStyle
203 rBits
.writeUB( 0, 1 ); // StateFillStyle0
204 rBits
.writeUB( bFilled
, 1 ); // StateFillStyle1
205 rBits
.writeUB( 1, 1 ); // StateMoveTo
207 sal_uInt16 nMoveBits
= max( getMaxBitsSigned( dx
), getMaxBitsSigned( dy
) );
209 rBits
.writeUB( nMoveBits
, 5 ); // Number of bits per value
210 // TODO: Optimize horizontal and vertical lines
211 rBits
.writeSB( dx
, nMoveBits
); // DeltaX
212 rBits
.writeSB( dy
, nMoveBits
); // DeltaY
214 rBits
.writeUB( 1, 1 ); // set FillStyle1 or LineStyle to 1
217 // -----------------------------------------------------------------------------
219 /** exports a straight edge record
221 void Writer::Impl_addStraightEdgeRecord( BitStream
& rBits
, sal_Int16 dx
, sal_Int16 dy
)
223 rBits
.writeUB( 1, 1 ); // TypeFlag
224 rBits
.writeUB( 1, 1 ); // StraightFlag
226 sal_uInt16 nBits
= max( getMaxBitsSigned( dx
), getMaxBitsSigned( dy
) );
228 rBits
.writeUB( nBits
- 2, 4 ); // Number of bits per value
230 if( (dx
!= 0) && (dy
!= 0) )
232 rBits
.writeUB( 1, 1 ); // GeneralLineFlag
233 rBits
.writeSB( dx
, nBits
); // DeltaX
234 rBits
.writeSB( dy
, nBits
); // DeltaY
238 rBits
.writeUB( 0, 1 );
239 rBits
.writeUB( ( dx
== 0 ), 1 );
242 rBits
.writeSB( dy
, nBits
); // DeltaY
246 rBits
.writeSB( dx
, nBits
); // DeltaX
251 // -----------------------------------------------------------------------------
253 /** exports a curved edge record
255 void Writer::Impl_addCurvedEdgeRecord( BitStream
& rBits
, sal_Int16 control_dx
, sal_Int16 control_dy
, sal_Int16 anchor_dx
, sal_Int16 anchor_dy
)
257 rBits
.writeUB( 1, 1 ); // TypeFlag
258 rBits
.writeUB( 0, 1 ); // CurvedFlag
260 sal_uInt8 nBits
= static_cast<sal_uInt8
>(
261 max( getMaxBitsSigned( control_dx
),
262 max( getMaxBitsSigned( control_dy
),
263 max( getMaxBitsSigned( anchor_dx
),
264 max( getMaxBitsSigned( anchor_dy
), (sal_uInt16
)3 ) ) ) ) );
266 rBits
.writeUB( nBits
- 2, 4 ); // Number of bits per value
268 rBits
.writeSB( control_dx
, nBits
); // DeltaX
269 rBits
.writeSB( control_dy
, nBits
); // DeltaY
270 rBits
.writeSB( anchor_dx
, nBits
); // DeltaX
271 rBits
.writeSB( anchor_dy
, nBits
); // DeltaY
274 // -----------------------------------------------------------------------------
276 /** exports a end shape record
278 void Writer::Impl_addEndShapeRecord( BitStream
& rBits
)
280 rBits
.writeUB( 0, 6 );
283 // -----------------------------------------------------------------------------
285 void Writer::Impl_writePolygon( const Polygon
& rPoly
, sal_Bool bFilled
)
287 PolyPolygon
aPolyPoly( rPoly
);
288 Impl_writePolyPolygon( aPolyPoly
, bFilled
);
291 // -----------------------------------------------------------------------------
293 void Writer::Impl_writePolygon( const Polygon
& rPoly
, sal_Bool bFilled
, const Color
& rFillColor
, const Color
& rLineColor
)
295 PolyPolygon
aPolyPoly( rPoly
);
296 Impl_writePolyPolygon( aPolyPoly
, bFilled
, rFillColor
, rLineColor
);
299 // -----------------------------------------------------------------------------
301 void Writer::Impl_writePolyPolygon( const PolyPolygon
& rPolyPoly
, sal_Bool bFilled
, sal_uInt8 nTransparence
/* = 0 */ )
303 Color
aLineColor( mpVDev
->GetLineColor() );
304 if( 0 == aLineColor
.GetTransparency() )
305 aLineColor
.SetTransparency( nTransparence
);
306 Color
aFillColor( mpVDev
->GetFillColor() );
307 if( 0 == aFillColor
.GetTransparency() )
308 aFillColor
.SetTransparency( nTransparence
);
309 Impl_writePolyPolygon(rPolyPoly
, bFilled
, aFillColor
, aLineColor
);
312 // -----------------------------------------------------------------------------
314 void Writer::Impl_writePolyPolygon( const PolyPolygon
& rPolyPoly
, sal_Bool bFilled
, const Color
& rFillColor
, const Color
& rLineColor
)
316 PolyPolygon
aPolyPoly( rPolyPoly
);
318 if( aPolyPoly
.Count() )
322 if( mpClipPolyPolygon
)
323 rPolyPoly
.GetIntersection( *mpClipPolyPolygon
, aPolyPoly
);
328 Color
aFillColor( rFillColor
);
329 if( 0 != mnGlobalTransparency
)
330 aFillColor
.SetTransparency( mnGlobalTransparency
);
332 FillStyle
aStyle( aFillColor
);
333 nID
= defineShape( aPolyPoly
, aStyle
);
337 Color
aLineColor( rLineColor
);
338 if( 0 != mnGlobalTransparency
)
339 aLineColor
.SetTransparency( mnGlobalTransparency
);
341 nID
= defineShape( aPolyPoly
, 1, aLineColor
);
343 maShapeIds
.push_back( nID
);
347 // -----------------------------------------------------------------------------
349 /** a gradient is a transition from one color to another, rendered inside a given polypolygon */
350 void Writer::Impl_writeGradientEx( const PolyPolygon
& rPolyPoly
, const Gradient
& rGradient
)
352 if( rPolyPoly
.Count() )
354 PolyPolygon
aPolyPolygon( rPolyPoly
);
357 if( (rGradient
.GetStyle() == GRADIENT_LINEAR
&& rGradient
.GetAngle() == 900) || (rGradient
.GetStyle() == GRADIENT_RADIAL
) )
359 const Rectangle
aBoundRect( aPolyPolygon
.GetBoundRect() );
361 FillStyle
aFillStyle( aBoundRect
, rGradient
);
363 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, aFillStyle
);
364 maShapeIds
.push_back( nShapeId
);
368 setClipping( &aPolyPolygon
);
370 // render the gradient filling to simple polygons
373 mpVDev
->AddGradientActions( aPolyPolygon
.GetBoundRect(), rGradient
, aTmpMtf
);
374 Impl_writeActions( aTmpMtf
);
382 // -----------------------------------------------------------------------------
384 void Writer::setClipping( const PolyPolygon
* pClipPolyPolygon
)
386 mpClipPolyPolygon
= pClipPolyPolygon
;
389 // -----------------------------------------------------------------------------
391 // AS: Just comparing fonts straight up is too literal. There are some
392 // differences in font that actually require different glyphs to be defined,
393 // and some that don't. This function is meant to capture all the differences
394 // that we care about.
395 bool compare_fonts_for_me(const Font
& rFont1
, const Font
& rFont2
)
397 return rFont1
.GetName() == rFont2
.GetName() &&
398 rFont1
.GetWeight() == rFont2
.GetWeight() &&
399 rFont1
.GetItalic() == rFont2
.GetItalic() &&
400 rFont1
.IsOutline() == rFont2
.IsOutline() &&
401 rFont1
.IsShadow() == rFont2
.IsShadow() &&
402 rFont1
.GetRelief() == rFont2
.GetRelief();
405 // -----------------------------------------------------------------------------
407 FlashFont
& Writer::Impl_getFont( const Font
& rFont
)
409 FontMap::iterator
aIter( maFonts
.begin() );
410 const FontMap::iterator
aEnd( maFonts
.end() );
412 while( aIter
!= aEnd
)
414 const Font tempFont
= (*aIter
)->getFont();
415 if( compare_fonts_for_me(tempFont
, rFont
) )
423 FlashFont
* pFont
= new FlashFont( rFont
, createID() );
424 maFonts
.push_back( pFont
);
428 // -----------------------------------------------------------------------------
430 void Writer::Impl_writeText( const Point
& rPos
, const String
& rText
, const sal_Int32
* pDXArray
, long nWidth
)
432 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
434 bool bTextSpecial
= aMetric
.IsShadow() || aMetric
.IsOutline() || (aMetric
.GetRelief() != RELIEF_NONE
);
438 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
442 if( aMetric
.GetRelief() != RELIEF_NONE
)
444 Color
aReliefColor( COL_LIGHTGRAY
);
445 Color
aTextColor( mpVDev
->GetTextColor() );
447 if ( aTextColor
.GetColor() == COL_BLACK
)
448 aTextColor
= Color( COL_WHITE
);
450 if ( aTextColor
.GetColor() == COL_WHITE
)
451 aReliefColor
= Color( COL_BLACK
);
455 Point
aOffset( 6,6 );
457 if ( aMetric
.GetRelief() == RELIEF_ENGRAVED
)
466 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, aReliefColor
);
467 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, aTextColor
);
471 if( aMetric
.IsShadow() )
473 long nOff
= 1 + ((aMetric
.GetLineHeight()-24)/24);
474 if ( aMetric
.IsOutline() )
477 Color
aTextColor( mpVDev
->GetTextColor() );
478 Color aShadowColor
= Color( COL_BLACK
);
480 if ( (aTextColor
.GetColor() == COL_BLACK
) || (aTextColor
.GetLuminance() < 8) )
481 aShadowColor
= Color( COL_LIGHTGRAY
);
484 aPos
+= Point( nOff
, nOff
);
485 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, aShadowColor
);
487 if( !aMetric
.IsOutline() )
489 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, aTextColor
);
493 if( aMetric
.IsOutline() )
495 Point aPos
= rPos
+ Point( -6, -6 );
496 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
497 aPos
= rPos
+ Point(+6,+6);
498 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
499 aPos
= rPos
+ Point(-6,+0);
500 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
501 aPos
= rPos
+ Point(-6,+6);
502 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
503 aPos
= rPos
+ Point(+0,+6);
504 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
505 aPos
= rPos
+ Point(+0,-6);
506 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
507 aPos
= rPos
+ Point(+6,-1);
508 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
509 aPos
= rPos
+ Point(+6,+0);
510 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
512 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, Color( COL_WHITE
) );
518 void Writer::Impl_writeText( const Point
& rPos
, const String
& rText
, const sal_Int32
* pDXArray
, long nWidth
, Color aTextColor
)
520 sal_uInt32 nLen
= rText
.Len();
525 const bool bRTL
= (mpVDev
->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL
) != 0;
527 sal_Int16 nScriptType
= ScriptType::LATIN
;
528 Reference
< XBreakIterator
> xBI( Impl_GetBreakIterator() );
531 const OUString
oText( rText
);
532 nScriptType
= xBI
->getScriptType( oText
, 0 );
535 // if the text is either right to left or complex or asian, we
536 // ask the output device for a polygon representation.
537 // On complex and asian text, each unicode character can have
538 // different glyph representation, based on context. Also positioning
539 // is not trivial so we let the output device do it for us.
540 if( bRTL
|| (nScriptType
!= ScriptType::LATIN
) )
542 // todo: optimize me as this will generate a huge amount of duplicate polygons
543 PolyPolygon aPolyPoygon
;
544 mpVDev
->GetTextOutline( aPolyPoygon
, rText
, 0, 0, (USHORT
)nLen
, TRUE
, nWidth
, pDXArray
);
545 aPolyPoygon
.Translate( rPos
);
546 Impl_writePolyPolygon( aPolyPoygon
, sal_True
, aTextColor
, aTextColor
);
551 sal_Int32
* pOwnArray
;
558 aNormSize
= Size( mpVDev
->GetTextWidth( rText
), 0 );
559 pDX
= (sal_Int32
*) pDXArray
;
563 pOwnArray
= new sal_Int32
[ nLen
];
564 aNormSize
= Size( mpVDev
->GetTextArray( rText
, pOwnArray
), 0 );
570 aNormSize
.Width() = pDX
[ nLen
- 2 ] + mpVDev
->GetTextWidth( rText
.GetChar( (USHORT
) nLen
- 1 ) );
572 if( nWidth
&& aNormSize
.Width() && ( nWidth
!= aNormSize
.Width() ) )
574 const double fFactor
= (double) nWidth
/ aNormSize
.Width();
577 for( i
= 0; i
< ( nLen
- 1 ); i
++ )
578 pDX
[ i
] = FRound( pDX
[ i
] * fFactor
);
582 Font
aOldFont( mpVDev
->GetFont() );
583 Point
aBaseLinePos( rPos
);
585 Font
aFont(aOldFont
);
586 short nOrientation
= aFont
.GetOrientation();
587 aFont
.SetOrientation( 0 );
588 aFont
.SetUnderline(UNDERLINE_NONE
);
589 aFont
.SetStrikeout(STRIKEOUT_NONE
);
590 mpVDev
->SetFont( aFont
);
592 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
594 FlashFont
& rFlashFont
= Impl_getFont( aFont
);
596 // always adjust text position to match baseline alignment
597 switch( aOldFont
.GetAlign() )
600 aBaseLinePos
.Y() += aMetric
.GetAscent();
603 case( ALIGN_BOTTOM
):
604 aBaseLinePos
.Y() -= aMetric
.GetDescent();
611 // get mapped text position
612 const Point
aPt( map( aBaseLinePos
) );
614 // write text element
616 /* test code to create a bound rect, not realy working for rotated text
617 Size aTextSize( map( Size( mpVDev->GetTextWidth( rText ), mpVDev->GetTextHeight() ) ) );
618 Point aMetricPoint( map( Point( aMetric.GetLeading(), aMetric.GetAscent() ) ) );
620 Point aTmpPoint( map( Point( - aMetric.GetLeading(), - aMetric.GetAscent() ) ) ); ;
621 Rectangle aTmpRectangle(aTmpPoint, aTextSize );
622 Polygon aPoly( aTmpRectangle );
624 aPoly.Rotate( aTmpPoint, (USHORT) nOrientation );
626 Rectangle aTextBoundRect( aPoly.GetBoundRect() );
628 aPoly.Move( aPt.X(), aPt.Y() - map( Size( 0, aMetric.GetDescent() ) ).Height() );
632 #if 0 // makes the calculated bound rect visible for debuging
634 Polygon
aTmpPoly( aPoly
);
635 sal_uInt16 nID
= FlashGeometryExporter::writePolygonShape( aMovieStream
, aTmpPoly
, false, Color(COL_MAGENTA
), Color(COL_MAGENTA
), mpClipPolyPolygon
);
636 ImplPlaceObject( nID
);
640 // CL: This is still a hack until we figure out how to calculate a correct bound rect
642 Rectangle
textBounds( 0, 0, static_cast<long>(mnDocWidth
*mnDocXScale
), static_cast<long>(mnDocHeight
*mnDocYScale
) );
644 ::basegfx::B2DHomMatrix m
; // #i73264#
648 // scale width if we have a stretched text
649 if( 0 != aFont
.GetSize().Width() )
651 Font
aTmpFont( aFont
);
652 aTmpFont
.SetWidth(0);
653 mpVDev
->SetFont( aTmpFont
);
655 const FontMetric
aMetric2( mpVDev
->GetFontMetric() );
656 mpVDev
->SetFont( aFont
);
658 const long n1
= aFont
.GetSize().Width();
659 const long n2
= aMetric2
.GetSize().Width();
660 scale
= (double)n1
/ (double)n2
;
663 m
.rotate( static_cast<double>(nOrientation
) * F_PI1800
);
664 m
.translate( double(aPt
.X() / scale
), double(aPt
.Y()) );
665 m
.scale( scale
, scale
);
667 sal_Int16 nHeight
= _Int16( map( Size( 0, aFont
.GetHeight() ) ).Height() );
669 startTag( TAG_DEFINETEXT
);
671 sal_uInt16 nTextId
= createID();
673 mpTag
->addUI16( nTextId
);
674 mpTag
->addRect( textBounds
);
675 mpTag
->addMatrix( m
);
677 sal_uInt8 nGlyphBits
= 16;
678 sal_uInt8 nAdvanceBits
= 16;
680 mpTag
->addUI8( nGlyphBits
);
681 mpTag
->addUI8( nAdvanceBits
);
683 // text style change record
684 mpTag
->addUI8( 0x8c );
685 mpTag
->addUI16( rFlashFont
.getID() );
686 mpTag
->addRGB( aTextColor
);
687 mpTag
->addUI16( _uInt16( nHeight
) );
689 DBG_ASSERT( nLen
<= 127, "TODO: handle text with more than 127 characters" );
692 mpTag
->addUI8( (sal_uInt8
) nLen
);
696 sal_Int32 nLastDX
= 0;
699 for( i
= 0; i
< nLen
; i
++ )
703 nAdvance
= pDX
[i
] - nLastDX
;
711 aBits
.writeUB( rFlashFont
.getGlyph(rText
.GetChar(_uInt16(i
)),mpVDev
), nGlyphBits
);
712 aBits
.writeSB( _Int16(map( Size( (long)( nAdvance
/ scale
), 0 ) ).Width() ), nAdvanceBits
);
715 mpTag
->addBits( aBits
);
720 maShapeIds
.push_back( nTextId
);
722 // AS: Write strikeout and underline, if neccessary. This code was originally taken from the SVG
723 // export facility, although the positioning had to be tweaked a little. I can't explain the
724 // numbers, but the flash lines up very well with the original OOo document. All of this should
725 // probably be converted to polygons as part of the meta file, though, as we don't handle any
726 // fancy lines (like dashes).
727 if( ( aOldFont
.GetStrikeout() != STRIKEOUT_NONE
) || ( aOldFont
.GetUnderline() != UNDERLINE_NONE
) )
730 const long nLineHeight
= Max( (long) FRound( aMetric
.GetLineHeight() * 0.05 ), (long) 1 );
732 if( aOldFont
.GetStrikeout() != STRIKEOUT_NONE
)
734 aPoly
[ 0 ].X() = aBaseLinePos
.X();
735 aPoly
[ 0 ].Y() = aBaseLinePos
.Y() - FRound( aMetric
.GetAscent() * 0.26 ) - nLineHeight
;
736 aPoly
[ 1 ].X() = aPoly
[ 0 ].X() + aNormSize
.Width() - 1;
737 aPoly
[ 1 ].Y() = aPoly
[ 0 ].Y();
738 aPoly
[ 2 ].X() = aPoly
[ 1 ].X();
739 aPoly
[ 2 ].Y() = aPoly
[ 1 ].Y() + nLineHeight
- 1;
740 aPoly
[ 3 ].X() = aPoly
[ 0 ].X();
741 aPoly
[ 3 ].Y() = aPoly
[ 2 ].Y();
743 Impl_writePolygon( aPoly
, sal_True
, aTextColor
, aTextColor
);
746 // AS: The factor of 1.5 on the nLineHeight is a magic number. I'm not sure why it works,
747 // but it looks good to me.
748 if( aOldFont
.GetUnderline() != UNDERLINE_NONE
)
750 aPoly
[ 0 ].X() = aBaseLinePos
.X();
751 aPoly
[ 0 ].Y() = static_cast<long>(aBaseLinePos
.Y() + 1.5*nLineHeight
);
752 aPoly
[ 1 ].X() = aPoly
[ 0 ].X() + aNormSize
.Width() - 1;
753 aPoly
[ 1 ].Y() = aPoly
[ 0 ].Y();
754 aPoly
[ 2 ].X() = aPoly
[ 1 ].X();
755 aPoly
[ 2 ].Y() = aPoly
[ 1 ].Y() + nLineHeight
- 1;
756 aPoly
[ 3 ].X() = aPoly
[ 0 ].X();
757 aPoly
[ 3 ].Y() = aPoly
[ 2 ].Y();
759 Impl_writePolygon( aPoly
, sal_True
, aTextColor
, aTextColor
);
763 mpVDev
->SetFont( aOldFont
);
768 // -----------------------------------------------------------------------------
769 // AS: Because JPEGs require the alpha channel provided seperately (JPEG does not
770 // natively support alpha channel, but SWF lets you provide it seperately), we
771 // extract the alpha channel into a seperate array here.
772 void getBitmapData( const BitmapEx
& aBmpEx
, sal_uInt8
*& tgadata
, sal_uInt8
*& tgaAlphadata
, sal_uInt32
& nWidth
, sal_uInt32
& nHeight
)
774 if( !aBmpEx
.IsEmpty() )
776 Bitmap
aBmp( aBmpEx
.GetBitmap() );
777 BitmapReadAccess
* pRAcc
= aBmp
.AcquireReadAccess();
782 nWidth
= pRAcc
->Width();
783 nHeight
= pRAcc
->Height();
784 tgadata
= new sal_uInt8
[nWidth
*nHeight
*4];
785 tgaAlphadata
= new sal_uInt8
[nWidth
*nHeight
];
786 sal_uInt8
* p
= tgadata
, *pAlpha
= tgaAlphadata
;
789 if( aBmpEx
.IsAlpha() )
790 aAlpha
= aBmpEx
.GetAlpha();
791 else if( aBmpEx
.IsTransparent() )
792 aAlpha
= aBmpEx
.GetMask();
795 sal_uInt8 cAlphaVal
= 0;
796 aAlpha
= AlphaMask( aBmp
.GetSizePixel(), &cAlphaVal
);
799 BitmapReadAccess
* pAAcc
= aAlpha
.AcquireReadAccess();
803 for( sal_uInt32 nY
= 0; nY
< nHeight
; nY
++ )
805 for( sal_uInt32 nX
= 0; nX
< nWidth
; nX
++ )
807 const sal_uInt8 nAlpha
= pAAcc
->GetPixel( nY
, nX
).GetIndex();
808 const BitmapColor
aPixelColor( pRAcc
->GetColor( nY
, nX
) );
820 *p
++ = aPixelColor
.GetRed();
821 *p
++ = aPixelColor
.GetGreen();
822 *p
++ = aPixelColor
.GetBlue();
824 *pAlpha
++ = 0xff - nAlpha
;
828 aAlpha
.ReleaseAccess( pAAcc
);
831 aBmp
.ReleaseAccess( pRAcc
);
836 // -----------------------------------------------------------------------------
837 sal_uInt16
Writer::defineBitmap( const BitmapEx
&bmpSource
, sal_Int32 nJPEGQualityLevel
)
839 ULONG bmpChecksum
= bmpSource
.GetChecksum();
841 ChecksumCache::iterator it
= mBitmapCache
.find(bmpChecksum
);
843 // AS: We already exported this bitmap, so just return its ID.
844 if (mBitmapCache
.end() != it
)
847 sal_uInt16 nBitmapId
= createID();
848 mBitmapCache
[bmpChecksum
] = nBitmapId
;
850 // AS: OK, we have a good image, so now we decide whether or not to JPEG it or
851 // or Lossless compress it.
853 //Figure out lossless size
854 sal_uInt8
*pImageData
, *pAlphaData
;
855 sal_uInt32 width
, height
;
857 getBitmapData( bmpSource
, pImageData
, pAlphaData
, width
, height
);
858 sal_uInt32 raw_size
= width
* height
* 4;
859 uLongf compressed_size
= raw_size
+ (sal_uInt32
)(raw_size
/100) + 12;
860 sal_uInt8
*pCompressed
= new sal_uInt8
[ compressed_size
];
863 if(compress2(pCompressed
, &compressed_size
, pImageData
, raw_size
, Z_BEST_COMPRESSION
) != Z_OK
)
865 DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
868 compress2(pCompressed
, &compressed_size
, pImageData
, raw_size
, Z_BEST_COMPRESSION
);
871 // AS: SWF files let you provide an Alpha mask for JPEG images, but we have
872 // to ZLIB compress the alpha channel seperately.
873 uLong alpha_compressed_size
= 0;
874 sal_uInt8
*pAlphaCompressed
= NULL
;
875 if (bmpSource
.IsAlpha() || bmpSource
.IsTransparent())
877 alpha_compressed_size
= uLongf(width
* height
+ (sal_uInt32
)(raw_size
/100) + 12);
878 pAlphaCompressed
= new sal_uInt8
[ compressed_size
];
881 if(compress2(pAlphaCompressed
, &alpha_compressed_size
, pAlphaData
, width
* height
, Z_BEST_COMPRESSION
) != Z_OK
)
883 DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
886 compress2(pAlphaCompressed
, &alpha_compressed_size
, pAlphaData
, width
* height
, Z_BEST_COMPRESSION
);
890 //Figure out JPEG size
891 const sal_uInt8
* pJpgData
= NULL
;;
892 sal_uInt32 nJpgDataLength
= 0xffffffff;
894 Graphic
aGraphic( bmpSource
);
895 SvMemoryStream
aDstStm( 65535, 65535 );
897 GraphicFilter aFilter
;
899 Sequence
< PropertyValue
> aFilterData(nJPEGQualityLevel
!= -1);
900 if( nJPEGQualityLevel
!= -1 )
902 aFilterData
[0].Name
= OUString( RTL_CONSTASCII_USTRINGPARAM("Quality"));
903 aFilterData
[0].Value
<<= nJPEGQualityLevel
;
907 // Debug code to see what we export to swf
909 SvFileStream
aDstStm( String( RTL_CONSTASCII_USTRINGPARAM("e:\\test.jpg") ), STREAM_READ
| STREAM_WRITE
| STREAM_TRUNC
);
910 aFilter
.ExportGraphic( aGraphic
, String(), aDstStm
,
911 aFilter
.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME
) ) ), &aFilterData
);
915 if( aFilter
.ExportGraphic( aGraphic
, String(), aDstStm
,
916 aFilter
.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME
) ) ), &aFilterData
) == ERRCODE_NONE
)
918 pJpgData
= reinterpret_cast<const sal_uInt8
*>(aDstStm
.GetData());
919 nJpgDataLength
= aDstStm
.Seek( STREAM_SEEK_TO_END
);
922 // AS: Ok, now go ahead and use whichever is smaller. If JPEG is smaller, then
923 // we have to export as TAG_DEFINEBITSJPEG3 in the case that there is alpha
925 if ( pJpgData
&& ( nJpgDataLength
+ alpha_compressed_size
< compressed_size
) )
926 Impl_writeJPEG(nBitmapId
, pJpgData
, nJpgDataLength
, pAlphaCompressed
, alpha_compressed_size
);
928 Impl_writeBmp( nBitmapId
, width
, height
, pCompressed
, compressed_size
);
930 delete[] pCompressed
;
931 delete[] pAlphaCompressed
;
938 // -----------------------------------------------------------------------------
940 void Writer::Impl_writeImage( const BitmapEx
& rBmpEx
, const Point
& rPt
, const Size
& rSz
, const Point
& /* rSrcPt */, const Size
& /* rSrcSz */, const Rectangle
& rClipRect
, bool bNeedToMapClipRect
)
944 BitmapEx
bmpSource( rBmpEx
);
946 Rectangle originalPixelRect
= Rectangle(Point(), bmpSource
.GetSizePixel());
948 Point
srcPt( map(rPt
) );
949 Size
srcSize( map(rSz
) );
950 Rectangle
destRect( srcPt
, srcSize
);
952 // AS: Christian, my scaling factors are different than yours, and work better for me.
953 // However, I can't explain why exactly. I got some of this by trial and error.
954 double XScale
= static_cast<double>(originalPixelRect
.GetWidth())/destRect
.GetWidth();
955 double YScale
= static_cast<double>(originalPixelRect
.GetHeight())/destRect
.GetHeight();
957 // AS: If rClipRect has a value set, then we need to crop the bmp appropriately.
958 // If a map event already occurred in the metafile, then we do not need to map
959 // the clip rect as it's already been done.
960 if (!rClipRect
.IsEmpty())
962 // AS: Christian, I also don't understand why bNeedToMapClipRect is necessary, but it
963 // works like a charm. Usually, the map event in the meta file does not cause the
964 // clipping rectangle to get mapped. However, sometimes there are multiple layers
965 // of mapping which eventually do cause the clipping rect to be mapped.
966 Size
clipSize( bNeedToMapClipRect
? map(rClipRect
.GetSize()) : rClipRect
.GetSize() );
967 Rectangle clipRect
= Rectangle(Point(), clipSize
);
968 destRect
.Intersection( clipRect
);
970 Rectangle
cropRect(destRect
);
972 // AS: The bmp origion is always 0,0 so we have to adjust before we crop.
973 cropRect
.Move(-srcPt
.X(), -srcPt
.Y());
974 // AS: Rectangle has no scale function (?!) so I do it manually...
975 Rectangle
cropPixelRect(static_cast<long>(cropRect
.Left()*XScale
),
976 static_cast<long>(cropRect
.Top()*YScale
),
977 static_cast<long>(cropRect
.Right()*XScale
),
978 static_cast<long>(cropRect
.Bottom()*YScale
));
980 bmpSource
.Crop(cropPixelRect
);
985 // #105949# fix images that are under 16 pixels width or height by
986 // expanding them. Some swf players can't display such small
988 const Size
& rSizePixel
= bmpSource
.GetSizePixel();
989 if( (rSizePixel
.Width() < 16) || (rSizePixel
.Height() < 16) )
991 const sal_uInt32 nDX
= rSizePixel
.Width() < 16 ? 16 - rSizePixel
.Width() : 0;
992 const sal_uInt32 nDY
= rSizePixel
.Height() < 16 ? 16 - rSizePixel
.Height() : 0;
993 bmpSource
.Expand( nDX
, nDY
);
996 sal_Int32 nJPEGQuality
= mnJPEGCompressMode
;
998 Size szDestPixel
= mpVDev
->LogicToPixel(srcSize
, aTWIPSMode
);
1000 double pixXScale
= static_cast<double>(szDestPixel
.Width()) / originalPixelRect
.GetWidth();
1001 double pixYScale
= static_cast<double>(szDestPixel
.Height()) / originalPixelRect
.GetHeight();
1003 // AS: If the image has been scaled down, then scale down the quality
1004 // that we use for JPEG compression.
1005 if (pixXScale
< 1.0 && pixYScale
< 1.0)
1008 double qualityScale
= (pixXScale
+ pixYScale
)/2;
1010 nJPEGQuality
= (sal_Int32
)( nJPEGQuality
* qualityScale
);
1012 if (nJPEGQuality
< 10)
1016 sal_uInt16 nBitmapId
= defineBitmap(bmpSource
, nJPEGQuality
);
1018 Polygon
aPoly( destRect
);
1020 // AS: Since images are being cropped now, no translation is normally necessary.
1021 // However, some things like graphical bullet points are still get translated.
1022 ::basegfx::B2DHomMatrix m
; // #i73264#
1023 m
.scale(1.0/XScale
, 1.0/YScale
);
1024 if (destRect
.Left() || destRect
.Top())
1025 m
.translate(destRect
.Left(), destRect
.Top());
1027 FillStyle
aFillStyle( nBitmapId
, true, m
);
1029 sal_uInt16 nShapeId
= defineShape( aPoly
, aFillStyle
);
1031 maShapeIds
.push_back( nShapeId
);
1035 // -----------------------------------------------------------------------------
1037 void Writer::Impl_writeBmp( sal_uInt16 nBitmapId
, sal_uInt32 width
, sal_uInt32 height
, sal_uInt8
*pCompressed
, sal_uInt32 compressed_size
)
1039 startTag( TAG_DEFINEBITSLOSSLESS2
);
1041 mpTag
->addUI16( nBitmapId
);
1043 mpTag
->addUI16( _uInt16(width
) );
1044 mpTag
->addUI16( _uInt16(height
) );
1046 mpTag
->Write( pCompressed
, compressed_size
);
1051 // -----------------------------------------------------------------------------
1053 void Writer::Impl_writeJPEG(sal_uInt16 nBitmapId
, const sal_uInt8
* pJpgData
, sal_uInt32 nJpgDataLength
, sal_uInt8
*pAlphaCompressed
, sal_uInt32 alpha_compressed_size
)
1055 // AS: Go through the actuall JPEG bits, seperating out the
1056 // header fields from the actual image fields. Fields are
1057 // identifed by 0xFFXX where XX is the field type. Both
1058 // the header and the image need start and stop (D8 and D9),
1059 // so that's why you see those written to both. I don't
1060 // really know what the rest of these are, I got it to work
1061 // kind of by trial and error and by comparing with known
1063 sal_uInt8 cType
= 0x01;
1064 const sal_uInt8
* pJpgSearch
= pJpgData
;
1068 SvMemoryStream EncodingTableStream
;
1069 SvMemoryStream ImageBitsStream
;
1070 for (;pJpgSearch
< pJpgData
+ nJpgDataLength
; pJpgSearch
+= nLength
)
1074 if (0xFF != *pJpgSearch
)
1076 DBG_ERROR( "Expected JPEG marker." ); ((void)0);
1080 cType
= *(pJpgSearch
+ 1);
1082 if (0xD8 == cType
|| 0xD9 == cType
)
1086 else if (0xDA == cType
)
1088 //AS: This is the actual image data, and runs to the
1089 // end of the file (as best I know), minus 2 bytes
1090 // for the closing 0xFFD9.
1091 nLength
= nJpgDataLength
- (pJpgSearch
- pJpgData
) - 2;
1095 // AS: Lengths are big endian.
1097 // Beware. pJpgSearch is not necessarily word-aligned,
1098 // so we access it byte-wise.
1100 // AS: Add 2 to the length to include the 0xFFXX itself.
1101 nLength
= 2 + (pJpgSearch
[2]<<8) + pJpgSearch
[3];
1104 // AS: I'm refering to libjpeg for a list of what these
1105 // markers are. See jdmarker.c for a list.
1106 // AS: I'm ignoring application specific markers 0xE1...0xEF
1107 // and comments 0xFE. I don't know what
1108 // 0xF0 or 0xFD are for, and they don't come up.
1109 // Additionally, 0xDE and 0xDF aren't clear to me.
1114 EncodingTableStream
.Write( pJpgSearch
, nLength
);
1115 ImageBitsStream
.Write( pJpgSearch
, nLength
);
1123 EncodingTableStream
.Write( pJpgSearch
, nLength
);
1133 // case 0xC8: Apparently reserved for JPEG extensions?
1142 ImageBitsStream
.Write( pJpgSearch
, nLength
);
1146 DBG_ERROR( "JPEG marker I didn't handle!" );
1151 EncodingTableStream
.Seek( STREAM_SEEK_TO_END
);
1152 sal_uInt32 nEncodingTableSize
= EncodingTableStream
.Tell();
1153 EncodingTableStream
.Seek( STREAM_SEEK_TO_BEGIN
);
1155 ImageBitsStream
.Seek( STREAM_SEEK_TO_END
);
1156 sal_uInt32 nImageBitsSize
= ImageBitsStream
.Tell();
1157 ImageBitsStream
.Seek( STREAM_SEEK_TO_BEGIN
);
1159 // AS: If we need alpha support, use TAG_DEFINEBITSJPEG3.
1160 if (alpha_compressed_size
> 0)
1162 startTag( TAG_DEFINEBITSJPEG3
);
1164 mpTag
->addUI16( nBitmapId
);
1166 mpTag
->addUI32( nEncodingTableSize
+ nImageBitsSize
);
1168 mpTag
->Write(EncodingTableStream
.GetData(), nEncodingTableSize
);
1169 mpTag
->Write(ImageBitsStream
.GetData(), nImageBitsSize
);
1171 mpTag
->Write( pAlphaCompressed
, alpha_compressed_size
);
1177 startTag( TAG_DEFINEBITSJPEG2
);
1179 mpTag
->addUI16( nBitmapId
);
1181 mpTag
->Write(EncodingTableStream
.GetData(), nEncodingTableSize
);
1182 mpTag
->Write(ImageBitsStream
.GetData(), nImageBitsSize
);
1188 // -----------------------------------------------------------------------------
1190 void Writer::Impl_writeLine( const Point
& rPt1
, const Point
& rPt2
, const Color
* pLineColor
)
1192 Color
aOldColor( mpVDev
->GetLineColor() );
1194 mpVDev
->SetLineColor( *pLineColor
);
1196 const Point aPtAry
[2] = { rPt1
, rPt2
};
1197 Polygon
aPoly( 2, aPtAry
);
1198 Impl_writePolyPolygon( aPoly
, false );
1200 mpVDev
->SetLineColor( aOldColor
);
1203 // -----------------------------------------------------------------------------
1205 void Writer::Impl_writeRect( const Rectangle
& rRect
, long nRadX
, long nRadY
)
1207 if( (rRect
.nTop
== rRect
.nBottom
) || (rRect
.nLeft
== rRect
.nRight
) )
1209 Color
aColor( mpVDev
->GetFillColor() );
1210 Impl_writeLine( rRect
.TopLeft(), rRect
.BottomRight(), &aColor
);
1214 Polygon
aPoly( rRect
, nRadX
, nRadY
);
1215 Impl_writePolyPolygon( aPoly
, true );
1219 // -----------------------------------------------------------------------------
1221 void Writer::Impl_writeEllipse( const Point
& rCenter
, long nRadX
, long nRadY
)
1223 Polygon
aPoly( rCenter
, nRadX
, nRadY
);
1224 Impl_writePolyPolygon( aPoly
, false );
1228 /** writes the stroke defined by SvtGraphicStroke and returns true or it returns
1229 false if it can't handle this stroke.
1231 bool Writer::Impl_writeStroke( SvtGraphicStroke
& rStroke
)
1234 rStroke
.getPath( aPolygon
);
1235 PolyPolygon
aPolyPolygon( aPolygon
);
1237 Rectangle
aOldRect( aPolyPolygon
.GetBoundRect() );
1239 map( aPolyPolygon
);
1241 Rectangle
aNewRect( aPolyPolygon
.GetBoundRect() );
1243 PolyPolygon aStartArrow
;
1244 rStroke
.getStartArrow( aStartArrow
);
1245 if( 0 != aStartArrow
.Count() )
1246 return false; // todo: Implement line ends
1248 PolyPolygon aEndArrow
;
1249 rStroke
.getEndArrow( aEndArrow
);
1250 if( 0 != aEndArrow
.Count() )
1251 return false; // todo: Implement line ends
1253 SvtGraphicStroke::DashArray aDashArray
;
1254 rStroke
.getDashArray( aDashArray
);
1255 if( 0 != aDashArray
.size() )
1256 return false; // todo: implement dashes
1258 Color
aColor( mpVDev
->GetLineColor() );
1260 if( 0.0 != rStroke
.getTransparency() )
1261 aColor
.SetTransparency( sal::static_int_cast
<UINT8
>( MinMax( (long int)( rStroke
.getTransparency() * 0xff ), 0, 0xff ) ) );
1263 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, sal::static_int_cast
<sal_uInt16
>( mapRelative( (sal_Int32
)( rStroke
.getStrokeWidth() ) ) ), aColor
);
1264 maShapeIds
.push_back( nShapeId
);
1268 // -----------------------------------------------------------------------------
1270 /** writes the filling defined by SvtGraphicFill and returns true or it returns
1271 false if it can't handle this filling.
1273 bool Writer::Impl_writeFilling( SvtGraphicFill
& rFilling
)
1275 PolyPolygon aPolyPolygon
;
1276 rFilling
.getPath( aPolyPolygon
);
1278 Rectangle
aOldRect( aPolyPolygon
.GetBoundRect() );
1280 map( aPolyPolygon
);
1282 Rectangle
aNewRect( aPolyPolygon
.GetBoundRect() );
1284 switch( rFilling
.getFillType() )
1286 case SvtGraphicFill::fillSolid
:
1288 Color
aColor( rFilling
.getFillColor() );
1290 if( 0.0 != rFilling
.getTransparency() )
1291 aColor
.SetTransparency( sal::static_int_cast
<UINT8
>( MinMax( (long int)( rFilling
.getTransparency() * 0xff ) , 0, 0xff ) ) );
1293 FillStyle
aFillStyle( aColor
);
1295 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, aFillStyle
);
1296 maShapeIds
.push_back( nShapeId
);
1299 case SvtGraphicFill::fillGradient
:
1301 case SvtGraphicFill::fillHatch
:
1303 case SvtGraphicFill::fillTexture
:
1306 rFilling
.getGraphic( aGraphic
);
1308 // CL->AS: Should we also scale down the quality here depending on image scale?
1309 sal_uInt16 nBitmapId
= defineBitmap( aGraphic
.GetBitmapEx(), mnJPEGCompressMode
);
1311 ::basegfx::B2DHomMatrix aMatrix
; // #i73264#
1313 SvtGraphicFill::Transform aTransform
;
1315 rFilling
.getTransform( aTransform
);
1318 for( a
= 0; a
< 2; a
++ )
1320 for( b
= 0; b
< 3; b
++ )
1322 aMatrix
.set(a
, b
, aTransform
.matrix
[a
*3+b
]);
1325 aMatrix
.set(2, 0, 0.0);
1326 aMatrix
.set(2, 1, 0.0);
1327 aMatrix
.set(2, 2, 1.0);
1330 Rectangle originalPixelRect
= Rectangle(Point(), aGraphic
.GetBitmapEx().GetSizePixel());
1332 double XScale
= (double)aNewRect
.GetWidth()/aOldRect
.GetWidth();
1333 double YScale
= (double)aNewRect
.GetHeight()/aOldRect
.GetHeight();
1335 aMatrix
.scale( XScale
, YScale
);
1337 FillStyle
aFillStyle( nBitmapId
, !rFilling
.IsTiling(), aMatrix
);
1339 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, aFillStyle
);
1340 maShapeIds
.push_back( nShapeId
);
1347 // -----------------------------------------------------------------------------
1349 /* CL: The idea was to export page fields as text fields that get theire
1350 string from a variable set with actionscript by each page. This didn't
1351 work out since the formating is always wrong when text follows the
1352 page number field since pages greater one may require more space than
1356 bool Writer::Impl_writePageField( Rectangle
& rTextBounds
)
1358 startTag( TAG_DEFINEEDITTEXT
);
1360 sal_uInt16 nTextId
= createID();
1362 mpTag
->addUI16( nTextId
);
1363 mpTag
->addRect( rTextBounds
);
1366 aBits
.writeUB( 1, 1 ); // HasText
1367 aBits
.writeUB( 0, 1 ); // WordWrap
1368 aBits
.writeUB( 0, 1 ); // MultiLine
1369 aBits
.writeUB( 0, 1 ); // Password
1370 aBits
.writeUB( 1, 1 ); // HasTextColor
1371 aBits
.writeUB( 0, 1 ); // HasMaxLength
1372 aBits
.writeUB( 0, 1 ); // HasFont
1373 aBits
.writeUB( 0, 1 ); // Reserved
1374 aBits
.writeUB( 0, 1 ); // AutoSize
1375 aBits
.writeUB( 0, 1 ); // HasLayout
1376 aBits
.writeUB( 1, 1 ); // NoSelect
1377 aBits
.writeUB( 1, 1 ); // Border
1378 aBits
.writeUB( 0, 1 ); // Reserved
1379 aBits
.writeUB( 0, 1 ); // HTML
1380 aBits
.writeUB( 0, 1 ); // UseOutlines
1381 mpTag
->addBits( aBits
);
1383 Color
aColor( COL_BLACK
);
1384 mpTag
->addRGB( aColor
);
1385 mpTag
->addString( "PageNumber" );
1386 mpTag
->addString( "XXX" );
1390 maShapeIds
.push_back( nTextId
);
1396 // -----------------------------------------------------------------------------
1398 void Writer::Impl_writeActions( const GDIMetaFile
& rMtf
)
1402 for( ULONG i
= 0, nCount
= rMtf
.GetActionCount(); i
< nCount
; i
++ )
1404 const MetaAction
* pAction
= rMtf
.GetAction( i
);
1405 const USHORT nType
= pAction
->GetType();
1409 case( META_PIXEL_ACTION
):
1411 const MetaPixelAction
* pA
= (const MetaPixelAction
*) pAction
;
1413 Impl_writeLine( pA
->GetPoint(), pA
->GetPoint(), &pA
->GetColor() );
1417 case( META_POINT_ACTION
):
1419 const MetaPointAction
* pA
= (const MetaPointAction
*) pAction
;
1421 Impl_writeLine( pA
->GetPoint(), pA
->GetPoint() );
1425 case( META_LINE_ACTION
):
1427 const MetaLineAction
* pA
= (const MetaLineAction
*) pAction
;
1429 Impl_writeLine( pA
->GetStartPoint(), pA
->GetEndPoint() );
1433 case( META_RECT_ACTION
):
1435 Impl_writeRect( ( (const MetaRectAction
*) pAction
)->GetRect(), 0, 0 );
1439 case( META_ROUNDRECT_ACTION
):
1441 const MetaRoundRectAction
* pA
= (const MetaRoundRectAction
*) pAction
;
1443 Impl_writeRect( pA
->GetRect(), pA
->GetHorzRound(), pA
->GetVertRound() );
1447 case( META_ELLIPSE_ACTION
):
1449 const MetaEllipseAction
* pA
= (const MetaEllipseAction
*) pAction
;
1450 const Rectangle
& rRect
= pA
->GetRect();
1452 Impl_writeEllipse( rRect
.Center(), rRect
.GetWidth() >> 1, rRect
.GetHeight() >> 1 );
1456 case( META_ARC_ACTION
):
1457 case( META_PIE_ACTION
):
1458 case( META_CHORD_ACTION
):
1459 case( META_POLYGON_ACTION
):
1465 case( META_ARC_ACTION
):
1467 const MetaArcAction
* pA
= (const MetaArcAction
*) pAction
;
1468 aPoly
= Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), POLY_ARC
);
1472 case( META_PIE_ACTION
):
1474 const MetaPieAction
* pA
= (const MetaPieAction
*) pAction
;
1475 aPoly
= Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), POLY_PIE
);
1479 case( META_CHORD_ACTION
):
1481 const MetaChordAction
* pA
= (const MetaChordAction
*) pAction
;
1482 aPoly
= Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), POLY_CHORD
);
1486 case( META_POLYGON_ACTION
):
1487 aPoly
= ( (const MetaPolygonAction
*) pAction
)->GetPolygon();
1491 if( aPoly
.GetSize() )
1493 Impl_writePolygon( aPoly
, sal_True
);
1498 case( META_POLYLINE_ACTION
):
1500 const MetaPolyLineAction
* pA
= (const MetaPolyLineAction
*) pAction
;
1501 const Polygon
& rPoly
= pA
->GetPolygon();
1503 if( rPoly
.GetSize() )
1504 Impl_writePolygon( rPoly
, sal_False
);
1508 case( META_POLYPOLYGON_ACTION
):
1510 const MetaPolyPolygonAction
* pA
= (const MetaPolyPolygonAction
*) pAction
;
1511 const PolyPolygon
& rPolyPoly
= pA
->GetPolyPolygon();
1513 if( rPolyPoly
.Count() )
1514 Impl_writePolyPolygon( rPolyPoly
, sal_True
);
1518 case( META_GRADIENT_ACTION
):
1520 const MetaGradientAction
* pA
= (const MetaGradientAction
*) pAction
;
1522 Polygon
aPoly( pA
->GetRect() );
1523 Impl_writeGradientEx( aPoly
, pA
->GetGradient() );
1527 case( META_GRADIENTEX_ACTION
):
1529 const MetaGradientExAction
* pA
= (const MetaGradientExAction
*) pAction
;
1530 Impl_writeGradientEx( pA
->GetPolyPolygon(), pA
->GetGradient() );
1534 case META_HATCH_ACTION
:
1536 const MetaHatchAction
* pA
= (const MetaHatchAction
*) pAction
;
1537 GDIMetaFile aTmpMtf
;
1539 mpVDev
->AddHatchActions( pA
->GetPolyPolygon(), pA
->GetHatch(), aTmpMtf
);
1540 Impl_writeActions( aTmpMtf
);
1544 case( META_TRANSPARENT_ACTION
):
1546 const MetaTransparentAction
* pA
= (const MetaTransparentAction
*) pAction
;
1547 const PolyPolygon
& rPolyPoly
= pA
->GetPolyPolygon();
1549 if( rPolyPoly
.Count() )
1551 // convert transparence from percent into 0x00 - 0xff
1552 sal_uInt8 nTransparence
= (sal_uInt8
) MinMax( FRound( pA
->GetTransparence() * 2.55 ), 0, 255 );
1553 Impl_writePolyPolygon( rPolyPoly
, sal_True
, nTransparence
);
1558 case( META_FLOATTRANSPARENT_ACTION
):
1560 const MetaFloatTransparentAction
* pA
= (const MetaFloatTransparentAction
*) pAction
;
1561 GDIMetaFile
aTmpMtf( pA
->GetGDIMetaFile() );
1562 Point
aSrcPt( aTmpMtf
.GetPrefMapMode().GetOrigin() );
1563 const Size
aSrcSize( aTmpMtf
.GetPrefSize() );
1564 const Point
aDestPt( pA
->GetPoint() );
1565 const Size
aDestSize( pA
->GetSize() );
1566 const double fScaleX
= aSrcSize
.Width() ? (double) aDestSize
.Width() / aSrcSize
.Width() : 1.0;
1567 const double fScaleY
= aSrcSize
.Height() ? (double) aDestSize
.Height() / aSrcSize
.Height() : 1.0;
1568 long nMoveX
, nMoveY
;
1570 if( fScaleX
!= 1.0 || fScaleY
!= 1.0 )
1572 aTmpMtf
.Scale( fScaleX
, fScaleY
);
1573 aSrcPt
.X() = FRound( aSrcPt
.X() * fScaleX
);
1574 aSrcPt
.Y() = FRound( aSrcPt
.Y() * fScaleY
);
1577 nMoveX
= aDestPt
.X() - aSrcPt
.X(), nMoveY
= aDestPt
.Y() - aSrcPt
.Y();
1579 if( nMoveX
|| nMoveY
)
1580 aTmpMtf
.Move( nMoveX
, nMoveY
);
1582 const Gradient
& rGradient
= pA
->GetGradient();
1583 sal_uInt32 nLuminance
= ((sal_Int32
)rGradient
.GetStartColor().GetLuminance() + (sal_Int32
)rGradient
.GetEndColor().GetLuminance() ) >> 1;
1585 sal_uInt8 nOldGlobalTransparency
= mnGlobalTransparency
;
1586 mnGlobalTransparency
= (sal_uInt8
)MinMax( nLuminance
, 0, 0xff );
1589 Impl_writeActions( aTmpMtf
);
1592 mnGlobalTransparency
= nOldGlobalTransparency
;
1596 case( META_EPS_ACTION
):
1598 const MetaEPSAction
* pA
= (const MetaEPSAction
*) pAction
;
1599 const GDIMetaFile
aGDIMetaFile( pA
->GetSubstitute() );
1600 sal_Bool bFound
= sal_False
;
1602 for( ULONG j
= 0, nC
= aGDIMetaFile
.GetActionCount(); ( j
< nC
) && !bFound
; j
++ )
1604 const MetaAction
* pSubstAct
= aGDIMetaFile
.GetAction( j
);
1606 if( pSubstAct
->GetType() == META_BMPSCALE_ACTION
)
1609 const MetaBmpScaleAction
* pBmpScaleAction
= (const MetaBmpScaleAction
*) pSubstAct
;
1610 Impl_writeImage( pBmpScaleAction
->GetBitmap(),
1611 pA
->GetPoint(), pA
->GetSize(),
1612 Point(), pBmpScaleAction
->GetBitmap().GetSizePixel(), clipRect
, 1 == bMap
);
1618 case( META_COMMENT_ACTION
):
1620 const MetaCommentAction
* pA
= (const MetaCommentAction
*) pAction
;
1621 const BYTE
* pData
= pA
->GetData();
1622 String aSkipComment
;
1624 if( pA
->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL
)
1626 const MetaGradientExAction
* pGradAction
= NULL
;
1627 sal_Bool bDone
= sal_False
;
1629 while( !bDone
&& ( ++i
< nCount
) )
1631 pAction
= rMtf
.GetAction( i
);
1633 if( pAction
->GetType() == META_GRADIENTEX_ACTION
)
1634 pGradAction
= (const MetaGradientExAction
*) pAction
;
1635 else if( ( pAction
->GetType() == META_COMMENT_ACTION
) &&
1636 ( ( (const MetaCommentAction
*) pAction
)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL
) )
1643 Impl_writeGradientEx( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient());
1645 else if( pA
->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_BEGIN" ) == COMPARE_EQUAL
&&
1649 // this comment encapsulates all high level information for a filling that caused
1650 // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1652 SvtGraphicFill aFilling
;
1653 SvMemoryStream
aMemStm( (void*)pData
, pA
->GetDataSize(), STREAM_READ
);
1655 // read the fill info
1656 aMemStm
>> aFilling
;
1658 // if impl_writeFilling can handle this high level filling, it returns true and we
1659 // skip all meta actions until "XPATHFILL_SEQ_END"
1660 if( Impl_writeFilling( aFilling
) )
1662 bool bDone
= sal_False
;
1664 while( !bDone
&& ( ++i
< nCount
) )
1666 pAction
= rMtf
.GetAction( i
);
1668 if( ( pAction
->GetType() == META_COMMENT_ACTION
) &&
1669 ( ( (const MetaCommentAction
*) pAction
)->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_END" ) == COMPARE_EQUAL
) )
1676 else if( pA
->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_BEGIN" ) == COMPARE_EQUAL
&&
1680 // this comment encapsulates all high level information for a filling that caused
1681 // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1683 SvtGraphicStroke aStroke
;
1684 SvMemoryStream
aMemStm( (void*)pData
, pA
->GetDataSize(), STREAM_READ
);
1686 // read the fill info
1689 // if impl_writeStroke can handle this high level stroke, it returns true and we
1690 // skip all meta actions until "XPATHSTROKE_SEQ_END"
1691 if( Impl_writeStroke( aStroke
) )
1693 bool bDone
= sal_False
;
1695 while( !bDone
&& ( ++i
< nCount
) )
1697 pAction
= rMtf
.GetAction( i
);
1699 if( ( pAction
->GetType() == META_COMMENT_ACTION
) &&
1700 ( ( (const MetaCommentAction
*) pAction
)->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_END" ) == COMPARE_EQUAL
) )
1708 else if( pA
->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_BEGIN;PageField" ) == COMPARE_EQUAL
)
1710 bool bDone
= sal_False
;
1712 while( !bDone
&& ( ++i
< nCount
) )
1714 pAction
= rMtf
.GetAction( i
);
1716 if( pAction
->GetType() == META_TEXTARRAY_ACTION
)
1718 const MetaTextArrayAction
* pA
= (const MetaTextArrayAction
*) pAction
;
1719 Rectangle
aRect( pA
->GetPoint(), Size( 100, 100 ) );
1720 Impl_writePageField( aRect
);
1723 if( ( pAction
->GetType() == META_COMMENT_ACTION
) &&
1724 ( ( (const MetaCommentAction
*) pAction
)->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_END" ) == COMPARE_EQUAL
) )
1734 case( META_BMPSCALE_ACTION
):
1736 const MetaBmpScaleAction
* pA
= (const MetaBmpScaleAction
*) pAction
;
1738 Impl_writeImage( pA
->GetBitmap(),
1739 pA
->GetPoint(), pA
->GetSize(),
1740 Point(), pA
->GetBitmap().GetSizePixel(), clipRect
, 1 == bMap
);
1744 case( META_BMP_ACTION
):
1746 const MetaBmpAction
* pA
= (const MetaBmpAction
*) pAction
;
1747 Impl_writeImage( pA
->GetBitmap(),
1748 pA
->GetPoint(), mpVDev
->PixelToLogic( pA
->GetBitmap().GetSizePixel()),
1749 Point(), pA
->GetBitmap().GetSizePixel(), clipRect
, 1 ==bMap
);
1753 case( META_BMPSCALEPART_ACTION
):
1755 const MetaBmpScalePartAction
* pA
= (const MetaBmpScalePartAction
*) pAction
;
1756 Impl_writeImage( pA
->GetBitmap(),
1757 pA
->GetDestPoint(), pA
->GetDestSize(),
1758 pA
->GetSrcPoint(), pA
->GetSrcSize(), clipRect
, 1 == bMap
);
1762 case( META_BMPEX_ACTION
):
1764 const MetaBmpExAction
* pA
= (const MetaBmpExAction
*) pAction
;
1765 Impl_writeImage( pA
->GetBitmapEx(),
1766 pA
->GetPoint(), mpVDev
->PixelToLogic( pA
->GetBitmapEx().GetSizePixel() ),
1767 Point(), pA
->GetBitmapEx().GetSizePixel(), clipRect
, 1 == bMap
);
1771 case( META_BMPEXSCALE_ACTION
):
1773 const MetaBmpExScaleAction
* pA
= (const MetaBmpExScaleAction
*) pAction
;
1774 Impl_writeImage( pA
->GetBitmapEx(),
1775 pA
->GetPoint(), pA
->GetSize(),
1776 Point(), pA
->GetBitmapEx().GetSizePixel(), clipRect
, 1 == bMap
);
1780 case( META_BMPEXSCALEPART_ACTION
):
1782 const MetaBmpExScalePartAction
* pA
= (const MetaBmpExScalePartAction
*) pAction
;
1783 Impl_writeImage( pA
->GetBitmapEx(),
1784 pA
->GetDestPoint(), pA
->GetDestSize(),
1785 pA
->GetSrcPoint(), pA
->GetSrcSize(), clipRect
, 1 == bMap
);
1789 case( META_TEXT_ACTION
):
1791 const MetaTextAction
* pA
= (const MetaTextAction
*) pAction
;
1792 Impl_writeText( pA
->GetPoint(), String( pA
->GetText(), pA
->GetIndex(), pA
->GetLen() ), NULL
, 0);
1796 case( META_TEXTRECT_ACTION
):
1798 const MetaTextRectAction
* pA
= (const MetaTextRectAction
*) pAction
;
1799 Impl_writeText( pA
->GetRect().TopLeft(), pA
->GetText(), NULL
, 0 );
1803 case( META_TEXTARRAY_ACTION
):
1805 const MetaTextArrayAction
* pA
= (const MetaTextArrayAction
*) pAction
;
1806 Impl_writeText( pA
->GetPoint(), String( pA
->GetText(), pA
->GetIndex(), pA
->GetLen() ), pA
->GetDXArray(), 0 );
1810 case( META_STRETCHTEXT_ACTION
):
1812 const MetaStretchTextAction
* pA
= (const MetaStretchTextAction
*) pAction
;
1813 Impl_writeText( pA
->GetPoint(), String( pA
->GetText(), pA
->GetIndex(), pA
->GetLen() ), NULL
, pA
->GetWidth() );
1817 case( META_ISECTRECTCLIPREGION_ACTION
):
1819 const MetaISectRectClipRegionAction
* pA
= (const MetaISectRectClipRegionAction
*) pAction
;
1820 clipRect
= pA
->GetRect();
1822 case( META_CLIPREGION_ACTION
):
1823 case( META_ISECTREGIONCLIPREGION_ACTION
):
1824 case( META_MOVECLIPREGION_ACTION
):
1826 ( (MetaAction
*) pAction
)->Execute( mpVDev
);
1827 // mbClipAttrChanged = sal_True;
1831 case( META_MAPMODE_ACTION
):
1833 // const MetaMapModeAction *pA = (const MetaMapModeAction*) pAction;
1834 // MapMode mm = pA->GetMapMode();
1835 // MapUnit mu = mm.GetMapUnit();
1837 // Point pt = mm.GetOrigin();
1838 // Fraction fx = mm.GetScaleX();
1839 // Fraction fy = mm.GetScaleY();
1843 case( META_REFPOINT_ACTION
):
1844 case( META_LINECOLOR_ACTION
):
1845 case( META_FILLCOLOR_ACTION
):
1846 case( META_TEXTLINECOLOR_ACTION
):
1847 case( META_TEXTFILLCOLOR_ACTION
):
1848 case( META_TEXTCOLOR_ACTION
):
1849 case( META_TEXTALIGN_ACTION
):
1850 case( META_FONT_ACTION
):
1851 case( META_PUSH_ACTION
):
1852 case( META_POP_ACTION
):
1853 case( META_LAYOUTMODE_ACTION
):
1855 ( (MetaAction
*) pAction
)->Execute( mpVDev
);
1859 case( META_RASTEROP_ACTION
):
1860 case( META_MASK_ACTION
):
1861 case( META_MASKSCALE_ACTION
):
1862 case( META_MASKSCALEPART_ACTION
):
1863 case( META_WALLPAPER_ACTION
):
1864 case( META_TEXTLINE_ACTION
):
1866 // !!! >>> we don't want to support these actions
1871 //DBG_ERROR( "FlashActionWriter::ImplWriteActions: unsupported MetaAction #" );
1878 /////////////////////////////////////////////////////////////////////////
1881 void Writer::Impl_addStraightLine( BitStream
& rBits
, Point
& rLastPoint
,
1882 const double P2x
, const double P2y
)
1884 Point
aPoint( FRound(P2x
), FRound(P2y
) );
1886 Impl_addStraightEdgeRecord( rBits
, _Int16(aPoint
.X() - rLastPoint
.X()),_Int16(aPoint
.Y() - rLastPoint
.Y()));
1887 rLastPoint
= aPoint
;
1891 // -----------------------------------------------------------------------------
1893 void Writer::Impl_addQuadBezier( BitStream
& rBits
, Point
& rLastPoint
,
1894 const double P2x
, const double P2y
,
1895 const double P3x
, const double P3y
)
1898 Point
aControlPoint( FRound(P2x
), FRound(P2y
) );
1899 Point
aAnchorPoint( FRound(P3x
), FRound(P3y
) );
1901 Impl_addCurvedEdgeRecord( rBits
,
1902 _Int16(aControlPoint
.X() - rLastPoint
.X()),_Int16(aControlPoint
.Y() - rLastPoint
.Y()),
1903 _Int16(aAnchorPoint
.X() - aControlPoint
.X()),_Int16(aAnchorPoint
.Y() - aControlPoint
.Y()) );
1904 rLastPoint
= aAnchorPoint
;
1907 // -----------------------------------------------------------------------------
1909 /* Approximate given cubic bezier curve by quadratic bezier segments */
1910 void Writer::Impl_quadBezierApprox( BitStream
& rBits
,
1913 const double P1x
, const double P1y
,
1914 const double P2x
, const double P2y
,
1915 const double P3x
, const double P3y
,
1916 const double P4x
, const double P4y
)
1918 // Check for degenerate case, where the given cubic bezier curve
1919 // is already quadratic: P4 == 3P3 - 3P2 + P1
1920 if( P4x
== 3.0*P3x
- 3.0*P2x
+ P1x
&&
1921 P4y
== 3.0*P3y
- 3.0*P2y
+ P1y
)
1923 Impl_addQuadBezier( rBits
, rLastPoint
,
1924 3.0/2.0*P2x
- 1.0/2.0*P1x
, 3.0/2.0*P2y
- 1.0/2.0*P1y
,
1929 // Create quadratic segment for given cubic:
1930 // Start and end point must coincide, determine quadratic control
1931 // point in such a way that it lies on the intersection of the
1932 // tangents at start and end point, resp. Thus, both cubic and
1933 // quadratic curve segments will match in 0th and 1st derivative
1934 // at the start and end points
1936 // Intersection of P2P1 and P4P3
1937 // (P2y-P4y)(P3x-P4x)-(P2x-P4x)(P3y-P4y)
1938 // lambda = -------------------------------------
1939 // (P1x-P2x)(P3y-P4y)-(P1y-P2y)(P3x-P4x)
1941 // Intersection point IP is now
1942 // IP = P2 + lambda(P1-P2)
1944 const double nominator( (P2y
-P4y
)*(P3x
-P4x
) - (P2x
-P4x
)*(P3y
-P4y
) );
1945 const double denominator( (P1x
-P2x
)*(P3y
-P4y
) - (P1y
-P2y
)*(P3x
-P4x
) );
1946 const double lambda( nominator
/ denominator
);
1948 const double IPx( P2x
+ lambda
*( P1x
- P2x
) );
1949 const double IPy( P2y
+ lambda
*( P1y
- P2y
) );
1951 // Introduce some alias names: quadratic start point is P1, end
1952 // point is P4, control point is IP
1953 const double QP1x( P1x
);
1954 const double QP1y( P1y
);
1955 const double QP2x( IPx
);
1956 const double QP2y( IPy
);
1957 const double QP3x( P4x
);
1958 const double QP3y( P4y
);
1960 // Adapted bezier flatness test (lecture notes from R. Schaback,
1961 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
1963 // ||C(t) - Q(t)|| <= max ||c_j - q_j||
1966 // In this case, we don't need the distance from the cubic bezier
1967 // to a straight line, but to a quadratic bezier. The c_j's are
1968 // the cubic bezier's bernstein coefficients, the q_j's the
1969 // quadratic bezier's. We have the c_j's given, the q_j's can be
1970 // calculated from QPi like this (sorry, mixed index notation, we
1971 // use [1,n], formulas use [0,n-1]):
1974 // q_1 = 1/3 QP1 + 2/3 QP2
1975 // q_2 = 2/3 QP2 + 1/3 QP3
1978 // We can drop case 0 and 3, since there the curves coincide
1979 // (distance is zero)
1981 // calculate argument of max for j=1 and j=2
1982 const double fJ1x( P2x
- 1.0/3.0*QP1x
- 2.0/3.0*QP2x
);
1983 const double fJ1y( P2y
- 1.0/3.0*QP1y
- 2.0/3.0*QP2y
);
1984 const double fJ2x( P3x
- 2.0/3.0*QP2x
- 1.0/3.0*QP3x
);
1985 const double fJ2y( P3y
- 2.0/3.0*QP2y
- 1.0/3.0*QP3y
);
1987 // stop if distance from cubic curve is guaranteed to be bounded by d
1988 // Should denominator be 0: then P1P2 and P3P4 are parallel (P1P2^T R[90,P3P4] = 0.0),
1989 // meaning that either we have a straight line or an inflexion point (see else block below)
1990 if( 0.0 != denominator
&&
1991 ::std::max( fJ1x
*fJ1x
+ fJ1y
*fJ1y
,
1992 fJ2x
*fJ2x
+ fJ2y
*fJ2y
) < d2
)
1994 // requested resolution reached.
1995 // Add end points to output file.
1996 // order is preserved, since this is so to say depth first traversal.
1997 Impl_addQuadBezier( rBits
, rLastPoint
,
2003 // Maybe subdivide further
2005 // This is for robustness reasons, since the line intersection
2006 // method below gets instable if the curve gets closer to a
2007 // straight line. If the given cubic bezier does not deviate by
2008 // more than d/4 from a straight line, either:
2009 // - take the line (that's what we do here)
2010 // - express the line by a quadratic bezier
2012 // Perform bezier flatness test (lecture notes from R. Schaback,
2013 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
2015 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
2018 // What is calculated here is an upper bound to the distance from
2019 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
2020 // curve. We can drop 0 and n from the running indices, since the
2021 // argument of max becomes zero for those cases.
2022 const double fJ1x2( P2x
- P1x
- 1.0/3.0*(P4x
- P1x
) );
2023 const double fJ1y2( P2y
- P1y
- 1.0/3.0*(P4y
- P1y
) );
2024 const double fJ2x2( P3x
- P1x
- 2.0/3.0*(P4x
- P1x
) );
2025 const double fJ2y2( P3y
- P1y
- 2.0/3.0*(P4y
- P1y
) );
2027 // stop if distance from line is guaranteed to be bounded by d/4
2028 if( ::std::max( fJ1x2
*fJ1x2
+ fJ1y2
*fJ1y2
,
2029 fJ2x2
*fJ2x2
+ fJ2y2
*fJ2y2
) < d2
/16.0 )
2031 // do not subdivide further, add straight line instead
2032 Impl_addStraightLine( rBits
, rLastPoint
, P4x
, P4y
);
2036 // deCasteljau bezier arc, split at t=0.5
2037 // Foley/vanDam, p. 508
2038 const double L1x( P1x
), L1y( P1y
);
2039 const double L2x( (P1x
+ P2x
)*0.5 ), L2y( (P1y
+ P2y
)*0.5 );
2040 const double Hx ( (P2x
+ P3x
)*0.5 ), Hy ( (P2y
+ P3y
)*0.5 );
2041 const double L3x( (L2x
+ Hx
)*0.5 ), L3y( (L2y
+ Hy
)*0.5 );
2042 const double R4x( P4x
), R4y( P4y
);
2043 const double R3x( (P3x
+ P4x
)*0.5 ), R3y( (P3y
+ P4y
)*0.5 );
2044 const double R2x( (Hx
+ R3x
)*0.5 ), R2y( (Hy
+ R3y
)*0.5 );
2045 const double R1x( (L3x
+ R2x
)*0.5 ), R1y( (L3y
+ R2y
)*0.5 );
2046 const double L4x( R1x
), L4y( R1y
);
2048 // subdivide further
2049 Impl_quadBezierApprox(rBits
, rLastPoint
, d2
, L1x
, L1y
, L2x
, L2y
, L3x
, L3y
, L4x
, L4y
);
2050 Impl_quadBezierApprox(rBits
, rLastPoint
, d2
, R1x
, R1y
, R2x
, R2y
, R3x
, R3y
, R4x
, R4y
);
2056 Reference
< XBreakIterator
> Writer::Impl_GetBreakIterator()
2058 if ( !mxBreakIterator
.is() )
2060 Reference
< XMultiServiceFactory
> xMSF( ::comphelper::getProcessServiceFactory() );
2061 mxBreakIterator
.set( xMSF
->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY
);
2063 return mxBreakIterator
;