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