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