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                 default:
166                 {
167                     break;
168                 }
169             }
170         }
171 
172         void extractFromGraphic(
173             const Graphic& rGraphic,
174             drawinglayer::primitive2d::Primitive2DSequence& rEmbedded,
175             basegfx::B2DRange& rViewBox,
176             BitmapEx& rBitmapEx)
177         {
178             if(GRAPHIC_BITMAP == rGraphic.GetType())
179             {
180                 if(rGraphic.getSvgData().get())
181                 {
182                     // embedded Svg
183                     rEmbedded = rGraphic.getSvgData()->getPrimitive2DSequence();
184 
185                     // fill aViewBox
186                     rViewBox = rGraphic.getSvgData()->getRange();
187                 }
188                 else
189                 {
190                     // get bitmap
191                     rBitmapEx = rGraphic.GetBitmapEx();
192                 }
193             }
194             else
195             {
196                 // evtl. convert to bitmap
197                 rBitmapEx = rGraphic.GetBitmapEx();
198             }
199         }
200 
201         void SvgImageNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool /*bReferenced*/) const
202         {
203             // get size range and create path
204             const SvgStyleAttributes* pStyle = getSvgStyleAttributes();
205 
206             if(pStyle && getWidth().isSet() && getHeight().isSet())
207             {
208                 const double fWidth(getWidth().solve(*this, xcoordinate));
209                 const double fHeight(getHeight().solve(*this, ycoordinate));
210 
211                 if(fWidth > 0.0 && fHeight > 0.0)
212                 {
213                     BitmapEx aBitmapEx;
214                     drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
215 
216                     // prepare Target and ViewBox for evtl. AspectRatio mappings
217                     const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0);
218                     const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0);
219                     const basegfx::B2DRange aTarget(fX, fY, fX + fWidth, fY + fHeight);
220                     basegfx::B2DRange aViewBox(aTarget);
221 
222                     if(maMimeType.getLength() && maData.getLength())
223                     {
224                         // use embedded base64 encoded data
225                         ::com::sun::star::uno::Sequence< sal_Int8 > aPass;
226                         ::sax::Converter::decodeBase64(aPass, maData);
227 
228                         if(aPass.hasElements())
229                         {
230                             SvMemoryStream aStream(aPass.getArray(), aPass.getLength(), STREAM_READ);
231                             Graphic aGraphic;
232 
233                             if(GRFILTER_OK == GraphicFilter::GetGraphicFilter()->ImportGraphic(
234                                 aGraphic,
235                                 String(),
236                                 aStream))
237                             {
238                                 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
239                             }
240                         }
241                     }
242                     else if(maUrl.getLength())
243                     {
244                         const rtl::OUString& rPath = getDocument().getAbsolutePath();
245                         const rtl::OUString aAbsUrl(rtl::Uri::convertRelToAbs(rPath, maUrl));
246 
247                         if(aAbsUrl.getLength())
248                         {
249                             SvFileStream aStream(aAbsUrl, STREAM_STD_READ);
250                             Graphic aGraphic;
251 
252                             if(GRFILTER_OK == GraphicFilter::GetGraphicFilter()->ImportGraphic(
253                                 aGraphic,
254                                 aAbsUrl,
255                                 aStream))
256                             {
257                                 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
258                             }
259                         }
260                     }
261                     else if(maXLink.getLength())
262                     {
263                         const SvgNode* mpXLink = getDocument().findSvgNodeById(maXLink);
264 
265                         if(mpXLink)
266                         {
267                             mpXLink->decomposeSvgNode(aNewTarget, true);
268 
269                             if(aNewTarget.hasElements())
270                             {
271                                 aViewBox = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
272                                     aNewTarget,
273                                     drawinglayer::geometry::ViewInformation2D());
274                             }
275                         }
276                     }
277 
278                     if(!aBitmapEx.IsEmpty())
279                     {
280                         // create content from created bitmap
281                         aNewTarget.realloc(1);
282                         aNewTarget[0] = new drawinglayer::primitive2d::BitmapPrimitive2D(
283                             aBitmapEx,
284                             basegfx::B2DHomMatrix());
285 
286                         // fill aViewBox. No size set yet, use unit size
287                         aViewBox = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
288                     }
289 
290                     if(aNewTarget.hasElements())
291                     {
292                         if(aTarget.equal(aViewBox))
293                         {
294                             // just add to rTarget
295                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
296                         }
297                         else
298                         {
299                             // create mapping
300                             const SvgAspectRatio& rRatio = getSvgAspectRatio();
301 
302                             if(rRatio.isSet())
303                             {
304                                 // let mapping be created from SvgAspectRatio
305                                 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createMapping(aTarget, aViewBox));
306 
307                                 if(!aEmbeddingTransform.isIdentity())
308                                 {
309                                     const drawinglayer::primitive2d::Primitive2DReference xRef(
310                                         new drawinglayer::primitive2d::TransformPrimitive2D(
311                                             aEmbeddingTransform,
312                                             aNewTarget));
313 
314                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
315                                 }
316 
317                                 if(!rRatio.isMeetOrSlice())
318                                 {
319                                     // need to embed in MaskPrimitive2D to ensure clipping
320                                     const drawinglayer::primitive2d::Primitive2DReference xMask(
321                                         new drawinglayer::primitive2d::MaskPrimitive2D(
322                                             basegfx::B2DPolyPolygon(
323                                                 basegfx::tools::createPolygonFromRect(aTarget)),
324                                             aNewTarget));
325 
326                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
327                                 }
328                             }
329                             else
330                             {
331                                 // choose default mapping
332                                 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createLinearMapping(aTarget, aViewBox));
333 
334                                 if(!aEmbeddingTransform.isIdentity())
335                                 {
336                                     const drawinglayer::primitive2d::Primitive2DReference xRef(
337                                         new drawinglayer::primitive2d::TransformPrimitive2D(
338                                             aEmbeddingTransform,
339                                             aNewTarget));
340 
341                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
342                                 }
343                             }
344 
345                             // embed and add to rTarget, take local extra-transform into account
346                             pStyle->add_postProcess(rTarget, aNewTarget, getTransform());
347                         }
348                     }
349                 }
350             }
351         }
352 
353     } // end of namespace svgreader
354 } // end of namespace svgio
355 
356 //////////////////////////////////////////////////////////////////////////////
357 // eof
358