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 dark mode yes: Dark mode
app language: Language of the user interface: app language: Language of the user interface:
card display: Card Display card display: Card Display
storage: Storage
zoom: &Zoom: zoom: &Zoom:
export: &Export: import: Import
scale: &Internal Scale: export: &Export
scale: Use the following scale:
use export scale: Use Export Scale use export scale: Use Export Scale
export around 300: Around 300 DPI export around 300: Around 300 DPI
export force 300: Force 300 DPI export force 300: Force 300 DPI
@@ -586,10 +586,11 @@ label:
external programs: External programs external programs: External programs
apprentice: &Apprentice: apprentice: &Apprentice:
apprentice exe: Apprentice Executable apprentice exe: Apprentice Executable
internal scale desc: export desc: When exporting cards to images:
Scale to internally store card images at. import desc: When importing images for illustrations:
Changing this may impact how Sharpening looks. internal scale desc:
Does not retroactively apply to existing images. Changing the scale does not retroactively apply
to existing images. You must re-import them.
check at startup: Check for new versions at startup check at startup: Check for new versions at startup
checking requires internet: checking requires internet:
Checking for updates requires an internet connection. Checking for updates requires an internet connection.
@@ -741,21 +742,23 @@ button:
high quality: &High quality rendering high quality: &High quality rendering
show lines: Show &lines around fields show lines: Show &lines around fields
show editing hints: Show boxes and hints for &editing show editing hints: Show boxes and hints for &editing
zoom export:
Use Viewer zoom and rotation
settings when e&xporting
rotation export: rotation export:
Use Viewer rotation Use the card preview
setting when e&xporting rotation setting
bleed export:
Add a crude print
bleed margin
notes export: notes export:
Export card notes inside Add the card notes in
image meta data the image metadata
spellcheck enabled: Show &spelling errors on cards spellcheck enabled: Show &spelling errors on cards
check now: Check &Now check now: Check &Now
always: Always always: Always
if internet connection exists: If internet connection exists if internet connection exists: If internet connection exists
never: Never never: Never
internal image extension: Store images internally with file extension internal image extension:
Store images internally
with file extension
# column select # column select
move up: Move &Up move up: Move &Up
@@ -824,7 +827,7 @@ title:
global: Global global: Global
display: Display display: Display
directories: Directories directories: Directories
internal: Internal transfers: Transfers
updates: Updates updates: Updates
update check: Update Check update check: Update Check
locate apprentice: Locate Apprentice 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: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_styling]] Get the styling data of a [[type:card]].
| [[fun:get_card_stylesheet]] Get the stylesheet 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_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_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]]. | [[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 Function: write_image_file
--Usage-- --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. If a file with the given name already exists it is overwritten.
Returns the name of the file written. 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-- --Parameters--
! Parameter Type Description ! Parameter Type Description
| @input@ [[type:image]] Image to write to the file. | @input@ [[type:image]] or [[type:card]] Image or Card to write to the file.
| @file@ [[type:string]] Name of the file to write to | @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. | @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. | @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-- --Examples--
> write_image_file(file:"image_out.png", linear_blend(...)) == "image_out.png" # image_out.png now contains the given image > 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 RealPoint getPos() const { return RealPoint(left, top); }
inline RealSize getSize() const { return RealSize(width, height); } inline RealSize getSize() const { return RealSize(width, height); }
inline RealRect getExternalRect() const { return RealRect(left, top, 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 inline String getExternalRectString(double scale, Radians angle, double bleed, int img_width, int img_height, int img_offset) { ///< update the style before calling this
return _("---") + wxString::Format(wxT("%i"), (int)std::ceil(scale * left + offset)) + double x = left * scale, y = top * scale;
_("-") + wxString::Format(wxT("%i"), (int)std::ceil(scale * top)) + double w = width * scale, h = height * scale;
_("-") + wxString::Format(wxT("%i"), (int)std::floor(scale * width)) + RealRect rect(x, y, w, h);
_("-") + wxString::Format(wxT("%i"), (int)std::floor(scale * height)) + 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]); img = export_image(set, cards[0]);
} }
else { else {
img = export_image(set, cards, true, 0, 1.0, 0.0); img = export_image(set, cards);
} }
String temp_path = wxFileName::CreateTempFileName(_("mse")) + _(".png"); String temp_path = wxFileName::CreateTempFileName(_("mse")) + _(".png");
img.SaveFile(temp_path, wxBITMAP_TYPE_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 // ----------------------------------------------------------------------------- : 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 /// 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 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, 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 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 CardP& card, const String& filename);
void export_image(const SetP& set, const vector<CardP>& cards, const String& path, const String& filename_template, FilenameConflicts conflicts); 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 <render/card/viewer.hpp>
#include <wx/filename.h> #include <wx/filename.h>
// ----------------------------------------------------------------------------- : Card export class ZoomedUnrotatedDataViewer : public DataViewer {
class UnzoomedDataViewer : public DataViewer {
public: public:
UnzoomedDataViewer(); ZoomedUnrotatedDataViewer(double zoom) : zoom(zoom) {};
UnzoomedDataViewer(double zoom, Radians angle); virtual ~ZoomedUnrotatedDataViewer() {};
virtual ~UnzoomedDataViewer() {}; Rotation getRotation() const override;
Rotation getRotation() const override; private:
private:
double zoom; double zoom;
double angle;
bool declared_values;
}; };
UnzoomedDataViewer::UnzoomedDataViewer() Rotation ZoomedUnrotatedDataViewer::getRotation() const {
: zoom(1.0) return Rotation(0.0, stylesheet->getCardRect(), zoom);
, 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;
} }
// ----------------------------------------------------------------------------- : wxImage export // ----------------------------------------------------------------------------- : wxImage export
Image export_image(const SetP& set, const CardP& card, const double zoom, const Radians angle_radians) { Image export_image(const SetP& set, const CardP& card, const bool write_metadata, const double zoom, const Radians angle_radians, const double bleed_pixels) {
Bitmap bitmap = export_bitmap(set, card, zoom, angle_radians); if (!set) throw Error(_("no set"));
Image img = bitmap.ConvertToImage(); /// create and zoom
String data = _("<mse-data-start>["); ZoomedUnrotatedDataViewer viewer = ZoomedUnrotatedDataViewer(zoom);
IndexMap<FieldP, ValueP>& card_data = card->data; viewer.setSet(set);
boost::json::object& cardv = mse_to_json(card, set.get()); viewer.setCard(card);
boost::json::object& cardv_data = cardv["data"].as_object(); RealSize size = viewer.getRotation().getExternalSize();
StyleSheetP stylesheet = set->stylesheetForP(card); int bleed = lround(bleed_pixels);
if (!settings.stylesheetSettingsFor(*stylesheet).card_notes_export()) cardv["notes"] = ""; Bitmap bitmap((int)size.width + 2 * bleed, (int)size.height + 2 * bleed);
for(IndexMap<FieldP, ValueP>::iterator it = card_data.begin() ; it != card_data.end() ; ++it) { if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
ImageValue* value = dynamic_cast<ImageValue*>(it->get()); wxMemoryDC dc;
if (value && !value->filename.empty()) { dc.SelectObject(bitmap);
FieldP field = (*it)->fieldP; dc.SetDeviceOrigin(bleed, bleed);
StyleP style = stylesheet->card_style.at(field->index); viewer.draw(dc);
if (style) { dc.SelectObject(wxNullBitmap);
style->update(set->getContext(card)); Image img = bitmap.ConvertToImage();
std::string rect = style->getExternalRectString(zoom, 0).ToStdString();
cardv_data[field->name.ToStdString()] = rect; /// 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];
} }
} }
} // fill top right corner
data += json_ugly_print(cardv) + _("]<mse-data-end>"); x_start = width - bleed;
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, data); y_start = 0;
return img; ref = x_start - 1 + bleed * width;
} for (int y = 0; y < bleed; ++y) {
for (int x = 0; x < bleed; ++x) {
Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians) { pixel = x_start + x + (y_start + y) * width;
vector<double> scales; pixels[3 * pixel + 0] = pixels[3 * ref + 0];
vector<int> offsets; pixels[3 * pixel + 1] = pixels[3 * ref + 1];
Bitmap bitmap = export_bitmap(set, cards, scale_to_lowest_dpi, padding, zoom, angle_radians, scales, offsets); pixels[3 * pixel + 2] = pixels[3 * ref + 2];
Image img = bitmap.ConvertToImage(); alpha[pixel] = alpha[ref];
String data = _("<mse-data-start>["); }
for (int i = 0; i < cards.size(); ++i) { }
if (i > 0) data += _(","); // fill bottom left corner
CardP card = cards[i]; 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; IndexMap<FieldP, ValueP>& card_data = card->data;
boost::json::object& cardv = mse_to_json(card, set.get()); boost::json::object& cardv = mse_to_json(card, set.get());
boost::json::object& cardv_data = cardv["data"].as_object(); 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); StyleP style = stylesheet->card_style.at(field->index);
if (style) { if (style) {
style->update(set->getContext(card)); 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; cardv_data[field->name.ToStdString()] = rect;
} }
} }
} }
data += json_ugly_print(cardv); metadata += json_ugly_print(cardv) + _("]</mse-card-data>");
} img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, metadata);
data += _("]<mse-data-end>"); }
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, data);
return img; 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) { void export_image(const SetP& set, const CardP& card, const String& filename) {
const StyleSheet& stylesheet = set->stylesheetFor(card); const StyleSheet& stylesheet = set->stylesheetFor(card);
StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet); StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet);
double zoom = settings.exportZoomSettingsFor(stylesheet); Settings::ExportSettings export_settings = settings.exportSettingsFor(stylesheet);
Radians angle = stylesheet_settings.card_normal_export() ? 0.0 : deg_to_rad(stylesheet_settings.card_angle()); Image img = export_image(set, card, true, export_settings.zoom, export_settings.angle_radians, export_settings.bleed_pixels);
Image img = export_image(set, card, zoom, angle);
img.SaveFile(filename); img.SaveFile(filename);
} }
+75 -62
View File
@@ -32,8 +32,8 @@ IMPLEMENT_REFLECTION_ENUM(CheckUpdates) {
} }
IMPLEMENT_REFLECTION_ENUM(InstallType) { IMPLEMENT_REFLECTION_ENUM(InstallType) {
VALUE_N("default", INSTALL_DEFAULT); //default VALUE_N("default", INSTALL_DEFAULT); //default
VALUE_N("local", INSTALL_LOCAL); VALUE_N("local", INSTALL_LOCAL);
VALUE_N("global", INSTALL_GLOBAL); VALUE_N("global", INSTALL_GLOBAL);
} }
@@ -47,13 +47,13 @@ bool is_install_local(InstallType type) {
} }
IMPLEMENT_REFLECTION_ENUM(FilenameConflicts) { IMPLEMENT_REFLECTION_ENUM(FilenameConflicts) {
VALUE_N("keep old", CONFLICT_KEEP_OLD); VALUE_N("keep old", CONFLICT_KEEP_OLD);
VALUE_N("overwrite", CONFLICT_OVERWRITE); VALUE_N("overwrite", CONFLICT_OVERWRITE);
VALUE_N("number", CONFLICT_NUMBER); VALUE_N("number", CONFLICT_NUMBER);
VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE); 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; const int COLUMN_NOT_INITIALIZED = -100000;
@@ -71,13 +71,13 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(ColumnSettings) {
} }
GameSettings::GameSettings() GameSettings::GameSettings()
: sort_cards_ascending(true) : sort_cards_ascending (true)
, images_export_filename(_("{card.name}.png")) , images_export_filename (_("{card.name}.png"))
, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE) , images_export_conflicts (CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace(true) , use_auto_replace (true)
, pack_seed_random(true) , pack_seed_random (true)
, pack_seed(123456) , pack_seed (123456)
, initialized(false) , initialized (false)
{} {}
void GameSettings::initDefaults(const Game& game) { void GameSettings::initDefaults(const Game& game) {
@@ -128,37 +128,40 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(GameSettings) {
StyleSheetSettings::StyleSheetSettings() StyleSheetSettings::StyleSheetSettings()
: card_zoom (1.0, true) : card_zoom (1.0, true)
, export_zoom_selection (0, true) , export_scale_selection (0, true)
, card_angle (0, true) , card_angle (0, true)
, card_anti_alias (true, true) , card_anti_alias (true, true)
, card_borders (true, true) , card_borders (true, true)
, card_draw_editing (true, true) , card_draw_editing (true, true)
, card_normal_export (true, true) , card_normal_export (true, true)
, card_notes_export (false, true) , card_bleed_export (false, true)
, card_spellcheck_enabled(true, true) , card_notes_export (false, true)
, card_spellcheck_enabled (true, true)
{} {}
void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) { void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
if (card_zoom .isDefault()) card_zoom .assignDefault(ss.card_zoom); 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_angle .isDefault()) card_angle .assignDefault(ss.card_angle);
if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias); 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_borders .isDefault()) card_borders .assignDefault(ss.card_borders);
if (card_draw_editing .isDefault()) card_draw_editing .assignDefault(ss.card_draw_editing); 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_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_notes_export .isDefault()) card_notes_export .assignDefault(ss.card_notes_export);
if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled); if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled);
} }
IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) { IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) {
REFLECT(card_zoom); REFLECT(card_zoom);
REFLECT(export_zoom_selection); REFLECT(export_scale_selection);
REFLECT(card_angle); REFLECT(card_angle);
REFLECT(card_anti_alias); REFLECT(card_anti_alias);
REFLECT(card_borders); REFLECT(card_borders);
REFLECT(card_draw_editing); REFLECT(card_draw_editing);
REFLECT(card_normal_export); REFLECT(card_normal_export);
REFLECT(card_bleed_export);
REFLECT(card_notes_export); REFLECT(card_notes_export);
REFLECT(card_spellcheck_enabled); REFLECT(card_spellcheck_enabled);
} }
@@ -184,29 +187,29 @@ IMPLEMENT_REFLECTION_ENUM(DarkModeType) {
Settings settings; Settings settings;
Settings::Settings() Settings::Settings()
: locale (_("en")) : locale (_("en"))
, set_window_maximized (false) , set_window_maximized (false)
, set_window_width (790) , set_window_width (790)
, set_window_height (300) , set_window_height (300)
, card_notes_height (40) , card_notes_height (40)
, open_sets_in_new_window (true) , open_sets_in_new_window (true)
, symbol_grid_size (30) , symbol_grid_size (30)
, symbol_grid (true) , symbol_grid (true)
, symbol_grid_snap (false) , symbol_grid_snap (false)
, print_spacing (0.33) , print_spacing (0.33)
, print_cutter_lines (CUTTER_ALL) , print_cutter_lines (CUTTER_ALL)
, dark_mode_type (DARKMODE_SYSTEM) , dark_mode_type (DARKMODE_SYSTEM)
, internal_scale_selection(0) , import_scale_selection (0)
, internal_image_extension(true) , internal_image_extension (true)
#if USE_OLD_STYLE_UPDATE_CHECKER #if USE_OLD_STYLE_UPDATE_CHECKER
, updates_url (_("https://magicseteditor.boards.net/page/downloads")) , updates_url (_("https://magicseteditor.boards.net/page/downloads"))
#endif #endif
, package_versions_url (_("https://magicseteditor.boards.net/page/downloads")) , package_versions_url (_("https://magicseteditor.boards.net/page/downloads"))
, installer_list_url (_("https://magicseteditor.boards.net/page/downloads")) , installer_list_url (_("https://magicseteditor.boards.net/page/downloads"))
, check_updates (CHECK_IF_CONNECTED) , check_updates (CHECK_IF_CONNECTED)
, check_updates_all (true) , check_updates_all (true)
, website_url (_("https://magicseteditor.boards.net/")) , website_url (_("https://magicseteditor.boards.net/"))
, install_type (INSTALL_DEFAULT) , install_type (INSTALL_DEFAULT)
{} {}
void Settings::addRecentFile(const String& filename) { void Settings::addRecentFile(const String& filename) {
@@ -230,7 +233,8 @@ GameSettings& Settings::gameSettingsFor(const Game& game) {
if (!gs) gs = make_intrusive<GameSettings>(); if (!gs) gs = make_intrusive<GameSettings>();
gs->initDefaults(game); gs->initDefaults(game);
return *gs; return *gs;
} }
ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) { ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) {
// Get game info // Get game info
GameSettings& gs = gameSettingsFor(game); GameSettings& gs = gameSettingsFor(game);
@@ -243,7 +247,8 @@ ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field
cs.width = field.card_list_width; cs.width = field.card_list_width;
} }
return cs; return cs;
} }
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) { StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
// Use the canonical form here since the stylesheet name will be used as a stored key. // 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. // 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; return *ss;
} }
double Settings::exportZoomSettingsFor(const StyleSheet& stylesheet) { double Settings::exportScaleSettingsFor(const StyleSheet& stylesheet) {
StyleSheetSettings& ss = stylesheetSettingsFor(stylesheet); StyleSheetSettings& ss = stylesheetSettingsFor(stylesheet);
int export_zoom = ss.export_zoom_selection(); int export_scale = ss.export_scale_selection();
if (export_zoom == 0) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0); if (export_scale == 0) return adaptiveScaleSettingsFor(stylesheet, 300.0, 50.0);
if (export_zoom == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0); if (export_scale == 1) return adaptiveScaleSettingsFor(stylesheet, 300.0, 1.0);
if (export_zoom == 2) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0); if (export_scale == 2) return adaptiveScaleSettingsFor(stylesheet, 150.0, 1.0);
return export_zoom_choices[export_zoom - 3] / 100; return scale_choices[export_scale - 3] / 100;
} }
double Settings::internalScaleSettingsFor(const StyleSheet& stylesheet) { double Settings::importScaleSettingsFor(const StyleSheet& stylesheet) {
if (internal_scale_selection == 0) return exportZoomSettingsFor(stylesheet); if (import_scale_selection == 0) return exportScaleSettingsFor(stylesheet);
if (internal_scale_selection == 1) return adaptiveZoomSettingsFor(stylesheet, 300.0, 50.0); if (import_scale_selection == 1) return adaptiveScaleSettingsFor(stylesheet, 300.0, 50.0);
if (internal_scale_selection == 2) return adaptiveZoomSettingsFor(stylesheet, 300.0, 1.0); if (import_scale_selection == 2) return adaptiveScaleSettingsFor(stylesheet, 300.0, 1.0);
if (internal_scale_selection == 3) return adaptiveZoomSettingsFor(stylesheet, 150.0, 1.0); if (import_scale_selection == 3) return adaptiveScaleSettingsFor(stylesheet, 150.0, 1.0);
return export_zoom_choices[internal_scale_selection - 4] / 100; 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; if (abs(stylesheet.card_dpi - dpi_target) <= dpi_leeway) return 1.0;
return dpi_target / max(10.0, stylesheet.card_dpi); 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) { IndexMap<FieldP,ValueP>& Settings::exportOptionsFor(const ExportTemplate& export_template) {
return export_options.get(export_template.name(), export_template.option_fields); 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(print_cutter_lines);
REFLECT(dark_mode_type); REFLECT(dark_mode_type);
REFLECT(apprentice_location); REFLECT(apprentice_location);
REFLECT(internal_scale_selection); REFLECT(import_scale_selection);
REFLECT(internal_image_extension); REFLECT(internal_image_extension);
#if USE_OLD_STYLE_UPDATE_CHECKER #if USE_OLD_STYLE_UPDATE_CHECKER
REFLECT(updates_url); REFLECT(updates_url);
+19 -13
View File
@@ -98,12 +98,13 @@ public:
// Rendering/display settings // Rendering/display settings
Defaultable<double> card_zoom; Defaultable<double> card_zoom;
Defaultable<int> export_zoom_selection; Defaultable<int> export_scale_selection;
Defaultable<Degrees> card_angle; Defaultable<Degrees> card_angle;
Defaultable<bool> card_anti_alias; Defaultable<bool> card_anti_alias;
Defaultable<bool> card_borders; Defaultable<bool> card_borders;
Defaultable<bool> card_draw_editing; Defaultable<bool> card_draw_editing;
Defaultable<bool> card_normal_export; Defaultable<bool> card_normal_export;
Defaultable<bool> card_bleed_export;
Defaultable<bool> card_notes_export; Defaultable<bool> card_notes_export;
Defaultable<bool> card_spellcheck_enabled; Defaultable<bool> card_spellcheck_enabled;
@@ -173,18 +174,23 @@ public:
String default_game; String default_game;
// --------------------------------------------------- : Game/stylesheet specific // --------------------------------------------------- : Game/stylesheet specific
/// Get the settings object for a specific game struct ExportSettings {
GameSettings& gameSettingsFor (const Game& game); double zoom, angle_radians, bleed_pixels;
/// 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);
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: private:
map<String,GameSettingsP> game_settings; map<String,GameSettingsP> game_settings;
@@ -221,7 +227,7 @@ public:
// --------------------------------------------------- : Internal settings // --------------------------------------------------- : Internal settings
int internal_scale_selection; int import_scale_selection;
bool internal_image_extension; bool internal_image_extension;
// --------------------------------------------------- : Update checking // --------------------------------------------------- : Update checking
+1
View File
@@ -23,6 +23,7 @@
/// Resample (resize) an image, uses bilenear filtering /// Resample (resize) an image, uses bilenear filtering
void resample(const Image& img_in, Image& img_out); 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, int width, int height);
Image resample(const Image& img_in, double zoom);
/// Resamples an image, first clips the input image to a specified rectangle /// Resamples an image, first clips the input image to a specified rectangle
/** The selected rectangle is resampled into the entire output image */ /** 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; 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) { void resample_and_clip(const Image& img_in, Image& img_out, wxRect rect) {
// mask to alpha // 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++) { for (IndexMap<FieldP, ValueP>::iterator it = card->data.begin(); it != card->data.end(); it++) {
ImageValue* value = dynamic_cast<ImageValue*>(it->get()); ImageValue* value = dynamic_cast<ImageValue*>(it->get());
if (value && !value->filename.empty()) { 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) { if (rect.width > 0 && rect.height > 0) {
Image& img = image.GetSubImage(rect); 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 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); img.SaveFile(set->nameOut(filename), wxBITMAP_TYPE_PNG);
value->filename = filename; value->filename = filename;
@@ -319,8 +322,8 @@ bool CardListBase::parseImage(Image& image, vector<CardP>& out) {
bool CardListBase::parseText(String& text, vector<CardP>& out) { bool CardListBase::parseText(String& text, vector<CardP>& out) {
size_t j = out.size(); size_t j = out.size();
if (size_t pos = text.find("<mse-data-start>") != wxString::npos) { if (size_t pos = text.find("<mse-card-data>") != wxString::npos) {
text = text.substr(pos + 15, text.find("<mse-data-end>") - pos - 15); text = text.substr(pos + 14, text.find("</mse-card-data>") - pos - 14);
} }
try { try {
ScriptValueP& sv = json_to_mse(text, set.get()); 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 { Image ImageSlice::getSlice() const {
wxSize scaled_target_size = target_size * scale; if (selection.width == target_size.GetWidth() && selection.height == target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
if (selection.width == scaled_target_size.GetWidth() && selection.height == scaled_target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
// exactly the right size // exactly the right size
return source.GetSubImage(selection); 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) { if (sharpen && sharpen_amount > 0 && sharpen_amount <= 100) {
sharp_resample_and_clip(source, target, selection, sharpen_amount); sharp_resample_and_clip(source, target, selection, sharpen_amount);
} else { } else {
@@ -104,7 +103,6 @@ ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const St
// init slice // init slice
pair<String, String> settings_entry = { filename, cardname }; pair<String, String> settings_entry = { filename, cardname };
if (previously_used_settings_value.find(settings_entry) != previously_used_settings_value.end()) { 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.aspect_fixed = false;
slice.sharpen = true; slice.sharpen = true;
slice.sharpen_amount = previously_used_settings_value[settings_entry].second; slice.sharpen_amount = previously_used_settings_value[settings_entry].second;
@@ -242,8 +240,8 @@ void ImageSliceWindow::onOk(wxCommandEvent&) {
EndModal(wxID_OK); EndModal(wxID_OK);
} }
Image ImageSliceWindow::getImage(double scale) const { Image ImageSliceWindow::getImage() const {
Image img = slice.getSlice(scale); Image img = slice.getSlice();
previously_used_settings_path[slice.card_name] = slice.source_path; 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 }; previously_used_settings_value[{ slice.source_path, slice.card_name }] = { slice.selection, slice.sharpen_amount };
return img; return img;
@@ -484,11 +482,12 @@ void ImageSlicePreview::draw(DC& dc) {
assert(image.GetWidth() == slice.target_size.GetWidth() && image.GetHeight() == slice.target_size.GetHeight()); assert(image.GetWidth() == slice.target_size.GetWidth() && image.GetHeight() == slice.target_size.GetHeight());
mask.setAlpha(image); mask.setAlpha(image);
if (image.HasAlpha()) { if (image.HasAlpha()) {
// create bitmap // create bitmap
bitmap = Bitmap(image.GetWidth(), image.GetHeight()); int width = image.GetWidth(), height = image.GetHeight();
bitmap = Bitmap(width, height);
wxMemoryDC mdc; mdc.SelectObject(bitmap); wxMemoryDC mdc; mdc.SelectObject(bitmap);
// draw checker pattern behind image // draw checker pattern behind image
RealRect rect = (RealRect) GetClientSize(); RealRect rect = RealRect(0, 0, width, height);
RotatedDC rdc(mdc, 0, rect, 1, QUALITY_LOW); RotatedDC rdc(mdc, 0, rect, 1, QUALITY_LOW);
draw_checker(rdc, rect); draw_checker(rdc, rect);
rdc.DrawImage(image, RealPoint(0,0)); rdc.DrawImage(image, RealPoint(0,0));
@@ -587,7 +586,6 @@ ImageSliceSelector::ImageSliceSelector(Window* parent, int id, ImageSlice& slice
, slice(slice) , slice(slice)
, mouse_down(false) , mouse_down(false)
{ {
float target_ratio = ((float) slice.source.GetWidth()) / ((float) slice.source.GetHeight()); float target_ratio = ((float) slice.source.GetWidth()) / ((float) slice.source.GetHeight());
if (target_ratio > 1.0) { if (target_ratio > 1.0) {
SetMinSize(wxSize(500, 500 / target_ratio)); SetMinSize(wxSize(500, 500 / target_ratio));
+3 -3
View File
@@ -36,7 +36,7 @@ public:
wxSize target_size; ///< Size of the target image wxSize target_size; ///< Size of the target image
wxRect selection; ///< Area to slice from source wxRect selection; ///< Area to slice from source
Color background; ///< Color for areas outside the source image 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? bool aspect_fixed; ///< Aspect ratio lock?
// Filters // Filters
@@ -50,7 +50,7 @@ public:
void centerSelectionHorizontally(); void centerSelectionHorizontally();
void centerSelectionVertically(); void centerSelectionVertically();
/// Get the sliced image /// Get the sliced image
Image getSlice(double scale = 1.0) const; Image getSlice() const;
// Zoom factor // Zoom factor
inline double zoomX() const { return target_size.GetWidth() / (double)selection.width; } 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); ImageSliceWindow(Window* parent, const Image& source, const String& filename, const String& cardname, const wxSize& target_size, const AlphaMask& target_mask);
/// Return the sliced image /// Return the sliced image
Image getImage(double scale) const; Image getImage() const;
// --------------------------------------------------- : Previously Used Settings // --------------------------------------------------- : Previously Used Settings
+76 -71
View File
@@ -52,27 +52,25 @@ public:
private: private:
DECLARE_EVENT_TABLE(); 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; wxComboBox* zoom;
int zoom_int; int zoom_int;
wxChoice* export_zoom;
void onSelectColumns(wxCommandEvent&); void onSelectColumns(wxCommandEvent&);
void onZoomChange(wxCommandEvent&); void onZoomChange(wxCommandEvent&);
void updateZoom(); void updateZoom();
}; };
class InternalPreferencesPage : public PreferencesPage { class TransfersPreferencesPage : public PreferencesPage {
public: public:
InternalPreferencesPage(Window* parent); TransfersPreferencesPage(Window* parent);
void store() override; void store() override;
private: 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 // Preferences page for directories of programs
@@ -116,7 +114,7 @@ PreferencesWindow::PreferencesWindow(Window* parent)
wxNotebook* nb = new wxNotebook(this, ID_NOTEBOOK); wxNotebook* nb = new wxNotebook(this, ID_NOTEBOOK);
nb->AddPage(new GlobalPreferencesPage (nb), _TITLE_("global")); nb->AddPage(new GlobalPreferencesPage (nb), _TITLE_("global"));
nb->AddPage(new DisplayPreferencesPage(nb), _TITLE_("display")); 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 DirsPreferencesPage (nb), _TITLE_("directories"));
nb->AddPage(new UpdatePreferencesPage (nb), _TITLE_("updates")); nb->AddPage(new UpdatePreferencesPage (nb), _TITLE_("updates"));
@@ -216,34 +214,18 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent)
borders = new wxCheckBox(this, wxID_ANY, _BUTTON_("show lines")); borders = new wxCheckBox(this, wxID_ANY, _BUTTON_("show lines"));
draw_editing = new wxCheckBox(this, wxID_ANY, _BUTTON_("show editing hints")); draw_editing = new wxCheckBox(this, wxID_ANY, _BUTTON_("show editing hints"));
spellcheck_enabled = new wxCheckBox(this, wxID_ANY, _BUTTON_("spellcheck enabled")); 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); 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 // set values
high_quality-> SetValue( settings.default_stylesheet_settings.card_anti_alias()); high_quality-> SetValue( settings.default_stylesheet_settings.card_anti_alias());
borders-> SetValue( settings.default_stylesheet_settings.card_borders()); borders-> SetValue( settings.default_stylesheet_settings.card_borders());
draw_editing-> SetValue( settings.default_stylesheet_settings.card_draw_editing()); draw_editing-> SetValue( settings.default_stylesheet_settings.card_draw_editing());
spellcheck_enabled->SetValue( settings.default_stylesheet_settings.card_spellcheck_enabled()); spellcheck_enabled->SetValue( settings.default_stylesheet_settings.card_spellcheck_enabled());
non_normal_export-> SetValue(!settings.default_stylesheet_settings.card_normal_export()); zoom_int = static_cast<int>( settings.default_stylesheet_settings.card_zoom() * 100);
notes_export-> SetValue( settings.default_stylesheet_settings.card_notes_export()); zoom->SetValue(String::Format(_("%d%%"),zoom_int));
zoom_int = static_cast<int>(settings.default_stylesheet_settings.card_zoom() * 100); for (int i : Settings::scale_choices) {
zoom->SetValue(String::Format(_("%d%%"),zoom_int)); zoom->Append(String::Format(_("%d%%"), i));
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);
// init sizer // init sizer
wxSizer* s = new wxBoxSizer(wxVERTICAL); wxSizer* s = new wxBoxSizer(wxVERTICAL);
@@ -257,19 +239,8 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent)
s3->AddSpacer(2); s3->AddSpacer(2);
s3->Add(zoom); s3->Add(zoom);
s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")),1, wxALL & ~wxRIGHT, 4); 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(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->Add(s2, 0, wxEXPAND | wxALL, 8);
s->SetSizeHints(this); s->SetSizeHints(this);
SetSizer(s); SetSizer(s);
} }
@@ -279,12 +250,9 @@ void DisplayPreferencesPage::store() {
settings.default_stylesheet_settings.card_borders = borders->GetValue(); settings.default_stylesheet_settings.card_borders = borders->GetValue();
settings.default_stylesheet_settings.card_draw_editing = draw_editing->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_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(); updateZoom();
settings.default_stylesheet_settings.card_zoom = zoom_int / 100.0; settings.default_stylesheet_settings.card_zoom = zoom_int / 100.0;
settings.default_stylesheet_settings.export_zoom_selection = export_zoom->GetSelection();
} }
void DisplayPreferencesPage::onSelectColumns(wxCommandEvent&) { void DisplayPreferencesPage::onSelectColumns(wxCommandEvent&) {
@@ -292,7 +260,7 @@ void DisplayPreferencesPage::onSelectColumns(wxCommandEvent&) {
} }
void DisplayPreferencesPage::onZoomChange(wxCommandEvent&) { void DisplayPreferencesPage::onZoomChange(wxCommandEvent&) {
updateZoom(); updateZoom();
} }
void DisplayPreferencesPage::updateZoom() { void DisplayPreferencesPage::updateZoom() {
@@ -312,41 +280,78 @@ END_EVENT_TABLE ()
// ----------------------------------------------------------------------------- : Preferences page : internal // ----------------------------------------------------------------------------- : 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_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_image_extension->SetValue(settings.internal_image_extension);
import_scale->Append(_LABEL_("use export scale"));
internal_scale->Append(_LABEL_("use export scale")); import_scale->Append(_LABEL_("export around 300"));
internal_scale->Append(_LABEL_("export around 300")); import_scale->Append(_LABEL_("export force 300"));
internal_scale->Append(_LABEL_("export force 300")); import_scale->Append(_LABEL_("export force 150"));
internal_scale->Append(_LABEL_("export force 150")); for (int i : Settings::scale_choices) {
for (int i : Settings::export_zoom_choices) { import_scale->Append(String::Format(_("%d%%"), i));
internal_scale->Append(String::Format(_("%d%%"), i));
} }
int default_internal_scale = settings.internal_scale_selection; int default_import_scale = settings.import_scale_selection;
if (default_internal_scale < 0 || default_internal_scale > internal_scale->GetCount() - 1) default_internal_scale = 0; if (default_import_scale < 0 || default_import_scale > import_scale->GetCount() - 1) default_import_scale = 0;
internal_scale->SetSelection(default_internal_scale); import_scale->SetSelection(default_import_scale);
wxSizer* s = new wxBoxSizer(wxVERTICAL); // init sizers
wxSizer* s2 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("storage")); wxSizer* s = new wxBoxSizer(wxVERTICAL);
wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL); wxSizer* s2 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("export"));
s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("scale")), 0, wxALL & ~wxLEFT, 4); s2->Add(new wxStaticText(this, wxID_ANY, _LABEL_("export desc")), 0, wxALL & ~wxLEFT, 4);
s3->AddSpacer(2); wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL);
s3->Add(internal_scale); s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("scale")), 0, wxALL & ~wxLEFT, 4);
//s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("percent of normal")), 1, wxALL & ~wxRIGHT, 4); s3->AddSpacer(2);
s2->Add(s3); s3->Add(export_scale);
s2->Add(new wxStaticText(this, wxID_ANY, _LABEL_("internal scale desc")), 0, wxALL & ~wxLEFT, 4); s2->Add(s3, 0, wxEXPAND | wxALL, 4);
s2->Add(internal_image_extension, 0, wxEXPAND | wxALL, 4); s2->Add(non_normal_export, 0, wxEXPAND | wxALL, 4);
s->Add(s2, 0, wxEXPAND | wxALL, 8); 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); s->SetSizeHints(this);
SetSizer(s); SetSizer(s);
} }
void InternalPreferencesPage::store() { void TransfersPreferencesPage::store() {
settings.internal_image_extension = internal_image_extension->GetValue(); settings.default_stylesheet_settings.card_normal_export = !non_normal_export->GetValue();
settings.internal_scale_selection = internal_scale->GetSelection(); 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 // ----------------------------------------------------------------------------- : 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) { void ImageValueEditor::sliceImage(const Image& image, const String& filename, const String& cardname) {
if (!image.Ok()) return; 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 // 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; AlphaMask mask;
style().mask.getNoCache(options,mask); style().mask.getNoCache(options, mask);
// slice // slice
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, filename, cardname, style().getSize(), mask); ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, filename, cardname, target_size, mask);
// clicked ok? // clicked ok?
if (s.ShowModal() == wxID_OK) { if (s.ShowModal() == wxID_OK) {
// store the image into the set // 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 LocalFileName new_image_file = getLocalPackage().newFileName(field().name, settings.internal_image_extension ? _(".png") : _("")); // a new unique name in the package
Image img = s.getImage();
// 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);
img.SaveFile(getLocalPackage().nameOut(new_image_file), wxBITMAP_TYPE_PNG); // always use PNG images, see #69. Disk space is cheap anyway. 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)); 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")); 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_FUNCTION(get_card_from_uid) {
SCRIPT_PARAM_C(Set*, set); SCRIPT_PARAM_C(Set*, set);
SCRIPT_PARAM_C(String, input); SCRIPT_PARAM_C(String, input);
@@ -999,82 +1015,83 @@ SCRIPT_FUNCTION(rule) {
void init_script_basic_functions(Context& ctx) { void init_script_basic_functions(Context& ctx) {
// app info // app info
ctx.setVariable(_("get_mse_version"), script_get_mse_version); ctx.setVariable(_("get_mse_version"), script_get_mse_version);
ctx.setVariable(_("get_mse_locale"), script_get_mse_locale); ctx.setVariable(_("get_mse_locale"), script_get_mse_locale);
ctx.setVariable(_("get_mse_path"), script_get_mse_path); ctx.setVariable(_("get_mse_path"), script_get_mse_path);
ctx.setVariable(_("get_mse_dark_mode"), script_get_mse_dark_mode); ctx.setVariable(_("get_mse_dark_mode"), script_get_mse_dark_mode);
// debugging // debugging
ctx.setVariable(_("trace"), script_trace); ctx.setVariable(_("trace"), script_trace);
ctx.setVariable(_("warning"), script_warning); ctx.setVariable(_("warning"), script_warning);
ctx.setVariable(_("error"), script_error); ctx.setVariable(_("error"), script_error);
ctx.setVariable(_("exists_in_package"), script_exists_in_package); ctx.setVariable(_("exists_in_package"), script_exists_in_package);
// conversion // conversion
ctx.setVariable(_("to_string"), script_to_string); ctx.setVariable(_("to_string"), script_to_string);
ctx.setVariable(_("to_int"), script_to_int); ctx.setVariable(_("to_int"), script_to_int);
ctx.setVariable(_("to_real"), script_to_real); ctx.setVariable(_("to_real"), script_to_real);
ctx.setVariable(_("to_number"), script_to_number); ctx.setVariable(_("to_number"), script_to_number);
ctx.setVariable(_("to_boolean"), script_to_boolean); ctx.setVariable(_("to_boolean"), script_to_boolean);
ctx.setVariable(_("to_color"), script_to_color); ctx.setVariable(_("to_color"), script_to_color);
ctx.setVariable(_("to_date"), script_to_date); ctx.setVariable(_("to_date"), script_to_date);
ctx.setVariable(_("to_code"), script_to_code); ctx.setVariable(_("to_code"), script_to_code);
ctx.setVariable(_("to_json"), script_to_json); ctx.setVariable(_("to_json"), script_to_json);
ctx.setVariable(_("from_json"), script_from_json); ctx.setVariable(_("from_json"), script_from_json);
ctx.setVariable(_("type_name"), script_type_name); ctx.setVariable(_("type_name"), script_type_name);
ctx.setVariable(_("make_map"), script_make_map); ctx.setVariable(_("make_map"), script_make_map);
ctx.setVariable(_("get_card_styling"), script_get_card_styling); ctx.setVariable(_("get_card_styling"), script_get_card_styling);
ctx.setVariable(_("get_card_stylesheet"), script_get_card_stylesheet); 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 // math
ctx.setVariable(_("abs"), script_abs); ctx.setVariable(_("abs"), script_abs);
ctx.setVariable(_("random_real"), script_random_real); ctx.setVariable(_("random_real"), script_random_real);
ctx.setVariable(_("random_int"), script_random_int); ctx.setVariable(_("random_int"), script_random_int);
ctx.setVariable(_("random_boolean"), script_random_boolean); ctx.setVariable(_("random_boolean"), script_random_boolean);
ctx.setVariable(_("sin"), script_sin); ctx.setVariable(_("sin"), script_sin);
ctx.setVariable(_("cos"), script_cos); ctx.setVariable(_("cos"), script_cos);
ctx.setVariable(_("tan"), script_tan); ctx.setVariable(_("tan"), script_tan);
ctx.setVariable(_("sin_deg"), script_sin_deg); ctx.setVariable(_("sin_deg"), script_sin_deg);
ctx.setVariable(_("cos_deg"), script_cos_deg); ctx.setVariable(_("cos_deg"), script_cos_deg);
ctx.setVariable(_("tan_deg"), script_tan_deg); ctx.setVariable(_("tan_deg"), script_tan_deg);
ctx.setVariable(_("exp"), script_exp); ctx.setVariable(_("exp"), script_exp);
ctx.setVariable(_("log"), script_log); ctx.setVariable(_("log"), script_log);
ctx.setVariable(_("log10"), script_log10); ctx.setVariable(_("log10"), script_log10);
ctx.setVariable(_("sqrt"), script_sqrt); ctx.setVariable(_("sqrt"), script_sqrt);
ctx.setVariable(_("pow"), script_pow); ctx.setVariable(_("pow"), script_pow);
// string // string
ctx.setVariable(_("to_upper"), script_to_upper); ctx.setVariable(_("to_upper"), script_to_upper);
ctx.setVariable(_("to_lower"), script_to_lower); ctx.setVariable(_("to_lower"), script_to_lower);
ctx.setVariable(_("to_title"), script_to_title); ctx.setVariable(_("to_title"), script_to_title);
ctx.setVariable(_("reverse"), script_reverse); ctx.setVariable(_("reverse"), script_reverse);
ctx.setVariable(_("trim"), script_trim); ctx.setVariable(_("trim"), script_trim);
ctx.setVariable(_("substring"), script_substring); ctx.setVariable(_("substring"), script_substring);
ctx.setVariable(_("contains"), script_contains); ctx.setVariable(_("contains"), script_contains);
ctx.setVariable(_("format"), script_format); ctx.setVariable(_("format"), script_format);
ctx.setVariable(_("format_rule"), make_intrusive<ScriptRule>(script_format)); ctx.setVariable(_("format_rule"), make_intrusive<ScriptRule>(script_format));
ctx.setVariable(_("curly_quotes"), script_curly_quotes); ctx.setVariable(_("curly_quotes"), script_curly_quotes);
ctx.setVariable(_("regex_escape"), script_regex_escape); ctx.setVariable(_("regex_escape"), script_regex_escape);
ctx.setVariable(_("sort_text"), script_sort_text); ctx.setVariable(_("sort_text"), script_sort_text);
ctx.setVariable(_("sort_rule"), make_intrusive<ScriptRule>(script_sort_text)); ctx.setVariable(_("sort_rule"), make_intrusive<ScriptRule>(script_sort_text));
// tagged string // tagged string
ctx.setVariable(_("tag_contents"), script_tag_contents); ctx.setVariable(_("tag_contents"), script_tag_contents);
ctx.setVariable(_("remove_tag"), script_remove_tag); ctx.setVariable(_("remove_tag"), script_remove_tag);
ctx.setVariable(_("remove_tags"), script_remove_tags); ctx.setVariable(_("remove_tags"), script_remove_tags);
ctx.setVariable(_("tag_contents_rule"), make_intrusive<ScriptRule>(script_tag_contents)); 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_remove_rule"), make_intrusive<ScriptRule>(script_remove_tag));
// collection // collection
ctx.setVariable(_("position"), script_position_of); ctx.setVariable(_("position"), script_position_of);
ctx.setVariable(_("length"), script_length); ctx.setVariable(_("length"), script_length);
ctx.setVariable(_("number_of_items"), script_number_of_items); // deprecated ctx.setVariable(_("number_of_items"), script_number_of_items); // deprecated
ctx.setVariable(_("filter_list"), script_filter_list); ctx.setVariable(_("filter_list"), script_filter_list);
ctx.setVariable(_("sort_list"), script_sort_list); ctx.setVariable(_("sort_list"), script_sort_list);
ctx.setVariable(_("random_shuffle"), script_random_shuffle); ctx.setVariable(_("random_shuffle"), script_random_shuffle);
ctx.setVariable(_("random_select"), script_random_select); ctx.setVariable(_("random_select"), script_random_select);
ctx.setVariable(_("random_select_many"), script_random_select_many); 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);
// keyword // keyword
ctx.setVariable(_("expand_keywords"), script_expand_keywords); ctx.setVariable(_("expand_keywords"), script_expand_keywords);
ctx.setVariable(_("expand_keywords_rule"), make_intrusive<ScriptRule>(script_expand_keywords)); ctx.setVariable(_("expand_keywords_rule"), make_intrusive<ScriptRule>(script_expand_keywords));
ctx.setVariable(_("keyword_usage"), script_keyword_usage); 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 SCRIPT_RETURN(file); // already written an image with this name
} }
// get image // get image
SCRIPT_PARAM_C(ScriptValueP, input); Image img;
SCRIPT_OPTIONAL_PARAM_(int, width); SCRIPT_PARAM(Set*, set);
SCRIPT_OPTIONAL_PARAM_(int, height); SCRIPT_PARAM_C(ScriptValueP, input);
ScriptObject<CardP>* card = dynamic_cast<ScriptObject<CardP>*>(input.get()); // is it a card? ScriptObject<CardP>* card = dynamic_cast<ScriptObject<CardP>*>(input.get()); // is the input a card or image?
Image image;
GeneratedImage::Options options(width, height, ei.export_template.get(), ei.set.get());
if (card) { 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 { } 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 // write
ensure_dir_valid(out_path); ensure_dir_valid(out_path);
image.SaveFile(out_path); img.SaveFile(out_path);
ei.exported_images.insert(make_pair(file, wxSize(image.GetWidth(), image.GetHeight()))); ei.exported_images.insert(make_pair(file, wxSize(img.GetWidth(), img.GetHeight())));
SCRIPT_RETURN(file); SCRIPT_RETURN(file);
} }
+8 -7
View File
@@ -35,19 +35,20 @@ SCRIPT_FUNCTION(to_card_image) {
SCRIPT_PARAM(CardP, input); SCRIPT_PARAM(CardP, input);
SCRIPT_PARAM_DEFAULT(double, zoom, 100.0); SCRIPT_PARAM_DEFAULT(double, zoom, 100.0);
SCRIPT_PARAM_DEFAULT(Degrees, angle, 0.0); SCRIPT_PARAM_DEFAULT(Degrees, angle, 0.0);
SCRIPT_PARAM_DEFAULT(double, bleed, 0.0);
SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false); SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false);
if (use_user_settings) { if (use_user_settings) {
// Use the User's Preferences for Export Zoom and Angle settings. // Use the User's Preferences for Export Zoom, Angle and Bleed settings.
const StyleSheet& stylesheet = set->stylesheetFor(input); Settings::ExportSettings card_settings = settings.exportSettingsFor(set->stylesheetFor(input));
StyleSheetSettings& stylesheet_settings = settings.stylesheetSettingsFor(stylesheet); zoom = card_settings.zoom;
zoom = settings.exportZoomSettingsFor(stylesheet); angle = card_settings.angle_radians;
angle = stylesheet_settings.card_normal_export() ? 0.0 : deg_to_rad(stylesheet_settings.card_angle()); bleed = card_settings.bleed_pixels;
} else { } else {
// Use the provided (or defaulted) Zoom and Angle. // Use the provided (or defaulted) Zoom, Angle and Bleed.
zoom = zoom / 100.0; zoom = zoom / 100.0;
angle = deg_to_rad(angle); 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) { 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; boost::json::parse_options options;
options.allow_invalid_utf8 = true; options.allow_invalid_utf8 = true;
boost::json::value jv = boost::json::parse(string.ToStdString(), ec, {}, options); 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); return json_to_mse(jv, set);
} }
catch (...) { 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? /// Are two floating point numbers equal up to a small epsilon?
inline bool almost_equal(double x, double y) { 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) { inline bool is_rad0(double x) {
return almost_equal(x,0) || almost_equal(x,rad360); 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; } inline String const& toStringForKey() const { return fn; }
/// Retreive a rect from a filename /// 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(_("---")); size_t first = filename.find(_("---"));
if (first == String::npos) return wxRect(); if (first == String::npos) return;
size_t last = filename.find(_("---"), first+3); 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)); String string = filename.substr(first + 3, last - (first + 3));
if (string.empty()) return wxRect(); if (string.empty()) return;
size_t divider = string.find(_("-")); size_t divider = string.find(_("-"));
if (divider == String::npos) return wxRect(); if (divider == String::npos) return;
if (divider == 0) return wxRect(); if (divider == 0) return;
int x; int x;
if(!string.substr(0, divider).ToInt(&x)) return wxRect(); if(!string.substr(0, divider).ToInt(&x)) return;
string = string.substr(divider + 1); string = string.substr(divider + 1);
divider = string.find(_("-")); divider = string.find(_("-"));
if (divider == String::npos) return wxRect(); if (divider == String::npos) return;
if (divider == 0) return wxRect(); if (divider == 0) return;
int y; int y;
if(!string.substr(0, divider).ToInt(&y)) return wxRect(); if(!string.substr(0, divider).ToInt(&y)) return;
string = string.substr(divider + 1); string = string.substr(divider + 1);
divider = string.find(_("-")); divider = string.find(_("-"));
if (divider == String::npos) return wxRect(); if (divider == String::npos) return;
if (divider == 0) return wxRect(); if (divider == 0) return;
int width; int width;
if(!string.substr(0, divider).ToInt(&width)) return wxRect(); if(!string.substr(0, divider).ToInt(&width)) return;
string = string.substr(divider + 1); string = string.substr(divider + 1);
divider = string.find(_("-"));
if (divider == String::npos) return;
if (divider == 0) return;
int height; 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() { inline void getExternalRect(wxRect& rect_out, int& degrees_out) {
return getExternalRect(fn); getExternalRect(fn, rect_out, degrees_out);
} }
private: private:
+1 -1
View File
@@ -75,7 +75,7 @@ RealSize Rotation::trSizeToBB(const RealSize& size) const {
} }
RealRect Rotation::trRectToBB(const RealRect& r) 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 x = r.x * zoomX, y = r.y * zoomY;
double w = r.width * zoomX, h = r.height * zoomY; double w = r.width * zoomX, h = r.height * zoomY;
if (special_case_optimization && is_rad0(angle)) { if (special_case_optimization && is_rad0(angle)) {
+1 -1
View File
@@ -310,7 +310,7 @@ enum ControlID {
ID_SHARPEN, ID_SHARPEN,
ID_SHARPEN_AMOUNT, ID_SHARPEN_AMOUNT,
// Internal window // Internal window
ID_INTERNAL_SCALE, ID_IMPORT_ZOOM,
// Updates window // Updates window
ID_PACKAGE_LIST, ID_PACKAGE_LIST,
ID_KEEP, ID_KEEP,
@@ -107,6 +107,7 @@ $built_in_functions = array(
'get_card_from_uid' =>'', 'get_card_from_uid' =>'',
'get_card_styling' =>'', 'get_card_styling' =>'',
'get_card_stylesheet' =>'', 'get_card_stylesheet' =>'',
'get_card_export_settings' =>'',
'add_card_to_set' =>'', 'add_card_to_set' =>'',
// html export // html export
'to_html' =>'', 'to_html' =>'',