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/svgimagenode.hxx>
26 #include <svgio/svgreader/svgdocument.hxx>
27 #include <sax/tools/converter.hxx>
28 #include <tools/stream.hxx>
29 #include <vcl/bitmapex.hxx>
30 #include <svtools/filter.hxx>
31 #include <basegfx/matrix/b2dhommatrixtools.hxx>
32 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
37 #include <basegfx/polygon/b2dpolygon.hxx>
38 #include <rtl/uri.hxx>
39 #include <drawinglayer/geometry/viewinformation2d.hxx>
40 
41 //////////////////////////////////////////////////////////////////////////////
42 
43 namespace svgio
44 {
45     namespace svgreader
46     {
47         SvgImageNode::SvgImageNode(
48             SvgDocument& rDocument,
49             SvgNode* pParent)
50         :   SvgNode(SVGTokenRect, rDocument, pParent),
51             maSvgStyleAttributes(*this),
52             maSvgAspectRatio(),
53             mpaTransform(0),
54             maX(0),
55             maY(0),
56             maWidth(0),
57             maHeight(0),
58             maXLink(),
59             maUrl(),
60             maMimeType(),
61             maData()
62         {
63         }
64 
65         SvgImageNode::~SvgImageNode()
66         {
67             if(mpaTransform) delete mpaTransform;
68         }
69 
70         const SvgStyleAttributes* SvgImageNode::getSvgStyleAttributes() const
71         {
72             static rtl::OUString aClassStr(rtl::OUString::createFromAscii("image"));
73 
74             return checkForCssStyle(aClassStr, maSvgStyleAttributes);
75         }
76 
77         void SvgImageNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent)
78         {
79             // call parent
80             SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
81 
82             // read style attributes
83             maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent);
84 
85             // parse own
86             switch(aSVGToken)
87             {
88                 case SVGTokenStyle:
89                 {
90                     maSvgStyleAttributes.readStyle(aContent);
91                     break;
92                 }
93                 case SVGTokenPreserveAspectRatio:
94                 {
95                     setSvgAspectRatio(readSvgAspectRatio(aContent));
96                     break;
97                 }
98                 case SVGTokenTransform:
99                 {
100                     const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
101 
102                     if(!aMatrix.isIdentity())
103                     {
104                         setTransform(&aMatrix);
105                     }
106                     break;
107                 }
108                 case SVGTokenX:
109                 {
110                     SvgNumber aNum;
111 
112                     if(readSingleNumber(aContent, aNum))
113                     {
114                         setX(aNum);
115                     }
116                     break;
117                 }
118                 case SVGTokenY:
119                 {
120                     SvgNumber aNum;
121 
122                     if(readSingleNumber(aContent, aNum))
123                     {
124                         setY(aNum);
125                     }
126                     break;
127                 }
128                 case SVGTokenWidth:
129                 {
130                     SvgNumber aNum;
131 
132                     if(readSingleNumber(aContent, aNum))
133                     {
134                         if(aNum.isPositive())
135                         {
136                             setWidth(aNum);
137                         }
138                     }
139                     break;
140                 }
141                 case SVGTokenHeight:
142                 {
143                     SvgNumber aNum;
144 
145                     if(readSingleNumber(aContent, aNum))
146                     {
147                         if(aNum.isPositive())
148                         {
149                             setHeight(aNum);
150                         }
151                     }
152                     break;
153                 }
154                 case SVGTokenXlinkHref:
155                 {
156                     const sal_Int32 nLen(aContent.getLength());
157 
158                     if(nLen)
159                     {
160                         readImageLink(aContent, maXLink, maUrl, maMimeType, maData);
161                     }
162                     break;
163                 }
164                 default:
165                 {
166                     break;
167                 }
168             }
169         }
170 
171         void extractFromGraphic(
172             const Graphic& rGraphic,
173             drawinglayer::primitive2d::Primitive2DSequence& rEmbedded,
174             basegfx::B2DRange& rViewBox,
175             BitmapEx& rBitmapEx)
176         {
177             if(GRAPHIC_BITMAP == rGraphic.GetType())
178             {
179                 if(rGraphic.getSvgData().get())
180                 {
181                     // embedded Svg
182                     rEmbedded = rGraphic.getSvgData()->getPrimitive2DSequence();
183 
184                     // fill aViewBox
185                     rViewBox = rGraphic.getSvgData()->getRange();
186                 }
187                 else
188                 {
189                     // get bitmap
190                     rBitmapEx = rGraphic.GetBitmapEx();
191                 }
192             }
193             else
194             {
195                 // evtl. convert to bitmap
196                 rBitmapEx = rGraphic.GetBitmapEx();
197             }
198         }
199 
200         void SvgImageNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool /*bReferenced*/) const
201         {
202             // get size range and create path
203             const SvgStyleAttributes* pStyle = getSvgStyleAttributes();
204 
205             if(pStyle && getWidth().isSet() && getHeight().isSet())
206             {
207                 const double fWidth(getWidth().solve(*this, xcoordinate));
208                 const double fHeight(getHeight().solve(*this, ycoordinate));
209 
210                 if(fWidth > 0.0 && fHeight > 0.0)
211                 {
212                     BitmapEx aBitmapEx;
213                     drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
214 
215                     // prepare Target and ViewBox for evtl. AspectRatio mappings
216                     const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0);
217                     const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0);
218                     const basegfx::B2DRange aTarget(fX, fY, fX + fWidth, fY + fHeight);
219                     basegfx::B2DRange aViewBox(aTarget);
220 
221                     if(maMimeType.getLength() && maData.getLength())
222                     {
223                         // use embedded base64 encoded data
224                         ::com::sun::star::uno::Sequence< sal_Int8 > aPass;
225                         ::sax::Converter::decodeBase64(aPass, maData);
226 
227                         if(aPass.hasElements())
228                         {
229                             SvMemoryStream aStream(aPass.getArray(), aPass.getLength(), STREAM_READ);
230                             Graphic aGraphic;
231 
232                             if(GRFILTER_OK == GraphicFilter::GetGraphicFilter()->ImportGraphic(
233                                 aGraphic,
234                                 String(),
235                                 aStream))
236                             {
237                                 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
238                             }
239                         }
240                     }
241                     else if(maUrl.getLength())
242                     {
243                         const rtl::OUString& rPath = getDocument().getAbsolutePath();
244                         const rtl::OUString aAbsUrl(rtl::Uri::convertRelToAbs(rPath, maUrl));
245 
246                         if(aAbsUrl.getLength())
247                         {
248                             SvFileStream aStream(aAbsUrl, STREAM_STD_READ);
249                             Graphic aGraphic;
250 
251                             if(GRFILTER_OK == GraphicFilter::GetGraphicFilter()->ImportGraphic(
252                                 aGraphic,
253                                 aAbsUrl,
254                                 aStream))
255                             {
256                                 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
257                             }
258                         }
259                     }
260                     else if(maXLink.getLength())
261                     {
262                         const SvgNode* mpXLink = getDocument().findSvgNodeById(maXLink);
263 
264                         if(mpXLink)
265                         {
266                             mpXLink->decomposeSvgNode(aNewTarget, true);
267 
268                             if(aNewTarget.hasElements())
269                             {
270                                 aViewBox = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
271                                     aNewTarget,
272                                     drawinglayer::geometry::ViewInformation2D());
273                             }
274                         }
275                     }
276 
277                     if(!aBitmapEx.IsEmpty())
278                     {
279                         // create content from created bitmap
280                         aNewTarget.realloc(1);
281                         aNewTarget[0] = new drawinglayer::primitive2d::BitmapPrimitive2D(
282                             aBitmapEx,
283                             basegfx::B2DHomMatrix());
284 
285                         // fill aViewBox. No size set yet, use unit size
286                         aViewBox = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
287                     }
288 
289                     if(aNewTarget.hasElements())
290                     {
291                         if(aTarget.equal(aViewBox))
292                         {
293                             // just add to rTarget
294                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
295                         }
296                         else
297                         {
298                             // create mapping
299                             const SvgAspectRatio& rRatio = getSvgAspectRatio();
300 
301                             if(rRatio.isSet())
302                             {
303                                 // let mapping be created from SvgAspectRatio
304                                 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createMapping(aTarget, aViewBox));
305 
306                                 if(!aEmbeddingTransform.isIdentity())
307                                 {
308                                     const drawinglayer::primitive2d::Primitive2DReference xRef(
309                                         new drawinglayer::primitive2d::TransformPrimitive2D(
310                                             aEmbeddingTransform,
311                                             aNewTarget));
312 
313                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
314                                 }
315 
316                                 if(!rRatio.isMeetOrSlice())
317                                 {
318                                     // need to embed in MaskPrimitive2D to ensure clipping
319                                     const drawinglayer::primitive2d::Primitive2DReference xMask(
320                                         new drawinglayer::primitive2d::MaskPrimitive2D(
321                                             basegfx::B2DPolyPolygon(
322                                                 basegfx::tools::createPolygonFromRect(aTarget)),
323                                             aNewTarget));
324 
325                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
326                                 }
327                             }
328                             else
329                             {
330                                 // choose default mapping
331                                 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createLinearMapping(aTarget, aViewBox));
332 
333                                 if(!aEmbeddingTransform.isIdentity())
334                                 {
335                                     const drawinglayer::primitive2d::Primitive2DReference xRef(
336                                         new drawinglayer::primitive2d::TransformPrimitive2D(
337                                             aEmbeddingTransform,
338                                             aNewTarget));
339 
340                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
341                                 }
342                             }
343 
344                             // embed and add to rTarget, take local extra-transform into account
345                             pStyle->add_postProcess(rTarget, aNewTarget, getTransform());
346                         }
347                     }
348                 }
349             }
350         }
351 
352     } // end of namespace svgreader
353 } // end of namespace svgio
354 
355 //////////////////////////////////////////////////////////////////////////////
356 // eof
357