File indexing completed on 2024-12-15 04:00:59
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "document_opener.hpp" 0008 0009 #include <QMessageBox> 0010 0011 #include "io/glaxnimate/glaxnimate_format.hpp" 0012 #include "utils/gzip.hpp" 0013 #include "model/assets/assets.hpp" 0014 #include "model/shapes/image.hpp" 0015 0016 #include "widgets/dialogs/trace_dialog.hpp" 0017 0018 #include "android_file_picker.hpp" 0019 #include "base_dialog.hpp" 0020 0021 class glaxnimate::android::DocumentOpener::Private 0022 { 0023 public: 0024 QWidget* widget_parent; 0025 0026 bool do_save_document(model::Composition *comp, const io::Options& opts, QIODevice& file) 0027 { 0028 return opts.format->save(file, opts.filename, comp, opts.settings); 0029 } 0030 0031 std::unique_ptr<model::Document> from_raster(const QByteArray& data) 0032 { 0033 auto btn = QMessageBox::information( 0034 widget_parent, 0035 i18n("Open File"), 0036 i18n("Raster images need to be traced into vectors"), 0037 QMessageBox::Ok|QMessageBox::Cancel 0038 ); 0039 if ( btn == QMessageBox::Cancel ) 0040 return {}; 0041 0042 auto doc = std::make_unique<model::Document>(""); 0043 auto main = doc->assets()->add_comp_no_undo(); 0044 auto asset = doc->assets()->add_image(QImage::fromData(data)); 0045 auto imageu = std::make_unique<model::Image>(doc.get()); 0046 auto image = imageu.get(); 0047 main->shapes.insert(std::move(imageu)); 0048 main->width.set(asset->width.get()); 0049 main->height.set(asset->height.get()); 0050 QPointF pos(asset->width.get()/2., asset->height.get()/2.); 0051 image->transform->anchor_point.set(pos); 0052 image->transform->position.set(pos); 0053 image->image.set(asset); 0054 0055 gui::TraceDialog dialog(image, widget_parent); 0056 DialogFixerFilter fixer(&dialog); 0057 if ( !dialog.exec() ) 0058 return {}; 0059 0060 doc->assets()->images->values.remove(0); 0061 main->shapes.remove(main->shapes.index_of(image)); 0062 0063 return doc; 0064 } 0065 0066 }; 0067 0068 glaxnimate::android::DocumentOpener::DocumentOpener(QWidget *widget_parent) 0069 : d(std::make_unique<Private>()) 0070 { 0071 d->widget_parent = widget_parent; 0072 } 0073 0074 glaxnimate::android::DocumentOpener::~DocumentOpener() 0075 { 0076 0077 } 0078 0079 bool glaxnimate::android::DocumentOpener::save(const QUrl &url, model::Composition* composition, io::Options &options) const 0080 { 0081 if ( !url.isValid() ) 0082 return false; 0083 0084 QString path = url.path(); 0085 QFileInfo finfo(path); 0086 0087 if ( !options.format ) 0088 options.format = io::glaxnimate::GlaxnimateFormat::instance(); 0089 0090 if ( url.isLocalFile() ) 0091 { 0092 options.filename = path; 0093 options.path = finfo.absoluteDir(); 0094 0095 QFile file(options.filename); 0096 return d->do_save_document(composition, options, file); 0097 } 0098 else 0099 { 0100 options.filename = url.toString(); 0101 QByteArray data; 0102 QBuffer buf(&data); 0103 bool ok = d->do_save_document(composition, options, buf); 0104 if ( !ok || !AndroidFilePicker::write_content_uri(url, data) ) 0105 { 0106 QMessageBox::warning(d->widget_parent, i18n("Save File"), i18n("Could not save the file")); 0107 return false; 0108 } 0109 return true; 0110 } 0111 } 0112 0113 std::unique_ptr<glaxnimate::model::Document> glaxnimate::android::DocumentOpener::open(const QUrl &url) const 0114 { 0115 if ( !url.isValid() ) 0116 return {}; 0117 0118 QString path = url.path(); 0119 QFileInfo finfo(path); 0120 QString extension = finfo.suffix(); 0121 io::Options options; 0122 options.format = io::IoRegistry::instance().from_extension(extension, io::ImportExport::Import); 0123 0124 if ( url.isLocalFile() ) 0125 { 0126 if ( !options.format ) 0127 { 0128 QMessageBox::warning(d->widget_parent, i18n("Open File"), i18n("Unknown file type")); 0129 return {}; 0130 } 0131 options.filename = path; 0132 options.path = finfo.absoluteDir(); 0133 QFile file(options.filename); 0134 0135 auto current_document = std::make_unique<model::Document>(options.filename); 0136 if ( !options.format->open(file, options.filename, current_document.get(), options.settings) ) 0137 return {}; 0138 0139 current_document->set_io_options(options); 0140 return current_document; 0141 } 0142 0143 options.filename = url.toString(); 0144 QByteArray data = AndroidFilePicker::read_content_uri(url); 0145 bool zipped = false; 0146 if ( !options.format ) 0147 { 0148 zipped = utils::gzip::is_compressed(data); 0149 if ( zipped ) 0150 { 0151 QByteArray out; 0152 if ( !utils::gzip::decompress(data, out, {}) ) 0153 { 0154 QMessageBox::warning(d->widget_parent, i18n("Open File"), i18n("Could not unzip the file")); 0155 return {}; 0156 } 0157 data = std::move(out); 0158 } 0159 0160 // json 0161 if ( data.startsWith('{') ) 0162 { 0163 if ( data.contains("\"__type__\"") ) 0164 options.format = io::glaxnimate::GlaxnimateFormat::instance(); 0165 else 0166 options.format = io::IoRegistry::instance().from_slug("lottie"); 0167 } 0168 else if ( data.contains("<svg") || data.contains("http://www.w3.org/2000/svg") ) 0169 { 0170 options.format = io::IoRegistry::instance().from_slug("svg"); 0171 options.settings["compressed"] = zipped; 0172 } 0173 else if ( data.startsWith("\x89PNG") ) 0174 { 0175 return d->from_raster(data); 0176 } 0177 } 0178 0179 if ( !options.format ) 0180 { 0181 0182 QString supported_formats; 0183 for ( const auto& fmt : io::IoRegistry::instance().importers() ) 0184 { 0185 if ( fmt->slug() == "raster" ) 0186 continue; 0187 if ( !supported_formats.isEmpty() ) 0188 supported_formats += ",\n"; 0189 supported_formats += fmt->name(); 0190 } 0191 supported_formats += ",\nPNG"; 0192 0193 QMessageBox::warning(d->widget_parent, i18n("Open File"), i18n("Unknown file type. Supported files:\n%1", supported_formats)); 0194 return {}; 0195 } 0196 0197 QBuffer file(&data); 0198 auto current_document = std::make_unique<model::Document>(options.filename); 0199 if ( !options.format->open(file, options.filename, current_document.get(), options.settings) ) 0200 { 0201 QMessageBox::warning(d->widget_parent, i18n("Open File"), i18n("Error loading %1 file", options.format->slug())); 0202 return {}; 0203 } 0204 0205 if ( zipped && options.format->slug() == "lottie" ) 0206 options.format = io::IoRegistry::instance().from_slug("tgs"); 0207 0208 current_document->set_io_options(options); 0209 return current_document; 0210 } 0211 0212 std::unique_ptr<glaxnimate::model::Document> glaxnimate::android::DocumentOpener::from_raster(const QByteArray &data) 0213 { 0214 return d->from_raster(data); 0215 }