]> git.sesse.net Git - kdenlive/blobdiff - src/titledocument.cpp
Integrate with the required MLT hooks for getting Movit to work.
[kdenlive] / src / titledocument.cpp
index ed9bc0b2815d9f6ce85e485a0d7d6464272ea913..f76707bf5e13e3afaa843c5fb78b7a8ebc8918cb 100644 (file)
@@ -21,7 +21,8 @@
 #include <KTemporaryFile>
 #include <kio/netaccess.h>
 #include <KApplication>
-#include <KLocale>
+#include <KStandardDirs>
+#include <KLocalizedString>
 #include <KMessageBox>
 
 #include <QGraphicsScene>
 #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)
@@ -52,16 +71,66 @@ void TitleDocument::setScene(QGraphicsScene* _scene, int width, int height)
     m_height = height;
 }
 
-QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* 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;
@@ -71,10 +140,12 @@ QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* en
         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");
@@ -102,11 +173,16 @@ QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* en
                 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()) content.setAttribute("font-outline-color", colorToString(QColor(t->data(102).toString())));
+                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(";"));
             }
 
@@ -155,7 +231,8 @@ QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* en
             if (blur) {
                 effect.setAttribute("type", "blur");
                 effect.setAttribute("blurradius", blur->blurRadius());
-            } else {
+            } /*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");
@@ -163,7 +240,7 @@ QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* en
                     effect.setAttribute("xoffset", shadow->xOffset());
                     effect.setAttribute("yoffset", shadow->yOffset());
                 }
-            }
+            }*/
             e.appendChild(effect);
         }
 #endif
@@ -193,12 +270,12 @@ QDomDocument TitleDocument::xml(QGraphicsRectItem* startv, QGraphicsRectItem* en
 
 /** \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;
@@ -209,20 +286,23 @@ QColor TitleDocument::getBackgroundColor()
 }
 
 
-bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int 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();
@@ -232,8 +312,9 @@ bool TitleDocument::saveDocument(const KUrl& url, QGraphicsRectItem* startv, QGr
     return KIO::NetAccess::upload(tmpfile.fileName(), url, 0);
 }
 
-int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGraphicsRectItem* endv, int *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")) {
@@ -245,18 +326,30 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGra
             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").toInt();
+    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 = 125;
+        *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();
@@ -333,20 +426,45 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGra
                     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
@@ -389,7 +507,7 @@ int TitleDocument::loadFromXml(QDomDocument doc, QGraphicsRectItem* startv, QGra
                 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;
@@ -444,7 +562,7 @@ QColor TitleDocument::stringToColor(const QString & s)
 
 QTransform TitleDocument::stringToTransform(const QString& s)
 {
-    QStringList l = s.split(',');
+    QStringList l = s.split(QLatin1Char(','));
     if (l.size() < 9)
         return QTransform();
     return QTransform(