Reporting Services 2008 – 嵌入 PDF 文档






3.63/5 (7投票s)
虽然不支持直接嵌入 PDF 文档的内容,但我们可以使用 SQL CLR 函数将 PDF 解析为图像列表来添加此功能。
引言
我们的 Web 应用程序的用户可以上传支持性的 PDF 文档,然后将它们以内联方式嵌入。当内容作者上传文档以支持他们输入到 Web 应用程序中的内容时,这非常有用。一个例子是附录,其中打印单独的文档无法实现页眉/页脚一致性或页面编号。此示例代码使用 TallComponents.PDF.Rasterizer,下面列出了其他替代方案。
背景
Reporting Services (RS) 工具栏中可用的控件不允许您向报表添加 PDF 文档。为了满足需求,我们在 C# 程序集中引用 TallComponents PDF Rasterizer 以将 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
--We will need this later in the SQL CLR function
CREATE PROCEDURE GetDocument
@Name nvarchar(255)
AS
SELECT [Document] FROM [Document] WHERE [Name] = @Name
--Instead of having the Web app load the test document we'll do it directly.
INSERT INTO [Document] ([Name], [Extension], [Document])
SELECT 'myPDF' AS [Name], 'pdf' AS [Extension],
* FROM OPENROWSET(BULK 'C:\CodeCamp2009.pdf', SINGLE_BLOB) AS [Document]
--Test that we can get the document back out
EXEC GetDocument 'myPDF'
--enable CLR for the SQL instance
exec sp_configure 'clr enabled', '1'
GO
reconfigure
GO
--We will be using C# System.Drawing and TallComponent
--assemblies which are not marked as safe for the SQL environment
ALTER DATABASE CodeCamp2009
SET TRUSTWORTHY ON
GO
接下来,创建程序集以将 PDF 解析为图像列表。 SQL 2008 CLR 基于 .NET 2.0,因此我们将 PDFParser 程序集设置为使用 .NET 2.0 运行。
我使用 TallComponents 中的示例应用程序 ConvertToImage 作为解析函数的模板。然后,我添加一个按钮到示例以测试 Parser 程序集。这是 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\Microsoft SQL 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 项目属性上,进行以下更改:数据库选项卡,设置为不安全,所有者设置为 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) 进行检查。
设置图像属性
- 图像源 = 数据库
- 字段 =
PDFPageImag
- MimeType = png
单击“预览”以查看 PDF 文档转换为一组图像以供报表使用。 太棒了!
关注点
有几种方法可以将 PDF 文档解析为图像列表
- PDF TallComponents Rasterizer:优点:可很好地缩放。 缺点:第三方,不确定可以放置在 PDF 文档中的所有内容能否呈现得很好,以及 Adobe 的更新将在多久后集成。
- Adobe SDK:优点:确信它可以在所有 PDF 内容上运行。 缺点:完整 SDK 很昂贵,在服务器环境中不支持使用桌面 DLL。
- 剪贴板和缩略图图像:优点:易于实现。 缺点:剪贴板,在服务器环境中无法使用,用户 A 可能会看到用户 B 的数据。
http://www.tallcomponents.com/pdfrasterizer2.aspx
http://www.daniweb.com/forums/thread32369.html
历史
第一篇 CodeProject 文章,感觉真棒。