diff --git a/plugins/impex/CMakeLists.txt b/plugins/impex/CMakeLists.txt --- a/plugins/impex/CMakeLists.txt +++ b/plugins/impex/CMakeLists.txt @@ -14,6 +14,7 @@ if(PNG_FOUND) add_subdirectory(png) + add_subdirectory(csv) endif() if(OPENEXR_FOUND) diff --git a/plugins/impex/csv/CMakeLists.txt b/plugins/impex/csv/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/CMakeLists.txt @@ -0,0 +1,34 @@ +add_subdirectory(tests) + +include_directories( ${CMAKE_SOURCE_DIR}/krita/image/metadata ) + +# import + +set(kritacsvimport_SOURCES + kis_csv_import.cpp + csv_loader.cpp + csv_read_line.cpp + csv_layer_record.cpp +) + +add_library(kritacsvimport MODULE ${kritacsvimport_SOURCES}) + +target_link_libraries(kritacsvimport kritaui ) + +install(TARGETS kritacsvimport DESTINATION ${CALLIGRA_PLUGIN_INSTALL_DIR}) + +# export + +set(kritacsvexport_SOURCES + kis_csv_export.cpp + csv_saver.cpp + csv_layer_record.cpp +) + +add_library(kritacsvexport MODULE ${kritacsvexport_SOURCES}) + +target_link_libraries(kritacsvexport kritaui ) + +install(TARGETS kritacsvexport DESTINATION ${CALLIGRA_PLUGIN_INSTALL_DIR}) + +install( PROGRAMS krita_csv.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/impex/csv/csv_layer_record.h b/plugins/impex/csv/csv_layer_record.h new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_layer_record.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef CSV_LAYER_RECORD_H_ +#define CSV_LAYER_RECORD_H_ + +#include + +#include "kis_raster_keyframe_channel.h" + +class CSVLayerRecord +{ +public: + CSVLayerRecord(); + virtual ~CSVLayerRecord(); + + QString name; + QString blending; + float density; + int visible; + + KisPaintLayer* layer; + KisRasterKeyframeChannel *channel; + QString last; + int frame; +}; + +#endif diff --git a/plugins/impex/csv/csv_layer_record.cpp b/plugins/impex/csv/csv_layer_record.cpp new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_layer_record.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "csv_layer_record.h" + +CSVLayerRecord::CSVLayerRecord() + : channel(NULL) + , layer(NULL) + , last("") + , frame(0) +{ +} + +CSVLayerRecord::~CSVLayerRecord() +{ +} diff --git a/plugins/impex/csv/csv_loader.h b/plugins/impex/csv/csv_loader.h new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_loader.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef CSV_LOADER_H_ +#define CSV_LOADER_H_ + +#include +#include + +#include "kis_image.h" +#include "kritaui_export.h" +class KisDocument; + +#include "csv_layer_record.h" + +/** + * Image import/export plugins can use these results to report about success or failure. + */ +enum KisImageBuilder_Result { + KisImageBuilder_RESULT_FAILURE = -400, + KisImageBuilder_RESULT_NOT_EXIST = -300, + KisImageBuilder_RESULT_NOT_LOCAL = -200, + KisImageBuilder_RESULT_BAD_FETCH = -100, + KisImageBuilder_RESULT_INVALID_ARG = -50, + KisImageBuilder_RESULT_OK = 0, + KisImageBuilder_RESULT_PROGRESS = 1, + KisImageBuilder_RESULT_EMPTY = 100, + KisImageBuilder_RESULT_BUSY = 150, + KisImageBuilder_RESULT_NO_URI = 200, + KisImageBuilder_RESULT_UNSUPPORTED = 300, + KisImageBuilder_RESULT_INTR = 400, + KisImageBuilder_RESULT_PATH = 500, + KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE = 600 + }; + +class CSVLoader : public QObject { + + Q_OBJECT + +public: + CSVLoader(KisDocument* doc); + virtual ~CSVLoader(); + + KisImageBuilder_Result buildAnimation(const QUrl &, const QString &); + + KisImageWSP image(); + +private: + KisImageBuilder_Result decode(const QUrl &, const QString &); + KisImageBuilder_Result setLayer(CSVLayerRecord* , KisDocument* ,const QString &); + KisImageBuilder_Result createNewImage(int, int, float, const QString &); + QString convertBlending(const QString &); + +private: + KisImageWSP m_image; + KisDocument* m_doc; + bool m_stop; +}; + +#endif diff --git a/plugins/impex/csv/csv_loader.cpp b/plugins/impex/csv/csv_loader.cpp new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_loader.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "csv_loader.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "csv_read_line.h" +#include "csv_layer_record.h" + +CSVLoader::CSVLoader(KisDocument *doc) + : m_image(0) + , m_doc(doc) + , m_stop(false) +{ +} + +CSVLoader::~CSVLoader() +{ +} + +KisImageBuilder_Result CSVLoader::decode(const QUrl &uri, const QString &filename) +{ + QString field; + int idx; + int frame = 0; + + QString projName; + int width = 0; + int height = 0; + int frameCount = 1; + float framerate = 24.0; + float pixelRatio = 1.0; + + int projNameIdx = -1; + int widthIdx = -1; + int heightIdx = -1; + int frameCountIdx = -1; + int framerateIdx = -1; + int pixelRatioIdx = -1; + + QVector layers; + + // open the csv file + QFile f(uri.toLocalFile()); + if (!f.exists()) + return KisImageBuilder_RESULT_NOT_EXIST; + + if (!f.open(QIODevice::ReadOnly)) + return KisImageBuilder_RESULT_NOT_EXIST; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + QString path = filename; + + if (path.right(4).toUpper() == ".CSV") + path = path.left(path.size() - 4); + + //according to the QT docs, the slash is a universal directory separator + path.append(".frames/"); + + KisImageBuilder_Result retval = KisImageBuilder_RESULT_OK; + + dbgFile << "pos:" << f.pos(); + + CSVReadLine readLine; + QScopedPointer importDoc(KisPart::instance()->createDocument()); + importDoc->setAutoSave(0); + importDoc->setSaveInBatchMode(true); + + QProgressDialog progress(i18n("Importing CSV..."), i18n("Cancel"), + 0, 0, KisPart::instance()->currentMainwindow(), 0); + + int step = 0; + + do { + qApp->processEvents(); + + if (progress.wasCanceled()) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + + if ((idx = readLine.nextLine(&f)) <= 0) { + if ((idx < 0) ||(step < 5)) + retval = KisImageBuilder_RESULT_FAILURE; + + break; + } + field = readLine.nextField(); //first field of the line + + if (field.isNull()) continue; //empty row + + switch (step) { + + case 0 : //skip first row + step = 1; + break; + + case 1 : //scene header names + step = 2; + + for (idx = 0; !field.isNull(); idx++) { + if (field == "Project Name") { + projNameIdx = idx; + + } else if (field == "Width") { + widthIdx = idx; + + } else if (field == "Height") { + heightIdx = idx; + + } else if (field == "Frame Count") { + frameCountIdx = idx; + + } else if (field == "Frame Rate") { + framerateIdx = idx; + + } else if (field == "Pixel Aspect Ratio") { + pixelRatioIdx = idx; + } + field= readLine.nextField(); + } + break; + + case 2 : //scene header values + step= 3; + + for (idx= 0; !field.isNull(); idx++) { + if (idx == projNameIdx) { + projName = field; + + } else if (idx == widthIdx) { + width = field.toInt(); + + } else if (idx == heightIdx) { + height = field.toInt(); + + } else if (idx == frameCountIdx) { + frameCount = field.toInt(); + + if (frameCount < 1) frameCount= 1; + + } else if (idx == framerateIdx) { + framerate = field.toFloat(); + + } else if (idx == pixelRatioIdx) { + pixelRatio = field.toFloat(); + + } + field= readLine.nextField(); + } + + if ((width < 1) || (height < 1)) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + progress.setWindowModality(Qt::ApplicationModal); + progress.setMinimumDuration(1000); + progress.setMaximum(frameCount); + progress.setValue(0); + + retval = createNewImage(width, height, pixelRatio, projName.isNull() ? filename : projName); + break; + + case 3 : //create level headers + if (field[0] != '#') break; + + for (; !(field = readLine.nextField()).isNull(); ) { + CSVLayerRecord* layerRecord = new CSVLayerRecord(); + layers.append(layerRecord); + } + readLine.rewind(); + field = readLine.nextField(); + step = 4; + //no break! + + case 4 : //level header + + if (field == "#Layers") { + //layer name + for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) + layers.at(idx)->name = field; + + break; + } + if (field == "#Density") { + //layer opacity + for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) + layers.at(idx)->density = field.toFloat(); + + break; + } + if (field == "#Blending") { + //layer blending mode + for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) + layers.at(idx)->blending = field; + + break; + } + if (field == "#Visible") { + //layer visibility + for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) + layers.at(idx)->visible = field.toInt(); + + break; + } + if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break; + + step = 5; + + //no break! + + case 5 : //frames + progress.setValue(frame); + + if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break; + + for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) { + CSVLayerRecord* layer = layers.at(idx); + + if (layer->last != field) { + retval = setLayer(layer, importDoc.data(), path); + layer->last = field; + layer->frame = frame; + } + } + frame++; + break; + } + } while (retval == KisImageBuilder_RESULT_OK); + + //finish the layers + + if (retval == KisImageBuilder_RESULT_OK) { + if (m_image) { + KisImageAnimationInterface *animation = m_image->animationInterface(); + + if (frame > frameCount) + frameCount = frame; + + animation->setFullClipRange(KisTimeRange::fromTime(0,frameCount - 1)); + animation->setFramerate((int)framerate); + } + + for (idx = 0; idx < layers.size(); idx++) { + CSVLayerRecord* layer = layers.at(idx); + //empty layers without any pictures are dropped + + if ((layer->frame > 0) || !layer->last.isEmpty()) { + retval = setLayer(layer, importDoc.data(), path); + + if (retval != KisImageBuilder_RESULT_OK) + break; + } + } + } + + if (m_image) { + //insert the existing layers by the right order + for (idx = layers.size() - 1; idx >= 0; idx--) { + CSVLayerRecord* layer = layers.at(idx); + + if (layer->layer) { + m_image->addNode(layer->layer, m_image->root()); + } + } + m_image->unlock(); + } + qDeleteAll(layers); + f.close(); + QApplication::restoreOverrideCursor(); + return retval; +} + +QString CSVLoader::convertBlending(const QString &blending) +{ + if (blending == "Color") return COMPOSITE_OVER; + if (blending == "Behind") return COMPOSITE_BEHIND; + if (blending == "Erase") return COMPOSITE_ERASE; + // "Shade" + if (blending == "Light") return COMPOSITE_LINEAR_LIGHT; + if (blending == "Colorize") return COMPOSITE_COLORIZE; + if (blending == "Hue") return COMPOSITE_HUE; + if (blending == "Add") return COMPOSITE_ADD; + if (blending == "Sub") return COMPOSITE_INVERSE_SUBTRACT; + if (blending == "Multiply") return COMPOSITE_MULT; + if (blending == "Screen") return COMPOSITE_SCREEN; + // "Replace" + // "Subtitute" + if (blending == "Difference") return COMPOSITE_DIFF; + if (blending == "Divide") return COMPOSITE_DIVIDE; + if (blending == "Overlay") return COMPOSITE_OVERLAY; + if (blending == "Light2") return COMPOSITE_DODGE; + if (blending == "Shade2") return COMPOSITE_BURN; + if (blending == "HardLight") return COMPOSITE_HARD_LIGHT; + if (blending == "SoftLight") return COMPOSITE_SOFT_LIGHT_PHOTOSHOP; + if (blending == "GrainExtract") return COMPOSITE_GRAIN_EXTRACT; + if (blending == "GrainMerge") return COMPOSITE_GRAIN_MERGE; + if (blending == "Sub2") return COMPOSITE_SUBTRACT; + if (blending == "Darken") return COMPOSITE_DARKEN; + if (blending == "Lighten") return COMPOSITE_LIGHTEN; + if (blending == "Saturation") return COMPOSITE_SATURATION; + + return COMPOSITE_OVER; +} + +KisImageBuilder_Result CSVLoader::setLayer(CSVLayerRecord* layer, KisDocument *importDoc, const QString &path) +{ + bool result = true; + + if (layer->channel == NULL) { + //create a new document layer + + float opacity = layer->density; + + if (opacity > 1.0) + opacity = 1.0; + else if (opacity < 0.0) + opacity = 0.0; + + const KoColorSpace* cs = m_image->colorSpace(); + const QString layerName = (layer->name).isEmpty() ? m_image->nextLayerName() : layer->name; + + KisPaintLayer* paintLayer = new KisPaintLayer(m_image, layerName, + (quint8)(opacity * OPACITY_OPAQUE_U8), cs); + + paintLayer->setCompositeOpId(convertBlending(layer->blending)); + paintLayer->setVisible(layer->visible); + paintLayer->enableAnimation(); + + layer->layer = paintLayer; + layer->channel = qobject_cast + (paintLayer->getKeyframeChannel(KisKeyframeChannel::Content.id())); + } + layer->channel->addKeyframe(layer->frame); + + if (!layer->last.isEmpty()) { + //png image + QString filename = path; + filename.append(layer->last); + + result = importDoc->openUrl(QUrl::fromLocalFile(filename), + KisDocument::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES); + if (result) + layer->channel->importFrame(layer->frame, importDoc->image()->projection(), NULL); + } + return (result) ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE; +} + +KisImageBuilder_Result CSVLoader::createNewImage(int width, int height, float ratio, const QString &name) +{ + //the CSV is RGBA 8bits, sRGB + + if (!m_image) { + const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace( + RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), 0); + + if (cs) m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, name); + + if (!m_image) return KisImageBuilder_RESULT_FAILURE; + + m_image->setResolution(ratio, 1.0); + m_image->lock(); + } + return KisImageBuilder_RESULT_OK; +} + +KisImageBuilder_Result CSVLoader::buildAnimation(const QUrl &uri,const QString &filename) +{ + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!uri.isLocalFile()) + return KisImageBuilder_RESULT_NOT_EXIST; + + return decode(uri, filename); +} + +KisImageWSP CSVLoader::image() +{ + return m_image; +} diff --git a/plugins/impex/csv/csv_read_line.h b/plugins/impex/csv/csv_read_line.h new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_read_line.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef CSV_READ_LINE_H_ +#define CSV_READ_LINE_H_ + +#include +#include +#include + +#include "csv_layer_record.h" + +/* The .png file names are within 20 characters, only the layer + * names can be longer. If the length exceeds this, it will be + * truncated. + */ +#define CSV_FIELD_MAX 50 + +class CSVReadLine +{ + +public: + CSVReadLine(); + ~CSVReadLine(); + + int nextLine(QIODevice* io); + QString nextField(); + void rewind(); + +private: + + QString splitNext(int* pos); + bool setLayer(CSVLayerRecord* ); + bool readFrame(int* pos); + bool finalize(); + int readImpl(QIODevice* io); + + char m_separator; + int m_row; + QByteArray m_linebuf; + int m_pos; +}; + +#endif diff --git a/plugins/impex/csv/csv_read_line.cpp b/plugins/impex/csv/csv_read_line.cpp new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_read_line.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "csv_read_line.h" + +#include + +#include +#include + +#include +#include +#include + +CSVReadLine::CSVReadLine() + : m_separator(0) + , m_row(0) + , m_linebuf(0) + , m_pos(-1) +{ +} + +CSVReadLine::~CSVReadLine() +{ +} + +// returns: 0 finished, + continue, - error +int CSVReadLine::nextLine(QIODevice *io) +{ + int retval= 0; + m_pos= -1; + + try { + m_linebuf= io->readLine(); + + if (!m_linebuf.size()) + retval= 0; //finished + else { + if (!m_separator) + m_separator= ((m_linebuf.size() > 5) && (m_linebuf[5] == ';')) ? + ';' : ','; + m_pos= 0; + retval= 1; + } + } catch(KisAslReaderUtils::ASLParseException &e) { + warnKrita << "WARNING: CSV:" << e.what(); + retval= -1; //error + } + return retval; +} + +QString CSVReadLine::nextField() +{ + char strBuf[CSV_FIELD_MAX]; + char *ptr; + char c; + int i,p,max; + + p= m_pos; + + if (p < 0) return QString(); + + ptr= strBuf; + max= m_linebuf.size(); + + do { if (p >= max) { + ptr[0]= 0; + m_pos= -1; + return QString(strBuf); + } + c= m_linebuf[p++]; + } while((c == ' ') || (c == '\t')); + + i= 0; + + if (c == '\"') { + //quoted + while(p < max) { + c= m_linebuf[p++]; + + if (c == '\"') { + + if (p >= max) break; + + if (m_linebuf[p] != c) break; + + //double quote escape sequence + ++p; + } + if (i < (CSV_FIELD_MAX - 1)) + ptr[i++]= c; + } + + while (p < max) { + c= m_linebuf[p++]; + if (c == m_separator) break; + } + } else { + //without quotes + while (c != m_separator) { + if (i < (CSV_FIELD_MAX - 1)) + ptr[i++]= c; + + if (p >= max) break; + + c= m_linebuf[p++]; + } + + while(i > 0) { + c= ptr[--i]; + if ((c != ' ') && (c != '\t') && + (c != '\r') && (c != '\n')) { + ++i; + break; + } + } + } + ptr[i]= 0; + m_pos= (p < max) ? p : -1; + return QString(strBuf); +} + +void CSVReadLine::rewind() +{ + m_pos= 0; +} diff --git a/plugins/impex/csv/csv_saver.h b/plugins/impex/csv/csv_saver.h new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_saver.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef CSV_SAVER_H_ +#define CSV_SAVER_H_ + +#include + +#include "kis_types.h" +#include "kis_raster_keyframe_channel.h" +#include "kis_png_converter.h" + +/* The KisImageBuilder_Result definitions come from kis_png_converter.h here */ + +#include "csv_layer_record.h" +class KisDocument; + +class CSVSaver : public QObject { + + Q_OBJECT + +public: + CSVSaver(KisDocument* doc); + virtual ~CSVSaver(); + + KisImageBuilder_Result buildAnimation(const QUrl &, const QString &); + KisImageWSP image(); + +private: + KisImageBuilder_Result encode(const QUrl &, const QString &); + KisImageBuilder_Result getLayer(CSVLayerRecord* , KisDocument* , KisKeyframeSP, const QString &, int, int); + void createTempImage(KisDocument* ); + QString convertToBlending(const QString &); + +private: + KisImageWSP m_image; + KisDocument* m_doc; + bool m_stop; +}; + +#endif diff --git a/plugins/impex/csv/csv_saver.cpp b/plugins/impex/csv/csv_saver.cpp new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/csv_saver.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "csv_saver.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "csv_layer_record.h" + +CSVSaver::CSVSaver(KisDocument *doc) + : m_image(doc->image()) + , m_doc(doc) + , m_stop(false) +{ +} + +CSVSaver::~CSVSaver() +{ +} + +KisImageWSP CSVSaver::image() +{ + return m_image; +} + +KisImageBuilder_Result CSVSaver::encode(const QUrl &uri,const QString &filename) +{ + int idx; + int start, end; + KisNodeSP node; + QByteArray ba; + KisKeyframeSP keyframe; + QVector layers; + + KisImageAnimationInterface *animation = m_image->animationInterface(); + + //open the csv file for writing + QFile f(uri.toLocalFile()); + if (!f.open(QIODevice::WriteOnly)) { + return KisImageBuilder_RESULT_NOT_LOCAL; + } + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + //DataStream instead of TextStream for correct line endings + QDataStream stream(&f); + + QString path = filename; + + if (path.right(4).toUpper() == ".CSV") + path = path.left(path.size() - 4); + + path.append(".frames"); + + //create directory + + QDir dir(path); + if (!dir.exists()) { + dir.mkpath("."); + } + //according to the QT docs, the slash is a universal directory separator + path.append("/"); + + m_image->lock(); + node = m_image->rootLayer()->firstChild(); + + //TODO: correct handling of the layer tree. + //for now, only top level paint layers are saved + + idx = 0; + + while (node) { + if (node->inherits("KisPaintLayer")) { + KisPaintLayer* paintLayer = dynamic_cast(node.data()); + CSVLayerRecord* layerRecord = new CSVLayerRecord(); + layers.prepend(layerRecord); //reverse order! + + layerRecord->name = paintLayer->name(); + layerRecord->name.replace(QRegExp("[\"\\r\\n]"), "_"); + + if (layerRecord->name.isEmpty()) + layerRecord->name= QString("Unnamed-%1").arg(idx); + + layerRecord->visible = (paintLayer->visible()) ? 1 : 0; + layerRecord->density = (float)(paintLayer->opacity()) / OPACITY_OPAQUE_U8; + layerRecord->blending = convertToBlending(paintLayer->compositeOpId()); + layerRecord->layer = paintLayer; + layerRecord->channel = paintLayer->projection()->keyframeChannel(); + layerRecord->last = ""; + layerRecord->frame = 0; + idx++; + } + node = node->nextSibling(); + } + + KisTimeRange range = animation->fullClipRange(); + + start = (range.isValid()) ? range.start() : 0; + + if (!range.isInfinite()) { + end = range.end(); + + if (end < start) end = start; + } else { + //undefined length, searching for the last keyframe + end = start; + + for (idx = 0; idx < layers.size(); idx++) { + keyframe = layers.at(idx)->channel->lastKeyframe(); + + if ( (!keyframe.isNull()) && (keyframe->time() > end) ) + end = keyframe->time(); + } + } + + //create temporary doc for exporting + QScopedPointer exportDoc(KisPart::instance()->createDocument()); + createTempImage(exportDoc.data()); + + KisImageBuilder_Result retval= KisImageBuilder_RESULT_OK; + + QProgressDialog progress(i18n("Exporting CSV..."), i18n("Cancel"), + start, end, KisPart::instance()->currentMainwindow(), 0); + + progress.setWindowModality(Qt::ApplicationModal); + progress.setMinimumDuration(1000); + progress.setValue(start); + + int frame = start; + int step = 0; + + do { + qApp->processEvents(); + + if (progress.wasCanceled()) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + + switch(step) { + + case 0 : //first row + if (f.write("UTF-8, TVPaint, \"CSV 1.0\"\r\n") < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + } + break; + + case 1 : //scene header names + if (f.write("Project Name, Width, Height, Frame Count, Layer Count, Frame Rate, Pixel Aspect Ratio, Field Mode\r\n") < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + } + break; + + case 2 : //scene header values + ba = QString("\"%1\", ").arg(m_image->objectName()).toUtf8(); + if (f.write(ba.data()) < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + ba = QString("%1, %2, ").arg(m_image->width()).arg(m_image->height()).toUtf8(); + if (f.write(ba.data()) < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + + ba = QString("%1, %2, ").arg(end - start + 1).arg(layers.size()).toUtf8(); + if (f.write(ba.data()) < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + //the framerate is an integer here + ba = QString("%1, ").arg((double)(animation->framerate()),0,'f',6).toUtf8(); + if (f.write(ba.data()) < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + ba = QString("%1, Progressive\r\n").arg((double)(m_image->xRes() / m_image->yRes()),0,'f',6).toUtf8(); + if (f.write(ba.data()) < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + break; + + case 3 : //layer header values + if (f.write("#Layers") < 0) { //Layers + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + + for (idx = 0; idx < layers.size(); idx++) { + ba = QString(", \"%1\"").arg(layers.at(idx)->name).toUtf8(); + if (f.write(ba.data()) < 0) + break; + } + break; + + case 4 : + if (f.write("\r\n#Density") < 0) { //Density + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + for (idx = 0; idx < layers.size(); idx++) { + ba = QString(", %1").arg((double)(layers.at(idx)->density), 0, 'f', 6).toUtf8(); + if (f.write(ba.data()) < 0) + break; + } + break; + + case 5 : + if (f.write("\r\n#Blending") < 0) { //Blending + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + for (idx = 0; idx < layers.size(); idx++) { + ba = QString(", \"%1\"").arg(layers.at(idx)->blending).toUtf8(); + if (f.write(ba.data()) < 0) + break; + } + break; + + case 6 : + if (f.write("\r\n#Visible") < 0) { //Visible + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + for (idx = 0; idx < layers.size(); idx++) { + ba = QString(", %1").arg(layers.at(idx)->visible).toUtf8(); + if (f.write(ba.data()) < 0) + break; + } + if (idx < layers.size()) { + retval = KisImageBuilder_RESULT_FAILURE; + } + break; + + default : //frames + progress.setValue(frame); + + if (frame > end) { + if (f.write("\r\n") < 0) + retval = KisImageBuilder_RESULT_FAILURE; + + step = 8; + break; + } + ba = QString("\r\n#%1").arg(frame, 5, 10, QChar('0')).toUtf8(); + if (f.write(ba.data()) < 0) { + retval = KisImageBuilder_RESULT_FAILURE; + break; + } + + for (idx = 0; idx < layers.size(); idx++) { + CSVLayerRecord *layer = layers.at(idx); + keyframe = layer->channel->keyframeAt(frame); + + if (!keyframe.isNull()) { + retval = getLayer(layer, exportDoc.data(), keyframe, path, frame, idx); + + if (retval != KisImageBuilder_RESULT_OK) + break; + } + ba = QString(", \"%1\"").arg(layer->last).toUtf8(); + + if (f.write(ba.data()) < 0) + break; + } + if (idx < layers.size()) + retval = KisImageBuilder_RESULT_FAILURE; + + frame++; + step = 6; //keep step here + break; + } + step++; + } while((retval == KisImageBuilder_RESULT_OK) && (step < 8)); + + m_image->unlock(); + + qDeleteAll(layers); + f.close(); + QApplication::restoreOverrideCursor(); + return retval; +} + +QString CSVSaver::convertToBlending(const QString &opid) +{ + if (opid == COMPOSITE_OVER) return "Color"; + if (opid == COMPOSITE_BEHIND) return "Behind"; + if (opid == COMPOSITE_ERASE) return "Erase"; + // "Shade" + if (opid == COMPOSITE_LINEAR_LIGHT) return "Light"; + if (opid == COMPOSITE_COLORIZE) return "Colorize"; + if (opid == COMPOSITE_HUE) return "Hue"; + if ((opid == COMPOSITE_ADD) || + (opid == COMPOSITE_LINEAR_DODGE)) return "Add"; + if (opid == COMPOSITE_INVERSE_SUBTRACT) return "Sub"; + if (opid == COMPOSITE_MULT) return "Multiply"; + if (opid == COMPOSITE_SCREEN) return "Screen"; + // "Replace" + // "Subtitute" + if (opid == COMPOSITE_DIFF) return "Difference"; + if (opid == COMPOSITE_DIVIDE) return "Divide"; + if (opid == COMPOSITE_OVERLAY) return "Overlay"; + if (opid == COMPOSITE_DODGE) return "Light2"; + if (opid == COMPOSITE_BURN) return "Shade2"; + if (opid == COMPOSITE_HARD_LIGHT) return "HardLight"; + if ((opid == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) || + (opid == COMPOSITE_SOFT_LIGHT_SVG)) return "SoftLight"; + if (opid == COMPOSITE_GRAIN_EXTRACT) return "GrainExtract"; + if (opid == COMPOSITE_GRAIN_MERGE) return "GrainMerge"; + if (opid == COMPOSITE_SUBTRACT) return "Sub2"; + if (opid == COMPOSITE_DARKEN) return "Darken"; + if (opid == COMPOSITE_LIGHTEN) return "Lighten"; + if (opid == COMPOSITE_SATURATION) return "Saturation"; + + return "Color"; +} + +KisImageBuilder_Result CSVSaver::getLayer(CSVLayerRecord* layer, KisDocument* exportDoc, KisKeyframeSP keyframe, const QString &path, int frame, int idx) +{ + //render to the temp layer + KisImageWSP image = exportDoc->image(); + KisPaintDeviceSP device = image->rootLayer()->firstChild()->projection(); + + layer->channel->fetchFrame(keyframe, device); + QRect bounds = device->exactBounds(); + + if (bounds.isEmpty()) { + layer->last = ""; //empty frame + return KisImageBuilder_RESULT_OK; + } + layer->last = QString("frame%1-%2.png").arg(idx + 1,5,10,QChar('0')).arg(frame,5,10,QChar('0')); + + QString filename = path; + filename.append(layer->last); + + //save to PNG + + KisSequentialConstIterator it(device, image->bounds()); + const KoColorSpace* cs = device->colorSpace(); + + bool isThereAlpha = false; + do { + if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) { + isThereAlpha = true; + break; + } + } while (it.nextPixel()); + + if (!KisPNGConverter::isColorSpaceSupported(cs)) { + device = new KisPaintDevice(*device.data()); + KUndo2Command *cmd= device->convertTo(KoColorSpaceRegistry::instance()->rgb8()); + delete cmd; + } + KisPNGOptions options; + + options.alpha = isThereAlpha; + options.interlace = false; + options.compression = 8; + options.tryToSaveAsIndexed = false; + options.transparencyFillColor = QColor(0,0,0); + options.saveSRGBProfile = true; //TVPaint can use only sRGB + options.forceSRGB = false; + + KisPNGConverter kpc(exportDoc); + + return kpc.buildFile( QUrl::fromLocalFile(filename), image->bounds(), + image->xRes(), image->yRes(), device, + image->beginAnnotations(), image->endAnnotations(), + options, (KisMetaData::Store* )0 ); +} + +void CSVSaver::createTempImage(KisDocument* exportDoc) +{ + exportDoc->setAutoSave(0); + exportDoc->setOutputMimeType("image/png"); + exportDoc->setSaveInBatchMode(true); + + KisImageWSP exportImage = new KisImage(exportDoc->createUndoStore(), + m_image->width(), m_image->height(), m_image->colorSpace(), + QString()); + + exportImage->setResolution(m_image->xRes(), m_image->yRes()); + exportDoc->setCurrentImage(exportImage); + + KisPaintLayer* paintLayer = new KisPaintLayer(exportImage, "paint device", OPACITY_OPAQUE_U8); + exportImage->addNode(paintLayer, exportImage->rootLayer(), KisLayerSP(0)); +} + + +KisImageBuilder_Result CSVSaver::buildAnimation(const QUrl &uri,const QString &filename) +{ + if (!m_image) + return KisImageBuilder_RESULT_EMPTY; + + if (uri.isEmpty()) + return KisImageBuilder_RESULT_NO_URI; + + if (!uri.isLocalFile()) + return KisImageBuilder_RESULT_NOT_EXIST; + + return encode(uri, filename); +} diff --git a/plugins/impex/csv/kis_csv_export.h b/plugins/impex/csv/kis_csv_export.h new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/kis_csv_export.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_CSV_EXPORT_H_ +#define _KIS_CSV_EXPORT_H_ + +#include + +#include + +class KisCSVExport : public KisImportExportFilter +{ + Q_OBJECT +public: + KisCSVExport(QObject *parent, const QVariantList &); + virtual ~KisCSVExport(); +public: + virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to); +}; + +#endif diff --git a/plugins/impex/csv/kis_csv_export.cpp b/plugins/impex/csv/kis_csv_export.cpp new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/kis_csv_export.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_csv_export.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "csv_saver.h" + +K_PLUGIN_FACTORY_WITH_JSON(KisCSVExportFactory, "krita_csv_export.json", registerPlugin();) + +bool checkHomogenity(KisNodeSP root) +{ + bool res = true; + KisNodeSP child = root->firstChild(); + + while (child) { + if (child->childCount() > 0) { + res= false; + break; + } + child = child->nextSibling(); + } + return res; +} + +KisCSVExport::KisCSVExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) +{ +} + +KisCSVExport::~KisCSVExport() +{ +} + +KisImportExportFilter::ConversionStatus KisCSVExport::convert(const QByteArray& from, const QByteArray& to) +{ + dbgFile << "CSV export! From:" << from << ", To:" << to << ""; + + if (from != "application/x-krita") + return KisImportExportFilter::NotImplemented; + + KisDocument* input = m_chain->inputDocument(); + QString filename = m_chain->outputFile(); + + if (!input) + return KisImportExportFilter::NoDocumentCreated; + + if (!checkHomogenity(input->image()->rootLayer())) { + if (!m_chain->manager()->getBatchMode()) { + QMessageBox::critical(0, + i18nc("@title:window", "CSV Export Error"), + i18n("Unable to save to the CSV format.\n" + "The CSV format not supports layer groups or masked layers.")); + } + return KisImportExportFilter::InvalidFormat; + } + qApp->processEvents(); // For vector layers to be updated + input->image()->waitForDone(); + + if (filename.isEmpty()) return KisImportExportFilter::FileNotFound; + + QUrl url = QUrl::fromLocalFile(filename); + + CSVSaver kpc(input); + KisImageBuilder_Result res; + + if ((res = kpc.buildAnimation(url, filename)) == KisImageBuilder_RESULT_OK) { + dbgFile <<"success !"; + return KisImportExportFilter::OK; + } + dbgFile <<" Result =" << res; + return KisImportExportFilter::InternalError; +} + +#include "kis_csv_export.moc" diff --git a/plugins/impex/csv/kis_csv_import.h b/plugins/impex/csv/kis_csv_import.h new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/kis_csv_import.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef _KIS_CSV_IMPORT_H_ +#define _KIS_CSV_IMPORT_H_ + +#include + +#include + +class KisCSVImport : public KisImportExportFilter +{ + Q_OBJECT +public: + KisCSVImport(QObject *parent, const QVariantList &); + virtual ~KisCSVImport(); +public: + virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to); +}; + +#endif diff --git a/plugins/impex/csv/kis_csv_import.cpp b/plugins/impex/csv/kis_csv_import.cpp new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/kis_csv_import.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kis_csv_import.h" + +#include + +#include +#include + +#include +#include + +#include +#include + +#include "csv_loader.h" + +K_PLUGIN_FACTORY_WITH_JSON(CSVImportFactory, "krita_csv_import.json", registerPlugin();) + +KisCSVImport::KisCSVImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) +{ +} + +KisCSVImport::~KisCSVImport() +{ +} + +KisImportExportFilter::ConversionStatus KisCSVImport::convert(const QByteArray&, const QByteArray& to) +{ + dbgFile << "Importing using CSVImport!"; + + if (to != "application/x-krita") + return KisImportExportFilter::BadMimeType; + + KisDocument * doc = m_chain->outputDocument(); + + if (!doc) + return KisImportExportFilter::NoDocumentCreated; + + QString filename = m_chain->inputFile(); + + doc -> prepareForImport(); + + if (!filename.isEmpty()) { + + QUrl url = QUrl::fromLocalFile(filename); + + if (url.isEmpty()) + return KisImportExportFilter::FileNotFound; + + CSVLoader ib(doc); + + KisImageBuilder_Result result = ib.buildAnimation(url,filename); + + switch (result) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KisImportExportFilter::NotImplemented; + case KisImageBuilder_RESULT_INVALID_ARG: + return KisImportExportFilter::BadMimeType; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_EXIST: + case KisImageBuilder_RESULT_NOT_LOCAL: + qDebug() << "ib returned KisImageBuilder_RESULT_NOT_LOCAL"; + return KisImportExportFilter::FileNotFound; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KisImportExportFilter::ParsingError; + case KisImageBuilder_RESULT_FAILURE: + return KisImportExportFilter::InternalError; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KisImportExportFilter::OK; + default: + return KisImportExportFilter::StorageCreationError; + } + } + return KisImportExportFilter::StorageCreationError; +} + +#include + diff --git a/plugins/impex/csv/krita_csv.desktop b/plugins/impex/csv/krita_csv.desktop new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/krita_csv.desktop @@ -0,0 +1,71 @@ +[Desktop Entry] +Name=Krita +Name[af]=Krita +Name[bg]=Krita +Name[br]=Krita +Name[bs]=Krita +Name[ca]=Krita +Name[ca@valencia]=Krita +Name[cs]=Krita +Name[cy]=Krita +Name[da]=Krita +Name[de]=Krita +Name[el]=Krita +Name[en_GB]=Krita +Name[eo]=Krita +Name[es]=Krita +Name[et]=Krita +Name[eu]=Krita +Name[fi]=Krita +Name[fr]=Krita +Name[fy]=Krita +Name[ga]=Krita +Name[gl]=Krita +Name[he]=Krita +Name[hi]=केरिता +Name[hne]=केरिता +Name[hr]=Krita +Name[hu]=Krita +Name[ia]=Krita +Name[is]=Krita +Name[it]=Krita +Name[kk]=Krita +Name[ko]=Krita +Name[lt]=Krita +Name[lv]=Krita +Name[mr]=क्रिटा +Name[ms]=Krita +Name[nb]=Krita +Name[nds]=Krita +Name[ne]=क्रिता +Name[nl]=Krita +Name[pl]=Krita +Name[pt]=Krita +Name[pt_BR]=Krita +Name[ro]=Krita +Name[ru]=Krita +Name[se]=Krita +Name[sk]=Krita +Name[sl]=Krita +Name[sv]=Krita +Name[ta]=கிரிட்டா +Name[tg]=Krita +Name[tr]=Krita +Name[ug]=Krita +Name[uk]=Krita +Name[uz]=Krita +Name[uz@cyrillic]=Krita +Name[wa]=Krita +Name[xh]=Krita +Name[x-test]=xxKritaxx +Name[zh_CN]=Krita +Name[zh_TW]=繪圖_Krita +Exec=krita %u +MimeType=text/csv; +Type=Application +Icon=calligrakrita +Categories=Qt;KDE;Office;Graphics; +StartupNotify=true +X-KDE-ServiceTypes=Calligra/Application +X-Calligra-DefaultMimeTypes=text/csv +NoDisplay=true diff --git a/plugins/impex/csv/krita_csv_export.json b/plugins/impex/csv/krita_csv_export.json new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/krita_csv_export.json @@ -0,0 +1,13 @@ +{ + "Id": "Krita CSV Export Filter", + "NoDisplay": "true", + "Type": "Service", + "X-KDE-Export": "text/csv", + "X-KDE-Import": "application/x-krita", + "X-KDE-Library": "kritacsvexport", + "X-KDE-ServiceTypes": [ + "Krita/FileFilter" + ], + "X-KDE-Weight": "1", + "X-KDE-Extensions" : "csv" +} diff --git a/plugins/impex/csv/krita_csv_import.json b/plugins/impex/csv/krita_csv_import.json new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/krita_csv_import.json @@ -0,0 +1,13 @@ +{ + "Id": "Krita CSV Import Filter", + "NoDisplay": "true", + "Type": "Service", + "X-KDE-Export": "application/x-krita", + "X-KDE-Import": "text/csv", + "X-KDE-Library": "kritacsvimport", + "X-KDE-ServiceTypes": [ + "Krita/FileFilter" + ], + "X-KDE-Weight": "1", + "X-KDE-Extensions" : "csv" +} diff --git a/plugins/impex/csv/tests/CMakeLists.txt b/plugins/impex/csv/tests/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) +include_directories( ${CMAKE_SOURCE_DIR}/sdk/tests ) + +macro_add_unittest_definitions() + +########### next target ############### +set(kis_csv_test_SRCS kis_csv_test.cpp ) + +kde4_add_unit_test(kis_csv_test TESTNAME krita-plugins-formats-csv_test ${kis_csv_test_SRCS}) + +target_link_libraries(kis_csv_test kritaui Qt5::Test) diff --git a/plugins/impex/csv/tests/kis_csv_test.h b/plugins/impex/csv/tests/kis_csv_test.h new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/tests/kis_csv_test.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_CSV_TEST_H_ +#define _KIS_CSV_TEST_H_ + +#include + +class KisCsvTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testFiles(); +}; + +#endif diff --git a/plugins/impex/csv/tests/kis_csv_test.cpp b/plugins/impex/csv/tests/kis_csv_test.cpp new file mode 100644 --- /dev/null +++ b/plugins/impex/csv/tests/kis_csv_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Laszlo Fazekas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_csv_test.h" + + +#include +#include + +#include + +#include "filestest.h" + +#ifndef FILES_DATA_DIR +#error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita" +#endif + + +void KisCsvTest::testFiles() +{ + TestUtil::testFiles(QString(FILES_DATA_DIR) + "/sources", QStringList()); +} +QTEST_MAIN(KisCsvTest) +