|
|
|
@@ -50,19 +50,28 @@ void PrintJob::measure_cards() {
|
|
|
|
|
std::sort(sorted_layouts.begin(), sorted_layouts.end());
|
|
|
|
|
}
|
|
|
|
|
PrintJob::CardLayout PrintJob::measure_card(const CardP& card) {
|
|
|
|
|
// initial measure
|
|
|
|
|
const StyleSheet& stylesheet = set->stylesheetFor(card);
|
|
|
|
|
RealSize size_mm(stylesheet.card_width * 25.4 / stylesheet.card_dpi, stylesheet.card_height * 25.4 / stylesheet.card_dpi);
|
|
|
|
|
RealSize size_px(stylesheet.card_width, stylesheet.card_height);
|
|
|
|
|
// check if we need to rotate to align best to default card height
|
|
|
|
|
Radians rotation = 0.0;
|
|
|
|
|
bool rotated = abs(size_mm.width - default_size_mm.height) < abs(size_mm.height - default_size_mm.height); // try to align best to default card height
|
|
|
|
|
if (rotated) {
|
|
|
|
|
if (abs(size_mm.width - default_size_mm.height) < abs(size_mm.height - default_size_mm.height)) {
|
|
|
|
|
swap(size_mm.width, size_mm.height);
|
|
|
|
|
swap(size_px.width, size_px.height);
|
|
|
|
|
rotation = rad90;
|
|
|
|
|
}
|
|
|
|
|
if (abs(size_mm.width - default_size_mm.width) < threshold_size.width) size_mm.width = default_size_mm.width; // snap to default_size_mm if we are close
|
|
|
|
|
}
|
|
|
|
|
// snap to default_size_mm if we are close
|
|
|
|
|
if (abs(size_mm.width - default_size_mm.width) < threshold_size.width) size_mm.width = default_size_mm.width;
|
|
|
|
|
if (abs(size_mm.height - default_size_mm.height) < threshold_size.height) size_mm.height = default_size_mm.height;
|
|
|
|
|
return CardLayout(card, size_mm, size_px, rotation);
|
|
|
|
|
// add bleed
|
|
|
|
|
double bleed_size_px = 0.0;
|
|
|
|
|
if (!almost_equal(settings.print_bleed, 0.0)) {
|
|
|
|
|
bleed_size_px = stylesheet.card_dpi * settings.print_bleed / 25.4;
|
|
|
|
|
size_mm = RealSize(size_mm.width + 2 * settings.print_bleed, size_mm.height + 2 * settings.print_bleed);
|
|
|
|
|
size_px = RealSize(size_px.width + 2 * bleed_size_px, size_px.height + 2 * bleed_size_px);
|
|
|
|
|
}
|
|
|
|
|
return CardLayout(card, size_mm, size_px, bleed_size_px, rotation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrintJob::layout_cards() {
|
|
|
|
@@ -223,6 +232,7 @@ private:
|
|
|
|
|
|
|
|
|
|
/// Draw cutter lines in the page margins, corresponding to the edges of cards
|
|
|
|
|
void drawCutterLines(DC& dc, PrintJobP& job, int page);
|
|
|
|
|
static constexpr double CUTTER_LINE_SIZE_MM = 8.0;
|
|
|
|
|
/// Draw cards according to their CardLayout info
|
|
|
|
|
void drawCards (DC& dc, PrintJobP& job, int page);
|
|
|
|
|
void drawCard (DC& dc, PrintJob::CardLayout& card_layout);
|
|
|
|
@@ -263,8 +273,8 @@ bool CardsPrintout::OnPrintPage(int page) {
|
|
|
|
|
// scale factor (pixels per mm)
|
|
|
|
|
printer_px_per_mm = RealSize((double)page_width_px / page_width_mm, (double)page_height_px / page_height_mm);
|
|
|
|
|
// print the cards that belong on this page
|
|
|
|
|
drawCards(dc, job, page);
|
|
|
|
|
if (settings.print_cutter_lines != CUTTER_NONE) drawCutterLines(dc, job, page);
|
|
|
|
|
drawCards(dc, job, page);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -273,22 +283,37 @@ void CardsPrintout::drawCards(DC& dc, PrintJobP& job, int page) {
|
|
|
|
|
drawCard(dc, card_layout);
|
|
|
|
|
}
|
|
|
|
|
dc.SetDeviceOrigin(0, 0);
|
|
|
|
|
//dc.SetUserScale(printer_px_per_mm.width, printer_px_per_mm.height);
|
|
|
|
|
}
|
|
|
|
|
void CardsPrintout::drawCard(DC& dc, PrintJob::CardLayout& card_layout) {
|
|
|
|
|
// upscale to printer dpi
|
|
|
|
|
double card_px_per_mm_width = card_layout.size_px.width / card_layout.size_mm.width;
|
|
|
|
|
double scale = printer_px_per_mm.width / card_px_per_mm_width;
|
|
|
|
|
RealPoint scale(printer_px_per_mm.width * card_layout.size_mm.width / card_layout.size_px.width,
|
|
|
|
|
printer_px_per_mm.height * card_layout.size_mm.height / card_layout.size_px.height);
|
|
|
|
|
int bleed = lround(card_layout.bleed_size_px * scale.x);
|
|
|
|
|
int width = lround(card_layout.size_px.width * scale.x), height = lround(card_layout.size_px.height * scale.y);
|
|
|
|
|
// render card to its own dc
|
|
|
|
|
wxBitmap buffer(card_layout.size_px.width * scale, card_layout.size_px.height * scale, 32);
|
|
|
|
|
wxBitmap buffer(width, height, 32);
|
|
|
|
|
wxMemoryDC bufferDC;
|
|
|
|
|
bufferDC.SelectObject(buffer);
|
|
|
|
|
clearDC(bufferDC,*wxWHITE_BRUSH);
|
|
|
|
|
RotatedDC rdc(bufferDC, card_layout.rot, RealRect(0, 0, card_layout.size_px.width, card_layout.size_px.height), scale, QUALITY_AA, ROTATION_ATTACH_TOP_LEFT);
|
|
|
|
|
RotatedDC rdc(bufferDC, card_layout.rot, RealRect(0, 0, card_layout.size_px.width, card_layout.size_px.height), scale.x, QUALITY_AA, ROTATION_ATTACH_TOP_LEFT);
|
|
|
|
|
if (is_rad90(card_layout.rot)) bufferDC.SetDeviceOrigin(bleed, height - width - 1 - bleed); // I don't understand why this is needed
|
|
|
|
|
else bufferDC.SetDeviceOrigin(bleed, bleed);
|
|
|
|
|
viewer.setCard(card_layout.card);
|
|
|
|
|
viewer.draw(rdc, *wxWHITE);
|
|
|
|
|
bufferDC.SelectObject(wxNullBitmap);
|
|
|
|
|
viewer.draw(rdc, *wxWHITE);
|
|
|
|
|
bufferDC.SetDeviceOrigin(0,0);
|
|
|
|
|
/// add print bleed edge
|
|
|
|
|
if (bleed > 0) {
|
|
|
|
|
Image left_border = bufferDC.GetAsBitmap(&wxRect(bleed + 1, bleed, bleed, height - 2 * bleed)).ConvertToImage();
|
|
|
|
|
bufferDC.DrawBitmap(left_border.Mirror(true), 0, bleed);
|
|
|
|
|
Image right_border = bufferDC.GetAsBitmap(&wxRect(width - 2 * bleed - 1, bleed, bleed, height - 2 * bleed)).ConvertToImage();
|
|
|
|
|
bufferDC.DrawBitmap(right_border.Mirror(true), width - bleed - 1, bleed);
|
|
|
|
|
Image top_border = bufferDC.GetAsBitmap(&wxRect(0, bleed + 1, width, bleed)).ConvertToImage();
|
|
|
|
|
bufferDC.DrawBitmap(top_border.Mirror(false), 0, 0);
|
|
|
|
|
Image bottom_border = bufferDC.GetAsBitmap(&wxRect(0, height - 2 * bleed - 1, width, bleed)).ConvertToImage();
|
|
|
|
|
bufferDC.DrawBitmap(bottom_border.Mirror(false), 0, height - bleed - 1);
|
|
|
|
|
}
|
|
|
|
|
// render card dc to page dc
|
|
|
|
|
bufferDC.SelectObject(wxNullBitmap);
|
|
|
|
|
dc.SetDeviceOrigin(int(printer_px_per_mm.width * card_layout.pos.width), int(printer_px_per_mm.height * card_layout.pos.height));
|
|
|
|
|
dc.DrawBitmap(buffer, 0, 0);
|
|
|
|
|
}
|
|
|
|
@@ -297,82 +322,85 @@ void CardsPrintout::drawCutterLines(DC& dc, PrintJobP& job, int page) {
|
|
|
|
|
const vector<PrintJob::CardLayout>& page_layouts = job->page_layouts[page - 1];
|
|
|
|
|
const RealSize& page_margin = job->page_margins[page - 1];
|
|
|
|
|
int page_width, page_height;
|
|
|
|
|
GetPageSizeMM(&page_width, &page_height);
|
|
|
|
|
|
|
|
|
|
double vertical_line_size = min(10.0, page_margin.height - 3.0);
|
|
|
|
|
if (vertical_line_size > 0.0) {
|
|
|
|
|
for (int i = 0; i < page_layouts.size(); ++i) {
|
|
|
|
|
double left_line = page_layouts[i].pos.width;
|
|
|
|
|
double right_line = left_line + page_layouts[i].size_mm.width;
|
|
|
|
|
bool draw_left_line = true;
|
|
|
|
|
bool draw_right_line = true;
|
|
|
|
|
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
|
|
|
|
|
// check if another card is in the way of this cutter line
|
|
|
|
|
for (int j = 0; j < page_layouts.size(); ++j) {
|
|
|
|
|
if (i == j) continue;
|
|
|
|
|
double other_left_line = page_layouts[j].pos.width;
|
|
|
|
|
double other_right_line = other_left_line + page_layouts[j].size_mm.width;
|
|
|
|
|
if (draw_left_line && left_line - other_left_line > job->threshold_bottom && other_right_line - left_line > job->threshold_bottom) {
|
|
|
|
|
draw_left_line = false;
|
|
|
|
|
if (!draw_right_line) break;
|
|
|
|
|
}
|
|
|
|
|
if (draw_right_line && right_line - other_left_line > job->threshold_bottom && other_right_line - right_line > job->threshold_bottom) {
|
|
|
|
|
draw_right_line = false;
|
|
|
|
|
if (!draw_left_line) break;
|
|
|
|
|
}
|
|
|
|
|
GetPageSizeMM(&page_width, &page_height);
|
|
|
|
|
wxPen pen(wxColour(0, 0, 0), 2, wxPENSTYLE_SOLID);
|
|
|
|
|
pen.SetQuality(wxPEN_QUALITY_HIGH);
|
|
|
|
|
dc.SetPen(pen);
|
|
|
|
|
// vertical
|
|
|
|
|
int v_bleed = printer_px_per_mm.width * settings.print_bleed;
|
|
|
|
|
for (int i = 0; i < page_layouts.size(); ++i) {
|
|
|
|
|
double left_line = page_layouts[i].pos.width;
|
|
|
|
|
double right_line = left_line + page_layouts[i].size_mm.width;
|
|
|
|
|
bool draw_left_line = true;
|
|
|
|
|
bool draw_right_line = true;
|
|
|
|
|
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
|
|
|
|
|
// check if another card is in the way of this cutter line
|
|
|
|
|
for (int j = 0; j < page_layouts.size(); ++j) {
|
|
|
|
|
if (i == j) continue;
|
|
|
|
|
double other_left_line = page_layouts[j].pos.width;
|
|
|
|
|
double other_right_line = other_left_line + page_layouts[j].size_mm.width;
|
|
|
|
|
if (draw_left_line && left_line - other_left_line > job->threshold_bottom && other_right_line - left_line > job->threshold_bottom) {
|
|
|
|
|
draw_left_line = false;
|
|
|
|
|
if (!draw_right_line) break;
|
|
|
|
|
}
|
|
|
|
|
if (draw_right_line && right_line - other_left_line > job->threshold_bottom && other_right_line - right_line > job->threshold_bottom) {
|
|
|
|
|
draw_right_line = false;
|
|
|
|
|
if (!draw_left_line) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (draw_left_line) {
|
|
|
|
|
int left = printer_px_per_mm.width * left_line;
|
|
|
|
|
dc.DrawLine(wxPoint(left, 0.0), wxPoint(left, printer_px_per_mm.height * vertical_line_size));
|
|
|
|
|
dc.DrawLine(wxPoint(left, printer_px_per_mm.height * page_height), wxPoint(left, printer_px_per_mm.height * (page_height - vertical_line_size)));
|
|
|
|
|
}
|
|
|
|
|
if (draw_right_line) {
|
|
|
|
|
int right = printer_px_per_mm.width * right_line;
|
|
|
|
|
dc.DrawLine(wxPoint(right, 0.0), wxPoint(right, printer_px_per_mm.height * vertical_line_size));
|
|
|
|
|
dc.DrawLine(wxPoint(right, printer_px_per_mm.height * page_height), wxPoint(right, printer_px_per_mm.height * (page_height - vertical_line_size)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
queue_message(MESSAGE_WARNING, _ERROR_("v margin too small for cutter"));
|
|
|
|
|
if (draw_left_line) {
|
|
|
|
|
int left = printer_px_per_mm.width * left_line;
|
|
|
|
|
dc.DrawLine(wxPoint(left + v_bleed, 0.0),
|
|
|
|
|
wxPoint(left + v_bleed, printer_px_per_mm.height * CUTTER_LINE_SIZE_MM));
|
|
|
|
|
dc.DrawLine(wxPoint(left + v_bleed, printer_px_per_mm.height * page_height),
|
|
|
|
|
wxPoint(left + v_bleed, printer_px_per_mm.height * (page_height - CUTTER_LINE_SIZE_MM)));
|
|
|
|
|
}
|
|
|
|
|
if (draw_right_line) {
|
|
|
|
|
int right = printer_px_per_mm.width * right_line;
|
|
|
|
|
dc.DrawLine(wxPoint(right - v_bleed, 0.0),
|
|
|
|
|
wxPoint(right - v_bleed, printer_px_per_mm.height * CUTTER_LINE_SIZE_MM));
|
|
|
|
|
dc.DrawLine(wxPoint(right - v_bleed, printer_px_per_mm.height * page_height),
|
|
|
|
|
wxPoint(right - v_bleed, printer_px_per_mm.height * (page_height - CUTTER_LINE_SIZE_MM)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double horizontal_line_size = min(10.0, page_margin.width - 3.0);
|
|
|
|
|
if (horizontal_line_size > 0.0) {
|
|
|
|
|
for (int i = 0; i < page_layouts.size(); ++i) {
|
|
|
|
|
double top_line = page_layouts[i].pos.height;
|
|
|
|
|
double bottom_line = top_line + page_layouts[i].size_mm.height;
|
|
|
|
|
bool draw_top_line = true;
|
|
|
|
|
bool draw_bottom_line = true;
|
|
|
|
|
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
|
|
|
|
|
// check if another card is in the way of this cutter line
|
|
|
|
|
for (int j = 0; j < page_layouts.size(); ++j) {
|
|
|
|
|
if (i == j) continue;
|
|
|
|
|
double other_top_line = page_layouts[j].pos.height;
|
|
|
|
|
double other_bottom_line = other_top_line + page_layouts[j].size_mm.height;
|
|
|
|
|
if (draw_top_line && top_line - other_top_line > job->threshold_bottom && other_bottom_line - top_line > job->threshold_bottom) {
|
|
|
|
|
draw_top_line = false;
|
|
|
|
|
if (!draw_bottom_line) break;
|
|
|
|
|
}
|
|
|
|
|
if (draw_bottom_line && bottom_line - other_top_line > job->threshold_bottom && other_bottom_line - bottom_line > job->threshold_bottom) {
|
|
|
|
|
draw_bottom_line = false;
|
|
|
|
|
if (!draw_top_line) break;
|
|
|
|
|
}
|
|
|
|
|
// horizontal
|
|
|
|
|
int h_bleed = printer_px_per_mm.height * settings.print_bleed;
|
|
|
|
|
for (int i = 0; i < page_layouts.size(); ++i) {
|
|
|
|
|
double top_line = page_layouts[i].pos.height;
|
|
|
|
|
double bottom_line = top_line + page_layouts[i].size_mm.height;
|
|
|
|
|
bool draw_top_line = true;
|
|
|
|
|
bool draw_bottom_line = true;
|
|
|
|
|
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
|
|
|
|
|
// check if another card is in the way of this cutter line
|
|
|
|
|
for (int j = 0; j < page_layouts.size(); ++j) {
|
|
|
|
|
if (i == j) continue;
|
|
|
|
|
double other_top_line = page_layouts[j].pos.height;
|
|
|
|
|
double other_bottom_line = other_top_line + page_layouts[j].size_mm.height;
|
|
|
|
|
if (draw_top_line && top_line - other_top_line > job->threshold_bottom && other_bottom_line - top_line > job->threshold_bottom) {
|
|
|
|
|
draw_top_line = false;
|
|
|
|
|
if (!draw_bottom_line) break;
|
|
|
|
|
}
|
|
|
|
|
if (draw_bottom_line && bottom_line - other_top_line > job->threshold_bottom && other_bottom_line - bottom_line > job->threshold_bottom) {
|
|
|
|
|
draw_bottom_line = false;
|
|
|
|
|
if (!draw_top_line) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (draw_top_line) {
|
|
|
|
|
int top = printer_px_per_mm.height * top_line;
|
|
|
|
|
dc.DrawLine(wxPoint(0.0, top), wxPoint(printer_px_per_mm.width * horizontal_line_size, top));
|
|
|
|
|
dc.DrawLine(wxPoint(printer_px_per_mm.width * page_width, top), wxPoint(printer_px_per_mm.width * (page_width - horizontal_line_size), top));
|
|
|
|
|
}
|
|
|
|
|
if (draw_bottom_line) {
|
|
|
|
|
int bottom = printer_px_per_mm.height * bottom_line;
|
|
|
|
|
dc.DrawLine(wxPoint(0.0, bottom), wxPoint(printer_px_per_mm.width * horizontal_line_size, bottom));
|
|
|
|
|
dc.DrawLine(wxPoint(printer_px_per_mm.width * page_width, bottom), wxPoint(printer_px_per_mm.width * (page_width - horizontal_line_size), bottom));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
queue_message(MESSAGE_WARNING, _ERROR_("h margin too small for cutter"));
|
|
|
|
|
if (draw_top_line) {
|
|
|
|
|
int top = printer_px_per_mm.height * top_line;
|
|
|
|
|
dc.DrawLine(wxPoint(0.0, top + h_bleed),
|
|
|
|
|
wxPoint(printer_px_per_mm.width * CUTTER_LINE_SIZE_MM, top + h_bleed));
|
|
|
|
|
dc.DrawLine(wxPoint(printer_px_per_mm.width * page_width, top + h_bleed),
|
|
|
|
|
wxPoint(printer_px_per_mm.width * (page_width - CUTTER_LINE_SIZE_MM), top + h_bleed));
|
|
|
|
|
}
|
|
|
|
|
if (draw_bottom_line) {
|
|
|
|
|
int bottom = printer_px_per_mm.height * bottom_line;
|
|
|
|
|
dc.DrawLine(wxPoint(0.0, bottom - h_bleed),
|
|
|
|
|
wxPoint(printer_px_per_mm.width * CUTTER_LINE_SIZE_MM, bottom - h_bleed));
|
|
|
|
|
dc.DrawLine(wxPoint(printer_px_per_mm.width * page_width, bottom - h_bleed),
|
|
|
|
|
wxPoint(printer_px_per_mm.width * (page_width - CUTTER_LINE_SIZE_MM), bottom - h_bleed));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -382,11 +410,11 @@ PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelect
|
|
|
|
|
// Let the user choose cards and spacing
|
|
|
|
|
// controls
|
|
|
|
|
ExportWindowBase wnd(parent, _TITLE_("select cards print"), set, choices);
|
|
|
|
|
wxFloatingPointValidator<float> validator(2, NULL, wxNUM_VAL_ZERO_AS_BLANK);
|
|
|
|
|
validator.SetRange(0, 100);
|
|
|
|
|
wxTextCtrl* space = new wxTextCtrl(&wnd, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0L, validator);
|
|
|
|
|
space->SetValue(wxString::Format(wxT("%lf"), settings.print_spacing));
|
|
|
|
|
wxChoice* cutter = new wxChoice(&wnd, wxID_ANY);
|
|
|
|
|
wxFloatingPointValidator<double> validator(2, NULL);
|
|
|
|
|
validator.SetRange(0.0, 10.0);
|
|
|
|
|
wxTextCtrl* space = new wxTextCtrl(&wnd, wxID_ANY, wxString::Format(wxT("%.2f"), settings.print_spacing), wxDefaultPosition, wxDefaultSize, 0L, validator);
|
|
|
|
|
wxTextCtrl* bleed = new wxTextCtrl(&wnd, wxID_ANY, wxString::Format(wxT("%.2f"), settings.print_bleed), wxDefaultPosition, wxDefaultSize, 0L, validator);
|
|
|
|
|
wxChoice* cutter = new wxChoice(&wnd, wxID_ANY);
|
|
|
|
|
cutter->Clear();
|
|
|
|
|
cutter->Append(_LABEL_("cutter lines all"));
|
|
|
|
|
cutter->Append(_LABEL_("cutter lines no intersect"));
|
|
|
|
@@ -400,6 +428,8 @@ PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelect
|
|
|
|
|
wxSizer* s4 = new wxStaticBoxSizer(wxVERTICAL, &wnd, _TITLE_("settings"));
|
|
|
|
|
s4->Add(new wxStaticText(&wnd, -1, _LABEL_("spacing print")), 0, wxALL, 8);
|
|
|
|
|
s4->Add(space, 0, wxALL & ~wxTOP, 8);
|
|
|
|
|
s4->Add(new wxStaticText(&wnd, -1, _LABEL_("bleed print")), 0, wxALL, 8);
|
|
|
|
|
s4->Add(bleed, 0, wxALL & ~wxTOP, 8);
|
|
|
|
|
s4->Add(new wxStaticText(&wnd, -1, _LABEL_("cutter lines print")), 0, wxALL, 8);
|
|
|
|
|
s4->Add(cutter, 0, wxALL & ~wxTOP, 8);
|
|
|
|
|
s2->Add(s4, 1, wxEXPAND | (wxALL & ~wxLEFT), 8);
|
|
|
|
@@ -416,6 +446,9 @@ PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelect
|
|
|
|
|
String spacing = space->GetValue();
|
|
|
|
|
if (spacing.empty()) spacing = _("0");
|
|
|
|
|
spacing.ToDouble(&settings.print_spacing);
|
|
|
|
|
String bleeding = bleed->GetValue();
|
|
|
|
|
if (bleeding.empty()) bleeding = _("0");
|
|
|
|
|
bleeding.ToDouble(&settings.print_bleed);
|
|
|
|
|
settings.print_cutter_lines = (CutterLinesType)cutter->GetSelection();
|
|
|
|
|
PrintJobP job = make_intrusive<PrintJob>(set, wnd.getSelection());
|
|
|
|
|
return job;
|
|
|
|
|