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