//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) Twan van Laarhoven and the other MSE developers | //| License: GNU General Public License 2 or later (see file COPYING) | //+----------------------------------------------------------------------------+ // ----------------------------------------------------------------------------- : Includes #include #include #include #include #include #include #include #include #include DECLARE_POINTER_TYPE(PageLayout); // ----------------------------------------------------------------------------- : Layout PageLayout::PageLayout() : margin_left(0), margin_right(0), margin_top(0), margin_bottom(0) , rows(0), cols(0), card_landscape(false) {} void PageLayout::init(const StyleSheet& stylesheet, PageLayoutType type, const RealSize& page_size) { this->page_size = page_size; margin_left = margin_right = margin_top = margin_bottom = 0; card_size.width = stylesheet.card_width * 25.4 / stylesheet.card_dpi; card_size.height = stylesheet.card_height * 25.4 / stylesheet.card_dpi; card_landscape = card_size.width > card_size.height; cols = int(floor(page_size.width / card_size.width)); rows = int(floor(page_size.height / card_size.height)); // spacing double hspace = (page_size.width - (cols * card_size.width )); double vspace = (page_size.height - (rows * card_size.height)); if (type == LAYOUT_NO_SPACE) { // no space between cards card_spacing.width = card_spacing.height = 0; margin_left = margin_right = hspace / 2; margin_top = vspace * 1./3; margin_bottom = vspace * 2./3; // most printers have more margin at the bottom } else { // distribute whitespace evenly margin_left = margin_right = card_spacing.width = hspace / (cols + 1); margin_top = margin_bottom = card_spacing.height = vspace / (rows + 1); } } // ----------------------------------------------------------------------------- : Printout /// A printout object specifying how to print a specified set of cards class CardsPrintout : public wxPrintout { public: CardsPrintout(PrintJobP const& job); /// Number of pages, and something else I don't understand... void GetPageInfo(int* pageMin, int* pageMax, int* pageFrom, int* pageTo) override; /// Again, 'number of pages', strange wx interface bool HasPage(int page) override; /// Determine the layout void OnPreparePrinting() override; /// Print a page bool OnPrintPage(int page) override; private: PrintJobP job; ///< Cards to print DataViewer viewer; double scale_x, scale_y; // priter pixel per mm int pageCount() { return job->num_pages(); } /// Draw a card, that is card_nr on this page, find the postion by asking the layout void drawCard(DC& dc, const CardP& card, int card_nr); }; CardsPrintout::CardsPrintout(PrintJobP const& job) : job(job) { viewer.setSet(job->set); } void CardsPrintout::GetPageInfo(int* page_min, int* page_max, int* page_from, int* page_to) { *page_from = *page_min = 1; *page_to = *page_max = pageCount(); } bool CardsPrintout::HasPage(int page) { return page <= pageCount(); // page number is 1 based } void CardsPrintout::OnPreparePrinting() { if (job->layout.empty()) { int pw_mm, ph_mm; GetPageSizeMM(&pw_mm, &ph_mm); job->layout.init(*job->set->stylesheet, job->layout_type, RealSize(pw_mm, ph_mm)); } } bool CardsPrintout::OnPrintPage(int page) { DC& dc = *GetDC(); // scale factors int pw_mm, ph_mm; GetPageSizeMM(&pw_mm, &ph_mm); int pw_px, ph_px; dc.GetSize(&pw_px, &ph_px); scale_x = (double)pw_px / pw_mm; scale_y = (double)ph_px / ph_mm; // print the cards that belong on this page int start = (page - 1) * job->layout.cards_per_page(); int end = min((int)job->cards.size(), start + job->layout.cards_per_page()); for (int i = start ; i < end ; ++i) { drawCard(dc, job->cards.at(i), i - start); } return true; } void CardsPrintout::drawCard(DC& dc, const CardP& card, int card_nr) { // determine position int col = card_nr % job->layout.cols; int row = card_nr / job->layout.cols; RealPoint pos( job->layout.margin_left + (job->layout.card_size.width + job->layout.card_spacing.width) * col , job->layout.margin_top + (job->layout.card_size.height + job->layout.card_spacing.height) * row); // determine rotation const StyleSheet& stylesheet = job->set->stylesheetFor(card); Radians rotation = 0; if ((stylesheet.card_width > stylesheet.card_height) != job->layout.card_landscape) { rotation = rad90; } /* // size of this particular card (in mm) RealSize card_size( stylesheet.card_width * 25.4 / stylesheet.card_dpi , stylesheet.card_height * 25.4 / stylesheet.card_dpi); if (is_rad90(rotation)) swap(card_size.width, card_size.height); // adjust card size, to center card in the available space (from job->layout.card_size)? // TODO: deal with different sized cards in general */ // create buffers int w = int(stylesheet.card_width), h = int(stylesheet.card_height); // in pixels if (is_rad90(rotation)) swap(w,h); // Draw using text buffer double zoom = IsPreview() ? 1 : 4; wxBitmap buffer(w*zoom,h*zoom,32); wxMemoryDC bufferDC; bufferDC.SelectObject(buffer); clearDC(bufferDC,*wxWHITE_BRUSH); RotatedDC rdc(bufferDC, rotation, stylesheet.getCardRect(), zoom, QUALITY_AA, ROTATION_ATTACH_TOP_LEFT); // render card to dc viewer.setCard(card); viewer.draw(rdc, *wxWHITE); // render buffer to device double px_per_mm = zoom * stylesheet.card_dpi / 25.4; dc.SetUserScale(scale_x / px_per_mm, scale_y / px_per_mm); dc.SetDeviceOrigin(int(scale_x * pos.x), int(scale_y * pos.y)); bufferDC.SelectObject(wxNullBitmap); dc.DrawBitmap(buffer, 0, 0); } // ----------------------------------------------------------------------------- : PrintWindow PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelectionChoices& choices) { // Let the user choose cards // controls ExportWindowBase wnd(parent, _TITLE_("select cards"), set, choices); wxCheckBox* space = new wxCheckBox(&wnd, wxID_ANY, L"Put space between cards"); space->SetValue(settings.print_layout); // layout wxSizer* s = new wxBoxSizer(wxVERTICAL); wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL); wxSizer* s3 = wnd.Create(); s2->Add(s3, 1, wxEXPAND | wxALL, 8); wxSizer* s4 = new wxStaticBoxSizer(wxVERTICAL, &wnd, L"Settings"); s4->Add(space, 1, wxALL | wxALIGN_TOP, 8); s2->Add(s4, 1, wxEXPAND | (wxALL & ~wxLEFT), 8); s->Add(s2, 1, wxEXPAND); s->Add(wnd.CreateButtonSizer(wxOK | wxCANCEL) , 0, wxEXPAND | wxALL, 8); s->SetSizeHints(&wnd); wnd.SetSizer(s); wnd.SetMinSize(wxSize(300,-1)); // show window if (wnd.ShowModal() != wxID_OK) { return PrintJobP(); // cancel } else { // make print job PrintJobP job = make_intrusive(set); job->layout_type = settings.print_layout = space->GetValue() ? LAYOUT_EQUAL_SPACE : LAYOUT_NO_SPACE; job->cards = wnd.getSelection(); return job; } } void print_preview(Window* parent, const PrintJobP& job) { if (!job) return; // Show the print preview wxPreviewFrame* frame = new wxPreviewFrame( new wxPrintPreview( new CardsPrintout(job), new CardsPrintout(job) ), parent, _TITLE_("print preview")); frame->Initialize(); frame->Maximize(true); frame->Show(); } void print_set(Window* parent, const PrintJobP& job) { if (!job) return; // Print the cards wxPrinter p; CardsPrintout pout(job); p.Print(parent, &pout, true); } void print_preview(Window* parent, const SetP& set, const ExportCardSelectionChoices& choices) { print_preview(parent, make_print_job(parent, set, choices)); } void print_set(Window* parent, const SetP& set, const ExportCardSelectionChoices& choices) { print_set(parent, make_print_job(parent, set, choices)); }