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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_xmloff.hxx"
26 #include "XMLImageMapExport.hxx"
27 #include <rtl/ustring.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <tools/debug.hxx>
30 #include <com/sun/star/uno/Reference.h>
31 #include <com/sun/star/uno/Sequence.h>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/lang/XServiceInfo.hpp>
34 #include <com/sun/star/container/XIndexContainer.hpp>
35 
36 #ifndef _COM_SUN_STAR_DOCUMENT_XEVENTSSUPPLIER_HPP
37 #include <com/sun/star/document/XEventsSupplier.hpp>
38 #endif
39 #include <com/sun/star/awt/Rectangle.hpp>
40 #include <com/sun/star/awt/Point.hpp>
41 #include <com/sun/star/awt/Size.hpp>
42 #include <com/sun/star/drawing/PointSequence.hpp>
43 #include <xmloff/xmlexp.hxx>
44 #include "xmloff/xmlnmspe.hxx"
45 #include <xmloff/xmltoken.hxx>
46 #include <xmloff/XMLEventExport.hxx>
47 #include <xmloff/xmluconv.hxx>
48 #include "xexptran.hxx"
49 
50 
51 
52 using namespace ::com::sun::star;
53 using namespace ::xmloff::token;
54 
55 using ::rtl::OUString;
56 using ::rtl::OUStringBuffer;
57 using ::com::sun::star::uno::Any;
58 using ::com::sun::star::uno::UNO_QUERY;
59 using ::com::sun::star::uno::Sequence;
60 using ::com::sun::star::uno::Reference;
61 using ::com::sun::star::beans::XPropertySet;
62 using ::com::sun::star::container::XIndexContainer;
63 using ::com::sun::star::document::XEventsSupplier;
64 using ::com::sun::star::lang::XServiceInfo;
65 using ::com::sun::star::drawing::PointSequence;
66 
67 
68 const sal_Char sAPI_ImageMapRectangleObject[] = "com.sun.star.image.ImageMapRectangleObject";
69 const sal_Char sAPI_ImageMapCircleObject[] = "com.sun.star.image.ImageMapCircleObject";
70 const sal_Char sAPI_ImageMapPolygonObject[] = "com.sun.star.image.ImageMapPolygonObject";
71 
72 XMLImageMapExport::XMLImageMapExport(SvXMLExport& rExp) :
73 	msBoundary(RTL_CONSTASCII_USTRINGPARAM("Boundary")),
74 	msCenter(RTL_CONSTASCII_USTRINGPARAM("Center")),
75 	msDescription(RTL_CONSTASCII_USTRINGPARAM("Description")),
76 	msImageMap(RTL_CONSTASCII_USTRINGPARAM("ImageMap")),
77 	msIsActive(RTL_CONSTASCII_USTRINGPARAM("IsActive")),
78 	msName(RTL_CONSTASCII_USTRINGPARAM("Name")),
79 	msPolygon(RTL_CONSTASCII_USTRINGPARAM("Polygon")),
80 	msRadius(RTL_CONSTASCII_USTRINGPARAM("Radius")),
81 	msTarget(RTL_CONSTASCII_USTRINGPARAM("Target")),
82 	msURL(RTL_CONSTASCII_USTRINGPARAM("URL")),
83 	msTitle(RTL_CONSTASCII_USTRINGPARAM("Title")),
84 	mrExport(rExp),
85 	mbWhiteSpace(sal_True)
86 {
87 }
88 
89 XMLImageMapExport::~XMLImageMapExport()
90 {
91 
92 }
93 
94 void XMLImageMapExport::Export(
95 	const Reference<XPropertySet> & rPropertySet)
96 {
97 	if (rPropertySet->getPropertySetInfo()->hasPropertyByName(msImageMap))
98 	{
99 		Any aAny = rPropertySet->getPropertyValue(msImageMap);
100 		Reference<XIndexContainer> aContainer;
101 		aAny >>= aContainer;
102 
103 		Export(aContainer);
104 	}
105 	// else: no ImageMap property -> nothing to do
106 }
107 
108 void XMLImageMapExport::Export(
109 	const Reference<XIndexContainer> & rContainer)
110 {
111 	if (rContainer.is())
112 	{
113 		if (rContainer->hasElements())
114 		{
115 			// image map container element
116 			SvXMLElementExport aImageMapElement(
117 				mrExport, XML_NAMESPACE_DRAW, XML_IMAGE_MAP,
118 				mbWhiteSpace, mbWhiteSpace);
119 
120 			// iterate over image map elements and call ExportMapEntry(...)
121 			// for each
122 			sal_Int32 nLength = rContainer->getCount();
123 			for(sal_Int32 i = 0; i < nLength; i++)
124 			{
125 				Any aAny = rContainer->getByIndex(i);
126 				Reference<XPropertySet> rElement;
127 				aAny >>= rElement;
128 
129 				DBG_ASSERT(rElement.is(), "Image map element is empty!");
130 				if (rElement.is())
131 				{
132 					ExportMapEntry(rElement);
133 				}
134 			}
135 		}
136 		// else: container is empty -> nothing to do
137 	}
138 	// else: no container -> nothign to do
139 }
140 
141 
142 void XMLImageMapExport::ExportMapEntry(
143 	const Reference<XPropertySet> & rPropertySet)
144 {
145 	Reference<XServiceInfo> xServiceInfo(rPropertySet, UNO_QUERY);
146 	if (xServiceInfo.is())
147 	{
148 		enum XMLTokenEnum eType = XML_TOKEN_INVALID;
149 
150 		// distinguish map entries by their service name
151 		Sequence<OUString> sServiceNames =
152 			xServiceInfo->getSupportedServiceNames();
153 		sal_Int32 nLength = sServiceNames.getLength();
154 		for( sal_Int32 i=0; i<nLength; i++ )
155 		{
156 			OUString& rName = sServiceNames[i];
157 
158 			if ( rName.equalsAsciiL(sAPI_ImageMapRectangleObject,
159 									sizeof(sAPI_ImageMapRectangleObject)-1) )
160 			{
161 				eType = XML_AREA_RECTANGLE;
162 				break;
163 			}
164 			else if ( rName.equalsAsciiL(sAPI_ImageMapCircleObject,
165 										 sizeof(sAPI_ImageMapCircleObject)-1) )
166 			{
167 				eType = XML_AREA_CIRCLE;
168 				break;
169 			}
170 			else if ( rName.equalsAsciiL(sAPI_ImageMapPolygonObject,
171 										 sizeof(sAPI_ImageMapPolygonObject)-1))
172 			{
173 				eType = XML_AREA_POLYGON;
174 				break;
175 			}
176 		}
177 
178 		// return from method if no proper service is found!
179 		DBG_ASSERT(XML_TOKEN_INVALID != eType,
180 				   "Image map element doesn't support appropriate service!");
181 		if (XML_TOKEN_INVALID == eType)
182 			return;
183 
184 		// now: handle ImageMapObject properties (those for all types)
185 
186 		// XLINK (URL property)
187 		Any aAny = rPropertySet->getPropertyValue(msURL);
188 		OUString sHref;
189 		aAny >>= sHref;
190 		if (sHref.getLength() > 0)
191 		{
192 			mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, mrExport.GetRelativeReference(sHref));
193 		}
194 		mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
195 
196 		// Target property (and xlink:show)
197 		aAny = rPropertySet->getPropertyValue(msTarget);
198 		OUString sTargt;
199 		aAny >>= sTargt;
200 		if (sTargt.getLength() > 0)
201 		{
202 			mrExport.AddAttribute(
203 				XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, sTargt);
204 
205 			mrExport.AddAttribute(
206 				XML_NAMESPACE_XLINK, XML_SHOW,
207 				sTargt.equalsAsciiL( "_blank", sizeof("_blank")-1 )
208 										? XML_NEW : XML_REPLACE );
209 		}
210 
211 		// name
212 		aAny = rPropertySet->getPropertyValue(msName);
213 		OUString sItemName;
214 		aAny >>= sItemName;
215 		if (sItemName.getLength() > 0)
216 		{
217 			mrExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_NAME, sItemName);
218 		}
219 
220 		// is-active
221 		aAny = rPropertySet->getPropertyValue(msIsActive);
222 		if (! *(sal_Bool*)aAny.getValue())
223 		{
224 			mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_NOHREF, XML_NOHREF);
225 		}
226 
227 		// call specific rectangle/circle/... method
228 		// also prepare element name
229 		switch (eType)
230 		{
231 			case XML_AREA_RECTANGLE:
232 				ExportRectangle(rPropertySet);
233 				break;
234 			case XML_AREA_CIRCLE:
235 				ExportCircle(rPropertySet);
236 				break;
237 			case XML_AREA_POLYGON:
238 				ExportPolygon(rPropertySet);
239 				break;
240 			default:
241 				break;
242 		}
243 
244 		// write element
245 		DBG_ASSERT(XML_TOKEN_INVALID != eType,
246                    "No name?! How did this happen?");
247 		SvXMLElementExport aAreaElement(mrExport, XML_NAMESPACE_DRAW, eType,
248 										mbWhiteSpace, mbWhiteSpace);
249 
250 		// title property (as <svg:title> element)
251 		OUString sTitle;
252 		rPropertySet->getPropertyValue(msTitle) >>= sTitle;
253 		if(sTitle.getLength())
254 		{
255 			SvXMLElementExport aEventElemt(mrExport, XML_NAMESPACE_SVG, XML_TITLE, mbWhiteSpace, sal_False);
256 			mrExport.Characters(sTitle);
257 		}
258 
259 		// description property (as <svg:desc> element)
260 		OUString sDescription;
261 		rPropertySet->getPropertyValue(msDescription) >>= sDescription;
262 		if (sDescription.getLength() > 0)
263 		{
264 			SvXMLElementExport aDesc(mrExport, XML_NAMESPACE_SVG, XML_DESC, mbWhiteSpace, sal_False);
265 			mrExport.Characters(sDescription);
266 		}
267 
268 		// export events attached to this
269 		Reference<XEventsSupplier> xSupplier(rPropertySet, UNO_QUERY);
270 		mrExport.GetEventExport().Export(xSupplier, mbWhiteSpace);
271 	}
272 	// else: no service info -> can't determine type -> ignore entry
273 }
274 
275 void XMLImageMapExport::ExportRectangle(
276 	const Reference<XPropertySet> & rPropertySet)
277 {
278 	// get boundary rectangle
279 	Any aAny = rPropertySet->getPropertyValue(msBoundary);
280 	awt::Rectangle aRectangle;
281 	aAny >>= aRectangle;
282 
283 	// parameters svg:x, svg:y, svg:width, svg:height
284 	OUStringBuffer aBuffer;
285 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, aRectangle.X);
286 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X,
287 						  aBuffer.makeStringAndClear() );
288 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, aRectangle.Y);
289 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y,
290 						  aBuffer.makeStringAndClear() );
291 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, aRectangle.Width);
292 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_WIDTH,
293 						  aBuffer.makeStringAndClear() );
294 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, aRectangle.Height);
295 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_HEIGHT,
296 						  aBuffer.makeStringAndClear() );
297 }
298 
299 void XMLImageMapExport::ExportCircle(
300 	const Reference<XPropertySet> & rPropertySet)
301 {
302 	// get boundary rectangle
303 	Any aAny = rPropertySet->getPropertyValue(msCenter);
304 	awt::Point aCenter;
305 	aAny >>= aCenter;
306 
307 	// parameters svg:cx, svg:cy
308 	OUStringBuffer aBuffer;
309 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, aCenter.X);
310 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_CX,
311 						  aBuffer.makeStringAndClear() );
312 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, aCenter.Y);
313 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_CY,
314 						  aBuffer.makeStringAndClear() );
315 
316 	// radius
317 	aAny = rPropertySet->getPropertyValue(msRadius);
318 	sal_Int32 nRadius = 0;
319 	aAny >>= nRadius;
320 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, nRadius);
321 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_R,
322 						  aBuffer.makeStringAndClear() );
323 }
324 
325 void XMLImageMapExport::ExportPolygon(
326 	const Reference<XPropertySet> & rPropertySet)
327 {
328 	// polygons get exported as bounding box, viewbox, and coordinate
329 	// pair sequence. The bounding box is always the entire image.
330 
331 	// get polygon point sequence
332 	Any aAny = rPropertySet->getPropertyValue(msPolygon);
333 	PointSequence aPoly;
334 	aAny >>= aPoly;
335 
336 	// get bounding box (assume top-left to be 0,0)
337 	sal_Int32 nWidth = 0;
338 	sal_Int32 nHeight = 0;
339 	sal_Int32 nLength = aPoly.getLength();
340 	const struct awt::Point* pPointPtr = aPoly.getConstArray();
341 	for	( sal_Int32 i = 0; i < nLength; i++ )
342 	{
343 		sal_Int32 nPolyX = pPointPtr->X;
344 		sal_Int32 nPolyY = pPointPtr->Y;
345 
346 		if ( nPolyX > nWidth )
347 			nWidth = nPolyX;
348 		if ( nPolyY > nHeight )
349 			nHeight = nPolyY;
350 
351 		pPointPtr++;
352 	}
353 	DBG_ASSERT(nWidth > 0, "impossible Polygon found");
354 	DBG_ASSERT(nHeight > 0, "impossible Polygon found");
355 
356 	// parameters svg:x, svg:y, svg:width, svg:height
357 	OUStringBuffer aBuffer;
358 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, 0);
359 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X,
360 						  aBuffer.makeStringAndClear() );
361 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, 0);
362 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y,
363 						  aBuffer.makeStringAndClear() );
364 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, nWidth);
365 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_WIDTH,
366 						  aBuffer.makeStringAndClear() );
367 	mrExport.GetMM100UnitConverter().convertMeasure(aBuffer, nHeight);
368 	mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_HEIGHT,
369 						  aBuffer.makeStringAndClear() );
370 
371 	// svg:viewbox
372 	SdXMLImExViewBox aViewBox(0, 0, nWidth, nHeight);
373 	mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_VIEWBOX,
374 				aViewBox.GetExportString());
375 
376 	// export point sequence
377 	awt::Point aPoint(0, 0);
378 	awt::Size aSize(nWidth, nHeight);
379 	SdXMLImExPointsElement aPoints( &aPoly, aViewBox, aPoint, aSize );
380 	mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_POINTS,
381 						  aPoints.GetExportString());
382 }
383