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