Aspose.Cells自行下载,这个小框架可以实现excel单元格套打、表格打印、插入图片等。

(一)定义打印元素:

/// <summary>
    /// 打印单元格元素
    /// </summary>
    public class PrintElem
    {
        /// <summary>
        /// 行索引(0开始)
        /// </summary>
        public int RowIndex { get; set; }
        /// <summary>
        /// 列索引(0开始,合并列,需要按未合并的算)
        /// </summary>
        public int ColumnIndex { get; set; }

        /// <summary>
        /// 是否为表底
        /// </summary>
        public bool IsFooter { get; set; } = false;

        /// <summary>
        /// 内容(图片元素,为图片文件的本地路径)
        /// </summary>
        public string ElemValue { get; set; }

        /// <summary>
        ///  格式化字符(日期、数字)
        /// </summary>
        public string FormatString { get; set; } = "";

        /// <summary>
        /// 数据类型
        /// </summary>
        public CellDataType DataType { get; set; } = CellDataType.String;

        /// <summary>
        /// 图片大小
        /// </summary>
        public Size PicCellSize { get; set; } = new Size(50, 50);

        /// <summary>
        /// 字体颜色
        /// </summary>
        public Color FontColor { get; set; } = Color.Black;

    }
 /// <summary>
    /// 数据类型
    /// </summary>
    public enum CellDataType
    {
        /// <summary>
        /// 字符
        /// </summary>
        String,
        /// <summary>
        /// 日期
        /// </summary>
        DateTime,
        /// <summary>
        /// 数字
        /// </summary>
        Double,
        /// <summary>
        /// 图片
        /// </summary>
        Image
    }

(二)定义表格打印列的配置:

/// <summary>
    /// 打印数据列对照
    /// </summary>
    public class PrintColumnMapping
    {
        /// <summary>
        /// 列索引(0开始)
        /// </summary>
        public int ColumnIndex { get; set; }

        /// <summary>
        /// 数据字段
        /// </summary>
        public string DbFiled { get; set; }

        /// <summary>
        /// 格式化字符(日期、数字)
        /// </summary>
        public string FormatString { get; set; } = "";

        /// <summary>
        /// 数据类型
        /// </summary>
        public CellDataType DataType { get; set; } = CellDataType.String;

        /// <summary>
        /// 图片列的宽高
        /// </summary>
        public Size PicCellSize { get; set; } = new Size(194, 50);

        public Color FontColor { get; set; } = Color.Black;
    }

(三)打印服务调用类:

public class ExportService
    {

        /// <summary>
        /// 生成打印文件
        /// </summary>
        /// <param name="title">文件标题</param>
        /// <param name="tbData">数据</param>
        /// <param name="clMaps">列对照</param>
        /// <param name="dicElems">元素集合</param>
        /// <param name="dataSratIndex">数据行开始的索引</param>
        /// <param name="templateName">打印模板名称(template文件夹下,xlsx文件)</param>
        /// <returns></returns>
        public static PrintResult PrintToFile(string title,DataTable tbData, List<PrintColumnMapping> clMaps,Dictionary<string,PrintElem> dicElems,int dataSratIndex,string templateName)
        {
            PrintResult rt = new PrintResult();

            try
            {


                Workbook book = new Workbook(Application.StartupPath + "\\template\\"+ templateName + ".xlsx"); //创建工作簿
                Worksheet sheet = book.Worksheets[0]; //创建工作表
                Cells cells = sheet.Cells; //单元格

                //临时文件ID
                //string fid = Guid.NewGuid().ToString().ToUpper().Replace("-", "");
                string fid = title + "_" + DateTime.Now.ToString("yyMMddssfff");
                string fExcel = Application.StartupPath + "\\tempfile\\" + fid + ".xlsx";
                string fPdf = Application.StartupPath + "\\tempfile\\" + fid + ".pdf";

                //Title
                cells[0, 0].PutValue(title);

                foreach (PrintElem elem in dicElems.Values )
                {
                    if(elem.IsFooter)
                    {
                        continue;
                    }
                    if(elem.DataType== CellDataType.DateTime && elem.FormatString!="")
                    {
                        cells[elem.RowIndex, elem.ColumnIndex].PutValue(DateTime.Parse(elem.ElemValue).ToString(elem.FormatString));
                    }
                    else if (elem.DataType == CellDataType.DateTime && elem.FormatString != "")
                    {
                        cells[elem.RowIndex, elem.ColumnIndex].PutValue(double.Parse(elem.ElemValue).ToString(elem.FormatString));
                    }
                    else if(elem.DataType== CellDataType.Image)
                    {
                        Image im = Image.FromFile(elem.ElemValue);

                        MemoryStream ms = new MemoryStream();
                        im.Save(ms, ImageFormat.Jpeg);

                        int iIndex = sheet.Pictures.Add(elem.RowIndex, elem.ColumnIndex, ms);
                        Aspose.Cells.Drawing.Picture pic = sheet.Pictures[iIndex];
                        pic.Placement = Aspose.Cells.Drawing.PlacementType.FreeFloating;

                        pic.Height = elem.PicCellSize.Height;
                        pic.Width = im.Width * pic.Height / im.Height;
                    }
                    else
                    {
                        cells[elem.RowIndex, elem.ColumnIndex].PutValue(elem.ElemValue);
                    }
                        
                }

                //data
                int rowCount = tbData.Rows.Count;
                for(int i=0;i<rowCount-1;i++)
                {
                    cells.InsertRow(dataSratIndex+1);
                    cells.CopyRow(cells, dataSratIndex + 1, dataSratIndex);
                }
                int startIdx = dataSratIndex;

                for(int i=0;i<rowCount;i++)
                {
                    DataRow dr = tbData.Rows[i];
                    int rIdx = startIdx + i;

                    foreach(PrintColumnMapping map in clMaps)
                    {
                        if(map.DataType == CellDataType.Image)
                        {
                            if(dr[map.DbFiled].ToString()!="")
                            {
                                Image im = (Image)dr[map.DbFiled];

                                MemoryStream ms = new MemoryStream();
                                im.Save(ms,ImageFormat.Jpeg);

                                int iIndex = sheet.Pictures.Add(rIdx, map.ColumnIndex, ms);
                                Aspose.Cells.Drawing.Picture pic = sheet.Pictures[iIndex];
                                pic.Placement = Aspose.Cells.Drawing.PlacementType.FreeFloating;
                               
                                pic.Height = map.PicCellSize.Height - 4;
                                pic.Width = im.Width * pic.Height / im.Height;

                                pic.Top = 2;
                                pic.Left = (map.PicCellSize.Width - pic.Width) / 2;

                                cells.SetRowHeight(rIdx, map.PicCellSize.Height);
                            }

                        }
                        else if(map.DataType == CellDataType.DateTime && map.FormatString!="")
                        {
                            cells[rIdx, map.ColumnIndex].PutValue(DateTime.Parse(dr[map.DbFiled].ToString()).ToString(map.FormatString));
                        }
                        else if (map.DataType == CellDataType.Double && map.FormatString != "")
                        {
                            cells[rIdx, map.ColumnIndex].PutValue(double.Parse(dr[map.DbFiled].ToString()).ToString(map.FormatString));
                        }
                        else
                        {
                            cells[rIdx, map.ColumnIndex].PutValue(dr[map.DbFiled].ToString());
                        }

                        if(map.FontColor!= Color.Black)//字体颜色
                        {
                            cells[rIdx, map.ColumnIndex].GetStyle().Font.Color = map.FontColor;
                        }
                        
                    }
                }

                //footer
                foreach (PrintElem elem in dicElems.Values)
                {
                    if (!elem.IsFooter)
                    {
                        continue;
                    }
                    if (elem.DataType == CellDataType.DateTime && elem.FormatString != "")
                    {
                        cells[elem.RowIndex + rowCount-1, elem.ColumnIndex].PutValue(DateTime.Parse(elem.ElemValue).ToString(elem.FormatString));
                    }
                    else if (elem.DataType == CellDataType.DateTime && elem.FormatString != "")
                    {
                        cells[elem.RowIndex + rowCount-1, elem.ColumnIndex].PutValue(double.Parse(elem.ElemValue).ToString(elem.FormatString));
                    }
                    else
                    {
                        cells[elem.RowIndex + rowCount-1, elem.ColumnIndex].PutValue(elem.ElemValue);
                    }

                    if (elem.FontColor != Color.Black)//字体颜色
                    {
                        cells[elem.RowIndex + rowCount - 1, elem.ColumnIndex].GetStyle().Font.Color = elem.FontColor;
                    }
                }

                //saveFile
                PdfSaveOptions opts = new PdfSaveOptions();
                opts.OnePagePerSheet = true;
                opts.Compliance = Aspose.Cells.Rendering.PdfCompliance.PdfA1a;

                book.Save(fPdf, opts);
                book.Save(fExcel, SaveFormat.Xlsx);


                rt.PdfFile = fPdf;
                rt.ExcelFile = fExcel;
                rt.Result = true;

                if(true)
                {
                    
                }

            }
            catch(Exception ex)
            {
                rt.Result = false;
                rt.Erro = "打印错误:" + ex.Message + " " + ex.StackTrace;
                MessageBox.Show("打印错误:"+ex.ToString());
            }
            return rt;
        }

        /// <summary>
        /// 打印浏览文件
        /// </summary>
        /// <param name="title"></param>
        /// <param name="pdfFile"></param>
        /// <param name="excelFile"></param>
        public static void PrintViewPdf(string title,string pdfFile,string excelFile)
        {
            FmPrintView fm = new FmPrintView();
            fm.Text = "打印预览:" + title;
            fm.PdfFile = pdfFile;
            fm.ExcelFile = excelFile;
            //fm.WindowState = System.Windows.Forms.FormWindowState.Maximized;

            int h  =  SystemInformation.VirtualScreen.Height;
            int w = SystemInformation.VirtualScreen.Width;

            fm.Height = h-30;
            fm.Width = 1000;

            fm.ShowDialog();
        }

        public static void ConvertExcelToPDF(string pdfFile, string excelFile)
        {
            using (Workbook book = new Workbook(excelFile))
            {
                //创建工作簿

                PdfSaveOptions opts = new PdfSaveOptions();
                opts.OnePagePerSheet = true;
                opts.Compliance = Aspose.Cells.Rendering.PdfCompliance.PdfA1a;

                book.Save(pdfFile, opts);

            }
        }

        /// <summary>
        /// 打印EXCEL文件
        /// </summary>
        /// <param name="excelFile"></param>
        public static void PrintExcelFile(string excelFile)
        {
            Spire.Xls.Workbook workbook = new Spire.Xls.Workbook();
            workbook.LoadFromFile(excelFile);
            workbook.PrintDocument.Print();
        }



        /// <summary>
        /// 打印PDF文件
        /// </summary>
        /// <param name="pdfFile"></param>
        public static void PrintPdfFile(string pdfFile)
        {
            Spire.Pdf.PdfDocument workbook = new Spire.Pdf.PdfDocument();
            workbook.LoadFromFile(pdfFile);
            workbook.Print();
        }
    }
 /// <summary>
    /// 打印文件结果
    /// </summary>
    public class PrintResult
    {
        /// <summary>
        /// 结果
        /// </summary>
        public bool Result { get; set; }

        /// <summary>
        /// 临时ID
        /// </summary>
        public string TempFIleID { get; set; }

        /// <summary>
        /// PDF文件名
        /// </summary>
        public string PdfFile { get; set; }

        /// <summary>
        /// Excel文件名
        /// </summary>
        public string ExcelFile { get; set; }

        /// <summary>
        /// 错误信息
        /// </summary>
        public string Erro { get; set; }


    }

(四)业务调用:

/// <summary>
        /// 统计报表导出/打印
        /// </summary>
        /// <param name="tbD">数据表</param>
        /// <param name="opType">操作类型 0 打印 1 导出EXCEL 2 导出PDF</param>
        public static void PrintStiReport(DataTable tbD,int opType)
        {
            /*调用方法
            * (1)引用ExportExcel.dll
            * (2)程序目录需拷贝Aspose.Cells.dll、Aspose.Words.dll、PdfiumViewer.dll、pdfium.dll
            * (3)创建文件夹template、tempfile
            * (4)打印模板放在template文件夹,必须为xlsx文件
            */

            //打印调用示列代码,示列打印模板:template\TEMP_SALE_ORDER.xlsx


            //打印模板名称(template文件夹下,xlsx文件)
            string template = "TEMP_SALE_ORDER_STI";

            //标题 默认第一行
            string title = "统计表";

            //数据开始行索引
            int dataSrartIndex = 3;

            //配置打印元素(单个单元格的)
            //注意EXCEL单元格的行列顺序从0开始,合并单元格,需要计算没合并的序号
            Dictionary<string, PrintElem> dicElems = new Dictionary<string, PrintElem>();


            //打印列配置(依据文印模板和数据列进行配置)
            List<PrintColumnMapping> lstMaps = new List<PrintColumnMapping>();
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 0, DbFiled = "SORT_INDEX", DataType = CellDataType.String });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 1, DbFiled = "DEPART", DataType = CellDataType.String });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 2, DbFiled = "S_DATA_01", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 3, DbFiled = "S_DATA_02", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 4, DbFiled = "S_DATA_03", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 5, DbFiled = "S_DATA_04", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 6, DbFiled = "S_DATA_05", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 7, DbFiled = "S_DATA_06", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 8, DbFiled = "S_DATA_07", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 9, DbFiled = "S_DATA_08", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 10, DbFiled = "S_DATA_09", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 11, DbFiled = "S_DATA_10", DataType = CellDataType.Double, FormatString = "0.00" });
            lstMaps.Add(new PrintColumnMapping() { ColumnIndex = 12, DbFiled = "S_DATA_11", DataType = CellDataType.Double, FormatString = "0.00" });

            //生成打印预览的文件
            var result = ExportService.PrintToFile(title, tbD, lstMaps, dicElems, dataSrartIndex, template);

            //生成成功,进行打印预览
            if (result.Result)
            {
                if (opType==0)
                {
                    //打印预览
                    ExportService.PrintViewPdf(title, result.PdfFile, result.ExcelFile);
                }
                else if (opType == 1)
                {
                    //导出Excel文件
                    SaveFileDialog sv = new SaveFileDialog();
                    sv.Filter = "*.xlsx|*.xlsx";
                    if(sv.ShowDialog() == DialogResult.OK)
                    {
                        var f = sv.FileName;

                        File.Copy(result.ExcelFile, f);
                    }
                }
                else if(opType == 2)
                {
                    //导出PDF文件
                    SaveFileDialog sv = new SaveFileDialog();
                    sv.Filter = "*.pdf|*.pdf";
                    if (sv.ShowDialog() == DialogResult.OK)
                    {
                        var f = sv.FileName;

                        File.Copy(result.PdfFile, f);
                    }
                }

            }
            else
            {
                MessageBox.Show("打印失败:" + result.Erro);
            }

        }


最后:

(1)打印同时输出EXCLE和PDF;

(2)打印预览,可以利用PdfiumViewer预览PDF;