///////////////////////////////////////////////////////////////////////////// // Name: src/osx/core/printmac.cpp // Purpose: wxMacPrinter framework // Author: Julian Smart, Stefan Csomor // Modified by: // Created: 04/01/98 // Copyright: (c) Julian Smart, Stefan Csomor // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #if wxUSE_PRINTING_ARCHITECTURE #ifndef WX_PRECOMP #include "wx/utils.h" #include "wx/dc.h" #include "wx/app.h" #include "wx/msgdlg.h" #include "wx/dcprint.h" #include "wx/math.h" #endif #include "wx/osx/private.h" #include "wx/osx/printmac.h" #include "wx/osx/private/print.h" #include "wx/printdlg.h" #include "wx/paper.h" #include "wx/display.h" #include "wx/osx/printdlg.h" #include // Helper function to get the printer "name": actually we use its unique ID // instead of the human-readable name which is not guaranteed to be unique. static wxString MacGetPrinterName(PMPrinter printer) { CFStringRef printerId = PMPrinterGetID(printer); CFRetain(printerId); return wxCFStringRef(printerId).AsString(); } // // move to print_osx.cpp // static int ResolutionSorter(const void *e1, const void *e2) { const PMResolution *res1 = (const PMResolution *)e1; const PMResolution *res2 = (const PMResolution *)e2; const double area1 = res1->hRes * res1->vRes; const double area2 = res2->hRes * res2->vRes; if (area1 < area2) return -1; else if (area1 > area2) return 1; else return 0; } static PMResolution *GetSupportedResolutions(PMPrinter printer, UInt32 *count) { PMResolution res, *resolutions = NULL; OSStatus status = PMPrinterGetPrinterResolutionCount(printer, count); if (status == noErr) { resolutions = (PMResolution *)malloc(sizeof(PMResolution) * (*count)); UInt32 realCount = 0; for (UInt32 i = 0; i < *count; i++) { if (PMPrinterGetIndexedPrinterResolution(printer, i + 1, &res) == noErr) resolutions[realCount++] = res; } qsort(resolutions, realCount, sizeof(PMResolution), ResolutionSorter); *count = realCount; } if ((*count == 0) && (resolutions)) { free(resolutions); resolutions = NULL; } return resolutions; } wxIMPLEMENT_DYNAMIC_CLASS(wxOSXPrintData, wxPrintNativeDataBase); bool wxOSXPrintData::IsOk() const { return (m_macPageFormat != kPMNoPageFormat) && (m_macPrintSettings != kPMNoPrintSettings) && (m_macPrintSession != kPMNoReference); } wxOSXPrintData::wxOSXPrintData() { m_macPageFormat = kPMNoPageFormat; m_macPrintSettings = kPMNoPrintSettings; m_macPrintSession = kPMNoReference ; m_macPaper = kPMNoData; } wxOSXPrintData::~wxOSXPrintData() { } void wxOSXPrintData::UpdateFromPMState() { } void wxOSXPrintData::UpdateToPMState() { } void wxOSXPrintData::TransferPrinterNameFrom( const wxPrintData &data ) { CFArrayRef printerList; if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) { CFIndex index, count; PMPrinter printer = NULL; count = CFArrayGetCount(printerList); for (index = 0; index < count; index++) { printer = static_cast(const_cast(CFArrayGetValueAtIndex(printerList, index))); if ((data.GetPrinterName().empty()) && (PMPrinterIsDefault(printer))) break; if (data.GetPrinterName() == MacGetPrinterName(printer)) break; } if (index < count) PMSessionSetCurrentPMPrinter(m_macPrintSession, printer); CFRelease(printerList); } } void wxOSXPrintData::TransferPaperInfoFrom( const wxPrintData &data ) { PMPrinter printer; PMSessionGetCurrentPrinter(m_macPrintSession, &printer); wxSize papersize = wxDefaultSize; const wxPaperSize paperId = data.GetPaperId(); if ( paperId != wxPAPER_NONE && wxThePrintPaperDatabase ) { papersize = wxThePrintPaperDatabase->GetSize(paperId); if ( papersize != wxDefaultSize ) { papersize.x /= 10; papersize.y /= 10; } } else { papersize = data.GetPaperSize(); } if ( papersize != wxDefaultSize ) { papersize.x = (wxInt32) (papersize.x * mm2pt); papersize.y = (wxInt32) (papersize.y * mm2pt); double height, width; PMPaperGetHeight(m_macPaper, &height); PMPaperGetWidth(m_macPaper, &width); if ( fabs( width - papersize.x ) >= 5 || fabs( height - papersize.y ) >= 5 ) { // we have to change the current paper CFArrayRef paperlist = 0 ; if ( PMPrinterGetPaperList( printer, &paperlist ) == noErr ) { PMPaper bestPaper = kPMNoData ; CFIndex top = CFArrayGetCount(paperlist); for ( CFIndex i = 0 ; i < top ; ++ i ) { PMPaper paper = static_cast(const_cast(CFArrayGetValueAtIndex(paperlist, i))); PMPaperGetHeight(paper, &height); PMPaperGetWidth(paper, &width); if ( fabs( width - papersize.x ) < 5 && fabs( height - papersize.y ) < 5 ) { // TODO test for duplicate hits and use additional // criteria for best match bestPaper = paper; } } PMPaper paper = kPMNoData; if ( bestPaper == kPMNoData ) { const PMPaperMargins margins = { 0.0, 0.0, 0.0, 0.0 }; wxString id, name(wxT("Custom paper")); id.Printf(wxT("wxPaperCustom%dx%d"), papersize.x, papersize.y); if ( PMPaperCreateCustom ( printer, wxCFStringRef(id, wxFont::GetDefaultEncoding()), wxCFStringRef(name, wxFont::GetDefaultEncoding()), papersize.x, papersize.y, &margins, &paper ) ) { bestPaper = paper; } } if ( bestPaper != kPMNoData ) { PMPageFormat pageFormat; PMCreatePageFormatWithPMPaper(&pageFormat, bestPaper); PMCopyPageFormat( pageFormat, m_macPageFormat ); PMRelease(pageFormat); PMGetPageFormatPaper(m_macPageFormat, &m_macPaper); } PMRelease(paper); } } } PMSetCopies( m_macPrintSettings , data.GetNoCopies() , false ) ; PMSetCollate(m_macPrintSettings, data.GetCollate()); if ( data.IsOrientationReversed() ) PMSetOrientation( m_macPageFormat , ( data.GetOrientation() == wxLANDSCAPE ) ? kPMReverseLandscape : kPMReversePortrait , false ) ; else PMSetOrientation( m_macPageFormat , ( data.GetOrientation() == wxLANDSCAPE ) ? kPMLandscape : kPMPortrait , false ) ; PMDuplexMode mode = 0 ; switch( data.GetDuplex() ) { case wxDUPLEX_HORIZONTAL : mode = kPMDuplexNoTumble ; break ; case wxDUPLEX_VERTICAL : mode = kPMDuplexTumble ; break ; case wxDUPLEX_SIMPLEX : default : mode = kPMDuplexNone ; break ; } PMSetDuplex( m_macPrintSettings, mode ) ; if ( data.IsOrientationReversed() ) PMSetOrientation( m_macPageFormat , ( data.GetOrientation() == wxLANDSCAPE ) ? kPMReverseLandscape : kPMReversePortrait , false ) ; else PMSetOrientation( m_macPageFormat , ( data.GetOrientation() == wxLANDSCAPE ) ? kPMLandscape : kPMPortrait , false ) ; } void wxOSXPrintData::TransferResolutionFrom( const wxPrintData &data ) { PMPrinter printer; PMSessionGetCurrentPrinter(m_macPrintSession, &printer); UInt32 resCount; PMResolution *resolutions = GetSupportedResolutions(printer, &resCount); if (resolutions) { wxPrintQuality quality = data.GetQuality(); if (quality >= 0) quality = wxPRINT_QUALITY_HIGH; PMResolution res = resolutions[((quality - wxPRINT_QUALITY_DRAFT) * (resCount - 1)) / 3]; PMPrinterSetOutputResolution(printer, m_macPrintSettings, &res); free(resolutions); } } bool wxOSXPrintData::TransferFrom( const wxPrintData &data ) { TransferPrinterNameFrom(data); TransferPaperInfoFrom(data); TransferResolutionFrom(data); // after setting the new resolution the format has to be updated, otherwise the page rect remains // at the 'old' scaling PMSessionValidatePageFormat(m_macPrintSession, m_macPageFormat, kPMDontWantBoolean); PMSessionValidatePrintSettings(m_macPrintSession, m_macPrintSettings, kPMDontWantBoolean); #if wxOSX_USE_COCOA UpdateFromPMState(); #endif return true ; } void wxOSXPrintData::TransferPrinterNameTo( wxPrintData &data ) { PMPrinter printer ; PMSessionGetCurrentPrinter( m_macPrintSession, &printer ); if (PMPrinterIsDefault(printer)) data.SetPrinterName(wxEmptyString); else data.SetPrinterName(MacGetPrinterName(printer)); } void wxOSXPrintData::TransferPaperInfoTo( wxPrintData &data ) { PMGetPageFormatPaper(m_macPageFormat, &m_macPaper); PMPrinter printer ; PMSessionGetCurrentPrinter( m_macPrintSession, &printer ); OSStatus err = noErr ; UInt32 copies ; err = PMGetCopies( m_macPrintSettings , &copies ) ; if ( err == noErr ) data.SetNoCopies( copies ) ; PMOrientation orientation ; err = PMGetOrientation( m_macPageFormat , &orientation ) ; if ( err == noErr ) { if ( orientation == kPMPortrait || orientation == kPMReversePortrait ) { data.SetOrientation( wxPORTRAIT ); data.SetOrientationReversed( orientation == kPMReversePortrait ); } else { data.SetOrientation( wxLANDSCAPE ); data.SetOrientationReversed( orientation == kPMReverseLandscape ); } } Boolean collate; if (PMGetCollate(m_macPrintSettings, &collate) == noErr) data.SetCollate(collate); PMDuplexMode mode = 0 ; PMGetDuplex( m_macPrintSettings, &mode ) ; switch( mode ) { case kPMDuplexNoTumble : data.SetDuplex(wxDUPLEX_HORIZONTAL); break ; case kPMDuplexTumble : data.SetDuplex(wxDUPLEX_VERTICAL); break ; case kPMDuplexNone : default : data.SetDuplex(wxDUPLEX_SIMPLEX); break ; } double height, width; PMPaperGetHeight(m_macPaper, &height); PMPaperGetWidth(m_macPaper, &width); wxSize sz((int)(width * pt2mm + 0.5 ) , (int)(height * pt2mm + 0.5 )); data.SetPaperSize(sz); wxPaperSize id = wxThePrintPaperDatabase->GetSize(wxSize(sz.x* 10, sz.y * 10)); if (id != wxPAPER_NONE) { data.SetPaperId(id); } } void wxOSXPrintData::TransferResolutionTo( wxPrintData &data ) { PMPrinter printer ; PMSessionGetCurrentPrinter( m_macPrintSession, &printer ); /* assume high quality, will change below if we are able to */ data.SetQuality(wxPRINT_QUALITY_HIGH); PMResolution *resolutions; UInt32 resCount; resolutions = GetSupportedResolutions(printer, &resCount); if (resolutions) { bool valid = false; PMResolution res; if ( PMPrinterGetOutputResolution(printer, m_macPrintSettings, &res) == noErr ) valid = true; if ( valid ) { UInt32 i; for (i = 0; i < resCount; i++) { if (resolutions[i].hRes == res.hRes && resolutions[i].vRes == res.vRes) break; } if (i < resCount) data.SetQuality((((i + 1) * 3) / resCount) + wxPRINT_QUALITY_DRAFT); } free(resolutions); } } bool wxOSXPrintData::TransferTo( wxPrintData &data ) { #if wxOSX_USE_COCOA UpdateToPMState(); #endif TransferPrinterNameTo(data); TransferPaperInfoTo(data); TransferResolutionTo(data); return true ; } void wxOSXPrintData::TransferFrom( const wxPageSetupDialogData *WXUNUSED(data) ) { // should we setup the page rect here ? // since MacOS sometimes has two same paper rects with different // page rects we could make it roundtrip safe perhaps } void wxOSXPrintData::TransferTo( wxPageSetupDialogData* data ) { #if wxOSX_USE_COCOA UpdateToPMState(); #endif PMRect rPaper; OSStatus err = PMGetUnadjustedPaperRect(m_macPageFormat, &rPaper); if ( err == noErr ) { wxSize sz((int)(( rPaper.right - rPaper.left ) * pt2mm + 0.5 ) , (int)(( rPaper.bottom - rPaper.top ) * pt2mm + 0.5 )); data->SetPaperSize(sz); PMRect rPage ; err = PMGetUnadjustedPageRect(m_macPageFormat , &rPage ) ; if ( err == noErr ) { data->SetMinMarginTopLeft( wxPoint ( (int)(((double) rPage.left - rPaper.left ) * pt2mm) , (int)(((double) rPage.top - rPaper.top ) * pt2mm) ) ) ; data->SetMinMarginBottomRight( wxPoint ( (wxCoord)(((double) rPaper.right - rPage.right ) * pt2mm), (wxCoord)(((double) rPaper.bottom - rPage.bottom ) * pt2mm)) ) ; if ( data->GetMarginTopLeft().x < data->GetMinMarginTopLeft().x ) data->SetMarginTopLeft( wxPoint( data->GetMinMarginTopLeft().x , data->GetMarginTopLeft().y ) ) ; if ( data->GetMarginBottomRight().x < data->GetMinMarginBottomRight().x ) data->SetMarginBottomRight( wxPoint( data->GetMinMarginBottomRight().x , data->GetMarginBottomRight().y ) ); if ( data->GetMarginTopLeft().y < data->GetMinMarginTopLeft().y ) data->SetMarginTopLeft( wxPoint( data->GetMarginTopLeft().x , data->GetMinMarginTopLeft().y ) ); if ( data->GetMarginBottomRight().y < data->GetMinMarginBottomRight().y ) data->SetMarginBottomRight( wxPoint( data->GetMarginBottomRight().x , data->GetMinMarginBottomRight().y) ); } } } void wxOSXPrintData::TransferTo( wxPrintDialogData* data ) { #if wxOSX_USE_COCOA UpdateToPMState(); #endif UInt32 minPage , maxPage ; PMGetPageRange( m_macPrintSettings , &minPage , &maxPage ) ; data->SetMinPage( minPage ) ; data->SetMaxPage( maxPage ) ; UInt32 copies ; PMGetCopies( m_macPrintSettings , &copies ) ; data->SetNoCopies( copies ) ; UInt32 from , to ; PMGetFirstPage( m_macPrintSettings , &from ) ; PMGetLastPage( m_macPrintSettings , &to ) ; if ( to >= 0x7FFFFFFF ) // due to an OS Bug we don't get back kPMPrintAllPages { data->SetAllPages( true ) ; // This means all pages, more or less data->SetFromPage(1); data->SetToPage(9999); } else { data->SetFromPage( from ) ; data->SetToPage( to ) ; data->SetAllPages( false ); } } void wxOSXPrintData::TransferFrom( const wxPrintDialogData* data ) { // Respect the value of m_printAllPages if ( data->GetAllPages() ) PMSetPageRange( m_macPrintSettings , data->GetMinPage() , (UInt32) kPMPrintAllPages ) ; else PMSetPageRange( m_macPrintSettings , data->GetMinPage() , data->GetMaxPage() ) ; PMSetCopies( m_macPrintSettings , data->GetNoCopies() , false ) ; PMSetFirstPage( m_macPrintSettings , data->GetFromPage() , false ) ; if (data->GetAllPages() || data->GetFromPage() == 0) PMSetLastPage( m_macPrintSettings , (UInt32) kPMPrintAllPages, true ) ; else PMSetLastPage( m_macPrintSettings , (UInt32) data->GetToPage() , false ) ; #if wxOSX_USE_COCOA UpdateFromPMState(); #endif } wxPrintNativeDataBase* wxOSXCreatePrintData() { #if wxOSX_USE_COCOA return new wxOSXCocoaPrintData(); #else return NULL; #endif } /* * Printer */ wxIMPLEMENT_DYNAMIC_CLASS(wxMacPrinter, wxPrinterBase); wxMacPrinter::wxMacPrinter(wxPrintDialogData *data): wxPrinterBase(data) { } wxMacPrinter::~wxMacPrinter() { } bool wxMacPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt) { sm_abortIt = false; sm_abortWindow = NULL; if (!printout) { sm_lastError = wxPRINTER_ERROR; return false; } if (m_printDialogData.GetMinPage() < 1) m_printDialogData.SetMinPage(1); if (m_printDialogData.GetMaxPage() < 1) m_printDialogData.SetMaxPage(9999); // Create a suitable device context wxPrinterDC *dc = NULL; if (prompt) { wxMacPrintDialog dialog(parent, & m_printDialogData); if (dialog.ShowModal() == wxID_OK) { dc = wxDynamicCast(dialog.GetPrintDC(), wxPrinterDC); wxASSERT(dc); m_printDialogData = dialog.GetPrintDialogData(); } } else { dc = new wxPrinterDC( m_printDialogData.GetPrintData() ) ; } // May have pressed cancel. if (!dc || !dc->IsOk()) { delete dc; return false; } PMResolution res; PMPrinter printer; wxOSXPrintData* nativeData = (wxOSXPrintData*) (m_printDialogData.GetPrintData().GetNativeData()); if (PMSessionGetCurrentPrinter(nativeData->GetPrintSession(), &printer) == noErr) { if (PMPrinterGetOutputResolution( printer, nativeData->GetPrintSettings(), &res) == -9589 /* kPMKeyNotFound */ ) { res.hRes = res.vRes = 300; } } else { // fallback res.hRes = res.vRes = 300; } printout->SetPPIPrinter(int(res.hRes), int(res.vRes)); // Set printout parameters printout->SetUp(*dc); // Create an abort window wxBeginBusyCursor(); printout->OnPreparePrinting(); // Get some parameters from the printout, if defined int fromPage, toPage; int minPage, maxPage; printout->GetPageInfo(&minPage, &maxPage, &fromPage, &toPage); if (maxPage == 0) { sm_lastError = wxPRINTER_ERROR; return false; } // Only set min and max, because from and to will be // set by the user if prompted for the print dialog above m_printDialogData.SetMinPage(minPage); m_printDialogData.SetMaxPage(maxPage); // Set from and to pages if bypassing the print dialog if ( !prompt ) { m_printDialogData.SetFromPage(fromPage); if( m_printDialogData.GetAllPages() ) m_printDialogData.SetToPage(maxPage); else m_printDialogData.SetToPage(toPage); } printout->OnBeginPrinting(); bool keepGoing = true; if (!printout->OnBeginDocument(m_printDialogData.GetFromPage(), m_printDialogData.GetToPage())) { wxEndBusyCursor(); wxMessageBox(wxT("Could not start printing."), wxT("Print Error"), wxOK, parent); } int pn; for (pn = m_printDialogData.GetFromPage(); keepGoing && (pn <= m_printDialogData.GetToPage()) && printout->HasPage(pn); pn++) { if (sm_abortIt) { break; } else { dc->StartPage(); keepGoing = printout->OnPrintPage(pn); dc->EndPage(); } } printout->OnEndDocument(); printout->OnEndPrinting(); if (sm_abortWindow) { sm_abortWindow->Show(false); wxDELETE(sm_abortWindow); } wxEndBusyCursor(); delete dc; return true; } wxDC* wxMacPrinter::PrintDialog(wxWindow *parent) { wxDC* dc = NULL; wxPrintDialog dialog(parent, & m_printDialogData); int ret = dialog.ShowModal(); if (ret == wxID_OK) { dc = dialog.GetPrintDC(); m_printDialogData = dialog.GetPrintDialogData(); } return dc; } bool wxMacPrinter::Setup(wxWindow *WXUNUSED(parent)) { #if 0 wxPrintDialog dialog(parent, & m_printDialogData); dialog.GetPrintDialogData().SetSetupDialog(true); int ret = dialog.ShowModal(); if (ret == wxID_OK) m_printDialogData = dialog.GetPrintDialogData(); return (ret == wxID_OK); #endif return false; } /* * Print preview */ wxIMPLEMENT_CLASS(wxMacPrintPreview, wxPrintPreviewBase); wxMacPrintPreview::wxMacPrintPreview(wxPrintout *printout, wxPrintout *printoutForPrinting, wxPrintDialogData *data) : wxPrintPreviewBase(printout, printoutForPrinting, data) { DetermineScaling(); } wxMacPrintPreview::wxMacPrintPreview(wxPrintout *printout, wxPrintout *printoutForPrinting, wxPrintData *data): wxPrintPreviewBase(printout, printoutForPrinting, data) { DetermineScaling(); } wxMacPrintPreview::~wxMacPrintPreview() { } bool wxMacPrintPreview::Print(bool interactive) { if (!m_printPrintout) return false; wxMacPrinter printer(&m_printDialogData); return printer.Print(m_previewFrame, m_printPrintout, interactive); } void wxMacPrintPreview::DetermineScaling() { wxSize ppiScreen = wxDisplay::GetStdPPI(); wxSize ppiPrinter( 72 , 72 ) ; m_previewPrintout->SetPPIScreen( ppiScreen.x , ppiScreen.y ) ; wxCoord w , h ; wxCoord ww, hh; wxRect paperRect; // Get a device context for the currently selected printer wxPrinterDC printerDC(m_printDialogData.GetPrintData()); if (printerDC.IsOk()) { printerDC.GetSizeMM(&ww, &hh); printerDC.GetSize( &w , &h ) ; ppiPrinter = printerDC.GetPPI() ; paperRect = printerDC.GetPaperRect(); m_isOk = true ; } else { // use some defaults w = 8 * 72 ; h = 11 * 72 ; ww = (wxCoord) (w * 25.4 / ppiPrinter.x) ; hh = (wxCoord) (h * 25.4 / ppiPrinter.y) ; paperRect = wxRect(0, 0, w, h); m_isOk = false ; } m_pageWidth = w; m_pageHeight = h; m_previewPrintout->SetPageSizePixels(w , h) ; m_previewPrintout->SetPageSizeMM(ww, hh); m_previewPrintout->SetPaperRectPixels(paperRect); m_previewPrintout->SetPPIPrinter( ppiPrinter.x , ppiPrinter.y ) ; m_previewScaleX = float(ppiScreen.x) / ppiPrinter.x; m_previewScaleY = float(ppiScreen.y) / ppiPrinter.y; } // // end of print_osx.cpp // #endif