1ddde725dSArmin Le Grand /**************************************************************
2ddde725dSArmin Le Grand  *
3ddde725dSArmin Le Grand  * Licensed to the Apache Software Foundation (ASF) under one
4ddde725dSArmin Le Grand  * or more contributor license agreements.  See the NOTICE file
5ddde725dSArmin Le Grand  * distributed with this work for additional information
6ddde725dSArmin Le Grand  * regarding copyright ownership.  The ASF licenses this file
7ddde725dSArmin Le Grand  * to you under the Apache License, Version 2.0 (the
8ddde725dSArmin Le Grand  * "License"); you may not use this file except in compliance
9ddde725dSArmin Le Grand  * with the License.  You may obtain a copy of the License at
10ddde725dSArmin Le Grand  *
11ddde725dSArmin Le Grand  *   http://www.apache.org/licenses/LICENSE-2.0
12ddde725dSArmin Le Grand  *
13ddde725dSArmin Le Grand  * Unless required by applicable law or agreed to in writing,
14ddde725dSArmin Le Grand  * software distributed under the License is distributed on an
15ddde725dSArmin Le Grand  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16ddde725dSArmin Le Grand  * KIND, either express or implied.  See the License for the
17ddde725dSArmin Le Grand  * specific language governing permissions and limitations
18ddde725dSArmin Le Grand  * under the License.
19ddde725dSArmin Le Grand  *
20ddde725dSArmin Le Grand  *************************************************************/
21ddde725dSArmin Le Grand 
22ddde725dSArmin Le Grand // MARKER(update_precomp.py): autogen include statement, do not remove
23ddde725dSArmin Le Grand #include "precompiled_svgio.hxx"
24ddde725dSArmin Le Grand 
25ddde725dSArmin Le Grand #include <svgio/svgreader/svgtextnode.hxx>
26ddde725dSArmin Le Grand #include <svgio/svgreader/svgcharacternode.hxx>
27ddde725dSArmin Le Grand #include <svgio/svgreader/svgstyleattributes.hxx>
28ddde725dSArmin Le Grand #include <svgio/svgreader/svgtrefnode.hxx>
29ddde725dSArmin Le Grand #include <svgio/svgreader/svgtextpathnode.hxx>
30ddde725dSArmin Le Grand #include <svgio/svgreader/svgtspannode.hxx>
31ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
32ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
33ddde725dSArmin Le Grand 
34ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
35ddde725dSArmin Le Grand 
36ddde725dSArmin Le Grand namespace svgio
37ddde725dSArmin Le Grand {
38ddde725dSArmin Le Grand     namespace svgreader
39ddde725dSArmin Le Grand     {
40ddde725dSArmin Le Grand         SvgTextNode::SvgTextNode(
41ddde725dSArmin Le Grand             SvgDocument& rDocument,
42ddde725dSArmin Le Grand             SvgNode* pParent)
43ddde725dSArmin Le Grand         :   SvgNode(SVGTokenText, rDocument, pParent),
44ddde725dSArmin Le Grand             maSvgStyleAttributes(*this),
45ddde725dSArmin Le Grand             mpaTransform(0),
46ddde725dSArmin Le Grand             maSvgTextPositions()
47ddde725dSArmin Le Grand         {
48ddde725dSArmin Le Grand         }
49ddde725dSArmin Le Grand 
50ddde725dSArmin Le Grand         SvgTextNode::~SvgTextNode()
51ddde725dSArmin Le Grand         {
52ddde725dSArmin Le Grand             if(mpaTransform) delete mpaTransform;
53ddde725dSArmin Le Grand         }
54ddde725dSArmin Le Grand 
55ddde725dSArmin Le Grand         const SvgStyleAttributes* SvgTextNode::getSvgStyleAttributes() const
56ddde725dSArmin Le Grand         {
57ddde725dSArmin Le Grand             static rtl::OUString aClassStr(rtl::OUString::createFromAscii("text"));
58ddde725dSArmin Le Grand 
5950b37974SArmin Le Grand             return checkForCssStyle(aClassStr, maSvgStyleAttributes);
60ddde725dSArmin Le Grand         }
61ddde725dSArmin Le Grand 
62ddde725dSArmin Le Grand         void SvgTextNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent)
63ddde725dSArmin Le Grand         {
64ddde725dSArmin Le Grand             // call parent
65ddde725dSArmin Le Grand             SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
66ddde725dSArmin Le Grand 
67ddde725dSArmin Le Grand             // read style attributes
68ddde725dSArmin Le Grand             maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent);
69ddde725dSArmin Le Grand 
70ddde725dSArmin Le Grand             // read text position attributes
71ddde725dSArmin Le Grand             maSvgTextPositions.parseTextPositionAttributes(rTokenName, aSVGToken, aContent);
72ddde725dSArmin Le Grand 
73ddde725dSArmin Le Grand             // parse own
74ddde725dSArmin Le Grand             switch(aSVGToken)
75ddde725dSArmin Le Grand             {
76ddde725dSArmin Le Grand                 case SVGTokenStyle:
77ddde725dSArmin Le Grand                 {
78*9d01bcdeSArmin Le Grand                     readLocalCssStyle(aContent);
79ddde725dSArmin Le Grand                     break;
80ddde725dSArmin Le Grand                 }
81ddde725dSArmin Le Grand                 case SVGTokenTransform:
82ddde725dSArmin Le Grand                 {
83ddde725dSArmin Le Grand                     const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
84ddde725dSArmin Le Grand 
85ddde725dSArmin Le Grand                     if(!aMatrix.isIdentity())
86ddde725dSArmin Le Grand                     {
87ddde725dSArmin Le Grand                         setTransform(&aMatrix);
88ddde725dSArmin Le Grand                     }
89ddde725dSArmin Le Grand                     break;
90ddde725dSArmin Le Grand                 }
91e2bf1e9dSArmin Le Grand                 default:
92e2bf1e9dSArmin Le Grand                 {
93e2bf1e9dSArmin Le Grand                     break;
94e2bf1e9dSArmin Le Grand                 }
95ddde725dSArmin Le Grand             }
96ddde725dSArmin Le Grand         }
97ddde725dSArmin Le Grand 
98ddde725dSArmin Le Grand         void SvgTextNode::addTextPrimitives(
99ddde725dSArmin Le Grand             const SvgNode& rCandidate,
100ddde725dSArmin Le Grand             drawinglayer::primitive2d::Primitive2DSequence& rTarget,
101ddde725dSArmin Le Grand             drawinglayer::primitive2d::Primitive2DSequence& rSource) const
102ddde725dSArmin Le Grand         {
103ddde725dSArmin Le Grand             if(rSource.hasElements())
104ddde725dSArmin Le Grand             {
105ddde725dSArmin Le Grand                 const SvgStyleAttributes* pAttributes = rCandidate.getSvgStyleAttributes();
106ddde725dSArmin Le Grand 
107ddde725dSArmin Le Grand                 if(pAttributes)
108ddde725dSArmin Le Grand                 {
109ddde725dSArmin Le Grand                     // add text with taking all Fill/Stroke attributes into account
110ddde725dSArmin Le Grand                     pAttributes->add_text(rTarget, rSource);
111ddde725dSArmin Le Grand                 }
112ddde725dSArmin Le Grand                 else
113ddde725dSArmin Le Grand                 {
114ddde725dSArmin Le Grand                     // should not happen, every subnode from SvgTextNode will at least
115ddde725dSArmin Le Grand                     // return the attributes from SvgTextNode. Nonetheless, add text
116ddde725dSArmin Le Grand                     drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, rSource);
117ddde725dSArmin Le Grand                 }
118ddde725dSArmin Le Grand             }
119ddde725dSArmin Le Grand         }
120ddde725dSArmin Le Grand 
121ddde725dSArmin Le Grand         void SvgTextNode::DecomposeChild(const SvgNode& rCandidate, drawinglayer::primitive2d::Primitive2DSequence& rTarget, SvgTextPosition& rSvgTextPosition) const
122ddde725dSArmin Le Grand         {
123ddde725dSArmin Le Grand             switch(rCandidate.getType())
124ddde725dSArmin Le Grand             {
125ddde725dSArmin Le Grand                 case SVGTokenCharacter:
126ddde725dSArmin Le Grand                 {
127ddde725dSArmin Le Grand                     // direct SvgTextPathNode derivates, decompose them
128ddde725dSArmin Le Grand                     const SvgCharacterNode& rSvgCharacterNode = static_cast< const SvgCharacterNode& >(rCandidate);
129ddde725dSArmin Le Grand                     rSvgCharacterNode.decomposeText(rTarget, rSvgTextPosition);
130ddde725dSArmin Le Grand                     break;
131ddde725dSArmin Le Grand                 }
132ddde725dSArmin Le Grand                 case SVGTokenTextPath:
133ddde725dSArmin Le Grand                 {
134ddde725dSArmin Le Grand                     // direct TextPath decompose
135ddde725dSArmin Le Grand                     const SvgTextPathNode& rSvgTextPathNode = static_cast< const SvgTextPathNode& >(rCandidate);
136ddde725dSArmin Le Grand                     const SvgNodeVector& rChildren = rSvgTextPathNode.getChildren();
137ddde725dSArmin Le Grand                     const sal_uInt32 nCount(rChildren.size());
138ddde725dSArmin Le Grand 
139ddde725dSArmin Le Grand                     if(nCount && rSvgTextPathNode.isValid())
140ddde725dSArmin Le Grand                     {
141ddde725dSArmin Le Grand                         // remember original TextStart to later detect hor/ver offsets
142ddde725dSArmin Le Grand                         const basegfx::B2DPoint aTextStart(rSvgTextPosition.getPosition());
143ddde725dSArmin Le Grand                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
144ddde725dSArmin Le Grand 
145ddde725dSArmin Le Grand                         // decompose to regular TextPrimitives
146ddde725dSArmin Le Grand                         for(sal_uInt32 a(0); a < nCount; a++)
147ddde725dSArmin Le Grand                         {
148ddde725dSArmin Le Grand                             DecomposeChild(*rChildren[a], aNewTarget, rSvgTextPosition);
149ddde725dSArmin Le Grand                         }
150ddde725dSArmin Le Grand 
151ddde725dSArmin Le Grand                         if(aNewTarget.hasElements())
152ddde725dSArmin Le Grand                         {
153ddde725dSArmin Le Grand                             const drawinglayer::primitive2d::Primitive2DSequence aPathContent(aNewTarget);
154ddde725dSArmin Le Grand                             aNewTarget.realloc(0);
155ddde725dSArmin Le Grand 
156ddde725dSArmin Le Grand                             // dismantle TextPrimitives and map them on curve/path
157ddde725dSArmin Le Grand                             rSvgTextPathNode.decomposePathNode(aPathContent, aNewTarget, aTextStart);
158ddde725dSArmin Le Grand                         }
159ddde725dSArmin Le Grand 
160ddde725dSArmin Le Grand                         if(aNewTarget.hasElements())
161ddde725dSArmin Le Grand                         {
162ddde725dSArmin Le Grand                             addTextPrimitives(rCandidate, rTarget, aNewTarget);
163ddde725dSArmin Le Grand                         }
164ddde725dSArmin Le Grand                     }
165ddde725dSArmin Le Grand 
166ddde725dSArmin Le Grand                     break;
167ddde725dSArmin Le Grand                 }
168ddde725dSArmin Le Grand                 case SVGTokenTspan:
169ddde725dSArmin Le Grand                 {
170ddde725dSArmin Le Grand                     // Tspan may have children, call recursively
171ddde725dSArmin Le Grand                     const SvgTspanNode& rSvgTspanNode = static_cast< const SvgTspanNode& >(rCandidate);
172ddde725dSArmin Le Grand                     const SvgNodeVector& rChildren = rSvgTspanNode.getChildren();
173ddde725dSArmin Le Grand                     const sal_uInt32 nCount(rChildren.size());
174ddde725dSArmin Le Grand 
175ddde725dSArmin Le Grand                     if(nCount)
176ddde725dSArmin Le Grand                     {
177ddde725dSArmin Le Grand                         SvgTextPosition aSvgTextPosition(&rSvgTextPosition, rSvgTspanNode, rSvgTspanNode.getSvgTextPositions());
178ddde725dSArmin Le Grand                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
179ddde725dSArmin Le Grand 
180ddde725dSArmin Le Grand                         for(sal_uInt32 a(0); a < nCount; a++)
181ddde725dSArmin Le Grand                         {
182ddde725dSArmin Le Grand                             DecomposeChild(*rChildren[a], aNewTarget, aSvgTextPosition);
183ddde725dSArmin Le Grand                         }
184ddde725dSArmin Le Grand 
185ddde725dSArmin Le Grand                         rSvgTextPosition.setPosition(aSvgTextPosition.getPosition());
186ddde725dSArmin Le Grand 
187ddde725dSArmin Le Grand                         if(aNewTarget.hasElements())
188ddde725dSArmin Le Grand                         {
189ddde725dSArmin Le Grand                             addTextPrimitives(rCandidate, rTarget, aNewTarget);
190ddde725dSArmin Le Grand                         }
191ddde725dSArmin Le Grand                     }
192ddde725dSArmin Le Grand                     break;
193ddde725dSArmin Le Grand                 }
194ddde725dSArmin Le Grand                 case SVGTokenTref:
195ddde725dSArmin Le Grand                 {
196ddde725dSArmin Le Grand                     const SvgTrefNode& rSvgTrefNode = static_cast< const SvgTrefNode& >(rCandidate);
197ddde725dSArmin Le Grand                     const SvgTextNode* pRefText = rSvgTrefNode.getReferencedSvgTextNode();
198ddde725dSArmin Le Grand 
199ddde725dSArmin Le Grand                     if(pRefText)
200ddde725dSArmin Le Grand                     {
201ddde725dSArmin Le Grand                         const SvgNodeVector& rChildren = pRefText->getChildren();
202ddde725dSArmin Le Grand                         const sal_uInt32 nCount(rChildren.size());
203ddde725dSArmin Le Grand                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
204ddde725dSArmin Le Grand 
205ddde725dSArmin Le Grand                         if(nCount)
206ddde725dSArmin Le Grand                         {
207ddde725dSArmin Le Grand                             for(sal_uInt32 a(0); a < nCount; a++)
208ddde725dSArmin Le Grand                             {
209ddde725dSArmin Le Grand                                 const SvgNode& rChildCandidate = *rChildren[a];
210ddde725dSArmin Le Grand                                 const_cast< SvgNode& >(rChildCandidate).setAlternativeParent(this);
211ddde725dSArmin Le Grand 
212ddde725dSArmin Le Grand                                 DecomposeChild(rChildCandidate, aNewTarget, rSvgTextPosition);
213ddde725dSArmin Le Grand                                 const_cast< SvgNode& >(rChildCandidate).setAlternativeParent(0);
214ddde725dSArmin Le Grand                             }
215ddde725dSArmin Le Grand 
216ddde725dSArmin Le Grand                             if(aNewTarget.hasElements())
217ddde725dSArmin Le Grand                             {
218ddde725dSArmin Le Grand                                 addTextPrimitives(rCandidate, rTarget, aNewTarget);
219ddde725dSArmin Le Grand                             }
220ddde725dSArmin Le Grand                         }
221ddde725dSArmin Le Grand                     }
222ddde725dSArmin Le Grand 
223ddde725dSArmin Le Grand                     break;
224ddde725dSArmin Le Grand                 }
225ddde725dSArmin Le Grand                 default:
226ddde725dSArmin Le Grand                 {
227ddde725dSArmin Le Grand                     OSL_ENSURE(false, "Unexpected node in text token (!)");
228ddde725dSArmin Le Grand                     break;
229ddde725dSArmin Le Grand                 }
230ddde725dSArmin Le Grand             }
231ddde725dSArmin Le Grand         }
232ddde725dSArmin Le Grand 
233e2bf1e9dSArmin Le Grand         void SvgTextNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool /*bReferenced`*/) const
234ddde725dSArmin Le Grand         {
235ddde725dSArmin Le Grand             // text has a group of child nodes, allowed are SVGTokenCharacter, SVGTokenTspan,
236ddde725dSArmin Le Grand             // SVGTokenTref and SVGTokenTextPath. These increase a given current text position
237ddde725dSArmin Le Grand             const SvgStyleAttributes* pStyle = getSvgStyleAttributes();
238ddde725dSArmin Le Grand 
239ddde725dSArmin Le Grand             if(pStyle && !getChildren().empty())
240ddde725dSArmin Le Grand             {
241ddde725dSArmin Le Grand                 const double fOpacity(pStyle->getOpacity().getNumber());
242ddde725dSArmin Le Grand 
243ddde725dSArmin Le Grand                 if(fOpacity > 0.0)
244ddde725dSArmin Le Grand                 {
245ddde725dSArmin Le Grand                     SvgTextPosition aSvgTextPosition(0, *this, getSvgTextPositions());
246ddde725dSArmin Le Grand                     drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
247ddde725dSArmin Le Grand                     const SvgNodeVector& rChildren = getChildren();
248ddde725dSArmin Le Grand                     const sal_uInt32 nCount(rChildren.size());
249ddde725dSArmin Le Grand 
250ddde725dSArmin Le Grand                     for(sal_uInt32 a(0); a < nCount; a++)
251ddde725dSArmin Le Grand                     {
252ddde725dSArmin Le Grand                         const SvgNode& rCandidate = *rChildren[a];
253ddde725dSArmin Le Grand 
254ddde725dSArmin Le Grand                         DecomposeChild(rCandidate, aNewTarget, aSvgTextPosition);
255ddde725dSArmin Le Grand                     }
256ddde725dSArmin Le Grand 
257ddde725dSArmin Le Grand                     if(aNewTarget.hasElements())
258ddde725dSArmin Le Grand                     {
259ddde725dSArmin Le Grand                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget2;
260ddde725dSArmin Le Grand 
261ddde725dSArmin Le Grand                         addTextPrimitives(*this, aNewTarget2, aNewTarget);
262ddde725dSArmin Le Grand                         aNewTarget = aNewTarget2;
263ddde725dSArmin Le Grand                     }
264ddde725dSArmin Le Grand 
265ddde725dSArmin Le Grand                     if(aNewTarget.hasElements())
266ddde725dSArmin Le Grand                     {
267ddde725dSArmin Le Grand                         pStyle->add_postProcess(rTarget, aNewTarget, getTransform());
268ddde725dSArmin Le Grand                     }
269ddde725dSArmin Le Grand                 }
270ddde725dSArmin Le Grand             }
271ddde725dSArmin Le Grand         }
272ddde725dSArmin Le Grand     } // end of namespace svgreader
273ddde725dSArmin Le Grand } // end of namespace svgio
274ddde725dSArmin Le Grand 
275ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
276ddde725dSArmin Le Grand // eof
277