update dev300-m58
[ooovba.git] / cppcanvas / source / mtfrenderer / implrenderer.cxx
blobaf045f65825115fcf835ec5b7b74abab9b91e923
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: implrenderer.cxx,v $
10 * $Revision: 1.25.4.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_cppcanvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <canvas/verbosetrace.hxx>
38 #include <osl/mutex.hxx>
39 #include <vos/mutex.hxx>
40 #include <vcl/svapp.hxx>
42 #include <rtl/logfile.hxx>
44 #include <comphelper/sequence.hxx>
45 #include <comphelper/anytostring.hxx>
46 #include <cppuhelper/exc_hlp.hxx>
48 #include <cppcanvas/canvas.hxx>
50 #include <com/sun/star/rendering/XGraphicDevice.hpp>
51 #include <com/sun/star/rendering/TexturingMode.hpp>
52 #include <com/sun/star/rendering/XParametricPolyPolygon2DFactory.hpp>
53 #include <com/sun/star/uno/Sequence.hxx>
54 #include <com/sun/star/geometry/RealPoint2D.hpp>
55 #include <com/sun/star/rendering/ViewState.hpp>
56 #include <com/sun/star/rendering/RenderState.hpp>
57 #include <com/sun/star/rendering/XCanvasFont.hpp>
58 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
59 #include <com/sun/star/rendering/XCanvas.hpp>
60 #include <com/sun/star/rendering/PathCapType.hpp>
61 #include <com/sun/star/rendering/PathJoinType.hpp>
63 #include <basegfx/tools/canvastools.hxx>
64 #include <basegfx/numeric/ftools.hxx>
65 #include <basegfx/polygon/b2dpolypolygontools.hxx>
66 #include <basegfx/polygon/b2dpolygontools.hxx>
67 #include <basegfx/polygon/b2dpolygon.hxx>
68 #include <basegfx/polygon/b2dpolypolygon.hxx>
69 #include <basegfx/matrix/b2dhommatrix.hxx>
70 #include <basegfx/vector/b2dsize.hxx>
71 #include <basegfx/range/b2drectangle.hxx>
72 #include <basegfx/point/b2dpoint.hxx>
73 #include <basegfx/tuple/b2dtuple.hxx>
74 #include <basegfx/polygon/b2dpolygonclipper.hxx>
75 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
77 #include <canvas/canvastools.hxx>
78 #include <vcl/canvastools.hxx>
79 #include <vcl/salbtype.hxx>
80 #include <vcl/gdimtf.hxx>
81 #include <vcl/metaact.hxx>
82 #include <vcl/virdev.hxx>
83 #include <vcl/metric.hxx>
84 #include <vcl/graphictools.hxx>
85 #include <tools/poly.hxx>
86 #include <i18npool/mslangid.hxx>
88 #include <implrenderer.hxx>
89 #include <tools.hxx>
90 #include <outdevstate.hxx>
92 #include <action.hxx>
93 #include <bitmapaction.hxx>
94 #include <lineaction.hxx>
95 #include <pointaction.hxx>
96 #include <polypolyaction.hxx>
97 #include <textaction.hxx>
98 #include <transparencygroupaction.hxx>
100 #include <vector>
101 #include <algorithm>
102 #include <iterator>
104 #include <boost/scoped_array.hpp>
106 #include "mtftools.hxx"
107 #include "outdevstate.hxx"
109 #define EMFP_DEBUG(x)
110 //#define EMFP_DEBUG(x) x
112 using namespace ::com::sun::star;
115 // free support functions
116 // ======================
117 namespace
119 template < class MetaActionType > void setStateColor( MetaActionType* pAct,
120 bool& rIsColorSet,
121 uno::Sequence< double >& rColorSequence,
122 const cppcanvas::CanvasSharedPtr& rCanvas )
124 // set rIsColorSet and check for true at the same time
125 if( (rIsColorSet=pAct->IsSetting()) != false )
127 ::Color aColor( pAct->GetColor() );
129 // force alpha part of color to
130 // opaque. transparent painting is done
131 // explicitely via META_TRANSPARENT_ACTION
132 aColor.SetTransparency(0);
133 //aColor.SetTransparency(128);
135 rColorSequence = ::vcl::unotools::colorToDoubleSequence(
136 aColor,
137 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
142 // state stack manipulators
143 // ------------------------
144 void clearStateStack( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
146 rStates.clear();
147 const ::cppcanvas::internal::OutDevState aDefaultState;
148 rStates.push_back( aDefaultState );
151 ::cppcanvas::internal::OutDevState& getState( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
153 return rStates.back();
156 const ::cppcanvas::internal::OutDevState& getState( const ::cppcanvas::internal::VectorOfOutDevStates& rStates )
158 return rStates.back();
161 void pushState( ::cppcanvas::internal::VectorOfOutDevStates& rStates,
162 USHORT nFlags )
164 rStates.push_back( getState( rStates ) );
165 getState( rStates ).pushFlags = nFlags;
168 void popState( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
170 if( getState( rStates ).pushFlags != PUSH_ALL )
172 // a state is pushed which is incomplete, i.e. does not
173 // restore everything to the previous stack level when
174 // popped.
175 // That means, we take the old state, and restore every
176 // OutDevState member whose flag is set, from the new to the
177 // old state. Then the new state gets overwritten by the
178 // calculated state
180 // preset to-be-calculated new state with old state
181 ::cppcanvas::internal::OutDevState aCalculatedNewState( getState( rStates ) );
183 // selectively copy to-be-restored content over saved old
184 // state
185 rStates.pop_back();
187 const ::cppcanvas::internal::OutDevState& rNewState( getState( rStates ) );
189 if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) )
191 aCalculatedNewState.lineColor = rNewState.lineColor;
192 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
195 if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) )
197 aCalculatedNewState.fillColor = rNewState.fillColor;
198 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
201 if( (aCalculatedNewState.pushFlags & PUSH_FONT) )
203 aCalculatedNewState.xFont = rNewState.xFont;
204 aCalculatedNewState.fontRotation = rNewState.fontRotation;
205 aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle;
206 aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle;
207 aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle;
208 aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle;
209 aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle;
210 aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet;
211 aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet;
212 aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet;
215 if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) )
217 aCalculatedNewState.textColor = rNewState.textColor;
220 if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) )
222 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
225 if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) )
227 aCalculatedNewState.clip = rNewState.clip;
228 aCalculatedNewState.clipRect = rNewState.clipRect;
229 aCalculatedNewState.xClipPoly = rNewState.xClipPoly;
232 // TODO(F2): Raster ops NYI
233 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
234 // {
235 // }
237 if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) )
239 aCalculatedNewState.textFillColor = rNewState.textFillColor;
240 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
243 if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) )
245 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
248 // TODO(F1): Refpoint handling NYI
249 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
250 // {
251 // }
253 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) )
255 aCalculatedNewState.textLineColor = rNewState.textLineColor;
256 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
259 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) )
261 aCalculatedNewState.textAlignment = rNewState.textAlignment;
262 aCalculatedNewState.textDirection = rNewState.textDirection;
265 // TODO(F2): Text language handling NYI
266 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
267 // {
268 // }
270 // always copy push mode
271 aCalculatedNewState.pushFlags = rNewState.pushFlags;
273 // flush to stack
274 getState( rStates ) = aCalculatedNewState;
276 else
278 rStates.pop_back();
282 void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes,
283 const ::cppcanvas::internal::ActionFactoryParameters& rParms,
284 const LineInfo& rLineInfo )
286 const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
287 o_rStrokeAttributes.StrokeWidth =
288 (getState( rParms.mrStates ).mapModeTransform * aWidth).getX();
290 // setup reasonable defaults
291 o_rStrokeAttributes.MiterLimit = 1.0;
292 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
293 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
294 o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
296 if( LINE_DASH == rLineInfo.GetStyle() )
298 const ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
300 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
302 // interpret dash info only if explicitely enabled as
303 // style
304 const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
305 const double nDistance( (rState.mapModeTransform * aDistance).getX() );
307 const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
308 const double nDashLen( (rState.mapModeTransform * aDashLen).getX() );
310 const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
311 const double nDotLen( (rState.mapModeTransform * aDotLen).getX() );
313 const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
314 2*rLineInfo.GetDotCount() );
316 o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
317 double* pDashArray = o_rStrokeAttributes.DashArray.getArray();
320 // iteratively fill dash array, first with dashs, then
321 // with dots.
322 // ===================================================
324 sal_Int32 nCurrEntry=0;
326 for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
328 pDashArray[nCurrEntry++] = nDashLen;
329 pDashArray[nCurrEntry++] = nDistance;
331 for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
333 pDashArray[nCurrEntry++] = nDotLen;
334 pDashArray[nCurrEntry++] = nDistance;
340 /** Create masked BitmapEx, where the white areas of rBitmap are
341 transparent, and the other appear in rMaskColor.
343 BitmapEx createMaskBmpEx( const Bitmap& rBitmap,
344 const ::Color& rMaskColor )
346 const ::Color aWhite( COL_WHITE );
347 BitmapPalette aBiLevelPalette(2);
348 aBiLevelPalette[0] = aWhite;
349 aBiLevelPalette[1] = rMaskColor;
351 Bitmap aMask( rBitmap.CreateMask( aWhite ));
352 Bitmap aSolid( rBitmap.GetSizePixel(),
354 &aBiLevelPalette );
355 aSolid.Erase( rMaskColor );
357 return BitmapEx( aSolid, aMask );
360 /** Shameless rip from vcl/source/gdi/outdev3.cxx
362 Should consolidate, into something like basetxt...
364 sal_Unicode getLocalizedChar( sal_Unicode nChar, LanguageType eLang )
366 // currently only conversion from ASCII digits is interesting
367 if( (nChar < '0') || ('9' < nChar) )
368 return nChar;
370 sal_Unicode nOffset(0);
371 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
372 // CAVEAT! To some like Mongolian MS assigned the same primary language
373 // although the script type is different!
374 switch( eLang & LANGUAGE_MASK_PRIMARY )
376 default:
377 break;
379 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
380 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
381 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
382 nOffset = 0x0660 - '0'; // arabic/persian/urdu
383 break;
384 case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY:
385 nOffset = 0x09E6 - '0'; // bengali
386 break;
387 case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY:
388 nOffset = 0x1040 - '0'; // burmese
389 break;
390 case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY:
391 nOffset = 0x0966 - '0'; // devanagari
392 break;
393 case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY:
394 nOffset = 0x0AE6 - '0'; // gujarati
395 break;
396 case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY:
397 nOffset = 0x0CE6 - '0'; // kannada
398 break;
399 case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY:
400 nOffset = 0x17E0 - '0'; // khmer
401 break;
402 case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY:
403 nOffset = 0x0ED0 - '0'; // lao
404 break;
405 case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY:
406 nOffset = 0x0D66 - '0'; // malayalam
407 break;
408 case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY:
409 if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
410 nOffset = 0x1810 - '0'; // mongolian
411 else
412 nOffset = 0; // mongolian cyrillic
413 break;
414 case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY:
415 nOffset = 0x0B66 - '0'; // oriya
416 break;
417 case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY:
418 nOffset = 0x0BE7 - '0'; // tamil
419 break;
420 case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY:
421 nOffset = 0x0C66 - '0'; // telugu
422 break;
423 case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY:
424 nOffset = 0x0E50 - '0'; // thai
425 break;
426 case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY:
427 nOffset = 0x0F20 - '0'; // tibetan
428 break;
431 nChar = sal::static_int_cast<sal_Unicode>(nChar + nOffset);
432 return nChar;
435 void convertToLocalizedNumerals( XubString& rStr,
436 LanguageType eTextLanguage )
438 const sal_Unicode* pBase = rStr.GetBuffer();
439 const sal_Unicode* pBegin = pBase + 0;
440 const xub_StrLen nEndIndex = rStr.Len();
441 const sal_Unicode* pEnd = pBase + nEndIndex;
443 for( ; pBegin < pEnd; ++pBegin )
445 // TODO: are there non-digit localizations?
446 if( (*pBegin >= '0') && (*pBegin <= '9') )
448 // translate characters to local preference
449 sal_Unicode cChar = getLocalizedChar( *pBegin, eTextLanguage );
450 if( cChar != *pBegin )
451 rStr.SetChar( sal::static_int_cast<USHORT>(pBegin - pBase), cChar );
458 namespace cppcanvas
460 namespace internal
462 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
463 const ActionFactoryParameters& rParms )
465 const OutDevState& rState( getState( rParms.mrStates ) );
466 if( (!rState.isLineColorSet &&
467 !rState.isFillColorSet) ||
468 (rState.lineColor.getLength() == 0 &&
469 rState.fillColor.getLength() == 0) )
471 return false;
474 ActionSharedPtr pPolyAction(
475 internal::PolyPolyActionFactory::createPolyPolyAction(
476 rPolyPoly, rParms.mrCanvas, rState ) );
478 if( pPolyAction )
480 maActions.push_back(
481 MtfAction(
482 pPolyAction,
483 rParms.mrCurrActionIndex ) );
485 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
488 return true;
491 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly,
492 const ActionFactoryParameters& rParms )
494 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
495 rParms );
498 void ImplRenderer::skipContent( GDIMetaFile& rMtf,
499 const char* pCommentString,
500 sal_Int32& io_rCurrActionIndex ) const
502 ENSURE_OR_THROW( pCommentString,
503 "ImplRenderer::skipContent(): NULL string given" );
505 MetaAction* pCurrAct;
506 while( (pCurrAct=rMtf.NextAction()) != NULL )
508 // increment action index, we've skipped an action.
509 ++io_rCurrActionIndex;
511 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
512 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii(
513 pCommentString ) == COMPARE_EQUAL )
515 // requested comment found, done
516 return;
520 // EOF
521 return;
524 bool ImplRenderer::isActionContained( GDIMetaFile& rMtf,
525 const char* pCommentString,
526 USHORT nType ) const
528 ENSURE_OR_THROW( pCommentString,
529 "ImplRenderer::isActionContained(): NULL string given" );
531 bool bRet( false );
533 // at least _one_ call to GDIMetaFile::NextAction() is
534 // executed
535 ULONG nPos( 1 );
537 MetaAction* pCurrAct;
538 while( (pCurrAct=rMtf.NextAction()) != NULL )
540 if( pCurrAct->GetType() == nType )
542 bRet = true; // action type found
543 break;
546 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
547 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii(
548 pCommentString ) == COMPARE_EQUAL )
550 // delimiting end comment found, done
551 bRet = false; // not yet found
552 break;
555 ++nPos;
558 // rewind metafile to previous position (this method must
559 // not change the current metaaction)
560 while( nPos-- )
561 rMtf.WindPrev();
563 if( !pCurrAct )
565 // EOF, and not yet found
566 bRet = false;
569 return bRet;
572 void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly,
573 const ::Gradient& rGradient,
574 const ActionFactoryParameters& rParms,
575 bool bIsPolygonRectangle,
576 bool bSubsettableActions )
578 DBG_TESTSOLARMUTEX();
580 ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
581 aDevicePoly.transform( getState( rParms.mrStates ).mapModeTransform );
583 // decide, whether this gradient can be rendered natively
584 // by the canvas, or must be emulated via VCL gradient
585 // action extraction.
586 const USHORT nSteps( rGradient.GetSteps() );
588 if( // step count is infinite, can use native canvas
589 // gradients here
590 nSteps == 0 ||
591 // step count is sufficiently high, such that no
592 // discernible difference should be visible.
593 nSteps > 64 )
595 uno::Reference< rendering::XParametricPolyPolygon2DFactory > xFactory(
596 rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
598 if( xFactory.is() )
600 ::basegfx::B2DHomMatrix aTextureTransformation;
601 rendering::Texture aTexture;
603 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
604 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
605 aTexture.Alpha = 1.0;
608 // setup start/end color values
609 // ----------------------------
611 // scale color coefficients with gradient intensities
612 const USHORT nStartIntensity( rGradient.GetStartIntensity() );
613 ::Color aVCLStartColor( rGradient.GetStartColor() );
614 aVCLStartColor.SetRed( (UINT8)(aVCLStartColor.GetRed() * nStartIntensity / 100) );
615 aVCLStartColor.SetGreen( (UINT8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
616 aVCLStartColor.SetBlue( (UINT8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) );
618 const USHORT nEndIntensity( rGradient.GetEndIntensity() );
619 ::Color aVCLEndColor( rGradient.GetEndColor() );
620 aVCLEndColor.SetRed( (UINT8)(aVCLEndColor.GetRed() * nEndIntensity / 100) );
621 aVCLEndColor.SetGreen( (UINT8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
622 aVCLEndColor.SetBlue( (UINT8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) );
624 uno::Reference<rendering::XColorSpace> xColorSpace(
625 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
626 const uno::Sequence< double > aStartColor(
627 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor,
628 xColorSpace ));
629 const uno::Sequence< double > aEndColor(
630 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor,
631 xColorSpace ));
633 uno::Sequence< uno::Sequence < double > > aColors(2);
634 uno::Sequence< double > aStops(2);
636 aStops[0] = 0.0;
637 aStops[1] = 1.0;
639 aColors[0] = aStartColor;
640 aColors[1] = aEndColor;
643 // Setup texture transformation
644 // ----------------------------
646 const ::basegfx::B2DRectangle aBounds(
647 ::basegfx::tools::getRange(aDevicePoly) );
649 // setup rotation angle. VCL rotates
650 // counter-clockwise, while canvas transformation
651 // rotates clockwise
652 double nRotation( -rGradient.GetAngle() * M_PI / 1800.0 );
654 switch( rGradient.GetStyle() )
656 case GRADIENT_LINEAR:
657 // FALLTHROUGH intended
658 case GRADIENT_AXIAL:
660 // standard orientation for VCL linear
661 // gradient is vertical, thus, rotate 90
662 // degrees
663 nRotation += M_PI/2.0;
665 const double nBorder(
666 ::basegfx::pruneScaleValue(
667 (1.0 - rGradient.GetBorder() / 100.0) ) );
669 // shrink texture, to account for border
670 // (only in x direction, linear gradient
671 // is constant in y direction, anyway)
672 aTextureTransformation.scale( nBorder,
673 1.0 );
675 // linear gradients don't respect offsets
676 // (they are implicitely assumed to be
677 // 50%). linear gradients don't have
678 // border on both sides, only on the
679 // startColor side, axial gradients have
680 // border on both sides. As both gradients
681 // are invariant in y direction: leave y
682 // offset alone.
683 double nOffsetX( rGradient.GetBorder() / 200.0 );
685 // determine type of gradient (and necessary
686 // transformation matrix, should it be emulated by a
687 // generic gradient)
688 switch( rGradient.GetStyle() )
690 case GRADIENT_LINEAR:
691 nOffsetX = rGradient.GetBorder() / 100.0;
692 aTexture.Gradient = xFactory->createLinearHorizontalGradient( aColors,
693 aStops );
694 break;
696 case GRADIENT_AXIAL:
697 // vcl considers center color as start color
698 ::std::swap(aColors[0],aColors[1]);
699 aTexture.Gradient = xFactory->createAxialHorizontalGradient( aColors,
700 aStops );
701 break;
703 default: // other cases can't happen
704 break;
707 // apply border offset values
708 aTextureTransformation.translate( nOffsetX,
709 0.0 );
711 // rotate texture according to gradient rotation
712 aTextureTransformation.translate( -0.5, -0.5 );
713 aTextureTransformation.rotate( nRotation );
715 // to let the first strip of a rotated
716 // gradient start at the _edge_ of the
717 // bound rect (and not, due to rotation,
718 // slightly inside), slightly enlarge the
719 // gradient:
721 // y/2 sin(alpha) + x/2 cos(alpha)
723 // (values to change are not actual
724 // gradient scales, but original bound
725 // rect dimensions. Since we still want
726 // the border setting to apply after that,
727 // we multiply with that as above for
728 // nScaleX)
729 const double nScale(
730 ::basegfx::pruneScaleValue(
731 fabs( aBounds.getHeight()*sin(nRotation) ) +
732 fabs( aBounds.getWidth()*cos(nRotation) )));
734 aTextureTransformation.scale( nScale, nScale );
736 // translate back origin to center of
737 // primitive
738 aTextureTransformation.translate( 0.5*aBounds.getWidth(),
739 0.5*aBounds.getHeight() );
741 break;
743 case GRADIENT_RADIAL:
744 // FALLTHROUGH intended
745 case GRADIENT_ELLIPTICAL:
746 // FALLTHROUGH intended
747 case GRADIENT_SQUARE:
748 // FALLTHROUGH intended
749 case GRADIENT_RECT:
751 // determine scale factors for the gradient (must
752 // be scaled up from [0,1]x[0,1] rect to object
753 // bounds). Will potentially changed in switch
754 // statement below.
755 // Respect border value, while doing so, the VCL
756 // gradient's border will effectively shrink the
757 // resulting gradient.
758 double nScaleX( aBounds.getWidth() * (1.0 - rGradient.GetBorder() / 100.0) );
759 double nScaleY( aBounds.getHeight()* (1.0 - rGradient.GetBorder() / 100.0) );
761 // determine offset values. Since the border is
762 // divided half-by-half to both sides of the
763 // gradient, divide translation offset by an
764 // additional 2. Also respect offset here, but
765 // since VCL gradients have their center at [0,0]
766 // for zero offset, but canvas gradients have
767 // their top, left edge aligned with the
768 // primitive, and offset of 50% effectively must
769 // yield zero shift. Both values will potentially
770 // be adapted in switch statement below.
771 double nOffsetX( aBounds.getWidth() *
772 (2.0 * rGradient.GetOfsX() - 100.0 + rGradient.GetBorder()) / 200.0 );
773 double nOffsetY( aBounds.getHeight() *
774 (2.0 * rGradient.GetOfsY() - 100.0 + rGradient.GetBorder()) / 200.0 );
776 // determine type of gradient (and necessary
777 // transformation matrix, should it be emulated by a
778 // generic gradient)
779 switch( rGradient.GetStyle() )
781 case GRADIENT_RADIAL:
783 // create isotrophic scaling
784 if( nScaleX > nScaleY )
786 nOffsetY -= (nScaleX - nScaleY) * 0.5;
787 nScaleY = nScaleX;
789 else
791 nOffsetX -= (nScaleY - nScaleX) * 0.5;
792 nScaleX = nScaleY;
795 // enlarge gradient to match bound rect diagonal
796 aTextureTransformation.translate( -0.5, -0.5 );
797 const double nScale( hypot(aBounds.getWidth(), aBounds.getHeight()) / nScaleX );
798 aTextureTransformation.scale( nScale, nScale );
799 aTextureTransformation.translate( 0.5, 0.5 );
801 aTexture.Gradient = xFactory->createEllipticalGradient( aColors,
802 aStops,
803 geometry::RealRectangle2D(0.0,0.0,
804 1.0,1.0) );
806 break;
808 case GRADIENT_ELLIPTICAL:
810 // enlarge gradient slightly
811 aTextureTransformation.translate( -0.5, -0.5 );
812 const double nSqrt2( sqrt(2.0) );
813 aTextureTransformation.scale( nSqrt2,nSqrt2 );
814 aTextureTransformation.translate( 0.5, 0.5 );
816 aTexture.Gradient = xFactory->createEllipticalGradient(
817 aColors,
818 aStops,
819 ::basegfx::unotools::rectangle2DFromB2DRectangle(
820 aBounds ));
822 break;
824 case GRADIENT_SQUARE:
825 // create isotrophic scaling
826 if( nScaleX > nScaleY )
828 nOffsetY -= (nScaleX - nScaleY) * 0.5;
829 nScaleY = nScaleX;
831 else
833 nOffsetX -= (nScaleY - nScaleX) * 0.5;
834 nScaleX = nScaleY;
837 aTexture.Gradient = xFactory->createRectangularGradient( aColors,
838 aStops,
839 geometry::RealRectangle2D(0.0,0.0,
840 1.0,1.0) );
841 break;
843 case GRADIENT_RECT:
844 aTexture.Gradient = xFactory->createRectangularGradient(
845 aColors,
846 aStops,
847 ::basegfx::unotools::rectangle2DFromB2DRectangle(
848 aBounds ) );
849 break;
851 default: // other cases can't happen
852 break;
855 nScaleX = ::basegfx::pruneScaleValue( nScaleX );
856 nScaleY = ::basegfx::pruneScaleValue( nScaleY );
858 aTextureTransformation.scale( nScaleX, nScaleY );
860 // rotate texture according to gradient rotation
861 aTextureTransformation.translate( -0.5*nScaleX, -0.5*nScaleY );
862 aTextureTransformation.rotate( nRotation );
863 aTextureTransformation.translate( 0.5*nScaleX, 0.5*nScaleY );
865 aTextureTransformation.translate( nOffsetX, nOffsetY );
867 break;
869 default:
870 ENSURE_OR_THROW( false,
871 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
872 break;
875 // As the texture coordinate space is relative to
876 // the polygon coordinate space (NOT to the
877 // polygon itself), move gradient to the start of
878 // the actual polygon. If we skip this, the
879 // gradient will always display at the origin, and
880 // not within the polygon bound (which might be
881 // miles away from the origin).
882 aTextureTransformation.translate( aBounds.getMinX(),
883 aBounds.getMinY() );
885 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
886 aTextureTransformation );
888 ActionSharedPtr pPolyAction(
889 internal::PolyPolyActionFactory::createPolyPolyAction(
890 aDevicePoly,
891 rParms.mrCanvas,
892 getState( rParms.mrStates ),
893 aTexture ) );
895 if( pPolyAction )
897 maActions.push_back(
898 MtfAction(
899 pPolyAction,
900 rParms.mrCurrActionIndex ) );
902 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
905 // done, using native gradients
906 return;
910 // cannot currently use native canvas gradients, as a
911 // finite step size is given (this funny feature is not
912 // supported by the XCanvas API)
913 pushState( rParms.mrStates, PUSH_ALL );
915 if( !bIsPolygonRectangle )
917 // only clip, if given polygon is not a rectangle in
918 // the first place (the gradient is always limited to
919 // the given bound rect)
920 updateClipping(
921 aDevicePoly,
922 rParms,
923 true );
926 GDIMetaFile aTmpMtf;
927 rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(),
928 rGradient,
929 aTmpMtf );
931 createActions( aTmpMtf, rParms, bSubsettableActions );
933 popState( rParms.mrStates );
936 uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation,
937 const ::Font& rFont,
938 const ActionFactoryParameters& rParms ) const
940 rendering::FontRequest aFontRequest;
942 if( rParms.mrParms.maFontName.isValid() )
943 aFontRequest.FontDescription.FamilyName = rParms.mrParms.maFontName.getValue();
944 else
945 aFontRequest.FontDescription.FamilyName = rFont.GetName();
947 aFontRequest.FontDescription.StyleName = rFont.GetStyleName();
949 aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
950 aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;
952 // TODO(F2): improve vclenum->panose conversion
953 aFontRequest.FontDescription.FontDescription.Weight =
954 rParms.mrParms.maFontWeight.isValid() ?
955 rParms.mrParms.maFontWeight.getValue() :
956 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
957 aFontRequest.FontDescription.FontDescription.Letterform =
958 rParms.mrParms.maFontLetterForm.isValid() ?
959 rParms.mrParms.maFontLetterForm.getValue() :
960 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
962 LanguageType aLang = rFont.GetLanguage();
963 aFontRequest.Locale = MsLangId::convertLanguageToLocale(aLang, false);
965 // setup state-local text transformation,
966 // if the font be rotated
967 const short nFontAngle( rFont.GetOrientation() );
968 if( nFontAngle != 0 )
970 // set to unity transform rotated by font angle
971 const double nAngle( nFontAngle * (F_PI / 1800.0) );
972 o_rFontRotation = -nAngle;
974 else
976 o_rFontRotation = 0.0;
979 geometry::Matrix2D aFontMatrix;
980 ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
982 // TODO(F2): use correct scale direction, font
983 // height might be width or anything else
985 // TODO(Q3): This code smells of programming by
986 // coincidence (the next two if statements)
987 const ::Size rFontSizeLog( rFont.GetSize() );
988 const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
989 if( nFontWidthLog != 0 )
991 ::Font aTestFont = rFont;
992 aTestFont.SetWidth( 0 );
993 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
994 if( nNormalWidth != nFontWidthLog )
995 if( nNormalWidth )
996 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
999 // #i52608# apply map mode scale also to font matrix - an
1000 // anisotrophic mapmode must be reflected in an
1001 // anisotrophic font matrix scale.
1002 const OutDevState& rState( getState( rParms.mrStates ) );
1003 if( !::basegfx::fTools::equal(
1004 rState.mapModeTransform.get(0,0),
1005 rState.mapModeTransform.get(1,1)) )
1007 const double nScaleX( rState.mapModeTransform.get(0,0) );
1008 const double nScaleY( rState.mapModeTransform.get(1,1) );
1010 // note: no reason to check for division by zero, we
1011 // always have the value closer (or equal) to zero as
1012 // the nominator.
1013 if( fabs(nScaleX) < fabs(nScaleY) )
1014 aFontMatrix.m00 *= nScaleX / nScaleY;
1015 else
1016 aFontMatrix.m11 *= nScaleY / nScaleX;
1018 aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
1020 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
1021 uno::Sequence< beans::PropertyValue >(),
1022 aFontMatrix );
1025 // create text effects such as shadow/relief/embossed
1026 void ImplRenderer::createTextAction( const ::Point& rStartPoint,
1027 const String rString,
1028 int nIndex,
1029 int nLength,
1030 const sal_Int32* pCharWidths,
1031 const ActionFactoryParameters& rParms,
1032 bool bSubsettableActions )
1034 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex,
1035 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
1037 if( !nLength )
1038 return; // zero-length text, no visible output
1040 const OutDevState& rState( getState( rParms.mrStates ) );
1042 // TODO(F2): implement all text effects
1043 // if( rState.textAlignment ); // TODO(F2): NYI
1045 ::Color aShadowColor( COL_AUTO );
1046 ::Color aReliefColor( COL_AUTO );
1047 ::Size aShadowOffset;
1048 ::Size aReliefOffset;
1050 uno::Reference<rendering::XColorSpace> xColorSpace(
1051 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1053 if( rState.isTextEffectShadowSet )
1055 // calculate shadow offset (similar to outdev3.cxx)
1056 // TODO(F3): better match with outdev3.cxx
1057 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
1058 if( nShadowOffset < 1 )
1059 nShadowOffset = 1;
1061 aShadowOffset.setWidth( nShadowOffset );
1062 aShadowOffset.setHeight( nShadowOffset );
1064 // determine shadow color (from outdev3.cxx)
1065 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
1066 rState.textColor, xColorSpace );
1067 bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
1068 || (aTextColor.GetLuminance() < 8);
1070 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
1071 aShadowColor.SetTransparency( aTextColor.GetTransparency() );
1074 if( rState.textReliefStyle )
1076 // calculate relief offset (similar to outdev3.cxx)
1077 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
1078 nReliefOffset += nReliefOffset/2;
1079 if( nReliefOffset < 1 )
1080 nReliefOffset = 1;
1082 if( rState.textReliefStyle == RELIEF_ENGRAVED )
1083 nReliefOffset = -nReliefOffset;
1085 aReliefOffset.setWidth( nReliefOffset );
1086 aReliefOffset.setHeight( nReliefOffset );
1088 // determine relief color (from outdev3.cxx)
1089 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
1090 rState.textColor, xColorSpace );
1092 aReliefColor = ::Color( COL_LIGHTGRAY );
1094 // we don't have a automatic color, so black is always
1095 // drawn on white (literally copied from
1096 // vcl/source/gdi/outdev3.cxx)
1097 if( aTextColor.GetColor() == COL_BLACK )
1099 aTextColor = ::Color( COL_WHITE );
1100 getState( rParms.mrStates ).textColor =
1101 ::vcl::unotools::colorToDoubleSequence(
1102 aTextColor, xColorSpace );
1105 if( aTextColor.GetColor() == COL_WHITE )
1106 aReliefColor = ::Color( COL_BLACK );
1107 aReliefColor.SetTransparency( aTextColor.GetTransparency() );
1110 // create the actual text action
1111 ActionSharedPtr pTextAction(
1112 TextActionFactory::createTextAction(
1113 rStartPoint,
1114 aReliefOffset,
1115 aReliefColor,
1116 aShadowOffset,
1117 aShadowColor,
1118 rString,
1119 nIndex,
1120 nLength,
1121 pCharWidths,
1122 rParms.mrVDev,
1123 rParms.mrCanvas,
1124 rState,
1125 rParms.mrParms,
1126 bSubsettableActions ) );
1128 ActionSharedPtr pStrikeoutTextAction;
1130 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
1132 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
1134 xub_Unicode pChars[5];
1135 if ( rState.textStrikeoutStyle == STRIKEOUT_X )
1136 pChars[0] = 'X';
1137 else
1138 pChars[0] = '/';
1139 pChars[3]=pChars[2]=pChars[1]=pChars[0];
1141 long nStrikeoutWidth = nWidth;
1142 String aStrikeoutTest( pChars, 4 );
1144 if( aStrikeoutTest.Len() )
1146 nStrikeoutWidth = ( rParms.mrVDev.GetTextWidth( aStrikeoutTest ) + 2 ) / 4;
1147 aStrikeoutTest.Erase();
1149 if( nStrikeoutWidth <= 0 )
1150 nStrikeoutWidth = 1;
1153 long nMaxWidth = nStrikeoutWidth/2;
1154 if ( nMaxWidth < 2 )
1155 nMaxWidth = 2;
1156 nMaxWidth += nWidth + 1;
1158 long nFullStrikeoutWidth = 0;
1159 String aStrikeoutText( pChars, 0 );
1160 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
1161 aStrikeoutText += pChars[0];
1164 sal_Int32 nStartPos = 0;
1165 xub_StrLen nLen = aStrikeoutText.Len();
1167 if( nLen )
1169 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
1170 nStrikeoutWidth += nInterval;
1171 sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen];
1173 for ( int i = 0;i<nLen; i++)
1175 pStrikeoutCharWidths[i] = nStrikeoutWidth;
1178 for ( int i = 1;i< nLen; i++ )
1180 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
1183 pStrikeoutTextAction =
1184 TextActionFactory::createTextAction(
1185 rStartPoint,
1186 aReliefOffset,
1187 aReliefColor,
1188 aShadowOffset,
1189 aShadowColor,
1190 aStrikeoutText,
1191 nStartPos,
1192 aStrikeoutText.Len(),
1193 pStrikeoutCharWidths,
1194 rParms.mrVDev,
1195 rParms.mrCanvas,
1196 rState,
1197 rParms.mrParms,
1198 bSubsettableActions ) ;
1202 if( pTextAction )
1204 maActions.push_back(
1205 MtfAction(
1206 pTextAction,
1207 rParms.mrCurrActionIndex ) );
1209 if ( pStrikeoutTextAction )
1211 maActions.push_back(
1212 MtfAction(
1213 pStrikeoutTextAction,
1214 rParms.mrCurrActionIndex ) );
1217 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1221 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1222 const ActionFactoryParameters& rParms,
1223 bool bIntersect )
1225 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
1226 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1228 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1229 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1231 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1232 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1234 if( !bIntersect ||
1235 (bEmptyClipRect && bEmptyClipPoly) )
1237 rState.clip = rClipPoly;
1239 else
1241 if( !bEmptyClipRect )
1243 // TODO(P3): Use Liang-Barsky polygon clip here,
1244 // after all, one object is just a rectangle!
1246 // convert rect to polygon beforehand, must revert
1247 // to general polygon clipping here.
1248 rState.clip = ::basegfx::B2DPolyPolygon(
1249 ::basegfx::tools::createPolygonFromRect(
1250 // #121100# VCL rectangular clips always
1251 // include one more pixel to the right
1252 // and the bottom
1253 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1254 rState.clipRect.Top(),
1255 rState.clipRect.Right()+1,
1256 rState.clipRect.Bottom()+1 ) ) );
1259 // AW: Simplified
1260 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1261 aClipPoly, rState.clip, true, false);
1264 // by now, our clip resides in the OutDevState::clip
1265 // poly-polygon.
1266 rState.clipRect.SetEmpty();
1268 if( rState.clip.count() == 0 )
1270 if( rState.clipRect.IsEmpty() )
1272 rState.xClipPoly.clear();
1274 else
1276 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1277 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1278 ::basegfx::B2DPolyPolygon(
1279 ::basegfx::tools::createPolygonFromRect(
1280 // #121100# VCL rectangular clips
1281 // always include one more pixel to
1282 // the right and the bottom
1283 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1284 rState.clipRect.Top(),
1285 rState.clipRect.Right()+1,
1286 rState.clipRect.Bottom()+1 ) ) ) );
1289 else
1291 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1292 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1293 rState.clip );
1297 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
1298 const ActionFactoryParameters& rParms,
1299 bool bIntersect )
1301 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
1303 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1304 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1306 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1307 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1309 if( !bIntersect ||
1310 (bEmptyClipRect && bEmptyClipPoly) )
1312 rState.clipRect = rClipRect;
1313 rState.clip.clear();
1315 else if( bEmptyClipPoly )
1317 rState.clipRect.Intersection( rClipRect );
1318 rState.clip.clear();
1320 else
1322 // TODO(P3): Handle a fourth case here, when all clip
1323 // polygons are rectangular, once B2DMultiRange's
1324 // sweep line implementation is done.
1326 // general case: convert to polygon and clip
1327 // -----------------------------------------
1329 // convert rect to polygon beforehand, must revert
1330 // to general polygon clipping here.
1331 ::basegfx::B2DPolyPolygon aClipPoly(
1332 ::basegfx::tools::createPolygonFromRect(
1333 ::basegfx::B2DRectangle( rClipRect.Left(),
1334 rClipRect.Top(),
1335 rClipRect.Right(),
1336 rClipRect.Bottom() ) ) );
1338 rState.clipRect.SetEmpty();
1340 // AW: Simplified
1341 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1342 aClipPoly, rState.clip, true, false);
1345 if( rState.clip.count() == 0 )
1347 if( rState.clipRect.IsEmpty() )
1349 rState.xClipPoly.clear();
1351 else
1353 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1354 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1355 ::basegfx::B2DPolyPolygon(
1356 ::basegfx::tools::createPolygonFromRect(
1357 // #121100# VCL rectangular clips
1358 // always include one more pixel to
1359 // the right and the bottom
1360 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1361 rState.clipRect.Top(),
1362 rState.clipRect.Right()+1,
1363 rState.clipRect.Bottom()+1 ) ) ) );
1366 else
1368 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1369 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1370 rState.clip );
1374 bool ImplRenderer::createActions( GDIMetaFile& rMtf,
1375 const ActionFactoryParameters& rFactoryParms,
1376 bool bSubsettableActions )
1378 /* TODO(P2): interpret mtf-comments
1379 ================================
1381 - gradient fillings (do that via comments)
1383 - think about mapping. _If_ we do everything in logical
1384 coordinates (which would solve the probs for stroke
1385 widths and text offsets), then we would have to
1386 recalc scaling for every drawing operation. This is
1387 because the outdev map mode might change at any time.
1388 Also keep in mind, that, although we've double precision
1389 float arithmetic now, different offsets might still
1390 generate different roundings (aka
1391 'OutputDevice::SetPixelOffset())
1395 // alias common parameters
1396 VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1397 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1398 ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1399 const Parameters& rParms(rFactoryParms.mrParms);
1400 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1403 // Loop over every metaaction
1404 // ==========================
1405 MetaAction* pCurrAct;
1407 // TODO(P1): think about caching
1408 for( pCurrAct=rMtf.FirstAction();
1409 pCurrAct;
1410 pCurrAct = rMtf.NextAction() )
1412 // execute every action, to keep VDev state up-to-date
1413 // currently used only for
1414 // - the map mode
1415 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1416 // - SetFont to process font metric specific actions
1417 pCurrAct->Execute( &rVDev );
1419 switch( pCurrAct->GetType() )
1421 // ------------------------------------------------------------
1423 // In the first part of this monster-switch, we
1424 // handle all state-changing meta actions. These
1425 // are all handled locally.
1427 // ------------------------------------------------------------
1429 case META_PUSH_ACTION:
1431 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1432 pushState( rStates,
1433 pPushAction->GetFlags() );
1435 break;
1437 case META_POP_ACTION:
1438 popState( rStates );
1439 break;
1441 case META_TEXTLANGUAGE_ACTION:
1442 // FALLTHROUGH intended
1443 case META_REFPOINT_ACTION:
1444 // handled via pCurrAct->Execute( &rVDev )
1445 break;
1447 case META_MAPMODE_ACTION:
1448 // modify current mapModeTransformation
1449 // transformation, such that subsequent
1450 // coordinates map correctly
1451 tools::calcLogic2PixelAffineTransform( getState( rStates ).mapModeTransform,
1452 rVDev );
1453 break;
1455 // monitor clip regions, to assemble clip polygon on our own
1456 case META_CLIPREGION_ACTION:
1458 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1460 if( !pClipAction->IsClipping() )
1462 // clear clipping
1463 getState( rStates ).clip.clear();
1465 else
1467 if( !pClipAction->GetRegion().HasPolyPolygon() )
1469 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1470 "region encountered, falling back to bounding box!" );
1472 // #121806# explicitely kept integer
1473 Rectangle aClipRect(
1474 rVDev.LogicToPixel(
1475 pClipAction->GetRegion().GetBoundRect() ) );
1477 // intersect current clip with given rect
1478 updateClipping(
1479 aClipRect,
1480 rFactoryParms,
1481 false );
1483 else
1485 // set new clip polygon (don't intersect
1486 // with old one, just set it)
1488 // #121806# explicitely kept integer
1489 updateClipping(
1490 rVDev.LogicToPixel(
1491 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1492 rFactoryParms,
1493 false );
1497 break;
1500 case META_ISECTRECTCLIPREGION_ACTION:
1502 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1504 // #121806# explicitely kept integer
1505 Rectangle aClipRect(
1506 rVDev.LogicToPixel( pClipAction->GetRect() ) );
1508 // intersect current clip with given rect
1509 updateClipping(
1510 aClipRect,
1511 rFactoryParms,
1512 true );
1514 break;
1517 case META_ISECTREGIONCLIPREGION_ACTION:
1519 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1521 if( !pClipAction->GetRegion().HasPolyPolygon() )
1523 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1524 "region encountered, falling back to bounding box!" );
1526 // #121806# explicitely kept integer
1527 Rectangle aClipRect(
1528 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1530 // intersect current clip with given rect
1531 updateClipping(
1532 aClipRect,
1533 rFactoryParms,
1534 true );
1536 else
1538 // intersect current clip with given clip polygon
1540 // #121806# explicitely kept integer
1541 updateClipping(
1542 rVDev.LogicToPixel(
1543 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1544 rFactoryParms,
1545 true );
1548 break;
1551 case META_MOVECLIPREGION_ACTION:
1552 // TODO(F2): NYI
1553 break;
1555 case META_LINECOLOR_ACTION:
1556 if( !rParms.maLineColor.isValid() )
1558 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1559 getState( rStates ).isLineColorSet,
1560 getState( rStates ).lineColor,
1561 rCanvas );
1563 break;
1565 case META_FILLCOLOR_ACTION:
1566 if( !rParms.maFillColor.isValid() )
1568 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1569 getState( rStates ).isFillColorSet,
1570 getState( rStates ).fillColor,
1571 rCanvas );
1573 break;
1575 case META_TEXTCOLOR_ACTION:
1577 if( !rParms.maTextColor.isValid() )
1579 // Text color is set unconditionally, thus, no
1580 // use of setStateColor here
1581 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1583 // force alpha part of color to
1584 // opaque. transparent painting is done
1585 // explicitely via META_TRANSPARENT_ACTION
1586 aColor.SetTransparency(0);
1588 getState( rStates ).textColor =
1589 ::vcl::unotools::colorToDoubleSequence(
1590 aColor,
1591 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1594 break;
1596 case META_TEXTFILLCOLOR_ACTION:
1597 if( !rParms.maTextColor.isValid() )
1599 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1600 getState( rStates ).isTextFillColorSet,
1601 getState( rStates ).textFillColor,
1602 rCanvas );
1604 break;
1606 case META_TEXTLINECOLOR_ACTION:
1607 if( !rParms.maTextColor.isValid() )
1609 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1610 getState( rStates ).isTextLineColorSet,
1611 getState( rStates ).textLineColor,
1612 rCanvas );
1614 break;
1616 case META_TEXTALIGN_ACTION:
1618 ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1619 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1621 rState.textReferencePoint = eTextAlign;
1623 break;
1625 case META_FONT_ACTION:
1627 ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1628 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1630 rState.xFont = createFont( rState.fontRotation,
1631 rFont,
1632 rFactoryParms );
1634 // TODO(Q2): define and use appropriate enumeration types
1635 rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
1636 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
1637 rState.textUnderlineStyle = rParms.maFontUnderline.isValid() ?
1638 (rParms.maFontUnderline.getValue() ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1639 (sal_Int8)rFont.GetUnderline();
1640 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
1641 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
1642 rState.isTextEffectShadowSet = (rFont.IsShadow() != FALSE);
1643 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != FALSE);
1644 rState.isTextOutlineModeSet = (rFont.IsOutline() != FALSE);
1646 break;
1648 case META_RASTEROP_ACTION:
1649 // TODO(F2): NYI
1650 break;
1652 case META_LAYOUTMODE_ACTION:
1654 // TODO(F2): A lot is missing here
1655 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1656 ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1657 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
1659 case TEXT_LAYOUT_BIDI_LTR:
1660 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1661 break;
1663 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
1664 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1665 break;
1667 case TEXT_LAYOUT_BIDI_RTL:
1668 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1669 break;
1671 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
1672 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1673 break;
1676 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1677 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1678 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1680 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1683 break;
1685 // ------------------------------------------------------------
1687 // In the second part of this monster-switch, we
1688 // handle all recursing meta actions. These are the
1689 // ones generating a metafile by themselves, which is
1690 // then processed by recursively calling this method.
1692 // ------------------------------------------------------------
1694 case META_GRADIENT_ACTION:
1696 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1697 createGradientAction( ::Polygon( pGradAct->GetRect() ),
1698 pGradAct->GetGradient(),
1699 rFactoryParms,
1700 true,
1701 bSubsettableActions );
1703 break;
1705 case META_HATCH_ACTION:
1707 // TODO(F2): use native Canvas hatches here
1708 GDIMetaFile aTmpMtf;
1710 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1711 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1712 aTmpMtf );
1713 createActions( aTmpMtf, rFactoryParms,
1714 bSubsettableActions );
1716 break;
1718 case META_EPS_ACTION:
1720 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
1721 const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
1723 // #121806# explicitely kept integer
1724 const Size aMtfSize( rSubstitute.GetPrefSize() );
1725 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1726 rSubstitute.GetPrefMapMode() ) );
1728 // #i44110# correct null-sized output - there
1729 // are metafiles which have zero size in at
1730 // least one dimension
1731 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1732 ::std::max( aMtfSizePixPre.Height(), 1L ) );
1734 // Setup local transform, such that the
1735 // metafile renders itself into the given
1736 // output rectangle
1737 pushState( rStates, PUSH_ALL );
1739 rVDev.Push();
1740 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1742 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1743 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1745 getState( rStates ).transform.translate( rPos.X(),
1746 rPos.Y() );
1747 getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1748 (double)rSize.Height() / aMtfSizePix.Height() );
1750 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1751 rFactoryParms,
1752 bSubsettableActions );
1754 rVDev.Pop();
1755 popState( rStates );
1757 break;
1759 // handle metafile comments, to retrieve
1760 // meta-information for gradients, fills and
1761 // strokes. May skip actions, and may recurse.
1762 case META_COMMENT_ACTION:
1764 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1766 // Handle gradients
1767 if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
1769 MetaGradientExAction* pGradAction = NULL;
1770 bool bDone( false );
1771 while( !bDone &&
1772 (pCurrAct=rMtf.NextAction()) != NULL )
1774 switch( pCurrAct->GetType() )
1776 // extract gradient info
1777 case META_GRADIENTEX_ACTION:
1778 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1779 break;
1781 // skip broken-down rendering, output gradient when sequence is ended
1782 case META_COMMENT_ACTION:
1783 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL )
1785 bDone = true;
1787 if( pGradAction )
1789 createGradientAction( pGradAction->GetPolyPolygon(),
1790 pGradAction->GetGradient(),
1791 rFactoryParms,
1792 false,
1793 bSubsettableActions );
1796 break;
1800 // TODO(P2): Handle drawing layer strokes, via
1801 // XPATHSTROKE_SEQ_BEGIN comment
1803 // Handle drawing layer fills
1804 else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) )
1806 const BYTE* pData = pAct->GetData();
1807 if ( pData )
1809 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1811 SvtGraphicFill aFill;
1812 aMemStm >> aFill;
1814 // TODO(P2): Also handle gradients and
1815 // hatches like this
1817 // only evaluate comment for pure
1818 // bitmap fills. If a transparency
1819 // gradient is involved (denoted by
1820 // the FloatTransparent action), take
1821 // the normal meta actions.
1822 if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1823 !isActionContained( rMtf,
1824 "XPATHFILL_SEQ_END",
1825 META_FLOATTRANSPARENT_ACTION ) )
1827 rendering::Texture aTexture;
1829 // TODO(F1): the SvtGraphicFill
1830 // can also transport metafiles
1831 // here, handle that case, too
1832 Graphic aGraphic;
1833 aFill.getGraphic( aGraphic );
1835 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1836 const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1838 ::SvtGraphicFill::Transform aTransform;
1839 aFill.getTransform( aTransform );
1841 ::basegfx::B2DHomMatrix aMatrix;
1843 // convert to basegfx matrix
1844 aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1845 aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1846 aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1847 aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1848 aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1849 aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1851 ::basegfx::B2DHomMatrix aScale;
1852 aScale.scale( aBmpSize.Width(),
1853 aBmpSize.Height() );
1855 // post-multiply with the bitmap
1856 // size (XCanvas' texture assumes
1857 // the given bitmap to be
1858 // normalized to [0,1]x[0,1]
1859 // rectangle)
1860 aMatrix = aMatrix * aScale;
1862 // pre-multiply with the
1863 // logic-to-pixel scale factor
1864 // (the metafile comment works in
1865 // logical coordinates).
1866 ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1867 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1868 rVDev );
1870 ::basegfx::unotools::affineMatrixFromHomMatrix(
1871 aTexture.AffineTransform,
1872 aMatrix );
1874 aTexture.Alpha = 1.0 - aFill.getTransparency();
1875 aTexture.Bitmap =
1876 ::vcl::unotools::xBitmapFromBitmapEx(
1877 rCanvas->getUNOCanvas()->getDevice(),
1878 aBmpEx );
1879 if( aFill.isTiling() )
1881 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1882 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1884 else
1886 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
1887 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
1890 ::PolyPolygon aPath;
1891 aFill.getPath( aPath );
1893 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1894 aPoly.transform( getState( rStates ).mapModeTransform );
1895 ActionSharedPtr pPolyAction(
1896 internal::PolyPolyActionFactory::createPolyPolyAction(
1897 aPoly,
1898 rCanvas,
1899 getState( rStates ),
1900 aTexture ) );
1902 if( pPolyAction )
1904 maActions.push_back(
1905 MtfAction(
1906 pPolyAction,
1907 io_rCurrActionIndex ) );
1909 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1912 // skip broken-down render output
1913 skipContent( rMtf,
1914 "XPATHFILL_SEQ_END",
1915 io_rCurrActionIndex );
1919 // Handle drawing layer fills
1920 else if( pAct->GetComment().Equals( "EMF_PLUS" ) ) {
1921 static int count = -1, limit = 0x7fffffff;
1922 if (count == -1) {
1923 count = 0;
1924 char *env;
1925 if (env = getenv ("EMF_PLUS_LIMIT")) {
1926 limit = atoi (env);
1927 EMFP_DEBUG (printf ("EMF+ records limit: %d\n", limit));
1930 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer, size: %d\n", pAct->GetDataSize ()));
1931 if (count < limit)
1932 processEMFPlus( pAct, rFactoryParms, getState( rStates ), rCanvas );
1933 count ++;
1934 } else if( pAct->GetComment().Equals( "EMF_PLUS_HEADER_INFO" ) ) {
1935 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer - header info, size: %d\n", pAct->GetDataSize ()));
1937 SvMemoryStream rMF ((void*) pAct->GetData (), pAct->GetDataSize (), STREAM_READ);
1939 rMF >> nFrameLeft >> nFrameTop >> nFrameRight >> nFrameBottom;
1940 EMFP_DEBUG (printf ("EMF+ picture frame: %d,%d - %d,%d\n", nFrameLeft, nFrameTop, nFrameRight, nFrameBottom));
1941 rMF >> nPixX >> nPixY >> nMmX >> nMmY;
1942 EMFP_DEBUG (printf ("EMF+ ref device pixel size: %dx%d mm size: %dx%d\n", nPixX, nPixY, nMmX, nMmY));
1944 rMF >> aBaseTransform;
1945 //aWorldTransform.Set (aBaseTransform);
1948 break;
1950 // ------------------------------------------------------------
1952 // In the third part of this monster-switch, we
1953 // handle all 'acting' meta actions. These are all
1954 // processed by constructing function objects for
1955 // them, which will later ease caching.
1957 // ------------------------------------------------------------
1959 case META_POINT_ACTION:
1961 const OutDevState& rState( getState( rStates ) );
1962 if( rState.lineColor.getLength() )
1964 ActionSharedPtr pPointAction(
1965 internal::PointActionFactory::createPointAction(
1966 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1967 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1968 rCanvas,
1969 rState ) );
1971 if( pPointAction )
1973 maActions.push_back(
1974 MtfAction(
1975 pPointAction,
1976 io_rCurrActionIndex ) );
1978 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1982 break;
1984 case META_PIXEL_ACTION:
1986 const OutDevState& rState( getState( rStates ) );
1987 if( rState.lineColor.getLength() )
1989 ActionSharedPtr pPointAction(
1990 internal::PointActionFactory::createPointAction(
1991 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1992 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1993 rCanvas,
1994 rState,
1995 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1997 if( pPointAction )
1999 maActions.push_back(
2000 MtfAction(
2001 pPointAction,
2002 io_rCurrActionIndex ) );
2004 io_rCurrActionIndex += pPointAction->getActionCount()-1;
2008 break;
2010 case META_LINE_ACTION:
2012 const OutDevState& rState( getState( rStates ) );
2013 if( rState.lineColor.getLength() )
2015 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
2017 const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
2019 const ::basegfx::B2DPoint aStartPoint(
2020 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
2021 const ::basegfx::B2DPoint aEndPoint(
2022 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
2024 ActionSharedPtr pLineAction;
2026 if( rLineInfo.IsDefault() )
2028 // plain hair line
2029 pLineAction =
2030 internal::LineActionFactory::createLineAction(
2031 aStartPoint,
2032 aEndPoint,
2033 rCanvas,
2034 rState );
2036 if( pLineAction )
2038 maActions.push_back(
2039 MtfAction(
2040 pLineAction,
2041 io_rCurrActionIndex ) );
2043 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2046 else if( LINE_NONE != rLineInfo.GetStyle() )
2048 // 'thick' line
2049 rendering::StrokeAttributes aStrokeAttributes;
2051 setupStrokeAttributes( aStrokeAttributes,
2052 rFactoryParms,
2053 rLineInfo );
2055 // XCanvas can only stroke polygons,
2056 // not simple lines - thus, handle
2057 // this case via the polypolygon
2058 // action
2059 ::basegfx::B2DPolygon aPoly;
2060 aPoly.append( aStartPoint );
2061 aPoly.append( aEndPoint );
2062 pLineAction =
2063 internal::PolyPolyActionFactory::createPolyPolyAction(
2064 ::basegfx::B2DPolyPolygon( aPoly ),
2065 rCanvas, rState, aStrokeAttributes );
2067 if( pLineAction )
2069 maActions.push_back(
2070 MtfAction(
2071 pLineAction,
2072 io_rCurrActionIndex ) );
2074 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2077 // else: line style is default
2078 // (i.e. invisible), don't generate action
2081 break;
2083 case META_RECT_ACTION:
2085 const Rectangle& rRect(
2086 static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
2088 if( rRect.IsEmpty() )
2089 break;
2091 const OutDevState& rState( getState( rStates ) );
2092 const ::basegfx::B2DPoint aTopLeftPixel(
2093 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
2094 const ::basegfx::B2DPoint aBottomRightPixel(
2095 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2096 // #121100# OutputDevice::DrawRect() fills
2097 // rectangles Apple-like, i.e. with one
2098 // additional pixel to the right and bottom.
2099 ::basegfx::B2DPoint(1,1) );
2101 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
2102 ::basegfx::B2DRange( aTopLeftPixel,
2103 aBottomRightPixel )),
2104 rFactoryParms );
2105 break;
2108 case META_ROUNDRECT_ACTION:
2110 const Rectangle& rRect(
2111 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
2113 if( rRect.IsEmpty() )
2114 break;
2116 ::basegfx::B2DPolygon aPoly(
2117 ::basegfx::tools::createPolygonFromRect(
2118 ::basegfx::B2DRange(
2119 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2120 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2121 ::basegfx::B2DPoint(1,1) ),
2122 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
2123 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
2124 aPoly.transform( getState( rStates ).mapModeTransform );
2126 createFillAndStroke( aPoly,
2127 rFactoryParms );
2129 break;
2131 case META_ELLIPSE_ACTION:
2133 const Rectangle& rRect(
2134 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
2136 if( rRect.IsEmpty() )
2137 break;
2139 const ::basegfx::B2DRange aRange(
2140 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2141 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2142 ::basegfx::B2DPoint(1,1) );
2144 ::basegfx::B2DPolygon aPoly(
2145 ::basegfx::tools::createPolygonFromEllipse(
2146 aRange.getCenter(),
2147 aRange.getWidth(),
2148 aRange.getHeight() ));
2149 aPoly.transform( getState( rStates ).mapModeTransform );
2151 createFillAndStroke( aPoly,
2152 rFactoryParms );
2154 break;
2156 case META_ARC_ACTION:
2158 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2159 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2160 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2161 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2162 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2163 aPoly.transform( getState( rStates ).mapModeTransform );
2165 createFillAndStroke( aPoly,
2166 rFactoryParms );
2168 break;
2170 case META_PIE_ACTION:
2172 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2173 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2174 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2175 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2176 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2177 aPoly.transform( getState( rStates ).mapModeTransform );
2179 createFillAndStroke( aPoly,
2180 rFactoryParms );
2182 break;
2184 case META_CHORD_ACTION:
2186 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2187 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2188 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2189 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2190 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2191 aPoly.transform( getState( rStates ).mapModeTransform );
2193 createFillAndStroke( aPoly,
2194 rFactoryParms );
2196 break;
2198 case META_POLYLINE_ACTION:
2200 const OutDevState& rState( getState( rStates ) );
2201 if( rState.lineColor.getLength() ||
2202 rState.fillColor.getLength() )
2204 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2206 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2207 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2208 aPoly.transform( rState.mapModeTransform );
2210 ActionSharedPtr pLineAction;
2212 if( rLineInfo.IsDefault() )
2214 // plain hair line polygon
2215 pLineAction =
2216 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2217 ::basegfx::B2DPolyPolygon(aPoly),
2218 rCanvas,
2219 rState );
2221 if( pLineAction )
2223 maActions.push_back(
2224 MtfAction(
2225 pLineAction,
2226 io_rCurrActionIndex ) );
2228 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2231 else if( LINE_NONE != rLineInfo.GetStyle() )
2233 // 'thick' line polygon
2234 rendering::StrokeAttributes aStrokeAttributes;
2236 setupStrokeAttributes( aStrokeAttributes,
2237 rFactoryParms,
2238 rLineInfo );
2240 pLineAction =
2241 internal::PolyPolyActionFactory::createPolyPolyAction(
2242 ::basegfx::B2DPolyPolygon(aPoly),
2243 rCanvas,
2244 rState,
2245 aStrokeAttributes ) ;
2247 if( pLineAction )
2249 maActions.push_back(
2250 MtfAction(
2251 pLineAction,
2252 io_rCurrActionIndex ) );
2254 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2257 // else: line style is default
2258 // (i.e. invisible), don't generate action
2261 break;
2263 case META_POLYGON_ACTION:
2265 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2266 aPoly.transform( getState( rStates ).mapModeTransform );
2267 createFillAndStroke( aPoly,
2268 rFactoryParms );
2270 break;
2272 case META_POLYPOLYGON_ACTION:
2274 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2275 aPoly.transform( getState( rStates ).mapModeTransform );
2276 createFillAndStroke( aPoly,
2277 rFactoryParms );
2279 break;
2281 case META_BMP_ACTION:
2283 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2285 ActionSharedPtr pBmpAction(
2286 internal::BitmapActionFactory::createBitmapAction(
2287 pAct->GetBitmap(),
2288 getState( rStates ).mapModeTransform *
2289 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2290 rCanvas,
2291 getState( rStates ) ) );
2293 if( pBmpAction )
2295 maActions.push_back(
2296 MtfAction(
2297 pBmpAction,
2298 io_rCurrActionIndex ) );
2300 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2303 break;
2305 case META_BMPSCALE_ACTION:
2307 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2309 ActionSharedPtr pBmpAction(
2310 internal::BitmapActionFactory::createBitmapAction(
2311 pAct->GetBitmap(),
2312 getState( rStates ).mapModeTransform *
2313 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2314 getState( rStates ).mapModeTransform *
2315 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2316 rCanvas,
2317 getState( rStates ) ) );
2319 if( pBmpAction )
2321 maActions.push_back(
2322 MtfAction(
2323 pBmpAction,
2324 io_rCurrActionIndex ) );
2326 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2329 break;
2331 case META_BMPSCALEPART_ACTION:
2333 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2335 // crop bitmap to given source rectangle (no
2336 // need to copy and convert the whole bitmap)
2337 Bitmap aBmp( pAct->GetBitmap() );
2338 const Rectangle aCropRect( pAct->GetSrcPoint(),
2339 pAct->GetSrcSize() );
2340 aBmp.Crop( aCropRect );
2342 ActionSharedPtr pBmpAction(
2343 internal::BitmapActionFactory::createBitmapAction(
2344 aBmp,
2345 getState( rStates ).mapModeTransform *
2346 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2347 getState( rStates ).mapModeTransform *
2348 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2349 rCanvas,
2350 getState( rStates ) ) );
2352 if( pBmpAction )
2354 maActions.push_back(
2355 MtfAction(
2356 pBmpAction,
2357 io_rCurrActionIndex ) );
2359 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2362 break;
2364 case META_BMPEX_ACTION:
2366 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2368 ActionSharedPtr pBmpAction(
2369 internal::BitmapActionFactory::createBitmapAction(
2370 pAct->GetBitmapEx(),
2371 getState( rStates ).mapModeTransform *
2372 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2373 rCanvas,
2374 getState( rStates ) ) );
2376 if( pBmpAction )
2378 maActions.push_back(
2379 MtfAction(
2380 pBmpAction,
2381 io_rCurrActionIndex ) );
2383 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2386 break;
2388 case META_BMPEXSCALE_ACTION:
2390 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2392 ActionSharedPtr pBmpAction(
2393 internal::BitmapActionFactory::createBitmapAction(
2394 pAct->GetBitmapEx(),
2395 getState( rStates ).mapModeTransform *
2396 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2397 getState( rStates ).mapModeTransform *
2398 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2399 rCanvas,
2400 getState( rStates ) ) );
2402 if( pBmpAction )
2404 maActions.push_back(
2405 MtfAction(
2406 pBmpAction,
2407 io_rCurrActionIndex ) );
2409 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2412 break;
2414 case META_BMPEXSCALEPART_ACTION:
2416 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2418 // crop bitmap to given source rectangle (no
2419 // need to copy and convert the whole bitmap)
2420 BitmapEx aBmp( pAct->GetBitmapEx() );
2421 const Rectangle aCropRect( pAct->GetSrcPoint(),
2422 pAct->GetSrcSize() );
2423 aBmp.Crop( aCropRect );
2425 ActionSharedPtr pBmpAction(
2426 internal::BitmapActionFactory::createBitmapAction(
2427 aBmp,
2428 getState( rStates ).mapModeTransform *
2429 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2430 getState( rStates ).mapModeTransform *
2431 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2432 rCanvas,
2433 getState( rStates ) ) );
2435 if( pBmpAction )
2437 maActions.push_back(
2438 MtfAction(
2439 pBmpAction,
2440 io_rCurrActionIndex ) );
2442 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2445 break;
2447 case META_MASK_ACTION:
2449 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2451 // create masked BitmapEx right here, as the
2452 // canvas does not provide equivalent
2453 // functionality
2454 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2455 pAct->GetColor() ));
2457 ActionSharedPtr pBmpAction(
2458 internal::BitmapActionFactory::createBitmapAction(
2459 aBmp,
2460 getState( rStates ).mapModeTransform *
2461 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2462 rCanvas,
2463 getState( rStates ) ) );
2465 if( pBmpAction )
2467 maActions.push_back(
2468 MtfAction(
2469 pBmpAction,
2470 io_rCurrActionIndex ) );
2472 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2475 break;
2477 case META_MASKSCALE_ACTION:
2479 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2481 // create masked BitmapEx right here, as the
2482 // canvas does not provide equivalent
2483 // functionality
2484 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2485 pAct->GetColor() ));
2487 ActionSharedPtr pBmpAction(
2488 internal::BitmapActionFactory::createBitmapAction(
2489 aBmp,
2490 getState( rStates ).mapModeTransform *
2491 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2492 getState( rStates ).mapModeTransform *
2493 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2494 rCanvas,
2495 getState( rStates ) ) );
2497 if( pBmpAction )
2499 maActions.push_back(
2500 MtfAction(
2501 pBmpAction,
2502 io_rCurrActionIndex ) );
2504 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2507 break;
2509 case META_MASKSCALEPART_ACTION:
2511 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2513 // create masked BitmapEx right here, as the
2514 // canvas does not provide equivalent
2515 // functionality
2516 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2517 pAct->GetColor() ));
2519 // crop bitmap to given source rectangle (no
2520 // need to copy and convert the whole bitmap)
2521 const Rectangle aCropRect( pAct->GetSrcPoint(),
2522 pAct->GetSrcSize() );
2523 aBmp.Crop( aCropRect );
2525 ActionSharedPtr pBmpAction(
2526 internal::BitmapActionFactory::createBitmapAction(
2527 aBmp,
2528 getState( rStates ).mapModeTransform *
2529 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2530 getState( rStates ).mapModeTransform *
2531 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2532 rCanvas,
2533 getState( rStates ) ) );
2535 if( pBmpAction )
2537 maActions.push_back(
2538 MtfAction(
2539 pBmpAction,
2540 io_rCurrActionIndex ) );
2542 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2545 break;
2547 case META_GRADIENTEX_ACTION:
2548 // TODO(F1): use native Canvas gradients here
2549 // action is ignored here, because redundant to META_GRADIENT_ACTION
2550 break;
2552 case META_WALLPAPER_ACTION:
2553 // TODO(F2): NYI
2554 break;
2556 case META_TRANSPARENT_ACTION:
2558 const OutDevState& rState( getState( rStates ) );
2559 if( rState.lineColor.getLength() ||
2560 rState.fillColor.getLength() )
2562 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2563 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2564 aPoly.transform( rState.mapModeTransform );
2566 ActionSharedPtr pPolyAction(
2567 internal::PolyPolyActionFactory::createPolyPolyAction(
2568 aPoly,
2569 rCanvas,
2570 rState,
2571 pAct->GetTransparence() ) );
2573 if( pPolyAction )
2575 maActions.push_back(
2576 MtfAction(
2577 pPolyAction,
2578 io_rCurrActionIndex ) );
2580 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2584 break;
2586 case META_FLOATTRANSPARENT_ACTION:
2588 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2590 internal::MtfAutoPtr pMtf(
2591 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2593 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2594 internal::GradientAutoPtr pGradient(
2595 new Gradient( pAct->GetGradient() ) );
2597 DBG_TESTSOLARMUTEX();
2599 ActionSharedPtr pFloatTransAction(
2600 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2601 pMtf,
2602 pGradient,
2603 rParms,
2604 getState( rStates ).mapModeTransform *
2605 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2606 getState( rStates ).mapModeTransform *
2607 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2608 rCanvas,
2609 getState( rStates ) ) );
2611 if( pFloatTransAction )
2613 maActions.push_back(
2614 MtfAction(
2615 pFloatTransAction,
2616 io_rCurrActionIndex ) );
2618 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2621 break;
2623 case META_TEXT_ACTION:
2625 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2626 XubString sText = XubString( pAct->GetText() );
2628 if( rVDev.GetDigitLanguage())
2629 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2631 createTextAction(
2632 pAct->GetPoint(),
2633 sText,
2634 pAct->GetIndex(),
2635 pAct->GetLen() == (USHORT)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2636 NULL,
2637 rFactoryParms,
2638 bSubsettableActions );
2640 break;
2642 case META_TEXTARRAY_ACTION:
2644 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2645 XubString sText = XubString( pAct->GetText() );
2647 if( rVDev.GetDigitLanguage())
2648 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2650 createTextAction(
2651 pAct->GetPoint(),
2652 sText,
2653 pAct->GetIndex(),
2654 pAct->GetLen() == (USHORT)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2655 pAct->GetDXArray(),
2656 rFactoryParms,
2657 bSubsettableActions );
2659 break;
2661 case META_TEXTLINE_ACTION:
2663 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2665 const OutDevState& rState( getState( rStates ) );
2666 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2667 rVDev ) );
2668 const ::Point aStartPoint( pAct->GetStartPoint() );
2669 const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2670 ::basegfx::B2DSize(pAct->GetWidth(),
2671 0 ));
2673 ActionSharedPtr pPolyAction(
2674 PolyPolyActionFactory::createPolyPolyAction(
2675 tools::createTextLinesPolyPolygon(
2676 rState.mapModeTransform *
2677 ::basegfx::B2DPoint(
2678 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2679 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2680 aSize.getX(),
2681 tools::createTextLineInfo( rVDev,
2682 rState )),
2683 rCanvas,
2684 rState ) );
2686 if( pPolyAction.get() )
2688 maActions.push_back(
2689 MtfAction(
2690 pPolyAction,
2691 io_rCurrActionIndex ) );
2693 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2696 break;
2698 case META_TEXTRECT_ACTION:
2700 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2702 pushState( rStates, PUSH_ALL );
2704 // use the VDev to break up the text rect
2705 // action into readily formatted lines
2706 GDIMetaFile aTmpMtf;
2707 rVDev.AddTextRectActions( pAct->GetRect(),
2708 pAct->GetText(),
2709 pAct->GetStyle(),
2710 aTmpMtf );
2712 createActions( aTmpMtf,
2713 rFactoryParms,
2714 bSubsettableActions );
2716 popState( rStates );
2718 break;
2721 case META_STRETCHTEXT_ACTION:
2723 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2724 XubString sText = XubString( pAct->GetText() );
2726 if( rVDev.GetDigitLanguage())
2727 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2729 const USHORT nLen( pAct->GetLen() == (USHORT)STRING_LEN ?
2730 pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() );
2732 // #i70897# Nothing to do, actually...
2733 if( nLen == 0 )
2734 break;
2736 // have to fit the text into the given
2737 // width. This is achieved by internally
2738 // generating a DX array, and uniformly
2739 // distributing the excess/insufficient width
2740 // to every logical character.
2741 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
2743 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2744 pAct->GetIndex(), pAct->GetLen() );
2746 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2748 // Last entry of pDXArray contains total width of the text
2749 sal_Int32* p=pDXArray.get();
2750 for( USHORT i=1; i<=nLen; ++i )
2752 // calc ratio for every array entry, to
2753 // distribute rounding errors 'evenly'
2754 // across the characters. Note that each
2755 // entry represents the 'end' position of
2756 // the corresponding character, thus, we
2757 // let i run from 1 to nLen.
2758 *p++ += (sal_Int32)i*nWidthDifference/nLen;
2761 createTextAction(
2762 pAct->GetPoint(),
2763 sText,
2764 pAct->GetIndex(),
2765 pAct->GetLen() == (USHORT)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2766 pDXArray.get(),
2767 rFactoryParms,
2768 bSubsettableActions );
2770 break;
2772 default:
2773 OSL_ENSURE( false,
2774 "Unknown meta action type encountered" );
2775 break;
2778 // increment action index (each mtf action counts _at
2779 // least_ one. Some count for more, therefore,
2780 // io_rCurrActionIndex is sometimes incremented by
2781 // pAct->getActionCount()-1 above, the -1 being the
2782 // correction for the unconditional increment here).
2783 ++io_rCurrActionIndex;
2786 return true;
2790 namespace
2792 class ActionRenderer
2794 public:
2795 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2796 maTransformation( rTransformation ),
2797 mbRet( true )
2801 bool result()
2803 return mbRet;
2806 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2808 // ANDing the result. We want to fail if at least
2809 // one action failed.
2810 mbRet &= rAction.mpAction->render( maTransformation );
2813 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2814 const Action::Subset& rSubset )
2816 // ANDing the result. We want to fail if at least
2817 // one action failed.
2818 mbRet &= rAction.mpAction->render( maTransformation,
2819 rSubset );
2822 private:
2823 ::basegfx::B2DHomMatrix maTransformation;
2824 bool mbRet;
2827 class AreaQuery
2829 public:
2830 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2831 maTransformation( rTransformation ),
2832 maBounds()
2836 bool result()
2838 return true; // nothing can fail here
2841 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2843 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2846 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2847 const Action::Subset& rSubset )
2849 maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2850 rSubset ) );
2853 ::basegfx::B2DRange getBounds() const
2855 return maBounds;
2858 private:
2859 ::basegfx::B2DHomMatrix maTransformation;
2860 ::basegfx::B2DRange maBounds;
2863 // Doing that via inline class. Compilers tend to not inline free
2864 // functions.
2865 struct UpperBoundActionIndexComparator
2867 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2868 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2870 const sal_Int32 nLHSCount( rLHS.mpAction ?
2871 rLHS.mpAction->getActionCount() : 0 );
2872 const sal_Int32 nRHSCount( rRHS.mpAction ?
2873 rRHS.mpAction->getActionCount() : 0 );
2875 // compare end of action range, to have an action selected
2876 // by lower_bound even if the requested index points in
2877 // the middle of the action's range
2878 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2882 /** Algorithm to apply given functor to a subset range
2884 @tpl Functor
2886 Functor to call for each element of the subset
2887 range. Must provide the following method signatures:
2888 bool result() (returning false if operation failed)
2891 template< typename Functor > bool
2892 forSubsetRange( Functor& rFunctor,
2893 ImplRenderer::ActionVector::const_iterator aRangeBegin,
2894 ImplRenderer::ActionVector::const_iterator aRangeEnd,
2895 sal_Int32 nStartIndex,
2896 sal_Int32 nEndIndex,
2897 const ImplRenderer::ActionVector::const_iterator& rEnd )
2899 if( aRangeBegin == aRangeEnd )
2901 // only a single action. Setup subset, and call functor
2902 Action::Subset aSubset;
2903 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2904 nStartIndex - aRangeBegin->mnOrigIndex );
2905 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2906 nEndIndex - aRangeBegin->mnOrigIndex );
2908 ENSURE_OR_RETURN( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2909 "ImplRenderer::forSubsetRange(): Invalid indices" );
2911 rFunctor( *aRangeBegin, aSubset );
2913 else
2915 // more than one action.
2917 // render partial first, full intermediate, and
2918 // partial last action
2919 Action::Subset aSubset;
2920 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2921 nStartIndex - aRangeBegin->mnOrigIndex );
2922 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2924 ENSURE_OR_RETURN( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2925 "ImplRenderer::forSubsetRange(): Invalid indices" );
2927 rFunctor( *aRangeBegin, aSubset );
2929 // first action rendered, skip to next
2930 ++aRangeBegin;
2932 // render full middle actions
2933 while( aRangeBegin != aRangeEnd )
2934 rFunctor( *aRangeBegin++ );
2936 if( aRangeEnd == rEnd ||
2937 aRangeEnd->mnOrigIndex > nEndIndex )
2939 // aRangeEnd denotes end of action vector,
2941 // or
2943 // nEndIndex references something _after_
2944 // aRangeBegin, but _before_ aRangeEnd
2946 // either way: no partial action left
2947 return rFunctor.result();
2950 aSubset.mnSubsetBegin = 0;
2951 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2953 ENSURE_OR_RETURN( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2954 "ImplRenderer::forSubsetRange(): Invalid indices" );
2956 rFunctor( *aRangeEnd, aSubset );
2959 return rFunctor.result();
2963 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2964 sal_Int32& io_rEndIndex,
2965 ActionVector::const_iterator& o_rRangeBegin,
2966 ActionVector::const_iterator& o_rRangeEnd ) const
2968 ENSURE_OR_RETURN( io_rStartIndex<=io_rEndIndex,
2969 "ImplRenderer::getSubsetIndices(): invalid action range" );
2971 ENSURE_OR_RETURN( !maActions.empty(),
2972 "ImplRenderer::getSubsetIndices(): no actions to render" );
2974 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2975 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2976 maActions.back().mpAction->getActionCount() );
2978 // clip given range to permissible values (there might be
2979 // ranges before and behind the valid indices)
2980 io_rStartIndex = ::std::max( nMinActionIndex,
2981 io_rStartIndex );
2982 io_rEndIndex = ::std::min( nMaxActionIndex,
2983 io_rEndIndex );
2985 if( io_rStartIndex == io_rEndIndex ||
2986 io_rStartIndex > io_rEndIndex )
2988 // empty range, don't render anything. The second
2989 // condition e.g. happens if the requested range lies
2990 // fully before or behind the valid action indices.
2991 return false;
2995 const ActionVector::const_iterator aBegin( maActions.begin() );
2996 const ActionVector::const_iterator aEnd( maActions.end() );
2999 // find start and end action
3000 // =========================
3001 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
3002 MtfAction( ActionSharedPtr(), io_rStartIndex ),
3003 UpperBoundActionIndexComparator() );
3004 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
3005 MtfAction( ActionSharedPtr(), io_rEndIndex ),
3006 UpperBoundActionIndexComparator() );
3007 return true;
3011 // Public methods
3012 // ====================================================================
3014 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
3015 const GDIMetaFile& rMtf,
3016 const Parameters& rParams ) :
3017 CanvasGraphicHelper( rCanvas ),
3018 maActions()
3020 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
3022 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
3023 "ImplRenderer::ImplRenderer(): Invalid canvas" );
3024 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
3025 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
3027 // make sure canvas and graphic device are valid; action
3028 // creation don't check that every time
3029 if( rCanvas.get() == NULL ||
3030 !rCanvas->getUNOCanvas().is() ||
3031 !rCanvas->getUNOCanvas()->getDevice().is() )
3033 // leave actions empty
3034 return;
3037 VectorOfOutDevStates aStateStack;
3039 VirtualDevice aVDev;
3040 aVDev.EnableOutput( FALSE );
3042 // Setup VDev for state tracking and mapping
3043 // =========================================
3045 aVDev.SetMapMode( rMtf.GetPrefMapMode() );
3047 const Size aMtfSize( rMtf.GetPrefSize() );
3048 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
3049 rMtf.GetPrefMapMode() ) );
3050 const Point aEmptyPt;
3051 const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) );
3053 // #i44110# correct null-sized output - there are shapes
3054 // which have zero size in at least one dimension
3055 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
3056 ::std::max( aMtfSizePixPre.Height(), 1L ) );
3058 sal_Int32 nCurrActions(0);
3059 ActionFactoryParameters aParms(aStateStack,
3060 rCanvas,
3061 aVDev,
3062 rParams,
3063 nCurrActions );
3065 // init state stack
3066 clearStateStack( aStateStack );
3068 // Setup local state, such that the metafile renders
3069 // itself into a one-by-one square at the origin for
3070 // identity view and render transformations
3071 getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(),
3072 1.0 / aMtfSizePix.Height() );
3074 tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform,
3075 aVDev );
3077 ColorSharedPtr pColor( getCanvas()->createColor() );
3079 // setup default text color to black
3080 getState( aStateStack ).textColor =
3081 getState( aStateStack ).textFillColor =
3082 getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF );
3084 // apply overrides from the Parameters struct
3085 if( rParams.maFillColor.isValid() )
3087 getState( aStateStack ).isFillColorSet = true;
3088 getState( aStateStack ).fillColor = pColor->getDeviceColor( rParams.maFillColor.getValue() );
3090 if( rParams.maLineColor.isValid() )
3092 getState( aStateStack ).isLineColorSet = true;
3093 getState( aStateStack ).lineColor = pColor->getDeviceColor( rParams.maLineColor.getValue() );
3095 if( rParams.maTextColor.isValid() )
3097 getState( aStateStack ).isTextFillColorSet = true;
3098 getState( aStateStack ).isTextLineColorSet = true;
3099 getState( aStateStack ).textColor =
3100 getState( aStateStack ).textFillColor =
3101 getState( aStateStack ).textLineColor = pColor->getDeviceColor( rParams.maTextColor.getValue() );
3103 if( rParams.maFontName.isValid() ||
3104 rParams.maFontWeight.isValid() ||
3105 rParams.maFontLetterForm.isValid() ||
3106 rParams.maFontUnderline.isValid() )
3108 ::cppcanvas::internal::OutDevState& rState = getState( aStateStack );
3110 rState.xFont = createFont( rState.fontRotation,
3111 ::Font(), // default font
3112 aParms );
3115 /* EMF+ */
3116 memset (aObjects, 0, sizeof (aObjects));
3117 mbMultipart = false;
3119 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
3120 // we're
3121 // changing
3122 // the
3123 // current
3124 // action
3125 // in
3126 // createActions!
3127 aParms,
3128 true // TODO(P1): make subsettability configurable
3132 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
3133 const BitmapEx& rBmpEx,
3134 const Parameters& rParams ) :
3135 CanvasGraphicHelper( rCanvas ),
3136 maActions()
3138 // TODO(F3): property modification parameters are
3139 // currently ignored for Bitmaps
3140 (void)rParams;
3142 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" );
3144 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
3145 "ImplRenderer::ImplRenderer(): Invalid canvas" );
3146 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
3147 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
3149 // make sure canvas and graphic device are valid; action
3150 // creation don't check that every time
3151 if( rCanvas.get() == NULL ||
3152 !rCanvas->getUNOCanvas().is() ||
3153 !rCanvas->getUNOCanvas()->getDevice().is() )
3155 // leave actions empty
3156 return;
3159 OutDevState aState;
3161 const Size aBmpSize( rBmpEx.GetSizePixel() );
3163 // Setup local state, such that the bitmap renders itself
3164 // into a one-by-one square for identity view and render
3165 // transformations
3166 aState.transform.scale( 1.0 / aBmpSize.Width(),
3167 1.0 / aBmpSize.Height() );
3169 // create a single action for the provided BitmapEx
3170 maActions.push_back(
3171 MtfAction(
3172 BitmapActionFactory::createBitmapAction(
3173 rBmpEx,
3174 ::basegfx::B2DPoint(),
3175 rCanvas,
3176 aState),
3177 0 ) );
3180 ImplRenderer::~ImplRenderer()
3184 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
3185 sal_Int32 nEndIndex ) const
3187 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3189 ActionVector::const_iterator aRangeBegin;
3190 ActionVector::const_iterator aRangeEnd;
3194 if( !getSubsetIndices( nStartIndex, nEndIndex,
3195 aRangeBegin, aRangeEnd ) )
3196 return true; // nothing to render (but _that_ was successful)
3198 // now, aRangeBegin references the action in which the
3199 // subset rendering must start, and aRangeEnd references
3200 // the action in which the subset rendering must end (it
3201 // might also end right at the start of the referenced
3202 // action, such that zero of that action needs to be
3203 // rendered).
3206 // render subset of actions
3207 // ========================
3209 ::basegfx::B2DHomMatrix aMatrix;
3210 ::canvas::tools::getRenderStateTransform( aMatrix,
3211 getRenderState() );
3213 ActionRenderer aRenderer( aMatrix );
3215 return forSubsetRange( aRenderer,
3216 aRangeBegin,
3217 aRangeEnd,
3218 nStartIndex,
3219 nEndIndex,
3220 maActions.end() );
3222 catch( uno::Exception& )
3224 OSL_ENSURE( false,
3225 rtl::OUStringToOString(
3226 comphelper::anyToString( cppu::getCaughtException() ),
3227 RTL_TEXTENCODING_UTF8 ).getStr() );
3229 // convert error to return value
3230 return false;
3234 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3235 sal_Int32 nEndIndex ) const
3237 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3239 ActionVector::const_iterator aRangeBegin;
3240 ActionVector::const_iterator aRangeEnd;
3242 if( !getSubsetIndices( nStartIndex, nEndIndex,
3243 aRangeBegin, aRangeEnd ) )
3244 return ::basegfx::B2DRange(); // nothing to render -> empty range
3246 // now, aRangeBegin references the action in which the
3247 // subset querying must start, and aRangeEnd references
3248 // the action in which the subset querying must end (it
3249 // might also end right at the start of the referenced
3250 // action, such that zero of that action needs to be
3251 // queried).
3254 // query bounds for subset of actions
3255 // ==================================
3257 ::basegfx::B2DHomMatrix aMatrix;
3258 ::canvas::tools::getRenderStateTransform( aMatrix,
3259 getRenderState() );
3261 AreaQuery aQuery( aMatrix );
3262 forSubsetRange( aQuery,
3263 aRangeBegin,
3264 aRangeEnd,
3265 nStartIndex,
3266 nEndIndex,
3267 maActions.end() );
3269 return aQuery.getBounds();
3272 bool ImplRenderer::draw() const
3274 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" );
3276 ::basegfx::B2DHomMatrix aMatrix;
3277 ::canvas::tools::getRenderStateTransform( aMatrix,
3278 getRenderState() );
3282 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3284 catch( uno::Exception& )
3286 OSL_ENSURE( false,
3287 rtl::OUStringToOString(
3288 comphelper::anyToString( cppu::getCaughtException() ),
3289 RTL_TEXTENCODING_UTF8 ).getStr() );
3291 return false;