Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / cppcanvas / source / mtfrenderer / implrenderer.cxx
bloba8145bb9d2f65057ec4d851aad5355f725184909
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <canvas/debug.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <canvas/verbosetrace.hxx>
23 #include <osl/mutex.hxx>
24 #include <vcl/svapp.hxx>
25 #include <comphelper/sequence.hxx>
26 #include <comphelper/anytostring.hxx>
27 #include <cppuhelper/exc_hlp.hxx>
28 #include <cppcanvas/canvas.hxx>
29 #include <com/sun/star/rendering/XGraphicDevice.hpp>
30 #include <com/sun/star/rendering/TexturingMode.hpp>
31 #include <com/sun/star/uno/Sequence.hxx>
32 #include <com/sun/star/geometry/RealPoint2D.hpp>
33 #include <com/sun/star/rendering/PanoseProportion.hpp>
34 #include <com/sun/star/rendering/ViewState.hpp>
35 #include <com/sun/star/rendering/RenderState.hpp>
36 #include <com/sun/star/rendering/XCanvasFont.hpp>
37 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
38 #include <com/sun/star/rendering/XCanvas.hpp>
39 #include <com/sun/star/rendering/PathCapType.hpp>
40 #include <com/sun/star/rendering/PathJoinType.hpp>
41 #include <basegfx/tools/canvastools.hxx>
42 #include <basegfx/tools/gradienttools.hxx>
43 #include <basegfx/numeric/ftools.hxx>
44 #include <basegfx/polygon/b2dpolypolygontools.hxx>
45 #include <basegfx/polygon/b2dpolygontools.hxx>
46 #include <basegfx/polygon/b2dpolygon.hxx>
47 #include <basegfx/polygon/b2dpolypolygon.hxx>
48 #include <basegfx/matrix/b2dhommatrix.hxx>
49 #include <basegfx/vector/b2dsize.hxx>
50 #include <basegfx/range/b2drectangle.hxx>
51 #include <basegfx/point/b2dpoint.hxx>
52 #include <basegfx/tuple/b2dtuple.hxx>
53 #include <basegfx/polygon/b2dpolygonclipper.hxx>
54 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
55 #include <canvas/canvastools.hxx>
56 #include <vcl/canvastools.hxx>
57 #include <vcl/salbtype.hxx>
58 #include <vcl/gdimtf.hxx>
59 #include <vcl/metaact.hxx>
60 #include <vcl/virdev.hxx>
61 #include <vcl/metric.hxx>
62 #include <vcl/graphictools.hxx>
63 #include <tools/poly.hxx>
64 #include <i18nlangtag/languagetag.hxx>
65 #include <implrenderer.hxx>
66 #include <tools.hxx>
67 #include <outdevstate.hxx>
68 #include <action.hxx>
69 #include <bitmapaction.hxx>
70 #include <lineaction.hxx>
71 #include <pointaction.hxx>
72 #include <polypolyaction.hxx>
73 #include <textaction.hxx>
74 #include <transparencygroupaction.hxx>
75 #include <vector>
76 #include <algorithm>
77 #include <iterator>
78 #include <boost/scoped_array.hpp>
79 #include "mtftools.hxx"
80 #include "outdevstate.hxx"
81 #include <basegfx/matrix/b2dhommatrixtools.hxx>
83 using namespace ::com::sun::star;
86 // free support functions
87 // ======================
88 namespace
90 template < class MetaActionType > void setStateColor( MetaActionType* pAct,
91 bool& rIsColorSet,
92 uno::Sequence< double >& rColorSequence,
93 const cppcanvas::CanvasSharedPtr& rCanvas )
95 // set rIsColorSet and check for true at the same time
96 if( (rIsColorSet=pAct->IsSetting()) != false )
98 ::Color aColor( pAct->GetColor() );
100 // force alpha part of color to
101 // opaque. transparent painting is done
102 // explicitly via META_TRANSPARENT_ACTION
103 aColor.SetTransparency(0);
104 //aColor.SetTransparency(128);
106 rColorSequence = ::vcl::unotools::colorToDoubleSequence(
107 aColor,
108 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
112 void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes,
113 const ::cppcanvas::internal::ActionFactoryParameters& rParms,
114 const LineInfo& rLineInfo )
116 const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
117 o_rStrokeAttributes.StrokeWidth =
118 (rParms.mrStates.getState().mapModeTransform * aWidth).getX();
120 // setup reasonable defaults
121 o_rStrokeAttributes.MiterLimit = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
122 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
123 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
125 switch(rLineInfo.GetLineJoin())
127 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
128 o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE;
129 break;
130 case basegfx::B2DLINEJOIN_BEVEL:
131 o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL;
132 break;
133 case basegfx::B2DLINEJOIN_MITER:
134 o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
135 break;
136 case basegfx::B2DLINEJOIN_ROUND:
137 o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND;
138 break;
141 switch(rLineInfo.GetLineCap())
143 default: /* com::sun::star::drawing::LineCap_BUTT */
145 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
146 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
147 break;
149 case com::sun::star::drawing::LineCap_ROUND:
151 o_rStrokeAttributes.StartCapType = rendering::PathCapType::ROUND;
152 o_rStrokeAttributes.EndCapType = rendering::PathCapType::ROUND;
153 break;
155 case com::sun::star::drawing::LineCap_SQUARE:
157 o_rStrokeAttributes.StartCapType = rendering::PathCapType::SQUARE;
158 o_rStrokeAttributes.EndCapType = rendering::PathCapType::SQUARE;
159 break;
163 if( LINE_DASH == rLineInfo.GetStyle() )
165 const ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
167 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
169 // interpret dash info only if explicitly enabled as
170 // style
171 const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
172 const double nDistance( (rState.mapModeTransform * aDistance).getX() );
174 const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
175 const double nDashLen( (rState.mapModeTransform * aDashLen).getX() );
177 const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
178 const double nDotLen( (rState.mapModeTransform * aDotLen).getX() );
180 const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
181 2*rLineInfo.GetDotCount() );
183 o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
184 double* pDashArray = o_rStrokeAttributes.DashArray.getArray();
187 // iteratively fill dash array, first with dashs, then
188 // with dots.
189 // ===================================================
191 sal_Int32 nCurrEntry=0;
193 for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
195 pDashArray[nCurrEntry++] = nDashLen;
196 pDashArray[nCurrEntry++] = nDistance;
198 for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
200 pDashArray[nCurrEntry++] = nDotLen;
201 pDashArray[nCurrEntry++] = nDistance;
207 /** Create masked BitmapEx, where the white areas of rBitmap are
208 transparent, and the other appear in rMaskColor.
210 BitmapEx createMaskBmpEx( const Bitmap& rBitmap,
211 const ::Color& rMaskColor )
213 const ::Color aWhite( COL_WHITE );
214 BitmapPalette aBiLevelPalette(2);
215 aBiLevelPalette[0] = aWhite;
216 aBiLevelPalette[1] = rMaskColor;
218 Bitmap aMask( rBitmap.CreateMask( aWhite ));
219 Bitmap aSolid( rBitmap.GetSizePixel(),
221 &aBiLevelPalette );
222 aSolid.Erase( rMaskColor );
224 return BitmapEx( aSolid, aMask );
227 OUString convertToLocalizedNumerals(const OUString& rStr,
228 LanguageType eTextLanguage)
230 OUStringBuffer aBuf(rStr);
231 for (sal_Int32 i = 0; i < aBuf.getLength(); ++i)
233 sal_Unicode nChar = aBuf[i];
234 if (nChar >= '0' && nChar <= '9')
235 aBuf[i] = GetLocalizedChar(nChar, eTextLanguage);
237 return aBuf.makeStringAndClear();
241 namespace cppcanvas
243 namespace internal
245 // state stack manipulators
246 // ------------------------
247 void VectorOfOutDevStates::clearStateStack()
249 m_aStates.clear();
250 const OutDevState aDefaultState;
251 m_aStates.push_back(aDefaultState);
254 OutDevState& VectorOfOutDevStates::getState()
256 return m_aStates.back();
259 const OutDevState& VectorOfOutDevStates::getState() const
261 return m_aStates.back();
264 void VectorOfOutDevStates::pushState(sal_uInt16 nFlags)
266 m_aStates.push_back( getState() );
267 getState().pushFlags = nFlags;
270 void VectorOfOutDevStates::popState()
272 if( getState().pushFlags != PUSH_ALL )
274 // a state is pushed which is incomplete, i.e. does not
275 // restore everything to the previous stack level when
276 // popped.
277 // That means, we take the old state, and restore every
278 // OutDevState member whose flag is set, from the new to the
279 // old state. Then the new state gets overwritten by the
280 // calculated state
282 // preset to-be-calculated new state with old state
283 OutDevState aCalculatedNewState( getState() );
285 // selectively copy to-be-restored content over saved old
286 // state
287 m_aStates.pop_back();
289 const OutDevState& rNewState( getState() );
291 if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) )
293 aCalculatedNewState.lineColor = rNewState.lineColor;
294 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
297 if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) )
299 aCalculatedNewState.fillColor = rNewState.fillColor;
300 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
303 if( (aCalculatedNewState.pushFlags & PUSH_FONT) )
305 aCalculatedNewState.xFont = rNewState.xFont;
306 aCalculatedNewState.fontRotation = rNewState.fontRotation;
307 aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle;
308 aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle;
309 aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle;
310 aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle;
311 aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle;
312 aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet;
313 aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet;
314 aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet;
317 if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) )
319 aCalculatedNewState.textColor = rNewState.textColor;
322 if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) )
324 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
327 if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) )
329 aCalculatedNewState.clip = rNewState.clip;
330 aCalculatedNewState.clipRect = rNewState.clipRect;
331 aCalculatedNewState.xClipPoly = rNewState.xClipPoly;
334 // TODO(F2): Raster ops NYI
335 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
336 // {
337 // }
339 if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) )
341 aCalculatedNewState.textFillColor = rNewState.textFillColor;
342 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
345 if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) )
347 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
350 // TODO(F1): Refpoint handling NYI
351 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
352 // {
353 // }
355 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) )
357 aCalculatedNewState.textLineColor = rNewState.textLineColor;
358 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
361 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) )
363 aCalculatedNewState.textAlignment = rNewState.textAlignment;
364 aCalculatedNewState.textDirection = rNewState.textDirection;
367 // TODO(F2): Text language handling NYI
368 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
369 // {
370 // }
372 // always copy push mode
373 aCalculatedNewState.pushFlags = rNewState.pushFlags;
375 // flush to stack
376 getState() = aCalculatedNewState;
378 else
380 m_aStates.pop_back();
384 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
385 const ActionFactoryParameters& rParms )
387 const OutDevState& rState( rParms.mrStates.getState() );
388 if( (!rState.isLineColorSet &&
389 !rState.isFillColorSet) ||
390 (rState.lineColor.getLength() == 0 &&
391 rState.fillColor.getLength() == 0) )
393 return false;
396 ActionSharedPtr pPolyAction(
397 internal::PolyPolyActionFactory::createPolyPolyAction(
398 rPolyPoly, rParms.mrCanvas, rState ) );
400 if( pPolyAction )
402 maActions.push_back(
403 MtfAction(
404 pPolyAction,
405 rParms.mrCurrActionIndex ) );
407 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
410 return true;
413 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly,
414 const ActionFactoryParameters& rParms )
416 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
417 rParms );
420 void ImplRenderer::skipContent( GDIMetaFile& rMtf,
421 const char* pCommentString,
422 sal_Int32& io_rCurrActionIndex ) const
424 ENSURE_OR_THROW( pCommentString,
425 "ImplRenderer::skipContent(): NULL string given" );
427 MetaAction* pCurrAct;
428 while( (pCurrAct=rMtf.NextAction()) != NULL )
430 // increment action index, we've skipped an action.
431 ++io_rCurrActionIndex;
433 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
434 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
435 pCommentString) )
437 // requested comment found, done
438 return;
442 // EOF
443 return;
446 bool ImplRenderer::isActionContained( GDIMetaFile& rMtf,
447 const char* pCommentString,
448 sal_uInt16 nType ) const
450 ENSURE_OR_THROW( pCommentString,
451 "ImplRenderer::isActionContained(): NULL string given" );
453 bool bRet( false );
455 // at least _one_ call to GDIMetaFile::NextAction() is
456 // executed
457 sal_uIntPtr nPos( 1 );
459 MetaAction* pCurrAct;
460 while( (pCurrAct=rMtf.NextAction()) != NULL )
462 if( pCurrAct->GetType() == nType )
464 bRet = true; // action type found
465 break;
468 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
469 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
470 pCommentString) )
472 // delimiting end comment found, done
473 bRet = false; // not yet found
474 break;
477 ++nPos;
480 // rewind metafile to previous position (this method must
481 // not change the current metaaction)
482 while( nPos-- )
483 rMtf.WindPrev();
485 if( !pCurrAct )
487 // EOF, and not yet found
488 bRet = false;
491 return bRet;
494 void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly,
495 const ::Gradient& rGradient,
496 const ActionFactoryParameters& rParms,
497 bool bIsPolygonRectangle,
498 bool bSubsettableActions )
500 DBG_TESTSOLARMUTEX();
502 ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
503 aDevicePoly.transform( rParms.mrStates.getState().mapModeTransform );
505 // decide, whether this gradient can be rendered natively
506 // by the canvas, or must be emulated via VCL gradient
507 // action extraction.
508 const sal_uInt16 nSteps( rGradient.GetSteps() );
510 if( // step count is infinite, can use native canvas
511 // gradients here
512 nSteps == 0 ||
513 // step count is sufficiently high, such that no
514 // discernible difference should be visible.
515 nSteps > 64 )
517 uno::Reference< lang::XMultiServiceFactory> xFactory(
518 rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
520 if( xFactory.is() )
522 rendering::Texture aTexture;
524 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
525 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
526 aTexture.Alpha = 1.0;
529 // setup start/end color values
530 // ----------------------------
532 // scale color coefficients with gradient intensities
533 const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() );
534 ::Color aVCLStartColor( rGradient.GetStartColor() );
535 aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) );
536 aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
537 aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) );
539 const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() );
540 ::Color aVCLEndColor( rGradient.GetEndColor() );
541 aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) );
542 aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
543 aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) );
545 uno::Reference<rendering::XColorSpace> xColorSpace(
546 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
547 const uno::Sequence< double > aStartColor(
548 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor,
549 xColorSpace ));
550 const uno::Sequence< double > aEndColor(
551 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor,
552 xColorSpace ));
554 uno::Sequence< uno::Sequence < double > > aColors(2);
555 uno::Sequence< double > aStops(2);
557 if( rGradient.GetStyle() == GradientStyle_AXIAL )
559 aStops.realloc(3);
560 aColors.realloc(3);
562 aStops[0] = 0.0;
563 aStops[1] = 0.5;
564 aStops[2] = 1.0;
566 aColors[0] = aEndColor;
567 aColors[1] = aStartColor;
568 aColors[2] = aEndColor;
570 else
572 aStops[0] = 0.0;
573 aStops[1] = 1.0;
575 aColors[0] = aStartColor;
576 aColors[1] = aEndColor;
579 const ::basegfx::B2DRectangle aBounds(
580 ::basegfx::tools::getRange(aDevicePoly) );
581 const ::basegfx::B2DVector aOffset(
582 rGradient.GetOfsX() / 100.0,
583 rGradient.GetOfsY() / 100.0);
584 double fRotation( rGradient.GetAngle() * M_PI / 1800.0 );
585 const double fBorder( rGradient.GetBorder() / 100.0 );
587 basegfx::B2DHomMatrix aRot90;
588 aRot90.rotate(M_PI_2);
590 basegfx::ODFGradientInfo aGradInfo;
591 OUString aGradientService;
592 switch( rGradient.GetStyle() )
594 case GradientStyle_LINEAR:
595 aGradInfo = basegfx::tools::createLinearODFGradientInfo(
596 aBounds,
597 nSteps,
598 fBorder,
599 fRotation);
600 // map ODF to svg gradient orientation - x
601 // instead of y direction
602 aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);
603 aGradientService = "LinearGradient";
604 break;
606 case GradientStyle_AXIAL:
608 // Adapt the border so that it is suitable
609 // for the axial gradient. An axial
610 // gradient consists of two linear
611 // gradients. Each of those covers half
612 // of the total size. In order to
613 // compensate for the condensed display of
614 // the linear gradients, we have to
615 // enlarge the area taken up by the actual
616 // gradient (1-fBorder). After that we
617 // have to turn the result back into a
618 // border value, hence the second (left
619 // most 1-...
620 const double fAxialBorder (1-2*(1-fBorder));
621 aGradInfo = basegfx::tools::createAxialODFGradientInfo(
622 aBounds,
623 nSteps,
624 fAxialBorder,
625 fRotation);
626 // map ODF to svg gradient orientation - x
627 // instead of y direction
628 aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);
630 // map ODF axial gradient to 3-stop linear
631 // gradient - shift left by 0.5
632 basegfx::B2DHomMatrix aShift;
634 aShift.translate(-0.5,0);
635 aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aShift);
636 aGradientService = "LinearGradient";
637 break;
640 case GradientStyle_RADIAL:
641 aGradInfo = basegfx::tools::createRadialODFGradientInfo(
642 aBounds,
643 aOffset,
644 nSteps,
645 fBorder);
646 aGradientService = "EllipticalGradient";
647 break;
649 case GradientStyle_ELLIPTICAL:
650 aGradInfo = basegfx::tools::createEllipticalODFGradientInfo(
651 aBounds,
652 aOffset,
653 nSteps,
654 fBorder,
655 fRotation);
656 aGradientService = "EllipticalGradient";
657 break;
659 case GradientStyle_SQUARE:
660 aGradInfo = basegfx::tools::createSquareODFGradientInfo(
661 aBounds,
662 aOffset,
663 nSteps,
664 fBorder,
665 fRotation);
666 aGradientService = "RectangularGradient";
667 break;
669 case GradientStyle_RECT:
670 aGradInfo = basegfx::tools::createRectangularODFGradientInfo(
671 aBounds,
672 aOffset,
673 nSteps,
674 fBorder,
675 fRotation);
676 aGradientService = "RectangularGradient";
677 break;
679 default:
680 ENSURE_OR_THROW( false,
681 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
682 break;
685 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
686 aGradInfo.getTextureTransform() );
688 uno::Sequence<uno::Any> args(3);
689 beans::PropertyValue aProp;
690 aProp.Name = "Colors";
691 aProp.Value <<= aColors;
692 args[0] <<= aProp;
693 aProp.Name = "Stops";
694 aProp.Value <<= aStops;
695 args[1] <<= aProp;
696 aProp.Name = "AspectRatio";
697 aProp.Value <<= aGradInfo.getAspectRatio();
698 args[2] <<= aProp;
700 aTexture.Gradient.set(
701 xFactory->createInstanceWithArguments(aGradientService,
702 args),
703 uno::UNO_QUERY);
704 if( aTexture.Gradient.is() )
706 ActionSharedPtr pPolyAction(
707 internal::PolyPolyActionFactory::createPolyPolyAction(
708 aDevicePoly,
709 rParms.mrCanvas,
710 rParms.mrStates.getState(),
711 aTexture ) );
713 if( pPolyAction )
715 maActions.push_back(
716 MtfAction(
717 pPolyAction,
718 rParms.mrCurrActionIndex ) );
720 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
723 // done, using native gradients
724 return;
729 // cannot currently use native canvas gradients, as a
730 // finite step size is given (this funny feature is not
731 // supported by the XCanvas API)
732 rParms.mrStates.pushState(PUSH_ALL);
734 if( !bIsPolygonRectangle )
736 // only clip, if given polygon is not a rectangle in
737 // the first place (the gradient is always limited to
738 // the given bound rect)
739 updateClipping(
740 aDevicePoly,
741 rParms,
742 true );
745 GDIMetaFile aTmpMtf;
746 rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(),
747 rGradient,
748 aTmpMtf );
750 createActions( aTmpMtf, rParms, bSubsettableActions );
752 rParms.mrStates.popState();
755 uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation,
756 const ::Font& rFont,
757 const ActionFactoryParameters& rParms ) const
759 rendering::FontRequest aFontRequest;
761 if( rParms.mrParms.maFontName.is_initialized() )
762 aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName;
763 else
764 aFontRequest.FontDescription.FamilyName = rFont.GetName();
766 aFontRequest.FontDescription.StyleName = rFont.GetStyleName();
768 aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
769 aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;
771 // TODO(F2): improve vclenum->panose conversion
772 aFontRequest.FontDescription.FontDescription.Weight =
773 rParms.mrParms.maFontWeight.is_initialized() ?
774 *rParms.mrParms.maFontWeight :
775 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
776 aFontRequest.FontDescription.FontDescription.Letterform =
777 rParms.mrParms.maFontLetterForm.is_initialized() ?
778 *rParms.mrParms.maFontLetterForm :
779 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
780 aFontRequest.FontDescription.FontDescription.Proportion =
781 rParms.mrParms.maFontProportion.is_initialized() ?
782 *rParms.mrParms.maFontProportion :
783 (rFont.GetPitch() == PITCH_FIXED)
784 ? rendering::PanoseProportion::MONO_SPACED
785 : rendering::PanoseProportion::ANYTHING;
787 LanguageType aLang = rFont.GetLanguage();
788 aFontRequest.Locale = LanguageTag::convertToLocale( aLang, false);
790 // setup state-local text transformation,
791 // if the font be rotated
792 const short nFontAngle( rFont.GetOrientation() );
793 if( nFontAngle != 0 )
795 // set to unity transform rotated by font angle
796 const double nAngle( nFontAngle * (F_PI / 1800.0) );
797 o_rFontRotation = -nAngle;
799 else
801 o_rFontRotation = 0.0;
804 geometry::Matrix2D aFontMatrix;
805 ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
807 // TODO(F2): use correct scale direction, font
808 // height might be width or anything else
810 // TODO(Q3): This code smells of programming by
811 // coincidence (the next two if statements)
812 const ::Size rFontSizeLog( rFont.GetSize() );
813 const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
814 if( nFontWidthLog != 0 )
816 ::Font aTestFont = rFont;
817 aTestFont.SetWidth( 0 );
818 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
819 if( nNormalWidth != nFontWidthLog )
820 if( nNormalWidth )
821 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
824 // #i52608# apply map mode scale also to font matrix - an
825 // anisotrophic mapmode must be reflected in an
826 // anisotrophic font matrix scale.
827 const OutDevState& rState( rParms.mrStates.getState() );
828 if( !::basegfx::fTools::equal(
829 rState.mapModeTransform.get(0,0),
830 rState.mapModeTransform.get(1,1)) )
832 const double nScaleX( rState.mapModeTransform.get(0,0) );
833 const double nScaleY( rState.mapModeTransform.get(1,1) );
835 // note: no reason to check for division by zero, we
836 // always have the value closer (or equal) to zero as
837 // the nominator.
838 if( fabs(nScaleX) < fabs(nScaleY) )
839 aFontMatrix.m00 *= nScaleX / nScaleY;
840 else
841 aFontMatrix.m11 *= nScaleY / nScaleX;
843 aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
845 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
846 uno::Sequence< beans::PropertyValue >(),
847 aFontMatrix );
850 // create text effects such as shadow/relief/embossed
851 void ImplRenderer::createTextAction( const ::Point& rStartPoint,
852 const OUString rString,
853 int nIndex,
854 int nLength,
855 const sal_Int32* pCharWidths,
856 const ActionFactoryParameters& rParms,
857 bool bSubsettableActions )
859 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.getLength() + nIndex,
860 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
862 if( !nLength )
863 return; // zero-length text, no visible output
865 const OutDevState& rState( rParms.mrStates.getState() );
867 // TODO(F2): implement all text effects
868 // if( rState.textAlignment ); // TODO(F2): NYI
870 ::Color aShadowColor( COL_AUTO );
871 ::Color aReliefColor( COL_AUTO );
872 ::Size aShadowOffset;
873 ::Size aReliefOffset;
875 uno::Reference<rendering::XColorSpace> xColorSpace(
876 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
878 if( rState.isTextEffectShadowSet )
880 // calculate shadow offset (similar to outdev3.cxx)
881 // TODO(F3): better match with outdev3.cxx
882 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
883 if( nShadowOffset < 1 )
884 nShadowOffset = 1;
886 aShadowOffset.setWidth( nShadowOffset );
887 aShadowOffset.setHeight( nShadowOffset );
889 // determine shadow color (from outdev3.cxx)
890 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
891 rState.textColor, xColorSpace );
892 bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
893 || (aTextColor.GetLuminance() < 8);
895 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
896 aShadowColor.SetTransparency( aTextColor.GetTransparency() );
899 if( rState.textReliefStyle )
901 // calculate relief offset (similar to outdev3.cxx)
902 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
903 nReliefOffset += nReliefOffset/2;
904 if( nReliefOffset < 1 )
905 nReliefOffset = 1;
907 if( rState.textReliefStyle == RELIEF_ENGRAVED )
908 nReliefOffset = -nReliefOffset;
910 aReliefOffset.setWidth( nReliefOffset );
911 aReliefOffset.setHeight( nReliefOffset );
913 // determine relief color (from outdev3.cxx)
914 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
915 rState.textColor, xColorSpace );
917 aReliefColor = ::Color( COL_LIGHTGRAY );
919 // we don't have a automatic color, so black is always
920 // drawn on white (literally copied from
921 // vcl/source/gdi/outdev3.cxx)
922 if( aTextColor.GetColor() == COL_BLACK )
924 aTextColor = ::Color( COL_WHITE );
925 rParms.mrStates.getState().textColor =
926 ::vcl::unotools::colorToDoubleSequence(
927 aTextColor, xColorSpace );
930 if( aTextColor.GetColor() == COL_WHITE )
931 aReliefColor = ::Color( COL_BLACK );
932 aReliefColor.SetTransparency( aTextColor.GetTransparency() );
935 // create the actual text action
936 ActionSharedPtr pTextAction(
937 TextActionFactory::createTextAction(
938 rStartPoint,
939 aReliefOffset,
940 aReliefColor,
941 aShadowOffset,
942 aShadowColor,
943 rString,
944 nIndex,
945 nLength,
946 pCharWidths,
947 rParms.mrVDev,
948 rParms.mrCanvas,
949 rState,
950 rParms.mrParms,
951 bSubsettableActions ) );
953 ActionSharedPtr pStrikeoutTextAction;
955 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
957 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
959 sal_Unicode pChars[4];
960 if ( rState.textStrikeoutStyle == STRIKEOUT_X )
961 pChars[0] = 'X';
962 else
963 pChars[0] = '/';
964 pChars[3]=pChars[2]=pChars[1]=pChars[0];
966 long nStrikeoutWidth = (rParms.mrVDev.GetTextWidth(
967 OUString(pChars, SAL_N_ELEMENTS(pChars))) + 2) / 4;
969 if( nStrikeoutWidth <= 0 )
970 nStrikeoutWidth = 1;
972 long nMaxWidth = nStrikeoutWidth/2;
973 if ( nMaxWidth < 2 )
974 nMaxWidth = 2;
975 nMaxWidth += nWidth + 1;
977 long nFullStrikeoutWidth = 0;
978 OUString aStrikeoutText;
979 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
980 aStrikeoutText += OUString(pChars[0]);
982 sal_Int32 nLen = aStrikeoutText.getLength();
984 if( nLen )
986 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
987 nStrikeoutWidth += nInterval;
988 sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen];
990 for ( int i = 0;i<nLen; i++)
992 pStrikeoutCharWidths[i] = nStrikeoutWidth;
995 for ( int i = 1;i< nLen; i++ )
997 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
1000 sal_Int32 nStartPos = 0;
1002 pStrikeoutTextAction =
1003 TextActionFactory::createTextAction(
1004 rStartPoint,
1005 aReliefOffset,
1006 aReliefColor,
1007 aShadowOffset,
1008 aShadowColor,
1009 aStrikeoutText,
1010 nStartPos,
1011 aStrikeoutText.getLength(),
1012 pStrikeoutCharWidths,
1013 rParms.mrVDev,
1014 rParms.mrCanvas,
1015 rState,
1016 rParms.mrParms,
1017 bSubsettableActions ) ;
1021 if( pTextAction )
1023 maActions.push_back(
1024 MtfAction(
1025 pTextAction,
1026 rParms.mrCurrActionIndex ) );
1028 if ( pStrikeoutTextAction )
1030 maActions.push_back(
1031 MtfAction(
1032 pStrikeoutTextAction,
1033 rParms.mrCurrActionIndex ) );
1036 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1040 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1041 const ActionFactoryParameters& rParms,
1042 bool bIntersect )
1044 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1045 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1047 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1048 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1050 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1051 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1053 if( !bIntersect ||
1054 (bEmptyClipRect && bEmptyClipPoly) )
1056 rState.clip = rClipPoly;
1058 else
1060 if( !bEmptyClipRect )
1062 // TODO(P3): Use Liang-Barsky polygon clip here,
1063 // after all, one object is just a rectangle!
1065 // convert rect to polygon beforehand, must revert
1066 // to general polygon clipping here.
1067 rState.clip = ::basegfx::B2DPolyPolygon(
1068 ::basegfx::tools::createPolygonFromRect(
1069 // #121100# VCL rectangular clips always
1070 // include one more pixel to the right
1071 // and the bottom
1072 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1073 rState.clipRect.Top(),
1074 rState.clipRect.Right()+1,
1075 rState.clipRect.Bottom()+1 ) ) );
1078 // AW: Simplified
1079 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1080 aClipPoly, rState.clip, true, false);
1083 // by now, our clip resides in the OutDevState::clip
1084 // poly-polygon.
1085 rState.clipRect.SetEmpty();
1087 if( rState.clip.count() == 0 )
1089 if( rState.clipRect.IsEmpty() )
1091 rState.xClipPoly.clear();
1093 else
1095 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1096 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1097 ::basegfx::B2DPolyPolygon(
1098 ::basegfx::tools::createPolygonFromRect(
1099 // #121100# VCL rectangular clips
1100 // always include one more pixel to
1101 // the right and the bottom
1102 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1103 rState.clipRect.Top(),
1104 rState.clipRect.Right()+1,
1105 rState.clipRect.Bottom()+1 ) ) ) );
1108 else
1110 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1111 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1112 rState.clip );
1116 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
1117 const ActionFactoryParameters& rParms,
1118 bool bIntersect )
1120 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1122 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1123 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1125 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1126 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1128 if( !bIntersect ||
1129 (bEmptyClipRect && bEmptyClipPoly) )
1131 rState.clipRect = rClipRect;
1132 rState.clip.clear();
1134 else if( bEmptyClipPoly )
1136 rState.clipRect.Intersection( rClipRect );
1137 rState.clip.clear();
1139 else
1141 // TODO(P3): Handle a fourth case here, when all clip
1142 // polygons are rectangular, once B2DMultiRange's
1143 // sweep line implementation is done.
1145 // general case: convert to polygon and clip
1146 // -----------------------------------------
1148 // convert rect to polygon beforehand, must revert
1149 // to general polygon clipping here.
1150 ::basegfx::B2DPolyPolygon aClipPoly(
1151 ::basegfx::tools::createPolygonFromRect(
1152 ::basegfx::B2DRectangle( rClipRect.Left(),
1153 rClipRect.Top(),
1154 rClipRect.Right(),
1155 rClipRect.Bottom() ) ) );
1157 rState.clipRect.SetEmpty();
1159 // AW: Simplified
1160 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1161 aClipPoly, rState.clip, true, false);
1164 if( rState.clip.count() == 0 )
1166 if( rState.clipRect.IsEmpty() )
1168 rState.xClipPoly.clear();
1170 else
1172 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1173 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1174 ::basegfx::B2DPolyPolygon(
1175 ::basegfx::tools::createPolygonFromRect(
1176 // #121100# VCL rectangular clips
1177 // always include one more pixel to
1178 // the right and the bottom
1179 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1180 rState.clipRect.Top(),
1181 rState.clipRect.Right()+1,
1182 rState.clipRect.Bottom()+1 ) ) ) );
1185 else
1187 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1188 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1189 rState.clip );
1193 bool ImplRenderer::createActions( GDIMetaFile& rMtf,
1194 const ActionFactoryParameters& rFactoryParms,
1195 bool bSubsettableActions )
1197 /* TODO(P2): interpret mtf-comments
1198 ================================
1200 - gradient fillings (do that via comments)
1202 - think about mapping. _If_ we do everything in logical
1203 coordinates (which would solve the probs for stroke
1204 widths and text offsets), then we would have to
1205 recalc scaling for every drawing operation. This is
1206 because the outdev map mode might change at any time.
1207 Also keep in mind, that, although we've double precision
1208 float arithmetic now, different offsets might still
1209 generate different roundings (aka
1210 'OutputDevice::SetPixelOffset())
1214 // alias common parameters
1215 VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1216 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1217 ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1218 const Parameters& rParms(rFactoryParms.mrParms);
1219 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1222 // Loop over every metaaction
1223 // ==========================
1224 MetaAction* pCurrAct;
1226 // TODO(P1): think about caching
1227 for( pCurrAct=rMtf.FirstAction();
1228 pCurrAct;
1229 pCurrAct = rMtf.NextAction() )
1231 // execute every action, to keep VDev state up-to-date
1232 // currently used only for
1233 // - the map mode
1234 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1235 // - SetFont to process font metric specific actions
1236 pCurrAct->Execute( &rVDev );
1238 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << pCurrAct->GetType() << " (" << pCurrAct->GetType() << ")");
1240 switch( pCurrAct->GetType() )
1242 // ------------------------------------------------------------
1244 // In the first part of this monster-switch, we
1245 // handle all state-changing meta actions. These
1246 // are all handled locally.
1248 // ------------------------------------------------------------
1250 case META_PUSH_ACTION:
1252 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1253 rStates.pushState(pPushAction->GetFlags());
1255 break;
1257 case META_POP_ACTION:
1258 rStates.popState();
1259 break;
1261 case META_TEXTLANGUAGE_ACTION:
1262 // FALLTHROUGH intended
1263 case META_REFPOINT_ACTION:
1264 // handled via pCurrAct->Execute( &rVDev )
1265 break;
1267 case META_MAPMODE_ACTION:
1268 // modify current mapModeTransformation
1269 // transformation, such that subsequent
1270 // coordinates map correctly
1271 tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
1272 rVDev );
1273 break;
1275 // monitor clip regions, to assemble clip polygon on our own
1276 case META_CLIPREGION_ACTION:
1278 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1280 if( !pClipAction->IsClipping() )
1282 // clear clipping
1283 rStates.getState().clip.clear();
1285 else
1287 if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1289 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1290 "region encountered, falling back to bounding box!" );
1292 // #121806# explicitly kept integer
1293 Rectangle aClipRect(
1294 rVDev.LogicToPixel(
1295 pClipAction->GetRegion().GetBoundRect() ) );
1297 // intersect current clip with given rect
1298 updateClipping(
1299 aClipRect,
1300 rFactoryParms,
1301 false );
1303 else
1305 // set new clip polygon (don't intersect
1306 // with old one, just set it)
1308 // #121806# explicitly kept integer
1309 basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1311 aPolyPolygon.transform(rVDev.GetViewTransformation());
1312 updateClipping(
1313 aPolyPolygon,
1314 rFactoryParms,
1315 false );
1319 break;
1322 case META_ISECTRECTCLIPREGION_ACTION:
1324 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1326 // #121806# explicitly kept integer
1327 Rectangle aClipRect(
1328 rVDev.LogicToPixel( pClipAction->GetRect() ) );
1330 // intersect current clip with given rect
1331 updateClipping(
1332 aClipRect,
1333 rFactoryParms,
1334 true );
1336 break;
1339 case META_ISECTREGIONCLIPREGION_ACTION:
1341 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1343 if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1345 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1346 "region encountered, falling back to bounding box!" );
1348 // #121806# explicitly kept integer
1349 Rectangle aClipRect(
1350 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1352 // intersect current clip with given rect
1353 updateClipping(
1354 aClipRect,
1355 rFactoryParms,
1356 true );
1358 else
1360 // intersect current clip with given clip polygon
1362 // #121806# explicitly kept integer
1363 basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1365 aPolyPolygon.transform(rVDev.GetViewTransformation());
1366 updateClipping(
1367 aPolyPolygon,
1368 rFactoryParms,
1369 true );
1372 break;
1375 case META_MOVECLIPREGION_ACTION:
1376 // TODO(F2): NYI
1377 break;
1379 case META_LINECOLOR_ACTION:
1380 if( !rParms.maLineColor.is_initialized() )
1382 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1383 rStates.getState().isLineColorSet,
1384 rStates.getState().lineColor,
1385 rCanvas );
1387 else
1389 // #120994# Do switch on/off LineColor, even when a overriding one is set
1390 bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting());
1392 rStates.getState().isLineColorSet = bSetting;
1394 break;
1396 case META_FILLCOLOR_ACTION:
1397 if( !rParms.maFillColor.is_initialized() )
1399 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1400 rStates.getState().isFillColorSet,
1401 rStates.getState().fillColor,
1402 rCanvas );
1404 else
1406 // #120994# Do switch on/off FillColor, even when a overriding one is set
1407 bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting());
1409 rStates.getState().isFillColorSet = bSetting;
1411 break;
1413 case META_TEXTCOLOR_ACTION:
1415 if( !rParms.maTextColor.is_initialized() )
1417 // Text color is set unconditionally, thus, no
1418 // use of setStateColor here
1419 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1421 // force alpha part of color to
1422 // opaque. transparent painting is done
1423 // explicitly via META_TRANSPARENT_ACTION
1424 aColor.SetTransparency(0);
1426 rStates.getState().textColor =
1427 ::vcl::unotools::colorToDoubleSequence(
1428 aColor,
1429 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1432 break;
1434 case META_TEXTFILLCOLOR_ACTION:
1435 if( !rParms.maTextColor.is_initialized() )
1437 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1438 rStates.getState().isTextFillColorSet,
1439 rStates.getState().textFillColor,
1440 rCanvas );
1442 else
1444 // #120994# Do switch on/off TextFillColor, even when a overriding one is set
1445 bool bSetting(static_cast<MetaTextFillColorAction*>(pCurrAct)->IsSetting());
1447 rStates.getState().isTextFillColorSet = bSetting;
1449 break;
1451 case META_TEXTLINECOLOR_ACTION:
1452 if( !rParms.maTextColor.is_initialized() )
1454 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1455 rStates.getState().isTextLineColorSet,
1456 rStates.getState().textLineColor,
1457 rCanvas );
1459 else
1461 // #120994# Do switch on/off TextLineColor, even when a overriding one is set
1462 bool bSetting(static_cast<MetaTextLineColorAction*>(pCurrAct)->IsSetting());
1464 rStates.getState().isTextLineColorSet = bSetting;
1466 break;
1468 case META_TEXTALIGN_ACTION:
1470 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1471 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1473 rState.textReferencePoint = eTextAlign;
1475 break;
1477 case META_FONT_ACTION:
1479 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1480 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1482 rState.xFont = createFont( rState.fontRotation,
1483 rFont,
1484 rFactoryParms );
1486 // TODO(Q2): define and use appropriate enumeration types
1487 rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
1488 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
1489 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ?
1490 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1491 (sal_Int8)rFont.GetUnderline();
1492 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
1493 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
1494 rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False);
1495 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False);
1496 rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False);
1498 break;
1500 case META_RASTEROP_ACTION:
1501 // TODO(F2): NYI
1502 break;
1504 case META_LAYOUTMODE_ACTION:
1506 // TODO(F2): A lot is missing here
1507 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1508 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1509 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
1511 case TEXT_LAYOUT_BIDI_LTR:
1512 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1513 break;
1515 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
1516 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1517 break;
1519 case TEXT_LAYOUT_BIDI_RTL:
1520 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1521 break;
1523 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
1524 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1525 break;
1528 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1529 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1530 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1532 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1535 break;
1537 // ------------------------------------------------------------
1539 // In the second part of this monster-switch, we
1540 // handle all recursing meta actions. These are the
1541 // ones generating a metafile by themselves, which is
1542 // then processed by recursively calling this method.
1544 // ------------------------------------------------------------
1546 case META_GRADIENT_ACTION:
1548 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1549 createGradientAction( ::Polygon( pGradAct->GetRect() ),
1550 pGradAct->GetGradient(),
1551 rFactoryParms,
1552 true,
1553 bSubsettableActions );
1555 break;
1557 case META_HATCH_ACTION:
1559 // TODO(F2): use native Canvas hatches here
1560 GDIMetaFile aTmpMtf;
1562 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1563 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1564 aTmpMtf );
1565 createActions( aTmpMtf, rFactoryParms,
1566 bSubsettableActions );
1568 break;
1570 case META_EPS_ACTION:
1572 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
1573 const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
1575 // #121806# explicitly kept integer
1576 const Size aMtfSize( rSubstitute.GetPrefSize() );
1577 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1578 rSubstitute.GetPrefMapMode() ) );
1580 // #i44110# correct null-sized output - there
1581 // are metafiles which have zero size in at
1582 // least one dimension
1583 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1584 ::std::max( aMtfSizePixPre.Height(), 1L ) );
1586 // Setup local transform, such that the
1587 // metafile renders itself into the given
1588 // output rectangle
1589 rStates.pushState(PUSH_ALL);
1591 rVDev.Push();
1592 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1594 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1595 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1597 rStates.getState().transform.translate( rPos.X(),
1598 rPos.Y() );
1599 rStates.getState().transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1600 (double)rSize.Height() / aMtfSizePix.Height() );
1602 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1603 rFactoryParms,
1604 bSubsettableActions );
1606 rVDev.Pop();
1607 rStates.popState();
1609 break;
1611 // handle metafile comments, to retrieve
1612 // meta-information for gradients, fills and
1613 // strokes. May skip actions, and may recurse.
1614 case META_COMMENT_ACTION:
1616 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1618 // Handle gradients
1619 if (pAct->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")))
1621 MetaGradientExAction* pGradAction = NULL;
1622 bool bDone( false );
1623 while( !bDone &&
1624 (pCurrAct=rMtf.NextAction()) != NULL )
1626 switch( pCurrAct->GetType() )
1628 // extract gradient info
1629 case META_GRADIENTEX_ACTION:
1630 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1631 break;
1633 // skip broken-down rendering, output gradient when sequence is ended
1634 case META_COMMENT_ACTION:
1635 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")) )
1637 bDone = true;
1639 if( pGradAction )
1641 createGradientAction( pGradAction->GetPolyPolygon(),
1642 pGradAction->GetGradient(),
1643 rFactoryParms,
1644 false,
1645 bSubsettableActions );
1648 break;
1652 // TODO(P2): Handle drawing layer strokes, via
1653 // XPATHSTROKE_SEQ_BEGIN comment
1655 // Handle drawing layer fills
1656 else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_BEGIN")) )
1658 const sal_uInt8* pData = pAct->GetData();
1659 if ( pData )
1661 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1663 SvtGraphicFill aFill;
1664 aMemStm >> aFill;
1666 // TODO(P2): Also handle gradients and
1667 // hatches like this
1669 // only evaluate comment for pure
1670 // bitmap fills. If a transparency
1671 // gradient is involved (denoted by
1672 // the FloatTransparent action), take
1673 // the normal meta actions.
1674 if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1675 !isActionContained( rMtf,
1676 "XPATHFILL_SEQ_END",
1677 META_FLOATTRANSPARENT_ACTION ) )
1679 rendering::Texture aTexture;
1681 // TODO(F1): the SvtGraphicFill
1682 // can also transport metafiles
1683 // here, handle that case, too
1684 Graphic aGraphic;
1685 aFill.getGraphic( aGraphic );
1687 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1688 const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1690 ::SvtGraphicFill::Transform aTransform;
1691 aFill.getTransform( aTransform );
1693 ::basegfx::B2DHomMatrix aMatrix;
1695 // convert to basegfx matrix
1696 aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1697 aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1698 aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1699 aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1700 aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1701 aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1703 ::basegfx::B2DHomMatrix aScale;
1704 aScale.scale( aBmpSize.Width(),
1705 aBmpSize.Height() );
1707 // post-multiply with the bitmap
1708 // size (XCanvas' texture assumes
1709 // the given bitmap to be
1710 // normalized to [0,1]x[0,1]
1711 // rectangle)
1712 aMatrix = aMatrix * aScale;
1714 // pre-multiply with the
1715 // logic-to-pixel scale factor
1716 // (the metafile comment works in
1717 // logical coordinates).
1718 ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1719 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1720 rVDev );
1722 ::basegfx::unotools::affineMatrixFromHomMatrix(
1723 aTexture.AffineTransform,
1724 aMatrix );
1726 aTexture.Alpha = 1.0 - aFill.getTransparency();
1727 aTexture.Bitmap =
1728 ::vcl::unotools::xBitmapFromBitmapEx(
1729 rCanvas->getUNOCanvas()->getDevice(),
1730 aBmpEx );
1731 if( aFill.isTiling() )
1733 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1734 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1736 else
1738 aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1739 aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1742 ::PolyPolygon aPath;
1743 aFill.getPath( aPath );
1745 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1746 aPoly.transform( rStates.getState().mapModeTransform );
1747 ActionSharedPtr pPolyAction(
1748 internal::PolyPolyActionFactory::createPolyPolyAction(
1749 aPoly,
1750 rCanvas,
1751 rStates.getState(),
1752 aTexture ) );
1754 if( pPolyAction )
1756 maActions.push_back(
1757 MtfAction(
1758 pPolyAction,
1759 io_rCurrActionIndex ) );
1761 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1764 // skip broken-down render output
1765 skipContent( rMtf,
1766 "XPATHFILL_SEQ_END",
1767 io_rCurrActionIndex );
1771 // Handle drawing layer fills
1772 else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS")) ) {
1773 static int count = -1, limit = 0x7fffffff;
1774 if (count == -1) {
1775 count = 0;
1776 if (char *env = getenv ("EMF_PLUS_LIMIT")) {
1777 limit = atoi (env);
1778 SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit);
1781 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct->GetDataSize ());
1782 if (count < limit)
1783 processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
1784 count ++;
1785 } else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS_HEADER_INFO")) ) {
1786 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct->GetDataSize ());
1788 SvMemoryStream rMF ((void*) pAct->GetData (), pAct->GetDataSize (), STREAM_READ);
1790 rMF >> nFrameLeft >> nFrameTop >> nFrameRight >> nFrameBottom;
1791 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft << "," << nFrameTop << " - " << nFrameRight << "," << nFrameBottom);
1792 rMF >> nPixX >> nPixY >> nMmX >> nMmY;
1793 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX << "x" << nPixY << " mm size: " << nMmX << "x" << nMmY);
1795 rMF >> aBaseTransform;
1796 //aWorldTransform.Set (aBaseTransform);
1799 break;
1801 // ------------------------------------------------------------
1803 // In the third part of this monster-switch, we
1804 // handle all 'acting' meta actions. These are all
1805 // processed by constructing function objects for
1806 // them, which will later ease caching.
1808 // ------------------------------------------------------------
1810 case META_POINT_ACTION:
1812 const OutDevState& rState( rStates.getState() );
1813 if( rState.lineColor.getLength() )
1815 ActionSharedPtr pPointAction(
1816 internal::PointActionFactory::createPointAction(
1817 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1818 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1819 rCanvas,
1820 rState ) );
1822 if( pPointAction )
1824 maActions.push_back(
1825 MtfAction(
1826 pPointAction,
1827 io_rCurrActionIndex ) );
1829 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1833 break;
1835 case META_PIXEL_ACTION:
1837 const OutDevState& rState( rStates.getState() );
1838 if( rState.lineColor.getLength() )
1840 ActionSharedPtr pPointAction(
1841 internal::PointActionFactory::createPointAction(
1842 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1843 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1844 rCanvas,
1845 rState,
1846 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1848 if( pPointAction )
1850 maActions.push_back(
1851 MtfAction(
1852 pPointAction,
1853 io_rCurrActionIndex ) );
1855 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1859 break;
1861 case META_LINE_ACTION:
1863 const OutDevState& rState( rStates.getState() );
1864 if( rState.lineColor.getLength() )
1866 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1868 const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1870 const ::basegfx::B2DPoint aStartPoint(
1871 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1872 const ::basegfx::B2DPoint aEndPoint(
1873 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1875 ActionSharedPtr pLineAction;
1877 if( rLineInfo.IsDefault() )
1879 // plain hair line
1880 pLineAction =
1881 internal::LineActionFactory::createLineAction(
1882 aStartPoint,
1883 aEndPoint,
1884 rCanvas,
1885 rState );
1887 if( pLineAction )
1889 maActions.push_back(
1890 MtfAction(
1891 pLineAction,
1892 io_rCurrActionIndex ) );
1894 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1897 else if( LINE_NONE != rLineInfo.GetStyle() )
1899 // 'thick' line
1900 rendering::StrokeAttributes aStrokeAttributes;
1902 setupStrokeAttributes( aStrokeAttributes,
1903 rFactoryParms,
1904 rLineInfo );
1906 // XCanvas can only stroke polygons,
1907 // not simple lines - thus, handle
1908 // this case via the polypolygon
1909 // action
1910 ::basegfx::B2DPolygon aPoly;
1911 aPoly.append( aStartPoint );
1912 aPoly.append( aEndPoint );
1913 pLineAction =
1914 internal::PolyPolyActionFactory::createPolyPolyAction(
1915 ::basegfx::B2DPolyPolygon( aPoly ),
1916 rCanvas, rState, aStrokeAttributes );
1918 if( pLineAction )
1920 maActions.push_back(
1921 MtfAction(
1922 pLineAction,
1923 io_rCurrActionIndex ) );
1925 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1928 // else: line style is default
1929 // (i.e. invisible), don't generate action
1932 break;
1934 case META_RECT_ACTION:
1936 const Rectangle& rRect(
1937 static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
1939 if( rRect.IsEmpty() )
1940 break;
1942 const OutDevState& rState( rStates.getState() );
1943 const ::basegfx::B2DPoint aTopLeftPixel(
1944 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
1945 const ::basegfx::B2DPoint aBottomRightPixel(
1946 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1947 // #121100# OutputDevice::DrawRect() fills
1948 // rectangles Apple-like, i.e. with one
1949 // additional pixel to the right and bottom.
1950 ::basegfx::B2DPoint(1,1) );
1952 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1953 ::basegfx::B2DRange( aTopLeftPixel,
1954 aBottomRightPixel )),
1955 rFactoryParms );
1956 break;
1959 case META_ROUNDRECT_ACTION:
1961 const Rectangle& rRect(
1962 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
1964 if( rRect.IsEmpty() )
1965 break;
1967 ::basegfx::B2DPolygon aPoly(
1968 ::basegfx::tools::createPolygonFromRect(
1969 ::basegfx::B2DRange(
1970 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1971 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1972 ::basegfx::B2DPoint(1,1) ),
1973 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
1974 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
1975 aPoly.transform( rStates.getState().mapModeTransform );
1977 createFillAndStroke( aPoly,
1978 rFactoryParms );
1980 break;
1982 case META_ELLIPSE_ACTION:
1984 const Rectangle& rRect(
1985 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
1987 if( rRect.IsEmpty() )
1988 break;
1990 const ::basegfx::B2DRange aRange(
1991 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1992 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1993 ::basegfx::B2DPoint(1,1) );
1995 ::basegfx::B2DPolygon aPoly(
1996 ::basegfx::tools::createPolygonFromEllipse(
1997 aRange.getCenter(),
1998 aRange.getWidth(),
1999 aRange.getHeight() ));
2000 aPoly.transform( rStates.getState().mapModeTransform );
2002 createFillAndStroke( aPoly,
2003 rFactoryParms );
2005 break;
2007 case META_ARC_ACTION:
2009 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2010 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2011 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2012 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2013 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2014 aPoly.transform( rStates.getState().mapModeTransform );
2016 createFillAndStroke( aPoly,
2017 rFactoryParms );
2019 break;
2021 case META_PIE_ACTION:
2023 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2024 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2025 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2026 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2027 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2028 aPoly.transform( rStates.getState().mapModeTransform );
2030 createFillAndStroke( aPoly,
2031 rFactoryParms );
2033 break;
2035 case META_CHORD_ACTION:
2037 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2038 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2039 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2040 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2041 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2042 aPoly.transform( rStates.getState().mapModeTransform );
2044 createFillAndStroke( aPoly,
2045 rFactoryParms );
2047 break;
2049 case META_POLYLINE_ACTION:
2051 const OutDevState& rState( rStates.getState() );
2052 if( rState.lineColor.getLength() ||
2053 rState.fillColor.getLength() )
2055 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2057 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2058 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2059 aPoly.transform( rState.mapModeTransform );
2061 ActionSharedPtr pLineAction;
2063 if( rLineInfo.IsDefault() )
2065 // plain hair line polygon
2066 pLineAction =
2067 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2068 ::basegfx::B2DPolyPolygon(aPoly),
2069 rCanvas,
2070 rState );
2072 if( pLineAction )
2074 maActions.push_back(
2075 MtfAction(
2076 pLineAction,
2077 io_rCurrActionIndex ) );
2079 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2082 else if( LINE_NONE != rLineInfo.GetStyle() )
2084 // 'thick' line polygon
2085 rendering::StrokeAttributes aStrokeAttributes;
2087 setupStrokeAttributes( aStrokeAttributes,
2088 rFactoryParms,
2089 rLineInfo );
2091 pLineAction =
2092 internal::PolyPolyActionFactory::createPolyPolyAction(
2093 ::basegfx::B2DPolyPolygon(aPoly),
2094 rCanvas,
2095 rState,
2096 aStrokeAttributes ) ;
2098 if( pLineAction )
2100 maActions.push_back(
2101 MtfAction(
2102 pLineAction,
2103 io_rCurrActionIndex ) );
2105 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2108 // else: line style is default
2109 // (i.e. invisible), don't generate action
2112 break;
2114 case META_POLYGON_ACTION:
2116 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2117 aPoly.transform( rStates.getState().mapModeTransform );
2118 createFillAndStroke( aPoly,
2119 rFactoryParms );
2121 break;
2123 case META_POLYPOLYGON_ACTION:
2125 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2126 aPoly.transform( rStates.getState().mapModeTransform );
2127 createFillAndStroke( aPoly,
2128 rFactoryParms );
2130 break;
2132 case META_BMP_ACTION:
2134 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2136 ActionSharedPtr pBmpAction(
2137 internal::BitmapActionFactory::createBitmapAction(
2138 pAct->GetBitmap(),
2139 rStates.getState().mapModeTransform *
2140 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2141 rCanvas,
2142 rStates.getState() ) );
2144 if( pBmpAction )
2146 maActions.push_back(
2147 MtfAction(
2148 pBmpAction,
2149 io_rCurrActionIndex ) );
2151 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2154 break;
2156 case META_BMPSCALE_ACTION:
2158 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2160 ActionSharedPtr pBmpAction(
2161 internal::BitmapActionFactory::createBitmapAction(
2162 pAct->GetBitmap(),
2163 rStates.getState().mapModeTransform *
2164 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2165 rStates.getState().mapModeTransform *
2166 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2167 rCanvas,
2168 rStates.getState() ) );
2170 if( pBmpAction )
2172 maActions.push_back(
2173 MtfAction(
2174 pBmpAction,
2175 io_rCurrActionIndex ) );
2177 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2180 break;
2182 case META_BMPSCALEPART_ACTION:
2184 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2186 // crop bitmap to given source rectangle (no
2187 // need to copy and convert the whole bitmap)
2188 Bitmap aBmp( pAct->GetBitmap() );
2189 const Rectangle aCropRect( pAct->GetSrcPoint(),
2190 pAct->GetSrcSize() );
2191 aBmp.Crop( aCropRect );
2193 ActionSharedPtr pBmpAction(
2194 internal::BitmapActionFactory::createBitmapAction(
2195 aBmp,
2196 rStates.getState().mapModeTransform *
2197 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2198 rStates.getState().mapModeTransform *
2199 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2200 rCanvas,
2201 rStates.getState() ) );
2203 if( pBmpAction )
2205 maActions.push_back(
2206 MtfAction(
2207 pBmpAction,
2208 io_rCurrActionIndex ) );
2210 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2213 break;
2215 case META_BMPEX_ACTION:
2217 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2219 ActionSharedPtr pBmpAction(
2220 internal::BitmapActionFactory::createBitmapAction(
2221 pAct->GetBitmapEx(),
2222 rStates.getState().mapModeTransform *
2223 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2224 rCanvas,
2225 rStates.getState() ) );
2227 if( pBmpAction )
2229 maActions.push_back(
2230 MtfAction(
2231 pBmpAction,
2232 io_rCurrActionIndex ) );
2234 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2237 break;
2239 case META_BMPEXSCALE_ACTION:
2241 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2243 ActionSharedPtr pBmpAction(
2244 internal::BitmapActionFactory::createBitmapAction(
2245 pAct->GetBitmapEx(),
2246 rStates.getState().mapModeTransform *
2247 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2248 rStates.getState().mapModeTransform *
2249 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2250 rCanvas,
2251 rStates.getState() ) );
2253 if( pBmpAction )
2255 maActions.push_back(
2256 MtfAction(
2257 pBmpAction,
2258 io_rCurrActionIndex ) );
2260 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2263 break;
2265 case META_BMPEXSCALEPART_ACTION:
2267 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2269 // crop bitmap to given source rectangle (no
2270 // need to copy and convert the whole bitmap)
2271 BitmapEx aBmp( pAct->GetBitmapEx() );
2272 const Rectangle aCropRect( pAct->GetSrcPoint(),
2273 pAct->GetSrcSize() );
2274 aBmp.Crop( aCropRect );
2276 ActionSharedPtr pBmpAction(
2277 internal::BitmapActionFactory::createBitmapAction(
2278 aBmp,
2279 rStates.getState().mapModeTransform *
2280 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2281 rStates.getState().mapModeTransform *
2282 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2283 rCanvas,
2284 rStates.getState() ) );
2286 if( pBmpAction )
2288 maActions.push_back(
2289 MtfAction(
2290 pBmpAction,
2291 io_rCurrActionIndex ) );
2293 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2296 break;
2298 case META_MASK_ACTION:
2300 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2302 // create masked BitmapEx right here, as the
2303 // canvas does not provide equivalent
2304 // functionality
2305 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2306 pAct->GetColor() ));
2308 ActionSharedPtr pBmpAction(
2309 internal::BitmapActionFactory::createBitmapAction(
2310 aBmp,
2311 rStates.getState().mapModeTransform *
2312 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2313 rCanvas,
2314 rStates.getState() ) );
2316 if( pBmpAction )
2318 maActions.push_back(
2319 MtfAction(
2320 pBmpAction,
2321 io_rCurrActionIndex ) );
2323 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2326 break;
2328 case META_MASKSCALE_ACTION:
2330 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2332 // create masked BitmapEx right here, as the
2333 // canvas does not provide equivalent
2334 // functionality
2335 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2336 pAct->GetColor() ));
2338 ActionSharedPtr pBmpAction(
2339 internal::BitmapActionFactory::createBitmapAction(
2340 aBmp,
2341 rStates.getState().mapModeTransform *
2342 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2343 rStates.getState().mapModeTransform *
2344 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2345 rCanvas,
2346 rStates.getState() ) );
2348 if( pBmpAction )
2350 maActions.push_back(
2351 MtfAction(
2352 pBmpAction,
2353 io_rCurrActionIndex ) );
2355 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2358 break;
2360 case META_MASKSCALEPART_ACTION:
2362 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2364 // create masked BitmapEx right here, as the
2365 // canvas does not provide equivalent
2366 // functionality
2367 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2368 pAct->GetColor() ));
2370 // crop bitmap to given source rectangle (no
2371 // need to copy and convert the whole bitmap)
2372 const Rectangle aCropRect( pAct->GetSrcPoint(),
2373 pAct->GetSrcSize() );
2374 aBmp.Crop( aCropRect );
2376 ActionSharedPtr pBmpAction(
2377 internal::BitmapActionFactory::createBitmapAction(
2378 aBmp,
2379 rStates.getState().mapModeTransform *
2380 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2381 rStates.getState().mapModeTransform *
2382 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2383 rCanvas,
2384 rStates.getState() ) );
2386 if( pBmpAction )
2388 maActions.push_back(
2389 MtfAction(
2390 pBmpAction,
2391 io_rCurrActionIndex ) );
2393 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2396 break;
2398 case META_GRADIENTEX_ACTION:
2399 // TODO(F1): use native Canvas gradients here
2400 // action is ignored here, because redundant to META_GRADIENT_ACTION
2401 break;
2403 case META_WALLPAPER_ACTION:
2404 // TODO(F2): NYI
2405 break;
2407 case META_TRANSPARENT_ACTION:
2409 const OutDevState& rState( rStates.getState() );
2410 if( rState.lineColor.getLength() ||
2411 rState.fillColor.getLength() )
2413 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2414 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2415 aPoly.transform( rState.mapModeTransform );
2417 ActionSharedPtr pPolyAction(
2418 internal::PolyPolyActionFactory::createPolyPolyAction(
2419 aPoly,
2420 rCanvas,
2421 rState,
2422 pAct->GetTransparence() ) );
2424 if( pPolyAction )
2426 maActions.push_back(
2427 MtfAction(
2428 pPolyAction,
2429 io_rCurrActionIndex ) );
2431 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2435 break;
2437 case META_FLOATTRANSPARENT_ACTION:
2439 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2441 internal::MtfAutoPtr pMtf(
2442 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2444 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2445 internal::GradientAutoPtr pGradient(
2446 new Gradient( pAct->GetGradient() ) );
2448 DBG_TESTSOLARMUTEX();
2450 ActionSharedPtr pFloatTransAction(
2451 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2452 pMtf,
2453 pGradient,
2454 rParms,
2455 rStates.getState().mapModeTransform *
2456 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2457 rStates.getState().mapModeTransform *
2458 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2459 rCanvas,
2460 rStates.getState() ) );
2462 if( pFloatTransAction )
2464 maActions.push_back(
2465 MtfAction(
2466 pFloatTransAction,
2467 io_rCurrActionIndex ) );
2469 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2472 break;
2474 case META_TEXT_ACTION:
2476 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2477 OUString sText = pAct->GetText();
2479 if (rVDev.GetDigitLanguage())
2480 sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2482 createTextAction(
2483 pAct->GetPoint(),
2484 sText,
2485 pAct->GetIndex(),
2486 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2487 NULL,
2488 rFactoryParms,
2489 bSubsettableActions );
2491 break;
2493 case META_TEXTARRAY_ACTION:
2495 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2496 OUString sText = pAct->GetText();
2498 if (rVDev.GetDigitLanguage())
2499 sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2501 createTextAction(
2502 pAct->GetPoint(),
2503 sText,
2504 pAct->GetIndex(),
2505 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2506 pAct->GetDXArray(),
2507 rFactoryParms,
2508 bSubsettableActions );
2510 break;
2512 case META_TEXTLINE_ACTION:
2514 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2516 const OutDevState& rState( rStates.getState() );
2517 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2518 rVDev ) );
2519 const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2520 ::basegfx::B2DSize(pAct->GetWidth(),
2521 0 ));
2523 ActionSharedPtr pPolyAction(
2524 PolyPolyActionFactory::createPolyPolyAction(
2525 tools::createTextLinesPolyPolygon(
2526 rState.mapModeTransform *
2527 ::basegfx::B2DPoint(
2528 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2529 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2530 aSize.getX(),
2531 tools::createTextLineInfo( rVDev,
2532 rState )),
2533 rCanvas,
2534 rState ) );
2536 if( pPolyAction.get() )
2538 maActions.push_back(
2539 MtfAction(
2540 pPolyAction,
2541 io_rCurrActionIndex ) );
2543 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2546 break;
2548 case META_TEXTRECT_ACTION:
2550 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2552 rStates.pushState(PUSH_ALL);
2554 // use the VDev to break up the text rect
2555 // action into readily formatted lines
2556 GDIMetaFile aTmpMtf;
2557 rVDev.AddTextRectActions( pAct->GetRect(),
2558 pAct->GetText(),
2559 pAct->GetStyle(),
2560 aTmpMtf );
2562 createActions( aTmpMtf,
2563 rFactoryParms,
2564 bSubsettableActions );
2566 rStates.popState();
2568 break;
2571 case META_STRETCHTEXT_ACTION:
2573 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2574 OUString sText = pAct->GetText();
2576 if (rVDev.GetDigitLanguage())
2577 sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2579 const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ?
2580 pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen() );
2582 // #i70897# Nothing to do, actually...
2583 if( nLen == 0 )
2584 break;
2586 // have to fit the text into the given
2587 // width. This is achieved by internally
2588 // generating a DX array, and uniformly
2589 // distributing the excess/insufficient width
2590 // to every logical character.
2591 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
2593 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2594 pAct->GetIndex(), pAct->GetLen() );
2596 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2598 // Last entry of pDXArray contains total width of the text
2599 sal_Int32* p=pDXArray.get();
2600 for( sal_uInt16 i=1; i<=nLen; ++i )
2602 // calc ratio for every array entry, to
2603 // distribute rounding errors 'evenly'
2604 // across the characters. Note that each
2605 // entry represents the 'end' position of
2606 // the corresponding character, thus, we
2607 // let i run from 1 to nLen.
2608 *p++ += (sal_Int32)i*nWidthDifference/nLen;
2611 createTextAction(
2612 pAct->GetPoint(),
2613 sText,
2614 pAct->GetIndex(),
2615 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2616 pDXArray.get(),
2617 rFactoryParms,
2618 bSubsettableActions );
2620 break;
2622 default:
2623 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2624 break;
2627 // increment action index (each mtf action counts _at
2628 // least_ one. Some count for more, therefore,
2629 // io_rCurrActionIndex is sometimes incremented by
2630 // pAct->getActionCount()-1 above, the -1 being the
2631 // correction for the unconditional increment here).
2632 ++io_rCurrActionIndex;
2635 return true;
2639 namespace
2641 class ActionRenderer
2643 public:
2644 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2645 maTransformation( rTransformation ),
2646 mbRet( true )
2650 bool result() const
2652 return mbRet;
2655 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2657 // ANDing the result. We want to fail if at least
2658 // one action failed.
2659 mbRet &= rAction.mpAction->render( maTransformation );
2662 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2663 const Action::Subset& rSubset )
2665 // ANDing the result. We want to fail if at least
2666 // one action failed.
2667 mbRet &= rAction.mpAction->renderSubset( maTransformation,
2668 rSubset );
2671 private:
2672 ::basegfx::B2DHomMatrix maTransformation;
2673 bool mbRet;
2676 class AreaQuery
2678 public:
2679 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2680 maTransformation( rTransformation ),
2681 maBounds()
2685 bool result() const
2687 return true; // nothing can fail here
2690 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2692 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2695 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2696 const Action::Subset& rSubset )
2698 maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2699 rSubset ) );
2702 ::basegfx::B2DRange getBounds() const
2704 return maBounds;
2707 private:
2708 ::basegfx::B2DHomMatrix maTransformation;
2709 ::basegfx::B2DRange maBounds;
2712 // Doing that via inline class. Compilers tend to not inline free
2713 // functions.
2714 struct UpperBoundActionIndexComparator
2716 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2717 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2719 const sal_Int32 nLHSCount( rLHS.mpAction ?
2720 rLHS.mpAction->getActionCount() : 0 );
2721 const sal_Int32 nRHSCount( rRHS.mpAction ?
2722 rRHS.mpAction->getActionCount() : 0 );
2724 // compare end of action range, to have an action selected
2725 // by lower_bound even if the requested index points in
2726 // the middle of the action's range
2727 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2731 /** Algorithm to apply given functor to a subset range
2733 @tpl Functor
2735 Functor to call for each element of the subset
2736 range. Must provide the following method signatures:
2737 bool result() (returning false if operation failed)
2740 template< typename Functor > bool
2741 forSubsetRange( Functor& rFunctor,
2742 ImplRenderer::ActionVector::const_iterator aRangeBegin,
2743 ImplRenderer::ActionVector::const_iterator aRangeEnd,
2744 sal_Int32 nStartIndex,
2745 sal_Int32 nEndIndex,
2746 const ImplRenderer::ActionVector::const_iterator& rEnd )
2748 if( aRangeBegin == aRangeEnd )
2750 // only a single action. Setup subset, and call functor
2751 Action::Subset aSubset;
2752 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2753 nStartIndex - aRangeBegin->mnOrigIndex );
2754 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2755 nEndIndex - aRangeBegin->mnOrigIndex );
2757 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2758 "ImplRenderer::forSubsetRange(): Invalid indices" );
2760 rFunctor( *aRangeBegin, aSubset );
2762 else
2764 // more than one action.
2766 // render partial first, full intermediate, and
2767 // partial last action
2768 Action::Subset aSubset;
2769 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2770 nStartIndex - aRangeBegin->mnOrigIndex );
2771 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2773 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2774 "ImplRenderer::forSubsetRange(): Invalid indices" );
2776 rFunctor( *aRangeBegin, aSubset );
2778 // first action rendered, skip to next
2779 ++aRangeBegin;
2781 // render full middle actions
2782 while( aRangeBegin != aRangeEnd )
2783 rFunctor( *aRangeBegin++ );
2785 if( aRangeEnd == rEnd ||
2786 aRangeEnd->mnOrigIndex > nEndIndex )
2788 // aRangeEnd denotes end of action vector,
2790 // or
2792 // nEndIndex references something _after_
2793 // aRangeBegin, but _before_ aRangeEnd
2795 // either way: no partial action left
2796 return rFunctor.result();
2799 aSubset.mnSubsetBegin = 0;
2800 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2802 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2803 "ImplRenderer::forSubsetRange(): Invalid indices" );
2805 rFunctor( *aRangeEnd, aSubset );
2808 return rFunctor.result();
2812 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2813 sal_Int32& io_rEndIndex,
2814 ActionVector::const_iterator& o_rRangeBegin,
2815 ActionVector::const_iterator& o_rRangeEnd ) const
2817 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2818 "ImplRenderer::getSubsetIndices(): invalid action range" );
2820 ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2821 "ImplRenderer::getSubsetIndices(): no actions to render" );
2823 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2824 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2825 maActions.back().mpAction->getActionCount() );
2827 // clip given range to permissible values (there might be
2828 // ranges before and behind the valid indices)
2829 io_rStartIndex = ::std::max( nMinActionIndex,
2830 io_rStartIndex );
2831 io_rEndIndex = ::std::min( nMaxActionIndex,
2832 io_rEndIndex );
2834 if( io_rStartIndex == io_rEndIndex ||
2835 io_rStartIndex > io_rEndIndex )
2837 // empty range, don't render anything. The second
2838 // condition e.g. happens if the requested range lies
2839 // fully before or behind the valid action indices.
2840 return false;
2844 const ActionVector::const_iterator aBegin( maActions.begin() );
2845 const ActionVector::const_iterator aEnd( maActions.end() );
2848 // find start and end action
2849 // =========================
2850 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2851 MtfAction( ActionSharedPtr(), io_rStartIndex ),
2852 UpperBoundActionIndexComparator() );
2853 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
2854 MtfAction( ActionSharedPtr(), io_rEndIndex ),
2855 UpperBoundActionIndexComparator() );
2856 return true;
2860 // Public methods
2861 // ====================================================================
2863 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
2864 const GDIMetaFile& rMtf,
2865 const Parameters& rParams ) :
2866 CanvasGraphicHelper( rCanvas ),
2867 maActions()
2869 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2871 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2872 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2873 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2874 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2876 // make sure canvas and graphic device are valid; action
2877 // creation don't check that every time
2878 if( rCanvas.get() == NULL ||
2879 !rCanvas->getUNOCanvas().is() ||
2880 !rCanvas->getUNOCanvas()->getDevice().is() )
2882 // leave actions empty
2883 return;
2886 VectorOfOutDevStates aStateStack;
2888 VirtualDevice aVDev;
2889 aVDev.EnableOutput( sal_False );
2891 // Setup VDev for state tracking and mapping
2892 // =========================================
2894 aVDev.SetMapMode( rMtf.GetPrefMapMode() );
2896 const Size aMtfSize( rMtf.GetPrefSize() );
2897 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
2898 rMtf.GetPrefMapMode() ) );
2900 // #i44110# correct null-sized output - there are shapes
2901 // which have zero size in at least one dimension
2902 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2903 ::std::max( aMtfSizePixPre.Height(), 1L ) );
2905 sal_Int32 nCurrActions(0);
2906 ActionFactoryParameters aParms(aStateStack,
2907 rCanvas,
2908 aVDev,
2909 rParams,
2910 nCurrActions );
2912 // init state stack
2913 aStateStack.clearStateStack();
2915 // Setup local state, such that the metafile renders
2916 // itself into a one-by-one square at the origin for
2917 // identity view and render transformations
2918 aStateStack.getState().transform.scale( 1.0 / aMtfSizePix.Width(),
2919 1.0 / aMtfSizePix.Height() );
2921 tools::calcLogic2PixelAffineTransform( aStateStack.getState().mapModeTransform,
2922 aVDev );
2924 ColorSharedPtr pColor( getCanvas()->createColor() );
2927 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2928 // setup default text color to black
2929 rState.textColor =
2930 rState.textFillColor =
2931 rState.textLineColor = pColor->getDeviceColor( 0x000000FF );
2934 // apply overrides from the Parameters struct
2935 if( rParams.maFillColor.is_initialized() )
2937 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2938 rState.isFillColorSet = true;
2939 rState.fillColor = pColor->getDeviceColor( *rParams.maFillColor );
2941 if( rParams.maLineColor.is_initialized() )
2943 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2944 rState.isLineColorSet = true;
2945 rState.lineColor = pColor->getDeviceColor( *rParams.maLineColor );
2947 if( rParams.maTextColor.is_initialized() )
2949 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2950 rState.isTextFillColorSet = true;
2951 rState.isTextLineColorSet = true;
2952 rState.textColor =
2953 rState.textFillColor =
2954 rState.textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
2956 if( rParams.maFontName.is_initialized() ||
2957 rParams.maFontWeight.is_initialized() ||
2958 rParams.maFontLetterForm.is_initialized() ||
2959 rParams.maFontUnderline.is_initialized() ||
2960 rParams.maFontProportion.is_initialized() )
2962 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2964 rState.xFont = createFont( rState.fontRotation,
2965 ::Font(), // default font
2966 aParms );
2969 /* EMF+ */
2970 memset (aObjects, 0, sizeof (aObjects));
2971 mbMultipart = false;
2973 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
2974 // we're
2975 // changing
2976 // the
2977 // current
2978 // action
2979 // in
2980 // createActions!
2981 aParms,
2982 true // TODO(P1): make subsettability configurable
2986 ImplRenderer::~ImplRenderer()
2988 // don't leak EMFPObjects
2989 for(unsigned int i=0; i<SAL_N_ELEMENTS(aObjects); ++i)
2990 delete aObjects[i];
2993 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
2994 sal_Int32 nEndIndex ) const
2996 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
2998 ActionVector::const_iterator aRangeBegin;
2999 ActionVector::const_iterator aRangeEnd;
3003 if( !getSubsetIndices( nStartIndex, nEndIndex,
3004 aRangeBegin, aRangeEnd ) )
3005 return true; // nothing to render (but _that_ was successful)
3007 // now, aRangeBegin references the action in which the
3008 // subset rendering must start, and aRangeEnd references
3009 // the action in which the subset rendering must end (it
3010 // might also end right at the start of the referenced
3011 // action, such that zero of that action needs to be
3012 // rendered).
3015 // render subset of actions
3016 // ========================
3018 ::basegfx::B2DHomMatrix aMatrix;
3019 ::canvas::tools::getRenderStateTransform( aMatrix,
3020 getRenderState() );
3022 ActionRenderer aRenderer( aMatrix );
3024 return forSubsetRange( aRenderer,
3025 aRangeBegin,
3026 aRangeEnd,
3027 nStartIndex,
3028 nEndIndex,
3029 maActions.end() );
3031 catch( uno::Exception& )
3033 SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3034 comphelper::anyToString( cppu::getCaughtException() ),
3035 RTL_TEXTENCODING_UTF8 ).getStr() );
3037 // convert error to return value
3038 return false;
3042 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3043 sal_Int32 nEndIndex ) const
3045 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3047 ActionVector::const_iterator aRangeBegin;
3048 ActionVector::const_iterator aRangeEnd;
3050 if( !getSubsetIndices( nStartIndex, nEndIndex,
3051 aRangeBegin, aRangeEnd ) )
3052 return ::basegfx::B2DRange(); // nothing to render -> empty range
3054 // now, aRangeBegin references the action in which the
3055 // subset querying must start, and aRangeEnd references
3056 // the action in which the subset querying must end (it
3057 // might also end right at the start of the referenced
3058 // action, such that zero of that action needs to be
3059 // queried).
3062 // query bounds for subset of actions
3063 // ==================================
3065 ::basegfx::B2DHomMatrix aMatrix;
3066 ::canvas::tools::getRenderStateTransform( aMatrix,
3067 getRenderState() );
3069 AreaQuery aQuery( aMatrix );
3070 forSubsetRange( aQuery,
3071 aRangeBegin,
3072 aRangeEnd,
3073 nStartIndex,
3074 nEndIndex,
3075 maActions.end() );
3077 return aQuery.getBounds();
3080 bool ImplRenderer::draw() const
3082 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3084 ::basegfx::B2DHomMatrix aMatrix;
3085 ::canvas::tools::getRenderStateTransform( aMatrix,
3086 getRenderState() );
3090 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3092 catch( uno::Exception& )
3094 SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3095 comphelper::anyToString( cppu::getCaughtException() ),
3096 RTL_TEXTENCODING_UTF8 ).getStr() );
3098 return false;
3104 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */