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