1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_filter.hxx"
26
27 #include <com/sun/star/i18n/ScriptType.hpp>
28 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
29 #include <comphelper/processfactory.hxx>
30 #include "swfwriter.hxx"
31 #include <vcl/metaact.hxx>
32 #include <vcl/gdimtf.hxx>
33 #include <vcl/bmpacc.hxx>
34 #include <vcl/virdev.hxx>
35 #include <vcl/metric.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
37 #include <svtools/filter.hxx>
38 #include <vcl/graphictools.hxx>
39
40 #ifndef _ZLIB_H
41 #ifdef SYSTEM_ZLIB
42 #include <zlib.h>
43 #else
44 #include <external/zlib/zlib.h>
45 #endif
46 #endif
47
48 #include <vcl/salbtype.hxx>
49 #include <basegfx/polygon/b2dpolygon.hxx>
50 #include <basegfx/polygon/b2dpolypolygon.hxx>
51
52 using namespace ::swf;
53 using namespace ::std;
54 using namespace ::rtl;
55 using namespace ::com::sun::star::i18n;
56 using namespace ::com::sun::star::uno;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::io;
59 using namespace ::com::sun::star::beans;
60
61 extern sal_uInt16 getMaxBitsUnsigned( sal_uInt32 nValue );
62 extern sal_uInt16 getMaxBitsSigned( sal_Int32 nValue );
63
64 static MapMode aTWIPSMode( MAP_TWIP );
65 static MapMode a100thmmMode( MAP_100TH_MM );
66
67 // -----------------------------------------------------------------------------
68
map(const Point & rPoint) const69 Point Writer::map( const Point& rPoint ) const
70 {
71 const MapMode& aSourceMapMode = mpVDev->GetMapMode();
72
73 Point retPoint = mpVDev->LogicToLogic( rPoint, &aSourceMapMode, &aTWIPSMode );
74
75 // AS: Produces a 'possible loss of data' warning that we can't fix without
76 // hurting code readability.
77 retPoint.X() = (long)( retPoint.X() * mnDocXScale );
78 retPoint.Y() = (long)( retPoint.Y() * mnDocYScale );
79
80 return retPoint;
81 }
82
83 // -----------------------------------------------------------------------------
84
map(const Size & rSize) const85 Size Writer::map( const Size& rSize ) const
86 {
87 const MapMode& aSourceMapMode = mpVDev->GetMapMode();
88
89 Size retSize = mpVDev->LogicToLogic( rSize, &aSourceMapMode, &aTWIPSMode );
90
91 // AS: Produces a 'possible loss of data' warning that we can't fix without
92 // hurting code readability.
93 retSize.Width() = (long)( retSize.Width() * mnDocXScale );
94 retSize.Height() = (long)( retSize.Height() * mnDocYScale );
95
96 return retSize;
97 }
98
99 // -----------------------------------------------------------------------------
100
map(PolyPolygon & rPolyPolygon) const101 void Writer::map( PolyPolygon& rPolyPolygon ) const
102 {
103 const sal_uInt16 nPolyCount = rPolyPolygon.Count();
104 if( nPolyCount )
105 {
106 sal_uInt16 nPoly, nPoint, nPointCount;
107 for( nPoly = 0; nPoly < nPolyCount; nPoly++ )
108 {
109 Polygon& rPoly = rPolyPolygon[nPoly];
110 nPointCount = rPoly.GetSize();
111
112 for( nPoint = 0; nPoint < nPointCount; nPoint++ )
113 {
114 rPoly[nPoint] = map( rPoly[nPoint] );
115 }
116 }
117 }
118 }
119
120 // -----------------------------------------------------------------------------
121
mapRelative(sal_Int32 n100thMM) const122 sal_Int32 Writer::mapRelative( sal_Int32 n100thMM ) const
123 {
124 MapMode aSourceMapMode( mpVDev->GetMapMode() );
125 aSourceMapMode.SetOrigin( Point() );
126
127 sal_Int32 nTwips = mpVDev->LogicToLogic( Point( n100thMM, n100thMM ), &aSourceMapMode, &aTWIPSMode ).X();
128 return nTwips;
129 }
130
131 // -----------------------------------------------------------------------------
132
133 /**
134 */
Impl_addPolygon(BitStream & rBits,const Polygon & rPoly,sal_Bool bFilled)135 void Writer::Impl_addPolygon( BitStream& rBits, const Polygon& rPoly, sal_Bool bFilled )
136 {
137 Point aLastPoint( rPoly[0] );
138
139 Impl_addShapeRecordChange( rBits, _Int16(aLastPoint.X()),_Int16(aLastPoint.Y()), bFilled );
140
141 sal_uInt16 i = 0, nSize = rPoly.GetSize();
142
143 double d = 16.0f;
144
145 // points
146 while( ( i + 1 ) < nSize )
147 {
148 if( ( i + 3 ) < nSize )
149 {
150 PolyFlags P1( rPoly.GetFlags( i ) );
151 PolyFlags P4( rPoly.GetFlags( i + 3 ) );
152
153 if( ( POLY_NORMAL == P1 || POLY_SMOOTH == P1 || POLY_SYMMTR == P1 ) &&
154 ( POLY_CONTROL == rPoly.GetFlags( i + 1 ) ) &&
155 ( POLY_CONTROL == rPoly.GetFlags( i + 2 ) ) &&
156 ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) )
157 {
158 Impl_quadBezierApprox( rBits, aLastPoint, d*d,
159 rPoly.GetPoint( i ).X(), rPoly.GetPoint( i ).Y(),
160 rPoly.GetPoint( i+1 ).X(), rPoly.GetPoint( i+1 ).Y(),
161 rPoly.GetPoint( i+2 ).X(), rPoly.GetPoint( i+2 ).Y(),
162 rPoly.GetPoint( i+3 ).X(), rPoly.GetPoint( i+3 ).Y() );
163 i += 3;
164 continue;
165 }
166 }
167
168 ++i;
169
170 const Point aPolyPoint( rPoly[ i ] );
171 if( aPolyPoint != aLastPoint )
172 {
173 Impl_addStraightEdgeRecord( rBits, _Int16(aPolyPoint.X() - aLastPoint.X()),_Int16(aPolyPoint.Y() - aLastPoint.Y()));
174 aLastPoint = aPolyPoint;
175 }
176 }
177
178 if( bFilled && (rPoly[0] != rPoly[nSize-1]))
179 {
180 const Point aPolyPoint( rPoly[ 0 ] );
181 if( aPolyPoint != aLastPoint )
182 {
183 Impl_addStraightEdgeRecord( rBits, _Int16(aPolyPoint.X() - aLastPoint.X()),_Int16(aPolyPoint.Y() - aLastPoint.Y()));
184 }
185 }
186 }
187
188 // -----------------------------------------------------------------------------
189
190 /** exports a style change record with a move to (x,y) and depending on bFilled a line style 1 or fill style 1
191 */
Impl_addShapeRecordChange(BitStream & rBits,sal_Int16 dx,sal_Int16 dy,sal_Bool bFilled)192 void Writer::Impl_addShapeRecordChange( BitStream& rBits, sal_Int16 dx, sal_Int16 dy, sal_Bool bFilled )
193 {
194 rBits.writeUB( 0, 1 ); // TypeFlag
195 rBits.writeUB( 0, 1 ); // StateNewStyles
196 rBits.writeUB( !bFilled, 1 ); // StateLineStyle
197 rBits.writeUB( 0, 1 ); // StateFillStyle0
198 rBits.writeUB( bFilled, 1 ); // StateFillStyle1
199 rBits.writeUB( 1, 1 ); // StateMoveTo
200
201 sal_uInt16 nMoveBits = max( getMaxBitsSigned( dx ), getMaxBitsSigned( dy ) );
202
203 rBits.writeUB( nMoveBits, 5 ); // Number of bits per value
204 // TODO: Optimize horizontal and vertical lines
205 rBits.writeSB( dx, nMoveBits ); // DeltaX
206 rBits.writeSB( dy, nMoveBits ); // DeltaY
207
208 rBits.writeUB( 1, 1 ); // set FillStyle1 or LineStyle to 1
209 }
210
211 // -----------------------------------------------------------------------------
212
213 /** exports a straight edge record
214 */
Impl_addStraightEdgeRecord(BitStream & rBits,sal_Int16 dx,sal_Int16 dy)215 void Writer::Impl_addStraightEdgeRecord( BitStream& rBits, sal_Int16 dx, sal_Int16 dy )
216 {
217 rBits.writeUB( 1, 1 ); // TypeFlag
218 rBits.writeUB( 1, 1 ); // StraightFlag
219
220 sal_uInt16 nBits = max( getMaxBitsSigned( dx ), getMaxBitsSigned( dy ) );
221
222 rBits.writeUB( nBits - 2, 4 ); // Number of bits per value
223
224 if( (dx != 0) && (dy != 0) )
225 {
226 rBits.writeUB( 1, 1 ); // GeneralLineFlag
227 rBits.writeSB( dx, nBits ); // DeltaX
228 rBits.writeSB( dy, nBits ); // DeltaY
229 }
230 else
231 {
232 rBits.writeUB( 0, 1 );
233 rBits.writeUB( ( dx == 0 ), 1 );
234 if( dx == 0 )
235 {
236 rBits.writeSB( dy, nBits ); // DeltaY
237 }
238 else
239 {
240 rBits.writeSB( dx, nBits ); // DeltaX
241 }
242 }
243 }
244
245 // -----------------------------------------------------------------------------
246
247 /** exports a curved edge record
248 */
Impl_addCurvedEdgeRecord(BitStream & rBits,sal_Int16 control_dx,sal_Int16 control_dy,sal_Int16 anchor_dx,sal_Int16 anchor_dy)249 void Writer::Impl_addCurvedEdgeRecord( BitStream& rBits, sal_Int16 control_dx, sal_Int16 control_dy, sal_Int16 anchor_dx, sal_Int16 anchor_dy )
250 {
251 rBits.writeUB( 1, 1 ); // TypeFlag
252 rBits.writeUB( 0, 1 ); // CurvedFlag
253
254 sal_uInt8 nBits = static_cast<sal_uInt8>(
255 max( getMaxBitsSigned( control_dx ),
256 max( getMaxBitsSigned( control_dy ),
257 max( getMaxBitsSigned( anchor_dx ),
258 max( getMaxBitsSigned( anchor_dy ), (sal_uInt16)3 ) ) ) ) );
259
260 rBits.writeUB( nBits - 2, 4 ); // Number of bits per value
261
262 rBits.writeSB( control_dx, nBits ); // DeltaX
263 rBits.writeSB( control_dy, nBits ); // DeltaY
264 rBits.writeSB( anchor_dx, nBits ); // DeltaX
265 rBits.writeSB( anchor_dy, nBits ); // DeltaY
266 }
267
268 // -----------------------------------------------------------------------------
269
270 /** exports a end shape record
271 */
Impl_addEndShapeRecord(BitStream & rBits)272 void Writer::Impl_addEndShapeRecord( BitStream& rBits )
273 {
274 rBits.writeUB( 0, 6 );
275 }
276
277 // -----------------------------------------------------------------------------
278
Impl_writePolygon(const Polygon & rPoly,sal_Bool bFilled)279 void Writer::Impl_writePolygon( const Polygon& rPoly, sal_Bool bFilled )
280 {
281 PolyPolygon aPolyPoly( rPoly );
282 Impl_writePolyPolygon( aPolyPoly, bFilled );
283 }
284
285 // -----------------------------------------------------------------------------
286
Impl_writePolygon(const Polygon & rPoly,sal_Bool bFilled,const Color & rFillColor,const Color & rLineColor)287 void Writer::Impl_writePolygon( const Polygon& rPoly, sal_Bool bFilled, const Color& rFillColor, const Color& rLineColor )
288 {
289 PolyPolygon aPolyPoly( rPoly );
290 Impl_writePolyPolygon( aPolyPoly, bFilled, rFillColor, rLineColor );
291 }
292
293 // -----------------------------------------------------------------------------
294
Impl_writePolyPolygon(const PolyPolygon & rPolyPoly,sal_Bool bFilled,sal_uInt8 nTransparence)295 void Writer::Impl_writePolyPolygon( const PolyPolygon& rPolyPoly, sal_Bool bFilled, sal_uInt8 nTransparence /* = 0 */ )
296 {
297 Color aLineColor( mpVDev->GetLineColor() );
298 if( 0 == aLineColor.GetTransparency() )
299 aLineColor.SetTransparency( nTransparence );
300 Color aFillColor( mpVDev->GetFillColor() );
301 if( 0 == aFillColor.GetTransparency() )
302 aFillColor.SetTransparency( nTransparence );
303 Impl_writePolyPolygon(rPolyPoly, bFilled, aFillColor, aLineColor );
304 }
305
306 // -----------------------------------------------------------------------------
307
Impl_writePolyPolygon(const PolyPolygon & rPolyPoly,sal_Bool bFilled,const Color & rFillColor,const Color & rLineColor)308 void Writer::Impl_writePolyPolygon( const PolyPolygon& rPolyPoly, sal_Bool bFilled, const Color& rFillColor, const Color& rLineColor )
309 {
310 PolyPolygon aPolyPoly( rPolyPoly );
311
312 if( aPolyPoly.Count() )
313 {
314 map( aPolyPoly );
315
316 if( mpClipPolyPolygon )
317 rPolyPoly.GetIntersection( *mpClipPolyPolygon, aPolyPoly );
318
319 sal_uInt16 nID;
320 if( bFilled )
321 {
322 Color aFillColor( rFillColor );
323 if( 0 != mnGlobalTransparency )
324 aFillColor.SetTransparency( mnGlobalTransparency );
325
326 FillStyle aStyle( aFillColor );
327 nID = defineShape( aPolyPoly, aStyle );
328 }
329 else
330 {
331 Color aLineColor( rLineColor );
332 if( 0 != mnGlobalTransparency )
333 aLineColor.SetTransparency( mnGlobalTransparency );
334
335 nID = defineShape( aPolyPoly, 1, aLineColor );
336 }
337 maShapeIds.push_back( nID );
338 }
339 }
340
341 // -----------------------------------------------------------------------------
342
343 /** a gradient is a transition from one color to another, rendered inside a given polypolygon */
Impl_writeGradientEx(const PolyPolygon & rPolyPoly,const Gradient & rGradient)344 void Writer::Impl_writeGradientEx( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
345 {
346 if( rPolyPoly.Count() )
347 {
348 PolyPolygon aPolyPolygon( rPolyPoly );
349 map( aPolyPolygon );
350
351 if( (rGradient.GetStyle() == GRADIENT_LINEAR && rGradient.GetAngle() == 900) || (rGradient.GetStyle() == GRADIENT_RADIAL) )
352 {
353 const Rectangle aBoundRect( aPolyPolygon.GetBoundRect() );
354
355 FillStyle aFillStyle( aBoundRect, rGradient );
356
357 sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
358 maShapeIds.push_back( nShapeId );
359 }
360 else
361 {
362 setClipping( &aPolyPolygon );
363
364 // render the gradient filling to simple polygons
365 {
366 GDIMetaFile aTmpMtf;
367 mpVDev->AddGradientActions( aPolyPolygon.GetBoundRect(), rGradient, aTmpMtf );
368 Impl_writeActions( aTmpMtf );
369 }
370
371 setClipping( NULL );
372 }
373 }
374 }
375
376 // -----------------------------------------------------------------------------
377
setClipping(const PolyPolygon * pClipPolyPolygon)378 void Writer::setClipping( const PolyPolygon* pClipPolyPolygon )
379 {
380 mpClipPolyPolygon = pClipPolyPolygon;
381 }
382
383 // -----------------------------------------------------------------------------
384
385 // AS: Just comparing fonts straight up is too literal. There are some
386 // differences in font that actually require different glyphs to be defined,
387 // and some that don't. This function is meant to capture all the differences
388 // that we care about.
compare_fonts_for_me(const Font & rFont1,const Font & rFont2)389 bool compare_fonts_for_me(const Font& rFont1, const Font& rFont2)
390 {
391 return rFont1.GetName() == rFont2.GetName() &&
392 rFont1.GetWeight() == rFont2.GetWeight() &&
393 rFont1.GetItalic() == rFont2.GetItalic() &&
394 rFont1.IsOutline() == rFont2.IsOutline() &&
395 rFont1.IsShadow() == rFont2.IsShadow() &&
396 rFont1.GetRelief() == rFont2.GetRelief();
397 }
398
399 // -----------------------------------------------------------------------------
400
Impl_getFont(const Font & rFont)401 FlashFont& Writer::Impl_getFont( const Font& rFont )
402 {
403 FontMap::iterator aIter( maFonts.begin() );
404 const FontMap::iterator aEnd( maFonts.end() );
405
406 while( aIter != aEnd )
407 {
408 const Font tempFont = (*aIter)->getFont();
409 if( compare_fonts_for_me(tempFont, rFont) )
410 {
411 return **aIter;
412 }
413
414 aIter++;
415 }
416
417 FlashFont* pFont = new FlashFont( rFont, createID() );
418 maFonts.push_back( pFont );
419 return *pFont;
420 }
421
422 // -----------------------------------------------------------------------------
423
Impl_writeText(const Point & rPos,const String & rText,const sal_Int32 * pDXArray,long nWidth)424 void Writer::Impl_writeText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth )
425 {
426 const FontMetric aMetric( mpVDev->GetFontMetric() );
427
428 bool bTextSpecial = aMetric.IsShadow() || aMetric.IsOutline() || (aMetric.GetRelief() != RELIEF_NONE);
429
430 if( !bTextSpecial )
431 {
432 Impl_writeText( rPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
433 }
434 else
435 {
436 if( aMetric.GetRelief() != RELIEF_NONE )
437 {
438 Color aReliefColor( COL_LIGHTGRAY );
439 Color aTextColor( mpVDev->GetTextColor() );
440
441 if ( aTextColor.GetColor() == COL_BLACK )
442 aTextColor = Color( COL_WHITE );
443
444 if ( aTextColor.GetColor() == COL_WHITE )
445 aReliefColor = Color( COL_BLACK );
446
447
448 Point aPos( rPos );
449 Point aOffset( 6,6 );
450
451 if ( aMetric.GetRelief() == RELIEF_ENGRAVED )
452 {
453 aPos -= aOffset;
454 }
455 else
456 {
457 aPos += aOffset;
458 }
459
460 Impl_writeText( aPos, rText, pDXArray, nWidth, aReliefColor );
461 Impl_writeText( rPos, rText, pDXArray, nWidth, aTextColor );
462 }
463 else
464 {
465 if( aMetric.IsShadow() )
466 {
467 long nOff = 1 + ((aMetric.GetLineHeight()-24)/24);
468 if ( aMetric.IsOutline() )
469 nOff += 6;
470
471 Color aTextColor( mpVDev->GetTextColor() );
472 Color aShadowColor = Color( COL_BLACK );
473
474 if ( (aTextColor.GetColor() == COL_BLACK) || (aTextColor.GetLuminance() < 8) )
475 aShadowColor = Color( COL_LIGHTGRAY );
476
477 Point aPos( rPos );
478 aPos += Point( nOff, nOff );
479 Impl_writeText( aPos, rText, pDXArray, nWidth, aShadowColor );
480
481 if( !aMetric.IsOutline() )
482 {
483 Impl_writeText( rPos, rText, pDXArray, nWidth, aTextColor );
484 }
485 }
486
487 if( aMetric.IsOutline() )
488 {
489 Point aPos = rPos + Point( -6, -6 );
490 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
491 aPos = rPos + Point(+6,+6);
492 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
493 aPos = rPos + Point(-6,+0);
494 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
495 aPos = rPos + Point(-6,+6);
496 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
497 aPos = rPos + Point(+0,+6);
498 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
499 aPos = rPos + Point(+0,-6);
500 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
501 aPos = rPos + Point(+6,-1);
502 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
503 aPos = rPos + Point(+6,+0);
504 Impl_writeText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
505
506 Impl_writeText( rPos, rText, pDXArray, nWidth, Color( COL_WHITE ) );
507 }
508 }
509 }
510 }
511
Impl_writeText(const Point & rPos,const String & rText,const sal_Int32 * pDXArray,long nWidth,Color aTextColor)512 void Writer::Impl_writeText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth, Color aTextColor )
513 {
514 sal_uInt32 nLen = rText.Len();
515
516 if( !nLen )
517 return;
518
519 const bool bRTL = (mpVDev->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL) != 0;
520
521 sal_Int16 nScriptType = ScriptType::LATIN;
522 Reference < XBreakIterator > xBI( Impl_GetBreakIterator() );
523 if( xBI.is() )
524 {
525 const OUString oText( rText );
526 nScriptType = xBI->getScriptType( oText, 0 );
527 }
528
529 // if the text is either right to left or complex or asian, we
530 // ask the output device for a polygon representation.
531 // On complex and asian text, each unicode character can have
532 // different glyph representation, based on context. Also positioning
533 // is not trivial so we let the output device do it for us.
534 if( bRTL || (nScriptType != ScriptType::LATIN) )
535 {
536 // todo: optimize me as this will generate a huge amount of duplicate polygons
537 PolyPolygon aPolyPoygon;
538 mpVDev->GetTextOutline( aPolyPoygon, rText, 0, 0, (sal_uInt16)nLen, sal_True, nWidth, pDXArray );
539 aPolyPoygon.Translate( rPos );
540 Impl_writePolyPolygon( aPolyPoygon, sal_True, aTextColor, aTextColor );
541 }
542 else
543 {
544 Size aNormSize;
545 sal_Int32* pOwnArray;
546 sal_Int32* pDX;
547
548 // get text sizes
549 if( pDXArray )
550 {
551 pOwnArray = NULL;
552 aNormSize = Size( mpVDev->GetTextWidth( rText ), 0 );
553 pDX = (sal_Int32*) pDXArray;
554 }
555 else
556 {
557 pOwnArray = new sal_Int32[ nLen ];
558 aNormSize = Size( mpVDev->GetTextArray( rText, pOwnArray ), 0 );
559 pDX = pOwnArray;
560 }
561
562 if( nLen > 1 )
563 {
564 aNormSize.Width() = pDX[ nLen - 2 ] + mpVDev->GetTextWidth( rText.GetChar( (sal_uInt16) nLen - 1 ) );
565
566 if( nWidth && aNormSize.Width() && ( nWidth != aNormSize.Width() ) )
567 {
568 const double fFactor = (double) nWidth / aNormSize.Width();
569
570 sal_uInt32 i;
571 for( i = 0; i < ( nLen - 1 ); i++ )
572 pDX[ i ] = FRound( pDX[ i ] * fFactor );
573 }
574 }
575
576 Font aOldFont( mpVDev->GetFont() );
577 Point aBaseLinePos( rPos );
578
579 Font aFont(aOldFont);
580 short nOrientation = aFont.GetOrientation();
581 aFont.SetOrientation( 0 );
582 aFont.SetUnderline(UNDERLINE_NONE);
583 aFont.SetStrikeout(STRIKEOUT_NONE);
584 mpVDev->SetFont( aFont );
585
586 const FontMetric aMetric( mpVDev->GetFontMetric() );
587
588 FlashFont& rFlashFont = Impl_getFont( aFont );
589
590 // always adjust text position to match baseline alignment
591 switch( aOldFont.GetAlign() )
592 {
593 case( ALIGN_TOP ):
594 aBaseLinePos.Y() += aMetric.GetAscent();
595 break;
596
597 case( ALIGN_BOTTOM ):
598 aBaseLinePos.Y() -= aMetric.GetDescent();
599 break;
600
601 default:
602 break;
603 }
604
605 // get mapped text position
606 const Point aPt( map( aBaseLinePos ) );
607
608 // write text element
609
610 /* test code to create a bound rect, not really working for rotated text
611 Size aTextSize( map( Size( mpVDev->GetTextWidth( rText ), mpVDev->GetTextHeight() ) ) );
612 Point aMetricPoint( map( Point( aMetric.GetLeading(), aMetric.GetAscent() ) ) );
613
614 Point aTmpPoint( map( Point( - aMetric.GetLeading(), - aMetric.GetAscent() ) ) ); ;
615 Rectangle aTmpRectangle(aTmpPoint, aTextSize );
616 Polygon aPoly( aTmpRectangle );
617
618 aPoly.Rotate( aTmpPoint, (sal_uInt16) nOrientation );
619
620 Rectangle aTextBoundRect( aPoly.GetBoundRect() );
621
622 aPoly.Move( aPt.X(), aPt.Y() - map( Size( 0, aMetric.GetDescent() ) ).Height() );
623
624 */
625
626 #if 0 // makes the calculated bound rect visible for debuging
627 {
628 Polygon aTmpPoly( aPoly );
629 sal_uInt16 nID = FlashGeometryExporter::writePolygonShape( aMovieStream, aTmpPoly, false, Color(COL_MAGENTA), Color(COL_MAGENTA), mpClipPolyPolygon );
630 ImplPlaceObject( nID );
631 }
632 #endif
633
634 // CL: This is still a hack until we figure out how to calculate a correct bound rect
635 // for rotatet text
636 Rectangle textBounds( 0, 0, static_cast<long>(mnDocWidth*mnDocXScale), static_cast<long>(mnDocHeight*mnDocYScale) );
637 double scale = 1.0;
638
639 // scale width if we have a stretched text
640 if( 0 != aFont.GetSize().Width() )
641 {
642 Font aTmpFont( aFont );
643 aTmpFont.SetWidth(0);
644 mpVDev->SetFont( aTmpFont );
645
646 const FontMetric aMetric2( mpVDev->GetFontMetric() );
647 mpVDev->SetFont( aFont );
648
649 const long n1 = aFont.GetSize().Width();
650 const long n2 = aMetric2.GetSize().Width();
651 scale = (double)n1 / (double)n2;
652 }
653
654 basegfx::B2DHomMatrix m(basegfx::tools::createRotateB2DHomMatrix(static_cast<double>(nOrientation) * F_PI1800));
655 m.translate( double(aPt.X() / scale), double(aPt.Y()) );
656 m.scale( scale, scale );
657
658 sal_Int16 nHeight = _Int16( map( Size( 0, aFont.GetHeight() ) ).Height() );
659
660 startTag( TAG_DEFINETEXT );
661
662 sal_uInt16 nTextId = createID();
663
664 mpTag->addUI16( nTextId );
665 mpTag->addRect( textBounds );
666 mpTag->addMatrix( m );
667
668 sal_uInt8 nGlyphBits = 16;
669 sal_uInt8 nAdvanceBits = 16;
670
671 mpTag->addUI8( nGlyphBits );
672 mpTag->addUI8( nAdvanceBits );
673
674 // text style change record
675 mpTag->addUI8( 0x8c );
676 mpTag->addUI16( rFlashFont.getID() );
677 mpTag->addRGB( aTextColor );
678 mpTag->addUI16( _uInt16( nHeight ) );
679
680 DBG_ASSERT( nLen <= 127, "TODO: handle text with more than 127 characters" );
681
682 // Glyph record
683 mpTag->addUI8( (sal_uInt8) nLen );
684
685 BitStream aBits;
686
687 sal_Int32 nLastDX = 0;
688 sal_Int32 nAdvance;
689 sal_uInt32 i;
690 for( i = 0; i < nLen; i++ )
691 {
692 if( i < (nLen-1) )
693 {
694 nAdvance = pDX[i] - nLastDX;
695 nLastDX = pDX[i];
696 }
697 else
698 {
699 nAdvance = 0;
700 }
701
702 aBits.writeUB( rFlashFont.getGlyph(rText.GetChar(_uInt16(i)),mpVDev), nGlyphBits );
703 aBits.writeSB( _Int16(map( Size( (long)( nAdvance / scale ), 0 ) ).Width() ), nAdvanceBits );
704 }
705
706 mpTag->addBits( aBits );
707 mpTag->addUI8( 0 );
708
709 endTag();
710
711 maShapeIds.push_back( nTextId );
712
713 // AS: Write strikeout and underline, if necessary. This code was originally taken from the SVG
714 // export facility, although the positioning had to be tweaked a little. I can't explain the
715 // numbers, but the flash lines up very well with the original OOo document. All of this should
716 // probably be converted to polygons as part of the meta file, though, as we don't handle any
717 // fancy lines (like dashes).
718 if( ( aOldFont.GetStrikeout() != STRIKEOUT_NONE ) || ( aOldFont.GetUnderline() != UNDERLINE_NONE ) )
719 {
720 Polygon aPoly( 4 );
721 const long nLineHeight = Max( (long) FRound( aMetric.GetLineHeight() * 0.05 ), (long) 1 );
722
723 if( aOldFont.GetStrikeout() != STRIKEOUT_NONE )
724 {
725 aPoly[ 0 ].X() = aBaseLinePos.X();
726 aPoly[ 0 ].Y() = aBaseLinePos.Y() - FRound( aMetric.GetAscent() * 0.26 ) - nLineHeight;
727 aPoly[ 1 ].X() = aPoly[ 0 ].X() + aNormSize.Width() - 1;
728 aPoly[ 1 ].Y() = aPoly[ 0 ].Y();
729 aPoly[ 2 ].X() = aPoly[ 1 ].X();
730 aPoly[ 2 ].Y() = aPoly[ 1 ].Y() + nLineHeight - 1;
731 aPoly[ 3 ].X() = aPoly[ 0 ].X();
732 aPoly[ 3 ].Y() = aPoly[ 2 ].Y();
733
734 Impl_writePolygon( aPoly, sal_True, aTextColor, aTextColor );
735 }
736
737 // AS: The factor of 1.5 on the nLineHeight is a magic number. I'm not sure why it works,
738 // but it looks good to me.
739 if( aOldFont.GetUnderline() != UNDERLINE_NONE )
740 {
741 aPoly[ 0 ].X() = aBaseLinePos.X();
742 aPoly[ 0 ].Y() = static_cast<long>(aBaseLinePos.Y() + 1.5*nLineHeight);
743 aPoly[ 1 ].X() = aPoly[ 0 ].X() + aNormSize.Width() - 1;
744 aPoly[ 1 ].Y() = aPoly[ 0 ].Y();
745 aPoly[ 2 ].X() = aPoly[ 1 ].X();
746 aPoly[ 2 ].Y() = aPoly[ 1 ].Y() + nLineHeight - 1;
747 aPoly[ 3 ].X() = aPoly[ 0 ].X();
748 aPoly[ 3 ].Y() = aPoly[ 2 ].Y();
749
750 Impl_writePolygon( aPoly, sal_True, aTextColor, aTextColor );
751 }
752 }
753
754 mpVDev->SetFont( aOldFont );
755 delete[] pOwnArray;
756 }
757 }
758
759 // -----------------------------------------------------------------------------
760 // AS: Because JPEGs require the alpha channel provided separately (JPEG does not
761 // natively support alpha channel, but SWF lets you provide it separately), we
762 // extract the alpha channel into a separate array here.
getBitmapData(const BitmapEx & aBmpEx,sal_uInt8 * & tgadata,sal_uInt8 * & tgaAlphadata,sal_uInt32 & nWidth,sal_uInt32 & nHeight)763 void getBitmapData( const BitmapEx& aBmpEx, sal_uInt8*& tgadata, sal_uInt8*& tgaAlphadata, sal_uInt32& nWidth, sal_uInt32& nHeight )
764 {
765 if( !aBmpEx.IsEmpty() )
766 {
767 Bitmap aBmp( aBmpEx.GetBitmap() );
768 BitmapReadAccess* pRAcc = aBmp.AcquireReadAccess();
769
770 if( pRAcc )
771 {
772 AlphaMask aAlpha;
773 nWidth = pRAcc->Width();
774 nHeight = pRAcc->Height();
775 tgadata = new sal_uInt8[nWidth*nHeight*4];
776 tgaAlphadata = new sal_uInt8[nWidth*nHeight];
777 sal_uInt8* p = tgadata, *pAlpha = tgaAlphadata;
778
779
780 if( aBmpEx.IsAlpha() )
781 aAlpha = aBmpEx.GetAlpha();
782 else if( aBmpEx.IsTransparent() )
783 aAlpha = aBmpEx.GetMask();
784 else
785 {
786 sal_uInt8 cAlphaVal = 0;
787 aAlpha = AlphaMask( aBmp.GetSizePixel(), &cAlphaVal );
788 }
789
790 BitmapReadAccess* pAAcc = aAlpha.AcquireReadAccess();
791
792 if( pAAcc )
793 {
794 for( sal_uInt32 nY = 0; nY < nHeight; nY++ )
795 {
796 for( sal_uInt32 nX = 0; nX < nWidth; nX++ )
797 {
798 const sal_uInt8 nAlpha = pAAcc->GetPixel( nY, nX ).GetIndex();
799 const BitmapColor aPixelColor( pRAcc->GetColor( nY, nX ) );
800
801 if( nAlpha == 0xff )
802 {
803 *p++ = 0;
804 *p++ = 0;
805 *p++ = 0;
806 *p++ = 0;
807 }
808 else
809 {
810 *p++ = 0xff-nAlpha;
811 *p++ = aPixelColor.GetRed();
812 *p++ = aPixelColor.GetGreen();
813 *p++ = aPixelColor.GetBlue();
814 }
815 *pAlpha++ = 0xff - nAlpha;
816 }
817 }
818
819 aAlpha.ReleaseAccess( pAAcc );
820 }
821
822 aBmp.ReleaseAccess( pRAcc );
823 }
824 }
825 }
826
827 // -----------------------------------------------------------------------------
defineBitmap(const BitmapEx & bmpSource,sal_Int32 nJPEGQualityLevel)828 sal_uInt16 Writer::defineBitmap( const BitmapEx &bmpSource, sal_Int32 nJPEGQualityLevel )
829 {
830 sal_uLong bmpChecksum = bmpSource.GetChecksum();
831
832 ChecksumCache::iterator it = mBitmapCache.find(bmpChecksum);
833
834 // AS: We already exported this bitmap, so just return its ID.
835 if (mBitmapCache.end() != it)
836 return it->second;
837
838 sal_uInt16 nBitmapId = createID();
839 mBitmapCache[bmpChecksum] = nBitmapId;
840
841 // AS: OK, we have a good image, so now we decide whether or not to JPEG it or
842 // or Lossless compress it.
843
844 //Figure out lossless size
845 sal_uInt8 *pImageData, *pAlphaData;
846 sal_uInt32 width, height;
847
848 getBitmapData( bmpSource, pImageData, pAlphaData, width, height );
849 sal_uInt32 raw_size = width * height * 4;
850 uLongf compressed_size = raw_size + (sal_uInt32)(raw_size/100) + 12;
851 sal_uInt8 *pCompressed = new sal_uInt8[ compressed_size ];
852
853 #ifdef DBG_UTIL
854 if(compress2(pCompressed, &compressed_size, pImageData, raw_size, Z_BEST_COMPRESSION) != Z_OK)
855 {
856 DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
857 }
858 #else
859 compress2(pCompressed, &compressed_size, pImageData, raw_size, Z_BEST_COMPRESSION);
860 #endif
861
862 // AS: SWF files let you provide an Alpha mask for JPEG images, but we have
863 // to ZLIB compress the alpha channel separately.
864 uLong alpha_compressed_size = 0;
865 sal_uInt8 *pAlphaCompressed = NULL;
866 if (bmpSource.IsAlpha() || bmpSource.IsTransparent())
867 {
868 alpha_compressed_size = uLongf(width * height + (sal_uInt32)(raw_size/100) + 12);
869 pAlphaCompressed = new sal_uInt8[ compressed_size ];
870
871 #ifdef DBG_UTIL
872 if(compress2(pAlphaCompressed, &alpha_compressed_size, pAlphaData, width * height, Z_BEST_COMPRESSION) != Z_OK)
873 {
874 DBG_ASSERT( false, "compress2 failed!" ); ((void)0);
875 }
876 #else
877 compress2(pAlphaCompressed, &alpha_compressed_size, pAlphaData, width * height, Z_BEST_COMPRESSION);
878 #endif
879 }
880
881 //Figure out JPEG size
882 const sal_uInt8* pJpgData = NULL;;
883 sal_uInt32 nJpgDataLength = 0xffffffff;
884
885 Graphic aGraphic( bmpSource );
886 SvMemoryStream aDstStm( 65535, 65535 );
887
888 GraphicFilter aFilter;
889
890 Sequence< PropertyValue > aFilterData(nJPEGQualityLevel != -1);
891 if( nJPEGQualityLevel != -1 )
892 {
893 aFilterData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM("Quality"));
894 aFilterData[0].Value <<= nJPEGQualityLevel;
895 }
896
897 #if 0
898 // Debug code to see what we export to swf
899 {
900 SvFileStream aDstStm( String( RTL_CONSTASCII_USTRINGPARAM("e:\\test.jpg") ), STREAM_READ | STREAM_WRITE | STREAM_TRUNC );
901 aFilter.ExportGraphic( aGraphic, String(), aDstStm,
902 aFilter.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME ) ) ), &aFilterData );
903 }
904 #endif
905
906 if( aFilter.ExportGraphic( aGraphic, String(), aDstStm,
907 aFilter.GetExportFormatNumberForShortName( OUString( RTL_CONSTASCII_USTRINGPARAM( JPG_SHORTNAME ) ) ), &aFilterData ) == ERRCODE_NONE )
908 {
909 pJpgData = reinterpret_cast<const sal_uInt8*>(aDstStm.GetData());
910 nJpgDataLength = aDstStm.Seek( STREAM_SEEK_TO_END );
911 }
912
913 // AS: Ok, now go ahead and use whichever is smaller. If JPEG is smaller, then
914 // we have to export as TAG_DEFINEBITSJPEG3 in the case that there is alpha
915 // channel data.
916 if ( pJpgData && ( nJpgDataLength + alpha_compressed_size < compressed_size) )
917 Impl_writeJPEG(nBitmapId, pJpgData, nJpgDataLength, pAlphaCompressed, alpha_compressed_size );
918 else
919 Impl_writeBmp( nBitmapId, width, height, pCompressed, compressed_size );
920
921 delete[] pCompressed;
922 delete[] pAlphaCompressed;
923 delete[] pImageData;
924 delete[] pAlphaData;
925
926 return nBitmapId;
927 }
928
929 // -----------------------------------------------------------------------------
930
Impl_writeImage(const BitmapEx & rBmpEx,const Point & rPt,const Size & rSz,const Point &,const Size &,const Rectangle & rClipRect,bool bNeedToMapClipRect)931 void Writer::Impl_writeImage( const BitmapEx& rBmpEx, const Point& rPt, const Size& rSz, const Point& /* rSrcPt */, const Size& /* rSrcSz */, const Rectangle& rClipRect, bool bNeedToMapClipRect )
932 {
933 if( !!rBmpEx )
934 {
935 BitmapEx bmpSource( rBmpEx );
936
937 Rectangle originalPixelRect = Rectangle(Point(), bmpSource.GetSizePixel());
938
939 Point srcPt( map(rPt) );
940 Size srcSize( map(rSz) );
941 Rectangle destRect( srcPt, srcSize );
942
943 // AS: Christian, my scaling factors are different than yours, and work better for me.
944 // However, I can't explain why exactly. I got some of this by trial and error.
945 double XScale = static_cast<double>(originalPixelRect.GetWidth())/destRect.GetWidth();
946 double YScale = static_cast<double>(originalPixelRect.GetHeight())/destRect.GetHeight();
947
948 // AS: If rClipRect has a value set, then we need to crop the bmp appropriately.
949 // If a map event already occurred in the metafile, then we do not need to map
950 // the clip rect as it's already been done.
951 if (!rClipRect.IsEmpty())
952 {
953 // AS: Christian, I also don't understand why bNeedToMapClipRect is necessary, but it
954 // works like a charm. Usually, the map event in the meta file does not cause the
955 // clipping rectangle to get mapped. However, sometimes there are multiple layers
956 // of mapping which eventually do cause the clipping rect to be mapped.
957 Size clipSize( bNeedToMapClipRect ? map(rClipRect.GetSize()) : rClipRect.GetSize() );
958 Rectangle clipRect = Rectangle(Point(), clipSize);
959 destRect.Intersection( clipRect );
960
961 Rectangle cropRect(destRect);
962
963 // AS: The bmp origion is always 0,0 so we have to adjust before we crop.
964 cropRect.Move(-srcPt.X(), -srcPt.Y());
965 // AS: Rectangle has no scale function (?!) so I do it manually...
966 Rectangle cropPixelRect(static_cast<long>(cropRect.Left()*XScale),
967 static_cast<long>(cropRect.Top()*YScale),
968 static_cast<long>(cropRect.Right()*XScale),
969 static_cast<long>(cropRect.Bottom()*YScale));
970
971 bmpSource.Crop(cropPixelRect);
972 }
973
974 if( !!bmpSource )
975 {
976 // #105949# fix images that are under 16 pixels width or height by
977 // expanding them. Some swf players can't display such small
978 // bitmaps
979 const Size& rSizePixel = bmpSource.GetSizePixel();
980 if( (rSizePixel.Width() < 16) || (rSizePixel.Height() < 16) )
981 {
982 const sal_uInt32 nDX = rSizePixel.Width() < 16 ? 16 - rSizePixel.Width() : 0;
983 const sal_uInt32 nDY = rSizePixel.Height() < 16 ? 16 - rSizePixel.Height() : 0;
984 bmpSource.Expand( nDX, nDY );
985 }
986
987 sal_Int32 nJPEGQuality = mnJPEGCompressMode;
988
989 Size szDestPixel = mpVDev->LogicToPixel(srcSize, aTWIPSMode);
990
991 double pixXScale = static_cast<double>(szDestPixel.Width()) / originalPixelRect.GetWidth();
992 double pixYScale = static_cast<double>(szDestPixel.Height()) / originalPixelRect.GetHeight();
993
994 // AS: If the image has been scaled down, then scale down the quality
995 // that we use for JPEG compression.
996 if (pixXScale < 1.0 && pixYScale < 1.0)
997 {
998
999 double qualityScale = (pixXScale + pixYScale)/2;
1000
1001 nJPEGQuality = (sal_Int32)( nJPEGQuality * qualityScale );
1002
1003 if (nJPEGQuality < 10)
1004 nJPEGQuality += 3;
1005 }
1006
1007 sal_uInt16 nBitmapId = defineBitmap(bmpSource, nJPEGQuality);
1008
1009 Polygon aPoly( destRect );
1010
1011 // AS: Since images are being cropped now, no translation is normally necessary.
1012 // However, some things like graphical bullet points are still get translated.
1013 ::basegfx::B2DHomMatrix m; // #i73264#
1014 m.scale(1.0/XScale, 1.0/YScale );
1015 if (destRect.Left() || destRect.Top())
1016 m.translate(destRect.Left(), destRect.Top());
1017
1018 FillStyle aFillStyle( nBitmapId, true, m );
1019
1020 sal_uInt16 nShapeId = defineShape( aPoly, aFillStyle );
1021
1022 maShapeIds.push_back( nShapeId );
1023 }
1024 }
1025 }
1026 // -----------------------------------------------------------------------------
1027
Impl_writeBmp(sal_uInt16 nBitmapId,sal_uInt32 width,sal_uInt32 height,sal_uInt8 * pCompressed,sal_uInt32 compressed_size)1028 void Writer::Impl_writeBmp( sal_uInt16 nBitmapId, sal_uInt32 width, sal_uInt32 height, sal_uInt8 *pCompressed, sal_uInt32 compressed_size )
1029 {
1030 startTag( TAG_DEFINEBITSLOSSLESS2 );
1031
1032 mpTag->addUI16( nBitmapId );
1033 mpTag->addUI8( 5 );
1034 mpTag->addUI16( _uInt16(width) );
1035 mpTag->addUI16( _uInt16(height) );
1036
1037 mpTag->Write( pCompressed, compressed_size );
1038
1039 endTag();
1040 }
1041
1042 // -----------------------------------------------------------------------------
1043
Impl_writeJPEG(sal_uInt16 nBitmapId,const sal_uInt8 * pJpgData,sal_uInt32 nJpgDataLength,sal_uInt8 * pAlphaCompressed,sal_uInt32 alpha_compressed_size)1044 void Writer::Impl_writeJPEG(sal_uInt16 nBitmapId, const sal_uInt8* pJpgData, sal_uInt32 nJpgDataLength, sal_uInt8 *pAlphaCompressed, sal_uInt32 alpha_compressed_size )
1045 {
1046 // AS: Go through the actuall JPEG bits, separating out the
1047 // header fields from the actual image fields. Fields are
1048 // identifed by 0xFFXX where XX is the field type. Both
1049 // the header and the image need start and stop (D8 and D9),
1050 // so that's why you see those written to both. I don't
1051 // really know what the rest of these are, I got it to work
1052 // kind of by trial and error and by comparing with known
1053 // good SWF files.
1054 sal_uInt8 cType = 0x01;
1055 const sal_uInt8* pJpgSearch = pJpgData;
1056
1057 int nLength = 0;
1058
1059 SvMemoryStream EncodingTableStream;
1060 SvMemoryStream ImageBitsStream;
1061 for (;pJpgSearch < pJpgData + nJpgDataLength; pJpgSearch += nLength)
1062 {
1063
1064 #ifdef DBG_UTIL
1065 if (0xFF != *pJpgSearch)
1066 {
1067 DBG_ERROR( "Expected JPEG marker." ); ((void)0);
1068 }
1069 #endif
1070
1071 cType = *(pJpgSearch + 1);
1072
1073 if (0xD8 == cType || 0xD9 == cType)
1074 {
1075 nLength = 2;
1076 }
1077 else if (0xDA == cType)
1078 {
1079 //AS: This is the actual image data, and runs to the
1080 // end of the file (as best I know), minus 2 bytes
1081 // for the closing 0xFFD9.
1082 nLength = nJpgDataLength - (pJpgSearch - pJpgData) - 2;
1083 }
1084 else
1085 {
1086 // AS: Lengths are big endian.
1087
1088 // Beware. pJpgSearch is not necessarily word-aligned,
1089 // so we access it byte-wise.
1090
1091 // AS: Add 2 to the length to include the 0xFFXX itself.
1092 nLength = 2 + (pJpgSearch[2]<<8) + pJpgSearch[3];
1093 }
1094
1095 // AS: I'm referring to libjpeg for a list of what these
1096 // markers are. See jdmarker.c for a list.
1097 // AS: I'm ignoring application specific markers 0xE1...0xEF
1098 // and comments 0xFE. I don't know what
1099 // 0xF0 or 0xFD are for, and they don't come up.
1100 // Additionally, 0xDE and 0xDF aren't clear to me.
1101 switch(cType)
1102 {
1103 case 0xD8:
1104 case 0xD9:
1105 EncodingTableStream.Write( pJpgSearch, nLength );
1106 ImageBitsStream.Write( pJpgSearch, nLength );
1107 break;
1108
1109 case 0x01:
1110 case 0xDB:
1111 case 0xDC:
1112 case 0xDD:
1113 case 0xC4:
1114 EncodingTableStream.Write( pJpgSearch, nLength );
1115 break;
1116
1117 case 0xC0:
1118 case 0xC1:
1119 case 0xC2:
1120 case 0xC3:
1121 case 0xC5:
1122 case 0xC6:
1123 case 0xC7:
1124 // case 0xC8: Apparently reserved for JPEG extensions?
1125 case 0xC9:
1126 case 0xCA:
1127 case 0xCB:
1128 case 0xCD:
1129 case 0xCE:
1130 case 0xCF:
1131 case 0xDA:
1132 case 0xE0:
1133 ImageBitsStream.Write( pJpgSearch, nLength );
1134 break;
1135
1136 default:
1137 DBG_ERROR( "JPEG marker I didn't handle!" );
1138
1139 }
1140 }
1141
1142 EncodingTableStream.Seek( STREAM_SEEK_TO_END );
1143 sal_uInt32 nEncodingTableSize = EncodingTableStream.Tell();
1144 EncodingTableStream.Seek( STREAM_SEEK_TO_BEGIN );
1145
1146 ImageBitsStream.Seek( STREAM_SEEK_TO_END );
1147 sal_uInt32 nImageBitsSize = ImageBitsStream.Tell();
1148 ImageBitsStream.Seek( STREAM_SEEK_TO_BEGIN );
1149
1150 // AS: If we need alpha support, use TAG_DEFINEBITSJPEG3.
1151 if (alpha_compressed_size > 0)
1152 {
1153 startTag( TAG_DEFINEBITSJPEG3 );
1154
1155 mpTag->addUI16( nBitmapId );
1156
1157 mpTag->addUI32( nEncodingTableSize + nImageBitsSize );
1158
1159 mpTag->Write(EncodingTableStream.GetData(), nEncodingTableSize);
1160 mpTag->Write(ImageBitsStream.GetData(), nImageBitsSize);
1161
1162 mpTag->Write( pAlphaCompressed, alpha_compressed_size );
1163
1164 endTag();
1165 }
1166 else
1167 {
1168 startTag( TAG_DEFINEBITSJPEG2 );
1169
1170 mpTag->addUI16( nBitmapId );
1171
1172 mpTag->Write(EncodingTableStream.GetData(), nEncodingTableSize);
1173 mpTag->Write(ImageBitsStream.GetData(), nImageBitsSize);
1174
1175 endTag();
1176 }
1177 }
1178
1179 // -----------------------------------------------------------------------------
1180
Impl_writeLine(const Point & rPt1,const Point & rPt2,const Color * pLineColor)1181 void Writer::Impl_writeLine( const Point& rPt1, const Point& rPt2, const Color* pLineColor )
1182 {
1183 Color aOldColor( mpVDev->GetLineColor() );
1184 if( pLineColor )
1185 mpVDev->SetLineColor( *pLineColor );
1186
1187 const Point aPtAry[2] = { rPt1, rPt2 };
1188 Polygon aPoly( 2, aPtAry );
1189 Impl_writePolyPolygon( aPoly, false );
1190
1191 mpVDev->SetLineColor( aOldColor );
1192 }
1193
1194 // -----------------------------------------------------------------------------
1195
Impl_writeRect(const Rectangle & rRect,long nRadX,long nRadY)1196 void Writer::Impl_writeRect( const Rectangle& rRect, long nRadX, long nRadY )
1197 {
1198 if( (rRect.nTop == rRect.nBottom) || (rRect.nLeft == rRect.nRight) )
1199 {
1200 Color aColor( mpVDev->GetFillColor() );
1201 Impl_writeLine( rRect.TopLeft(), rRect.BottomRight(), &aColor );
1202 }
1203 else
1204 {
1205 Polygon aPoly( rRect, nRadX, nRadY );
1206 Impl_writePolyPolygon( aPoly, true );
1207 }
1208 }
1209
1210 // -----------------------------------------------------------------------------
1211
Impl_writeEllipse(const Point & rCenter,long nRadX,long nRadY)1212 void Writer::Impl_writeEllipse( const Point& rCenter, long nRadX, long nRadY )
1213 {
1214 Polygon aPoly( rCenter, nRadX, nRadY );
1215 Impl_writePolyPolygon( aPoly, false );
1216 }
1217
1218
1219 /** writes the stroke defined by SvtGraphicStroke and returns true or it returns
1220 false if it can't handle this stroke.
1221 */
Impl_writeStroke(SvtGraphicStroke & rStroke)1222 bool Writer::Impl_writeStroke( SvtGraphicStroke& rStroke )
1223 {
1224 Polygon aPolygon;
1225 rStroke.getPath( aPolygon );
1226 PolyPolygon aPolyPolygon( aPolygon );
1227
1228 Rectangle aOldRect( aPolyPolygon.GetBoundRect() );
1229
1230 map( aPolyPolygon );
1231
1232 Rectangle aNewRect( aPolyPolygon.GetBoundRect() );
1233
1234 // as log as not LINESTYLE2 and DefineShape4 is used (which
1235 // added support for LineJoin), only round LineJoins are
1236 // supported. Fallback to META_POLYLINE_ACTION and META_LINE_ACTION
1237 if(SvtGraphicStroke::joinRound != rStroke.getJoinType())
1238 return false;
1239
1240 PolyPolygon aStartArrow;
1241 rStroke.getStartArrow( aStartArrow );
1242 if( 0 != aStartArrow.Count() )
1243 return false; // todo: Implement line ends
1244
1245 PolyPolygon aEndArrow;
1246 rStroke.getEndArrow( aEndArrow );
1247 if( 0 != aEndArrow.Count() )
1248 return false; // todo: Implement line ends
1249
1250 SvtGraphicStroke::DashArray aDashArray;
1251 rStroke.getDashArray( aDashArray );
1252 if( 0 != aDashArray.size() )
1253 return false; // todo: implement dashes
1254
1255 Color aColor( mpVDev->GetLineColor() );
1256
1257 if( 0.0 != rStroke.getTransparency() )
1258 aColor.SetTransparency( sal::static_int_cast<sal_uInt8>( MinMax( (long int)( rStroke.getTransparency() * 0xff ), 0, 0xff ) ) );
1259
1260 sal_uInt16 nShapeId = defineShape( aPolyPolygon, sal::static_int_cast<sal_uInt16>( mapRelative( (sal_Int32)( rStroke.getStrokeWidth() ) ) ), aColor );
1261 maShapeIds.push_back( nShapeId );
1262 return true;
1263 }
1264
1265 // -----------------------------------------------------------------------------
1266
1267 /** writes the filling defined by SvtGraphicFill and returns true or it returns
1268 false if it can't handle this filling.
1269 */
Impl_writeFilling(SvtGraphicFill & rFilling)1270 bool Writer::Impl_writeFilling( SvtGraphicFill& rFilling )
1271 {
1272 PolyPolygon aPolyPolygon;
1273 rFilling.getPath( aPolyPolygon );
1274
1275 Rectangle aOldRect( aPolyPolygon.GetBoundRect() );
1276
1277 map( aPolyPolygon );
1278
1279 Rectangle aNewRect( aPolyPolygon.GetBoundRect() );
1280
1281 switch( rFilling.getFillType() )
1282 {
1283 case SvtGraphicFill::fillSolid:
1284 {
1285 Color aColor( rFilling.getFillColor() );
1286
1287 if( 0.0 != rFilling.getTransparency() )
1288 aColor.SetTransparency( sal::static_int_cast<sal_uInt8>( MinMax( (long int)( rFilling.getTransparency() * 0xff ) , 0, 0xff ) ) );
1289
1290 FillStyle aFillStyle( aColor );
1291
1292 sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
1293 maShapeIds.push_back( nShapeId );
1294 }
1295 break;
1296 case SvtGraphicFill::fillGradient:
1297 return false;
1298 case SvtGraphicFill::fillHatch:
1299 return false;
1300 case SvtGraphicFill::fillTexture:
1301 {
1302 Graphic aGraphic;
1303 rFilling.getGraphic( aGraphic );
1304
1305 // CL->AS: Should we also scale down the quality here depending on image scale?
1306 sal_uInt16 nBitmapId = defineBitmap( aGraphic.GetBitmapEx(), mnJPEGCompressMode );
1307
1308 ::basegfx::B2DHomMatrix aMatrix; // #i73264#
1309
1310 SvtGraphicFill::Transform aTransform;
1311
1312 rFilling.getTransform( aTransform );
1313
1314 sal_uInt16 a,b;
1315 for( a = 0; a < 2; a++ )
1316 {
1317 for( b = 0; b < 3; b++ )
1318 {
1319 aMatrix.set(a, b, aTransform.matrix[a*3+b]);
1320 }
1321 }
1322 aMatrix.set(2, 0, 0.0);
1323 aMatrix.set(2, 1, 0.0);
1324 aMatrix.set(2, 2, 1.0);
1325
1326 // scale bitmap
1327 Rectangle originalPixelRect = Rectangle(Point(), aGraphic.GetBitmapEx().GetSizePixel());
1328
1329 double XScale = (double)aNewRect.GetWidth()/aOldRect.GetWidth();
1330 double YScale = (double)aNewRect.GetHeight()/aOldRect.GetHeight();
1331
1332 aMatrix.scale( XScale, YScale );
1333
1334 FillStyle aFillStyle( nBitmapId, !rFilling.IsTiling(), aMatrix );
1335
1336 sal_uInt16 nShapeId = defineShape( aPolyPolygon, aFillStyle );
1337 maShapeIds.push_back( nShapeId );
1338 }
1339 break;
1340 }
1341 return true;
1342 }
1343
1344 // -----------------------------------------------------------------------------
1345
1346 /* CL: The idea was to export page fields as text fields that get theire
1347 string from a variable set with actionscript by each page. This didn't
1348 work out since the formatting is always wrong when text follows the
1349 page number field since pages greater one may require more space than
1350 page 1
1351 */
1352 #if 0
1353 bool Writer::Impl_writePageField( Rectangle& rTextBounds )
1354 {
1355 startTag( TAG_DEFINEEDITTEXT );
1356
1357 sal_uInt16 nTextId = createID();
1358
1359 mpTag->addUI16( nTextId );
1360 mpTag->addRect( rTextBounds );
1361
1362 BitStream aBits;
1363 aBits.writeUB( 1, 1 ); // HasText
1364 aBits.writeUB( 0, 1 ); // WordWrap
1365 aBits.writeUB( 0, 1 ); // MultiLine
1366 aBits.writeUB( 0, 1 ); // Password
1367 aBits.writeUB( 1, 1 ); // HasTextColor
1368 aBits.writeUB( 0, 1 ); // HasMaxLength
1369 aBits.writeUB( 0, 1 ); // HasFont
1370 aBits.writeUB( 0, 1 ); // Reserved
1371 aBits.writeUB( 0, 1 ); // AutoSize
1372 aBits.writeUB( 0, 1 ); // HasLayout
1373 aBits.writeUB( 1, 1 ); // NoSelect
1374 aBits.writeUB( 1, 1 ); // Border
1375 aBits.writeUB( 0, 1 ); // Reserved
1376 aBits.writeUB( 0, 1 ); // HTML
1377 aBits.writeUB( 0, 1 ); // UseOutlines
1378 mpTag->addBits( aBits );
1379
1380 Color aColor( COL_BLACK );
1381 mpTag->addRGB( aColor );
1382 mpTag->addString( "PageNumber" );
1383 mpTag->addString( "XXX" );
1384
1385 endTag();
1386
1387 maShapeIds.push_back( nTextId );
1388
1389 return true;
1390 }
1391 #endif
1392
1393 // -----------------------------------------------------------------------------
1394
Impl_handleLineInfoPolyPolygons(const LineInfo & rInfo,const basegfx::B2DPolygon & rLinePolygon)1395 void Writer::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon)
1396 {
1397 if(rLinePolygon.count())
1398 {
1399 basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
1400 basegfx::B2DPolyPolygon aFillPolyPolygon;
1401
1402 rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);
1403
1404 if(aLinePolyPolygon.count())
1405 {
1406 for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
1407 {
1408 const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
1409 Impl_writePolygon(Polygon(aCandidate), sal_False );
1410 }
1411 }
1412
1413 if(aFillPolyPolygon.count())
1414 {
1415 const Color aOldLineColor(mpVDev->GetLineColor());
1416 const Color aOldFillColor(mpVDev->GetFillColor());
1417
1418 mpVDev->SetLineColor();
1419 mpVDev->SetFillColor(aOldLineColor);
1420
1421 for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
1422 {
1423 const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a));
1424 Impl_writePolyPolygon(PolyPolygon(Polygon(aPolygon)), sal_True );
1425 }
1426
1427 mpVDev->SetLineColor(aOldLineColor);
1428 mpVDev->SetFillColor(aOldFillColor);
1429 }
1430 }
1431 }
1432
1433 // -----------------------------------------------------------------------------
1434
Impl_writeActions(const GDIMetaFile & rMtf)1435 void Writer::Impl_writeActions( const GDIMetaFile& rMtf )
1436 {
1437 Rectangle clipRect;
1438 int bMap = 0;
1439 for( sal_uLong i = 0, nCount = rMtf.GetActionCount(); i < nCount; i++ )
1440 {
1441 const MetaAction* pAction = rMtf.GetAction( i );
1442 const sal_uInt16 nType = pAction->GetType();
1443
1444 switch( nType )
1445 {
1446 case( META_PIXEL_ACTION ):
1447 {
1448 const MetaPixelAction* pA = (const MetaPixelAction*) pAction;
1449
1450 Impl_writeLine( pA->GetPoint(), pA->GetPoint(), &pA->GetColor() );
1451 }
1452 break;
1453
1454 case( META_POINT_ACTION ):
1455 {
1456 const MetaPointAction* pA = (const MetaPointAction*) pAction;
1457
1458 Impl_writeLine( pA->GetPoint(), pA->GetPoint() );
1459 }
1460 break;
1461
1462 case( META_LINE_ACTION ):
1463 {
1464 const MetaLineAction* pA = (const MetaLineAction*) pAction;
1465
1466 if(pA->GetLineInfo().IsDefault())
1467 {
1468 Impl_writeLine( pA->GetStartPoint(), pA->GetEndPoint() );
1469 }
1470 else
1471 {
1472 // LineInfo used; handle Dash/Dot and fat lines
1473 basegfx::B2DPolygon aPolygon;
1474 aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
1475 aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
1476 Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
1477 }
1478 }
1479 break;
1480
1481 case( META_RECT_ACTION ):
1482 {
1483 Impl_writeRect( ( (const MetaRectAction*) pAction )->GetRect(), 0, 0 );
1484 }
1485 break;
1486
1487 case( META_ROUNDRECT_ACTION ):
1488 {
1489 const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction;
1490
1491 Impl_writeRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
1492 }
1493 break;
1494
1495 case( META_ELLIPSE_ACTION ):
1496 {
1497 const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction;
1498 const Rectangle& rRect = pA->GetRect();
1499
1500 Impl_writeEllipse( rRect.Center(), rRect.GetWidth() >> 1, rRect.GetHeight() >> 1 );
1501 }
1502 break;
1503
1504 case( META_ARC_ACTION ):
1505 case( META_PIE_ACTION ):
1506 case( META_CHORD_ACTION ):
1507 case( META_POLYGON_ACTION ):
1508 {
1509 Polygon aPoly;
1510
1511 switch( nType )
1512 {
1513 case( META_ARC_ACTION ):
1514 {
1515 const MetaArcAction* pA = (const MetaArcAction*) pAction;
1516 aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC );
1517 }
1518 break;
1519
1520 case( META_PIE_ACTION ):
1521 {
1522 const MetaPieAction* pA = (const MetaPieAction*) pAction;
1523 aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE );
1524 }
1525 break;
1526
1527 case( META_CHORD_ACTION ):
1528 {
1529 const MetaChordAction* pA = (const MetaChordAction*) pAction;
1530 aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD );
1531 }
1532 break;
1533
1534 case( META_POLYGON_ACTION ):
1535 aPoly = ( (const MetaPolygonAction*) pAction )->GetPolygon();
1536 break;
1537 }
1538
1539 if( aPoly.GetSize() )
1540 {
1541 Impl_writePolygon( aPoly, sal_True );
1542 }
1543 }
1544 break;
1545
1546 case( META_POLYLINE_ACTION ):
1547 {
1548 const MetaPolyLineAction* pA = (const MetaPolyLineAction*) pAction;
1549 const Polygon& rPoly = pA->GetPolygon();
1550
1551 if( rPoly.GetSize() )
1552 {
1553 if(pA->GetLineInfo().IsDefault())
1554 {
1555 Impl_writePolygon( rPoly, sal_False );
1556 }
1557 else
1558 {
1559 // LineInfo used; handle Dash/Dot and fat lines
1560 Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
1561 }
1562 }
1563 }
1564 break;
1565
1566 case( META_POLYPOLYGON_ACTION ):
1567 {
1568 const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*) pAction;
1569 const PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
1570
1571 if( rPolyPoly.Count() )
1572 Impl_writePolyPolygon( rPolyPoly, sal_True );
1573 }
1574 break;
1575
1576 case( META_GRADIENT_ACTION ):
1577 {
1578 const MetaGradientAction* pA = (const MetaGradientAction*) pAction;
1579
1580 Polygon aPoly( pA->GetRect() );
1581 Impl_writeGradientEx( aPoly, pA->GetGradient() );
1582 }
1583 break;
1584
1585 case( META_GRADIENTEX_ACTION ):
1586 {
1587 const MetaGradientExAction* pA = (const MetaGradientExAction*) pAction;
1588 Impl_writeGradientEx( pA->GetPolyPolygon(), pA->GetGradient() );
1589 }
1590 break;
1591
1592 case META_HATCH_ACTION:
1593 {
1594 const MetaHatchAction* pA = (const MetaHatchAction*) pAction;
1595 GDIMetaFile aTmpMtf;
1596
1597 mpVDev->AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
1598 Impl_writeActions( aTmpMtf );
1599 }
1600 break;
1601
1602 case( META_TRANSPARENT_ACTION ):
1603 {
1604 const MetaTransparentAction* pA = (const MetaTransparentAction*) pAction;
1605 const PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
1606
1607 if( rPolyPoly.Count() )
1608 {
1609 // convert transparence from percent into 0x00 - 0xff
1610 sal_uInt8 nTransparence = (sal_uInt8) MinMax( FRound( pA->GetTransparence() * 2.55 ), 0, 255 );
1611 Impl_writePolyPolygon( rPolyPoly, sal_True, nTransparence );
1612 }
1613 }
1614 break;
1615
1616 case( META_FLOATTRANSPARENT_ACTION ):
1617 {
1618 const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction;
1619 GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
1620 Point aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
1621 const Size aSrcSize( aTmpMtf.GetPrefSize() );
1622 const Point aDestPt( pA->GetPoint() );
1623 const Size aDestSize( pA->GetSize() );
1624 const double fScaleX = aSrcSize.Width() ? (double) aDestSize.Width() / aSrcSize.Width() : 1.0;
1625 const double fScaleY = aSrcSize.Height() ? (double) aDestSize.Height() / aSrcSize.Height() : 1.0;
1626 long nMoveX, nMoveY;
1627
1628 if( fScaleX != 1.0 || fScaleY != 1.0 )
1629 {
1630 aTmpMtf.Scale( fScaleX, fScaleY );
1631 aSrcPt.X() = FRound( aSrcPt.X() * fScaleX );
1632 aSrcPt.Y() = FRound( aSrcPt.Y() * fScaleY );
1633 }
1634
1635 nMoveX = aDestPt.X() - aSrcPt.X(), nMoveY = aDestPt.Y() - aSrcPt.Y();
1636
1637 if( nMoveX || nMoveY )
1638 aTmpMtf.Move( nMoveX, nMoveY );
1639
1640 const Gradient& rGradient = pA->GetGradient();
1641 sal_uInt32 nLuminance = ((sal_Int32)rGradient.GetStartColor().GetLuminance() + (sal_Int32)rGradient.GetEndColor().GetLuminance() ) >> 1;
1642
1643 sal_uInt8 nOldGlobalTransparency = mnGlobalTransparency;
1644 mnGlobalTransparency = (sal_uInt8)MinMax( nLuminance, 0, 0xff );
1645
1646 mpVDev->Push();
1647 Impl_writeActions( aTmpMtf );
1648 mpVDev->Pop();
1649
1650 mnGlobalTransparency = nOldGlobalTransparency;
1651 }
1652 break;
1653
1654 case( META_EPS_ACTION ):
1655 {
1656 const MetaEPSAction* pA = (const MetaEPSAction*) pAction;
1657 const GDIMetaFile aGDIMetaFile( pA->GetSubstitute() );
1658 sal_Bool bFound = sal_False;
1659
1660 for( sal_uLong j = 0, nC = aGDIMetaFile.GetActionCount(); ( j < nC ) && !bFound; j++ )
1661 {
1662 const MetaAction* pSubstAct = aGDIMetaFile.GetAction( j );
1663
1664 if( pSubstAct->GetType() == META_BMPSCALE_ACTION )
1665 {
1666 bFound = sal_True;
1667 const MetaBmpScaleAction* pBmpScaleAction = (const MetaBmpScaleAction*) pSubstAct;
1668 Impl_writeImage( pBmpScaleAction->GetBitmap(),
1669 pA->GetPoint(), pA->GetSize(),
1670 Point(), pBmpScaleAction->GetBitmap().GetSizePixel(), clipRect, 1 == bMap );
1671 }
1672 }
1673 }
1674 break;
1675
1676 case( META_COMMENT_ACTION ):
1677 {
1678 const MetaCommentAction* pA = (const MetaCommentAction*) pAction;
1679 const sal_uInt8* pData = pA->GetData();
1680 String aSkipComment;
1681
1682 if( pA->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
1683 {
1684 const MetaGradientExAction* pGradAction = NULL;
1685 sal_Bool bDone = sal_False;
1686
1687 while( !bDone && ( ++i < nCount ) )
1688 {
1689 pAction = rMtf.GetAction( i );
1690
1691 if( pAction->GetType() == META_GRADIENTEX_ACTION )
1692 pGradAction = (const MetaGradientExAction*) pAction;
1693 else if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1694 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) )
1695 {
1696 bDone = sal_True;
1697 }
1698 }
1699
1700 if( pGradAction )
1701 Impl_writeGradientEx( pGradAction->GetPolyPolygon(), pGradAction->GetGradient());
1702 }
1703 else if( pA->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_BEGIN" ) == COMPARE_EQUAL &&
1704 pData )
1705 {
1706
1707 // this comment encapsulates all high level information for a filling that caused
1708 // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1709
1710 SvtGraphicFill aFilling;
1711 SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
1712
1713 // read the fill info
1714 aMemStm >> aFilling;
1715
1716 // if impl_writeFilling can handle this high level filling, it returns true and we
1717 // skip all meta actions until "XPATHFILL_SEQ_END"
1718 if( Impl_writeFilling( aFilling ) )
1719 {
1720 bool bDone = sal_False;
1721
1722 while( !bDone && ( ++i < nCount ) )
1723 {
1724 pAction = rMtf.GetAction( i );
1725
1726 if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1727 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XPATHFILL_SEQ_END" ) == COMPARE_EQUAL ) )
1728 {
1729 bDone = sal_True;
1730 }
1731 }
1732 }
1733 }
1734 else if( pA->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_BEGIN" ) == COMPARE_EQUAL &&
1735 pData )
1736 {
1737
1738 // this comment encapsulates all high level information for a filling that caused
1739 // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
1740
1741 SvtGraphicStroke aStroke;
1742 SvMemoryStream aMemStm( (void*)pData, pA->GetDataSize(), STREAM_READ );
1743
1744 // read the fill info
1745 aMemStm >> aStroke;
1746
1747 // if impl_writeStroke can handle this high level stroke, it returns true and we
1748 // skip all meta actions until "XPATHSTROKE_SEQ_END"
1749 if( Impl_writeStroke( aStroke ) )
1750 {
1751 bool bDone = sal_False;
1752
1753 while( !bDone && ( ++i < nCount ) )
1754 {
1755 pAction = rMtf.GetAction( i );
1756
1757 if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1758 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "XPATHSTROKE_SEQ_END" ) == COMPARE_EQUAL ) )
1759 {
1760 bDone = sal_True;
1761 }
1762 }
1763 }
1764 }
1765 #if 0
1766 else if( pA->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_BEGIN;PageField" ) == COMPARE_EQUAL )
1767 {
1768 bool bDone = sal_False;
1769
1770 while( !bDone && ( ++i < nCount ) )
1771 {
1772 pAction = rMtf.GetAction( i );
1773
1774 if( pAction->GetType() == META_TEXTARRAY_ACTION )
1775 {
1776 const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction;
1777 Rectangle aRect( pA->GetPoint(), Size( 100, 100 ) );
1778 Impl_writePageField( aRect );
1779 }
1780
1781 if( ( pAction->GetType() == META_COMMENT_ACTION ) &&
1782 ( ( (const MetaCommentAction*) pAction )->GetComment().CompareIgnoreCaseToAscii( "FIELD_SEQ_END" ) == COMPARE_EQUAL ) )
1783 {
1784 bDone = sal_True;
1785 }
1786 }
1787 }
1788 #endif
1789 }
1790 break;
1791
1792 case( META_BMPSCALE_ACTION ):
1793 {
1794 const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction;
1795
1796 Impl_writeImage( pA->GetBitmap(),
1797 pA->GetPoint(), pA->GetSize(),
1798 Point(), pA->GetBitmap().GetSizePixel(), clipRect, 1 == bMap );
1799 }
1800 break;
1801
1802 case( META_BMP_ACTION ):
1803 {
1804 const MetaBmpAction* pA = (const MetaBmpAction*) pAction;
1805 Impl_writeImage( pA->GetBitmap(),
1806 pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmap().GetSizePixel()),
1807 Point(), pA->GetBitmap().GetSizePixel(), clipRect, 1 ==bMap );
1808 }
1809 break;
1810
1811 case( META_BMPSCALEPART_ACTION ):
1812 {
1813 const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*) pAction;
1814 Impl_writeImage( pA->GetBitmap(),
1815 pA->GetDestPoint(), pA->GetDestSize(),
1816 pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap );
1817 }
1818 break;
1819
1820 case( META_BMPEX_ACTION ):
1821 {
1822 const MetaBmpExAction* pA = (const MetaBmpExAction*) pAction;
1823 Impl_writeImage( pA->GetBitmapEx(),
1824 pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmapEx().GetSizePixel() ),
1825 Point(), pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap );
1826 }
1827 break;
1828
1829 case( META_BMPEXSCALE_ACTION ):
1830 {
1831 const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction;
1832 Impl_writeImage( pA->GetBitmapEx(),
1833 pA->GetPoint(), pA->GetSize(),
1834 Point(), pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap );
1835 }
1836 break;
1837
1838 case( META_BMPEXSCALEPART_ACTION ):
1839 {
1840 const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pAction;
1841 Impl_writeImage( pA->GetBitmapEx(),
1842 pA->GetDestPoint(), pA->GetDestSize(),
1843 pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap );
1844 }
1845 break;
1846
1847 case( META_TEXT_ACTION ):
1848 {
1849 const MetaTextAction* pA = (const MetaTextAction*) pAction;
1850 Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), NULL, 0);
1851 }
1852 break;
1853
1854 case( META_TEXTRECT_ACTION ):
1855 {
1856 const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction;
1857 Impl_writeText( pA->GetRect().TopLeft(), pA->GetText(), NULL, 0 );
1858 }
1859 break;
1860
1861 case( META_TEXTARRAY_ACTION ):
1862 {
1863 const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction;
1864 Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), pA->GetDXArray(), 0 );
1865 }
1866 break;
1867
1868 case( META_STRETCHTEXT_ACTION ):
1869 {
1870 const MetaStretchTextAction* pA = (const MetaStretchTextAction*) pAction;
1871 Impl_writeText( pA->GetPoint(), String( pA->GetText(), pA->GetIndex(), pA->GetLen() ), NULL, pA->GetWidth() );
1872 }
1873 break;
1874
1875 case( META_ISECTRECTCLIPREGION_ACTION ):
1876 {
1877 const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*) pAction;
1878 clipRect = pA->GetRect();
1879 }
1880 case( META_CLIPREGION_ACTION ):
1881 case( META_ISECTREGIONCLIPREGION_ACTION ):
1882 case( META_MOVECLIPREGION_ACTION ):
1883 {
1884 ( (MetaAction*) pAction )->Execute( mpVDev );
1885 // mbClipAttrChanged = sal_True;
1886 }
1887 break;
1888
1889 case( META_MAPMODE_ACTION ):
1890 {
1891 // const MetaMapModeAction *pA = (const MetaMapModeAction*) pAction;
1892 // MapMode mm = pA->GetMapMode();
1893 // MapUnit mu = mm.GetMapUnit();
1894 //
1895 // Point pt = mm.GetOrigin();
1896 // Fraction fx = mm.GetScaleX();
1897 // Fraction fy = mm.GetScaleY();
1898
1899 bMap++;
1900 }
1901 case( META_REFPOINT_ACTION ):
1902 case( META_LINECOLOR_ACTION ):
1903 case( META_FILLCOLOR_ACTION ):
1904 case( META_TEXTLINECOLOR_ACTION ):
1905 case( META_TEXTFILLCOLOR_ACTION ):
1906 case( META_TEXTCOLOR_ACTION ):
1907 case( META_TEXTALIGN_ACTION ):
1908 case( META_FONT_ACTION ):
1909 case( META_PUSH_ACTION ):
1910 case( META_POP_ACTION ):
1911 case( META_LAYOUTMODE_ACTION ):
1912 {
1913 ( (MetaAction*) pAction )->Execute( mpVDev );
1914 }
1915 break;
1916
1917 case( META_RASTEROP_ACTION ):
1918 case( META_MASK_ACTION ):
1919 case( META_MASKSCALE_ACTION ):
1920 case( META_MASKSCALEPART_ACTION ):
1921 case( META_WALLPAPER_ACTION ):
1922 case( META_TEXTLINE_ACTION ):
1923 {
1924 // !!! >>> we don't want to support these actions
1925 }
1926 break;
1927
1928 default:
1929 //DBG_ERROR( "FlashActionWriter::ImplWriteActions: unsupported MetaAction #" );
1930 break;
1931 }
1932 }
1933 }
1934
1935
1936 /////////////////////////////////////////////////////////////////////////
1937
1938
Impl_addStraightLine(BitStream & rBits,Point & rLastPoint,const double P2x,const double P2y)1939 void Writer::Impl_addStraightLine( BitStream& rBits, Point& rLastPoint,
1940 const double P2x, const double P2y )
1941 {
1942 Point aPoint( FRound(P2x), FRound(P2y) );
1943
1944 Impl_addStraightEdgeRecord( rBits, _Int16(aPoint.X() - rLastPoint.X()),_Int16(aPoint.Y() - rLastPoint.Y()));
1945 rLastPoint = aPoint;
1946
1947 }
1948
1949 // -----------------------------------------------------------------------------
1950
Impl_addQuadBezier(BitStream & rBits,Point & rLastPoint,const double P2x,const double P2y,const double P3x,const double P3y)1951 void Writer::Impl_addQuadBezier( BitStream& rBits, Point& rLastPoint,
1952 const double P2x, const double P2y,
1953 const double P3x, const double P3y )
1954 {
1955
1956 Point aControlPoint( FRound(P2x), FRound(P2y) );
1957 Point aAnchorPoint( FRound(P3x), FRound(P3y) );
1958
1959 Impl_addCurvedEdgeRecord( rBits,
1960 _Int16(aControlPoint.X() - rLastPoint.X()),_Int16(aControlPoint.Y() - rLastPoint.Y()),
1961 _Int16(aAnchorPoint.X() - aControlPoint.X()),_Int16(aAnchorPoint.Y() - aControlPoint.Y()) );
1962 rLastPoint = aAnchorPoint;
1963 }
1964
1965 // -----------------------------------------------------------------------------
1966
1967 /* Approximate given cubic bezier curve by quadratic bezier segments */
Impl_quadBezierApprox(BitStream & rBits,Point & rLastPoint,const double d2,const double P1x,const double P1y,const double P2x,const double P2y,const double P3x,const double P3y,const double P4x,const double P4y)1968 void Writer::Impl_quadBezierApprox( BitStream& rBits,
1969 Point& rLastPoint,
1970 const double d2,
1971 const double P1x, const double P1y,
1972 const double P2x, const double P2y,
1973 const double P3x, const double P3y,
1974 const double P4x, const double P4y )
1975 {
1976 // Check for degenerate case, where the given cubic bezier curve
1977 // is already quadratic: P4 == 3P3 - 3P2 + P1
1978 if( P4x == 3.0*P3x - 3.0*P2x + P1x &&
1979 P4y == 3.0*P3y - 3.0*P2y + P1y )
1980 {
1981 Impl_addQuadBezier( rBits, rLastPoint,
1982 3.0/2.0*P2x - 1.0/2.0*P1x, 3.0/2.0*P2y - 1.0/2.0*P1y,
1983 P4x, P4y);
1984 }
1985 else
1986 {
1987 // Create quadratic segment for given cubic:
1988 // Start and end point must coincide, determine quadratic control
1989 // point in such a way that it lies on the intersection of the
1990 // tangents at start and end point, resp. Thus, both cubic and
1991 // quadratic curve segments will match in 0th and 1st derivative
1992 // at the start and end points
1993
1994 // Intersection of P2P1 and P4P3
1995 // (P2y-P4y)(P3x-P4x)-(P2x-P4x)(P3y-P4y)
1996 // lambda = -------------------------------------
1997 // (P1x-P2x)(P3y-P4y)-(P1y-P2y)(P3x-P4x)
1998 //
1999 // Intersection point IP is now
2000 // IP = P2 + lambda(P1-P2)
2001 //
2002 const double nominator( (P2y-P4y)*(P3x-P4x) - (P2x-P4x)*(P3y-P4y) );
2003 const double denominator( (P1x-P2x)*(P3y-P4y) - (P1y-P2y)*(P3x-P4x) );
2004 const double lambda( nominator / denominator );
2005
2006 const double IPx( P2x + lambda*( P1x - P2x) );
2007 const double IPy( P2y + lambda*( P1y - P2y) );
2008
2009 // Introduce some alias names: quadratic start point is P1, end
2010 // point is P4, control point is IP
2011 const double QP1x( P1x );
2012 const double QP1y( P1y );
2013 const double QP2x( IPx );
2014 const double QP2y( IPy );
2015 const double QP3x( P4x );
2016 const double QP3y( P4y );
2017
2018 // Adapted bezier flatness test (lecture notes from R. Schaback,
2019 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
2020 //
2021 // ||C(t) - Q(t)|| <= max ||c_j - q_j||
2022 // 0<=j<=n
2023 //
2024 // In this case, we don't need the distance from the cubic bezier
2025 // to a straight line, but to a quadratic bezier. The c_j's are
2026 // the cubic bezier's bernstein coefficients, the q_j's the
2027 // quadratic bezier's. We have the c_j's given, the q_j's can be
2028 // calculated from QPi like this (sorry, mixed index notation, we
2029 // use [1,n], formulas use [0,n-1]):
2030 //
2031 // q_0 = QP1 = P1
2032 // q_1 = 1/3 QP1 + 2/3 QP2
2033 // q_2 = 2/3 QP2 + 1/3 QP3
2034 // q_3 = QP3 = P4
2035 //
2036 // We can drop case 0 and 3, since there the curves coincide
2037 // (distance is zero)
2038
2039 // calculate argument of max for j=1 and j=2
2040 const double fJ1x( P2x - 1.0/3.0*QP1x - 2.0/3.0*QP2x );
2041 const double fJ1y( P2y - 1.0/3.0*QP1y - 2.0/3.0*QP2y );
2042 const double fJ2x( P3x - 2.0/3.0*QP2x - 1.0/3.0*QP3x );
2043 const double fJ2y( P3y - 2.0/3.0*QP2y - 1.0/3.0*QP3y );
2044
2045 // stop if distance from cubic curve is guaranteed to be bounded by d
2046 // Should denominator be 0: then P1P2 and P3P4 are parallel (P1P2^T R[90,P3P4] = 0.0),
2047 // meaning that either we have a straight line or an inflexion point (see else block below)
2048 if( 0.0 != denominator &&
2049 ::std::max( fJ1x*fJ1x + fJ1y*fJ1y,
2050 fJ2x*fJ2x + fJ2y*fJ2y) < d2 )
2051 {
2052 // requested resolution reached.
2053 // Add end points to output file.
2054 // order is preserved, since this is so to say depth first traversal.
2055 Impl_addQuadBezier( rBits, rLastPoint,
2056 QP2x, QP2y,
2057 QP3x, QP3y);
2058 }
2059 else
2060 {
2061 // Maybe subdivide further
2062
2063 // This is for robustness reasons, since the line intersection
2064 // method below gets instable if the curve gets closer to a
2065 // straight line. If the given cubic bezier does not deviate by
2066 // more than d/4 from a straight line, either:
2067 // - take the line (that's what we do here)
2068 // - express the line by a quadratic bezier
2069
2070 // Perform bezier flatness test (lecture notes from R. Schaback,
2071 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
2072 //
2073 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
2074 // 0<=j<=n
2075 //
2076 // What is calculated here is an upper bound to the distance from
2077 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
2078 // curve. We can drop 0 and n from the running indices, since the
2079 // argument of max becomes zero for those cases.
2080 const double fJ1x2( P2x - P1x - 1.0/3.0*(P4x - P1x) );
2081 const double fJ1y2( P2y - P1y - 1.0/3.0*(P4y - P1y) );
2082 const double fJ2x2( P3x - P1x - 2.0/3.0*(P4x - P1x) );
2083 const double fJ2y2( P3y - P1y - 2.0/3.0*(P4y - P1y) );
2084
2085 // stop if distance from line is guaranteed to be bounded by d/4
2086 if( ::std::max( fJ1x2*fJ1x2 + fJ1y2*fJ1y2,
2087 fJ2x2*fJ2x2 + fJ2y2*fJ2y2) < d2/16.0 )
2088 {
2089 // do not subdivide further, add straight line instead
2090 Impl_addStraightLine( rBits, rLastPoint, P4x, P4y);
2091 }
2092 else
2093 {
2094 // deCasteljau bezier arc, split at t=0.5
2095 // Foley/vanDam, p. 508
2096 const double L1x( P1x ), L1y( P1y );
2097 const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 );
2098 const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 );
2099 const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 );
2100 const double R4x( P4x ), R4y( P4y );
2101 const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 );
2102 const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 );
2103 const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 );
2104 const double L4x( R1x ), L4y( R1y );
2105
2106 // subdivide further
2107 Impl_quadBezierApprox(rBits, rLastPoint, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y);
2108 Impl_quadBezierApprox(rBits, rLastPoint, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y);
2109 }
2110 }
2111 }
2112 }
2113
Impl_GetBreakIterator()2114 Reference < XBreakIterator > Writer::Impl_GetBreakIterator()
2115 {
2116 if ( !mxBreakIterator.is() )
2117 {
2118 Reference< XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
2119 mxBreakIterator.set( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
2120 }
2121 return mxBreakIterator;
2122 }
2123