Bump version to 5.0-14
[LibreOffice.git] / cppcanvas / source / mtfrenderer / implrenderer.cxx
blob99c76b2ae12d1ef8f4ebf5c1010d4b0b6ef7453b
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 <utility>
76 #include <vector>
77 #include <algorithm>
78 #include <iterator>
79 #include <boost/scoped_array.hpp>
80 #include "mtftools.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 rIsColorSet = pAct->IsSetting();
96 if (rIsColorSet)
98 ::Color aColor( pAct->GetColor() );
100 // force alpha part of color to
101 // opaque. transparent painting is done
102 // explicitly via MetaActionType::Transparent
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.
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
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(PushFlags nFlags)
266 m_aStates.push_back( getState() );
267 getState().pushFlags = nFlags;
270 void VectorOfOutDevStates::popState()
272 if( getState().pushFlags != PushFlags::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 & PushFlags::LINECOLOR) )
293 aCalculatedNewState.lineColor = rNewState.lineColor;
294 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
297 if( (aCalculatedNewState.pushFlags & PushFlags::FILLCOLOR) )
299 aCalculatedNewState.fillColor = rNewState.fillColor;
300 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
303 if( (aCalculatedNewState.pushFlags & PushFlags::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 & PushFlags::TEXTCOLOR) )
319 aCalculatedNewState.textColor = rNewState.textColor;
322 if( (aCalculatedNewState.pushFlags & PushFlags::MAPMODE) )
324 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
327 if( (aCalculatedNewState.pushFlags & PushFlags::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 & PushFlags::RASTEROP) )
336 // {
337 // }
339 if( (aCalculatedNewState.pushFlags & PushFlags::TEXTFILLCOLOR) )
341 aCalculatedNewState.textFillColor = rNewState.textFillColor;
342 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
345 if( (aCalculatedNewState.pushFlags & PushFlags::TEXTALIGN) )
347 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
350 // TODO(F1): Refpoint handling NYI
351 // if( (aCalculatedNewState.pushFlags & PushFlags::REFPOINT) )
352 // {
353 // }
355 if( (aCalculatedNewState.pushFlags & PushFlags::TEXTLINECOLOR) )
357 aCalculatedNewState.textLineColor = rNewState.textLineColor;
358 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
361 if( (aCalculatedNewState.pushFlags & PushFlags::TEXTLAYOUTMODE) )
363 aCalculatedNewState.textAlignment = rNewState.textAlignment;
364 aCalculatedNewState.textDirection = rNewState.textDirection;
367 // TODO(F2): Text language handling NYI
368 // if( (aCalculatedNewState.pushFlags & PushFlags::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 )
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() == MetaActionType::COMMENT &&
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 MetaActionType nType )
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() == MetaActionType::COMMENT &&
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 ::tools::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
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(PushFlags::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 vcl::Font& rFont,
757 const ActionFactoryParameters& rParms )
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)
813 ::Size rFontSizeLog( rFont.GetSize() );
815 if (rFontSizeLog.Height() == 0)
817 // guess 16 pixel (as in VCL)
818 rFontSizeLog = ::Size(0, 16);
820 // convert to target MapUnit if not pixels
821 rFontSizeLog = OutputDevice::LogicToLogic(rFontSizeLog, MAP_PIXEL, rParms.mrVDev.GetMapMode());
824 const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
825 if( nFontWidthLog != 0 )
827 vcl::Font aTestFont = rFont;
828 aTestFont.SetWidth( 0 );
829 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
830 if( nNormalWidth != nFontWidthLog )
831 if( nNormalWidth )
832 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
835 // #i52608# apply map mode scale also to font matrix - an
836 // anisotrophic mapmode must be reflected in an
837 // anisotrophic font matrix scale.
838 const OutDevState& rState( rParms.mrStates.getState() );
839 if( !::basegfx::fTools::equal(
840 rState.mapModeTransform.get(0,0),
841 rState.mapModeTransform.get(1,1)) )
843 const double nScaleX( rState.mapModeTransform.get(0,0) );
844 const double nScaleY( rState.mapModeTransform.get(1,1) );
846 // note: no reason to check for division by zero, we
847 // always have the value closer (or equal) to zero as
848 // the nominator.
849 if( fabs(nScaleX) < fabs(nScaleY) )
850 aFontMatrix.m00 *= nScaleX / nScaleY;
851 else
852 aFontMatrix.m11 *= nScaleY / nScaleX;
854 aFontRequest.CellSize = (rState.mapModeTransform * vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
856 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
857 uno::Sequence< beans::PropertyValue >(),
858 aFontMatrix );
861 // create text effects such as shadow/relief/embossed
862 void ImplRenderer::createTextAction( const ::Point& rStartPoint,
863 const OUString& rString,
864 int nIndex,
865 int nLength,
866 const long* pCharWidths,
867 const ActionFactoryParameters& rParms,
868 bool bSubsettableActions )
870 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.getLength() + nIndex,
871 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
873 if( !nLength )
874 return; // zero-length text, no visible output
876 const OutDevState& rState( rParms.mrStates.getState() );
878 // TODO(F2): implement all text effects
879 // if( rState.textAlignment ); // TODO(F2): NYI
881 ::Color aShadowColor( COL_AUTO );
882 ::Color aReliefColor( COL_AUTO );
883 ::Size aShadowOffset;
884 ::Size aReliefOffset;
886 uno::Reference<rendering::XColorSpace> xColorSpace(
887 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
889 if( rState.isTextEffectShadowSet )
891 // calculate shadow offset (similar to outdev3.cxx)
892 // TODO(F3): better match with outdev3.cxx
893 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
894 if( nShadowOffset < 1 )
895 nShadowOffset = 1;
897 aShadowOffset.setWidth( nShadowOffset );
898 aShadowOffset.setHeight( nShadowOffset );
900 // determine shadow color (from outdev3.cxx)
901 ::Color aTextColor = vcl::unotools::doubleSequenceToColor(
902 rState.textColor, xColorSpace );
903 bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
904 || (aTextColor.GetLuminance() < 8);
906 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
907 aShadowColor.SetTransparency( aTextColor.GetTransparency() );
910 if( rState.textReliefStyle )
912 // calculate relief offset (similar to outdev3.cxx)
913 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
914 nReliefOffset += nReliefOffset/2;
915 if( nReliefOffset < 1 )
916 nReliefOffset = 1;
918 if( rState.textReliefStyle == RELIEF_ENGRAVED )
919 nReliefOffset = -nReliefOffset;
921 aReliefOffset.setWidth( nReliefOffset );
922 aReliefOffset.setHeight( nReliefOffset );
924 // determine relief color (from outdev3.cxx)
925 ::Color aTextColor = vcl::unotools::doubleSequenceToColor(
926 rState.textColor, xColorSpace );
928 aReliefColor = ::Color( COL_LIGHTGRAY );
930 // we don't have a automatic color, so black is always
931 // drawn on white (literally copied from
932 // vcl/source/gdi/outdev3.cxx)
933 if( aTextColor.GetColor() == COL_BLACK )
935 aTextColor = ::Color( COL_WHITE );
936 rParms.mrStates.getState().textColor =
937 vcl::unotools::colorToDoubleSequence(
938 aTextColor, xColorSpace );
941 if( aTextColor.GetColor() == COL_WHITE )
942 aReliefColor = ::Color( COL_BLACK );
943 aReliefColor.SetTransparency( aTextColor.GetTransparency() );
946 // create the actual text action
947 ActionSharedPtr pTextAction(
948 TextActionFactory::createTextAction(
949 rStartPoint,
950 aReliefOffset,
951 aReliefColor,
952 aShadowOffset,
953 aShadowColor,
954 rString,
955 nIndex,
956 nLength,
957 pCharWidths,
958 rParms.mrVDev,
959 rParms.mrCanvas,
960 rState,
961 rParms.mrParms,
962 bSubsettableActions ) );
964 ActionSharedPtr pStrikeoutTextAction;
966 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
968 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
970 sal_Unicode pChars[4];
971 if ( rState.textStrikeoutStyle == STRIKEOUT_X )
972 pChars[0] = 'X';
973 else
974 pChars[0] = '/';
975 pChars[3]=pChars[2]=pChars[1]=pChars[0];
977 long nStrikeoutWidth = (rParms.mrVDev.GetTextWidth(
978 OUString(pChars, SAL_N_ELEMENTS(pChars))) + 2) / 4;
980 if( nStrikeoutWidth <= 0 )
981 nStrikeoutWidth = 1;
983 long nMaxWidth = nStrikeoutWidth/2;
984 if ( nMaxWidth < 2 )
985 nMaxWidth = 2;
986 nMaxWidth += nWidth + 1;
988 long nFullStrikeoutWidth = 0;
989 OUString aStrikeoutText;
990 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
991 aStrikeoutText += OUString(pChars[0]);
993 sal_Int32 nLen = aStrikeoutText.getLength();
995 if( nLen )
997 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
998 nStrikeoutWidth += nInterval;
999 long* pStrikeoutCharWidths = new long[nLen];
1001 for ( int i = 0;i<nLen; i++)
1003 pStrikeoutCharWidths[i] = nStrikeoutWidth;
1006 for ( int i = 1;i< nLen; i++ )
1008 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
1011 sal_Int32 nStartPos = 0;
1013 pStrikeoutTextAction =
1014 TextActionFactory::createTextAction(
1015 rStartPoint,
1016 aReliefOffset,
1017 aReliefColor,
1018 aShadowOffset,
1019 aShadowColor,
1020 aStrikeoutText,
1021 nStartPos,
1022 aStrikeoutText.getLength(),
1023 pStrikeoutCharWidths,
1024 rParms.mrVDev,
1025 rParms.mrCanvas,
1026 rState,
1027 rParms.mrParms,
1028 bSubsettableActions ) ;
1032 if( pTextAction )
1034 maActions.push_back(
1035 MtfAction(
1036 pTextAction,
1037 rParms.mrCurrActionIndex ) );
1039 if ( pStrikeoutTextAction )
1041 maActions.push_back(
1042 MtfAction(
1043 pStrikeoutTextAction,
1044 rParms.mrCurrActionIndex ) );
1047 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1051 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1052 const ActionFactoryParameters& rParms,
1053 bool bIntersect )
1055 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1056 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1058 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1059 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1061 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1062 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1064 if( !bIntersect ||
1065 (bEmptyClipRect && bEmptyClipPoly) )
1067 rState.clip = rClipPoly;
1069 else
1071 if( !bEmptyClipRect )
1073 // TODO(P3): Use Liang-Barsky polygon clip here,
1074 // after all, one object is just a rectangle!
1076 // convert rect to polygon beforehand, must revert
1077 // to general polygon clipping here.
1078 rState.clip = ::basegfx::B2DPolyPolygon(
1079 ::basegfx::tools::createPolygonFromRect(
1080 // #121100# VCL rectangular clips always
1081 // include one more pixel to the right
1082 // and the bottom
1083 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1084 rState.clipRect.Top(),
1085 rState.clipRect.Right()+1,
1086 rState.clipRect.Bottom()+1 ) ) );
1089 // AW: Simplified
1090 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1091 aClipPoly, rState.clip, true, false);
1094 // by now, our clip resides in the OutDevState::clip
1095 // poly-polygon.
1096 rState.clipRect.SetEmpty();
1098 if( rState.clip.count() == 0 )
1100 if( rState.clipRect.IsEmpty() )
1102 rState.xClipPoly.clear();
1104 else
1106 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1107 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1108 ::basegfx::B2DPolyPolygon(
1109 ::basegfx::tools::createPolygonFromRect(
1110 // #121100# VCL rectangular clips
1111 // always include one more pixel to
1112 // the right and the bottom
1113 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1114 rState.clipRect.Top(),
1115 rState.clipRect.Right()+1,
1116 rState.clipRect.Bottom()+1 ) ) ) );
1119 else
1121 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1122 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1123 rState.clip );
1127 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
1128 const ActionFactoryParameters& rParms,
1129 bool bIntersect )
1131 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1133 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1134 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1136 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1137 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1139 if( !bIntersect ||
1140 (bEmptyClipRect && bEmptyClipPoly) )
1142 rState.clipRect = rClipRect;
1143 rState.clip.clear();
1145 else if( bEmptyClipPoly )
1147 rState.clipRect.Intersection( rClipRect );
1148 rState.clip.clear();
1150 else
1152 // TODO(P3): Handle a fourth case here, when all clip
1153 // polygons are rectangular, once B2DMultiRange's
1154 // sweep line implementation is done.
1156 // general case: convert to polygon and clip
1159 // convert rect to polygon beforehand, must revert
1160 // to general polygon clipping here.
1161 ::basegfx::B2DPolyPolygon aClipPoly(
1162 ::basegfx::tools::createPolygonFromRect(
1163 ::basegfx::B2DRectangle( rClipRect.Left(),
1164 rClipRect.Top(),
1165 rClipRect.Right(),
1166 rClipRect.Bottom() ) ) );
1168 rState.clipRect.SetEmpty();
1170 // AW: Simplified
1171 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1172 aClipPoly, rState.clip, true, false);
1175 if( rState.clip.count() == 0 )
1177 if( rState.clipRect.IsEmpty() )
1179 rState.xClipPoly.clear();
1181 else
1183 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1184 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1185 ::basegfx::B2DPolyPolygon(
1186 ::basegfx::tools::createPolygonFromRect(
1187 // #121100# VCL rectangular clips
1188 // always include one more pixel to
1189 // the right and the bottom
1190 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1191 rState.clipRect.Top(),
1192 rState.clipRect.Right()+1,
1193 rState.clipRect.Bottom()+1 ) ) ) );
1196 else
1198 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1199 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1200 rState.clip );
1204 bool ImplRenderer::createActions( GDIMetaFile& rMtf,
1205 const ActionFactoryParameters& rFactoryParms,
1206 bool bSubsettableActions )
1208 /* TODO(P2): interpret mtf-comments
1209 ================================
1211 - gradient fillings (do that via comments)
1213 - think about mapping. _If_ we do everything in logical
1214 coordinates (which would solve the probs for stroke
1215 widths and text offsets), then we would have to
1216 recalc scaling for every drawing operation. This is
1217 because the outdev map mode might change at any time.
1218 Also keep in mind, that, although we've double precision
1219 float arithmetic now, different offsets might still
1220 generate different roundings (aka
1221 'OutputDevice::SetPixelOffset())
1225 // alias common parameters
1226 VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1227 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1228 ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1229 const Parameters& rParms(rFactoryParms.mrParms);
1230 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1233 // Loop over every metaaction
1234 // ==========================
1235 MetaAction* pCurrAct;
1237 // TODO(P1): think about caching
1238 for( pCurrAct=rMtf.FirstAction();
1239 pCurrAct;
1240 pCurrAct = rMtf.NextAction() )
1242 // execute every action, to keep VDev state up-to-date
1243 // currently used only for
1244 // - the map mode
1245 // - the line/fill color when processing a MetaActionType::Transparent
1246 // - SetFont to process font metric specific actions
1247 pCurrAct->Execute( &rVDev );
1249 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << static_cast<sal_uInt16>(pCurrAct->GetType()) << " (" << static_cast<sal_uInt16>(pCurrAct->GetType()) << ")");
1251 switch( pCurrAct->GetType() )
1255 // In the first part of this monster-switch, we
1256 // handle all state-changing meta actions. These
1257 // are all handled locally.
1261 case MetaActionType::PUSH:
1263 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1264 rStates.pushState(pPushAction->GetFlags());
1266 break;
1268 case MetaActionType::POP:
1269 rStates.popState();
1270 break;
1272 case MetaActionType::TEXTLANGUAGE:
1273 // FALLTHROUGH intended
1274 case MetaActionType::REFPOINT:
1275 // handled via pCurrAct->Execute( &rVDev )
1276 break;
1278 case MetaActionType::MAPMODE:
1279 // modify current mapModeTransformation
1280 // transformation, such that subsequent
1281 // coordinates map correctly
1282 tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
1283 rVDev );
1284 break;
1286 // monitor clip regions, to assemble clip polygon on our own
1287 case MetaActionType::CLIPREGION:
1289 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1291 if( !pClipAction->IsClipping() )
1293 // clear clipping
1294 rStates.getState().clip.clear();
1296 else
1298 if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1300 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1301 "region encountered, falling back to bounding box!" );
1303 // #121806# explicitly kept integer
1304 Rectangle aClipRect(
1305 rVDev.LogicToPixel(
1306 pClipAction->GetRegion().GetBoundRect() ) );
1308 // intersect current clip with given rect
1309 updateClipping(
1310 aClipRect,
1311 rFactoryParms,
1312 false );
1314 else
1316 // set new clip polygon (don't intersect
1317 // with old one, just set it)
1319 // #121806# explicitly kept integer
1320 basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1322 aPolyPolygon.transform(rVDev.GetViewTransformation());
1323 updateClipping(
1324 aPolyPolygon,
1325 rFactoryParms,
1326 false );
1330 break;
1333 case MetaActionType::ISECTRECTCLIPREGION:
1335 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1337 // #121806# explicitly kept integer
1338 Rectangle aClipRect(
1339 rVDev.LogicToPixel( pClipAction->GetRect() ) );
1341 // intersect current clip with given rect
1342 updateClipping(
1343 aClipRect,
1344 rFactoryParms,
1345 true );
1347 break;
1350 case MetaActionType::ISECTREGIONCLIPREGION:
1352 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1354 if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1356 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1357 "region encountered, falling back to bounding box!" );
1359 // #121806# explicitly kept integer
1360 Rectangle aClipRect(
1361 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1363 // intersect current clip with given rect
1364 updateClipping(
1365 aClipRect,
1366 rFactoryParms,
1367 true );
1369 else
1371 // intersect current clip with given clip polygon
1373 // #121806# explicitly kept integer
1374 basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1376 aPolyPolygon.transform(rVDev.GetViewTransformation());
1377 updateClipping(
1378 aPolyPolygon,
1379 rFactoryParms,
1380 true );
1383 break;
1386 case MetaActionType::MOVECLIPREGION:
1387 // TODO(F2): NYI
1388 break;
1390 case MetaActionType::LINECOLOR:
1391 if( !rParms.maLineColor.is_initialized() )
1393 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1394 rStates.getState().isLineColorSet,
1395 rStates.getState().lineColor,
1396 rCanvas );
1398 else
1400 // #120994# Do switch on/off LineColor, even when a overriding one is set
1401 bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting());
1403 rStates.getState().isLineColorSet = bSetting;
1405 break;
1407 case MetaActionType::FILLCOLOR:
1408 if( !rParms.maFillColor.is_initialized() )
1410 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1411 rStates.getState().isFillColorSet,
1412 rStates.getState().fillColor,
1413 rCanvas );
1415 else
1417 // #120994# Do switch on/off FillColor, even when a overriding one is set
1418 bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting());
1420 rStates.getState().isFillColorSet = bSetting;
1422 break;
1424 case MetaActionType::TEXTCOLOR:
1426 if( !rParms.maTextColor.is_initialized() )
1428 // Text color is set unconditionally, thus, no
1429 // use of setStateColor here
1430 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1432 // force alpha part of color to
1433 // opaque. transparent painting is done
1434 // explicitly via MetaActionType::Transparent
1435 aColor.SetTransparency(0);
1437 rStates.getState().textColor =
1438 vcl::unotools::colorToDoubleSequence(
1439 aColor,
1440 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1443 break;
1445 case MetaActionType::TEXTFILLCOLOR:
1446 if( !rParms.maTextColor.is_initialized() )
1448 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1449 rStates.getState().isTextFillColorSet,
1450 rStates.getState().textFillColor,
1451 rCanvas );
1453 else
1455 // #120994# Do switch on/off TextFillColor, even when a overriding one is set
1456 bool bSetting(static_cast<MetaTextFillColorAction*>(pCurrAct)->IsSetting());
1458 rStates.getState().isTextFillColorSet = bSetting;
1460 break;
1462 case MetaActionType::TEXTLINECOLOR:
1463 if( !rParms.maTextColor.is_initialized() )
1465 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1466 rStates.getState().isTextLineColorSet,
1467 rStates.getState().textLineColor,
1468 rCanvas );
1470 else
1472 // #120994# Do switch on/off TextLineColor, even when a overriding one is set
1473 bool bSetting(static_cast<MetaTextLineColorAction*>(pCurrAct)->IsSetting());
1475 rStates.getState().isTextLineColorSet = bSetting;
1477 break;
1479 case MetaActionType::TEXTALIGN:
1481 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1482 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1484 rState.textReferencePoint = eTextAlign;
1486 break;
1488 case MetaActionType::FONT:
1490 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1491 const vcl::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1493 rState.xFont = createFont( rState.fontRotation,
1494 rFont,
1495 rFactoryParms );
1497 // TODO(Q2): define and use appropriate enumeration types
1498 rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
1499 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
1500 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ?
1501 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1502 (sal_Int8)rFont.GetUnderline();
1503 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
1504 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
1505 rState.isTextEffectShadowSet = rFont.IsShadow();
1506 rState.isTextWordUnderlineSet = rFont.IsWordLineMode();
1507 rState.isTextOutlineModeSet = rFont.IsOutline();
1509 break;
1511 case MetaActionType::RASTEROP:
1512 // TODO(F2): NYI
1513 break;
1515 case MetaActionType::LAYOUTMODE:
1517 // TODO(F2): A lot is missing here
1518 ComplexTextLayoutMode nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1519 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1521 ComplexTextLayoutMode nBidiLayoutMode = nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG);
1522 if( nBidiLayoutMode == TEXT_LAYOUT_DEFAULT)
1523 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1524 else if( nBidiLayoutMode == TEXT_LAYOUT_BIDI_STRONG)
1525 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1526 else if( nBidiLayoutMode == TEXT_LAYOUT_BIDI_RTL)
1527 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1528 else if( nBidiLayoutMode == (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG))
1529 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1531 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1532 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1533 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1535 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1538 break;
1542 // In the second part of this monster-switch, we
1543 // handle all recursing meta actions. These are the
1544 // ones generating a metafile by themselves, which is
1545 // then processed by recursively calling this method.
1549 case MetaActionType::GRADIENT:
1551 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1552 createGradientAction( ::Polygon( pGradAct->GetRect() ),
1553 pGradAct->GetGradient(),
1554 rFactoryParms,
1555 true,
1556 bSubsettableActions );
1558 break;
1560 case MetaActionType::HATCH:
1562 // TODO(F2): use native Canvas hatches here
1563 GDIMetaFile aTmpMtf;
1565 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1566 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1567 aTmpMtf );
1568 createActions( aTmpMtf, rFactoryParms,
1569 bSubsettableActions );
1571 break;
1573 case MetaActionType::EPS:
1575 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
1576 const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
1578 // #121806# explicitly kept integer
1579 const Size aMtfSize( rSubstitute.GetPrefSize() );
1580 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1581 rSubstitute.GetPrefMapMode() ) );
1583 // #i44110# correct null-sized output - there
1584 // are metafiles which have zero size in at
1585 // least one dimension
1586 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1587 ::std::max( aMtfSizePixPre.Height(), 1L ) );
1589 // Setup local transform, such that the
1590 // metafile renders itself into the given
1591 // output rectangle
1592 rStates.pushState(PushFlags::ALL);
1594 rVDev.Push();
1595 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1597 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1598 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1600 rStates.getState().transform.translate( rPos.X(),
1601 rPos.Y() );
1602 rStates.getState().transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1603 (double)rSize.Height() / aMtfSizePix.Height() );
1605 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1606 rFactoryParms,
1607 bSubsettableActions );
1609 rVDev.Pop();
1610 rStates.popState();
1612 break;
1614 // handle metafile comments, to retrieve
1615 // meta-information for gradients, fills and
1616 // strokes. May skip actions, and may recurse.
1617 case MetaActionType::COMMENT:
1619 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1621 // Handle gradients
1622 if (pAct->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
1624 MetaGradientExAction* pGradAction = NULL;
1625 bool bDone( false );
1626 while( !bDone &&
1627 (pCurrAct=rMtf.NextAction()) != NULL )
1629 switch( pCurrAct->GetType() )
1631 // extract gradient info
1632 case MetaActionType::GRADIENTEX:
1633 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1634 break;
1636 // skip broken-down rendering, output gradient when sequence is ended
1637 case MetaActionType::COMMENT:
1638 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") )
1640 bDone = true;
1642 if( pGradAction )
1644 createGradientAction( pGradAction->GetPolyPolygon(),
1645 pGradAction->GetGradient(),
1646 rFactoryParms,
1647 false,
1648 bSubsettableActions );
1651 break;
1652 default: break;
1656 // TODO(P2): Handle drawing layer strokes, via
1657 // XPATHSTROKE_SEQ_BEGIN comment
1659 // Handle drawing layer fills
1660 else if( pAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1662 const sal_uInt8* pData = pAct->GetData();
1663 if ( pData )
1665 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), StreamMode::READ );
1667 SvtGraphicFill aFill;
1668 ReadSvtGraphicFill( aMemStm, aFill );
1670 // TODO(P2): Also handle gradients and
1671 // hatches like this
1673 // only evaluate comment for pure
1674 // bitmap fills. If a transparency
1675 // gradient is involved (denoted by
1676 // the FloatTransparent action), take
1677 // the normal meta actions.
1678 if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1679 !isActionContained( rMtf,
1680 "XPATHFILL_SEQ_END",
1681 MetaActionType::FLOATTRANSPARENT ) )
1683 rendering::Texture aTexture;
1685 // TODO(F1): the SvtGraphicFill
1686 // can also transport metafiles
1687 // here, handle that case, too
1688 Graphic aGraphic;
1689 aFill.getGraphic( aGraphic );
1691 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1692 const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1694 ::SvtGraphicFill::Transform aTransform;
1695 aFill.getTransform( aTransform );
1697 ::basegfx::B2DHomMatrix aMatrix;
1699 // convert to basegfx matrix
1700 aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1701 aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1702 aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1703 aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1704 aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1705 aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1707 ::basegfx::B2DHomMatrix aScale;
1708 aScale.scale( aBmpSize.Width(),
1709 aBmpSize.Height() );
1711 // post-multiply with the bitmap
1712 // size (XCanvas' texture assumes
1713 // the given bitmap to be
1714 // normalized to [0,1]x[0,1]
1715 // rectangle)
1716 aMatrix = aMatrix * aScale;
1718 // pre-multiply with the
1719 // logic-to-pixel scale factor
1720 // (the metafile comment works in
1721 // logical coordinates).
1722 ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1723 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1724 rVDev );
1726 ::basegfx::unotools::affineMatrixFromHomMatrix(
1727 aTexture.AffineTransform,
1728 aMatrix );
1730 aTexture.Alpha = 1.0 - aFill.getTransparency();
1731 aTexture.Bitmap =
1732 vcl::unotools::xBitmapFromBitmapEx(
1733 rCanvas->getUNOCanvas()->getDevice(),
1734 aBmpEx );
1735 if( aFill.isTiling() )
1737 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1738 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1740 else
1742 aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1743 aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1746 ::tools::PolyPolygon aPath;
1747 aFill.getPath( aPath );
1749 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1750 aPoly.transform( rStates.getState().mapModeTransform );
1751 ActionSharedPtr pPolyAction(
1752 internal::PolyPolyActionFactory::createPolyPolyAction(
1753 aPoly,
1754 rCanvas,
1755 rStates.getState(),
1756 aTexture ) );
1758 if( pPolyAction )
1760 maActions.push_back(
1761 MtfAction(
1762 pPolyAction,
1763 io_rCurrActionIndex ) );
1765 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1768 // skip broken-down render output
1769 skipContent( rMtf,
1770 "XPATHFILL_SEQ_END",
1771 io_rCurrActionIndex );
1775 // Handle drawing layer fills
1776 else if( pAct->GetComment() == "EMF_PLUS" ) {
1777 static int count = -1, limit = 0x7fffffff;
1778 if (count == -1) {
1779 count = 0;
1780 if (char *env = getenv ("EMF_PLUS_LIMIT")) {
1781 limit = atoi (env);
1782 SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit);
1785 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct->GetDataSize ());
1786 if (count < limit)
1787 processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
1788 count ++;
1789 } else if( pAct->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1790 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct->GetDataSize ());
1792 SvMemoryStream rMF ((void*) pAct->GetData (), pAct->GetDataSize (), StreamMode::READ);
1794 rMF.ReadInt32( nFrameLeft ).ReadInt32( nFrameTop ).ReadInt32( nFrameRight ).ReadInt32( nFrameBottom );
1795 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft << "," << nFrameTop << " - " << nFrameRight << "," << nFrameBottom);
1796 rMF.ReadInt32( nPixX ).ReadInt32( nPixY ).ReadInt32( nMmX ).ReadInt32( nMmY );
1797 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX << "x" << nPixY << " mm size: " << nMmX << "x" << nMmY);
1799 ReadXForm( rMF, aBaseTransform );
1800 //aWorldTransform.Set (aBaseTransform);
1803 break;
1807 // In the third part of this monster-switch, we
1808 // handle all 'acting' meta actions. These are all
1809 // processed by constructing function objects for
1810 // them, which will later ease caching.
1814 case MetaActionType::POINT:
1816 const OutDevState& rState( rStates.getState() );
1817 if( rState.lineColor.getLength() )
1819 ActionSharedPtr pPointAction(
1820 internal::PointActionFactory::createPointAction(
1821 rState.mapModeTransform * vcl::unotools::b2DPointFromPoint(
1822 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1823 rCanvas,
1824 rState ) );
1826 if( pPointAction )
1828 maActions.push_back(
1829 MtfAction(
1830 pPointAction,
1831 io_rCurrActionIndex ) );
1833 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1837 break;
1839 case MetaActionType::PIXEL:
1841 const OutDevState& rState( rStates.getState() );
1842 if( rState.lineColor.getLength() )
1844 ActionSharedPtr pPointAction(
1845 internal::PointActionFactory::createPointAction(
1846 rState.mapModeTransform * vcl::unotools::b2DPointFromPoint(
1847 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1848 rCanvas,
1849 rState,
1850 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1852 if( pPointAction )
1854 maActions.push_back(
1855 MtfAction(
1856 pPointAction,
1857 io_rCurrActionIndex ) );
1859 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1863 break;
1865 case MetaActionType::LINE:
1867 const OutDevState& rState( rStates.getState() );
1868 if( rState.lineColor.getLength() )
1870 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1872 const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1874 const ::basegfx::B2DPoint aStartPoint(
1875 rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1876 const ::basegfx::B2DPoint aEndPoint(
1877 rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1879 ActionSharedPtr pLineAction;
1881 if( rLineInfo.IsDefault() )
1883 // plain hair line
1884 pLineAction =
1885 internal::LineActionFactory::createLineAction(
1886 aStartPoint,
1887 aEndPoint,
1888 rCanvas,
1889 rState );
1891 if( pLineAction )
1893 maActions.push_back(
1894 MtfAction(
1895 pLineAction,
1896 io_rCurrActionIndex ) );
1898 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1901 else if( LINE_NONE != rLineInfo.GetStyle() )
1903 // 'thick' line
1904 rendering::StrokeAttributes aStrokeAttributes;
1906 setupStrokeAttributes( aStrokeAttributes,
1907 rFactoryParms,
1908 rLineInfo );
1910 // XCanvas can only stroke polygons,
1911 // not simple lines - thus, handle
1912 // this case via the polypolygon
1913 // action
1914 ::basegfx::B2DPolygon aPoly;
1915 aPoly.append( aStartPoint );
1916 aPoly.append( aEndPoint );
1917 pLineAction =
1918 internal::PolyPolyActionFactory::createPolyPolyAction(
1919 ::basegfx::B2DPolyPolygon( aPoly ),
1920 rCanvas, rState, aStrokeAttributes );
1922 if( pLineAction )
1924 maActions.push_back(
1925 MtfAction(
1926 pLineAction,
1927 io_rCurrActionIndex ) );
1929 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1932 // else: line style is default
1933 // (i.e. invisible), don't generate action
1936 break;
1938 case MetaActionType::RECT:
1940 const Rectangle& rRect(
1941 static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
1943 if( rRect.IsEmpty() )
1944 break;
1946 const OutDevState& rState( rStates.getState() );
1947 const ::basegfx::B2DPoint aTopLeftPixel(
1948 rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
1949 const ::basegfx::B2DPoint aBottomRightPixel(
1950 rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1951 // #121100# OutputDevice::DrawRect() fills
1952 // rectangles Apple-like, i.e. with one
1953 // additional pixel to the right and bottom.
1954 ::basegfx::B2DPoint(1,1) );
1956 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1957 ::basegfx::B2DRange( aTopLeftPixel,
1958 aBottomRightPixel )),
1959 rFactoryParms );
1960 break;
1963 case MetaActionType::ROUNDRECT:
1965 const Rectangle& rRect(
1966 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
1968 if( rRect.IsEmpty() )
1969 break;
1971 ::basegfx::B2DPolygon aPoly(
1972 ::basegfx::tools::createPolygonFromRect(
1973 ::basegfx::B2DRange(
1974 vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1975 vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1976 ::basegfx::B2DPoint(1,1) ),
1977 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
1978 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
1979 aPoly.transform( rStates.getState().mapModeTransform );
1981 createFillAndStroke( aPoly,
1982 rFactoryParms );
1984 break;
1986 case MetaActionType::ELLIPSE:
1988 const Rectangle& rRect(
1989 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
1991 if( rRect.IsEmpty() )
1992 break;
1994 const ::basegfx::B2DRange aRange(
1995 vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1996 vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1997 ::basegfx::B2DPoint(1,1) );
1999 ::basegfx::B2DPolygon aPoly(
2000 ::basegfx::tools::createPolygonFromEllipse(
2001 aRange.getCenter(),
2002 aRange.getWidth(),
2003 aRange.getHeight() ));
2004 aPoly.transform( rStates.getState().mapModeTransform );
2006 createFillAndStroke( aPoly,
2007 rFactoryParms );
2009 break;
2011 case MetaActionType::ARC:
2013 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2014 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2015 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2016 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2017 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2018 aPoly.transform( rStates.getState().mapModeTransform );
2020 createFillAndStroke( aPoly,
2021 rFactoryParms );
2023 break;
2025 case MetaActionType::PIE:
2027 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2028 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2029 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2030 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2031 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2032 aPoly.transform( rStates.getState().mapModeTransform );
2034 createFillAndStroke( aPoly,
2035 rFactoryParms );
2037 break;
2039 case MetaActionType::CHORD:
2041 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2042 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2043 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2044 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2045 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2046 aPoly.transform( rStates.getState().mapModeTransform );
2048 createFillAndStroke( aPoly,
2049 rFactoryParms );
2051 break;
2053 case MetaActionType::POLYLINE:
2055 const OutDevState& rState( rStates.getState() );
2056 if( rState.lineColor.getLength() ||
2057 rState.fillColor.getLength() )
2059 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2061 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2062 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2063 aPoly.transform( rState.mapModeTransform );
2065 ActionSharedPtr pLineAction;
2067 if( rLineInfo.IsDefault() )
2069 // plain hair line polygon
2070 pLineAction =
2071 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2072 ::basegfx::B2DPolyPolygon(aPoly),
2073 rCanvas,
2074 rState );
2076 if( pLineAction )
2078 maActions.push_back(
2079 MtfAction(
2080 pLineAction,
2081 io_rCurrActionIndex ) );
2083 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2086 else if( LINE_NONE != rLineInfo.GetStyle() )
2088 // 'thick' line polygon
2089 rendering::StrokeAttributes aStrokeAttributes;
2091 setupStrokeAttributes( aStrokeAttributes,
2092 rFactoryParms,
2093 rLineInfo );
2095 pLineAction =
2096 internal::PolyPolyActionFactory::createPolyPolyAction(
2097 ::basegfx::B2DPolyPolygon(aPoly),
2098 rCanvas,
2099 rState,
2100 aStrokeAttributes ) ;
2102 if( pLineAction )
2104 maActions.push_back(
2105 MtfAction(
2106 pLineAction,
2107 io_rCurrActionIndex ) );
2109 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2112 // else: line style is default
2113 // (i.e. invisible), don't generate action
2116 break;
2118 case MetaActionType::POLYGON:
2120 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2121 aPoly.transform( rStates.getState().mapModeTransform );
2122 createFillAndStroke( aPoly,
2123 rFactoryParms );
2125 break;
2127 case MetaActionType::POLYPOLYGON:
2129 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2130 aPoly.transform( rStates.getState().mapModeTransform );
2131 createFillAndStroke( aPoly,
2132 rFactoryParms );
2134 break;
2136 case MetaActionType::BMP:
2138 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2140 ActionSharedPtr pBmpAction(
2141 internal::BitmapActionFactory::createBitmapAction(
2142 pAct->GetBitmap(),
2143 rStates.getState().mapModeTransform *
2144 vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2145 rCanvas,
2146 rStates.getState() ) );
2148 if( pBmpAction )
2150 maActions.push_back(
2151 MtfAction(
2152 pBmpAction,
2153 io_rCurrActionIndex ) );
2155 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2158 break;
2160 case MetaActionType::BMPSCALE:
2162 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2164 ActionSharedPtr pBmpAction(
2165 internal::BitmapActionFactory::createBitmapAction(
2166 pAct->GetBitmap(),
2167 rStates.getState().mapModeTransform *
2168 vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2169 rStates.getState().mapModeTransform *
2170 vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2171 rCanvas,
2172 rStates.getState() ) );
2174 if( pBmpAction )
2176 maActions.push_back(
2177 MtfAction(
2178 pBmpAction,
2179 io_rCurrActionIndex ) );
2181 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2184 break;
2186 case MetaActionType::BMPSCALEPART:
2188 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2190 // crop bitmap to given source rectangle (no
2191 // need to copy and convert the whole bitmap)
2192 Bitmap aBmp( pAct->GetBitmap() );
2193 const Rectangle aCropRect( pAct->GetSrcPoint(),
2194 pAct->GetSrcSize() );
2195 aBmp.Crop( aCropRect );
2197 ActionSharedPtr pBmpAction(
2198 internal::BitmapActionFactory::createBitmapAction(
2199 aBmp,
2200 rStates.getState().mapModeTransform *
2201 vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2202 rStates.getState().mapModeTransform *
2203 vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2204 rCanvas,
2205 rStates.getState() ) );
2207 if( pBmpAction )
2209 maActions.push_back(
2210 MtfAction(
2211 pBmpAction,
2212 io_rCurrActionIndex ) );
2214 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2217 break;
2219 case MetaActionType::BMPEX:
2221 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2223 ActionSharedPtr pBmpAction(
2224 internal::BitmapActionFactory::createBitmapAction(
2225 pAct->GetBitmapEx(),
2226 rStates.getState().mapModeTransform *
2227 vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2228 rCanvas,
2229 rStates.getState() ) );
2231 if( pBmpAction )
2233 maActions.push_back(
2234 MtfAction(
2235 pBmpAction,
2236 io_rCurrActionIndex ) );
2238 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2241 break;
2243 case MetaActionType::BMPEXSCALE:
2245 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2247 ActionSharedPtr pBmpAction(
2248 internal::BitmapActionFactory::createBitmapAction(
2249 pAct->GetBitmapEx(),
2250 rStates.getState().mapModeTransform *
2251 vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2252 rStates.getState().mapModeTransform *
2253 vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2254 rCanvas,
2255 rStates.getState() ) );
2257 if( pBmpAction )
2259 maActions.push_back(
2260 MtfAction(
2261 pBmpAction,
2262 io_rCurrActionIndex ) );
2264 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2267 break;
2269 case MetaActionType::BMPEXSCALEPART:
2271 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2273 // crop bitmap to given source rectangle (no
2274 // need to copy and convert the whole bitmap)
2275 BitmapEx aBmp( pAct->GetBitmapEx() );
2276 const Rectangle aCropRect( pAct->GetSrcPoint(),
2277 pAct->GetSrcSize() );
2278 aBmp.Crop( aCropRect );
2280 ActionSharedPtr pBmpAction(
2281 internal::BitmapActionFactory::createBitmapAction(
2282 aBmp,
2283 rStates.getState().mapModeTransform *
2284 vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2285 rStates.getState().mapModeTransform *
2286 vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2287 rCanvas,
2288 rStates.getState() ) );
2290 if( pBmpAction )
2292 maActions.push_back(
2293 MtfAction(
2294 pBmpAction,
2295 io_rCurrActionIndex ) );
2297 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2300 break;
2302 case MetaActionType::MASK:
2304 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2306 // create masked BitmapEx right here, as the
2307 // canvas does not provide equivalent
2308 // functionality
2309 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2310 pAct->GetColor() ));
2312 ActionSharedPtr pBmpAction(
2313 internal::BitmapActionFactory::createBitmapAction(
2314 aBmp,
2315 rStates.getState().mapModeTransform *
2316 vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2317 rCanvas,
2318 rStates.getState() ) );
2320 if( pBmpAction )
2322 maActions.push_back(
2323 MtfAction(
2324 pBmpAction,
2325 io_rCurrActionIndex ) );
2327 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2330 break;
2332 case MetaActionType::MASKSCALE:
2334 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2336 // create masked BitmapEx right here, as the
2337 // canvas does not provide equivalent
2338 // functionality
2339 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2340 pAct->GetColor() ));
2342 ActionSharedPtr pBmpAction(
2343 internal::BitmapActionFactory::createBitmapAction(
2344 aBmp,
2345 rStates.getState().mapModeTransform *
2346 vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2347 rStates.getState().mapModeTransform *
2348 vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2349 rCanvas,
2350 rStates.getState() ) );
2352 if( pBmpAction )
2354 maActions.push_back(
2355 MtfAction(
2356 pBmpAction,
2357 io_rCurrActionIndex ) );
2359 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2362 break;
2364 case MetaActionType::MASKSCALEPART:
2366 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2368 // create masked BitmapEx right here, as the
2369 // canvas does not provide equivalent
2370 // functionality
2371 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2372 pAct->GetColor() ));
2374 // crop bitmap to given source rectangle (no
2375 // need to copy and convert the whole bitmap)
2376 const Rectangle aCropRect( pAct->GetSrcPoint(),
2377 pAct->GetSrcSize() );
2378 aBmp.Crop( aCropRect );
2380 ActionSharedPtr pBmpAction(
2381 internal::BitmapActionFactory::createBitmapAction(
2382 aBmp,
2383 rStates.getState().mapModeTransform *
2384 vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2385 rStates.getState().mapModeTransform *
2386 vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2387 rCanvas,
2388 rStates.getState() ) );
2390 if( pBmpAction )
2392 maActions.push_back(
2393 MtfAction(
2394 pBmpAction,
2395 io_rCurrActionIndex ) );
2397 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2400 break;
2402 case MetaActionType::GRADIENTEX:
2403 // TODO(F1): use native Canvas gradients here
2404 // action is ignored here, because redundant to MetaActionType::GRADIENT
2405 break;
2407 case MetaActionType::WALLPAPER:
2408 // TODO(F2): NYI
2409 break;
2411 case MetaActionType::Transparent:
2413 const OutDevState& rState( rStates.getState() );
2414 if( rState.lineColor.getLength() ||
2415 rState.fillColor.getLength() )
2417 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2418 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2419 aPoly.transform( rState.mapModeTransform );
2421 ActionSharedPtr pPolyAction(
2422 internal::PolyPolyActionFactory::createPolyPolyAction(
2423 aPoly,
2424 rCanvas,
2425 rState,
2426 pAct->GetTransparence() ) );
2428 if( pPolyAction )
2430 maActions.push_back(
2431 MtfAction(
2432 pPolyAction,
2433 io_rCurrActionIndex ) );
2435 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2439 break;
2441 case MetaActionType::FLOATTRANSPARENT:
2443 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2445 internal::MtfAutoPtr pMtf(
2446 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2448 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2449 internal::GradientAutoPtr pGradient(
2450 new Gradient( pAct->GetGradient() ) );
2452 DBG_TESTSOLARMUTEX();
2454 ActionSharedPtr pFloatTransAction(
2455 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2456 std::move(pMtf),
2457 std::move(pGradient),
2458 rParms,
2459 rStates.getState().mapModeTransform *
2460 vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2461 rStates.getState().mapModeTransform *
2462 vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2463 rCanvas,
2464 rStates.getState() ) );
2466 if( pFloatTransAction )
2468 maActions.push_back(
2469 MtfAction(
2470 pFloatTransAction,
2471 io_rCurrActionIndex ) );
2473 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2476 break;
2478 case MetaActionType::TEXT:
2480 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2481 OUString sText = pAct->GetText();
2483 if (rVDev.GetDigitLanguage())
2484 sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2486 const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2488 createTextAction(
2489 pAct->GetPoint(),
2490 sText,
2491 pAct->GetIndex(),
2492 nLen,
2493 NULL,
2494 rFactoryParms,
2495 bSubsettableActions );
2497 break;
2499 case MetaActionType::TEXTARRAY:
2501 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2502 OUString sText = pAct->GetText();
2504 if (rVDev.GetDigitLanguage())
2505 sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2507 const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2509 createTextAction(
2510 pAct->GetPoint(),
2511 sText,
2512 pAct->GetIndex(),
2513 nLen,
2514 pAct->GetDXArray(),
2515 rFactoryParms,
2516 bSubsettableActions );
2518 break;
2520 case MetaActionType::TEXTLINE:
2522 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2524 const OutDevState& rState( rStates.getState() );
2525 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2526 rVDev ) );
2527 const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2528 ::basegfx::B2DSize(pAct->GetWidth(),
2529 0 ));
2531 ActionSharedPtr pPolyAction(
2532 PolyPolyActionFactory::createPolyPolyAction(
2533 tools::createTextLinesPolyPolygon(
2534 rState.mapModeTransform *
2535 ::basegfx::B2DPoint(
2536 vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2537 vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2538 aSize.getX(),
2539 tools::createTextLineInfo( rVDev,
2540 rState )),
2541 rCanvas,
2542 rState ) );
2544 if( pPolyAction.get() )
2546 maActions.push_back(
2547 MtfAction(
2548 pPolyAction,
2549 io_rCurrActionIndex ) );
2551 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2554 break;
2556 case MetaActionType::TEXTRECT:
2558 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2560 rStates.pushState(PushFlags::ALL);
2562 // use the VDev to break up the text rect
2563 // action into readily formatted lines
2564 GDIMetaFile aTmpMtf;
2565 rVDev.AddTextRectActions( pAct->GetRect(),
2566 pAct->GetText(),
2567 pAct->GetStyle(),
2568 aTmpMtf );
2570 createActions( aTmpMtf,
2571 rFactoryParms,
2572 bSubsettableActions );
2574 rStates.popState();
2576 break;
2579 case MetaActionType::STRETCHTEXT:
2581 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2582 OUString sText = pAct->GetText();
2584 if (rVDev.GetDigitLanguage())
2585 sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2587 const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2589 // #i70897# Nothing to do, actually...
2590 if( nLen == 0 )
2591 break;
2593 // have to fit the text into the given
2594 // width. This is achieved by internally
2595 // generating a DX array, and uniformly
2596 // distributing the excess/insufficient width
2597 // to every logical character.
2598 ::boost::scoped_array< long > pDXArray( new long[nLen] );
2600 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2601 pAct->GetIndex(), pAct->GetLen() );
2603 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2605 // Last entry of pDXArray contains total width of the text
2606 long* p = pDXArray.get();
2607 for (sal_Int32 i = 1; i <= nLen; ++i)
2609 // calc ratio for every array entry, to
2610 // distribute rounding errors 'evenly'
2611 // across the characters. Note that each
2612 // entry represents the 'end' position of
2613 // the corresponding character, thus, we
2614 // let i run from 1 to nLen.
2615 *p++ += (long)i*nWidthDifference/nLen;
2618 createTextAction(
2619 pAct->GetPoint(),
2620 sText,
2621 pAct->GetIndex(),
2622 nLen,
2623 pDXArray.get(),
2624 rFactoryParms,
2625 bSubsettableActions );
2627 break;
2629 default:
2630 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2631 break;
2634 // increment action index (each mtf action counts _at
2635 // least_ one. Some count for more, therefore,
2636 // io_rCurrActionIndex is sometimes incremented by
2637 // pAct->getActionCount()-1 above, the -1 being the
2638 // correction for the unconditional increment here).
2639 ++io_rCurrActionIndex;
2642 return true;
2646 namespace
2648 class ActionRenderer
2650 public:
2651 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2652 maTransformation( rTransformation ),
2653 mbRet( true )
2657 bool result() const
2659 return mbRet;
2662 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2664 // ANDing the result. We want to fail if at least
2665 // one action failed.
2666 mbRet &= rAction.mpAction->render( maTransformation );
2669 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2670 const Action::Subset& rSubset )
2672 // ANDing the result. We want to fail if at least
2673 // one action failed.
2674 mbRet &= rAction.mpAction->renderSubset( maTransformation,
2675 rSubset );
2678 private:
2679 ::basegfx::B2DHomMatrix maTransformation;
2680 bool mbRet;
2683 class AreaQuery
2685 public:
2686 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2687 maTransformation( rTransformation ),
2688 maBounds()
2692 static bool result()
2694 return true; // nothing can fail here
2697 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2699 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2702 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2703 const Action::Subset& rSubset )
2705 maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2706 rSubset ) );
2709 ::basegfx::B2DRange getBounds() const
2711 return maBounds;
2714 private:
2715 ::basegfx::B2DHomMatrix maTransformation;
2716 ::basegfx::B2DRange maBounds;
2719 // Doing that via inline class. Compilers tend to not inline free
2720 // functions.
2721 struct UpperBoundActionIndexComparator
2723 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2724 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2726 const sal_Int32 nLHSCount( rLHS.mpAction ?
2727 rLHS.mpAction->getActionCount() : 0 );
2728 const sal_Int32 nRHSCount( rRHS.mpAction ?
2729 rRHS.mpAction->getActionCount() : 0 );
2731 // compare end of action range, to have an action selected
2732 // by lower_bound even if the requested index points in
2733 // the middle of the action's range
2734 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2738 /** Algorithm to apply given functor to a subset range
2740 @tpl Functor
2742 Functor to call for each element of the subset
2743 range. Must provide the following method signatures:
2744 bool result() (returning false if operation failed)
2747 template< typename Functor > bool
2748 forSubsetRange( Functor& rFunctor,
2749 ImplRenderer::ActionVector::const_iterator aRangeBegin,
2750 ImplRenderer::ActionVector::const_iterator aRangeEnd,
2751 sal_Int32 nStartIndex,
2752 sal_Int32 nEndIndex,
2753 const ImplRenderer::ActionVector::const_iterator& rEnd )
2755 if( aRangeBegin == aRangeEnd )
2757 // only a single action. Setup subset, and call functor
2758 Action::Subset aSubset;
2759 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2760 nStartIndex - aRangeBegin->mnOrigIndex );
2761 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2762 nEndIndex - aRangeBegin->mnOrigIndex );
2764 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2765 "ImplRenderer::forSubsetRange(): Invalid indices" );
2767 rFunctor( *aRangeBegin, aSubset );
2769 else
2771 // more than one action.
2773 // render partial first, full intermediate, and
2774 // partial last action
2775 Action::Subset aSubset;
2776 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2777 nStartIndex - aRangeBegin->mnOrigIndex );
2778 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2780 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2781 "ImplRenderer::forSubsetRange(): Invalid indices" );
2783 rFunctor( *aRangeBegin, aSubset );
2785 // first action rendered, skip to next
2786 ++aRangeBegin;
2788 // render full middle actions
2789 while( aRangeBegin != aRangeEnd )
2790 rFunctor( *aRangeBegin++ );
2792 if( aRangeEnd == rEnd ||
2793 aRangeEnd->mnOrigIndex > nEndIndex )
2795 // aRangeEnd denotes end of action vector,
2797 // or
2799 // nEndIndex references something _after_
2800 // aRangeBegin, but _before_ aRangeEnd
2802 // either way: no partial action left
2803 return rFunctor.result();
2806 aSubset.mnSubsetBegin = 0;
2807 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2809 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2810 "ImplRenderer::forSubsetRange(): Invalid indices" );
2812 rFunctor( *aRangeEnd, aSubset );
2815 return rFunctor.result();
2819 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2820 sal_Int32& io_rEndIndex,
2821 ActionVector::const_iterator& o_rRangeBegin,
2822 ActionVector::const_iterator& o_rRangeEnd ) const
2824 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2825 "ImplRenderer::getSubsetIndices(): invalid action range" );
2827 ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2828 "ImplRenderer::getSubsetIndices(): no actions to render" );
2830 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2831 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2832 maActions.back().mpAction->getActionCount() );
2834 // clip given range to permissible values (there might be
2835 // ranges before and behind the valid indices)
2836 io_rStartIndex = ::std::max( nMinActionIndex,
2837 io_rStartIndex );
2838 io_rEndIndex = ::std::min( nMaxActionIndex,
2839 io_rEndIndex );
2841 if( io_rStartIndex == io_rEndIndex ||
2842 io_rStartIndex > io_rEndIndex )
2844 // empty range, don't render anything. The second
2845 // condition e.g. happens if the requested range lies
2846 // fully before or behind the valid action indices.
2847 return false;
2851 const ActionVector::const_iterator aBegin( maActions.begin() );
2852 const ActionVector::const_iterator aEnd( maActions.end() );
2855 // find start and end action
2856 // =========================
2857 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2858 MtfAction( ActionSharedPtr(), io_rStartIndex ),
2859 UpperBoundActionIndexComparator() );
2860 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
2861 MtfAction( ActionSharedPtr(), io_rEndIndex ),
2862 UpperBoundActionIndexComparator() );
2863 return true;
2867 // Public methods
2870 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
2871 const GDIMetaFile& rMtf,
2872 const Parameters& rParams )
2873 : CanvasGraphicHelper(rCanvas)
2874 , maActions()
2875 , fPageScale(0.0)
2876 , nOriginX(0)
2877 , nOriginY(0)
2878 , nHDPI(0)
2879 , nVDPI(0)
2880 , nFrameLeft(0)
2881 , nFrameTop(0)
2882 , nFrameRight(0)
2883 , nFrameBottom(0)
2884 , nPixX(0)
2885 , nPixY(0)
2886 , nMmX(0)
2887 , nMmY(0)
2888 , mbMultipart(false)
2889 , mMFlags(0)
2891 memset (aObjects, 0, sizeof (aObjects));
2893 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2895 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2896 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2897 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2898 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2900 // make sure canvas and graphic device are valid; action
2901 // creation don't check that every time
2902 if( rCanvas.get() == NULL ||
2903 !rCanvas->getUNOCanvas().is() ||
2904 !rCanvas->getUNOCanvas()->getDevice().is() )
2906 // leave actions empty
2907 return;
2910 VectorOfOutDevStates aStateStack;
2912 ScopedVclPtrInstance< VirtualDevice > aVDev;
2913 aVDev->EnableOutput( false );
2915 // Setup VDev for state tracking and mapping
2916 // =========================================
2918 aVDev->SetMapMode( rMtf.GetPrefMapMode() );
2920 const Size aMtfSize( rMtf.GetPrefSize() );
2921 const Size aMtfSizePixPre( aVDev->LogicToPixel( aMtfSize,
2922 rMtf.GetPrefMapMode() ) );
2924 // #i44110# correct null-sized output - there are shapes
2925 // which have zero size in at least one dimension
2926 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2927 ::std::max( aMtfSizePixPre.Height(), 1L ) );
2929 sal_Int32 nCurrActions(0);
2930 ActionFactoryParameters aParms(aStateStack,
2931 rCanvas,
2932 *aVDev.get(),
2933 rParams,
2934 nCurrActions );
2936 // init state stack
2937 aStateStack.clearStateStack();
2939 // Setup local state, such that the metafile renders
2940 // itself into a one-by-one square at the origin for
2941 // identity view and render transformations
2942 aStateStack.getState().transform.scale( 1.0 / aMtfSizePix.Width(),
2943 1.0 / aMtfSizePix.Height() );
2945 tools::calcLogic2PixelAffineTransform( aStateStack.getState().mapModeTransform,
2946 *aVDev.get() );
2948 ColorSharedPtr pColor( getCanvas()->createColor() );
2951 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2952 // setup default text color to black
2953 rState.textColor =
2954 rState.textFillColor =
2955 rState.textLineColor = pColor->getDeviceColor( 0x000000FF );
2958 // apply overrides from the Parameters struct
2959 if( rParams.maFillColor.is_initialized() )
2961 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2962 rState.isFillColorSet = true;
2963 rState.fillColor = pColor->getDeviceColor( *rParams.maFillColor );
2965 if( rParams.maLineColor.is_initialized() )
2967 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2968 rState.isLineColorSet = true;
2969 rState.lineColor = pColor->getDeviceColor( *rParams.maLineColor );
2971 if( rParams.maTextColor.is_initialized() )
2973 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2974 rState.isTextFillColorSet = true;
2975 rState.isTextLineColorSet = true;
2976 rState.textColor =
2977 rState.textFillColor =
2978 rState.textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
2980 if( rParams.maFontName.is_initialized() ||
2981 rParams.maFontWeight.is_initialized() ||
2982 rParams.maFontLetterForm.is_initialized() ||
2983 rParams.maFontUnderline.is_initialized() ||
2984 rParams.maFontProportion.is_initialized() )
2986 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2988 rState.xFont = createFont( rState.fontRotation,
2989 vcl::Font(), // default font
2990 aParms );
2993 /* EMF+ */
2994 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
2995 // we're
2996 // changing
2997 // the
2998 // current
2999 // action
3000 // in
3001 // createActions!
3002 aParms,
3003 true // TODO(P1): make subsettability configurable
3007 ImplRenderer::~ImplRenderer()
3009 // don't leak EMFPObjects
3010 for(unsigned int i=0; i<SAL_N_ELEMENTS(aObjects); ++i)
3011 delete aObjects[i];
3014 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
3015 sal_Int32 nEndIndex ) const
3017 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3019 ActionVector::const_iterator aRangeBegin;
3020 ActionVector::const_iterator aRangeEnd;
3024 if( !getSubsetIndices( nStartIndex, nEndIndex,
3025 aRangeBegin, aRangeEnd ) )
3026 return true; // nothing to render (but _that_ was successful)
3028 // now, aRangeBegin references the action in which the
3029 // subset rendering must start, and aRangeEnd references
3030 // the action in which the subset rendering must end (it
3031 // might also end right at the start of the referenced
3032 // action, such that zero of that action needs to be
3033 // rendered).
3036 // render subset of actions
3037 // ========================
3039 ::basegfx::B2DHomMatrix aMatrix;
3040 ::canvas::tools::getRenderStateTransform( aMatrix,
3041 getRenderState() );
3043 ActionRenderer aRenderer( aMatrix );
3045 return forSubsetRange( aRenderer,
3046 aRangeBegin,
3047 aRangeEnd,
3048 nStartIndex,
3049 nEndIndex,
3050 maActions.end() );
3052 catch( uno::Exception& )
3054 SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3055 comphelper::anyToString( cppu::getCaughtException() ),
3056 RTL_TEXTENCODING_UTF8 ).getStr() );
3058 // convert error to return value
3059 return false;
3063 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3064 sal_Int32 nEndIndex ) const
3066 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3068 ActionVector::const_iterator aRangeBegin;
3069 ActionVector::const_iterator aRangeEnd;
3071 if( !getSubsetIndices( nStartIndex, nEndIndex,
3072 aRangeBegin, aRangeEnd ) )
3073 return ::basegfx::B2DRange(); // nothing to render -> empty range
3075 // now, aRangeBegin references the action in which the
3076 // subset querying must start, and aRangeEnd references
3077 // the action in which the subset querying must end (it
3078 // might also end right at the start of the referenced
3079 // action, such that zero of that action needs to be
3080 // queried).
3083 // query bounds for subset of actions
3084 // ==================================
3086 ::basegfx::B2DHomMatrix aMatrix;
3087 ::canvas::tools::getRenderStateTransform( aMatrix,
3088 getRenderState() );
3090 AreaQuery aQuery( aMatrix );
3091 forSubsetRange( aQuery,
3092 aRangeBegin,
3093 aRangeEnd,
3094 nStartIndex,
3095 nEndIndex,
3096 maActions.end() );
3098 return aQuery.getBounds();
3101 bool ImplRenderer::draw() const
3103 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3105 ::basegfx::B2DHomMatrix aMatrix;
3106 ::canvas::tools::getRenderStateTransform( aMatrix,
3107 getRenderState() );
3111 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3113 catch( uno::Exception& )
3115 SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3116 comphelper::anyToString( cppu::getCaughtException() ),
3117 RTL_TEXTENCODING_UTF8 ).getStr() );
3119 return false;
3125 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */