Search code examples
excelpython-2.7printingwin32com

Get Python to pdf/image print selected cells in MS Excel


From a python program, I need to capture a selection of cells from an Excel file as an image. Right now, my solution is complex and involves using win32com to save the Excel file as a PDF, then creating an image of the PDF using an image printer, then cropping the area desired. The problem is that sometimes the location of the desired cells shifts slightly in the saved PDF, which makes accurately/repeatably cropping the desired cells in the image impossible.

I want to select a range of cells in Excel as shown below

Selecting a range of cells in Excel

Then I want to print those selected cells (I don't mean print to screen, but to a printer configured in the OS) from Python using win32com, or similar. Is this possible? If so, please provide some sample code, or a link to a tutorial, etc.

Excel has this "Print Selection" option in its printing options (see below).

Excel option to print only selected cells - Print Selection

If this is possible, I could use the image printer I have installed to print that selection of cells directly to an image.


Solution

  • OK, I found a way to do this myself, so I thought I'd post how I did it. The solution launches a custom Excel Macro from a Python routine.

    The first thing I needed was to create a dummy/blank Excel file with an .xlsm extension (I called mine Selection_Printer.xlsm. Then in one of the VBA modules in that Excel file I wrote the following code:

    Sub PrintSelection(wbPath As String, minRow As Integer, maxRow As Integer, minCol As Integer, maxCol As Integer)
    
        Dim wb As Workbook, sh As Worksheet
    
        'Get path of this file
        'myPath = Application.ThisWorkbook.Path
    
        'Get the workbook objects needed
        Set wb = Workbooks.Open(wbPath)
    
        'Get the worksheet objects needed
        'All of the sheets I wanted to print were "Sheet1", 
        '  but another parameter could be added to make this dynamic
        Set sh = wb.Sheets("Sheet1")
    
        'Select the cells desired
        Call SelectRange(sh, minRow, maxRow, minCol, maxCol)
    
        'Print the selection only
        Selection.PrintOut From:=1, To:=1, Copies:=1, Collate:=True
    
    End Sub
    
    Sub SelectRange(sh As Worksheet, minRow As Integer, maxRow As Integer, minCol As Integer, maxCol As Integer)
    
        Dim selRange As Range
    
        With sh
          .Cells(minRow, maxCol).Select 'start by selecting upper left corner
          Set selRange = .Range(.Cells(minRow, minCol), .Cells(maxRow, maxCol))
        End With
    
        selRange.Select
    
    End Sub
    

    This prints a cell range defined by minRow, maxRow, minCol, and maxCol in the worksheet at the Path location of wbPath.

    In my Python code I call this macro using the following code:

    import os    
    from win32com import client
    import win32print
    
    def excel_jpg_macro(xl_path, row_min, row_max, col_min, col_max):
        """
        Program that calls the selection printing Macro in Excel from Python
    
        :param xl_path: Full path to XL file that has cell selection to be printed
        :param row_min: starting row of the selection desired (Excel uses indexing 
                        starting at 1) as an integer
        :param row_max: ending row of the selection desired as an integer
        :param col_min: starting column of the selection desired as an integer
        :param col_max: ending column of the selection desired as an integer
        """
    
        print_path = os.path.abspath('./Selection_Printer.xlsm')
        xl_macro = 'PrintSelection'
        # Use win32 to run the Excel Macro from Python
        xl = client.Dispatch("Excel.Application")
        xl.Workbooks.Open(Filename=print_path, ReadOnly=True)
        xl.Application.Run(xl_macro, xl_path, row_min, row_max, col_min, col_max)
        xl.Application.Quit() 
        del xl
    
    
    def jpg_excel_selection(xl_path, selection, save_name):
        """
        Prints a selection from an excel file to a jpg image printer
        :param xl_path: Full OS path to the Excel file that you want to print from
        :param selection: tuple with the (minimum row, maximum row, minimum column,
                          maximum column) with each value inside the tuple as an 
                          integer.  NOTE: Excel uses indexing starting at 1
        """
    
        try:
            # Capture the current default printer
            curr_printer = win32print.GetDefaultPrinter()
    
            # Temporarily change the default printer to be the image printer
            temp_printer = "ImagePrinter Pro"
            win32print.SetDefaultPrinter(temp_printer)
    
            # Run the macro that prints just the selection
            xl_path = os.path.abspath(xl_path)
            excel_jpg_macro(xl_path, *selection)
    
            # Change the default printer back to the original setting
            win32print.SetDefaultPrinter(curr_printer)
        except:
            print("Printing XL Selection Image Failed")