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