1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/i18n/BreakIterator.hpp>
21 #include <com/sun/star/i18n/ScriptType.hpp>
22 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
23 #include <comphelper/processfactory.hxx>
24 #include "swfwriter.hxx"
25 #include <vcl/metaact.hxx>
26 #include <vcl/gdimtf.hxx>
27 #include <vcl/bitmapaccess.hxx>
28 #include <vcl/virdev.hxx>
29 #include <vcl/metric.hxx>
30 #include <basegfx/matrix/b2dhommatrixtools.hxx>
31 #include <vcl/graphicfilter.hxx>
32 #include <vcl/graphictools.hxx>
33 #include <sal/log.hxx>
34 #include <tools/helpers.hxx>
35 #include <tools/debug.hxx>
39 #include <basegfx/polygon/b2dpolygon.hxx>
40 #include <basegfx/polygon/b2dpolypolygon.hxx>
43 using namespace ::swf
;
44 using namespace ::com::sun::star::i18n
;
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::lang
;
47 using namespace ::com::sun::star::io
;
48 using namespace ::com::sun::star::beans
;
50 static MapMode
aTWIPSMode( MapUnit::MapTwip
);
51 static MapMode
a100thmmMode( MapUnit::Map100thMM
);
54 Point
Writer::map( const Point
& rPoint
) const
56 const MapMode
& aSourceMapMode
= mpVDev
->GetMapMode();
58 Point retPoint
= mpVDev
->LogicToLogic( rPoint
, &aSourceMapMode
, &aTWIPSMode
);
60 // AS: Produces a 'possible loss of data' warning that we can't fix without
61 // hurting code readability.
62 retPoint
.setX( static_cast<long>( retPoint
.X() * mnDocXScale
) );
63 retPoint
.setY( static_cast<long>( retPoint
.Y() * mnDocYScale
) );
69 Size
Writer::map( const Size
& rSize
) const
71 const MapMode
& aSourceMapMode
= mpVDev
->GetMapMode();
73 Size retSize
= mpVDev
->LogicToLogic( rSize
, &aSourceMapMode
, &aTWIPSMode
);
75 // AS: Produces a 'possible loss of data' warning that we can't fix without
76 // hurting code readability.
77 retSize
.setWidth( static_cast<long>( retSize
.Width() * mnDocXScale
) );
78 retSize
.setHeight( static_cast<long>( retSize
.Height() * mnDocYScale
) );
84 void Writer::map( tools::PolyPolygon
& rPolyPolygon
) const
86 const sal_uInt16 nPolyCount
= rPolyPolygon
.Count();
89 sal_uInt16 nPoly
, nPoint
, nPointCount
;
90 for( nPoly
= 0; nPoly
< nPolyCount
; nPoly
++ )
92 tools::Polygon
& rPoly
= rPolyPolygon
[nPoly
];
93 nPointCount
= rPoly
.GetSize();
95 for( nPoint
= 0; nPoint
< nPointCount
; nPoint
++ )
97 rPoly
[nPoint
] = map( rPoly
[nPoint
] );
104 sal_Int32
Writer::mapRelative( sal_Int32 n100thMM
) const
106 MapMode
aSourceMapMode( mpVDev
->GetMapMode() );
107 aSourceMapMode
.SetOrigin( Point() );
109 sal_Int32 nTwips
= mpVDev
->LogicToLogic( Point( n100thMM
, n100thMM
), &aSourceMapMode
, &aTWIPSMode
).X();
114 void Writer::Impl_addPolygon( BitStream
& rBits
, const tools::Polygon
& rPoly
, bool bFilled
)
116 Point
aLastPoint( rPoly
[0] );
118 Impl_addShapeRecordChange( rBits
, Int16_(aLastPoint
.X()),Int16_(aLastPoint
.Y()), bFilled
);
120 sal_uInt16 i
= 0, nSize
= rPoly
.GetSize();
125 while( ( i
+ 1 ) < nSize
)
127 if( ( i
+ 3 ) < nSize
)
129 PolyFlags
P1( rPoly
.GetFlags( i
) );
130 PolyFlags
P4( rPoly
.GetFlags( i
+ 3 ) );
132 if( ( PolyFlags::Normal
== P1
|| PolyFlags::Smooth
== P1
|| PolyFlags::Symmetric
== P1
) &&
133 ( PolyFlags::Control
== rPoly
.GetFlags( i
+ 1 ) ) &&
134 ( PolyFlags::Control
== rPoly
.GetFlags( i
+ 2 ) ) &&
135 ( PolyFlags::Normal
== P4
|| PolyFlags::Smooth
== P4
|| PolyFlags::Symmetric
== P4
) )
137 Impl_quadBezierApprox( rBits
, aLastPoint
, d
*d
,
138 rPoly
.GetPoint( i
).X(), rPoly
.GetPoint( i
).Y(),
139 rPoly
.GetPoint( i
+1 ).X(), rPoly
.GetPoint( i
+1 ).Y(),
140 rPoly
.GetPoint( i
+2 ).X(), rPoly
.GetPoint( i
+2 ).Y(),
141 rPoly
.GetPoint( i
+3 ).X(), rPoly
.GetPoint( i
+3 ).Y() );
149 const Point
aPolyPoint( rPoly
[ i
] );
150 if( aPolyPoint
!= aLastPoint
)
152 Impl_addStraightEdgeRecord( rBits
, Int16_(aPolyPoint
.X() - aLastPoint
.X()),Int16_(aPolyPoint
.Y() - aLastPoint
.Y()));
153 aLastPoint
= aPolyPoint
;
157 if( bFilled
&& (rPoly
[0] != rPoly
[nSize
-1]))
159 const Point
aPolyPoint( rPoly
[ 0 ] );
160 if( aPolyPoint
!= aLastPoint
)
162 Impl_addStraightEdgeRecord( rBits
, Int16_(aPolyPoint
.X() - aLastPoint
.X()),Int16_(aPolyPoint
.Y() - aLastPoint
.Y()));
168 /** Exports a style change record with a move to (x,y) and depending on bFilled a line style 1 or fill style 1
170 void Writer::Impl_addShapeRecordChange( BitStream
& rBits
, sal_Int16 dx
, sal_Int16 dy
, bool bFilled
)
172 rBits
.writeUB( 0, 1 ); // TypeFlag
173 rBits
.writeUB( 0, 1 ); // StateNewStyles
174 rBits
.writeUB( sal_uInt32(!bFilled
), 1 ); // StateLineStyle
175 rBits
.writeUB( 0, 1 ); // StateFillStyle0
176 rBits
.writeUB( bFilled
? 1 : 0, 1 ); // StateFillStyle1
177 rBits
.writeUB( 1, 1 ); // StateMoveTo
179 sal_uInt16 nMoveBits
= std::max( getMaxBitsSigned( dx
), getMaxBitsSigned( dy
) );
181 rBits
.writeUB( nMoveBits
, 5 ); // Number of bits per value
182 // TODO: Optimize horizontal and vertical lines
183 rBits
.writeSB( dx
, nMoveBits
); // DeltaX
184 rBits
.writeSB( dy
, nMoveBits
); // DeltaY
186 rBits
.writeUB( 1, 1 ); // set FillStyle1 or LineStyle to 1
190 /** Exports a straight edge record
192 void Writer::Impl_addStraightEdgeRecord( BitStream
& rBits
, sal_Int16 dx
, sal_Int16 dy
)
194 rBits
.writeUB( 1, 1 ); // TypeFlag
195 rBits
.writeUB( 1, 1 ); // StraightFlag
197 sal_uInt16 nBits
= std::max( getMaxBitsSigned( dx
), getMaxBitsSigned( dy
) );
199 rBits
.writeUB( nBits
- 2, 4 ); // Number of bits per value
201 if( (dx
!= 0) && (dy
!= 0) )
203 rBits
.writeUB( 1, 1 ); // GeneralLineFlag
204 rBits
.writeSB( dx
, nBits
); // DeltaX
205 rBits
.writeSB( dy
, nBits
); // DeltaY
209 rBits
.writeUB( 0, 1 );
210 rBits
.writeUB( sal_uInt32( dx
== 0 ), 1 );
213 rBits
.writeSB( dy
, nBits
); // DeltaY
217 rBits
.writeSB( dx
, nBits
); // DeltaX
223 /** Exports a curved edge record
225 void Writer::Impl_addCurvedEdgeRecord( BitStream
& rBits
, sal_Int16 control_dx
, sal_Int16 control_dy
, sal_Int16 anchor_dx
, sal_Int16 anchor_dy
)
227 rBits
.writeUB( 1, 1 ); // TypeFlag
228 rBits
.writeUB( 0, 1 ); // CurvedFlag
230 sal_uInt8 nBits
= static_cast<sal_uInt8
>(
231 std::max( getMaxBitsSigned( control_dx
),
232 std::max( getMaxBitsSigned( control_dy
),
233 std::max( getMaxBitsSigned( anchor_dx
),
234 std::max( getMaxBitsSigned( anchor_dy
), sal_uInt16(3) ) ) ) ) );
236 rBits
.writeUB( nBits
- 2, 4 ); // Number of bits per value
238 rBits
.writeSB( control_dx
, nBits
); // DeltaX
239 rBits
.writeSB( control_dy
, nBits
); // DeltaY
240 rBits
.writeSB( anchor_dx
, nBits
); // DeltaX
241 rBits
.writeSB( anchor_dy
, nBits
); // DeltaY
245 /** Exports an end shape record
247 void Writer::Impl_addEndShapeRecord( BitStream
& rBits
)
249 rBits
.writeUB( 0, 6 );
253 void Writer::Impl_writePolygon( const tools::Polygon
& rPoly
, bool bFilled
)
255 tools::PolyPolygon
aPolyPoly( rPoly
);
256 Impl_writePolyPolygon( aPolyPoly
, bFilled
);
260 void Writer::Impl_writePolygon( const tools::Polygon
& rPoly
, bool bFilled
, const Color
& rFillColor
, const Color
& rLineColor
)
262 tools::PolyPolygon
aPolyPoly( rPoly
);
263 Impl_writePolyPolygon( aPolyPoly
, bFilled
, rFillColor
, rLineColor
);
267 void Writer::Impl_writePolyPolygon( const tools::PolyPolygon
& rPolyPoly
, bool bFilled
, sal_uInt8 nTransparence
/* = 0 */ )
269 Color
aLineColor( mpVDev
->GetLineColor() );
270 if( 0 == aLineColor
.GetTransparency() )
271 aLineColor
.SetTransparency( nTransparence
);
272 Color
aFillColor( mpVDev
->GetFillColor() );
273 if( 0 == aFillColor
.GetTransparency() )
274 aFillColor
.SetTransparency( nTransparence
);
275 Impl_writePolyPolygon(rPolyPoly
, bFilled
, aFillColor
, aLineColor
);
279 void Writer::Impl_writePolyPolygon( const tools::PolyPolygon
& rPolyPoly
, bool bFilled
, const Color
& rFillColor
, const Color
& rLineColor
)
281 tools::PolyPolygon
aPolyPoly( rPolyPoly
);
283 if( aPolyPoly
.Count() )
287 if( mpClipPolyPolygon
)
288 rPolyPoly
.GetIntersection( *mpClipPolyPolygon
, aPolyPoly
);
293 Color
aFillColor( rFillColor
);
294 if( 0 != mnGlobalTransparency
)
295 aFillColor
.SetTransparency( mnGlobalTransparency
);
297 FillStyle
aStyle( aFillColor
);
298 nID
= defineShape( aPolyPoly
, aStyle
);
302 Color
aLineColor( rLineColor
);
303 if( 0 != mnGlobalTransparency
)
304 aLineColor
.SetTransparency( mnGlobalTransparency
);
306 nID
= defineShape( aPolyPoly
, 1, aLineColor
);
308 maShapeIds
.push_back( nID
);
313 /** A gradient is a transition from one color to another, rendered inside a given polypolygon */
314 void Writer::Impl_writeGradientEx( const tools::PolyPolygon
& rPolyPoly
, const Gradient
& rGradient
)
316 if( rPolyPoly
.Count() )
318 tools::PolyPolygon
aPolyPolygon( rPolyPoly
);
321 if( (rGradient
.GetStyle() == GradientStyle::Linear
&& rGradient
.GetAngle() == 900) || (rGradient
.GetStyle() == GradientStyle::Radial
) )
323 const tools::Rectangle
aBoundRect( aPolyPolygon
.GetBoundRect() );
325 FillStyle
aFillStyle( aBoundRect
, rGradient
);
327 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, aFillStyle
);
328 maShapeIds
.push_back( nShapeId
);
332 setClipping( &aPolyPolygon
);
334 // render the gradient filling to simple polygons
337 mpVDev
->AddGradientActions( aPolyPolygon
.GetBoundRect(), rGradient
, aTmpMtf
);
338 Impl_writeActions( aTmpMtf
);
341 setClipping( nullptr );
347 void Writer::setClipping( const tools::PolyPolygon
* pClipPolyPolygon
)
349 mpClipPolyPolygon
= pClipPolyPolygon
;
353 // AS: Just comparing fonts straight up is too literal. There are some
354 // differences in font that actually require different glyphs to be defined,
355 // and some that don't. This function is meant to capture all the differences
356 // that we care about.
357 static bool compare_fonts_for_me(const vcl::Font
& rFont1
, const vcl::Font
& rFont2
)
359 return rFont1
.GetFamilyName() == rFont2
.GetFamilyName() &&
360 rFont1
.GetWeight() == rFont2
.GetWeight() &&
361 rFont1
.GetItalic() == rFont2
.GetItalic() &&
362 rFont1
.IsOutline() == rFont2
.IsOutline() &&
363 rFont1
.IsShadow() == rFont2
.IsShadow() &&
364 rFont1
.GetRelief() == rFont2
.GetRelief();
368 FlashFont
& Writer::Impl_getFont( const vcl::Font
& rFont
)
370 for (auto const& font
: maFonts
)
372 const vcl::Font tempFont
= font
->getFont();
373 if( compare_fonts_for_me(tempFont
, rFont
) )
379 FlashFont
* pFont
= new FlashFont( rFont
, createID() );
380 maFonts
.emplace_back( pFont
);
385 void Writer::Impl_writeText( const Point
& rPos
, const OUString
& rText
, const long* pDXArray
, long nWidth
)
387 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
389 bool bTextSpecial
= aMetric
.IsShadow() || aMetric
.IsOutline() || (aMetric
.GetRelief() != FontRelief::NONE
);
393 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
397 if( aMetric
.GetRelief() != FontRelief::NONE
)
399 Color
aReliefColor( COL_LIGHTGRAY
);
400 Color
aTextColor( mpVDev
->GetTextColor() );
402 if ( aTextColor
== COL_BLACK
)
403 aTextColor
= COL_WHITE
;
405 if ( aTextColor
== COL_WHITE
)
406 aReliefColor
= COL_BLACK
;
410 Point
aOffset( 6,6 );
412 if ( aMetric
.GetRelief() == FontRelief::Engraved
)
421 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, aReliefColor
);
422 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, aTextColor
);
426 if( aMetric
.IsShadow() )
428 long nOff
= 1 + ((aMetric
.GetLineHeight()-24)/24);
429 if ( aMetric
.IsOutline() )
432 Color
aTextColor( mpVDev
->GetTextColor() );
433 Color
aShadowColor( COL_BLACK
);
435 if ( (aTextColor
== COL_BLACK
) || (aTextColor
.GetLuminance() < 8) )
436 aShadowColor
= COL_LIGHTGRAY
;
439 aPos
+= Point( nOff
, nOff
);
440 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, aShadowColor
);
442 if( !aMetric
.IsOutline() )
444 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, aTextColor
);
448 if( aMetric
.IsOutline() )
450 Point aPos
= rPos
+ Point( -6, -6 );
451 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
452 aPos
= rPos
+ Point(+6,+6);
453 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
454 aPos
= rPos
+ Point(-6,+0);
455 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
456 aPos
= rPos
+ Point(-6,+6);
457 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
458 aPos
= rPos
+ Point(+0,+6);
459 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
460 aPos
= rPos
+ Point(+0,-6);
461 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
462 aPos
= rPos
+ Point(+6,-1);
463 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
464 aPos
= rPos
+ Point(+6,+0);
465 Impl_writeText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
467 Impl_writeText( rPos
, rText
, pDXArray
, nWidth
, COL_WHITE
);
473 void Writer::Impl_writeText( const Point
& rPos
, const OUString
& rText
, const long* pDXArray
, long nWidth
, Color aTextColor
)
475 sal_Int32 nLen
= rText
.getLength();
480 const bool bRTL
= bool(mpVDev
->GetLayoutMode() & ComplexTextLayoutFlags::BiDiRtl
);
482 sal_Int16 nScriptType
= ScriptType::LATIN
;
483 Reference
< XBreakIterator
> xBI( Impl_GetBreakIterator() );
486 nScriptType
= xBI
->getScriptType( rText
, 0 );
489 // if the text is either right to left or complex or asian, we
490 // ask the output device for a polygon representation.
491 // On complex and asian text, each unicode character can have
492 // different glyph representation, based on context. Also positioning
493 // is not trivial so we let the output device do it for us.
494 if( bRTL
|| (nScriptType
!= ScriptType::LATIN
) )
496 // todo: optimize me as this will generate a huge amount of duplicate polygons
497 tools::PolyPolygon aPolyPoygon
;
498 mpVDev
->GetTextOutline( aPolyPoygon
, rText
, static_cast<sal_uInt16
>(nLen
), nWidth
, pDXArray
);
499 aPolyPoygon
.Translate( rPos
);
500 Impl_writePolyPolygon( aPolyPoygon
, true, aTextColor
, aTextColor
);
505 std::unique_ptr
<long[]> pOwnArray
;
511 aNormSize
= Size( mpVDev
->GetTextWidth( rText
), 0 );
512 pDX
= const_cast<long*>(pDXArray
);
516 pOwnArray
.reset(new long[ nLen
]);
517 aNormSize
= Size( mpVDev
->GetTextArray( rText
, pOwnArray
.get() ), 0 );
518 pDX
= pOwnArray
.get();
523 aNormSize
.setWidth( pDX
[ nLen
- 2 ] + mpVDev
->GetTextWidth( OUString(rText
[nLen
- 1]) ) );
525 if( nWidth
&& aNormSize
.Width() && ( nWidth
!= aNormSize
.Width() ) )
527 const double fFactor
= static_cast<double>(nWidth
) / aNormSize
.Width();
529 for( sal_Int32 i
= 0; i
< ( nLen
- 1 ); i
++ )
530 pDX
[ i
] = FRound( pDX
[ i
] * fFactor
);
534 vcl::Font
aOldFont( mpVDev
->GetFont() );
535 Point
aBaseLinePos( rPos
);
537 vcl::Font
aFont(aOldFont
);
538 short nOrientation
= aFont
.GetOrientation();
539 aFont
.SetOrientation( 0 );
540 aFont
.SetUnderline(LINESTYLE_NONE
);
541 aFont
.SetStrikeout(STRIKEOUT_NONE
);
542 mpVDev
->SetFont( aFont
);
544 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
546 FlashFont
& rFlashFont
= Impl_getFont( aFont
);
548 // always adjust text position to match baseline alignment
549 switch( aOldFont
.GetAlignment() )
552 aBaseLinePos
.AdjustY(aMetric
.GetAscent() );
556 aBaseLinePos
.AdjustY( -(aMetric
.GetDescent()) );
563 // get mapped text position
564 const Point
aPt( map( aBaseLinePos
) );
566 // write text element
568 #if 0 // makes the calculated bound rect visible for debugging
570 tools::Polygon
aTmpPoly( aPoly
);
571 sal_uInt16 nID
= FlashGeometryExporter::writePolygonShape( aMovieStream
, aTmpPoly
, false, COL_MAGENTA
, COL_MAGENTA
, mpClipPolyPolygon
);
572 ImplPlaceObject( nID
);
576 // CL: This is still a hack until we figure out how to calculate a correct bound rect
578 tools::Rectangle
textBounds( 0, 0, static_cast<long>(mnDocWidth
*mnDocXScale
), static_cast<long>(mnDocHeight
*mnDocYScale
) );
581 // scale width if we have a stretched text
582 if( 0 != aFont
.GetFontSize().Width() )
584 vcl::Font
aTmpFont( aFont
);
585 aTmpFont
.SetAverageFontWidth(0);
586 mpVDev
->SetFont( aTmpFont
);
588 const FontMetric
aMetric2( mpVDev
->GetFontMetric() );
589 mpVDev
->SetFont( aFont
);
591 const long n1
= aFont
.GetFontSize().Width();
592 const long n2
= aMetric2
.GetFontSize().Width();
593 scale
= static_cast<double>(n1
) / static_cast<double>(n2
);
596 basegfx::B2DHomMatrix
m(basegfx::utils::createRotateB2DHomMatrix(static_cast<double>(nOrientation
) * F_PI1800
));
597 m
.translate( aPt
.X() / scale
, double(aPt
.Y()) );
598 m
.scale( scale
, scale
);
600 sal_Int16 nHeight
= Int16_( map( Size( 0, aFont
.GetFontHeight() ) ).Height() );
602 startTag( TAG_DEFINETEXT
);
604 sal_uInt16 nTextId
= createID();
606 mpTag
->addUI16( nTextId
);
607 mpTag
->addRect( textBounds
);
608 mpTag
->addMatrix( m
);
610 sal_uInt8 nGlyphBits
= 16;
611 sal_uInt8 nAdvanceBits
= 16;
613 mpTag
->addUI8( nGlyphBits
);
614 mpTag
->addUI8( nAdvanceBits
);
616 // text style change record
617 mpTag
->addUI8( 0x8c );
618 mpTag
->addUI16( rFlashFont
.getID() );
619 mpTag
->addRGB( aTextColor
);
620 mpTag
->addUI16( uInt16_( nHeight
) );
622 DBG_ASSERT( nLen
<= 127, "TODO: handle text with more than 127 characters" );
625 mpTag
->addUI8( static_cast<sal_uInt8
>(nLen
) );
629 sal_Int32 nLastDX
= 0;
631 for( sal_Int32 i
= 0; i
< nLen
; i
++ )
635 nAdvance
= pDX
[i
] - nLastDX
;
643 aBits
.writeUB( rFlashFont
.getGlyph(rText
[i
],mpVDev
), nGlyphBits
);
644 aBits
.writeSB( Int16_(map( Size( static_cast<long>( nAdvance
/ scale
), 0 ) ).Width() ), nAdvanceBits
);
647 mpTag
->addBits( aBits
);
652 maShapeIds
.push_back( nTextId
);
654 // AS: Write strikeout and underline, if necessary. This code was originally taken from the SVG
655 // export facility, although the positioning had to be tweaked a little. I can't explain the
656 // numbers, but the flash lines up very well with the original OOo document. All of this should
657 // probably be converted to polygons as part of the meta file, though, as we don't handle any
658 // fancy lines (like dashes).
659 if( ( aOldFont
.GetStrikeout() != STRIKEOUT_NONE
) || ( aOldFont
.GetUnderline() != LINESTYLE_NONE
) )
661 tools::Polygon
aPoly( 4 );
662 const long nLineHeight
= std::max
<long>( FRound( aMetric
.GetLineHeight() * 0.05 ), 1 );
664 if( aOldFont
.GetStrikeout() != STRIKEOUT_NONE
)
666 aPoly
[ 0 ].setX( aBaseLinePos
.X() );
667 aPoly
[ 0 ].setY( aBaseLinePos
.Y() - FRound( aMetric
.GetAscent() * 0.26 ) - nLineHeight
);
668 aPoly
[ 1 ].setX( aPoly
[ 0 ].X() + aNormSize
.Width() - 1 );
669 aPoly
[ 1 ].setY( aPoly
[ 0 ].Y() );
670 aPoly
[ 2 ].setX( aPoly
[ 1 ].X() );
671 aPoly
[ 2 ].setY( aPoly
[ 1 ].Y() + nLineHeight
- 1 );
672 aPoly
[ 3 ].setX( aPoly
[ 0 ].X() );
673 aPoly
[ 3 ].setY( aPoly
[ 2 ].Y() );
675 Impl_writePolygon( aPoly
, true, aTextColor
, aTextColor
);
678 // AS: The factor of 1.5 on the nLineHeight is a magic number. I'm not sure why it works,
679 // but it looks good to me.
680 if( aOldFont
.GetUnderline() != LINESTYLE_NONE
)
682 aPoly
[ 0 ].setX( aBaseLinePos
.X() );
683 aPoly
[ 0 ].setY( static_cast<long>(aBaseLinePos
.Y() + 1.5*nLineHeight
) );
684 aPoly
[ 1 ].setX( aPoly
[ 0 ].X() + aNormSize
.Width() - 1 );
685 aPoly
[ 1 ].setY( aPoly
[ 0 ].Y() );
686 aPoly
[ 2 ].setX( aPoly
[ 1 ].X() );
687 aPoly
[ 2 ].setY( aPoly
[ 1 ].Y() + nLineHeight
- 1 );
688 aPoly
[ 3 ].setX( aPoly
[ 0 ].X() );
689 aPoly
[ 3 ].setY( aPoly
[ 2 ].Y() );
691 Impl_writePolygon( aPoly
, true, aTextColor
, aTextColor
);
695 mpVDev
->SetFont( aOldFont
);
699 sal_uInt16
Writer::defineBitmap( const BitmapEx
&bmpSource
, sal_Int32 nJPEGQualityLevel
)
701 BitmapChecksum bmpChecksum
= bmpSource
.GetChecksum();
703 ChecksumCache::iterator it
= mBitmapCache
.find(bmpChecksum
);
705 // AS: We already exported this bitmap, so just return its ID.
706 if (mBitmapCache
.end() != it
)
709 sal_uInt16 nBitmapId
= createID();
710 mBitmapCache
[bmpChecksum
] = nBitmapId
;
712 // AS: OK, we have a good image, so now we decide whether or not to JPEG it or
713 // or Lossless compress it.
715 // Figure out lossless size
716 std::vector
<sal_uInt8
> aImageData
, aAlphaData
;
718 sal_uInt32 width
= bmpSource
.GetPrefSize().Width();
719 sal_uInt32 height
= bmpSource
.GetPrefSize().Height();
720 bmpSource
.GetSplitData(aImageData
, aAlphaData
);
721 sal_uInt32 raw_size
= width
* height
* 4;
722 uLongf compressed_size
= raw_size
+ static_cast<sal_uInt32
>(raw_size
/100) + 12;
723 std::unique_ptr
<sal_uInt8
[]> pCompressed(new sal_uInt8
[ compressed_size
]);
725 if(compress2(pCompressed
.get(), &compressed_size
, aImageData
.data(), raw_size
, Z_BEST_COMPRESSION
) != Z_OK
)
727 SAL_WARN( "filter.flash", "compress2 failed!" ); ((void)0);
730 // AS: SWF files let you provide an Alpha mask for JPEG images, but we have
731 // to ZLIB compress the alpha channel separately.
732 uLong alpha_compressed_size
= 0;
733 std::unique_ptr
<sal_uInt8
[]> pAlphaCompressed
;
734 if (bmpSource
.IsAlpha() || bmpSource
.IsTransparent())
736 alpha_compressed_size
= uLongf(width
* height
+ static_cast<sal_uInt32
>(raw_size
/100) + 12);
737 pAlphaCompressed
.reset(new sal_uInt8
[ compressed_size
]);
739 if(compress2(pAlphaCompressed
.get(), &alpha_compressed_size
, aAlphaData
.data(), width
* height
, Z_BEST_COMPRESSION
) != Z_OK
)
741 SAL_WARN( "filter.flash", "compress2 failed!" ); ((void)0);
745 // clear these early for less peak memory usage
746 aImageData
.resize(0);
747 aAlphaData
.resize(0);
749 // Figure out JPEG size
750 const sal_uInt8
* pJpgData
= nullptr;
751 sal_uInt32 nJpgDataLength
= 0xffffffff;
753 Graphic
aGraphic( bmpSource
);
754 SvMemoryStream
aDstStm( 65535, 65535 );
756 GraphicFilter aFilter
;
758 Sequence
< PropertyValue
> aFilterData(sal_Int32(nJPEGQualityLevel
!= -1));
759 if( nJPEGQualityLevel
!= -1 )
761 aFilterData
[0].Name
= "Quality";
762 aFilterData
[0].Value
<<= nJPEGQualityLevel
;
765 if( aFilter
.ExportGraphic( aGraphic
, OUString(), aDstStm
,
766 aFilter
.GetExportFormatNumberForShortName( JPG_SHORTNAME
), &aFilterData
) == ERRCODE_NONE
)
768 pJpgData
= static_cast<const sal_uInt8
*>(aDstStm
.GetData());
769 nJpgDataLength
= aDstStm
.TellEnd();
772 // AS: Ok, now go ahead and use whichever is smaller. If JPEG is smaller, then
773 // we have to export as TAG_DEFINEBITSJPEG3 in the case that there is alpha
775 if ( pJpgData
&& ( nJpgDataLength
+ alpha_compressed_size
< compressed_size
) )
776 Impl_writeJPEG(nBitmapId
, pJpgData
, nJpgDataLength
, pAlphaCompressed
.get(), alpha_compressed_size
);
778 Impl_writeBmp( nBitmapId
, width
, height
, pCompressed
.get(), compressed_size
);
784 void Writer::Impl_writeImage( const BitmapEx
& rBmpEx
, const Point
& rPt
, const Size
& rSz
, const Point
& /* rSrcPt */, const Size
& /* rSrcSz */, const tools::Rectangle
& rClipRect
, bool bNeedToMapClipRect
)
788 BitmapEx
bmpSource( rBmpEx
);
790 tools::Rectangle
originalPixelRect(Point(), bmpSource
.GetSizePixel());
792 Point
srcPt( map(rPt
) );
793 Size
srcSize( map(rSz
) );
794 tools::Rectangle
destRect( srcPt
, srcSize
);
796 // AS: Christian, my scaling factors are different than yours, and work better for me.
797 // However, I can't explain why exactly. I got some of this by trial and error.
798 double XScale
= destRect
.GetWidth() ? static_cast<double>(originalPixelRect
.GetWidth())/destRect
.GetWidth() : 1.0;
799 double YScale
= destRect
.GetHeight() ? static_cast<double>(originalPixelRect
.GetHeight())/destRect
.GetHeight() : 1.0;
801 // AS: If rClipRect has a value set, then we need to crop the bmp appropriately.
802 // If a map event already occurred in the metafile, then we do not need to map
803 // the clip rect as it's already been done.
804 if (!rClipRect
.IsEmpty())
806 // AS: Christian, I also don't understand why bNeedToMapClipRect is necessary, but it
807 // works like a charm. Usually, the map event in the meta file does not cause the
808 // clipping rectangle to get mapped. However, sometimes there are multiple layers
809 // of mapping which eventually do cause the clipping rect to be mapped.
810 Size
clipSize( bNeedToMapClipRect
? map(rClipRect
.GetSize()) : rClipRect
.GetSize() );
811 tools::Rectangle
clipRect(Point(), clipSize
);
812 destRect
.Intersection( clipRect
);
814 tools::Rectangle
cropRect(destRect
);
816 // AS: The bmp origin is always 0,0 so we have to adjust before we crop.
817 cropRect
.Move(-srcPt
.X(), -srcPt
.Y());
818 // AS: Rectangle has no scale function (?!) so I do it manually...
819 tools::Rectangle
cropPixelRect(static_cast<long>(cropRect
.Left()*XScale
),
820 static_cast<long>(cropRect
.Top()*YScale
),
821 static_cast<long>(cropRect
.Right()*XScale
),
822 static_cast<long>(cropRect
.Bottom()*YScale
));
824 bmpSource
.Crop(cropPixelRect
);
829 // #105949# fix images that are under 16 pixels width or height by
830 // expanding them. Some swf players can't display such small
832 const Size
& rSizePixel
= bmpSource
.GetSizePixel();
833 if( (rSizePixel
.Width() < 16) || (rSizePixel
.Height() < 16) )
835 const sal_uInt32 nDX
= rSizePixel
.Width() < 16 ? 16 - rSizePixel
.Width() : 0;
836 const sal_uInt32 nDY
= rSizePixel
.Height() < 16 ? 16 - rSizePixel
.Height() : 0;
837 bmpSource
.Expand( nDX
, nDY
);
840 sal_Int32 nJPEGQuality
= mnJPEGCompressMode
;
842 Size szDestPixel
= mpVDev
->LogicToPixel(srcSize
, aTWIPSMode
);
844 double pixXScale
= originalPixelRect
.GetWidth() ? static_cast<double>(szDestPixel
.Width()) / originalPixelRect
.GetWidth() : 1.0;
845 double pixYScale
= originalPixelRect
.GetHeight() ? static_cast<double>(szDestPixel
.Height()) / originalPixelRect
.GetHeight() : 1.0;
847 // AS: If the image has been scaled down, then scale down the quality
848 // that we use for JPEG compression.
849 if (pixXScale
< 1.0 && pixYScale
< 1.0)
852 double qualityScale
= (pixXScale
+ pixYScale
)/2;
854 nJPEGQuality
= static_cast<sal_Int32
>( nJPEGQuality
* qualityScale
);
856 if (nJPEGQuality
< 10)
860 sal_uInt16 nBitmapId
= defineBitmap(bmpSource
, nJPEGQuality
);
862 tools::Polygon
aPoly( destRect
);
864 // AS: Since images are being cropped now, no translation is normally necessary.
865 // However, some things like graphical bullet points still get translated.
866 ::basegfx::B2DHomMatrix m
; // #i73264#
867 m
.scale(1.0/XScale
, 1.0/YScale
);
868 if (destRect
.Left() || destRect
.Top())
869 m
.translate(destRect
.Left(), destRect
.Top());
871 FillStyle
aFillStyle( nBitmapId
, true, m
);
873 sal_uInt16 nShapeId
= defineShape( aPoly
, aFillStyle
);
875 maShapeIds
.push_back( nShapeId
);
881 void Writer::Impl_writeBmp( sal_uInt16 nBitmapId
, sal_uInt32 width
, sal_uInt32 height
, sal_uInt8
const *pCompressed
, sal_uInt32 compressed_size
)
883 startTag( TAG_DEFINEBITSLOSSLESS2
);
885 mpTag
->addUI16( nBitmapId
);
887 mpTag
->addUI16( uInt16_(width
) );
888 mpTag
->addUI16( uInt16_(height
) );
890 mpTag
->WriteBytes(pCompressed
, compressed_size
);
896 void Writer::Impl_writeJPEG(sal_uInt16 nBitmapId
, const sal_uInt8
* pJpgData
, sal_uInt32 nJpgDataLength
, sal_uInt8
const *pAlphaCompressed
, sal_uInt32 alpha_compressed_size
)
898 // AS: Go through the actual JPEG bits, separating out the
899 // header fields from the actual image fields. Fields are
900 // identified by 0xFFXX where XX is the field type. Both
901 // the header and the image need start and stop (D8 and D9),
902 // so that's why you see those written to both. I don't
903 // really know what the rest of these are, I got it to work
904 // kind of by trial and error and by comparing with known
906 sal_uInt8 cType
= 0x01;
907 const sal_uInt8
* pJpgSearch
= pJpgData
;
911 SvMemoryStream EncodingTableStream
;
912 SvMemoryStream ImageBitsStream
;
913 for (;pJpgSearch
< pJpgData
+ nJpgDataLength
; pJpgSearch
+= nLength
)
917 if (0xFF != *pJpgSearch
)
919 OSL_FAIL( "Expected JPEG marker." ); ((void)0);
923 cType
= *(pJpgSearch
+ 1);
925 if (0xD8 == cType
|| 0xD9 == cType
)
929 else if (0xDA == cType
)
931 //AS: This is the actual image data, and runs to the
932 // end of the file (as best I know), minus 2 bytes
933 // for the closing 0xFFD9.
934 nLength
= nJpgDataLength
- (pJpgSearch
- pJpgData
) - 2;
938 // AS: Lengths are big endian.
940 // Beware. pJpgSearch is not necessarily word-aligned,
941 // so we access it byte-wise.
943 // AS: Add 2 to the length to include the 0xFFXX itself.
944 nLength
= 2 + (pJpgSearch
[2]<<8) + pJpgSearch
[3];
947 // AS: I'm referring to libjpeg for a list of what these
948 // markers are. See jdmarker.c for a list.
949 // AS: I'm ignoring application specific markers 0xE1...0xEF
950 // and comments 0xFE. I don't know what
951 // 0xF0 or 0xFD are for, and they don't come up.
952 // Additionally, 0xDE and 0xDF aren't clear to me.
957 EncodingTableStream
.WriteBytes(pJpgSearch
, nLength
);
958 ImageBitsStream
.WriteBytes(pJpgSearch
, nLength
);
966 EncodingTableStream
.WriteBytes(pJpgSearch
, nLength
);
976 // case 0xC8: Apparently reserved for JPEG extensions?
985 ImageBitsStream
.WriteBytes(pJpgSearch
, nLength
);
989 OSL_FAIL( "JPEG marker I didn't handle!" );
994 sal_uInt32 nEncodingTableSize
= EncodingTableStream
.TellEnd();
995 EncodingTableStream
.Seek( STREAM_SEEK_TO_BEGIN
);
997 sal_uInt32 nImageBitsSize
= ImageBitsStream
.TellEnd();
998 ImageBitsStream
.Seek( STREAM_SEEK_TO_BEGIN
);
1000 // AS: If we need alpha support, use TAG_DEFINEBITSJPEG3.
1001 if (alpha_compressed_size
> 0)
1003 startTag( TAG_DEFINEBITSJPEG3
);
1005 mpTag
->addUI16( nBitmapId
);
1007 mpTag
->addUI32( nEncodingTableSize
+ nImageBitsSize
);
1009 mpTag
->WriteBytes(EncodingTableStream
.GetData(), nEncodingTableSize
);
1010 mpTag
->WriteBytes(ImageBitsStream
.GetData(), nImageBitsSize
);
1012 mpTag
->WriteBytes(pAlphaCompressed
, alpha_compressed_size
);
1018 startTag( TAG_DEFINEBITSJPEG2
);
1020 mpTag
->addUI16( nBitmapId
);
1022 mpTag
->WriteBytes(EncodingTableStream
.GetData(), nEncodingTableSize
);
1023 mpTag
->WriteBytes(ImageBitsStream
.GetData(), nImageBitsSize
);
1030 void Writer::Impl_writeLine( const Point
& rPt1
, const Point
& rPt2
, const Color
* pLineColor
)
1032 Color
aOldColor( mpVDev
->GetLineColor() );
1034 mpVDev
->SetLineColor( *pLineColor
);
1036 const Point aPtAry
[2] = { rPt1
, rPt2
};
1037 tools::Polygon
aPoly( 2, aPtAry
);
1038 Impl_writePolyPolygon( aPoly
, false );
1040 mpVDev
->SetLineColor( aOldColor
);
1044 void Writer::Impl_writeRect( const tools::Rectangle
& rRect
, long nRadX
, long nRadY
)
1046 if( (rRect
.Top() == rRect
.Bottom()) || (rRect
.Left() == rRect
.Right()) )
1048 Color
aColor( mpVDev
->GetFillColor() );
1049 Impl_writeLine( rRect
.TopLeft(), rRect
.BottomRight(), &aColor
);
1053 tools::Polygon
aPoly( rRect
, nRadX
, nRadY
);
1054 Impl_writePolyPolygon( aPoly
, true );
1059 void Writer::Impl_writeEllipse( const Point
& rCenter
, long nRadX
, long nRadY
)
1061 tools::Polygon
aPoly( rCenter
, nRadX
, nRadY
);
1062 Impl_writePolyPolygon( aPoly
, false );
1066 /** Writes the stroke defined by SvtGraphicStroke and returns true or it returns
1067 false if it can't handle this stroke.
1069 bool Writer::Impl_writeStroke( SvtGraphicStroke
const & rStroke
)
1071 tools::Polygon aPolygon
;
1072 rStroke
.getPath( aPolygon
);
1073 tools::PolyPolygon
aPolyPolygon( aPolygon
);
1075 map( aPolyPolygon
);
1077 // as log as not LINESTYLE2 and DefineShape4 is used (which
1078 // added support for LineJoin), only round LineJoins are
1079 // supported. Fallback to MetaActionType::POLYLINE and MetaActionType::LINE
1080 if(SvtGraphicStroke::joinRound
!= rStroke
.getJoinType())
1083 tools::PolyPolygon aStartArrow
;
1084 rStroke
.getStartArrow( aStartArrow
);
1085 if( 0 != aStartArrow
.Count() )
1086 return false; // todo: Implement line ends
1088 tools::PolyPolygon aEndArrow
;
1089 rStroke
.getEndArrow( aEndArrow
);
1090 if( 0 != aEndArrow
.Count() )
1091 return false; // todo: Implement line ends
1093 SvtGraphicStroke::DashArray aDashArray
;
1094 rStroke
.getDashArray( aDashArray
);
1095 if( !aDashArray
.empty() )
1096 return false; // todo: implement dashes
1098 Color
aColor( mpVDev
->GetLineColor() );
1100 if( 0.0 != rStroke
.getTransparency() )
1101 aColor
.SetTransparency( sal::static_int_cast
<sal_uInt8
>( MinMax( static_cast<long int>( rStroke
.getTransparency() * 0xff ), 0, 0xff ) ) );
1103 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, sal::static_int_cast
<sal_uInt16
>( mapRelative( static_cast<sal_Int32
>( rStroke
.getStrokeWidth() ) ) ), aColor
);
1104 maShapeIds
.push_back( nShapeId
);
1109 /** Writes the filling defined by SvtGraphicFill and returns true or it returns
1110 false if it can't handle this filling.
1112 bool Writer::Impl_writeFilling( SvtGraphicFill
const & rFilling
)
1114 tools::PolyPolygon aPolyPolygon
;
1115 rFilling
.getPath( aPolyPolygon
);
1117 tools::Rectangle
aOldRect( aPolyPolygon
.GetBoundRect() );
1119 map( aPolyPolygon
);
1121 tools::Rectangle
aNewRect( aPolyPolygon
.GetBoundRect() );
1123 switch( rFilling
.getFillType() )
1125 case SvtGraphicFill::fillSolid
:
1127 Color
aColor( rFilling
.getFillColor() );
1129 if( 0.0 != rFilling
.getTransparency() )
1130 aColor
.SetTransparency( sal::static_int_cast
<sal_uInt8
>( MinMax( static_cast<long int>( rFilling
.getTransparency() * 0xff ) , 0, 0xff ) ) );
1132 FillStyle
aFillStyle( aColor
);
1134 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, aFillStyle
);
1135 maShapeIds
.push_back( nShapeId
);
1138 case SvtGraphicFill::fillGradient
:
1140 case SvtGraphicFill::fillHatch
:
1142 case SvtGraphicFill::fillTexture
:
1145 rFilling
.getGraphic( aGraphic
);
1147 // CL->AS: Should we also scale down the quality here depending on image scale?
1148 sal_uInt16 nBitmapId
= defineBitmap( aGraphic
.GetBitmapEx(), mnJPEGCompressMode
);
1150 ::basegfx::B2DHomMatrix aMatrix
; // #i73264#
1152 SvtGraphicFill::Transform aTransform
;
1154 rFilling
.getTransform( aTransform
);
1157 for( a
= 0; a
< 2; a
++ )
1159 for( b
= 0; b
< 3; b
++ )
1161 aMatrix
.set(a
, b
, aTransform
.matrix
[a
*3+b
]);
1164 aMatrix
.set(2, 0, 0.0);
1165 aMatrix
.set(2, 1, 0.0);
1166 aMatrix
.set(2, 2, 1.0);
1169 double XScale
= aOldRect
.GetWidth() ? static_cast<double>(aNewRect
.GetWidth())/aOldRect
.GetWidth() : 1.0;
1170 double YScale
= aOldRect
.GetHeight() ? static_cast<double>(aNewRect
.GetHeight())/aOldRect
.GetHeight() : 1.0;
1172 aMatrix
.scale( XScale
, YScale
);
1174 FillStyle
aFillStyle( nBitmapId
, !rFilling
.IsTiling(), aMatrix
);
1176 sal_uInt16 nShapeId
= defineShape( aPolyPolygon
, aFillStyle
);
1177 maShapeIds
.push_back( nShapeId
);
1185 /* CL: The idea was to export page fields as text fields that get their
1186 string from a variable set with actionscript by each page. This didn't
1187 work out since the formatting is always wrong when text follows the
1188 page number field since pages greater one may require more space than
1192 bool Writer::Impl_writePageField( Rectangle
& rTextBounds
)
1194 startTag( TAG_DEFINEEDITTEXT
);
1196 sal_uInt16 nTextId
= createID();
1198 mpTag
->addUI16( nTextId
);
1199 mpTag
->addRect( rTextBounds
);
1202 aBits
.writeUB( 1, 1 ); // HasText
1203 aBits
.writeUB( 0, 1 ); // WordWrap
1204 aBits
.writeUB( 0, 1 ); // MultiLine
1205 aBits
.writeUB( 0, 1 ); // Password
1206 aBits
.writeUB( 1, 1 ); // HasTextColor
1207 aBits
.writeUB( 0, 1 ); // HasMaxLength
1208 aBits
.writeUB( 0, 1 ); // HasFont
1209 aBits
.writeUB( 0, 1 ); // Reserved
1210 aBits
.writeUB( 0, 1 ); // AutoSize
1211 aBits
.writeUB( 0, 1 ); // HasLayout
1212 aBits
.writeUB( 1, 1 ); // NoSelect
1213 aBits
.writeUB( 1, 1 ); // Border
1214 aBits
.writeUB( 0, 1 ); // Reserved
1215 aBits
.writeUB( 0, 1 ); // HTML
1216 aBits
.writeUB( 0, 1 ); // UseOutlines
1217 mpTag
->addBits( aBits
);
1220 mpTag
->addRGB( aColor
);
1221 mpTag
->addString( "PageNumber" );
1222 mpTag
->addString( "XXX" );
1226 maShapeIds
.push_back( nTextId
);
1233 void Writer::Impl_handleLineInfoPolyPolygons(const LineInfo
& rInfo
, const basegfx::B2DPolygon
& rLinePolygon
)
1235 if(rLinePolygon
.count())
1237 basegfx::B2DPolyPolygon
aLinePolyPolygon(rLinePolygon
);
1238 basegfx::B2DPolyPolygon aFillPolyPolygon
;
1240 rInfo
.applyToB2DPolyPolygon(aLinePolyPolygon
, aFillPolyPolygon
);
1242 if(aLinePolyPolygon
.count())
1244 for(sal_uInt32
a(0); a
< aLinePolyPolygon
.count(); a
++)
1246 const basegfx::B2DPolygon
& aCandidate(aLinePolyPolygon
.getB2DPolygon(a
));
1247 Impl_writePolygon( tools::Polygon(aCandidate
), false );
1251 if(aFillPolyPolygon
.count())
1253 const Color
aOldLineColor(mpVDev
->GetLineColor());
1254 const Color
aOldFillColor(mpVDev
->GetFillColor());
1256 mpVDev
->SetLineColor();
1257 mpVDev
->SetFillColor(aOldLineColor
);
1259 for(sal_uInt32
a(0); a
< aFillPolyPolygon
.count(); a
++)
1261 const tools::Polygon
aPolygon(aFillPolyPolygon
.getB2DPolygon(a
));
1262 Impl_writePolyPolygon(tools::PolyPolygon(aPolygon
), true );
1265 mpVDev
->SetLineColor(aOldLineColor
);
1266 mpVDev
->SetFillColor(aOldFillColor
);
1272 void Writer::Impl_writeActions( const GDIMetaFile
& rMtf
)
1274 tools::Rectangle clipRect
;
1276 for( size_t i
= 0, nCount
= rMtf
.GetActionSize(); i
< nCount
; i
++ )
1278 const MetaAction
* pAction
= rMtf
.GetAction( i
);
1279 const MetaActionType nType
= pAction
->GetType();
1283 case MetaActionType::PIXEL
:
1285 const MetaPixelAction
* pA
= static_cast<const MetaPixelAction
*>(pAction
);
1287 Impl_writeLine( pA
->GetPoint(), pA
->GetPoint(), &pA
->GetColor() );
1291 case MetaActionType::POINT
:
1293 const MetaPointAction
* pA
= static_cast<const MetaPointAction
*>(pAction
);
1295 Impl_writeLine( pA
->GetPoint(), pA
->GetPoint() );
1299 case MetaActionType::LINE
:
1301 const MetaLineAction
* pA
= static_cast<const MetaLineAction
*>(pAction
);
1303 if(pA
->GetLineInfo().IsDefault())
1305 Impl_writeLine( pA
->GetStartPoint(), pA
->GetEndPoint() );
1309 // LineInfo used; handle Dash/Dot and fat lines
1310 basegfx::B2DPolygon aPolygon
;
1311 aPolygon
.append(basegfx::B2DPoint(pA
->GetStartPoint().X(), pA
->GetStartPoint().Y()));
1312 aPolygon
.append(basegfx::B2DPoint(pA
->GetEndPoint().X(), pA
->GetEndPoint().Y()));
1313 Impl_handleLineInfoPolyPolygons(pA
->GetLineInfo(), aPolygon
);
1318 case MetaActionType::RECT
:
1320 Impl_writeRect( static_cast<const MetaRectAction
*>(pAction
)->GetRect(), 0, 0 );
1324 case MetaActionType::ROUNDRECT
:
1326 const MetaRoundRectAction
* pA
= static_cast<const MetaRoundRectAction
*>(pAction
);
1328 Impl_writeRect( pA
->GetRect(), pA
->GetHorzRound(), pA
->GetVertRound() );
1332 case MetaActionType::ELLIPSE
:
1334 const MetaEllipseAction
* pA
= static_cast<const MetaEllipseAction
*>(pAction
);
1335 const tools::Rectangle
& rRect
= pA
->GetRect();
1337 Impl_writeEllipse( rRect
.Center(), rRect
.GetWidth() >> 1, rRect
.GetHeight() >> 1 );
1341 case MetaActionType::ARC
:
1342 case MetaActionType::PIE
:
1343 case MetaActionType::CHORD
:
1344 case MetaActionType::POLYGON
:
1346 tools::Polygon aPoly
;
1350 case MetaActionType::ARC
:
1352 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
1353 aPoly
= tools::Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Arc
);
1357 case MetaActionType::PIE
:
1359 const MetaPieAction
* pA
= static_cast<const MetaPieAction
*>(pAction
);
1360 aPoly
= tools::Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Pie
);
1364 case MetaActionType::CHORD
:
1366 const MetaChordAction
* pA
= static_cast<const MetaChordAction
*>(pAction
);
1367 aPoly
= tools::Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Chord
);
1371 case MetaActionType::POLYGON
:
1372 aPoly
= static_cast<const MetaPolygonAction
*>(pAction
)->GetPolygon();
1377 if( aPoly
.GetSize() )
1379 Impl_writePolygon( aPoly
, true );
1384 case MetaActionType::POLYLINE
:
1386 const MetaPolyLineAction
* pA
= static_cast<const MetaPolyLineAction
*>(pAction
);
1387 const tools::Polygon
& rPoly
= pA
->GetPolygon();
1389 if( rPoly
.GetSize() )
1391 if(pA
->GetLineInfo().IsDefault())
1393 Impl_writePolygon( rPoly
, false );
1397 // LineInfo used; handle Dash/Dot and fat lines
1398 Impl_handleLineInfoPolyPolygons(pA
->GetLineInfo(), rPoly
.getB2DPolygon());
1404 case MetaActionType::POLYPOLYGON
:
1406 const MetaPolyPolygonAction
* pA
= static_cast<const MetaPolyPolygonAction
*>(pAction
);
1407 const tools::PolyPolygon
& rPolyPoly
= pA
->GetPolyPolygon();
1409 if( rPolyPoly
.Count() )
1410 Impl_writePolyPolygon( rPolyPoly
, true );
1414 case MetaActionType::GRADIENT
:
1416 const MetaGradientAction
* pA
= static_cast<const MetaGradientAction
*>(pAction
);
1418 tools::Polygon
aPoly( pA
->GetRect() );
1419 Impl_writeGradientEx( aPoly
, pA
->GetGradient() );
1423 case MetaActionType::GRADIENTEX
:
1425 const MetaGradientExAction
* pA
= static_cast<const MetaGradientExAction
*>(pAction
);
1426 Impl_writeGradientEx( pA
->GetPolyPolygon(), pA
->GetGradient() );
1430 case MetaActionType::HATCH
:
1432 const MetaHatchAction
* pA
= static_cast<const MetaHatchAction
*>(pAction
);
1433 GDIMetaFile aTmpMtf
;
1435 mpVDev
->AddHatchActions( pA
->GetPolyPolygon(), pA
->GetHatch(), aTmpMtf
);
1436 Impl_writeActions( aTmpMtf
);
1440 case MetaActionType::Transparent
:
1442 const MetaTransparentAction
* pA
= static_cast<const MetaTransparentAction
*>(pAction
);
1443 const tools::PolyPolygon
& rPolyPoly
= pA
->GetPolyPolygon();
1445 if( rPolyPoly
.Count() )
1447 // convert transparence from percent into 0x00 - 0xff
1448 sal_uInt8 nTransparence
= static_cast<sal_uInt8
>(MinMax( FRound( pA
->GetTransparence() * 2.55 ), 0, 255 ));
1449 Impl_writePolyPolygon( rPolyPoly
, true, nTransparence
);
1454 case MetaActionType::FLOATTRANSPARENT
:
1456 const MetaFloatTransparentAction
* pA
= static_cast<const MetaFloatTransparentAction
*>(pAction
);
1457 GDIMetaFile
aTmpMtf( pA
->GetGDIMetaFile() );
1458 Point
aSrcPt( aTmpMtf
.GetPrefMapMode().GetOrigin() );
1459 const Size
aSrcSize( aTmpMtf
.GetPrefSize() );
1460 const Point
aDestPt( pA
->GetPoint() );
1461 const Size
aDestSize( pA
->GetSize() );
1462 const double fScaleX
= aSrcSize
.Width() ? static_cast<double>(aDestSize
.Width()) / aSrcSize
.Width() : 1.0;
1463 const double fScaleY
= aSrcSize
.Height() ? static_cast<double>(aDestSize
.Height()) / aSrcSize
.Height() : 1.0;
1464 long nMoveX
, nMoveY
;
1466 if( fScaleX
!= 1.0 || fScaleY
!= 1.0 )
1468 aTmpMtf
.Scale( fScaleX
, fScaleY
);
1469 aSrcPt
.setX( FRound( aSrcPt
.X() * fScaleX
) );
1470 aSrcPt
.setY( FRound( aSrcPt
.Y() * fScaleY
) );
1473 nMoveX
= aDestPt
.X() - aSrcPt
.X();
1474 nMoveY
= aDestPt
.Y() - aSrcPt
.Y();
1476 if( nMoveX
|| nMoveY
)
1477 aTmpMtf
.Move( nMoveX
, nMoveY
);
1479 const Gradient
& rGradient
= pA
->GetGradient();
1480 sal_uInt32 nLuminance
= (static_cast<sal_Int32
>(rGradient
.GetStartColor().GetLuminance()) + static_cast<sal_Int32
>(rGradient
.GetEndColor().GetLuminance()) ) >> 1;
1482 sal_uInt8 nOldGlobalTransparency
= mnGlobalTransparency
;
1483 mnGlobalTransparency
= static_cast<sal_uInt8
>(MinMax( nLuminance
, 0, 0xff ));
1486 Impl_writeActions( aTmpMtf
);
1489 mnGlobalTransparency
= nOldGlobalTransparency
;
1493 case MetaActionType::EPS
:
1495 const MetaEPSAction
* pA
= static_cast<const MetaEPSAction
*>(pAction
);
1496 const GDIMetaFile
& aGDIMetaFile( pA
->GetSubstitute() );
1497 bool bFound
= false;
1499 for( size_t j
= 0, nC
= aGDIMetaFile
.GetActionSize(); ( j
< nC
) && !bFound
; j
++ )
1501 const MetaAction
* pSubstAct
= aGDIMetaFile
.GetAction( j
);
1503 if( pSubstAct
->GetType() == MetaActionType::BMPSCALE
)
1506 const MetaBmpScaleAction
* pBmpScaleAction
= static_cast<const MetaBmpScaleAction
*>(pSubstAct
);
1507 Impl_writeImage( BitmapEx(pBmpScaleAction
->GetBitmap()),
1508 pA
->GetPoint(), pA
->GetSize(),
1509 Point(), pBmpScaleAction
->GetBitmap().GetSizePixel(), clipRect
, 1 == bMap
);
1515 case MetaActionType::COMMENT
:
1517 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
1518 const sal_uInt8
* pData
= pA
->GetData();
1520 if( pA
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN") )
1522 const MetaGradientExAction
* pGradAction
= nullptr;
1525 while( !bDone
&& ( ++i
< nCount
) )
1527 pAction
= rMtf
.GetAction( i
);
1529 if( pAction
->GetType() == MetaActionType::GRADIENTEX
)
1530 pGradAction
= static_cast<const MetaGradientExAction
*>(pAction
);
1531 else if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
1532 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") ) )
1539 Impl_writeGradientEx( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient());
1541 else if( pA
->GetComment().equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN") && pData
)
1544 // this comment encapsulates all high level information for a filling that caused
1545 // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1547 SvtGraphicFill aFilling
;
1548 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pData
), pA
->GetDataSize(), StreamMode::READ
);
1550 // read the fill info
1551 ReadSvtGraphicFill( aMemStm
, aFilling
);
1553 // if impl_writeFilling can handle this high level filling, it returns true and we
1554 // skip all meta actions until "XPATHFILL_SEQ_END"
1555 if( Impl_writeFilling( aFilling
) )
1559 while( !bDone
&& ( ++i
< nCount
) )
1561 pAction
= rMtf
.GetAction( i
);
1563 if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
1564 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().equalsIgnoreAsciiCase("XPATHFILL_SEQ_END") ) )
1571 else if( pA
->GetComment().equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN") && pData
)
1574 // this comment encapsulates all high level information for a filling that caused
1575 // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1577 SvtGraphicStroke aStroke
;
1578 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pData
), pA
->GetDataSize(), StreamMode::READ
);
1580 // read the fill info
1581 ReadSvtGraphicStroke( aMemStm
, aStroke
);
1583 // if impl_writeStroke can handle this high level stroke, it returns true and we
1584 // skip all meta actions until "XPATHSTROKE_SEQ_END"
1585 if( Impl_writeStroke( aStroke
) )
1589 while( !bDone
&& ( ++i
< nCount
) )
1591 pAction
= rMtf
.GetAction( i
);
1593 if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
1594 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_END") ) )
1604 case MetaActionType::BMPSCALE
:
1606 const MetaBmpScaleAction
* pA
= static_cast<const MetaBmpScaleAction
*>(pAction
);
1608 Impl_writeImage( BitmapEx(pA
->GetBitmap()),
1609 pA
->GetPoint(), pA
->GetSize(),
1610 Point(), pA
->GetBitmap().GetSizePixel(), clipRect
, 1 == bMap
);
1614 case MetaActionType::BMP
:
1616 const MetaBmpAction
* pA
= static_cast<const MetaBmpAction
*>(pAction
);
1617 Impl_writeImage( BitmapEx(pA
->GetBitmap()),
1618 pA
->GetPoint(), mpVDev
->PixelToLogic( pA
->GetBitmap().GetSizePixel()),
1619 Point(), pA
->GetBitmap().GetSizePixel(), clipRect
, 1 ==bMap
);
1623 case MetaActionType::BMPSCALEPART
:
1625 const MetaBmpScalePartAction
* pA
= static_cast<const MetaBmpScalePartAction
*>(pAction
);
1626 Impl_writeImage( BitmapEx(pA
->GetBitmap()),
1627 pA
->GetDestPoint(), pA
->GetDestSize(),
1628 pA
->GetSrcPoint(), pA
->GetSrcSize(), clipRect
, 1 == bMap
);
1632 case MetaActionType::BMPEX
:
1634 const MetaBmpExAction
* pA
= static_cast<const MetaBmpExAction
*>(pAction
);
1635 Impl_writeImage( pA
->GetBitmapEx(),
1636 pA
->GetPoint(), mpVDev
->PixelToLogic( pA
->GetBitmapEx().GetSizePixel() ),
1637 Point(), pA
->GetBitmapEx().GetSizePixel(), clipRect
, 1 == bMap
);
1641 case MetaActionType::BMPEXSCALE
:
1643 const MetaBmpExScaleAction
* pA
= static_cast<const MetaBmpExScaleAction
*>(pAction
);
1644 Impl_writeImage( pA
->GetBitmapEx(),
1645 pA
->GetPoint(), pA
->GetSize(),
1646 Point(), pA
->GetBitmapEx().GetSizePixel(), clipRect
, 1 == bMap
);
1650 case MetaActionType::BMPEXSCALEPART
:
1652 const MetaBmpExScalePartAction
* pA
= static_cast<const MetaBmpExScalePartAction
*>(pAction
);
1653 Impl_writeImage( pA
->GetBitmapEx(),
1654 pA
->GetDestPoint(), pA
->GetDestSize(),
1655 pA
->GetSrcPoint(), pA
->GetSrcSize(), clipRect
, 1 == bMap
);
1659 case MetaActionType::TEXT
:
1661 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
1662 Impl_writeText( pA
->GetPoint(), pA
->GetText().copy( pA
->GetIndex(), pA
->GetLen() ), nullptr, 0);
1666 case MetaActionType::TEXTRECT
:
1668 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
1669 Impl_writeText( pA
->GetRect().TopLeft(), pA
->GetText(), nullptr, 0 );
1673 case MetaActionType::TEXTARRAY
:
1675 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
1676 Impl_writeText( pA
->GetPoint(), pA
->GetText().copy( pA
->GetIndex(), pA
->GetLen() ), pA
->GetDXArray(), 0 );
1680 case MetaActionType::STRETCHTEXT
:
1682 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
1683 Impl_writeText( pA
->GetPoint(), pA
->GetText().copy( pA
->GetIndex(), pA
->GetLen() ), nullptr, pA
->GetWidth() );
1687 case MetaActionType::ISECTRECTCLIPREGION
:
1689 const MetaISectRectClipRegionAction
* pA
= static_cast<const MetaISectRectClipRegionAction
*>(pAction
);
1690 clipRect
= pA
->GetRect();
1693 case MetaActionType::CLIPREGION
:
1694 case MetaActionType::ISECTREGIONCLIPREGION
:
1695 case MetaActionType::MOVECLIPREGION
:
1697 const_cast<MetaAction
*>(pAction
)->Execute( mpVDev
);
1701 case MetaActionType::MAPMODE
:
1706 case MetaActionType::REFPOINT
:
1707 case MetaActionType::LINECOLOR
:
1708 case MetaActionType::FILLCOLOR
:
1709 case MetaActionType::TEXTLINECOLOR
:
1710 case MetaActionType::TEXTFILLCOLOR
:
1711 case MetaActionType::TEXTCOLOR
:
1712 case MetaActionType::TEXTALIGN
:
1713 case MetaActionType::FONT
:
1714 case MetaActionType::PUSH
:
1715 case MetaActionType::POP
:
1716 case MetaActionType::LAYOUTMODE
:
1718 const_cast<MetaAction
*>(pAction
)->Execute( mpVDev
);
1722 case MetaActionType::RASTEROP
:
1723 case MetaActionType::MASK
:
1724 case MetaActionType::MASKSCALE
:
1725 case MetaActionType::MASKSCALEPART
:
1726 case MetaActionType::WALLPAPER
:
1727 case MetaActionType::TEXTLINE
:
1729 // !!! >>> we don't want to support these actions
1740 void Writer::Impl_addStraightLine( BitStream
& rBits
, Point
& rLastPoint
,
1741 const double P2x
, const double P2y
)
1743 Point
aPoint( FRound(P2x
), FRound(P2y
) );
1745 Impl_addStraightEdgeRecord( rBits
, Int16_(aPoint
.X() - rLastPoint
.X()),Int16_(aPoint
.Y() - rLastPoint
.Y()));
1746 rLastPoint
= aPoint
;
1751 void Writer::Impl_addQuadBezier( BitStream
& rBits
, Point
& rLastPoint
,
1752 const double P2x
, const double P2y
,
1753 const double P3x
, const double P3y
)
1756 Point
aControlPoint( FRound(P2x
), FRound(P2y
) );
1757 Point
aAnchorPoint( FRound(P3x
), FRound(P3y
) );
1759 Impl_addCurvedEdgeRecord( rBits
,
1760 Int16_(aControlPoint
.X() - rLastPoint
.X()),Int16_(aControlPoint
.Y() - rLastPoint
.Y()),
1761 Int16_(aAnchorPoint
.X() - aControlPoint
.X()),Int16_(aAnchorPoint
.Y() - aControlPoint
.Y()) );
1762 rLastPoint
= aAnchorPoint
;
1766 /* Approximate given cubic bezier curve by quadratic bezier segments */
1767 void Writer::Impl_quadBezierApprox( BitStream
& rBits
,
1770 const double P1x
, const double P1y
,
1771 const double P2x
, const double P2y
,
1772 const double P3x
, const double P3y
,
1773 const double P4x
, const double P4y
)
1775 // Check for degenerate case, where the given cubic bezier curve
1776 // is already quadratic: P4 == 3P3 - 3P2 + P1
1777 if( P4x
== 3.0*P3x
- 3.0*P2x
+ P1x
&&
1778 P4y
== 3.0*P3y
- 3.0*P2y
+ P1y
)
1780 Impl_addQuadBezier( rBits
, rLastPoint
,
1781 3.0/2.0*P2x
- 1.0/2.0*P1x
, 3.0/2.0*P2y
- 1.0/2.0*P1y
,
1786 // Create quadratic segment for given cubic:
1787 // Start and end point must coincide, determine quadratic control
1788 // point in such a way that it lies on the intersection of the
1789 // tangents at start and end point, resp. Thus, both cubic and
1790 // quadratic curve segments will match in 0th and 1st derivative
1791 // at the start and end points
1793 // Intersection of P2P1 and P4P3
1794 // (P2y-P4y)(P3x-P4x)-(P2x-P4x)(P3y-P4y)
1795 // lambda = -------------------------------------
1796 // (P1x-P2x)(P3y-P4y)-(P1y-P2y)(P3x-P4x)
1798 // Intersection point IP is now
1799 // IP = P2 + lambda(P1-P2)
1801 const double nominator( (P2y
-P4y
)*(P3x
-P4x
) - (P2x
-P4x
)*(P3y
-P4y
) );
1802 const double denominator( (P1x
-P2x
)*(P3y
-P4y
) - (P1y
-P2y
)*(P3x
-P4x
) );
1803 const double lambda( nominator
/ denominator
);
1805 const double IPx( P2x
+ lambda
*( P1x
- P2x
) );
1806 const double IPy( P2y
+ lambda
*( P1y
- P2y
) );
1808 // Introduce some alias names: quadratic start point is P1, end
1809 // point is P4, control point is IP
1810 const double QP1x( P1x
);
1811 const double QP1y( P1y
);
1812 const double QP2x( IPx
);
1813 const double QP2y( IPy
);
1814 const double QP3x( P4x
);
1815 const double QP3y( P4y
);
1817 // Adapted bezier flatness test (lecture notes from R. Schaback,
1818 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
1820 // ||C(t) - Q(t)|| <= max ||c_j - q_j||
1823 // In this case, we don't need the distance from the cubic bezier
1824 // to a straight line, but to a quadratic bezier. The c_j's are
1825 // the cubic bezier's bernstein coefficients, the q_j's the
1826 // quadratic bezier's. We have the c_j's given, the q_j's can be
1827 // calculated from QPi like this (sorry, mixed index notation, we
1828 // use [1,n], formulas use [0,n-1]):
1831 // q_1 = 1/3 QP1 + 2/3 QP2
1832 // q_2 = 2/3 QP2 + 1/3 QP3
1835 // We can drop case 0 and 3, since there the curves coincide
1836 // (distance is zero)
1838 // calculate argument of max for j=1 and j=2
1839 const double fJ1x( P2x
- 1.0/3.0*QP1x
- 2.0/3.0*QP2x
);
1840 const double fJ1y( P2y
- 1.0/3.0*QP1y
- 2.0/3.0*QP2y
);
1841 const double fJ2x( P3x
- 2.0/3.0*QP2x
- 1.0/3.0*QP3x
);
1842 const double fJ2y( P3y
- 2.0/3.0*QP2y
- 1.0/3.0*QP3y
);
1844 // stop if distance from cubic curve is guaranteed to be bounded by d
1845 // Should denominator be 0: then P1P2 and P3P4 are parallel (P1P2^T R[90,P3P4] = 0.0),
1846 // meaning that either we have a straight line or an inflexion point (see else block below)
1847 if( 0.0 != denominator
&&
1848 ::std::max( fJ1x
*fJ1x
+ fJ1y
*fJ1y
,
1849 fJ2x
*fJ2x
+ fJ2y
*fJ2y
) < d2
)
1851 // requested resolution reached.
1852 // Add end points to output file.
1853 // order is preserved, since this is so to say depth first traversal.
1854 Impl_addQuadBezier( rBits
, rLastPoint
,
1860 // Maybe subdivide further
1862 // This is for robustness reasons, since the line intersection
1863 // method below gets instable if the curve gets closer to a
1864 // straight line. If the given cubic bezier does not deviate by
1865 // more than d/4 from a straight line, either:
1866 // - take the line (that's what we do here)
1867 // - express the line by a quadratic bezier
1869 // Perform bezier flatness test (lecture notes from R. Schaback,
1870 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
1872 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
1875 // What is calculated here is an upper bound to the distance from
1876 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
1877 // curve. We can drop 0 and n from the running indices, since the
1878 // argument of max becomes zero for those cases.
1879 const double fJ1x2( P2x
- P1x
- 1.0/3.0*(P4x
- P1x
) );
1880 const double fJ1y2( P2y
- P1y
- 1.0/3.0*(P4y
- P1y
) );
1881 const double fJ2x2( P3x
- P1x
- 2.0/3.0*(P4x
- P1x
) );
1882 const double fJ2y2( P3y
- P1y
- 2.0/3.0*(P4y
- P1y
) );
1884 // stop if distance from line is guaranteed to be bounded by d/4
1885 if( ::std::max( fJ1x2
*fJ1x2
+ fJ1y2
*fJ1y2
,
1886 fJ2x2
*fJ2x2
+ fJ2y2
*fJ2y2
) < d2
/16.0 )
1888 // do not subdivide further, add straight line instead
1889 Impl_addStraightLine( rBits
, rLastPoint
, P4x
, P4y
);
1893 // deCasteljau bezier arc, split at t=0.5
1894 // Foley/vanDam, p. 508
1895 const double L1x( P1x
), L1y( P1y
);
1896 const double L2x( (P1x
+ P2x
)*0.5 ), L2y( (P1y
+ P2y
)*0.5 );
1897 const double Hx ( (P2x
+ P3x
)*0.5 ), Hy ( (P2y
+ P3y
)*0.5 );
1898 const double L3x( (L2x
+ Hx
)*0.5 ), L3y( (L2y
+ Hy
)*0.5 );
1899 const double R4x( P4x
), R4y( P4y
);
1900 const double R3x( (P3x
+ P4x
)*0.5 ), R3y( (P3y
+ P4y
)*0.5 );
1901 const double R2x( (Hx
+ R3x
)*0.5 ), R2y( (Hy
+ R3y
)*0.5 );
1902 const double R1x( (L3x
+ R2x
)*0.5 ), R1y( (L3y
+ R2y
)*0.5 );
1903 const double L4x( R1x
), L4y( R1y
);
1905 // subdivide further
1906 Impl_quadBezierApprox(rBits
, rLastPoint
, d2
, L1x
, L1y
, L2x
, L2y
, L3x
, L3y
, L4x
, L4y
);
1907 Impl_quadBezierApprox(rBits
, rLastPoint
, d2
, R1x
, R1y
, R2x
, R2y
, R3x
, R3y
, R4x
, R4y
);
1913 Reference
< XBreakIterator
> const & Writer::Impl_GetBreakIterator()
1915 if ( !mxBreakIterator
.is() )
1917 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1918 mxBreakIterator
= BreakIterator::create(xContext
);
1920 return mxBreakIterator
;
1923 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */