Bump for 3.6-28
[LibreOffice.git] / cppcanvas / source / mtfrenderer / implrenderer.cxx
blob028aa0c7d33c09471df95138b1df8901e8dde837
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <canvas/debug.hxx>
31 #include <tools/diagnose_ex.h>
32 #include <canvas/verbosetrace.hxx>
33 #include <osl/mutex.hxx>
34 #include <vcl/svapp.hxx>
35 #include <rtl/logfile.hxx>
36 #include <comphelper/sequence.hxx>
37 #include <comphelper/anytostring.hxx>
38 #include <cppuhelper/exc_hlp.hxx>
39 #include <cppcanvas/canvas.hxx>
40 #include <com/sun/star/rendering/XGraphicDevice.hpp>
41 #include <com/sun/star/rendering/TexturingMode.hpp>
42 #include <com/sun/star/uno/Sequence.hxx>
43 #include <com/sun/star/geometry/RealPoint2D.hpp>
44 #include <com/sun/star/rendering/PanoseProportion.hpp>
45 #include <com/sun/star/rendering/ViewState.hpp>
46 #include <com/sun/star/rendering/RenderState.hpp>
47 #include <com/sun/star/rendering/XCanvasFont.hpp>
48 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
49 #include <com/sun/star/rendering/XCanvas.hpp>
50 #include <com/sun/star/rendering/PathCapType.hpp>
51 #include <com/sun/star/rendering/PathJoinType.hpp>
52 #include <basegfx/tools/canvastools.hxx>
53 #include <basegfx/tools/gradienttools.hxx>
54 #include <basegfx/numeric/ftools.hxx>
55 #include <basegfx/polygon/b2dpolypolygontools.hxx>
56 #include <basegfx/polygon/b2dpolygontools.hxx>
57 #include <basegfx/polygon/b2dpolygon.hxx>
58 #include <basegfx/polygon/b2dpolypolygon.hxx>
59 #include <basegfx/matrix/b2dhommatrix.hxx>
60 #include <basegfx/vector/b2dsize.hxx>
61 #include <basegfx/range/b2drectangle.hxx>
62 #include <basegfx/point/b2dpoint.hxx>
63 #include <basegfx/tuple/b2dtuple.hxx>
64 #include <basegfx/polygon/b2dpolygonclipper.hxx>
65 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
66 #include <canvas/canvastools.hxx>
67 #include <vcl/canvastools.hxx>
68 #include <vcl/salbtype.hxx>
69 #include <vcl/gdimtf.hxx>
70 #include <vcl/metaact.hxx>
71 #include <vcl/virdev.hxx>
72 #include <vcl/metric.hxx>
73 #include <vcl/graphictools.hxx>
74 #include <tools/poly.hxx>
75 #include <i18npool/mslangid.hxx>
76 #include <implrenderer.hxx>
77 #include <tools.hxx>
78 #include <outdevstate.hxx>
79 #include <action.hxx>
80 #include <bitmapaction.hxx>
81 #include <lineaction.hxx>
82 #include <pointaction.hxx>
83 #include <polypolyaction.hxx>
84 #include <rendergraphicaction.hxx>
85 #include <textaction.hxx>
86 #include <transparencygroupaction.hxx>
87 #include <vector>
88 #include <algorithm>
89 #include <iterator>
90 #include <boost/scoped_array.hpp>
91 #include "mtftools.hxx"
92 #include "outdevstate.hxx"
93 #include <basegfx/matrix/b2dhommatrixtools.hxx>
95 #if OSL_DEBUG_LEVEL > 1
96 #define EMFP_DEBUG(x) x
97 #else
98 #define EMFP_DEBUG(x)
99 #endif
101 using namespace ::com::sun::star;
104 // free support functions
105 // ======================
106 namespace
108 template < class MetaActionType > void setStateColor( MetaActionType* pAct,
109 bool& rIsColorSet,
110 uno::Sequence< double >& rColorSequence,
111 const cppcanvas::CanvasSharedPtr& rCanvas )
113 // set rIsColorSet and check for true at the same time
114 if( (rIsColorSet=pAct->IsSetting()) != false )
116 ::Color aColor( pAct->GetColor() );
118 // force alpha part of color to
119 // opaque. transparent painting is done
120 // explicitly via META_TRANSPARENT_ACTION
121 aColor.SetTransparency(0);
122 //aColor.SetTransparency(128);
124 rColorSequence = ::vcl::unotools::colorToDoubleSequence(
125 aColor,
126 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
130 void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes,
131 const ::cppcanvas::internal::ActionFactoryParameters& rParms,
132 const LineInfo& rLineInfo )
134 const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
135 o_rStrokeAttributes.StrokeWidth =
136 (rParms.mrStates.getState().mapModeTransform * aWidth).getX();
138 // setup reasonable defaults
139 o_rStrokeAttributes.MiterLimit = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
140 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
141 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
143 switch(rLineInfo.GetLineJoin())
145 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
146 o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE;
147 break;
148 case basegfx::B2DLINEJOIN_BEVEL:
149 o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL;
150 break;
151 case basegfx::B2DLINEJOIN_MITER:
152 o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
153 break;
154 case basegfx::B2DLINEJOIN_ROUND:
155 o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND;
156 break;
159 if( LINE_DASH == rLineInfo.GetStyle() )
161 const ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
163 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
165 // interpret dash info only if explicitly enabled as
166 // style
167 const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
168 const double nDistance( (rState.mapModeTransform * aDistance).getX() );
170 const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
171 const double nDashLen( (rState.mapModeTransform * aDashLen).getX() );
173 const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
174 const double nDotLen( (rState.mapModeTransform * aDotLen).getX() );
176 const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
177 2*rLineInfo.GetDotCount() );
179 o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
180 double* pDashArray = o_rStrokeAttributes.DashArray.getArray();
183 // iteratively fill dash array, first with dashs, then
184 // with dots.
185 // ===================================================
187 sal_Int32 nCurrEntry=0;
189 for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
191 pDashArray[nCurrEntry++] = nDashLen;
192 pDashArray[nCurrEntry++] = nDistance;
194 for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
196 pDashArray[nCurrEntry++] = nDotLen;
197 pDashArray[nCurrEntry++] = nDistance;
203 /** Create masked BitmapEx, where the white areas of rBitmap are
204 transparent, and the other appear in rMaskColor.
206 BitmapEx createMaskBmpEx( const Bitmap& rBitmap,
207 const ::Color& rMaskColor )
209 const ::Color aWhite( COL_WHITE );
210 BitmapPalette aBiLevelPalette(2);
211 aBiLevelPalette[0] = aWhite;
212 aBiLevelPalette[1] = rMaskColor;
214 Bitmap aMask( rBitmap.CreateMask( aWhite ));
215 Bitmap aSolid( rBitmap.GetSizePixel(),
217 &aBiLevelPalette );
218 aSolid.Erase( rMaskColor );
220 return BitmapEx( aSolid, aMask );
223 /** Shameless rip from vcl/source/gdi/outdev3.cxx
225 Should consolidate, into something like basetxt...
227 sal_Unicode getLocalizedChar( sal_Unicode nChar, LanguageType eLang )
229 // currently only conversion from ASCII digits is interesting
230 if( (nChar < '0') || ('9' < nChar) )
231 return nChar;
233 sal_Unicode nOffset(0);
234 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
235 // CAVEAT! To some like Mongolian MS assigned the same primary language
236 // although the script type is different!
237 switch( eLang & LANGUAGE_MASK_PRIMARY )
239 default:
240 break;
242 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
243 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
244 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
245 nOffset = 0x0660 - '0'; // arabic/persian/urdu
246 break;
247 case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY:
248 nOffset = 0x09E6 - '0'; // bengali
249 break;
250 case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY:
251 nOffset = 0x1040 - '0'; // burmese
252 break;
253 case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY:
254 nOffset = 0x0966 - '0'; // devanagari
255 break;
256 case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY:
257 nOffset = 0x0AE6 - '0'; // gujarati
258 break;
259 case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY:
260 nOffset = 0x0CE6 - '0'; // kannada
261 break;
262 case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY:
263 nOffset = 0x17E0 - '0'; // khmer
264 break;
265 case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY:
266 nOffset = 0x0ED0 - '0'; // lao
267 break;
268 case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY:
269 nOffset = 0x0D66 - '0'; // malayalam
270 break;
271 case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY:
272 if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
273 nOffset = 0x1810 - '0'; // mongolian
274 else
275 nOffset = 0; // mongolian cyrillic
276 break;
277 case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY:
278 nOffset = 0x0B66 - '0'; // oriya
279 break;
280 case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY:
281 nOffset = 0x0BE7 - '0'; // tamil
282 break;
283 case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY:
284 nOffset = 0x0C66 - '0'; // telugu
285 break;
286 case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY:
287 nOffset = 0x0E50 - '0'; // thai
288 break;
289 case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY:
290 nOffset = 0x0F20 - '0'; // tibetan
291 break;
294 nChar = sal::static_int_cast<sal_Unicode>(nChar + nOffset);
295 return nChar;
298 void convertToLocalizedNumerals( XubString& rStr,
299 LanguageType eTextLanguage )
301 const sal_Unicode* pBase = rStr.GetBuffer();
302 const sal_Unicode* pBegin = pBase + 0;
303 const xub_StrLen nEndIndex = rStr.Len();
304 const sal_Unicode* pEnd = pBase + nEndIndex;
306 for( ; pBegin < pEnd; ++pBegin )
308 // TODO: are there non-digit localizations?
309 if( (*pBegin >= '0') && (*pBegin <= '9') )
311 // translate characters to local preference
312 sal_Unicode cChar = getLocalizedChar( *pBegin, eTextLanguage );
313 if( cChar != *pBegin )
314 rStr.SetChar( sal::static_int_cast<sal_uInt16>(pBegin - pBase), cChar );
321 namespace cppcanvas
323 namespace internal
325 // state stack manipulators
326 // ------------------------
327 void VectorOfOutDevStates::clearStateStack()
329 m_aStates.clear();
330 const OutDevState aDefaultState;
331 m_aStates.push_back(aDefaultState);
334 OutDevState& VectorOfOutDevStates::getState()
336 return m_aStates.back();
339 const OutDevState& VectorOfOutDevStates::getState() const
341 return m_aStates.back();
344 void VectorOfOutDevStates::pushState(sal_uInt16 nFlags)
346 m_aStates.push_back( getState() );
347 getState().pushFlags = nFlags;
350 void VectorOfOutDevStates::popState()
352 if( getState().pushFlags != PUSH_ALL )
354 // a state is pushed which is incomplete, i.e. does not
355 // restore everything to the previous stack level when
356 // popped.
357 // That means, we take the old state, and restore every
358 // OutDevState member whose flag is set, from the new to the
359 // old state. Then the new state gets overwritten by the
360 // calculated state
362 // preset to-be-calculated new state with old state
363 OutDevState aCalculatedNewState( getState() );
365 // selectively copy to-be-restored content over saved old
366 // state
367 m_aStates.pop_back();
369 const OutDevState& rNewState( getState() );
371 if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) )
373 aCalculatedNewState.lineColor = rNewState.lineColor;
374 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
377 if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) )
379 aCalculatedNewState.fillColor = rNewState.fillColor;
380 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
383 if( (aCalculatedNewState.pushFlags & PUSH_FONT) )
385 aCalculatedNewState.xFont = rNewState.xFont;
386 aCalculatedNewState.fontRotation = rNewState.fontRotation;
387 aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle;
388 aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle;
389 aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle;
390 aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle;
391 aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle;
392 aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet;
393 aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet;
394 aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet;
397 if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) )
399 aCalculatedNewState.textColor = rNewState.textColor;
402 if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) )
404 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
407 if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) )
409 aCalculatedNewState.clip = rNewState.clip;
410 aCalculatedNewState.clipRect = rNewState.clipRect;
411 aCalculatedNewState.xClipPoly = rNewState.xClipPoly;
414 // TODO(F2): Raster ops NYI
415 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
416 // {
417 // }
419 if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) )
421 aCalculatedNewState.textFillColor = rNewState.textFillColor;
422 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
425 if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) )
427 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
430 // TODO(F1): Refpoint handling NYI
431 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
432 // {
433 // }
435 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) )
437 aCalculatedNewState.textLineColor = rNewState.textLineColor;
438 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
441 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) )
443 aCalculatedNewState.textAlignment = rNewState.textAlignment;
444 aCalculatedNewState.textDirection = rNewState.textDirection;
447 // TODO(F2): Text language handling NYI
448 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
449 // {
450 // }
452 // always copy push mode
453 aCalculatedNewState.pushFlags = rNewState.pushFlags;
455 // flush to stack
456 getState() = aCalculatedNewState;
458 else
460 m_aStates.pop_back();
464 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
465 const ActionFactoryParameters& rParms )
467 const OutDevState& rState( rParms.mrStates.getState() );
468 if( (!rState.isLineColorSet &&
469 !rState.isFillColorSet) ||
470 (rState.lineColor.getLength() == 0 &&
471 rState.fillColor.getLength() == 0) )
473 return false;
476 ActionSharedPtr pPolyAction(
477 internal::PolyPolyActionFactory::createPolyPolyAction(
478 rPolyPoly, rParms.mrCanvas, rState ) );
480 if( pPolyAction )
482 maActions.push_back(
483 MtfAction(
484 pPolyAction,
485 rParms.mrCurrActionIndex ) );
487 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
490 return true;
493 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly,
494 const ActionFactoryParameters& rParms )
496 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
497 rParms );
500 void ImplRenderer::skipContent( GDIMetaFile& rMtf,
501 const char* pCommentString,
502 sal_Int32& io_rCurrActionIndex ) const
504 ENSURE_OR_THROW( pCommentString,
505 "ImplRenderer::skipContent(): NULL string given" );
507 MetaAction* pCurrAct;
508 while( (pCurrAct=rMtf.NextAction()) != NULL )
510 // increment action index, we've skipped an action.
511 ++io_rCurrActionIndex;
513 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
514 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
515 pCommentString) )
517 // requested comment found, done
518 return;
522 // EOF
523 return;
526 bool ImplRenderer::isActionContained( GDIMetaFile& rMtf,
527 const char* pCommentString,
528 sal_uInt16 nType ) const
530 ENSURE_OR_THROW( pCommentString,
531 "ImplRenderer::isActionContained(): NULL string given" );
533 bool bRet( false );
535 // at least _one_ call to GDIMetaFile::NextAction() is
536 // executed
537 sal_uIntPtr nPos( 1 );
539 MetaAction* pCurrAct;
540 while( (pCurrAct=rMtf.NextAction()) != NULL )
542 if( pCurrAct->GetType() == nType )
544 bRet = true; // action type found
545 break;
548 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
549 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
550 pCommentString) )
552 // delimiting end comment found, done
553 bRet = false; // not yet found
554 break;
557 ++nPos;
560 // rewind metafile to previous position (this method must
561 // not change the current metaaction)
562 while( nPos-- )
563 rMtf.WindPrev();
565 if( !pCurrAct )
567 // EOF, and not yet found
568 bRet = false;
571 return bRet;
574 void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly,
575 const ::Gradient& rGradient,
576 const ActionFactoryParameters& rParms,
577 bool bIsPolygonRectangle,
578 bool bSubsettableActions )
580 DBG_TESTSOLARMUTEX();
582 ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
583 aDevicePoly.transform( rParms.mrStates.getState().mapModeTransform );
585 // decide, whether this gradient can be rendered natively
586 // by the canvas, or must be emulated via VCL gradient
587 // action extraction.
588 const sal_uInt16 nSteps( rGradient.GetSteps() );
590 if( // step count is infinite, can use native canvas
591 // gradients here
592 nSteps == 0 ||
593 // step count is sufficiently high, such that no
594 // discernible difference should be visible.
595 nSteps > 64 )
597 uno::Reference< lang::XMultiServiceFactory> xFactory(
598 rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
600 if( xFactory.is() )
602 rendering::Texture aTexture;
604 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
605 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
606 aTexture.Alpha = 1.0;
609 // setup start/end color values
610 // ----------------------------
612 // scale color coefficients with gradient intensities
613 const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() );
614 ::Color aVCLStartColor( rGradient.GetStartColor() );
615 aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) );
616 aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
617 aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) );
619 const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() );
620 ::Color aVCLEndColor( rGradient.GetEndColor() );
621 aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) );
622 aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
623 aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) );
625 uno::Reference<rendering::XColorSpace> xColorSpace(
626 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
627 const uno::Sequence< double > aStartColor(
628 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor,
629 xColorSpace ));
630 const uno::Sequence< double > aEndColor(
631 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor,
632 xColorSpace ));
634 uno::Sequence< uno::Sequence < double > > aColors(2);
635 uno::Sequence< double > aStops(2);
637 if( rGradient.GetStyle() == GradientStyle_AXIAL )
639 aStops.realloc(3);
640 aColors.realloc(3);
642 aStops[0] = 0.0;
643 aStops[1] = 0.5;
644 aStops[2] = 1.0;
646 aColors[0] = aEndColor;
647 aColors[1] = aStartColor;
648 aColors[2] = aEndColor;
650 else
652 aStops[0] = 0.0;
653 aStops[1] = 1.0;
655 aColors[0] = aStartColor;
656 aColors[1] = aEndColor;
659 const ::basegfx::B2DRectangle aBounds(
660 ::basegfx::tools::getRange(aDevicePoly) );
661 const ::basegfx::B2DVector aOffset(
662 rGradient.GetOfsX() / 100.0,
663 rGradient.GetOfsY() / 100.0);
664 double fRotation( rGradient.GetAngle() * M_PI / 1800.0 );
665 const double fBorder( rGradient.GetBorder() / 100.0 );
667 basegfx::B2DHomMatrix aRot90;
668 aRot90.rotate(M_PI_2);
670 basegfx::ODFGradientInfo aGradInfo;
671 rtl::OUString aGradientService;
672 switch( rGradient.GetStyle() )
674 case GradientStyle_LINEAR:
675 basegfx::tools::createLinearODFGradientInfo(aGradInfo,
676 aBounds,
677 nSteps,
678 fBorder,
679 fRotation);
680 // map odf to svg gradient orientation - x
681 // instead of y direction
682 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
683 aGradientService = "LinearGradient";
684 break;
686 case GradientStyle_AXIAL:
688 // Adapt the border so that it is suitable
689 // for the axial gradient. An axial
690 // gradient consists of two linear
691 // gradients. Each of those covers half
692 // of the total size. In order to
693 // compensate for the condensed display of
694 // the linear gradients, we have to
695 // enlarge the area taken up by the actual
696 // gradient (1-fBorder). After that we
697 // have to turn the result back into a
698 // border value, hence the second (left
699 // most 1-...
700 const double fAxialBorder (1-2*(1-fBorder));
701 basegfx::tools::createAxialODFGradientInfo(aGradInfo,
702 aBounds,
703 nSteps,
704 fAxialBorder,
705 fRotation);
706 // map odf to svg gradient orientation - x
707 // instead of y direction
708 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
710 // map odf axial gradient to 3-stop linear
711 // gradient - shift left by 0.5
712 basegfx::B2DHomMatrix aShift;
713 aShift.translate(-0.5,0);
714 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aShift;
716 aGradientService = "LinearGradient";
717 break;
720 case GradientStyle_RADIAL:
721 basegfx::tools::createRadialODFGradientInfo(aGradInfo,
722 aBounds,
723 aOffset,
724 nSteps,
725 fBorder);
726 aGradientService = "EllipticalGradient";
727 break;
729 case GradientStyle_ELLIPTICAL:
730 basegfx::tools::createEllipticalODFGradientInfo(aGradInfo,
731 aBounds,
732 aOffset,
733 nSteps,
734 fBorder,
735 fRotation);
736 aGradientService = "EllipticalGradient";
737 break;
739 case GradientStyle_SQUARE:
740 basegfx::tools::createSquareODFGradientInfo(aGradInfo,
741 aBounds,
742 aOffset,
743 nSteps,
744 fBorder,
745 fRotation);
746 aGradientService = "RectangularGradient";
747 break;
749 case GradientStyle_RECT:
750 basegfx::tools::createRectangularODFGradientInfo(aGradInfo,
751 aBounds,
752 aOffset,
753 nSteps,
754 fBorder,
755 fRotation);
756 aGradientService = "RectangularGradient";
757 break;
759 default:
760 ENSURE_OR_THROW( false,
761 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
762 break;
765 // As the texture coordinate space is relative to
766 // the polygon coordinate space (NOT to the
767 // polygon itself), move gradient to the start of
768 // the actual polygon. If we skip this, the
769 // gradient will always display at the origin, and
770 // not within the polygon bound (which might be
771 // miles away from the origin).
772 aGradInfo.maTextureTransform.translate( aBounds.getMinX(),
773 aBounds.getMinY() );
774 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
775 aGradInfo.maTextureTransform );
777 uno::Sequence<uno::Any> args(3);
778 beans::PropertyValue aProp;
779 aProp.Name = "Colors";
780 aProp.Value <<= aColors;
781 args[0] <<= aProp;
782 aProp.Name = "Stops";
783 aProp.Value <<= aStops;
784 args[1] <<= aProp;
785 aProp.Name = "AspectRatio";
786 aProp.Value <<= aGradInfo.mfAspectRatio;
787 args[2] <<= aProp;
789 aTexture.Gradient.set(
790 xFactory->createInstanceWithArguments(aGradientService,
791 args),
792 uno::UNO_QUERY);
793 if( aTexture.Gradient.is() )
795 ActionSharedPtr pPolyAction(
796 internal::PolyPolyActionFactory::createPolyPolyAction(
797 aDevicePoly,
798 rParms.mrCanvas,
799 rParms.mrStates.getState(),
800 aTexture ) );
802 if( pPolyAction )
804 maActions.push_back(
805 MtfAction(
806 pPolyAction,
807 rParms.mrCurrActionIndex ) );
809 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
812 // done, using native gradients
813 return;
818 // cannot currently use native canvas gradients, as a
819 // finite step size is given (this funny feature is not
820 // supported by the XCanvas API)
821 rParms.mrStates.pushState(PUSH_ALL);
823 if( !bIsPolygonRectangle )
825 // only clip, if given polygon is not a rectangle in
826 // the first place (the gradient is always limited to
827 // the given bound rect)
828 updateClipping(
829 aDevicePoly,
830 rParms,
831 true );
834 GDIMetaFile aTmpMtf;
835 rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(),
836 rGradient,
837 aTmpMtf );
839 createActions( aTmpMtf, rParms, bSubsettableActions );
841 rParms.mrStates.popState();
844 uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation,
845 const ::Font& rFont,
846 const ActionFactoryParameters& rParms ) const
848 rendering::FontRequest aFontRequest;
850 if( rParms.mrParms.maFontName.is_initialized() )
851 aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName;
852 else
853 aFontRequest.FontDescription.FamilyName = rFont.GetName();
855 aFontRequest.FontDescription.StyleName = rFont.GetStyleName();
857 aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
858 aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;
860 // TODO(F2): improve vclenum->panose conversion
861 aFontRequest.FontDescription.FontDescription.Weight =
862 rParms.mrParms.maFontWeight.is_initialized() ?
863 *rParms.mrParms.maFontWeight :
864 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
865 aFontRequest.FontDescription.FontDescription.Letterform =
866 rParms.mrParms.maFontLetterForm.is_initialized() ?
867 *rParms.mrParms.maFontLetterForm :
868 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
869 aFontRequest.FontDescription.FontDescription.Proportion =
870 rParms.mrParms.maFontProportion.is_initialized() ?
871 *rParms.mrParms.maFontProportion :
872 (rFont.GetPitch() == PITCH_FIXED)
873 ? rendering::PanoseProportion::MONO_SPACED
874 : rendering::PanoseProportion::ANYTHING;
876 LanguageType aLang = rFont.GetLanguage();
877 aFontRequest.Locale = MsLangId::convertLanguageToLocale(aLang, false);
879 // setup state-local text transformation,
880 // if the font be rotated
881 const short nFontAngle( rFont.GetOrientation() );
882 if( nFontAngle != 0 )
884 // set to unity transform rotated by font angle
885 const double nAngle( nFontAngle * (F_PI / 1800.0) );
886 o_rFontRotation = -nAngle;
888 else
890 o_rFontRotation = 0.0;
893 geometry::Matrix2D aFontMatrix;
894 ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
896 // TODO(F2): use correct scale direction, font
897 // height might be width or anything else
899 // TODO(Q3): This code smells of programming by
900 // coincidence (the next two if statements)
901 const ::Size rFontSizeLog( rFont.GetSize() );
902 const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
903 if( nFontWidthLog != 0 )
905 ::Font aTestFont = rFont;
906 aTestFont.SetWidth( 0 );
907 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
908 if( nNormalWidth != nFontWidthLog )
909 if( nNormalWidth )
910 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
913 // #i52608# apply map mode scale also to font matrix - an
914 // anisotrophic mapmode must be reflected in an
915 // anisotrophic font matrix scale.
916 const OutDevState& rState( rParms.mrStates.getState() );
917 if( !::basegfx::fTools::equal(
918 rState.mapModeTransform.get(0,0),
919 rState.mapModeTransform.get(1,1)) )
921 const double nScaleX( rState.mapModeTransform.get(0,0) );
922 const double nScaleY( rState.mapModeTransform.get(1,1) );
924 // note: no reason to check for division by zero, we
925 // always have the value closer (or equal) to zero as
926 // the nominator.
927 if( fabs(nScaleX) < fabs(nScaleY) )
928 aFontMatrix.m00 *= nScaleX / nScaleY;
929 else
930 aFontMatrix.m11 *= nScaleY / nScaleX;
932 aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
934 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
935 uno::Sequence< beans::PropertyValue >(),
936 aFontMatrix );
939 // create text effects such as shadow/relief/embossed
940 void ImplRenderer::createTextAction( const ::Point& rStartPoint,
941 const String rString,
942 int nIndex,
943 int nLength,
944 const sal_Int32* pCharWidths,
945 const ActionFactoryParameters& rParms,
946 bool bSubsettableActions )
948 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex,
949 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
951 if( !nLength )
952 return; // zero-length text, no visible output
954 const OutDevState& rState( rParms.mrStates.getState() );
956 // TODO(F2): implement all text effects
957 // if( rState.textAlignment ); // TODO(F2): NYI
959 ::Color aShadowColor( COL_AUTO );
960 ::Color aReliefColor( COL_AUTO );
961 ::Size aShadowOffset;
962 ::Size aReliefOffset;
964 uno::Reference<rendering::XColorSpace> xColorSpace(
965 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
967 if( rState.isTextEffectShadowSet )
969 // calculate shadow offset (similar to outdev3.cxx)
970 // TODO(F3): better match with outdev3.cxx
971 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
972 if( nShadowOffset < 1 )
973 nShadowOffset = 1;
975 aShadowOffset.setWidth( nShadowOffset );
976 aShadowOffset.setHeight( nShadowOffset );
978 // determine shadow color (from outdev3.cxx)
979 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
980 rState.textColor, xColorSpace );
981 bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
982 || (aTextColor.GetLuminance() < 8);
984 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
985 aShadowColor.SetTransparency( aTextColor.GetTransparency() );
988 if( rState.textReliefStyle )
990 // calculate relief offset (similar to outdev3.cxx)
991 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
992 nReliefOffset += nReliefOffset/2;
993 if( nReliefOffset < 1 )
994 nReliefOffset = 1;
996 if( rState.textReliefStyle == RELIEF_ENGRAVED )
997 nReliefOffset = -nReliefOffset;
999 aReliefOffset.setWidth( nReliefOffset );
1000 aReliefOffset.setHeight( nReliefOffset );
1002 // determine relief color (from outdev3.cxx)
1003 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
1004 rState.textColor, xColorSpace );
1006 aReliefColor = ::Color( COL_LIGHTGRAY );
1008 // we don't have a automatic color, so black is always
1009 // drawn on white (literally copied from
1010 // vcl/source/gdi/outdev3.cxx)
1011 if( aTextColor.GetColor() == COL_BLACK )
1013 aTextColor = ::Color( COL_WHITE );
1014 rParms.mrStates.getState().textColor =
1015 ::vcl::unotools::colorToDoubleSequence(
1016 aTextColor, xColorSpace );
1019 if( aTextColor.GetColor() == COL_WHITE )
1020 aReliefColor = ::Color( COL_BLACK );
1021 aReliefColor.SetTransparency( aTextColor.GetTransparency() );
1024 // create the actual text action
1025 ActionSharedPtr pTextAction(
1026 TextActionFactory::createTextAction(
1027 rStartPoint,
1028 aReliefOffset,
1029 aReliefColor,
1030 aShadowOffset,
1031 aShadowColor,
1032 rString,
1033 nIndex,
1034 nLength,
1035 pCharWidths,
1036 rParms.mrVDev,
1037 rParms.mrCanvas,
1038 rState,
1039 rParms.mrParms,
1040 bSubsettableActions ) );
1042 ActionSharedPtr pStrikeoutTextAction;
1044 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
1046 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
1048 xub_Unicode pChars[5];
1049 if ( rState.textStrikeoutStyle == STRIKEOUT_X )
1050 pChars[0] = 'X';
1051 else
1052 pChars[0] = '/';
1053 pChars[3]=pChars[2]=pChars[1]=pChars[0];
1055 long nStrikeoutWidth = nWidth;
1056 String aStrikeoutTest( pChars, 4 );
1058 if( aStrikeoutTest.Len() )
1060 nStrikeoutWidth = ( rParms.mrVDev.GetTextWidth( aStrikeoutTest ) + 2 ) / 4;
1061 aStrikeoutTest.Erase();
1063 if( nStrikeoutWidth <= 0 )
1064 nStrikeoutWidth = 1;
1067 long nMaxWidth = nStrikeoutWidth/2;
1068 if ( nMaxWidth < 2 )
1069 nMaxWidth = 2;
1070 nMaxWidth += nWidth + 1;
1072 long nFullStrikeoutWidth = 0;
1073 String aStrikeoutText( pChars, 0 );
1074 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
1075 aStrikeoutText += pChars[0];
1078 xub_StrLen nLen = aStrikeoutText.Len();
1080 if( nLen )
1082 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
1083 nStrikeoutWidth += nInterval;
1084 sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen];
1086 for ( int i = 0;i<nLen; i++)
1088 pStrikeoutCharWidths[i] = nStrikeoutWidth;
1091 for ( int i = 1;i< nLen; i++ )
1093 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
1096 sal_Int32 nStartPos = 0;
1098 pStrikeoutTextAction =
1099 TextActionFactory::createTextAction(
1100 rStartPoint,
1101 aReliefOffset,
1102 aReliefColor,
1103 aShadowOffset,
1104 aShadowColor,
1105 aStrikeoutText,
1106 nStartPos,
1107 aStrikeoutText.Len(),
1108 pStrikeoutCharWidths,
1109 rParms.mrVDev,
1110 rParms.mrCanvas,
1111 rState,
1112 rParms.mrParms,
1113 bSubsettableActions ) ;
1117 if( pTextAction )
1119 maActions.push_back(
1120 MtfAction(
1121 pTextAction,
1122 rParms.mrCurrActionIndex ) );
1124 if ( pStrikeoutTextAction )
1126 maActions.push_back(
1127 MtfAction(
1128 pStrikeoutTextAction,
1129 rParms.mrCurrActionIndex ) );
1132 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1136 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1137 const ActionFactoryParameters& rParms,
1138 bool bIntersect )
1140 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1141 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1143 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1144 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1146 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1147 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1149 if( !bIntersect ||
1150 (bEmptyClipRect && bEmptyClipPoly) )
1152 rState.clip = rClipPoly;
1154 else
1156 if( !bEmptyClipRect )
1158 // TODO(P3): Use Liang-Barsky polygon clip here,
1159 // after all, one object is just a rectangle!
1161 // convert rect to polygon beforehand, must revert
1162 // to general polygon clipping here.
1163 rState.clip = ::basegfx::B2DPolyPolygon(
1164 ::basegfx::tools::createPolygonFromRect(
1165 // #121100# VCL rectangular clips always
1166 // include one more pixel to the right
1167 // and the bottom
1168 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1169 rState.clipRect.Top(),
1170 rState.clipRect.Right()+1,
1171 rState.clipRect.Bottom()+1 ) ) );
1174 // AW: Simplified
1175 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1176 aClipPoly, rState.clip, true, false);
1179 // by now, our clip resides in the OutDevState::clip
1180 // poly-polygon.
1181 rState.clipRect.SetEmpty();
1183 if( rState.clip.count() == 0 )
1185 if( rState.clipRect.IsEmpty() )
1187 rState.xClipPoly.clear();
1189 else
1191 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1192 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1193 ::basegfx::B2DPolyPolygon(
1194 ::basegfx::tools::createPolygonFromRect(
1195 // #121100# VCL rectangular clips
1196 // always include one more pixel to
1197 // the right and the bottom
1198 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1199 rState.clipRect.Top(),
1200 rState.clipRect.Right()+1,
1201 rState.clipRect.Bottom()+1 ) ) ) );
1204 else
1206 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1207 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1208 rState.clip );
1212 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
1213 const ActionFactoryParameters& rParms,
1214 bool bIntersect )
1216 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1218 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1219 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1221 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1222 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1224 if( !bIntersect ||
1225 (bEmptyClipRect && bEmptyClipPoly) )
1227 rState.clipRect = rClipRect;
1228 rState.clip.clear();
1230 else if( bEmptyClipPoly )
1232 rState.clipRect.Intersection( rClipRect );
1233 rState.clip.clear();
1235 else
1237 // TODO(P3): Handle a fourth case here, when all clip
1238 // polygons are rectangular, once B2DMultiRange's
1239 // sweep line implementation is done.
1241 // general case: convert to polygon and clip
1242 // -----------------------------------------
1244 // convert rect to polygon beforehand, must revert
1245 // to general polygon clipping here.
1246 ::basegfx::B2DPolyPolygon aClipPoly(
1247 ::basegfx::tools::createPolygonFromRect(
1248 ::basegfx::B2DRectangle( rClipRect.Left(),
1249 rClipRect.Top(),
1250 rClipRect.Right(),
1251 rClipRect.Bottom() ) ) );
1253 rState.clipRect.SetEmpty();
1255 // AW: Simplified
1256 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1257 aClipPoly, rState.clip, true, false);
1260 if( rState.clip.count() == 0 )
1262 if( rState.clipRect.IsEmpty() )
1264 rState.xClipPoly.clear();
1266 else
1268 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1269 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1270 ::basegfx::B2DPolyPolygon(
1271 ::basegfx::tools::createPolygonFromRect(
1272 // #121100# VCL rectangular clips
1273 // always include one more pixel to
1274 // the right and the bottom
1275 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1276 rState.clipRect.Top(),
1277 rState.clipRect.Right()+1,
1278 rState.clipRect.Bottom()+1 ) ) ) );
1281 else
1283 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1284 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1285 rState.clip );
1289 bool ImplRenderer::createActions( GDIMetaFile& rMtf,
1290 const ActionFactoryParameters& rFactoryParms,
1291 bool bSubsettableActions )
1293 /* TODO(P2): interpret mtf-comments
1294 ================================
1296 - gradient fillings (do that via comments)
1298 - think about mapping. _If_ we do everything in logical
1299 coordinates (which would solve the probs for stroke
1300 widths and text offsets), then we would have to
1301 recalc scaling for every drawing operation. This is
1302 because the outdev map mode might change at any time.
1303 Also keep in mind, that, although we've double precision
1304 float arithmetic now, different offsets might still
1305 generate different roundings (aka
1306 'OutputDevice::SetPixelOffset())
1310 // alias common parameters
1311 VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1312 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1313 ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1314 const Parameters& rParms(rFactoryParms.mrParms);
1315 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1318 // Loop over every metaaction
1319 // ==========================
1320 MetaAction* pCurrAct;
1322 // TODO(P1): think about caching
1323 for( pCurrAct=rMtf.FirstAction();
1324 pCurrAct;
1325 pCurrAct = rMtf.NextAction() )
1327 // execute every action, to keep VDev state up-to-date
1328 // currently used only for
1329 // - the map mode
1330 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1331 // - SetFont to process font metric specific actions
1332 pCurrAct->Execute( &rVDev );
1334 EMFP_DEBUG(printf("MTF\trecord type: %x\n", pCurrAct->GetType()));
1336 switch( pCurrAct->GetType() )
1338 // ------------------------------------------------------------
1340 // In the first part of this monster-switch, we
1341 // handle all state-changing meta actions. These
1342 // are all handled locally.
1344 // ------------------------------------------------------------
1346 case META_PUSH_ACTION:
1348 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1349 rStates.pushState(pPushAction->GetFlags());
1351 break;
1353 case META_POP_ACTION:
1354 rStates.popState();
1355 break;
1357 case META_TEXTLANGUAGE_ACTION:
1358 // FALLTHROUGH intended
1359 case META_REFPOINT_ACTION:
1360 // handled via pCurrAct->Execute( &rVDev )
1361 break;
1363 case META_MAPMODE_ACTION:
1364 // modify current mapModeTransformation
1365 // transformation, such that subsequent
1366 // coordinates map correctly
1367 tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
1368 rVDev );
1369 break;
1371 // monitor clip regions, to assemble clip polygon on our own
1372 case META_CLIPREGION_ACTION:
1374 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1376 if( !pClipAction->IsClipping() )
1378 // clear clipping
1379 rStates.getState().clip.clear();
1381 else
1383 if( !pClipAction->GetRegion().HasPolyPolygon() )
1385 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1386 "region encountered, falling back to bounding box!" );
1388 // #121806# explicitly kept integer
1389 Rectangle aClipRect(
1390 rVDev.LogicToPixel(
1391 pClipAction->GetRegion().GetBoundRect() ) );
1393 // intersect current clip with given rect
1394 updateClipping(
1395 aClipRect,
1396 rFactoryParms,
1397 false );
1399 else
1401 // set new clip polygon (don't intersect
1402 // with old one, just set it)
1404 // #121806# explicitly kept integer
1405 updateClipping(
1406 rVDev.LogicToPixel(
1407 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1408 rFactoryParms,
1409 false );
1413 break;
1416 case META_ISECTRECTCLIPREGION_ACTION:
1418 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1420 // #121806# explicitly kept integer
1421 Rectangle aClipRect(
1422 rVDev.LogicToPixel( pClipAction->GetRect() ) );
1424 // intersect current clip with given rect
1425 updateClipping(
1426 aClipRect,
1427 rFactoryParms,
1428 true );
1430 break;
1433 case META_ISECTREGIONCLIPREGION_ACTION:
1435 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1437 if( !pClipAction->GetRegion().HasPolyPolygon() )
1439 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1440 "region encountered, falling back to bounding box!" );
1442 // #121806# explicitly kept integer
1443 Rectangle aClipRect(
1444 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1446 // intersect current clip with given rect
1447 updateClipping(
1448 aClipRect,
1449 rFactoryParms,
1450 true );
1452 else
1454 // intersect current clip with given clip polygon
1456 // #121806# explicitly kept integer
1457 updateClipping(
1458 rVDev.LogicToPixel(
1459 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1460 rFactoryParms,
1461 true );
1464 break;
1467 case META_MOVECLIPREGION_ACTION:
1468 // TODO(F2): NYI
1469 break;
1471 case META_LINECOLOR_ACTION:
1472 if( !rParms.maLineColor.is_initialized() )
1474 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1475 rStates.getState().isLineColorSet,
1476 rStates.getState().lineColor,
1477 rCanvas );
1479 break;
1481 case META_FILLCOLOR_ACTION:
1482 if( !rParms.maFillColor.is_initialized() )
1484 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1485 rStates.getState().isFillColorSet,
1486 rStates.getState().fillColor,
1487 rCanvas );
1489 break;
1491 case META_TEXTCOLOR_ACTION:
1493 if( !rParms.maTextColor.is_initialized() )
1495 // Text color is set unconditionally, thus, no
1496 // use of setStateColor here
1497 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1499 // force alpha part of color to
1500 // opaque. transparent painting is done
1501 // explicitly via META_TRANSPARENT_ACTION
1502 aColor.SetTransparency(0);
1504 rStates.getState().textColor =
1505 ::vcl::unotools::colorToDoubleSequence(
1506 aColor,
1507 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1510 break;
1512 case META_TEXTFILLCOLOR_ACTION:
1513 if( !rParms.maTextColor.is_initialized() )
1515 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1516 rStates.getState().isTextFillColorSet,
1517 rStates.getState().textFillColor,
1518 rCanvas );
1520 break;
1522 case META_TEXTLINECOLOR_ACTION:
1523 if( !rParms.maTextColor.is_initialized() )
1525 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1526 rStates.getState().isTextLineColorSet,
1527 rStates.getState().textLineColor,
1528 rCanvas );
1530 break;
1532 case META_TEXTALIGN_ACTION:
1534 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1535 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1537 rState.textReferencePoint = eTextAlign;
1539 break;
1541 case META_FONT_ACTION:
1543 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1544 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1546 rState.xFont = createFont( rState.fontRotation,
1547 rFont,
1548 rFactoryParms );
1550 // TODO(Q2): define and use appropriate enumeration types
1551 rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
1552 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
1553 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ?
1554 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1555 (sal_Int8)rFont.GetUnderline();
1556 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
1557 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
1558 rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False);
1559 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False);
1560 rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False);
1562 break;
1564 case META_RASTEROP_ACTION:
1565 // TODO(F2): NYI
1566 break;
1568 case META_LAYOUTMODE_ACTION:
1570 // TODO(F2): A lot is missing here
1571 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1572 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1573 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
1575 case TEXT_LAYOUT_BIDI_LTR:
1576 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1577 break;
1579 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
1580 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1581 break;
1583 case TEXT_LAYOUT_BIDI_RTL:
1584 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1585 break;
1587 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
1588 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1589 break;
1592 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1593 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1594 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1596 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1599 break;
1601 // ------------------------------------------------------------
1603 // In the second part of this monster-switch, we
1604 // handle all recursing meta actions. These are the
1605 // ones generating a metafile by themselves, which is
1606 // then processed by recursively calling this method.
1608 // ------------------------------------------------------------
1610 case META_GRADIENT_ACTION:
1612 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1613 createGradientAction( ::Polygon( pGradAct->GetRect() ),
1614 pGradAct->GetGradient(),
1615 rFactoryParms,
1616 true,
1617 bSubsettableActions );
1619 break;
1621 case META_HATCH_ACTION:
1623 // TODO(F2): use native Canvas hatches here
1624 GDIMetaFile aTmpMtf;
1626 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1627 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1628 aTmpMtf );
1629 createActions( aTmpMtf, rFactoryParms,
1630 bSubsettableActions );
1632 break;
1634 case META_EPS_ACTION:
1636 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
1637 const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
1639 // #121806# explicitly kept integer
1640 const Size aMtfSize( rSubstitute.GetPrefSize() );
1641 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1642 rSubstitute.GetPrefMapMode() ) );
1644 // #i44110# correct null-sized output - there
1645 // are metafiles which have zero size in at
1646 // least one dimension
1647 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1648 ::std::max( aMtfSizePixPre.Height(), 1L ) );
1650 // Setup local transform, such that the
1651 // metafile renders itself into the given
1652 // output rectangle
1653 rStates.pushState(PUSH_ALL);
1655 rVDev.Push();
1656 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1658 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1659 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1661 rStates.getState().transform.translate( rPos.X(),
1662 rPos.Y() );
1663 rStates.getState().transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1664 (double)rSize.Height() / aMtfSizePix.Height() );
1666 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1667 rFactoryParms,
1668 bSubsettableActions );
1670 rVDev.Pop();
1671 rStates.popState();
1673 break;
1675 // handle metafile comments, to retrieve
1676 // meta-information for gradients, fills and
1677 // strokes. May skip actions, and may recurse.
1678 case META_COMMENT_ACTION:
1680 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1682 // Handle gradients
1683 if (pAct->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")))
1685 MetaGradientExAction* pGradAction = NULL;
1686 bool bDone( false );
1687 while( !bDone &&
1688 (pCurrAct=rMtf.NextAction()) != NULL )
1690 switch( pCurrAct->GetType() )
1692 // extract gradient info
1693 case META_GRADIENTEX_ACTION:
1694 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1695 break;
1697 // skip broken-down rendering, output gradient when sequence is ended
1698 case META_COMMENT_ACTION:
1699 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")) )
1701 bDone = true;
1703 if( pGradAction )
1705 createGradientAction( pGradAction->GetPolyPolygon(),
1706 pGradAction->GetGradient(),
1707 rFactoryParms,
1708 false,
1709 bSubsettableActions );
1712 break;
1716 // TODO(P2): Handle drawing layer strokes, via
1717 // XPATHSTROKE_SEQ_BEGIN comment
1719 // Handle drawing layer fills
1720 else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_BEGIN")) )
1722 const sal_uInt8* pData = pAct->GetData();
1723 if ( pData )
1725 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1727 SvtGraphicFill aFill;
1728 aMemStm >> aFill;
1730 // TODO(P2): Also handle gradients and
1731 // hatches like this
1733 // only evaluate comment for pure
1734 // bitmap fills. If a transparency
1735 // gradient is involved (denoted by
1736 // the FloatTransparent action), take
1737 // the normal meta actions.
1738 if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1739 !isActionContained( rMtf,
1740 "XPATHFILL_SEQ_END",
1741 META_FLOATTRANSPARENT_ACTION ) )
1743 rendering::Texture aTexture;
1745 // TODO(F1): the SvtGraphicFill
1746 // can also transport metafiles
1747 // here, handle that case, too
1748 Graphic aGraphic;
1749 aFill.getGraphic( aGraphic );
1751 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1752 const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1754 ::SvtGraphicFill::Transform aTransform;
1755 aFill.getTransform( aTransform );
1757 ::basegfx::B2DHomMatrix aMatrix;
1759 // convert to basegfx matrix
1760 aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1761 aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1762 aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1763 aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1764 aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1765 aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1767 ::basegfx::B2DHomMatrix aScale;
1768 aScale.scale( aBmpSize.Width(),
1769 aBmpSize.Height() );
1771 // post-multiply with the bitmap
1772 // size (XCanvas' texture assumes
1773 // the given bitmap to be
1774 // normalized to [0,1]x[0,1]
1775 // rectangle)
1776 aMatrix = aMatrix * aScale;
1778 // pre-multiply with the
1779 // logic-to-pixel scale factor
1780 // (the metafile comment works in
1781 // logical coordinates).
1782 ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1783 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1784 rVDev );
1786 ::basegfx::unotools::affineMatrixFromHomMatrix(
1787 aTexture.AffineTransform,
1788 aMatrix );
1790 aTexture.Alpha = 1.0 - aFill.getTransparency();
1791 aTexture.Bitmap =
1792 ::vcl::unotools::xBitmapFromBitmapEx(
1793 rCanvas->getUNOCanvas()->getDevice(),
1794 aBmpEx );
1795 if( aFill.isTiling() )
1797 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1798 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1800 else
1802 aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1803 aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1806 ::PolyPolygon aPath;
1807 aFill.getPath( aPath );
1809 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1810 aPoly.transform( rStates.getState().mapModeTransform );
1811 ActionSharedPtr pPolyAction(
1812 internal::PolyPolyActionFactory::createPolyPolyAction(
1813 aPoly,
1814 rCanvas,
1815 rStates.getState(),
1816 aTexture ) );
1818 if( pPolyAction )
1820 maActions.push_back(
1821 MtfAction(
1822 pPolyAction,
1823 io_rCurrActionIndex ) );
1825 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1828 // skip broken-down render output
1829 skipContent( rMtf,
1830 "XPATHFILL_SEQ_END",
1831 io_rCurrActionIndex );
1835 // Handle drawing layer fills
1836 else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS")) ) {
1837 static int count = -1, limit = 0x7fffffff;
1838 if (count == -1) {
1839 count = 0;
1840 if (char *env = getenv ("EMF_PLUS_LIMIT")) {
1841 limit = atoi (env);
1842 EMFP_DEBUG (printf ("EMF+ records limit: %d\n", limit));
1845 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer, size: %u\n", (unsigned int)pAct->GetDataSize ()));
1846 if (count < limit)
1847 processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
1848 count ++;
1849 } else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS_HEADER_INFO")) ) {
1850 EMFP_DEBUG (printf ("EMF+ passed to canvas mtf renderer - header info, size: %u\n", (unsigned int)pAct->GetDataSize ()));
1852 SvMemoryStream rMF ((void*) pAct->GetData (), pAct->GetDataSize (), STREAM_READ);
1854 rMF >> nFrameLeft >> nFrameTop >> nFrameRight >> nFrameBottom;
1855 EMFP_DEBUG (printf ("EMF+ picture frame: %d,%d - %d,%d\n", (int)nFrameLeft, (int)nFrameTop, (int)nFrameRight, (int)nFrameBottom));
1856 rMF >> nPixX >> nPixY >> nMmX >> nMmY;
1857 EMFP_DEBUG (printf ("EMF+ ref device pixel size: %dx%d mm size: %dx%d\n", (int)nPixX, (int)nPixY, (int)nMmX, (int)nMmY));
1859 rMF >> aBaseTransform;
1860 //aWorldTransform.Set (aBaseTransform);
1863 break;
1865 // ------------------------------------------------------------
1867 // In the third part of this monster-switch, we
1868 // handle all 'acting' meta actions. These are all
1869 // processed by constructing function objects for
1870 // them, which will later ease caching.
1872 // ------------------------------------------------------------
1874 case META_POINT_ACTION:
1876 const OutDevState& rState( rStates.getState() );
1877 if( rState.lineColor.getLength() )
1879 ActionSharedPtr pPointAction(
1880 internal::PointActionFactory::createPointAction(
1881 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1882 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1883 rCanvas,
1884 rState ) );
1886 if( pPointAction )
1888 maActions.push_back(
1889 MtfAction(
1890 pPointAction,
1891 io_rCurrActionIndex ) );
1893 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1897 break;
1899 case META_PIXEL_ACTION:
1901 const OutDevState& rState( rStates.getState() );
1902 if( rState.lineColor.getLength() )
1904 ActionSharedPtr pPointAction(
1905 internal::PointActionFactory::createPointAction(
1906 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1907 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1908 rCanvas,
1909 rState,
1910 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1912 if( pPointAction )
1914 maActions.push_back(
1915 MtfAction(
1916 pPointAction,
1917 io_rCurrActionIndex ) );
1919 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1923 break;
1925 case META_LINE_ACTION:
1927 const OutDevState& rState( rStates.getState() );
1928 if( rState.lineColor.getLength() )
1930 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1932 const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1934 const ::basegfx::B2DPoint aStartPoint(
1935 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1936 const ::basegfx::B2DPoint aEndPoint(
1937 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1939 ActionSharedPtr pLineAction;
1941 if( rLineInfo.IsDefault() )
1943 // plain hair line
1944 pLineAction =
1945 internal::LineActionFactory::createLineAction(
1946 aStartPoint,
1947 aEndPoint,
1948 rCanvas,
1949 rState );
1951 if( pLineAction )
1953 maActions.push_back(
1954 MtfAction(
1955 pLineAction,
1956 io_rCurrActionIndex ) );
1958 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1961 else if( LINE_NONE != rLineInfo.GetStyle() )
1963 // 'thick' line
1964 rendering::StrokeAttributes aStrokeAttributes;
1966 setupStrokeAttributes( aStrokeAttributes,
1967 rFactoryParms,
1968 rLineInfo );
1970 // XCanvas can only stroke polygons,
1971 // not simple lines - thus, handle
1972 // this case via the polypolygon
1973 // action
1974 ::basegfx::B2DPolygon aPoly;
1975 aPoly.append( aStartPoint );
1976 aPoly.append( aEndPoint );
1977 pLineAction =
1978 internal::PolyPolyActionFactory::createPolyPolyAction(
1979 ::basegfx::B2DPolyPolygon( aPoly ),
1980 rCanvas, rState, aStrokeAttributes );
1982 if( pLineAction )
1984 maActions.push_back(
1985 MtfAction(
1986 pLineAction,
1987 io_rCurrActionIndex ) );
1989 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1992 // else: line style is default
1993 // (i.e. invisible), don't generate action
1996 break;
1998 case META_RECT_ACTION:
2000 const Rectangle& rRect(
2001 static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
2003 if( rRect.IsEmpty() )
2004 break;
2006 const OutDevState& rState( rStates.getState() );
2007 const ::basegfx::B2DPoint aTopLeftPixel(
2008 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
2009 const ::basegfx::B2DPoint aBottomRightPixel(
2010 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2011 // #121100# OutputDevice::DrawRect() fills
2012 // rectangles Apple-like, i.e. with one
2013 // additional pixel to the right and bottom.
2014 ::basegfx::B2DPoint(1,1) );
2016 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
2017 ::basegfx::B2DRange( aTopLeftPixel,
2018 aBottomRightPixel )),
2019 rFactoryParms );
2020 break;
2023 case META_ROUNDRECT_ACTION:
2025 const Rectangle& rRect(
2026 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
2028 if( rRect.IsEmpty() )
2029 break;
2031 ::basegfx::B2DPolygon aPoly(
2032 ::basegfx::tools::createPolygonFromRect(
2033 ::basegfx::B2DRange(
2034 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2035 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2036 ::basegfx::B2DPoint(1,1) ),
2037 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
2038 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
2039 aPoly.transform( rStates.getState().mapModeTransform );
2041 createFillAndStroke( aPoly,
2042 rFactoryParms );
2044 break;
2046 case META_ELLIPSE_ACTION:
2048 const Rectangle& rRect(
2049 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
2051 if( rRect.IsEmpty() )
2052 break;
2054 const ::basegfx::B2DRange aRange(
2055 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2056 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2057 ::basegfx::B2DPoint(1,1) );
2059 ::basegfx::B2DPolygon aPoly(
2060 ::basegfx::tools::createPolygonFromEllipse(
2061 aRange.getCenter(),
2062 aRange.getWidth(),
2063 aRange.getHeight() ));
2064 aPoly.transform( rStates.getState().mapModeTransform );
2066 createFillAndStroke( aPoly,
2067 rFactoryParms );
2069 break;
2071 case META_ARC_ACTION:
2073 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2074 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2075 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2076 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2077 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2078 aPoly.transform( rStates.getState().mapModeTransform );
2080 createFillAndStroke( aPoly,
2081 rFactoryParms );
2083 break;
2085 case META_PIE_ACTION:
2087 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2088 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2089 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2090 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2091 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2092 aPoly.transform( rStates.getState().mapModeTransform );
2094 createFillAndStroke( aPoly,
2095 rFactoryParms );
2097 break;
2099 case META_CHORD_ACTION:
2101 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2102 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2103 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2104 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2105 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2106 aPoly.transform( rStates.getState().mapModeTransform );
2108 createFillAndStroke( aPoly,
2109 rFactoryParms );
2111 break;
2113 case META_POLYLINE_ACTION:
2115 const OutDevState& rState( rStates.getState() );
2116 if( rState.lineColor.getLength() ||
2117 rState.fillColor.getLength() )
2119 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2121 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2122 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2123 aPoly.transform( rState.mapModeTransform );
2125 ActionSharedPtr pLineAction;
2127 if( rLineInfo.IsDefault() )
2129 // plain hair line polygon
2130 pLineAction =
2131 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2132 ::basegfx::B2DPolyPolygon(aPoly),
2133 rCanvas,
2134 rState );
2136 if( pLineAction )
2138 maActions.push_back(
2139 MtfAction(
2140 pLineAction,
2141 io_rCurrActionIndex ) );
2143 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2146 else if( LINE_NONE != rLineInfo.GetStyle() )
2148 // 'thick' line polygon
2149 rendering::StrokeAttributes aStrokeAttributes;
2151 setupStrokeAttributes( aStrokeAttributes,
2152 rFactoryParms,
2153 rLineInfo );
2155 pLineAction =
2156 internal::PolyPolyActionFactory::createPolyPolyAction(
2157 ::basegfx::B2DPolyPolygon(aPoly),
2158 rCanvas,
2159 rState,
2160 aStrokeAttributes ) ;
2162 if( pLineAction )
2164 maActions.push_back(
2165 MtfAction(
2166 pLineAction,
2167 io_rCurrActionIndex ) );
2169 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2172 // else: line style is default
2173 // (i.e. invisible), don't generate action
2176 break;
2178 case META_POLYGON_ACTION:
2180 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2181 aPoly.transform( rStates.getState().mapModeTransform );
2182 createFillAndStroke( aPoly,
2183 rFactoryParms );
2185 break;
2187 case META_POLYPOLYGON_ACTION:
2189 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2190 aPoly.transform( rStates.getState().mapModeTransform );
2191 createFillAndStroke( aPoly,
2192 rFactoryParms );
2194 break;
2196 case META_BMP_ACTION:
2198 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2200 ActionSharedPtr pBmpAction(
2201 internal::BitmapActionFactory::createBitmapAction(
2202 pAct->GetBitmap(),
2203 rStates.getState().mapModeTransform *
2204 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2205 rCanvas,
2206 rStates.getState() ) );
2208 if( pBmpAction )
2210 maActions.push_back(
2211 MtfAction(
2212 pBmpAction,
2213 io_rCurrActionIndex ) );
2215 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2218 break;
2220 case META_BMPSCALE_ACTION:
2222 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2224 ActionSharedPtr pBmpAction(
2225 internal::BitmapActionFactory::createBitmapAction(
2226 pAct->GetBitmap(),
2227 rStates.getState().mapModeTransform *
2228 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2229 rStates.getState().mapModeTransform *
2230 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2231 rCanvas,
2232 rStates.getState() ) );
2234 if( pBmpAction )
2236 maActions.push_back(
2237 MtfAction(
2238 pBmpAction,
2239 io_rCurrActionIndex ) );
2241 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2244 break;
2246 case META_BMPSCALEPART_ACTION:
2248 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2250 // crop bitmap to given source rectangle (no
2251 // need to copy and convert the whole bitmap)
2252 Bitmap aBmp( pAct->GetBitmap() );
2253 const Rectangle aCropRect( pAct->GetSrcPoint(),
2254 pAct->GetSrcSize() );
2255 aBmp.Crop( aCropRect );
2257 ActionSharedPtr pBmpAction(
2258 internal::BitmapActionFactory::createBitmapAction(
2259 aBmp,
2260 rStates.getState().mapModeTransform *
2261 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2262 rStates.getState().mapModeTransform *
2263 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2264 rCanvas,
2265 rStates.getState() ) );
2267 if( pBmpAction )
2269 maActions.push_back(
2270 MtfAction(
2271 pBmpAction,
2272 io_rCurrActionIndex ) );
2274 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2277 break;
2279 case META_BMPEX_ACTION:
2281 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2283 ActionSharedPtr pBmpAction(
2284 internal::BitmapActionFactory::createBitmapAction(
2285 pAct->GetBitmapEx(),
2286 rStates.getState().mapModeTransform *
2287 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2288 rCanvas,
2289 rStates.getState() ) );
2291 if( pBmpAction )
2293 maActions.push_back(
2294 MtfAction(
2295 pBmpAction,
2296 io_rCurrActionIndex ) );
2298 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2301 break;
2303 case META_BMPEXSCALE_ACTION:
2305 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2307 ActionSharedPtr pBmpAction(
2308 internal::BitmapActionFactory::createBitmapAction(
2309 pAct->GetBitmapEx(),
2310 rStates.getState().mapModeTransform *
2311 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2312 rStates.getState().mapModeTransform *
2313 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2314 rCanvas,
2315 rStates.getState() ) );
2317 if( pBmpAction )
2319 maActions.push_back(
2320 MtfAction(
2321 pBmpAction,
2322 io_rCurrActionIndex ) );
2324 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2327 break;
2329 case META_BMPEXSCALEPART_ACTION:
2331 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2333 // crop bitmap to given source rectangle (no
2334 // need to copy and convert the whole bitmap)
2335 BitmapEx aBmp( pAct->GetBitmapEx() );
2336 const Rectangle aCropRect( pAct->GetSrcPoint(),
2337 pAct->GetSrcSize() );
2338 aBmp.Crop( aCropRect );
2340 ActionSharedPtr pBmpAction(
2341 internal::BitmapActionFactory::createBitmapAction(
2342 aBmp,
2343 rStates.getState().mapModeTransform *
2344 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2345 rStates.getState().mapModeTransform *
2346 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2347 rCanvas,
2348 rStates.getState() ) );
2350 if( pBmpAction )
2352 maActions.push_back(
2353 MtfAction(
2354 pBmpAction,
2355 io_rCurrActionIndex ) );
2357 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2360 break;
2362 case META_MASK_ACTION:
2364 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2366 // create masked BitmapEx right here, as the
2367 // canvas does not provide equivalent
2368 // functionality
2369 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2370 pAct->GetColor() ));
2372 ActionSharedPtr pBmpAction(
2373 internal::BitmapActionFactory::createBitmapAction(
2374 aBmp,
2375 rStates.getState().mapModeTransform *
2376 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2377 rCanvas,
2378 rStates.getState() ) );
2380 if( pBmpAction )
2382 maActions.push_back(
2383 MtfAction(
2384 pBmpAction,
2385 io_rCurrActionIndex ) );
2387 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2390 break;
2392 case META_MASKSCALE_ACTION:
2394 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2396 // create masked BitmapEx right here, as the
2397 // canvas does not provide equivalent
2398 // functionality
2399 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2400 pAct->GetColor() ));
2402 ActionSharedPtr pBmpAction(
2403 internal::BitmapActionFactory::createBitmapAction(
2404 aBmp,
2405 rStates.getState().mapModeTransform *
2406 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2407 rStates.getState().mapModeTransform *
2408 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2409 rCanvas,
2410 rStates.getState() ) );
2412 if( pBmpAction )
2414 maActions.push_back(
2415 MtfAction(
2416 pBmpAction,
2417 io_rCurrActionIndex ) );
2419 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2422 break;
2424 case META_MASKSCALEPART_ACTION:
2426 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2428 // create masked BitmapEx right here, as the
2429 // canvas does not provide equivalent
2430 // functionality
2431 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2432 pAct->GetColor() ));
2434 // crop bitmap to given source rectangle (no
2435 // need to copy and convert the whole bitmap)
2436 const Rectangle aCropRect( pAct->GetSrcPoint(),
2437 pAct->GetSrcSize() );
2438 aBmp.Crop( aCropRect );
2440 ActionSharedPtr pBmpAction(
2441 internal::BitmapActionFactory::createBitmapAction(
2442 aBmp,
2443 rStates.getState().mapModeTransform *
2444 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2445 rStates.getState().mapModeTransform *
2446 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2447 rCanvas,
2448 rStates.getState() ) );
2450 if( pBmpAction )
2452 maActions.push_back(
2453 MtfAction(
2454 pBmpAction,
2455 io_rCurrActionIndex ) );
2457 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2460 break;
2462 case META_GRADIENTEX_ACTION:
2463 // TODO(F1): use native Canvas gradients here
2464 // action is ignored here, because redundant to META_GRADIENT_ACTION
2465 break;
2467 case META_WALLPAPER_ACTION:
2468 // TODO(F2): NYI
2469 break;
2471 case META_TRANSPARENT_ACTION:
2473 const OutDevState& rState( rStates.getState() );
2474 if( rState.lineColor.getLength() ||
2475 rState.fillColor.getLength() )
2477 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2478 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2479 aPoly.transform( rState.mapModeTransform );
2481 ActionSharedPtr pPolyAction(
2482 internal::PolyPolyActionFactory::createPolyPolyAction(
2483 aPoly,
2484 rCanvas,
2485 rState,
2486 pAct->GetTransparence() ) );
2488 if( pPolyAction )
2490 maActions.push_back(
2491 MtfAction(
2492 pPolyAction,
2493 io_rCurrActionIndex ) );
2495 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2499 break;
2501 case META_FLOATTRANSPARENT_ACTION:
2503 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2505 internal::MtfAutoPtr pMtf(
2506 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2508 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2509 internal::GradientAutoPtr pGradient(
2510 new Gradient( pAct->GetGradient() ) );
2512 DBG_TESTSOLARMUTEX();
2514 ActionSharedPtr pFloatTransAction(
2515 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2516 pMtf,
2517 pGradient,
2518 rParms,
2519 rStates.getState().mapModeTransform *
2520 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2521 rStates.getState().mapModeTransform *
2522 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2523 rCanvas,
2524 rStates.getState() ) );
2526 if( pFloatTransAction )
2528 maActions.push_back(
2529 MtfAction(
2530 pFloatTransAction,
2531 io_rCurrActionIndex ) );
2533 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2536 break;
2538 case META_TEXT_ACTION:
2540 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2541 XubString sText = XubString( pAct->GetText() );
2543 if( rVDev.GetDigitLanguage())
2544 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2546 createTextAction(
2547 pAct->GetPoint(),
2548 sText,
2549 pAct->GetIndex(),
2550 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2551 NULL,
2552 rFactoryParms,
2553 bSubsettableActions );
2555 break;
2557 case META_TEXTARRAY_ACTION:
2559 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2560 XubString sText = XubString( pAct->GetText() );
2562 if( rVDev.GetDigitLanguage())
2563 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2565 createTextAction(
2566 pAct->GetPoint(),
2567 sText,
2568 pAct->GetIndex(),
2569 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2570 pAct->GetDXArray(),
2571 rFactoryParms,
2572 bSubsettableActions );
2574 break;
2576 case META_TEXTLINE_ACTION:
2578 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2580 const OutDevState& rState( rStates.getState() );
2581 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2582 rVDev ) );
2583 const ::Point aStartPoint( pAct->GetStartPoint() );
2584 const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2585 ::basegfx::B2DSize(pAct->GetWidth(),
2586 0 ));
2588 ActionSharedPtr pPolyAction(
2589 PolyPolyActionFactory::createPolyPolyAction(
2590 tools::createTextLinesPolyPolygon(
2591 rState.mapModeTransform *
2592 ::basegfx::B2DPoint(
2593 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2594 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2595 aSize.getX(),
2596 tools::createTextLineInfo( rVDev,
2597 rState )),
2598 rCanvas,
2599 rState ) );
2601 if( pPolyAction.get() )
2603 maActions.push_back(
2604 MtfAction(
2605 pPolyAction,
2606 io_rCurrActionIndex ) );
2608 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2611 break;
2613 case META_TEXTRECT_ACTION:
2615 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2617 rStates.pushState(PUSH_ALL);
2619 // use the VDev to break up the text rect
2620 // action into readily formatted lines
2621 GDIMetaFile aTmpMtf;
2622 rVDev.AddTextRectActions( pAct->GetRect(),
2623 pAct->GetText(),
2624 pAct->GetStyle(),
2625 aTmpMtf );
2627 createActions( aTmpMtf,
2628 rFactoryParms,
2629 bSubsettableActions );
2631 rStates.popState();
2633 break;
2636 case META_STRETCHTEXT_ACTION:
2638 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2639 XubString sText = XubString( pAct->GetText() );
2641 if( rVDev.GetDigitLanguage())
2642 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2644 const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ?
2645 pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen() );
2647 // #i70897# Nothing to do, actually...
2648 if( nLen == 0 )
2649 break;
2651 // have to fit the text into the given
2652 // width. This is achieved by internally
2653 // generating a DX array, and uniformly
2654 // distributing the excess/insufficient width
2655 // to every logical character.
2656 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
2658 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2659 pAct->GetIndex(), pAct->GetLen() );
2661 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2663 // Last entry of pDXArray contains total width of the text
2664 sal_Int32* p=pDXArray.get();
2665 for( sal_uInt16 i=1; i<=nLen; ++i )
2667 // calc ratio for every array entry, to
2668 // distribute rounding errors 'evenly'
2669 // across the characters. Note that each
2670 // entry represents the 'end' position of
2671 // the corresponding character, thus, we
2672 // let i run from 1 to nLen.
2673 *p++ += (sal_Int32)i*nWidthDifference/nLen;
2676 createTextAction(
2677 pAct->GetPoint(),
2678 sText,
2679 pAct->GetIndex(),
2680 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2681 pDXArray.get(),
2682 rFactoryParms,
2683 bSubsettableActions );
2685 break;
2687 case META_RENDERGRAPHIC_ACTION:
2689 MetaRenderGraphicAction* pAct = static_cast<MetaRenderGraphicAction*>(pCurrAct);
2691 ActionSharedPtr pRenderGraphicAction(
2692 internal::RenderGraphicActionFactory::createRenderGraphicAction(
2693 pAct->GetRenderGraphic(),
2694 rStates.getState().mapModeTransform *
2695 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2696 rStates.getState().mapModeTransform *
2697 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2698 rCanvas,
2699 rStates.getState() ) );
2701 if( pRenderGraphicAction )
2703 maActions.push_back(
2704 MtfAction(
2705 pRenderGraphicAction,
2706 io_rCurrActionIndex ) );
2708 io_rCurrActionIndex += pRenderGraphicAction->getActionCount()-1;
2711 break;
2713 default:
2714 OSL_FAIL( "Unknown meta action type encountered" );
2715 break;
2718 // increment action index (each mtf action counts _at
2719 // least_ one. Some count for more, therefore,
2720 // io_rCurrActionIndex is sometimes incremented by
2721 // pAct->getActionCount()-1 above, the -1 being the
2722 // correction for the unconditional increment here).
2723 ++io_rCurrActionIndex;
2726 return true;
2730 namespace
2732 class ActionRenderer
2734 public:
2735 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2736 maTransformation( rTransformation ),
2737 mbRet( true )
2741 bool result() const
2743 return mbRet;
2746 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2748 // ANDing the result. We want to fail if at least
2749 // one action failed.
2750 mbRet &= rAction.mpAction->render( maTransformation );
2753 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2754 const Action::Subset& rSubset )
2756 // ANDing the result. We want to fail if at least
2757 // one action failed.
2758 mbRet &= rAction.mpAction->renderSubset( maTransformation,
2759 rSubset );
2762 private:
2763 ::basegfx::B2DHomMatrix maTransformation;
2764 bool mbRet;
2767 class AreaQuery
2769 public:
2770 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2771 maTransformation( rTransformation ),
2772 maBounds()
2776 bool result() const
2778 return true; // nothing can fail here
2781 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2783 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2786 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2787 const Action::Subset& rSubset )
2789 maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2790 rSubset ) );
2793 ::basegfx::B2DRange getBounds() const
2795 return maBounds;
2798 private:
2799 ::basegfx::B2DHomMatrix maTransformation;
2800 ::basegfx::B2DRange maBounds;
2803 // Doing that via inline class. Compilers tend to not inline free
2804 // functions.
2805 struct UpperBoundActionIndexComparator
2807 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2808 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2810 const sal_Int32 nLHSCount( rLHS.mpAction ?
2811 rLHS.mpAction->getActionCount() : 0 );
2812 const sal_Int32 nRHSCount( rRHS.mpAction ?
2813 rRHS.mpAction->getActionCount() : 0 );
2815 // compare end of action range, to have an action selected
2816 // by lower_bound even if the requested index points in
2817 // the middle of the action's range
2818 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2822 /** Algorithm to apply given functor to a subset range
2824 @tpl Functor
2826 Functor to call for each element of the subset
2827 range. Must provide the following method signatures:
2828 bool result() (returning false if operation failed)
2831 template< typename Functor > bool
2832 forSubsetRange( Functor& rFunctor,
2833 ImplRenderer::ActionVector::const_iterator aRangeBegin,
2834 ImplRenderer::ActionVector::const_iterator aRangeEnd,
2835 sal_Int32 nStartIndex,
2836 sal_Int32 nEndIndex,
2837 const ImplRenderer::ActionVector::const_iterator& rEnd )
2839 if( aRangeBegin == aRangeEnd )
2841 // only a single action. Setup subset, and call functor
2842 Action::Subset aSubset;
2843 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2844 nStartIndex - aRangeBegin->mnOrigIndex );
2845 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2846 nEndIndex - aRangeBegin->mnOrigIndex );
2848 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2849 "ImplRenderer::forSubsetRange(): Invalid indices" );
2851 rFunctor( *aRangeBegin, aSubset );
2853 else
2855 // more than one action.
2857 // render partial first, full intermediate, and
2858 // partial last action
2859 Action::Subset aSubset;
2860 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2861 nStartIndex - aRangeBegin->mnOrigIndex );
2862 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2864 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2865 "ImplRenderer::forSubsetRange(): Invalid indices" );
2867 rFunctor( *aRangeBegin, aSubset );
2869 // first action rendered, skip to next
2870 ++aRangeBegin;
2872 // render full middle actions
2873 while( aRangeBegin != aRangeEnd )
2874 rFunctor( *aRangeBegin++ );
2876 if( aRangeEnd == rEnd ||
2877 aRangeEnd->mnOrigIndex > nEndIndex )
2879 // aRangeEnd denotes end of action vector,
2881 // or
2883 // nEndIndex references something _after_
2884 // aRangeBegin, but _before_ aRangeEnd
2886 // either way: no partial action left
2887 return rFunctor.result();
2890 aSubset.mnSubsetBegin = 0;
2891 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2893 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2894 "ImplRenderer::forSubsetRange(): Invalid indices" );
2896 rFunctor( *aRangeEnd, aSubset );
2899 return rFunctor.result();
2903 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2904 sal_Int32& io_rEndIndex,
2905 ActionVector::const_iterator& o_rRangeBegin,
2906 ActionVector::const_iterator& o_rRangeEnd ) const
2908 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2909 "ImplRenderer::getSubsetIndices(): invalid action range" );
2911 ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2912 "ImplRenderer::getSubsetIndices(): no actions to render" );
2914 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2915 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2916 maActions.back().mpAction->getActionCount() );
2918 // clip given range to permissible values (there might be
2919 // ranges before and behind the valid indices)
2920 io_rStartIndex = ::std::max( nMinActionIndex,
2921 io_rStartIndex );
2922 io_rEndIndex = ::std::min( nMaxActionIndex,
2923 io_rEndIndex );
2925 if( io_rStartIndex == io_rEndIndex ||
2926 io_rStartIndex > io_rEndIndex )
2928 // empty range, don't render anything. The second
2929 // condition e.g. happens if the requested range lies
2930 // fully before or behind the valid action indices.
2931 return false;
2935 const ActionVector::const_iterator aBegin( maActions.begin() );
2936 const ActionVector::const_iterator aEnd( maActions.end() );
2939 // find start and end action
2940 // =========================
2941 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2942 MtfAction( ActionSharedPtr(), io_rStartIndex ),
2943 UpperBoundActionIndexComparator() );
2944 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
2945 MtfAction( ActionSharedPtr(), io_rEndIndex ),
2946 UpperBoundActionIndexComparator() );
2947 return true;
2951 // Public methods
2952 // ====================================================================
2954 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
2955 const GDIMetaFile& rMtf,
2956 const Parameters& rParams ) :
2957 CanvasGraphicHelper( rCanvas ),
2958 maActions()
2960 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2962 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2963 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2964 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2965 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2967 // make sure canvas and graphic device are valid; action
2968 // creation don't check that every time
2969 if( rCanvas.get() == NULL ||
2970 !rCanvas->getUNOCanvas().is() ||
2971 !rCanvas->getUNOCanvas()->getDevice().is() )
2973 // leave actions empty
2974 return;
2977 VectorOfOutDevStates aStateStack;
2979 VirtualDevice aVDev;
2980 aVDev.EnableOutput( sal_False );
2982 // Setup VDev for state tracking and mapping
2983 // =========================================
2985 aVDev.SetMapMode( rMtf.GetPrefMapMode() );
2987 const Size aMtfSize( rMtf.GetPrefSize() );
2988 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
2989 rMtf.GetPrefMapMode() ) );
2990 const Point aEmptyPt;
2991 const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) );
2993 // #i44110# correct null-sized output - there are shapes
2994 // which have zero size in at least one dimension
2995 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2996 ::std::max( aMtfSizePixPre.Height(), 1L ) );
2998 sal_Int32 nCurrActions(0);
2999 ActionFactoryParameters aParms(aStateStack,
3000 rCanvas,
3001 aVDev,
3002 rParams,
3003 nCurrActions );
3005 // init state stack
3006 aStateStack.clearStateStack();
3008 // Setup local state, such that the metafile renders
3009 // itself into a one-by-one square at the origin for
3010 // identity view and render transformations
3011 aStateStack.getState().transform.scale( 1.0 / aMtfSizePix.Width(),
3012 1.0 / aMtfSizePix.Height() );
3014 tools::calcLogic2PixelAffineTransform( aStateStack.getState().mapModeTransform,
3015 aVDev );
3017 ColorSharedPtr pColor( getCanvas()->createColor() );
3020 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
3021 // setup default text color to black
3022 rState.textColor =
3023 rState.textFillColor =
3024 rState.textLineColor = pColor->getDeviceColor( 0x000000FF );
3027 // apply overrides from the Parameters struct
3028 if( rParams.maFillColor.is_initialized() )
3030 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
3031 rState.isFillColorSet = true;
3032 rState.fillColor = pColor->getDeviceColor( *rParams.maFillColor );
3034 if( rParams.maLineColor.is_initialized() )
3036 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
3037 rState.isLineColorSet = true;
3038 rState.lineColor = pColor->getDeviceColor( *rParams.maLineColor );
3040 if( rParams.maTextColor.is_initialized() )
3042 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
3043 rState.isTextFillColorSet = true;
3044 rState.isTextLineColorSet = true;
3045 rState.textColor =
3046 rState.textFillColor =
3047 rState.textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
3049 if( rParams.maFontName.is_initialized() ||
3050 rParams.maFontWeight.is_initialized() ||
3051 rParams.maFontLetterForm.is_initialized() ||
3052 rParams.maFontUnderline.is_initialized() ||
3053 rParams.maFontProportion.is_initialized() )
3055 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
3057 rState.xFont = createFont( rState.fontRotation,
3058 ::Font(), // default font
3059 aParms );
3062 /* EMF+ */
3063 memset (aObjects, 0, sizeof (aObjects));
3064 mbMultipart = false;
3066 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
3067 // we're
3068 // changing
3069 // the
3070 // current
3071 // action
3072 // in
3073 // createActions!
3074 aParms,
3075 true // TODO(P1): make subsettability configurable
3079 ImplRenderer::~ImplRenderer()
3083 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
3084 sal_Int32 nEndIndex ) const
3086 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3088 ActionVector::const_iterator aRangeBegin;
3089 ActionVector::const_iterator aRangeEnd;
3093 if( !getSubsetIndices( nStartIndex, nEndIndex,
3094 aRangeBegin, aRangeEnd ) )
3095 return true; // nothing to render (but _that_ was successful)
3097 // now, aRangeBegin references the action in which the
3098 // subset rendering must start, and aRangeEnd references
3099 // the action in which the subset rendering must end (it
3100 // might also end right at the start of the referenced
3101 // action, such that zero of that action needs to be
3102 // rendered).
3105 // render subset of actions
3106 // ========================
3108 ::basegfx::B2DHomMatrix aMatrix;
3109 ::canvas::tools::getRenderStateTransform( aMatrix,
3110 getRenderState() );
3112 ActionRenderer aRenderer( aMatrix );
3114 return forSubsetRange( aRenderer,
3115 aRangeBegin,
3116 aRangeEnd,
3117 nStartIndex,
3118 nEndIndex,
3119 maActions.end() );
3121 catch( uno::Exception& )
3123 OSL_FAIL( rtl::OUStringToOString(
3124 comphelper::anyToString( cppu::getCaughtException() ),
3125 RTL_TEXTENCODING_UTF8 ).getStr() );
3127 // convert error to return value
3128 return false;
3132 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3133 sal_Int32 nEndIndex ) const
3135 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3137 ActionVector::const_iterator aRangeBegin;
3138 ActionVector::const_iterator aRangeEnd;
3140 if( !getSubsetIndices( nStartIndex, nEndIndex,
3141 aRangeBegin, aRangeEnd ) )
3142 return ::basegfx::B2DRange(); // nothing to render -> empty range
3144 // now, aRangeBegin references the action in which the
3145 // subset querying must start, and aRangeEnd references
3146 // the action in which the subset querying must end (it
3147 // might also end right at the start of the referenced
3148 // action, such that zero of that action needs to be
3149 // queried).
3152 // query bounds for subset of actions
3153 // ==================================
3155 ::basegfx::B2DHomMatrix aMatrix;
3156 ::canvas::tools::getRenderStateTransform( aMatrix,
3157 getRenderState() );
3159 AreaQuery aQuery( aMatrix );
3160 forSubsetRange( aQuery,
3161 aRangeBegin,
3162 aRangeEnd,
3163 nStartIndex,
3164 nEndIndex,
3165 maActions.end() );
3167 return aQuery.getBounds();
3170 bool ImplRenderer::draw() const
3172 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" );
3174 ::basegfx::B2DHomMatrix aMatrix;
3175 ::canvas::tools::getRenderStateTransform( aMatrix,
3176 getRenderState() );
3180 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3182 catch( uno::Exception& )
3184 OSL_FAIL( rtl::OUStringToOString(
3185 comphelper::anyToString( cppu::getCaughtException() ),
3186 RTL_TEXTENCODING_UTF8 ).getStr() );
3188 return false;
3194 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */