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 <utility>
76 : #include <vector>
77 : #include <algorithm>
78 : #include <iterator>
79 : #include <boost/scoped_array.hpp>
80 : #include "mtftools.hxx"
81 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
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 0 : rIsColorSet = pAct->IsSetting();
96 0 : if (rIsColorSet)
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(PushFlags 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 != PushFlags::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 & PushFlags::LINECOLOR) )
292 : {
293 0 : aCalculatedNewState.lineColor = rNewState.lineColor;
294 0 : aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
295 : }
296 :
297 0 : if( (aCalculatedNewState.pushFlags & PushFlags::FILLCOLOR) )
298 : {
299 0 : aCalculatedNewState.fillColor = rNewState.fillColor;
300 0 : aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
301 : }
302 :
303 0 : if( (aCalculatedNewState.pushFlags & PushFlags::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 & PushFlags::TEXTCOLOR) )
318 : {
319 0 : aCalculatedNewState.textColor = rNewState.textColor;
320 : }
321 :
322 0 : if( (aCalculatedNewState.pushFlags & PushFlags::MAPMODE) )
323 : {
324 0 : aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
325 : }
326 :
327 0 : if( (aCalculatedNewState.pushFlags & PushFlags::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 & PushFlags::RASTEROP) )
336 : // {
337 : // }
338 :
339 0 : if( (aCalculatedNewState.pushFlags & PushFlags::TEXTFILLCOLOR) )
340 : {
341 0 : aCalculatedNewState.textFillColor = rNewState.textFillColor;
342 0 : aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
343 : }
344 :
345 0 : if( (aCalculatedNewState.pushFlags & PushFlags::TEXTALIGN) )
346 : {
347 0 : aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
348 : }
349 :
350 : // TODO(F1): Refpoint handling NYI
351 : // if( (aCalculatedNewState.pushFlags & PushFlags::REFPOINT) )
352 : // {
353 : // }
354 :
355 0 : if( (aCalculatedNewState.pushFlags & PushFlags::TEXTLINECOLOR) )
356 : {
357 0 : aCalculatedNewState.textLineColor = rNewState.textLineColor;
358 0 : aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
359 : }
360 :
361 0 : if( (aCalculatedNewState.pushFlags & PushFlags::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 & PushFlags::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 ::tools::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(PushFlags::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 vcl::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 :
813 0 : ::Size rFontSizeLog( rFont.GetSize() );
814 :
815 0 : if (rFontSizeLog.Height() == 0)
816 : {
817 : // guess 16 pixel (as in VCL)
818 0 : rFontSizeLog = ::Size(0, 16);
819 :
820 : // convert to target MapUnit if not pixels
821 0 : rFontSizeLog = OutputDevice::LogicToLogic(rFontSizeLog, MAP_PIXEL, rParms.mrVDev.GetMapMode());
822 : }
823 :
824 0 : const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
825 0 : if( nFontWidthLog != 0 )
826 : {
827 0 : vcl::Font aTestFont = rFont;
828 0 : aTestFont.SetWidth( 0 );
829 0 : sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
830 0 : if( nNormalWidth != nFontWidthLog )
831 0 : if( nNormalWidth )
832 0 : aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
833 : }
834 :
835 : // #i52608# apply map mode scale also to font matrix - an
836 : // anisotrophic mapmode must be reflected in an
837 : // anisotrophic font matrix scale.
838 0 : const OutDevState& rState( rParms.mrStates.getState() );
839 0 : if( !::basegfx::fTools::equal(
840 0 : rState.mapModeTransform.get(0,0),
841 0 : rState.mapModeTransform.get(1,1)) )
842 : {
843 0 : const double nScaleX( rState.mapModeTransform.get(0,0) );
844 0 : const double nScaleY( rState.mapModeTransform.get(1,1) );
845 :
846 : // note: no reason to check for division by zero, we
847 : // always have the value closer (or equal) to zero as
848 : // the nominator.
849 0 : if( fabs(nScaleX) < fabs(nScaleY) )
850 0 : aFontMatrix.m00 *= nScaleX / nScaleY;
851 : else
852 0 : aFontMatrix.m11 *= nScaleY / nScaleX;
853 : }
854 0 : aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
855 :
856 0 : return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
857 : uno::Sequence< beans::PropertyValue >(),
858 0 : aFontMatrix );
859 : }
860 :
861 : // create text effects such as shadow/relief/embossed
862 0 : void ImplRenderer::createTextAction( const ::Point& rStartPoint,
863 : const OUString& rString,
864 : int nIndex,
865 : int nLength,
866 : const long* pCharWidths,
867 : const ActionFactoryParameters& rParms,
868 : bool bSubsettableActions )
869 : {
870 0 : ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.getLength() + nIndex,
871 : "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
872 :
873 0 : if( !nLength )
874 0 : return; // zero-length text, no visible output
875 :
876 0 : const OutDevState& rState( rParms.mrStates.getState() );
877 :
878 : // TODO(F2): implement all text effects
879 : // if( rState.textAlignment ); // TODO(F2): NYI
880 :
881 0 : ::Color aShadowColor( COL_AUTO );
882 0 : ::Color aReliefColor( COL_AUTO );
883 0 : ::Size aShadowOffset;
884 0 : ::Size aReliefOffset;
885 :
886 : uno::Reference<rendering::XColorSpace> xColorSpace(
887 0 : rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
888 :
889 0 : if( rState.isTextEffectShadowSet )
890 : {
891 : // calculate shadow offset (similar to outdev3.cxx)
892 : // TODO(F3): better match with outdev3.cxx
893 0 : sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
894 0 : if( nShadowOffset < 1 )
895 0 : nShadowOffset = 1;
896 :
897 0 : aShadowOffset.setWidth( nShadowOffset );
898 0 : aShadowOffset.setHeight( nShadowOffset );
899 :
900 : // determine shadow color (from outdev3.cxx)
901 : ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
902 0 : rState.textColor, xColorSpace );
903 0 : bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
904 0 : || (aTextColor.GetLuminance() < 8);
905 :
906 0 : aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
907 0 : aShadowColor.SetTransparency( aTextColor.GetTransparency() );
908 : }
909 :
910 0 : if( rState.textReliefStyle )
911 : {
912 : // calculate relief offset (similar to outdev3.cxx)
913 0 : sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
914 0 : nReliefOffset += nReliefOffset/2;
915 0 : if( nReliefOffset < 1 )
916 0 : nReliefOffset = 1;
917 :
918 0 : if( rState.textReliefStyle == RELIEF_ENGRAVED )
919 0 : nReliefOffset = -nReliefOffset;
920 :
921 0 : aReliefOffset.setWidth( nReliefOffset );
922 0 : aReliefOffset.setHeight( nReliefOffset );
923 :
924 : // determine relief color (from outdev3.cxx)
925 : ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
926 0 : rState.textColor, xColorSpace );
927 :
928 0 : aReliefColor = ::Color( COL_LIGHTGRAY );
929 :
930 : // we don't have a automatic color, so black is always
931 : // drawn on white (literally copied from
932 : // vcl/source/gdi/outdev3.cxx)
933 0 : if( aTextColor.GetColor() == COL_BLACK )
934 : {
935 0 : aTextColor = ::Color( COL_WHITE );
936 0 : rParms.mrStates.getState().textColor =
937 : ::vcl::unotools::colorToDoubleSequence(
938 0 : aTextColor, xColorSpace );
939 : }
940 :
941 0 : if( aTextColor.GetColor() == COL_WHITE )
942 0 : aReliefColor = ::Color( COL_BLACK );
943 0 : aReliefColor.SetTransparency( aTextColor.GetTransparency() );
944 : }
945 :
946 : // create the actual text action
947 : ActionSharedPtr pTextAction(
948 : TextActionFactory::createTextAction(
949 : rStartPoint,
950 : aReliefOffset,
951 : aReliefColor,
952 : aShadowOffset,
953 : aShadowColor,
954 : rString,
955 : nIndex,
956 : nLength,
957 : pCharWidths,
958 : rParms.mrVDev,
959 : rParms.mrCanvas,
960 : rState,
961 : rParms.mrParms,
962 0 : bSubsettableActions ) );
963 :
964 0 : ActionSharedPtr pStrikeoutTextAction;
965 :
966 0 : if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
967 : {
968 0 : long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
969 :
970 : sal_Unicode pChars[4];
971 0 : if ( rState.textStrikeoutStyle == STRIKEOUT_X )
972 0 : pChars[0] = 'X';
973 : else
974 0 : pChars[0] = '/';
975 0 : pChars[3]=pChars[2]=pChars[1]=pChars[0];
976 :
977 : long nStrikeoutWidth = (rParms.mrVDev.GetTextWidth(
978 0 : OUString(pChars, SAL_N_ELEMENTS(pChars))) + 2) / 4;
979 :
980 0 : if( nStrikeoutWidth <= 0 )
981 0 : nStrikeoutWidth = 1;
982 :
983 0 : long nMaxWidth = nStrikeoutWidth/2;
984 0 : if ( nMaxWidth < 2 )
985 0 : nMaxWidth = 2;
986 0 : nMaxWidth += nWidth + 1;
987 :
988 0 : long nFullStrikeoutWidth = 0;
989 0 : OUString aStrikeoutText;
990 0 : while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
991 0 : aStrikeoutText += OUString(pChars[0]);
992 :
993 0 : sal_Int32 nLen = aStrikeoutText.getLength();
994 :
995 0 : if( nLen )
996 : {
997 0 : long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
998 0 : nStrikeoutWidth += nInterval;
999 0 : long* pStrikeoutCharWidths = new long[nLen];
1000 :
1001 0 : for ( int i = 0;i<nLen; i++)
1002 : {
1003 0 : pStrikeoutCharWidths[i] = nStrikeoutWidth;
1004 : }
1005 :
1006 0 : for ( int i = 1;i< nLen; i++ )
1007 : {
1008 0 : pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
1009 : }
1010 :
1011 0 : sal_Int32 nStartPos = 0;
1012 :
1013 0 : pStrikeoutTextAction =
1014 : TextActionFactory::createTextAction(
1015 : rStartPoint,
1016 : aReliefOffset,
1017 : aReliefColor,
1018 : aShadowOffset,
1019 : aShadowColor,
1020 : aStrikeoutText,
1021 : nStartPos,
1022 : aStrikeoutText.getLength(),
1023 : pStrikeoutCharWidths,
1024 : rParms.mrVDev,
1025 : rParms.mrCanvas,
1026 : rState,
1027 : rParms.mrParms,
1028 0 : bSubsettableActions ) ;
1029 0 : }
1030 : }
1031 :
1032 0 : if( pTextAction )
1033 : {
1034 : maActions.push_back(
1035 : MtfAction(
1036 : pTextAction,
1037 0 : rParms.mrCurrActionIndex ) );
1038 :
1039 0 : if ( pStrikeoutTextAction )
1040 : {
1041 : maActions.push_back(
1042 : MtfAction(
1043 : pStrikeoutTextAction,
1044 0 : rParms.mrCurrActionIndex ) );
1045 : }
1046 :
1047 0 : rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1048 0 : }
1049 : }
1050 :
1051 0 : void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1052 : const ActionFactoryParameters& rParms,
1053 : bool bIntersect )
1054 : {
1055 0 : ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1056 0 : ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1057 :
1058 0 : const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1059 0 : const bool bEmptyClipPoly( rState.clip.count() == 0 );
1060 :
1061 0 : ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1062 : "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1063 :
1064 0 : if( !bIntersect ||
1065 0 : (bEmptyClipRect && bEmptyClipPoly) )
1066 : {
1067 0 : rState.clip = rClipPoly;
1068 : }
1069 : else
1070 : {
1071 0 : if( !bEmptyClipRect )
1072 : {
1073 : // TODO(P3): Use Liang-Barsky polygon clip here,
1074 : // after all, one object is just a rectangle!
1075 :
1076 : // convert rect to polygon beforehand, must revert
1077 : // to general polygon clipping here.
1078 0 : rState.clip = ::basegfx::B2DPolyPolygon(
1079 : ::basegfx::tools::createPolygonFromRect(
1080 : // #121100# VCL rectangular clips always
1081 : // include one more pixel to the right
1082 : // and the bottom
1083 0 : ::basegfx::B2DRectangle( rState.clipRect.Left(),
1084 0 : rState.clipRect.Top(),
1085 0 : rState.clipRect.Right()+1,
1086 0 : rState.clipRect.Bottom()+1 ) ) );
1087 : }
1088 :
1089 : // AW: Simplified
1090 0 : rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1091 0 : aClipPoly, rState.clip, true, false);
1092 : }
1093 :
1094 : // by now, our clip resides in the OutDevState::clip
1095 : // poly-polygon.
1096 0 : rState.clipRect.SetEmpty();
1097 :
1098 0 : if( rState.clip.count() == 0 )
1099 : {
1100 0 : if( rState.clipRect.IsEmpty() )
1101 : {
1102 0 : rState.xClipPoly.clear();
1103 : }
1104 : else
1105 : {
1106 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1107 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1108 : ::basegfx::B2DPolyPolygon(
1109 : ::basegfx::tools::createPolygonFromRect(
1110 : // #121100# VCL rectangular clips
1111 : // always include one more pixel to
1112 : // the right and the bottom
1113 0 : ::basegfx::B2DRectangle( rState.clipRect.Left(),
1114 0 : rState.clipRect.Top(),
1115 0 : rState.clipRect.Right()+1,
1116 0 : rState.clipRect.Bottom()+1 ) ) ) );
1117 : }
1118 : }
1119 : else
1120 : {
1121 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1122 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1123 0 : rState.clip );
1124 0 : }
1125 0 : }
1126 :
1127 0 : void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
1128 : const ActionFactoryParameters& rParms,
1129 : bool bIntersect )
1130 : {
1131 0 : ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1132 :
1133 0 : const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1134 0 : const bool bEmptyClipPoly( rState.clip.count() == 0 );
1135 :
1136 0 : ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1137 : "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1138 :
1139 0 : if( !bIntersect ||
1140 0 : (bEmptyClipRect && bEmptyClipPoly) )
1141 : {
1142 0 : rState.clipRect = rClipRect;
1143 0 : rState.clip.clear();
1144 : }
1145 0 : else if( bEmptyClipPoly )
1146 : {
1147 0 : rState.clipRect.Intersection( rClipRect );
1148 0 : rState.clip.clear();
1149 : }
1150 : else
1151 : {
1152 : // TODO(P3): Handle a fourth case here, when all clip
1153 : // polygons are rectangular, once B2DMultiRange's
1154 : // sweep line implementation is done.
1155 :
1156 : // general case: convert to polygon and clip
1157 :
1158 :
1159 : // convert rect to polygon beforehand, must revert
1160 : // to general polygon clipping here.
1161 : ::basegfx::B2DPolyPolygon aClipPoly(
1162 : ::basegfx::tools::createPolygonFromRect(
1163 0 : ::basegfx::B2DRectangle( rClipRect.Left(),
1164 0 : rClipRect.Top(),
1165 0 : rClipRect.Right(),
1166 0 : rClipRect.Bottom() ) ) );
1167 :
1168 0 : rState.clipRect.SetEmpty();
1169 :
1170 : // AW: Simplified
1171 0 : rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1172 0 : aClipPoly, rState.clip, true, false);
1173 : }
1174 :
1175 0 : if( rState.clip.count() == 0 )
1176 : {
1177 0 : if( rState.clipRect.IsEmpty() )
1178 : {
1179 0 : rState.xClipPoly.clear();
1180 : }
1181 : else
1182 : {
1183 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1184 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1185 : ::basegfx::B2DPolyPolygon(
1186 : ::basegfx::tools::createPolygonFromRect(
1187 : // #121100# VCL rectangular clips
1188 : // always include one more pixel to
1189 : // the right and the bottom
1190 0 : ::basegfx::B2DRectangle( rState.clipRect.Left(),
1191 0 : rState.clipRect.Top(),
1192 0 : rState.clipRect.Right()+1,
1193 0 : rState.clipRect.Bottom()+1 ) ) ) );
1194 : }
1195 : }
1196 : else
1197 : {
1198 0 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1199 0 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1200 0 : rState.clip );
1201 : }
1202 0 : }
1203 :
1204 0 : bool ImplRenderer::createActions( GDIMetaFile& rMtf,
1205 : const ActionFactoryParameters& rFactoryParms,
1206 : bool bSubsettableActions )
1207 : {
1208 : /* TODO(P2): interpret mtf-comments
1209 : ================================
1210 :
1211 : - gradient fillings (do that via comments)
1212 :
1213 : - think about mapping. _If_ we do everything in logical
1214 : coordinates (which would solve the probs for stroke
1215 : widths and text offsets), then we would have to
1216 : recalc scaling for every drawing operation. This is
1217 : because the outdev map mode might change at any time.
1218 : Also keep in mind, that, although we've double precision
1219 : float arithmetic now, different offsets might still
1220 : generate different roundings (aka
1221 : 'OutputDevice::SetPixelOffset())
1222 :
1223 : */
1224 :
1225 : // alias common parameters
1226 0 : VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1227 0 : const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1228 0 : ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1229 0 : const Parameters& rParms(rFactoryParms.mrParms);
1230 0 : sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1231 :
1232 :
1233 : // Loop over every metaaction
1234 : // ==========================
1235 : MetaAction* pCurrAct;
1236 :
1237 : // TODO(P1): think about caching
1238 0 : for( pCurrAct=rMtf.FirstAction();
1239 : pCurrAct;
1240 : pCurrAct = rMtf.NextAction() )
1241 : {
1242 : // execute every action, to keep VDev state up-to-date
1243 : // currently used only for
1244 : // - the map mode
1245 : // - the line/fill color when processing a META_TRANSPARENT_ACTION
1246 : // - SetFont to process font metric specific actions
1247 0 : pCurrAct->Execute( &rVDev );
1248 :
1249 : SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << pCurrAct->GetType() << " (" << pCurrAct->GetType() << ")");
1250 :
1251 0 : switch( pCurrAct->GetType() )
1252 : {
1253 :
1254 :
1255 : // In the first part of this monster-switch, we
1256 : // handle all state-changing meta actions. These
1257 : // are all handled locally.
1258 :
1259 :
1260 :
1261 : case META_PUSH_ACTION:
1262 : {
1263 0 : MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1264 0 : rStates.pushState(pPushAction->GetFlags());
1265 : }
1266 0 : break;
1267 :
1268 : case META_POP_ACTION:
1269 0 : rStates.popState();
1270 0 : break;
1271 :
1272 : case META_TEXTLANGUAGE_ACTION:
1273 : // FALLTHROUGH intended
1274 : case META_REFPOINT_ACTION:
1275 : // handled via pCurrAct->Execute( &rVDev )
1276 0 : break;
1277 :
1278 : case META_MAPMODE_ACTION:
1279 : // modify current mapModeTransformation
1280 : // transformation, such that subsequent
1281 : // coordinates map correctly
1282 0 : tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
1283 0 : rVDev );
1284 0 : break;
1285 :
1286 : // monitor clip regions, to assemble clip polygon on our own
1287 : case META_CLIPREGION_ACTION:
1288 : {
1289 0 : MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1290 :
1291 0 : if( !pClipAction->IsClipping() )
1292 : {
1293 : // clear clipping
1294 0 : rStates.getState().clip.clear();
1295 : }
1296 : else
1297 : {
1298 0 : if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1299 : {
1300 : VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1301 : "region encountered, falling back to bounding box!" );
1302 :
1303 : // #121806# explicitly kept integer
1304 : Rectangle aClipRect(
1305 : rVDev.LogicToPixel(
1306 0 : pClipAction->GetRegion().GetBoundRect() ) );
1307 :
1308 : // intersect current clip with given rect
1309 : updateClipping(
1310 : aClipRect,
1311 : rFactoryParms,
1312 0 : false );
1313 : }
1314 : else
1315 : {
1316 : // set new clip polygon (don't intersect
1317 : // with old one, just set it)
1318 :
1319 : // #121806# explicitly kept integer
1320 0 : basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1321 :
1322 0 : aPolyPolygon.transform(rVDev.GetViewTransformation());
1323 : updateClipping(
1324 : aPolyPolygon,
1325 : rFactoryParms,
1326 0 : false );
1327 : }
1328 : }
1329 :
1330 0 : break;
1331 : }
1332 :
1333 : case META_ISECTRECTCLIPREGION_ACTION:
1334 : {
1335 0 : MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1336 :
1337 : // #121806# explicitly kept integer
1338 : Rectangle aClipRect(
1339 0 : rVDev.LogicToPixel( pClipAction->GetRect() ) );
1340 :
1341 : // intersect current clip with given rect
1342 : updateClipping(
1343 : aClipRect,
1344 : rFactoryParms,
1345 0 : true );
1346 :
1347 0 : break;
1348 : }
1349 :
1350 : case META_ISECTREGIONCLIPREGION_ACTION:
1351 : {
1352 0 : MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1353 :
1354 0 : if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1355 : {
1356 : VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1357 : "region encountered, falling back to bounding box!" );
1358 :
1359 : // #121806# explicitly kept integer
1360 : Rectangle aClipRect(
1361 0 : rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1362 :
1363 : // intersect current clip with given rect
1364 : updateClipping(
1365 : aClipRect,
1366 : rFactoryParms,
1367 0 : true );
1368 : }
1369 : else
1370 : {
1371 : // intersect current clip with given clip polygon
1372 :
1373 : // #121806# explicitly kept integer
1374 0 : basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1375 :
1376 0 : aPolyPolygon.transform(rVDev.GetViewTransformation());
1377 : updateClipping(
1378 : aPolyPolygon,
1379 : rFactoryParms,
1380 0 : true );
1381 : }
1382 :
1383 0 : break;
1384 : }
1385 :
1386 : case META_MOVECLIPREGION_ACTION:
1387 : // TODO(F2): NYI
1388 0 : break;
1389 :
1390 : case META_LINECOLOR_ACTION:
1391 0 : if( !rParms.maLineColor.is_initialized() )
1392 : {
1393 : setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1394 0 : rStates.getState().isLineColorSet,
1395 0 : rStates.getState().lineColor,
1396 0 : rCanvas );
1397 : }
1398 : else
1399 : {
1400 : // #120994# Do switch on/off LineColor, even when a overriding one is set
1401 0 : bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting());
1402 :
1403 0 : rStates.getState().isLineColorSet = bSetting;
1404 : }
1405 0 : break;
1406 :
1407 : case META_FILLCOLOR_ACTION:
1408 0 : if( !rParms.maFillColor.is_initialized() )
1409 : {
1410 : setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1411 0 : rStates.getState().isFillColorSet,
1412 0 : rStates.getState().fillColor,
1413 0 : rCanvas );
1414 : }
1415 : else
1416 : {
1417 : // #120994# Do switch on/off FillColor, even when a overriding one is set
1418 0 : bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting());
1419 :
1420 0 : rStates.getState().isFillColorSet = bSetting;
1421 : }
1422 0 : break;
1423 :
1424 : case META_TEXTCOLOR_ACTION:
1425 : {
1426 0 : if( !rParms.maTextColor.is_initialized() )
1427 : {
1428 : // Text color is set unconditionally, thus, no
1429 : // use of setStateColor here
1430 0 : ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1431 :
1432 : // force alpha part of color to
1433 : // opaque. transparent painting is done
1434 : // explicitly via META_TRANSPARENT_ACTION
1435 0 : aColor.SetTransparency(0);
1436 :
1437 0 : rStates.getState().textColor =
1438 : ::vcl::unotools::colorToDoubleSequence(
1439 : aColor,
1440 0 : rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1441 : }
1442 : }
1443 0 : break;
1444 :
1445 : case META_TEXTFILLCOLOR_ACTION:
1446 0 : if( !rParms.maTextColor.is_initialized() )
1447 : {
1448 : setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1449 0 : rStates.getState().isTextFillColorSet,
1450 0 : rStates.getState().textFillColor,
1451 0 : rCanvas );
1452 : }
1453 : else
1454 : {
1455 : // #120994# Do switch on/off TextFillColor, even when a overriding one is set
1456 0 : bool bSetting(static_cast<MetaTextFillColorAction*>(pCurrAct)->IsSetting());
1457 :
1458 0 : rStates.getState().isTextFillColorSet = bSetting;
1459 : }
1460 0 : break;
1461 :
1462 : case META_TEXTLINECOLOR_ACTION:
1463 0 : if( !rParms.maTextColor.is_initialized() )
1464 : {
1465 : setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1466 0 : rStates.getState().isTextLineColorSet,
1467 0 : rStates.getState().textLineColor,
1468 0 : rCanvas );
1469 : }
1470 : else
1471 : {
1472 : // #120994# Do switch on/off TextLineColor, even when a overriding one is set
1473 0 : bool bSetting(static_cast<MetaTextLineColorAction*>(pCurrAct)->IsSetting());
1474 :
1475 0 : rStates.getState().isTextLineColorSet = bSetting;
1476 : }
1477 0 : break;
1478 :
1479 : case META_TEXTALIGN_ACTION:
1480 : {
1481 0 : ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1482 0 : const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1483 :
1484 0 : rState.textReferencePoint = eTextAlign;
1485 : }
1486 0 : break;
1487 :
1488 : case META_FONT_ACTION:
1489 : {
1490 0 : ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1491 0 : const vcl::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1492 :
1493 0 : rState.xFont = createFont( rState.fontRotation,
1494 : rFont,
1495 0 : rFactoryParms );
1496 :
1497 : // TODO(Q2): define and use appropriate enumeration types
1498 0 : rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
1499 0 : rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
1500 0 : rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ?
1501 0 : (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1502 0 : (sal_Int8)rFont.GetUnderline();
1503 0 : rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
1504 0 : rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
1505 0 : rState.isTextEffectShadowSet = rFont.IsShadow();
1506 0 : rState.isTextWordUnderlineSet = rFont.IsWordLineMode();
1507 0 : rState.isTextOutlineModeSet = rFont.IsOutline();
1508 : }
1509 0 : break;
1510 :
1511 : case META_RASTEROP_ACTION:
1512 : // TODO(F2): NYI
1513 0 : break;
1514 :
1515 : case META_LAYOUTMODE_ACTION:
1516 : {
1517 : // TODO(F2): A lot is missing here
1518 0 : ComplexTextLayoutMode nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1519 0 : ::cppcanvas::internal::OutDevState& rState = rStates.getState();
1520 :
1521 0 : ComplexTextLayoutMode nBidiLayoutMode = nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG);
1522 0 : if( nBidiLayoutMode == TEXT_LAYOUT_DEFAULT)
1523 0 : rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1524 0 : else if( nBidiLayoutMode == TEXT_LAYOUT_BIDI_STRONG)
1525 0 : rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1526 0 : else if( nBidiLayoutMode == TEXT_LAYOUT_BIDI_RTL)
1527 0 : rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1528 0 : else if( nBidiLayoutMode == (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG))
1529 0 : rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1530 :
1531 0 : rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1532 0 : if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1533 0 : && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1534 : {
1535 0 : rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1536 : }
1537 : }
1538 0 : break;
1539 :
1540 :
1541 :
1542 : // In the second part of this monster-switch, we
1543 : // handle all recursing meta actions. These are the
1544 : // ones generating a metafile by themselves, which is
1545 : // then processed by recursively calling this method.
1546 :
1547 :
1548 :
1549 : case META_GRADIENT_ACTION:
1550 : {
1551 0 : MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1552 0 : createGradientAction( ::Polygon( pGradAct->GetRect() ),
1553 0 : pGradAct->GetGradient(),
1554 : rFactoryParms,
1555 : true,
1556 0 : bSubsettableActions );
1557 : }
1558 0 : break;
1559 :
1560 : case META_HATCH_ACTION:
1561 : {
1562 : // TODO(F2): use native Canvas hatches here
1563 0 : GDIMetaFile aTmpMtf;
1564 :
1565 0 : rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1566 0 : static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1567 0 : aTmpMtf );
1568 : createActions( aTmpMtf, rFactoryParms,
1569 0 : bSubsettableActions );
1570 : }
1571 0 : break;
1572 :
1573 : case META_EPS_ACTION:
1574 : {
1575 0 : MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
1576 0 : const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
1577 :
1578 : // #121806# explicitly kept integer
1579 0 : const Size aMtfSize( rSubstitute.GetPrefSize() );
1580 : const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1581 0 : rSubstitute.GetPrefMapMode() ) );
1582 :
1583 : // #i44110# correct null-sized output - there
1584 : // are metafiles which have zero size in at
1585 : // least one dimension
1586 0 : const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1587 0 : ::std::max( aMtfSizePixPre.Height(), 1L ) );
1588 :
1589 : // Setup local transform, such that the
1590 : // metafile renders itself into the given
1591 : // output rectangle
1592 0 : rStates.pushState(PushFlags::ALL);
1593 :
1594 0 : rVDev.Push();
1595 0 : rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1596 :
1597 0 : const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1598 0 : const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1599 :
1600 0 : rStates.getState().transform.translate( rPos.X(),
1601 0 : rPos.Y() );
1602 0 : rStates.getState().transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1603 0 : (double)rSize.Height() / aMtfSizePix.Height() );
1604 :
1605 0 : createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1606 : rFactoryParms,
1607 0 : bSubsettableActions );
1608 :
1609 0 : rVDev.Pop();
1610 0 : rStates.popState();
1611 : }
1612 0 : break;
1613 :
1614 : // handle metafile comments, to retrieve
1615 : // meta-information for gradients, fills and
1616 : // strokes. May skip actions, and may recurse.
1617 : case META_COMMENT_ACTION:
1618 : {
1619 0 : MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1620 :
1621 : // Handle gradients
1622 0 : if (pAct->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
1623 : {
1624 0 : MetaGradientExAction* pGradAction = NULL;
1625 0 : bool bDone( false );
1626 0 : while( !bDone &&
1627 : (pCurrAct=rMtf.NextAction()) != NULL )
1628 : {
1629 0 : switch( pCurrAct->GetType() )
1630 : {
1631 : // extract gradient info
1632 : case META_GRADIENTEX_ACTION:
1633 0 : pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1634 0 : break;
1635 :
1636 : // skip broken-down rendering, output gradient when sequence is ended
1637 : case META_COMMENT_ACTION:
1638 0 : if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") )
1639 : {
1640 0 : bDone = true;
1641 :
1642 0 : if( pGradAction )
1643 : {
1644 0 : createGradientAction( pGradAction->GetPolyPolygon(),
1645 0 : pGradAction->GetGradient(),
1646 : rFactoryParms,
1647 : false,
1648 0 : bSubsettableActions );
1649 : }
1650 : }
1651 0 : break;
1652 : }
1653 : }
1654 : }
1655 : // TODO(P2): Handle drawing layer strokes, via
1656 : // XPATHSTROKE_SEQ_BEGIN comment
1657 :
1658 : // Handle drawing layer fills
1659 0 : else if( pAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1660 : {
1661 0 : const sal_uInt8* pData = pAct->GetData();
1662 0 : if ( pData )
1663 : {
1664 0 : SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1665 :
1666 0 : SvtGraphicFill aFill;
1667 0 : ReadSvtGraphicFill( aMemStm, aFill );
1668 :
1669 : // TODO(P2): Also handle gradients and
1670 : // hatches like this
1671 :
1672 : // only evaluate comment for pure
1673 : // bitmap fills. If a transparency
1674 : // gradient is involved (denoted by
1675 : // the FloatTransparent action), take
1676 : // the normal meta actions.
1677 0 : if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1678 : !isActionContained( rMtf,
1679 : "XPATHFILL_SEQ_END",
1680 0 : META_FLOATTRANSPARENT_ACTION ) )
1681 : {
1682 0 : rendering::Texture aTexture;
1683 :
1684 : // TODO(F1): the SvtGraphicFill
1685 : // can also transport metafiles
1686 : // here, handle that case, too
1687 0 : Graphic aGraphic;
1688 0 : aFill.getGraphic( aGraphic );
1689 :
1690 0 : BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1691 0 : const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1692 :
1693 0 : ::SvtGraphicFill::Transform aTransform;
1694 0 : aFill.getTransform( aTransform );
1695 :
1696 0 : ::basegfx::B2DHomMatrix aMatrix;
1697 :
1698 : // convert to basegfx matrix
1699 0 : aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1700 0 : aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1701 0 : aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1702 0 : aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1703 0 : aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1704 0 : aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1705 :
1706 0 : ::basegfx::B2DHomMatrix aScale;
1707 0 : aScale.scale( aBmpSize.Width(),
1708 0 : aBmpSize.Height() );
1709 :
1710 : // post-multiply with the bitmap
1711 : // size (XCanvas' texture assumes
1712 : // the given bitmap to be
1713 : // normalized to [0,1]x[0,1]
1714 : // rectangle)
1715 0 : aMatrix = aMatrix * aScale;
1716 :
1717 : // pre-multiply with the
1718 : // logic-to-pixel scale factor
1719 : // (the metafile comment works in
1720 : // logical coordinates).
1721 0 : ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1722 : aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1723 0 : rVDev );
1724 :
1725 : ::basegfx::unotools::affineMatrixFromHomMatrix(
1726 : aTexture.AffineTransform,
1727 0 : aMatrix );
1728 :
1729 0 : aTexture.Alpha = 1.0 - aFill.getTransparency();
1730 0 : aTexture.Bitmap =
1731 : ::vcl::unotools::xBitmapFromBitmapEx(
1732 0 : rCanvas->getUNOCanvas()->getDevice(),
1733 0 : aBmpEx );
1734 0 : if( aFill.isTiling() )
1735 : {
1736 0 : aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1737 0 : aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1738 : }
1739 : else
1740 : {
1741 0 : aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1742 0 : aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1743 : }
1744 :
1745 0 : ::tools::PolyPolygon aPath;
1746 0 : aFill.getPath( aPath );
1747 :
1748 0 : ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1749 0 : aPoly.transform( rStates.getState().mapModeTransform );
1750 : ActionSharedPtr pPolyAction(
1751 : internal::PolyPolyActionFactory::createPolyPolyAction(
1752 : aPoly,
1753 : rCanvas,
1754 0 : rStates.getState(),
1755 0 : aTexture ) );
1756 :
1757 0 : if( pPolyAction )
1758 : {
1759 : maActions.push_back(
1760 : MtfAction(
1761 : pPolyAction,
1762 0 : io_rCurrActionIndex ) );
1763 :
1764 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1765 : }
1766 :
1767 : // skip broken-down render output
1768 : skipContent( rMtf,
1769 : "XPATHFILL_SEQ_END",
1770 0 : io_rCurrActionIndex );
1771 0 : }
1772 : }
1773 : }
1774 : // Handle drawing layer fills
1775 0 : else if( pAct->GetComment() == "EMF_PLUS" ) {
1776 : static int count = -1, limit = 0x7fffffff;
1777 0 : if (count == -1) {
1778 0 : count = 0;
1779 0 : if (char *env = getenv ("EMF_PLUS_LIMIT")) {
1780 0 : limit = atoi (env);
1781 : SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit);
1782 : }
1783 : }
1784 : SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct->GetDataSize ());
1785 0 : if (count < limit)
1786 0 : processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
1787 0 : count ++;
1788 0 : } else if( pAct->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1789 : SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct->GetDataSize ());
1790 :
1791 0 : SvMemoryStream rMF ((void*) pAct->GetData (), pAct->GetDataSize (), STREAM_READ);
1792 :
1793 0 : rMF.ReadInt32( nFrameLeft ).ReadInt32( nFrameTop ).ReadInt32( nFrameRight ).ReadInt32( nFrameBottom );
1794 : SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft << "," << nFrameTop << " - " << nFrameRight << "," << nFrameBottom);
1795 0 : rMF.ReadInt32( nPixX ).ReadInt32( nPixY ).ReadInt32( nMmX ).ReadInt32( nMmY );
1796 : SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX << "x" << nPixY << " mm size: " << nMmX << "x" << nMmY);
1797 :
1798 0 : ReadXForm( rMF, aBaseTransform );
1799 : //aWorldTransform.Set (aBaseTransform);
1800 : }
1801 : }
1802 0 : break;
1803 :
1804 :
1805 :
1806 : // In the third part of this monster-switch, we
1807 : // handle all 'acting' meta actions. These are all
1808 : // processed by constructing function objects for
1809 : // them, which will later ease caching.
1810 :
1811 :
1812 :
1813 : case META_POINT_ACTION:
1814 : {
1815 0 : const OutDevState& rState( rStates.getState() );
1816 0 : if( rState.lineColor.getLength() )
1817 : {
1818 : ActionSharedPtr pPointAction(
1819 : internal::PointActionFactory::createPointAction(
1820 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1821 0 : static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1822 : rCanvas,
1823 0 : rState ) );
1824 :
1825 0 : if( pPointAction )
1826 : {
1827 : maActions.push_back(
1828 : MtfAction(
1829 : pPointAction,
1830 0 : io_rCurrActionIndex ) );
1831 :
1832 0 : io_rCurrActionIndex += pPointAction->getActionCount()-1;
1833 0 : }
1834 : }
1835 : }
1836 0 : break;
1837 :
1838 : case META_PIXEL_ACTION:
1839 : {
1840 0 : const OutDevState& rState( rStates.getState() );
1841 0 : if( rState.lineColor.getLength() )
1842 : {
1843 : ActionSharedPtr pPointAction(
1844 : internal::PointActionFactory::createPointAction(
1845 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1846 0 : static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1847 : rCanvas,
1848 : rState,
1849 0 : static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1850 :
1851 0 : if( pPointAction )
1852 : {
1853 : maActions.push_back(
1854 : MtfAction(
1855 : pPointAction,
1856 0 : io_rCurrActionIndex ) );
1857 :
1858 0 : io_rCurrActionIndex += pPointAction->getActionCount()-1;
1859 0 : }
1860 : }
1861 : }
1862 0 : break;
1863 :
1864 : case META_LINE_ACTION:
1865 : {
1866 0 : const OutDevState& rState( rStates.getState() );
1867 0 : if( rState.lineColor.getLength() )
1868 : {
1869 0 : MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1870 :
1871 0 : const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1872 :
1873 : const ::basegfx::B2DPoint aStartPoint(
1874 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1875 : const ::basegfx::B2DPoint aEndPoint(
1876 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1877 :
1878 0 : ActionSharedPtr pLineAction;
1879 :
1880 0 : if( rLineInfo.IsDefault() )
1881 : {
1882 : // plain hair line
1883 0 : pLineAction =
1884 : internal::LineActionFactory::createLineAction(
1885 : aStartPoint,
1886 : aEndPoint,
1887 : rCanvas,
1888 0 : rState );
1889 :
1890 0 : if( pLineAction )
1891 : {
1892 : maActions.push_back(
1893 : MtfAction(
1894 : pLineAction,
1895 0 : io_rCurrActionIndex ) );
1896 :
1897 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
1898 : }
1899 : }
1900 0 : else if( LINE_NONE != rLineInfo.GetStyle() )
1901 : {
1902 : // 'thick' line
1903 0 : rendering::StrokeAttributes aStrokeAttributes;
1904 :
1905 : setupStrokeAttributes( aStrokeAttributes,
1906 : rFactoryParms,
1907 0 : rLineInfo );
1908 :
1909 : // XCanvas can only stroke polygons,
1910 : // not simple lines - thus, handle
1911 : // this case via the polypolygon
1912 : // action
1913 0 : ::basegfx::B2DPolygon aPoly;
1914 0 : aPoly.append( aStartPoint );
1915 0 : aPoly.append( aEndPoint );
1916 0 : pLineAction =
1917 : internal::PolyPolyActionFactory::createPolyPolyAction(
1918 : ::basegfx::B2DPolyPolygon( aPoly ),
1919 0 : rCanvas, rState, aStrokeAttributes );
1920 :
1921 0 : if( pLineAction )
1922 : {
1923 : maActions.push_back(
1924 : MtfAction(
1925 : pLineAction,
1926 0 : io_rCurrActionIndex ) );
1927 :
1928 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
1929 0 : }
1930 0 : }
1931 : // else: line style is default
1932 : // (i.e. invisible), don't generate action
1933 : }
1934 : }
1935 0 : break;
1936 :
1937 : case META_RECT_ACTION:
1938 : {
1939 : const Rectangle& rRect(
1940 0 : static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
1941 :
1942 0 : if( rRect.IsEmpty() )
1943 0 : break;
1944 :
1945 0 : const OutDevState& rState( rStates.getState() );
1946 : const ::basegfx::B2DPoint aTopLeftPixel(
1947 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
1948 : const ::basegfx::B2DPoint aBottomRightPixel(
1949 0 : rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1950 : // #121100# OutputDevice::DrawRect() fills
1951 : // rectangles Apple-like, i.e. with one
1952 : // additional pixel to the right and bottom.
1953 0 : ::basegfx::B2DPoint(1,1) );
1954 :
1955 : createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1956 : ::basegfx::B2DRange( aTopLeftPixel,
1957 : aBottomRightPixel )),
1958 0 : rFactoryParms );
1959 0 : break;
1960 : }
1961 :
1962 : case META_ROUNDRECT_ACTION:
1963 : {
1964 : const Rectangle& rRect(
1965 0 : static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
1966 :
1967 0 : if( rRect.IsEmpty() )
1968 0 : break;
1969 :
1970 : ::basegfx::B2DPolygon aPoly(
1971 : ::basegfx::tools::createPolygonFromRect(
1972 : ::basegfx::B2DRange(
1973 : ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1974 0 : ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1975 : ::basegfx::B2DPoint(1,1) ),
1976 0 : ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
1977 0 : ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
1978 0 : aPoly.transform( rStates.getState().mapModeTransform );
1979 :
1980 : createFillAndStroke( aPoly,
1981 0 : rFactoryParms );
1982 : }
1983 0 : break;
1984 :
1985 : case META_ELLIPSE_ACTION:
1986 : {
1987 : const Rectangle& rRect(
1988 0 : static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
1989 :
1990 0 : if( rRect.IsEmpty() )
1991 0 : break;
1992 :
1993 : const ::basegfx::B2DRange aRange(
1994 : ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1995 0 : ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1996 0 : ::basegfx::B2DPoint(1,1) );
1997 :
1998 : ::basegfx::B2DPolygon aPoly(
1999 : ::basegfx::tools::createPolygonFromEllipse(
2000 : aRange.getCenter(),
2001 : aRange.getWidth(),
2002 0 : aRange.getHeight() ));
2003 0 : aPoly.transform( rStates.getState().mapModeTransform );
2004 :
2005 : createFillAndStroke( aPoly,
2006 0 : rFactoryParms );
2007 : }
2008 0 : break;
2009 :
2010 : case META_ARC_ACTION:
2011 : {
2012 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2013 0 : const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2014 0 : static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2015 0 : static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2016 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2017 0 : aPoly.transform( rStates.getState().mapModeTransform );
2018 :
2019 : createFillAndStroke( aPoly,
2020 0 : rFactoryParms );
2021 : }
2022 0 : break;
2023 :
2024 : case META_PIE_ACTION:
2025 : {
2026 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2027 0 : const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2028 0 : static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2029 0 : static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2030 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2031 0 : aPoly.transform( rStates.getState().mapModeTransform );
2032 :
2033 : createFillAndStroke( aPoly,
2034 0 : rFactoryParms );
2035 : }
2036 0 : break;
2037 :
2038 : case META_CHORD_ACTION:
2039 : {
2040 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2041 0 : const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2042 0 : static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2043 0 : static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2044 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2045 0 : aPoly.transform( rStates.getState().mapModeTransform );
2046 :
2047 : createFillAndStroke( aPoly,
2048 0 : rFactoryParms );
2049 : }
2050 0 : break;
2051 :
2052 : case META_POLYLINE_ACTION:
2053 : {
2054 0 : const OutDevState& rState( rStates.getState() );
2055 0 : if( rState.lineColor.getLength() ||
2056 0 : rState.fillColor.getLength() )
2057 : {
2058 0 : MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2059 :
2060 0 : const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2061 0 : ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2062 0 : aPoly.transform( rState.mapModeTransform );
2063 :
2064 0 : ActionSharedPtr pLineAction;
2065 :
2066 0 : if( rLineInfo.IsDefault() )
2067 : {
2068 : // plain hair line polygon
2069 0 : pLineAction =
2070 : internal::PolyPolyActionFactory::createLinePolyPolyAction(
2071 : ::basegfx::B2DPolyPolygon(aPoly),
2072 : rCanvas,
2073 0 : rState );
2074 :
2075 0 : if( pLineAction )
2076 : {
2077 : maActions.push_back(
2078 : MtfAction(
2079 : pLineAction,
2080 0 : io_rCurrActionIndex ) );
2081 :
2082 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
2083 : }
2084 : }
2085 0 : else if( LINE_NONE != rLineInfo.GetStyle() )
2086 : {
2087 : // 'thick' line polygon
2088 0 : rendering::StrokeAttributes aStrokeAttributes;
2089 :
2090 : setupStrokeAttributes( aStrokeAttributes,
2091 : rFactoryParms,
2092 0 : rLineInfo );
2093 :
2094 0 : pLineAction =
2095 : internal::PolyPolyActionFactory::createPolyPolyAction(
2096 : ::basegfx::B2DPolyPolygon(aPoly),
2097 : rCanvas,
2098 : rState,
2099 0 : aStrokeAttributes ) ;
2100 :
2101 0 : if( pLineAction )
2102 : {
2103 : maActions.push_back(
2104 : MtfAction(
2105 : pLineAction,
2106 0 : io_rCurrActionIndex ) );
2107 :
2108 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
2109 0 : }
2110 0 : }
2111 : // else: line style is default
2112 : // (i.e. invisible), don't generate action
2113 : }
2114 : }
2115 0 : break;
2116 :
2117 : case META_POLYGON_ACTION:
2118 : {
2119 0 : ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2120 0 : aPoly.transform( rStates.getState().mapModeTransform );
2121 : createFillAndStroke( aPoly,
2122 0 : rFactoryParms );
2123 : }
2124 0 : break;
2125 :
2126 : case META_POLYPOLYGON_ACTION:
2127 : {
2128 0 : ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2129 0 : aPoly.transform( rStates.getState().mapModeTransform );
2130 : createFillAndStroke( aPoly,
2131 0 : rFactoryParms );
2132 : }
2133 0 : break;
2134 :
2135 : case META_BMP_ACTION:
2136 : {
2137 0 : MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2138 :
2139 : ActionSharedPtr pBmpAction(
2140 : internal::BitmapActionFactory::createBitmapAction(
2141 0 : pAct->GetBitmap(),
2142 0 : rStates.getState().mapModeTransform *
2143 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2144 : rCanvas,
2145 0 : rStates.getState() ) );
2146 :
2147 0 : if( pBmpAction )
2148 : {
2149 : maActions.push_back(
2150 : MtfAction(
2151 : pBmpAction,
2152 0 : io_rCurrActionIndex ) );
2153 :
2154 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2155 0 : }
2156 : }
2157 0 : break;
2158 :
2159 : case META_BMPSCALE_ACTION:
2160 : {
2161 0 : MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2162 :
2163 : ActionSharedPtr pBmpAction(
2164 : internal::BitmapActionFactory::createBitmapAction(
2165 0 : pAct->GetBitmap(),
2166 0 : rStates.getState().mapModeTransform *
2167 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2168 0 : rStates.getState().mapModeTransform *
2169 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2170 : rCanvas,
2171 0 : rStates.getState() ) );
2172 :
2173 0 : if( pBmpAction )
2174 : {
2175 : maActions.push_back(
2176 : MtfAction(
2177 : pBmpAction,
2178 0 : io_rCurrActionIndex ) );
2179 :
2180 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2181 0 : }
2182 : }
2183 0 : break;
2184 :
2185 : case META_BMPSCALEPART_ACTION:
2186 : {
2187 0 : MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2188 :
2189 : // crop bitmap to given source rectangle (no
2190 : // need to copy and convert the whole bitmap)
2191 0 : Bitmap aBmp( pAct->GetBitmap() );
2192 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2193 0 : pAct->GetSrcSize() );
2194 0 : aBmp.Crop( aCropRect );
2195 :
2196 : ActionSharedPtr pBmpAction(
2197 : internal::BitmapActionFactory::createBitmapAction(
2198 : aBmp,
2199 0 : rStates.getState().mapModeTransform *
2200 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2201 0 : rStates.getState().mapModeTransform *
2202 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2203 : rCanvas,
2204 0 : rStates.getState() ) );
2205 :
2206 0 : if( pBmpAction )
2207 : {
2208 : maActions.push_back(
2209 : MtfAction(
2210 : pBmpAction,
2211 0 : io_rCurrActionIndex ) );
2212 :
2213 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2214 0 : }
2215 : }
2216 0 : break;
2217 :
2218 : case META_BMPEX_ACTION:
2219 : {
2220 0 : MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2221 :
2222 : ActionSharedPtr pBmpAction(
2223 : internal::BitmapActionFactory::createBitmapAction(
2224 0 : pAct->GetBitmapEx(),
2225 0 : rStates.getState().mapModeTransform *
2226 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2227 : rCanvas,
2228 0 : rStates.getState() ) );
2229 :
2230 0 : if( pBmpAction )
2231 : {
2232 : maActions.push_back(
2233 : MtfAction(
2234 : pBmpAction,
2235 0 : io_rCurrActionIndex ) );
2236 :
2237 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2238 0 : }
2239 : }
2240 0 : break;
2241 :
2242 : case META_BMPEXSCALE_ACTION:
2243 : {
2244 0 : MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2245 :
2246 : ActionSharedPtr pBmpAction(
2247 : internal::BitmapActionFactory::createBitmapAction(
2248 0 : pAct->GetBitmapEx(),
2249 0 : rStates.getState().mapModeTransform *
2250 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2251 0 : rStates.getState().mapModeTransform *
2252 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2253 : rCanvas,
2254 0 : rStates.getState() ) );
2255 :
2256 0 : if( pBmpAction )
2257 : {
2258 : maActions.push_back(
2259 : MtfAction(
2260 : pBmpAction,
2261 0 : io_rCurrActionIndex ) );
2262 :
2263 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2264 0 : }
2265 : }
2266 0 : break;
2267 :
2268 : case META_BMPEXSCALEPART_ACTION:
2269 : {
2270 0 : MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2271 :
2272 : // crop bitmap to given source rectangle (no
2273 : // need to copy and convert the whole bitmap)
2274 0 : BitmapEx aBmp( pAct->GetBitmapEx() );
2275 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2276 0 : pAct->GetSrcSize() );
2277 0 : aBmp.Crop( aCropRect );
2278 :
2279 : ActionSharedPtr pBmpAction(
2280 : internal::BitmapActionFactory::createBitmapAction(
2281 : aBmp,
2282 0 : rStates.getState().mapModeTransform *
2283 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2284 0 : rStates.getState().mapModeTransform *
2285 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2286 : rCanvas,
2287 0 : rStates.getState() ) );
2288 :
2289 0 : if( pBmpAction )
2290 : {
2291 : maActions.push_back(
2292 : MtfAction(
2293 : pBmpAction,
2294 0 : io_rCurrActionIndex ) );
2295 :
2296 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2297 0 : }
2298 : }
2299 0 : break;
2300 :
2301 : case META_MASK_ACTION:
2302 : {
2303 0 : MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2304 :
2305 : // create masked BitmapEx right here, as the
2306 : // canvas does not provide equivalent
2307 : // functionality
2308 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2309 0 : pAct->GetColor() ));
2310 :
2311 : ActionSharedPtr pBmpAction(
2312 : internal::BitmapActionFactory::createBitmapAction(
2313 : aBmp,
2314 0 : rStates.getState().mapModeTransform *
2315 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2316 : rCanvas,
2317 0 : rStates.getState() ) );
2318 :
2319 0 : if( pBmpAction )
2320 : {
2321 : maActions.push_back(
2322 : MtfAction(
2323 : pBmpAction,
2324 0 : io_rCurrActionIndex ) );
2325 :
2326 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2327 0 : }
2328 : }
2329 0 : break;
2330 :
2331 : case META_MASKSCALE_ACTION:
2332 : {
2333 0 : MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2334 :
2335 : // create masked BitmapEx right here, as the
2336 : // canvas does not provide equivalent
2337 : // functionality
2338 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2339 0 : pAct->GetColor() ));
2340 :
2341 : ActionSharedPtr pBmpAction(
2342 : internal::BitmapActionFactory::createBitmapAction(
2343 : aBmp,
2344 0 : rStates.getState().mapModeTransform *
2345 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2346 0 : rStates.getState().mapModeTransform *
2347 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2348 : rCanvas,
2349 0 : rStates.getState() ) );
2350 :
2351 0 : if( pBmpAction )
2352 : {
2353 : maActions.push_back(
2354 : MtfAction(
2355 : pBmpAction,
2356 0 : io_rCurrActionIndex ) );
2357 :
2358 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2359 0 : }
2360 : }
2361 0 : break;
2362 :
2363 : case META_MASKSCALEPART_ACTION:
2364 : {
2365 0 : MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2366 :
2367 : // create masked BitmapEx right here, as the
2368 : // canvas does not provide equivalent
2369 : // functionality
2370 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2371 0 : pAct->GetColor() ));
2372 :
2373 : // crop bitmap to given source rectangle (no
2374 : // need to copy and convert the whole bitmap)
2375 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2376 0 : pAct->GetSrcSize() );
2377 0 : aBmp.Crop( aCropRect );
2378 :
2379 : ActionSharedPtr pBmpAction(
2380 : internal::BitmapActionFactory::createBitmapAction(
2381 : aBmp,
2382 0 : rStates.getState().mapModeTransform *
2383 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2384 0 : rStates.getState().mapModeTransform *
2385 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2386 : rCanvas,
2387 0 : rStates.getState() ) );
2388 :
2389 0 : if( pBmpAction )
2390 : {
2391 : maActions.push_back(
2392 : MtfAction(
2393 : pBmpAction,
2394 0 : io_rCurrActionIndex ) );
2395 :
2396 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2397 0 : }
2398 : }
2399 0 : break;
2400 :
2401 : case META_GRADIENTEX_ACTION:
2402 : // TODO(F1): use native Canvas gradients here
2403 : // action is ignored here, because redundant to META_GRADIENT_ACTION
2404 0 : break;
2405 :
2406 : case META_WALLPAPER_ACTION:
2407 : // TODO(F2): NYI
2408 0 : break;
2409 :
2410 : case META_TRANSPARENT_ACTION:
2411 : {
2412 0 : const OutDevState& rState( rStates.getState() );
2413 0 : if( rState.lineColor.getLength() ||
2414 0 : rState.fillColor.getLength() )
2415 : {
2416 0 : MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2417 0 : ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2418 0 : aPoly.transform( rState.mapModeTransform );
2419 :
2420 : ActionSharedPtr pPolyAction(
2421 : internal::PolyPolyActionFactory::createPolyPolyAction(
2422 : aPoly,
2423 : rCanvas,
2424 : rState,
2425 0 : pAct->GetTransparence() ) );
2426 :
2427 0 : if( pPolyAction )
2428 : {
2429 : maActions.push_back(
2430 : MtfAction(
2431 : pPolyAction,
2432 0 : io_rCurrActionIndex ) );
2433 :
2434 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2435 0 : }
2436 : }
2437 : }
2438 0 : break;
2439 :
2440 : case META_FLOATTRANSPARENT_ACTION:
2441 : {
2442 0 : MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2443 :
2444 : internal::MtfAutoPtr pMtf(
2445 0 : new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2446 :
2447 : // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2448 : internal::GradientAutoPtr pGradient(
2449 0 : new Gradient( pAct->GetGradient() ) );
2450 :
2451 : DBG_TESTSOLARMUTEX();
2452 :
2453 : ActionSharedPtr pFloatTransAction(
2454 : internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2455 0 : std::move(pMtf),
2456 0 : std::move(pGradient),
2457 : rParms,
2458 0 : rStates.getState().mapModeTransform *
2459 0 : ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2460 0 : rStates.getState().mapModeTransform *
2461 0 : ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2462 : rCanvas,
2463 0 : rStates.getState() ) );
2464 :
2465 0 : if( pFloatTransAction )
2466 : {
2467 : maActions.push_back(
2468 : MtfAction(
2469 : pFloatTransAction,
2470 0 : io_rCurrActionIndex ) );
2471 :
2472 0 : io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2473 0 : }
2474 : }
2475 0 : break;
2476 :
2477 : case META_TEXT_ACTION:
2478 : {
2479 0 : MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2480 0 : OUString sText = pAct->GetText();
2481 :
2482 0 : if (rVDev.GetDigitLanguage())
2483 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2484 :
2485 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2486 :
2487 : createTextAction(
2488 0 : pAct->GetPoint(),
2489 : sText,
2490 : pAct->GetIndex(),
2491 : nLen,
2492 : NULL,
2493 : rFactoryParms,
2494 0 : bSubsettableActions );
2495 : }
2496 0 : break;
2497 :
2498 : case META_TEXTARRAY_ACTION:
2499 : {
2500 0 : MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2501 0 : OUString sText = pAct->GetText();
2502 :
2503 0 : if (rVDev.GetDigitLanguage())
2504 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2505 :
2506 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2507 :
2508 : createTextAction(
2509 0 : pAct->GetPoint(),
2510 : sText,
2511 : pAct->GetIndex(),
2512 : nLen,
2513 0 : pAct->GetDXArray(),
2514 : rFactoryParms,
2515 0 : bSubsettableActions );
2516 : }
2517 0 : break;
2518 :
2519 : case META_TEXTLINE_ACTION:
2520 : {
2521 0 : MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2522 :
2523 0 : const OutDevState& rState( rStates.getState() );
2524 : const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2525 0 : rVDev ) );
2526 : const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2527 0 : ::basegfx::B2DSize(pAct->GetWidth(),
2528 0 : 0 ));
2529 :
2530 : ActionSharedPtr pPolyAction(
2531 : PolyPolyActionFactory::createPolyPolyAction(
2532 : tools::createTextLinesPolyPolygon(
2533 0 : rState.mapModeTransform *
2534 : ::basegfx::B2DPoint(
2535 0 : ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2536 : ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2537 0 : aSize.getX(),
2538 : tools::createTextLineInfo( rVDev,
2539 : rState )),
2540 : rCanvas,
2541 0 : rState ) );
2542 :
2543 0 : if( pPolyAction.get() )
2544 : {
2545 : maActions.push_back(
2546 : MtfAction(
2547 : pPolyAction,
2548 0 : io_rCurrActionIndex ) );
2549 :
2550 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2551 0 : }
2552 : }
2553 0 : break;
2554 :
2555 : case META_TEXTRECT_ACTION:
2556 : {
2557 0 : MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2558 :
2559 0 : rStates.pushState(PushFlags::ALL);
2560 :
2561 : // use the VDev to break up the text rect
2562 : // action into readily formatted lines
2563 0 : GDIMetaFile aTmpMtf;
2564 0 : rVDev.AddTextRectActions( pAct->GetRect(),
2565 0 : pAct->GetText(),
2566 0 : pAct->GetStyle(),
2567 0 : aTmpMtf );
2568 :
2569 : createActions( aTmpMtf,
2570 : rFactoryParms,
2571 0 : bSubsettableActions );
2572 :
2573 0 : rStates.popState();
2574 :
2575 0 : break;
2576 : }
2577 :
2578 : case META_STRETCHTEXT_ACTION:
2579 : {
2580 0 : MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2581 0 : OUString sText = pAct->GetText();
2582 :
2583 0 : if (rVDev.GetDigitLanguage())
2584 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2585 :
2586 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2587 :
2588 : // #i70897# Nothing to do, actually...
2589 0 : if( nLen == 0 )
2590 0 : break;
2591 :
2592 : // have to fit the text into the given
2593 : // width. This is achieved by internally
2594 : // generating a DX array, and uniformly
2595 : // distributing the excess/insufficient width
2596 : // to every logical character.
2597 0 : ::boost::scoped_array< long > pDXArray( new long[nLen] );
2598 :
2599 0 : rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2600 0 : pAct->GetIndex(), pAct->GetLen() );
2601 :
2602 0 : const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2603 :
2604 : // Last entry of pDXArray contains total width of the text
2605 0 : long* p = pDXArray.get();
2606 0 : for (sal_Int32 i = 1; i <= nLen; ++i)
2607 : {
2608 : // calc ratio for every array entry, to
2609 : // distribute rounding errors 'evenly'
2610 : // across the characters. Note that each
2611 : // entry represents the 'end' position of
2612 : // the corresponding character, thus, we
2613 : // let i run from 1 to nLen.
2614 0 : *p++ += (long)i*nWidthDifference/nLen;
2615 : }
2616 :
2617 : createTextAction(
2618 0 : pAct->GetPoint(),
2619 : sText,
2620 : pAct->GetIndex(),
2621 : nLen,
2622 0 : pDXArray.get(),
2623 : rFactoryParms,
2624 0 : bSubsettableActions );
2625 : }
2626 0 : break;
2627 :
2628 : default:
2629 : SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2630 0 : break;
2631 : }
2632 :
2633 : // increment action index (each mtf action counts _at
2634 : // least_ one. Some count for more, therefore,
2635 : // io_rCurrActionIndex is sometimes incremented by
2636 : // pAct->getActionCount()-1 above, the -1 being the
2637 : // correction for the unconditional increment here).
2638 0 : ++io_rCurrActionIndex;
2639 : }
2640 :
2641 0 : return true;
2642 : }
2643 :
2644 :
2645 : namespace
2646 : {
2647 0 : class ActionRenderer
2648 : {
2649 : public:
2650 0 : ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2651 : maTransformation( rTransformation ),
2652 0 : mbRet( true )
2653 : {
2654 0 : }
2655 :
2656 0 : bool result() const
2657 : {
2658 0 : return mbRet;
2659 : }
2660 :
2661 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2662 : {
2663 : // ANDing the result. We want to fail if at least
2664 : // one action failed.
2665 0 : mbRet &= rAction.mpAction->render( maTransformation );
2666 0 : }
2667 :
2668 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2669 : const Action::Subset& rSubset )
2670 : {
2671 : // ANDing the result. We want to fail if at least
2672 : // one action failed.
2673 0 : mbRet &= rAction.mpAction->renderSubset( maTransformation,
2674 0 : rSubset );
2675 0 : }
2676 :
2677 : private:
2678 : ::basegfx::B2DHomMatrix maTransformation;
2679 : bool mbRet;
2680 : };
2681 :
2682 0 : class AreaQuery
2683 : {
2684 : public:
2685 0 : AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2686 : maTransformation( rTransformation ),
2687 0 : maBounds()
2688 : {
2689 0 : }
2690 :
2691 0 : bool result() const
2692 : {
2693 0 : return true; // nothing can fail here
2694 : }
2695 :
2696 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2697 : {
2698 0 : maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2699 0 : }
2700 :
2701 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2702 : const Action::Subset& rSubset )
2703 : {
2704 0 : maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2705 0 : rSubset ) );
2706 0 : }
2707 :
2708 0 : ::basegfx::B2DRange getBounds() const
2709 : {
2710 0 : return maBounds;
2711 : }
2712 :
2713 : private:
2714 : ::basegfx::B2DHomMatrix maTransformation;
2715 : ::basegfx::B2DRange maBounds;
2716 : };
2717 :
2718 : // Doing that via inline class. Compilers tend to not inline free
2719 : // functions.
2720 : struct UpperBoundActionIndexComparator
2721 : {
2722 0 : bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2723 : const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2724 : {
2725 : const sal_Int32 nLHSCount( rLHS.mpAction ?
2726 0 : rLHS.mpAction->getActionCount() : 0 );
2727 : const sal_Int32 nRHSCount( rRHS.mpAction ?
2728 0 : rRHS.mpAction->getActionCount() : 0 );
2729 :
2730 : // compare end of action range, to have an action selected
2731 : // by lower_bound even if the requested index points in
2732 : // the middle of the action's range
2733 0 : return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2734 : }
2735 : };
2736 :
2737 : /** Algorithm to apply given functor to a subset range
2738 :
2739 : @tpl Functor
2740 :
2741 : Functor to call for each element of the subset
2742 : range. Must provide the following method signatures:
2743 : bool result() (returning false if operation failed)
2744 :
2745 : */
2746 : template< typename Functor > bool
2747 0 : forSubsetRange( Functor& rFunctor,
2748 : ImplRenderer::ActionVector::const_iterator aRangeBegin,
2749 : ImplRenderer::ActionVector::const_iterator aRangeEnd,
2750 : sal_Int32 nStartIndex,
2751 : sal_Int32 nEndIndex,
2752 : const ImplRenderer::ActionVector::const_iterator& rEnd )
2753 : {
2754 0 : if( aRangeBegin == aRangeEnd )
2755 : {
2756 : // only a single action. Setup subset, and call functor
2757 : Action::Subset aSubset;
2758 0 : aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2759 0 : nStartIndex - aRangeBegin->mnOrigIndex );
2760 0 : aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2761 0 : nEndIndex - aRangeBegin->mnOrigIndex );
2762 :
2763 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2764 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2765 :
2766 0 : rFunctor( *aRangeBegin, aSubset );
2767 : }
2768 : else
2769 : {
2770 : // more than one action.
2771 :
2772 : // render partial first, full intermediate, and
2773 : // partial last action
2774 : Action::Subset aSubset;
2775 0 : aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2776 0 : nStartIndex - aRangeBegin->mnOrigIndex );
2777 0 : aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2778 :
2779 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2780 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2781 :
2782 0 : rFunctor( *aRangeBegin, aSubset );
2783 :
2784 : // first action rendered, skip to next
2785 0 : ++aRangeBegin;
2786 :
2787 : // render full middle actions
2788 0 : while( aRangeBegin != aRangeEnd )
2789 0 : rFunctor( *aRangeBegin++ );
2790 :
2791 0 : if( aRangeEnd == rEnd ||
2792 0 : aRangeEnd->mnOrigIndex > nEndIndex )
2793 : {
2794 : // aRangeEnd denotes end of action vector,
2795 :
2796 : // or
2797 :
2798 : // nEndIndex references something _after_
2799 : // aRangeBegin, but _before_ aRangeEnd
2800 :
2801 : // either way: no partial action left
2802 0 : return rFunctor.result();
2803 : }
2804 :
2805 0 : aSubset.mnSubsetBegin = 0;
2806 0 : aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2807 :
2808 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2809 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2810 :
2811 0 : rFunctor( *aRangeEnd, aSubset );
2812 : }
2813 :
2814 0 : return rFunctor.result();
2815 : }
2816 : }
2817 :
2818 0 : bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2819 : sal_Int32& io_rEndIndex,
2820 : ActionVector::const_iterator& o_rRangeBegin,
2821 : ActionVector::const_iterator& o_rRangeEnd ) const
2822 : {
2823 0 : ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2824 : "ImplRenderer::getSubsetIndices(): invalid action range" );
2825 :
2826 0 : ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2827 : "ImplRenderer::getSubsetIndices(): no actions to render" );
2828 :
2829 0 : const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2830 0 : const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2831 0 : maActions.back().mpAction->getActionCount() );
2832 :
2833 : // clip given range to permissible values (there might be
2834 : // ranges before and behind the valid indices)
2835 : io_rStartIndex = ::std::max( nMinActionIndex,
2836 0 : io_rStartIndex );
2837 : io_rEndIndex = ::std::min( nMaxActionIndex,
2838 0 : io_rEndIndex );
2839 :
2840 0 : if( io_rStartIndex == io_rEndIndex ||
2841 0 : io_rStartIndex > io_rEndIndex )
2842 : {
2843 : // empty range, don't render anything. The second
2844 : // condition e.g. happens if the requested range lies
2845 : // fully before or behind the valid action indices.
2846 0 : return false;
2847 : }
2848 :
2849 :
2850 0 : const ActionVector::const_iterator aBegin( maActions.begin() );
2851 0 : const ActionVector::const_iterator aEnd( maActions.end() );
2852 :
2853 :
2854 : // find start and end action
2855 : // =========================
2856 : o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2857 : MtfAction( ActionSharedPtr(), io_rStartIndex ),
2858 0 : UpperBoundActionIndexComparator() );
2859 : o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
2860 : MtfAction( ActionSharedPtr(), io_rEndIndex ),
2861 0 : UpperBoundActionIndexComparator() );
2862 0 : return true;
2863 : }
2864 :
2865 :
2866 : // Public methods
2867 :
2868 :
2869 0 : ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
2870 : const GDIMetaFile& rMtf,
2871 : const Parameters& rParams )
2872 : : CanvasGraphicHelper(rCanvas)
2873 : , maActions()
2874 : , fPageScale(0.0)
2875 : , nOriginX(0)
2876 : , nOriginY(0)
2877 : , nHDPI(0)
2878 : , nVDPI(0)
2879 : , nFrameLeft(0)
2880 : , nFrameTop(0)
2881 : , nFrameRight(0)
2882 : , nFrameBottom(0)
2883 : , nPixX(0)
2884 : , nPixY(0)
2885 : , nMmX(0)
2886 : , nMmY(0)
2887 : , mbMultipart(false)
2888 0 : , mMFlags(0)
2889 : {
2890 0 : memset (aObjects, 0, sizeof (aObjects));
2891 :
2892 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2893 :
2894 : OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2895 : "ImplRenderer::ImplRenderer(): Invalid canvas" );
2896 : OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2897 : "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2898 :
2899 : // make sure canvas and graphic device are valid; action
2900 : // creation don't check that every time
2901 0 : if( rCanvas.get() == NULL ||
2902 0 : !rCanvas->getUNOCanvas().is() ||
2903 0 : !rCanvas->getUNOCanvas()->getDevice().is() )
2904 : {
2905 : // leave actions empty
2906 0 : return;
2907 : }
2908 :
2909 0 : VectorOfOutDevStates aStateStack;
2910 :
2911 0 : VirtualDevice aVDev;
2912 0 : aVDev.EnableOutput( false );
2913 :
2914 : // Setup VDev for state tracking and mapping
2915 : // =========================================
2916 :
2917 0 : aVDev.SetMapMode( rMtf.GetPrefMapMode() );
2918 :
2919 0 : const Size aMtfSize( rMtf.GetPrefSize() );
2920 : const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
2921 0 : rMtf.GetPrefMapMode() ) );
2922 :
2923 : // #i44110# correct null-sized output - there are shapes
2924 : // which have zero size in at least one dimension
2925 0 : const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2926 0 : ::std::max( aMtfSizePixPre.Height(), 1L ) );
2927 :
2928 0 : sal_Int32 nCurrActions(0);
2929 : ActionFactoryParameters aParms(aStateStack,
2930 : rCanvas,
2931 : aVDev,
2932 : rParams,
2933 0 : nCurrActions );
2934 :
2935 : // init state stack
2936 0 : aStateStack.clearStateStack();
2937 :
2938 : // Setup local state, such that the metafile renders
2939 : // itself into a one-by-one square at the origin for
2940 : // identity view and render transformations
2941 0 : aStateStack.getState().transform.scale( 1.0 / aMtfSizePix.Width(),
2942 0 : 1.0 / aMtfSizePix.Height() );
2943 :
2944 0 : tools::calcLogic2PixelAffineTransform( aStateStack.getState().mapModeTransform,
2945 0 : aVDev );
2946 :
2947 0 : ColorSharedPtr pColor( getCanvas()->createColor() );
2948 :
2949 : {
2950 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2951 : // setup default text color to black
2952 0 : rState.textColor =
2953 0 : rState.textFillColor =
2954 0 : rState.textLineColor = pColor->getDeviceColor( 0x000000FF );
2955 : }
2956 :
2957 : // apply overrides from the Parameters struct
2958 0 : if( rParams.maFillColor.is_initialized() )
2959 : {
2960 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2961 0 : rState.isFillColorSet = true;
2962 0 : rState.fillColor = pColor->getDeviceColor( *rParams.maFillColor );
2963 : }
2964 0 : if( rParams.maLineColor.is_initialized() )
2965 : {
2966 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2967 0 : rState.isLineColorSet = true;
2968 0 : rState.lineColor = pColor->getDeviceColor( *rParams.maLineColor );
2969 : }
2970 0 : if( rParams.maTextColor.is_initialized() )
2971 : {
2972 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2973 0 : rState.isTextFillColorSet = true;
2974 0 : rState.isTextLineColorSet = true;
2975 0 : rState.textColor =
2976 0 : rState.textFillColor =
2977 0 : rState.textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
2978 : }
2979 0 : if( rParams.maFontName.is_initialized() ||
2980 0 : rParams.maFontWeight.is_initialized() ||
2981 0 : rParams.maFontLetterForm.is_initialized() ||
2982 0 : rParams.maFontUnderline.is_initialized() ||
2983 0 : rParams.maFontProportion.is_initialized() )
2984 : {
2985 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2986 :
2987 0 : rState.xFont = createFont( rState.fontRotation,
2988 : ::vcl::Font(), // default font
2989 0 : aParms );
2990 : }
2991 :
2992 : /* EMF+ */
2993 : createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
2994 : // we're
2995 : // changing
2996 : // the
2997 : // current
2998 : // action
2999 : // in
3000 : // createActions!
3001 : aParms,
3002 : true // TODO(P1): make subsettability configurable
3003 0 : );
3004 : }
3005 :
3006 0 : ImplRenderer::~ImplRenderer()
3007 : {
3008 : // don't leak EMFPObjects
3009 0 : for(unsigned int i=0; i<SAL_N_ELEMENTS(aObjects); ++i)
3010 0 : delete aObjects[i];
3011 0 : }
3012 :
3013 0 : bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
3014 : sal_Int32 nEndIndex ) const
3015 : {
3016 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3017 :
3018 0 : ActionVector::const_iterator aRangeBegin;
3019 0 : ActionVector::const_iterator aRangeEnd;
3020 :
3021 : try
3022 : {
3023 0 : if( !getSubsetIndices( nStartIndex, nEndIndex,
3024 0 : aRangeBegin, aRangeEnd ) )
3025 0 : return true; // nothing to render (but _that_ was successful)
3026 :
3027 : // now, aRangeBegin references the action in which the
3028 : // subset rendering must start, and aRangeEnd references
3029 : // the action in which the subset rendering must end (it
3030 : // might also end right at the start of the referenced
3031 : // action, such that zero of that action needs to be
3032 : // rendered).
3033 :
3034 :
3035 : // render subset of actions
3036 : // ========================
3037 :
3038 0 : ::basegfx::B2DHomMatrix aMatrix;
3039 : ::canvas::tools::getRenderStateTransform( aMatrix,
3040 0 : getRenderState() );
3041 :
3042 0 : ActionRenderer aRenderer( aMatrix );
3043 :
3044 : return forSubsetRange( aRenderer,
3045 : aRangeBegin,
3046 : aRangeEnd,
3047 : nStartIndex,
3048 : nEndIndex,
3049 0 : maActions.end() );
3050 : }
3051 0 : catch( uno::Exception& )
3052 : {
3053 : SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3054 : comphelper::anyToString( cppu::getCaughtException() ),
3055 : RTL_TEXTENCODING_UTF8 ).getStr() );
3056 :
3057 : // convert error to return value
3058 0 : return false;
3059 : }
3060 : }
3061 :
3062 0 : ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3063 : sal_Int32 nEndIndex ) const
3064 : {
3065 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3066 :
3067 0 : ActionVector::const_iterator aRangeBegin;
3068 0 : ActionVector::const_iterator aRangeEnd;
3069 :
3070 0 : if( !getSubsetIndices( nStartIndex, nEndIndex,
3071 0 : aRangeBegin, aRangeEnd ) )
3072 0 : return ::basegfx::B2DRange(); // nothing to render -> empty range
3073 :
3074 : // now, aRangeBegin references the action in which the
3075 : // subset querying must start, and aRangeEnd references
3076 : // the action in which the subset querying must end (it
3077 : // might also end right at the start of the referenced
3078 : // action, such that zero of that action needs to be
3079 : // queried).
3080 :
3081 :
3082 : // query bounds for subset of actions
3083 : // ==================================
3084 :
3085 0 : ::basegfx::B2DHomMatrix aMatrix;
3086 : ::canvas::tools::getRenderStateTransform( aMatrix,
3087 0 : getRenderState() );
3088 :
3089 0 : AreaQuery aQuery( aMatrix );
3090 : forSubsetRange( aQuery,
3091 : aRangeBegin,
3092 : aRangeEnd,
3093 : nStartIndex,
3094 : nEndIndex,
3095 0 : maActions.end() );
3096 :
3097 0 : return aQuery.getBounds();
3098 : }
3099 :
3100 0 : bool ImplRenderer::draw() const
3101 : {
3102 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3103 :
3104 0 : ::basegfx::B2DHomMatrix aMatrix;
3105 : ::canvas::tools::getRenderStateTransform( aMatrix,
3106 0 : getRenderState() );
3107 :
3108 : try
3109 : {
3110 0 : return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3111 : }
3112 0 : catch( uno::Exception& )
3113 : {
3114 : SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3115 : comphelper::anyToString( cppu::getCaughtException() ),
3116 : RTL_TEXTENCODING_UTF8 ).getStr() );
3117 :
3118 0 : return false;
3119 0 : }
3120 : }
3121 : }
3122 1143 : }
3123 :
3124 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|