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