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