Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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>
82 :
83 : using namespace ::com::sun::star;
84 :
85 :
86 : // free support functions
87 : // ======================
88 : namespace
89 : {
90 0 : template < class MetaActionType > void setStateColor( MetaActionType* pAct,
91 : bool& rIsColorSet,
92 : uno::Sequence< double >& rColorSequence,
93 : const cppcanvas::CanvasSharedPtr& rCanvas )
94 : {
95 : // set rIsColorSet and check for true at the same time
96 0 : if( (rIsColorSet=pAct->IsSetting()) != false )
97 : {
98 0 : ::Color aColor( pAct->GetColor() );
99 :
100 : // force alpha part of color to
101 : // opaque. transparent painting is done
102 : // explicitly via META_TRANSPARENT_ACTION
103 0 : aColor.SetTransparency(0);
104 : //aColor.SetTransparency(128);
105 :
106 0 : rColorSequence = ::vcl::unotools::colorToDoubleSequence(
107 : aColor,
108 0 : rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
109 : }
110 0 : }
111 :
112 0 : void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes,
113 : const ::cppcanvas::internal::ActionFactoryParameters& rParms,
114 : const LineInfo& rLineInfo )
115 : {
116 0 : const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
117 : o_rStrokeAttributes.StrokeWidth =
118 0 : (rParms.mrStates.getState().mapModeTransform * aWidth).getX();
119 :
120 : // setup reasonable defaults
121 0 : o_rStrokeAttributes.MiterLimit = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
122 0 : o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
123 0 : o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
124 :
125 0 : switch(rLineInfo.GetLineJoin())
126 : {
127 : default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
128 0 : o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE;
129 0 : break;
130 : case basegfx::B2DLINEJOIN_BEVEL:
131 0 : o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL;
132 0 : break;
133 : case basegfx::B2DLINEJOIN_MITER:
134 0 : o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
135 0 : break;
136 : case basegfx::B2DLINEJOIN_ROUND:
137 0 : o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND;
138 0 : break;
139 : }
140 :
141 0 : switch(rLineInfo.GetLineCap())
142 : {
143 : default: /* com::sun::star::drawing::LineCap_BUTT */
144 : {
145 0 : o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
146 0 : o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
147 0 : break;
148 : }
149 : case com::sun::star::drawing::LineCap_ROUND:
150 : {
151 0 : o_rStrokeAttributes.StartCapType = rendering::PathCapType::ROUND;
152 0 : o_rStrokeAttributes.EndCapType = rendering::PathCapType::ROUND;
153 0 : break;
154 : }
155 : case com::sun::star::drawing::LineCap_SQUARE:
156 : {
157 0 : o_rStrokeAttributes.StartCapType = rendering::PathCapType::SQUARE;
158 0 : o_rStrokeAttributes.EndCapType = rendering::PathCapType::SQUARE;
159 0 : break;
160 : }
161 : }
162 :
163 0 : if( LINE_DASH == rLineInfo.GetStyle() )
164 : {
165 0 : const ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
166 :
167 : // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
168 :
169 : // interpret dash info only if explicitly enabled as
170 : // style
171 0 : const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
172 0 : const double nDistance( (rState.mapModeTransform * aDistance).getX() );
173 :
174 0 : const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
175 0 : const double nDashLen( (rState.mapModeTransform * aDashLen).getX() );
176 :
177 0 : const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
178 0 : const double nDotLen( (rState.mapModeTransform * aDotLen).getX() );
179 :
180 0 : const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
181 0 : 2*rLineInfo.GetDotCount() );
182 :
183 0 : o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
184 0 : double* pDashArray = o_rStrokeAttributes.DashArray.getArray();
185 :
186 :
187 : // iteratively fill dash array, first with dashs, then
188 : // with dots.
189 :
190 :
191 0 : sal_Int32 nCurrEntry=0;
192 :
193 0 : for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
194 : {
195 0 : pDashArray[nCurrEntry++] = nDashLen;
196 0 : pDashArray[nCurrEntry++] = nDistance;
197 : }
198 0 : for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
199 : {
200 0 : pDashArray[nCurrEntry++] = nDotLen;
201 0 : pDashArray[nCurrEntry++] = nDistance;
202 0 : }
203 0 : }
204 0 : }
205 :
206 :
207 : /** Create masked BitmapEx, where the white areas of rBitmap are
208 : transparent, and the other appear in rMaskColor.
209 : */
210 0 : BitmapEx createMaskBmpEx( const Bitmap& rBitmap,
211 : const ::Color& rMaskColor )
212 : {
213 0 : const ::Color aWhite( COL_WHITE );
214 0 : BitmapPalette aBiLevelPalette(2);
215 0 : aBiLevelPalette[0] = aWhite;
216 0 : aBiLevelPalette[1] = rMaskColor;
217 :
218 0 : Bitmap aMask( rBitmap.CreateMask( aWhite ));
219 : Bitmap aSolid( rBitmap.GetSizePixel(),
220 : 1,
221 0 : &aBiLevelPalette );
222 0 : aSolid.Erase( rMaskColor );
223 :
224 0 : return BitmapEx( aSolid, aMask );
225 : }
226 :
227 0 : OUString convertToLocalizedNumerals(const OUString& rStr,
228 : LanguageType eTextLanguage)
229 : {
230 0 : OUStringBuffer aBuf(rStr);
231 0 : for (sal_Int32 i = 0; i < aBuf.getLength(); ++i)
232 : {
233 0 : sal_Unicode nChar = aBuf[i];
234 0 : if (nChar >= '0' && nChar <= '9')
235 0 : aBuf[i] = GetLocalizedChar(nChar, eTextLanguage);
236 : }
237 0 : return aBuf.makeStringAndClear();
238 : }
239 : }
240 :
241 : namespace cppcanvas
242 : {
243 : namespace internal
244 : {
245 : // state stack manipulators
246 :
247 0 : void VectorOfOutDevStates::clearStateStack()
248 : {
249 0 : m_aStates.clear();
250 0 : const OutDevState aDefaultState;
251 0 : m_aStates.push_back(aDefaultState);
252 0 : }
253 :
254 0 : OutDevState& VectorOfOutDevStates::getState()
255 : {
256 0 : return m_aStates.back();
257 : }
258 :
259 0 : const OutDevState& VectorOfOutDevStates::getState() const
260 : {
261 0 : return m_aStates.back();
262 : }
263 :
264 0 : void VectorOfOutDevStates::pushState(sal_uInt16 nFlags)
265 : {
266 0 : m_aStates.push_back( getState() );
267 0 : getState().pushFlags = nFlags;
268 0 : }
269 :
270 0 : void VectorOfOutDevStates::popState()
271 : {
272 0 : if( getState().pushFlags != PUSH_ALL )
273 : {
274 : // a state is pushed which is incomplete, i.e. does not
275 : // restore everything to the previous stack level when
276 : // popped.
277 : // That means, we take the old state, and restore every
278 : // OutDevState member whose flag is set, from the new to the
279 : // old state. Then the new state gets overwritten by the
280 : // calculated state
281 :
282 : // preset to-be-calculated new state with old state
283 0 : OutDevState aCalculatedNewState( getState() );
284 :
285 : // selectively copy to-be-restored content over saved old
286 : // state
287 0 : m_aStates.pop_back();
288 :
289 0 : const OutDevState& rNewState( getState() );
290 :
291 0 : if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) )
292 : {
293 0 : aCalculatedNewState.lineColor = rNewState.lineColor;
294 0 : aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
295 : }
296 :
297 0 : if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) )
298 : {
299 0 : aCalculatedNewState.fillColor = rNewState.fillColor;
300 0 : aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
301 : }
302 :
303 0 : if( (aCalculatedNewState.pushFlags & PUSH_FONT) )
304 : {
305 0 : aCalculatedNewState.xFont = rNewState.xFont;
306 0 : aCalculatedNewState.fontRotation = rNewState.fontRotation;
307 0 : aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle;
308 0 : aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle;
309 0 : aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle;
310 0 : aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle;
311 0 : aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle;
312 0 : aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet;
313 0 : aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet;
314 0 : aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet;
315 : }
316 :
317 0 : if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) )
318 : {
319 0 : aCalculatedNewState.textColor = rNewState.textColor;
320 : }
321 :
322 0 : if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) )
323 : {
324 0 : aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
325 : }
326 :
327 0 : if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) )
328 : {
329 0 : aCalculatedNewState.clip = rNewState.clip;
330 0 : aCalculatedNewState.clipRect = rNewState.clipRect;
331 0 : aCalculatedNewState.xClipPoly = rNewState.xClipPoly;
332 : }
333 :
334 : // TODO(F2): Raster ops NYI
335 : // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
336 : // {
337 : // }
338 :
339 0 : if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) )
340 : {
341 0 : aCalculatedNewState.textFillColor = rNewState.textFillColor;
342 0 : aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
343 : }
344 :
345 0 : if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) )
346 : {
347 0 : aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
348 : }
349 :
350 : // TODO(F1): Refpoint handling NYI
351 : // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
352 : // {
353 : // }
354 :
355 0 : if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) )
356 : {
357 0 : aCalculatedNewState.textLineColor = rNewState.textLineColor;
358 0 : aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
359 : }
360 :
361 0 : if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) )
362 : {
363 0 : aCalculatedNewState.textAlignment = rNewState.textAlignment;
364 0 : aCalculatedNewState.textDirection = rNewState.textDirection;
365 : }
366 :
367 : // TODO(F2): Text language handling NYI
368 : // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
369 : // {
370 : // }
371 :
372 : // always copy push mode
373 0 : aCalculatedNewState.pushFlags = rNewState.pushFlags;
374 :
375 : // flush to stack
376 0 : getState() = aCalculatedNewState;
377 : }
378 : else
379 : {
380 0 : m_aStates.pop_back();
381 : }
382 0 : }
383 :
384 0 : bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
385 : const ActionFactoryParameters& rParms )
386 : {
387 0 : const OutDevState& rState( rParms.mrStates.getState() );
388 0 : if( (!rState.isLineColorSet &&
389 0 : !rState.isFillColorSet) ||
390 0 : (rState.lineColor.getLength() == 0 &&
391 0 : rState.fillColor.getLength() == 0) )
392 : {
393 0 : return false;
394 : }
395 :
396 : ActionSharedPtr pPolyAction(
397 : internal::PolyPolyActionFactory::createPolyPolyAction(
398 0 : rPolyPoly, rParms.mrCanvas, rState ) );
399 :
400 0 : if( pPolyAction )
401 : {
402 : maActions.push_back(
403 : MtfAction(
404 : pPolyAction,
405 0 : rParms.mrCurrActionIndex ) );
406 :
407 0 : rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
408 : }
409 :
410 0 : return true;
411 : }
412 :
413 0 : bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly,
414 : const ActionFactoryParameters& rParms )
415 : {
416 : return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
417 0 : rParms );
418 : }
419 :
420 0 : void ImplRenderer::skipContent( GDIMetaFile& rMtf,
421 : const char* pCommentString,
422 : sal_Int32& io_rCurrActionIndex ) const
423 : {
424 0 : ENSURE_OR_THROW( pCommentString,
425 : "ImplRenderer::skipContent(): NULL string given" );
426 :
427 : MetaAction* pCurrAct;
428 0 : while( (pCurrAct=rMtf.NextAction()) != NULL )
429 : {
430 : // increment action index, we've skipped an action.
431 0 : ++io_rCurrActionIndex;
432 :
433 0 : if( pCurrAct->GetType() == META_COMMENT_ACTION &&
434 0 : static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
435 0 : pCommentString) )
436 : {
437 : // requested comment found, done
438 0 : return;
439 : }
440 : }
441 :
442 : // EOF
443 0 : return;
444 : }
445 :
446 0 : bool ImplRenderer::isActionContained( GDIMetaFile& rMtf,
447 : const char* pCommentString,
448 : sal_uInt16 nType ) const
449 : {
450 0 : ENSURE_OR_THROW( pCommentString,
451 : "ImplRenderer::isActionContained(): NULL string given" );
452 :
453 0 : bool bRet( false );
454 :
455 : // at least _one_ call to GDIMetaFile::NextAction() is
456 : // executed
457 0 : sal_uIntPtr nPos( 1 );
458 :
459 : MetaAction* pCurrAct;
460 0 : while( (pCurrAct=rMtf.NextAction()) != NULL )
461 : {
462 0 : if( pCurrAct->GetType() == nType )
463 : {
464 0 : bRet = true; // action type found
465 0 : break;
466 : }
467 :
468 0 : if( pCurrAct->GetType() == META_COMMENT_ACTION &&
469 0 : static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
470 0 : pCommentString) )
471 : {
472 : // delimiting end comment found, done
473 0 : bRet = false; // not yet found
474 0 : break;
475 : }
476 :
477 0 : ++nPos;
478 : }
479 :
480 : // rewind metafile to previous position (this method must
481 : // not change the current metaaction)
482 0 : while( nPos-- )
483 0 : rMtf.WindPrev();
484 :
485 0 : if( !pCurrAct )
486 : {
487 : // EOF, and not yet found
488 0 : bRet = false;
489 : }
490 :
491 0 : return bRet;
492 : }
493 :
494 0 : void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly,
495 : const ::Gradient& rGradient,
496 : const ActionFactoryParameters& rParms,
497 : bool bIsPolygonRectangle,
498 : bool bSubsettableActions )
499 : {
500 : DBG_TESTSOLARMUTEX();
501 :
502 0 : ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
503 0 : aDevicePoly.transform( rParms.mrStates.getState().mapModeTransform );
504 :
505 : // decide, whether this gradient can be rendered natively
506 : // by the canvas, or must be emulated via VCL gradient
507 : // action extraction.
508 0 : const sal_uInt16 nSteps( rGradient.GetSteps() );
509 :
510 0 : if( // step count is infinite, can use native canvas
511 : // gradients here
512 0 : nSteps == 0 ||
513 : // step count is sufficiently high, such that no
514 : // discernible difference should be visible.
515 : nSteps > 64 )
516 : {
517 : uno::Reference< lang::XMultiServiceFactory> xFactory(
518 0 : rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
519 :
520 0 : if( xFactory.is() )
521 : {
522 0 : rendering::Texture aTexture;
523 :
524 0 : aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
525 0 : aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
526 0 : aTexture.Alpha = 1.0;
527 :
528 :
529 : // setup start/end color values
530 :
531 :
532 : // scale color coefficients with gradient intensities
533 0 : const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() );
534 0 : ::Color aVCLStartColor( rGradient.GetStartColor() );
535 0 : aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) );
536 0 : aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
537 0 : aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) );
538 :
539 0 : const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() );
540 0 : ::Color aVCLEndColor( rGradient.GetEndColor() );
541 0 : aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) );
542 0 : aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
543 0 : aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) );
544 :
545 : uno::Reference<rendering::XColorSpace> xColorSpace(
546 0 : rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
547 : const uno::Sequence< double > aStartColor(
548 : ::vcl::unotools::colorToDoubleSequence( aVCLStartColor,
549 0 : xColorSpace ));
550 : const uno::Sequence< double > aEndColor(
551 : ::vcl::unotools::colorToDoubleSequence( aVCLEndColor,
552 0 : xColorSpace ));
553 :
554 0 : uno::Sequence< uno::Sequence < double > > aColors(2);
555 0 : uno::Sequence< double > aStops(2);
556 :
557 0 : if( rGradient.GetStyle() == GradientStyle_AXIAL )
558 : {
559 0 : aStops.realloc(3);
560 0 : aColors.realloc(3);
561 :
562 0 : aStops[0] = 0.0;
563 0 : aStops[1] = 0.5;
564 0 : aStops[2] = 1.0;
565 :
566 0 : aColors[0] = aEndColor;
567 0 : aColors[1] = aStartColor;
568 0 : aColors[2] = aEndColor;
569 : }
570 : else
571 : {
572 0 : aStops[0] = 0.0;
573 0 : aStops[1] = 1.0;
574 :
575 0 : aColors[0] = aStartColor;
576 0 : aColors[1] = aEndColor;
577 : }
578 :
579 : const ::basegfx::B2DRectangle aBounds(
580 0 : ::basegfx::tools::getRange(aDevicePoly) );
581 : const ::basegfx::B2DVector aOffset(
582 0 : rGradient.GetOfsX() / 100.0,
583 0 : rGradient.GetOfsY() / 100.0);
584 0 : double fRotation( rGradient.GetAngle() * M_PI / 1800.0 );
585 0 : const double fBorder( rGradient.GetBorder() / 100.0 );
586 :
587 0 : basegfx::B2DHomMatrix aRot90;
588 0 : aRot90.rotate(M_PI_2);
589 :
590 0 : basegfx::ODFGradientInfo aGradInfo;
591 0 : OUString aGradientService;
592 0 : switch( rGradient.GetStyle() )
593 : {
594 : case GradientStyle_LINEAR:
595 0 : aGradInfo = basegfx::tools::createLinearODFGradientInfo(
596 : aBounds,
597 : nSteps,
598 : fBorder,
599 0 : fRotation);
600 : // map ODF to svg gradient orientation - x
601 : // instead of y direction
602 0 : aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);
603 0 : aGradientService = "LinearGradient";
604 0 : break;
605 :
606 : case GradientStyle_AXIAL:
607 : {
608 : // Adapt the border so that it is suitable
609 : // for the axial gradient. An axial
610 : // gradient consists of two linear
611 : // gradients. Each of those covers half
612 : // of the total size. In order to
613 : // compensate for the condensed display of
614 : // the linear gradients, we have to
615 : // enlarge the area taken up by the actual
616 : // gradient (1-fBorder). After that we
617 : // have to turn the result back into a
618 : // border value, hence the second (left
619 : // most 1-...
620 0 : const double fAxialBorder (1-2*(1-fBorder));
621 0 : aGradInfo = basegfx::tools::createAxialODFGradientInfo(
622 : aBounds,
623 : nSteps,
624 : fAxialBorder,
625 0 : fRotation);
626 : // map ODF to svg gradient orientation - x
627 : // instead of y direction
628 0 : aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);
629 :
630 : // map ODF axial gradient to 3-stop linear
631 : // gradient - shift left by 0.5
632 0 : basegfx::B2DHomMatrix aShift;
633 :
634 0 : aShift.translate(-0.5,0);
635 0 : aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aShift);
636 0 : aGradientService = "LinearGradient";
637 0 : break;
638 : }
639 :
640 : case GradientStyle_RADIAL:
641 0 : aGradInfo = basegfx::tools::createRadialODFGradientInfo(
642 : aBounds,
643 : aOffset,
644 : nSteps,
645 0 : fBorder);
646 0 : aGradientService = "EllipticalGradient";
647 0 : break;
648 :
649 : case GradientStyle_ELLIPTICAL:
650 0 : aGradInfo = basegfx::tools::createEllipticalODFGradientInfo(
651 : aBounds,
652 : aOffset,
653 : nSteps,
654 : fBorder,
655 0 : fRotation);
656 0 : aGradientService = "EllipticalGradient";
657 0 : break;
658 :
659 : case GradientStyle_SQUARE:
660 0 : aGradInfo = basegfx::tools::createSquareODFGradientInfo(
661 : aBounds,
662 : aOffset,
663 : nSteps,
664 : fBorder,
665 0 : fRotation);
666 0 : aGradientService = "RectangularGradient";
667 0 : break;
668 :
669 : case GradientStyle_RECT:
670 0 : aGradInfo = basegfx::tools::createRectangularODFGradientInfo(
671 : aBounds,
672 : aOffset,
673 : nSteps,
674 : fBorder,
675 0 : fRotation);
676 0 : aGradientService = "RectangularGradient";
677 0 : break;
678 :
679 : default:
680 0 : ENSURE_OR_THROW( false,
681 : "ImplRenderer::createGradientAction(): Unexpected gradient type" );
682 : break;
683 : }
684 :
685 : ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
686 0 : aGradInfo.getTextureTransform() );
687 :
688 0 : uno::Sequence<uno::Any> args(3);
689 0 : beans::PropertyValue aProp;
690 0 : aProp.Name = "Colors";
691 0 : aProp.Value <<= aColors;
692 0 : args[0] <<= aProp;
693 0 : aProp.Name = "Stops";
694 0 : aProp.Value <<= aStops;
695 0 : args[1] <<= aProp;
696 0 : aProp.Name = "AspectRatio";
697 0 : aProp.Value <<= aGradInfo.getAspectRatio();
698 0 : args[2] <<= aProp;
699 :
700 : aTexture.Gradient.set(
701 0 : xFactory->createInstanceWithArguments(aGradientService,
702 0 : args),
703 0 : uno::UNO_QUERY);
704 0 : if( aTexture.Gradient.is() )
705 : {
706 : ActionSharedPtr pPolyAction(
707 : internal::PolyPolyActionFactory::createPolyPolyAction(
708 : aDevicePoly,
709 : rParms.mrCanvas,
710 0 : rParms.mrStates.getState(),
711 0 : aTexture ) );
712 :
713 0 : if( pPolyAction )
714 : {
715 : maActions.push_back(
716 : MtfAction(
717 : pPolyAction,
718 0 : rParms.mrCurrActionIndex ) );
719 :
720 0 : rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
721 : }
722 :
723 : // done, using native gradients
724 0 : return;
725 0 : }
726 0 : }
727 : }
728 :
729 : // cannot currently use native canvas gradients, as a
730 : // finite step size is given (this funny feature is not
731 : // supported by the XCanvas API)
732 0 : rParms.mrStates.pushState(PUSH_ALL);
733 :
734 0 : if( !bIsPolygonRectangle )
735 : {
736 : // only clip, if given polygon is not a rectangle in
737 : // the first place (the gradient is always limited to
738 : // the given bound rect)
739 : updateClipping(
740 : aDevicePoly,
741 : rParms,
742 0 : true );
743 : }
744 :
745 0 : GDIMetaFile aTmpMtf;
746 : rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(),
747 : rGradient,
748 0 : aTmpMtf );
749 :
750 0 : createActions( aTmpMtf, rParms, bSubsettableActions );
751 :
752 0 : rParms.mrStates.popState();
753 : }
754 :
755 0 : uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation,
756 : const ::Font& rFont,
757 : const ActionFactoryParameters& rParms ) const
758 : {
759 0 : rendering::FontRequest aFontRequest;
760 :
761 0 : if( rParms.mrParms.maFontName.is_initialized() )
762 0 : aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName;
763 : else
764 0 : aFontRequest.FontDescription.FamilyName = rFont.GetName();
765 :
766 0 : aFontRequest.FontDescription.StyleName = rFont.GetStyleName();
767 :
768 0 : aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
769 0 : aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;
770 :
771 : // TODO(F2): improve vclenum->panose conversion
772 : aFontRequest.FontDescription.FontDescription.Weight =
773 0 : rParms.mrParms.maFontWeight.is_initialized() ?
774 0 : *rParms.mrParms.maFontWeight :
775 0 : ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
776 : aFontRequest.FontDescription.FontDescription.Letterform =
777 0 : rParms.mrParms.maFontLetterForm.is_initialized() ?
778 0 : *rParms.mrParms.maFontLetterForm :
779 0 : (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
780 : aFontRequest.FontDescription.FontDescription.Proportion =
781 0 : rParms.mrParms.maFontProportion.is_initialized() ?
782 0 : *rParms.mrParms.maFontProportion :
783 0 : (rFont.GetPitch() == PITCH_FIXED)
784 : ? rendering::PanoseProportion::MONO_SPACED
785 0 : : rendering::PanoseProportion::ANYTHING;
786 :
787 0 : LanguageType aLang = rFont.GetLanguage();
788 0 : aFontRequest.Locale = LanguageTag::convertToLocale( aLang, false);
789 :
790 : // setup state-local text transformation,
791 : // if the font be rotated
792 0 : const short nFontAngle( rFont.GetOrientation() );
793 0 : if( nFontAngle != 0 )
794 : {
795 : // set to unity transform rotated by font angle
796 0 : const double nAngle( nFontAngle * (F_PI / 1800.0) );
797 0 : o_rFontRotation = -nAngle;
798 : }
799 : else
800 : {
801 0 : o_rFontRotation = 0.0;
802 : }
803 :
804 0 : geometry::Matrix2D aFontMatrix;
805 0 : ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
806 :
807 : // TODO(F2): use correct scale direction, font
808 : // height might be width or anything else
809 :
810 : // TODO(Q3): This code smells of programming by
811 : // coincidence (the next two if statements)
812 0 : const ::Size rFontSizeLog( rFont.GetSize() );
813 0 : const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
814 0 : if( nFontWidthLog != 0 )
815 : {
816 0 : ::Font aTestFont = rFont;
817 0 : aTestFont.SetWidth( 0 );
818 0 : sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
819 0 : if( nNormalWidth != nFontWidthLog )
820 0 : if( nNormalWidth )
821 0 : aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
822 : }
823 :
824 : // #i52608# apply map mode scale also to font matrix - an
825 : // anisotrophic mapmode must be reflected in an
826 : // anisotrophic font matrix scale.
827 0 : const OutDevState& rState( rParms.mrStates.getState() );
828 0 : if( !::basegfx::fTools::equal(
829 0 : rState.mapModeTransform.get(0,0),
830 0 : rState.mapModeTransform.get(1,1)) )
831 : {
832 0 : const double nScaleX( rState.mapModeTransform.get(0,0) );
833 0 : const double nScaleY( rState.mapModeTransform.get(1,1) );
834 :
835 : // note: no reason to check for division by zero, we
836 : // always have the value closer (or equal) to zero as
837 : // the nominator.
838 0 : if( fabs(nScaleX) < fabs(nScaleY) )
839 0 : aFontMatrix.m00 *= nScaleX / nScaleY;
840 : else
841 0 : aFontMatrix.m11 *= nScaleY / nScaleX;
842 : }
843 0 : aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
844 :
845 0 : return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
846 : uno::Sequence< beans::PropertyValue >(),
847 0 : aFontMatrix );
848 : }
849 :
850 : // create text effects such as shadow/relief/embossed
851 0 : void ImplRenderer::createTextAction( const ::Point& rStartPoint,
852 : const OUString& rString,
853 : int nIndex,
854 : int nLength,
855 : const sal_Int32* pCharWidths,
856 : const ActionFactoryParameters& rParms,
857 : bool bSubsettableActions )
858 : {
859 0 : ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.getLength() + nIndex,
860 : "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
861 :
862 0 : if( !nLength )
863 0 : return; // zero-length text, no visible output
864 :
865 0 : const OutDevState& rState( rParms.mrStates.getState() );
866 :
867 : // TODO(F2): implement all text effects
868 : // if( rState.textAlignment ); // TODO(F2): NYI
869 :
870 0 : ::Color aShadowColor( COL_AUTO );
871 0 : ::Color aReliefColor( COL_AUTO );
872 0 : ::Size aShadowOffset;
873 0 : ::Size aReliefOffset;
874 :
875 : uno::Reference<rendering::XColorSpace> xColorSpace(
876 0 : rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
877 :
878 0 : if( rState.isTextEffectShadowSet )
879 : {
880 : // calculate shadow offset (similar to outdev3.cxx)
881 : // TODO(F3): better match with outdev3.cxx
882 0 : sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
883 0 : if( nShadowOffset < 1 )
884 0 : nShadowOffset = 1;
885 :
886 0 : aShadowOffset.setWidth( nShadowOffset );
887 0 : aShadowOffset.setHeight( nShadowOffset );
888 :
889 : // determine shadow color (from outdev3.cxx)
890 : ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
891 0 : rState.textColor, xColorSpace );
892 0 : bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
893 0 : || (aTextColor.GetLuminance() < 8);
894 :
895 0 : aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
896 0 : aShadowColor.SetTransparency( aTextColor.GetTransparency() );
897 : }
898 :
899 0 : if( rState.textReliefStyle )
900 : {
901 : // calculate relief offset (similar to outdev3.cxx)
902 0 : sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
903 0 : nReliefOffset += nReliefOffset/2;
904 0 : if( nReliefOffset < 1 )
905 0 : nReliefOffset = 1;
906 :
907 0 : if( rState.textReliefStyle == RELIEF_ENGRAVED )
908 0 : nReliefOffset = -nReliefOffset;
909 :
910 0 : aReliefOffset.setWidth( nReliefOffset );
911 0 : aReliefOffset.setHeight( nReliefOffset );
912 :
913 : // determine relief color (from outdev3.cxx)
914 : ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
915 0 : rState.textColor, xColorSpace );
916 :
917 0 : aReliefColor = ::Color( COL_LIGHTGRAY );
918 :
919 : // we don't have a automatic color, so black is always
920 : // drawn on white (literally copied from
921 : // vcl/source/gdi/outdev3.cxx)
922 0 : if( aTextColor.GetColor() == COL_BLACK )
923 : {
924 0 : aTextColor = ::Color( COL_WHITE );
925 0 : rParms.mrStates.getState().textColor =
926 : ::vcl::unotools::colorToDoubleSequence(
927 0 : aTextColor, xColorSpace );
928 : }
929 :
930 0 : if( aTextColor.GetColor() == COL_WHITE )
931 0 : aReliefColor = ::Color( COL_BLACK );
932 0 : aReliefColor.SetTransparency( aTextColor.GetTransparency() );
933 : }
934 :
935 : // create the actual text action
936 : ActionSharedPtr pTextAction(
937 : TextActionFactory::createTextAction(
938 : rStartPoint,
939 : aReliefOffset,
940 : aReliefColor,
941 : aShadowOffset,
942 : aShadowColor,
943 : rString,
944 : nIndex,
945 : nLength,
946 : pCharWidths,
947 : rParms.mrVDev,
948 : rParms.mrCanvas,
949 : rState,
950 : rParms.mrParms,
951 0 : bSubsettableActions ) );
952 :
953 0 : ActionSharedPtr pStrikeoutTextAction;
954 :
955 0 : if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
956 : {
957 0 : long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
958 :
959 : sal_Unicode pChars[4];
960 0 : if ( rState.textStrikeoutStyle == STRIKEOUT_X )
961 0 : pChars[0] = 'X';
962 : else
963 0 : pChars[0] = '/';
964 0 : pChars[3]=pChars[2]=pChars[1]=pChars[0];
965 :
966 : long nStrikeoutWidth = (rParms.mrVDev.GetTextWidth(
967 0 : OUString(pChars, SAL_N_ELEMENTS(pChars))) + 2) / 4;
968 :
969 0 : if( nStrikeoutWidth <= 0 )
970 0 : nStrikeoutWidth = 1;
971 :
972 0 : long nMaxWidth = nStrikeoutWidth/2;
973 0 : if ( nMaxWidth < 2 )
974 0 : nMaxWidth = 2;
975 0 : nMaxWidth += nWidth + 1;
976 :
977 0 : long nFullStrikeoutWidth = 0;
978 0 : OUString aStrikeoutText;
979 0 : while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
980 0 : aStrikeoutText += OUString(pChars[0]);
981 :
982 0 : sal_Int32 nLen = aStrikeoutText.getLength();
983 :
984 0 : if( nLen )
985 : {
986 0 : long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
987 0 : nStrikeoutWidth += nInterval;
988 0 : sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen];
989 :
990 0 : for ( int i = 0;i<nLen; i++)
991 : {
992 0 : pStrikeoutCharWidths[i] = nStrikeoutWidth;
993 : }
994 :
995 0 : for ( int i = 1;i< nLen; i++ )
996 : {
997 0 : pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
998 : }
999 :
1000 0 : sal_Int32 nStartPos = 0;
1001 :
1002 0 : pStrikeoutTextAction =
1003 : TextActionFactory::createTextAction(
1004 : rStartPoint,
1005 : aReliefOffset,
1006 : aReliefColor,
1007 : aShadowOffset,
1008 : aShadowColor,
1009 : aStrikeoutText,
1010 : nStartPos,
1011 : aStrikeoutText.getLength(),
1012 : pStrikeoutCharWidths,
1013 : rParms.mrVDev,
1014 : rParms.mrCanvas,
1015 : rState,
1016 : rParms.mrParms,
1017 0 : bSubsettableActions ) ;
1018 0 : }
1019 : }
1020 :
1021 0 : if( pTextAction )
1022 : {
1023 : maActions.push_back(
1024 : MtfAction(
1025 : pTextAction,
1026 0 : rParms.mrCurrActionIndex ) );
1027 :
1028 0 : if ( pStrikeoutTextAction )
1029 : {
1030 : maActions.push_back(
1031 : MtfAction(
1032 : pStrikeoutTextAction,
1033 0 : rParms.mrCurrActionIndex ) );
1034 : }
1035 :
1036 0 : rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1037 0 : }
1038 : }
1039 :
1040 0 : void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1041 : const ActionFactoryParameters& rParms,
1042 : bool bIntersect )
1043 : {
1044 0 : ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1045 0 : ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1046 :
1047 0 : const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1048 0 : const bool bEmptyClipPoly( rState.clip.count() == 0 );
1049 :
1050 0 : ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1051 : "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1052 :
1053 0 : if( !bIntersect ||
1054 0 : (bEmptyClipRect && bEmptyClipPoly) )
1055 : {
1056 0 : rState.clip = rClipPoly;
1057 : }
1058 : else
1059 : {
1060 0 : if( !bEmptyClipRect )
1061 : {
1062 : // TODO(P3): Use Liang-Barsky polygon clip here,
1063 : // after all, one object is just a rectangle!
1064 :
1065 : // convert rect to polygon beforehand, must revert
1066 : // to general polygon clipping here.
1067 0 : rState.clip = ::basegfx::B2DPolyPolygon(
1068 : ::basegfx::tools::createPolygonFromRect(
1069 : // #121100# VCL rectangular clips always
1070 : // include one more pixel to the right
1071 : // and the bottom
1072 0 : ::basegfx::B2DRectangle( rState.clipRect.Left(),
1073 0 : rState.clipRect.Top(),
1074 0 : rState.clipRect.Right()+1,
1075 0 : rState.clipRect.Bottom()+1 ) ) );
1076 : }
1077 :
1078 : // AW: Simplified
1079 0 : rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1080 0 : aClipPoly, rState.clip, true, false);
1081 : }
1082 :
1083 : // by now, our clip resides in the OutDevState::clip
1084 : // poly-polygon.
1085 0 : rState.clipRect.SetEmpty();
1086 :
1087 0 : if( rState.clip.count() == 0 )
1088 : {
1089 0 : if( rState.clipRect.IsEmpty() )
1090 : {
1091 0 : rState.xClipPoly.clear();
1092 : }
1093 : else
1094 : {
1095 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1096 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1097 : ::basegfx::B2DPolyPolygon(
1098 : ::basegfx::tools::createPolygonFromRect(
1099 : // #121100# VCL rectangular clips
1100 : // always include one more pixel to
1101 : // the right and the bottom
1102 0 : ::basegfx::B2DRectangle( rState.clipRect.Left(),
1103 0 : rState.clipRect.Top(),
1104 0 : rState.clipRect.Right()+1,
1105 0 : rState.clipRect.Bottom()+1 ) ) ) );
1106 : }
1107 : }
1108 : else
1109 : {
1110 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1111 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1112 0 : rState.clip );
1113 0 : }
1114 0 : }
1115 :
1116 0 : void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
1117 : const ActionFactoryParameters& rParms,
1118 : bool bIntersect )
1119 : {
1120 0 : ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1121 :
1122 0 : const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1123 0 : const bool bEmptyClipPoly( rState.clip.count() == 0 );
1124 :
1125 0 : ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1126 : "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1127 :
1128 0 : if( !bIntersect ||
1129 0 : (bEmptyClipRect && bEmptyClipPoly) )
1130 : {
1131 0 : rState.clipRect = rClipRect;
1132 0 : rState.clip.clear();
1133 : }
1134 0 : else if( bEmptyClipPoly )
1135 : {
1136 0 : rState.clipRect.Intersection( rClipRect );
1137 0 : rState.clip.clear();
1138 : }
1139 : else
1140 : {
1141 : // TODO(P3): Handle a fourth case here, when all clip
1142 : // polygons are rectangular, once B2DMultiRange's
1143 : // sweep line implementation is done.
1144 :
1145 : // general case: convert to polygon and clip
1146 :
1147 :
1148 : // convert rect to polygon beforehand, must revert
1149 : // to general polygon clipping here.
1150 : ::basegfx::B2DPolyPolygon aClipPoly(
1151 : ::basegfx::tools::createPolygonFromRect(
1152 0 : ::basegfx::B2DRectangle( rClipRect.Left(),
1153 0 : rClipRect.Top(),
1154 0 : rClipRect.Right(),
1155 0 : rClipRect.Bottom() ) ) );
1156 :
1157 0 : rState.clipRect.SetEmpty();
1158 :
1159 : // AW: Simplified
1160 0 : rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1161 0 : aClipPoly, rState.clip, true, false);
1162 : }
1163 :
1164 0 : if( rState.clip.count() == 0 )
1165 : {
1166 0 : if( rState.clipRect.IsEmpty() )
1167 : {
1168 0 : rState.xClipPoly.clear();
1169 : }
1170 : else
1171 : {
1172 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1173 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1174 : ::basegfx::B2DPolyPolygon(
1175 : ::basegfx::tools::createPolygonFromRect(
1176 : // #121100# VCL rectangular clips
1177 : // always include one more pixel to
1178 : // the right and the bottom
1179 0 : ::basegfx::B2DRectangle( rState.clipRect.Left(),
1180 0 : rState.clipRect.Top(),
1181 0 : rState.clipRect.Right()+1,
1182 0 : rState.clipRect.Bottom()+1 ) ) ) );
1183 : }
1184 : }
1185 : else
1186 : {
1187 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1188 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1189 0 : rState.clip );
1190 : }
1191 0 : }
1192 :
1193 0 : bool ImplRenderer::createActions( GDIMetaFile& rMtf,
1194 : const ActionFactoryParameters& rFactoryParms,
1195 : bool bSubsettableActions )
1196 : {
1197 : /* TODO(P2): interpret mtf-comments
1198 : ================================
1199 :
1200 : - gradient fillings (do that via comments)
1201 :
1202 : - think about mapping. _If_ we do everything in logical
1203 : coordinates (which would solve the probs for stroke
1204 : widths and text offsets), then we would have to
1205 : recalc scaling for every drawing operation. This is
1206 : because the outdev map mode might change at any time.
1207 : Also keep in mind, that, although we've double precision
1208 : float arithmetic now, different offsets might still
1209 : generate different roundings (aka
1210 : 'OutputDevice::SetPixelOffset())
1211 :
1212 : */
1213 :
1214 : // alias common parameters
1215 0 : VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1216 0 : const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1217 0 : ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1218 0 : const Parameters& rParms(rFactoryParms.mrParms);
1219 0 : sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1220 :
1221 :
1222 : // Loop over every metaaction
1223 : // ==========================
1224 : MetaAction* pCurrAct;
1225 :
1226 : // TODO(P1): think about caching
1227 0 : for( pCurrAct=rMtf.FirstAction();
1228 : pCurrAct;
1229 : pCurrAct = rMtf.NextAction() )
1230 : {
1231 : // execute every action, to keep VDev state up-to-date
1232 : // currently used only for
1233 : // - the map mode
1234 : // - the line/fill color when processing a META_TRANSPARENT_ACTION
1235 : // - SetFont to process font metric specific actions
1236 0 : pCurrAct->Execute( &rVDev );
1237 :
1238 : SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << pCurrAct->GetType() << " (" << pCurrAct->GetType() << ")");
1239 :
1240 0 : switch( pCurrAct->GetType() )
1241 : {
1242 :
1243 :
1244 : // In the first part of this monster-switch, we
1245 : // handle all state-changing meta actions. These
1246 : // are all handled locally.
1247 :
1248 :
1249 :
1250 : case META_PUSH_ACTION:
1251 : {
1252 0 : MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1253 0 : rStates.pushState(pPushAction->GetFlags());
1254 : }
1255 0 : break;
1256 :
1257 : case META_POP_ACTION:
1258 0 : rStates.popState();
1259 0 : break;
1260 :
1261 : case META_TEXTLANGUAGE_ACTION:
1262 : // FALLTHROUGH intended
1263 : case META_REFPOINT_ACTION:
1264 : // handled via pCurrAct->Execute( &rVDev )
1265 0 : break;
1266 :
1267 : case META_MAPMODE_ACTION:
1268 : // modify current mapModeTransformation
1269 : // transformation, such that subsequent
1270 : // coordinates map correctly
1271 0 : tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
1272 0 : rVDev );
1273 0 : break;
1274 :
1275 : // monitor clip regions, to assemble clip polygon on our own
1276 : case META_CLIPREGION_ACTION:
1277 : {
1278 0 : MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1279 :
1280 0 : if( !pClipAction->IsClipping() )
1281 : {
1282 : // clear clipping
1283 0 : rStates.getState().clip.clear();
1284 : }
1285 : else
1286 : {
1287 0 : if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1288 : {
1289 : VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1290 : "region encountered, falling back to bounding box!" );
1291 :
1292 : // #121806# explicitly kept integer
1293 : Rectangle aClipRect(
1294 : rVDev.LogicToPixel(
1295 0 : pClipAction->GetRegion().GetBoundRect() ) );
1296 :
1297 : // intersect current clip with given rect
1298 : updateClipping(
1299 : aClipRect,
1300 : rFactoryParms,
1301 0 : false );
1302 : }
1303 : else
1304 : {
1305 : // set new clip polygon (don't intersect
1306 : // with old one, just set it)
1307 :
1308 : // #121806# explicitly kept integer
1309 0 : basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1310 :
1311 0 : aPolyPolygon.transform(rVDev.GetViewTransformation());
1312 : updateClipping(
1313 : aPolyPolygon,
1314 : rFactoryParms,
1315 0 : false );
1316 : }
1317 : }
1318 :
1319 0 : break;
1320 : }
1321 :
1322 : case META_ISECTRECTCLIPREGION_ACTION:
1323 : {
1324 0 : MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1325 :
1326 : // #121806# explicitly kept integer
1327 : Rectangle aClipRect(
1328 0 : rVDev.LogicToPixel( pClipAction->GetRect() ) );
1329 :
1330 : // intersect current clip with given rect
1331 : updateClipping(
1332 : aClipRect,
1333 : rFactoryParms,
1334 0 : true );
1335 :
1336 0 : break;
1337 : }
1338 :
1339 : case META_ISECTREGIONCLIPREGION_ACTION:
1340 : {
1341 0 : MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1342 :
1343 0 : if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1344 : {
1345 : VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1346 : "region encountered, falling back to bounding box!" );
1347 :
1348 : // #121806# explicitly kept integer
1349 : Rectangle aClipRect(
1350 0 : rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1351 :
1352 : // intersect current clip with given rect
1353 : updateClipping(
1354 : aClipRect,
1355 : rFactoryParms,
1356 0 : true );
1357 : }
1358 : else
1359 : {
1360 : // intersect current clip with given clip polygon
1361 :
1362 : // #121806# explicitly kept integer
1363 0 : basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1364 :
1365 0 : aPolyPolygon.transform(rVDev.GetViewTransformation());
1366 : updateClipping(
1367 : aPolyPolygon,
1368 : rFactoryParms,
1369 0 : true );
1370 : }
1371 :
1372 0 : break;
1373 : }
1374 :
1375 : case META_MOVECLIPREGION_ACTION:
1376 : // TODO(F2): NYI
1377 0 : break;
1378 :
1379 : case META_LINECOLOR_ACTION:
1380 0 : if( !rParms.maLineColor.is_initialized() )
1381 : {
1382 : setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1383 0 : rStates.getState().isLineColorSet,
1384 0 : rStates.getState().lineColor,
1385 0 : rCanvas );
1386 : }
1387 : else
1388 : {
1389 : // #120994# Do switch on/off LineColor, even when a overriding one is set
1390 0 : bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting());
1391 :
1392 0 : rStates.getState().isLineColorSet = bSetting;
1393 : }
1394 0 : break;
1395 :
1396 : case META_FILLCOLOR_ACTION:
1397 0 : if( !rParms.maFillColor.is_initialized() )
1398 : {
1399 : setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1400 0 : rStates.getState().isFillColorSet,
1401 0 : rStates.getState().fillColor,
1402 0 : rCanvas );
1403 : }
1404 : else
1405 : {
1406 : // #120994# Do switch on/off FillColor, even when a overriding one is set
1407 0 : bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting());
1408 :
1409 0 : rStates.getState().isFillColorSet = bSetting;
1410 : }
1411 0 : break;
1412 :
1413 : case META_TEXTCOLOR_ACTION:
1414 : {
1415 0 : if( !rParms.maTextColor.is_initialized() )
1416 : {
1417 : // Text color is set unconditionally, thus, no
1418 : // use of setStateColor here
1419 0 : ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1420 :
1421 : // force alpha part of color to
1422 : // opaque. transparent painting is done
1423 : // explicitly via META_TRANSPARENT_ACTION
1424 0 : aColor.SetTransparency(0);
1425 :
1426 0 : rStates.getState().textColor =
1427 : ::vcl::unotools::colorToDoubleSequence(
1428 : aColor,
1429 0 : rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1430 : }
1431 : }
1432 0 : break;
1433 :
1434 : case META_TEXTFILLCOLOR_ACTION:
1435 0 : if( !rParms.maTextColor.is_initialized() )
1436 : {
1437 : setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1438 0 : rStates.getState().isTextFillColorSet,
1439 0 : rStates.getState().textFillColor,
1440 0 : rCanvas );
1441 : }
1442 : else
1443 : {
1444 : // #120994# Do switch on/off TextFillColor, even when a overriding one is set
1445 0 : bool bSetting(static_cast<MetaTextFillColorAction*>(pCurrAct)->IsSetting());
1446 :
1447 0 : rStates.getState().isTextFillColorSet = bSetting;
1448 : }
1449 0 : break;
1450 :
1451 : case META_TEXTLINECOLOR_ACTION:
1452 0 : if( !rParms.maTextColor.is_initialized() )
1453 : {
1454 : setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1455 0 : rStates.getState().isTextLineColorSet,
1456 0 : rStates.getState().textLineColor,
1457 0 : rCanvas );
1458 : }
1459 : else
1460 : {
1461 : // #120994# Do switch on/off TextLineColor, even when a overriding one is set
1462 0 : bool bSetting(static_cast<MetaTextLineColorAction*>(pCurrAct)->IsSetting());
1463 :
1464 0 : rStates.getState().isTextLineColorSet = bSetting;
1465 : }
1466 0 : break;
1467 :
1468 : case META_TEXTALIGN_ACTION:
1469 : {
1470 0 : ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1471 0 : const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1472 :
1473 0 : rState.textReferencePoint = eTextAlign;
1474 : }
1475 0 : break;
1476 :
1477 : case META_FONT_ACTION:
1478 : {
1479 0 : ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1480 0 : const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1481 :
1482 0 : rState.xFont = createFont( rState.fontRotation,
1483 : rFont,
1484 0 : rFactoryParms );
1485 :
1486 : // TODO(Q2): define and use appropriate enumeration types
1487 0 : rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
1488 0 : rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
1489 0 : rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ?
1490 0 : (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1491 0 : (sal_Int8)rFont.GetUnderline();
1492 0 : rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
1493 0 : rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
1494 0 : rState.isTextEffectShadowSet = rFont.IsShadow();
1495 0 : rState.isTextWordUnderlineSet = rFont.IsWordLineMode();
1496 0 : rState.isTextOutlineModeSet = rFont.IsOutline();
1497 : }
1498 0 : break;
1499 :
1500 : case META_RASTEROP_ACTION:
1501 : // TODO(F2): NYI
1502 0 : break;
1503 :
1504 : case META_LAYOUTMODE_ACTION:
1505 : {
1506 : // TODO(F2): A lot is missing here
1507 0 : int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1508 0 : ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1509 0 : switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
1510 : {
1511 : case TEXT_LAYOUT_BIDI_LTR:
1512 0 : rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1513 0 : break;
1514 :
1515 : case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
1516 0 : rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1517 0 : break;
1518 :
1519 : case TEXT_LAYOUT_BIDI_RTL:
1520 0 : rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1521 0 : break;
1522 :
1523 : case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
1524 0 : rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1525 0 : break;
1526 : }
1527 :
1528 0 : rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1529 0 : if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1530 0 : && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1531 : {
1532 0 : rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1533 : }
1534 : }
1535 0 : break;
1536 :
1537 :
1538 :
1539 : // In the second part of this monster-switch, we
1540 : // handle all recursing meta actions. These are the
1541 : // ones generating a metafile by themselves, which is
1542 : // then processed by recursively calling this method.
1543 :
1544 :
1545 :
1546 : case META_GRADIENT_ACTION:
1547 : {
1548 0 : MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1549 0 : createGradientAction( ::Polygon( pGradAct->GetRect() ),
1550 0 : pGradAct->GetGradient(),
1551 : rFactoryParms,
1552 : true,
1553 0 : bSubsettableActions );
1554 : }
1555 0 : break;
1556 :
1557 : case META_HATCH_ACTION:
1558 : {
1559 : // TODO(F2): use native Canvas hatches here
1560 0 : GDIMetaFile aTmpMtf;
1561 :
1562 0 : rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1563 0 : static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1564 0 : aTmpMtf );
1565 : createActions( aTmpMtf, rFactoryParms,
1566 0 : bSubsettableActions );
1567 : }
1568 0 : break;
1569 :
1570 : case META_EPS_ACTION:
1571 : {
1572 0 : MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
1573 0 : const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
1574 :
1575 : // #121806# explicitly kept integer
1576 0 : const Size aMtfSize( rSubstitute.GetPrefSize() );
1577 : const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1578 0 : rSubstitute.GetPrefMapMode() ) );
1579 :
1580 : // #i44110# correct null-sized output - there
1581 : // are metafiles which have zero size in at
1582 : // least one dimension
1583 0 : const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1584 0 : ::std::max( aMtfSizePixPre.Height(), 1L ) );
1585 :
1586 : // Setup local transform, such that the
1587 : // metafile renders itself into the given
1588 : // output rectangle
1589 0 : rStates.pushState(PUSH_ALL);
1590 :
1591 0 : rVDev.Push();
1592 0 : rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1593 :
1594 0 : const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1595 0 : const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1596 :
1597 0 : rStates.getState().transform.translate( rPos.X(),
1598 0 : rPos.Y() );
1599 0 : rStates.getState().transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1600 0 : (double)rSize.Height() / aMtfSizePix.Height() );
1601 :
1602 0 : createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1603 : rFactoryParms,
1604 0 : bSubsettableActions );
1605 :
1606 0 : rVDev.Pop();
1607 0 : rStates.popState();
1608 : }
1609 0 : break;
1610 :
1611 : // handle metafile comments, to retrieve
1612 : // meta-information for gradients, fills and
1613 : // strokes. May skip actions, and may recurse.
1614 : case META_COMMENT_ACTION:
1615 : {
1616 0 : MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1617 :
1618 : // Handle gradients
1619 0 : if (pAct->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
1620 : {
1621 0 : MetaGradientExAction* pGradAction = NULL;
1622 0 : bool bDone( false );
1623 0 : while( !bDone &&
1624 : (pCurrAct=rMtf.NextAction()) != NULL )
1625 : {
1626 0 : switch( pCurrAct->GetType() )
1627 : {
1628 : // extract gradient info
1629 : case META_GRADIENTEX_ACTION:
1630 0 : pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1631 0 : break;
1632 :
1633 : // skip broken-down rendering, output gradient when sequence is ended
1634 : case META_COMMENT_ACTION:
1635 0 : if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") )
1636 : {
1637 0 : bDone = true;
1638 :
1639 0 : if( pGradAction )
1640 : {
1641 0 : createGradientAction( pGradAction->GetPolyPolygon(),
1642 0 : pGradAction->GetGradient(),
1643 : rFactoryParms,
1644 : false,
1645 0 : bSubsettableActions );
1646 : }
1647 : }
1648 0 : break;
1649 : }
1650 : }
1651 : }
1652 : // TODO(P2): Handle drawing layer strokes, via
1653 : // XPATHSTROKE_SEQ_BEGIN comment
1654 :
1655 : // Handle drawing layer fills
1656 0 : else if( pAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1657 : {
1658 0 : const sal_uInt8* pData = pAct->GetData();
1659 0 : if ( pData )
1660 : {
1661 0 : SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1662 :
1663 0 : SvtGraphicFill aFill;
1664 0 : ReadSvtGraphicFill( aMemStm, aFill );
1665 :
1666 : // TODO(P2): Also handle gradients and
1667 : // hatches like this
1668 :
1669 : // only evaluate comment for pure
1670 : // bitmap fills. If a transparency
1671 : // gradient is involved (denoted by
1672 : // the FloatTransparent action), take
1673 : // the normal meta actions.
1674 0 : if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1675 : !isActionContained( rMtf,
1676 : "XPATHFILL_SEQ_END",
1677 0 : META_FLOATTRANSPARENT_ACTION ) )
1678 : {
1679 0 : rendering::Texture aTexture;
1680 :
1681 : // TODO(F1): the SvtGraphicFill
1682 : // can also transport metafiles
1683 : // here, handle that case, too
1684 0 : Graphic aGraphic;
1685 0 : aFill.getGraphic( aGraphic );
1686 :
1687 0 : BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1688 0 : const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1689 :
1690 0 : ::SvtGraphicFill::Transform aTransform;
1691 0 : aFill.getTransform( aTransform );
1692 :
1693 0 : ::basegfx::B2DHomMatrix aMatrix;
1694 :
1695 : // convert to basegfx matrix
1696 0 : aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1697 0 : aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1698 0 : aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1699 0 : aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1700 0 : aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1701 0 : aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1702 :
1703 0 : ::basegfx::B2DHomMatrix aScale;
1704 0 : aScale.scale( aBmpSize.Width(),
1705 0 : aBmpSize.Height() );
1706 :
1707 : // post-multiply with the bitmap
1708 : // size (XCanvas' texture assumes
1709 : // the given bitmap to be
1710 : // normalized to [0,1]x[0,1]
1711 : // rectangle)
1712 0 : aMatrix = aMatrix * aScale;
1713 :
1714 : // pre-multiply with the
1715 : // logic-to-pixel scale factor
1716 : // (the metafile comment works in
1717 : // logical coordinates).
1718 0 : ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1719 : aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1720 0 : rVDev );
1721 :
1722 : ::basegfx::unotools::affineMatrixFromHomMatrix(
1723 : aTexture.AffineTransform,
1724 0 : aMatrix );
1725 :
1726 0 : aTexture.Alpha = 1.0 - aFill.getTransparency();
1727 0 : aTexture.Bitmap =
1728 : ::vcl::unotools::xBitmapFromBitmapEx(
1729 0 : rCanvas->getUNOCanvas()->getDevice(),
1730 0 : aBmpEx );
1731 0 : if( aFill.isTiling() )
1732 : {
1733 0 : aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1734 0 : aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1735 : }
1736 : else
1737 : {
1738 0 : aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1739 0 : aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1740 : }
1741 :
1742 0 : ::PolyPolygon aPath;
1743 0 : aFill.getPath( aPath );
1744 :
1745 0 : ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1746 0 : aPoly.transform( rStates.getState().mapModeTransform );
1747 : ActionSharedPtr pPolyAction(
1748 : internal::PolyPolyActionFactory::createPolyPolyAction(
1749 : aPoly,
1750 : rCanvas,
1751 0 : rStates.getState(),
1752 0 : aTexture ) );
1753 :
1754 0 : if( pPolyAction )
1755 : {
1756 : maActions.push_back(
1757 : MtfAction(
1758 : pPolyAction,
1759 0 : io_rCurrActionIndex ) );
1760 :
1761 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1762 : }
1763 :
1764 : // skip broken-down render output
1765 : skipContent( rMtf,
1766 : "XPATHFILL_SEQ_END",
1767 0 : io_rCurrActionIndex );
1768 0 : }
1769 : }
1770 : }
1771 : // Handle drawing layer fills
1772 0 : else if( pAct->GetComment() == "EMF_PLUS" ) {
1773 : static int count = -1, limit = 0x7fffffff;
1774 0 : if (count == -1) {
1775 0 : count = 0;
1776 0 : if (char *env = getenv ("EMF_PLUS_LIMIT")) {
1777 0 : limit = atoi (env);
1778 : SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit);
1779 : }
1780 : }
1781 : SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct->GetDataSize ());
1782 0 : if (count < limit)
1783 0 : processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
1784 0 : count ++;
1785 0 : } else if( pAct->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1786 : SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct->GetDataSize ());
1787 :
1788 0 : SvMemoryStream rMF ((void*) pAct->GetData (), pAct->GetDataSize (), STREAM_READ);
1789 :
1790 0 : rMF.ReadInt32( nFrameLeft ).ReadInt32( nFrameTop ).ReadInt32( nFrameRight ).ReadInt32( nFrameBottom );
1791 : SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft << "," << nFrameTop << " - " << nFrameRight << "," << nFrameBottom);
1792 0 : rMF.ReadInt32( nPixX ).ReadInt32( nPixY ).ReadInt32( nMmX ).ReadInt32( nMmY );
1793 : SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX << "x" << nPixY << " mm size: " << nMmX << "x" << nMmY);
1794 :
1795 0 : ReadXForm( rMF, aBaseTransform );
1796 : //aWorldTransform.Set (aBaseTransform);
1797 : }
1798 : }
1799 0 : break;
1800 :
1801 :
1802 :
1803 : // In the third part of this monster-switch, we
1804 : // handle all 'acting' meta actions. These are all
1805 : // processed by constructing function objects for
1806 : // them, which will later ease caching.
1807 :
1808 :
1809 :
1810 : case META_POINT_ACTION:
1811 : {
1812 0 : const OutDevState& rState( rStates.getState() );
1813 0 : if( rState.lineColor.getLength() )
1814 : {
1815 : ActionSharedPtr pPointAction(
1816 : internal::PointActionFactory::createPointAction(
1817 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1818 0 : static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1819 : rCanvas,
1820 0 : rState ) );
1821 :
1822 0 : if( pPointAction )
1823 : {
1824 : maActions.push_back(
1825 : MtfAction(
1826 : pPointAction,
1827 0 : io_rCurrActionIndex ) );
1828 :
1829 0 : io_rCurrActionIndex += pPointAction->getActionCount()-1;
1830 0 : }
1831 : }
1832 : }
1833 0 : break;
1834 :
1835 : case META_PIXEL_ACTION:
1836 : {
1837 0 : const OutDevState& rState( rStates.getState() );
1838 0 : if( rState.lineColor.getLength() )
1839 : {
1840 : ActionSharedPtr pPointAction(
1841 : internal::PointActionFactory::createPointAction(
1842 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1843 0 : static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1844 : rCanvas,
1845 : rState,
1846 0 : static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1847 :
1848 0 : if( pPointAction )
1849 : {
1850 : maActions.push_back(
1851 : MtfAction(
1852 : pPointAction,
1853 0 : io_rCurrActionIndex ) );
1854 :
1855 0 : io_rCurrActionIndex += pPointAction->getActionCount()-1;
1856 0 : }
1857 : }
1858 : }
1859 0 : break;
1860 :
1861 : case META_LINE_ACTION:
1862 : {
1863 0 : const OutDevState& rState( rStates.getState() );
1864 0 : if( rState.lineColor.getLength() )
1865 : {
1866 0 : MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1867 :
1868 0 : const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1869 :
1870 : const ::basegfx::B2DPoint aStartPoint(
1871 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1872 : const ::basegfx::B2DPoint aEndPoint(
1873 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1874 :
1875 0 : ActionSharedPtr pLineAction;
1876 :
1877 0 : if( rLineInfo.IsDefault() )
1878 : {
1879 : // plain hair line
1880 0 : pLineAction =
1881 : internal::LineActionFactory::createLineAction(
1882 : aStartPoint,
1883 : aEndPoint,
1884 : rCanvas,
1885 0 : rState );
1886 :
1887 0 : if( pLineAction )
1888 : {
1889 : maActions.push_back(
1890 : MtfAction(
1891 : pLineAction,
1892 0 : io_rCurrActionIndex ) );
1893 :
1894 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
1895 : }
1896 : }
1897 0 : else if( LINE_NONE != rLineInfo.GetStyle() )
1898 : {
1899 : // 'thick' line
1900 0 : rendering::StrokeAttributes aStrokeAttributes;
1901 :
1902 : setupStrokeAttributes( aStrokeAttributes,
1903 : rFactoryParms,
1904 0 : rLineInfo );
1905 :
1906 : // XCanvas can only stroke polygons,
1907 : // not simple lines - thus, handle
1908 : // this case via the polypolygon
1909 : // action
1910 0 : ::basegfx::B2DPolygon aPoly;
1911 0 : aPoly.append( aStartPoint );
1912 0 : aPoly.append( aEndPoint );
1913 0 : pLineAction =
1914 : internal::PolyPolyActionFactory::createPolyPolyAction(
1915 : ::basegfx::B2DPolyPolygon( aPoly ),
1916 0 : rCanvas, rState, aStrokeAttributes );
1917 :
1918 0 : if( pLineAction )
1919 : {
1920 : maActions.push_back(
1921 : MtfAction(
1922 : pLineAction,
1923 0 : io_rCurrActionIndex ) );
1924 :
1925 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
1926 0 : }
1927 0 : }
1928 : // else: line style is default
1929 : // (i.e. invisible), don't generate action
1930 : }
1931 : }
1932 0 : break;
1933 :
1934 : case META_RECT_ACTION:
1935 : {
1936 : const Rectangle& rRect(
1937 0 : static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
1938 :
1939 0 : if( rRect.IsEmpty() )
1940 0 : break;
1941 :
1942 0 : const OutDevState& rState( rStates.getState() );
1943 : const ::basegfx::B2DPoint aTopLeftPixel(
1944 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
1945 : const ::basegfx::B2DPoint aBottomRightPixel(
1946 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1947 : // #121100# OutputDevice::DrawRect() fills
1948 : // rectangles Apple-like, i.e. with one
1949 : // additional pixel to the right and bottom.
1950 0 : ::basegfx::B2DPoint(1,1) );
1951 :
1952 : createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1953 : ::basegfx::B2DRange( aTopLeftPixel,
1954 : aBottomRightPixel )),
1955 0 : rFactoryParms );
1956 0 : break;
1957 : }
1958 :
1959 : case META_ROUNDRECT_ACTION:
1960 : {
1961 : const Rectangle& rRect(
1962 0 : static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
1963 :
1964 0 : if( rRect.IsEmpty() )
1965 0 : break;
1966 :
1967 : ::basegfx::B2DPolygon aPoly(
1968 : ::basegfx::tools::createPolygonFromRect(
1969 : ::basegfx::B2DRange(
1970 : ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1971 0 : ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1972 : ::basegfx::B2DPoint(1,1) ),
1973 0 : ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
1974 0 : ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
1975 0 : aPoly.transform( rStates.getState().mapModeTransform );
1976 :
1977 : createFillAndStroke( aPoly,
1978 0 : rFactoryParms );
1979 : }
1980 0 : break;
1981 :
1982 : case META_ELLIPSE_ACTION:
1983 : {
1984 : const Rectangle& rRect(
1985 0 : static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
1986 :
1987 0 : if( rRect.IsEmpty() )
1988 0 : break;
1989 :
1990 : const ::basegfx::B2DRange aRange(
1991 : ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1992 0 : ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1993 0 : ::basegfx::B2DPoint(1,1) );
1994 :
1995 : ::basegfx::B2DPolygon aPoly(
1996 : ::basegfx::tools::createPolygonFromEllipse(
1997 : aRange.getCenter(),
1998 : aRange.getWidth(),
1999 0 : aRange.getHeight() ));
2000 0 : aPoly.transform( rStates.getState().mapModeTransform );
2001 :
2002 : createFillAndStroke( aPoly,
2003 0 : rFactoryParms );
2004 : }
2005 0 : break;
2006 :
2007 : case META_ARC_ACTION:
2008 : {
2009 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2010 0 : const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2011 0 : static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2012 0 : static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2013 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2014 0 : aPoly.transform( rStates.getState().mapModeTransform );
2015 :
2016 : createFillAndStroke( aPoly,
2017 0 : rFactoryParms );
2018 : }
2019 0 : break;
2020 :
2021 : case META_PIE_ACTION:
2022 : {
2023 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2024 0 : const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2025 0 : static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2026 0 : static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2027 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2028 0 : aPoly.transform( rStates.getState().mapModeTransform );
2029 :
2030 : createFillAndStroke( aPoly,
2031 0 : rFactoryParms );
2032 : }
2033 0 : break;
2034 :
2035 : case META_CHORD_ACTION:
2036 : {
2037 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2038 0 : const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2039 0 : static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2040 0 : static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2041 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2042 0 : aPoly.transform( rStates.getState().mapModeTransform );
2043 :
2044 : createFillAndStroke( aPoly,
2045 0 : rFactoryParms );
2046 : }
2047 0 : break;
2048 :
2049 : case META_POLYLINE_ACTION:
2050 : {
2051 0 : const OutDevState& rState( rStates.getState() );
2052 0 : if( rState.lineColor.getLength() ||
2053 0 : rState.fillColor.getLength() )
2054 : {
2055 0 : MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2056 :
2057 0 : const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2058 0 : ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2059 0 : aPoly.transform( rState.mapModeTransform );
2060 :
2061 0 : ActionSharedPtr pLineAction;
2062 :
2063 0 : if( rLineInfo.IsDefault() )
2064 : {
2065 : // plain hair line polygon
2066 0 : pLineAction =
2067 : internal::PolyPolyActionFactory::createLinePolyPolyAction(
2068 : ::basegfx::B2DPolyPolygon(aPoly),
2069 : rCanvas,
2070 0 : rState );
2071 :
2072 0 : if( pLineAction )
2073 : {
2074 : maActions.push_back(
2075 : MtfAction(
2076 : pLineAction,
2077 0 : io_rCurrActionIndex ) );
2078 :
2079 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
2080 : }
2081 : }
2082 0 : else if( LINE_NONE != rLineInfo.GetStyle() )
2083 : {
2084 : // 'thick' line polygon
2085 0 : rendering::StrokeAttributes aStrokeAttributes;
2086 :
2087 : setupStrokeAttributes( aStrokeAttributes,
2088 : rFactoryParms,
2089 0 : rLineInfo );
2090 :
2091 0 : pLineAction =
2092 : internal::PolyPolyActionFactory::createPolyPolyAction(
2093 : ::basegfx::B2DPolyPolygon(aPoly),
2094 : rCanvas,
2095 : rState,
2096 0 : aStrokeAttributes ) ;
2097 :
2098 0 : if( pLineAction )
2099 : {
2100 : maActions.push_back(
2101 : MtfAction(
2102 : pLineAction,
2103 0 : io_rCurrActionIndex ) );
2104 :
2105 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
2106 0 : }
2107 0 : }
2108 : // else: line style is default
2109 : // (i.e. invisible), don't generate action
2110 : }
2111 : }
2112 0 : break;
2113 :
2114 : case META_POLYGON_ACTION:
2115 : {
2116 0 : ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2117 0 : aPoly.transform( rStates.getState().mapModeTransform );
2118 : createFillAndStroke( aPoly,
2119 0 : rFactoryParms );
2120 : }
2121 0 : break;
2122 :
2123 : case META_POLYPOLYGON_ACTION:
2124 : {
2125 0 : ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2126 0 : aPoly.transform( rStates.getState().mapModeTransform );
2127 : createFillAndStroke( aPoly,
2128 0 : rFactoryParms );
2129 : }
2130 0 : break;
2131 :
2132 : case META_BMP_ACTION:
2133 : {
2134 0 : MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2135 :
2136 : ActionSharedPtr pBmpAction(
2137 : internal::BitmapActionFactory::createBitmapAction(
2138 0 : pAct->GetBitmap(),
2139 0 : rStates.getState().mapModeTransform *
2140 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2141 : rCanvas,
2142 0 : rStates.getState() ) );
2143 :
2144 0 : if( pBmpAction )
2145 : {
2146 : maActions.push_back(
2147 : MtfAction(
2148 : pBmpAction,
2149 0 : io_rCurrActionIndex ) );
2150 :
2151 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2152 0 : }
2153 : }
2154 0 : break;
2155 :
2156 : case META_BMPSCALE_ACTION:
2157 : {
2158 0 : MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2159 :
2160 : ActionSharedPtr pBmpAction(
2161 : internal::BitmapActionFactory::createBitmapAction(
2162 0 : pAct->GetBitmap(),
2163 0 : rStates.getState().mapModeTransform *
2164 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2165 0 : rStates.getState().mapModeTransform *
2166 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2167 : rCanvas,
2168 0 : rStates.getState() ) );
2169 :
2170 0 : if( pBmpAction )
2171 : {
2172 : maActions.push_back(
2173 : MtfAction(
2174 : pBmpAction,
2175 0 : io_rCurrActionIndex ) );
2176 :
2177 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2178 0 : }
2179 : }
2180 0 : break;
2181 :
2182 : case META_BMPSCALEPART_ACTION:
2183 : {
2184 0 : MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2185 :
2186 : // crop bitmap to given source rectangle (no
2187 : // need to copy and convert the whole bitmap)
2188 0 : Bitmap aBmp( pAct->GetBitmap() );
2189 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2190 0 : pAct->GetSrcSize() );
2191 0 : aBmp.Crop( aCropRect );
2192 :
2193 : ActionSharedPtr pBmpAction(
2194 : internal::BitmapActionFactory::createBitmapAction(
2195 : aBmp,
2196 0 : rStates.getState().mapModeTransform *
2197 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2198 0 : rStates.getState().mapModeTransform *
2199 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2200 : rCanvas,
2201 0 : rStates.getState() ) );
2202 :
2203 0 : if( pBmpAction )
2204 : {
2205 : maActions.push_back(
2206 : MtfAction(
2207 : pBmpAction,
2208 0 : io_rCurrActionIndex ) );
2209 :
2210 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2211 0 : }
2212 : }
2213 0 : break;
2214 :
2215 : case META_BMPEX_ACTION:
2216 : {
2217 0 : MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2218 :
2219 : ActionSharedPtr pBmpAction(
2220 : internal::BitmapActionFactory::createBitmapAction(
2221 0 : pAct->GetBitmapEx(),
2222 0 : rStates.getState().mapModeTransform *
2223 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2224 : rCanvas,
2225 0 : rStates.getState() ) );
2226 :
2227 0 : if( pBmpAction )
2228 : {
2229 : maActions.push_back(
2230 : MtfAction(
2231 : pBmpAction,
2232 0 : io_rCurrActionIndex ) );
2233 :
2234 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2235 0 : }
2236 : }
2237 0 : break;
2238 :
2239 : case META_BMPEXSCALE_ACTION:
2240 : {
2241 0 : MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2242 :
2243 : ActionSharedPtr pBmpAction(
2244 : internal::BitmapActionFactory::createBitmapAction(
2245 0 : pAct->GetBitmapEx(),
2246 0 : rStates.getState().mapModeTransform *
2247 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2248 0 : rStates.getState().mapModeTransform *
2249 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2250 : rCanvas,
2251 0 : rStates.getState() ) );
2252 :
2253 0 : if( pBmpAction )
2254 : {
2255 : maActions.push_back(
2256 : MtfAction(
2257 : pBmpAction,
2258 0 : io_rCurrActionIndex ) );
2259 :
2260 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2261 0 : }
2262 : }
2263 0 : break;
2264 :
2265 : case META_BMPEXSCALEPART_ACTION:
2266 : {
2267 0 : MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2268 :
2269 : // crop bitmap to given source rectangle (no
2270 : // need to copy and convert the whole bitmap)
2271 0 : BitmapEx aBmp( pAct->GetBitmapEx() );
2272 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2273 0 : pAct->GetSrcSize() );
2274 0 : aBmp.Crop( aCropRect );
2275 :
2276 : ActionSharedPtr pBmpAction(
2277 : internal::BitmapActionFactory::createBitmapAction(
2278 : aBmp,
2279 0 : rStates.getState().mapModeTransform *
2280 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2281 0 : rStates.getState().mapModeTransform *
2282 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2283 : rCanvas,
2284 0 : rStates.getState() ) );
2285 :
2286 0 : if( pBmpAction )
2287 : {
2288 : maActions.push_back(
2289 : MtfAction(
2290 : pBmpAction,
2291 0 : io_rCurrActionIndex ) );
2292 :
2293 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2294 0 : }
2295 : }
2296 0 : break;
2297 :
2298 : case META_MASK_ACTION:
2299 : {
2300 0 : MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2301 :
2302 : // create masked BitmapEx right here, as the
2303 : // canvas does not provide equivalent
2304 : // functionality
2305 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2306 0 : pAct->GetColor() ));
2307 :
2308 : ActionSharedPtr pBmpAction(
2309 : internal::BitmapActionFactory::createBitmapAction(
2310 : aBmp,
2311 0 : rStates.getState().mapModeTransform *
2312 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2313 : rCanvas,
2314 0 : rStates.getState() ) );
2315 :
2316 0 : if( pBmpAction )
2317 : {
2318 : maActions.push_back(
2319 : MtfAction(
2320 : pBmpAction,
2321 0 : io_rCurrActionIndex ) );
2322 :
2323 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2324 0 : }
2325 : }
2326 0 : break;
2327 :
2328 : case META_MASKSCALE_ACTION:
2329 : {
2330 0 : MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2331 :
2332 : // create masked BitmapEx right here, as the
2333 : // canvas does not provide equivalent
2334 : // functionality
2335 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2336 0 : pAct->GetColor() ));
2337 :
2338 : ActionSharedPtr pBmpAction(
2339 : internal::BitmapActionFactory::createBitmapAction(
2340 : aBmp,
2341 0 : rStates.getState().mapModeTransform *
2342 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2343 0 : rStates.getState().mapModeTransform *
2344 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2345 : rCanvas,
2346 0 : rStates.getState() ) );
2347 :
2348 0 : if( pBmpAction )
2349 : {
2350 : maActions.push_back(
2351 : MtfAction(
2352 : pBmpAction,
2353 0 : io_rCurrActionIndex ) );
2354 :
2355 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2356 0 : }
2357 : }
2358 0 : break;
2359 :
2360 : case META_MASKSCALEPART_ACTION:
2361 : {
2362 0 : MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2363 :
2364 : // create masked BitmapEx right here, as the
2365 : // canvas does not provide equivalent
2366 : // functionality
2367 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2368 0 : pAct->GetColor() ));
2369 :
2370 : // crop bitmap to given source rectangle (no
2371 : // need to copy and convert the whole bitmap)
2372 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2373 0 : pAct->GetSrcSize() );
2374 0 : aBmp.Crop( aCropRect );
2375 :
2376 : ActionSharedPtr pBmpAction(
2377 : internal::BitmapActionFactory::createBitmapAction(
2378 : aBmp,
2379 0 : rStates.getState().mapModeTransform *
2380 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2381 0 : rStates.getState().mapModeTransform *
2382 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2383 : rCanvas,
2384 0 : rStates.getState() ) );
2385 :
2386 0 : if( pBmpAction )
2387 : {
2388 : maActions.push_back(
2389 : MtfAction(
2390 : pBmpAction,
2391 0 : io_rCurrActionIndex ) );
2392 :
2393 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2394 0 : }
2395 : }
2396 0 : break;
2397 :
2398 : case META_GRADIENTEX_ACTION:
2399 : // TODO(F1): use native Canvas gradients here
2400 : // action is ignored here, because redundant to META_GRADIENT_ACTION
2401 0 : break;
2402 :
2403 : case META_WALLPAPER_ACTION:
2404 : // TODO(F2): NYI
2405 0 : break;
2406 :
2407 : case META_TRANSPARENT_ACTION:
2408 : {
2409 0 : const OutDevState& rState( rStates.getState() );
2410 0 : if( rState.lineColor.getLength() ||
2411 0 : rState.fillColor.getLength() )
2412 : {
2413 0 : MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2414 0 : ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2415 0 : aPoly.transform( rState.mapModeTransform );
2416 :
2417 : ActionSharedPtr pPolyAction(
2418 : internal::PolyPolyActionFactory::createPolyPolyAction(
2419 : aPoly,
2420 : rCanvas,
2421 : rState,
2422 0 : pAct->GetTransparence() ) );
2423 :
2424 0 : if( pPolyAction )
2425 : {
2426 : maActions.push_back(
2427 : MtfAction(
2428 : pPolyAction,
2429 0 : io_rCurrActionIndex ) );
2430 :
2431 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2432 0 : }
2433 : }
2434 : }
2435 0 : break;
2436 :
2437 : case META_FLOATTRANSPARENT_ACTION:
2438 : {
2439 0 : MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2440 :
2441 : internal::MtfAutoPtr pMtf(
2442 0 : new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2443 :
2444 : // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2445 : internal::GradientAutoPtr pGradient(
2446 0 : new Gradient( pAct->GetGradient() ) );
2447 :
2448 : DBG_TESTSOLARMUTEX();
2449 :
2450 : ActionSharedPtr pFloatTransAction(
2451 : internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2452 : pMtf,
2453 : pGradient,
2454 : rParms,
2455 0 : rStates.getState().mapModeTransform *
2456 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2457 0 : rStates.getState().mapModeTransform *
2458 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2459 : rCanvas,
2460 0 : rStates.getState() ) );
2461 :
2462 0 : if( pFloatTransAction )
2463 : {
2464 : maActions.push_back(
2465 : MtfAction(
2466 : pFloatTransAction,
2467 0 : io_rCurrActionIndex ) );
2468 :
2469 0 : io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2470 0 : }
2471 : }
2472 0 : break;
2473 :
2474 : case META_TEXT_ACTION:
2475 : {
2476 0 : MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2477 0 : OUString sText = pAct->GetText();
2478 :
2479 0 : if (rVDev.GetDigitLanguage())
2480 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2481 :
2482 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2483 :
2484 : createTextAction(
2485 0 : pAct->GetPoint(),
2486 : sText,
2487 0 : pAct->GetIndex(),
2488 : nLen,
2489 : NULL,
2490 : rFactoryParms,
2491 0 : bSubsettableActions );
2492 : }
2493 0 : break;
2494 :
2495 : case META_TEXTARRAY_ACTION:
2496 : {
2497 0 : MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2498 0 : OUString sText = pAct->GetText();
2499 :
2500 0 : if (rVDev.GetDigitLanguage())
2501 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2502 :
2503 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2504 :
2505 : createTextAction(
2506 0 : pAct->GetPoint(),
2507 : sText,
2508 0 : pAct->GetIndex(),
2509 : nLen,
2510 0 : pAct->GetDXArray(),
2511 : rFactoryParms,
2512 0 : bSubsettableActions );
2513 : }
2514 0 : break;
2515 :
2516 : case META_TEXTLINE_ACTION:
2517 : {
2518 0 : MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2519 :
2520 0 : const OutDevState& rState( rStates.getState() );
2521 : const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2522 0 : rVDev ) );
2523 : const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2524 0 : ::basegfx::B2DSize(pAct->GetWidth(),
2525 0 : 0 ));
2526 :
2527 : ActionSharedPtr pPolyAction(
2528 : PolyPolyActionFactory::createPolyPolyAction(
2529 : tools::createTextLinesPolyPolygon(
2530 0 : rState.mapModeTransform *
2531 : ::basegfx::B2DPoint(
2532 0 : ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2533 : ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2534 0 : aSize.getX(),
2535 : tools::createTextLineInfo( rVDev,
2536 : rState )),
2537 : rCanvas,
2538 0 : rState ) );
2539 :
2540 0 : if( pPolyAction.get() )
2541 : {
2542 : maActions.push_back(
2543 : MtfAction(
2544 : pPolyAction,
2545 0 : io_rCurrActionIndex ) );
2546 :
2547 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2548 0 : }
2549 : }
2550 0 : break;
2551 :
2552 : case META_TEXTRECT_ACTION:
2553 : {
2554 0 : MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2555 :
2556 0 : rStates.pushState(PUSH_ALL);
2557 :
2558 : // use the VDev to break up the text rect
2559 : // action into readily formatted lines
2560 0 : GDIMetaFile aTmpMtf;
2561 0 : rVDev.AddTextRectActions( pAct->GetRect(),
2562 0 : pAct->GetText(),
2563 0 : pAct->GetStyle(),
2564 0 : aTmpMtf );
2565 :
2566 : createActions( aTmpMtf,
2567 : rFactoryParms,
2568 0 : bSubsettableActions );
2569 :
2570 0 : rStates.popState();
2571 :
2572 0 : break;
2573 : }
2574 :
2575 : case META_STRETCHTEXT_ACTION:
2576 : {
2577 0 : MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2578 0 : OUString sText = pAct->GetText();
2579 :
2580 0 : if (rVDev.GetDigitLanguage())
2581 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2582 :
2583 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2584 :
2585 : // #i70897# Nothing to do, actually...
2586 0 : if( nLen == 0 )
2587 0 : break;
2588 :
2589 : // have to fit the text into the given
2590 : // width. This is achieved by internally
2591 : // generating a DX array, and uniformly
2592 : // distributing the excess/insufficient width
2593 : // to every logical character.
2594 0 : ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
2595 :
2596 0 : rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2597 0 : pAct->GetIndex(), pAct->GetLen() );
2598 :
2599 0 : const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2600 :
2601 : // Last entry of pDXArray contains total width of the text
2602 0 : sal_Int32* p=pDXArray.get();
2603 0 : for (sal_Int32 i=1; i<=nLen; ++i)
2604 : {
2605 : // calc ratio for every array entry, to
2606 : // distribute rounding errors 'evenly'
2607 : // across the characters. Note that each
2608 : // entry represents the 'end' position of
2609 : // the corresponding character, thus, we
2610 : // let i run from 1 to nLen.
2611 0 : *p++ += (sal_Int32)i*nWidthDifference/nLen;
2612 : }
2613 :
2614 : createTextAction(
2615 0 : pAct->GetPoint(),
2616 : sText,
2617 0 : pAct->GetIndex(),
2618 : nLen,
2619 0 : pDXArray.get(),
2620 : rFactoryParms,
2621 0 : bSubsettableActions );
2622 : }
2623 0 : break;
2624 :
2625 : default:
2626 : SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2627 0 : break;
2628 : }
2629 :
2630 : // increment action index (each mtf action counts _at
2631 : // least_ one. Some count for more, therefore,
2632 : // io_rCurrActionIndex is sometimes incremented by
2633 : // pAct->getActionCount()-1 above, the -1 being the
2634 : // correction for the unconditional increment here).
2635 0 : ++io_rCurrActionIndex;
2636 : }
2637 :
2638 0 : return true;
2639 : }
2640 :
2641 :
2642 : namespace
2643 : {
2644 0 : class ActionRenderer
2645 : {
2646 : public:
2647 0 : ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2648 : maTransformation( rTransformation ),
2649 0 : mbRet( true )
2650 : {
2651 0 : }
2652 :
2653 0 : bool result() const
2654 : {
2655 0 : return mbRet;
2656 : }
2657 :
2658 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2659 : {
2660 : // ANDing the result. We want to fail if at least
2661 : // one action failed.
2662 0 : mbRet &= rAction.mpAction->render( maTransformation );
2663 0 : }
2664 :
2665 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2666 : const Action::Subset& rSubset )
2667 : {
2668 : // ANDing the result. We want to fail if at least
2669 : // one action failed.
2670 0 : mbRet &= rAction.mpAction->renderSubset( maTransformation,
2671 0 : rSubset );
2672 0 : }
2673 :
2674 : private:
2675 : ::basegfx::B2DHomMatrix maTransformation;
2676 : bool mbRet;
2677 : };
2678 :
2679 0 : class AreaQuery
2680 : {
2681 : public:
2682 0 : AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2683 : maTransformation( rTransformation ),
2684 0 : maBounds()
2685 : {
2686 0 : }
2687 :
2688 0 : bool result() const
2689 : {
2690 0 : return true; // nothing can fail here
2691 : }
2692 :
2693 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2694 : {
2695 0 : maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2696 0 : }
2697 :
2698 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2699 : const Action::Subset& rSubset )
2700 : {
2701 0 : maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2702 0 : rSubset ) );
2703 0 : }
2704 :
2705 0 : ::basegfx::B2DRange getBounds() const
2706 : {
2707 0 : return maBounds;
2708 : }
2709 :
2710 : private:
2711 : ::basegfx::B2DHomMatrix maTransformation;
2712 : ::basegfx::B2DRange maBounds;
2713 : };
2714 :
2715 : // Doing that via inline class. Compilers tend to not inline free
2716 : // functions.
2717 : struct UpperBoundActionIndexComparator
2718 : {
2719 0 : bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2720 : const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2721 : {
2722 : const sal_Int32 nLHSCount( rLHS.mpAction ?
2723 0 : rLHS.mpAction->getActionCount() : 0 );
2724 : const sal_Int32 nRHSCount( rRHS.mpAction ?
2725 0 : rRHS.mpAction->getActionCount() : 0 );
2726 :
2727 : // compare end of action range, to have an action selected
2728 : // by lower_bound even if the requested index points in
2729 : // the middle of the action's range
2730 0 : return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2731 : }
2732 : };
2733 :
2734 : /** Algorithm to apply given functor to a subset range
2735 :
2736 : @tpl Functor
2737 :
2738 : Functor to call for each element of the subset
2739 : range. Must provide the following method signatures:
2740 : bool result() (returning false if operation failed)
2741 :
2742 : */
2743 : template< typename Functor > bool
2744 0 : forSubsetRange( Functor& rFunctor,
2745 : ImplRenderer::ActionVector::const_iterator aRangeBegin,
2746 : ImplRenderer::ActionVector::const_iterator aRangeEnd,
2747 : sal_Int32 nStartIndex,
2748 : sal_Int32 nEndIndex,
2749 : const ImplRenderer::ActionVector::const_iterator& rEnd )
2750 : {
2751 0 : if( aRangeBegin == aRangeEnd )
2752 : {
2753 : // only a single action. Setup subset, and call functor
2754 : Action::Subset aSubset;
2755 0 : aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2756 0 : nStartIndex - aRangeBegin->mnOrigIndex );
2757 0 : aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2758 0 : nEndIndex - aRangeBegin->mnOrigIndex );
2759 :
2760 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2761 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2762 :
2763 0 : rFunctor( *aRangeBegin, aSubset );
2764 : }
2765 : else
2766 : {
2767 : // more than one action.
2768 :
2769 : // render partial first, full intermediate, and
2770 : // partial last action
2771 : Action::Subset aSubset;
2772 0 : aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2773 0 : nStartIndex - aRangeBegin->mnOrigIndex );
2774 0 : aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2775 :
2776 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2777 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2778 :
2779 0 : rFunctor( *aRangeBegin, aSubset );
2780 :
2781 : // first action rendered, skip to next
2782 0 : ++aRangeBegin;
2783 :
2784 : // render full middle actions
2785 0 : while( aRangeBegin != aRangeEnd )
2786 0 : rFunctor( *aRangeBegin++ );
2787 :
2788 0 : if( aRangeEnd == rEnd ||
2789 0 : aRangeEnd->mnOrigIndex > nEndIndex )
2790 : {
2791 : // aRangeEnd denotes end of action vector,
2792 :
2793 : // or
2794 :
2795 : // nEndIndex references something _after_
2796 : // aRangeBegin, but _before_ aRangeEnd
2797 :
2798 : // either way: no partial action left
2799 0 : return rFunctor.result();
2800 : }
2801 :
2802 0 : aSubset.mnSubsetBegin = 0;
2803 0 : aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2804 :
2805 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2806 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2807 :
2808 0 : rFunctor( *aRangeEnd, aSubset );
2809 : }
2810 :
2811 0 : return rFunctor.result();
2812 : }
2813 : }
2814 :
2815 0 : bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2816 : sal_Int32& io_rEndIndex,
2817 : ActionVector::const_iterator& o_rRangeBegin,
2818 : ActionVector::const_iterator& o_rRangeEnd ) const
2819 : {
2820 0 : ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2821 : "ImplRenderer::getSubsetIndices(): invalid action range" );
2822 :
2823 0 : ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2824 : "ImplRenderer::getSubsetIndices(): no actions to render" );
2825 :
2826 0 : const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2827 0 : const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2828 0 : maActions.back().mpAction->getActionCount() );
2829 :
2830 : // clip given range to permissible values (there might be
2831 : // ranges before and behind the valid indices)
2832 : io_rStartIndex = ::std::max( nMinActionIndex,
2833 0 : io_rStartIndex );
2834 : io_rEndIndex = ::std::min( nMaxActionIndex,
2835 0 : io_rEndIndex );
2836 :
2837 0 : if( io_rStartIndex == io_rEndIndex ||
2838 0 : io_rStartIndex > io_rEndIndex )
2839 : {
2840 : // empty range, don't render anything. The second
2841 : // condition e.g. happens if the requested range lies
2842 : // fully before or behind the valid action indices.
2843 0 : return false;
2844 : }
2845 :
2846 :
2847 0 : const ActionVector::const_iterator aBegin( maActions.begin() );
2848 0 : const ActionVector::const_iterator aEnd( maActions.end() );
2849 :
2850 :
2851 : // find start and end action
2852 : // =========================
2853 : o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2854 : MtfAction( ActionSharedPtr(), io_rStartIndex ),
2855 0 : UpperBoundActionIndexComparator() );
2856 : o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
2857 : MtfAction( ActionSharedPtr(), io_rEndIndex ),
2858 0 : UpperBoundActionIndexComparator() );
2859 0 : return true;
2860 : }
2861 :
2862 :
2863 : // Public methods
2864 :
2865 :
2866 0 : ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
2867 : const GDIMetaFile& rMtf,
2868 : const Parameters& rParams ) :
2869 : CanvasGraphicHelper( rCanvas ),
2870 0 : maActions()
2871 : {
2872 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2873 :
2874 : OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2875 : "ImplRenderer::ImplRenderer(): Invalid canvas" );
2876 : OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2877 : "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2878 :
2879 : // make sure canvas and graphic device are valid; action
2880 : // creation don't check that every time
2881 0 : if( rCanvas.get() == NULL ||
2882 0 : !rCanvas->getUNOCanvas().is() ||
2883 0 : !rCanvas->getUNOCanvas()->getDevice().is() )
2884 : {
2885 : // leave actions empty
2886 0 : return;
2887 : }
2888 :
2889 0 : VectorOfOutDevStates aStateStack;
2890 :
2891 0 : VirtualDevice aVDev;
2892 0 : aVDev.EnableOutput( false );
2893 :
2894 : // Setup VDev for state tracking and mapping
2895 : // =========================================
2896 :
2897 0 : aVDev.SetMapMode( rMtf.GetPrefMapMode() );
2898 :
2899 0 : const Size aMtfSize( rMtf.GetPrefSize() );
2900 : const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
2901 0 : rMtf.GetPrefMapMode() ) );
2902 :
2903 : // #i44110# correct null-sized output - there are shapes
2904 : // which have zero size in at least one dimension
2905 0 : const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2906 0 : ::std::max( aMtfSizePixPre.Height(), 1L ) );
2907 :
2908 0 : sal_Int32 nCurrActions(0);
2909 : ActionFactoryParameters aParms(aStateStack,
2910 : rCanvas,
2911 : aVDev,
2912 : rParams,
2913 0 : nCurrActions );
2914 :
2915 : // init state stack
2916 0 : aStateStack.clearStateStack();
2917 :
2918 : // Setup local state, such that the metafile renders
2919 : // itself into a one-by-one square at the origin for
2920 : // identity view and render transformations
2921 0 : aStateStack.getState().transform.scale( 1.0 / aMtfSizePix.Width(),
2922 0 : 1.0 / aMtfSizePix.Height() );
2923 :
2924 0 : tools::calcLogic2PixelAffineTransform( aStateStack.getState().mapModeTransform,
2925 0 : aVDev );
2926 :
2927 0 : ColorSharedPtr pColor( getCanvas()->createColor() );
2928 :
2929 : {
2930 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2931 : // setup default text color to black
2932 0 : rState.textColor =
2933 0 : rState.textFillColor =
2934 0 : rState.textLineColor = pColor->getDeviceColor( 0x000000FF );
2935 : }
2936 :
2937 : // apply overrides from the Parameters struct
2938 0 : if( rParams.maFillColor.is_initialized() )
2939 : {
2940 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2941 0 : rState.isFillColorSet = true;
2942 0 : rState.fillColor = pColor->getDeviceColor( *rParams.maFillColor );
2943 : }
2944 0 : if( rParams.maLineColor.is_initialized() )
2945 : {
2946 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2947 0 : rState.isLineColorSet = true;
2948 0 : rState.lineColor = pColor->getDeviceColor( *rParams.maLineColor );
2949 : }
2950 0 : if( rParams.maTextColor.is_initialized() )
2951 : {
2952 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2953 0 : rState.isTextFillColorSet = true;
2954 0 : rState.isTextLineColorSet = true;
2955 0 : rState.textColor =
2956 0 : rState.textFillColor =
2957 0 : rState.textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
2958 : }
2959 0 : if( rParams.maFontName.is_initialized() ||
2960 0 : rParams.maFontWeight.is_initialized() ||
2961 0 : rParams.maFontLetterForm.is_initialized() ||
2962 0 : rParams.maFontUnderline.is_initialized() ||
2963 0 : rParams.maFontProportion.is_initialized() )
2964 : {
2965 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2966 :
2967 0 : rState.xFont = createFont( rState.fontRotation,
2968 : ::Font(), // default font
2969 0 : aParms );
2970 : }
2971 :
2972 : /* EMF+ */
2973 0 : memset (aObjects, 0, sizeof (aObjects));
2974 0 : mbMultipart = false;
2975 :
2976 : createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
2977 : // we're
2978 : // changing
2979 : // the
2980 : // current
2981 : // action
2982 : // in
2983 : // createActions!
2984 : aParms,
2985 : true // TODO(P1): make subsettability configurable
2986 0 : );
2987 : }
2988 :
2989 0 : ImplRenderer::~ImplRenderer()
2990 : {
2991 : // don't leak EMFPObjects
2992 0 : for(unsigned int i=0; i<SAL_N_ELEMENTS(aObjects); ++i)
2993 0 : delete aObjects[i];
2994 0 : }
2995 :
2996 0 : bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
2997 : sal_Int32 nEndIndex ) const
2998 : {
2999 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3000 :
3001 0 : ActionVector::const_iterator aRangeBegin;
3002 0 : ActionVector::const_iterator aRangeEnd;
3003 :
3004 : try
3005 : {
3006 0 : if( !getSubsetIndices( nStartIndex, nEndIndex,
3007 0 : aRangeBegin, aRangeEnd ) )
3008 0 : return true; // nothing to render (but _that_ was successful)
3009 :
3010 : // now, aRangeBegin references the action in which the
3011 : // subset rendering must start, and aRangeEnd references
3012 : // the action in which the subset rendering must end (it
3013 : // might also end right at the start of the referenced
3014 : // action, such that zero of that action needs to be
3015 : // rendered).
3016 :
3017 :
3018 : // render subset of actions
3019 : // ========================
3020 :
3021 0 : ::basegfx::B2DHomMatrix aMatrix;
3022 : ::canvas::tools::getRenderStateTransform( aMatrix,
3023 0 : getRenderState() );
3024 :
3025 0 : ActionRenderer aRenderer( aMatrix );
3026 :
3027 : return forSubsetRange( aRenderer,
3028 : aRangeBegin,
3029 : aRangeEnd,
3030 : nStartIndex,
3031 : nEndIndex,
3032 0 : maActions.end() );
3033 : }
3034 0 : catch( uno::Exception& )
3035 : {
3036 : SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3037 : comphelper::anyToString( cppu::getCaughtException() ),
3038 : RTL_TEXTENCODING_UTF8 ).getStr() );
3039 :
3040 : // convert error to return value
3041 0 : return false;
3042 : }
3043 : }
3044 :
3045 0 : ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3046 : sal_Int32 nEndIndex ) const
3047 : {
3048 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3049 :
3050 0 : ActionVector::const_iterator aRangeBegin;
3051 0 : ActionVector::const_iterator aRangeEnd;
3052 :
3053 0 : if( !getSubsetIndices( nStartIndex, nEndIndex,
3054 0 : aRangeBegin, aRangeEnd ) )
3055 0 : return ::basegfx::B2DRange(); // nothing to render -> empty range
3056 :
3057 : // now, aRangeBegin references the action in which the
3058 : // subset querying must start, and aRangeEnd references
3059 : // the action in which the subset querying must end (it
3060 : // might also end right at the start of the referenced
3061 : // action, such that zero of that action needs to be
3062 : // queried).
3063 :
3064 :
3065 : // query bounds for subset of actions
3066 : // ==================================
3067 :
3068 0 : ::basegfx::B2DHomMatrix aMatrix;
3069 : ::canvas::tools::getRenderStateTransform( aMatrix,
3070 0 : getRenderState() );
3071 :
3072 0 : AreaQuery aQuery( aMatrix );
3073 : forSubsetRange( aQuery,
3074 : aRangeBegin,
3075 : aRangeEnd,
3076 : nStartIndex,
3077 : nEndIndex,
3078 0 : maActions.end() );
3079 :
3080 0 : return aQuery.getBounds();
3081 : }
3082 :
3083 0 : bool ImplRenderer::draw() const
3084 : {
3085 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3086 :
3087 0 : ::basegfx::B2DHomMatrix aMatrix;
3088 : ::canvas::tools::getRenderStateTransform( aMatrix,
3089 0 : getRenderState() );
3090 :
3091 : try
3092 : {
3093 0 : return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3094 : }
3095 0 : catch( uno::Exception& )
3096 : {
3097 : SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3098 : comphelper::anyToString( cppu::getCaughtException() ),
3099 : RTL_TEXTENCODING_UTF8 ).getStr() );
3100 :
3101 0 : return false;
3102 0 : }
3103 : }
3104 : }
3105 : }
3106 :
3107 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|