LibreOffice Module drawinglayer (master) 1
vclmetafileprocessor2d.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
20#include <cmath>
21#include <memory>
24#include <rtl/ustring.hxx>
25#include <tools/gen.hxx>
26#include <tools/stream.hxx>
30#include <config_global.h>
36#include <vcl/virdev.hxx>
37#include <vcl/gdimtf.hxx>
38#include <vcl/gradient.hxx>
39#include <vcl/graphictools.hxx>
40#include <vcl/metaact.hxx>
41#include <vcl/graph.hxx> // for PDFExtOutDevData Graphic support
42#include <vcl/formpdfexport.hxx> // for PDFExtOutDevData Graphic support
66#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> // for Title/Description metadata
69#include <tools/vcompat.hxx>
70
71#include <com/sun/star/awt/XControl.hpp>
72#include <com/sun/star/i18n/BreakIterator.hpp>
73#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
74#include <com/sun/star/i18n/WordType.hpp>
75#include <com/sun/star/beans/XPropertySet.hpp>
76
77using namespace com::sun::star;
78
79// #112245# definition for maximum allowed point count due to Metafile target.
80// To be on the safe side with the old tools polygon, use slightly less than
81// the theoretical maximum (bad experiences with tools polygon)
82
83#define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0)
84
85namespace
86{
87// #112245# helper to split line polygon in half
88void splitLinePolygon(const basegfx::B2DPolygon& rBasePolygon, basegfx::B2DPolygon& o_aLeft,
89 basegfx::B2DPolygon& o_aRight)
90{
91 const sal_uInt32 nCount(rBasePolygon.count());
92
93 if (nCount)
94 {
95 const sal_uInt32 nHalfCount((nCount - 1) >> 1);
96
97 o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1);
98 o_aLeft.setClosed(false);
99
100 o_aRight = basegfx::B2DPolygon(rBasePolygon, nHalfCount, nCount - nHalfCount);
101 o_aRight.setClosed(false);
102
103 if (rBasePolygon.isClosed())
104 {
105 o_aRight.append(rBasePolygon.getB2DPoint(0));
106
107 if (rBasePolygon.areControlPointsUsed())
108 {
109 o_aRight.setControlPoints(o_aRight.count() - 1, rBasePolygon.getPrevControlPoint(0),
110 rBasePolygon.getNextControlPoint(0));
111 }
112 }
113 }
114 else
115 {
116 o_aLeft.clear();
117 o_aRight.clear();
118 }
119}
120
121// #112245# helper to evtl. split filled polygons to maximum metafile point count
122void fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon)
123{
124 const sal_uInt32 nPolyCount(rPolyPolygon.count());
125
126 if (!nPolyCount)
127 return;
128
129 basegfx::B2DPolyPolygon aSplitted;
130
131 for (sal_uInt32 a(0); a < nPolyCount; a++)
132 {
133 const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(a));
134 const sal_uInt32 nPointCount(aCandidate.count());
135 bool bNeedToSplit(false);
136
137 if (aCandidate.areControlPointsUsed())
138 {
139 // compare with the maximum for bezier curved polygons
140 bNeedToSplit = nPointCount > ((MAX_POLYGON_POINT_COUNT_METAFILE / 3L) - 1);
141 }
142 else
143 {
144 // compare with the maximum for simple point polygons
145 bNeedToSplit = nPointCount > (MAX_POLYGON_POINT_COUNT_METAFILE - 1);
146 }
147
148 if (bNeedToSplit)
149 {
150 // need to split the partial polygon
151 const basegfx::B2DRange aRange(aCandidate.getB2DRange());
152 const basegfx::B2DPoint aCenter(aRange.getCenter());
153
154 if (aRange.getWidth() > aRange.getHeight())
155 {
156 // clip in left and right
158 aCandidate, false, true, aCenter.getX(), false));
160 aCandidate, false, false, aCenter.getX(), false));
161
162 aSplitted.append(aLeft);
163 aSplitted.append(aRight);
164 }
165 else
166 {
167 // clip in top and bottom
169 aCandidate, true, true, aCenter.getY(), false));
171 aCandidate, true, false, aCenter.getY(), false));
172
173 aSplitted.append(aTop);
174 aSplitted.append(aBottom);
175 }
176 }
177 else
178 {
179 aSplitted.append(aCandidate);
180 }
181 }
182
183 if (aSplitted.count() != nPolyCount)
184 {
185 rPolyPolygon = aSplitted;
186 }
187}
188
198tools::PolyPolygon getFillPolyPolygon(const ::basegfx::B2DPolyPolygon& rPoly)
199{
200 // filter input rPoly
202 sal_uInt32 nCount(rPoly.count());
203 for (sal_uInt32 i = 0; i < nCount; ++i)
204 {
205 const basegfx::B2DPolygon& aCandidate(rPoly.getB2DPolygon(i));
206 if (!aCandidate.isClosed() || aCandidate.count() > 1)
207 aPoly.append(aCandidate);
208 }
209 return tools::PolyPolygon(aPoly);
210}
211
212} // end of anonymous namespace
213
215{
218 GDIMetaFile& o_rContentMetafile)
219{
220 // Prepare VDev, MetaFile and connections
221 OutputDevice* pLastOutputDevice = mpOutputDevice;
222 GDIMetaFile* pLastMetafile = mpMetaFile;
223 basegfx::B2DRange aPrimitiveRange(rContent.getB2DRange(getViewInformation2D()));
224
225 // transform primitive range with current transformation (e.g shadow offset)
226 aPrimitiveRange.transform(maCurrentTransformation);
227
228 const tools::Rectangle aPrimitiveRectangle(
229 basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()),
230 basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY()));
232 MapMode aNewMapMode(pLastOutputDevice->GetMapMode());
233
234 mpOutputDevice = aContentVDev.get();
235 mpMetaFile = &o_rContentMetafile;
236 aContentVDev->EnableOutput(false);
237 aContentVDev->SetMapMode(pLastOutputDevice->GetMapMode());
238 o_rContentMetafile.Record(aContentVDev.get());
239 aContentVDev->SetLineColor(pLastOutputDevice->GetLineColor());
240 aContentVDev->SetFillColor(pLastOutputDevice->GetFillColor());
241 aContentVDev->SetFont(pLastOutputDevice->GetFont());
242 aContentVDev->SetDrawMode(pLastOutputDevice->GetDrawMode());
243 aContentVDev->SetSettings(pLastOutputDevice->GetSettings());
244 aContentVDev->SetRefPoint(pLastOutputDevice->GetRefPoint());
245
246 // dump to MetaFile
247 process(rContent);
248
249 // cleanups
250 o_rContentMetafile.Stop();
251 o_rContentMetafile.WindStart();
252 aNewMapMode.SetOrigin(aPrimitiveRectangle.TopLeft());
253 o_rContentMetafile.SetPrefMapMode(aNewMapMode);
254 o_rContentMetafile.SetPrefSize(aPrimitiveRectangle.GetSize());
255 mpOutputDevice = pLastOutputDevice;
256 mpMetaFile = pLastMetafile;
257
258 return aPrimitiveRectangle;
259}
260
262 Gradient& o_rVCLGradient, const attribute::FillGradientAttribute& rFiGrAtt,
263 bool bIsTransparenceGradient) const
264{
265 const basegfx::BColor aStartColor(rFiGrAtt.getColorStops().front().getStopColor());
266 const basegfx::BColor aEndColor(rFiGrAtt.getColorStops().back().getStopColor());
267
268 if (bIsTransparenceGradient)
269 {
270 // it's about transparence channel intensities (black/white), do not use color modifier
271 o_rVCLGradient.SetStartColor(Color(aStartColor));
272 o_rVCLGradient.SetEndColor(Color(aEndColor));
273 }
274 else
275 {
276 // use color modifier to influence start/end color of gradient
277 o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(aStartColor)));
278 o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(aEndColor)));
279 }
280
281 o_rVCLGradient.SetAngle(
282 Degree10(static_cast<sal_uInt32>(basegfx::rad2deg<10>(rFiGrAtt.getAngle()))));
283 o_rVCLGradient.SetBorder(static_cast<sal_uInt16>(rFiGrAtt.getBorder() * 100.0));
284 o_rVCLGradient.SetOfsX(static_cast<sal_uInt16>(rFiGrAtt.getOffsetX() * 100.0));
285 o_rVCLGradient.SetOfsY(static_cast<sal_uInt16>(rFiGrAtt.getOffsetY() * 100.0));
286 o_rVCLGradient.SetSteps(rFiGrAtt.getSteps());
287
288 // defaults for intensity; those were computed into the start/end colors already
289 o_rVCLGradient.SetStartIntensity(100);
290 o_rVCLGradient.SetEndIntensity(100);
291 o_rVCLGradient.SetStyle(rFiGrAtt.getStyle());
292}
293
295{
296 if (pSvtGraphicFill && !mnSvtGraphicFillCount)
297 {
298 SvMemoryStream aMemStm;
299
300 WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill);
302 "XPATHFILL_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
303 aMemStm.TellEnd()));
305 }
306}
307
309{
310 if (pSvtGraphicFill && mnSvtGraphicFillCount)
311 {
313 mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
314 }
315}
316
318{
319 // #i113922# the LineWidth is duplicated in the MetaPolylineAction,
320 // and also inside the SvtGraphicStroke and needs transforming into
321 // the same space as its coordinates here cf. fdo#61789
322 // This is a partial fix. When an object transformation is used which
323 // e.g. contains a scaleX != scaleY, an unproportional scaling will happen.
325 * basegfx::B2DVector(fWidth, 0.0));
326
327 return aDiscreteUnit.getLength();
328}
329
331 const basegfx::B2DPolygon& rB2DPolygon, const basegfx::BColor* pColor,
332 const attribute::LineAttribute* pLineAttribute,
333 const attribute::StrokeAttribute* pStrokeAttribute,
335{
336 std::unique_ptr<SvtGraphicStroke> pRetval;
337
338 if (rB2DPolygon.count() && !mnSvtGraphicStrokeCount)
339 {
340 basegfx::B2DPolygon aLocalPolygon(rB2DPolygon);
341 basegfx::BColor aStrokeColor;
342 basegfx::B2DPolyPolygon aStartArrow;
343 basegfx::B2DPolyPolygon aEndArrow;
344
345 if (pColor)
346 {
347 aStrokeColor = *pColor;
348 }
349 else if (pLineAttribute)
350 {
351 aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor());
352 }
353
354 // It IS needed to record the stroke color at all in the metafile,
355 // SvtGraphicStroke has NO entry for stroke color(!)
356 mpOutputDevice->SetLineColor(Color(aStrokeColor));
357
358 if (!aLocalPolygon.isClosed())
359 {
360 double fPolyLength(0.0);
361 double fStart(0.0);
362 double fEnd(0.0);
363
364 if (pStart && pStart->isActive())
365 {
366 fPolyLength = basegfx::utils::getLength(aLocalPolygon);
367
369 aLocalPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(),
370 fPolyLength, pStart->isCentered() ? 0.5 : 0.0, &fStart);
371 }
372
373 if (pEnd && pEnd->isActive())
374 {
375 if (basegfx::fTools::equalZero(fPolyLength))
376 {
377 fPolyLength = basegfx::utils::getLength(aLocalPolygon);
378 }
379
381 aLocalPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), fPolyLength,
382 pEnd->isCentered() ? 0.5 : 0.0, &fEnd);
383 }
384
385 if (0.0 != fStart || 0.0 != fEnd)
386 {
387 // build new poly, consume something from old poly
388 aLocalPolygon = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart,
389 fPolyLength - fEnd, fPolyLength);
390 }
391 }
392
395 double fLineWidth(0.0);
396 double fMiterLength(0.0);
398
399 if (pLineAttribute)
400 {
401 fLineWidth = fMiterLength = getTransformedLineWidth(pLineAttribute->getWidth());
402
403 // get Join
404 switch (pLineAttribute->getLineJoin())
405 {
407 {
409 break;
410 }
412 {
414 break;
415 }
417 {
419 // ATM 15 degrees is assumed
420 // TODO wait for P1383R0 and C++20's std::numbers::pi
421 fMiterLength /= std::sin(M_PI / 12);
422 break;
423 }
425 {
427 break;
428 }
429 }
430
431 // get stroke
432 switch (pLineAttribute->getLineCap())
433 {
434 default: /* css::drawing::LineCap_BUTT */
435 {
437 break;
438 }
439 case css::drawing::LineCap_ROUND:
440 {
442 break;
443 }
444 case css::drawing::LineCap_SQUARE:
445 {
447 break;
448 }
449 }
450 }
451
452 if (pStrokeAttribute)
453 {
454 // copy dash array
455 aDashArray = pStrokeAttribute->getDotDashArray();
456 }
457
458 // #i101734# apply current object transformation to created geometry.
459 // This is a partial fix. When an object transformation is used which
460 // e.g. contains a scaleX != scaleY, an unproportional scaling would
461 // have to be applied to the evtl. existing fat line. The current
462 // concept of PDF export and SvtGraphicStroke usage does simply not
463 // allow handling such definitions. The only clean way would be to
464 // add the transformation to SvtGraphicStroke and to handle it there
465 aLocalPolygon.transform(maCurrentTransformation);
468
469 pRetval.reset(
470 new SvtGraphicStroke(tools::Polygon(aLocalPolygon), tools::PolyPolygon(aStartArrow),
472 fLineWidth, eCap, eJoin, fMiterLength, std::move(aDashArray)));
473 }
474
475 return pRetval;
476}
477
479{
480 if (pSvtGraphicStroke && !mnSvtGraphicStrokeCount)
481 {
482 SvMemoryStream aMemStm;
483
484 WriteSvtGraphicStroke(aMemStm, *pSvtGraphicStroke);
486 "XPATHSTROKE_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
487 aMemStm.TellEnd()));
489 }
490}
491
493{
494 if (pSvtGraphicStroke && mnSvtGraphicStrokeCount)
495 {
497 mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
498 }
499}
500
502{
503 if (!maListElements.empty() && maListElements.top() == eElem)
504 {
505 maListElements.pop();
507 }
508}
509
511{
514}
515
517{
518 popListItem();
520}
521
522// init static break iterator
525
527 OutputDevice& rOutDev)
528 : VclProcessor2D(rViewInformation, rOutDev)
529 , mpMetaFile(rOutDev.GetConnectMetaFile())
530 , mnSvtGraphicFillCount(0)
531 , mnSvtGraphicStrokeCount(0)
532 , mfCurrentUnifiedTransparence(0.0)
533 , mpPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>(rOutDev.GetExtOutDevData()))
534 , mnCurrentOutlineLevel(-1)
535 , mbInListItem(false)
536 , mbBulletPresent(false)
537{
538 OSL_ENSURE(rOutDev.GetConnectMetaFile(),
539 "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)");
540 // draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation
541 // but only to ObjectTransformation. Do not change MapMode of destination.
543}
544
546{
547 // MapMode was not changed, no restore necessary
548}
549
550/***********************************************************************************************
551
552 Support of MetaCommentActions in the VclMetafileProcessor2D
553 Found MetaCommentActions and how they are supported:
554
555 XGRAD_SEQ_BEGIN, XGRAD_SEQ_END:
556
557 Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action.
558 It is used in various exporters/importers to have direct access to the gradient before it
559 is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g.
560 the Metafile to SdrObject import creates its gradient objects.
561 Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
562 map it back to the corresponding tools tools::PolyPolygon and the Gradient and just call
563 OutputDevice::DrawGradient which creates the necessary compatible actions.
564
565 XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END:
566
567 Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed
568 inside GDIMetaFile::Rotate, nothing to take care of here.
569 The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used
570 with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not
571 XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it
572 to the comment action. A closing end token is created in the destructor.
573 Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and
574 SdrRectObj.
575 The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind
576 of filled objects, even simple colored polygons. It is added as extra information; the
577 Metafile actions between the two tokens are interpreted as output generated from those
578 fills. Thus, users have the choice to use the SvtGraphicFill info or the created output
579 actions.
580 Even for XFillTransparenceItem it is used, thus it may need to be supported in
581 UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon.
582 Implemented for:
583 PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D,
584 PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D,
585 PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
586 PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D,
587 and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence
588
589 XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END:
590
591 Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one
592 is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the
593 contained path accordingly.
594 The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and
595 only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this
596 would hinder to make use of tools::PolyPolygon strokes. I will need to add support at:
597 PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
598 PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
599 PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
600 This can be done hierarchical, too.
601 Okay, base implementation done based on those three primitives.
602
603 FIELD_SEQ_BEGIN, FIELD_SEQ_END
604
605 Used from slideshow for URLs, created from diverse SvxField implementations inside
606 createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx
607 inside ImpEditEngine::Paint.
608 Created TextHierarchyFieldPrimitive2D and added needed infos there; it is a group primitive and wraps
609 text primitives (but is not limited to that). It contains the field type if special actions for the
610 support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is
611 needed, it may be supported there.
612 FIELD_SEQ_BEGIN;PageField
613 FIELD_SEQ_END
614 Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too.
615
616 XTEXT
617
618 XTEXT_EOC(i) end of character
619 XTEXT_EOW(i) end of word
620 XTEXT_EOS(i) end of sentence
621
622 this three are with index and are created with the help of an i18n::XBreakIterator in
623 ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some
624 data structure for holding those TEXT infos.
625 Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text
626 primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage
627 that this creations do not need to be done for all paints all the time. This would be
628 expensive since the BreakIterator and it's usage is expensive and for each paint also the
629 whole character stops would need to be created.
630 Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below)
631
632 XTEXT_EOL() end of line
633 XTEXT_EOP() end of paragraph
634
635 First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well,
636 i decided to solve it with structure. I added the TextHierarchyPrimitives for this,
637 namely:
638 - TextHierarchyLinePrimitive2D: Encapsulates single line
639 - TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph
640 - TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM)
641 Those are now supported in hierarchy. This means the MetaFile renderer will support them
642 by using them, recursively using their content and adding MetaFile comments as needed.
643 This also means that when another text layouter will be used it will be necessary to
644 create/support the same HierarchyPrimitives to support users.
645 To transport the information using this hierarchy is best suited to all future needs;
646 the slideshow will be able to profit from it directly when using primitives; all other
647 renderers not interested in the text structure will just ignore the encapsulations.
648
649 XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END
650 Supported now by the TextHierarchyBlockPrimitive2D.
651
652 EPSReplacementGraphic:
653 Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to
654 hold the original EPS which was imported in the same MetaFile as first 2 entries. Only
655 used to export the original again (if exists).
656 Not necessary to support with MetaFileRenderer.
657
658 XTEXT_SCROLLRECT, XTEXT_PAINTRECT
659 Currently used to get extra MetaFile infos using GraphicExporter which again uses
660 SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since
661 the rectangle data is added directly by the GraphicsExporter as comment. Does not need
662 to be adapted at once.
663 When adapting later, the only user - the diashow - should directly use the provided
664 Animation infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D)
665
666 PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END
667 VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as
668 a fix (hack) while VCL printing. It is needed to not downscale a bitmap which
669 was explicitly created for the printer already again to some default maximum
670 bitmap sizes.
671 Nothing to do here for the primitive renderer.
672
673 Support for vcl::PDFExtOutDevData:
674 PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at
675 the OutDev. When set, some extra data is written there. Trying simple PDF export and
676 watching if I get those infos.
677 Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses
678 the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check
679 if I get a PDFExtOutDevData at the target output device.
680 Indeed, I get one. Checking what all may be done when that extra-device-info is there.
681
682 All in all I have to talk to SJ. I will need to emulate some of those actions, but
683 i need to discuss which ones.
684 In the future, all those infos would be taken from the primitive sequence anyways,
685 thus these extensions would potentially be temporary, too.
686 Discussed with SJ, added the necessary support and tested it. Details follow.
687
688 - In ImpEditEngine::Paint, paragraph infos and URL stuff is added.
689 Added in primitive MetaFile renderer.
690 Checking URL: Indeed, current version exports it, but it is missing in primitive
691 CWS version. Adding support.
692 Okay, URLs work. Checked, Done.
693
694 - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
695 target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
696 This was added in primitive MetaFile renderer.
697 Checked form control export, it works well. Done.
698
699 - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
700 generated. I will need to check what happens here with primitives.
701 To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed.
702 Added support, but feature is broken in main version, so i cannot test at all.
703 Writing a bug to CL (or SJ) and seeing what happens (#i80380#).
704 SJ took a look and we got it working. Tested VCL MetaFile Renderer based export,
705 as intended, the original file is exported. Works, Done.
706
707
708 To be done:
709
710 - Maybe there are more places to take care of for vcl::PDFExtOutDevData!
711
712
713****************************************************************************************************/
714
716{
717 switch (rCandidate.getPrimitive2DID())
718 {
720 {
721 // directdraw of wrong spell primitive
722 // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only
723 break;
724 }
726 {
728 static_cast<const primitive2d::GraphicPrimitive2D&>(rCandidate));
729 break;
730 }
732 {
734 static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate));
735 break;
736 }
738 {
740 static_cast<const primitive2d::TextHierarchyFieldPrimitive2D&>(rCandidate));
741 break;
742 }
744 {
746 static_cast<const primitive2d::TextHierarchyLinePrimitive2D&>(rCandidate));
747 break;
748 }
750 {
752 static_cast<const primitive2d::TextHierarchyBulletPrimitive2D&>(rCandidate));
753 break;
754 }
756 {
758 static_cast<const primitive2d::TextHierarchyParagraphPrimitive2D&>(rCandidate));
759 break;
760 }
762 {
764 static_cast<const primitive2d::TextHierarchyBlockPrimitive2D&>(rCandidate));
765 break;
766 }
769 {
770 // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate
772 static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate));
773 break;
774 }
776 {
778 static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
779 break;
780 }
782 {
784 static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
785 break;
786 }
788 {
790 static_cast<const primitive2d::PolygonStrokeArrowPrimitive2D&>(rCandidate));
791 break;
792 }
794 {
795 // direct draw of transformed BitmapEx primitive; use default processing, but without
796 // former testing if graphic content is inside discrete local viewport; this is not
797 // setup for metafile targets (metafile renderer tries to render in logic coordinates,
798 // the mapping is kept to the OutputDevice for better Metafile recording)
799 RenderBitmapPrimitive2D(static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate));
800 break;
801 }
803 {
805 {
806 // tdf#151104 unfortunately processPolyPolygonGraphicPrimitive2D below
807 // does not support an active BColorModifierStack, so use the default
808 process(rCandidate);
809 }
810 else
811 {
813 static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
814 }
815 break;
816 }
818 {
820 static_cast<const primitive2d::PolyPolygonHatchPrimitive2D&>(rCandidate));
821 break;
822 }
824 {
826 static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate));
827 break;
828 }
830 {
832 static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
833 break;
834 }
836 {
837 processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
838 break;
839 }
841 {
842 // modified color group. Force output to unified color. Use default processing.
844 static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
845 break;
846 }
848 {
850 static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
851 break;
852 }
854 {
856 static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate));
857 break;
858 }
860 {
861 // use default transform group processing
863 static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
864 break;
865 }
867 {
868 // new XDrawPage for ViewInformation2D
870 static_cast<const primitive2d::PagePreviewPrimitive2D&>(rCandidate));
871 break;
872 }
874 {
875 // use default marker array processing
877 static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
878 break;
879 }
881 {
882 // use default point array processing
884 static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate));
885 break;
886 }
888 {
890 static_cast<const primitive2d::StructureTagPrimitive2D&>(rCandidate));
891 break;
892 }
894 {
895 // This primitive is created if a text edit is active and contains it's
896 // current content, not from model data itself.
897 // Pixel renderers need to suppress that content, it gets displayed by the active
898 // TextEdit in the EditView. Suppression is done by decomposing to nothing.
899 // MetaFile renderers have to show it, so that the edited text is part of the
900 // MetaFile, e.g. needed for presentation previews and exports.
901 // So take action here and process it's content:
902 // Note: Former error was #i97628#
903 process(static_cast<const primitive2d::TextHierarchyEditPrimitive2D&>(rCandidate)
904 .getContent());
905 break;
906 }
908 {
909 RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate));
910 break;
911 }
913 {
915 static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate));
916 break;
917 }
918 default:
919 {
920 // process recursively
921 process(rCandidate);
922 break;
923 }
924 }
925}
926
928 primitive2d::ObjectInfoPrimitive2D const& rObjectInfoPrimitive2D)
929{
930 // tdf#154982 process content first, so this object overrides any nested one
931 process(rObjectInfoPrimitive2D.getChildren());
932
933 // currently StructureTagPrimitive2D is only used for SdrObjects - have to
934 // avoid adding Alt text if the SdrObject is not actually tagged, as it
935 // would then end up on an unrelated structure element.
937 {
938 // Create image alternative description from ObjectInfoPrimitive2D info
939 // for PDF export, for the currently active SdrObject's structure element
941 {
942 OUString aAlternateDescription;
943
944 if (!rObjectInfoPrimitive2D.getTitle().isEmpty())
945 {
946 aAlternateDescription += rObjectInfoPrimitive2D.getTitle();
947 }
948
949 if (!rObjectInfoPrimitive2D.getDesc().isEmpty())
950 {
951 if (!aAlternateDescription.isEmpty())
952 {
953 aAlternateDescription += " - ";
954 }
955
956 aAlternateDescription += rObjectInfoPrimitive2D.getDesc();
957 }
958
959 // Use SetAlternateText to set it. This will work as long as some
960 // structure is used (see PDFWriterImpl::setAlternateText and
961 // m_nCurrentStructElement - tagged PDF export works with this in
962 // Draw/Impress/Writer, but not in Calc due to too less structure...)
963 //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
964 if (!aAlternateDescription.isEmpty())
965 {
966 mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
967 }
968 }
969 }
970}
971
973 const primitive2d::GraphicPrimitive2D& rGraphicPrimitive)
974{
975 bool bUsingPDFExtOutDevData(false);
976 basegfx::B2DVector aTranslate, aScale;
977 static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore
978
979 if (mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport)
980 {
981 // emulate data handling from UnoControlPDFExportContact, original see
982 // svtools/source/graphic/grfmgr.cxx
983 const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic();
984
985 if (rGraphic.IsGfxLink())
986 {
987 const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
988
989 if (!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted())
990 {
991 const basegfx::B2DHomMatrix& rTransform = rGraphicPrimitive.getTransform();
992 double fRotate, fShearX;
993 rTransform.decompose(aScale, aTranslate, fRotate, fShearX);
994
995 if (basegfx::fTools::equalZero(fRotate) && (aScale.getX() > 0.0)
996 && (aScale.getY() > 0.0))
997 {
998 bUsingPDFExtOutDevData = true;
1000 }
1001 }
1002 }
1003 }
1004
1005 // process recursively and add MetaFile comment
1006 process(rGraphicPrimitive);
1007
1008 if (!bUsingPDFExtOutDevData)
1009 return;
1010
1011 // emulate data handling from UnoControlPDFExportContact, original see
1012 // svtools/source/graphic/grfmgr.cxx
1013 const basegfx::B2DRange aCurrentRange(aTranslate.getX(), aTranslate.getY(),
1014 aTranslate.getX() + aScale.getX(),
1015 aTranslate.getY() + aScale.getY());
1016 const tools::Rectangle aCurrentRect(
1017 sal_Int32(floor(aCurrentRange.getMinX())), sal_Int32(floor(aCurrentRange.getMinY())),
1018 sal_Int32(ceil(aCurrentRange.getMaxX())), sal_Int32(ceil(aCurrentRange.getMaxY())));
1019 const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
1020 // fdo#72530 don't pass empty Rectangle to EndGroup
1021 tools::Rectangle aCropRect(aCurrentRect);
1022
1023 if (rAttr.IsCropped())
1024 {
1025 // calculate scalings between real image size and logic object size. This
1026 // is necessary since the crop values are relative to original bitmap size
1027 double fFactorX(1.0);
1028 double fFactorY(1.0);
1029
1030 {
1031 const MapMode aMapMode100thmm(MapUnit::Map100thMM);
1032 const Size aBitmapSize(OutputDevice::LogicToLogic(
1033 rGraphicPrimitive.getGraphicObject().GetPrefSize(),
1034 rGraphicPrimitive.getGraphicObject().GetPrefMapMode(), aMapMode100thmm));
1035 const double fDivX(aBitmapSize.Width() - rAttr.GetLeftCrop() - rAttr.GetRightCrop());
1036 const double fDivY(aBitmapSize.Height() - rAttr.GetTopCrop() - rAttr.GetBottomCrop());
1037
1038 if (!basegfx::fTools::equalZero(fDivX))
1039 {
1040 fFactorX = aScale.getX() / fDivX;
1041 }
1042
1043 if (!basegfx::fTools::equalZero(fDivY))
1044 {
1045 fFactorY = aScale.getY() / fDivY;
1046 }
1047 }
1048
1049 // calculate crop range and rect
1050 basegfx::B2DRange aCropRange;
1051 aCropRange.expand(
1052 aCurrentRange.getMinimum()
1053 - basegfx::B2DPoint(rAttr.GetLeftCrop() * fFactorX, rAttr.GetTopCrop() * fFactorY));
1054 aCropRange.expand(
1055 aCurrentRange.getMaximum()
1056 + basegfx::B2DPoint(rAttr.GetRightCrop() * fFactorX, rAttr.GetBottomCrop() * fFactorY));
1057
1058 aCropRect = tools::Rectangle(
1059 sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())),
1060 sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
1061 }
1062
1063 // #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped
1064 // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded,
1065 // uncropped region. Thus, correct order is aCropRect, aCurrentRect
1067 255 - rAttr.GetAlpha(), aCropRect, aCurrentRect);
1068}
1069
1071 const primitive2d::ControlPrimitive2D& rControlPrimitive)
1072{
1073 const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl());
1074 bool bIsPrintableControl(false);
1075
1076 // find out if control is printable
1077 if (rXControl.is())
1078 {
1079 try
1080 {
1081 uno::Reference<beans::XPropertySet> xModelProperties(rXControl->getModel(),
1082 uno::UNO_QUERY);
1083 uno::Reference<beans::XPropertySetInfo> xPropertyInfo(
1084 xModelProperties.is() ? xModelProperties->getPropertySetInfo()
1085 : uno::Reference<beans::XPropertySetInfo>());
1086 static constexpr OUStringLiteral sPrintablePropertyName(u"Printable");
1087
1088 if (xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName))
1089 {
1090 OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName)
1091 >>= bIsPrintableControl);
1092 }
1093 }
1094 catch (const uno::Exception&)
1095 {
1096 TOOLS_WARN_EXCEPTION("drawinglayer",
1097 "VclMetafileProcessor2D: No access to printable flag of Control");
1098 }
1099 }
1100
1101 // PDF export and printing only for printable controls
1102 if (!bIsPrintableControl)
1103 return;
1104
1105 const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields());
1106 bool bDoProcessRecursively(true);
1107
1108 if (bPDFExport)
1109 {
1110 // PDF export. Emulate data handling from UnoControlPDFExportContact
1111 std::unique_ptr<vcl::PDFWriter::AnyWidget> pPDFControl(
1112 ::toolkitform::describePDFControl(rXControl, *mpPDFExtOutDevData));
1113
1114 if (pPDFControl)
1115 {
1116 // still need to fill in the location (is a class Rectangle)
1117 const basegfx::B2DRange aRangeLogic(
1118 rControlPrimitive.getB2DRange(getViewInformation2D()));
1119 const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aRangeLogic.getMinX())),
1120 static_cast<sal_Int32>(floor(aRangeLogic.getMinY())),
1121 static_cast<sal_Int32>(ceil(aRangeLogic.getMaxX())),
1122 static_cast<sal_Int32>(ceil(aRangeLogic.getMaxY())));
1123 pPDFControl->Location = aRectLogic;
1124
1125 Size aFontSize(pPDFControl->TextFont.GetFontSize());
1126 aFontSize = OutputDevice::LogicToLogic(aFontSize, MapMode(MapUnit::MapPoint),
1127 mpOutputDevice->GetMapMode());
1128 pPDFControl->TextFont.SetFontSize(aFontSize);
1129
1132 switch (pPDFControl->Type)
1133 {
1135 role = vcl::PDFWriter::Pb;
1136 break;
1138 role = vcl::PDFWriter::Rb;
1139 break;
1141 role = vcl::PDFWriter::Cb;
1142 break;
1143 default: // there is a paucity of roles, tv is the catch-all one
1144 role = vcl::PDFWriter::Tv;
1145 break;
1146 }
1147 // ISO 14289-1:2014, Clause: 7.18.4
1149 // ISO 14289-1:2014, Clause: 7.18.1
1150 OUString const& rAltText(rControlPrimitive.GetAltText());
1151 if (!rAltText.isEmpty())
1152 {
1154 }
1155 mpPDFExtOutDevData->CreateControl(*pPDFControl);
1157
1158 // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
1159 // do not process recursively
1160 bDoProcessRecursively = false;
1161 }
1162 else
1163 {
1164 // PDF export did not work, try simple output.
1165 // Fallback to printer output by not setting bDoProcessRecursively
1166 // to false.
1167 }
1168 }
1169
1170 if (!bDoProcessRecursively)
1171 {
1172 return;
1173 }
1174
1176 { // no corresponding PDF Form, use Figure instead
1179 auto const range(rControlPrimitive.getB2DRange(getViewInformation2D()));
1180 tools::Rectangle const aLogicRect(
1181 basegfx::fround(range.getMinX()), basegfx::fround(range.getMinY()),
1182 basegfx::fround(range.getMaxX()), basegfx::fround(range.getMaxY()));
1184 OUString const& rAltText(rControlPrimitive.GetAltText());
1185 if (!rAltText.isEmpty())
1186 {
1188 }
1189 }
1190
1191 // #i93169# used flag the wrong way; true means that nothing was done yet
1192 if (bDoProcessRecursively)
1193 {
1194 // printer output
1195 try
1196 {
1197 // remember old graphics and create new
1198 uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW);
1199 const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics());
1200 const uno::Reference<awt::XGraphics> xNewGraphics(mpOutputDevice->CreateUnoGraphics());
1201
1202 if (xNewGraphics.is())
1203 {
1204 // link graphics and view
1205 xControlView->setGraphics(xNewGraphics);
1206
1207 // get position
1208 const basegfx::B2DHomMatrix aObjectToDiscrete(
1209 getViewInformation2D().getObjectToViewTransformation()
1210 * rControlPrimitive.getTransform());
1211 const basegfx::B2DPoint aTopLeftDiscrete(aObjectToDiscrete
1212 * basegfx::B2DPoint(0.0, 0.0));
1213
1214 // draw it
1215 xControlView->draw(basegfx::fround(aTopLeftDiscrete.getX()),
1216 basegfx::fround(aTopLeftDiscrete.getY()));
1217 bDoProcessRecursively = false;
1218
1219 // restore original graphics
1220 xControlView->setGraphics(xOriginalGraphics);
1221 }
1222 }
1223 catch (const uno::Exception&)
1224 {
1225 TOOLS_WARN_EXCEPTION("drawinglayer",
1226 "VclMetafileProcessor2D: Printing of Control failed");
1227 }
1228 }
1229
1230 // process recursively if not done yet to export as decomposition (bitmap)
1231 if (bDoProcessRecursively)
1232 {
1233 process(rControlPrimitive);
1234 }
1235
1237 {
1239 }
1240}
1241
1243 const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive)
1244{
1245 // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
1246 // thus do the MetafileAction embedding stuff but just handle recursively.
1247 static constexpr OStringLiteral aCommentStringCommon("FIELD_SEQ_BEGIN");
1248 OUString aURL;
1249
1250 switch (rFieldPrimitive.getType())
1251 {
1252 default: // case drawinglayer::primitive2d::FIELD_TYPE_COMMON :
1253 {
1254 mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon));
1255 break;
1256 }
1258 {
1259 mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_BEGIN;PageField"));
1260 break;
1261 }
1263 {
1264 aURL = rFieldPrimitive.getValue("URL");
1265
1266 if (!aURL.isEmpty())
1267 {
1269 aCommentStringCommon, 0, reinterpret_cast<const sal_uInt8*>(aURL.getStr()),
1270 2 * aURL.getLength()));
1271 }
1272
1273 break;
1274 }
1275 }
1276
1277 // process recursively
1279 rFieldPrimitive.get2DDecomposition(rContent, getViewInformation2D());
1280 process(rContent);
1281
1282 // for the end comment the type is not relevant yet, they are all the same. Just add.
1283 mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_END"));
1284
1285 if (!(mpPDFExtOutDevData
1286 && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType()))
1287 return;
1288
1289 // emulate data handling from ImpEditEngine::Paint
1290 const basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D()));
1291 const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())),
1292 static_cast<sal_Int32>(floor(aViewRange.getMinY())),
1293 static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
1294 static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
1296 OUString const content(rFieldPrimitive.getValue("Representation"));
1297 aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic, content);
1298 aBookmark.aBookmark = aURL;
1299 std::vector<vcl::PDFExtOutDevBookmarkEntry>& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
1300 rBookmarks.push_back(aBookmark);
1301}
1302
1304 const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive)
1305{
1306 // process recursively and add MetaFile comment
1307 process(rLinePrimitive);
1308 mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOL"));
1309}
1310
1312 const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive)
1313{
1314 // this is a part of list item, start LILabel ( = bullet)
1315 if (mbInListItem)
1316 {
1319 }
1320
1321 // process recursively and add MetaFile comment
1322 process(rBulletPrimitive);
1323 // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
1324 // "XTEXT_EOC" is used, use here, too.
1325 mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"));
1326
1327 if (mbInListItem)
1328 {
1330 {
1331 maListElements.pop();
1332 mpPDFExtOutDevData->EndStructureElement(); // end LILabel
1333 mbBulletPresent = true;
1334 }
1335 }
1336}
1337
1339 const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive)
1340{
1341 static constexpr OStringLiteral aCommentString("XTEXT_EOP");
1342 static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore
1343
1344 if (nullptr == mpPDFExtOutDevData || bSuppressPDFExtOutDevDataSupport)
1345 {
1346 // Non-PDF export behaviour (metafile only).
1347 // Process recursively and add MetaFile comment.
1348 process(rParagraphPrimitive);
1349 mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1350 return;
1351 }
1352
1354 {
1355 // No Tagged PDF -> Dump as Paragraph
1356 // Emulate data handling from old ImpEditEngine::Paint
1358
1359 // Process recursively and add MetaFile comment
1360 process(rParagraphPrimitive);
1361 mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1362
1363 // Emulate data handling from ImpEditEngine::Paint
1365 return;
1366 }
1367
1368 // Create Tagged PDF -> deeper tagged data using StructureElements.
1369 // Use OutlineLevel from ParagraphPrimitive, ensure not below -1 what
1370 // means 'not active'
1371 const sal_Int16 nNewOutlineLevel(
1372 std::max(static_cast<sal_Int16>(-1), rParagraphPrimitive.getOutlineLevel()));
1373
1374 // Do we have a change in OutlineLevel compared to the current one?
1375 if (nNewOutlineLevel != mnCurrentOutlineLevel)
1376 {
1377 if (nNewOutlineLevel > mnCurrentOutlineLevel)
1378 {
1379 // increase List level
1380 for (sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; ++a)
1381 {
1384 }
1385 }
1386 else // if(nNewOutlineLevel < mnCurrentOutlineLevel)
1387 {
1388 // close list levels below nNewOutlineLevel completely by removing
1389 // list items as well as list tag itself
1390 for (sal_Int16 a(nNewOutlineLevel); a < mnCurrentOutlineLevel; ++a)
1391 {
1392 popList(); // end LBody LI and L
1393 }
1394
1395 // on nNewOutlineLevel close the previous list item (LBody and LI)
1396 popListItem();
1397 }
1398
1399 // Remember new current OutlineLevel
1400 mnCurrentOutlineLevel = nNewOutlineLevel;
1401 }
1402 else // the same list level
1403 {
1404 // close the previous list item (LBody and LI)
1405 popListItem();
1406 }
1407
1408 const bool bDumpAsListItem(-1 != mnCurrentOutlineLevel);
1409
1410 if (bDumpAsListItem)
1411 {
1412 // Dump as ListItem
1415 mbInListItem = true;
1416 }
1417 else
1418 {
1419 // Dump as Paragraph
1421 }
1422
1423 // Process recursively and add MetaFile comment
1424 process(rParagraphPrimitive);
1425 mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1426
1427 if (bDumpAsListItem)
1428 mbInListItem = false;
1429 else
1430 mpPDFExtOutDevData->EndStructureElement(); // end Paragraph
1431}
1432
1434 const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive)
1435{
1436 // add MetaFile comment, process recursively and add MetaFile comment
1437 mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_BEGIN"));
1438 process(rBlockPrimitive);
1439
1440 if (mnCurrentOutlineLevel >= 0)
1441 {
1442 // end any opened List structure elements (LBody, LI, L)
1443 for (sal_Int16 a(0); a <= mnCurrentOutlineLevel; ++a)
1444 {
1445 popList();
1446 }
1447 }
1448
1449 mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_END"));
1450}
1451
1453 const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
1454{
1455 // Adapt evtl. used special DrawMode
1456 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1458
1459 // this is a 2nd portion of list item
1460 // bullet has been already processed, start LIBody
1462 {
1465 }
1466
1467 // directdraw of text simple portion; use default processing
1469
1471 mbBulletPresent = false;
1472
1473 // restore DrawMode
1474 mpOutputDevice->SetDrawMode(nOriginalDrawMode);
1475
1476 // #i101169# if(pTextDecoratedCandidate)
1477 {
1478 // support for TEXT_ MetaFile actions only for decorated texts
1479 if (!mxBreakIterator.get() || !mxBreakIterator.get()->get())
1480 {
1481 uno::Reference<uno::XComponentContext> xContext(
1482 ::comphelper::getProcessComponentContext());
1483 mxBreakIterator.set(i18n::BreakIterator::create(xContext));
1484 }
1485 auto& rBreakIterator = *mxBreakIterator.get()->get();
1486
1487 const OUString& rTxt = rTextCandidate.getText();
1488 const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength());
1489
1490 if (nTextLength)
1491 {
1492 const css::lang::Locale& rLocale = rTextCandidate.getLocale();
1493 const sal_Int32 nTextPosition(rTextCandidate.getTextPosition());
1494
1495 sal_Int32 nDone;
1496 sal_Int32 nNextCellBreak(rBreakIterator.nextCharacters(
1497 rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0,
1498 nDone));
1499 css::i18n::Boundary nNextWordBoundary(rBreakIterator.getWordBoundary(
1500 rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true));
1501 sal_Int32 nNextSentenceBreak(
1502 rBreakIterator.endOfSentence(rTxt, nTextPosition, rLocale));
1503 static constexpr OStringLiteral aCommentStringA("XTEXT_EOC");
1504 static constexpr OStringLiteral aCommentStringB("XTEXT_EOW");
1505 static constexpr OStringLiteral aCommentStringC("XTEXT_EOS");
1506
1507 for (sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++)
1508 {
1509 // create the entries for the respective break positions
1510 if (i == nNextCellBreak)
1511 {
1513 new MetaCommentAction(aCommentStringA, i - nTextPosition));
1514 nNextCellBreak = rBreakIterator.nextCharacters(
1515 rTxt, i, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
1516 }
1517 if (i == nNextWordBoundary.endPos)
1518 {
1520 new MetaCommentAction(aCommentStringB, i - nTextPosition));
1521 nNextWordBoundary = rBreakIterator.getWordBoundary(
1522 rTxt, i + 1, rLocale, css::i18n::WordType::ANY_WORD, true);
1523 }
1524 if (i == nNextSentenceBreak)
1525 {
1527 new MetaCommentAction(aCommentStringC, i - nTextPosition));
1528 nNextSentenceBreak = rBreakIterator.endOfSentence(rTxt, i + 1, rLocale);
1529 }
1530 }
1531 }
1532 }
1533}
1534
1536 const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive)
1537{
1538 const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon();
1539
1540 if (rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1541 {
1542 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1543 // per polygon. If there are more, split the polygon in half and call recursively
1544 basegfx::B2DPolygon aLeft, aRight;
1545 splitLinePolygon(rBasePolygon, aLeft, aRight);
1547 new primitive2d::PolygonHairlinePrimitive2D(std::move(aLeft),
1548 rHairlinePrimitive.getBColor()));
1550 new primitive2d::PolygonHairlinePrimitive2D(std::move(aRight),
1551 rHairlinePrimitive.getBColor()));
1552
1553 processBasePrimitive2D(*xPLeft);
1554 processBasePrimitive2D(*xPRight);
1555 }
1556 else
1557 {
1558 // direct draw of hairline; use default processing
1559 // support SvtGraphicStroke MetaCommentAction
1560 const basegfx::BColor aLineColor(
1561 maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor()));
1562 std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke;
1563
1564 // #i121267# Not needed, does not give better quality compared with
1565 // the MetaActionType::POLYPOLYGON written by RenderPolygonHairlinePrimitive2D
1566 // below
1567 const bool bSupportSvtGraphicStroke(false);
1568
1569 if (bSupportSvtGraphicStroke)
1570 {
1571 pSvtGraphicStroke
1572 = impTryToCreateSvtGraphicStroke(rHairlinePrimitive.getB2DPolygon(), &aLineColor,
1573 nullptr, nullptr, nullptr, nullptr);
1574
1575 impStartSvtGraphicStroke(pSvtGraphicStroke.get());
1576 }
1577
1578 RenderPolygonHairlinePrimitive2D(rHairlinePrimitive, false);
1579
1580 if (bSupportSvtGraphicStroke)
1581 {
1582 impEndSvtGraphicStroke(pSvtGraphicStroke.get());
1583 }
1584 }
1585}
1586
1588 const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive)
1589{
1590 const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon();
1591
1592 if (rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1593 {
1594 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1595 // per polygon. If there are more, split the polygon in half and call recursively
1596 basegfx::B2DPolygon aLeft, aRight;
1597 splitLinePolygon(rBasePolygon, aLeft, aRight);
1599 new primitive2d::PolygonStrokePrimitive2D(std::move(aLeft),
1600 rStrokePrimitive.getLineAttribute(),
1601 rStrokePrimitive.getStrokeAttribute()));
1603 new primitive2d::PolygonStrokePrimitive2D(std::move(aRight),
1604 rStrokePrimitive.getLineAttribute(),
1605 rStrokePrimitive.getStrokeAttribute()));
1606
1607 processBasePrimitive2D(*xPLeft);
1608 processBasePrimitive2D(*xPRight);
1609 }
1610 else
1611 {
1613
1614 // support SvtGraphicStroke MetaCommentAction
1615 std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1616 rBasePolygon, nullptr, &rStrokePrimitive.getLineAttribute(),
1617 &rStrokePrimitive.getStrokeAttribute(), nullptr, nullptr);
1618
1619 impStartSvtGraphicStroke(pSvtGraphicStroke.get());
1620 const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute();
1621
1622 // create MetaPolyLineActions, but without LineStyle::Dash
1623 if (basegfx::fTools::more(rLine.getWidth(), 0.0))
1624 {
1625 const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute();
1626
1627 const basegfx::BColor aHairlineColor(
1629 mpOutputDevice->SetLineColor(Color(aHairlineColor));
1630 mpOutputDevice->SetFillColor();
1631
1632 // use the transformed line width
1633 LineInfo aLineInfo(LineStyle::Solid,
1635 aLineInfo.SetLineJoin(rLine.getLineJoin());
1636 aLineInfo.SetLineCap(rLine.getLineCap());
1637
1638 basegfx::B2DPolyPolygon aHairLinePolyPolygon;
1639 if (0.0 == rStroke.getFullDotDashLen())
1640 {
1641 aHairLinePolyPolygon.append(rBasePolygon);
1642 }
1643 else
1644 {
1645 bool done = false;
1646 const std::vector<double>& array = rStroke.getDotDashArray();
1647 // The dotdash array should generally have the form
1648 // (<dashLen> <distance>)+ (<dotLen> <distance>)*
1649 // (where (,),+ and * have their regex meaning).
1650 // Find out what the lengths and their counts are.
1651 if (!array.empty() && array.size() % 2 == 0)
1652 {
1653 double dashLen = array[0];
1654 double distance = array[1];
1655 int dashCount = 1;
1656 double dotLen = 0;
1657 int dotCount = 0;
1658 size_t pos = 2;
1659 while (pos + 2 <= array.size())
1660 {
1661 if (array[pos] != dashLen || array[pos + 1] != distance)
1662 break;
1663 ++dashCount;
1664 pos += 2;
1665 }
1666 if (pos + 2 <= array.size() && array[pos + 1] == distance)
1667 {
1668 dotLen = array[pos];
1669 ++dotCount;
1670 pos += 2;
1671 while (pos + 2 <= array.size())
1672 {
1673 if (array[pos] != dotLen || array[pos + 1] != distance)
1674 break;
1675 ++dotCount;
1676 pos += 2;
1677 }
1678 }
1679 if (array.size() == pos)
1680 {
1681 aHairLinePolyPolygon.append(rBasePolygon);
1682 // This will be used by setupStrokeAttributes() in cppcanvas.
1683 aLineInfo.SetStyle(LineStyle::Dash);
1684 aLineInfo.SetDashCount(dashCount);
1685 aLineInfo.SetDashLen(getTransformedLineWidth(dashLen));
1687 if (dotCount != 0)
1688 {
1689 aLineInfo.SetDotCount(dotCount);
1690 aLineInfo.SetDotLen(getTransformedLineWidth(dotLen));
1691 }
1692 done = true;
1693 }
1694 }
1695 if (!done)
1696 {
1697 // LineInfo can hold only limited info about dashing, apply dashing manually
1698 // if LineInfo cannot describe it. That should not happen though.
1699 SAL_WARN("drawinglayer", "dotdash array cannot be converted to LineInfo");
1700 basegfx::utils::applyLineDashing(rBasePolygon, rStroke.getDotDashArray(),
1701 &aHairLinePolyPolygon, nullptr,
1702 rStroke.getFullDotDashLen());
1703 }
1704 }
1705 aHairLinePolyPolygon.transform(maCurrentTransformation);
1706
1707 for (sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
1708 {
1709 const basegfx::B2DPolygon& aCandidate(aHairLinePolyPolygon.getB2DPolygon(a));
1710
1711 if (aCandidate.count() > 1)
1712 {
1713 const tools::Polygon aToolsPolygon(aCandidate);
1714
1715 mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo));
1716 }
1717 }
1718 }
1719 else
1720 {
1721 process(rStrokePrimitive);
1722 }
1723
1724 impEndSvtGraphicStroke(pSvtGraphicStroke.get());
1725
1726 mpOutputDevice->Pop();
1727 }
1728}
1729
1731 const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive)
1732{
1733 const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon();
1734
1735 if (rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1736 {
1737 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1738 // per polygon. If there are more, split the polygon in half and call recursively
1739 basegfx::B2DPolygon aLeft, aRight;
1740 splitLinePolygon(rBasePolygon, aLeft, aRight);
1744 aLeft, rStrokeArrowPrimitive.getLineAttribute(),
1745 rStrokeArrowPrimitive.getStrokeAttribute(), rStrokeArrowPrimitive.getStart(),
1746 aEmpty));
1749 aRight, rStrokeArrowPrimitive.getLineAttribute(),
1750 rStrokeArrowPrimitive.getStrokeAttribute(), aEmpty,
1751 rStrokeArrowPrimitive.getEnd()));
1752
1753 processBasePrimitive2D(*xPLeft);
1754 processBasePrimitive2D(*xPRight);
1755 }
1756 else
1757 {
1758 // support SvtGraphicStroke MetaCommentAction
1759 std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1760 rBasePolygon, nullptr, &rStrokeArrowPrimitive.getLineAttribute(),
1761 &rStrokeArrowPrimitive.getStrokeAttribute(), &rStrokeArrowPrimitive.getStart(),
1762 &rStrokeArrowPrimitive.getEnd());
1763
1764 // write LineGeometry start marker
1765 impStartSvtGraphicStroke(pSvtGraphicStroke.get());
1766
1767 // #i116162# When B&W is set as DrawMode, DrawModeFlags::WhiteFill is used
1768 // to let all fills be just white; for lines DrawModeFlags::BlackLine is used
1769 // so all line geometry is supposed to get black. Since in the in-between
1770 // stages of line geometry drawing filled polygons are used (e.g. line
1771 // start/ends) it is necessary to change these drawmodes to preserve
1772 // that lines shall be black; thus change DrawModeFlags::WhiteFill to
1773 // DrawModeFlags::BlackFill during line geometry processing to have line geometry
1774 // parts filled black.
1775 const DrawModeFlags nOldDrawMode(mpOutputDevice->GetDrawMode());
1776 const bool bDrawmodeChange(nOldDrawMode & DrawModeFlags::WhiteFill
1778
1779 if (bDrawmodeChange)
1780 {
1781 mpOutputDevice->SetDrawMode((nOldDrawMode & ~DrawModeFlags::WhiteFill)
1782 | DrawModeFlags::BlackFill);
1783 }
1784
1785 // process sub-line geometry (evtl. filled PolyPolygons)
1786 process(rStrokeArrowPrimitive);
1787
1788 if (bDrawmodeChange)
1789 {
1790 mpOutputDevice->SetDrawMode(nOldDrawMode);
1791 }
1792
1793 // write LineGeometry end marker
1794 impEndSvtGraphicStroke(pSvtGraphicStroke.get());
1795 }
1796}
1797
1799 const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate)
1800{
1801 // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1802 basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon());
1803
1804 if (!rBitmapCandidate.getDefinitionRange().isEmpty()
1805 && aLocalPolyPolygon.getB2DRange() != rBitmapCandidate.getDefinitionRange())
1806 {
1807 // The range which defines the bitmap fill is defined and different from the
1808 // range of the defining geometry (e.g. used for FillStyle UseSlideBackground).
1809 // This cannot be done calling vcl, thus use decomposition here directly
1810 process(rBitmapCandidate);
1811 return;
1812 }
1813
1814 fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
1815
1816 std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
1817
1818 if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1819 {
1820 // #121194# Changed implementation and checked usages of convert to metafile,
1821 // presentation start (uses SvtGraphicFill) and printing.
1822
1823 // calculate transformation. Get real object size, all values in FillGraphicAttribute
1824 // are relative to the unified object
1825 aLocalPolyPolygon.transform(maCurrentTransformation);
1826 const basegfx::B2DVector aOutlineSize(aLocalPolyPolygon.getB2DRange().getRange());
1827
1828 // the scaling needs scale from pixel to logic coordinate system
1829 const attribute::FillGraphicAttribute& rFillGraphicAttribute
1830 = rBitmapCandidate.getFillGraphic();
1831 const Size aBmpSizePixel(rFillGraphicAttribute.getGraphic().GetSizePixel());
1832
1833 // setup transformation like in impgrfll. Multiply with aOutlineSize
1834 // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange()
1835 // to object coordinates with object's top left being at (0,0). Divide
1836 // by pixel size so that scale from pixel to logic will work in SvtGraphicFill.
1837 const basegfx::B2DVector aTransformScale(
1838 rFillGraphicAttribute.getGraphicRange().getRange()
1839 / basegfx::B2DVector(std::max(1.0, double(aBmpSizePixel.Width())),
1840 std::max(1.0, double(aBmpSizePixel.Height())))
1841 * aOutlineSize);
1842 const basegfx::B2DPoint aTransformPosition(
1843 rFillGraphicAttribute.getGraphicRange().getMinimum() * aOutlineSize);
1844
1845 // setup transformation like in impgrfll
1846 SvtGraphicFill::Transform aTransform;
1847
1848 // scale values are divided by bitmap pixel sizes
1849 aTransform.matrix[0] = aTransformScale.getX();
1850 aTransform.matrix[4] = aTransformScale.getY();
1851
1852 // translates are absolute
1853 aTransform.matrix[2] = aTransformPosition.getX();
1854 aTransform.matrix[5] = aTransformPosition.getY();
1855
1856 pSvtGraphicFill.reset(new SvtGraphicFill(
1857 getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd,
1858 SvtGraphicFill::fillTexture, aTransform, rFillGraphicAttribute.getTiling(),
1860 Color(), 0, rFillGraphicAttribute.getGraphic()));
1861 }
1862
1863 // Do use decomposition; encapsulate with SvtGraphicFill
1864 impStartSvtGraphicFill(pSvtGraphicFill.get());
1865 process(rBitmapCandidate);
1866 impEndSvtGraphicFill(pSvtGraphicFill.get());
1867}
1868
1870 const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate)
1871{
1872 // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1873 const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch();
1874 basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon());
1875
1876 if (aLocalPolyPolygon.getB2DRange() != rHatchCandidate.getDefinitionRange())
1877 {
1878 // the range which defines the hatch is different from the range of the
1879 // geometry (used for writer frames). This cannot be done calling vcl, thus use
1880 // decomposition here
1881 process(rHatchCandidate);
1882 return;
1883 }
1884
1885 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1886 // per polygon. Split polygon until there are less than that
1887 fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
1888
1889 if (rFillHatchAttribute.isFillBackground())
1890 {
1891 // with fixing #i111954# (see below) the possible background
1892 // fill of a hatched object was lost.Generate a background fill
1893 // primitive and render it
1894 const primitive2d::Primitive2DReference xBackground(
1895 new primitive2d::PolyPolygonColorPrimitive2D(aLocalPolyPolygon,
1896 rHatchCandidate.getBackgroundColor()));
1897
1899 }
1900
1901 std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
1902 aLocalPolyPolygon.transform(maCurrentTransformation);
1903
1904 if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1905 {
1906 // re-create a VCL hatch as base data
1908
1909 switch (rFillHatchAttribute.getStyle())
1910 {
1911 default: // attribute::HatchStyle::Single :
1912 {
1914 break;
1915 }
1917 {
1919 break;
1920 }
1922 {
1924 break;
1925 }
1926 }
1927
1928 SvtGraphicFill::Transform aTransform;
1929
1930 // scale
1931 aTransform.matrix[0] *= rFillHatchAttribute.getDistance();
1932 aTransform.matrix[4] *= rFillHatchAttribute.getDistance();
1933
1934 // rotate (was never correct in impgrfll anyways, use correct angle now)
1935 aTransform.matrix[0] *= cos(rFillHatchAttribute.getAngle());
1936 aTransform.matrix[1] *= -sin(rFillHatchAttribute.getAngle());
1937 aTransform.matrix[3] *= sin(rFillHatchAttribute.getAngle());
1938 aTransform.matrix[4] *= cos(rFillHatchAttribute.getAngle());
1939
1940 pSvtGraphicFill.reset(new SvtGraphicFill(
1941 getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd,
1942 SvtGraphicFill::fillHatch, aTransform, false, eHatch,
1943 Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())),
1945 }
1946
1947 // Do use decomposition; encapsulate with SvtGraphicFill
1948 impStartSvtGraphicFill(pSvtGraphicFill.get());
1949
1950 // #i111954# do NOT use decomposition, but use direct VCL-command
1951 // process(rCandidate.get2DDecomposition(getViewInformation2D()));
1952 const tools::PolyPolygon aToolsPolyPolygon(
1953 basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon));
1954 const HatchStyle aHatchStyle(
1955 attribute::HatchStyle::Single == rFillHatchAttribute.getStyle()
1956 ? HatchStyle::Single
1957 : attribute::HatchStyle::Double == rFillHatchAttribute.getStyle() ? HatchStyle::Double
1958 : HatchStyle::Triple);
1959
1960 mpOutputDevice->DrawHatch(
1961 aToolsPolyPolygon,
1962 Hatch(aHatchStyle,
1963 Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())),
1964 basegfx::fround(rFillHatchAttribute.getDistance()),
1965 Degree10(basegfx::fround(basegfx::rad2deg<10>(rFillHatchAttribute.getAngle())))));
1966
1967 impEndSvtGraphicFill(pSvtGraphicFill.get());
1968}
1969
1971 const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate)
1972{
1973 bool useDecompose(false);
1974
1975 if (!useDecompose)
1976 {
1977 basegfx::B2DVector aScale, aTranslate;
1978 double fRotate, fShearX;
1979
1980 maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
1981
1982 // detect if transformation is rotated, sheared or mirrored in X and/or Y
1984 || aScale.getX() < 0.0 || aScale.getY() < 0.0)
1985 {
1986 // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
1987 // This is because VCL Gradient mechanism does *not* support to rotate the gradient
1988 // with objects and this case is not expressible in a Metafile (and cannot be added
1989 // since the FileFormats used, e.g. *.wmf, do not support it either).
1990 // Such cases happen when a graphic object uses a Metafile as graphic information or
1991 // a fill style definition uses a Metafile. In this cases the graphic content is
1992 // rotated with the graphic or filled object; this is not supported by the target
1993 // format of this conversion renderer - Metafiles.
1994 // To solve this, not a Gradient is written, but the decomposition of this object
1995 // is written to the Metafile. This is the PolyPolygons building the gradient fill.
1996 // These will need more space and time, but the result will be as if the Gradient
1997 // was rotated with the object.
1998 // This mechanism is used by all exporters still not using Primitives (e.g. Print,
1999 // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
2000 // transfers. One more reason to *change* these to primitives.
2001 // BTW: One more example how useful the principles of primitives are; the decomposition
2002 // is by definition a simpler, maybe more expensive representation of the same content.
2003 useDecompose = true;
2004 }
2005 }
2006
2007 // tdf#150551 for PDF export, use the decomposition for better gradient visualization
2008 if (!useDecompose && nullptr != mpPDFExtOutDevData)
2009 {
2010 useDecompose = true;
2011 }
2012
2013 basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon());
2014
2015 if (!useDecompose && aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange())
2016 {
2017 // the range which defines the gradient is different from the range of the
2018 // geometry (used for writer frames). This cannot be done calling vcl, thus use
2019 // decomposition here
2020 useDecompose = true;
2021 }
2022
2023 const attribute::FillGradientAttribute& rFillGradient(rGradientCandidate.getFillGradient());
2024
2025 if (!useDecompose && rFillGradient.cannotBeHandledByVCL())
2026 {
2027 // MCGR: if we have ColorStops, do not try to fallback to old VCL-Gradient,
2028 // that will *not* be capable of representing this properly. Use the
2029 // correct decomposition instead
2030 useDecompose = true;
2031 }
2032
2033 if (useDecompose)
2034 {
2035 GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile());
2036
2037 // tdf#155479 only add 'BGRAD_SEQ_BEGIN' if SVG export
2038 if (nullptr != pMetaFile && pMetaFile->getSVG())
2039 {
2040 // write the color stops to a memory stream
2041 SvMemoryStream aMemStm;
2042 VersionCompatWrite aCompat(aMemStm, 1);
2043
2044 const basegfx::BColorStops& rColorStops(rFillGradient.getColorStops());
2045 sal_uInt16 nTmp(sal::static_int_cast<sal_uInt16>(rColorStops.size()));
2046 aMemStm.WriteUInt16(nTmp);
2047
2048 for (auto const& rCand : rColorStops)
2049 {
2050 aMemStm.WriteDouble(rCand.getStopOffset());
2051 const basegfx::BColor& rColor(rCand.getStopColor());
2052 aMemStm.WriteDouble(rColor.getRed());
2053 aMemStm.WriteDouble(rColor.getGreen());
2054 aMemStm.WriteDouble(rColor.getBlue());
2055 }
2056
2057 // Add a new MetaCommentAction section of type 'BGRAD_SEQ_BEGIN/BGRAD_SEQ_END'
2058 // that is capable of holding the new color step information, plus the
2059 // already used MetaActionType::GRADIENTEX.
2060 // With that combination only places that know about that new BGRAD_SEQ_* will
2061 // use it while all others will work on the created decomposition of the
2062 // gradient for compatibility - which are single-color filled polygons
2063 pMetaFile->AddAction(new MetaCommentAction(
2064 "BGRAD_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
2065 aMemStm.TellEnd()));
2066
2067 // create MetaActionType::GRADIENTEX
2068 // NOTE: with the new BGRAD_SEQ_* we could use basegfx::B2DPolygon and
2069 // basegfx::BGradient here directly, but may have to add streaming OPs
2070 // for these, so for now just go with what we use all the time. The real
2071 // work for improvement should not go to this 'compromize' but to a real
2072 // re-work of the SVG export (or/and others) to no longer work on metafiles
2073 // but on UNO API or primitives (whatever fits best to the specific export)
2074 fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
2075 Gradient aVCLGradient;
2076 impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false);
2077 aLocalPolyPolygon.transform(maCurrentTransformation);
2078 const tools::PolyPolygon aToolsPolyPolygon(
2079 getFillPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon)));
2080 mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
2081 }
2082
2083 // use decompose to draw, will create PolyPolygon ColorFill actions
2084 process(rGradientCandidate);
2085
2086 // tdf#155479 only add 'BGRAD_SEQ_END' if SVG export
2087 if (nullptr != pMetaFile && pMetaFile->getSVG())
2088 {
2089 // close the BGRAD_SEQ_* actions range
2090 pMetaFile->AddAction(new MetaCommentAction("BGRAD_SEQ_END"));
2091 }
2092
2093 return;
2094 }
2095
2096 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
2097 // per polygon. Split polygon until there are less than that
2098 fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
2099
2100 // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END
2101 // it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
2102 // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
2103 Gradient aVCLGradient;
2104 impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false);
2105 aLocalPolyPolygon.transform(maCurrentTransformation);
2106
2107 // #i82145# ATM VCL printing of gradients using curved shapes does not work,
2108 // i submitted the bug with the given ID to THB. When that task is fixed it is
2109 // necessary to again remove this subdivision since it decreases possible
2110 // printing quality (not even resolution-dependent for now). THB will tell
2111 // me when that task is fixed in the master
2112 const tools::PolyPolygon aToolsPolyPolygon(
2113 getFillPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon)));
2114
2115 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
2116 std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
2117
2118 if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
2119 {
2120 // setup gradient stuff like in impgrfll
2122
2123 switch (aVCLGradient.GetStyle())
2124 {
2125 default: // css::awt::GradientStyle_LINEAR:
2126 case css::awt::GradientStyle_AXIAL:
2128 break;
2129 case css::awt::GradientStyle_RADIAL:
2130 case css::awt::GradientStyle_ELLIPTICAL:
2132 break;
2133 case css::awt::GradientStyle_SQUARE:
2134 case css::awt::GradientStyle_RECT:
2136 break;
2137 }
2138
2139 pSvtGraphicFill.reset(new SvtGraphicFill(
2140 aToolsPolyPolygon, Color(), 0.0, SvtGraphicFill::fillEvenOdd,
2142 SvtGraphicFill::hatchSingle, Color(), eGrad, aVCLGradient.GetStartColor(),
2143 aVCLGradient.GetEndColor(), aVCLGradient.GetSteps(), Graphic()));
2144 }
2145
2146 // call VCL directly; encapsulate with SvtGraphicFill
2147 impStartSvtGraphicFill(pSvtGraphicFill.get());
2148 mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
2149 impEndSvtGraphicFill(pSvtGraphicFill.get());
2150}
2151
2153 const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate)
2154{
2156 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
2157
2158 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
2159 // per polygon. Split polygon until there are less than that
2160 fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
2161
2162 const basegfx::BColor aPolygonColor(
2163 maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
2164 aLocalPolyPolygon.transform(maCurrentTransformation);
2165
2166 // set line and fill color
2167 mpOutputDevice->SetFillColor(Color(aPolygonColor));
2168 mpOutputDevice->SetLineColor();
2169
2170 mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
2171
2172 mpOutputDevice->Pop();
2173}
2174
2176 const primitive2d::MaskPrimitive2D& rMaskCandidate)
2177{
2178 // mask group. Special handling for MetaFiles.
2179 if (rMaskCandidate.getChildren().empty())
2180 return;
2181
2182 basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
2183
2184 if (aMask.count())
2185 {
2186 // prepare new mask polygon and rescue current one
2188 const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon);
2189
2191 {
2192 // there is already a clip polygon set; build clipped union of
2193 // current mask polygon and new one
2195 aMask, maClipPolyPolygon,
2196 true, // #i106516# we want the inside of aMask, not the outside
2197 false);
2198 }
2199 else
2200 {
2201 // use mask directly
2202 maClipPolyPolygon = aMask;
2203 }
2204
2206 {
2207 // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!)
2208 // Removed subdivision and fixed in vcl::Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
2209 // the ClipRegion is built from the Polygon. An AdaptiveSubdivide on the source polygon was missing there
2212
2213 // recursively paint content
2214 // #i121267# Only need to process sub-content when clip polygon is *not* empty.
2215 // If it is empty, the clip is empty and there can be nothing inside.
2216 process(rMaskCandidate.getChildren());
2217
2218 // restore VCL clip region
2219 mpOutputDevice->Pop();
2220 }
2221
2222 // restore to rescued clip polygon
2223 maClipPolyPolygon = aLastClipPolyPolygon;
2224 }
2225 else
2226 {
2227 // no mask, no clipping. recursively paint content
2228 process(rMaskCandidate.getChildren());
2229 }
2230}
2231
2233 const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate)
2234{
2236 // for metafile: Need to examine what the pure vcl version is doing here actually
2237 // - uses DrawTransparent with metafile for content and a gradient
2238 // - uses DrawTransparent for single PolyPolygons directly. Can be detected by
2239 // checking the content for single PolyPolygonColorPrimitive2D
2240 const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren();
2241
2242 if (!rContent.empty())
2243 {
2244 if (0.0 == rUniTransparenceCandidate.getTransparence())
2245 {
2246 // not transparent at all, use content
2247 process(rUniTransparenceCandidate.getChildren());
2248 }
2249 else if (rUniTransparenceCandidate.getTransparence() > 0.0
2250 && rUniTransparenceCandidate.getTransparence() < 1.0)
2251 {
2252 // try to identify a single PolyPolygonColorPrimitive2D in the
2253 // content part of the transparence primitive
2254 const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = nullptr;
2255 static bool bForceToMetafile(false); // loplugin:constvars:ignore
2256
2257 if (!bForceToMetafile && 1 == rContent.size())
2258 {
2259 const primitive2d::Primitive2DReference xReference(rContent[0]);
2260 pPoPoColor = dynamic_cast<const primitive2d::PolyPolygonColorPrimitive2D*>(
2261 xReference.get());
2262 }
2263
2264 // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and
2265 // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D.
2266 // Check also for correct ID to exclude derived implementations
2267 if (pPoPoColor
2269 {
2270 // single transparent tools::PolyPolygon identified, use directly
2271 const basegfx::BColor aPolygonColor(
2273 basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon());
2274
2275 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
2276 // per polygon. Split polygon until there are less than that
2277 fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
2278
2279 // now transform
2280 aLocalPolyPolygon.transform(maCurrentTransformation);
2281
2282 // set line and fill color
2283 const sal_uInt16 nTransPercentVcl(static_cast<sal_uInt16>(
2284 basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0)));
2285 mpOutputDevice->SetFillColor(Color(aPolygonColor));
2286 mpOutputDevice->SetLineColor();
2287
2288 mpOutputDevice->DrawTransparent(tools::PolyPolygon(aLocalPolyPolygon),
2289 nTransPercentVcl);
2290 }
2291 else
2292 {
2293 // save old mfCurrentUnifiedTransparence and set new one
2294 // so that contained SvtGraphicStroke may use the current one
2295 const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence);
2296 // #i105377# paint the content metafile opaque as the transparency gets
2297 // split of into the gradient below
2298 // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence();
2300
2301 // various content, create content-metafile
2302 GDIMetaFile aContentMetafile;
2303
2304 // tdf#155479 always forward propagate SVG flag for sub-content,
2305 // it may contain cannotBeHandledByVCL gradients or transparencyGradients
2306 aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG());
2307
2308 const tools::Rectangle aPrimitiveRectangle(
2309 impDumpToMetaFile(rContent, aContentMetafile));
2310
2311 // restore mfCurrentUnifiedTransparence; it may have been used
2312 // while processing the sub-content in impDumpToMetaFile
2313 mfCurrentUnifiedTransparence = fLastCurrentUnifiedTransparence;
2314
2315 // create uniform VCL gradient for uniform transparency
2316 Gradient aVCLGradient;
2317 const sal_uInt8 nTransPercentVcl(static_cast<sal_uInt8>(
2318 basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0)));
2319 const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
2320
2321 aVCLGradient.SetStyle(css::awt::GradientStyle_LINEAR);
2322 aVCLGradient.SetStartColor(aTransColor);
2323 aVCLGradient.SetEndColor(aTransColor);
2324 aVCLGradient.SetAngle(0_deg10);
2325 aVCLGradient.SetBorder(0);
2326 aVCLGradient.SetOfsX(0);
2327 aVCLGradient.SetOfsY(0);
2328 aVCLGradient.SetStartIntensity(100);
2329 aVCLGradient.SetEndIntensity(100);
2330 aVCLGradient.SetSteps(2);
2331
2332 // render it to VCL
2333 mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
2334 aPrimitiveRectangle.GetSize(), aVCLGradient);
2335 }
2336 }
2337 }
2338
2339 mpOutputDevice->Pop();
2340}
2341
2343 const primitive2d::TransparencePrimitive2D& rTransparenceCandidate)
2344{
2345 // for metafile: Need to examine what the pure vcl version is doing here actually
2346 // - uses DrawTransparent with metafile for content and a gradient
2347 // i can detect this here with checking the gradient part for a single
2348 // FillGradientPrimitive2D and reconstruct the gradient.
2349 // If that detection goes wrong, I have to create a transparence-blended bitmap. Eventually
2350 // do that in stripes, else RenderTransparencePrimitive2D may just be used
2351 const primitive2d::Primitive2DContainer& rContent(rTransparenceCandidate.getChildren());
2352 const primitive2d::Primitive2DContainer& rTransparence(
2353 rTransparenceCandidate.getTransparence());
2354
2355 if (rContent.empty() || rTransparence.empty())
2356 return;
2357
2358 // try to identify a single FillGradientPrimitive2D in the
2359 // transparence part of the primitive. The hope is to handle
2360 // the more specific case in a better way than the general
2361 // TransparencePrimitive2D which has strongly separated
2362 // definitions for transparency and content, both completely
2363 // free definable by primitives
2364 const primitive2d::FillGradientPrimitive2D* pFiGradient(nullptr);
2365 static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore
2366
2367 // check for single FillGradientPrimitive2D
2368 if (!bForceToBigTransparentVDev && 1 == rTransparence.size())
2369 {
2370 pFiGradient
2371 = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(rTransparence[0].get());
2372
2373 // check also for correct ID to exclude derived implementations
2374 if (pFiGradient
2376 pFiGradient = nullptr;
2377 }
2378
2379 // tdf#155479 preps for holding extra-MCGR infos
2380 bool bSVGTransparencyColorStops(false);
2381 basegfx::BColorStops aSVGTransparencyColorStops;
2382
2383 // MCGR: tdf#155437 If we have identified a transparency gradient,
2384 // check if VCL is able to handle it at all
2385 if (nullptr != pFiGradient && pFiGradient->getFillGradient().cannotBeHandledByVCL())
2386 {
2387 // If not, reset the pointer and do not make use of this special case.
2388 // Adding a gradient in incomplete state that can not be handled by vcl
2389 // makes no sense and will knowingly lead to errors, especially with
2390 // MCGR extended possibilities. I checked what happens with the
2391 // MetaFloatTransparentAction added by OutputDevice::DrawTransparent, but
2392 // in most cases it gets converted to bitmap or even ignored, see e.g.
2393 // - vcl/source/gdi/pdfwriter_impl2.cxx for PDF export
2394 // - vcl/source/filter/wmf/wmfwr.cxx -> does ignore TransparenceGradient completely
2395 // - vcl/source/filter/wmf/emfwr.cxx -> same
2396 // - vcl/source/filter/eps/eps.cxx -> same
2397 // NOTE: Theoretically it would be possible to make the new extended Gradient data
2398 // available in metafiles, with the known limitations (not backward comp, all
2399 // places using it would need adaption, ...), but combined with knowing that nearly
2400 // all usages ignore or render it locally anyways makes that a non-option.
2401
2402 // tdf#155479 Yepp, as already mentioned above we need to add
2403 // some MCGR infos in case of SVG export, prepare that here
2404 if (mpOutputDevice->GetConnectMetaFile()->getSVG())
2405 {
2406 // for SVG, do not use decompose & prep extra data
2407 bSVGTransparencyColorStops = true;
2408 aSVGTransparencyColorStops = pFiGradient->getFillGradient().getColorStops();
2409 }
2410 else
2411 {
2412 // use decomposition
2413 pFiGradient = nullptr;
2414 }
2415 }
2416
2417 if (nullptr != pFiGradient)
2418 {
2419 // this combination of Gradient can be expressed/handled by
2420 // vcl/metafile, so add it directly. various content, create content-metafile
2421 GDIMetaFile aContentMetafile;
2422
2423 // tdf#155479 always forward propagate SVG flag for sub-content,
2424 // it may contain cannotBeHandledByVCL gradients or transparencyGradients
2425 aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG());
2426
2427 const tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
2428
2429 // re-create a VCL-gradient from FillGradientPrimitive2D
2430 Gradient aVCLGradient;
2432 true);
2433
2434 if (bSVGTransparencyColorStops)
2435 {
2436 // tdf#155479 create action directly & add extra
2437 // MCGR infos to the metafile, do that by adding - ONLY in
2438 // case of SVG export - to the MetaFileAction. For that
2439 // reason, do what OutputDevice::DrawTransparent will do,
2440 // but locally.
2441 // NOTE: That would be good for this whole
2442 // VclMetafileProcessor2D anyways to allow to get it
2443 // completely independent from OutputDevice in the long run
2444 GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile());
2446 new MetaFloatTransparentAction(aContentMetafile, aPrimitiveRectangle.TopLeft(),
2447 aPrimitiveRectangle.GetSize(), aVCLGradient));
2448
2449 pAction->addSVGTransparencyColorStops(aSVGTransparencyColorStops);
2450 pMetaFile->AddAction(pAction);
2451 }
2452 else
2453 {
2454 // render it to VCL (creates MetaFloatTransparentAction)
2455 mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
2456 aPrimitiveRectangle.GetSize(), aVCLGradient);
2457 }
2458 return;
2459 }
2460
2461 // Here we need to create a correct replacement visualization for the
2462 // TransparencePrimitive2D for the target metafile.
2463 // I replaced the n'th iteration to convert-to-bitmap which was
2464 // used here by using the existing tooling. The orig here was also producing
2465 // transparency errors with test-file from tdf#155437 on the right part of the
2466 // image.
2467 // Just rely on existing tooling doing the right thing in one place, so also
2468 // corrections/optimizations can be in one single place
2469
2470 // Start by getting logic range of content, transform object-to-world, then world-to-view
2471 // to get to discrete values ('pixels'). Matrix multiplication is right-to-left (and not
2472 // commutative)
2473 basegfx::B2DRange aLogicRange(rTransparenceCandidate.getB2DRange(getViewInformation2D()));
2474 aLogicRange.transform(mpOutputDevice->GetViewTransformation() * maCurrentTransformation);
2475
2476 // expand in discrete coordinates to next-bigger 'pixel' boundaries and remember
2477 // created discrete range
2478 aLogicRange.expand(
2479 basegfx::B2DPoint(floor(aLogicRange.getMinX()), floor(aLogicRange.getMinY())));
2480 aLogicRange.expand(basegfx::B2DPoint(ceil(aLogicRange.getMaxX()), ceil(aLogicRange.getMaxY())));
2481 const basegfx::B2DRange aDiscreteRange(aLogicRange);
2482
2483 // transform back from discrete to world coordinates: this creates the
2484 // pixel-boundaries extended logic range we need to cover all content
2485 // reliably
2486 aLogicRange.transform(mpOutputDevice->GetInverseViewTransformation());
2487
2488 // create transform embedding for renderer. Goal is to translate what we
2489 // want to paint to top/left 0/0 and the calculated discrete size
2491 -aLogicRange.getMinX(), -aLogicRange.getMinY()));
2492 const double fLogicWidth(
2493 basegfx::fTools::equalZero(aLogicRange.getWidth()) ? 1.0 : aLogicRange.getWidth());
2494 const double fLogicHeight(
2495 basegfx::fTools::equalZero(aLogicRange.getHeight()) ? 1.0 : aLogicRange.getHeight());
2496 aEmbedding.scale(aDiscreteRange.getWidth() / fLogicWidth,
2497 aDiscreteRange.getHeight() / fLogicHeight);
2498
2499 // use the whole TransparencePrimitive2D as input (no need to create a new
2500 // one with the sub-contents, these are ref-counted) and add to embedding
2501 // primitive2d::TransparencePrimitive2D& rTrCand();
2503 rTransparenceCandidate) };
2505 aEmbedding, std::move(xEmbedSeq)) };
2506
2507 // use empty ViewInformation & a useful MaximumQuadraticPixels
2508 // limitation to paint the content
2509 const auto aViewInformation2D(geometry::createViewInformation2D({}));
2510 const sal_uInt32 nMaximumQuadraticPixels(500000);
2511 const BitmapEx aBitmapEx(convertToBitmapEx(
2512 std::move(xEmbedSeq), aViewInformation2D, basegfx::fround(aDiscreteRange.getWidth()),
2513 basegfx::fround(aDiscreteRange.getHeight()), nMaximumQuadraticPixels));
2514
2515 // add to target metafile (will create MetaFloatTransparentAction)
2516 mpOutputDevice->DrawBitmapEx(
2517 Point(basegfx::fround(aLogicRange.getMinX()), basegfx::fround(aLogicRange.getMinY())),
2518 Size(basegfx::fround(aLogicRange.getWidth()), basegfx::fround(aLogicRange.getHeight())),
2519 aBitmapEx);
2520}
2521
2523 const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate)
2524{
2525 ::comphelper::ValueRestorationGuard const g(mpCurrentStructureTag, &rStructureTagCandidate);
2526
2527 // structured tag primitive
2528 const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
2529 bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement));
2530 bool bNeedEndAnchor(false);
2531
2532 if (!rStructureTagCandidate.isTaggedSdrObject())
2533 {
2534 bTagUsed = false;
2535 }
2536
2537 if (mpPDFExtOutDevData && bTagUsed)
2538 {
2539 // foreground object: tag as regular structure element
2540 if (!rStructureTagCandidate.isBackground())
2541 {
2542 if (rStructureTagCandidate.GetAnchorStructureElementKey() != nullptr)
2543 {
2544 sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement(
2545 rStructureTagCandidate.GetAnchorStructureElementKey());
2547 bNeedEndAnchor = true;
2548 }
2550 switch (rTagElement)
2551 {
2552 case vcl::PDFWriter::H1:
2553 case vcl::PDFWriter::H2:
2554 case vcl::PDFWriter::H3:
2555 case vcl::PDFWriter::H4:
2556 case vcl::PDFWriter::H5:
2557 case vcl::PDFWriter::H6:
2568 break;
2573 break;
2574 default:
2575 break;
2576 }
2577 switch (rTagElement)
2578 {
2583 {
2584 auto const range(rStructureTagCandidate.getB2DRange(getViewInformation2D()));
2585 tools::Rectangle const aLogicRect(
2586 basegfx::fround(range.getMinX()), basegfx::fround(range.getMinY()),
2587 basegfx::fround(range.getMaxX()), basegfx::fround(range.getMaxY()));
2589 break;
2590 }
2591 default:
2592 break;
2593 }
2594 if (rTagElement == vcl::PDFWriter::Annot)
2595 {
2596 mpPDFExtOutDevData->SetStructureAnnotIds(rStructureTagCandidate.GetAnnotIds());
2597 }
2598 if (rTagElement == vcl::PDFWriter::TableHeader)
2599 {
2602 }
2603 }
2604 // background object
2605 else
2606 {
2607 // background image: tag as artifact
2608 if (rStructureTagCandidate.isImage())
2610 // any other background object: do not tag
2611 else
2612 assert(false);
2613 }
2614 }
2615
2616 // process children normally
2617 process(rStructureTagCandidate.getChildren());
2618
2619 if (mpPDFExtOutDevData && bTagUsed)
2620 {
2621 // write end tag
2623 if (bNeedEndAnchor)
2624 {
2626 }
2627 }
2628}
2629
2630} // end of namespace
2631
2632/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
DrawModeFlags
void WindStart()
void AddAction(const rtl::Reference< MetaAction > &pAction)
void setSVG(bool bNew)
bool getSVG() const
void SetPrefMapMode(const MapMode &rMapMode)
void Record(OutputDevice *pOutDev)
void SetPrefSize(const Size &rSize)
void SetStyle(css::awt::GradientStyle eStyle)
void SetOfsX(sal_uInt16 nOfsX)
void SetStartIntensity(sal_uInt16 nIntens)
const Color & GetEndColor() const
void SetBorder(sal_uInt16 nBorder)
const Color & GetStartColor() const
void SetStartColor(const Color &rColor)
void SetSteps(sal_uInt16 nSteps)
css::awt::GradientStyle GetStyle() const
sal_uInt16 GetSteps() const
void SetOfsY(sal_uInt16 nOfsY)
void SetAngle(Degree10 nAngle)
void SetEndColor(const Color &rColor)
void SetEndIntensity(sal_uInt16 nIntens)
bool IsCropped() const
tools::Long GetTopCrop() const
bool IsSpecialDrawMode() const
tools::Long GetBottomCrop() const
tools::Long GetRightCrop() const
sal_uInt8 GetAlpha() const
bool IsAdjusted() const
tools::Long GetLeftCrop() const
Size GetPrefSize() const
MapMode GetPrefMapMode() const
const Graphic & GetGraphic() const
Size GetSizePixel(const OutputDevice *pRefDevice=nullptr) const
bool IsGfxLink() const
void SetDashCount(sal_uInt16 nDashCount)
void SetDashLen(double nDashLen)
void SetDotCount(sal_uInt16 nDotCount)
void SetLineJoin(basegfx::B2DLineJoin eLineJoin)
void SetLineCap(css::drawing::LineCap eLineCap)
void SetDistance(double nDistance)
void SetStyle(LineStyle eStyle)
void SetDotLen(double nDotLen)
void SetOrigin(const Point &rOrigin)
GDIMetaFile * GetConnectMetaFile() const
const vcl::Font & GetFont() const
const Point & GetRefPoint() const
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
const Color & GetLineColor() const
const MapMode & GetMapMode() const
DrawModeFlags GetDrawMode() const
const AllSettings & GetSettings() const
const Color & GetFillColor() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const void * GetData()
virtual sal_uInt64 TellEnd() override
SvStream & WriteDouble(const double &rDouble)
SvStream & WriteUInt16(sal_uInt16 nUInt16)
::std::vector< double > DashArray
reference_type * get() const
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void scale(double fX, double fY)
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void transform(const basegfx::B2DHomMatrix &rMatrix)
B2DRange getB2DRange() const
sal_uInt32 count() const
bool isClosed() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
void transform(const basegfx::B2DHomMatrix &rMatrix)
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
void setControlPoints(sal_uInt32 nIndex, const basegfx::B2DPoint &rPrev, const basegfx::B2DPoint &rNext)
bool areControlPointsUsed() const
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
B2DRange const & getB2DRange() const
sal_uInt32 count() const
void setClosed(bool bNew)
basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const
B2DPoint getMaximum() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
B2DVector getRange() const
B2DPoint getCenter() const
B2DPoint getMinimum() const
double getLength() const
::basegfx::BColor getModifiedColor(const ::basegfx::BColor &rSource) const
sal_uInt32 count() const
double getBlue() const
double getRed() const
double getGreen() const
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
void expand(const Tuple2D< TYPE > &rTuple)
TYPE getMaxY() const
bool isEmpty() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
const basegfx::BColorStops & getColorStops() const
const basegfx::B2DRange & getGraphicRange() const
basegfx::B2DLineJoin getLineJoin() const
css::drawing::LineCap getLineCap() const
const basegfx::BColor & getColor() const
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
const ::std::vector< double > & getDotDashArray() const
const basegfx::B2DHomMatrix & getObjectTransformation() const
data access
virtual sal_uInt32 getPrimitive2DID() const =0
provide unique ID for fast identifying of known primitive implementations in renderers.
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const
The default implementation will use getDecomposition results to create the range.
const css::uno::Reference< css::awt::XControl > & getXControl() const
mxControl access.
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
const basegfx::B2DHomMatrix & getTransform() const
data read access
const attribute::FillGradientAttribute & getFillGradient() const
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
const basegfx::B2DHomMatrix & getTransform() const
data read access
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition. Implementation will just return children
const Primitive2DContainer & getChildren() const
data read access
const basegfx::B2DPolyPolygon & getMask() const
data read access
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
data read access
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
data read access
const attribute::FillGradientAttribute & getFillGradient() const
const attribute::FillGraphicAttribute & getFillGraphic() const
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
data read access
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
data read access
const attribute::FillHatchAttribute & getFillHatch() const
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
const attribute::LineStartEndAttribute & getStart() const
data read access
const attribute::LineStartEndAttribute & getEnd() const
const attribute::StrokeAttribute & getStrokeAttribute() const
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
const attribute::LineAttribute & getLineAttribute() const
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
const vcl::PDFWriter::StructElement & getStructureElement() const
data read access
const Primitive2DContainer & getTransparence() const
data read access
void process(const primitive2d::BasePrimitive2D &rCandidate)
const geometry::ViewInformation2D & getViewInformation2D() const
data read access
void processPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D &rBitmapCandidate)
void processControlPrimitive2D(const primitive2d::ControlPrimitive2D &rControlPrimitive)
virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D &rCandidate) override
primitive2d::StructureTagPrimitive2D const * mpCurrentStructureTag
void processTextHierarchyFieldPrimitive2D(const primitive2d::TextHierarchyFieldPrimitive2D &rFieldPrimitive)
void impEndSvtGraphicStroke(SvtGraphicStroke const *pSvtGraphicStroke)
void processUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D &rUniTransparenceCandidate)
void impStartSvtGraphicStroke(SvtGraphicStroke const *pSvtGraphicStroke)
void processPolyPolygonHatchPrimitive2D(const primitive2d::PolyPolygonHatchPrimitive2D &rHatchCandidate)
void impEndSvtGraphicFill(SvtGraphicFill const *pSvtGraphicFill)
double getTransformedLineWidth(double fWidth) const
Convert the fWidth to the same space as its coordinates.
void processGraphicPrimitive2D(const primitive2d::GraphicPrimitive2D &rGraphicPrimitive)
void processMaskPrimitive2D(const primitive2d::MaskPrimitive2D &rMaskCandidate)
void processObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D &rObjectInfoPrimitive2D)
void processStructureTagPrimitive2D(const primitive2d::StructureTagPrimitive2D &rStructureTagCandidate)
void impStartSvtGraphicFill(SvtGraphicFill const *pSvtGraphicFill)
basegfx::B2DPolyPolygon maClipPolyPolygon
the current clipping tools::PolyPolygon from MaskPrimitive2D
void processTextHierarchyBulletPrimitive2D(const primitive2d::TextHierarchyBulletPrimitive2D &rBulletPrimitive)
void processTextSimplePortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D &rTextCandidate)
std::stack< vcl::PDFWriter::StructElement > maListElements
sal_uInt32 mnSvtGraphicStrokeCount
same for SvtGraphicStroke
static vcl::DeleteOnDeinit< css::uno::Reference< css::i18n::XBreakIterator > > mxBreakIterator
void popStructureElement(vcl::PDFWriter::StructElement eElem)
void processTextHierarchyLinePrimitive2D(const primitive2d::TextHierarchyLinePrimitive2D &rLinePrimitive)
std::unique_ptr< SvtGraphicStroke > impTryToCreateSvtGraphicStroke(const basegfx::B2DPolygon &rB2DPolygon, const basegfx::BColor *pColor, const attribute::LineAttribute *pLineAttribute, const attribute::StrokeAttribute *pStrokeAttribute, const attribute::LineStartEndAttribute *pStart, const attribute::LineStartEndAttribute *pEnd)
void processPolyPolygonGradientPrimitive2D(const primitive2d::PolyPolygonGradientPrimitive2D &rGradientCandidate)
void processTextHierarchyParagraphPrimitive2D(const primitive2d::TextHierarchyParagraphPrimitive2D &rParagraphPrimitive)
void processPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D &rStrokePrimitive)
void impConvertFillGradientAttributeToVCLGradient(Gradient &o_rVCLGradient, const attribute::FillGradientAttribute &rFiGrAtt, bool bIsTransparenceGradient) const
void processTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D &rTransparenceCandidate)
void processPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D &rPolygonCandidate)
void processPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D &rHairlinePrimitive)
void processTextHierarchyBlockPrimitive2D(const primitive2d::TextHierarchyBlockPrimitive2D &rBlockPrimitive)
VclMetafileProcessor2D(const geometry::ViewInformation2D &rViewInformation, OutputDevice &rOutDev)
constructor/destructor
void processPolygonStrokeArrowPrimitive2D(const primitive2d::PolygonStrokeArrowPrimitive2D &rStrokeArrowPrimitive)
tools::Rectangle impDumpToMetaFile(const primitive2d::Primitive2DContainer &rContent, GDIMetaFile &o_rContentMetafile)
void RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D &rPointArrayCandidate)
void RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D &rEpsPrimitive2D)
basegfx::BColorModifierStack maBColorModifierStack
void RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D &rPolygonCandidate, bool bPixelBased)
void RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D &rTransformCandidate)
void RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D &rPagePreviewCandidate)
void RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D &rModifiedCandidate)
void RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D &rMarkerArrayCandidate)
void RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D &rTextCandidate)
void RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D &rBitmapCandidate)
constexpr Point TopLeft() const
constexpr Size GetSize() const
std::optional< T > set(Args &&... args)
void SetStructureBoundingBox(const tools::Rectangle &rRect)
sal_Int32 EnsureStructureElement(void const *key)
std::vector< PDFExtOutDevBookmarkEntry > & GetBookmarks()
void SetStructureAnnotIds(::std::vector< sal_Int32 > const &rAnnotIds)
bool GetIsExportTaggedPDF() const
void BeginStructureElement(sal_Int32 id)
void SetAlternateText(const OUString &rText)
void SetStructureAttribute(PDFWriter::StructAttribute eAttr, PDFWriter::StructAttributeValue eVal)
bool GetIsExportFormFields() const
void EndGroup(const Graphic &rGraphic, sal_uInt8 nTransparency, const tools::Rectangle &rOutputRect, const tools::Rectangle &rVisibleOutputRect)
void CreateControl(const PDFWriter::AnyWidget &rControlType)
sal_Int32 WrapBeginStructureElement(PDFWriter::StructElement eType, const OUString &rAlias=OUString())
sal_Int32 CreateLink(const tools::Rectangle &rRect, OUString const &rAltText, sal_Int32 nPageNr=-1)
int nCount
#define TOOLS_WARN_EXCEPTION(area, stream)
URL aURL
#define PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
#define PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
#define PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D
#define PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D
#define PRIMITIVE2D_ID_CONTROLPRIMITIVE2D
#define PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D
#define PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D
#define PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
#define PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
#define PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D
#define PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D
#define PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D
#define PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D
#define PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D
#define PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
#define PRIMITIVE2D_ID_EPSPRIMITIVE2D
#define PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D
#define PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D
#define PRIMITIVE2D_ID_MASKPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
#define PRIMITIVE2D_ID_BITMAPPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
#define PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D
#define PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D
#define PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
#define PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D
float u
SvStream & WriteSvtGraphicFill(SvStream &rOStm, const SvtGraphicFill &rClass)
SvStream & WriteSvtGraphicStroke(SvStream &rOStm, const SvtGraphicStroke &rClass)
double distance
uno_Any a
#define SAL_WARN(area, stream)
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
double getLength(const B2DPolygon &rCandidate)
void applyLineDashing(const B2DPolygon &rCandidate, const std::vector< double > &rDotDashArray, B2DPolyPolygon *pLineTarget, B2DPolyPolygon *pGapTarget, double fDotDashLength)
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
B2DPolyPolygon clipPolyPolygonOnPolyPolygon(const B2DPolyPolygon &rCandidate, const B2DPolyPolygon &rClip, bool bInside, bool bStroke, size_t *pPointLimit)
B2DPolygon getSnippetAbsolute(const B2DPolygon &rCandidate, double fFrom, double fTo, double fLength)
B2DPolyPolygon clipPolygonOnParallelAxis(const B2DPolygon &rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
B2DPolyPolygon createAreaGeometryForLineStartEnd(const B2DPolygon &rCandidate, const B2DPolyPolygon &rArrow, bool bStart, double fWidth, double fCandidateLength, double fDockingPosition, double *pConsumedLength=nullptr, double fShift=0.0)
B2IRange fround(const B2DRange &rRange)
ViewInformation2D createViewInformation2D(const css::uno::Sequence< css::beans::PropertyValue > &rViewParameters)
@ FIELD_TYPE_URL
uses URL as string -> special handling
@ FIELD_TYPE_PAGE
uses "FIELD_SEQ_BEGIN;PageField" -> special handling
BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer &&rSeq, const geometry::ViewInformation2D &rViewInformation2D, sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight, sal_uInt32 nMaxSquarePixels)
Definition: converters.cxx:156
int i
double matrix[MatrixSize]
unsigned char sal_uInt8
HatchStyle
#define MAX_POLYGON_POINT_COUNT_METAFILE
size_t pos