]> git.sesse.net Git - kdenlive/blob - src/titledocument.cpp
Don't convert number when not necessary
[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("textwidth", t->sceneBoundingRect().width());
186                 content.setAttribute(effectName, effectParams.join(";"));
187             }
188
189             // Only save when necessary.
190             if (t->data(OriginXLeft).toInt() == AxisInverted) {
191                 content.setAttribute("kdenlive-axis-x-inverted", t->data(OriginXLeft).toInt());
192             }
193             if (t->data(OriginYTop).toInt() == AxisInverted) {
194                 content.setAttribute("kdenlive-axis-y-inverted", t->data(OriginYTop).toInt());
195             }
196             if (t->textWidth() != -1) {
197                 content.setAttribute("alignment", t->textCursor().blockFormat().alignment());
198             }
199             break;
200         default:
201             continue;
202         }
203
204         // position
205         QDomElement pos = doc.createElement("position");
206         pos.setAttribute("x", item->pos().x());
207         pos.setAttribute("y", item->pos().y());
208         QTransform transform = item->transform();
209         QDomElement tr = doc.createElement("transform");
210         if (!item->data(ZOOMFACTOR).isNull()) {
211             tr.setAttribute("zoom", QString::number(item->data(ZOOMFACTOR).toInt()));
212         }
213         if (!item->data(ROTATEFACTOR).isNull()) {
214             QList<QVariant> rotlist = item->data(ROTATEFACTOR).toList();
215             tr.setAttribute("rotation", QString("%1,%2,%3").arg(rotlist[0].toDouble()).arg(rotlist[1].toDouble()).arg(rotlist[2].toDouble()));
216         }
217         tr.appendChild(doc.createTextNode(
218                            QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
219                                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())
220                        )
221                       );
222         e.setAttribute("z-index", item->zValue());
223         pos.appendChild(tr);
224
225 #if QT_VERSION >= 0x040600
226         // effects
227         QGraphicsEffect *eff = item->graphicsEffect();
228         if (eff) {
229             QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
230             QDomElement effect = doc.createElement("effect");
231             if (blur) {
232                 effect.setAttribute("type", "blur");
233                 effect.setAttribute("blurradius", blur->blurRadius());
234             } else {
235                 QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
236                 if (shadow) {
237                     effect.setAttribute("type", "shadow");
238                     effect.setAttribute("blurradius", shadow->blurRadius());
239                     effect.setAttribute("xoffset", shadow->xOffset());
240                     effect.setAttribute("yoffset", shadow->yOffset());
241                 }
242             }
243             e.appendChild(effect);
244         }
245 #endif
246
247         e.appendChild(pos);
248         e.appendChild(content);
249         if (item->zValue() > -1000) main.appendChild(e);
250     }
251     if (startv && endv) {
252         QDomElement endp = doc.createElement("endviewport");
253         QDomElement startp = doc.createElement("startviewport");
254         QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
255         endp.setAttribute("rect", rectFToString(r));
256         QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
257         startp.setAttribute("rect", rectFToString(r2));
258
259         main.appendChild(startp);
260         main.appendChild(endp);
261     }
262     QDomElement backgr = doc.createElement("background");
263     QColor color = getBackgroundColor();
264     backgr.setAttribute("color", colorToString(color));
265     main.appendChild(backgr);
266
267     return doc;
268 }
269
270 /** \brief Get the background color (incl. alpha) from the document, if possibly
271   * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
272 QColor TitleDocument::getBackgroundColor()
273 {
274     QColor color(0, 0, 0, 0);
275     if (m_scene) {
276         QList<QGraphicsItem *> items = m_scene->items();
277         for (int i = 0; i < items.size(); i++) {
278             if (items.at(i)->zValue() == -1100) {
279                 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
280                 return color;
281             }
282         }
283     }
284     return color;
285 }
286
287
288 bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int duration, bool embed)
289 {
290     if (!m_scene)
291         return false;
292
293     QDomDocument doc = xml(startv, endv, embed);
294     doc.documentElement().setAttribute("duration", duration);
295     // keep some time for backwards compatibility (opening projects with older versions) - 26/12/12
296     doc.documentElement().setAttribute("out", duration);
297     KTemporaryFile tmpfile;
298     if (!tmpfile.open()) {
299         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
300         return false;
301     }
302     QFile xmlf(tmpfile.fileName());
303     if (!xmlf.open(QIODevice::WriteOnly))
304         return false;
305     xmlf.write(doc.toString().toUtf8());
306     if (xmlf.error() != QFile::NoError) {
307         xmlf.close();
308         return false;
309     }
310     xmlf.close();
311     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
312 }
313
314 int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *duration, const QString& projectpath)
315 {
316     m_projectPath = projectpath;
317     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
318     //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
319     if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
320         int doc_width = doc.documentElement().attribute("width").toInt();
321         int doc_height = doc.documentElement().attribute("height").toInt();
322         if (doc_width != m_width || doc_height != m_height) {
323             KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
324             //TODO: convert using QTransform
325             m_width = doc_width;
326             m_height = doc_height;
327         }
328     } else {
329         // Document has no size info, it is likely an old version title, so ignore viewport data
330         QDomNodeList viewportlist = doc.documentElement().elementsByTagName("startviewport");
331         if (!viewportlist.isEmpty()) {
332             doc.documentElement().removeChild(viewportlist.at(0));
333         }
334         viewportlist = doc.documentElement().elementsByTagName("endviewport");
335         if (!viewportlist.isEmpty()) {
336             doc.documentElement().removeChild(viewportlist.at(0));
337         }
338     }
339     //TODO: get default title duration instead of hardcoded one
340     if (doc.documentElement().hasAttribute("duration"))
341         *duration = doc.documentElement().attribute("duration").toInt();
342     else if (doc.documentElement().hasAttribute("out"))
343         *duration = doc.documentElement().attribute("out").toInt();
344     else
345         *duration = 125;
346
347     int maxZValue = 0;
348     if (titles.size()) {
349
350         QDomNodeList items = titles.item(0).childNodes();
351         for (int i = 0; i < items.count(); i++) {
352             QGraphicsItem *gitem = NULL;
353             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
354             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
355             if (zValue > -1000) {
356                 if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsTextItem") {
357                     QDomNamedNodeMap txtProperties = items.item(i).namedItem("content").attributes();
358                     QFont font(txtProperties.namedItem("font").nodeValue());
359
360                     QDomNode node = txtProperties.namedItem("font-bold");
361                     if (!node.isNull()) {
362                         // Old: Bold/Not bold.
363                         font.setBold(node.nodeValue().toInt());
364                     } else {
365                         // New: Font weight (QFont::)
366                         font.setWeight(txtProperties.namedItem("font-weight").nodeValue().toInt());
367                     }
368                     //font.setBold(txtProperties.namedItem("font-bold").nodeValue().toInt());
369                     font.setItalic(txtProperties.namedItem("font-italic").nodeValue().toInt());
370                     font.setUnderline(txtProperties.namedItem("font-underline").nodeValue().toInt());
371                     // Older Kdenlive version did not store pixel size but point size
372                     if (txtProperties.namedItem("font-pixel-size").isNull()) {
373                         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"));
374                         QFont f2;
375                         f2.setPointSize(txtProperties.namedItem("font-size").nodeValue().toInt());
376                         font.setPixelSize(QFontInfo(f2).pixelSize());
377                     } else
378                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
379                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
380                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
381                     QTextCursor cursor(txt->document());
382                     cursor.select(QTextCursor::Document);
383                     QTextCharFormat format;
384                     if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
385                         txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
386                         txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
387                         format.setTextOutline(
388                             QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
389                                  txtProperties.namedItem("font-outline").nodeValue().toDouble(),
390                                  Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)
391                         );
392
393                     }
394                     format.setForeground(QBrush(col));
395                     cursor.mergeCharFormat(format);
396                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
397                     if (txtProperties.namedItem("alignment").isNull() == false) {
398                         txt->setTextWidth(txt->boundingRect().width());
399                         QTextCursor cur = txt->textCursor();
400                         QTextBlockFormat format = cur.blockFormat();
401                         format.setAlignment((Qt::Alignment) txtProperties.namedItem("alignment").nodeValue().toInt());
402                         cur.select(QTextCursor::Document);
403                         cur.setBlockFormat(format);
404                         txt->setTextCursor(cur);
405                         cur.clearSelection();
406                         txt->setTextCursor(cur);
407                     }
408
409                     if (!txtProperties.namedItem("kdenlive-axis-x-inverted").isNull()) {
410                         txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt());
411                     }
412                     if (!txtProperties.namedItem("kdenlive-axis-y-inverted").isNull()) {
413                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
414                     }
415
416                     // Effects
417                     if (!txtProperties.namedItem("typewriter").isNull()) {
418                         QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
419                         txt->setData(100, effData);
420                     }
421
422                     gitem = txt;
423                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
424                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
425                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
426                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
427                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
428                     QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin), QBrush(stringToColor(br_str)));
429                     gitem = rec;
430                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
431                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
432                     QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue();
433                     QPixmap pix;
434                     if (base64.isEmpty()) {
435                         pix.load(url);
436                     } else {
437                         pix.loadFromData(QByteArray::fromBase64(base64.toAscii()));
438                     }
439                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
440                     rec->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
441                     rec->setData(Qt::UserRole, url);
442                     if (!base64.isEmpty()) {
443                         rec->setData(Qt::UserRole + 1, base64);
444                     }
445                     gitem = rec;
446                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
447                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
448                     QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue();
449                     QGraphicsSvgItem *rec = NULL;
450                     if (base64.isEmpty()) {
451                         rec = new QGraphicsSvgItem(url);
452                     } else {
453                         rec = new QGraphicsSvgItem();
454                         QSvgRenderer *renderer = new QSvgRenderer(QByteArray::fromBase64(base64.toAscii()), rec);
455                         rec->setSharedRenderer(renderer);
456                         //QString elem=rec->elementId();
457                         //QRectF bounds = renderer->boundsOnElement(elem);
458                     }
459                     if (rec) {
460                         m_scene->addItem(rec);
461                         rec->setData(Qt::UserRole, url);
462                         if (!base64.isEmpty()) {
463                             rec->setData(Qt::UserRole + 1, base64);
464                         }
465                         gitem = rec;
466                     }
467                 }
468             }
469             //pos and transform
470             if (gitem) {
471                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
472                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
473                 gitem->setPos(p);
474                 QDomElement trans = items.item(i).namedItem("position").firstChild().toElement();
475                 gitem->setTransform(stringToTransform(trans.firstChild().nodeValue()));
476                 QString rotate = trans.attribute("rotation");
477                 if (!rotate.isEmpty()) gitem->setData(ROTATEFACTOR, stringToList(rotate));
478                 QString zoom = trans.attribute("zoom");
479                 if (!zoom.isEmpty()) gitem->setData(ZOOMFACTOR, zoom.toInt());
480                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
481                 if (zValue > maxZValue) maxZValue = zValue;
482                 gitem->setZValue(zValue);
483                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
484
485 #if QT_VERSION >= 0x040600
486                 // effects
487                 QDomNode eff = items.item(i).namedItem("effect");
488                 if (!eff.isNull()) {
489                     QDomElement e = eff.toElement();
490                     if (e.attribute("type") == "blur") {
491                         QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
492                         blur->setBlurRadius(e.attribute("blurradius").toInt());
493                         gitem->setGraphicsEffect(blur);
494                     } else if (e.attribute("type") == "shadow") {
495                         QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
496                         shadow->setBlurRadius(e.attribute("blurradius").toInt());
497                         shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
498                         gitem->setGraphicsEffect(shadow);
499                     }
500                 }
501 #endif
502             }
503
504             if (items.item(i).nodeName() == "background") {
505                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
506                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
507                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
508                 QList<QGraphicsItem *> items = m_scene->items();
509                 for (int i = 0; i < items.size(); i++) {
510                     if (items.at(i)->zValue() == -1100) {
511                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
512                         break;
513                     }
514                 }
515             } else if (items.item(i).nodeName() == "startviewport" && startv) {
516                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
517                 QRectF r = stringToRect(rect);
518                 startv->setRect(0, 0, r.width(), r.height());
519                 startv->setPos(r.topLeft());
520             } else if (items.item(i).nodeName() == "endviewport" && endv) {
521                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
522                 QRectF r = stringToRect(rect);
523                 endv->setRect(0, 0, r.width(), r.height());
524                 endv->setPos(r.topLeft());
525             }
526         }
527     }
528     return maxZValue;
529 }
530
531 QString TitleDocument::colorToString(const QColor& c)
532 {
533     QString ret = "%1,%2,%3,%4";
534     ret = ret.arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
535     return ret;
536 }
537
538 QString TitleDocument::rectFToString(const QRectF& c)
539 {
540     QString ret = "%1,%2,%3,%4";
541     ret = ret.arg(c.left()).arg(c.top()).arg(c.width()).arg(c.height());
542     return ret;
543 }
544
545 QRectF TitleDocument::stringToRect(const QString & s)
546 {
547
548     QStringList l = s.split(',');
549     if (l.size() < 4)
550         return QRectF();
551     return QRectF(l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(), l.at(3).toDouble()).normalized();
552 }
553
554 QColor TitleDocument::stringToColor(const QString & s)
555 {
556     QStringList l = s.split(',');
557     if (l.size() < 4)
558         return QColor();
559     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
560 }
561
562 QTransform TitleDocument::stringToTransform(const QString& s)
563 {
564     QStringList l = s.split(',');
565     if (l.size() < 9)
566         return QTransform();
567     return QTransform(
568                l.at(0).toDouble(), l.at(1).toDouble(), l.at(2).toDouble(),
569                l.at(3).toDouble(), l.at(4).toDouble(), l.at(5).toDouble(),
570                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
571            );
572 }
573
574 QList<QVariant> TitleDocument::stringToList(const QString & s)
575 {
576     QStringList l = s.split(',');
577     if (l.size() < 3)
578         return QList<QVariant>();
579     return QList<QVariant>() << QVariant(l.at(0).toDouble()) << QVariant(l.at(1).toDouble()) << QVariant(l.at(2).toDouble());
580 }
581
582 int TitleDocument::frameWidth() const
583 {
584     return m_width;
585 }
586
587 int TitleDocument::frameHeight() const
588 {
589     return m_height;
590 }
591
592