]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
titledocument.cpp: initialize variables [Coverity 11/14] by Mikko Rapeli
[kdenlive] / src / titledocument.cpp
1 /***************************************************************************
2                           titledocument.h  -  description
3                              -------------------
4     begin                : Feb 28 2008
5     copyright            : (C) 2008 by Marco Gittler
6     email                : g.marco@freenet.de
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "titledocument.h"
19
20 #include <KDebug>
21 #include <KTemporaryFile>
22 #include <kio/netaccess.h>
23 #include <KApplication>
24 #include <KStandardDirs>
25 #include <KLocale>
26 #include <KMessageBox>
27
28 #include <QGraphicsScene>
29 #include <QDomElement>
30 #include <QGraphicsItem>
31 #include <QGraphicsRectItem>
32 #include <QGraphicsTextItem>
33 #include <QGraphicsSvgItem>
34 #include <QCryptographicHash>
35 #include <QSvgRenderer>
36 #include <QFontInfo>
37 #include <QFile>
38 #include <QTextCursor>
39
40 #include <locale.h>
41
42 #if QT_VERSION >= 0x040600
43 #include <QGraphicsEffect>
44 #include <QGraphicsBlurEffect>
45 #include <QGraphicsDropShadowEffect>
46 #endif
47
48 QByteArray fileToByteArray(const QString& filename)
49 {
50     QByteArray ret;
51     QFile file(filename);
52     if (file.open(QIODevice::ReadOnly)) {
53         while (!file.atEnd()) {
54             ret.append(file.readLine());
55         }
56     }
57     return ret;
58 }
59
60 TitleDocument::TitleDocument()
61 {
62     m_scene = NULL;
63     m_width = 0;
64     m_height = 0;
65 }
66
67 void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
68 {
69     m_scene = _scene;
70     m_width = width;
71     m_height = height;
72 }
73
74 int TitleDocument::base64ToUrl(QGraphicsItem* item, QDomElement& content, bool embed)
75 {
76     if (embed) {
77         if (!item->data(Qt::UserRole + 1).toString().isEmpty()) {
78             content.setAttribute("base64", item->data(Qt::UserRole + 1).toString());
79         } else if (!item->data(Qt::UserRole).toString().isEmpty()) {
80             content.setAttribute("base64", fileToByteArray(item->data(Qt::UserRole).toString()).toBase64().data());
81         }
82         content.removeAttribute("url");
83     } else {
84         // save for project files to disk
85         QString base64 = item->data(Qt::UserRole + 1).toString();
86         if (!base64.isEmpty()) {
87             QString titlePath;
88             if (!m_projectPath.isEmpty()) {
89                 titlePath = m_projectPath;
90             } else {
91                 titlePath = "/tmp/titles";
92             }
93             QString filename = extractBase64Image(titlePath, base64);
94             if (!filename.isEmpty()) {
95                 content.setAttribute("url", filename);
96                 content.removeAttribute("base64");
97             }
98
99         } else {
100             return 1;
101         }
102     }
103     return 0;
104 }
105
106
107 //static
108 const QString TitleDocument::extractBase64Image(const QString &titlePath, const QString &data)
109 {
110     QString filename = titlePath + QString(QCryptographicHash::hash(data.toAscii(), QCryptographicHash::Md5).toHex().append(".titlepart"));
111     KStandardDirs::makeDir(titlePath);
112     QFile f(filename);
113     if (f.open(QIODevice::WriteOnly)) {
114         f.write(QByteArray::fromBase64(data.toAscii())) ;
115         f.close();
116         return filename;
117     }
118     return QString();
119 }
120
121 QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* endv, bool embed)
122 {
123     QDomDocument doc;
124
125     QDomElement main = doc.createElement("kdenlivetitle");
126     main.setAttribute("width", m_width);
127     main.setAttribute("height", m_height);
128     // Save locale
129     const char *locale = setlocale(LC_NUMERIC, NULL);
130     main.setAttribute("LC_NUMERIC", locale);
131     doc.appendChild(main);
132
133     foreach(QGraphicsItem * item, m_scene->items()) {
134         QDomElement e = doc.createElement("item");
135         QDomElement content = doc.createElement("content");
136         QFont font;
137         QGraphicsTextItem *t;
138
139         switch (item->type()) {
140         case 7:
141             e.setAttribute("type", "QGraphicsPixmapItem");
142             content.setAttribute("url", item->data(Qt::UserRole).toString());
143             base64ToUrl(item, content, embed);
144             break;
145         case 13:
146             e.setAttribute("type", "QGraphicsSvgItem");
147             content.setAttribute("url", item->data(Qt::UserRole).toString());
148             base64ToUrl(item, content, embed);
149             break;
150         case 3:
151             e.setAttribute("type", "QGraphicsRectItem");
152             content.setAttribute("rect", rectFToString(((QGraphicsRectItem*)item)->rect().normalized()));
153             content.setAttribute("pencolor", colorToString(((QGraphicsRectItem*)item)->pen().color()));
154             content.setAttribute("penwidth", ((QGraphicsRectItem*)item)->pen().width());
155             content.setAttribute("brushcolor", colorToString(((QGraphicsRectItem*)item)->brush().color()));
156             break;
157         case 8:
158             e.setAttribute("type", "QGraphicsTextItem");
159             t = static_cast<QGraphicsTextItem *>(item);
160             // Don't save empty text nodes
161             if (t->toPlainText().simplified().isEmpty()) continue;
162             //content.appendChild(doc.createTextNode(((QGraphicsTextItem*)item)->toHtml()));
163             content.appendChild(doc.createTextNode(t->toPlainText()));
164             font = t->font();
165             content.setAttribute("font", font.family());
166             content.setAttribute("font-weight", font.weight());
167             content.setAttribute("font-pixel-size", font.pixelSize());
168             content.setAttribute("font-italic", font.italic());
169             content.setAttribute("font-underline", font.underline());
170             {
171                 QTextCursor cursor(t->document());
172                 cursor.select(QTextCursor::Document);
173                 QColor fontcolor = cursor.charFormat().foreground().color();
174                 content.setAttribute("font-color", colorToString(fontcolor));
175                 if (!t->data(101).isNull()) content.setAttribute("font-outline", t->data(101).toDouble());
176                 if (!t->data(102).isNull()) {
177                     QVariant variant = t->data(102);
178                     QColor outlineColor = variant.value<QColor>();
179                     content.setAttribute("font-outline-color", colorToString(outlineColor));
180                 }
181             }
182             if (!t->data(100).isNull()) {
183                 QStringList effectParams = t->data(100).toStringList();
184                 QString effectName = effectParams.takeFirst();
185                 content.setAttribute(effectName, effectParams.join(";"));
186             }
187
188             // Only save when necessary.
189             if (t->data(OriginXLeft).toInt() == AxisInverted) {
190                 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
191             }
192             if (t->data(OriginYTop).toInt() == AxisInverted) {
193                 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
194             }
195             if (t->textWidth() != -1) {
196                 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
197             }
198             break;
199         default:
200             continue;
201         }
202
203         // position
204         QDomElement pos = doc.createElement("position");
205         pos.setAttribute("x", item->pos().x());
206         pos.setAttribute("y", item->pos().y());
207         QTransform transform = item->transform();
208         QDomElement tr = doc.createElement("transform");
209         if (!item->data(ZOOMFACTOR).isNull()) {
210             tr.setAttribute("zoom", QString::number(item->data(ZOOMFACTOR).toInt()));
211         }
212         if (!item->data(ROTATEFACTOR).isNull()) {
213             QList<QVariant> rotlist = item->data(ROTATEFACTOR).toList();
214             tr.setAttribute("rotation", QString("%1,%2,%3").arg(rotlist[0].toDouble()).arg(rotlist[1].toDouble()).arg(rotlist[2].toDouble()));
215         }
216         tr.appendChild(doc.createTextNode(
217                            QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
218                                transform.m11()).arg(transform.m12()).arg(transform.m13()).arg(transform.m21()).arg(transform.m22()).arg(transform.m23()).arg(transform.m31()).arg(transform.m32()).arg(transform.m33())
219                        )
220                       );
221         e.setAttribute("z-index", item->zValue());
222         pos.appendChild(tr);
223
224 #if QT_VERSION >= 0x040600
225         // effects
226         QGraphicsEffect *eff = item->graphicsEffect();
227         if (eff) {
228             QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
229             QDomElement effect = doc.createElement("effect");
230             if (blur) {
231                 effect.setAttribute("type", "blur");
232                 effect.setAttribute("blurradius", blur->blurRadius());
233             } else {
234                 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
235                 if (shadow) {
236                     effect.setAttribute("type", "shadow");
237                     effect.setAttribute("blurradius", shadow->blurRadius());
238                     effect.setAttribute("xoffset", shadow->xOffset());
239                     effect.setAttribute("yoffset", shadow->yOffset());
240                 }
241             }
242             e.appendChild(effect);
243         }
244 #endif
245
246         e.appendChild(pos);
247         e.appendChild(content);
248         if (item->zValue() > -1000) main.appendChild(e);
249     }
250     if (startv && endv) {
251         QDomElement endp = doc.createElement("endviewport");
252         QDomElement startp = doc.createElement("startviewport");
253         QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
254         endp.setAttribute("rect", rectFToString(r));
255         QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
256         startp.setAttribute("rect", rectFToString(r2));
257
258         main.appendChild(startp);
259         main.appendChild(endp);
260     }
261     QDomElement backgr = doc.createElement("background");
262     QColor color = getBackgroundColor();
263     backgr.setAttribute("color", colorToString(color));
264     main.appendChild(backgr);
265
266     return doc;
267 }
268
269 /** \brief Get the background color (incl. alpha) from the document, if possibly
270   * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
271 QColor TitleDocument::getBackgroundColor()
272 {
273     QColor color(0, 0, 0, 0);
274     if (m_scene) {
275         QList<QGraphicsItem *> items = m_scene->items();
276         for (int i = 0; i < items.size(); i++) {
277             if (items.at(i)->zValue() == -1100) {
278                 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
279                 return color;
280             }
281         }
282     }
283     return color;
284 }
285
286
287 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int out, bool embed)
288 {
289     if (!m_scene)
290         return false;
291
292     QDomDocument doc = xml(startv, endv, embed);
293     doc.documentElement().setAttribute("out", out);
294     KTemporaryFile tmpfile;
295     if (!tmpfile.open()) {
296         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
297         return false;
298     }
299     QFile xmlf(tmpfile.fileName());
300     xmlf.open(QIODevice::WriteOnly);
301     xmlf.write(doc.toString().toUtf8());
302     if (xmlf.error() != QFile::NoError) {
303         xmlf.close();
304         return false;
305     }
306     xmlf.close();
307     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
308 }
309
310 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *out, const QString& projectpath)
311 {
312     m_projectPath = projectpath;
313     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
314     //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
315     if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
316         int doc_width = doc.documentElement().attribute("width").toInt();
317         int doc_height = doc.documentElement().attribute("height").toInt();
318         if (doc_width != m_width || doc_height != m_height) {
319             KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
320             //TODO: convert using QTransform
321             m_width = doc_width;
322             m_height = doc_height;
323         }
324     } else {
325         // Document has no size info, it is likely an old version title, so ignore viewport data
326         QDomNodeList viewportlist = doc.documentElement().elementsByTagName("startviewport");
327         if (!viewportlist.isEmpty()) {
328             doc.documentElement().removeChild(viewportlist.at(0));
329         }
330         viewportlist = doc.documentElement().elementsByTagName("endviewport");
331         if (!viewportlist.isEmpty()) {
332             doc.documentElement().removeChild(viewportlist.at(0));
333         }
334     }
335     //TODO: get default title duration instead of hardcoded one
336     if (doc.documentElement().hasAttribute("out"))
337         *out = doc.documentElement().attribute("out").toInt();
338     else
339         *out = 125;
340
341     int maxZValue = 0;
342     if (titles.size()) {
343
344         QDomNodeList items = titles.item(0).childNodes();
345         for (int i = 0; i < items.count(); i++) {
346             QGraphicsItem *gitem = NULL;
347             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
348             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
349             if (zValue > -1000) {
350                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
351                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
352                     QFont font(txtProperties.namedItem("font").nodeValue());
353
354                     QDomNode node = txtProperties.namedItem("font-bold");
355                     if (!node.isNull()) {
356                         // Old: Bold/Not bold.
357                         font.setBold(node.nodeValue().toInt());
358                     } else {
359                         // New: Font weight (QFont::)
360                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
361                     }
362                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
363                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
364                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
365                     // Older Kdenlive version did not store pixel size but point size
366                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
367                         KMessageBox::information(kapp->activeWindow(), i18n("Some of your text clips were saved with size in points, which means different sizes on different displays. They will be converted to pixel size, making them portable, but you could have to adjust their size."), i18n("Text Clips Updated"));
368                         QFont f2;
369                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
370                         font.setPixelSize(QFontInfo(f2).pixelSize());
371                     } else
372                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
373                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
374                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
375                     QTextCursor cursor(txt->document());
376                     cursor.select(QTextCursor::Document);
377                     QTextCharFormat format;
378                     if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
379                         txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
380                         txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
381                         format.setTextOutline(
382                             QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
383                                  txtProperties.namedItem("font-outline").nodeValue().toDouble(),
384                                  Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)
385                         );
386
387                     }
388                     format.setForeground(QBrush(col));
389                     cursor.mergeCharFormat(format);
390                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
391                     if (txtProperties.namedItem("alignment").isNull() == false) {
392                         txt->setTextWidth(txt->boundingRect().width());
393                         QTextCursor cur = txt->textCursor();
394                         QTextBlockFormat format = cur.blockFormat();
395                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
396                         cur.select(QTextCursor::Document);
397                         cur.setBlockFormat(format);
398                         txt->setTextCursor(cur);
399                         cur.clearSelection();
400                         txt->setTextCursor(cur);
401                     }
402
403                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
404                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
405                     }
406                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
407                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
408                     }
409
410                     // Effects
411                     if (!txtProperties.namedItem("typewriter").isNull()) {
412                         QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
413                         txt->setData(100, effData);
414                     }
415
416                     gitem = txt;
417                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
418                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
419                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
420                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
421                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
422                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin), QBrush(stringToColor(br_str)));
423                     gitem = rec;
424                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
425                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
426                     QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue();
427                     QPixmap pix;
428                     if (base64.isEmpty()) {
429                         pix.load(url);
430                     } else {
431                         pix.loadFromData(QByteArray::fromBase64(base64.toAscii()));
432                     }
433                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
434                     rec->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
435                     rec->setData(Qt::UserRole, url);
436                     if (!base64.isEmpty()) {
437                         rec->setData(Qt::UserRole + 1, base64);
438                     }
439                     gitem = rec;
440                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
441                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
442                     QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue();
443                     QGraphicsSvgItem *rec = NULL;
444                     if (base64.isEmpty()) {
445                         rec = new QGraphicsSvgItem(url);
446                     } else {
447                         rec = new QGraphicsSvgItem();
448                         QSvgRenderer *renderer = new QSvgRenderer(QByteArray::fromBase64(base64.toAscii()), rec);
449                         rec->setSharedRenderer(renderer);
450                         //QString elem=rec->elementId();
451                         //QRectF bounds = renderer->boundsOnElement(elem);
452                     }
453                     if (rec) {
454                         m_scene->addItem(rec);
455                         rec->setData(Qt::UserRole, url);
456                         if (!base64.isEmpty()) {
457                             rec->setData(Qt::UserRole + 1, base64);
458                         }
459                         gitem = rec;
460                     }
461                 }
462             }
463             //pos and transform
464             if (gitem) {
465                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
466                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
467                 gitem->setPos(p);
468                 QDomElement trans = items.item(i).namedItem("position").firstChild().toElement();
469                 gitem->setTransform(stringToTransform(trans.firstChild().nodeValue()));
470                 QString rotate = trans.attribute("rotation");
471                 if (!rotate.isEmpty()) gitem->setData(ROTATEFACTOR, stringToList(rotate));
472                 QString zoom = trans.attribute("zoom");
473                 if (!zoom.isEmpty()) gitem->setData(ZOOMFACTOR, zoom.toInt());
474                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
475                 if (zValue > maxZValue) maxZValue = zValue;
476                 gitem->setZValue(zValue);
477                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
478
479 #if QT_VERSION >= 0x040600
480                 // effects
481                 QDomNode eff = items.item(i).namedItem("effect");
482                 if (!eff.isNull()) {
483                     QDomElement e = eff.toElement();
484                     if (e.attribute("type") == "blur") {
485                         QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
486                         blur->setBlurRadius(e.attribute("blurradius").toInt());
487                         gitem->setGraphicsEffect(blur);
488                     } else if (e.attribute("type") == "shadow") {
489                         QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
490                         shadow->setBlurRadius(e.attribute("blurradius").toInt());
491                         shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
492                         gitem->setGraphicsEffect(shadow);
493                     }
494                 }
495 #endif
496             }
497
498             if (items.item(i).nodeName() == "background") {
499                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
500                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
501                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
502                 QList<QGraphicsItem *> items = m_scene->items();
503                 for (int i = 0; i < items.size(); i++) {
504                     if (items.at(i)->zValue() == -1100) {
505                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
506                         break;
507                     }
508                 }
509             } else if (items.item(i).nodeName() == "startviewport" && startv) {
510                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
511                 QRectF r = stringToRect(rect);
512                 startv->setRect(0, 0, r.width(), r.height());
513                 startv->setPos(r.topLeft());
514             } else if (items.item(i).nodeName() == "endviewport" && endv) {
515                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
516                 QRectF r = stringToRect(rect);
517                 endv->setRect(0, 0, r.width(), r.height());
518                 endv->setPos(r.topLeft());
519             }
520         }
521     }
522     return maxZValue;
523 }
524
525 QString TitleDocument::colorToString(const QColor& c)
526 {
527     QString ret = "%1,%2,%3,%4";
528     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
529     return ret;
530 }
531
532 QString TitleDocument::rectFToString(const QRectF& c)
533 {
534     QString ret = "%1,%2,%3,%4";
535     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
536     return ret;
537 }
538
539 QRectF TitleDocument::stringToRect(const QString & s)
540 {
541
542     QStringList l = s.split(',');
543     if (l.size() < 4)
544         return QRectF();
545     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
546 }
547
548 QColor TitleDocument::stringToColor(const QString & s)
549 {
550     QStringList l = s.split(',');
551     if (l.size() < 4)
552         return QColor();
553     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
554 }
555
556 QTransform TitleDocument::stringToTransform(const QString& s)
557 {
558     QStringList l = s.split(',');
559     if (l.size() < 9)
560         return QTransform();
561     return QTransform(
562                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
563                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
564                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
565            );
566 }
567
568 QList<QVariant> TitleDocument::stringToList(const QString & s)
569 {
570     QStringList l = s.split(',');
571     if (l.size() < 3)
572         return QList<QVariant>();
573     return QList<QVariant>() << QVariant(l.at(0).toDouble()) << QVariant(l.at(1).toDouble()) << QVariant(l.at(2).toDouble());
574 }
575
576 int TitleDocument::frameWidth() const
577 {
578     return m_width;
579 }
580
581 int TitleDocument::frameHeight() const
582 {
583     return m_height;
584 }
585
586