3k+ 行的 PDFSharp / Migradoc PDF 生成速度极慢

PDFSharp / Migradoc PDF Generation for 3k+ rows going extremely slow

我正在尝试生成包含大约 3,500 条记录的 PDF。大约需要 5-10 分钟。这简直是​​荒谬的。我在这里错过了什么吗?它卡在 RenderDocument() 方法上。我使用的是 MigraDoc.Rendering.dllPdfSharp.dll

的 1.32 版

这是我的代码片段。也许我可以在构建 PDF 的方式中优化一些东西?我使用此代码生成的其他 PDF 会立即创建约 50 条记录。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.DocumentObjectModel.Shapes;
using MigraDoc.Rendering;
using System.Diagnostics;

namespace Migradoc_PDF_Example
{
    class PDFGenerator
    {
        Document document;
        public void BuildAndOpenPDF()
        {
            //Argument = true if unicode
            PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(true); 

            // Set the MigraDoc document
            pdfRenderer.Document = document;

            // Create the PDF document
            pdfRenderer.RenderDocument();
            pdfRenderer.Save("ExamplePDF.pdf");

            //Let's create a process to open our generated PDF
            Process p = new Process(); 
            p.StartInfo = new ProcessStartInfo("ExamplePDF.pdf");
            p.Start(); //Open PDF
        }
        public PDFGenerator() //In this example, the class constructor will set up the document and BuildAndOpenPDF will build the file/open it
        {
            //Actually generate invoice document.. everything before this is getting the data built together before actually writing to the pdf
            document = new Document();
            document.Info.Title = "DOCUMENT TITLE";
            document.Info.Subject = "DOCUMENT SUBJECT";
            document.Info.Author = "Me";

            // Get the predefined style Normal.
            Style style; //Creates style variable so we can change the different styles in the document as seen below..
            style = document.Styles["Normal"]; //The normal style = default for everything
            style.Font.Name = "Times New Roman"; //Default normal Font Type to Times New Roman

            style = document.Styles[StyleNames.Header]; //Style for Header of document
            style.ParagraphFormat.AddTabStop("16cm", TabAlignment.Right);

            style = document.Styles[StyleNames.Footer]; //Style for footer of document
            style.ParagraphFormat.AddTabStop("8cm", TabAlignment.Center);

            // Create a new style called Table based on style Normal
            style = document.Styles.AddStyle("Table", "Normal");
            style.Font.Name = "Times New Roman";
            style.Font.Size = 9;

            // Create a new style called Reference based on style Normal
            style = document.Styles.AddStyle("Reference", "Normal");
            style.ParagraphFormat.SpaceBefore = "5mm";
            style.ParagraphFormat.SpaceAfter = "5mm";
            style.ParagraphFormat.TabStops.AddTabStop("16cm", TabAlignment.Right);

            // Create a new style called TextBox based on style Normal
            style = document.Styles.AddStyle("TextBox", "Normal");
            style.ParagraphFormat.Borders.Width = 2.5;
            style.ParagraphFormat.Borders.Distance = "3pt";
            style.ParagraphFormat.Shading.Color = Colors.SkyBlue;

            // Each MigraDoc document needs at least one section.
            Section section = document.AddSection();

            // Create TextFrame to store the text at the top (inside the borderFrame)
            TextFrame addressFrame;
            addressFrame = section.AddTextFrame(); //add this TextFrame to our section in the document
            addressFrame.Height = "1.5cm";  //12 Pt Font  = 0.5cm  so 3 lines = 1.5cm
            addressFrame.Width = "14cm"; //16 cm width - 1 inch left indention - 1 inch right indention = 14cm
            addressFrame.Left = "1cm";
            addressFrame.Top = "1.0cm";
            addressFrame.RelativeVertical = RelativeVertical.Page;
            addressFrame.LineFormat.Width = "1pt"; //Border pixel width = 1pt
            addressFrame.LineFormat.Color = Colors.Black; //Border color = black

            Paragraph addressinfo = addressFrame.AddParagraph(); //Add paragraph to addressFrame to store text
            addressinfo.Format.Alignment = ParagraphAlignment.Center; //Align paragraph in center
            addressinfo.AddFormattedText("LINE 1 EX.\n", TextFormat.Bold);
            addressinfo.AddFormattedText("LINE 2 EX.\n", TextFormat.Bold);
            addressinfo.AddFormattedText("LINE 3 EX.\n", TextFormat.Bold);
            addressinfo.Format.Font.Name = "Times New Roman";
            addressinfo.Format.Font.Size = 12;
            addressinfo.Format.SpaceAfter = 0;

            Paragraph Spacing = section.AddParagraph(); //Space between top and datagrid
            Spacing.Format.SpaceAfter = "4cm";

            /**************************************************************************************
             *                                  TABLE LOGIC BELOW
             *                                                                                   */
            //Next is all the crap for the table below
            Table table = section.AddTable();
            table.Style = "Table"; //Use the table style we created above
            table.Borders.Color = new Color(81, 125, 192); //Red, Green, Blue
            table.Borders.Width = 0.25;
            table.Borders.Left.Width = 0.5;
            table.Borders.Right.Width = 0.5;
            table.Rows.LeftIndent = 0;

            decimal tableWidth = 0.0M; //Used so we can center the table properly

            //Before adding any rows, we must add our columns
            Column column = table.AddColumn("1.0cm"); //ID Column
            tableWidth += 1.0M; //Required for table center to be properly calculated
            column.Format.Alignment = ParagraphAlignment.Left;

            column = table.AddColumn("1.0cm"); //Another Column
            tableWidth += 1.0M; //Required for table center to be properly calculated
            column.Format.Alignment = ParagraphAlignment.Left;

            column = table.AddColumn("1.0cm"); //Another Column
            tableWidth += 1.0M; //Required for table center to be properly calculated
            column.Format.Alignment = ParagraphAlignment.Left;

            column = table.AddColumn("1.0cm"); //Another Column
            tableWidth += 1.0M; //Required for table center to be properly calculated
            column.Format.Alignment = ParagraphAlignment.Left;

            column = table.AddColumn("1.0cm"); //Another Column
            tableWidth += 1.0M; //Required for table center to be properly calculated
            column.Format.Alignment = ParagraphAlignment.Left;

            column = table.AddColumn("1.0cm"); //Another Column
            tableWidth += 1.0M; //Required for table center to be properly calculated
            column.Format.Alignment = ParagraphAlignment.Left;

            table.Rows.LeftIndent = ((16 - tableWidth) / 2).ToString() + "cm"; //Use this to center the table - Note: Max table width = 16CM

            //Create Header Row for the table
            Row row = table.AddRow();
            row.HeadingFormat = true;
            row.Format.Alignment = ParagraphAlignment.Center;
            row.Format.Font.Bold = true;
            row.Shading.Color = new Color(235, 240, 249); //Red Green Blue
            row.Cells[0].AddParagraph("ID");
            row.Cells[0].Format.Alignment = ParagraphAlignment.Left;
            row.Cells[1].AddParagraph("poopy");
            row.Cells[1].Format.Alignment = ParagraphAlignment.Left;
            row.Cells[2].AddParagraph("butt");
            row.Cells[2].Format.Alignment = ParagraphAlignment.Left;
            row.Cells[3].AddParagraph("randall");
            row.Cells[3].Format.Alignment = ParagraphAlignment.Left;
            row.Cells[4].AddParagraph("jim");
            row.Cells[4].Format.Alignment = ParagraphAlignment.Left;
            row.Cells[5].AddParagraph("duh");
            row.Cells[5].Format.Alignment = ParagraphAlignment.Left;
            table.SetEdge(0, 0, 6, 1, Edge.Box, BorderStyle.Single, 0.75, Color.Empty); //This is to draw box around header row, arguments are weird, only change 3rd argument to # of columns

            //Now let's add our fake data
            for (int i=0; i<3600; i++)
            {
                Row newRow = table.AddRow();
                //ID Column [0]
                newRow.Cells[0].Format.Alignment = ParagraphAlignment.Left;
                newRow.Cells[0].AddParagraph("ID#"+i.ToString()); //Ex. ID#1 ID#2
                //Another Column [1]
                newRow.Cells[1].Format.Alignment = ParagraphAlignment.Left; 
                newRow.Cells[1].AddParagraph("test1"); 
                                                      //Amount Column [1]
                newRow.Cells[2].Format.Alignment = ParagraphAlignment.Left; 
                newRow.Cells[2].AddParagraph("test2"); 

                //Another Column [1]
                newRow.Cells[3].Format.Alignment = ParagraphAlignment.Left; 
                newRow.Cells[3].AddParagraph("test3"); 

                //Another Column [1]
                newRow.Cells[4].Format.Alignment = ParagraphAlignment.Left; 
                newRow.Cells[4].AddParagraph("test4"); 

                //Another Column [1]
                newRow.Cells[5].Format.Alignment = ParagraphAlignment.Left; 
                newRow.Cells[5].AddParagraph("test5"); 
            }

            // Create footer
            Paragraph footer = section.Footers.Primary.AddParagraph();
            footer.AddText("Created by Me " + DateTime.Now.Year.ToString());
            footer.Format.Font.Size = 8;
            footer.Format.Alignment = ParagraphAlignment.Center;
        }

    }
}

