xref: /AOO41X/main/svgio/source/svgreader/svgimagenode.cxx (revision a12ae337ec5ceb799fc055816d4155eeb3e45c1a)
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 "linkedimagepolicy.hxx"
40 #include <drawinglayer/geometry/viewinformation2d.hxx>
41 
42 //////////////////////////////////////////////////////////////////////////////
43 
44 namespace svgio
45 {
46     namespace svgreader
47     {
SvgImageNode(SvgDocument & rDocument,SvgNode * pParent)48         SvgImageNode::SvgImageNode(
49             SvgDocument& rDocument,
50             SvgNode* pParent)
51         :   SvgNode(SVGTokenRect, rDocument, pParent),
52             maSvgStyleAttributes(*this),
53             maSvgAspectRatio(),
54             mpaTransform(0),
55             maX(0),
56             maY(0),
57             maWidth(0),
58             maHeight(0),
59             maXLink(),
60             maUrl(),
61             maMimeType(),
62             maData()
63         {
64         }
65 
~SvgImageNode()66         SvgImageNode::~SvgImageNode()
67         {
68             if(mpaTransform) delete mpaTransform;
69         }
70 
getSvgStyleAttributes() const71         const SvgStyleAttributes* SvgImageNode::getSvgStyleAttributes() const
72         {
73             static rtl::OUString aClassStr(rtl::OUString::createFromAscii("image"));
74 
75             return checkForCssStyle(aClassStr, maSvgStyleAttributes);
76         }
77 
parseAttribute(const rtl::OUString & rTokenName,SVGToken aSVGToken,const rtl::OUString & aContent)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 
extractFromGraphic(const Graphic & rGraphic,drawinglayer::primitive2d::Primitive2DSequence & rEmbedded,basegfx::B2DRange & rViewBox,BitmapEx & rBitmapEx)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 
decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence & rTarget,bool) const201         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 
246                         if(rPath.getLength())
247                         {
248                             // The reference is document content: resolve it
249                             // against this file's own location and only open it
250                             // when it stays there (see linkedimagepolicy.hxx).
251                             rtl::OUString aAbsUrl;
252 
253                             if(resolveLinkedImageUrl(rPath, maUrl, aAbsUrl))
254                             {
255                                 SvFileStream aStream(aAbsUrl, STREAM_STD_READ);
256                                 Graphic aGraphic;
257 
258                                 if(GRFILTER_OK == GraphicFilter::GetGraphicFilter()->ImportGraphic(
259                                     aGraphic,
260                                     aAbsUrl,
261                                     aStream))
262                                 {
263                                     extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
264                                 }
265                             }
266                         }
267                         else
268                         {
269                             // #123042# detect missing path and assert - content will be missing. The
270                             // absolute path to itself needs to be set to correctly import linked
271                             // content in a SVG file
272                             OSL_ENSURE(false, "SVG graphic with internal links is interpreted, but local AbsolutePath is not set: linked content will be missing (!)");
273                         }
274                     }
275                     else if(maXLink.getLength())
276                     {
277                         const SvgNode* mpXLink = getDocument().findSvgNodeById(maXLink);
278 
279                         if(mpXLink && Display_none != mpXLink->getDisplay())
280                         {
281                             mpXLink->decomposeSvgNode(aNewTarget, true);
282 
283                             if(aNewTarget.hasElements())
284                             {
285                                 aViewBox = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
286                                     aNewTarget,
287                                     drawinglayer::geometry::ViewInformation2D());
288                             }
289                         }
290                     }
291 
292                     if(!aBitmapEx.IsEmpty())
293                     {
294                         // create content from created bitmap
295                         aNewTarget.realloc(1);
296                         aNewTarget[0] = new drawinglayer::primitive2d::BitmapPrimitive2D(
297                             aBitmapEx,
298                             basegfx::B2DHomMatrix());
299 
300                         // fill aViewBox. No size set yet, use unit size
301                         aViewBox = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
302                     }
303 
304                     if(aNewTarget.hasElements())
305                     {
306                         if(aTarget.equal(aViewBox))
307                         {
308                             // just add to rTarget
309                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
310                         }
311                         else
312                         {
313                             // create mapping
314                             const SvgAspectRatio& rRatio = getSvgAspectRatio();
315 
316                             if(rRatio.isSet())
317                             {
318                                 // let mapping be created from SvgAspectRatio
319                                 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createMapping(aTarget, aViewBox));
320 
321                                 if(!aEmbeddingTransform.isIdentity())
322                                 {
323                                     const drawinglayer::primitive2d::Primitive2DReference xRef(
324                                         new drawinglayer::primitive2d::TransformPrimitive2D(
325                                             aEmbeddingTransform,
326                                             aNewTarget));
327 
328                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
329                                 }
330 
331                                 if(!rRatio.isMeetOrSlice())
332                                 {
333                                     // need to embed in MaskPrimitive2D to ensure clipping
334                                     const drawinglayer::primitive2d::Primitive2DReference xMask(
335                                         new drawinglayer::primitive2d::MaskPrimitive2D(
336                                             basegfx::B2DPolyPolygon(
337                                                 basegfx::tools::createPolygonFromRect(aTarget)),
338                                             aNewTarget));
339 
340                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
341                                 }
342                             }
343                             else
344                             {
345                                 // choose default mapping
346                                 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createLinearMapping(aTarget, aViewBox));
347 
348                                 if(!aEmbeddingTransform.isIdentity())
349                                 {
350                                     const drawinglayer::primitive2d::Primitive2DReference xRef(
351                                         new drawinglayer::primitive2d::TransformPrimitive2D(
352                                             aEmbeddingTransform,
353                                             aNewTarget));
354 
355                                     aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
356                                 }
357                             }
358 
359                             // embed and add to rTarget, take local extra-transform into account
360                             pStyle->add_postProcess(rTarget, aNewTarget, getTransform());
361                         }
362                     }
363                 }
364             }
365         }
366 
367     } // end of namespace svgreader
368 } // end of namespace svgio
369 
370 //////////////////////////////////////////////////////////////////////////////
371 // eof
372