diff --git a/data/en.mse-locale/locale b/data/en.mse-locale/locale index dbe12600..e98ccb86 100644 --- a/data/en.mse-locale/locale +++ b/data/en.mse-locale/locale @@ -803,6 +803,17 @@ error: To resolve this, add: depends on: %s %s + # Image import + import not found: File not found: '%s' + can't import image without set: Must first save or load a set file before importing file: '%s' + can't create file stream: Failed to create file stream: '%s' + can't write image to set: Failed to write image to set: '%s' + can't import image: Failed to import image: '%s' + + # Card creation + no field with name: Card doesn't have a field named '%s' + can't set value: Can not set card value '%s', it is not of the right type + # Script stuff has no member: %s has no member '%s' can't convert: Can't convert from %s to %s diff --git a/doc/function/import_image.txt b/doc/function/import_image.txt new file mode 100644 index 00000000..874fab5a --- /dev/null +++ b/doc/function/import_image.txt @@ -0,0 +1,13 @@ +Function: import_image + +--Usage-- +> import_image(image_path) + +Load an image from outside the data folder. Intended for use from the CLI. + +--Parameters-- +! Parameter Type Description +| @input@ [[type:string]] Full path of the image to load + +--Examples-- +> new_card([image: import_image("D:/Art/Ajani.png"), card_color: "green"]) diff --git a/doc/function/index.txt b/doc/function/index.txt index 22ebd80a..b1549d2d 100644 --- a/doc/function/index.txt +++ b/doc/function/index.txt @@ -94,6 +94,7 @@ These functions are built into the program, other [[type:function]]s can be defi | [[fun:rotate_image]] Rotate an image. | [[fun:drop_shadow]] Add a drop shadow to an image. | [[fun:symbol_variation]] Render a variation of a [[type:symbol]]. +| [[fun:import_image]] Load an image from outside the data folder. | [[fun:built_in_image]] Return an image built into the program. ! Cards <<< diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp index 2c1f32c4..720019b7 100644 --- a/src/gfx/generated_image.cpp +++ b/src/gfx/generated_image.cpp @@ -14,6 +14,7 @@ #include #include #include // load_resource_image +#include // ----------------------------------------------------------------------------- : GeneratedImage @@ -528,3 +529,48 @@ bool ImageValueToImage::operator == (const GeneratedImage& that) const { return that2 && filename == that2->filename && age == that2->age; } + +// ----------------------------------------------------------------------------- : ExternalImage + +Image ExternalImage::generate(const Options& opt) const { + wxFileName fname(filepath, wxPATH_UNIX); + String filePathString = fname.GetAbsolutePath(); + + // has a pre-existing .mse-set file been loaded? + if (opt.local_package->needSaveAs()) throw ScriptError(_ERROR_1_("can't import image without set", filePathString)); + + // does the file pointed to by filepath exist? + if (!fname.FileExists()) throw ScriptError(_ERROR_1_("import not found", filePathString)); + + String fileExt = fname.GetExt(); + wxBitmapType bitmapType; + if (fileExt == _("png")) bitmapType = wxBITMAP_TYPE_PNG; + else if (fileExt == _("jpg") || fileExt == _("jpeg")) bitmapType = wxBITMAP_TYPE_JPEG; + else bitmapType = wxBITMAP_TYPE_BMP; + + // does the file exist in the package? + String fileNameNoExtension = fname.GetName(); + if (!opt.local_package->existsIn(fileNameNoExtension)) { + auto outStream = opt.local_package->openOut(fileNameNoExtension); + wxFileInputStream inStream = wxFileInputStream(filepath.ToStdString()); + if (!inStream.IsOk()) throw ScriptError(_ERROR_1_("can't create file stream", filePathString)); + outStream->Write(inStream); + if (!outStream->IsOk()) throw ScriptError(_ERROR_1_("can't write image to set", filePathString)); + outStream->Close(); + } + + // save the package with the new image + opt.local_package->save(false); + + auto imageInputStream = opt.local_package->openIn(fileNameNoExtension); + Image img(*imageInputStream.get(), bitmapType); + + if (!img.IsOk()) throw ScriptError(_ERROR_1_("can't import image", filePathString)); + + return img; +} + +bool ExternalImage::operator == (const GeneratedImage& that) const { + const ExternalImage* that2 = dynamic_cast(&that); + return that2 && that2->filepath == filepath; +} diff --git a/src/gfx/generated_image.hpp b/src/gfx/generated_image.hpp index ef53dff7..380dccab 100644 --- a/src/gfx/generated_image.hpp +++ b/src/gfx/generated_image.hpp @@ -413,4 +413,17 @@ private: LocalFileName filename; Age age; ///< Age the image was last updated }; - + +// ----------------------------------------------------------------------------- : ExternalImage + +/// Load an image from the filesystem +class ExternalImage : public GeneratedImage { +public: + ExternalImage(const String& filepath) : filepath(filepath) {}; + Image generate(const Options&) const override; + bool operator == (const GeneratedImage& that) const override; + inline String toString() { return filepath; } + inline String toCode() const override { return _(""); } +private: + String filepath; +}; diff --git a/src/script/functions/construction.cpp b/src/script/functions/construction.cpp index d876d579..e96cd034 100644 --- a/src/script/functions/construction.cpp +++ b/src/script/functions/construction.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,7 @@ SCRIPT_FUNCTION(new_card) { // find value to update IndexMap::const_iterator value_it = new_card->data.find(name); if (value_it == new_card->data.end()) { - throw ScriptError(format_string(_("Card doesn't have a field named '%s'"),name)); + throw ScriptError(_ERROR_1_("no field with name", name)); } Value* value = value_it->get(); // set the value @@ -45,8 +46,11 @@ SCRIPT_FUNCTION(new_card) { pvalue->package_name = v->toString(); } else if (ColorValue* cvalue = dynamic_cast(value)) { cvalue->value = v->toColor(); + } else if (ImageValue* ivalue = dynamic_cast(value)) { + wxFileName fname( static_cast(v.get())->toString() ); + ivalue->filename = LocalFileName::fromReadString( fname.GetName(), ""); } else { - throw ScriptError(format_string(_("Can not set value '%s', it is not of the right type"),name)); + throw ScriptError(_ERROR_1_("can't set value", name)); } } SCRIPT_RETURN(new_card); diff --git a/src/script/functions/image.cpp b/src/script/functions/image.cpp index 787484ac..dfa16cfd 100644 --- a/src/script/functions/image.cpp +++ b/src/script/functions/image.cpp @@ -19,6 +19,7 @@ #include #include #include +#include // for MSE_CLI void parse_enum(const String&, ImageCombine& out); @@ -43,6 +44,15 @@ SCRIPT_FUNCTION(to_card_image) { return make_intrusive(export_bitmap(set, input, (zoom / 100), deg_to_rad(angle)).ConvertToImage()); } } + +SCRIPT_FUNCTION(import_image) { + SCRIPT_PARAM(Set*, set); + SCRIPT_PARAM(String, input); + auto extImg = make_intrusive(input); + if (cli.haveConsole()) // makes sure generate() is called, but only once, when using the CLI + extImg->generate(GeneratedImage::Options(0, 0, set->stylesheet.get(), set)); + return extImg; +} // ----------------------------------------------------------------------------- : Image functions @@ -269,4 +279,5 @@ void init_script_image_functions(Context& ctx) { ctx.setVariable(_("drop_shadow"), script_drop_shadow); ctx.setVariable(_("symbol_variation"), script_symbol_variation); ctx.setVariable(_("built_in_image"), script_built_in_image); + ctx.setVariable(_("import_image"), script_import_image); } diff --git a/tools/website/drupal/mse-drupal-modules/highlight.inc b/tools/website/drupal/mse-drupal-modules/highlight.inc index d19472e1..a481a99d 100644 --- a/tools/website/drupal/mse-drupal-modules/highlight.inc +++ b/tools/website/drupal/mse-drupal-modules/highlight.inc @@ -90,6 +90,7 @@ $built_in_functions = array( 'rotate' =>'', 'drop_shadow' =>'', 'symbol_variation' =>'', + 'import_image' =>'', 'built_in_image' =>'', // cards 'new_card' =>'',