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 MetaActionType::Transparent
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 2 : void VectorOfOutDevStates::clearStateStack()
248 : {
249 2 : m_aStates.clear();
250 2 : const OutDevState aDefaultState;
251 2 : m_aStates.push_back(aDefaultState);
252 2 : }
253 :
254 86 : OutDevState& VectorOfOutDevStates::getState()
255 : {
256 86 : return m_aStates.back();
257 : }
258 :
259 0 : const OutDevState& VectorOfOutDevStates::getState() const
260 : {
261 0 : return m_aStates.back();
262 : }
263 :
264 8 : void VectorOfOutDevStates::pushState(PushFlags nFlags)
265 : {
266 8 : m_aStates.push_back( getState() );
267 8 : getState().pushFlags = nFlags;
268 8 : }
269 :
270 8 : void VectorOfOutDevStates::popState()
271 : {
272 8 : 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 8 : OutDevState aCalculatedNewState( getState() );
284 :
285 : // selectively copy to-be-restored content over saved old
286 : // state
287 8 : m_aStates.pop_back();
288 :
289 8 : const OutDevState& rNewState( getState() );
290 :
291 8 : if( (aCalculatedNewState.pushFlags & PushFlags::LINECOLOR) )
292 : {
293 0 : aCalculatedNewState.lineColor = rNewState.lineColor;
294 0 : aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
295 : }
296 :
297 8 : if( (aCalculatedNewState.pushFlags & PushFlags::FILLCOLOR) )
298 : {
299 0 : aCalculatedNewState.fillColor = rNewState.fillColor;
300 0 : aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
301 : }
302 :
303 8 : 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 8 : if( (aCalculatedNewState.pushFlags & PushFlags::TEXTCOLOR) )
318 : {
319 0 : aCalculatedNewState.textColor = rNewState.textColor;
320 : }
321 :
322 8 : if( (aCalculatedNewState.pushFlags & PushFlags::MAPMODE) )
323 : {
324 0 : aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
325 : }
326 :
327 8 : if( (aCalculatedNewState.pushFlags & PushFlags::CLIPREGION) )
328 : {
329 8 : aCalculatedNewState.clip = rNewState.clip;
330 8 : aCalculatedNewState.clipRect = rNewState.clipRect;
331 8 : aCalculatedNewState.xClipPoly = rNewState.xClipPoly;
332 : }
333 :
334 : // TODO(F2): Raster ops NYI
335 : // if( (aCalculatedNewState.pushFlags & PushFlags::RASTEROP) )
336 : // {
337 : // }
338 :
339 8 : if( (aCalculatedNewState.pushFlags & PushFlags::TEXTFILLCOLOR) )
340 : {
341 0 : aCalculatedNewState.textFillColor = rNewState.textFillColor;
342 0 : aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
343 : }
344 :
345 8 : 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 8 : if( (aCalculatedNewState.pushFlags & PushFlags::TEXTLINECOLOR) )
356 : {
357 0 : aCalculatedNewState.textLineColor = rNewState.textLineColor;
358 0 : aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
359 : }
360 :
361 8 : 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 8 : aCalculatedNewState.pushFlags = rNewState.pushFlags;
374 :
375 : // flush to stack
376 8 : getState() = aCalculatedNewState;
377 : }
378 : else
379 : {
380 0 : m_aStates.pop_back();
381 : }
382 8 : }
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 )
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() == MetaActionType::COMMENT &&
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 : MetaActionType nType )
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() == MetaActionType::COMMENT &&
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 )
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 8 : void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
1052 : const ActionFactoryParameters& rParms,
1053 : bool bIntersect )
1054 : {
1055 8 : ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );
1056 8 : ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1057 :
1058 8 : const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1059 8 : const bool bEmptyClipPoly( rState.clip.count() == 0 );
1060 :
1061 8 : ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1062 : "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1063 :
1064 8 : if( !bIntersect ||
1065 0 : (bEmptyClipRect && bEmptyClipPoly) )
1066 : {
1067 8 : 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 8 : rState.clipRect.SetEmpty();
1097 :
1098 8 : if( rState.clip.count() == 0 )
1099 : {
1100 4 : if( rState.clipRect.IsEmpty() )
1101 : {
1102 4 : 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 12 : rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1122 8 : rParms.mrCanvas->getUNOCanvas()->getDevice(),
1123 4 : rState.clip );
1124 8 : }
1125 8 : }
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 2 : 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 2 : VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
1227 2 : const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1228 2 : ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
1229 2 : const Parameters& rParms(rFactoryParms.mrParms);
1230 2 : 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 52 : 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 MetaActionType::Transparent
1246 : // - SetFont to process font metric specific actions
1247 50 : pCurrAct->Execute( &rVDev );
1248 :
1249 : SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << static_cast<sal_uInt16>(pCurrAct->GetType()) << " (" << static_cast<sal_uInt16>(pCurrAct->GetType()) << ")");
1250 :
1251 50 : 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 MetaActionType::PUSH:
1262 : {
1263 8 : MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1264 8 : rStates.pushState(pPushAction->GetFlags());
1265 : }
1266 8 : break;
1267 :
1268 : case MetaActionType::POP:
1269 8 : rStates.popState();
1270 8 : break;
1271 :
1272 : case MetaActionType::TEXTLANGUAGE:
1273 : // FALLTHROUGH intended
1274 : case MetaActionType::REFPOINT:
1275 : // handled via pCurrAct->Execute( &rVDev )
1276 0 : break;
1277 :
1278 : case MetaActionType::MAPMODE:
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 MetaActionType::CLIPREGION:
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 MetaActionType::ISECTRECTCLIPREGION:
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 MetaActionType::ISECTREGIONCLIPREGION:
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 MetaActionType::MOVECLIPREGION:
1387 : // TODO(F2): NYI
1388 0 : break;
1389 :
1390 : case MetaActionType::LINECOLOR:
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 MetaActionType::FILLCOLOR:
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 MetaActionType::TEXTCOLOR:
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 MetaActionType::Transparent
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 MetaActionType::TEXTFILLCOLOR:
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 MetaActionType::TEXTLINECOLOR:
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 MetaActionType::TEXTALIGN:
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 MetaActionType::FONT:
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 MetaActionType::RASTEROP:
1512 : // TODO(F2): NYI
1513 8 : break;
1514 :
1515 : case MetaActionType::LAYOUTMODE:
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 MetaActionType::GRADIENT:
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 MetaActionType::HATCH:
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 MetaActionType::EPS:
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 MetaActionType::COMMENT:
1618 : {
1619 26 : MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1620 :
1621 : // Handle gradients
1622 26 : 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 MetaActionType::GRADIENTEX:
1633 0 : pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1634 0 : break;
1635 :
1636 : // skip broken-down rendering, output gradient when sequence is ended
1637 : case MetaActionType::COMMENT:
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 0 : default: break;
1653 : }
1654 : }
1655 : }
1656 : // TODO(P2): Handle drawing layer strokes, via
1657 : // XPATHSTROKE_SEQ_BEGIN comment
1658 :
1659 : // Handle drawing layer fills
1660 26 : else if( pAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1661 : {
1662 0 : const sal_uInt8* pData = pAct->GetData();
1663 0 : if ( pData )
1664 : {
1665 0 : SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pData), pAct->GetDataSize(), StreamMode::READ );
1666 :
1667 0 : SvtGraphicFill aFill;
1668 0 : ReadSvtGraphicFill( aMemStm, aFill );
1669 :
1670 : // TODO(P2): Also handle gradients and
1671 : // hatches like this
1672 :
1673 : // only evaluate comment for pure
1674 : // bitmap fills. If a transparency
1675 : // gradient is involved (denoted by
1676 : // the FloatTransparent action), take
1677 : // the normal meta actions.
1678 0 : if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1679 : !isActionContained( rMtf,
1680 : "XPATHFILL_SEQ_END",
1681 0 : MetaActionType::FLOATTRANSPARENT ) )
1682 : {
1683 0 : rendering::Texture aTexture;
1684 :
1685 : // TODO(F1): the SvtGraphicFill
1686 : // can also transport metafiles
1687 : // here, handle that case, too
1688 0 : Graphic aGraphic;
1689 0 : aFill.getGraphic( aGraphic );
1690 :
1691 0 : BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
1692 0 : const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1693 :
1694 0 : ::SvtGraphicFill::Transform aTransform;
1695 0 : aFill.getTransform( aTransform );
1696 :
1697 0 : ::basegfx::B2DHomMatrix aMatrix;
1698 :
1699 : // convert to basegfx matrix
1700 0 : aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1701 0 : aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1702 0 : aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1703 0 : aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1704 0 : aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1705 0 : aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1706 :
1707 0 : ::basegfx::B2DHomMatrix aScale;
1708 0 : aScale.scale( aBmpSize.Width(),
1709 0 : aBmpSize.Height() );
1710 :
1711 : // post-multiply with the bitmap
1712 : // size (XCanvas' texture assumes
1713 : // the given bitmap to be
1714 : // normalized to [0,1]x[0,1]
1715 : // rectangle)
1716 0 : aMatrix = aMatrix * aScale;
1717 :
1718 : // pre-multiply with the
1719 : // logic-to-pixel scale factor
1720 : // (the metafile comment works in
1721 : // logical coordinates).
1722 0 : ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1723 : aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1724 0 : rVDev );
1725 :
1726 : ::basegfx::unotools::affineMatrixFromHomMatrix(
1727 : aTexture.AffineTransform,
1728 0 : aMatrix );
1729 :
1730 0 : aTexture.Alpha = 1.0 - aFill.getTransparency();
1731 0 : aTexture.Bitmap =
1732 : vcl::unotools::xBitmapFromBitmapEx(
1733 0 : rCanvas->getUNOCanvas()->getDevice(),
1734 0 : aBmpEx );
1735 0 : if( aFill.isTiling() )
1736 : {
1737 0 : aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1738 0 : aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1739 : }
1740 : else
1741 : {
1742 0 : aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1743 0 : aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1744 : }
1745 :
1746 0 : ::tools::PolyPolygon aPath;
1747 0 : aFill.getPath( aPath );
1748 :
1749 0 : ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1750 0 : aPoly.transform( rStates.getState().mapModeTransform );
1751 : ActionSharedPtr pPolyAction(
1752 : internal::PolyPolyActionFactory::createPolyPolyAction(
1753 : aPoly,
1754 : rCanvas,
1755 0 : rStates.getState(),
1756 0 : aTexture ) );
1757 :
1758 0 : if( pPolyAction )
1759 : {
1760 : maActions.push_back(
1761 : MtfAction(
1762 : pPolyAction,
1763 0 : io_rCurrActionIndex ) );
1764 :
1765 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1766 : }
1767 :
1768 : // skip broken-down render output
1769 : skipContent( rMtf,
1770 : "XPATHFILL_SEQ_END",
1771 0 : io_rCurrActionIndex );
1772 0 : }
1773 : }
1774 : }
1775 : // Handle drawing layer fills
1776 26 : else if( pAct->GetComment() == "EMF_PLUS" ) {
1777 : static int count = -1, limit = 0x7fffffff;
1778 24 : if (count == -1) {
1779 1 : count = 0;
1780 1 : if (char *env = getenv ("EMF_PLUS_LIMIT")) {
1781 0 : limit = atoi (env);
1782 : SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit);
1783 : }
1784 : }
1785 : SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct->GetDataSize ());
1786 24 : if (count < limit)
1787 24 : processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
1788 24 : count ++;
1789 2 : } else if( pAct->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1790 : SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct->GetDataSize ());
1791 :
1792 2 : SvMemoryStream rMF (const_cast<sal_uInt8 *>(pAct->GetData ()), pAct->GetDataSize (), StreamMode::READ);
1793 :
1794 2 : rMF.ReadInt32( nFrameLeft ).ReadInt32( nFrameTop ).ReadInt32( nFrameRight ).ReadInt32( nFrameBottom );
1795 : SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft << "," << nFrameTop << " - " << nFrameRight << "," << nFrameBottom);
1796 2 : rMF.ReadInt32( nPixX ).ReadInt32( nPixY ).ReadInt32( nMmX ).ReadInt32( nMmY );
1797 : SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX << "x" << nPixY << " mm size: " << nMmX << "x" << nMmY);
1798 :
1799 2 : ReadXForm( rMF, aBaseTransform );
1800 : //aWorldTransform.Set (aBaseTransform);
1801 : }
1802 : }
1803 26 : break;
1804 :
1805 :
1806 :
1807 : // In the third part of this monster-switch, we
1808 : // handle all 'acting' meta actions. These are all
1809 : // processed by constructing function objects for
1810 : // them, which will later ease caching.
1811 :
1812 :
1813 :
1814 : case MetaActionType::POINT:
1815 : {
1816 0 : const OutDevState& rState( rStates.getState() );
1817 0 : if( rState.lineColor.getLength() )
1818 : {
1819 : ActionSharedPtr pPointAction(
1820 : internal::PointActionFactory::createPointAction(
1821 0 : rState.mapModeTransform * vcl::unotools::b2DPointFromPoint(
1822 0 : static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1823 : rCanvas,
1824 0 : rState ) );
1825 :
1826 0 : if( pPointAction )
1827 : {
1828 : maActions.push_back(
1829 : MtfAction(
1830 : pPointAction,
1831 0 : io_rCurrActionIndex ) );
1832 :
1833 0 : io_rCurrActionIndex += pPointAction->getActionCount()-1;
1834 0 : }
1835 : }
1836 : }
1837 0 : break;
1838 :
1839 : case MetaActionType::PIXEL:
1840 : {
1841 0 : const OutDevState& rState( rStates.getState() );
1842 0 : if( rState.lineColor.getLength() )
1843 : {
1844 : ActionSharedPtr pPointAction(
1845 : internal::PointActionFactory::createPointAction(
1846 0 : rState.mapModeTransform * vcl::unotools::b2DPointFromPoint(
1847 0 : static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1848 : rCanvas,
1849 : rState,
1850 0 : static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1851 :
1852 0 : if( pPointAction )
1853 : {
1854 : maActions.push_back(
1855 : MtfAction(
1856 : pPointAction,
1857 0 : io_rCurrActionIndex ) );
1858 :
1859 0 : io_rCurrActionIndex += pPointAction->getActionCount()-1;
1860 0 : }
1861 : }
1862 : }
1863 0 : break;
1864 :
1865 : case MetaActionType::LINE:
1866 : {
1867 0 : const OutDevState& rState( rStates.getState() );
1868 0 : if( rState.lineColor.getLength() )
1869 : {
1870 0 : MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1871 :
1872 0 : const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1873 :
1874 : const ::basegfx::B2DPoint aStartPoint(
1875 0 : rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1876 : const ::basegfx::B2DPoint aEndPoint(
1877 0 : rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1878 :
1879 0 : ActionSharedPtr pLineAction;
1880 :
1881 0 : if( rLineInfo.IsDefault() )
1882 : {
1883 : // plain hair line
1884 0 : pLineAction =
1885 : internal::LineActionFactory::createLineAction(
1886 : aStartPoint,
1887 : aEndPoint,
1888 : rCanvas,
1889 0 : rState );
1890 :
1891 0 : if( pLineAction )
1892 : {
1893 : maActions.push_back(
1894 : MtfAction(
1895 : pLineAction,
1896 0 : io_rCurrActionIndex ) );
1897 :
1898 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
1899 : }
1900 : }
1901 0 : else if( LINE_NONE != rLineInfo.GetStyle() )
1902 : {
1903 : // 'thick' line
1904 0 : rendering::StrokeAttributes aStrokeAttributes;
1905 :
1906 : setupStrokeAttributes( aStrokeAttributes,
1907 : rFactoryParms,
1908 0 : rLineInfo );
1909 :
1910 : // XCanvas can only stroke polygons,
1911 : // not simple lines - thus, handle
1912 : // this case via the polypolygon
1913 : // action
1914 0 : ::basegfx::B2DPolygon aPoly;
1915 0 : aPoly.append( aStartPoint );
1916 0 : aPoly.append( aEndPoint );
1917 0 : pLineAction =
1918 : internal::PolyPolyActionFactory::createPolyPolyAction(
1919 : ::basegfx::B2DPolyPolygon( aPoly ),
1920 0 : rCanvas, rState, aStrokeAttributes );
1921 :
1922 0 : if( pLineAction )
1923 : {
1924 : maActions.push_back(
1925 : MtfAction(
1926 : pLineAction,
1927 0 : io_rCurrActionIndex ) );
1928 :
1929 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
1930 0 : }
1931 0 : }
1932 : // else: line style is default
1933 : // (i.e. invisible), don't generate action
1934 : }
1935 : }
1936 0 : break;
1937 :
1938 : case MetaActionType::RECT:
1939 : {
1940 : const Rectangle& rRect(
1941 0 : static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
1942 :
1943 0 : if( rRect.IsEmpty() )
1944 0 : break;
1945 :
1946 0 : const OutDevState& rState( rStates.getState() );
1947 : const ::basegfx::B2DPoint aTopLeftPixel(
1948 0 : rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
1949 : const ::basegfx::B2DPoint aBottomRightPixel(
1950 0 : rState.mapModeTransform * vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1951 : // #121100# OutputDevice::DrawRect() fills
1952 : // rectangles Apple-like, i.e. with one
1953 : // additional pixel to the right and bottom.
1954 0 : ::basegfx::B2DPoint(1,1) );
1955 :
1956 : createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1957 : ::basegfx::B2DRange( aTopLeftPixel,
1958 : aBottomRightPixel )),
1959 0 : rFactoryParms );
1960 0 : break;
1961 : }
1962 :
1963 : case MetaActionType::ROUNDRECT:
1964 : {
1965 : const Rectangle& rRect(
1966 0 : static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
1967 :
1968 0 : if( rRect.IsEmpty() )
1969 0 : break;
1970 :
1971 : ::basegfx::B2DPolygon aPoly(
1972 : ::basegfx::tools::createPolygonFromRect(
1973 : ::basegfx::B2DRange(
1974 : vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1975 0 : vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1976 : ::basegfx::B2DPoint(1,1) ),
1977 0 : ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound() ) / rRect.GetWidth(),
1978 0 : ( (double) static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) / rRect.GetHeight() ) );
1979 0 : aPoly.transform( rStates.getState().mapModeTransform );
1980 :
1981 : createFillAndStroke( aPoly,
1982 0 : rFactoryParms );
1983 : }
1984 0 : break;
1985 :
1986 : case MetaActionType::ELLIPSE:
1987 : {
1988 : const Rectangle& rRect(
1989 0 : static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
1990 :
1991 0 : if( rRect.IsEmpty() )
1992 0 : break;
1993 :
1994 : const ::basegfx::B2DRange aRange(
1995 : vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
1996 0 : vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1997 0 : ::basegfx::B2DPoint(1,1) );
1998 :
1999 : ::basegfx::B2DPolygon aPoly(
2000 : ::basegfx::tools::createPolygonFromEllipse(
2001 : aRange.getCenter(),
2002 : aRange.getWidth(),
2003 0 : aRange.getHeight() ));
2004 0 : aPoly.transform( rStates.getState().mapModeTransform );
2005 :
2006 : createFillAndStroke( aPoly,
2007 0 : rFactoryParms );
2008 : }
2009 0 : break;
2010 :
2011 : case MetaActionType::ARC:
2012 : {
2013 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2014 0 : const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2015 0 : static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2016 0 : static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2017 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2018 0 : aPoly.transform( rStates.getState().mapModeTransform );
2019 :
2020 : createFillAndStroke( aPoly,
2021 0 : rFactoryParms );
2022 : }
2023 0 : break;
2024 :
2025 : case MetaActionType::PIE:
2026 : {
2027 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2028 0 : const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2029 0 : static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2030 0 : static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2031 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2032 0 : aPoly.transform( rStates.getState().mapModeTransform );
2033 :
2034 : createFillAndStroke( aPoly,
2035 0 : rFactoryParms );
2036 : }
2037 0 : break;
2038 :
2039 : case MetaActionType::CHORD:
2040 : {
2041 : // TODO(F1): Missing basegfx functionality. Mind empty rects!
2042 0 : const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2043 0 : static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2044 0 : static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2045 0 : ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2046 0 : aPoly.transform( rStates.getState().mapModeTransform );
2047 :
2048 : createFillAndStroke( aPoly,
2049 0 : rFactoryParms );
2050 : }
2051 0 : break;
2052 :
2053 : case MetaActionType::POLYLINE:
2054 : {
2055 0 : const OutDevState& rState( rStates.getState() );
2056 0 : if( rState.lineColor.getLength() ||
2057 0 : rState.fillColor.getLength() )
2058 : {
2059 0 : MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2060 :
2061 0 : const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2062 0 : ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2063 0 : aPoly.transform( rState.mapModeTransform );
2064 :
2065 0 : ActionSharedPtr pLineAction;
2066 :
2067 0 : if( rLineInfo.IsDefault() )
2068 : {
2069 : // plain hair line polygon
2070 0 : pLineAction =
2071 : internal::PolyPolyActionFactory::createLinePolyPolyAction(
2072 : ::basegfx::B2DPolyPolygon(aPoly),
2073 : rCanvas,
2074 0 : rState );
2075 :
2076 0 : if( pLineAction )
2077 : {
2078 : maActions.push_back(
2079 : MtfAction(
2080 : pLineAction,
2081 0 : io_rCurrActionIndex ) );
2082 :
2083 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
2084 : }
2085 : }
2086 0 : else if( LINE_NONE != rLineInfo.GetStyle() )
2087 : {
2088 : // 'thick' line polygon
2089 0 : rendering::StrokeAttributes aStrokeAttributes;
2090 :
2091 : setupStrokeAttributes( aStrokeAttributes,
2092 : rFactoryParms,
2093 0 : rLineInfo );
2094 :
2095 0 : pLineAction =
2096 : internal::PolyPolyActionFactory::createPolyPolyAction(
2097 : ::basegfx::B2DPolyPolygon(aPoly),
2098 : rCanvas,
2099 : rState,
2100 0 : aStrokeAttributes ) ;
2101 :
2102 0 : if( pLineAction )
2103 : {
2104 : maActions.push_back(
2105 : MtfAction(
2106 : pLineAction,
2107 0 : io_rCurrActionIndex ) );
2108 :
2109 0 : io_rCurrActionIndex += pLineAction->getActionCount()-1;
2110 0 : }
2111 0 : }
2112 : // else: line style is default
2113 : // (i.e. invisible), don't generate action
2114 : }
2115 : }
2116 0 : break;
2117 :
2118 : case MetaActionType::POLYGON:
2119 : {
2120 0 : ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2121 0 : aPoly.transform( rStates.getState().mapModeTransform );
2122 : createFillAndStroke( aPoly,
2123 0 : rFactoryParms );
2124 : }
2125 0 : break;
2126 :
2127 : case MetaActionType::POLYPOLYGON:
2128 : {
2129 0 : ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2130 0 : aPoly.transform( rStates.getState().mapModeTransform );
2131 : createFillAndStroke( aPoly,
2132 0 : rFactoryParms );
2133 : }
2134 0 : break;
2135 :
2136 : case MetaActionType::BMP:
2137 : {
2138 0 : MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2139 :
2140 : ActionSharedPtr pBmpAction(
2141 : internal::BitmapActionFactory::createBitmapAction(
2142 0 : pAct->GetBitmap(),
2143 0 : rStates.getState().mapModeTransform *
2144 0 : vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2145 : rCanvas,
2146 0 : rStates.getState() ) );
2147 :
2148 0 : if( pBmpAction )
2149 : {
2150 : maActions.push_back(
2151 : MtfAction(
2152 : pBmpAction,
2153 0 : io_rCurrActionIndex ) );
2154 :
2155 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2156 0 : }
2157 : }
2158 0 : break;
2159 :
2160 : case MetaActionType::BMPSCALE:
2161 : {
2162 0 : MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2163 :
2164 : ActionSharedPtr pBmpAction(
2165 : internal::BitmapActionFactory::createBitmapAction(
2166 0 : pAct->GetBitmap(),
2167 0 : rStates.getState().mapModeTransform *
2168 0 : vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2169 0 : rStates.getState().mapModeTransform *
2170 0 : vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2171 : rCanvas,
2172 0 : rStates.getState() ) );
2173 :
2174 0 : if( pBmpAction )
2175 : {
2176 : maActions.push_back(
2177 : MtfAction(
2178 : pBmpAction,
2179 0 : io_rCurrActionIndex ) );
2180 :
2181 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2182 0 : }
2183 : }
2184 0 : break;
2185 :
2186 : case MetaActionType::BMPSCALEPART:
2187 : {
2188 0 : MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2189 :
2190 : // crop bitmap to given source rectangle (no
2191 : // need to copy and convert the whole bitmap)
2192 0 : Bitmap aBmp( pAct->GetBitmap() );
2193 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2194 0 : pAct->GetSrcSize() );
2195 0 : aBmp.Crop( aCropRect );
2196 :
2197 : ActionSharedPtr pBmpAction(
2198 : internal::BitmapActionFactory::createBitmapAction(
2199 : aBmp,
2200 0 : rStates.getState().mapModeTransform *
2201 0 : vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2202 0 : rStates.getState().mapModeTransform *
2203 0 : vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2204 : rCanvas,
2205 0 : rStates.getState() ) );
2206 :
2207 0 : if( pBmpAction )
2208 : {
2209 : maActions.push_back(
2210 : MtfAction(
2211 : pBmpAction,
2212 0 : io_rCurrActionIndex ) );
2213 :
2214 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2215 0 : }
2216 : }
2217 0 : break;
2218 :
2219 : case MetaActionType::BMPEX:
2220 : {
2221 0 : MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2222 :
2223 : ActionSharedPtr pBmpAction(
2224 : internal::BitmapActionFactory::createBitmapAction(
2225 0 : pAct->GetBitmapEx(),
2226 0 : rStates.getState().mapModeTransform *
2227 0 : vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2228 : rCanvas,
2229 0 : rStates.getState() ) );
2230 :
2231 0 : if( pBmpAction )
2232 : {
2233 : maActions.push_back(
2234 : MtfAction(
2235 : pBmpAction,
2236 0 : io_rCurrActionIndex ) );
2237 :
2238 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2239 0 : }
2240 : }
2241 0 : break;
2242 :
2243 : case MetaActionType::BMPEXSCALE:
2244 : {
2245 0 : MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2246 :
2247 : ActionSharedPtr pBmpAction(
2248 : internal::BitmapActionFactory::createBitmapAction(
2249 0 : pAct->GetBitmapEx(),
2250 0 : rStates.getState().mapModeTransform *
2251 0 : vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2252 0 : rStates.getState().mapModeTransform *
2253 0 : vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2254 : rCanvas,
2255 0 : rStates.getState() ) );
2256 :
2257 0 : if( pBmpAction )
2258 : {
2259 : maActions.push_back(
2260 : MtfAction(
2261 : pBmpAction,
2262 0 : io_rCurrActionIndex ) );
2263 :
2264 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2265 0 : }
2266 : }
2267 0 : break;
2268 :
2269 : case MetaActionType::BMPEXSCALEPART:
2270 : {
2271 0 : MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2272 :
2273 : // crop bitmap to given source rectangle (no
2274 : // need to copy and convert the whole bitmap)
2275 0 : BitmapEx aBmp( pAct->GetBitmapEx() );
2276 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2277 0 : pAct->GetSrcSize() );
2278 0 : aBmp.Crop( aCropRect );
2279 :
2280 : ActionSharedPtr pBmpAction(
2281 : internal::BitmapActionFactory::createBitmapAction(
2282 : aBmp,
2283 0 : rStates.getState().mapModeTransform *
2284 0 : vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2285 0 : rStates.getState().mapModeTransform *
2286 0 : vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2287 : rCanvas,
2288 0 : rStates.getState() ) );
2289 :
2290 0 : if( pBmpAction )
2291 : {
2292 : maActions.push_back(
2293 : MtfAction(
2294 : pBmpAction,
2295 0 : io_rCurrActionIndex ) );
2296 :
2297 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2298 0 : }
2299 : }
2300 0 : break;
2301 :
2302 : case MetaActionType::MASK:
2303 : {
2304 0 : MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2305 :
2306 : // create masked BitmapEx right here, as the
2307 : // canvas does not provide equivalent
2308 : // functionality
2309 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2310 0 : pAct->GetColor() ));
2311 :
2312 : ActionSharedPtr pBmpAction(
2313 : internal::BitmapActionFactory::createBitmapAction(
2314 : aBmp,
2315 0 : rStates.getState().mapModeTransform *
2316 0 : vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2317 : rCanvas,
2318 0 : rStates.getState() ) );
2319 :
2320 0 : if( pBmpAction )
2321 : {
2322 : maActions.push_back(
2323 : MtfAction(
2324 : pBmpAction,
2325 0 : io_rCurrActionIndex ) );
2326 :
2327 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2328 0 : }
2329 : }
2330 0 : break;
2331 :
2332 : case MetaActionType::MASKSCALE:
2333 : {
2334 0 : MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2335 :
2336 : // create masked BitmapEx right here, as the
2337 : // canvas does not provide equivalent
2338 : // functionality
2339 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2340 0 : pAct->GetColor() ));
2341 :
2342 : ActionSharedPtr pBmpAction(
2343 : internal::BitmapActionFactory::createBitmapAction(
2344 : aBmp,
2345 0 : rStates.getState().mapModeTransform *
2346 0 : vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2347 0 : rStates.getState().mapModeTransform *
2348 0 : vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2349 : rCanvas,
2350 0 : rStates.getState() ) );
2351 :
2352 0 : if( pBmpAction )
2353 : {
2354 : maActions.push_back(
2355 : MtfAction(
2356 : pBmpAction,
2357 0 : io_rCurrActionIndex ) );
2358 :
2359 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2360 0 : }
2361 : }
2362 0 : break;
2363 :
2364 : case MetaActionType::MASKSCALEPART:
2365 : {
2366 0 : MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2367 :
2368 : // create masked BitmapEx right here, as the
2369 : // canvas does not provide equivalent
2370 : // functionality
2371 0 : BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2372 0 : pAct->GetColor() ));
2373 :
2374 : // crop bitmap to given source rectangle (no
2375 : // need to copy and convert the whole bitmap)
2376 0 : const Rectangle aCropRect( pAct->GetSrcPoint(),
2377 0 : pAct->GetSrcSize() );
2378 0 : aBmp.Crop( aCropRect );
2379 :
2380 : ActionSharedPtr pBmpAction(
2381 : internal::BitmapActionFactory::createBitmapAction(
2382 : aBmp,
2383 0 : rStates.getState().mapModeTransform *
2384 0 : vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2385 0 : rStates.getState().mapModeTransform *
2386 0 : vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2387 : rCanvas,
2388 0 : rStates.getState() ) );
2389 :
2390 0 : if( pBmpAction )
2391 : {
2392 : maActions.push_back(
2393 : MtfAction(
2394 : pBmpAction,
2395 0 : io_rCurrActionIndex ) );
2396 :
2397 0 : io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2398 0 : }
2399 : }
2400 0 : break;
2401 :
2402 : case MetaActionType::GRADIENTEX:
2403 : // TODO(F1): use native Canvas gradients here
2404 : // action is ignored here, because redundant to MetaActionType::GRADIENT
2405 0 : break;
2406 :
2407 : case MetaActionType::WALLPAPER:
2408 : // TODO(F2): NYI
2409 0 : break;
2410 :
2411 : case MetaActionType::Transparent:
2412 : {
2413 0 : const OutDevState& rState( rStates.getState() );
2414 0 : if( rState.lineColor.getLength() ||
2415 0 : rState.fillColor.getLength() )
2416 : {
2417 0 : MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2418 0 : ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2419 0 : aPoly.transform( rState.mapModeTransform );
2420 :
2421 : ActionSharedPtr pPolyAction(
2422 : internal::PolyPolyActionFactory::createPolyPolyAction(
2423 : aPoly,
2424 : rCanvas,
2425 : rState,
2426 0 : pAct->GetTransparence() ) );
2427 :
2428 0 : if( pPolyAction )
2429 : {
2430 : maActions.push_back(
2431 : MtfAction(
2432 : pPolyAction,
2433 0 : io_rCurrActionIndex ) );
2434 :
2435 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2436 0 : }
2437 : }
2438 : }
2439 0 : break;
2440 :
2441 : case MetaActionType::FLOATTRANSPARENT:
2442 : {
2443 0 : MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2444 :
2445 : internal::MtfAutoPtr pMtf(
2446 0 : new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2447 :
2448 : // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2449 : internal::GradientAutoPtr pGradient(
2450 0 : new Gradient( pAct->GetGradient() ) );
2451 :
2452 : DBG_TESTSOLARMUTEX();
2453 :
2454 : ActionSharedPtr pFloatTransAction(
2455 : internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2456 0 : std::move(pMtf),
2457 0 : std::move(pGradient),
2458 : rParms,
2459 0 : rStates.getState().mapModeTransform *
2460 0 : vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2461 0 : rStates.getState().mapModeTransform *
2462 0 : vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2463 : rCanvas,
2464 0 : rStates.getState() ) );
2465 :
2466 0 : if( pFloatTransAction )
2467 : {
2468 : maActions.push_back(
2469 : MtfAction(
2470 : pFloatTransAction,
2471 0 : io_rCurrActionIndex ) );
2472 :
2473 0 : io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2474 0 : }
2475 : }
2476 0 : break;
2477 :
2478 : case MetaActionType::TEXT:
2479 : {
2480 0 : MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2481 0 : OUString sText = pAct->GetText();
2482 :
2483 0 : if (rVDev.GetDigitLanguage())
2484 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2485 :
2486 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2487 :
2488 : createTextAction(
2489 0 : pAct->GetPoint(),
2490 : sText,
2491 : pAct->GetIndex(),
2492 : nLen,
2493 : NULL,
2494 : rFactoryParms,
2495 0 : bSubsettableActions );
2496 : }
2497 0 : break;
2498 :
2499 : case MetaActionType::TEXTARRAY:
2500 : {
2501 0 : MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2502 0 : OUString sText = pAct->GetText();
2503 :
2504 0 : if (rVDev.GetDigitLanguage())
2505 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2506 :
2507 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2508 :
2509 : createTextAction(
2510 0 : pAct->GetPoint(),
2511 : sText,
2512 : pAct->GetIndex(),
2513 : nLen,
2514 0 : pAct->GetDXArray(),
2515 : rFactoryParms,
2516 0 : bSubsettableActions );
2517 : }
2518 0 : break;
2519 :
2520 : case MetaActionType::TEXTLINE:
2521 : {
2522 0 : MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2523 :
2524 0 : const OutDevState& rState( rStates.getState() );
2525 : const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2526 0 : rVDev ) );
2527 : const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2528 0 : ::basegfx::B2DSize(pAct->GetWidth(),
2529 0 : 0 ));
2530 :
2531 : ActionSharedPtr pPolyAction(
2532 : PolyPolyActionFactory::createPolyPolyAction(
2533 : tools::createTextLinesPolyPolygon(
2534 0 : rState.mapModeTransform *
2535 : ::basegfx::B2DPoint(
2536 0 : vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2537 : vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2538 0 : aSize.getX(),
2539 : tools::createTextLineInfo( rVDev,
2540 : rState )),
2541 : rCanvas,
2542 0 : rState ) );
2543 :
2544 0 : if( pPolyAction.get() )
2545 : {
2546 : maActions.push_back(
2547 : MtfAction(
2548 : pPolyAction,
2549 0 : io_rCurrActionIndex ) );
2550 :
2551 0 : io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2552 0 : }
2553 : }
2554 0 : break;
2555 :
2556 : case MetaActionType::TEXTRECT:
2557 : {
2558 0 : MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2559 :
2560 0 : rStates.pushState(PushFlags::ALL);
2561 :
2562 : // use the VDev to break up the text rect
2563 : // action into readily formatted lines
2564 0 : GDIMetaFile aTmpMtf;
2565 0 : rVDev.AddTextRectActions( pAct->GetRect(),
2566 0 : pAct->GetText(),
2567 : pAct->GetStyle(),
2568 0 : aTmpMtf );
2569 :
2570 : createActions( aTmpMtf,
2571 : rFactoryParms,
2572 0 : bSubsettableActions );
2573 :
2574 0 : rStates.popState();
2575 :
2576 0 : break;
2577 : }
2578 :
2579 : case MetaActionType::STRETCHTEXT:
2580 : {
2581 0 : MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2582 0 : OUString sText = pAct->GetText();
2583 :
2584 0 : if (rVDev.GetDigitLanguage())
2585 0 : sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage());
2586 :
2587 0 : const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
2588 :
2589 : // #i70897# Nothing to do, actually...
2590 0 : if( nLen == 0 )
2591 0 : break;
2592 :
2593 : // have to fit the text into the given
2594 : // width. This is achieved by internally
2595 : // generating a DX array, and uniformly
2596 : // distributing the excess/insufficient width
2597 : // to every logical character.
2598 0 : ::boost::scoped_array< long > pDXArray( new long[nLen] );
2599 :
2600 0 : rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2601 0 : pAct->GetIndex(), pAct->GetLen() );
2602 :
2603 0 : const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2604 :
2605 : // Last entry of pDXArray contains total width of the text
2606 0 : long* p = pDXArray.get();
2607 0 : for (sal_Int32 i = 1; i <= nLen; ++i)
2608 : {
2609 : // calc ratio for every array entry, to
2610 : // distribute rounding errors 'evenly'
2611 : // across the characters. Note that each
2612 : // entry represents the 'end' position of
2613 : // the corresponding character, thus, we
2614 : // let i run from 1 to nLen.
2615 0 : *p++ += (long)i*nWidthDifference/nLen;
2616 : }
2617 :
2618 : createTextAction(
2619 0 : pAct->GetPoint(),
2620 : sText,
2621 : pAct->GetIndex(),
2622 : nLen,
2623 0 : pDXArray.get(),
2624 : rFactoryParms,
2625 0 : bSubsettableActions );
2626 : }
2627 0 : break;
2628 :
2629 : default:
2630 : SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2631 0 : break;
2632 : }
2633 :
2634 : // increment action index (each mtf action counts _at
2635 : // least_ one. Some count for more, therefore,
2636 : // io_rCurrActionIndex is sometimes incremented by
2637 : // pAct->getActionCount()-1 above, the -1 being the
2638 : // correction for the unconditional increment here).
2639 50 : ++io_rCurrActionIndex;
2640 : }
2641 :
2642 2 : return true;
2643 : }
2644 :
2645 :
2646 : namespace
2647 : {
2648 6 : class ActionRenderer
2649 : {
2650 : public:
2651 2 : explicit ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2652 : maTransformation( rTransformation ),
2653 2 : mbRet( true )
2654 : {
2655 2 : }
2656 :
2657 2 : bool result() const
2658 : {
2659 2 : return mbRet;
2660 : }
2661 :
2662 12 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2663 : {
2664 : // ANDing the result. We want to fail if at least
2665 : // one action failed.
2666 12 : mbRet &= rAction.mpAction->render( maTransformation );
2667 12 : }
2668 :
2669 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2670 : const Action::Subset& rSubset )
2671 : {
2672 : // ANDing the result. We want to fail if at least
2673 : // one action failed.
2674 0 : mbRet &= rAction.mpAction->renderSubset( maTransformation,
2675 0 : rSubset );
2676 0 : }
2677 :
2678 : private:
2679 : ::basegfx::B2DHomMatrix maTransformation;
2680 : bool mbRet;
2681 : };
2682 :
2683 0 : class AreaQuery
2684 : {
2685 : public:
2686 0 : explicit AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2687 : maTransformation( rTransformation ),
2688 0 : maBounds()
2689 : {
2690 0 : }
2691 :
2692 0 : static bool result()
2693 : {
2694 0 : return true; // nothing can fail here
2695 : }
2696 :
2697 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2698 : {
2699 0 : maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2700 0 : }
2701 :
2702 0 : void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
2703 : const Action::Subset& rSubset )
2704 : {
2705 0 : maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2706 0 : rSubset ) );
2707 0 : }
2708 :
2709 0 : ::basegfx::B2DRange getBounds() const
2710 : {
2711 0 : return maBounds;
2712 : }
2713 :
2714 : private:
2715 : ::basegfx::B2DHomMatrix maTransformation;
2716 : ::basegfx::B2DRange maBounds;
2717 : };
2718 :
2719 : // Doing that via inline class. Compilers tend to not inline free
2720 : // functions.
2721 : struct UpperBoundActionIndexComparator
2722 : {
2723 0 : bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2724 : const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2725 : {
2726 : const sal_Int32 nLHSCount( rLHS.mpAction ?
2727 0 : rLHS.mpAction->getActionCount() : 0 );
2728 : const sal_Int32 nRHSCount( rRHS.mpAction ?
2729 0 : rRHS.mpAction->getActionCount() : 0 );
2730 :
2731 : // compare end of action range, to have an action selected
2732 : // by lower_bound even if the requested index points in
2733 : // the middle of the action's range
2734 0 : return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2735 : }
2736 : };
2737 :
2738 : /** Algorithm to apply given functor to a subset range
2739 :
2740 : @tpl Functor
2741 :
2742 : Functor to call for each element of the subset
2743 : range. Must provide the following method signatures:
2744 : bool result() (returning false if operation failed)
2745 :
2746 : */
2747 : template< typename Functor > bool
2748 0 : forSubsetRange( Functor& rFunctor,
2749 : ImplRenderer::ActionVector::const_iterator aRangeBegin,
2750 : ImplRenderer::ActionVector::const_iterator aRangeEnd,
2751 : sal_Int32 nStartIndex,
2752 : sal_Int32 nEndIndex,
2753 : const ImplRenderer::ActionVector::const_iterator& rEnd )
2754 : {
2755 0 : if( aRangeBegin == aRangeEnd )
2756 : {
2757 : // only a single action. Setup subset, and call functor
2758 : Action::Subset aSubset;
2759 0 : aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2760 0 : nStartIndex - aRangeBegin->mnOrigIndex );
2761 0 : aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
2762 0 : nEndIndex - aRangeBegin->mnOrigIndex );
2763 :
2764 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2765 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2766 :
2767 0 : rFunctor( *aRangeBegin, aSubset );
2768 : }
2769 : else
2770 : {
2771 : // more than one action.
2772 :
2773 : // render partial first, full intermediate, and
2774 : // partial last action
2775 : Action::Subset aSubset;
2776 0 : aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2777 0 : nStartIndex - aRangeBegin->mnOrigIndex );
2778 0 : aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
2779 :
2780 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2781 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2782 :
2783 0 : rFunctor( *aRangeBegin, aSubset );
2784 :
2785 : // first action rendered, skip to next
2786 0 : ++aRangeBegin;
2787 :
2788 : // render full middle actions
2789 0 : while( aRangeBegin != aRangeEnd )
2790 0 : rFunctor( *aRangeBegin++ );
2791 :
2792 0 : if( aRangeEnd == rEnd ||
2793 0 : aRangeEnd->mnOrigIndex > nEndIndex )
2794 : {
2795 : // aRangeEnd denotes end of action vector,
2796 :
2797 : // or
2798 :
2799 : // nEndIndex references something _after_
2800 : // aRangeBegin, but _before_ aRangeEnd
2801 :
2802 : // either way: no partial action left
2803 0 : return rFunctor.result();
2804 : }
2805 :
2806 0 : aSubset.mnSubsetBegin = 0;
2807 0 : aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
2808 :
2809 0 : ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2810 : "ImplRenderer::forSubsetRange(): Invalid indices" );
2811 :
2812 0 : rFunctor( *aRangeEnd, aSubset );
2813 : }
2814 :
2815 0 : return rFunctor.result();
2816 : }
2817 : }
2818 :
2819 0 : bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
2820 : sal_Int32& io_rEndIndex,
2821 : ActionVector::const_iterator& o_rRangeBegin,
2822 : ActionVector::const_iterator& o_rRangeEnd ) const
2823 : {
2824 0 : ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2825 : "ImplRenderer::getSubsetIndices(): invalid action range" );
2826 :
2827 0 : ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2828 : "ImplRenderer::getSubsetIndices(): no actions to render" );
2829 :
2830 0 : const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2831 0 : const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2832 0 : maActions.back().mpAction->getActionCount() );
2833 :
2834 : // clip given range to permissible values (there might be
2835 : // ranges before and behind the valid indices)
2836 : io_rStartIndex = ::std::max( nMinActionIndex,
2837 0 : io_rStartIndex );
2838 : io_rEndIndex = ::std::min( nMaxActionIndex,
2839 0 : io_rEndIndex );
2840 :
2841 0 : if( io_rStartIndex == io_rEndIndex ||
2842 0 : io_rStartIndex > io_rEndIndex )
2843 : {
2844 : // empty range, don't render anything. The second
2845 : // condition e.g. happens if the requested range lies
2846 : // fully before or behind the valid action indices.
2847 0 : return false;
2848 : }
2849 :
2850 :
2851 0 : const ActionVector::const_iterator aBegin( maActions.begin() );
2852 0 : const ActionVector::const_iterator aEnd( maActions.end() );
2853 :
2854 :
2855 : // find start and end action
2856 : // =========================
2857 : o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2858 : MtfAction( ActionSharedPtr(), io_rStartIndex ),
2859 0 : UpperBoundActionIndexComparator() );
2860 : o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
2861 : MtfAction( ActionSharedPtr(), io_rEndIndex ),
2862 0 : UpperBoundActionIndexComparator() );
2863 0 : return true;
2864 : }
2865 :
2866 :
2867 : // Public methods
2868 :
2869 :
2870 2 : ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
2871 : const GDIMetaFile& rMtf,
2872 : const Parameters& rParams )
2873 : : CanvasGraphicHelper(rCanvas)
2874 : , maActions()
2875 : , fPageScale(0.0)
2876 : , nOriginX(0)
2877 : , nOriginY(0)
2878 : , nHDPI(0)
2879 : , nVDPI(0)
2880 : , nFrameLeft(0)
2881 : , nFrameTop(0)
2882 : , nFrameRight(0)
2883 : , nFrameBottom(0)
2884 : , nPixX(0)
2885 : , nPixY(0)
2886 : , nMmX(0)
2887 : , nMmY(0)
2888 : , mbMultipart(false)
2889 2 : , mMFlags(0)
2890 : {
2891 2 : memset (aObjects, 0, sizeof (aObjects));
2892 :
2893 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2894 :
2895 : OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2896 : "ImplRenderer::ImplRenderer(): Invalid canvas" );
2897 : OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2898 : "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2899 :
2900 : // make sure canvas and graphic device are valid; action
2901 : // creation don't check that every time
2902 10 : if( rCanvas.get() == NULL ||
2903 14 : !rCanvas->getUNOCanvas().is() ||
2904 6 : !rCanvas->getUNOCanvas()->getDevice().is() )
2905 : {
2906 : // leave actions empty
2907 2 : return;
2908 : }
2909 :
2910 2 : VectorOfOutDevStates aStateStack;
2911 :
2912 4 : ScopedVclPtrInstance< VirtualDevice > aVDev;
2913 2 : aVDev->EnableOutput( false );
2914 :
2915 : // Setup VDev for state tracking and mapping
2916 : // =========================================
2917 :
2918 2 : aVDev->SetMapMode( rMtf.GetPrefMapMode() );
2919 :
2920 2 : const Size aMtfSize( rMtf.GetPrefSize() );
2921 2 : const Size aMtfSizePixPre( aVDev->LogicToPixel( aMtfSize,
2922 4 : rMtf.GetPrefMapMode() ) );
2923 :
2924 : // #i44110# correct null-sized output - there are shapes
2925 : // which have zero size in at least one dimension
2926 4 : const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2927 6 : ::std::max( aMtfSizePixPre.Height(), 1L ) );
2928 :
2929 2 : sal_Int32 nCurrActions(0);
2930 : ActionFactoryParameters aParms(aStateStack,
2931 : rCanvas,
2932 2 : *aVDev.get(),
2933 : rParams,
2934 2 : nCurrActions );
2935 :
2936 : // init state stack
2937 2 : aStateStack.clearStateStack();
2938 :
2939 : // Setup local state, such that the metafile renders
2940 : // itself into a one-by-one square at the origin for
2941 : // identity view and render transformations
2942 4 : aStateStack.getState().transform.scale( 1.0 / aMtfSizePix.Width(),
2943 6 : 1.0 / aMtfSizePix.Height() );
2944 :
2945 2 : tools::calcLogic2PixelAffineTransform( aStateStack.getState().mapModeTransform,
2946 4 : *aVDev.get() );
2947 :
2948 4 : ColorSharedPtr pColor( getCanvas()->createColor() );
2949 :
2950 : {
2951 2 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2952 : // setup default text color to black
2953 2 : rState.textColor =
2954 2 : rState.textFillColor =
2955 4 : rState.textLineColor = pColor->getDeviceColor( 0x000000FF );
2956 : }
2957 :
2958 : // apply overrides from the Parameters struct
2959 2 : if( rParams.maFillColor.is_initialized() )
2960 : {
2961 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2962 0 : rState.isFillColorSet = true;
2963 0 : rState.fillColor = pColor->getDeviceColor( *rParams.maFillColor );
2964 : }
2965 2 : if( rParams.maLineColor.is_initialized() )
2966 : {
2967 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2968 0 : rState.isLineColorSet = true;
2969 0 : rState.lineColor = pColor->getDeviceColor( *rParams.maLineColor );
2970 : }
2971 2 : if( rParams.maTextColor.is_initialized() )
2972 : {
2973 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2974 0 : rState.isTextFillColorSet = true;
2975 0 : rState.isTextLineColorSet = true;
2976 0 : rState.textColor =
2977 0 : rState.textFillColor =
2978 0 : rState.textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
2979 : }
2980 6 : if( rParams.maFontName.is_initialized() ||
2981 4 : rParams.maFontWeight.is_initialized() ||
2982 4 : rParams.maFontLetterForm.is_initialized() ||
2983 6 : rParams.maFontUnderline.is_initialized() ||
2984 2 : rParams.maFontProportion.is_initialized() )
2985 : {
2986 0 : ::cppcanvas::internal::OutDevState& rState = aStateStack.getState();
2987 :
2988 0 : rState.xFont = createFont( rState.fontRotation,
2989 : vcl::Font(), // default font
2990 0 : aParms );
2991 : }
2992 :
2993 : /* EMF+ */
2994 : createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
2995 : // we're
2996 : // changing
2997 : // the
2998 : // current
2999 : // action
3000 : // in
3001 : // createActions!
3002 : aParms,
3003 : true // TODO(P1): make subsettability configurable
3004 4 : );
3005 : }
3006 :
3007 8 : ImplRenderer::~ImplRenderer()
3008 : {
3009 : // don't leak EMFPObjects
3010 514 : for(unsigned int i=0; i<SAL_N_ELEMENTS(aObjects); ++i)
3011 512 : delete aObjects[i];
3012 6 : }
3013 :
3014 0 : bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
3015 : sal_Int32 nEndIndex ) const
3016 : {
3017 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3018 :
3019 0 : ActionVector::const_iterator aRangeBegin;
3020 0 : ActionVector::const_iterator aRangeEnd;
3021 :
3022 : try
3023 : {
3024 0 : if( !getSubsetIndices( nStartIndex, nEndIndex,
3025 0 : aRangeBegin, aRangeEnd ) )
3026 0 : return true; // nothing to render (but _that_ was successful)
3027 :
3028 : // now, aRangeBegin references the action in which the
3029 : // subset rendering must start, and aRangeEnd references
3030 : // the action in which the subset rendering must end (it
3031 : // might also end right at the start of the referenced
3032 : // action, such that zero of that action needs to be
3033 : // rendered).
3034 :
3035 :
3036 : // render subset of actions
3037 : // ========================
3038 :
3039 0 : ::basegfx::B2DHomMatrix aMatrix;
3040 : ::canvas::tools::getRenderStateTransform( aMatrix,
3041 0 : getRenderState() );
3042 :
3043 0 : ActionRenderer aRenderer( aMatrix );
3044 :
3045 : return forSubsetRange( aRenderer,
3046 : aRangeBegin,
3047 : aRangeEnd,
3048 : nStartIndex,
3049 : nEndIndex,
3050 0 : maActions.end() );
3051 : }
3052 0 : catch( uno::Exception& )
3053 : {
3054 : SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3055 : comphelper::anyToString( cppu::getCaughtException() ),
3056 : RTL_TEXTENCODING_UTF8 ).getStr() );
3057 :
3058 : // convert error to return value
3059 0 : return false;
3060 : }
3061 : }
3062 :
3063 0 : ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
3064 : sal_Int32 nEndIndex ) const
3065 : {
3066 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3067 :
3068 0 : ActionVector::const_iterator aRangeBegin;
3069 0 : ActionVector::const_iterator aRangeEnd;
3070 :
3071 0 : if( !getSubsetIndices( nStartIndex, nEndIndex,
3072 0 : aRangeBegin, aRangeEnd ) )
3073 0 : return ::basegfx::B2DRange(); // nothing to render -> empty range
3074 :
3075 : // now, aRangeBegin references the action in which the
3076 : // subset querying must start, and aRangeEnd references
3077 : // the action in which the subset querying must end (it
3078 : // might also end right at the start of the referenced
3079 : // action, such that zero of that action needs to be
3080 : // queried).
3081 :
3082 :
3083 : // query bounds for subset of actions
3084 : // ==================================
3085 :
3086 0 : ::basegfx::B2DHomMatrix aMatrix;
3087 : ::canvas::tools::getRenderStateTransform( aMatrix,
3088 0 : getRenderState() );
3089 :
3090 0 : AreaQuery aQuery( aMatrix );
3091 : forSubsetRange( aQuery,
3092 : aRangeBegin,
3093 : aRangeEnd,
3094 : nStartIndex,
3095 : nEndIndex,
3096 0 : maActions.end() );
3097 :
3098 0 : return aQuery.getBounds();
3099 : }
3100 :
3101 2 : bool ImplRenderer::draw() const
3102 : {
3103 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3104 :
3105 2 : ::basegfx::B2DHomMatrix aMatrix;
3106 : ::canvas::tools::getRenderStateTransform( aMatrix,
3107 2 : getRenderState() );
3108 :
3109 : try
3110 : {
3111 2 : return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3112 : }
3113 0 : catch( uno::Exception& )
3114 : {
3115 : SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3116 : comphelper::anyToString( cppu::getCaughtException() ),
3117 : RTL_TEXTENCODING_UTF8 ).getStr() );
3118 :
3119 0 : return false;
3120 2 : }
3121 : }
3122 : }
3123 : }
3124 :
3125 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|