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