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_svgio.hxx" 24 25 #include <svgio/svgreader/svgmasknode.hxx> 26 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 27 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> 28 #include <basegfx/matrix/b2dhommatrixtools.hxx> 29 #include <drawinglayer/geometry/viewinformation2d.hxx> 30 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> 31 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 32 #include <basegfx/polygon/b2dpolygontools.hxx> 33 #include <basegfx/polygon/b2dpolygon.hxx> 34 35 ////////////////////////////////////////////////////////////////////////////// 36 37 namespace svgio 38 { 39 namespace svgreader 40 { SvgMaskNode(SvgDocument & rDocument,SvgNode * pParent)41 SvgMaskNode::SvgMaskNode( 42 SvgDocument& rDocument, 43 SvgNode* pParent) 44 : SvgNode(SVGTokenMask, rDocument, pParent), 45 maSvgStyleAttributes(*this), 46 maX(SvgNumber(-10.0, Unit_percent, true)), 47 maY(SvgNumber(-10.0, Unit_percent, true)), 48 maWidth(SvgNumber(120.0, Unit_percent, true)), 49 maHeight(SvgNumber(120.0, Unit_percent, true)), 50 mpaTransform(0), 51 maMaskUnits(objectBoundingBox), 52 maMaskContentUnits(userSpaceOnUse) 53 { 54 } 55 ~SvgMaskNode()56 SvgMaskNode::~SvgMaskNode() 57 { 58 if(mpaTransform) delete mpaTransform; 59 } 60 getSvgStyleAttributes() const61 const SvgStyleAttributes* SvgMaskNode::getSvgStyleAttributes() const 62 { 63 return &maSvgStyleAttributes; 64 } 65 parseAttribute(const rtl::OUString & rTokenName,SVGToken aSVGToken,const rtl::OUString & aContent)66 void SvgMaskNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent) 67 { 68 // call parent 69 SvgNode::parseAttribute(rTokenName, aSVGToken, aContent); 70 71 // read style attributes 72 maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent, false); 73 74 // parse own 75 switch(aSVGToken) 76 { 77 case SVGTokenStyle: 78 { 79 readLocalCssStyle(aContent); 80 break; 81 } 82 case SVGTokenX: 83 { 84 SvgNumber aNum; 85 86 if(readSingleNumber(aContent, aNum)) 87 { 88 setX(aNum); 89 } 90 break; 91 } 92 case SVGTokenY: 93 { 94 SvgNumber aNum; 95 96 if(readSingleNumber(aContent, aNum)) 97 { 98 setY(aNum); 99 } 100 break; 101 } 102 case SVGTokenWidth: 103 { 104 SvgNumber aNum; 105 106 if(readSingleNumber(aContent, aNum)) 107 { 108 if(aNum.isPositive()) 109 { 110 setWidth(aNum); 111 } 112 } 113 break; 114 } 115 case SVGTokenHeight: 116 { 117 SvgNumber aNum; 118 119 if(readSingleNumber(aContent, aNum)) 120 { 121 if(aNum.isPositive()) 122 { 123 setHeight(aNum); 124 } 125 } 126 break; 127 } 128 case SVGTokenTransform: 129 { 130 const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this)); 131 132 if(!aMatrix.isIdentity()) 133 { 134 setTransform(&aMatrix); 135 } 136 break; 137 } 138 case SVGTokenMaskUnits: 139 { 140 if(aContent.getLength()) 141 { 142 if(aContent.match(commonStrings::aStrUserSpaceOnUse, 0)) 143 { 144 setMaskUnits(userSpaceOnUse); 145 } 146 else if(aContent.match(commonStrings::aStrObjectBoundingBox, 0)) 147 { 148 setMaskUnits(objectBoundingBox); 149 } 150 } 151 break; 152 } 153 case SVGTokenMaskContentUnits: 154 { 155 if(aContent.getLength()) 156 { 157 if(aContent.match(commonStrings::aStrUserSpaceOnUse, 0)) 158 { 159 setMaskContentUnits(userSpaceOnUse); 160 } 161 else if(aContent.match(commonStrings::aStrObjectBoundingBox, 0)) 162 { 163 setMaskContentUnits(objectBoundingBox); 164 } 165 } 166 break; 167 } 168 default: 169 { 170 break; 171 } 172 } 173 } 174 decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence & rTarget,bool bReferenced) const175 void SvgMaskNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const 176 { 177 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 178 179 // decompose childs 180 SvgNode::decomposeSvgNode(aNewTarget, bReferenced); 181 182 if(aNewTarget.hasElements()) 183 { 184 if(getTransform()) 185 { 186 // create embedding group element with transformation 187 const drawinglayer::primitive2d::Primitive2DReference xRef( 188 new drawinglayer::primitive2d::TransformPrimitive2D( 189 *getTransform(), 190 aNewTarget)); 191 192 aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 193 } 194 195 // append to current target 196 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget); 197 } 198 } 199 apply(drawinglayer::primitive2d::Primitive2DSequence & rTarget,const basegfx::B2DHomMatrix * pTransform) const200 void SvgMaskNode::apply( 201 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 202 const basegfx::B2DHomMatrix* pTransform) const 203 { 204 if(rTarget.hasElements() && Display_none != getDisplay()) 205 { 206 drawinglayer::primitive2d::Primitive2DSequence aMaskTarget; 207 208 // get mask definition as primitives 209 decomposeSvgNode(aMaskTarget, true); 210 211 if(aMaskTarget.hasElements()) 212 { 213 // get range of content to be masked 214 const basegfx::B2DRange aContentRange( 215 drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence( 216 rTarget, 217 drawinglayer::geometry::ViewInformation2D())); 218 const double fContentWidth(aContentRange.getWidth()); 219 const double fContentHeight(aContentRange.getHeight()); 220 221 if(fContentWidth > 0.0 && fContentHeight > 0.0) 222 { 223 // create OffscreenBufferRange 224 basegfx::B2DRange aOffscreenBufferRange; 225 226 if(objectBoundingBox == getMaskUnits()) 227 { 228 // fractions or percentages of the bounding box of the element to which the mask is applied 229 const double fX(Unit_percent == getX().getUnit() ? getX().getNumber() * 0.01 : getX().getNumber()); 230 const double fY(Unit_percent == getY().getUnit() ? getY().getNumber() * 0.01 : getY().getNumber()); 231 const double fW(Unit_percent == getWidth().getUnit() ? getWidth().getNumber() * 0.01 : getWidth().getNumber()); 232 const double fH(Unit_percent == getHeight().getUnit() ? getHeight().getNumber() * 0.01 : getHeight().getNumber()); 233 234 aOffscreenBufferRange = basegfx::B2DRange( 235 aContentRange.getMinX() + (fX * fContentWidth), 236 aContentRange.getMinY() + (fY * fContentHeight), 237 aContentRange.getMinX() + ((fX + fW) * fContentWidth), 238 aContentRange.getMinY() + ((fY + fH) * fContentHeight)); 239 } 240 else 241 { 242 const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0); 243 const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0); 244 245 aOffscreenBufferRange = basegfx::B2DRange( 246 fX, 247 fY, 248 fX + (getWidth().isSet() ? getWidth().solve(*this, xcoordinate) : 0.0), 249 fY + (getHeight().isSet() ? getHeight().solve(*this, ycoordinate) : 0.0)); 250 } 251 252 if(objectBoundingBox == getMaskContentUnits()) 253 { 254 // mask is object-relative, embed in content transformation 255 const drawinglayer::primitive2d::Primitive2DReference xTransform( 256 new drawinglayer::primitive2d::TransformPrimitive2D( 257 basegfx::tools::createScaleTranslateB2DHomMatrix( 258 aContentRange.getRange(), 259 aContentRange.getMinimum()), 260 aMaskTarget)); 261 262 aMaskTarget = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1); 263 } 264 else // userSpaceOnUse 265 { 266 // #i124852# 267 if(pTransform) 268 { 269 const drawinglayer::primitive2d::Primitive2DReference xTransform( 270 new drawinglayer::primitive2d::TransformPrimitive2D( 271 *pTransform, 272 aMaskTarget)); 273 274 aMaskTarget = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1); 275 } 276 } 277 278 // embed content to a ModifiedColorPrimitive2D since the definitions 279 // how content is used as alpha is special for Svg 280 { 281 const drawinglayer::primitive2d::Primitive2DReference xInverseMask( 282 new drawinglayer::primitive2d::ModifiedColorPrimitive2D( 283 aMaskTarget, 284 basegfx::BColorModifierSharedPtr( 285 new basegfx::BColorModifier_luminance_to_alpha()))); 286 287 aMaskTarget = drawinglayer::primitive2d::Primitive2DSequence(&xInverseMask, 1); 288 } 289 290 // prepare new content 291 drawinglayer::primitive2d::Primitive2DReference xNewContent( 292 new drawinglayer::primitive2d::TransparencePrimitive2D( 293 rTarget, 294 aMaskTarget)); 295 296 // output up to now is defined by aContentRange and mask is oriented 297 // relative to it. It is possible that aOffscreenBufferRange defines 298 // a smaller area. In that case, embed to a mask primitive 299 if(!aOffscreenBufferRange.isInside(aContentRange)) 300 { 301 xNewContent = new drawinglayer::primitive2d::MaskPrimitive2D( 302 basegfx::B2DPolyPolygon( 303 basegfx::tools::createPolygonFromRect( 304 aOffscreenBufferRange)), 305 drawinglayer::primitive2d::Primitive2DSequence(&xNewContent, 1)); 306 } 307 308 // redefine target. Use TransparencePrimitive2D with created mask 309 // geometry 310 rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xNewContent, 1); 311 } 312 else 313 { 314 // content is geometrically empty 315 rTarget.realloc(0); 316 } 317 } 318 else 319 { 320 // An empty clipping path will completely clip away the element that had 321 // the �clip-path� property applied. (Svg spec) 322 rTarget.realloc(0); 323 } 324 } 325 } 326 327 } // end of namespace svgreader 328 } // end of namespace svgio 329 330 ////////////////////////////////////////////////////////////////////////////// 331 // eof 332