Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "vclmetafileprocessor2d.hxx"
21 : #include <tools/gen.hxx>
22 : #include <vcl/virdev.hxx>
23 : #include <vcl/gdimtf.hxx>
24 : #include <vcl/gradient.hxx>
25 : #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
26 : #include <drawinglayer/primitive2d/textprimitive2d.hxx>
27 : #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
28 : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
29 : #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
30 : #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
31 : #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
32 : #include <basegfx/polygon/b2dpolygonclipper.hxx>
33 : #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 : #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
35 : #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
36 : #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
37 : #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
38 : #include "vclpixelprocessor2d.hxx"
39 : #include <tools/stream.hxx>
40 : #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
41 : #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
42 : #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
43 : #include <vcl/graphictools.hxx>
44 : #include <vcl/metaact.hxx>
45 : #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
46 : #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
47 : #include <comphelper/processfactory.hxx>
48 : #include <rtl/ustring.hxx>
49 : #include <com/sun/star/i18n/BreakIterator.hpp>
50 : #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
51 : #include <com/sun/star/i18n/WordType.hpp>
52 : #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
53 : #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
54 : #include <basegfx/polygon/b2dpolygontools.hxx>
55 : #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
56 : #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
57 : #include <basegfx/polygon/b2dlinegeometry.hxx>
58 : #include <vcl/dibtools.hxx>
59 :
60 : //////////////////////////////////////////////////////////////////////////////
61 : // for PDFExtOutDevData Graphic support
62 :
63 : #include <vcl/graph.hxx>
64 : #include <vcl/svapp.hxx>
65 : #include <toolkit/helper/formpdfexport.hxx>
66 :
67 : //////////////////////////////////////////////////////////////////////////////
68 : // for Control printing
69 :
70 : #include <com/sun/star/beans/XPropertySet.hpp>
71 :
72 : //////////////////////////////////////////////////////////////////////////////
73 : // for StructureTagPrimitive support in sd's unomodel.cxx
74 :
75 : #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
76 :
77 : //////////////////////////////////////////////////////////////////////////////
78 :
79 : using namespace com::sun::star;
80 :
81 : //////////////////////////////////////////////////////////////////////////////
82 : // #112245# definition for maximum allowed point count due to Metafile target.
83 : // To be on the safe side with the old tools polygon, use slightly less then
84 : // the theoretical maximum (bad experiences with tools polygon)
85 :
86 : #define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0)
87 :
88 : //////////////////////////////////////////////////////////////////////////////
89 :
90 : namespace
91 : {
92 : // #112245# helper to split line polygon in half
93 0 : void splitLinePolygon(
94 : const basegfx::B2DPolygon& rBasePolygon,
95 : basegfx::B2DPolygon& o_aLeft,
96 : basegfx::B2DPolygon& o_aRight)
97 : {
98 0 : const sal_uInt32 nCount(rBasePolygon.count());
99 :
100 0 : if(nCount)
101 : {
102 0 : const sal_uInt32 nHalfCount((nCount - 1) >> 1);
103 :
104 0 : o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1);
105 0 : o_aLeft.setClosed(false);
106 :
107 0 : o_aRight = basegfx::B2DPolygon(rBasePolygon, nHalfCount, nCount - nHalfCount);
108 0 : o_aRight.setClosed(false);
109 :
110 0 : if(rBasePolygon.isClosed())
111 : {
112 0 : o_aRight.append(rBasePolygon.getB2DPoint(0));
113 :
114 0 : if(rBasePolygon.areControlPointsUsed())
115 : {
116 : o_aRight.setControlPoints(
117 0 : o_aRight.count() - 1,
118 : rBasePolygon.getPrevControlPoint(0),
119 0 : rBasePolygon.getNextControlPoint(0));
120 : }
121 : }
122 : }
123 : else
124 : {
125 0 : o_aLeft.clear();
126 0 : o_aRight.clear();
127 : }
128 0 : }
129 :
130 : // #112245# helper to evtl. split filled polygons to maximum metafile point count
131 31 : bool fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon)
132 : {
133 31 : bool bRetval(false);
134 31 : const sal_uInt32 nPolyCount(rPolyPolygon.count());
135 :
136 31 : if(nPolyCount)
137 : {
138 31 : basegfx::B2DPolyPolygon aSplitted;
139 :
140 196 : for(sal_uInt32 a(0); a < nPolyCount; a++)
141 : {
142 165 : const basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));
143 165 : const sal_uInt32 nPointCount(aCandidate.count());
144 165 : bool bNeedToSplit(false);
145 :
146 165 : if(aCandidate.areControlPointsUsed())
147 : {
148 : // compare with the maximum for bezier curved polygons
149 16 : bNeedToSplit = nPointCount > ((MAX_POLYGON_POINT_COUNT_METAFILE / 3L) - 1L);
150 : }
151 : else
152 : {
153 : // compare with the maximum for simple point polygons
154 149 : bNeedToSplit = nPointCount > (MAX_POLYGON_POINT_COUNT_METAFILE - 1);
155 : }
156 :
157 165 : if(bNeedToSplit)
158 : {
159 : // need to split the partial polygon
160 0 : const basegfx::B2DRange aRange(aCandidate.getB2DRange());
161 0 : const basegfx::B2DPoint aCenter(aRange.getCenter());
162 :
163 0 : if(aRange.getWidth() > aRange.getHeight())
164 : {
165 : // clip in left and right
166 : const basegfx::B2DPolyPolygon aLeft(
167 : basegfx::tools::clipPolygonOnParallelAxis(
168 : aCandidate,
169 : false,
170 : true,
171 : aCenter.getX(),
172 0 : false));
173 : const basegfx::B2DPolyPolygon aRight(
174 : basegfx::tools::clipPolygonOnParallelAxis(
175 : aCandidate,
176 : false,
177 : false,
178 : aCenter.getX(),
179 0 : false));
180 :
181 0 : aSplitted.append(aLeft);
182 0 : aSplitted.append(aRight);
183 : }
184 : else
185 : {
186 : // clip in top and bottom
187 : const basegfx::B2DPolyPolygon aTop(
188 : basegfx::tools::clipPolygonOnParallelAxis(
189 : aCandidate,
190 : true,
191 : true,
192 : aCenter.getY(),
193 0 : false));
194 : const basegfx::B2DPolyPolygon aBottom(
195 : basegfx::tools::clipPolygonOnParallelAxis(
196 : aCandidate,
197 : true,
198 : false,
199 : aCenter.getY(),
200 0 : false));
201 :
202 0 : aSplitted.append(aTop);
203 0 : aSplitted.append(aBottom);
204 0 : }
205 : }
206 : else
207 : {
208 165 : aSplitted.append(aCandidate);
209 : }
210 165 : }
211 :
212 31 : if(aSplitted.count() != nPolyCount)
213 : {
214 0 : rPolyPolygon = aSplitted;
215 31 : }
216 : }
217 :
218 31 : return bRetval;
219 : }
220 :
221 : /** Filter input polypolygon for effectively empty sub-fills
222 :
223 : Needed to fix fdo#37559
224 :
225 : @param rPoly
226 : PolyPolygon to filter
227 :
228 : @return converted tools PolyPolygon, w/o one-point fills
229 : */
230 0 : PolyPolygon getFillPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly )
231 : {
232 : // filter input rPoly
233 0 : basegfx::B2DPolyPolygon aPoly;
234 0 : sal_uInt32 nCount(rPoly.count());
235 0 : for( sal_uInt32 i=0; i<nCount; ++i )
236 : {
237 0 : basegfx::B2DPolygon aCandidate(rPoly.getB2DPolygon(i));
238 0 : if( !aCandidate.isClosed() || aCandidate.count() > 1 )
239 0 : aPoly.append(aCandidate);
240 0 : }
241 0 : return PolyPolygon(aPoly);
242 : }
243 :
244 : } // end of anonymous namespace
245 :
246 : //////////////////////////////////////////////////////////////////////////////
247 :
248 : namespace drawinglayer
249 : {
250 : namespace processor2d
251 : {
252 0 : Rectangle VclMetafileProcessor2D::impDumpToMetaFile(
253 : const primitive2d::Primitive2DSequence& rContent,
254 : GDIMetaFile& o_rContentMetafile)
255 : {
256 : // Prepare VDev, MetaFile and connections
257 0 : OutputDevice* pLastOutputDevice = mpOutputDevice;
258 0 : GDIMetaFile* pLastMetafile = mpMetaFile;
259 0 : basegfx::B2DRange aPrimitiveRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
260 :
261 : // transform primitive range with current transformation (e.g shadow offset)
262 0 : aPrimitiveRange.transform(maCurrentTransformation);
263 :
264 : const Rectangle aPrimitiveRectangle(
265 : basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()),
266 0 : basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY()));
267 0 : VirtualDevice aContentVDev;
268 0 : MapMode aNewMapMode(pLastOutputDevice->GetMapMode());
269 :
270 0 : mpOutputDevice = &aContentVDev;
271 0 : mpMetaFile = &o_rContentMetafile;
272 0 : aContentVDev.EnableOutput(false);
273 0 : aContentVDev.SetMapMode(pLastOutputDevice->GetMapMode());
274 0 : o_rContentMetafile.Record(&aContentVDev);
275 0 : aContentVDev.SetLineColor(pLastOutputDevice->GetLineColor());
276 0 : aContentVDev.SetFillColor(pLastOutputDevice->GetFillColor());
277 0 : aContentVDev.SetFont(pLastOutputDevice->GetFont());
278 0 : aContentVDev.SetDrawMode(pLastOutputDevice->GetDrawMode());
279 0 : aContentVDev.SetSettings(pLastOutputDevice->GetSettings());
280 0 : aContentVDev.SetRefPoint(pLastOutputDevice->GetRefPoint());
281 :
282 : // dump to MetaFile
283 0 : process(rContent);
284 :
285 : // cleanups
286 0 : o_rContentMetafile.Stop();
287 0 : o_rContentMetafile.WindStart();
288 0 : aNewMapMode.SetOrigin(aPrimitiveRectangle.TopLeft());
289 0 : o_rContentMetafile.SetPrefMapMode(aNewMapMode);
290 0 : o_rContentMetafile.SetPrefSize(aPrimitiveRectangle.GetSize());
291 0 : mpOutputDevice = pLastOutputDevice;
292 0 : mpMetaFile = pLastMetafile;
293 :
294 0 : return aPrimitiveRectangle;
295 : }
296 :
297 0 : void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
298 : Gradient& o_rVCLGradient,
299 : const attribute::FillGradientAttribute& rFiGrAtt,
300 : bool bIsTransparenceGradient)
301 : {
302 0 : if(bIsTransparenceGradient)
303 : {
304 : // it's about transparence channel intensities (black/white), do not use color modifier
305 0 : o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor()));
306 0 : o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor()));
307 : }
308 : else
309 : {
310 : // use color modifier to influence start/end color of gradient
311 0 : o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor())));
312 0 : o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor())));
313 : }
314 :
315 0 : o_rVCLGradient.SetAngle(static_cast< sal_uInt16 >(rFiGrAtt.getAngle() * (1.0 / F_PI1800)));
316 0 : o_rVCLGradient.SetBorder(static_cast< sal_uInt16 >(rFiGrAtt.getBorder() * 100.0));
317 0 : o_rVCLGradient.SetOfsX(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetX() * 100.0));
318 0 : o_rVCLGradient.SetOfsY(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetY() * 100.0));
319 0 : o_rVCLGradient.SetSteps(rFiGrAtt.getSteps());
320 :
321 : // defaults for intensity; those were computed into the start/end colors already
322 0 : o_rVCLGradient.SetStartIntensity(100);
323 0 : o_rVCLGradient.SetEndIntensity(100);
324 :
325 0 : switch(rFiGrAtt.getStyle())
326 : {
327 : default : // attribute::GRADIENTSTYLE_LINEAR :
328 : {
329 0 : o_rVCLGradient.SetStyle(GradientStyle_LINEAR);
330 0 : break;
331 : }
332 : case attribute::GRADIENTSTYLE_AXIAL :
333 : {
334 0 : o_rVCLGradient.SetStyle(GradientStyle_AXIAL);
335 0 : break;
336 : }
337 : case attribute::GRADIENTSTYLE_RADIAL :
338 : {
339 0 : o_rVCLGradient.SetStyle(GradientStyle_RADIAL);
340 0 : break;
341 : }
342 : case attribute::GRADIENTSTYLE_ELLIPTICAL :
343 : {
344 0 : o_rVCLGradient.SetStyle(GradientStyle_ELLIPTICAL);
345 0 : break;
346 : }
347 : case attribute::GRADIENTSTYLE_SQUARE :
348 : {
349 0 : o_rVCLGradient.SetStyle(GradientStyle_SQUARE);
350 0 : break;
351 : }
352 : case attribute::GRADIENTSTYLE_RECT :
353 : {
354 0 : o_rVCLGradient.SetStyle(GradientStyle_RECT);
355 0 : break;
356 : }
357 : }
358 0 : }
359 :
360 0 : void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill)
361 : {
362 0 : if(pSvtGraphicFill && !mnSvtGraphicFillCount)
363 : {
364 0 : SvMemoryStream aMemStm;
365 :
366 0 : aMemStm << *pSvtGraphicFill;
367 0 : mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END)));
368 0 : mnSvtGraphicFillCount++;
369 : }
370 0 : }
371 :
372 0 : void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill)
373 : {
374 0 : if(pSvtGraphicFill && mnSvtGraphicFillCount)
375 : {
376 0 : mnSvtGraphicFillCount--;
377 0 : mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
378 0 : delete pSvtGraphicFill;
379 : }
380 0 : }
381 :
382 153 : double VclMetafileProcessor2D::getTransformedLineWidth( double fWidth ) const
383 : {
384 : // #i113922# the LineWidth is duplicated in the MetaPolylineAction,
385 : // and also inside the SvtGraphicStroke and needs transforming into
386 : // the same space as its co-ordinates here cf. fdo#61789
387 : // This is a partial fix. When a object transformation is used which
388 : // e.g. contains a scaleX != scaleY, an unproportional scaling will happen.
389 153 : const basegfx::B2DVector aDiscreteUnit( maCurrentTransformation * basegfx::B2DVector( fWidth, 0.0 ) );
390 :
391 153 : return aDiscreteUnit.getLength();
392 : }
393 :
394 145 : SvtGraphicStroke* VclMetafileProcessor2D::impTryToCreateSvtGraphicStroke(
395 : const basegfx::B2DPolygon& rB2DPolygon,
396 : const basegfx::BColor* pColor,
397 : const attribute::LineAttribute* pLineAttribute,
398 : const attribute::StrokeAttribute* pStrokeAttribute,
399 : const attribute::LineStartEndAttribute* pStart,
400 : const attribute::LineStartEndAttribute* pEnd)
401 : {
402 145 : SvtGraphicStroke* pRetval = 0;
403 :
404 145 : if(rB2DPolygon.count() && !mnSvtGraphicStrokeCount)
405 : {
406 144 : basegfx::B2DPolygon aLocalPolygon(rB2DPolygon);
407 288 : basegfx::BColor aStrokeColor;
408 288 : basegfx::B2DPolyPolygon aStartArrow;
409 288 : basegfx::B2DPolyPolygon aEndArrow;
410 :
411 144 : if(pColor)
412 : {
413 0 : aStrokeColor = *pColor;
414 : }
415 144 : else if(pLineAttribute)
416 : {
417 144 : aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor());
418 : }
419 :
420 : // It IS needed to record the stroke color at all in the metafile,
421 : // SvtGraphicStroke has NO entry for stroke color(!)
422 144 : mpOutputDevice->SetLineColor(Color(aStrokeColor));
423 :
424 144 : if(!aLocalPolygon.isClosed())
425 : {
426 7 : double fPolyLength(0.0);
427 7 : double fStart(0.0);
428 7 : double fEnd(0.0);
429 :
430 7 : if(pStart && pStart->isActive())
431 : {
432 0 : fPolyLength = basegfx::tools::getLength(aLocalPolygon);
433 :
434 0 : aStartArrow = basegfx::tools::createAreaGeometryForLineStartEnd(
435 0 : aLocalPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(),
436 0 : fPolyLength, pStart->isCentered() ? 0.5 : 0.0, &fStart);
437 : }
438 :
439 7 : if(pEnd && pEnd->isActive())
440 : {
441 1 : if(basegfx::fTools::equalZero(fPolyLength))
442 : {
443 1 : fPolyLength = basegfx::tools::getLength(aLocalPolygon);
444 : }
445 :
446 3 : aEndArrow = basegfx::tools::createAreaGeometryForLineStartEnd(
447 1 : aLocalPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(),
448 2 : fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, &fEnd);
449 : }
450 :
451 7 : if(0.0 != fStart || 0.0 != fEnd)
452 : {
453 : // build new poly, consume something from old poly
454 1 : aLocalPolygon = basegfx::tools::getSnippetAbsolute(aLocalPolygon, fStart, fPolyLength - fEnd, fPolyLength);
455 : }
456 : }
457 :
458 144 : SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone);
459 144 : SvtGraphicStroke::CapType eCap(SvtGraphicStroke::capButt);
460 144 : double fLineWidth(0.0);
461 144 : double fMiterLength(0.0);
462 288 : SvtGraphicStroke::DashArray aDashArray;
463 :
464 144 : if(pLineAttribute)
465 : {
466 144 : fLineWidth = fMiterLength = getTransformedLineWidth( pLineAttribute->getWidth() );
467 :
468 : // get Join
469 144 : switch(pLineAttribute->getLineJoin())
470 : {
471 : default : // basegfx::B2DLINEJOIN_NONE :
472 : {
473 1 : eJoin = SvtGraphicStroke::joinNone;
474 1 : break;
475 : }
476 : case basegfx::B2DLINEJOIN_BEVEL :
477 : {
478 0 : eJoin = SvtGraphicStroke::joinBevel;
479 0 : break;
480 : }
481 : case basegfx::B2DLINEJOIN_MIDDLE :
482 : case basegfx::B2DLINEJOIN_MITER :
483 : {
484 0 : eJoin = SvtGraphicStroke::joinMiter;
485 : // ATM 15 degrees is assumed
486 0 : fMiterLength /= rtl::math::sin(M_PI * (15.0 / 360.0));
487 0 : break;
488 : }
489 : case basegfx::B2DLINEJOIN_ROUND :
490 : {
491 143 : eJoin = SvtGraphicStroke::joinRound;
492 143 : break;
493 : }
494 : }
495 :
496 : // get stroke
497 144 : switch(pLineAttribute->getLineCap())
498 : {
499 : default: /* com::sun::star::drawing::LineCap_BUTT */
500 : {
501 144 : eCap = SvtGraphicStroke::capButt;
502 144 : break;
503 : }
504 : case com::sun::star::drawing::LineCap_ROUND:
505 : {
506 0 : eCap = SvtGraphicStroke::capRound;
507 0 : break;
508 : }
509 : case com::sun::star::drawing::LineCap_SQUARE:
510 : {
511 0 : eCap = SvtGraphicStroke::capSquare;
512 0 : break;
513 : }
514 : }
515 : }
516 :
517 144 : if(pStrokeAttribute)
518 : {
519 : // copy dash array
520 144 : aDashArray = pStrokeAttribute->getDotDashArray();
521 : }
522 :
523 : // #i101734# apply current object transformation to created geometry.
524 : // This is a partial fix. When a object transformation is used which
525 : // e.g. contains a scaleX != scaleY, an unproportional scaling would
526 : // have to be applied to the evtl. existing fat line. The current
527 : // concept of PDF export and SvtGraphicStroke usage does simply not
528 : // allow handling such definitions. The only clean way would be to
529 : // add the transformation to SvtGraphicStroke and to handle it there
530 144 : aLocalPolygon.transform(maCurrentTransformation);
531 144 : aStartArrow.transform(maCurrentTransformation);
532 144 : aEndArrow.transform(maCurrentTransformation);
533 :
534 : pRetval = new SvtGraphicStroke(
535 : Polygon(aLocalPolygon),
536 : PolyPolygon(aStartArrow),
537 : PolyPolygon(aEndArrow),
538 : mfCurrentUnifiedTransparence,
539 : fLineWidth,
540 : eCap,
541 : eJoin,
542 : fMiterLength,
543 288 : aDashArray);
544 : }
545 :
546 145 : return pRetval;
547 : }
548 :
549 145 : void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke)
550 : {
551 145 : if(pSvtGraphicStroke && !mnSvtGraphicStrokeCount)
552 : {
553 144 : SvMemoryStream aMemStm;
554 :
555 144 : aMemStm << *pSvtGraphicStroke;
556 144 : mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END)));
557 144 : mnSvtGraphicStrokeCount++;
558 : }
559 145 : }
560 :
561 145 : void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke)
562 : {
563 145 : if(pSvtGraphicStroke && mnSvtGraphicStrokeCount)
564 : {
565 144 : mnSvtGraphicStrokeCount--;
566 144 : mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
567 144 : delete pSvtGraphicStroke;
568 : }
569 145 : }
570 :
571 : // init static break iterator
572 136 : uno::Reference< ::com::sun::star::i18n::XBreakIterator > VclMetafileProcessor2D::mxBreakIterator;
573 :
574 63 : VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev)
575 : : VclProcessor2D(rViewInformation, rOutDev),
576 63 : mpMetaFile(rOutDev.GetConnectMetaFile()),
577 : mnSvtGraphicFillCount(0),
578 : mnSvtGraphicStrokeCount(0),
579 : mfCurrentUnifiedTransparence(0.0),
580 126 : mpPDFExtOutDevData(dynamic_cast< vcl::PDFExtOutDevData* >(rOutDev.GetExtOutDevData()))
581 : {
582 : OSL_ENSURE(rOutDev.GetConnectMetaFile(), "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)");
583 : // draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation
584 : // but only to ObjectTransformation. Do not change MapMode of destination.
585 63 : maCurrentTransformation = rViewInformation.getObjectTransformation();
586 63 : }
587 :
588 126 : VclMetafileProcessor2D::~VclMetafileProcessor2D()
589 : {
590 : // MapMode was not changed, no restore necessary
591 126 : }
592 :
593 : /***********************************************************************************************
594 :
595 : Support of MetaCommentActions in the VclMetafileProcessor2D
596 : Found MetaCommentActions and how they are supported:
597 :
598 : XGRAD_SEQ_BEGIN, XGRAD_SEQ_END:
599 :
600 : Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action.
601 : It is used in various exporters/importers to have direct access to the gradient before it
602 : is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g.
603 : the Metafile to SdrObject import creates it's gradient objects.
604 : Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
605 : map it back to the corresponding tools PolyPolygon and the Gradient and just call
606 : OutputDevice::DrawGradient which creates the necessary compatible actions.
607 :
608 : XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END:
609 :
610 : Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed
611 : inside GDIMetaFile::Rotate, nothing to take care of here.
612 : The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used
613 : with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not
614 : XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it
615 : to the comment action. A closing end token is created in the destructor.
616 : Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and
617 : SdrRectObj.
618 : The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind
619 : of filled objects, even simple colored polygons. It is added as extra information; the
620 : Metafile actions between the two tokens are interpreted as output generated from those
621 : fills. Thus, users have the choice to use the SvtGraphicFill info or the created output
622 : actions.
623 : Even for XFillTransparenceItem it is used, thus it may need to be supported in
624 : UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon.
625 : Implemented for:
626 : PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D,
627 : PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D,
628 : PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
629 : PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D,
630 : and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence
631 :
632 : XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END:
633 :
634 : Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one
635 : is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the
636 : contained path accordingly.
637 : The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and
638 : only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this
639 : would hinder to make use of PolyPolygon strokes. I will need to add support at:
640 : PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
641 : PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
642 : PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
643 : This can be done hierarchical, too.
644 : Okay, base implementation done based on those three primitives.
645 :
646 : FIELD_SEQ_BEGIN, FIELD_SEQ_END
647 :
648 : Used from slideshow for URLs, created from diverse SvxField implementations inside
649 : createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx
650 : inside ImpEditEngine::Paint.
651 : Created TextHierarchyFieldPrimitive2D and added needed infos there; it is an group primitive and wraps
652 : text primitives (but is not limited to that). It contains the field type if special actions for the
653 : support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is
654 : needed, it may be supported there.
655 : FIELD_SEQ_BEGIN;PageField
656 : FIELD_SEQ_END
657 : Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too.
658 :
659 : XTEXT
660 :
661 : XTEXT_EOC(i) end of character
662 : XTEXT_EOW(i) end of word
663 : XTEXT_EOS(i) end of sentence
664 :
665 : this three are with index and are created with the help of a i18n::XBreakIterator in
666 : ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some
667 : data structure for holding those TEXT infos.
668 : Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text
669 : primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage
670 : that this creations do not need to be done for all paints all the time. This would be
671 : expensive since the BreakIterator and it's usage is expensive and for each paint also the
672 : whole character stops would need to be created.
673 : Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below)
674 :
675 : XTEXT_EOL() end of line
676 : XTEXT_EOP() end of paragraph
677 :
678 : First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well,
679 : i decided to solve it with structure. I added the TextHierarchyPrimitives for this,
680 : namely:
681 : - TextHierarchyLinePrimitive2D: Encapsulates single line
682 : - TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph
683 : - TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM)
684 : Those are now supported in hierarchy. This means the MetaFile renderer will support them
685 : by using them, reculrively using their content and adding MetaFile comments as needed.
686 : This also means that when another text layouter will be used it will be necessary to
687 : create/support the same HierarchyPrimitives to support users.
688 : To transport the information using this hierarchy is best suited to all future needs;
689 : the slideshow will be able to profit from it directly when using primitives; all other
690 : renderers not interested in the text structure will just ignore the encapsulations.
691 :
692 : XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END
693 : Supported now by the TextHierarchyBlockPrimitive2D.
694 :
695 : EPSReplacementGraphic:
696 : Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to
697 : hold the original EPS which was imported in the same MetaFile as first 2 entries. Only
698 : used to export the original again (if exists).
699 : Not necessary to support with MetaFuleRenderer.
700 :
701 : XTEXT_SCROLLRECT, XTEXT_PAINTRECT
702 : Currently used to get extra MetaFile infos using GraphicExporter which again uses
703 : SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since
704 : the rectangle data is added directly by the GraphicsExporter as comment. Does not need
705 : to be adapted at once.
706 : When adapting later, the only user - the diashow - should directly use the provided
707 : Anination infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D)
708 :
709 : PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END
710 : VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as
711 : a fix (hack) while VCL printing. It is needed to not downscale a bitmap which
712 : was explicitly created for the printer already again to some default maximum
713 : bitmap sizes.
714 : Nothing to do here for the primitive renderer.
715 :
716 : Support for vcl::PDFExtOutDevData:
717 : PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at
718 : the OutDev. When set, some extra data is written there. Trying simple PDF export and
719 : watching if i get those infos.
720 : Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses
721 : the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check
722 : if i get a PDFExtOutDevData at the target output device.
723 : Indeed, i get one. Checking what all may be done when that extra-device-info is there.
724 :
725 : All in all i have to talk to SJ. I will need to emulate some of those actions, but
726 : i need to discuss which ones.
727 : In the future, all those infos would be taken from the primitive sequence anyways,
728 : thus these extensions would potentially be temporary, too.
729 : Discussed with SJ, added the necessary support and tested it. Details follow.
730 :
731 : - In ImpEditEngine::Paint, paragraph infos and URL stuff is added.
732 : Added in primitive MetaFile renderer.
733 : Checking URL: Indeed, current version exports it, but it is missing in primitive
734 : CWS version. Adding support.
735 : Okay, URLs work. Checked, Done.
736 :
737 : - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
738 : target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
739 : This may be added in primitive MetaFile renderer.
740 : Adding support...
741 : OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace
742 : svxform. Have to talk to FS if this has to be like that. Especially since
743 : ::vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl.
744 : Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move
745 : that stuff to somewhere else, maybe tools or svtools ?!? We will see...
746 : Moved to toolkit, so i have to link against it. I tried VCL first, but it did
747 : not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other then the name
748 : may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself,
749 : the lowest move,ment plave is toolkit.
750 : Checked form control export, it works well. Done.
751 :
752 : - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
753 : generated. I will need to check what happens here with primitives.
754 : To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed.
755 : Added support, but feature is broken in main version, so i cannot test at all.
756 : Writing a bug to CL (or SJ) and seeing what happens (#i80380#).
757 : SJ took a look and we got it working. Tested VCL MetaFile Renderer based export,
758 : as intended, the original file is exported. Works, Done.
759 :
760 :
761 :
762 :
763 : To be done:
764 :
765 : - Maybe there are more places to take care of for vcl::PDFExtOutDevData!
766 :
767 :
768 :
769 : ****************************************************************************************************/
770 :
771 1866 : void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
772 : {
773 1866 : switch(rCandidate.getPrimitive2DID())
774 : {
775 : case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
776 : {
777 : // directdraw of wrong spell primitive
778 : // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only
779 0 : break;
780 : }
781 : case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D :
782 : {
783 18 : const primitive2d::GraphicPrimitive2D& rGraphicPrimitive = static_cast< const primitive2d::GraphicPrimitive2D& >(rCandidate);
784 18 : bool bUsingPDFExtOutDevData(false);
785 36 : basegfx::B2DVector aTranslate, aScale;
786 : static bool bSuppressPDFExtOutDevDataSupport(false);
787 :
788 18 : if(mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport)
789 : {
790 : // emulate data handling from UnoControlPDFExportContact, original see
791 : // svtools/source/graphic/grfmgr.cxx
792 0 : const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic();
793 :
794 0 : if(rGraphic.IsLink())
795 : {
796 0 : const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
797 :
798 0 : if(!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted())
799 : {
800 0 : const basegfx::B2DHomMatrix& rTransform = rGraphicPrimitive.getTransform();
801 : double fRotate, fShearX;
802 0 : rTransform.decompose(aScale, aTranslate, fRotate, fShearX);
803 :
804 0 : if( basegfx::fTools::equalZero( fRotate ) && ( aScale.getX() > 0.0 ) && ( aScale.getY() > 0.0 ) )
805 : {
806 0 : bUsingPDFExtOutDevData = true;
807 0 : mpPDFExtOutDevData->BeginGroup();
808 : }
809 : }
810 : }
811 : }
812 :
813 : // process recursively and add MetaFile comment
814 18 : process(rGraphicPrimitive.get2DDecomposition(getViewInformation2D()));
815 :
816 18 : if(bUsingPDFExtOutDevData)
817 : {
818 : // emulate data handling from UnoControlPDFExportContact, original see
819 : // svtools/source/graphic/grfmgr.cxx
820 : const basegfx::B2DRange aCurrentRange(
821 : aTranslate.getX(), aTranslate.getY(),
822 0 : aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
823 : const Rectangle aCurrentRect(
824 0 : sal_Int32(floor(aCurrentRange.getMinX())), sal_Int32(floor(aCurrentRange.getMinY())),
825 0 : sal_Int32(ceil(aCurrentRange.getMaxX())), sal_Int32(ceil(aCurrentRange.getMaxY())));
826 0 : const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
827 0 : Rectangle aCropRect;
828 :
829 0 : if(rAttr.IsCropped())
830 : {
831 : // calculate scalings between real image size and logic object size. This
832 : // is necessary since the crop values are relative to original bitmap size
833 0 : double fFactorX(1.0);
834 0 : double fFactorY(1.0);
835 :
836 : {
837 0 : const MapMode aMapMode100thmm(MAP_100TH_MM);
838 0 : const Size aBitmapSize(Application::GetDefaultDevice()->LogicToLogic(
839 0 : rGraphicPrimitive.getGraphicObject().GetPrefSize(),
840 0 : rGraphicPrimitive.getGraphicObject().GetPrefMapMode(), aMapMode100thmm));
841 0 : const double fDivX(aBitmapSize.Width() - rAttr.GetLeftCrop() - rAttr.GetRightCrop());
842 0 : const double fDivY(aBitmapSize.Height() - rAttr.GetTopCrop() - rAttr.GetBottomCrop());
843 :
844 0 : if(!basegfx::fTools::equalZero(fDivX))
845 : {
846 0 : fFactorX = aScale.getX() / fDivX;
847 : }
848 :
849 0 : if(!basegfx::fTools::equalZero(fDivY))
850 : {
851 0 : fFactorY = aScale.getY() / fDivY;
852 0 : }
853 : }
854 :
855 : // calculate crop range and rect
856 0 : basegfx::B2DRange aCropRange;
857 0 : aCropRange.expand(aCurrentRange.getMinimum() - basegfx::B2DPoint(rAttr.GetLeftCrop() * fFactorX, rAttr.GetTopCrop() * fFactorY));
858 0 : aCropRange.expand(aCurrentRange.getMaximum() + basegfx::B2DPoint(rAttr.GetRightCrop() * fFactorX, rAttr.GetBottomCrop() * fFactorY));
859 :
860 : aCropRect = Rectangle(
861 0 : sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())),
862 0 : sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
863 : }
864 :
865 0 : mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(),
866 0 : rAttr.GetTransparency(),
867 : aCurrentRect,
868 0 : aCropRect);
869 : }
870 :
871 36 : break;
872 : }
873 : case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
874 : {
875 1 : const primitive2d::ControlPrimitive2D& rControlPrimitive = static_cast< const primitive2d::ControlPrimitive2D& >(rCandidate);
876 1 : const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl());
877 1 : bool bIsPrintableControl(false);
878 :
879 : // find out if control is printable
880 1 : if(rXControl.is())
881 : {
882 : try
883 : {
884 1 : uno::Reference< beans::XPropertySet > xModelProperties(rXControl->getModel(), uno::UNO_QUERY);
885 1 : uno::Reference< beans::XPropertySetInfo > xPropertyInfo(xModelProperties.is()
886 1 : ? xModelProperties->getPropertySetInfo()
887 3 : : uno::Reference< beans::XPropertySetInfo >());
888 2 : const OUString sPrintablePropertyName("Printable");
889 :
890 1 : if(xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName))
891 : {
892 1 : OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) >>= bIsPrintableControl);
893 1 : }
894 : }
895 0 : catch(const uno::Exception&)
896 : {
897 : OSL_FAIL("VclMetafileProcessor2D: No access to printable flag of Control, caught an exception!");
898 : }
899 : }
900 :
901 : // PDF export and printing only for printable controls
902 1 : if(bIsPrintableControl)
903 : {
904 1 : const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields());
905 1 : bool bDoProcessRecursively(true);
906 :
907 1 : if(bPDFExport)
908 : {
909 : // PDF export. Emulate data handling from UnoControlPDFExportContact
910 : // I have now moved describePDFControl to toolkit, thus i can implement the PDF
911 : // form control support now as follows
912 0 : ::std::auto_ptr< ::vcl::PDFWriter::AnyWidget > pPDFControl;
913 0 : ::toolkitform::describePDFControl( rXControl, pPDFControl, *mpPDFExtOutDevData );
914 :
915 0 : if(pPDFControl.get())
916 : {
917 : // still need to fill in the location (is a class Rectangle)
918 0 : const basegfx::B2DRange aRangeLogic(rControlPrimitive.getB2DRange(getViewInformation2D()));
919 : const Rectangle aRectLogic(
920 0 : (sal_Int32)floor(aRangeLogic.getMinX()), (sal_Int32)floor(aRangeLogic.getMinY()),
921 0 : (sal_Int32)ceil(aRangeLogic.getMaxX()), (sal_Int32)ceil(aRangeLogic.getMaxY()));
922 0 : pPDFControl->Location = aRectLogic;
923 :
924 0 : Size aFontSize(pPDFControl->TextFont.GetSize());
925 0 : aFontSize = mpOutputDevice->LogicToLogic(aFontSize, MapMode(MAP_POINT), mpOutputDevice->GetMapMode());
926 0 : pPDFControl->TextFont.SetSize(aFontSize);
927 :
928 0 : mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
929 0 : mpPDFExtOutDevData->CreateControl(*pPDFControl.get());
930 0 : mpPDFExtOutDevData->EndStructureElement();
931 :
932 : // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
933 : // do not process recursively
934 0 : bDoProcessRecursively = false;
935 : }
936 : else
937 : {
938 : // PDF export did not work, try simple output.
939 : // Fallback to printer output by not setting bDoProcessRecursively
940 : // to false.
941 0 : }
942 : }
943 :
944 : // #i93169# used flag the wrong way; true means that nothing was done yet
945 1 : if(bDoProcessRecursively)
946 : {
947 : // printer output
948 : try
949 : {
950 : // remember old graphics and create new
951 1 : uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY_THROW);
952 2 : const uno::Reference< awt::XGraphics > xOriginalGraphics(xControlView->getGraphics());
953 2 : const uno::Reference< awt::XGraphics > xNewGraphics(mpOutputDevice->CreateUnoGraphics());
954 :
955 1 : if(xNewGraphics.is())
956 : {
957 : // link graphics and view
958 1 : xControlView->setGraphics(xNewGraphics);
959 :
960 : // get position
961 1 : const basegfx::B2DHomMatrix aObjectToDiscrete(getViewInformation2D().getObjectToViewTransformation() * rControlPrimitive.getTransform());
962 2 : const basegfx::B2DPoint aTopLeftDiscrete(aObjectToDiscrete * basegfx::B2DPoint(0.0, 0.0));
963 :
964 : // draw it
965 1 : xControlView->draw(basegfx::fround(aTopLeftDiscrete.getX()), basegfx::fround(aTopLeftDiscrete.getY()));
966 1 : bDoProcessRecursively = false;
967 :
968 : // restore original graphics
969 2 : xControlView->setGraphics(xOriginalGraphics);
970 1 : }
971 : }
972 0 : catch( const uno::Exception& )
973 : {
974 : OSL_FAIL("VclMetafileProcessor2D: Printing of Control failed, caught an exception!");
975 : }
976 : }
977 :
978 : // process recursively if not done yet to export as decomposition (bitmap)
979 1 : if(bDoProcessRecursively)
980 : {
981 0 : process(rControlPrimitive.get2DDecomposition(getViewInformation2D()));
982 : }
983 : }
984 :
985 1 : break;
986 : }
987 : case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D :
988 : {
989 : // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
990 : // thus do the MetafileAction embedding stuff but just handle recursively.
991 4 : const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive = static_cast< const primitive2d::TextHierarchyFieldPrimitive2D& >(rCandidate);
992 4 : const OString aCommentStringCommon(RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_BEGIN"));
993 8 : const OString aCommentStringPage(RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_BEGIN;PageField"));
994 8 : const OString aCommentStringEnd(RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_END"));
995 :
996 4 : switch(rFieldPrimitive.getType())
997 : {
998 : default : // case drawinglayer::primitive2d::FIELD_TYPE_COMMON :
999 : {
1000 4 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon));
1001 4 : break;
1002 : }
1003 : case drawinglayer::primitive2d::FIELD_TYPE_PAGE :
1004 : {
1005 0 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringPage));
1006 0 : break;
1007 : }
1008 : case drawinglayer::primitive2d::FIELD_TYPE_URL :
1009 : {
1010 0 : const OUString& rURL = rFieldPrimitive.getString();
1011 0 : const String aOldString(rURL);
1012 0 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast< const sal_uInt8* >(aOldString.GetBuffer()), 2 * aOldString.Len()));
1013 0 : break;
1014 : }
1015 : }
1016 :
1017 : // process recursively
1018 4 : const primitive2d::Primitive2DSequence rContent = rFieldPrimitive.get2DDecomposition(getViewInformation2D());
1019 4 : process(rContent);
1020 :
1021 : // for the end comment the type is not relevant yet, they are all the same. Just add.
1022 4 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringEnd));
1023 :
1024 4 : if(mpPDFExtOutDevData && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType())
1025 : {
1026 : // emulate data handling from ImpEditEngine::Paint
1027 0 : const basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
1028 : const Rectangle aRectLogic(
1029 0 : (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()),
1030 0 : (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
1031 0 : vcl::PDFExtOutDevBookmarkEntry aBookmark;
1032 0 : aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic);
1033 0 : aBookmark.aBookmark = rFieldPrimitive.getString();
1034 0 : std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
1035 0 : rBookmarks.push_back( aBookmark );
1036 : }
1037 :
1038 8 : break;
1039 : }
1040 : case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D :
1041 : {
1042 21 : const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive = static_cast< const primitive2d::TextHierarchyLinePrimitive2D& >(rCandidate);
1043 21 : const OString aCommentString(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOL"));
1044 :
1045 : // process recursively and add MetaFile comment
1046 21 : process(rLinePrimitive.get2DDecomposition(getViewInformation2D()));
1047 21 : mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1048 :
1049 21 : break;
1050 : }
1051 : case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D :
1052 : {
1053 : // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
1054 : // "XTEXT_EOC" is used, use here, too.
1055 0 : const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive = static_cast< const primitive2d::TextHierarchyBulletPrimitive2D& >(rCandidate);
1056 0 : const OString aCommentString(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOC"));
1057 :
1058 : // process recursively and add MetaFile comment
1059 0 : process(rBulletPrimitive.get2DDecomposition(getViewInformation2D()));
1060 0 : mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1061 :
1062 0 : break;
1063 : }
1064 : case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D :
1065 : {
1066 31 : const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive = static_cast< const primitive2d::TextHierarchyParagraphPrimitive2D& >(rCandidate);
1067 31 : const OString aCommentString(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOP"));
1068 :
1069 31 : if(mpPDFExtOutDevData)
1070 : {
1071 : // emulate data handling from ImpEditEngine::Paint
1072 0 : mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph );
1073 : }
1074 :
1075 : // process recursively and add MetaFile comment
1076 31 : process(rParagraphPrimitive.get2DDecomposition(getViewInformation2D()));
1077 31 : mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1078 :
1079 31 : if(mpPDFExtOutDevData)
1080 : {
1081 : // emulate data handling from ImpEditEngine::Paint
1082 0 : mpPDFExtOutDevData->EndStructureElement();
1083 : }
1084 :
1085 31 : break;
1086 : }
1087 : case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D :
1088 : {
1089 19 : const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive = static_cast< const primitive2d::TextHierarchyBlockPrimitive2D& >(rCandidate);
1090 19 : const OString aCommentStringA(RTL_CONSTASCII_STRINGPARAM("XTEXT_PAINTSHAPE_BEGIN"));
1091 38 : const OString aCommentStringB(RTL_CONSTASCII_STRINGPARAM("XTEXT_PAINTSHAPE_END"));
1092 :
1093 : // add MetaFile comment, process recursively and add MetaFile comment
1094 19 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA));
1095 19 : process(rBlockPrimitive.get2DDecomposition(getViewInformation2D()));
1096 19 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB));
1097 :
1098 38 : break;
1099 : }
1100 : case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
1101 : case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
1102 : {
1103 : // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate
1104 66 : const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate = static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate);
1105 : // const primitive2d::TextDecoratedPortionPrimitive2D* pTextDecoratedCandidate = dynamic_cast< const primitive2d::TextDecoratedPortionPrimitive2D* >(&rCandidate);
1106 :
1107 : // Adapt evtl. used special DrawMode
1108 66 : const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1109 66 : adaptTextToFillDrawMode();
1110 :
1111 : // directdraw of text simple portion; use default processing
1112 66 : RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate);
1113 :
1114 : // restore DrawMode
1115 66 : mpOutputDevice->SetDrawMode(nOriginalDrawMode);
1116 :
1117 : // #i101169# if(pTextDecoratedCandidate)
1118 : {
1119 : // support for TEXT_ MetaFile actions only for decorated texts
1120 66 : if(!mxBreakIterator.is())
1121 : {
1122 3 : uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1123 3 : mxBreakIterator = i18n::BreakIterator::create(xContext);
1124 : }
1125 :
1126 66 : const OUString& rTxt = rTextCandidate.getText();
1127 66 : const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength());
1128 :
1129 66 : if(nTextLength)
1130 : {
1131 66 : const ::com::sun::star::lang::Locale& rLocale = rTextCandidate.getLocale();
1132 66 : const sal_Int32 nTextPosition(rTextCandidate.getTextPosition());
1133 :
1134 : sal_Int32 nDone;
1135 66 : sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
1136 66 : ::com::sun::star::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True));
1137 66 : sal_Int32 nNextSentenceBreak(mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale));
1138 66 : const OString aCommentStringA(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOC"));
1139 132 : const OString aCommentStringB(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOW"));
1140 132 : const OString aCommentStringC(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOS"));
1141 :
1142 317 : for(sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++)
1143 : {
1144 : // create the entries for the respective break positions
1145 251 : if(i == nNextCellBreak)
1146 : {
1147 251 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA, i - nTextPosition));
1148 251 : nNextCellBreak = mxBreakIterator->nextCharacters(rTxt, i, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
1149 : }
1150 251 : if(i == nNextWordBoundary.endPos)
1151 : {
1152 18 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB, i - nTextPosition));
1153 18 : nNextWordBoundary = mxBreakIterator->getWordBoundary(rTxt, i + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True);
1154 : }
1155 251 : if(i == nNextSentenceBreak)
1156 : {
1157 4 : mpMetaFile->AddAction(new MetaCommentAction(aCommentStringC, i - nTextPosition));
1158 4 : nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale);
1159 : }
1160 66 : }
1161 66 : }
1162 : }
1163 :
1164 66 : break;
1165 : }
1166 : case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
1167 : {
1168 1231 : const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive = static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate);
1169 1231 : const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon();
1170 :
1171 1231 : if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1172 : {
1173 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1174 : // per polygon. If there are more, split the polygon in half and call recursively
1175 0 : basegfx::B2DPolygon aLeft, aRight;
1176 0 : splitLinePolygon(rBasePolygon, aLeft, aRight);
1177 0 : const primitive2d::PolygonHairlinePrimitive2D aPLeft(aLeft, rHairlinePrimitive.getBColor());
1178 0 : const primitive2d::PolygonHairlinePrimitive2D aPRight(aRight, rHairlinePrimitive.getBColor());
1179 :
1180 0 : processBasePrimitive2D(aPLeft);
1181 0 : processBasePrimitive2D(aPRight);
1182 : }
1183 : else
1184 : {
1185 : // direct draw of hairline; use default processing
1186 : // support SvtGraphicStroke MetaCommentAction
1187 1231 : const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor()));
1188 1231 : SvtGraphicStroke* pSvtGraphicStroke = 0;
1189 :
1190 : // #i121267# Not needed, does not give better quality compared with
1191 : // the META_POLYPOLYGON_ACTION written by RenderPolygonHairlinePrimitive2D
1192 : // below
1193 1231 : bool bSupportSvtGraphicStroke(false);
1194 :
1195 1231 : if(bSupportSvtGraphicStroke)
1196 : {
1197 : pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1198 0 : rHairlinePrimitive.getB2DPolygon(),
1199 : &aLineColor,
1200 0 : 0, 0, 0, 0);
1201 :
1202 0 : impStartSvtGraphicStroke(pSvtGraphicStroke);
1203 : }
1204 :
1205 1231 : RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate), false);
1206 :
1207 1231 : if(bSupportSvtGraphicStroke)
1208 : {
1209 0 : impEndSvtGraphicStroke(pSvtGraphicStroke);
1210 1231 : }
1211 : }
1212 1231 : break;
1213 : }
1214 : case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
1215 : {
1216 144 : const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive = static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate);
1217 144 : const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon();
1218 :
1219 144 : if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1220 : {
1221 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1222 : // per polygon. If there are more, split the polygon in half and call recursively
1223 0 : basegfx::B2DPolygon aLeft, aRight;
1224 0 : splitLinePolygon(rBasePolygon, aLeft, aRight);
1225 : const primitive2d::PolygonStrokePrimitive2D aPLeft(
1226 0 : aLeft, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute());
1227 : const primitive2d::PolygonStrokePrimitive2D aPRight(
1228 0 : aRight, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute());
1229 :
1230 0 : processBasePrimitive2D(aPLeft);
1231 0 : processBasePrimitive2D(aPRight);
1232 : }
1233 : else
1234 : {
1235 : // support SvtGraphicStroke MetaCommentAction
1236 : SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1237 : rBasePolygon, 0,
1238 144 : &rStrokePrimitive.getLineAttribute(),
1239 144 : &rStrokePrimitive.getStrokeAttribute(),
1240 144 : 0, 0);
1241 :
1242 144 : impStartSvtGraphicStroke(pSvtGraphicStroke);
1243 144 : const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute();
1244 :
1245 : // create MetaPolyLineActions, but without LINE_DASH
1246 144 : if(basegfx::fTools::more(rLine.getWidth(), 0.0))
1247 : {
1248 9 : const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute();
1249 9 : basegfx::B2DPolyPolygon aHairLinePolyPolygon;
1250 :
1251 9 : if(0.0 == rStroke.getFullDotDashLen())
1252 : {
1253 9 : aHairLinePolyPolygon.append(rBasePolygon);
1254 : }
1255 : else
1256 : {
1257 : basegfx::tools::applyLineDashing(
1258 0 : rBasePolygon, rStroke.getDotDashArray(),
1259 0 : &aHairLinePolyPolygon, 0, rStroke.getFullDotDashLen());
1260 : }
1261 :
1262 18 : const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLine.getColor()));
1263 9 : mpOutputDevice->SetLineColor(Color(aHairlineColor));
1264 9 : mpOutputDevice->SetFillColor();
1265 9 : aHairLinePolyPolygon.transform(maCurrentTransformation);
1266 :
1267 : // use the transformed line width
1268 18 : LineInfo aLineInfo(LINE_SOLID, basegfx::fround(getTransformedLineWidth(rLine.getWidth())));
1269 9 : aLineInfo.SetLineJoin(rLine.getLineJoin());
1270 9 : aLineInfo.SetLineCap(rLine.getLineCap());
1271 :
1272 18 : for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
1273 : {
1274 9 : const basegfx::B2DPolygon aCandidate(aHairLinePolyPolygon.getB2DPolygon(a));
1275 :
1276 9 : if(aCandidate.count() > 1)
1277 : {
1278 9 : const Polygon aToolsPolygon(aCandidate);
1279 :
1280 9 : mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo));
1281 : }
1282 18 : }
1283 : }
1284 : else
1285 : {
1286 135 : process(rCandidate.get2DDecomposition(getViewInformation2D()));
1287 : }
1288 :
1289 144 : impEndSvtGraphicStroke(pSvtGraphicStroke);
1290 : }
1291 :
1292 144 : break;
1293 : }
1294 : case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D :
1295 : {
1296 1 : const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive = static_cast< const primitive2d::PolygonStrokeArrowPrimitive2D& >(rCandidate);
1297 1 : const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon();
1298 :
1299 1 : if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1300 : {
1301 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1302 : // per polygon. If there are more, split the polygon in half and call recursively
1303 0 : basegfx::B2DPolygon aLeft, aRight;
1304 0 : splitLinePolygon(rBasePolygon, aLeft, aRight);
1305 0 : const attribute::LineStartEndAttribute aEmpty;
1306 : const primitive2d::PolygonStrokeArrowPrimitive2D aPLeft(
1307 : aLeft,
1308 0 : rStrokeArrowPrimitive.getLineAttribute(),
1309 0 : rStrokeArrowPrimitive.getStrokeAttribute(),
1310 0 : rStrokeArrowPrimitive.getStart(),
1311 0 : aEmpty);
1312 : const primitive2d::PolygonStrokeArrowPrimitive2D aPRight(
1313 : aRight,
1314 0 : rStrokeArrowPrimitive.getLineAttribute(),
1315 0 : rStrokeArrowPrimitive.getStrokeAttribute(),
1316 : aEmpty,
1317 0 : rStrokeArrowPrimitive.getEnd());
1318 :
1319 0 : processBasePrimitive2D(aPLeft);
1320 0 : processBasePrimitive2D(aPRight);
1321 : }
1322 : else
1323 : {
1324 : // support SvtGraphicStroke MetaCommentAction
1325 : SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1326 : rBasePolygon, 0,
1327 1 : &rStrokeArrowPrimitive.getLineAttribute(),
1328 1 : &rStrokeArrowPrimitive.getStrokeAttribute(),
1329 1 : &rStrokeArrowPrimitive.getStart(),
1330 2 : &rStrokeArrowPrimitive.getEnd());
1331 :
1332 : // write LineGeometry start marker
1333 1 : impStartSvtGraphicStroke(pSvtGraphicStroke);
1334 :
1335 : // #i116162# When B&W is set as DrawMode, DRAWMODE_WHITEFILL is used
1336 : // to let all fills be just white; for lines DRAWMODE_BLACKLINE is used
1337 : // so all line geometry is supposed to get black. Since in the in-between
1338 : // stages of line geometry drawing filled polygons are used (e.g. line
1339 : // start/ends) it is necessary to change these drawmodes to preserve
1340 : // that lines shall be black; thus change DRAWMODE_WHITEFILL to
1341 : // DRAWMODE_BLACKFILL during line geometry processing to have line geometry
1342 : // parts filled black.
1343 1 : const sal_uLong nOldDrawMode(mpOutputDevice->GetDrawMode());
1344 1 : const bool bDrawmodeChange(nOldDrawMode & DRAWMODE_WHITEFILL && mnSvtGraphicStrokeCount);
1345 :
1346 1 : if(bDrawmodeChange)
1347 : {
1348 0 : mpOutputDevice->SetDrawMode((nOldDrawMode & ~DRAWMODE_WHITEFILL) | DRAWMODE_BLACKFILL);
1349 : }
1350 :
1351 : // process sub-line geometry (evtl. filled PolyPolygons)
1352 1 : process(rCandidate.get2DDecomposition(getViewInformation2D()));
1353 :
1354 1 : if(bDrawmodeChange)
1355 : {
1356 0 : mpOutputDevice->SetDrawMode(nOldDrawMode);
1357 : }
1358 :
1359 : // write LineGeometry end marker
1360 1 : impEndSvtGraphicStroke(pSvtGraphicStroke);
1361 : }
1362 :
1363 1 : break;
1364 : }
1365 : case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
1366 : {
1367 : // direct draw of transformed BitmapEx primitive; use default processing, but without
1368 : // former testing if graphic content is inside discrete local viewport; this is not
1369 : // setup for metafile targets (metafile renderer tries to render in logic coordinates,
1370 : // the mapping is kept to the OutputDevice for better Metafile recording)
1371 15 : RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
1372 15 : break;
1373 : }
1374 : case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D :
1375 : {
1376 : // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1377 0 : const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate = static_cast< const primitive2d::PolyPolygonGraphicPrimitive2D& >(rCandidate);
1378 0 : basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon());
1379 :
1380 0 : if(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1381 : {
1382 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1383 : // per polygon. If there are more use the splitted polygon and call recursively
1384 : const primitive2d::PolyPolygonGraphicPrimitive2D aSplitted(
1385 : aLocalPolyPolygon,
1386 0 : rBitmapCandidate.getFillGraphic());
1387 :
1388 0 : processBasePrimitive2D(aSplitted);
1389 : }
1390 : else
1391 : {
1392 0 : SvtGraphicFill* pSvtGraphicFill = 0;
1393 :
1394 0 : if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1395 : {
1396 : // #121194# Changed implementation and checked usages fo convert to metafile,
1397 : // presentation start (uses SvtGraphicFill) and printing.
1398 :
1399 : // calculate transformation. Get real object size, all values in FillGraphicAttribute
1400 : // are relative to the unified object
1401 0 : aLocalPolyPolygon.transform(maCurrentTransformation);
1402 0 : const basegfx::B2DVector aOutlineSize(aLocalPolyPolygon.getB2DRange().getRange());
1403 :
1404 : // the scaling needs scale from pixel to logic coordinate system
1405 0 : const attribute::FillGraphicAttribute& rFillGraphicAttribute = rBitmapCandidate.getFillGraphic();
1406 0 : const Size aBmpSizePixel(rFillGraphicAttribute.getGraphic().GetSizePixel());
1407 :
1408 : // setup transformation like in impgrfll. Multiply with aOutlineSize
1409 : // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange()
1410 : // to object coordinates with object's top left being at (0,0). Divide
1411 : // by pixel size so that scale from pixel to logic will work in SvtGraphicFill.
1412 : const basegfx::B2DVector aTransformScale(
1413 0 : rFillGraphicAttribute.getGraphicRange().getRange() /
1414 : basegfx::B2DVector(
1415 0 : std::max(1.0, double(aBmpSizePixel.Width())),
1416 0 : std::max(1.0, double(aBmpSizePixel.Height()))) *
1417 0 : aOutlineSize);
1418 : const basegfx::B2DPoint aTransformPosition(
1419 0 : rFillGraphicAttribute.getGraphicRange().getMinimum() * aOutlineSize);
1420 :
1421 : // setup transformation like in impgrfll
1422 0 : SvtGraphicFill::Transform aTransform;
1423 :
1424 : // scale values are divided by bitmap pixel sizes
1425 0 : aTransform.matrix[0] = aTransformScale.getX();
1426 0 : aTransform.matrix[4] = aTransformScale.getY();
1427 :
1428 : // translates are absolute
1429 0 : aTransform.matrix[2] = aTransformPosition.getX();
1430 0 : aTransform.matrix[5] = aTransformPosition.getY();
1431 :
1432 : pSvtGraphicFill = new SvtGraphicFill(
1433 : getFillPolyPolygon(aLocalPolyPolygon),
1434 : Color(),
1435 : 0.0,
1436 : SvtGraphicFill::fillEvenOdd,
1437 : SvtGraphicFill::fillTexture,
1438 : aTransform,
1439 0 : rFillGraphicAttribute.getTiling(),
1440 : SvtGraphicFill::hatchSingle,
1441 : Color(),
1442 : SvtGraphicFill::gradientLinear,
1443 : Color(),
1444 : Color(),
1445 : 0,
1446 0 : rFillGraphicAttribute.getGraphic());
1447 : }
1448 :
1449 : // Do use decomposition; encapsulate with SvtGraphicFill
1450 0 : impStartSvtGraphicFill(pSvtGraphicFill);
1451 0 : process(rCandidate.get2DDecomposition(getViewInformation2D()));
1452 0 : impEndSvtGraphicFill(pSvtGraphicFill);
1453 : }
1454 :
1455 0 : break;
1456 : }
1457 : case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D :
1458 : {
1459 : // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1460 0 : const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate = static_cast< const primitive2d::PolyPolygonHatchPrimitive2D& >(rCandidate);
1461 0 : const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch();
1462 0 : basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon());
1463 :
1464 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1465 : // per polygon. Split polygon until there are less than that
1466 0 : while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1467 : ;
1468 :
1469 0 : if(rFillHatchAttribute.isFillBackground())
1470 : {
1471 : // with fixing #i111954# (see below) the possible background
1472 : // fill of a hatched object was lost.Generate a background fill
1473 : // primitive and render it
1474 : const primitive2d::Primitive2DReference xBackground(
1475 : new primitive2d::PolyPolygonColorPrimitive2D(
1476 : aLocalPolyPolygon,
1477 0 : rHatchCandidate.getBackgroundColor()));
1478 :
1479 0 : process(primitive2d::Primitive2DSequence(&xBackground, 1));
1480 : }
1481 :
1482 0 : SvtGraphicFill* pSvtGraphicFill = 0;
1483 0 : aLocalPolyPolygon.transform(maCurrentTransformation);
1484 :
1485 0 : if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1486 : {
1487 : // re-create a VCL hatch as base data
1488 0 : SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle);
1489 :
1490 0 : switch(rFillHatchAttribute.getStyle())
1491 : {
1492 : default: // attribute::HATCHSTYLE_SINGLE :
1493 : {
1494 0 : eHatch = SvtGraphicFill::hatchSingle;
1495 0 : break;
1496 : }
1497 : case attribute::HATCHSTYLE_DOUBLE :
1498 : {
1499 0 : eHatch = SvtGraphicFill::hatchDouble;
1500 0 : break;
1501 : }
1502 : case attribute::HATCHSTYLE_TRIPLE :
1503 : {
1504 0 : eHatch = SvtGraphicFill::hatchTriple;
1505 0 : break;
1506 : }
1507 : }
1508 :
1509 0 : SvtGraphicFill::Transform aTransform;
1510 :
1511 : // scale
1512 0 : aTransform.matrix[0] *= rFillHatchAttribute.getDistance();
1513 0 : aTransform.matrix[4] *= rFillHatchAttribute.getDistance();
1514 :
1515 : // rotate (was never correct in impgrfll anyways, use correct angle now)
1516 0 : aTransform.matrix[0] *= cos(rFillHatchAttribute.getAngle());
1517 0 : aTransform.matrix[1] *= -sin(rFillHatchAttribute.getAngle());
1518 0 : aTransform.matrix[3] *= sin(rFillHatchAttribute.getAngle());
1519 0 : aTransform.matrix[4] *= cos(rFillHatchAttribute.getAngle());
1520 :
1521 : pSvtGraphicFill = new SvtGraphicFill(
1522 : getFillPolyPolygon(aLocalPolyPolygon),
1523 : Color(),
1524 : 0.0,
1525 : SvtGraphicFill::fillEvenOdd,
1526 : SvtGraphicFill::fillHatch,
1527 : aTransform,
1528 : false,
1529 : eHatch,
1530 0 : Color(rFillHatchAttribute.getColor()),
1531 : SvtGraphicFill::gradientLinear,
1532 : Color(),
1533 : Color(),
1534 : 0,
1535 0 : Graphic());
1536 : }
1537 :
1538 : // Do use decomposition; encapsulate with SvtGraphicFill
1539 0 : impStartSvtGraphicFill(pSvtGraphicFill);
1540 :
1541 : // #i111954# do NOT use decomposition, but use direct VCL-command
1542 : // process(rCandidate.get2DDecomposition(getViewInformation2D()));
1543 0 : const PolyPolygon aToolsPolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon));
1544 : const HatchStyle aHatchStyle(
1545 0 : attribute::HATCHSTYLE_SINGLE == rFillHatchAttribute.getStyle() ? HATCH_SINGLE :
1546 0 : attribute::HATCHSTYLE_DOUBLE == rFillHatchAttribute.getStyle() ? HATCH_DOUBLE :
1547 0 : HATCH_TRIPLE);
1548 :
1549 : mpOutputDevice->DrawHatch(aToolsPolyPolygon,
1550 : Hatch(aHatchStyle,
1551 0 : Color(rFillHatchAttribute.getColor()),
1552 : basegfx::fround(rFillHatchAttribute.getDistance()),
1553 0 : basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800)));
1554 :
1555 0 : impEndSvtGraphicFill(pSvtGraphicFill);
1556 :
1557 0 : break;
1558 : }
1559 : case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D :
1560 : {
1561 0 : basegfx::B2DVector aScale, aTranslate;
1562 : double fRotate, fShearX;
1563 :
1564 0 : maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
1565 :
1566 0 : if(!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX))
1567 : {
1568 : // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
1569 : // This is because VCL Gradient mechanism does *not* support to rotate the gradient
1570 : // with objects and this case is not expressable in a Metafile (and cannot be added
1571 : // since the FileFormats used, e.g. *.wmf, do not support it either).
1572 : // Such cases happen when a graphic object uses a Metafile as graphic information or
1573 : // a fill style definition uses a Metafile. In this cases the graphic content is
1574 : // rotated with the graphic or filled object; this is not supported by the target
1575 : // format of this conversion renderer - Metafiles.
1576 : // To solve this, not a Gradient is written, but the decomposition of this object
1577 : // is written to the Metafile. This is the PolyPolygons building the gradient fill.
1578 : // These will need more space and time, but the result will be as if the Gradient
1579 : // was rotated with the object.
1580 : // This mechanism is used by all exporters still not using Primtives (e.g. Print,
1581 : // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
1582 : // transfers. One more reason to *change* these to primitives.
1583 : // BTW: One more example how useful the principles of primitives are; the decomposition
1584 : // is by definition a simpler, maybe more expensive representation of the same content.
1585 0 : process(rCandidate.get2DDecomposition(getViewInformation2D()));
1586 : }
1587 : else
1588 : {
1589 0 : const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate = static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate);
1590 0 : basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon());
1591 :
1592 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1593 : // per polygon. Split polygon until there are less than that
1594 0 : while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1595 : ;
1596 :
1597 : // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END
1598 : // it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
1599 : // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
1600 0 : Gradient aVCLGradient;
1601 0 : impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(), false);
1602 0 : aLocalPolyPolygon.transform(maCurrentTransformation);
1603 :
1604 : // #i82145# ATM VCL printing of gradients using curved shapes does not work,
1605 : // i submitted the bug with the given ID to THB. When that task is fixed it is
1606 : // necessary to again remove this subdivision since it decreases possible
1607 : // printing quality (not even resolution-dependent for now). THB will tell
1608 : // me when that task is fixed in the master
1609 : const PolyPolygon aToolsPolyPolygon(
1610 : getFillPolyPolygon(
1611 0 : basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon)));
1612 :
1613 :
1614 : // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1615 0 : SvtGraphicFill* pSvtGraphicFill = 0;
1616 :
1617 0 : if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1618 : {
1619 : // setup gradient stuff like in like in impgrfll
1620 0 : SvtGraphicFill::GradientType eGrad(SvtGraphicFill::gradientLinear);
1621 :
1622 0 : switch(aVCLGradient.GetStyle())
1623 : {
1624 : default : // GradientStyle_LINEAR:
1625 : case GradientStyle_AXIAL:
1626 0 : eGrad = SvtGraphicFill::gradientLinear;
1627 0 : break;
1628 : case GradientStyle_RADIAL:
1629 : case GradientStyle_ELLIPTICAL:
1630 0 : eGrad = SvtGraphicFill::gradientRadial;
1631 0 : break;
1632 : case GradientStyle_SQUARE:
1633 : case GradientStyle_RECT:
1634 0 : eGrad = SvtGraphicFill::gradientRectangular;
1635 0 : break;
1636 : }
1637 :
1638 : pSvtGraphicFill = new SvtGraphicFill(
1639 : aToolsPolyPolygon,
1640 : Color(),
1641 : 0.0,
1642 : SvtGraphicFill::fillEvenOdd,
1643 : SvtGraphicFill::fillGradient,
1644 : SvtGraphicFill::Transform(),
1645 : false,
1646 : SvtGraphicFill::hatchSingle,
1647 : Color(),
1648 : eGrad,
1649 0 : aVCLGradient.GetStartColor(),
1650 0 : aVCLGradient.GetEndColor(),
1651 0 : aVCLGradient.GetSteps(),
1652 0 : Graphic());
1653 : }
1654 :
1655 : // call VCL directly; encapsulate with SvtGraphicFill
1656 0 : impStartSvtGraphicFill(pSvtGraphicFill);
1657 0 : mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
1658 0 : impEndSvtGraphicFill(pSvtGraphicFill);
1659 : }
1660 :
1661 0 : break;
1662 : }
1663 : case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
1664 : {
1665 28 : const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
1666 28 : basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
1667 :
1668 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1669 : // per polygon. Split polygon until there are less than that
1670 28 : while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1671 : ;
1672 :
1673 56 : const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
1674 28 : aLocalPolyPolygon.transform(maCurrentTransformation);
1675 :
1676 : // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1677 28 : SvtGraphicFill* pSvtGraphicFill = 0;
1678 :
1679 : // #i121267# Not needed, does not give better quality compared with
1680 : // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
1681 : // below
1682 28 : bool bSupportSvtGraphicFill(false);
1683 :
1684 28 : if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1685 : {
1686 : // setup simple color fill stuff like in impgrfll
1687 : pSvtGraphicFill = new SvtGraphicFill(
1688 : getFillPolyPolygon(aLocalPolyPolygon),
1689 : Color(aPolygonColor),
1690 : 0.0,
1691 : SvtGraphicFill::fillEvenOdd,
1692 : SvtGraphicFill::fillSolid,
1693 : SvtGraphicFill::Transform(),
1694 : false,
1695 : SvtGraphicFill::hatchSingle,
1696 : Color(),
1697 : SvtGraphicFill::gradientLinear,
1698 : Color(),
1699 : Color(),
1700 : 0,
1701 0 : Graphic());
1702 : }
1703 :
1704 : // set line and fill color
1705 28 : mpOutputDevice->SetFillColor(Color(aPolygonColor));
1706 28 : mpOutputDevice->SetLineColor();
1707 :
1708 : // call VCL directly; encapsulate with SvtGraphicFill
1709 28 : if(bSupportSvtGraphicFill)
1710 : {
1711 0 : impStartSvtGraphicFill(pSvtGraphicFill);
1712 : }
1713 :
1714 28 : mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
1715 :
1716 28 : if(bSupportSvtGraphicFill)
1717 : {
1718 0 : impEndSvtGraphicFill(pSvtGraphicFill);
1719 : }
1720 :
1721 56 : break;
1722 : }
1723 : case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
1724 : {
1725 : static bool bUseMetaFilePrimitiveDecomposition(true);
1726 4 : const primitive2d::MetafilePrimitive2D& aMetafile = static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate);
1727 :
1728 4 : if(bUseMetaFilePrimitiveDecomposition && !aMetafile.getMetaFile().GetUseCanvas())
1729 : {
1730 : // Use new Metafile decomposition.
1731 : // TODO EMF+ stuffed into METACOMMENT support required
1732 4 : process(rCandidate.get2DDecomposition(getViewInformation2D()));
1733 : }
1734 : else
1735 : {
1736 : // direct draw of MetaFile, use default processing
1737 0 : RenderMetafilePrimitive2D(aMetafile);
1738 : }
1739 :
1740 4 : break;
1741 : }
1742 : case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
1743 : {
1744 : // mask group. Special handling for MetaFiles.
1745 2 : const primitive2d::MaskPrimitive2D& rMaskCandidate = static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate);
1746 :
1747 2 : if(rMaskCandidate.getChildren().hasElements())
1748 : {
1749 2 : basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
1750 :
1751 2 : if(aMask.count())
1752 : {
1753 : // prepare new mask polygon and rescue current one
1754 2 : aMask.transform(maCurrentTransformation);
1755 2 : const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon);
1756 :
1757 2 : if(maClipPolyPolygon.count())
1758 : {
1759 : // there is already a clip polygon set; build clipped union of
1760 : // current mask polygon and new one
1761 0 : maClipPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1762 : aMask,
1763 : maClipPolyPolygon,
1764 : true, // #i106516# we want the inside of aMask, not the outside
1765 0 : false);
1766 : }
1767 : else
1768 : {
1769 : // use mask directly
1770 2 : maClipPolyPolygon = aMask;
1771 : }
1772 :
1773 2 : if(maClipPolyPolygon.count())
1774 : {
1775 : // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!)
1776 : // Removed subdivision and fixed in Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
1777 : // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there
1778 2 : mpOutputDevice->Push(PUSH_CLIPREGION);
1779 2 : mpOutputDevice->SetClipRegion(Region(maClipPolyPolygon));
1780 :
1781 : // recursively paint content
1782 : // #i121267# Only need to process sub-content when clip polygon is *not* empty.
1783 : // If it is empty, the clip is empty and there can be nothing inside.
1784 2 : process(rMaskCandidate.getChildren());
1785 :
1786 : // restore VCL clip region
1787 2 : mpOutputDevice->Pop();
1788 : }
1789 :
1790 : // restore to rescued clip polygon
1791 2 : maClipPolyPolygon = aLastClipPolyPolygon;
1792 : }
1793 : else
1794 : {
1795 : // no mask, no clipping. recursively paint content
1796 0 : process(rMaskCandidate.getChildren());
1797 2 : }
1798 : }
1799 :
1800 2 : break;
1801 : }
1802 : case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D :
1803 : {
1804 : // modified color group. Force output to unified color. Use default pocessing.
1805 0 : RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate));
1806 0 : break;
1807 : }
1808 : case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D :
1809 : {
1810 : // for metafile: Need to examine what the pure vcl version is doing here actually
1811 : // - uses DrawTransparent with metafile for content and a gradient
1812 : // - uses DrawTransparent for single PolyPoylgons directly. Can be detected by
1813 : // checking the content for single PolyPolygonColorPrimitive2D
1814 3 : const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate = static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate);
1815 3 : const primitive2d::Primitive2DSequence rContent = rUniTransparenceCandidate.getChildren();
1816 :
1817 3 : if(rContent.hasElements())
1818 : {
1819 3 : if(0.0 == rUniTransparenceCandidate.getTransparence())
1820 : {
1821 : // not transparent at all, use content
1822 0 : process(rUniTransparenceCandidate.getChildren());
1823 : }
1824 3 : else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0)
1825 : {
1826 : // try to identify a single PolyPolygonColorPrimitive2D in the
1827 : // content part of the transparence primitive
1828 3 : const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = 0;
1829 : static bool bForceToMetafile(false);
1830 :
1831 3 : if(!bForceToMetafile && 1 == rContent.getLength())
1832 : {
1833 3 : const primitive2d::Primitive2DReference xReference(rContent[0]);
1834 3 : pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get());
1835 : }
1836 :
1837 : // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and
1838 : // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D.
1839 : // Check also for correct ID to exclude derived implementations
1840 3 : if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID())
1841 : {
1842 : // single transparent PolyPolygon identified, use directly
1843 3 : const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor()));
1844 6 : basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon());
1845 :
1846 : // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1847 : // per polygon. Split polygon until there are less than that
1848 3 : while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1849 : ;
1850 :
1851 : // now transform
1852 3 : aLocalPolyPolygon.transform(maCurrentTransformation);
1853 :
1854 : // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1855 3 : SvtGraphicFill* pSvtGraphicFill = 0;
1856 :
1857 : // #i121267# Not needed, does not give better quality compared with
1858 : // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
1859 : // below
1860 3 : bool bSupportSvtGraphicFill(false);
1861 :
1862 3 : if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1863 : {
1864 : // setup simple color with transparence fill stuff like in impgrfll
1865 : pSvtGraphicFill = new SvtGraphicFill(
1866 : getFillPolyPolygon(aLocalPolyPolygon),
1867 : Color(aPolygonColor),
1868 : rUniTransparenceCandidate.getTransparence(),
1869 : SvtGraphicFill::fillEvenOdd,
1870 : SvtGraphicFill::fillSolid,
1871 : SvtGraphicFill::Transform(),
1872 : false,
1873 : SvtGraphicFill::hatchSingle,
1874 : Color(),
1875 : SvtGraphicFill::gradientLinear,
1876 : Color(),
1877 : Color(),
1878 : 0,
1879 0 : Graphic());
1880 : }
1881 :
1882 : // set line and fill color
1883 3 : const sal_uInt16 nTransPercentVcl((sal_uInt16)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0));
1884 3 : mpOutputDevice->SetFillColor(Color(aPolygonColor));
1885 3 : mpOutputDevice->SetLineColor();
1886 :
1887 : // call VCL directly; encapsulate with SvtGraphicFill
1888 3 : if(bSupportSvtGraphicFill)
1889 : {
1890 0 : impStartSvtGraphicFill(pSvtGraphicFill);
1891 : }
1892 :
1893 : mpOutputDevice->DrawTransparent(
1894 : PolyPolygon(aLocalPolyPolygon),
1895 3 : nTransPercentVcl);
1896 :
1897 3 : if(bSupportSvtGraphicFill)
1898 : {
1899 0 : impEndSvtGraphicFill(pSvtGraphicFill);
1900 3 : }
1901 : }
1902 : else
1903 : {
1904 : // svae old mfCurrentUnifiedTransparence and set new one
1905 : // so that contained SvtGraphicStroke may use the current one
1906 0 : const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence);
1907 : // #i105377# paint the content metafile opaque as the transparency gets
1908 : // split of into the gradient below
1909 : // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence();
1910 0 : mfCurrentUnifiedTransparence = 0;
1911 :
1912 : // various content, create content-metafile
1913 0 : GDIMetaFile aContentMetafile;
1914 0 : const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
1915 :
1916 : // restore mfCurrentUnifiedTransparence; it may have been used
1917 : // while processing the sub-content in impDumpToMetaFile
1918 0 : mfCurrentUnifiedTransparence = fLastCurrentUnifiedTransparence;
1919 :
1920 : // create uniform VCL gradient for uniform transparency
1921 0 : Gradient aVCLGradient;
1922 0 : const sal_uInt8 nTransPercentVcl((sal_uInt8)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0));
1923 0 : const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
1924 :
1925 0 : aVCLGradient.SetStyle(GradientStyle_LINEAR);
1926 0 : aVCLGradient.SetStartColor(aTransColor);
1927 0 : aVCLGradient.SetEndColor(aTransColor);
1928 0 : aVCLGradient.SetAngle(0);
1929 0 : aVCLGradient.SetBorder(0);
1930 0 : aVCLGradient.SetOfsX(0);
1931 0 : aVCLGradient.SetOfsY(0);
1932 0 : aVCLGradient.SetStartIntensity(100);
1933 0 : aVCLGradient.SetEndIntensity(100);
1934 0 : aVCLGradient.SetSteps(2);
1935 :
1936 : // render it to VCL
1937 : mpOutputDevice->DrawTransparent(
1938 : aContentMetafile, aPrimitiveRectangle.TopLeft(),
1939 0 : aPrimitiveRectangle.GetSize(), aVCLGradient);
1940 : }
1941 : }
1942 : }
1943 :
1944 3 : break;
1945 : }
1946 : case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
1947 : {
1948 : // for metafile: Need to examine what the pure vcl version is doing here actually
1949 : // - uses DrawTransparent with metafile for content and a gradient
1950 : // i can detect this here with checking the gradient part for a single
1951 : // FillGradientPrimitive2D and reconstruct the gradient.
1952 : // If that detection goes wrong, i have to create an transparence-blended bitmap. Eventually
1953 : // do that in stripes, else RenderTransparencePrimitive2D may just be used
1954 0 : const primitive2d::TransparencePrimitive2D& rTransparenceCandidate = static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate);
1955 0 : const primitive2d::Primitive2DSequence rContent = rTransparenceCandidate.getChildren();
1956 0 : const primitive2d::Primitive2DSequence rTransparence = rTransparenceCandidate.getTransparence();
1957 :
1958 0 : if(rContent.hasElements() && rTransparence.hasElements())
1959 : {
1960 : // try to identify a single FillGradientPrimitive2D in the
1961 : // transparence part of the primitive
1962 0 : const primitive2d::FillGradientPrimitive2D* pFiGradient = 0;
1963 : static bool bForceToBigTransparentVDev(false);
1964 :
1965 0 : if(!bForceToBigTransparentVDev && 1 == rTransparence.getLength())
1966 : {
1967 0 : const primitive2d::Primitive2DReference xReference(rTransparence[0]);
1968 0 : pFiGradient = dynamic_cast< const primitive2d::FillGradientPrimitive2D* >(xReference.get());
1969 : }
1970 :
1971 : // Check also for correct ID to exclude derived implementations
1972 0 : if(pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID())
1973 : {
1974 : // various content, create content-metafile
1975 0 : GDIMetaFile aContentMetafile;
1976 0 : const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
1977 :
1978 : // re-create a VCL-gradient from FillGradientPrimitive2D
1979 0 : Gradient aVCLGradient;
1980 0 : impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(), true);
1981 :
1982 : // render it to VCL
1983 : mpOutputDevice->DrawTransparent(
1984 : aContentMetafile, aPrimitiveRectangle.TopLeft(),
1985 0 : aPrimitiveRectangle.GetSize(), aVCLGradient);
1986 : }
1987 : else
1988 : {
1989 : // sub-transparence group. Draw to VDev first.
1990 : // this may get refined to tiling when resolution is too big here
1991 :
1992 : // need to avoid switching off MapMode stuff here; maybe need another
1993 : // tooling class, cannot just do the same as with the pixel renderer.
1994 : // Need to experiment...
1995 :
1996 : // Okay, basic implementation finished and tested. The DPI stuff was hard
1997 : // and not easy to find out that it's needed.
1998 : // Since this will not yet happen normally (as long as noone constructs
1999 : // transparence primitives with non-trivial transparence content) i will for now not
2000 : // refine to tiling here.
2001 :
2002 0 : basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
2003 0 : aViewRange.transform(maCurrentTransformation);
2004 : const Rectangle aRectLogic(
2005 0 : (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()),
2006 0 : (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
2007 0 : const Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic));
2008 0 : Size aSizePixel(aRectPixel.GetSize());
2009 0 : const Point aEmptyPoint;
2010 0 : VirtualDevice aBufferDevice;
2011 0 : const sal_uInt32 nMaxQuadratPixels(500000);
2012 0 : const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight());
2013 0 : double fReduceFactor(1.0);
2014 :
2015 0 : if(nViewVisibleArea > nMaxQuadratPixels)
2016 : {
2017 : // reduce render size
2018 0 : fReduceFactor = sqrt((double)nMaxQuadratPixels / (double)nViewVisibleArea);
2019 0 : aSizePixel = Size(basegfx::fround((double)aSizePixel.getWidth() * fReduceFactor),
2020 0 : basegfx::fround((double)aSizePixel.getHeight() * fReduceFactor));
2021 : }
2022 :
2023 0 : if(aBufferDevice.SetOutputSizePixel(aSizePixel))
2024 : {
2025 : // create and set MapModes for target devices
2026 0 : MapMode aNewMapMode(mpOutputDevice->GetMapMode());
2027 0 : aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top()));
2028 0 : aBufferDevice.SetMapMode(aNewMapMode);
2029 :
2030 : // prepare view transformation for target renderers
2031 : // ATTENTION! Need to apply another scaling because of the potential DPI differences
2032 : // between Printer and VDev (mpOutputDevice and aBufferDevice here).
2033 : // To get the DPI, LogicToPixel from (1,1) from MAP_INCH needs to be used.
2034 0 : basegfx::B2DHomMatrix aViewTransform(aBufferDevice.GetViewTransformation());
2035 0 : const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MAP_INCH));
2036 0 : const Size aDPINew(aBufferDevice.LogicToPixel(Size(1, 1), MAP_INCH));
2037 0 : const double fDPIXChange((double)aDPIOld.getWidth() / (double)aDPINew.getWidth());
2038 0 : const double fDPIYChange((double)aDPIOld.getHeight() / (double)aDPINew.getHeight());
2039 :
2040 0 : if(!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0))
2041 : {
2042 0 : aViewTransform.scale(fDPIXChange, fDPIYChange);
2043 : }
2044 :
2045 : // also take scaling from Size reduction into acount
2046 0 : if(!basegfx::fTools::equal(fReduceFactor, 1.0))
2047 : {
2048 0 : aViewTransform.scale(fReduceFactor, fReduceFactor);
2049 : }
2050 :
2051 : // create view information and pixel renderer. Reuse known ViewInformation
2052 : // except new transformation and range
2053 : const geometry::ViewInformation2D aViewInfo(
2054 0 : getViewInformation2D().getObjectTransformation(),
2055 : aViewTransform,
2056 : aViewRange,
2057 0 : getViewInformation2D().getVisualizedPage(),
2058 0 : getViewInformation2D().getViewTime(),
2059 0 : getViewInformation2D().getExtendedInformationSequence());
2060 :
2061 0 : VclPixelProcessor2D aBufferProcessor(aViewInfo, aBufferDevice);
2062 :
2063 : // draw content using pixel renderer
2064 0 : aBufferProcessor.process(rContent);
2065 0 : const Bitmap aBmContent(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel));
2066 :
2067 : // draw transparence using pixel renderer
2068 0 : aBufferDevice.Erase();
2069 0 : aBufferProcessor.process(rTransparence);
2070 0 : const AlphaMask aBmAlpha(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel));
2071 :
2072 : // paint
2073 : mpOutputDevice->DrawBitmapEx(
2074 : aRectLogic.TopLeft(),
2075 : aRectLogic.GetSize(),
2076 0 : BitmapEx(aBmContent, aBmAlpha));
2077 0 : }
2078 : }
2079 : }
2080 :
2081 0 : break;
2082 : }
2083 : case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
2084 : {
2085 : // use default transform group pocessing
2086 4 : RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
2087 4 : break;
2088 : }
2089 : case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
2090 : {
2091 : // new XDrawPage for ViewInformation2D
2092 0 : RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate));
2093 0 : break;
2094 : }
2095 : case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
2096 : {
2097 : // use default marker array pocessing
2098 0 : RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate));
2099 0 : break;
2100 : }
2101 : case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
2102 : {
2103 : // use default point array pocessing
2104 0 : RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate));
2105 0 : break;
2106 : }
2107 : case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D :
2108 : {
2109 : // structured tag primitive
2110 0 : const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate = static_cast< const primitive2d::StructureTagPrimitive2D& >(rCandidate);
2111 0 : const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
2112 0 : const bool bTagUsed(vcl::PDFWriter::NonStructElement != rTagElement);
2113 :
2114 0 : if(mpPDFExtOutDevData && bTagUsed)
2115 : {
2116 : // write start tag
2117 0 : mpPDFExtOutDevData->BeginStructureElement(rTagElement);
2118 : }
2119 :
2120 : // proccess children normally
2121 0 : process(rStructureTagCandidate.getChildren());
2122 :
2123 0 : if(mpPDFExtOutDevData && bTagUsed)
2124 : {
2125 : // write end tag
2126 0 : mpPDFExtOutDevData->EndStructureElement();
2127 : }
2128 :
2129 0 : break;
2130 : }
2131 : case PRIMITIVE2D_ID_EPSPRIMITIVE2D :
2132 : {
2133 0 : RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate));
2134 0 : break;
2135 : }
2136 : default :
2137 : {
2138 : // process recursively
2139 274 : process(rCandidate.get2DDecomposition(getViewInformation2D()));
2140 274 : break;
2141 : }
2142 : }
2143 1866 : }
2144 : } // end of namespace processor2d
2145 408 : } // end of namespace drawinglayer
2146 :
2147 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|