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/svgstylenode.hxx>
26 #include <svgio/svgreader/svgdocument.hxx>
27 
28 //////////////////////////////////////////////////////////////////////////////
29 
30 namespace svgio
31 {
32     namespace svgreader
33     {
SvgStyleNode(SvgDocument & rDocument,SvgNode * pParent)34         SvgStyleNode::SvgStyleNode(
35             SvgDocument& rDocument,
36             SvgNode* pParent)
37         :   SvgNode(SVGTokenStyle, rDocument, pParent),
38             maSvgStyleAttributes(),
39             mbTextCss(false)
40         {
41         }
42 
~SvgStyleNode()43         SvgStyleNode::~SvgStyleNode()
44         {
45             while(!maSvgStyleAttributes.empty())
46             {
47                 delete *(maSvgStyleAttributes.end() - 1);
48                 maSvgStyleAttributes.pop_back();
49             }
50         }
51 
52         // #125258# no parent when we are a CssStyle holder to break potential loops because
53         // when using CssStyles we jump uncontrolled inside the node tree hierarchy
supportsParentStyle() const54         bool SvgStyleNode::supportsParentStyle() const
55         {
56             if(isTextCss())
57             {
58                 return false;
59             }
60 
61             // call parent
62             return SvgNode::supportsParentStyle();
63         }
64 
parseAttribute(const rtl::OUString & rTokenName,SVGToken aSVGToken,const rtl::OUString & aContent)65         void SvgStyleNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent)
66         {
67             // call parent
68             SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
69 
70             // parse own
71             switch(aSVGToken)
72             {
73                 case SVGTokenType:
74                 {
75                     if(aContent.getLength())
76                     {
77                         static rtl::OUString aStrTextCss(rtl::OUString::createFromAscii("text/css"));
78 
79                         if(aContent.match(aStrTextCss))
80                         {
81                             setTextCss(true);
82                         }
83                     }
84                     break;
85                 }
86                 default:
87                 {
88                     break;
89                 }
90             }
91         }
92 
addCssStyleSheet(const rtl::OUString & aSelectors,const SvgStyleAttributes & rNewStyle)93         void SvgStyleNode::addCssStyleSheet(const rtl::OUString& aSelectors, const SvgStyleAttributes& rNewStyle)
94         {
95             // aSelectors: CssStyle selectors, any combination, no comma separations, no spaces at start/end
96             // rNewStyle: the already preapared style to register on that name
97             if(aSelectors.getLength())
98             {
99                 std::vector< rtl::OUString > aSelectorParts;
100                 const sal_Int32 nLen(aSelectors.getLength());
101                 sal_Int32 nPos(0);
102                 rtl::OUStringBuffer aToken;
103 
104                 // split into single tokens (currently only space separator)
105                 while(nPos < nLen)
106                 {
107                     const sal_Int32 nInitPos(nPos);
108                     copyToLimiter(aSelectors, sal_Unicode(' '), nPos, aToken, nLen);
109                     skip_char(aSelectors, sal_Unicode(' '), nPos, nLen);
110                     const rtl::OUString aSelectorPart(aToken.makeStringAndClear().trim());
111 
112                     if(aSelectorPart.getLength())
113                     {
114                         aSelectorParts.push_back(aSelectorPart);
115                     }
116 
117                     if(nInitPos == nPos)
118                     {
119                         OSL_ENSURE(false, "Could not interpret on current position (!)");
120                         nPos++;
121                     }
122                 }
123 
124                 if(aSelectorParts.size())
125                 {
126                     rtl::OUString aConcatenatedSelector;
127 
128                     // re-combine without spaces, create a unique name (for now)
129                     for(sal_uInt32 a(0); a < aSelectorParts.size(); a++)
130                     {
131                         aConcatenatedSelector += aSelectorParts[a];
132                     }
133 
134                     // CssStyles in SVG are currently not completely supported; the current idea for
135                     // supporting the needed minimal set is to register CssStyles associated to a string
136                     // which is just the space-char cleaned, concatenated Selectors. The part to 'match'
137                     // these is in fillCssStyleVectorUsingHierarchyAndSelectors. There, the same string is
138                     // built up using the priorities of local CssStyle, Id, Class and other info combined
139                     // with the existing hierarchy. This creates a specificity- and priority-sorted local
140                     // list for each node which is then chained using get/setCssStyleParent.
141                     // The current solution is capable of solving space-separated selectors which can be
142                     // mixed between Id, Class and type specifiers.
143                     // When CssStyles need more specific solving, the start point is here; remember the
144                     // needed infos not in maIdStyleTokenMapperList at the document, but select evtl.
145                     // more specific infos there in a class capable of handling more complex matchings.
146                     // Additionally fillCssStyleVector (or the mechanism above that when a linked list of
147                     // SvgStyleAttributes will not do it) will have to be adapted to make use of it.
148 
149                     // register new style at document for (evtl. concatenated) stylename
150                     const_cast< SvgDocument& >(getDocument()).addSvgStyleAttributesToMapper(aConcatenatedSelector, rNewStyle);
151                 }
152             }
153         }
154 
addCssStyleSheet(const rtl::OUString & aSelectors,const rtl::OUString & aContent)155         void SvgStyleNode::addCssStyleSheet(const rtl::OUString& aSelectors, const rtl::OUString& aContent)
156         {
157             // aSelectors: possible comma-separated list of CssStyle definitions, no spaces at start/end
158             // aContent: the svg style definitions as string
159             if(aSelectors.getLength() && aContent.getLength())
160             {
161                 // create new style and add to local list (for ownership control)
162                 SvgStyleAttributes* pNewStyle = new SvgStyleAttributes(*this);
163                 maSvgStyleAttributes.push_back(pNewStyle);
164 
165                 // fill with content
166                 pNewStyle->readCssStyle(aContent);
167 
168                 // comma-separated split (Css abbreviation for same style for multiple selectors)
169                 const sal_Int32 nLen(aSelectors.getLength());
170                 sal_Int32 nPos(0);
171                 rtl::OUStringBuffer aToken;
172 
173                 while(nPos < nLen)
174                 {
175                     const sal_Int32 nInitPos(nPos);
176                     copyToLimiter(aSelectors, sal_Unicode(','), nPos, aToken, nLen);
177                     skip_char(aSelectors, sal_Unicode(' '), sal_Unicode(','), nPos, nLen);
178 
179                     const rtl::OUString aSingleName(aToken.makeStringAndClear().trim());
180 
181                     if(aSingleName.getLength())
182                     {
183                         addCssStyleSheet(aSingleName, *pNewStyle);
184                     }
185 
186                     if(nInitPos == nPos)
187                     {
188                         OSL_ENSURE(false, "Could not interpret on current position (!)");
189                         nPos++;
190                     }
191                 }
192             }
193         }
194 
addCssStyleSheet(const rtl::OUString & aSelectorsAndContent)195         void SvgStyleNode::addCssStyleSheet(const rtl::OUString& aSelectorsAndContent)
196         {
197             const sal_Int32 nLen(aSelectorsAndContent.getLength());
198 
199             if(nLen)
200             {
201                 sal_Int32 nPos(0);
202                 rtl::OUStringBuffer aToken;
203 
204                 while(nPos < nLen)
205                 {
206                     // read the full selectors (may be multiple, comma-separated)
207                     const sal_Int32 nInitPos(nPos);
208                     skip_char(aSelectorsAndContent, sal_Unicode(' '), nPos, nLen);
209                     copyToLimiter(aSelectorsAndContent, sal_Unicode('{'), nPos, aToken, nLen);
210                     skip_char(aSelectorsAndContent, sal_Unicode(' '), sal_Unicode('{'), nPos, nLen);
211 
212                     const rtl::OUString aSelectors(aToken.makeStringAndClear().trim());
213                     rtl::OUString aContent;
214 
215                     if(aSelectors.getLength() && nPos < nLen)
216                     {
217                         // isolate content as text, embraced by '{' and '}'
218                         copyToLimiter(aSelectorsAndContent, sal_Unicode('}'), nPos, aToken, nLen);
219                         skip_char(aSelectorsAndContent, sal_Unicode(' '), sal_Unicode('}'), nPos, nLen);
220 
221                         aContent = aToken.makeStringAndClear().trim();
222                     }
223 
224                     if(aSelectors.getLength() && aContent.getLength())
225                     {
226                         addCssStyleSheet(aSelectors, aContent);
227                     }
228 
229                     if(nInitPos == nPos)
230                     {
231                         OSL_ENSURE(false, "Could not interpret on current position (!)");
232                         nPos++;
233                     }
234                 }
235             }
236         }
237 
238     } // end of namespace svgreader
239 } // end of namespace svgio
240 
241 //////////////////////////////////////////////////////////////////////////////
242 // eof
243