#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)
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;
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");
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(";"));
}
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())
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");
effect.setAttribute("xoffset", shadow->xOffset());
effect.setAttribute("yoffset", shadow->yOffset());
}
- }
+ }*/
e.appendChild(effect);
}
#endif
/** \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;
}
-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();
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")) {
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();
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());
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
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);
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;
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(
);
}
+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;