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