1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <drawinglayer/tools/primitive2dxmldump.hxx>
11 
12 #include <vcl/metaact.hxx>
13 #include <rtl/string.hxx>
14 #include <rtl/strbuf.hxx>
15 #include <tools/stream.hxx>
16 #include <tools/XmlWriter.hxx>
17 
18 #include <memory>
19 
20 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
21 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
23 #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
25 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
26 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
30 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
31 #include <drawinglayer/geometry/viewinformation2d.hxx>
32 #include <drawinglayer/attribute/lineattribute.hxx>
33 #include <drawinglayer/attribute/fontattribute.hxx>
34 
35 #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
37 
38 using namespace drawinglayer::primitive2d;
39 
40 namespace drawinglayer::tools
41 {
42 
43 namespace
44 {
45 
46 const size_t constMaxActionType = 513;
47 
convertColorToString(const basegfx::BColor & rColor)48 OUString convertColorToString(const basegfx::BColor& rColor)
49 {
50     OUString aRGBString = Color(rColor).AsRGBHexString();
51     return "#" + aRGBString;
52 }
53 
writePolyPolygon(::tools::XmlWriter & rWriter,const basegfx::B2DPolyPolygon & rB2DPolyPolygon)54 void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon& rB2DPolyPolygon)
55 {
56     rWriter.startElement("polypolygon");
57     const basegfx::B2DRange aB2DRange(rB2DPolyPolygon.getB2DRange());
58     rWriter.attributeDouble("height", aB2DRange.getHeight());
59     rWriter.attributeDouble("width", aB2DRange.getWidth());
60     rWriter.attributeDouble("minx", aB2DRange.getMinX());
61     rWriter.attributeDouble("miny", aB2DRange.getMinY());
62     rWriter.attributeDouble("maxx", aB2DRange.getMaxX());
63     rWriter.attributeDouble("maxy", aB2DRange.getMaxY());
64     rWriter.attribute("path", basegfx::utils::exportToSvgD(rB2DPolyPolygon, true, true, false));
65 
66     for (basegfx::B2DPolygon const & rPolygon : rB2DPolyPolygon)
67     {
68         rWriter.startElement("polygon");
69         for (sal_uInt32 i = 0; i <rPolygon.count(); ++i)
70         {
71             basegfx::B2DPoint const & rPoint = rPolygon.getB2DPoint(i);
72 
73             rWriter.startElement("point");
74             rWriter.attribute("x", OUString::number(rPoint.getX()));
75             rWriter.attribute("y", OUString::number(rPoint.getY()));
76             rWriter.endElement();
77         }
78         rWriter.endElement();
79     }
80 
81     rWriter.endElement();
82 }
83 
84 } // end anonymous namespace
85 
Primitive2dXmlDump()86 Primitive2dXmlDump::Primitive2dXmlDump() :
87     maFilter(constMaxActionType, false)
88 {}
89 
90 Primitive2dXmlDump::~Primitive2dXmlDump() = default;
91 
dump(const drawinglayer::primitive2d::Primitive2DContainer & rPrimitive2DSequence,const OUString & rStreamName)92 void Primitive2dXmlDump::dump(
93     const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence,
94     const OUString& rStreamName)
95 {
96     std::unique_ptr<SvStream> pStream;
97 
98     if (rStreamName.isEmpty())
99         pStream.reset(new SvMemoryStream());
100     else
101         pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
102 
103     ::tools::XmlWriter aWriter(pStream.get());
104     aWriter.startDocument();
105     aWriter.startElement("primitive2D");
106 
107     decomposeAndWrite(rPrimitive2DSequence, aWriter);
108 
109     aWriter.endElement();
110     aWriter.endDocument();
111 
112     pStream->Seek(STREAM_SEEK_TO_BEGIN);
113 }
114 
dumpAndParse(const drawinglayer::primitive2d::Primitive2DContainer & rPrimitive2DSequence,const OUString & rStreamName)115 xmlDocPtr Primitive2dXmlDump::dumpAndParse(
116     const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence,
117     const OUString& rStreamName)
118 {
119     std::unique_ptr<SvStream> pStream;
120 
121     if (rStreamName.isEmpty())
122         pStream.reset(new SvMemoryStream());
123     else
124         pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
125 
126     ::tools::XmlWriter aWriter(pStream.get());
127     aWriter.startDocument();
128     aWriter.startElement("primitive2D");
129 
130     decomposeAndWrite(rPrimitive2DSequence, aWriter);
131 
132     aWriter.endElement();
133     aWriter.endDocument();
134 
135     pStream->Seek(STREAM_SEEK_TO_BEGIN);
136 
137     std::size_t nSize = pStream->remainingSize();
138     std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nSize + 1]);
139     pStream->ReadBytes(pBuffer.get(), nSize);
140     pBuffer[nSize] = 0;
141 
142     xmlDocPtr pDoc = xmlParseDoc(reinterpret_cast<xmlChar*>(pBuffer.get()));
143 
144     return pDoc;
145 }
146 
decomposeAndWrite(const drawinglayer::primitive2d::Primitive2DContainer & rPrimitive2DSequence,::tools::XmlWriter & rWriter)147 void Primitive2dXmlDump::decomposeAndWrite(
148     const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence,
149     ::tools::XmlWriter& rWriter)
150 {
151     for (size_t i = 0; i < rPrimitive2DSequence.size(); i++)
152     {
153         drawinglayer::primitive2d::Primitive2DReference xPrimitive2DReference = rPrimitive2DSequence[i];
154         const BasePrimitive2D* pBasePrimitive = dynamic_cast<const BasePrimitive2D* >(xPrimitive2DReference.get());
155         if (!pBasePrimitive)
156             continue;
157         sal_uInt32 nId = pBasePrimitive->getPrimitive2DID();
158         if (maFilter[nId])
159             continue;
160 
161         OUString sCurrentElementTag = drawinglayer::primitive2d::idToString(nId);
162 
163         switch (nId)
164         {
165             case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D:
166             {
167                 const HiddenGeometryPrimitive2D& rHiddenGeometryPrimitive2D = dynamic_cast<const HiddenGeometryPrimitive2D&>(*pBasePrimitive);
168                 rWriter.startElement("hiddengeometry");
169                 decomposeAndWrite(rHiddenGeometryPrimitive2D.getChildren(), rWriter);
170                 rWriter.endElement();
171             }
172             break;
173 
174             case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
175             {
176                 const TransformPrimitive2D& rTransformPrimitive2D = dynamic_cast<const TransformPrimitive2D&>(*pBasePrimitive);
177                 rWriter.startElement("transform");
178 
179                 basegfx::B2DHomMatrix const & rMatrix = rTransformPrimitive2D.getTransformation();
180                 rWriter.attributeDouble("xy11", rMatrix.get(0,0));
181                 rWriter.attributeDouble("xy12", rMatrix.get(0,1));
182                 rWriter.attributeDouble("xy13", rMatrix.get(0,2));
183                 rWriter.attributeDouble("xy21", rMatrix.get(1,0));
184                 rWriter.attributeDouble("xy22", rMatrix.get(1,1));
185                 rWriter.attributeDouble("xy23", rMatrix.get(1,2));
186                 rWriter.attributeDouble("xy31", rMatrix.get(2,0));
187                 rWriter.attributeDouble("xy32", rMatrix.get(2,1));
188                 rWriter.attributeDouble("xy33", rMatrix.get(2,2));
189 
190                 decomposeAndWrite(rTransformPrimitive2D.getChildren(), rWriter);
191                 rWriter.endElement();
192             }
193             break;
194 
195             case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
196             {
197                 const PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D = dynamic_cast<const PolyPolygonColorPrimitive2D&>(*pBasePrimitive);
198 
199                 rWriter.startElement("polypolygoncolor");
200                 rWriter.attribute("color", convertColorToString(rPolyPolygonColorPrimitive2D.getBColor()));
201 
202                 const basegfx::B2DPolyPolygon& aB2DPolyPolygon(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon());
203                 writePolyPolygon(rWriter, aB2DPolyPolygon);
204 
205                 rWriter.endElement();
206             }
207             break;
208 
209             case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D:
210             {
211                 const PolyPolygonStrokePrimitive2D& rPolyPolygonStrokePrimitive2D = dynamic_cast<const PolyPolygonStrokePrimitive2D&>(*pBasePrimitive);
212                 rWriter.startElement("polypolygonstroke");
213 
214                 rWriter.startElement("line");
215                 const drawinglayer::attribute::LineAttribute& aLineAttribute = rPolyPolygonStrokePrimitive2D.getLineAttribute();
216                 rWriter.attribute("color", convertColorToString(aLineAttribute.getColor()));
217                 rWriter.attribute("width", aLineAttribute.getWidth());
218                 //rWriter.attribute("linejoin", aLineAttribute.getLineJoin());
219                 //rWriter.attribute("linecap", aLineAttribute.getLineCap());
220                 rWriter.endElement();
221 
222                 //getStrokeAttribute()
223 
224                 writePolyPolygon(rWriter, rPolyPolygonStrokePrimitive2D.getB2DPolyPolygon());
225 
226                 rWriter.endElement();
227             }
228             break;
229 
230             case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
231             {
232                 const PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D = dynamic_cast<const PolygonHairlinePrimitive2D&>(*pBasePrimitive);
233                 rWriter.startElement("polygonhairline");
234 
235                 rWriter.attribute("color", convertColorToString(rPolygonHairlinePrimitive2D.getBColor()));
236 
237                 rWriter.startElement("polygon");
238                 rWriter.content(basegfx::utils::exportToSvgPoints(rPolygonHairlinePrimitive2D.getB2DPolygon()));
239                 rWriter.endElement();
240 
241 
242                 rWriter.endElement();
243             }
244             break;
245 
246             case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
247             {
248                 const TextSimplePortionPrimitive2D& rTextSimplePortionPrimitive2D = dynamic_cast<const TextSimplePortionPrimitive2D&>(*pBasePrimitive);
249                 rWriter.startElement("textsimpleportion");
250 
251                 basegfx::B2DVector aScale, aTranslate;
252                 double fRotate, fShearX;
253                 if(rTextSimplePortionPrimitive2D.getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
254                 {
255                     rWriter.attribute("height", aScale.getY());
256                 }
257                 rWriter.attribute("x", aTranslate.getX());
258                 rWriter.attribute("y", aTranslate.getY());
259                 rWriter.attribute("text", rTextSimplePortionPrimitive2D.getText());
260                 rWriter.attribute("fontcolor", convertColorToString(rTextSimplePortionPrimitive2D.getFontColor()));
261 
262                 const drawinglayer::attribute::FontAttribute& aFontAttribute = rTextSimplePortionPrimitive2D.getFontAttribute();
263                 rWriter.attribute("familyname", aFontAttribute.getFamilyName());
264                 rWriter.endElement();
265             }
266             break;
267 
268             case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
269             {
270                 const MaskPrimitive2D& rMaskPrimitive2D = dynamic_cast<const MaskPrimitive2D&>(*pBasePrimitive);
271                 rWriter.startElement("mask");
272                 writePolyPolygon(rWriter, rMaskPrimitive2D.getMask());
273                 decomposeAndWrite(rMaskPrimitive2D.getChildren(), rWriter);
274                 rWriter.endElement();
275             }
276             break;
277 
278             case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
279             {
280                 const UnifiedTransparencePrimitive2D& rUnifiedTransparencePrimitive2D = dynamic_cast<const UnifiedTransparencePrimitive2D&>(*pBasePrimitive);
281                 rWriter.startElement("unifiedtransparence");
282                 rWriter.attribute("transparence", OString::number(rUnifiedTransparencePrimitive2D.getTransparence()));
283                 decomposeAndWrite(rUnifiedTransparencePrimitive2D.getChildren(), rWriter);
284 
285                 rWriter.endElement();
286             }
287             break;
288 
289             case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
290             {
291                 const ObjectInfoPrimitive2D& rObjectInfoPrimitive2D = dynamic_cast<const ObjectInfoPrimitive2D&>(*pBasePrimitive);
292                 rWriter.startElement("objectinfo");
293 
294                 decomposeAndWrite(rObjectInfoPrimitive2D.getChildren(), rWriter);
295                 rWriter.endElement();
296             }
297             break;
298 
299             case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D:
300             {
301                 const SvgRadialGradientPrimitive2D& rSvgRadialGradientPrimitive2D = dynamic_cast<const SvgRadialGradientPrimitive2D&>(*pBasePrimitive);
302                 rWriter.startElement("svgradialgradient");
303                 basegfx::B2DPoint aFocusAttribute = rSvgRadialGradientPrimitive2D.getFocal();
304 
305                 rWriter.attribute("radius", OString::number(rSvgRadialGradientPrimitive2D.getRadius()));
306                 rWriter.attribute("focusx", aFocusAttribute.getX());
307                 rWriter.attribute("focusy", aFocusAttribute.getY());
308 
309                 rWriter.endElement();
310             }
311             break;
312 
313             case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D:
314             {
315                 const SvgLinearGradientPrimitive2D& rSvgLinearGradientPrimitive2D = dynamic_cast<const SvgLinearGradientPrimitive2D&>(*pBasePrimitive);
316                 rWriter.startElement("svglineargradient");
317                 basegfx::B2DPoint aEndAttribute = rSvgLinearGradientPrimitive2D.getEnd();
318 
319                 rWriter.attribute("endx", aEndAttribute.getX());
320                 rWriter.attribute("endy", aEndAttribute.getY());
321 
322                 rWriter.endElement();
323             }
324             break;
325 
326             case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D:
327             {
328                 const MetafilePrimitive2D& rMetafilePrimitive2D = dynamic_cast<const MetafilePrimitive2D&>(*pBasePrimitive);
329                 rWriter.startElement("metafile");
330                 drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
331                 // since the graphic is not rendered in a document, we do not need a concrete view information
332                 rMetafilePrimitive2D.get2DDecomposition(aPrimitiveContainer, drawinglayer::geometry::ViewInformation2D());
333                 decomposeAndWrite(aPrimitiveContainer,rWriter);
334                 rWriter.endElement();
335             }
336 
337             break;
338 
339             default:
340             {
341                 rWriter.element(OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8));
342             }
343             break;
344         }
345 
346     }
347 }
348 
349 } // end namespace drawinglayer::tools
350 
351 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
352