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