rewrite print logic once again

- move Front Face // Back Face card link to exe level
- print front faces and back faces together
- use printer dpi instead of stylesheet dpi
This commit is contained in:
GenevensiS
2025-11-30 10:37:59 +01:00
parent 22b3a3e8b2
commit 2d6c0d2aee
6 changed files with 206 additions and 142 deletions
+22 -4
View File
@@ -21,7 +21,7 @@
// ----------------------------------------------------------------------------- : Card
Card::Card()
// for files made before we saved these, set the time to 'yesterday', generate a uid
// for files made before we saved these, set the time to 'yesterday', generate a uid
: time_created (wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, time_modified(wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, uid(generate_uid())
@@ -80,7 +80,7 @@ void Card::link(const Set& set, const vector<CardP>& linked_cards, const String&
if (
this_linked_uid.empty() || // Not a reference
all_existing_uids.find(this_linked_uid) == all_existing_uids.end() // Reference to nonexistent card
) free_link_count++;
) free_link_count++;
}
if (free_link_count < linked_cards.size()) {
queue_message(MESSAGE_WARNING, _ERROR_("not enough free links"));
@@ -265,7 +265,7 @@ void Card::updateLink(String old_uid, String new_uid) {
}
}
vector<pair<CardP, String>> Card::getLinkedCards(const Set& set) {
vector<pair<CardP, String>> Card::getLinkedCards(const vector<CardP>& cards) {
unordered_map<String, String> links{
{ linked_card_1, linked_relation_1 },
{ linked_card_2, linked_relation_2 },
@@ -273,13 +273,31 @@ vector<pair<CardP, String>> Card::getLinkedCards(const Set& set) {
{ linked_card_4, linked_relation_4 }
};
vector<pair<CardP, String>> linked_cards;
FOR_EACH(other_card, set.cards) {
FOR_EACH(other_card, cards) {
if (links.find(other_card->uid) != links.end()) {
linked_cards.push_back(make_pair(other_card, links.at(other_card->uid)));
}
}
return linked_cards;
}
vector<pair<CardP, String>> Card::getLinkedCards(const Set& set) {
return getLinkedCards(set.cards);
}
CardP Card::getOtherFace(const vector<CardP>& cards) {
unordered_set<String> faces;
if (linked_relation_1 == _("Front Face") || linked_relation_1 == _("Back Face")) faces.emplace(linked_card_1);
if (linked_relation_2 == _("Front Face") || linked_relation_2 == _("Back Face")) faces.emplace(linked_card_2);
if (linked_relation_3 == _("Front Face") || linked_relation_3 == _("Back Face")) faces.emplace(linked_card_3);
if (linked_relation_4 == _("Front Face") || linked_relation_4 == _("Back Face")) faces.emplace(linked_card_4);
FOR_EACH(other_card, cards) {
if (faces.find(other_card->uid) != faces.end()) return other_card;
}
return nullptr;
}
CardP Card::getOtherFace(const Set& set) {
return getOtherFace(set.cards);
}
IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) {
return extra_data.get(stylesheet.name(), stylesheet.extra_card_fields);
+11 -8
View File
@@ -35,7 +35,7 @@ public:
Card();
/// Creates a card using the given game
Card(const Game& game);
/// The values on the fields of the card.
/** The indices should correspond to the card_fields in the Game */
IndexMap<FieldP, ValueP> data;
@@ -60,26 +60,26 @@ public:
StyleSheetP stylesheet;
/// Alternative options to use for this card, for this card's stylesheet
/** Optional; if not set use the styling data from the set.
* If stylesheet is set then contains data for the this->stylesheet, otherwise for set->stylesheet
*/
* If stylesheet is set then contains data for the this->stylesheet, otherwise for set->stylesheet
*/
IndexMap<FieldP,ValueP> styling_data;
/// Is the styling_data set?
bool has_styling;
/// Extra values for specitic stylesheets, indexed by stylesheet name
DelayedIndexMaps<FieldP,ValueP> extra_data;
/// Styling information for a particular stylesheet
IndexMap<FieldP, ValueP>& extraDataFor(const StyleSheet& stylesheet);
/// Keyword usage statistics
vector<pair<const Value*,const Keyword*>> keyword_usage;
/// Get the identification of this card, an identification is something like a name, title, etc.
/** May return "" */
String identification() const;
/// Does any field contains the given query string?
bool contains(QuickFilterPart const& query) const;
/// Link or unlink other cards to this card
void link(const Set& set, const vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation);
void link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation);
@@ -89,7 +89,10 @@ public:
void copyLink(const Set& set, String old_uid, String new_uid);
void updateLink(String old_uid, String new_uid);
vector<pair<CardP, String>> getLinkedCards(const vector<CardP>& cards);
vector<pair<CardP, String>> getLinkedCards(const Set& set);
CardP getOtherFace(const vector<CardP>& cards);
CardP getOtherFace(const Set& set);
/// Find a value in the data by name and type
template <typename T> T& value(const String& name) {
@@ -112,7 +115,7 @@ public:
}
throw InternalError(_("Expected a card field with name '")+name+_("'"));
}
DECLARE_REFLECTION();
};
+41 -40
View File
@@ -100,7 +100,7 @@ Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_
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);
@@ -124,7 +124,7 @@ Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_
globalDC.SelectObject(global_bitmap);
clearDC(globalDC, *wxWHITE_BRUSH);
int offset = 0;
FOR_EACH(bitmap, bitmaps) {
FOR_EACH(bitmap, bitmaps) {
offsets_out.push_back(offset);
globalDC.SetDeviceOrigin(offset, 0);
globalDC.DrawBitmap(bitmap, 0, 0);
@@ -138,57 +138,58 @@ Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_
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();
if (!settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_notes_export()) cardv["notes"] = "";
for(IndexMap<FieldP, ValueP>::iterator it = card_data.begin() ; it != card_data.end() ; ++it) {
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 = set->stylesheetFor(card).card_style.at(field->index);
if (style) {
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;
}
}
}
std::string rect = style->getExternalRectString(zoom, 0).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
}
}
}
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;
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();
Image img = bitmap.ConvertToImage();
String data = _("<mse-data-start>[");
for (int i = 0; i < cards.size(); ++i) {
if (i > 0) data += _(",");
for (int i = 0; i < cards.size(); ++i) {
if (i > 0) data += _(",");
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();
if (!settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_notes_export()) cardv["notes"] = "";
for(IndexMap<FieldP, ValueP>::iterator it = card_data.begin() ; it != card_data.end() ; ++it) {
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;
StyleSheetP stylesheet = set->stylesheetForP(card);
if (value && !value->filename.empty()) {
FieldP field = (*it)->fieldP;
StyleP style = stylesheet->card_style.at(field->index);
if (style) {
if (style) {
style->update(set->getContext(card));
std::string rect = style->getExternalRectString(scales[i], offsets[i]).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
}
}
}
std::string rect = style->getExternalRectString(scales[i], offsets[i]).ToStdString();
cardv_data[field->name.ToStdString()] = rect;
}
}
}
data += json_ugly_print(cardv);
}
}
data += _("]<mse-data-end>");
img.SetOption(wxIMAGE_OPTION_PNG_DESCRIPTION, data);
return img;
@@ -197,11 +198,11 @@ Image export_image(const SetP& set, const vector<CardP>& cards, bool scale_to_lo
void export_image(const SetP& set, const CardP& card, const String& filename) {
Image img = export_image(set, card);
img.SaveFile(filename); // can't use Bitmap::saveFile, it wants to know the file type
// but image.saveFile determines it automagicly
// but image.saveFile determines it automagicly
}
void export_image(const SetP& set, const vector<CardP>& cards,
const String& path, const String& filename_template, FilenameConflicts conflicts)
const String& path, const String& filename_template, FilenameConflicts conflicts)
{
wxBusyCursor busy;
// Script