3k+ 行的 PDFSharp / Migradoc PDF 生成速度极慢
PDFSharp / Migradoc PDF Generation for 3k+ rows going extremely slow
我正在尝试生成包含大约 3,500 条记录的 PDF。大约需要 5-10 分钟。这简直是荒谬的。我在这里错过了什么吗?它卡在 RenderDocument()
方法上。我使用的是 MigraDoc.Rendering.dll
和 PdfSharp.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);
}
}
我正在尝试生成包含大约 3,500 条记录的 PDF。大约需要 5-10 分钟。这简直是荒谬的。我在这里错过了什么吗?它卡在 RenderDocument()
方法上。我使用的是 MigraDoc.Rendering.dll
和 PdfSharp.dll
这是我的代码片段。也许我可以在构建 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);
}
}