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 }