update credits
[LibreOffice.git] / cppcanvas / source / mtfrenderer / implrenderer.cxx
blob43094a3f09f9832b9c27a959fa9dea1ba71d0479
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <canvas/debug.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <canvas/verbosetrace.hxx>
23 #include <osl/mutex.hxx>
24 #include <vcl/svapp.hxx>
25 #include <comphelper/sequence.hxx>
26 #include <comphelper/anytostring.hxx>
27 #include <cppuhelper/exc_hlp.hxx>
28 #include <cppcanvas/canvas.hxx>
29 #include <com/sun/star/rendering/XGraphicDevice.hpp>
30 #include <com/sun/star/rendering/TexturingMode.hpp>
31 #include <com/sun/star/uno/Sequence.hxx>
32 #include <com/sun/star/geometry/RealPoint2D.hpp>
33 #include <com/sun/star/rendering/PanoseProportion.hpp>
34 #include <com/sun/star/rendering/ViewState.hpp>
35 #include <com/sun/star/rendering/RenderState.hpp>
36 #include <com/sun/star/rendering/XCanvasFont.hpp>
37 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
38 #include <com/sun/star/rendering/XCanvas.hpp>
39 #include <com/sun/star/rendering/PathCapType.hpp>
40 #include <com/sun/star/rendering/PathJoinType.hpp>
41 #include <basegfx/tools/canvastools.hxx>
42 #include <basegfx/tools/gradienttools.hxx>
43 #include <basegfx/numeric/ftools.hxx>
44 #include <basegfx/polygon/b2dpolypolygontools.hxx>
45 #include <basegfx/polygon/b2dpolygontools.hxx>
46 #include <basegfx/polygon/b2dpolygon.hxx>
47 #include <basegfx/polygon/b2dpolypolygon.hxx>
48 #include <basegfx/matrix/b2dhommatrix.hxx>
49 #include <basegfx/vector/b2dsize.hxx>
50 #include <basegfx/range/b2drectangle.hxx>
51 #include <basegfx/point/b2dpoint.hxx>
52 #include <basegfx/tuple/b2dtuple.hxx>
53 #include <basegfx/polygon/b2dpolygonclipper.hxx>
54 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
55 #include <canvas/canvastools.hxx>
56 #include <vcl/canvastools.hxx>
57 #include <vcl/salbtype.hxx>
58 #include <vcl/gdimtf.hxx>
59 #include <vcl/metaact.hxx>
60 #include <vcl/virdev.hxx>
61 #include <vcl/metric.hxx>
62 #include <vcl/graphictools.hxx>
63 #include <tools/poly.hxx>
64 #include <i18nlangtag/languagetag.hxx>
65 #include <implrenderer.hxx>
66 #include <tools.hxx>
67 #include <outdevstate.hxx>
68 #include <action.hxx>
69 #include <bitmapaction.hxx>
70 #include <lineaction.hxx>
71 #include <pointaction.hxx>
72 #include <polypolyaction.hxx>
73 #include <textaction.hxx>
74 #include <transparencygroupaction.hxx>
75 #include <vector>
76 #include <algorithm>
77 #include <iterator>
78 #include <boost/scoped_array.hpp>
79 #include "mtftools.hxx"
80 #include "outdevstate.hxx"
81 #include <basegfx/matrix/b2dhommatrixtools.hxx>
83 using namespace ::com::sun::star;
86 // free support functions
87 // ======================
88 namespace
90 template < class MetaActionType > void setStateColor( MetaActionType* pAct,
91 bool& rIsColorSet,
92 uno::Sequence< double >& rColorSequence,
93 const cppcanvas::CanvasSharedPtr& rCanvas )
95 // set rIsColorSet and check for true at the same time
96 if( (rIsColorSet=pAct->IsSetting()) != false )
98 ::Color aColor( pAct->GetColor() );
100 // force alpha part of color to
101 // opaque. transparent painting is done
102 // explicitly via META_TRANSPARENT_ACTION
103 aColor.SetTransparency(0);
104 //aColor.SetTransparency(128);
106 rColorSequence = ::vcl::unotools::colorToDoubleSequence(
107 aColor,
108 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
112 void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes,
113 const ::cppcanvas::internal::ActionFactoryParameters& rParms,
114 const LineInfo& rLineInfo )
116 const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
117 o_rStrokeAttributes.StrokeWidth =
118 (rParms.mrStates.getState().mapModeTransform * aWidth).getX();
120 // setup reasonable defaults
121 o_rStrokeAttributes.MiterLimit = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
122 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
123 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
125 switch(rLineInfo.GetLineJoin())
127 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
128 o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE;
129 break;
130 case basegfx::B2DLINEJOIN_BEVEL:
131 o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL;
132 break;
133 case basegfx::B2DLINEJOIN_MITER:
134 o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
135 break;
136 case basegfx::B2DLINEJOIN_ROUND:
137 o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND;
138 break;
141 switch(rLineInfo.GetLineCap())
143 default: /* com::sun::star::drawing::LineCap_BUTT */
145 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
146 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
147 break;
149 case com::sun::star::drawing::LineCap_ROUND:
151 o_rStrokeAttributes.StartCapType = rendering::PathCapType::ROUND;
152 o_rStrokeAttributes.EndCapType = rendering::PathCapType::ROUND;
153 break;
155 case com::sun::star::drawing::LineCap_SQUARE:
157 o_rStrokeAttributes.StartCapType = rendering::PathCapType::SQUARE;
158 o_rStrokeAttributes.EndCapType = rendering::PathCapType::SQUARE;
159 break;
163 if( LINE_DASH == rLineInfo.GetStyle() )
165 const ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
167 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
169 // interpret dash info only if explicitly enabled as
170 // style
171 const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
172 const double nDistance( (rState.mapModeTransform * aDistance).getX() );
174 const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
175 const double nDashLen( (rState.mapModeTransform * aDashLen).getX() );
177 const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
178 const double nDotLen( (rState.mapModeTransform * aDotLen).getX() );
180 const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
181 2*rLineInfo.GetDotCount() );
183 o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
184 double* pDashArray = o_rStrokeAttributes.DashArray.getArray();
187 // iteratively fill dash array, first with dashs, then
188 // with dots.
189 // ===================================================
191 sal_Int32 nCurrEntry=0;
193 for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
195 pDashArray[nCurrEntry++] = nDashLen;
196 pDashArray[nCurrEntry++] = nDistance;
198 for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
200 pDashArray[nCurrEntry++] = nDotLen;
201 pDashArray[nCurrEntry++] = nDistance;
207 /** Create masked BitmapEx, where the white areas of rBitmap are
208 transparent, and the other appear in rMaskColor.
210 BitmapEx createMaskBmpEx( const Bitmap& rBitmap,
211 const ::Color& rMaskColor )
213 const ::Color aWhite( COL_WHITE );
214 BitmapPalette aBiLevelPalette(2);
215 aBiLevelPalette[0] = aWhite;
216 aBiLevelPalette[1] = rMaskColor;
218 Bitmap aMask( rBitmap.CreateMask( aWhite ));
219 Bitmap aSolid( rBitmap.GetSizePixel(),
221 &aBiLevelPalette );
222 aSolid.Erase( rMaskColor );
224 return BitmapEx( aSolid, aMask );
227 /** Shameless rip from vcl/source/gdi/outdev3.cxx
229 Should consolidate, into something like basetxt...
231 sal_Unicode getLocalizedChar( sal_Unicode nChar, LanguageType eLang )
233 // currently only conversion from ASCII digits is interesting
234 if( (nChar < '0') || ('9' < nChar) )
235 return nChar;
237 sal_Unicode nOffset(0);
238 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
239 // CAVEAT! To some like Mongolian MS assigned the same primary language
240 // although the script type is different!
241 switch( eLang & LANGUAGE_MASK_PRIMARY )
243 default:
244 break;
246 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
247 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
248 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
249 nOffset = 0x0660 - '0'; // arabic/persian/urdu
250 break;
251 case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY:
252 nOffset = 0x09E6 - '0'; // bengali
253 break;
254 case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY:
255 nOffset = 0x1040 - '0'; // burmese
256 break;
257 case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY:
258 nOffset = 0x0966 - '0'; // devanagari
259 break;
260 case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY:
261 nOffset = 0x0AE6 - '0'; // gujarati
262 break;
263 case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY:
264 nOffset = 0x0CE6 - '0'; // kannada
265 break;
266 case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY:
267 nOffset = 0x17E0 - '0'; // khmer
268 break;
269 case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY:
270 nOffset = 0x0ED0 - '0'; // lao
271 break;
272 case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY:
273 nOffset = 0x0D66 - '0'; // malayalam
274 break;
275 case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY:
276 if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
277 nOffset = 0x1810 - '0'; // mongolian
278 else
279 nOffset = 0; // mongolian cyrillic
280 break;
281 case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY:
282 nOffset = 0x0B66 - '0'; // oriya
283 break;
284 case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY:
285 nOffset = 0x0BE7 - '0'; // tamil
286 break;
287 case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY:
288 nOffset = 0x0C66 - '0'; // telugu
289 break;
290 case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY:
291 nOffset = 0x0E50 - '0'; // thai
292 break;
293 case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY:
294 nOffset = 0x0F20 - '0'; // tibetan
295 break;
298 nChar = sal::static_int_cast<sal_Unicode>(nChar + nOffset);
299 return nChar;
302 void convertToLocalizedNumerals( XubString& rStr,
303 LanguageType eTextLanguage )
305 const sal_Unicode* pBase = rStr.GetBuffer();
306 const sal_Unicode* pBegin = pBase + 0;
307 const xub_StrLen nEndIndex = rStr.Len();
308 const sal_Unicode* pEnd = pBase + nEndIndex;
310 for( ; pBegin < pEnd; ++pBegin )
312 // TODO: are there non-digit localizations?
313 if( (*pBegin >= '0') && (*pBegin <= '9') )
315 // translate characters to local preference
316 sal_Unicode cChar = getLocalizedChar( *pBegin, eTextLanguage );
317 if( cChar != *pBegin )
318 rStr.SetChar( sal::static_int_cast<sal_uInt16>(pBegin - pBase), cChar );
325 namespace cppcanvas
327 namespace internal
329 // state stack manipulators
330 // ------------------------
331 void VectorOfOutDevStates::clearStateStack()
333 m_aStates.clear();
334 const OutDevState aDefaultState;
335 m_aStates.push_back(aDefaultState);
338 OutDevState& VectorOfOutDevStates::getState()
340 return m_aStates.back();
343 const OutDevState& VectorOfOutDevStates::getState() const
345 return m_aStates.back();
348 void VectorOfOutDevStates::pushState(sal_uInt16 nFlags)
350 m_aStates.push_back( getState() );
351 getState().pushFlags = nFlags;
354 void VectorOfOutDevStates::popState()
356 if( getState().pushFlags != PUSH_ALL )
358 // a state is pushed which is incomplete, i.e. does not
359 // restore everything to the previous stack level when
360 // popped.
361 // That means, we take the old state, and restore every
362 // OutDevState member whose flag is set, from the new to the
363 // old state. Then the new state gets overwritten by the
364 // calculated state
366 // preset to-be-calculated new state with old state
367 OutDevState aCalculatedNewState( getState() );
369 // selectively copy to-be-restored content over saved old
370 // state
371 m_aStates.pop_back();
373 const OutDevState& rNewState( getState() );
375 if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) )
377 aCalculatedNewState.lineColor = rNewState.lineColor;
378 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
381 if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) )
383 aCalculatedNewState.fillColor = rNewState.fillColor;
384 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
387 if( (aCalculatedNewState.pushFlags & PUSH_FONT) )
389 aCalculatedNewState.xFont = rNewState.xFont;
390 aCalculatedNewState.fontRotation = rNewState.fontRotation;
391 aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle;
392 aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle;
393 aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle;
394 aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle;
395 aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle;
396 aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet;
397 aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet;
398 aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet;
401 if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) )
403 aCalculatedNewState.textColor = rNewState.textColor;
406 if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) )
408 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
411 if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) )
413 aCalculatedNewState.clip = rNewState.clip;
414 aCalculatedNewState.clipRect = rNewState.clipRect;
415 aCalculatedNewState.xClipPoly = rNewState.xClipPoly;
418 // TODO(F2): Raster ops NYI
419 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
420 // {
421 // }
423 if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) )
425 aCalculatedNewState.textFillColor = rNewState.textFillColor;
426 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
429 if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) )
431 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
434 // TODO(F1): Refpoint handling NYI
435 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
436 // {
437 // }
439 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) )
441 aCalculatedNewState.textLineColor = rNewState.textLineColor;
442 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
445 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) )
447 aCalculatedNewState.textAlignment = rNewState.textAlignment;
448 aCalculatedNewState.textDirection = rNewState.textDirection;
451 // TODO(F2): Text language handling NYI
452 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
453 // {
454 // }
456 // always copy push mode
457 aCalculatedNewState.pushFlags = rNewState.pushFlags;
459 // flush to stack
460 getState() = aCalculatedNewState;
462 else
464 m_aStates.pop_back();
468 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
469 const ActionFactoryParameters& rParms )
471 const OutDevState& rState( rParms.mrStates.getState() );
472 if( (!rState.isLineColorSet &&
473 !rState.isFillColorSet) ||
474 (rState.lineColor.getLength() == 0 &&
475 rState.fillColor.getLength() == 0) )
477 return false;
480 ActionSharedPtr pPolyAction(
481 internal::PolyPolyActionFactory::createPolyPolyAction(
482 rPolyPoly, rParms.mrCanvas, rState ) );
484 if( pPolyAction )
486 maActions.push_back(
487 MtfAction(
488 pPolyAction,
489 rParms.mrCurrActionIndex ) );
491 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
494 return true;
497 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly,
498 const ActionFactoryParameters& rParms )
500 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
501 rParms );
504 void ImplRenderer::skipContent( GDIMetaFile& rMtf,
505 const char* pCommentString,
506 sal_Int32& io_rCurrActionIndex ) const
508 ENSURE_OR_THROW( pCommentString,
509 "ImplRenderer::skipContent(): NULL string given" );
511 MetaAction* pCurrAct;
512 while( (pCurrAct=rMtf.NextAction()) != NULL )
514 // increment action index, we've skipped an action.
515 ++io_rCurrActionIndex;
517 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
518 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
519 pCommentString) )
521 // requested comment found, done
522 return;
526 // EOF
527 return;
530 bool ImplRenderer::isActionContained( GDIMetaFile& rMtf,
531 const char* pCommentString,
532 sal_uInt16 nType ) const
534 ENSURE_OR_THROW( pCommentString,
535 "ImplRenderer::isActionContained(): NULL string given" );
537 bool bRet( false );
539 // at least _one_ call to GDIMetaFile::NextAction() is
540 // executed
541 sal_uIntPtr nPos( 1 );
543 MetaAction* pCurrAct;
544 while( (pCurrAct=rMtf.NextAction()) != NULL )
546 if( pCurrAct->GetType() == nType )
548 bRet = true; // action type found
549 break;
552 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
553 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
554 pCommentString) )
556 // delimiting end comment found, done
557 bRet = false; // not yet found
558 break;
561 ++nPos;
564 // rewind metafile to previous position (this method must
565 // not change the current metaaction)
566 while( nPos-- )
567 rMtf.WindPrev();
569 if( !pCurrAct )
571 // EOF, and not yet found
572 bRet = false;
575 return bRet;
578 void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly,
579 const ::Gradient& rGradient,
580 const ActionFactoryParameters& rParms,
581 bool bIsPolygonRectangle,
582 bool bSubsettableActions )
584 DBG_TESTSOLARMUTEX();
586 ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
587 aDevicePoly.transform( rParms.mrStates.getState().mapModeTransform );
589 // decide, whether this gradient can be rendered natively
590 // by the canvas, or must be emulated via VCL gradient
591 // action extraction.
592 const sal_uInt16 nSteps( rGradient.GetSteps() );
594 if( // step count is infinite, can use native canvas
595 // gradients here
596 nSteps == 0 ||
597 // step count is sufficiently high, such that no
598 // discernible difference should be visible.
599 nSteps > 64 )
601 uno::Reference< lang::XMultiServiceFactory> xFactory(
602 rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
604 if( xFactory.is() )
606 rendering::Texture aTexture;
608 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
609 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
610 aTexture.Alpha = 1.0;
613 // setup start/end color values
614 // ----------------------------
616 // scale color coefficients with gradient intensities
617 const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() );
618 ::Color aVCLStartColor( rGradient.GetStartColor() );
619 aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) );
620 aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
621 aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) );
623 const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() );
624 ::Color aVCLEndColor( rGradient.GetEndColor() );
625 aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) );
626 aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
627 aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) );
629 uno::Reference<rendering::XColorSpace> xColorSpace(
630 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
631 const uno::Sequence< double > aStartColor(
632 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor,
633 xColorSpace ));
634 const uno::Sequence< double > aEndColor(
635 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor,
636 xColorSpace ));
638 uno::Sequence< uno::Sequence < double > > aColors(2);
639 uno::Sequence< double > aStops(2);
641 if( rGradient.GetStyle() == GradientStyle_AXIAL )
643 aStops.realloc(3);
644 aColors.realloc(3);
646 aStops[0] = 0.0;
647 aStops[1] = 0.5;
648 aStops[2] = 1.0;
650 aColors[0] = aEndColor;
651 aColors[1] = aStartColor;
652 aColors[2] = aEndColor;
654 else
656 aStops[0] = 0.0;
657 aStops[1] = 1.0;
659 aColors[0] = aStartColor;
660 aColors[1] = aEndColor;
663 const ::basegfx::B2DRectangle aBounds(
664 ::basegfx::tools::getRange(aDevicePoly) );
665 const ::basegfx::B2DVector aOffset(
666 rGradient.GetOfsX() / 100.0,
667 rGradient.GetOfsY() / 100.0);
668 double fRotation( rGradient.GetAngle() * M_PI / 1800.0 );
669 const double fBorder( rGradient.GetBorder() / 100.0 );
671 basegfx::B2DHomMatrix aRot90;
672 aRot90.rotate(M_PI_2);
674 basegfx::ODFGradientInfo aGradInfo;
675 OUString aGradientService;
676 switch( rGradient.GetStyle() )
678 case GradientStyle_LINEAR:
679 basegfx::tools::createLinearODFGradientInfo(aGradInfo,
680 aBounds,
681 nSteps,
682 fBorder,
683 fRotation);
684 // map ODF to svg gradient orientation - x
685 // instead of y direction
686 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
687 aGradientService = "LinearGradient";
688 break;
690 case GradientStyle_AXIAL:
692 // Adapt the border so that it is suitable
693 // for the axial gradient. An axial
694 // gradient consists of two linear
695 // gradients. Each of those covers half
696 // of the total size. In order to
697 // compensate for the condensed display of
698 // the linear gradients, we have to
699 // enlarge the area taken up by the actual
700 // gradient (1-fBorder). After that we
701 // have to turn the result back into a
702 // border value, hence the second (left
703 // most 1-...
704 const double fAxialBorder (1-2*(1-fBorder));
705 basegfx::tools::createAxialODFGradientInfo(aGradInfo,
706 aBounds,
707 nSteps,
708 fAxialBorder,
709 fRotation);
710 // map ODF to svg gradient orientation - x
711 // instead of y direction
712 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
714 // map ODF axial gradient to 3-stop linear
715 // gradient - shift left by 0.5
716 basegfx::B2DHomMatrix aShift;
717 aShift.translate(-0.5,0);
718 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aShift;
720 aGradientService = "LinearGradient";
721 break;
724 case GradientStyle_RADIAL:
725 basegfx::tools::createRadialODFGradientInfo(aGradInfo,
726 aBounds,
727 aOffset,
728 nSteps,
729 fBorder);
730 aGradientService = "EllipticalGradient";
731 break;
733 case GradientStyle_ELLIPTICAL:
734 basegfx::tools::createEllipticalODFGradientInfo(aGradInfo,
735 aBounds,
736 aOffset,
737 nSteps,
738 fBorder,
739 fRotation);
740 aGradientService = "EllipticalGradient";
741 break;
743 case GradientStyle_SQUARE:
744 basegfx::tools::createSquareODFGradientInfo(aGradInfo,
745 aBounds,
746 aOffset,
747 nSteps,
748 fBorder,
749 fRotation);
750 aGradientService = "RectangularGradient";
751 break;
753 case GradientStyle_RECT:
754 basegfx::tools::createRectangularODFGradientInfo(aGradInfo,
755 aBounds,
756 aOffset,
757 nSteps,
758 fBorder,
759 fRotation);
760 aGradientService = "RectangularGradient";
761 break;
763 default:
764 ENSURE_OR_THROW( false,
765 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
766 break;
769 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
770 aGradInfo.maTextureTransform );
772 uno::Sequence<uno::Any> args(3);
773 beans::PropertyValue aProp;
774 aProp.Name = "Colors";
775 aProp.Value <<= aColors;
776 args[0] <<= aProp;
777 aProp.Name = "Stops";
778 aProp.Value <<= aStops;
779 args[1] <<= aProp;
780 aProp.Name = "AspectRatio";
781 aProp.Value <<= aGradInfo.mfAspectRatio;
782 args[2] <<= aProp;
784 aTexture.Gradient.set(
785 xFactory->createInstanceWithArguments(aGradientService,
786 args),
787 uno::UNO_QUERY);
788 if( aTexture.Gradient.is() )
790 ActionSharedPtr pPolyAction(
791 internal::PolyPolyActionFactory::createPolyPolyAction(
792 aDevicePoly,
793 rParms.mrCanvas,
794 rParms.mrStates.getState(),
795 aTexture ) );
797 if( pPolyAction )
799 maActions.push_back(
800 MtfAction(
801 pPolyAction,
802 rParms.mrCurrActionIndex ) );
804 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
807 // done, using native gradients
808 return;
813 // cannot currently use native canvas gradients, as a
814 // finite step size is given (this funny feature is not
815 // supported by the XCanvas API)
816 rParms.mrStates.pushState(PUSH_ALL);
818 if( !bIsPolygonRectangle )
820 // only clip, if given polygon is not a rectangle in
821 // the first place (the gradient is always limited to
822 // the given bound rect)
823 updateClipping(
824 aDevicePoly,
825 rParms,
826 true );
829 GDIMetaFile aTmpMtf;
830 rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(),
831 rGradient,
832 aTmpMtf );
834 createActions( aTmpMtf, rParms, bSubsettableActions );
836 rParms.mrStates.popState();
839 uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation,
840 const ::Font& rFont,
841 const ActionFactoryParameters& rParms ) const
843 rendering::FontRequest aFontRequest;
845 if( rParms.mrParms.maFontName.is_initialized() )
846 aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName;
847 else
848 aFontRequest.FontDescription.FamilyName = rFont.GetName();
850 aFontRequest.FontDescription.StyleName = rFont.GetStyleName();
852 aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
853 aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;
855 // TODO(F2): improve vclenum->panose conversion
856 aFontRequest.FontDescription.FontDescription.Weight =
857 rParms.mrParms.maFontWeight.is_initialized() ?
858 *rParms.mrParms.maFontWeight :
859 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
860 aFontRequest.FontDescription.FontDescription.Letterform =
861 rParms.mrParms.maFontLetterForm.is_initialized() ?
862 *rParms.mrParms.maFontLetterForm :
863 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
864 aFontRequest.FontDescription.FontDescription.Proportion =
865 rParms.mrParms.maFontProportion.is_initialized() ?
866 *rParms.mrParms.maFontProportion :
867 (rFont.GetPitch() == PITCH_FIXED)
868 ? rendering::PanoseProportion::MONO_SPACED
869 : rendering::PanoseProportion::ANYTHING;
871 LanguageType aLang = rFont.GetLanguage();
872 aFontRequest.Locale = LanguageTag( aLang).getLocale( false);
874 // setup state-local text transformation,
875 // if the font be rotated
876 const short nFontAngle( rFont.GetOrientation() );
877 if( nFontAngle != 0 )
879 // set to unity transform rotated by font angle
880 const double nAngle( nFontAngle * (F_PI / 1800.0) );
881 o_rFontRotation = -nAngle;
883 else
885 o_rFontRotation = 0.0;
888 geometry::Matrix2D aFontMatrix;
889 ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
891 // TODO(F2): use correct scale direction, font
892 // height might be width or anything else
894 // TODO(Q3): This code smells of programming by
895 // coincidence (the next two if statements)
896 const ::Size rFontSizeLog( rFont.GetSize() );
897 const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
898 if( nFontWidthLog != 0 )
900 ::Font aTestFont = rFont;
901 aTestFont.SetWidth( 0 );
902 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
903 if( nNormalWidth != nFontWidthLog )
904 if( nNormalWidth )
905 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
908 // #i52608# apply map mode scale also to font matrix - an
909 // anisotrophic mapmode must be reflected in an
910 // anisotrophic font matrix scale.
911 const OutDevState& rState( rParms.mrStates.getState() );
912 if( !::basegfx::fTools::equal(
913 rState.mapModeTransform.get(0,0),
914 rState.mapModeTransform.get(1,1)) )
916 const double nScaleX( rState.mapModeTransform.get(0,0) );
917 const double nScaleY( rState.mapModeTransform.get(1,1) );
919 // note: no reason to check for division by zero, we
920 // always have the value closer (or equal) to zero as
921 // the nominator.
922 if( fabs(nScaleX) < fabs(nScaleY) )
923 aFontMatrix.m00 *= nScaleX / nScaleY;
924 else
925 aFontMatrix.m11 *= nScaleY / nScaleX;
927 aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
929 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
930 uno::Sequence< beans::PropertyValue >(),
931 aFontMatrix );
934 // create text effects such as shadow/relief/embossed
935 void ImplRenderer::createTextAction( const ::Point& rStartPoint,
936 const String rString,
937 int nIndex,
938 int nLength,
939 const sal_Int32* pCharWidths,
940 const ActionFactoryParameters& rParms,
941 bool bSubsettableActions )
943 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex,
944 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
946 if( !nLength )
947 return; // zero-length text, no visible output
949 const OutDevState& rState( rParms.mrStates.getState() );
951 // TODO(F2): implement all text effects
952 // if( rState.textAlignment ); // TODO(F2): NYI
954 ::Color aShadowColor( COL_AUTO );
955 ::Color aReliefColor( COL_AUTO );
956 ::Size aShadowOffset;
957 ::Size aReliefOffset;
959 uno::Reference<rendering::XColorSpace> xColorSpace(
960 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
962 if( rState.isTextEffectShadowSet )
964 // calculate shadow offset (similar to outdev3.cxx)
965 // TODO(F3): better match with outdev3.cxx
966 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
967 if( nShadowOffset < 1 )
968 nShadowOffset = 1;
970 aShadowOffset.setWidth( nShadowOffset );
971 aShadowOffset.setHeight( nShadowOffset );
973 // determine shadow color (from outdev3.cxx)
974 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
975 rState.textColor, xColorSpace );
976 bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
977 || (aTextColor.GetLuminance() < 8);
979 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
980 aShadowColor.SetTransparency( aTextColor.GetTransparency() );
983 if( rState.textReliefStyle )
985 // calculate relief offset (similar to outdev3.cxx)
986 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
987 nReliefOffset += nReliefOffset/2;
988 if( nReliefOffset < 1 )
989 nReliefOffset = 1;
991 if( rState.textReliefStyle == RELIEF_ENGRAVED )
992 nReliefOffset = -nReliefOffset;
994 aReliefOffset.setWidth( nReliefOffset );
995 aReliefOffset.setHeight( nReliefOffset );
997 // determine relief color (from outdev3.cxx)
998 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
999 rState.textColor, xColorSpace );
1001 aReliefColor = ::Color( COL_LIGHTGRAY );
1003 // we don't have a automatic color, so black is always
1004 // drawn on white (literally copied from
1005 // vcl/source/gdi/outdev3.cxx)
1006 if( aTextColor.GetColor() == COL_BLACK )
1008 aTextColor = ::Color( COL_WHITE );
1009 rParms.mrStates.getState().textColor =
1010 ::vcl::unotools::colorToDoubleSequence(
1011 aTextColor, xColorSpace );
1014 if( aTextColor.GetColor() == COL_WHITE )
1015 aReliefColor = ::Color( COL_BLACK );
1016 aReliefColor.SetTransparency( aTextColor.GetTransparency() );
1019 // create the actual text action
1020 ActionSharedPtr pTextAction(
1021 TextActionFactory::createTextAction(
1022 rStartPoint,
1023 aReliefOffset,
1024 aReliefColor,
1025 aShadowOffset,
1026 aShadowColor,
1027 rString,
1028 nIndex,
1029 nLength,
1030 pCharWidths,
1031 rParms.mrVDev,
1032 rParms.mrCanvas,
1033 rState,
1034 rParms.mrParms,
1035 bSubsettableActions ) );
1037 ActionSharedPtr pStrikeoutTextAction;
1039 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
1041 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
1043 sal_Unicode pChars[4];
1044 if ( rState.textStrikeoutStyle == STRIKEOUT_X )
1045 pChars[0] = 'X';
1046 else
1047 pChars[0] = '/';
1048 pChars[3]=pChars[2]=pChars[1]=pChars[0];
1050 long nStrikeoutWidth = (rParms.mrVDev.GetTextWidth(
1051 OUString(pChars, SAL_N_ELEMENTS(pChars))) + 2) / 4;
1053 if( nStrikeoutWidth <= 0 )
1054 nStrikeoutWidth = 1;
1056 long nMaxWidth = nStrikeoutWidth/2;
1057 if ( nMaxWidth < 2 )
1058 nMaxWidth = 2;
1059 nMaxWidth += nWidth + 1;
1061 long nFullStrikeoutWidth = 0;
1062 String aStrikeoutText;
1063 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
1064 aStrikeoutText += pChars[0];
1066 xub_StrLen nLen = aStrikeoutText.Len();
1068 if( nLen )
1070 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
1071 nStrikeoutWidth += nInterval;
1072 sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen];
1074 for ( int i = 0;i<nLen; i++)
1076 pStrikeoutCharWidths[i] = nStrikeoutWidth;
1079 for ( int i = 1;i< nLen; i++ )
1081 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
1084 sal_Int32 nStartPos = 0;
1086 pStrikeoutTextAction =
1087 TextActionFactory::createTextAction(
1088 rStartPoint,
1089 aReliefOffset,
1090 aReliefColor,
1091 aShadowOffset,
1092 aShadowColor,
1093 aStrikeoutText,
1094 nStartPos,
1095 aStrikeoutText.Len(),
1096 pStrikeoutCharWidths,
1097 rParms.mrVDev,
1098 rParms.mrCanvas,
1099 rState,
1100 rParms.mrParms,
1101 bSubsettableActions ) ;
1105 if( pTextAction )
1107 maActions.push_back(
1108 MtfAction(
1109 pTextAction,
1110 rParms.mrCurrActionIndex ) );
1112 if ( pStrikeoutTextAction )
1114 maActions.push_back(
1115 MtfAction(
1116 pStrikeoutTextAction,
1117 rParms.mrCurrActionIndex ) );
1120 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1124 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1125 const ActionFactoryParameters& rParms,
1126 bool bIntersect )
1128 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1129 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1131 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1132 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1134 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1135 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1137 if( !bIntersect ||
1138 (bEmptyClipRect && bEmptyClipPoly) )
1140 rState.clip = rClipPoly;
1142 else
1144 if( !bEmptyClipRect )
1146 // TODO(P3): Use Liang-Barsky polygon clip here,
1147 // after all, one object is just a rectangle!
1149 // convert rect to polygon beforehand, must revert
1150 // to general polygon clipping here.
1151 rState.clip = ::basegfx::B2DPolyPolygon(
1152 ::basegfx::tools::createPolygonFromRect(
1153 // #121100# VCL rectangular clips always
1154 // include one more pixel to the right
1155 // and the bottom
1156 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1157 rState.clipRect.Top(),
1158 rState.clipRect.Right()+1,
1159 rState.clipRect.Bottom()+1 ) ) );
1162 // AW: Simplified
1163 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1164 aClipPoly, rState.clip, true, false);
1167 // by now, our clip resides in the OutDevState::clip
1168 // poly-polygon.
1169 rState.clipRect.SetEmpty();
1171 if( rState.clip.count() == 0 )
1173 if( rState.clipRect.IsEmpty() )
1175 rState.xClipPoly.clear();
1177 else
1179 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1180 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1181 ::basegfx::B2DPolyPolygon(
1182 ::basegfx::tools::createPolygonFromRect(
1183 // #121100# VCL rectangular clips
1184 // always include one more pixel to
1185 // the right and the bottom
1186 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1187 rState.clipRect.Top(),
1188 rState.clipRect.Right()+1,
1189 rState.clipRect.Bottom()+1 ) ) ) );
1192 else
1194 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1195 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1196 rState.clip );
1200 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
1201 const ActionFactoryParameters& rParms,
1202 bool bIntersect )
1204 ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1206 const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1207 const bool bEmptyClipPoly( rState.clip.count() == 0 );
1209 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1210 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1212 if( !bIntersect ||
1213 (bEmptyClipRect && bEmptyClipPoly) )
1215 rState.clipRect = rClipRect;
1216 rState.clip.clear();
1218 else if( bEmptyClipPoly )
1220 rState.clipRect.Intersection( rClipRect );
1221 rState.clip.clear();
1223 else
1225 // TODO(P3): Handle a fourth case here, when all clip
1226 // polygons are rectangular, once B2DMultiRange's
1227 // sweep line implementation is done.
1229 // general case: convert to polygon and clip
1230 // -----------------------------------------
1232 // convert rect to polygon beforehand, must revert
1233 // to general polygon clipping here.
1234 ::basegfx::B2DPolyPolygon aClipPoly(
1235 ::basegfx::tools::createPolygonFromRect(
1236 ::basegfx::B2DRectangle( rClipRect.Left(),
1237 rClipRect.Top(),
1238 rClipRect.Right(),
1239 rClipRect.Bottom() ) ) );
1241 rState.clipRect.SetEmpty();
1243 // AW: Simplified
1244 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1245 aClipPoly, rState.clip, true, false);
1248 if( rState.clip.count() == 0 )
1250 if( rState.clipRect.IsEmpty() )
1252 rState.xClipPoly.clear();
1254 else
1256 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1257 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1258 ::basegfx::B2DPolyPolygon(
1259 ::basegfx::tools::createPolygonFromRect(
1260 // #121100# VCL rectangular clips
1261 // always include one more pixel to
1262 // the right and the bottom
1263 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1264 rState.clipRect.Top(),
1265 rState.clipRect.Right()+1,
1266 rState.clipRect.Bottom()+1 ) ) ) );
1269 else
1271 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1272 rParms.mrCanvas->getUNOCanvas()->getDevice(),
1273 rState.clip );
1277 bool ImplRenderer::createActions( GDIMetaFile& rMtf,
1278 const ActionFactoryParameters& rFactoryParms,
1279 bool bSubsettableActions )
1281 /* TODO(P2): interpret mtf-comments
1282 ================================
1284 - gradient fillings (do that via comments)
1286 - think about mapping. _If_ we do everything in logical
1287 coordinates (which would solve the probs for stroke
1288 widths and text offsets), then we would have to
1289 recalc scaling for every drawing operation. This is
1290 because the outdev map mode might change at any time.
1291 Also keep in mind, that, although we've double precision
1292 float arithmetic now, different offsets might still
1293 generate different roundings (aka
1294 'OutputDevice::SetPixelOffset())
1298 // alias common parameters
1299 VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1300 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1301 ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1302 const Parameters& rParms(rFactoryParms.mrParms);
1303 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1306 // Loop over every metaaction
1307 // ==========================
1308 MetaAction* pCurrAct;
1310 // TODO(P1): think about caching
1311 for( pCurrAct=rMtf.FirstAction();
1312 pCurrAct;
1313 pCurrAct = rMtf.NextAction() )
1315 // execute every action, to keep VDev state up-to-date
1316 // currently used only for
1317 // - the map mode
1318 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1319 // - SetFont to process font metric specific actions
1320 pCurrAct->Execute( &rVDev );
1322 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << pCurrAct->GetType() << " (" << pCurrAct->GetType() << ")");
1324 switch( pCurrAct->GetType() )
1326 // ------------------------------------------------------------
1328 // In the first part of this monster-switch, we
1329 // handle all state-changing meta actions. These
1330 // are all handled locally.
1332 // ------------------------------------------------------------
1334 case META_PUSH_ACTION:
1336 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1337 rStates.pushState(pPushAction->GetFlags());
1339 break;
1341 case META_POP_ACTION:
1342 rStates.popState();
1343 break;
1345 case META_TEXTLANGUAGE_ACTION:
1346 // FALLTHROUGH intended
1347 case META_REFPOINT_ACTION:
1348 // handled via pCurrAct->Execute( &rVDev )
1349 break;
1351 case META_MAPMODE_ACTION:
1352 // modify current mapModeTransformation
1353 // transformation, such that subsequent
1354 // coordinates map correctly
1355 tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
1356 rVDev );
1357 break;
1359 // monitor clip regions, to assemble clip polygon on our own
1360 case META_CLIPREGION_ACTION:
1362 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1364 if( !pClipAction->IsClipping() )
1366 // clear clipping
1367 rStates.getState().clip.clear();
1369 else
1371 if( !pClipAction->GetRegion().HasPolyPolygon() )
1373 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1374 "region encountered, falling back to bounding box!" );
1376 // #121806# explicitly kept integer
1377 Rectangle aClipRect(
1378 rVDev.LogicToPixel(
1379 pClipAction->GetRegion().GetBoundRect() ) );
1381 // intersect current clip with given rect
1382 updateClipping(
1383 aClipRect,
1384 rFactoryParms,
1385 false );
1387 else
1389 // set new clip polygon (don't intersect
1390 // with old one, just set it)
1392 // #121806# explicitly kept integer
1393 updateClipping(
1394 rVDev.LogicToPixel(
1395 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1396 rFactoryParms,
1397 false );
1401 break;
1404 case META_ISECTRECTCLIPREGION_ACTION:
1406 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1408 // #121806# explicitly kept integer
1409 Rectangle aClipRect(
1410 rVDev.LogicToPixel( pClipAction->GetRect() ) );
1412 // intersect current clip with given rect
1413 updateClipping(
1414 aClipRect,
1415 rFactoryParms,
1416 true );
1418 break;
1421 case META_ISECTREGIONCLIPREGION_ACTION:
1423 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1425 if( !pClipAction->GetRegion().HasPolyPolygon() )
1427 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1428 "region encountered, falling back to bounding box!" );
1430 // #121806# explicitly kept integer
1431 Rectangle aClipRect(
1432 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1434 // intersect current clip with given rect
1435 updateClipping(
1436 aClipRect,
1437 rFactoryParms,
1438 true );
1440 else
1442 // intersect current clip with given clip polygon
1444 // #121806# explicitly kept integer
1445 updateClipping(
1446 rVDev.LogicToPixel(
1447 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1448 rFactoryParms,
1449 true );
1452 break;
1455 case META_MOVECLIPREGION_ACTION:
1456 // TODO(F2): NYI
1457 break;
1459 case META_LINECOLOR_ACTION:
1460 if( !rParms.maLineColor.is_initialized() )
1462 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1463 rStates.getState().isLineColorSet,
1464 rStates.getState().lineColor,
1465 rCanvas );
1467 break;
1469 case META_FILLCOLOR_ACTION:
1470 if( !rParms.maFillColor.is_initialized() )
1472 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1473 rStates.getState().isFillColorSet,
1474 rStates.getState().fillColor,
1475 rCanvas );
1477 break;
1479 case META_TEXTCOLOR_ACTION:
1481 if( !rParms.maTextColor.is_initialized() )
1483 // Text color is set unconditionally, thus, no
1484 // use of setStateColor here
1485 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1487 // force alpha part of color to
1488 // opaque. transparent painting is done
1489 // explicitly via META_TRANSPARENT_ACTION
1490 aColor.SetTransparency(0);
1492 rStates.getState().textColor =
1493 ::vcl::unotools::colorToDoubleSequence(
1494 aColor,
1495 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1498 break;
1500 case META_TEXTFILLCOLOR_ACTION:
1501 if( !rParms.maTextColor.is_initialized() )
1503 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1504 rStates.getState().isTextFillColorSet,
1505 rStates.getState().textFillColor,
1506 rCanvas );
1508 break;
1510 case META_TEXTLINECOLOR_ACTION:
1511 if( !rParms.maTextColor.is_initialized() )
1513 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1514 rStates.getState().isTextLineColorSet,
1515 rStates.getState().textLineColor,
1516 rCanvas );
1518 break;
1520 case META_TEXTALIGN_ACTION:
1522 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1523 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1525 rState.textReferencePoint = eTextAlign;
1527 break;
1529 case META_FONT_ACTION:
1531 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1532 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1534 rState.xFont = createFont( rState.fontRotation,
1535 rFont,
1536 rFactoryParms );
1538 // TODO(Q2): define and use appropriate enumeration types
1539 rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
1540 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
1541 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ?
1542 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1543 (sal_Int8)rFont.GetUnderline();
1544 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
1545 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
1546 rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False);
1547 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False);
1548 rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False);
1550 break;
1552 case META_RASTEROP_ACTION:
1553 // TODO(F2): NYI
1554 break;
1556 case META_LAYOUTMODE_ACTION:
1558 // TODO(F2): A lot is missing here
1559 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1560 ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1561 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
1563 case TEXT_LAYOUT_BIDI_LTR:
1564 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1565 break;
1567 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
1568 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1569 break;
1571 case TEXT_LAYOUT_BIDI_RTL:
1572 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1573 break;
1575 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
1576 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1577 break;
1580 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1581 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1582 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1584 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1587 break;
1589 // ------------------------------------------------------------
1591 // In the second part of this monster-switch, we
1592 // handle all recursing meta actions. These are the
1593 // ones generating a metafile by themselves, which is
1594 // then processed by recursively calling this method.
1596 // ------------------------------------------------------------
1598 case META_GRADIENT_ACTION:
1600 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1601 createGradientAction( ::Polygon( pGradAct->GetRect() ),
1602 pGradAct->GetGradient(),
1603 rFactoryParms,
1604 true,
1605 bSubsettableActions );
1607 break;
1609 case META_HATCH_ACTION:
1611 // TODO(F2): use native Canvas hatches here
1612 GDIMetaFile aTmpMtf;
1614 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1615 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1616 aTmpMtf );
1617 createActions( aTmpMtf, rFactoryParms,
1618 bSubsettableActions );
1620 break;
1622 case META_EPS_ACTION:
1624 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
1625 const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
1627 // #121806# explicitly kept integer
1628 const Size aMtfSize( rSubstitute.GetPrefSize() );
1629 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1630 rSubstitute.GetPrefMapMode() ) );
1632 // #i44110# correct null-sized output - there
1633 // are metafiles which have zero size in at
1634 // least one dimension
1635 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1636 ::std::max( aMtfSizePixPre.Height(), 1L ) );
1638 // Setup local transform, such that the
1639 // metafile renders itself into the given
1640 // output rectangle
1641 rStates.pushState(PUSH_ALL);
1643 rVDev.Push();
1644 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1646 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1647 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1649 rStates.getState().transform.translate( rPos.X(),
1650 rPos.Y() );
1651 rStates.getState().transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1652 (double)rSize.Height() / aMtfSizePix.Height() );
1654 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1655 rFactoryParms,
1656 bSubsettableActions );
1658 rVDev.Pop();
1659 rStates.popState();
1661 break;
1663 // handle metafile comments, to retrieve
1664 // meta-information for gradients, fills and
1665 // strokes. May skip actions, and may recurse.
1666 case META_COMMENT_ACTION:
1668 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1670 // Handle gradients
1671 if (pAct->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")))
1673 MetaGradientExAction* pGradAction = NULL;
1674 bool bDone( false );
1675 while( !bDone &&
1676 (pCurrAct=rMtf.NextAction()) != NULL )
1678 switch( pCurrAct->GetType() )
1680 // extract gradient info
1681 case META_GRADIENTEX_ACTION:
1682 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1683 break;
1685 // skip broken-down rendering, output gradient when sequence is ended
1686 case META_COMMENT_ACTION:
1687 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")) )
1689 bDone = true;
1691 if( pGradAction )
1693 createGradientAction( pGradAction->GetPolyPolygon(),
1694 pGradAction->GetGradient(),
1695 rFactoryParms,
1696 false,
1697 bSubsettableActions );
1700 break;
1704 // TODO(P2): Handle drawing layer strokes, via
1705 // XPATHSTROKE_SEQ_BEGIN comment
1707 // Handle drawing layer fills
1708 else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_BEGIN")) )
1710 const sal_uInt8* pData = pAct->GetData();
1711 if ( pData )
1713 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1715 SvtGraphicFill aFill;
1716 aMemStm >> aFill;
1718 // TODO(P2): Also handle gradients and
1719 // hatches like this
1721 // only evaluate comment for pure
1722 // bitmap fills. If a transparency
1723 // gradient is involved (denoted by
1724 // the FloatTransparent action), take
1725 // the normal meta actions.
1726 if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1727 !isActionContained( rMtf,
1728 "XPATHFILL_SEQ_END",
1729 META_FLOATTRANSPARENT_ACTION ) )
1731 rendering::Texture aTexture;
1733 // TODO(F1): the SvtGraphicFill
1734 // can also transport metafiles
1735 // here, handle that case, too
1736 Graphic aGraphic;
1737 aFill.getGraphic( aGraphic );
1739 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1740 const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1742 ::SvtGraphicFill::Transform aTransform;
1743 aFill.getTransform( aTransform );
1745 ::basegfx::B2DHomMatrix aMatrix;
1747 // convert to basegfx matrix
1748 aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1749 aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1750 aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1751 aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1752 aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1753 aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1755 ::basegfx::B2DHomMatrix aScale;
1756 aScale.scale( aBmpSize.Width(),
1757 aBmpSize.Height() );
1759 // post-multiply with the bitmap
1760 // size (XCanvas' texture assumes
1761 // the given bitmap to be
1762 // normalized to [0,1]x[0,1]
1763 // rectangle)
1764 aMatrix = aMatrix * aScale;
1766 // pre-multiply with the
1767 // logic-to-pixel scale factor
1768 // (the metafile comment works in
1769 // logical coordinates).
1770 ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1771 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1772 rVDev );
1774 ::basegfx::unotools::affineMatrixFromHomMatrix(
1775 aTexture.AffineTransform,
1776 aMatrix );
1778 aTexture.Alpha = 1.0 - aFill.getTransparency();
1779 aTexture.Bitmap =
1780 ::vcl::unotools::xBitmapFromBitmapEx(
1781 rCanvas->getUNOCanvas()->getDevice(),
1782 aBmpEx );
1783 if( aFill.isTiling() )
1785 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1786 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1788 else
1790 aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1791 aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1794 ::PolyPolygon aPath;
1795 aFill.getPath( aPath );
1797 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1798 aPoly.transform( rStates.getState().mapModeTransform );
1799 ActionSharedPtr pPolyAction(
1800 internal::PolyPolyActionFactory::createPolyPolyAction(
1801 aPoly,
1802 rCanvas,
1803 rStates.getState(),
1804 aTexture ) );
1806 if( pPolyAction )
1808 maActions.push_back(
1809 MtfAction(
1810 pPolyAction,
1811 io_rCurrActionIndex ) );
1813 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1816 // skip broken-down render output
1817 skipContent( rMtf,
1818 "XPATHFILL_SEQ_END",
1819 io_rCurrActionIndex );
1823 // Handle drawing layer fills
1824 else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS")) ) {
1825 static int count = -1, limit = 0x7fffffff;
1826 if (count == -1) {
1827 count = 0;
1828 if (char *env = getenv ("EMF_PLUS_LIMIT")) {
1829 limit = atoi (env);
1830 SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit);
1833 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct->GetDataSize ());
1834 if (count < limit)
1835 processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
1836 count ++;
1837 } else if( pAct->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS_HEADER_INFO")) ) {
1838 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct->GetDataSize ());
1840 SvMemoryStream rMF ((void*) pAct->GetData (), pAct->GetDataSize (), STREAM_READ);
1842 rMF >> nFrameLeft >> nFrameTop >> nFrameRight >> nFrameBottom;
1843 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft << "," << nFrameTop << " - " << nFrameRight << "," << nFrameBottom);
1844 rMF >> nPixX >> nPixY >> nMmX >> nMmY;
1845 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX << "x" << nPixY << " mm size: " << nMmX << "x" << nMmY);
1847 rMF >> aBaseTransform;
1848 //aWorldTransform.Set (aBaseTransform);
1851 break;
1853 // ------------------------------------------------------------
1855 // In the third part of this monster-switch, we
1856 // handle all 'acting' meta actions. These are all
1857 // processed by constructing function objects for
1858 // them, which will later ease caching.
1860 // ------------------------------------------------------------
1862 case META_POINT_ACTION:
1864 const OutDevState& rState( rStates.getState() );
1865 if( rState.lineColor.getLength() )
1867 ActionSharedPtr pPointAction(
1868 internal::PointActionFactory::createPointAction(
1869 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1870 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1871 rCanvas,
1872 rState ) );
1874 if( pPointAction )
1876 maActions.push_back(
1877 MtfAction(
1878 pPointAction,
1879 io_rCurrActionIndex ) );
1881 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1885 break;
1887 case META_PIXEL_ACTION:
1889 const OutDevState& rState( rStates.getState() );
1890 if( rState.lineColor.getLength() )
1892 ActionSharedPtr pPointAction(
1893 internal::PointActionFactory::createPointAction(
1894 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1895 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1896 rCanvas,
1897 rState,
1898 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1900 if( pPointAction )
1902 maActions.push_back(
1903 MtfAction(
1904 pPointAction,
1905 io_rCurrActionIndex ) );
1907 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1911 break;
1913 case META_LINE_ACTION:
1915 const OutDevState& rState( rStates.getState() );
1916 if( rState.lineColor.getLength() )
1918 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1920 const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1922 const ::basegfx::B2DPoint aStartPoint(
1923 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1924 const ::basegfx::B2DPoint aEndPoint(
1925 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1927 ActionSharedPtr pLineAction;
1929 if( rLineInfo.IsDefault() )
1931 // plain hair line
1932 pLineAction =
1933 internal::LineActionFactory::createLineAction(
1934 aStartPoint,
1935 aEndPoint,
1936 rCanvas,
1937 rState );
1939 if( pLineAction )
1941 maActions.push_back(
1942 MtfAction(
1943 pLineAction,
1944 io_rCurrActionIndex ) );
1946 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1949 else if( LINE_NONE != rLineInfo.GetStyle() )
1951 // 'thick' line
1952 rendering::StrokeAttributes aStrokeAttributes;
1954 setupStrokeAttributes( aStrokeAttributes,
1955 rFactoryParms,
1956 rLineInfo );
1958 // XCanvas can only stroke polygons,
1959 // not simple lines - thus, handle
1960 // this case via the polypolygon
1961 // action
1962 ::basegfx::B2DPolygon aPoly;
1963 aPoly.append( aStartPoint );
1964 aPoly.append( aEndPoint );
1965 pLineAction =
1966 internal::PolyPolyActionFactory::createPolyPolyAction(
1967 ::basegfx::B2DPolyPolygon( aPoly ),
1968 rCanvas, rState, aStrokeAttributes );
1970 if( pLineAction )
1972 maActions.push_back(
1973 MtfAction(
1974 pLineAction,
1975 io_rCurrActionIndex ) );
1977 io_rCurrActionIndex += pLineAction->getActionCount()-1;
1980 // else: line style is default
1981 // (i.e. invisible), don't generate action
1984 break;
1986 case META_RECT_ACTION:
1988 const Rectangle& rRect(
1989 static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
1991 if( rRect.IsEmpty() )
1992 break;
1994 const OutDevState& rState( rStates.getState() );
1995 const ::basegfx::B2DPoint aTopLeftPixel(
1996 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
1997 const ::basegfx::B2DPoint aBottomRightPixel(
1998 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1999 // #121100# OutputDevice::DrawRect() fills
2000 // rectangles Apple-like, i.e. with one
2001 // additional pixel to the right and bottom.
2002 ::basegfx::B2DPoint(1,1) );
2004 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
2005 ::basegfx::B2DRange( aTopLeftPixel,
2006 aBottomRightPixel )),
2007 rFactoryParms );
2008 break;
2011 case META_ROUNDRECT_ACTION:
2013 const Rectangle& rRect(
2014 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
2016 if( rRect.IsEmpty() )
2017 break;
2019 ::basegfx::B2DPolygon aPoly(
2020 ::basegfx::tools::createPolygonFromRect(
2021 ::basegfx::B2DRange(
2022 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2023 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2024 ::basegfx::B2DPoint(1,1) ),
2025 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
2026 ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
2027 aPoly.transform( rStates.getState().mapModeTransform );
2029 createFillAndStroke( aPoly,
2030 rFactoryParms );
2032 break;
2034 case META_ELLIPSE_ACTION:
2036 const Rectangle& rRect(
2037 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
2039 if( rRect.IsEmpty() )
2040 break;
2042 const ::basegfx::B2DRange aRange(
2043 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2044 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2045 ::basegfx::B2DPoint(1,1) );
2047 ::basegfx::B2DPolygon aPoly(
2048 ::basegfx::tools::createPolygonFromEllipse(
2049 aRange.getCenter(),
2050 aRange.getWidth(),
2051 aRange.getHeight() ));
2052 aPoly.transform( rStates.getState().mapModeTransform );
2054 createFillAndStroke( aPoly,
2055 rFactoryParms );
2057 break;
2059 case META_ARC_ACTION:
2061 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2062 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2063 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2064 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2065 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2066 aPoly.transform( rStates.getState().mapModeTransform );
2068 createFillAndStroke( aPoly,
2069 rFactoryParms );
2071 break;
2073 case META_PIE_ACTION:
2075 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2076 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2077 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2078 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2079 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2080 aPoly.transform( rStates.getState().mapModeTransform );
2082 createFillAndStroke( aPoly,
2083 rFactoryParms );
2085 break;
2087 case META_CHORD_ACTION:
2089 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2090 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2091 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2092 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2093 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2094 aPoly.transform( rStates.getState().mapModeTransform );
2096 createFillAndStroke( aPoly,
2097 rFactoryParms );
2099 break;
2101 case META_POLYLINE_ACTION:
2103 const OutDevState& rState( rStates.getState() );
2104 if( rState.lineColor.getLength() ||
2105 rState.fillColor.getLength() )
2107 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2109 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2110 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2111 aPoly.transform( rState.mapModeTransform );
2113 ActionSharedPtr pLineAction;
2115 if( rLineInfo.IsDefault() )
2117 // plain hair line polygon
2118 pLineAction =
2119 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2120 ::basegfx::B2DPolyPolygon(aPoly),
2121 rCanvas,
2122 rState );
2124 if( pLineAction )
2126 maActions.push_back(
2127 MtfAction(
2128 pLineAction,
2129 io_rCurrActionIndex ) );
2131 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2134 else if( LINE_NONE != rLineInfo.GetStyle() )
2136 // 'thick' line polygon
2137 rendering::StrokeAttributes aStrokeAttributes;
2139 setupStrokeAttributes( aStrokeAttributes,
2140 rFactoryParms,
2141 rLineInfo );
2143 pLineAction =
2144 internal::PolyPolyActionFactory::createPolyPolyAction(
2145 ::basegfx::B2DPolyPolygon(aPoly),
2146 rCanvas,
2147 rState,
2148 aStrokeAttributes ) ;
2150 if( pLineAction )
2152 maActions.push_back(
2153 MtfAction(
2154 pLineAction,
2155 io_rCurrActionIndex ) );
2157 io_rCurrActionIndex += pLineAction->getActionCount()-1;
2160 // else: line style is default
2161 // (i.e. invisible), don't generate action
2164 break;
2166 case META_POLYGON_ACTION:
2168 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2169 aPoly.transform( rStates.getState().mapModeTransform );
2170 createFillAndStroke( aPoly,
2171 rFactoryParms );
2173 break;
2175 case META_POLYPOLYGON_ACTION:
2177 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2178 aPoly.transform( rStates.getState().mapModeTransform );
2179 createFillAndStroke( aPoly,
2180 rFactoryParms );
2182 break;
2184 case META_BMP_ACTION:
2186 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2188 ActionSharedPtr pBmpAction(
2189 internal::BitmapActionFactory::createBitmapAction(
2190 pAct->GetBitmap(),
2191 rStates.getState().mapModeTransform *
2192 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2193 rCanvas,
2194 rStates.getState() ) );
2196 if( pBmpAction )
2198 maActions.push_back(
2199 MtfAction(
2200 pBmpAction,
2201 io_rCurrActionIndex ) );
2203 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2206 break;
2208 case META_BMPSCALE_ACTION:
2210 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2212 ActionSharedPtr pBmpAction(
2213 internal::BitmapActionFactory::createBitmapAction(
2214 pAct->GetBitmap(),
2215 rStates.getState().mapModeTransform *
2216 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2217 rStates.getState().mapModeTransform *
2218 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2219 rCanvas,
2220 rStates.getState() ) );
2222 if( pBmpAction )
2224 maActions.push_back(
2225 MtfAction(
2226 pBmpAction,
2227 io_rCurrActionIndex ) );
2229 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2232 break;
2234 case META_BMPSCALEPART_ACTION:
2236 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2238 // crop bitmap to given source rectangle (no
2239 // need to copy and convert the whole bitmap)
2240 Bitmap aBmp( pAct->GetBitmap() );
2241 const Rectangle aCropRect( pAct->GetSrcPoint(),
2242 pAct->GetSrcSize() );
2243 aBmp.Crop( aCropRect );
2245 ActionSharedPtr pBmpAction(
2246 internal::BitmapActionFactory::createBitmapAction(
2247 aBmp,
2248 rStates.getState().mapModeTransform *
2249 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2250 rStates.getState().mapModeTransform *
2251 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2252 rCanvas,
2253 rStates.getState() ) );
2255 if( pBmpAction )
2257 maActions.push_back(
2258 MtfAction(
2259 pBmpAction,
2260 io_rCurrActionIndex ) );
2262 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2265 break;
2267 case META_BMPEX_ACTION:
2269 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2271 ActionSharedPtr pBmpAction(
2272 internal::BitmapActionFactory::createBitmapAction(
2273 pAct->GetBitmapEx(),
2274 rStates.getState().mapModeTransform *
2275 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2276 rCanvas,
2277 rStates.getState() ) );
2279 if( pBmpAction )
2281 maActions.push_back(
2282 MtfAction(
2283 pBmpAction,
2284 io_rCurrActionIndex ) );
2286 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2289 break;
2291 case META_BMPEXSCALE_ACTION:
2293 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2295 ActionSharedPtr pBmpAction(
2296 internal::BitmapActionFactory::createBitmapAction(
2297 pAct->GetBitmapEx(),
2298 rStates.getState().mapModeTransform *
2299 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2300 rStates.getState().mapModeTransform *
2301 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2302 rCanvas,
2303 rStates.getState() ) );
2305 if( pBmpAction )
2307 maActions.push_back(
2308 MtfAction(
2309 pBmpAction,
2310 io_rCurrActionIndex ) );
2312 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2315 break;
2317 case META_BMPEXSCALEPART_ACTION:
2319 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2321 // crop bitmap to given source rectangle (no
2322 // need to copy and convert the whole bitmap)
2323 BitmapEx aBmp( pAct->GetBitmapEx() );
2324 const Rectangle aCropRect( pAct->GetSrcPoint(),
2325 pAct->GetSrcSize() );
2326 aBmp.Crop( aCropRect );
2328 ActionSharedPtr pBmpAction(
2329 internal::BitmapActionFactory::createBitmapAction(
2330 aBmp,
2331 rStates.getState().mapModeTransform *
2332 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2333 rStates.getState().mapModeTransform *
2334 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2335 rCanvas,
2336 rStates.getState() ) );
2338 if( pBmpAction )
2340 maActions.push_back(
2341 MtfAction(
2342 pBmpAction,
2343 io_rCurrActionIndex ) );
2345 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2348 break;
2350 case META_MASK_ACTION:
2352 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2354 // create masked BitmapEx right here, as the
2355 // canvas does not provide equivalent
2356 // functionality
2357 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2358 pAct->GetColor() ));
2360 ActionSharedPtr pBmpAction(
2361 internal::BitmapActionFactory::createBitmapAction(
2362 aBmp,
2363 rStates.getState().mapModeTransform *
2364 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2365 rCanvas,
2366 rStates.getState() ) );
2368 if( pBmpAction )
2370 maActions.push_back(
2371 MtfAction(
2372 pBmpAction,
2373 io_rCurrActionIndex ) );
2375 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2378 break;
2380 case META_MASKSCALE_ACTION:
2382 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2384 // create masked BitmapEx right here, as the
2385 // canvas does not provide equivalent
2386 // functionality
2387 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2388 pAct->GetColor() ));
2390 ActionSharedPtr pBmpAction(
2391 internal::BitmapActionFactory::createBitmapAction(
2392 aBmp,
2393 rStates.getState().mapModeTransform *
2394 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2395 rStates.getState().mapModeTransform *
2396 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2397 rCanvas,
2398 rStates.getState() ) );
2400 if( pBmpAction )
2402 maActions.push_back(
2403 MtfAction(
2404 pBmpAction,
2405 io_rCurrActionIndex ) );
2407 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2410 break;
2412 case META_MASKSCALEPART_ACTION:
2414 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2416 // create masked BitmapEx right here, as the
2417 // canvas does not provide equivalent
2418 // functionality
2419 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2420 pAct->GetColor() ));
2422 // crop bitmap to given source rectangle (no
2423 // need to copy and convert the whole bitmap)
2424 const Rectangle aCropRect( pAct->GetSrcPoint(),
2425 pAct->GetSrcSize() );
2426 aBmp.Crop( aCropRect );
2428 ActionSharedPtr pBmpAction(
2429 internal::BitmapActionFactory::createBitmapAction(
2430 aBmp,
2431 rStates.getState().mapModeTransform *
2432 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2433 rStates.getState().mapModeTransform *
2434 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2435 rCanvas,
2436 rStates.getState() ) );
2438 if( pBmpAction )
2440 maActions.push_back(
2441 MtfAction(
2442 pBmpAction,
2443 io_rCurrActionIndex ) );
2445 io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2448 break;
2450 case META_GRADIENTEX_ACTION:
2451 // TODO(F1): use native Canvas gradients here
2452 // action is ignored here, because redundant to META_GRADIENT_ACTION
2453 break;
2455 case META_WALLPAPER_ACTION:
2456 // TODO(F2): NYI
2457 break;
2459 case META_TRANSPARENT_ACTION:
2461 const OutDevState& rState( rStates.getState() );
2462 if( rState.lineColor.getLength() ||
2463 rState.fillColor.getLength() )
2465 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2466 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2467 aPoly.transform( rState.mapModeTransform );
2469 ActionSharedPtr pPolyAction(
2470 internal::PolyPolyActionFactory::createPolyPolyAction(
2471 aPoly,
2472 rCanvas,
2473 rState,
2474 pAct->GetTransparence() ) );
2476 if( pPolyAction )
2478 maActions.push_back(
2479 MtfAction(
2480 pPolyAction,
2481 io_rCurrActionIndex ) );
2483 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2487 break;
2489 case META_FLOATTRANSPARENT_ACTION:
2491 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2493 internal::MtfAutoPtr pMtf(
2494 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2496 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2497 internal::GradientAutoPtr pGradient(
2498 new Gradient( pAct->GetGradient() ) );
2500 DBG_TESTSOLARMUTEX();
2502 ActionSharedPtr pFloatTransAction(
2503 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2504 pMtf,
2505 pGradient,
2506 rParms,
2507 rStates.getState().mapModeTransform *
2508 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2509 rStates.getState().mapModeTransform *
2510 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2511 rCanvas,
2512 rStates.getState() ) );
2514 if( pFloatTransAction )
2516 maActions.push_back(
2517 MtfAction(
2518 pFloatTransAction,
2519 io_rCurrActionIndex ) );
2521 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2524 break;
2526 case META_TEXT_ACTION:
2528 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2529 XubString sText = XubString( pAct->GetText() );
2531 if( rVDev.GetDigitLanguage())
2532 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2534 createTextAction(
2535 pAct->GetPoint(),
2536 sText,
2537 pAct->GetIndex(),
2538 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2539 NULL,
2540 rFactoryParms,
2541 bSubsettableActions );
2543 break;
2545 case META_TEXTARRAY_ACTION:
2547 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2548 XubString sText = XubString( pAct->GetText() );
2550 if( rVDev.GetDigitLanguage())
2551 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2553 createTextAction(
2554 pAct->GetPoint(),
2555 sText,
2556 pAct->GetIndex(),
2557 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2558 pAct->GetDXArray(),
2559 rFactoryParms,
2560 bSubsettableActions );
2562 break;
2564 case META_TEXTLINE_ACTION:
2566 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2568 const OutDevState& rState( rStates.getState() );
2569 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2570 rVDev ) );
2571 const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2572 ::basegfx::B2DSize(pAct->GetWidth(),
2573 0 ));
2575 ActionSharedPtr pPolyAction(
2576 PolyPolyActionFactory::createPolyPolyAction(
2577 tools::createTextLinesPolyPolygon(
2578 rState.mapModeTransform *
2579 ::basegfx::B2DPoint(
2580 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2581 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2582 aSize.getX(),
2583 tools::createTextLineInfo( rVDev,
2584 rState )),
2585 rCanvas,
2586 rState ) );
2588 if( pPolyAction.get() )
2590 maActions.push_back(
2591 MtfAction(
2592 pPolyAction,
2593 io_rCurrActionIndex ) );
2595 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2598 break;
2600 case META_TEXTRECT_ACTION:
2602 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2604 rStates.pushState(PUSH_ALL);
2606 // use the VDev to break up the text rect
2607 // action into readily formatted lines
2608 GDIMetaFile aTmpMtf;
2609 rVDev.AddTextRectActions( pAct->GetRect(),
2610 pAct->GetText(),
2611 pAct->GetStyle(),
2612 aTmpMtf );
2614 createActions( aTmpMtf,
2615 rFactoryParms,
2616 bSubsettableActions );
2618 rStates.popState();
2620 break;
2623 case META_STRETCHTEXT_ACTION:
2625 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2626 XubString sText = XubString( pAct->GetText() );
2628 if( rVDev.GetDigitLanguage())
2629 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2631 const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ?
2632 pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen() );
2634 // #i70897# Nothing to do, actually...
2635 if( nLen == 0 )
2636 break;
2638 // have to fit the text into the given
2639 // width. This is achieved by internally
2640 // generating a DX array, and uniformly
2641 // distributing the excess/insufficient width
2642 // to every logical character.
2643 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
2645 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2646 pAct->GetIndex(), pAct->GetLen() );
2648 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2650 // Last entry of pDXArray contains total width of the text
2651 sal_Int32* p=pDXArray.get();
2652 for( sal_uInt16 i=1; i<=nLen; ++i )
2654 // calc ratio for every array entry, to
2655 // distribute rounding errors 'evenly'
2656 // across the characters. Note that each
2657 // entry represents the 'end' position of
2658 // the corresponding character, thus, we
2659 // let i run from 1 to nLen.
2660 *p++ += (sal_Int32)i*nWidthDifference/nLen;
2663 createTextAction(
2664 pAct->GetPoint(),
2665 sText,
2666 pAct->GetIndex(),
2667 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen(),
2668 pDXArray.get(),
2669 rFactoryParms,
2670 bSubsettableActions );
2672 break;
2674 default:
2675 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2676 break;
2679 // increment action index (each mtf action counts _at
2680 // least_ one. Some count for more, therefore,
2681 // io_rCurrActionIndex is sometimes incremented by
2682 // pAct->getActionCount()-1 above, the -1 being the
2683 // correction for the unconditional increment here).
2684 ++io_rCurrActionIndex;
2687 return true;
2691 namespace
2693 class ActionRenderer
2695 public:
2696 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2697 maTransformation( rTransformation ),
2698 mbRet( true )
2702 bool result() const
2704 return mbRet;
2707 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2709 // ANDing the result. We want to fail if at least
2710 // one action failed.
2711 mbRet &= rAction.mpAction->render( maTransformation );
2714 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2715 const Action::Subset& rSubset )
2717 // ANDing the result. We want to fail if at least
2718 // one action failed.
2719 mbRet &= rAction.mpAction->renderSubset( maTransformation,
2720 rSubset );
2723 private:
2724 ::basegfx::B2DHomMatrix maTransformation;
2725 bool mbRet;
2728 class AreaQuery
2730 public:
2731 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2732 maTransformation( rTransformation ),
2733 maBounds()
2737 bool result() const
2739 return true; // nothing can fail here
2742 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2744 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2747 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2748 const Action::Subset& rSubset )
2750 maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2751 rSubset ) );
2754 ::basegfx::B2DRange getBounds() const
2756 return maBounds;
2759 private:
2760 ::basegfx::B2DHomMatrix maTransformation;
2761 ::basegfx::B2DRange maBounds;
2764 // Doing that via inline class. Compilers tend to not inline free
2765 // functions.
2766 struct UpperBoundActionIndexComparator
2768 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2769 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2771 const sal_Int32 nLHSCount( rLHS.mpAction ?
2772 rLHS.mpAction->getActionCount() : 0 );
2773 const sal_Int32 nRHSCount( rRHS.mpAction ?
2774 rRHS.mpAction->getActionCount() : 0 );
2776 // compare end of action range, to have an action selected
2777 // by lower_bound even if the requested index points in
2778 // the middle of the action's range
2779 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2783 /** Algorithm to apply given functor to a subset range
2785 @tpl Functor
2787 Functor to call for each element of the subset
2788 range. Must provide the following method signatures:
2789 bool result() (returning false if operation failed)
2792 template< typename Functor > bool
2793 forSubsetRange( Functor& rFunctor,
2794 ImplRenderer::ActionVector::const_iterator aRangeBegin,
2795 ImplRenderer::ActionVector::const_iterator aRangeEnd,
2796 sal_Int32 nStartIndex,
2797 sal_Int32 nEndIndex,
2798 const ImplRenderer::ActionVector::const_iterator& rEnd )
2800 if( aRangeBegin == aRangeEnd )
2802 // only a single action. Setup subset, and call functor
2803 Action::Subset aSubset;
2804 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2805 nStartIndex - aRangeBegin->mnOrigIndex );
2806 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2807 nEndIndex - aRangeBegin->mnOrigIndex );
2809 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2810 "ImplRenderer::forSubsetRange(): Invalid indices" );
2812 rFunctor( *aRangeBegin, aSubset );
2814 else
2816 // more than one action.
2818 // render partial first, full intermediate, and
2819 // partial last action
2820 Action::Subset aSubset;
2821 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2822 nStartIndex - aRangeBegin->mnOrigIndex );
2823 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2825 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2826 "ImplRenderer::forSubsetRange(): Invalid indices" );
2828 rFunctor( *aRangeBegin, aSubset );
2830 // first action rendered, skip to next
2831 ++aRangeBegin;
2833 // render full middle actions
2834 while( aRangeBegin != aRangeEnd )
2835 rFunctor( *aRangeBegin++ );
2837 if( aRangeEnd == rEnd ||
2838 aRangeEnd->mnOrigIndex > nEndIndex )
2840 // aRangeEnd denotes end of action vector,
2842 // or
2844 // nEndIndex references something _after_
2845 // aRangeBegin, but _before_ aRangeEnd
2847 // either way: no partial action left
2848 return rFunctor.result();
2851 aSubset.mnSubsetBegin = 0;
2852 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2854 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2855 "ImplRenderer::forSubsetRange(): Invalid indices" );
2857 rFunctor( *aRangeEnd, aSubset );
2860 return rFunctor.result();
2864 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2865 sal_Int32& io_rEndIndex,
2866 ActionVector::const_iterator& o_rRangeBegin,
2867 ActionVector::const_iterator& o_rRangeEnd ) const
2869 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2870 "ImplRenderer::getSubsetIndices(): invalid action range" );
2872 ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2873 "ImplRenderer::getSubsetIndices(): no actions to render" );
2875 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2876 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2877 maActions.back().mpAction->getActionCount() );
2879 // clip given range to permissible values (there might be
2880 // ranges before and behind the valid indices)
2881 io_rStartIndex = ::std::max( nMinActionIndex,
2882 io_rStartIndex );
2883 io_rEndIndex = ::std::min( nMaxActionIndex,
2884 io_rEndIndex );
2886 if( io_rStartIndex == io_rEndIndex ||
2887 io_rStartIndex > io_rEndIndex )
2889 // empty range, don't render anything. The second
2890 // condition e.g. happens if the requested range lies
2891 // fully before or behind the valid action indices.
2892 return false;
2896 const ActionVector::const_iterator aBegin( maActions.begin() );
2897 const ActionVector::const_iterator aEnd( maActions.end() );
2900 // find start and end action
2901 // =========================
2902 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2903 MtfAction( ActionSharedPtr(), io_rStartIndex ),
2904 UpperBoundActionIndexComparator() );
2905 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
2906 MtfAction( ActionSharedPtr(), io_rEndIndex ),
2907 UpperBoundActionIndexComparator() );
2908 return true;
2912 // Public methods
2913 // ====================================================================
2915 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
2916 const GDIMetaFile& rMtf,
2917 const Parameters& rParams ) :
2918 CanvasGraphicHelper( rCanvas ),
2919 maActions()
2921 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2923 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2924 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2925 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2926 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2928 // make sure canvas and graphic device are valid; action
2929 // creation don't check that every time
2930 if( rCanvas.get() == NULL ||
2931 !rCanvas->getUNOCanvas().is() ||
2932 !rCanvas->getUNOCanvas()->getDevice().is() )
2934 // leave actions empty
2935 return;
2938 VectorOfOutDevStates aStateStack;
2940 VirtualDevice aVDev;
2941 aVDev.EnableOutput( sal_False );
2943 // Setup VDev for state tracking and mapping
2944 // =========================================
2946 aVDev.SetMapMode( rMtf.GetPrefMapMode() );
2948 const Size aMtfSize( rMtf.GetPrefSize() );
2949 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
2950 rMtf.GetPrefMapMode() ) );
2952 // #i44110# correct null-sized output - there are shapes
2953 // which have zero size in at least one dimension
2954 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2955 ::std::max( aMtfSizePixPre.Height(), 1L ) );
2957 sal_Int32 nCurrActions(0);
2958 ActionFactoryParameters aParms(aStateStack,
2959 rCanvas,
2960 aVDev,
2961 rParams,
2962 nCurrActions );
2964 // init state stack
2965 aStateStack.clearStateStack();
2967 // Setup local state, such that the metafile renders
2968 // itself into a one-by-one square at the origin for
2969 // identity view and render transformations
2970 aStateStack.getState().transform.scale( 1.0 / aMtfSizePix.Width(),
2971 1.0 / aMtfSizePix.Height() );
2973 tools::calcLogic2PixelAffineTransform( aStateStack.getState().mapModeTransform,
2974 aVDev );
2976 ColorSharedPtr pColor( getCanvas()->createColor() );
2979 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2980 // setup default text color to black
2981 rState.textColor =
2982 rState.textFillColor =
2983 rState.textLineColor = pColor->getDeviceColor( 0x000000FF );
2986 // apply overrides from the Parameters struct
2987 if( rParams.maFillColor.is_initialized() )
2989 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2990 rState.isFillColorSet = true;
2991 rState.fillColor = pColor->getDeviceColor( *rParams.maFillColor );
2993 if( rParams.maLineColor.is_initialized() )
2995 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2996 rState.isLineColorSet = true;
2997 rState.lineColor = pColor->getDeviceColor( *rParams.maLineColor );
2999 if( rParams.maTextColor.is_initialized() )
3001 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
3002 rState.isTextFillColorSet = true;
3003 rState.isTextLineColorSet = true;
3004 rState.textColor =
3005 rState.textFillColor =
3006 rState.textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
3008 if( rParams.maFontName.is_initialized() ||
3009 rParams.maFontWeight.is_initialized() ||
3010 rParams.maFontLetterForm.is_initialized() ||
3011 rParams.maFontUnderline.is_initialized() ||
3012 rParams.maFontProportion.is_initialized() )
3014 ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
3016 rState.xFont = createFont( rState.fontRotation,
3017 ::Font(), // default font
3018 aParms );
3021 /* EMF+ */
3022 memset (aObjects, 0, sizeof (aObjects));
3023 mbMultipart = false;
3025 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
3026 // we're
3027 // changing
3028 // the
3029 // current
3030 // action
3031 // in
3032 // createActions!
3033 aParms,
3034 true // TODO(P1): make subsettability configurable
3038 ImplRenderer::~ImplRenderer()
3040 // don't leak EMFPObjects
3041 for(unsigned int i=0; i<SAL_N_ELEMENTS(aObjects); ++i)
3042 delete aObjects[i];
3045 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
3046 sal_Int32 nEndIndex ) const
3048 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3050 ActionVector::const_iterator aRangeBegin;
3051 ActionVector::const_iterator aRangeEnd;
3055 if( !getSubsetIndices( nStartIndex, nEndIndex,
3056 aRangeBegin, aRangeEnd ) )
3057 return true; // nothing to render (but _that_ was successful)
3059 // now, aRangeBegin references the action in which the
3060 // subset rendering must start, and aRangeEnd references
3061 // the action in which the subset rendering must end (it
3062 // might also end right at the start of the referenced
3063 // action, such that zero of that action needs to be
3064 // rendered).
3067 // render subset of actions
3068 // ========================
3070 ::basegfx::B2DHomMatrix aMatrix;
3071 ::canvas::tools::getRenderStateTransform( aMatrix,
3072 getRenderState() );
3074 ActionRenderer aRenderer( aMatrix );
3076 return forSubsetRange( aRenderer,
3077 aRangeBegin,
3078 aRangeEnd,
3079 nStartIndex,
3080 nEndIndex,
3081 maActions.end() );
3083 catch( uno::Exception& )
3085 SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3086 comphelper::anyToString( cppu::getCaughtException() ),
3087 RTL_TEXTENCODING_UTF8 ).getStr() );
3089 // convert error to return value
3090 return false;
3094 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3095 sal_Int32 nEndIndex ) const
3097 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3099 ActionVector::const_iterator aRangeBegin;
3100 ActionVector::const_iterator aRangeEnd;
3102 if( !getSubsetIndices( nStartIndex, nEndIndex,
3103 aRangeBegin, aRangeEnd ) )
3104 return ::basegfx::B2DRange(); // nothing to render -> empty range
3106 // now, aRangeBegin references the action in which the
3107 // subset querying must start, and aRangeEnd references
3108 // the action in which the subset querying must end (it
3109 // might also end right at the start of the referenced
3110 // action, such that zero of that action needs to be
3111 // queried).
3114 // query bounds for subset of actions
3115 // ==================================
3117 ::basegfx::B2DHomMatrix aMatrix;
3118 ::canvas::tools::getRenderStateTransform( aMatrix,
3119 getRenderState() );
3121 AreaQuery aQuery( aMatrix );
3122 forSubsetRange( aQuery,
3123 aRangeBegin,
3124 aRangeEnd,
3125 nStartIndex,
3126 nEndIndex,
3127 maActions.end() );
3129 return aQuery.getBounds();
3132 bool ImplRenderer::draw() const
3134 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3136 ::basegfx::B2DHomMatrix aMatrix;
3137 ::canvas::tools::getRenderStateTransform( aMatrix,
3138 getRenderState() );
3142 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3144 catch( uno::Exception& )
3146 SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3147 comphelper::anyToString( cppu::getCaughtException() ),
3148 RTL_TEXTENCODING_UTF8 ).getStr() );
3150 return false;
3156 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */