xref: /aoo42x/main/svgio/source/svgreader/svgnode.cxx (revision 52cb04b8)
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/svgnode.hxx>
26ddde725dSArmin Le Grand #include <basegfx/polygon/b2dpolypolygontools.hxx>
27ddde725dSArmin Le Grand #include <svgio/svgreader/svgdocument.hxx>
28ddde725dSArmin Le Grand #include <svgio/svgreader/svgnode.hxx>
29ddde725dSArmin Le Grand #include <svgio/svgreader/svgstyleattributes.hxx>
30025b0597SArmin Le Grand #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
31172c67b2SArmin Le Grand #include <tools/urlobj.hxx>
32ddde725dSArmin Le Grand 
33ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
34ddde725dSArmin Le Grand 
35ddde725dSArmin Le Grand namespace svgio
36ddde725dSArmin Le Grand {
37ddde725dSArmin Le Grand     namespace svgreader
38ddde725dSArmin Le Grand     {
394374d266SArmin Le Grand         /// #125258#
404374d266SArmin Le Grand         bool SvgNode::supportsParentStyle() const
414374d266SArmin Le Grand         {
424374d266SArmin Le Grand             return true;
434374d266SArmin Le Grand         }
444374d266SArmin Le Grand 
45ddde725dSArmin Le Grand         const SvgStyleAttributes* SvgNode::getSvgStyleAttributes() const
46ddde725dSArmin Le Grand         {
47ddde725dSArmin Le Grand             return 0;
48ddde725dSArmin Le Grand         }
49ddde725dSArmin Le Grand 
50eb82bfcdSArmin Le Grand         void SvgNode::fillCssStyleVectorUsingHierarchyAndSelectors(
51eb82bfcdSArmin Le Grand             const rtl::OUString& rClassStr,
52eb82bfcdSArmin Le Grand             const SvgNode& rCurrent,
53eb82bfcdSArmin Le Grand             rtl::OUString aConcatenated)
5450b37974SArmin Le Grand         {
559d01bcdeSArmin Le Grand             const SvgDocument& rDocument = getDocument();
569d01bcdeSArmin Le Grand 
57eb82bfcdSArmin Le Grand             if(rDocument.hasGlobalCssStyleAttributes())
589d01bcdeSArmin Le Grand             {
59eb82bfcdSArmin Le Grand                 const SvgNode* pParent = rCurrent.getParent();
60eb82bfcdSArmin Le Grand 
61eb82bfcdSArmin Le Grand                 // check for ID (highest priority)
62eb82bfcdSArmin Le Grand                 if(rCurrent.getId())
6350b37974SArmin Le Grand                 {
64eb82bfcdSArmin Le Grand                     const rtl::OUString& rId = *rCurrent.getId();
65eb82bfcdSArmin Le Grand 
66eb82bfcdSArmin Le Grand                     if(rId.getLength())
67eb82bfcdSArmin Le Grand                     {
68eb82bfcdSArmin Le Grand                         const rtl::OUString aNewConcatenated(
69eb82bfcdSArmin Le Grand                             rtl::OUString::createFromAscii("#") +
70eb82bfcdSArmin Le Grand                             rId +
71eb82bfcdSArmin Le Grand                             aConcatenated);
72eb82bfcdSArmin Le Grand 
73eb82bfcdSArmin Le Grand                         if(pParent)
74eb82bfcdSArmin Le Grand                         {
75eb82bfcdSArmin Le Grand                             // check for combined selectors at parent firstso that higher specificity will be in front
76eb82bfcdSArmin Le Grand                             fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated);
77eb82bfcdSArmin Le Grand                         }
78eb82bfcdSArmin Le Grand 
79eb82bfcdSArmin Le Grand                         const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated);
809d01bcdeSArmin Le Grand 
81eb82bfcdSArmin Le Grand                         if(pNew)
829d01bcdeSArmin Le Grand                         {
83eb82bfcdSArmin Le Grand                             // add CssStyle if found
84eb82bfcdSArmin Le Grand                             maCssStyleVector.push_back(pNew);
859d01bcdeSArmin Le Grand                         }
869d01bcdeSArmin Le Grand                     }
879d01bcdeSArmin Le Grand                 }
889d01bcdeSArmin Le Grand 
89eb82bfcdSArmin Le Grand                 // check for 'class' references (a list of entries is allowed)
90eb82bfcdSArmin Le Grand                 if(rCurrent.getClass())
919d01bcdeSArmin Le Grand                 {
92eb82bfcdSArmin Le Grand                     const rtl::OUString& rClassList = *rCurrent.getClass();
93eb82bfcdSArmin Le Grand                     const sal_Int32 nLen(rClassList.getLength());
949d01bcdeSArmin Le Grand 
95eb82bfcdSArmin Le Grand                     if(nLen)
960906e779SArmin Le Grand                     {
97eb82bfcdSArmin Le Grand                         std::vector< rtl::OUString > aParts;
98eb82bfcdSArmin Le Grand                         sal_Int32 nPos(0);
99eb82bfcdSArmin Le Grand                         rtl::OUStringBuffer aToken;
1009d56236fSArmin Le Grand 
101eb82bfcdSArmin Le Grand                         while(nPos < nLen)
1029d01bcdeSArmin Le Grand                         {
103eb82bfcdSArmin Le Grand                             const sal_Int32 nInitPos(nPos);
104eb82bfcdSArmin Le Grand                             copyToLimiter(rClassList, sal_Unicode(' '), nPos, aToken, nLen);
105eb82bfcdSArmin Le Grand                             skip_char(rClassList, sal_Unicode(' '), nPos, nLen);
106eb82bfcdSArmin Le Grand                             const rtl::OUString aPart(aToken.makeStringAndClear().trim());
107eb82bfcdSArmin Le Grand 
108eb82bfcdSArmin Le Grand                             if(aPart.getLength())
109eb82bfcdSArmin Le Grand                             {
110eb82bfcdSArmin Le Grand                                 aParts.push_back(aPart);
111eb82bfcdSArmin Le Grand                             }
112eb82bfcdSArmin Le Grand 
113eb82bfcdSArmin Le Grand                             if(nInitPos == nPos)
114eb82bfcdSArmin Le Grand                             {
115eb82bfcdSArmin Le Grand                                 OSL_ENSURE(false, "Could not interpret on current position (!)");
116eb82bfcdSArmin Le Grand                                 nPos++;
117eb82bfcdSArmin Le Grand                             }
1180906e779SArmin Le Grand                         }
1199d56236fSArmin Le Grand 
120eb82bfcdSArmin Le Grand                         for(sal_uInt32 a(0); a < aParts.size(); a++)
1210906e779SArmin Le Grand                         {
122eb82bfcdSArmin Le Grand                             const rtl::OUString aNewConcatenated(
123eb82bfcdSArmin Le Grand                                 rtl::OUString::createFromAscii(".") +
124eb82bfcdSArmin Le Grand                                 aParts[a] +
125eb82bfcdSArmin Le Grand                                 aConcatenated);
126eb82bfcdSArmin Le Grand 
127eb82bfcdSArmin Le Grand                             if(pParent)
128eb82bfcdSArmin Le Grand                             {
129eb82bfcdSArmin Le Grand                                 // check for combined selectors at parent firstso that higher specificity will be in front
130eb82bfcdSArmin Le Grand                                 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated);
131eb82bfcdSArmin Le Grand                             }
132eb82bfcdSArmin Le Grand 
133eb82bfcdSArmin Le Grand                             const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated);
134eb82bfcdSArmin Le Grand 
135eb82bfcdSArmin Le Grand                             if(pNew)
136eb82bfcdSArmin Le Grand                             {
137eb82bfcdSArmin Le Grand                                 // add CssStyle if found
138eb82bfcdSArmin Le Grand                                 maCssStyleVector.push_back(pNew);
139eb82bfcdSArmin Le Grand                             }
1400906e779SArmin Le Grand                         }
14150b37974SArmin Le Grand                     }
14250b37974SArmin Le Grand                 }
1439d01bcdeSArmin Le Grand 
1449d01bcdeSArmin Le Grand                 // check for class-dependent references to CssStyles
1459d01bcdeSArmin Le Grand                 if(rClassStr.getLength())
1469d01bcdeSArmin Le Grand                 {
147eb82bfcdSArmin Le Grand                     rtl::OUString aNewConcatenated(aConcatenated);
1489d01bcdeSArmin Le Grand 
149eb82bfcdSArmin Le Grand                     if(!rCurrent.getId() && !rCurrent.getClass() && 0 == aConcatenated.indexOf(rClassStr))
1509d01bcdeSArmin Le Grand                     {
151eb82bfcdSArmin Le Grand                         // no new CssStyle Selector and already starts with rClassStr, do not concatenate;
152eb82bfcdSArmin Le Grand                         // we pass an 'empty' node (in the sense of CssStyle Selector)
153eb82bfcdSArmin Le Grand                     }
154eb82bfcdSArmin Le Grand                     else
155eb82bfcdSArmin Le Grand                     {
156eb82bfcdSArmin Le Grand                         aNewConcatenated = rClassStr + aConcatenated;
157eb82bfcdSArmin Le Grand                     }
158eb82bfcdSArmin Le Grand 
159eb82bfcdSArmin Le Grand                     if(pParent)
160eb82bfcdSArmin Le Grand                     {
161eb82bfcdSArmin Le Grand                         // check for combined selectors at parent firstso that higher specificity will be in front
162eb82bfcdSArmin Le Grand                         fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated);
1639d01bcdeSArmin Le Grand                     }
164eb82bfcdSArmin Le Grand 
165eb82bfcdSArmin Le Grand                     const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated);
166eb82bfcdSArmin Le Grand 
167eb82bfcdSArmin Le Grand                     if(pNew)
168eb82bfcdSArmin Le Grand                     {
169eb82bfcdSArmin Le Grand                         // add CssStyle if found
170eb82bfcdSArmin Le Grand                         maCssStyleVector.push_back(pNew);
171eb82bfcdSArmin Le Grand                     }
1729d01bcdeSArmin Le Grand                 }
1739d01bcdeSArmin Le Grand             }
1749d01bcdeSArmin Le Grand         }
1759d01bcdeSArmin Le Grand 
176eb82bfcdSArmin Le Grand         void SvgNode::fillCssStyleVector(const rtl::OUString& rClassStr)
177eb82bfcdSArmin Le Grand         {
178eb82bfcdSArmin Le Grand             OSL_ENSURE(!mbCssStyleVectorBuilt, "OOps, fillCssStyleVector called double ?!?");
179eb82bfcdSArmin Le Grand             mbCssStyleVectorBuilt = true;
180eb82bfcdSArmin Le Grand 
181eb82bfcdSArmin Le Grand             // #125293# If we have CssStyles we need to buuild a linked list of SvgStyleAttributes
182eb82bfcdSArmin Le Grand             // which represent this for the current object. There are various methods to
183eb82bfcdSArmin Le Grand             // specify CssStyles which need to be taken into account in a given order:
184eb82bfcdSArmin Le Grand             // - local CssStyle (independent from global CssStyles at SvgDocument)
185eb82bfcdSArmin Le Grand             // - 'id' CssStyle
186eb82bfcdSArmin Le Grand             // - 'class' CssStyle(s)
187eb82bfcdSArmin Le Grand             // - type-dependent elements (e..g. 'rect' for all rect elements)
188eb82bfcdSArmin Le Grand             // - local attributes (rOriginal)
189eb82bfcdSArmin Le Grand             // - inherited attributes (up the hierarchy)
190eb82bfcdSArmin Le Grand             // The first four will be collected in maCssStyleVector for the current element
191eb82bfcdSArmin Le Grand             // (once, this will not change) and be linked in the needed order using the
192eb82bfcdSArmin Le Grand             // get/setCssStyleParent at the SvgStyleAttributes which will be used preferred in
193eb82bfcdSArmin Le Grand             // member evaluation over the existing parent hierarchy
194eb82bfcdSArmin Le Grand 
195eb82bfcdSArmin Le Grand             // check for local CssStyle with highest priority
196eb82bfcdSArmin Le Grand             if(mpLocalCssStyle)
197eb82bfcdSArmin Le Grand             {
198eb82bfcdSArmin Le Grand                 // if we have one, use as first entry
199eb82bfcdSArmin Le Grand                 maCssStyleVector.push_back(mpLocalCssStyle);
200eb82bfcdSArmin Le Grand             }
201eb82bfcdSArmin Le Grand 
202eb82bfcdSArmin Le Grand             // check the hierarchy for concatenated patterns of Selectors
203eb82bfcdSArmin Le Grand             fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *this, rtl::OUString());
204eb82bfcdSArmin Le Grand         }
205eb82bfcdSArmin Le Grand 
2069d01bcdeSArmin Le Grand         const SvgStyleAttributes* SvgNode::checkForCssStyle(const rtl::OUString& rClassStr, const SvgStyleAttributes& rOriginal) const
2079d01bcdeSArmin Le Grand         {
2089d01bcdeSArmin Le Grand             if(!mbCssStyleVectorBuilt)
2099d01bcdeSArmin Le Grand             {
2109d01bcdeSArmin Le Grand                 // build needed CssStyleVector for local node
2119d01bcdeSArmin Le Grand                 const_cast< SvgNode* >(this)->fillCssStyleVector(rClassStr);
21250b37974SArmin Le Grand             }
21350b37974SArmin Le Grand 
2149d56236fSArmin Le Grand             if(maCssStyleVector.empty())
2159d56236fSArmin Le Grand             {
2169d01bcdeSArmin Le Grand                 // return given original if no CssStlyes found
2179d56236fSArmin Le Grand                 return &rOriginal;
2189d56236fSArmin Le Grand             }
2199d56236fSArmin Le Grand             else
22050b37974SArmin Le Grand             {
2219d56236fSArmin Le Grand                 // #125293# rOriginal will be the last element in the linked list; use no CssStyleParent
2229d56236fSArmin Le Grand                 // there (reset it) to ensure that the parent hierarchy will be used when it's base
2239d56236fSArmin Le Grand                 // is referenced. This new chaning inserts the CssStyles before the original style,
2249d56236fSArmin Le Grand                 // this makes the whole process much safer since the original style when used will
2259d56236fSArmin Le Grand                 // be not different to the situation without CssStyles; thus loops which may be caused
2269d56236fSArmin Le Grand                 // by trying to use the parent hierarchy of the owner of the style will be avoided
2279d56236fSArmin Le Grand                 // already in this mechanism. It's still good to keep the supportsParentStyle
2289d56236fSArmin Le Grand                 // from #125258# in place, though.
2299d56236fSArmin Le Grand                 // This chain building using pointers will be done every time when checkForCssStyle
2309d56236fSArmin Le Grand                 // is used (not the search, only the chaining). This is needed since the CssStyles
2319d56236fSArmin Le Grand                 // themselves will be potentially used multiple times. It is not expensive since it's
2329d56236fSArmin Le Grand                 // only changing some pointers.
2339d56236fSArmin Le Grand                 // The alternative would be to create the style hierarchy for every element (or even
2349d56236fSArmin Le Grand                 // for the element containing the hierarchy) in a vector of pointers and to use that.
2359d56236fSArmin Le Grand                 // Resetting the CssStyleParent on rOriginal is probably not needeed
2369d56236fSArmin Le Grand                 // but simply safer to do.
2379d56236fSArmin Le Grand                 const_cast< SvgStyleAttributes& >(rOriginal).setCssStyleParent(0);
2389d56236fSArmin Le Grand 
2399d56236fSArmin Le Grand                 // loop over the existing CssStyles and link them. There is a first one, take
2409d56236fSArmin Le Grand                 // as current
2419d56236fSArmin Le Grand                 SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(maCssStyleVector[0]);
2429d56236fSArmin Le Grand 
2439d56236fSArmin Le Grand                 for(sal_uInt32 a(1); a < maCssStyleVector.size(); a++)
24450b37974SArmin Le Grand                 {
2457b027e49SArmin Le Grand                     SvgStyleAttributes* pNext = const_cast< SvgStyleAttributes* >(maCssStyleVector[a]);
24650b37974SArmin Le Grand 
2477b027e49SArmin Le Grand                     pCurrent->setCssStyleParent(pNext);
2487b027e49SArmin Le Grand                     pCurrent = pNext;
24950b37974SArmin Le Grand                 }
2507b027e49SArmin Le Grand 
2519d56236fSArmin Le Grand                 // pCurrent is the last used CssStyle, let it point to the original style
2529d56236fSArmin Le Grand                 pCurrent->setCssStyleParent(&rOriginal);
2539d56236fSArmin Le Grand 
2549d56236fSArmin Le Grand                 // return 1st CssStyle as style chain start element (only for the
2559d56236fSArmin Le Grand                 // local element, still no hierarchy used here)
2569d56236fSArmin Le Grand                 return maCssStyleVector[0];
2579d56236fSArmin Le Grand             }
25850b37974SArmin Le Grand         }
25950b37974SArmin Le Grand 
260ddde725dSArmin Le Grand         SvgNode::SvgNode(
261ddde725dSArmin Le Grand             SVGToken aType,
262ddde725dSArmin Le Grand             SvgDocument& rDocument,
263ddde725dSArmin Le Grand             SvgNode* pParent)
264ddde725dSArmin Le Grand         :   maType(aType),
265ddde725dSArmin Le Grand             mrDocument(rDocument),
266ddde725dSArmin Le Grand             mpParent(pParent),
267ddde725dSArmin Le Grand             mpAlternativeParent(0),
268ddde725dSArmin Le Grand             maChildren(),
269ddde725dSArmin Le Grand             mpId(0),
270ddde725dSArmin Le Grand             mpClass(0),
27150b37974SArmin Le Grand             maXmlSpace(XmlSpace_notset),
272a275c134SArmin Le Grand             maDisplay(Display_inline),
2739d01bcdeSArmin Le Grand             maCssStyleVector(),
2749d01bcdeSArmin Le Grand             mpLocalCssStyle(0),
2759d01bcdeSArmin Le Grand             mbCssStyleVectorBuilt(false)
276ddde725dSArmin Le Grand         {
277ddde725dSArmin Le Grand             OSL_ENSURE(SVGTokenUnknown != maType, "SvgNode with unknown type created (!)");
278ddde725dSArmin Le Grand 
279ddde725dSArmin Le Grand             if(pParent)
280ddde725dSArmin Le Grand             {
281ddde725dSArmin Le Grand                 pParent->maChildren.push_back(this);
282ddde725dSArmin Le Grand             }
283ddde725dSArmin Le Grand             else
284ddde725dSArmin Le Grand             {
285ddde725dSArmin Le Grand #ifdef DBG_UTIL
286ddde725dSArmin Le Grand                 if(SVGTokenSvg != getType())
287ddde725dSArmin Le Grand                 {
288ddde725dSArmin Le Grand                     OSL_ENSURE(false, "No parent for this node (!)");
289ddde725dSArmin Le Grand                 }
290ddde725dSArmin Le Grand #endif
291ddde725dSArmin Le Grand             }
292ddde725dSArmin Le Grand         }
293ddde725dSArmin Le Grand 
294ddde725dSArmin Le Grand         SvgNode::~SvgNode()
295ddde725dSArmin Le Grand         {
296ddde725dSArmin Le Grand             while(maChildren.size())
297ddde725dSArmin Le Grand             {
298ddde725dSArmin Le Grand                 delete maChildren[maChildren.size() - 1];
299ddde725dSArmin Le Grand                 maChildren.pop_back();
300ddde725dSArmin Le Grand             }
301ddde725dSArmin Le Grand 
3029d01bcdeSArmin Le Grand             if(mpId)
3039d01bcdeSArmin Le Grand             {
3049d01bcdeSArmin Le Grand                 delete mpId;
3059d01bcdeSArmin Le Grand             }
3069d01bcdeSArmin Le Grand 
3079d01bcdeSArmin Le Grand             if(mpClass)
3089d01bcdeSArmin Le Grand             {
3099d01bcdeSArmin Le Grand                 delete mpClass;
3109d01bcdeSArmin Le Grand             }
3119d01bcdeSArmin Le Grand 
3129d01bcdeSArmin Le Grand             if(mpLocalCssStyle)
3139d01bcdeSArmin Le Grand             {
3149d01bcdeSArmin Le Grand                 delete mpLocalCssStyle;
3159d01bcdeSArmin Le Grand             }
3169d01bcdeSArmin Le Grand         }
3179d01bcdeSArmin Le Grand 
3189d01bcdeSArmin Le Grand         void SvgNode::readLocalCssStyle(const rtl::OUString& aContent)
3199d01bcdeSArmin Le Grand         {
3209d01bcdeSArmin Le Grand             if(!mpLocalCssStyle)
3219d01bcdeSArmin Le Grand             {
3229d01bcdeSArmin Le Grand                 // create LocalCssStyle if needed but not yet added
3239d01bcdeSArmin Le Grand                 mpLocalCssStyle = new SvgStyleAttributes(*this);
3249d01bcdeSArmin Le Grand             }
3259d01bcdeSArmin Le Grand             else
3269d01bcdeSArmin Le Grand             {
3279d01bcdeSArmin Le Grand                 // 2nd fill would be an error
3289d01bcdeSArmin Le Grand                 OSL_ENSURE(false, "Svg node has two local CssStyles, this may lead to problems (!)");
3299d01bcdeSArmin Le Grand             }
3309d01bcdeSArmin Le Grand 
3319d01bcdeSArmin Le Grand             if(mpLocalCssStyle)
3329d01bcdeSArmin Le Grand             {
3339d01bcdeSArmin Le Grand                 // parse and set values to it
334*52cb04b8SArmin Le Grand                 mpLocalCssStyle->readCssStyle(aContent);
3359d01bcdeSArmin Le Grand             }
3369d01bcdeSArmin Le Grand             else
3379d01bcdeSArmin Le Grand             {
3389d01bcdeSArmin Le Grand                 OSL_ENSURE(false, "Could not get/create a local CssStyle for a node (!)");
3399d01bcdeSArmin Le Grand             }
340ddde725dSArmin Le Grand         }
341ddde725dSArmin Le Grand 
342ddde725dSArmin Le Grand         void SvgNode::parseAttributes(const com::sun::star::uno::Reference< com::sun::star::xml::sax::XAttributeList >& xAttribs)
343ddde725dSArmin Le Grand         {
3449d01bcdeSArmin Le Grand             // no longer need to pre-sort moving 'style' entries to the back so that
3459d01bcdeSArmin Le Grand             // values get overwritten - that was the previous, not complete solution for
3469d01bcdeSArmin Le Grand             // handling the priorities between svg and Css properties
347ddde725dSArmin Le Grand             const sal_uInt32 nAttributes(xAttribs->getLength());
348175cd092SArmin Le Grand 
349ddde725dSArmin Le Grand             for(sal_uInt32 a(0); a < nAttributes; a++)
350ddde725dSArmin Le Grand             {
351ddde725dSArmin Le Grand                 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(a));
352*52cb04b8SArmin Le Grand                 const SVGToken aSVGToken(StrToSVGToken(aTokenName, false));
353175cd092SArmin Le Grand 
3549d01bcdeSArmin Le Grand                 parseAttribute(aTokenName, aSVGToken, xAttribs->getValueByIndex(a));
355ddde725dSArmin Le Grand             }
356ddde725dSArmin Le Grand         }
357ddde725dSArmin Le Grand 
35801e92ad6SArmin Le Grand         Display getDisplayFromContent(const rtl::OUString& aContent)
35901e92ad6SArmin Le Grand         {
36001e92ad6SArmin Le Grand             if(aContent.getLength())
36101e92ad6SArmin Le Grand             {
362e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrInline(rtl::OUString::createFromAscii("inline"));
363e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrBlock(rtl::OUString::createFromAscii("block"));
364e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrList_item(rtl::OUString::createFromAscii("list-item"));
365e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrRun_in(rtl::OUString::createFromAscii("run-in"));
366e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrCompact(rtl::OUString::createFromAscii("compact"));
367e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrMarker(rtl::OUString::createFromAscii("marker"));
368e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrTable(rtl::OUString::createFromAscii("table"));
369e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrInline_table(rtl::OUString::createFromAscii("inline-table"));
370e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrTable_row_group(rtl::OUString::createFromAscii("table-row-group"));
371e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrTable_header_group(rtl::OUString::createFromAscii("table-header-group"));
372e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrTable_footer_group(rtl::OUString::createFromAscii("table-footer-group"));
373e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrTable_row(rtl::OUString::createFromAscii("table-row"));
374e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrTable_column_group(rtl::OUString::createFromAscii("table-column-group"));
375e92bb418SOliver-Rainer Wittmann                 static rtl::OUString aStrTable_column(rtl::OUString::createFromAscii("table-column"));
37601e92ad6SArmin Le Grand                 static rtl::OUString aStrTable_cell(rtl::OUString::createFromAscii("table-cell"));
37701e92ad6SArmin Le Grand                 static rtl::OUString aStrTable_caption(rtl::OUString::createFromAscii("table-caption"));
37801e92ad6SArmin Le Grand                 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
37901e92ad6SArmin Le Grand                 static rtl::OUString aStrInherit(rtl::OUString::createFromAscii("inherit"));
38001e92ad6SArmin Le Grand 
38101e92ad6SArmin Le Grand                 if(aContent.match(aStrInline))
38201e92ad6SArmin Le Grand                 {
38301e92ad6SArmin Le Grand                     return Display_inline;
38401e92ad6SArmin Le Grand                 }
38501e92ad6SArmin Le Grand                 else if(aContent.match(aStrNone))
38601e92ad6SArmin Le Grand                 {
38701e92ad6SArmin Le Grand                     return Display_none;
38801e92ad6SArmin Le Grand                 }
38901e92ad6SArmin Le Grand                 else if(aContent.match(aStrInherit))
39001e92ad6SArmin Le Grand                 {
39101e92ad6SArmin Le Grand                     return Display_inherit;
39201e92ad6SArmin Le Grand                 }
393e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrBlock))
39401e92ad6SArmin Le Grand                 {
39501e92ad6SArmin Le Grand                     return Display_block;
39601e92ad6SArmin Le Grand                 }
397e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrList_item))
39801e92ad6SArmin Le Grand                 {
39901e92ad6SArmin Le Grand                     return Display_list_item;
40001e92ad6SArmin Le Grand                 }
401e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrRun_in))
40201e92ad6SArmin Le Grand                 {
40301e92ad6SArmin Le Grand                     return Display_run_in;
40401e92ad6SArmin Le Grand                 }
405e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrCompact))
40601e92ad6SArmin Le Grand                 {
40701e92ad6SArmin Le Grand                     return Display_compact;
40801e92ad6SArmin Le Grand                 }
409e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrMarker))
41001e92ad6SArmin Le Grand                 {
41101e92ad6SArmin Le Grand                     return Display_marker;
41201e92ad6SArmin Le Grand                 }
413e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrTable))
41401e92ad6SArmin Le Grand                 {
41501e92ad6SArmin Le Grand                     return Display_table;
41601e92ad6SArmin Le Grand                 }
417e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrInline_table))
41801e92ad6SArmin Le Grand                 {
41901e92ad6SArmin Le Grand                     return Display_inline_table;
42001e92ad6SArmin Le Grand                 }
421e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrTable_row_group))
42201e92ad6SArmin Le Grand                 {
42301e92ad6SArmin Le Grand                     return Display_table_row_group;
42401e92ad6SArmin Le Grand                 }
425e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrTable_header_group))
42601e92ad6SArmin Le Grand                 {
42701e92ad6SArmin Le Grand                     return Display_table_header_group;
42801e92ad6SArmin Le Grand                 }
429e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrTable_footer_group))
43001e92ad6SArmin Le Grand                 {
43101e92ad6SArmin Le Grand                     return Display_table_footer_group;
43201e92ad6SArmin Le Grand                 }
433e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrTable_row))
43401e92ad6SArmin Le Grand                 {
43501e92ad6SArmin Le Grand                     return Display_table_row;
43601e92ad6SArmin Le Grand                 }
437e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrTable_column_group))
43801e92ad6SArmin Le Grand                 {
43901e92ad6SArmin Le Grand                     return Display_table_column_group;
44001e92ad6SArmin Le Grand                 }
441e92bb418SOliver-Rainer Wittmann                 else if(aContent.match(aStrTable_column))
44201e92ad6SArmin Le Grand                 {
44301e92ad6SArmin Le Grand                     return Display_table_column;
44401e92ad6SArmin Le Grand                 }
44501e92ad6SArmin Le Grand                 else if(aContent.match(aStrTable_cell))
44601e92ad6SArmin Le Grand                 {
44701e92ad6SArmin Le Grand                     return Display_table_cell;
44801e92ad6SArmin Le Grand                 }
44901e92ad6SArmin Le Grand                 else if(aContent.match(aStrTable_caption))
45001e92ad6SArmin Le Grand                 {
45101e92ad6SArmin Le Grand                     return Display_table_caption;
45201e92ad6SArmin Le Grand                 }
45301e92ad6SArmin Le Grand             }
45401e92ad6SArmin Le Grand 
45501e92ad6SArmin Le Grand             // return the default
45601e92ad6SArmin Le Grand             return Display_inline;
45701e92ad6SArmin Le Grand         }
45801e92ad6SArmin Le Grand 
459e2bf1e9dSArmin Le Grand         void SvgNode::parseAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent)
460ddde725dSArmin Le Grand         {
461ddde725dSArmin Le Grand             switch(aSVGToken)
462ddde725dSArmin Le Grand             {
463ddde725dSArmin Le Grand                 case SVGTokenId:
464ddde725dSArmin Le Grand                 {
465ddde725dSArmin Le Grand                     if(aContent.getLength())
466ddde725dSArmin Le Grand                     {
467ddde725dSArmin Le Grand                         setId(&aContent);
468ddde725dSArmin Le Grand                     }
469ddde725dSArmin Le Grand                     break;
470ddde725dSArmin Le Grand                 }
471ddde725dSArmin Le Grand                 case SVGTokenClass:
472ddde725dSArmin Le Grand                 {
473ddde725dSArmin Le Grand                     if(aContent.getLength())
474ddde725dSArmin Le Grand                     {
475ddde725dSArmin Le Grand                         setClass(&aContent);
476ddde725dSArmin Le Grand                     }
477ddde725dSArmin Le Grand                     break;
478ddde725dSArmin Le Grand                 }
479ddde725dSArmin Le Grand                 case SVGTokenXmlSpace:
480ddde725dSArmin Le Grand                 {
481ddde725dSArmin Le Grand                     if(aContent.getLength())
482ddde725dSArmin Le Grand                     {
483ddde725dSArmin Le Grand                         static rtl::OUString aStrDefault(rtl::OUString::createFromAscii("default"));
484ddde725dSArmin Le Grand                         static rtl::OUString aStrPreserve(rtl::OUString::createFromAscii("preserve"));
485ddde725dSArmin Le Grand 
486ddde725dSArmin Le Grand                         if(aContent.match(aStrDefault))
487ddde725dSArmin Le Grand                         {
488ddde725dSArmin Le Grand                             setXmlSpace(XmlSpace_default);
489ddde725dSArmin Le Grand                         }
490ddde725dSArmin Le Grand                         else if(aContent.match(aStrPreserve))
491ddde725dSArmin Le Grand                         {
492ddde725dSArmin Le Grand                             setXmlSpace(XmlSpace_preserve);
493ddde725dSArmin Le Grand                         }
494ddde725dSArmin Le Grand                     }
495ddde725dSArmin Le Grand                     break;
496ddde725dSArmin Le Grand                 }
497a275c134SArmin Le Grand                 case SVGTokenDisplay:
498a275c134SArmin Le Grand                 {
499a275c134SArmin Le Grand                     if(aContent.getLength())
500a275c134SArmin Le Grand                     {
50101e92ad6SArmin Le Grand                         setDisplay(getDisplayFromContent(aContent));
502a275c134SArmin Le Grand                     }
503a275c134SArmin Le Grand                     break;
504a275c134SArmin Le Grand                 }
505e2bf1e9dSArmin Le Grand                 default:
506e2bf1e9dSArmin Le Grand                 {
507e2bf1e9dSArmin Le Grand                     break;
508e2bf1e9dSArmin Le Grand                 }
509ddde725dSArmin Le Grand             }
510ddde725dSArmin Le Grand         }
511ddde725dSArmin Le Grand 
512ddde725dSArmin Le Grand         void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
513ddde725dSArmin Le Grand         {
514a275c134SArmin Le Grand             if(Display_none == getDisplay())
515a275c134SArmin Le Grand             {
516a275c134SArmin Le Grand                 return;
517a275c134SArmin Le Grand             }
518a275c134SArmin Le Grand 
519ddde725dSArmin Le Grand             if(!bReferenced)
520ddde725dSArmin Le Grand             {
521ddde725dSArmin Le Grand                 if(SVGTokenDefs == getType() ||
522ddde725dSArmin Le Grand                     SVGTokenSymbol == getType() ||
523ddde725dSArmin Le Grand                     SVGTokenClipPathNode == getType() ||
524ddde725dSArmin Le Grand                     SVGTokenMask == getType() ||
525ddde725dSArmin Le Grand                     SVGTokenMarker == getType() ||
526ddde725dSArmin Le Grand                     SVGTokenPattern == getType())
527ddde725dSArmin Le Grand                 {
528ddde725dSArmin Le Grand                     // do not decompose defs or symbol nodes (these hold only style-like
529ddde725dSArmin Le Grand                     // objects which may be used by referencing them) except when doing
530ddde725dSArmin Le Grand                     // so controlled referenced
531ddde725dSArmin Le Grand 
532ddde725dSArmin Le Grand                     // also do not decompose ClipPaths and Masks. These should be embedded
533ddde725dSArmin Le Grand                     // in a defs node (which gets not decomposed by itself), but you never
534ddde725dSArmin Le Grand                     // know
535ddde725dSArmin Le Grand 
536ddde725dSArmin Le Grand                     // also not directly used are Markers and Patterns, only indirecty used
537ddde725dSArmin Le Grand                     // by reference
538a275c134SArmin Le Grand 
539a275c134SArmin Le Grand                     // #121656# also do not decompose nodes which have display="none" set
540a275c134SArmin Le Grand                     // as property
541ddde725dSArmin Le Grand                     return;
542ddde725dSArmin Le Grand                 }
543ddde725dSArmin Le Grand             }
544ddde725dSArmin Le Grand 
545ddde725dSArmin Le Grand             const SvgNodeVector& rChildren = getChildren();
546ddde725dSArmin Le Grand 
547ddde725dSArmin Le Grand             if(!rChildren.empty())
548ddde725dSArmin Le Grand             {
549ddde725dSArmin Le Grand                 const sal_uInt32 nCount(rChildren.size());
550ddde725dSArmin Le Grand 
551ddde725dSArmin Le Grand                 for(sal_uInt32 a(0); a < nCount; a++)
552ddde725dSArmin Le Grand                 {
553ddde725dSArmin Le Grand                     SvgNode* pCandidate = rChildren[a];
554ddde725dSArmin Le Grand 
555a275c134SArmin Le Grand                     if(pCandidate && Display_none != pCandidate->getDisplay())
556ddde725dSArmin Le Grand                     {
557ddde725dSArmin Le Grand                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
558ddde725dSArmin Le Grand 
559ddde725dSArmin Le Grand                         pCandidate->decomposeSvgNode(aNewTarget, bReferenced);
560ddde725dSArmin Le Grand 
561ddde725dSArmin Le Grand                         if(aNewTarget.hasElements())
562ddde725dSArmin Le Grand                         {
563ddde725dSArmin Le Grand                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
564ddde725dSArmin Le Grand                         }
565ddde725dSArmin Le Grand                     }
566ddde725dSArmin Le Grand                     else
567ddde725dSArmin Le Grand                     {
568ddde725dSArmin Le Grand                         OSL_ENSURE(false, "Null-Pointer in child node list (!)");
569ddde725dSArmin Le Grand                     }
570ddde725dSArmin Le Grand                 }
571025b0597SArmin Le Grand 
572025b0597SArmin Le Grand                 if(rTarget.hasElements())
573025b0597SArmin Le Grand                 {
574025b0597SArmin Le Grand                     const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
575025b0597SArmin Le Grand 
576025b0597SArmin Le Grand                     if(pStyles)
577025b0597SArmin Le Grand                     {
578025b0597SArmin Le Grand                         // check if we have Title or Desc
579025b0597SArmin Le Grand                         const rtl::OUString& rTitle = pStyles->getTitle();
580025b0597SArmin Le Grand                         const rtl::OUString& rDesc = pStyles->getDesc();
581025b0597SArmin Le Grand 
582025b0597SArmin Le Grand                         if(rTitle.getLength() || rDesc.getLength())
583025b0597SArmin Le Grand                         {
584025b0597SArmin Le Grand                             // default object name is empty
585025b0597SArmin Le Grand                             rtl::OUString aObjectName;
586025b0597SArmin Le Grand 
587025b0597SArmin Le Grand                             // use path as object name when outmost element
588025b0597SArmin Le Grand                             if(SVGTokenSvg == getType())
589025b0597SArmin Le Grand                             {
590025b0597SArmin Le Grand                                 aObjectName = getDocument().getAbsolutePath();
591172c67b2SArmin Le Grand 
592172c67b2SArmin Le Grand                                 if(aObjectName.getLength())
593172c67b2SArmin Le Grand                                 {
594172c67b2SArmin Le Grand                             		INetURLObject aURL(aObjectName);
595172c67b2SArmin Le Grand 
596172c67b2SArmin Le Grand                                     aObjectName = aURL.getName(
597172c67b2SArmin Le Grand                                         INetURLObject::LAST_SEGMENT,
598172c67b2SArmin Le Grand                                         true,
599172c67b2SArmin Le Grand                                         INetURLObject::DECODE_WITH_CHARSET);
600172c67b2SArmin Le Grand                                 }
601025b0597SArmin Le Grand                             }
602025b0597SArmin Le Grand 
603025b0597SArmin Le Grand                             // pack in ObjectInfoPrimitive2D group
604025b0597SArmin Le Grand                             const drawinglayer::primitive2d::Primitive2DReference xRef(
605025b0597SArmin Le Grand                                 new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
606025b0597SArmin Le Grand                                     rTarget,
607025b0597SArmin Le Grand                                     aObjectName,
608025b0597SArmin Le Grand                                     rTitle,
609025b0597SArmin Le Grand                                     rDesc));
610025b0597SArmin Le Grand 
611025b0597SArmin Le Grand                             rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
612025b0597SArmin Le Grand                         }
613025b0597SArmin Le Grand                     }
614025b0597SArmin Le Grand                 }
615ddde725dSArmin Le Grand             }
616ddde725dSArmin Le Grand         }
617ddde725dSArmin Le Grand 
618e92bb418SOliver-Rainer Wittmann         const basegfx::B2DRange SvgNode::getCurrentViewPort() const
619ddde725dSArmin Le Grand         {
620ddde725dSArmin Le Grand             if(getParent())
621ddde725dSArmin Le Grand             {
622ddde725dSArmin Le Grand                 return getParent()->getCurrentViewPort();
623ddde725dSArmin Le Grand             }
624ddde725dSArmin Le Grand             else
625ddde725dSArmin Le Grand             {
626e92bb418SOliver-Rainer Wittmann                 return basegfx::B2DRange(); // return empty B2DRange
627ddde725dSArmin Le Grand             }
628ddde725dSArmin Le Grand         }
629ddde725dSArmin Le Grand 
630ddde725dSArmin Le Grand         double SvgNode::getCurrentFontSize() const
631ddde725dSArmin Le Grand         {
632ddde725dSArmin Le Grand             if(getSvgStyleAttributes())
633ddde725dSArmin Le Grand             {
634ddde725dSArmin Le Grand                 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate);
635ddde725dSArmin Le Grand             }
636ddde725dSArmin Le Grand             else if(getParent())
637ddde725dSArmin Le Grand             {
638ddde725dSArmin Le Grand                 return getParent()->getCurrentFontSize();
639ddde725dSArmin Le Grand             }
640ddde725dSArmin Le Grand             else
641ddde725dSArmin Le Grand             {
642ddde725dSArmin Le Grand                 return 0.0;
643ddde725dSArmin Le Grand             }
644ddde725dSArmin Le Grand         }
645ddde725dSArmin Le Grand 
646ddde725dSArmin Le Grand         double SvgNode::getCurrentXHeight() const
647ddde725dSArmin Le Grand         {
648ddde725dSArmin Le Grand             if(getSvgStyleAttributes())
649ddde725dSArmin Le Grand             {
650ddde725dSArmin Le Grand                 // for XHeight, use FontSize currently
651ddde725dSArmin Le Grand                 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate);
652ddde725dSArmin Le Grand             }
653ddde725dSArmin Le Grand             else if(getParent())
654ddde725dSArmin Le Grand             {
655ddde725dSArmin Le Grand                 return getParent()->getCurrentXHeight();
656ddde725dSArmin Le Grand             }
657ddde725dSArmin Le Grand             else
658ddde725dSArmin Le Grand             {
659ddde725dSArmin Le Grand                 return 0.0;
660ddde725dSArmin Le Grand             }
661ddde725dSArmin Le Grand         }
662ddde725dSArmin Le Grand 
663ddde725dSArmin Le Grand         void SvgNode::setId(const rtl::OUString* pfId)
664ddde725dSArmin Le Grand         {
665ddde725dSArmin Le Grand             if(mpId)
666ddde725dSArmin Le Grand             {
667ddde725dSArmin Le Grand                 mrDocument.removeSvgNodeFromMapper(*mpId);
668ddde725dSArmin Le Grand                 delete mpId;
669ddde725dSArmin Le Grand                 mpId = 0;
670ddde725dSArmin Le Grand             }
671ddde725dSArmin Le Grand 
672ddde725dSArmin Le Grand             if(pfId)
673ddde725dSArmin Le Grand             {
674ddde725dSArmin Le Grand                 mpId = new rtl::OUString(*pfId);
675ddde725dSArmin Le Grand                 mrDocument.addSvgNodeToMapper(*mpId, *this);
676ddde725dSArmin Le Grand             }
677ddde725dSArmin Le Grand         }
678ddde725dSArmin Le Grand 
679ddde725dSArmin Le Grand         void SvgNode::setClass(const rtl::OUString* pfClass)
680ddde725dSArmin Le Grand         {
681ddde725dSArmin Le Grand             if(mpClass)
682ddde725dSArmin Le Grand             {
683ddde725dSArmin Le Grand                 mrDocument.removeSvgNodeFromMapper(*mpClass);
684ddde725dSArmin Le Grand                 delete mpClass;
685ddde725dSArmin Le Grand                 mpClass = 0;
686ddde725dSArmin Le Grand             }
687ddde725dSArmin Le Grand 
688ddde725dSArmin Le Grand             if(pfClass)
689ddde725dSArmin Le Grand             {
690ddde725dSArmin Le Grand                 mpClass = new rtl::OUString(*pfClass);
691ddde725dSArmin Le Grand                 mrDocument.addSvgNodeToMapper(*mpClass, *this);
692ddde725dSArmin Le Grand             }
693ddde725dSArmin Le Grand         }
694ddde725dSArmin Le Grand 
695ddde725dSArmin Le Grand         XmlSpace SvgNode::getXmlSpace() const
696ddde725dSArmin Le Grand         {
697ddde725dSArmin Le Grand             if(maXmlSpace != XmlSpace_notset)
698ddde725dSArmin Le Grand             {
699ddde725dSArmin Le Grand                 return maXmlSpace;
700ddde725dSArmin Le Grand             }
701ddde725dSArmin Le Grand 
702ddde725dSArmin Le Grand             if(getParent())
703ddde725dSArmin Le Grand             {
704ddde725dSArmin Le Grand                 return getParent()->getXmlSpace();
705ddde725dSArmin Le Grand             }
706ddde725dSArmin Le Grand 
707ddde725dSArmin Le Grand             // default is XmlSpace_default
708ddde725dSArmin Le Grand             return XmlSpace_default;
709ddde725dSArmin Le Grand         }
710ddde725dSArmin Le Grand 
711ddde725dSArmin Le Grand     } // end of namespace svgreader
712ddde725dSArmin Le Grand } // end of namespace svgio
713ddde725dSArmin Le Grand 
714ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
715ddde725dSArmin Le Grand // eof
716