add bleed edge option, get_card_export_settings function

This commit is contained in:
GenevensiS
2025-12-08 16:11:36 +01:00
parent a616dd007b
commit 2888dc4059
26 changed files with 663 additions and 475 deletions
+19 -16
View File
@@ -574,10 +574,10 @@ label:
dark mode yes: Dark mode
app language: Language of the user interface:
card display: Card Display
storage: Storage
zoom: &Zoom:
export: &Export:
scale: &Internal Scale:
import: Import
export: &Export
scale: Use the following scale:
use export scale: Use Export Scale
export around 300: Around 300 DPI
export force 300: Force 300 DPI
@@ -586,10 +586,11 @@ label:
external programs: External programs
apprentice: &Apprentice:
apprentice exe: Apprentice Executable
internal scale desc:
Scale to internally store card images at.
Changing this may impact how Sharpening looks.
Does not retroactively apply to existing images.
export desc: When exporting cards to images:
import desc: When importing images for illustrations:
internal scale desc:
Changing the scale does not retroactively apply
to existing images. You must re-import them.
check at startup: Check for new versions at startup
checking requires internet:
Checking for updates requires an internet connection.
@@ -741,21 +742,23 @@ button:
high quality: &High quality rendering
show lines: Show &lines around fields
show editing hints: Show boxes and hints for &editing
zoom export:
Use Viewer zoom and rotation
settings when e&xporting
rotation export:
Use Viewer rotation
setting when e&xporting
Use the card preview
rotation setting
bleed export:
Add a crude print
bleed margin
notes export:
Export card notes inside
image meta data
Add the card notes in
the image metadata
spellcheck enabled: Show &spelling errors on cards
check now: Check &Now
always: Always
if internet connection exists: If internet connection exists
never: Never
internal image extension: Store images internally with file extension
internal image extension:
Store images internally
with file extension
# column select
move up: Move &Up
@@ -824,7 +827,7 @@ title:
global: Global
display: Display
directories: Directories
internal: Internal
transfers: Transfers
updates: Updates
update check: Update Check
locate apprentice: Locate Apprentice
+13
View File
@@ -0,0 +1,13 @@
Function: get_card_export_settings
--Usage--
> get_card_export_settings(card)
Get the zoom, angle and bleed with which a card is to be exported, as specified by the user in the preferences menu.
Returns and array whose first element is the zoom percentage, the second is the angle in degrees, and the third is the bleed edge size in pixels.
--Parameters--
! Parameter Type Description
| @input@ [[type:card]] The card you want to retrieve the export settings from.
| @set@ [[type:set]] The set the card belongs to. This can be omited since "set" is a predefined variable.
+1
View File
@@ -108,6 +108,7 @@ These functions are built into the program, other [[type:function]]s can be defi
| [[fun:add_card_to_set]] Add a [[type:card]] to a [[type:set]].
| [[fun:get_card_styling]] Get the styling data of a [[type:card]].
| [[fun:get_card_stylesheet]] Get the stylesheet of a [[type:card]].
| [[fun:get_card_export_settings]] Get the zoom, angle and bleed size at which the card will be exported.
| [[fun:get_card_from_uid]] Find the [[type:card]] with the given uid.
| [[fun:get_cards_from_link]] Find all [[type:card]]s that have the given link type to the given [[type:card]].
| [[fun:get_front_face]] Find a [[type:card]] that has the link type "Front Face" to the given [[type:card]].
+9 -4
View File
@@ -1,9 +1,10 @@
Function: write_image_file
--Usage--
> write_image_file(some_image, file: filename)
> write_image_file(some_image, file: filename, width: 375, height: 523)
> write_image_file(some_card, file: filename, zoom: 1.0, angle: 0.0, bleed: 18.0)
Write an image to a file in the output directory.
Write a given image, or a given card's image to a file in the output directory.
If a file with the given name already exists it is overwritten.
Returns the name of the file written.
@@ -12,10 +13,14 @@ This function can only be used in an [[type:export template]], when <tt>create d
--Parameters--
! Parameter Type Description
| @input@ [[type:image]] Image to write to the file.
| @file@ [[type:string]] Name of the file to write to
| @input@ [[type:image]] or [[type:card]] Image or Card to write to the file.
| @file@ [[type:string]] Name of the file to write to.
| @width@ [[type:int]] Width in pixels to use for the image, by default the size of the image is used if available.
| @height@ [[type:int]] Height in pixels to use for the image, by default the size of the image is used if available.
| @zoom@ [[type:double]] Zoom percentage to apply to the card render.
| @angle@ [[type:double]] Angle in degrees to apply to the card render.
| @bleed@ [[type:double]] Print bleed margin in pixels to apply to the card render.
| @use_user_settings@ [[type:bool]] Use app settings for zoom, angle and bleed instead.
--Examples--
> write_image_file(file:"image_out.png", linear_blend(...)) == "image_out.png" # image_out.png now contains the given image
+23 -5
View File
@@ -126,11 +126,29 @@ public:
inline RealPoint getPos() const { return RealPoint(left, top); }
inline RealSize getSize() const { return RealSize(width, height); }
inline RealRect getExternalRect() const { return RealRect(left, top, width, height); }
inline String getExternalRectString(double scale = 1.0, int offset = 0) { ///< update the style before calling this
return _("---") + wxString::Format(wxT("%i"), (int)std::ceil(scale * left + offset)) +
_("-") + wxString::Format(wxT("%i"), (int)std::ceil(scale * top)) +
_("-") + wxString::Format(wxT("%i"), (int)std::floor(scale * width)) +
_("-") + wxString::Format(wxT("%i"), (int)std::floor(scale * height)) +
inline String getExternalRectString(double scale, Radians angle, double bleed, int img_width, int img_height, int img_offset) { ///< update the style before calling this
double x = left * scale, y = top * scale;
double w = width * scale, h = height * scale;
RealRect rect(x, y, w, h);
int degrees = 0;
if (is_rad0(angle)) {
} else if (is_rad180(angle)) {
rect = RealRect(img_width - x - w, img_height - y - h, w, h);
degrees = 180;
} else if (is_rad90(angle)) {
rect = RealRect(y, img_height - x - w, h, w);
degrees = 90;
} else if (is_rad270(angle)) {
rect = RealRect(img_width - y - h, x, h, w);
degrees = 270;
} else {
return _("");
}
return _("---") + wxString::Format(wxT("%i"), (int)std::ceil( rect.x + bleed + img_offset)) +
_("-") + wxString::Format(wxT("%i"), (int)std::ceil( rect.y + bleed)) +
_("-") + wxString::Format(wxT("%i"), (int)std::floor(rect.width)) +
_("-") + wxString::Format(wxT("%i"), (int)std::floor(rect.height)) +
_("-") + wxString::Format(wxT("%i"), degrees) +
_("---");
}
+1 -1
View File
@@ -151,7 +151,7 @@ CardsOnClipboard::CardsOnClipboard(const SetP& set, const String id, const vecto
img = export_image(set, cards[0]);
}
else {
img = export_image(set, cards, true, 0, 1.0, 0.0);
img = export_image(set, cards);
}
String temp_path = wxFileName::CreateTempFileName(_("mse")) + _(".png");
img.SaveFile(temp_path, wxBITMAP_TYPE_PNG);
+3 -7
View File
@@ -88,15 +88,11 @@ FileFormatP mtg_editor_file_format();
// ----------------------------------------------------------------------------- : Other ways to export
/// Generate a wxBitmap of one or more cards
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom = 1.0, const Radians angle_radians = 0.0);
Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians, vector<double>& scales_out, vector<int>& offsets_out);
/// Generate a wxImage of one or more cards
Image export_image(const SetP& set, const CardP& card, const double zoom = 1.0, const Radians angle_radians = 0.0);
Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi = false, int padding = 0, const double zoom = 1.0, const Radians angle_radians = 0.0);
Image export_image(const SetP& set, const CardP& card, const bool write_metadata = true, const double zoom = 1.0, const Radians angle_radians = 0.0, const double bleed_pixels = 0.0);
Image export_image(const SetP& set, const vector<CardP>& cards, const int padding = 2, const double global_zoom = 1.0, const bool use_zoom_setting = true, const bool use_rotation_setting = true, const bool use_bleed_setting = false);
/// Export the image of one or more cards to a given filename, using the app's zoom and rotation settings
/// Export the image of one or more cards to a given filename, using the app's zoom, rotation and bleed settings, and including metadata
void export_image(const SetP& set, const CardP& card, const String& filename);
void export_image(const SetP& set, const vector<CardP>& cards, const String& path, const String& filename_template, FilenameConflicts conflicts);
+238 -150
View File
@@ -20,157 +20,160 @@
#include <render/card/viewer.hpp>
#include <wx/filename.h>
// ----------------------------------------------------------------------------- : Card export
class UnzoomedDataViewer : public DataViewer {
class ZoomedUnrotatedDataViewer : public DataViewer {
public:
UnzoomedDataViewer();
UnzoomedDataViewer(double zoom, Radians angle);
virtual ~UnzoomedDataViewer() {};
Rotation getRotation() const override;
private:
ZoomedUnrotatedDataViewer(double zoom) : zoom(zoom) {};
virtual ~ZoomedUnrotatedDataViewer() {};
Rotation getRotation() const override;
private:
double zoom;
double angle;
bool declared_values;
};
UnzoomedDataViewer::UnzoomedDataViewer()
: zoom(1.0)
, angle(0.0)
, declared_values(false)
{}
UnzoomedDataViewer::UnzoomedDataViewer(const double zoom, const Radians angle = 0.0)
: zoom(zoom)
, angle(angle)
, declared_values(true)
{}
Rotation UnzoomedDataViewer::getRotation() const {
if (!stylesheet) stylesheet = set->stylesheet;
if (declared_values) {
return Rotation(angle, stylesheet->getCardRect(), zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
double export_zoom = settings.exportZoomSettingsFor(set->stylesheetFor(card));
bool use_viewer_rotation = !settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export();
if (use_viewer_rotation) {
return Rotation(DataViewer::getRotation().getAngle(), stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
} else {
return Rotation(angle, stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
}
// ----------------------------------------------------------------------------- : wxBitmap export
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, const Radians angle_radians) {
if (!set) throw Error(_("no set"));
UnzoomedDataViewer viewer = UnzoomedDataViewer(zoom, angle_radians);
viewer.setSet(set);
viewer.setCard(card);
// size of cards
RealSize size = viewer.getRotation().getExternalSize();
// create bitmap & dc
Bitmap bitmap((int)size.width, (int)size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
}
Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians, vector<double>& scales_out, vector<int>& offsets_out) {
if (!set) throw Error(_("no set"));
vector<Bitmap> bitmaps;
int width = 0;
int height = 0;
double lowest_dpi = 1200.0;
if (scale_to_lowest_dpi) {
FOR_EACH(card, cards) {
lowest_dpi = min(lowest_dpi, set->stylesheetFor(card).card_dpi);
}
lowest_dpi = max(lowest_dpi, 150.0);
}
// Draw card bitmaps
FOR_EACH(card, cards) {
double scale = zoom;
if (scale_to_lowest_dpi) {
double dpi = max(set->stylesheetFor(card).card_dpi, 150.0);
scale *= lowest_dpi / dpi;
}
scales_out.push_back(scale);
UnzoomedDataViewer viewer = UnzoomedDataViewer(scale, angle_radians);
viewer.setSet(set);
viewer.setCard(card);
RealSize size = viewer.getRotation().getExternalSize();
Bitmap bitmap((int)size.width, (int)size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC bufferDC;
bufferDC.SelectObject(bitmap);
clearDC(bufferDC, *wxWHITE_BRUSH);
viewer.draw(bufferDC);
bufferDC.SelectObject(wxNullBitmap);
width += (int)size.width;
height = max(height, (int)size.height);
bitmaps.push_back(bitmap);
}
// Draw global bitmap
Bitmap global_bitmap(width + (bitmaps.size()-1) * padding, height);
if (!global_bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC globalDC;
globalDC.SelectObject(global_bitmap);
clearDC(globalDC, *wxWHITE_BRUSH);
int offset = 0;
FOR_EACH(bitmap, bitmaps) {
offsets_out.push_back(offset);
globalDC.SetDeviceOrigin(offset, 0);
globalDC.DrawBitmap(bitmap, 0, 0);
offset += bitmap.GetWidth() + padding;
}
globalDC.SelectObject(wxNullBitmap);
return global_bitmap;
Rotation ZoomedUnrotatedDataViewer::getRotation() const {
return Rotation(0.0, stylesheet->getCardRect(), zoom);
}
// ----------------------------------------------------------------------------- : wxImage export
Image export_image(const SetP& set, const CardP& card, const double zoom, const Radians angle_radians) {
Bitmap bitmap = export_bitmap(set, card, zoom, angle_radians);
Image img = bitmap.ConvertToImage();
String data = _("<mse-data-start>[");
IndexMap<FieldP, ValueP>& card_data = card->data;
boost::json::object& cardv = mse_to_json(card, set.get());
boost::json::object& cardv_data = cardv["data"].as_object();
StyleSheetP stylesheet = set->stylesheetForP(card);
if (!settings.stylesheetSettingsFor(*stylesheet).card_notes_export()) cardv["notes"] = "";
for(IndexMap<FieldP, ValueP>::iterator it = card_data.begin() ; it != card_data.end() ; ++it) {
ImageValue* value = dynamic_cast<ImageValue*>(it->get());
if (value && !value->filename.empty()) {
FieldP field = (*it)->fieldP;
StyleP style = stylesheet->card_style.at(field->index);
if (style) {
style->update(set->getContext(card));
std::string rect = style->getExternalRectString(zoom, 0).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
Image export_image(const SetP& set, const CardP& card, const bool write_metadata, const double zoom, const Radians angle_radians, const double bleed_pixels) {
if (!set) throw Error(_("no set"));
/// create and zoom
ZoomedUnrotatedDataViewer viewer = ZoomedUnrotatedDataViewer(zoom);
viewer.setSet(set);
viewer.setCard(card);
RealSize size = viewer.getRotation().getExternalSize();
int bleed = lround(bleed_pixels);
Bitmap bitmap((int)size.width + 2 * bleed, (int)size.height + 2 * bleed);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
dc.SetDeviceOrigin(bleed, bleed);
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
Image img = bitmap.ConvertToImage();
/// rotate
img = rotate_image(img, angle_radians);
int width = img.GetWidth(), height = img.GetHeight();
/// add print bleed edge
if (size.width < 1.0 || size.height < 1.0) {
queue_message(MESSAGE_ERROR, _("Cannot add bleed edge to empty image"));
}
else {
if (!img.HasAlpha()) img.InitAlpha();
Byte* pixels = img.GetData();
Byte* alpha = img.GetAlpha();
// fill top left corner
int pixel;
int x_start = 0;
int y_start = 0;
int ref = bleed + bleed * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
}
data += json_ugly_print(cardv) + _("]<mse-data-end>");
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, data);
return img;
}
Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians) {
vector<double> scales;
vector<int> offsets;
Bitmap bitmap = export_bitmap(set, cards, scale_to_lowest_dpi, padding, zoom, angle_radians, scales, offsets);
Image img = bitmap.ConvertToImage();
String data = _("<mse-data-start>[");
for (int i = 0; i < cards.size(); ++i) {
if (i > 0) data += _(",");
CardP card = cards[i];
// fill top right corner
x_start = width - bleed;
y_start = 0;
ref = x_start - 1 + bleed * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom left corner
x_start = 0;
y_start = height - bleed;
ref = bleed + (y_start - 1) * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom right corner
x_start = width - bleed;
y_start = height - bleed;
ref = (x_start - 1) + (y_start - 1) * width;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill left border
x_start = 0;
y_start = bleed;
for (int y = 0; y < height - bleed - bleed; ++y) {
ref = bleed + (y_start + y) * width;
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill top border
x_start = bleed;
y_start = 0;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < width - bleed - bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + bleed * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill right border
x_start = width - bleed;
y_start = bleed;
for (int y = 0; y < height - bleed - bleed; ++y) {
ref = width - bleed - 1 + (y_start + y) * width;
for (int x = 0; x < bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom border
x_start = bleed;
y_start = height - bleed;
for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < width - bleed - bleed; ++x) {
pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + (height - bleed - 1) * width;
pixels[3 * pixel + 0] = pixels[3 * ref + 0];
pixels[3 * pixel + 1] = pixels[3 * ref + 1];
pixels[3 * pixel + 2] = pixels[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
}
/// add metadata
if (write_metadata) {
String metadata = _("<mse-card-data>[");
IndexMap<FieldP, ValueP>& card_data = card->data;
boost::json::object& cardv = mse_to_json(card, set.get());
boost::json::object& cardv_data = cardv["data"].as_object();
@@ -183,24 +186,109 @@ Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lo
StyleP style = stylesheet->card_style.at(field->index);
if (style) {
style->update(set->getContext(card));
std::string rect = style->getExternalRectString(scales[i], offsets[i]).ToStdString();
std::string rect = style->getExternalRectString(zoom, angle_radians, bleed_pixels, width, height, 0).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
}
}
}
data += json_ugly_print(cardv);
}
data += _("]<mse-data-end>");
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, data);
metadata += json_ugly_print(cardv) + _("]</mse-card-data>");
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata);
}
return img;
}
Image export_image( const SetP& set, const vector<CardP>& cards,
const int padding,
const double global_zoom,
const bool use_zoom_setting,
const bool use_rotation_setting,
const bool use_bleed_setting) {
if (!set) throw Error(_("no set"));
if (cards.size() == 0) throw Error(_("no cards"));
vector<Image> imgs;
vector<int> offsets;
vector<double> zooms;
vector<double> angles;
vector<double> bleeds;
// Draw card images
FOR_EACH(card, cards) {
Settings::ExportSettings card_settings = settings.exportSettingsFor(set->stylesheetFor(card));
double zoom = use_zoom_setting ? global_zoom * card_settings.zoom : global_zoom;
double angle = use_rotation_setting ? card_settings.angle_radians : 0.0;
double bleed = use_bleed_setting ? card_settings.bleed_pixels : 0.0;
imgs.push_back(export_image(set, card, false, zoom, angle, bleed));
zooms.push_back(zoom);
angles.push_back(angle);
bleeds.push_back(bleed);
}
int global_width = 0;
int global_height = 0;
vector<int> widths;
vector<int> heights;
FOR_EACH(img, imgs) {
int width = img.GetWidth();
int height = img.GetHeight();
widths.push_back(width);
heights.push_back(height);
offsets.push_back(global_width);
global_width += padding + width;
global_height = max(global_height, height);
}
global_width -= padding;
// Draw global image
Image global_img = Image(global_width, global_height);
if (!global_img.Ok()) throw InternalError(_("Unable to create image"));
global_img.InitAlpha();
Byte* pixels = global_img.GetData();
Byte* alpha = global_img.GetAlpha();
// fill with transparent
for (UInt i = 0; i < global_width*global_height; ++i) {
pixels[3 * i + 0] = 0;
pixels[3 * i + 1] = 0;
pixels[3 * i + 2] = 0;
alpha[i] = 0;
}
// Paste card images
FOR_EACH_2(img, imgs, offset, offsets) {
global_img.Paste(img, offset, 0);
}
// Write metadata
String metadata = _("<mse-card-data>[");
for (int i = 0; i < cards.size(); ++i) {
if (i > 0) metadata += _(",");
CardP card = cards[i];
IndexMap<FieldP, ValueP>& card_data = card->data;
boost::json::object& cardv = mse_to_json(card, set.get());
boost::json::object& cardv_data = cardv["data"].as_object();
StyleSheetP stylesheet = set->stylesheetForP(card);
if (!settings.stylesheetSettingsFor(*stylesheet).card_notes_export()) cardv["notes"] = "";
for(IndexMap<FieldP, ValueP>::iterator it = card_data.begin() ; it != card_data.end() ; ++it) {
ImageValue* value = dynamic_cast<ImageValue*>(it->get());
if (value && !value->filename.empty()) {
FieldP field = (*it)->fieldP;
StyleP style = stylesheet->card_style.at(field->index);
if (style) {
style->update(set->getContext(card));
Rotation rotation();
std::string rect = style->getExternalRectString(zooms[i], angles[i], bleeds[i], widths[i], heights[i], offsets[i]).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
}
}
}
metadata += json_ugly_print(cardv);
}
metadata += _("]</mse-card-data>");
global_img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata);
return global_img;
}
void export_image(const SetP& set, const CardP& card, const String& filename) {
const StyleSheet& stylesheet = set->stylesheetFor(card);
StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet);
double zoom = settings.exportZoomSettingsFor(stylesheet);
Radians angle = stylesheet_settings.card_normal_export() ? 0.0 : deg_to_rad(stylesheet_settings.card_angle());
Image img = export_image(set, card, zoom, angle);
StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet);
Settings::ExportSettings export_settings = settings.exportSettingsFor(stylesheet);
Image img = export_image(set, card, true, export_settings.zoom, export_settings.angle_radians, export_settings.bleed_pixels);
img.SaveFile(filename);
}
+75 -62
View File
@@ -32,8 +32,8 @@ IMPLEMENT_REFLECTION_ENUM(CheckUpdates) {
}
IMPLEMENT_REFLECTION_ENUM(InstallType) {
VALUE_N("default", INSTALL_DEFAULT); //default
VALUE_N("local", INSTALL_LOCAL);
VALUE_N("default", INSTALL_DEFAULT); //default
VALUE_N("local", INSTALL_LOCAL);
VALUE_N("global", INSTALL_GLOBAL);
}
@@ -47,13 +47,13 @@ bool is_install_local(InstallType type) {
}
IMPLEMENT_REFLECTION_ENUM(FilenameConflicts) {
VALUE_N("keep old", CONFLICT_KEEP_OLD);
VALUE_N("overwrite", CONFLICT_OVERWRITE);
VALUE_N("number", CONFLICT_NUMBER);
VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE);
VALUE_N("keep old", CONFLICT_KEEP_OLD);
VALUE_N("overwrite", CONFLICT_OVERWRITE);
VALUE_N("number", CONFLICT_NUMBER);
VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE);
}
const vector<int> Settings::export_zoom_choices = { 50,66,75,80,100,120,125,150,175,200 };
const vector<int> Settings::scale_choices = { 50,66,75,80,100,120,125,150,175,200 };
const int COLUMN_NOT_INITIALIZED = -100000;
@@ -71,13 +71,13 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(ColumnSettings) {
}
GameSettings::GameSettings()
: sort_cards_ascending(true)
, images_export_filename(_("{card.name}.png"))
, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace(true)
, pack_seed_random(true)
, pack_seed(123456)
, initialized(false)
: sort_cards_ascending (true)
, images_export_filename (_("{card.name}.png"))
, images_export_conflicts (CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace (true)
, pack_seed_random (true)
, pack_seed (123456)
, initialized (false)
{}
void GameSettings::initDefaults(const Game& game) {
@@ -128,37 +128,40 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(GameSettings) {
StyleSheetSettings::StyleSheetSettings()
: card_zoom (1.0, true)
, export_zoom_selection (0, true)
, card_angle (0, true)
, card_anti_alias (true, true)
, card_borders (true, true)
, card_draw_editing (true, true)
, card_normal_export (true, true)
, card_notes_export (false, true)
, card_spellcheck_enabled(true, true)
: card_zoom (1.0, true)
, export_scale_selection (0, true)
, card_angle (0, true)
, card_anti_alias (true, true)
, card_borders (true, true)
, card_draw_editing (true, true)
, card_normal_export (true, true)
, card_bleed_export (false, true)
, card_notes_export (false, true)
, card_spellcheck_enabled (true, true)
{}
void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
if (card_zoom .isDefault()) card_zoom .assignDefault(ss.card_zoom);
if (export_zoom_selection .isDefault()) export_zoom_selection .assignDefault(ss.export_zoom_selection);
if (export_scale_selection .isDefault()) export_scale_selection .assignDefault(ss.export_scale_selection);
if (card_angle .isDefault()) card_angle .assignDefault(ss.card_angle);
if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias);
if (card_borders .isDefault()) card_borders .assignDefault(ss.card_borders);
if (card_draw_editing .isDefault()) card_draw_editing .assignDefault(ss.card_draw_editing);
if (card_normal_export .isDefault()) card_normal_export .assignDefault(ss.card_normal_export);
if (card_bleed_export .isDefault()) card_bleed_export .assignDefault(ss.card_bleed_export);
if (card_notes_export .isDefault()) card_notes_export .assignDefault(ss.card_notes_export);
if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled);
}
IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) {
REFLECT(card_zoom);
REFLECT(export_zoom_selection);
REFLECT(export_scale_selection);
REFLECT(card_angle);
REFLECT(card_anti_alias);
REFLECT(card_borders);
REFLECT(card_draw_editing);
REFLECT(card_normal_export);
REFLECT(card_bleed_export);
REFLECT(card_notes_export);
REFLECT(card_spellcheck_enabled);
}
@@ -184,29 +187,29 @@ IMPLEMENT_REFLECTION_ENUM(DarkModeType) {
Settings settings;
Settings::Settings()
: locale (_("en"))
, set_window_maximized (false)
, set_window_width (790)
, set_window_height (300)
, card_notes_height (40)
, open_sets_in_new_window (true)
, symbol_grid_size (30)
, symbol_grid (true)
, symbol_grid_snap (false)
, print_spacing (0.33)
, print_cutter_lines (CUTTER_ALL)
, dark_mode_type (DARKMODE_SYSTEM)
, internal_scale_selection(0)
, internal_image_extension(true)
: locale (_("en"))
, set_window_maximized (false)
, set_window_width (790)
, set_window_height (300)
, card_notes_height (40)
, open_sets_in_new_window (true)
, symbol_grid_size (30)
, symbol_grid (true)
, symbol_grid_snap (false)
, print_spacing (0.33)
, print_cutter_lines (CUTTER_ALL)
, dark_mode_type (DARKMODE_SYSTEM)
, import_scale_selection (0)
, internal_image_extension (true)
#if USE_OLD_STYLE_UPDATE_CHECKER
, updates_url (_("https://magicseteditor.boards.net/page/downloads"))
, updates_url (_("https://magicseteditor.boards.net/page/downloads"))
#endif
, package_versions_url (_("https://magicseteditor.boards.net/page/downloads"))
, installer_list_url (_("https://magicseteditor.boards.net/page/downloads"))
, check_updates (CHECK_IF_CONNECTED)
, check_updates_all (true)
, website_url (_("https://magicseteditor.boards.net/"))
, install_type (INSTALL_DEFAULT)
, package_versions_url (_("https://magicseteditor.boards.net/page/downloads"))
, installer_list_url (_("https://magicseteditor.boards.net/page/downloads"))
, check_updates (CHECK_IF_CONNECTED)
, check_updates_all (true)
, website_url (_("https://magicseteditor.boards.net/"))
, install_type (INSTALL_DEFAULT)
{}
void Settings::addRecentFile(const String& filename) {
@@ -230,7 +233,8 @@ GameSettings& Settings::gameSettingsFor(const Game& game) {
if (!gs) gs = make_intrusive<GameSettings>();
gs->initDefaults(game);
return *gs;
}
}
ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) {
// Get game info
GameSettings& gs = gameSettingsFor(game);
@@ -243,7 +247,8 @@ ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field
cs.width = field.card_list_width;
}
return cs;
}
}
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
// Use the canonical form here since the stylesheet name will be used as a stored key.
// This does introduce the possibility of collision if two stylesheets return the same value canonically, but I think that's just a necessary risk.
@@ -253,28 +258,36 @@ StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet
return *ss;
}
double Settings::exportZoomSettingsFor(const StyleSheet& stylesheet) {
double Settings::exportScaleSettingsFor(const StyleSheet& stylesheet) {
StyleSheetSettings& ss = stylesheetSettingsFor(stylesheet);
int export_zoom = ss.export_zoom_selection();
if (export_zoom == 0) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0);
if (export_zoom == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0);
if (export_zoom == 2) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0);
return export_zoom_choices[export_zoom - 3] / 100;
int export_scale = ss.export_scale_selection();
if (export_scale == 0) return adaptiveScaleSettingsFor(stylesheet, 300.0, 50.0);
if (export_scale == 1) return adaptiveScaleSettingsFor(stylesheet, 300.0, 1.0);
if (export_scale == 2) return adaptiveScaleSettingsFor(stylesheet, 150.0, 1.0);
return scale_choices[export_scale - 3] / 100;
}
double Settings::internalScaleSettingsFor(const StyleSheet& stylesheet) {
if (internal_scale_selection == 0) return exportZoomSettingsFor(stylesheet);
if (internal_scale_selection == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0);
if (internal_scale_selection == 2) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0);
if (internal_scale_selection == 3) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0);
return export_zoom_choices[internal_scale_selection - 4] / 100;
double Settings::importScaleSettingsFor(const StyleSheet& stylesheet) {
if (import_scale_selection == 0) return exportScaleSettingsFor(stylesheet);
if (import_scale_selection == 1) return adaptiveScaleSettingsFor(stylesheet, 300.0, 50.0);
if (import_scale_selection == 2) return adaptiveScaleSettingsFor(stylesheet, 300.0, 1.0);
if (import_scale_selection == 3) return adaptiveScaleSettingsFor(stylesheet, 150.0, 1.0);
return scale_choices[import_scale_selection - 4] / 100;
}
double Settings::adaptiveZoomSettingsFor(const StyleSheet& stylesheet, double dpi_target, double dpi_leeway) {
double Settings::adaptiveScaleSettingsFor(const StyleSheet& stylesheet, double dpi_target, double dpi_leeway) {
if (abs(stylesheet.card_dpi - dpi_target) <= dpi_leeway) return 1.0;
return dpi_target / max(10.0, stylesheet.card_dpi);
}
Settings::ExportSettings Settings::exportSettingsFor(const StyleSheet& stylesheet) {
StyleSheetSettings& ss = stylesheetSettingsFor(stylesheet);
double zoom = settings.exportScaleSettingsFor(stylesheet);
double angle = ss.card_normal_export() ? 0.0 : deg_to_rad(ss.card_angle());
double bleed = ss.card_bleed_export() ? (stylesheet.card_dpi / 300.0) * 36.0 * zoom : 0.0; // 36 pixels of bleed on a 300 DPI print
return ExportSettings{zoom, angle, bleed};
}
IndexMap<FieldP,ValueP>& Settings::exportOptionsFor(const ExportTemplate& export_template) {
return export_options.get(export_template.name(), export_template.option_fields);
}
@@ -324,7 +337,7 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(Settings) {
REFLECT(print_cutter_lines);
REFLECT(dark_mode_type);
REFLECT(apprentice_location);
REFLECT(internal_scale_selection);
REFLECT(import_scale_selection);
REFLECT(internal_image_extension);
#if USE_OLD_STYLE_UPDATE_CHECKER
REFLECT(updates_url);
+19 -13
View File
@@ -98,12 +98,13 @@ public:
// Rendering/display settings
Defaultable<double> card_zoom;
Defaultable<int> export_zoom_selection;
Defaultable<int> export_scale_selection;
Defaultable<Degrees> card_angle;
Defaultable<bool> card_anti_alias;
Defaultable<bool> card_borders;
Defaultable<bool> card_draw_editing;
Defaultable<bool> card_normal_export;
Defaultable<bool> card_bleed_export;
Defaultable<bool> card_notes_export;
Defaultable<bool> card_spellcheck_enabled;
@@ -173,18 +174,23 @@ public:
String default_game;
// --------------------------------------------------- : Game/stylesheet specific
/// Get the settings object for a specific game
GameSettings& gameSettingsFor (const Game& game);
/// Get the settings for a column for a specific field in a game
ColumnSettings& columnSettingsFor (const Game& game, const Field& field);
/// Get the settings object for a specific stylesheet
StyleSheetSettings& stylesheetSettingsFor (const StyleSheet& stylesheet);
double exportZoomSettingsFor (const StyleSheet& stylesheet);
double internalScaleSettingsFor(const StyleSheet& stylesheet);
double adaptiveZoomSettingsFor (const StyleSheet& stylesheet, double target_dpi, double leeway_dpi);
struct ExportSettings {
double zoom, angle_radians, bleed_pixels;
};
static const vector<int> export_zoom_choices;
/// Get the settings object for a specific game
GameSettings& gameSettingsFor (const Game& game);
/// Get the settings for a column for a specific field in a game
ColumnSettings& columnSettingsFor (const Game& game, const Field& field);
/// Get the settings object for a specific stylesheet
StyleSheetSettings& stylesheetSettingsFor (const StyleSheet& stylesheet);
double exportScaleSettingsFor (const StyleSheet& stylesheet);
double importScaleSettingsFor (const StyleSheet& stylesheet);
double adaptiveScaleSettingsFor (const StyleSheet& stylesheet, double target_dpi, double leeway_dpi);
ExportSettings exportSettingsFor (const StyleSheet& stylesheet);
static const vector<int> scale_choices;
private:
map<String,GameSettingsP> game_settings;
@@ -221,7 +227,7 @@ public:
// --------------------------------------------------- : Internal settings
int internal_scale_selection;
int import_scale_selection;
bool internal_image_extension;
// --------------------------------------------------- : Update checking
+1
View File
@@ -23,6 +23,7 @@
/// Resample (resize) an image, uses bilenear filtering
void resample(const Image& img_in, Image& img_out);
Image resample(const Image& img_in, int width, int height);
Image resample(const Image& img_in, double zoom);
/// Resamples an image, first clips the input image to a specified rectangle
/** The selected rectangle is resampled into the entire output image */
+3
View File
@@ -134,6 +134,9 @@ Image resample(const Image& img_in, int width, int height) {
return img_out;
}
}
Image resample(const Image& img_in, double zoom) {
return resample(img_in, (int)(img_in.GetWidth() * zoom), (int)(img_in.GetHeight() * zoom));
}
void resample_and_clip(const Image& img_in, Image& img_out, wxRect rect) {
// mask to alpha
+6 -3
View File
@@ -303,9 +303,12 @@ bool CardListBase::parseImage(Image& image, vector<CardP>& out) {
for (IndexMap<FieldP, ValueP>::iterator it = card->data.begin(); it != card->data.end(); it++) {
ImageValue* value = dynamic_cast<ImageValue*>(it->get());
if (value && !value->filename.empty()) {
wxRect rect = value->filename.getExternalRect();
wxRect rect = wxRect(0,0,0,0);
int degrees = 0;
value->filename.getExternalRect(rect, degrees);
if (rect.width > 0 && rect.height > 0) {
Image& img = image.GetSubImage(rect);
img = rotate_image(img, deg_to_rad(360-degrees));
LocalFileName filename = set->newFileName((*it)->fieldP->name, settings.internal_image_extension ? _(".png") : _("")); // a new unique name in the package
img.SaveFile(set->nameOut(filename), wxBITMAP_TYPE_PNG);
value->filename = filename;
@@ -319,8 +322,8 @@ bool CardListBase::parseImage(Image& image, vector<CardP>& out) {
bool CardListBase::parseText(String& text, vector<CardP>& out) {
size_t j = out.size();
if (size_t pos = text.find("<mse-data-start>") != wxString::npos) {
text = text.substr(pos + 15, text.find("<mse-data-end>") - pos - 15);
if (size_t pos = text.find("<mse-card-data>") != wxString::npos) {
text = text.substr(pos + 14, text.find("</mse-card-data>") - pos - 14);
}
try {
ScriptValueP& sv = json_to_mse(text, set.get());
+9 -11
View File
@@ -71,13 +71,12 @@ void ImageSlice::centerSelectionVertically() {
}
}
Image ImageSlice::getSlice(double scale) const {
wxSize scaled_target_size = target_size * scale;
if (selection.width == scaled_target_size.GetWidth() && selection.height == scaled_target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
Image ImageSlice::getSlice() const {
if (selection.width == target_size.GetWidth() && selection.height == target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
// exactly the right size
return source.GetSubImage(selection);
}
Image target(scaled_target_size.GetWidth(), scaled_target_size.GetHeight(), false);
Image target(target_size.GetWidth(), target_size.GetHeight(), false);
if (sharpen && sharpen_amount > 0 && sharpen_amount <= 100) {
sharp_resample_and_clip(source, target, selection, sharpen_amount);
} else {
@@ -104,7 +103,6 @@ ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const St
// init slice
pair<String, String> settings_entry = { filename, cardname };
if (previously_used_settings_value.find(settings_entry) != previously_used_settings_value.end()) {
//slice.allow_outside = true; this currrently crashes
slice.aspect_fixed = false;
slice.sharpen = true;
slice.sharpen_amount = previously_used_settings_value[settings_entry].second;
@@ -242,8 +240,8 @@ void ImageSliceWindow::onOk(wxCommandEvent&) {
EndModal(wxID_OK);
}
Image ImageSliceWindow::getImage(double scale) const {
Image img = slice.getSlice(scale);
Image ImageSliceWindow::getImage() const {
Image img = slice.getSlice();
previously_used_settings_path[slice.card_name] = slice.source_path;
previously_used_settings_value[{ slice.source_path, slice.card_name }] = { slice.selection, slice.sharpen_amount };
return img;
@@ -484,11 +482,12 @@ void ImageSlicePreview::draw(DC& dc) {
assert(image.GetWidth() == slice.target_size.GetWidth() && image.GetHeight() == slice.target_size.GetHeight());
mask.setAlpha(image);
if (image.HasAlpha()) {
// create bitmap
bitmap = Bitmap(image.GetWidth(), image.GetHeight());
// create bitmap
int width = image.GetWidth(), height = image.GetHeight();
bitmap = Bitmap(width, height);
wxMemoryDC mdc; mdc.SelectObject(bitmap);
// draw checker pattern behind image
RealRect rect = (RealRect) GetClientSize();
RealRect rect = RealRect(0, 0, width, height);
RotatedDC rdc(mdc, 0, rect, 1, QUALITY_LOW);
draw_checker(rdc, rect);
rdc.DrawImage(image, RealPoint(0,0));
@@ -587,7 +586,6 @@ ImageSliceSelector::ImageSliceSelector(Window* parent, int id, ImageSlice& slice
, slice(slice)
, mouse_down(false)
{
float target_ratio = ((float) slice.source.GetWidth()) / ((float) slice.source.GetHeight());
if (target_ratio > 1.0) {
SetMinSize(wxSize(500, 500 / target_ratio));
+3 -3
View File
@@ -36,7 +36,7 @@ public:
wxSize target_size; ///< Size of the target image
wxRect selection; ///< Area to slice from source
Color background; ///< Color for areas outside the source image
bool allow_outside; ///< Allow the slice to extend outside the source image?
bool allow_outside; ///< Allow the slice to extend outside the source image? TODO: This currently crashes
bool aspect_fixed; ///< Aspect ratio lock?
// Filters
@@ -50,7 +50,7 @@ public:
void centerSelectionHorizontally();
void centerSelectionVertically();
/// Get the sliced image
Image getSlice(double scale = 1.0) const;
Image getSlice() const;
// Zoom factor
inline double zoomX() const { return target_size.GetWidth() / (double)selection.width; }
@@ -68,7 +68,7 @@ public:
ImageSliceWindow(Window* parent, const Image& source, const String& filename, const String& cardname, const wxSize& target_size, const AlphaMask& target_mask);
/// Return the sliced image
Image getImage(double scale) const;
Image getImage() const;
// --------------------------------------------------- : Previously Used Settings
+76 -71
View File
@@ -52,27 +52,25 @@ public:
private:
DECLARE_EVENT_TABLE();
wxCheckBox* high_quality, *borders, *draw_editing, *spellcheck_enabled, *non_normal_export, *notes_export;
wxCheckBox* high_quality, *borders, *draw_editing, *spellcheck_enabled;
wxComboBox* zoom;
int zoom_int;
wxChoice* export_zoom;
void onSelectColumns(wxCommandEvent&);
void onZoomChange(wxCommandEvent&);
void updateZoom();
};
class InternalPreferencesPage : public PreferencesPage {
class TransfersPreferencesPage : public PreferencesPage {
public:
InternalPreferencesPage(Window* parent);
TransfersPreferencesPage(Window* parent);
void store() override;
private:
wxCheckBox* internal_image_extension;
wxCheckBox* non_normal_export, *bleed_export, *notes_export, *internal_image_extension;
wxChoice* internal_scale;
wxChoice* export_scale, *import_scale;
};
// Preferences page for directories of programs
@@ -116,7 +114,7 @@ PreferencesWindow::PreferencesWindow(Window* parent)
wxNotebook* nb = new wxNotebook(this, ID_NOTEBOOK);
nb->AddPage(new GlobalPreferencesPage (nb), _TITLE_("global"));
nb->AddPage(new DisplayPreferencesPage(nb), _TITLE_("display"));
nb->AddPage(new InternalPreferencesPage(nb), _TITLE_("internal"));
nb->AddPage(new TransfersPreferencesPage(nb), _TITLE_("transfers"));
nb->AddPage(new DirsPreferencesPage (nb), _TITLE_("directories"));
nb->AddPage(new UpdatePreferencesPage (nb), _TITLE_("updates"));
@@ -216,34 +214,18 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent)
borders = new wxCheckBox(this, wxID_ANY, _BUTTON_("show lines"));
draw_editing = new wxCheckBox(this, wxID_ANY, _BUTTON_("show editing hints"));
spellcheck_enabled = new wxCheckBox(this, wxID_ANY, _BUTTON_("spellcheck enabled"));
non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("rotation export"));
notes_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("notes export"));
zoom = new wxComboBox(this, ID_ZOOM);
export_zoom = new wxChoice (this, ID_EXPORT_ZOOM);
//wxButton* columns = new wxButton(this, ID_SELECT_COLUMNS, _BUTTON_("select"));
// set values
high_quality-> SetValue( settings.default_stylesheet_settings.card_anti_alias());
borders-> SetValue( settings.default_stylesheet_settings.card_borders());
draw_editing-> SetValue( settings.default_stylesheet_settings.card_draw_editing());
spellcheck_enabled->SetValue( settings.default_stylesheet_settings.card_spellcheck_enabled());
non_normal_export-> SetValue(!settings.default_stylesheet_settings.card_normal_export());
notes_export-> SetValue( settings.default_stylesheet_settings.card_notes_export());
zoom_int = static_cast<int>(settings.default_stylesheet_settings.card_zoom() * 100);
zoom->SetValue(String::Format(_("%d%%"),zoom_int));
for (int i : Settings::export_zoom_choices) {
zoom->Append(String::Format(_("%d%%"), i));
}
export_zoom->Append(_LABEL_("export around 300"));
export_zoom->Append(_LABEL_("export force 300"));
export_zoom->Append(_LABEL_("export force 150"));
for (int i : Settings::export_zoom_choices) {
export_zoom->Append(String::Format(_("%d%%"), i));
}
int default_export_zoom = settings.default_stylesheet_settings.export_zoom_selection();
if (default_export_zoom < 0 || default_export_zoom > export_zoom->GetCount() - 1) default_export_zoom = 0;
export_zoom->SetSelection(default_export_zoom);
zoom_int = static_cast<int>( settings.default_stylesheet_settings.card_zoom() * 100);
zoom->SetValue(String::Format(_("%d%%"),zoom_int));
for (int i : Settings::scale_choices) {
zoom->Append(String::Format(_("%d%%"), i));
}
// init sizer
wxSizer* s = new wxBoxSizer(wxVERTICAL);
@@ -257,19 +239,8 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent)
s3->AddSpacer(2);
s3->Add(zoom);
s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")),1, wxALL & ~wxRIGHT, 4);
wxSizer* s4 = new wxBoxSizer(wxHORIZONTAL);
s4->Add(new wxStaticText(this, wxID_ANY, _LABEL_("export")), 0, wxALL & ~wxLEFT, 4);
s4->AddSpacer(2);
s4->Add(export_zoom);
//s4->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")), 1, wxALL & ~wxRIGHT, 4);
s2->Add(s3, 0, wxEXPAND | wxALL, 4);
s2->Add(s4, 0, wxEXPAND | wxALL, 4);
s2->Add(non_normal_export, 0, wxEXPAND | wxALL, 4);
s2->Add(notes_export, 0, wxEXPAND | wxALL, 4);
s->Add(s2, 0, wxEXPAND | wxALL, 8);
s->SetSizeHints(this);
SetSizer(s);
}
@@ -279,12 +250,9 @@ void DisplayPreferencesPage::store() {
settings.default_stylesheet_settings.card_borders = borders->GetValue();
settings.default_stylesheet_settings.card_draw_editing = draw_editing->GetValue();
settings.default_stylesheet_settings.card_spellcheck_enabled = spellcheck_enabled->GetValue();
settings.default_stylesheet_settings.card_normal_export = !non_normal_export->GetValue();
settings.default_stylesheet_settings.card_notes_export = notes_export->GetValue();
updateZoom();
settings.default_stylesheet_settings.card_zoom = zoom_int / 100.0;
settings.default_stylesheet_settings.export_zoom_selection = export_zoom->GetSelection();
}
void DisplayPreferencesPage::onSelectColumns(wxCommandEvent&) {
@@ -292,7 +260,7 @@ void DisplayPreferencesPage::onSelectColumns(wxCommandEvent&) {
}
void DisplayPreferencesPage::onZoomChange(wxCommandEvent&) {
updateZoom();
updateZoom();
}
void DisplayPreferencesPage::updateZoom() {
@@ -312,41 +280,78 @@ END_EVENT_TABLE ()
// ----------------------------------------------------------------------------- : Preferences page : internal
InternalPreferencesPage::InternalPreferencesPage(Window* parent) : PreferencesPage(parent) {
TransfersPreferencesPage::TransfersPreferencesPage(Window* parent) : PreferencesPage(parent) {
// init controls
non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("rotation export"));
bleed_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("bleed export"));
notes_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("notes export"));
export_scale = new wxChoice (this, ID_EXPORT_ZOOM);
internal_image_extension = new wxCheckBox(this, wxID_ANY, _BUTTON_("internal image extension"));
internal_scale = new wxChoice(this, ID_INTERNAL_SCALE);
import_scale = new wxChoice (this, ID_IMPORT_ZOOM);
// set values
non_normal_export-> SetValue(!settings.default_stylesheet_settings.card_normal_export());
bleed_export-> SetValue( settings.default_stylesheet_settings.card_bleed_export());
notes_export-> SetValue( settings.default_stylesheet_settings.card_notes_export());
export_scale->Append(_LABEL_("export around 300"));
export_scale->Append(_LABEL_("export force 300"));
export_scale->Append(_LABEL_("export force 150"));
for (int i : Settings::scale_choices) {
export_scale->Append(String::Format(_("%d%%"), i));
}
int default_export_scale = settings.default_stylesheet_settings.export_scale_selection();
if (default_export_scale < 0 || default_export_scale > export_scale->GetCount() - 1) default_export_scale = 0;
export_scale->SetSelection(default_export_scale);
internal_image_extension->SetValue(settings.internal_image_extension);
internal_scale->Append(_LABEL_("use export scale"));
internal_scale->Append(_LABEL_("export around 300"));
internal_scale->Append(_LABEL_("export force 300"));
internal_scale->Append(_LABEL_("export force 150"));
for (int i : Settings::export_zoom_choices) {
internal_scale->Append(String::Format(_("%d%%"), i));
import_scale->Append(_LABEL_("use export scale"));
import_scale->Append(_LABEL_("export around 300"));
import_scale->Append(_LABEL_("export force 300"));
import_scale->Append(_LABEL_("export force 150"));
for (int i : Settings::scale_choices) {
import_scale->Append(String::Format(_("%d%%"), i));
}
int default_internal_scale = settings.internal_scale_selection;
if (default_internal_scale < 0 || default_internal_scale > internal_scale->GetCount() - 1) default_internal_scale = 0;
internal_scale->SetSelection(default_internal_scale);
wxSizer* s = new wxBoxSizer(wxVERTICAL);
wxSizer* s2 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("storage"));
wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL);
s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("scale")), 0, wxALL & ~wxLEFT, 4);
s3->AddSpacer(2);
s3->Add(internal_scale);
//s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")), 1, wxALL & ~wxRIGHT, 4);
s2->Add(s3);
s2->Add(new wxStaticText(this, wxID_ANY, _LABEL_("internal scale desc")), 0, wxALL & ~wxLEFT, 4);
s2->Add(internal_image_extension, 0, wxEXPAND | wxALL, 4);
s->Add(s2, 0, wxEXPAND | wxALL, 8);
int default_import_scale = settings.import_scale_selection;
if (default_import_scale < 0 || default_import_scale > import_scale->GetCount() - 1) default_import_scale = 0;
import_scale->SetSelection(default_import_scale);
// init sizers
wxSizer* s = new wxBoxSizer(wxVERTICAL);
wxSizer* s2 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("export"));
s2->Add(new wxStaticText(this, wxID_ANY, _LABEL_("export desc")), 0, wxALL & ~wxLEFT, 4);
wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL);
s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("scale")), 0, wxALL & ~wxLEFT, 4);
s3->AddSpacer(2);
s3->Add(export_scale);
s2->Add(s3, 0, wxEXPAND | wxALL, 4);
s2->Add(non_normal_export, 0, wxEXPAND | wxALL, 4);
s2->Add(bleed_export, 0, wxEXPAND | wxALL, 4);
s2->Add(notes_export, 0, wxEXPAND | wxALL, 4);
wxSizer* s5 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("import"));
s5->Add(new wxStaticText(this, wxID_ANY, _LABEL_("import desc")), 0, wxALL & ~wxLEFT, 4);
wxSizer* s6 = new wxBoxSizer(wxHORIZONTAL);
s6->Add(new wxStaticText(this, wxID_ANY, _LABEL_("scale")), 0, wxALL & ~wxLEFT, 4);
s6->AddSpacer(2);
s6->Add(import_scale);
s5->Add(s6, 0, wxEXPAND | wxALL & ~wxBottom, 4);
s5->Add(new wxStaticText(this, wxID_ANY, _LABEL_("internal scale desc")), 0, wxALL & ~wxTOP, 4);
s5->Add(internal_image_extension, 0, wxEXPAND | wxALL, 4);
s->Add(s2, 0, wxEXPAND | wxALL, 8);
s->Add(s5, 0, wxEXPAND | wxALL, 8);
export_scale->SetFocus();
s->SetSizeHints(this);
SetSizer(s);
}
void InternalPreferencesPage::store() {
settings.internal_image_extension = internal_image_extension->GetValue();
settings.internal_scale_selection = internal_scale->GetSelection();
void TransfersPreferencesPage::store() {
settings.default_stylesheet_settings.card_normal_export = !non_normal_export->GetValue();
settings.default_stylesheet_settings.card_bleed_export = bleed_export->GetValue();
settings.default_stylesheet_settings.card_notes_export = notes_export->GetValue();
settings.default_stylesheet_settings.export_scale_selection = export_scale->GetSelection();
settings.internal_image_extension = internal_image_extension->GetValue();
settings.import_scale_selection = import_scale->GetSelection();
}
// ----------------------------------------------------------------------------- : Preferences page : directories
+12 -19
View File
@@ -50,31 +50,24 @@ bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) {
void ImageValueEditor::sliceImage(const Image& image, const String& filename, const String& cardname) {
if (!image.Ok()) return;
// determine import scale based on the user's settings.
double import_scale = 1.0;
StyleSheetP stylesheet = editor().getCard()->stylesheet;
if (!stylesheet) stylesheet = editor().getSet()->stylesheet;
if (stylesheet) import_scale = settings.importScaleSettingsFor(*stylesheet);
RealSize target_size = RealSize(style().getSize() * import_scale);
target_size = RealSize((int)target_size.width, (int)target_size.height);
// mask
GeneratedImage::Options options((int)style().width, (int)style().height, &parent.getStylePackage(), &parent.getLocalPackage());
GeneratedImage::Options options((int)target_size.width, (int)target_size.height, &parent.getStylePackage(), &parent.getLocalPackage());
AlphaMask mask;
style().mask.getNoCache(options,mask);
// slice
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, filename, cardname, style().getSize(), mask);
style().mask.getNoCache(options, mask);
// slice
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, filename, cardname, target_size, mask);
// clicked ok?
if (s.ShowModal() == wxID_OK) {
// store the image into the set
LocalFileName new_image_file = getLocalPackage().newFileName(field().name, settings.internal_image_extension ? _(".png") : _("")); // a new unique name in the package
// Specify a desired size based on the stylesheet and a scale multiplier defined within the user's settings.
// Storing at a greater than 100% resolution allows for better exports >100%, but may change how images look when filters (sharpen) are applied.
// It also disrupts some of the patterns in use for doing popout planeswalkers since you have to do the math at both scales.
// Additionally, this bloats the set file size as even under-resolution images are upscaled to the new minimum size.
double internal_scale = 1.0;
try {
// surrounding this in try catch to be safe for now. maybe this is overkill
StyleSheetP stylesheet = editor().getCard()->stylesheet;
if (!stylesheet) stylesheet = editor().getSet()->stylesheet;
internal_scale = settings.internalScaleSettingsFor(*stylesheet);
} catch (...) {
queue_message(MESSAGE_ERROR, _("Could not find stylesheet to determine export zoom.\nfilename: " + filename + _("\ncardname: " + cardname)));
}
Image img = s.getImage(internal_scale);
Image img = s.getImage();
img.SaveFile(getLocalPackage().nameOut(new_image_file), wxBITMAP_TYPE_PNG); // always use PNG images, see #69. Disk space is cheap anyway.
addAction(value_action(valueP(), new_image_file));
}
+88 -71
View File
@@ -759,6 +759,22 @@ SCRIPT_FUNCTION(get_card_stylesheet) {
throw ScriptError(_("invalid set or card argument"));
}
SCRIPT_FUNCTION(get_card_export_settings) {
SCRIPT_PARAM_C(ScriptValueP, input);
SCRIPT_PARAM_C(ScriptValueP, set);
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get());
ScriptObject<Set*>* s = dynamic_cast<ScriptObject<Set*>*>(set.get());
if (s && c) {
Settings::ExportSettings card_settings = settings.exportSettingsFor(s->getValue()->stylesheetFor(c->getValue()));
ScriptCustomCollectionP ret(new ScriptCustomCollection());
ret->value.push_back(to_script(lround(card_settings.zoom * 100)));
ret->value.push_back(to_script(lround(rad_to_deg(card_settings.angle_radians))));
ret->value.push_back(to_script(lround(card_settings.bleed_pixels)));
return ret;
}
throw ScriptError(_("invalid set or card argument"));
}
SCRIPT_FUNCTION(get_card_from_uid) {
SCRIPT_PARAM_C(Set*, set);
SCRIPT_PARAM_C(String, input);
@@ -999,82 +1015,83 @@ SCRIPT_FUNCTION(rule) {
void init_script_basic_functions(Context& ctx) {
// app info
ctx.setVariable(_("get_mse_version"), script_get_mse_version);
ctx.setVariable(_("get_mse_locale"), script_get_mse_locale);
ctx.setVariable(_("get_mse_path"), script_get_mse_path);
ctx.setVariable(_("get_mse_dark_mode"), script_get_mse_dark_mode);
ctx.setVariable(_("get_mse_version"), script_get_mse_version);
ctx.setVariable(_("get_mse_locale"), script_get_mse_locale);
ctx.setVariable(_("get_mse_path"), script_get_mse_path);
ctx.setVariable(_("get_mse_dark_mode"), script_get_mse_dark_mode);
// debugging
ctx.setVariable(_("trace"), script_trace);
ctx.setVariable(_("warning"), script_warning);
ctx.setVariable(_("error"), script_error);
ctx.setVariable(_("exists_in_package"), script_exists_in_package);
ctx.setVariable(_("trace"), script_trace);
ctx.setVariable(_("warning"), script_warning);
ctx.setVariable(_("error"), script_error);
ctx.setVariable(_("exists_in_package"), script_exists_in_package);
// conversion
ctx.setVariable(_("to_string"), script_to_string);
ctx.setVariable(_("to_int"), script_to_int);
ctx.setVariable(_("to_real"), script_to_real);
ctx.setVariable(_("to_number"), script_to_number);
ctx.setVariable(_("to_boolean"), script_to_boolean);
ctx.setVariable(_("to_color"), script_to_color);
ctx.setVariable(_("to_date"), script_to_date);
ctx.setVariable(_("to_code"), script_to_code);
ctx.setVariable(_("to_json"), script_to_json);
ctx.setVariable(_("from_json"), script_from_json);
ctx.setVariable(_("type_name"), script_type_name);
ctx.setVariable(_("make_map"), script_make_map);
ctx.setVariable(_("get_card_styling"), script_get_card_styling);
ctx.setVariable(_("get_card_stylesheet"), script_get_card_stylesheet);
ctx.setVariable(_("to_string"), script_to_string);
ctx.setVariable(_("to_int"), script_to_int);
ctx.setVariable(_("to_real"), script_to_real);
ctx.setVariable(_("to_number"), script_to_number);
ctx.setVariable(_("to_boolean"), script_to_boolean);
ctx.setVariable(_("to_color"), script_to_color);
ctx.setVariable(_("to_date"), script_to_date);
ctx.setVariable(_("to_code"), script_to_code);
ctx.setVariable(_("to_json"), script_to_json);
ctx.setVariable(_("from_json"), script_from_json);
ctx.setVariable(_("type_name"), script_type_name);
ctx.setVariable(_("make_map"), script_make_map);
ctx.setVariable(_("get_card_styling"), script_get_card_styling);
ctx.setVariable(_("get_card_stylesheet"), script_get_card_stylesheet);
ctx.setVariable(_("get_card_export_settings"), script_get_card_export_settings);
ctx.setVariable(_("get_card_from_uid"), script_get_card_from_uid);
ctx.setVariable(_("get_cards_from_link"), script_get_cards_from_link);
ctx.setVariable(_("get_back_face"), script_get_back_face);
ctx.setVariable(_("get_front_face"), script_get_front_face);
ctx.setVariable(_("has_link"), script_has_link);
// math
ctx.setVariable(_("abs"), script_abs);
ctx.setVariable(_("random_real"), script_random_real);
ctx.setVariable(_("random_int"), script_random_int);
ctx.setVariable(_("random_boolean"), script_random_boolean);
ctx.setVariable(_("sin"), script_sin);
ctx.setVariable(_("cos"), script_cos);
ctx.setVariable(_("tan"), script_tan);
ctx.setVariable(_("sin_deg"), script_sin_deg);
ctx.setVariable(_("cos_deg"), script_cos_deg);
ctx.setVariable(_("tan_deg"), script_tan_deg);
ctx.setVariable(_("exp"), script_exp);
ctx.setVariable(_("log"), script_log);
ctx.setVariable(_("log10"), script_log10);
ctx.setVariable(_("sqrt"), script_sqrt);
ctx.setVariable(_("pow"), script_pow);
ctx.setVariable(_("abs"), script_abs);
ctx.setVariable(_("random_real"), script_random_real);
ctx.setVariable(_("random_int"), script_random_int);
ctx.setVariable(_("random_boolean"), script_random_boolean);
ctx.setVariable(_("sin"), script_sin);
ctx.setVariable(_("cos"), script_cos);
ctx.setVariable(_("tan"), script_tan);
ctx.setVariable(_("sin_deg"), script_sin_deg);
ctx.setVariable(_("cos_deg"), script_cos_deg);
ctx.setVariable(_("tan_deg"), script_tan_deg);
ctx.setVariable(_("exp"), script_exp);
ctx.setVariable(_("log"), script_log);
ctx.setVariable(_("log10"), script_log10);
ctx.setVariable(_("sqrt"), script_sqrt);
ctx.setVariable(_("pow"), script_pow);
// string
ctx.setVariable(_("to_upper"), script_to_upper);
ctx.setVariable(_("to_lower"), script_to_lower);
ctx.setVariable(_("to_title"), script_to_title);
ctx.setVariable(_("reverse"), script_reverse);
ctx.setVariable(_("trim"), script_trim);
ctx.setVariable(_("substring"), script_substring);
ctx.setVariable(_("contains"), script_contains);
ctx.setVariable(_("format"), script_format);
ctx.setVariable(_("format_rule"), make_intrusive<ScriptRule>(script_format));
ctx.setVariable(_("curly_quotes"), script_curly_quotes);
ctx.setVariable(_("regex_escape"), script_regex_escape);
ctx.setVariable(_("sort_text"), script_sort_text);
ctx.setVariable(_("sort_rule"), make_intrusive<ScriptRule>(script_sort_text));
ctx.setVariable(_("to_upper"), script_to_upper);
ctx.setVariable(_("to_lower"), script_to_lower);
ctx.setVariable(_("to_title"), script_to_title);
ctx.setVariable(_("reverse"), script_reverse);
ctx.setVariable(_("trim"), script_trim);
ctx.setVariable(_("substring"), script_substring);
ctx.setVariable(_("contains"), script_contains);
ctx.setVariable(_("format"), script_format);
ctx.setVariable(_("format_rule"), make_intrusive<ScriptRule>(script_format));
ctx.setVariable(_("curly_quotes"), script_curly_quotes);
ctx.setVariable(_("regex_escape"), script_regex_escape);
ctx.setVariable(_("sort_text"), script_sort_text);
ctx.setVariable(_("sort_rule"), make_intrusive<ScriptRule>(script_sort_text));
// tagged string
ctx.setVariable(_("tag_contents"), script_tag_contents);
ctx.setVariable(_("remove_tag"), script_remove_tag);
ctx.setVariable(_("remove_tags"), script_remove_tags);
ctx.setVariable(_("tag_contents_rule"), make_intrusive<ScriptRule>(script_tag_contents));
ctx.setVariable(_("tag_remove_rule"), make_intrusive<ScriptRule>(script_remove_tag));
ctx.setVariable(_("tag_contents"), script_tag_contents);
ctx.setVariable(_("remove_tag"), script_remove_tag);
ctx.setVariable(_("remove_tags"), script_remove_tags);
ctx.setVariable(_("tag_contents_rule"), make_intrusive<ScriptRule>(script_tag_contents));
ctx.setVariable(_("tag_remove_rule"), make_intrusive<ScriptRule>(script_remove_tag));
// collection
ctx.setVariable(_("position"), script_position_of);
ctx.setVariable(_("length"), script_length);
ctx.setVariable(_("number_of_items"), script_number_of_items); // deprecated
ctx.setVariable(_("filter_list"), script_filter_list);
ctx.setVariable(_("sort_list"), script_sort_list);
ctx.setVariable(_("random_shuffle"), script_random_shuffle);
ctx.setVariable(_("random_select"), script_random_select);
ctx.setVariable(_("random_select_many"), script_random_select_many);
ctx.setVariable(_("get_card_from_uid"), script_get_card_from_uid);
ctx.setVariable(_("get_cards_from_link"), script_get_cards_from_link);
ctx.setVariable(_("get_back_face"), script_get_back_face);
ctx.setVariable(_("get_front_face"), script_get_front_face);
ctx.setVariable(_("has_link"), script_has_link);
ctx.setVariable(_("position"), script_position_of);
ctx.setVariable(_("length"), script_length);
ctx.setVariable(_("number_of_items"), script_number_of_items); // deprecated
ctx.setVariable(_("filter_list"), script_filter_list);
ctx.setVariable(_("sort_list"), script_sort_list);
ctx.setVariable(_("random_shuffle"), script_random_shuffle);
ctx.setVariable(_("random_select"), script_random_select);
ctx.setVariable(_("random_select_many"), script_random_select_many);
// keyword
ctx.setVariable(_("expand_keywords"), script_expand_keywords);
ctx.setVariable(_("expand_keywords_rule"), make_intrusive<ScriptRule>(script_expand_keywords));
ctx.setVariable(_("keyword_usage"), script_keyword_usage);
ctx.setVariable(_("expand_keywords"), script_expand_keywords);
ctx.setVariable(_("expand_keywords_rule"), make_intrusive<ScriptRule>(script_expand_keywords));
ctx.setVariable(_("keyword_usage"), script_keyword_usage);
}
+27 -11
View File
@@ -428,22 +428,38 @@ SCRIPT_FUNCTION(write_image_file) {
SCRIPT_RETURN(file); // already written an image with this name
}
// get image
SCRIPT_PARAM_C(ScriptValueP, input);
SCRIPT_OPTIONAL_PARAM_(int, width);
SCRIPT_OPTIONAL_PARAM_(int, height);
ScriptObject<CardP>* card = dynamic_cast<ScriptObject<CardP>*>(input.get()); // is it a card?
Image image;
GeneratedImage::Options options(width, height, ei.export_template.get(), ei.set.get());
Image img;
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM_C(ScriptValueP, input);
ScriptObject<CardP>* card = dynamic_cast<ScriptObject<CardP>*>(input.get()); // is the input a card or image?
if (card) {
image = conform_image(export_image(ei.set, card->getValue()), options);
SCRIPT_PARAM_DEFAULT(double, zoom, 100.0);
SCRIPT_PARAM_DEFAULT(Degrees, angle, 0.0);
SCRIPT_PARAM_DEFAULT(double, bleed, 0.0);
SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false);
if (use_user_settings) {
// Use the User's Preferences for Export Zoom, Angle and Bleed settings.
Settings::ExportSettings card_settings = settings.exportSettingsFor(set->stylesheetFor(card->getValue()));
zoom = card_settings.zoom;
angle = card_settings.angle_radians;
bleed = card_settings.bleed_pixels;
} else {
// Use the provided (or defaulted) Zoom, Angle and Bleed.
zoom = zoom / 100.0;
angle = deg_to_rad(angle);
}
img = export_image(set, card->getValue(), true, zoom, angle, bleed);
} else {
image = input->toImage()->generateConform(options);
SCRIPT_OPTIONAL_PARAM_(int, width)
SCRIPT_OPTIONAL_PARAM_(int, height)
GeneratedImage::Options options(width, height, ei.export_template.get(), ei.set.get());
img = input->toImage()->generateConform(options);
}
if (!image.Ok()) throw Error(_("Unable to generate image for file ") + file);
if (!img.Ok()) throw Error(_("Unable to generate image for file ") + file);
// write
ensure_dir_valid(out_path);
image.SaveFile(out_path);
ei.exported_images.insert(make_pair(file, wxSize(image.GetWidth(), image.GetHeight())));
img.SaveFile(out_path);
ei.exported_images.insert(make_pair(file, wxSize(img.GetWidth(), img.GetHeight())));
SCRIPT_RETURN(file);
}
+8 -7
View File
@@ -35,19 +35,20 @@ SCRIPT_FUNCTION(to_card_image) {
SCRIPT_PARAM(CardP, input);
SCRIPT_PARAM_DEFAULT(double, zoom, 100.0);
SCRIPT_PARAM_DEFAULT(Degrees, angle, 0.0);
SCRIPT_PARAM_DEFAULT(double, bleed, 0.0);
SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false);
if (use_user_settings) {
// Use the User's Preferences for Export Zoom and Angle settings.
const StyleSheet& stylesheet = set->stylesheetFor(input);
StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet);
zoom = settings.exportZoomSettingsFor(stylesheet);
angle = stylesheet_settings.card_normal_export() ? 0.0 : deg_to_rad(stylesheet_settings.card_angle());
// Use the User's Preferences for Export Zoom, Angle and Bleed settings.
Settings::ExportSettings card_settings = settings.exportSettingsFor(set->stylesheetFor(input));
zoom = card_settings.zoom;
angle = card_settings.angle_radians;
bleed = card_settings.bleed_pixels;
} else {
// Use the provided (or defaulted) Zoom and Angle.
// Use the provided (or defaulted) Zoom, Angle and Bleed.
zoom = zoom / 100.0;
angle = deg_to_rad(angle);
}
return make_intrusive<ArbitraryImage>(export_image(set, input, zoom, angle));
return make_intrusive<ArbitraryImage>(export_image(set, input, true, zoom, angle, bleed));
}
SCRIPT_FUNCTION(import_image) {
+2 -1
View File
@@ -363,7 +363,8 @@ inline static ScriptValueP json_to_mse(const String& string, Set* set) {
boost::json::parse_options options;
options.allow_invalid_utf8 = true;
boost::json::value jv = boost::json::parse(string.ToStdString(), ec, {}, options);
if(ec) return script_nil; //queue_message(MESSAGE_ERROR, _ERROR_("json cant parse") + _("\n\n") + ec.message());
//if(ec) queue_message(MESSAGE_ERROR, _ERROR_("json cant parse") + _("\n\n") + ec.message());
if(ec) return script_nil;
return json_to_mse(jv, set);
}
catch (...) {
+1 -1
View File
@@ -31,7 +31,7 @@ const Radians rad360 = 2.0*M_PI;
/// Are two floating point numbers equal up to a small epsilon?
inline bool almost_equal(double x, double y) {
return fabs(x-y) < 1e-10;
return fabs(x-y) < 1e-5;
}
inline bool is_rad0(double x) {
return almost_equal(x,0) || almost_equal(x,rad360);
+23 -17
View File
@@ -54,42 +54,48 @@ public:
inline String const& toStringForKey() const { return fn; }
/// Retreive a rect from a filename
inline static wxRect getExternalRect(const String& filename) {
inline static void getExternalRect(const String& filename, wxRect& rect_out, int& degrees_out) {
size_t first = filename.find(_("---"));
if (first == String::npos) return wxRect();
if (first == String::npos) return;
size_t last = filename.find(_("---"), first+3);
if (last == String::npos) return wxRect();
if (last == String::npos) return;
String string = filename.substr(first + 3, last - (first + 3));
if (string.empty()) return wxRect();
if (string.empty()) return;
size_t divider = string.find(_("-"));
if (divider == String::npos) return wxRect();
if (divider == 0) return wxRect();
if (divider == String::npos) return;
if (divider == 0) return;
int x;
if(!string.substr(0, divider).ToInt(&x)) return wxRect();
if(!string.substr(0, divider).ToInt(&x)) return;
string = string.substr(divider + 1);
divider = string.find(_("-"));
if (divider == String::npos) return wxRect();
if (divider == 0) return wxRect();
if (divider == String::npos) return;
if (divider == 0) return;
int y;
if(!string.substr(0, divider).ToInt(&y)) return wxRect();
if(!string.substr(0, divider).ToInt(&y)) return;
string = string.substr(divider + 1);
divider = string.find(_("-"));
if (divider == String::npos) return wxRect();
if (divider == 0) return wxRect();
if (divider == String::npos) return;
if (divider == 0) return;
int width;
if(!string.substr(0, divider).ToInt(&width)) return wxRect();
if(!string.substr(0, divider).ToInt(&width)) return;
string = string.substr(divider + 1);
divider = string.find(_("-"));
if (divider == String::npos) return;
if (divider == 0) return;
int height;
if(!string.ToInt(&height)) return wxRect();
if(!string.substr(0, divider).ToInt(&height)) return;
string = string.substr(divider + 1);
return wxRect(x, y, width, height);
if(!string.ToInt(&degrees_out)) return;
rect_out = wxRect(x, y, width, height);
}
inline wxRect getExternalRect() {
return getExternalRect(fn);
inline void getExternalRect(wxRect& rect_out, int& degrees_out) {
getExternalRect(fn, rect_out, degrees_out);
}
private:
+1 -1
View File
@@ -75,7 +75,7 @@ RealSize Rotation::trSizeToBB(const RealSize& size) const {
}
RealRect Rotation::trRectToBB(const RealRect& r) const {
const bool special_case_optimization = false;
const bool special_case_optimization = true;
double x = r.x * zoomX, y = r.y * zoomY;
double w = r.width * zoomX, h = r.height * zoomY;
if (special_case_optimization && is_rad0(angle)) {
+1 -1
View File
@@ -310,7 +310,7 @@ enum ControlID {
ID_SHARPEN,
ID_SHARPEN_AMOUNT,
// Internal window
ID_INTERNAL_SCALE,
ID_IMPORT_ZOOM,
// Updates window
ID_PACKAGE_LIST,
ID_KEEP,
@@ -107,6 +107,7 @@ $built_in_functions = array(
'get_card_from_uid' =>'',
'get_card_styling' =>'',
'get_card_stylesheet' =>'',
'get_card_export_settings' =>'',
'add_card_to_set' =>'',
// html export
'to_html' =>'',