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