首先,试用 PDFsharp 1.50 beta 3b。速度快多了。

在此处查看基准测试结果:
http://forum.pdfsharp.net/viewtopic.php?p=9380#p9380

所以发现它在渲染大型数据集时确实非常慢,而且 PDF Sharp 1.5 Beta 仍处于测试阶段,所以它也没有那么好。我必须获取第 3 方补丁解决方案:

调试模式:http://www.pakeha_by.my-webs.org/downloads/MigraDoc-1.32-patched-debug.zip

发布模式:http://www.pakeha_by.my-webs.org/downloads/MigraDoc-1.32-patched-release.zip

调用PrepareDocument函数似乎非常耗时。所以我们决定尝试捕获可能的错误,只在需要时调用它:

    public void RenderObject(DocumentRenderer docRenderer, XUnit xPosition, XUnit yPosition, XUnit width, DocumentObject documentObject, XGraphics currentGfx)
    {
        // we risk to cause an exception in order to call the time consuming function PrepareDocument as few times as possible
        try
        {
            docRenderer.RenderObject(currentGfx, xPosition, yPosition, width, documentObject);
        }
        catch
        {
            docRenderer.PrepareDocument();
            docRenderer.RenderObject(currentGfx, xPosition, yPosition, width, documentObject);
        }
    }