1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2002-2016 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "xml.h"
14 #include "property.h"
15 #include "scoreElement.h"
16 
17 namespace Ms {
18 
19 //---------------------------------------------------------
20 //   Xml
21 //---------------------------------------------------------
22 
XmlWriter(Score * s)23 XmlWriter::XmlWriter(Score* s)
24       {
25       _score = s;
26       setCodec("UTF-8");
27       }
28 
XmlWriter(Score * s,QIODevice * device)29 XmlWriter::XmlWriter(Score* s, QIODevice* device)
30    : QTextStream(device)
31       {
32       _score = s;
33       setCodec("UTF-8");
34       }
35 
36 //---------------------------------------------------------
37 //   pTag
38 //---------------------------------------------------------
39 
pTag(const char * name,PlaceText place)40 void XmlWriter::pTag(const char* name, PlaceText place)
41       {
42       const char* tags[] = {
43             "auto", "above", "below", "left"
44             };
45       tag(name, tags[int(place)]);
46       }
47 
48 //---------------------------------------------------------
49 //   putLevel
50 //---------------------------------------------------------
51 
putLevel()52 void XmlWriter::putLevel()
53       {
54       int level = stack.size();
55       for (int i = 0; i < level * 2; ++i)
56             *this << ' ';
57       }
58 
59 //---------------------------------------------------------
60 //   header
61 //---------------------------------------------------------
62 
header()63 void XmlWriter::header()
64       {
65       *this << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
66       }
67 
68 //---------------------------------------------------------
69 //   stag
70 //    <mops attribute="value">
71 //---------------------------------------------------------
72 
stag(const QString & s)73 void XmlWriter::stag(const QString& s)
74       {
75       putLevel();
76       *this << '<' << s << '>' << endl;
77       stack.append(s.split(' ')[0]);
78       }
79 
80 //---------------------------------------------------------
81 //   stag
82 //    <mops attribute="value">
83 //---------------------------------------------------------
84 
stag(const ScoreElement * se,const QString & attributes)85 void XmlWriter::stag(const ScoreElement* se, const QString& attributes)
86       {
87       stag(se->name(), se, attributes);
88       }
89 
90 //---------------------------------------------------------
91 //   stag
92 //    <mops attribute="value">
93 //---------------------------------------------------------
94 
stag(const QString & name,const ScoreElement * se,const QString & attributes)95 void XmlWriter::stag(const QString& name, const ScoreElement* se, const QString& attributes)
96       {
97       putLevel();
98       *this << '<' << name;
99       if (!attributes.isEmpty())
100             *this << ' ' << attributes;
101       *this << '>' << endl;
102       stack.append(name);
103 
104       if (_recordElements)
105             _elements.emplace_back(se, name);
106       }
107 
108 //---------------------------------------------------------
109 //   etag
110 //    </mops>
111 //---------------------------------------------------------
112 
etag()113 void XmlWriter::etag()
114       {
115       putLevel();
116       *this << "</" << stack.takeLast() << '>' << endl;
117       }
118 
119 //---------------------------------------------------------
120 //   tagE
121 //    <mops attribute="value"/>
122 //---------------------------------------------------------
123 
tagE(const char * format,...)124 void XmlWriter::tagE(const char* format, ...)
125       {
126       va_list args;
127       va_start(args, format);
128       putLevel();
129       *this << '<';
130       char buffer[BS];
131       vsnprintf(buffer, BS, format, args);
132       *this << buffer;
133       va_end(args);
134       *this << "/>" << endl;
135       }
136 
137 //---------------------------------------------------------
138 //   tagE
139 //---------------------------------------------------------
140 
tagE(const QString & s)141 void XmlWriter::tagE(const QString& s)
142       {
143       putLevel();
144       *this << '<' << s << "/>\n";
145       }
146 
147 //---------------------------------------------------------
148 //   ntag
149 //    <mops> without newline
150 //---------------------------------------------------------
151 
ntag(const char * name)152 void XmlWriter::ntag(const char* name)
153       {
154       putLevel();
155       *this << "<" << name << ">";
156       }
157 
158 //---------------------------------------------------------
159 //   netag
160 //    </mops>     without indentation
161 //---------------------------------------------------------
162 
netag(const char * s)163 void XmlWriter::netag(const char* s)
164       {
165       *this << "</" << s << '>' << endl;
166       }
167 
168 //---------------------------------------------------------
169 //   tag
170 //---------------------------------------------------------
171 
tag(Pid id,QVariant data,QVariant defaultData)172 void XmlWriter::tag(Pid id, QVariant data, QVariant defaultData)
173       {
174       if (data == defaultData)
175             return;
176       const char* name = propertyName(id);
177       if (name == 0)
178             return;
179       const QString writableVal(propertyToString(id, data, /* mscx */ true));
180       if (writableVal.isEmpty())
181             tag(name, data);
182       else
183             tag(name, QVariant(writableVal));
184       }
185 
186 //---------------------------------------------------------
187 //   tag
188 //    <mops>value</mops>
189 //---------------------------------------------------------
190 
tag(const char * name,QVariant data,QVariant defaultData)191 void XmlWriter::tag(const char* name, QVariant data, QVariant defaultData)
192       {
193       if (data != defaultData)
194             tag(QString(name), data);
195       }
196 
tag(const QString & name,QVariant data)197 void XmlWriter::tag(const QString& name, QVariant data)
198       {
199       QString ename(name.split(' ')[0]);
200 
201       putLevel();
202       switch(data.type()) {
203             case QVariant::Bool:
204             case QVariant::Char:
205             case QVariant::Int:
206             case QVariant::UInt:
207                   *this << "<" << name << ">";
208                   *this << data.toInt();
209                   *this << "</" << ename << ">\n";
210                   break;
211             case QVariant::LongLong:
212                   *this << "<" << name << ">";
213                   *this << data.toLongLong();
214                   *this << "</" << ename << ">\n";
215                   break;
216             case QVariant::Double:
217                   *this << "<" << name << ">";
218                   *this << data.value<double>();
219                   *this << "</" << ename << ">\n";
220                   break;
221             case QVariant::String:
222                   *this << "<" << name << ">";
223                   *this << xmlString(data.value<QString>());
224                   *this << "</" << ename << ">\n";
225                   break;
226             case QVariant::Color:
227                   {
228                   QColor color(data.value<QColor>());
229                   *this << QString("<%1 r=\"%2\" g=\"%3\" b=\"%4\" a=\"%5\"/>\n")
230                      .arg(name).arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha());
231                   }
232                   break;
233             case QVariant::Rect:
234                   {
235                   const QRect& r(data.value<QRect>());
236                   *this << QString("<%1 x=\"%2\" y=\"%3\" w=\"%4\" h=\"%5\"/>\n").arg(name).arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
237                   }
238                   break;
239             case QVariant::RectF:
240                   {
241                   const QRectF& r(data.value<QRectF>());
242                   *this << QString("<%1 x=\"%2\" y=\"%3\" w=\"%4\" h=\"%5\"/>\n").arg(name).arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
243                   }
244                   break;
245             case QVariant::PointF:
246                   {
247                   const QPointF& p(data.value<QPointF>());
248                   *this << QString("<%1 x=\"%2\" y=\"%3\"/>\n").arg(name).arg(p.x()).arg(p.y());
249                   }
250                   break;
251             case QVariant::SizeF:
252                   {
253                   const QSizeF& p(data.value<QSizeF>());
254                   *this << QString("<%1 w=\"%2\" h=\"%3\"/>\n").arg(name).arg(p.width()).arg(p.height());
255                   }
256                   break;
257             default: {
258                   const char* type = data.typeName();
259                   if (strcmp(type, "Ms::Spatium") == 0) {
260                         *this << "<" << name << ">";
261                         *this << data.value<Spatium>().val();
262                         *this << "</" << ename << ">\n";
263                         }
264                   else if (strcmp(type, "Ms::Fraction") == 0) {
265                         const Fraction& f = data.value<Fraction>();
266                         *this << QString("<%1>%2/%3</%1>\n").arg(name).arg(f.numerator()).arg(f.denominator());
267                         }
268                   else if (strcmp(type, "Ms::Direction") == 0)
269                         *this << QString("<%1>%2</%1>\n").arg(name, toString(data.value<Direction>()));
270                   else if (strcmp(type, "Ms::Align") == 0) {
271                         // TODO: remove from here? (handled in Ms::propertyWritableValue())
272                         Align a = Align(data.toInt());
273                         const char* h;
274                         if (a & Align::HCENTER)
275                               h = "center";
276                         else if (a & Align::RIGHT)
277                               h = "right";
278                         else
279                               h = "left";
280                         const char* v;
281                         if (a & Align::BOTTOM)
282                               v = "bottom";
283                         else if (a & Align::VCENTER)
284                               v = "center";
285                         else if (a & Align::BASELINE)
286                               v = "baseline";
287                         else
288                               v = "top";
289                         *this << QString("<%1>%2,%3</%1>\n").arg(name)
290                                     .arg(h, v);
291                         }
292                   else {
293                         qFatal("XmlWriter::tag: unsupported type %d %s", data.type(), type);
294                         }
295                   }
296                   break;
297             }
298       }
299 
tag(const char * name,const QWidget * g)300 void XmlWriter::tag(const char* name, const QWidget* g)
301       {
302       tag(name, QRect(g->pos(), g->size()));
303       }
304 
305 //---------------------------------------------------------
306 //   comment
307 //---------------------------------------------------------
308 
comment(const QString & text)309 void XmlWriter::comment(const QString& text)
310       {
311       putLevel();
312       *this << "<!-- " << text << " -->" << endl;
313       }
314 
315 //---------------------------------------------------------
316 //   xmlString
317 //---------------------------------------------------------
318 
xmlString(ushort c)319 QString XmlWriter::xmlString(ushort c)
320       {
321       switch(c) {
322             case '<':
323                   return QLatin1String("&lt;");
324             case '>':
325                   return QLatin1String("&gt;");
326             case '&':
327                   return QLatin1String("&amp;");
328             case '\"':
329                   return QLatin1String("&quot;");
330             default:
331                   // ignore invalid characters in xml 1.0
332                   if ((c < 0x20 && c != 0x09 && c != 0x0A && c != 0x0D))
333                         return QString();
334                   return QString(QChar(c));
335             }
336       }
337 
338 //---------------------------------------------------------
339 //   xmlString
340 //---------------------------------------------------------
341 
xmlString(const QString & s)342 QString XmlWriter::xmlString(const QString& s)
343       {
344       QString escaped;
345       escaped.reserve(s.size());
346       for (int i = 0; i < s.size(); ++i) {
347             ushort c = s.at(i).unicode();
348             escaped += xmlString(c);
349             }
350       return escaped;
351       }
352 
353 //---------------------------------------------------------
354 //   dump
355 //---------------------------------------------------------
356 
dump(int len,const unsigned char * p)357 void XmlWriter::dump(int len, const unsigned char* p)
358       {
359       putLevel();
360       int col = 0;
361       setFieldWidth(5);
362       setNumberFlags(numberFlags() | QTextStream::ShowBase);
363       setIntegerBase(16);
364       for (int i = 0; i < len; ++i, ++col) {
365             if (col >= 16) {
366                   setFieldWidth(0);
367                   *this << endl;
368                   col = 0;
369                   putLevel();
370                   setFieldWidth(5);
371                   }
372             *this << (p[i] & 0xff);
373             }
374       if (col)
375             *this << endl << dec;
376       setFieldWidth(0);
377       setIntegerBase(10);
378       }
379 
380 //---------------------------------------------------------
381 //   writeXml
382 //    string s is already escaped (& -> "&amp;")
383 //---------------------------------------------------------
384 
writeXml(const QString & name,QString s)385 void XmlWriter::writeXml(const QString& name, QString s)
386       {
387       QString ename(name.split(' ')[0]);
388       putLevel();
389       for (int i = 0; i < s.size(); ++i) {
390             ushort c = s.at(i).unicode();
391             if (c < 0x20 && c != 0x09 && c != 0x0A && c != 0x0D)
392                   s[i] = '?';
393             }
394       *this << "<" << name << ">";
395       *this << s;
396       *this << "</" << ename << ">\n";
397       }
398 
399 //---------------------------------------------------------
400 //   assignLocalIndex
401 //---------------------------------------------------------
402 
assignLocalIndex(const Location & mainElementLocation)403 int XmlWriter::assignLocalIndex(const Location& mainElementLocation)
404       {
405       return _linksIndexer.assignLocalIndex(mainElementLocation);
406       }
407 
408 //---------------------------------------------------------
409 //   canWrite
410 //---------------------------------------------------------
411 
canWrite(const Element * e) const412 bool XmlWriter::canWrite(const Element* e) const
413       {
414       if (!_clipboardmode)
415             return true;
416       return _filter.canSelect(e);
417       }
418 
419 //---------------------------------------------------------
420 //   canWriteVoice
421 //---------------------------------------------------------
422 
canWriteVoice(int track) const423 bool XmlWriter::canWriteVoice(int track) const
424       {
425       if (!_clipboardmode)
426             return true;
427       return _filter.canSelectVoice(track);
428       }
429 
430 }
431 
432 
433