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/svgtextnode.hxx> 26 #include <svgio/svgreader/svgcharacternode.hxx> 27 #include <svgio/svgreader/svgstyleattributes.hxx> 28 #include <svgio/svgreader/svgtrefnode.hxx> 29 #include <svgio/svgreader/svgtextpathnode.hxx> 30 #include <svgio/svgreader/svgtspannode.hxx> 31 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 32 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 33 34 ////////////////////////////////////////////////////////////////////////////// 35 36 namespace svgio 37 { 38 namespace svgreader 39 { SvgTextNode(SvgDocument & rDocument,SvgNode * pParent)40 SvgTextNode::SvgTextNode( 41 SvgDocument& rDocument, 42 SvgNode* pParent) 43 : SvgNode(SVGTokenText, rDocument, pParent), 44 maSvgStyleAttributes(*this), 45 mpaTransform(0), 46 maSvgTextPositions() 47 { 48 } 49 ~SvgTextNode()50 SvgTextNode::~SvgTextNode() 51 { 52 if(mpaTransform) delete mpaTransform; 53 } 54 getSvgStyleAttributes() const55 const SvgStyleAttributes* SvgTextNode::getSvgStyleAttributes() const 56 { 57 static rtl::OUString aClassStr(rtl::OUString::createFromAscii("text")); 58 59 return checkForCssStyle(aClassStr, maSvgStyleAttributes); 60 } 61 parseAttribute(const rtl::OUString & rTokenName,SVGToken aSVGToken,const rtl::OUString & aContent)62 void SvgTextNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent) 63 { 64 // call parent 65 SvgNode::parseAttribute(rTokenName, aSVGToken, aContent); 66 67 // read style attributes 68 maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent); 69 70 // read text position attributes 71 maSvgTextPositions.parseTextPositionAttributes(rTokenName, aSVGToken, aContent); 72 73 // parse own 74 switch(aSVGToken) 75 { 76 case SVGTokenStyle: 77 { 78 maSvgStyleAttributes.readStyle(aContent); 79 break; 80 } 81 case SVGTokenTransform: 82 { 83 const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this)); 84 85 if(!aMatrix.isIdentity()) 86 { 87 setTransform(&aMatrix); 88 } 89 break; 90 } 91 default: 92 { 93 break; 94 } 95 } 96 } 97 addTextPrimitives(const SvgNode & rCandidate,drawinglayer::primitive2d::Primitive2DSequence & rTarget,drawinglayer::primitive2d::Primitive2DSequence & rSource) const98 void SvgTextNode::addTextPrimitives( 99 const SvgNode& rCandidate, 100 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 101 drawinglayer::primitive2d::Primitive2DSequence& rSource) const 102 { 103 if(rSource.hasElements()) 104 { 105 const SvgStyleAttributes* pAttributes = rCandidate.getSvgStyleAttributes(); 106 107 if(pAttributes) 108 { 109 // add text with taking all Fill/Stroke attributes into account 110 pAttributes->add_text(rTarget, rSource); 111 } 112 else 113 { 114 // should not happen, every subnode from SvgTextNode will at least 115 // return the attributes from SvgTextNode. Nonetheless, add text 116 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, rSource); 117 } 118 } 119 } 120 DecomposeChild(const SvgNode & rCandidate,drawinglayer::primitive2d::Primitive2DSequence & rTarget,SvgTextPosition & rSvgTextPosition) const121 void SvgTextNode::DecomposeChild(const SvgNode& rCandidate, drawinglayer::primitive2d::Primitive2DSequence& rTarget, SvgTextPosition& rSvgTextPosition) const 122 { 123 switch(rCandidate.getType()) 124 { 125 case SVGTokenCharacter: 126 { 127 // direct SvgTextPathNode derivates, decompose them 128 const SvgCharacterNode& rSvgCharacterNode = static_cast< const SvgCharacterNode& >(rCandidate); 129 rSvgCharacterNode.decomposeText(rTarget, rSvgTextPosition); 130 break; 131 } 132 case SVGTokenTextPath: 133 { 134 // direct TextPath decompose 135 const SvgTextPathNode& rSvgTextPathNode = static_cast< const SvgTextPathNode& >(rCandidate); 136 const SvgNodeVector& rChildren = rSvgTextPathNode.getChildren(); 137 const sal_uInt32 nCount(rChildren.size()); 138 139 if(nCount && rSvgTextPathNode.isValid()) 140 { 141 // remember original TextStart to later detect hor/ver offsets 142 const basegfx::B2DPoint aTextStart(rSvgTextPosition.getPosition()); 143 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 144 145 // decompose to regular TextPrimitives 146 for(sal_uInt32 a(0); a < nCount; a++) 147 { 148 DecomposeChild(*rChildren[a], aNewTarget, rSvgTextPosition); 149 } 150 151 if(aNewTarget.hasElements()) 152 { 153 const drawinglayer::primitive2d::Primitive2DSequence aPathContent(aNewTarget); 154 aNewTarget.realloc(0); 155 156 // dismantle TextPrimitives and map them on curve/path 157 rSvgTextPathNode.decomposePathNode(aPathContent, aNewTarget, aTextStart); 158 } 159 160 if(aNewTarget.hasElements()) 161 { 162 addTextPrimitives(rCandidate, rTarget, aNewTarget); 163 } 164 } 165 166 break; 167 } 168 case SVGTokenTspan: 169 { 170 // Tspan may have children, call recursively 171 const SvgTspanNode& rSvgTspanNode = static_cast< const SvgTspanNode& >(rCandidate); 172 const SvgNodeVector& rChildren = rSvgTspanNode.getChildren(); 173 const sal_uInt32 nCount(rChildren.size()); 174 175 if(nCount) 176 { 177 SvgTextPosition aSvgTextPosition(&rSvgTextPosition, rSvgTspanNode, rSvgTspanNode.getSvgTextPositions()); 178 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 179 180 for(sal_uInt32 a(0); a < nCount; a++) 181 { 182 DecomposeChild(*rChildren[a], aNewTarget, aSvgTextPosition); 183 } 184 185 rSvgTextPosition.setPosition(aSvgTextPosition.getPosition()); 186 187 if(aNewTarget.hasElements()) 188 { 189 addTextPrimitives(rCandidate, rTarget, aNewTarget); 190 } 191 } 192 break; 193 } 194 case SVGTokenTref: 195 { 196 const SvgTrefNode& rSvgTrefNode = static_cast< const SvgTrefNode& >(rCandidate); 197 const SvgTextNode* pRefText = rSvgTrefNode.getReferencedSvgTextNode(); 198 199 if(pRefText) 200 { 201 const SvgNodeVector& rChildren = pRefText->getChildren(); 202 const sal_uInt32 nCount(rChildren.size()); 203 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 204 205 if(nCount) 206 { 207 for(sal_uInt32 a(0); a < nCount; a++) 208 { 209 const SvgNode& rChildCandidate = *rChildren[a]; 210 const_cast< SvgNode& >(rChildCandidate).setAlternativeParent(this); 211 212 DecomposeChild(rChildCandidate, aNewTarget, rSvgTextPosition); 213 const_cast< SvgNode& >(rChildCandidate).setAlternativeParent(0); 214 } 215 216 if(aNewTarget.hasElements()) 217 { 218 addTextPrimitives(rCandidate, rTarget, aNewTarget); 219 } 220 } 221 } 222 223 break; 224 } 225 default: 226 { 227 OSL_ENSURE(false, "Unexpected node in text token (!)"); 228 break; 229 } 230 } 231 } 232 decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence & rTarget,bool) const233 void SvgTextNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool /*bReferenced`*/) const 234 { 235 // text has a group of child nodes, allowed are SVGTokenCharacter, SVGTokenTspan, 236 // SVGTokenTref and SVGTokenTextPath. These increase a given current text position 237 const SvgStyleAttributes* pStyle = getSvgStyleAttributes(); 238 239 if(pStyle && !getChildren().empty()) 240 { 241 const double fOpacity(pStyle->getOpacity().getNumber()); 242 243 if(fOpacity > 0.0) 244 { 245 SvgTextPosition aSvgTextPosition(0, *this, getSvgTextPositions()); 246 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 247 const SvgNodeVector& rChildren = getChildren(); 248 const sal_uInt32 nCount(rChildren.size()); 249 250 for(sal_uInt32 a(0); a < nCount; a++) 251 { 252 const SvgNode& rCandidate = *rChildren[a]; 253 254 DecomposeChild(rCandidate, aNewTarget, aSvgTextPosition); 255 } 256 257 if(aNewTarget.hasElements()) 258 { 259 drawinglayer::primitive2d::Primitive2DSequence aNewTarget2; 260 261 addTextPrimitives(*this, aNewTarget2, aNewTarget); 262 aNewTarget = aNewTarget2; 263 } 264 265 if(aNewTarget.hasElements()) 266 { 267 pStyle->add_postProcess(rTarget, aNewTarget, getTransform()); 268 } 269 } 270 } 271 } 272 } // end of namespace svgreader 273 } // end of namespace svgio 274 275 ////////////////////////////////////////////////////////////////////////////// 276 // eof 277