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