]> git.sesse.net Git - kdenlive/blobdiff - src/titledocument.cpp
Fix label
[kdenlive] / src / titledocument.cpp
index 5c077a0a296a768eb3b208cf054e2399f6bc89a4..16fcda43e73d77cbd0965efb7d502ab929b6a2a0 100644 (file)
@@ -21,6 +21,7 @@
 #include <KTemporaryFile>
 #include <kio/netaccess.h>
 #include <KApplication>
+#include <KStandardDirs>
 #include <KLocale>
 #include <KMessageBox>
 
 #include <QGraphicsRectItem>
 #include <QGraphicsTextItem>
 #include <QGraphicsSvgItem>
+#include <QCryptographicHash>
+#include <QSvgRenderer>
 #include <QFontInfo>
 #include <QFile>
 #include <QTextCursor>
 
+#include <locale.h>
+
+#if QT_VERSION >= 0x040600
+#include <QGraphicsEffect>
+#include <QGraphicsBlurEffect>
+#include <QGraphicsDropShadowEffect>
+#endif
+
+QByteArray fileToByteArray(const QString& filename)
+{
+    QByteArray ret;
+    QFile file(filename);
+    if (file.open(QIODevice::ReadOnly)) {
+        while (!file.atEnd()) {
+            ret.append(file.readLine());
+        }
+    }
+    return ret;
+}
 
 TitleDocument::TitleDocument()
 {
     m_scene = NULL;
+    m_width = 0;
+    m_height = 0;
 }
 
 void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
@@ -47,16 +71,66 @@ void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
     m_height = height;
 }
 
-QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv)
+int TitleDocument::base64ToUrl(QGraphicsItem* item, QDomElement& content, bool embed)
+{
+    if (embed) {
+        if (!item->data(Qt::UserRole + 1).toString().isEmpty()) {
+            content.setAttribute("base64", item->data(Qt::UserRole + 1).toString());
+        } else if (!item->data(Qt::UserRole).toString().isEmpty()) {
+            content.setAttribute("base64", fileToByteArray(item->data(Qt::UserRole).toString()).toBase64().data());
+        }
+        content.removeAttribute("url");
+    } else {
+        // save for project files to disk
+        QString base64 = item->data(Qt::UserRole + 1).toString();
+        if (!base64.isEmpty()) {
+            QString titlePath;
+            if (!m_projectPath.isEmpty()) {
+                titlePath = m_projectPath;
+            } else {
+                titlePath = "/tmp/titles";
+            }
+            QString filename = extractBase64Image(titlePath, base64);
+            if (!filename.isEmpty()) {
+                content.setAttribute("url", filename);
+                content.removeAttribute("base64");
+            }
+
+        } else {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+//static
+const QString TitleDocument::extractBase64Image(const QString &titlePath, const QString &data)
+{
+    QString filename = titlePath + QString(QCryptographicHash::hash(data.toAscii(), QCryptographicHash::Md5).toHex().append(".titlepart"));
+    KStandardDirs::makeDir(titlePath);
+    QFile f(filename);
+    if (f.open(QIODevice::WriteOnly)) {
+        f.write(QByteArray::fromBase64(data.toAscii())) ;
+        f.close();
+        return filename;
+    }
+    return QString();
+}
+
+QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* endv, bool embed)
 {
     QDomDocument doc;
 
     QDomElement main = doc.createElement("kdenlivetitle");
     main.setAttribute("width", m_width);
     main.setAttribute("height", m_height);
+    // Save locale
+    const char *locale = setlocale(LC_NUMERIC, NULL);
+    main.setAttribute("LC_NUMERIC", locale);
     doc.appendChild(main);
 
-    foreach(QGraphicsItem* item, m_scene->items()) {
+    foreach(QGraphicsItem * item, m_scene->items()) {
         QDomElement e = doc.createElement("item");
         QDomElement content = doc.createElement("content");
         QFont font;
@@ -66,10 +140,12 @@ QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonIt
         case 7:
             e.setAttribute("type", "QGraphicsPixmapItem");
             content.setAttribute("url", item->data(Qt::UserRole).toString());
+            base64ToUrl(item, content, embed);
             break;
         case 13:
             e.setAttribute("type", "QGraphicsSvgItem");
             content.setAttribute("url", item->data(Qt::UserRole).toString());
+            base64ToUrl(item, content, embed);
             break;
         case 3:
             e.setAttribute("type", "QGraphicsRectItem");
@@ -91,7 +167,24 @@ QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonIt
             content.setAttribute("font-pixel-size", font.pixelSize());
             content.setAttribute("font-italic", font.italic());
             content.setAttribute("font-underline", font.underline());
-            content.setAttribute("font-color", colorToString(t->defaultTextColor()));
+            {
+                QTextCursor cursor(t->document());
+                cursor.select(QTextCursor::Document);
+                QColor fontcolor = cursor.charFormat().foreground().color();
+                content.setAttribute("font-color", colorToString(fontcolor));
+                if (!t->data(101).isNull()) content.setAttribute("font-outline", t->data(101).toDouble());
+                if (!t->data(102).isNull()) {
+                    QVariant variant = t->data(102);
+                    QColor outlineColor = variant.value<QColor>();
+                    content.setAttribute("font-outline-color", colorToString(outlineColor));
+                }
+            }
+            if (!t->data(100).isNull()) {
+                QStringList effectParams = t->data(100).toStringList();
+                QString effectName = effectParams.takeFirst();
+               content.setAttribute("textwidth", t->sceneBoundingRect().width());
+                content.setAttribute(effectName, effectParams.join(";"));
+            }
 
             // Only save when necessary.
             if (t->data(OriginXLeft).toInt() == AxisInverted) {
@@ -107,11 +200,20 @@ QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonIt
         default:
             continue;
         }
+
+        // position
         QDomElement pos = doc.createElement("position");
         pos.setAttribute("x", item->pos().x());
         pos.setAttribute("y", item->pos().y());
         QTransform transform = item->transform();
         QDomElement tr = doc.createElement("transform");
+        if (!item->data(ZOOMFACTOR).isNull()) {
+            tr.setAttribute("zoom", QString::number(item->data(ZOOMFACTOR).toInt()));
+        }
+        if (!item->data(ROTATEFACTOR).isNull()) {
+            QList<QVariant> rotlist = item->data(ROTATEFACTOR).toList();
+            tr.setAttribute("rotation", QString("%1,%2,%3").arg(rotlist[0].toDouble()).arg(rotlist[1].toDouble()).arg(rotlist[2].toDouble()));
+        }
         tr.appendChild(doc.createTextNode(
                            QString("%1,%2,%3,%4,%5,%6,%7,%8,%9").arg(
                                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())
@@ -120,26 +222,41 @@ QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonIt
         e.setAttribute("z-index", item->zValue());
         pos.appendChild(tr);
 
+#if QT_VERSION >= 0x040600
+        // effects
+        QGraphicsEffect *eff = item->graphicsEffect();
+        if (eff) {
+            QGraphicsBlurEffect *blur = static_cast <QGraphicsBlurEffect *>(eff);
+            QDomElement effect = doc.createElement("effect");
+            if (blur) {
+                effect.setAttribute("type", "blur");
+                effect.setAttribute("blurradius", blur->blurRadius());
+            } /*else {
+               //WARNING:those effects are anyways broken because they use QPixmaps which are not safe for MLT's threaded workflow
+                QGraphicsDropShadowEffect *shadow = static_cast <QGraphicsDropShadowEffect *>(eff);
+                if (shadow) {
+                    effect.setAttribute("type", "shadow");
+                    effect.setAttribute("blurradius", shadow->blurRadius());
+                    effect.setAttribute("xoffset", shadow->xOffset());
+                    effect.setAttribute("yoffset", shadow->yOffset());
+                }
+            }*/
+            e.appendChild(effect);
+        }
+#endif
 
         e.appendChild(pos);
         e.appendChild(content);
-        if (item->zValue() > -1100) main.appendChild(e);
+        if (item->zValue() > -1000) main.appendChild(e);
     }
     if (startv && endv) {
         QDomElement endp = doc.createElement("endviewport");
         QDomElement startp = doc.createElement("startviewport");
-        endp.setAttribute("x", endv->data(0).toString());
-        endp.setAttribute("y", endv->data(1).toString());
-        endp.setAttribute("size", endv->data(2).toString());
-        endp.setAttribute("rect", rectFToString(endv->boundingRect()));
-
-        startp.setAttribute("x", startv->data(0).toString());
-        startp.setAttribute("y", startv->data(1).toString());
-        startp.setAttribute("size", startv->data(2).toString());
-        startp.setAttribute("rect", rectFToString(startv->boundingRect()));
-
-        startp.setAttribute("z-index", startv->zValue());
-        endp.setAttribute("z-index", endv->zValue());
+        QRectF r(endv->pos().x(), endv->pos().y(), endv->rect().width(), endv->rect().height());
+        endp.setAttribute("rect", rectFToString(r));
+        QRectF r2(startv->pos().x(), startv->pos().y(), startv->rect().width(), startv->rect().height());
+        startp.setAttribute("rect", rectFToString(r2));
+
         main.appendChild(startp);
         main.appendChild(endp);
     }
@@ -153,12 +270,12 @@ QDomDocument TitleDocument::xml(QGraphicsPolygonItem* startv, QGraphicsPolygonIt
 
 /** \brief Get the background color (incl. alpha) from the document, if possibly
   * \returns The background color of the document, inclusive alpha. If none found, returns (0,0,0,0) */
-QColor TitleDocument::getBackgroundColor()
+QColor TitleDocument::getBackgroundColor() const
 {
     QColor color(0, 0, 0, 0);
     if (m_scene) {
         QList<QGraphicsItem *> items = m_scene->items();
-        for (int i = 0; i < items.size(); i++) {
+        for (int i = 0; i < items.size(); ++i) {
             if (items.at(i)->zValue() == -1100) {
                 color = ((QGraphicsRectItem *)items.at(i))->brush().color();
                 return color;
@@ -169,20 +286,23 @@ QColor TitleDocument::getBackgroundColor()
 }
 
 
-bool TitleDocument::saveDocument(const KUrl& url, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv, double out)
+bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int duration, bool embed)
 {
     if (!m_scene)
         return false;
 
-    QDomDocument doc = xml(startv, endv);
-    doc.documentElement().setAttribute("out", out);
+    QDomDocument doc = xml(startv, endv, embed);
+    doc.documentElement().setAttribute("duration", duration);
+    // keep some time for backwards compatibility (opening projects with older versions) - 26/12/12
+    doc.documentElement().setAttribute("out", duration);
     KTemporaryFile tmpfile;
     if (!tmpfile.open()) {
         kWarning() << "/////  CANNOT CREATE TMP FILE in: " << tmpfile.fileName();
         return false;
     }
     QFile xmlf(tmpfile.fileName());
-    xmlf.open(QIODevice::WriteOnly);
+    if (!xmlf.open(QIODevice::WriteOnly))
+        return false;
     xmlf.write(doc.toString().toUtf8());
     if (xmlf.error() != QFile::NoError) {
         xmlf.close();
@@ -192,49 +312,44 @@ bool TitleDocument::saveDocument(const KUrl& url, QGraphicsPolygonItem* startv,
     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
 }
 
-int TitleDocument::loadDocument(const KUrl& url, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv, double *out)
-{
-    QString tmpfile;
-    QDomDocument doc;
-    if (!m_scene)
-        return -1;
-
-    if (KIO::NetAccess::download(url, tmpfile, 0)) {
-        QFile file(tmpfile);
-        if (file.open(QIODevice::ReadOnly)) {
-            doc.setContent(&file, false);
-            file.close();
-        } else
-            return -1;
-        KIO::NetAccess::removeTempFile(tmpfile);
-        return loadFromXml(doc, startv, endv, out);
-    }
-    return -1;
-}
-
-int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, QGraphicsPolygonItem* endv, double *out)
+int TitleDocument::loadFromXml(const QDomDocument& doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *duration, const QString& projectpath)
 {
+    m_projectPath = projectpath;
     QDomNodeList titles = doc.elementsByTagName("kdenlivetitle");
     //TODO: Check if the opened title size is equal to project size, otherwise warn user and rescale
     if (doc.documentElement().hasAttribute("width") && doc.documentElement().hasAttribute("height")) {
         int doc_width = doc.documentElement().attribute("width").toInt();
         int doc_height = doc.documentElement().attribute("height").toInt();
         if (doc_width != m_width || doc_height != m_height) {
-            KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size. It will now be converted to the current project's size."), i18n("Resizing Title Clip"));
+            KMessageBox::information(kapp->activeWindow(), i18n("This title clip was created with a different frame size."), i18n("Title Profile"));
             //TODO: convert using QTransform
+            m_width = doc_width;
+            m_height = doc_height;
+        }
+    } else {
+        // Document has no size info, it is likely an old version title, so ignore viewport data
+        QDomNodeList viewportlist = doc.documentElement().elementsByTagName("startviewport");
+        if (!viewportlist.isEmpty()) {
+            doc.documentElement().removeChild(viewportlist.at(0));
+        }
+        viewportlist = doc.documentElement().elementsByTagName("endviewport");
+        if (!viewportlist.isEmpty()) {
+            doc.documentElement().removeChild(viewportlist.at(0));
         }
     }
     //TODO: get default title duration instead of hardcoded one
-    if (doc.documentElement().hasAttribute("out"))
-        *out = doc.documentElement().attribute("out").toDouble();
+    if (doc.documentElement().hasAttribute("duration"))
+        *duration = doc.documentElement().attribute("duration").toInt();
+    else if (doc.documentElement().hasAttribute("out"))
+        *duration = doc.documentElement().attribute("out").toInt();
     else
-        *out = 5000;
+        *duration = 125;
 
     int maxZValue = 0;
     if (titles.size()) {
 
         QDomNodeList items = titles.item(0).childNodes();
-        for (int i = 0; i < items.count(); i++) {
+        for (int i = 0; i < items.count(); ++i) {
             QGraphicsItem *gitem = NULL;
             kDebug() << items.item(i).attributes().namedItem("type").nodeValue();
             int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
@@ -264,7 +379,21 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, Q
                         font.setPixelSize(txtProperties.namedItem("font-pixel-size").nodeValue().toInt());
                     QColor col(stringToColor(txtProperties.namedItem("font-color").nodeValue()));
                     QGraphicsTextItem *txt = m_scene->addText(items.item(i).namedItem("content").firstChild().nodeValue(), font);
-                    txt->setDefaultTextColor(col);
+                    QTextCursor cursor(txt->document());
+                    cursor.select(QTextCursor::Document);
+                    QTextCharFormat format;
+                    if (txtProperties.namedItem("font-outline").nodeValue().toDouble() > 0.0) {
+                        txt->setData(101, txtProperties.namedItem("font-outline").nodeValue().toDouble());
+                        txt->setData(102, stringToColor(txtProperties.namedItem("font-outline-color").nodeValue()));
+                        format.setTextOutline(
+                            QPen(QColor(stringToColor(txtProperties.namedItem("font-outline-color").nodeValue())),
+                                 txtProperties.namedItem("font-outline").nodeValue().toDouble(),
+                                 Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)
+                        );
+
+                    }
+                    format.setForeground(QBrush(col));
+                    cursor.mergeCharFormat(format);
                     txt->setTextInteractionFlags(Qt::NoTextInteraction);
                     if (txtProperties.namedItem("alignment").isNull() == false) {
                         txt->setTextWidth(txt->boundingRect().width());
@@ -285,26 +414,57 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, Q
                         txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt());
                     }
 
+                    // Effects
+                    if (!txtProperties.namedItem("typewriter").isNull()) {
+                        QStringList effData = QStringList() << "typewriter" << txtProperties.namedItem("typewriter").nodeValue();
+                        txt->setData(100, effData);
+                    }
+
                     gitem = txt;
                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsRectItem") {
                     QString rect = items.item(i).namedItem("content").attributes().namedItem("rect").nodeValue();
                     QString br_str = items.item(i).namedItem("content").attributes().namedItem("brushcolor").nodeValue();
                     QString pen_str = items.item(i).namedItem("content").attributes().namedItem("pencolor").nodeValue();
                     double penwidth = items.item(i).namedItem("content").attributes().namedItem("penwidth").nodeValue().toDouble();
-                    QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth), QBrush(stringToColor(br_str)));
+                    QGraphicsRectItem *rec = m_scene->addRect(stringToRect(rect), QPen(QBrush(stringToColor(pen_str)), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin), QBrush(stringToColor(br_str)));
                     gitem = rec;
                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsPixmapItem") {
                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
-                    QPixmap pix(url);
+                    QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue();
+                    QPixmap pix;
+                    if (base64.isEmpty()) {
+                        pix.load(url);
+                    } else {
+                        pix.loadFromData(QByteArray::fromBase64(base64.toAscii()));
+                    }
                     QGraphicsPixmapItem *rec = m_scene->addPixmap(pix);
+                    rec->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
                     rec->setData(Qt::UserRole, url);
+                    if (!base64.isEmpty()) {
+                        rec->setData(Qt::UserRole + 1, base64);
+                    }
                     gitem = rec;
                 } else if (items.item(i).attributes().namedItem("type").nodeValue() == "QGraphicsSvgItem") {
                     QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue();
-                    QGraphicsSvgItem *rec = new QGraphicsSvgItem(url);
-                    m_scene->addItem(rec);
-                    rec->setData(Qt::UserRole, url);
-                    gitem = rec;
+                    QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue();
+                    QGraphicsSvgItem *rec = NULL;
+                    if (base64.isEmpty()) {
+                        rec = new QGraphicsSvgItem(url);
+                    } else {
+                        rec = new QGraphicsSvgItem();
+                        QSvgRenderer *renderer = new QSvgRenderer(QByteArray::fromBase64(base64.toAscii()), rec);
+                        rec->setSharedRenderer(renderer);
+                        //QString elem=rec->elementId();
+                        //QRectF bounds = renderer->boundsOnElement(elem);
+                    }
+                    if (rec) {
+                        m_scene->addItem(rec);
+                        rec->setData(Qt::UserRole, url);
+                        if (!base64.isEmpty()) {
+                            rec->setData(Qt::UserRole + 1, base64);
+                        }
+                        gitem = rec;
+                    }
                 }
             }
             //pos and transform
@@ -312,18 +472,42 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, Q
                 QPointF p(items.item(i).namedItem("position").attributes().namedItem("x").nodeValue().toDouble(),
                           items.item(i).namedItem("position").attributes().namedItem("y").nodeValue().toDouble());
                 gitem->setPos(p);
-                gitem->setTransform(stringToTransform(items.item(i).namedItem("position").firstChild().firstChild().nodeValue()));
+                QDomElement trans = items.item(i).namedItem("position").firstChild().toElement();
+                gitem->setTransform(stringToTransform(trans.firstChild().nodeValue()));
+                QString rotate = trans.attribute("rotation");
+                if (!rotate.isEmpty()) gitem->setData(ROTATEFACTOR, stringToList(rotate));
+                QString zoom = trans.attribute("zoom");
+                if (!zoom.isEmpty()) gitem->setData(ZOOMFACTOR, zoom.toInt());
                 int zValue = items.item(i).attributes().namedItem("z-index").nodeValue().toInt();
                 if (zValue > maxZValue) maxZValue = zValue;
                 gitem->setZValue(zValue);
                 gitem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
+
+#if QT_VERSION >= 0x040600
+                // effects
+                QDomNode eff = items.item(i).namedItem("effect");
+                if (!eff.isNull()) {
+                    QDomElement e = eff.toElement();
+                    if (e.attribute("type") == "blur") {
+                        QGraphicsBlurEffect *blur = new QGraphicsBlurEffect();
+                        blur->setBlurRadius(e.attribute("blurradius").toInt());
+                        gitem->setGraphicsEffect(blur);
+                    } else if (e.attribute("type") == "shadow") {
+                        QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
+                        shadow->setBlurRadius(e.attribute("blurradius").toInt());
+                        shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt());
+                        gitem->setGraphicsEffect(shadow);
+                    }
+                }
+#endif
             }
+
             if (items.item(i).nodeName() == "background") {
                 kDebug() << items.item(i).attributes().namedItem("color").nodeValue();
                 QColor color = QColor(stringToColor(items.item(i).attributes().namedItem("color").nodeValue()));
                 //color.setAlpha(items.item(i).attributes().namedItem("alpha").nodeValue().toInt());
                 QList<QGraphicsItem *> items = m_scene->items();
-                for (int i = 0; i < items.size(); i++) {
+                for (int i = 0; i < items.size(); ++i) {
                     if (items.at(i)->zValue() == -1100) {
                         ((QGraphicsRectItem *)items.at(i))->setBrush(QBrush(color));
                         break;
@@ -331,23 +515,14 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsPolygonItem* startv, Q
                 }
             } else if (items.item(i).nodeName() == "startviewport" && startv) {
                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
-                startv->setPolygon(stringToRect(rect));
-                int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
-                int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
-                int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
-                startv->setData(0, x);
-                startv->setData(1, y);
-                startv->setData(2, size);
-                //startv->setPos(p);
+                QRectF r = stringToRect(rect);
+                startv->setRect(0, 0, r.width(), r.height());
+                startv->setPos(r.topLeft());
             } else if (items.item(i).nodeName() == "endviewport" && endv) {
                 QString rect = items.item(i).attributes().namedItem("rect").nodeValue();
-                endv->setPolygon(stringToRect(rect));
-                int x = items.item(i).attributes().namedItem("x").nodeValue().toInt();
-                int y = items.item(i).attributes().namedItem("y").nodeValue().toInt();
-                int size = items.item(i).attributes().namedItem("size").nodeValue().toInt();
-                endv->setData(0, x);
-                endv->setData(1, y);
-                endv->setData(2, size);
+                QRectF r = stringToRect(rect);
+                endv->setRect(0, 0, r.width(), r.height());
+                endv->setPos(r.topLeft());
             }
         }
     }
@@ -384,9 +559,10 @@ QColor TitleDocument::stringToColor(const QString & s)
         return QColor();
     return QColor(l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt(), l.at(3).toInt());;
 }
+
 QTransform TitleDocument::stringToTransform(const QString& s)
 {
-    QStringList l = s.split(',');
+    QStringList l = s.split(QLatin1Char(','));
     if (l.size() < 9)
         return QTransform();
     return QTransform(
@@ -395,3 +571,23 @@ QTransform TitleDocument::stringToTransform(const QString& s)
                l.at(6).toDouble(), l.at(7).toDouble(), l.at(8).toDouble()
            );
 }
+
+QList<QVariant> TitleDocument::stringToList(const QString & s)
+{
+    QStringList l = s.split(',');
+    if (l.size() < 3)
+        return QList<QVariant>();
+    return QList<QVariant>() << QVariant(l.at(0).toDouble()) << QVariant(l.at(1).toDouble()) << QVariant(l.at(2).toDouble());
+}
+
+int TitleDocument::frameWidth() const
+{
+    return m_width;
+}
+
+int TitleDocument::frameHeight() const
+{
+    return m_height;
+}
+
+