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