在Web应用程序中,将PDF文档嵌入到报表中是一种常见的需求。例如,当内容作者上传文档以支持他们输入到Web应用程序中的内容时,这种方法非常有效。一个典型的用例是附录,如果打印单独的文档,则无法实现页眉/页脚一致性或页码编号。本文将介绍如何使用TallComponents.PDF.Rasterizer将PDF解析为图像列表,并在Reporting Services (RS)报告中嵌入这些图像。
Reporting Services (RS)工具栏中提供的控件不允许将PDF文档添加到报告中。为了满足这一需求,通过引用TallComponents PDF Rasterizer在C#程序集中解析PDF为图像列表。然后,使用SQL CLR表值函数将程序集包装起来,返回图像数据集。这样,就可以使用Reporting Services的Tablix和Image控件在RS报告中渲染图像列表。
首先,创建一个数据库来存储PDF文档并托管CLR程序集。使用Visual Studio Data Dude来创建数据库。
CREATE TABLE [Document] (
[Name] nvarchar(255) NOT NULL,
[Extension] nvarchar(10) NOT NULL,
[Document] image NOT NULL
)
GO
接下来,需要在SQL CLR函数中使用这个表。
CREATE PROCEDURE GetDocument @Name nvarchar(255) AS
SELECT [Document]
FROM [Document]
WHERE [Name] = @Name
GO
然后,将测试文档直接插入到数据库中,而不是通过Web应用程序加载。
INSERT INTO [Document] ([Name], [Extension], [Document])
SELECT 'myPDF' AS [Name], 'pdf' AS [Extension],
* FROM OPENROWSET(
BULK 'C:\CodeCamp2009.pdf', SINGLE_BLOB) AS [Document]
GO
测试否可以从数据库中检索文档。
EXEC GetDocument 'myPDF'
GO
启用SQL实例的CLR。
EXEC sp_configure 'clr enabled', '1' GO
RECONFIGURE GO
接下来,创建程序集以将PDF解析为图像列表。SQL 2008 CLR是.NET 2.0,所以将PDFParser程序集设置为使用.NET 2.0运行。
使用TallComponents的示例应用程序ConvertToImage作为解析函数的模板。然后,向示例中添加了一个按钮以测试解析程序集。以下是PDFParser程序集的代码。
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using TallComponents.PDF.Rasterizer;
using System.IO;
namespace PDFParser
{
public class Parse
{
public List<Image> Split(byte[] document)
{
Document pdfDoc = new Document(new BinaryReader(new MemoryStream(document)));
Page page = null;
List<Image> returnVal = new List<Image>();
for (int i = 0; i < pdfDoc.Pages.Count; i++)
{
page = pdfDoc.Pages[i];
using (Bitmap bitmap = new Bitmap((int)page.Width, (int)page.Height))
{
Graphics graphics = Graphics.FromImage(bitmap);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
page.Draw(graphics);
returnVal.Add((Image)bitmap.Clone());
}
}
return returnVal;
}
}
}
以下是用来测试上述程序集的代码。
private void cmdTestPDFParser_Click(object sender, EventArgs e)
{
FileStream fs = new FileStream(@"C:\CodeCamp2009.pdf", FileMode.Open);
byte[] pdf = new byte[fs.Length];
fs.Read(pdf, 0, (int)fs.Length);
PDFParser.Parse parser = new PDFParser.Parse();
List<Image> images = parser.Split(pdf);
}
现在,有一个程序集可以将PDF文档解析为图像列表。接下来,将这个程序集包装在SQL CLR函数中。为了使SQL CLR函数能够使用引用,需要在SQL数据库中注册它及其所有依赖项,所以将运行以下SQL。
将TallComponents.PDF.Rasterizer.dll复制到'C:\Program Files\MicrosoftSQL Server\MSSQL10.SQL2008\MSSQL\Binn\TallComponents.PDF.Rasterizer.dll'。
CREATE ASSEMBLY [System.Drawing] FROM 'C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll' WITH PERMISSION_SET = UNSAFE
CREATE ASSEMBLY [System.Web] FROM 'C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Web.dll' WITH PERMISSION_SET = UNSAFE
CREATE ASSEMBLY [TallComponents.PDF.Rasterizer] FROM 'C:\Program Files\Microsoft SQL Server\MSSQL10.SQL2008\MSSQL\Binn\TallComponents.PDF.Rasterizer.dll' WITH PERMISSION_SET = UNSAFE
CREATE ASSEMBLY [PDFParser.Parse] FROM 'C:\Program Files\Microsoft SQL Server\MSSQL10.SQL2008\MSSQL\Binn\CodeCamp2009\PDFParser.dll' WITH PERMISSION_SET = UNSAFE
在SQL项目属性中,进行以下更改:数据库选项卡,设置为Unsafe,设置所有者为dbo。
现在已经设置了SQL CLR环境,并创建并注册了依赖项,将编写包装表值函数。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Security.Permissions;
using System.Collections;
using System.Drawing;
using System.Collections.Generic;
using System.IO;
using System.Drawing.Imaging;
[assembly: System.Security.AllowPartiallyTrustedCallers, FileIOPermission(SecurityAction.RequestMinimum, Unrestricted = true)]
namespace SQLCLR
{
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Assert, Unrestricted = true)]
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "GetPDF_FillRow", TableDefinition = "PDFPageImage Varbinary(max)", DataAccess = DataAccessKind.Read)]
public static IEnumerable GetPDF(SqlString DocumentName)
{
ArrayList items = new ArrayList();
List<Image> pages = new List<Image>();
object[] images;
images = new object[1];
MemoryStream pageStream = new MemoryStream();
PDFParser.Parse pdfParser = new PDFParser.Parse();
using (SqlConnection conn = new SqlConnection("context connection = true"))
{
conn.Open();
SqlPipe pipe = SqlContext.Pipe;
SqlCommand cmd = new SqlCommand("GetDocument", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@Name", DocumentName));
SqlDataReader reader = cmd.ExecuteReader();
byte[] pdfContent = null;
while (reader.Read())
{
pdfContent = (byte[])reader.GetSqlBinary(0);
pages = pdfParser.Split(pdfContent);
for (int i = 0; i < pages.Count; i++)
{
MemoryStream ms = new MemoryStream();
pages[i].Save(ms, ImageFormat.Png);
items.Add((SqlBinary)ms.ToArray());
}
}
reader.Close();
reader = null;
pdfContent = null;
}
return items;
}
private static void GetPDF_FillRow(Object obj, out SqlBinary sItem)
{
SqlBinary sTemp = (SqlBinary)obj;
sItem = sTemp;
}
}
}
部署SQL CLR项目后,可以在SQL Management Studio中使用以下查询进行测试:
select * from dbo.GetPDF('myPDF')
应该得到类似于此的数据集的图像数据。
创建一个RS项目并添加一个报告。使用向导与查询select * from dbo.GetPDF('myPDF')。将详细文本框替换为矩形,然后是一个Image控件,并调整大小以填充页面。层次结构应该是Tablix\Rectangle\Image,使用文档大纲视图(Ctrl + Alt + T)进行检查。
设置图像属性:
点击预览以查看PDF文档转换为一组图像,供报告使用。