使用开源 Scryber 库生成动态 PDF 文档






4.95/5 (46投票s)
Scryber 的功能简介,
引言
创建动态 PDF 文档一直是一件苦差事。创建美观的动态 PDF 文档则更加困难。这通常涉及到大量的自定义代码、硬编码的元素放置,以及至少对 PDF 文档结构的一点了解。
有了 Scryber,编写 PDF 文档现在变得像创建 HTML 页面一样简单。它支持级联样式和数据绑定,以及通用的布局组件 - 页眉、线条、文本块、列、容器、图像、字体等。
背景
Scryber 是由 PerceiveIT Limited 创建的开源 PDF 库,在 LGPL 许可下发布。这比其他 GPL 库的限制性更小,允许您链接您的商业应用程序以及开源应用程序 - 前提是您没有修改原始 Scryber 源代码。
概述
为了展示 Scryber 库的一些功能,本文将使用 CodeProject 新文章 RSS feed(可通过此处获取)来构建一个类似下面的、包含 feed 内容的精美 PDF 文档。
下载和安装 Scryber
开始之前,您需要安装当前的 Scryber 框架。
- Visual Studio 2012 / .NET 4.0+
- 该框架可以直接从 Visual Studio 中以 VSIX 形式下载 - 在可用的在线扩展和更新中搜索 scryber,您将能够直接下载最新版本。
http://visualstudiogallery.msdn.microsoft.com/1f14c378-c102-4687-a16f-ce4eaaef845d[^] - NuGet 包 .Net 4.0+
- 框架本身作为包发布在 Nuget.org 上,可以从那里安装。这不包括下面讨论的项目模板,因此您需要添加自己的 xml 文件。
https://nuget.net.cn/packages/scryber/[^] - 旧版本,包括 VS 2008 / VS2010, .NET 3.5
- 旧版本的安装程序可从 codeplex 网站下载。目前它保持最新,但将来可能会停止更新。
http://scryber.codeplex.com/releases/[^]
Scryber 安装程序或 VSIX 会将所需的程序集和模板添加到您的环境中,文章的其余部分将重点介绍如何使用 Visual Studio 从 feeds 生成文档。
此项目的可用源代码作为最终版本附在后面。
我们的第一个 PDF 文档
我们首先需要一个站点,如果您还没有,请在 Visual Studio 中创建一个新的 .NET 3.5 Web 应用程序,并为其添加一个名为“PDFs”的文件夹。右键单击此文件夹,选择“添加 -> 新项…”。如果一切安装正确,左侧应该有一个名为 Scryber 的项目项组,其中包含许多模板。选择 PDF Document Template,将其重命名为 CodeProjectFeed.pdfx,然后将其添加到项目中。
文档应在 XML 编辑器中打开,如下所示:
<?xml version="1.0" encoding="utf-8" ?>
<?pdfx parser-mode="Strict" parser-log="True" ?>
<pdf:Document id="CodeProjectFeed"
xmlns:pdf="Scryber.Components, Scryber.Components"
xmlns:style="Scryber.Styles, Scryber.Styles"
xmlns:data="Scryber.Data, Scryber.Components"
auto-bind="true" >
<Styles>
<!-- Default page style -->
<style:Style applied-type="pdf:Page">
<style:Page size="A4" orientation="Portrait" />
</style:Style>
</Styles>
<Pages>
<!-- First page-->
<pdf:Page id="FirstPage" >
<Content>Hello World</Content>
</pdf:Page>
</Pages>
</pdf:Document>
这应该会自动添加对 Scryber 库或 Scryber NuGet 包的必要引用,具体取决于版本 – 如果没有,您需要手动从安装目录将“Scryber.Components
”等添加到项目引用中。该包还将向 web.config 文件添加一些额外的部分以支持 scryber。
输出 PDF
要实际生成 PDF 文件,我们需要调用它。所以,让我们在我们的 default.aspx 页面(或任何其他页面)上放置一个按钮来触发一个回发事件。
<asp:Button runat="server" ID="GeneratePDF" Text="Generate"
OnClick="GeneratePDF_Click" />
在代码隐藏中处理此事件,方法是解析 pdfx 文件,然后将生成的 PDF 输出到回发响应。
using Scryber.Components;
.
.
.
protected void GeneratePDF_Click(object sender, EventArgs e)
{
using(PDFDocument doc = PDFDocument.ParseDocument("~/PDFs/CodeProjectFeed.pdfx"))
{
doc.ProcessDocument(this.Response);
}
}
现在,如果我们运行我们的项目并在浏览器中打开此 ASPX 页面,我们将看到“生成”按钮。单击此按钮,我们的 PDF 将作为附件返回,以便在默认 PDF 阅读器应用程序中打开。
代码级支持
由于 Scryber 完全支持基于代码的文档创建或修改,如果我们愿意,我们也可以完全在页面源代码中完成此操作…
protected void GeneratePDF_Click(object sender, EventArgs e)
{
using(PDFDocument doc = new PDFDocument())
{
PDFPage page = new PDFPage();
page.PaperSize = Scryber.PaperSize.A4;
page.PaperOrientation = Scryber.PaperOrientation.Portrait;
doc.Pages.Add(page);
PDFLabel lbl = new PDFLabel();
lbl.Text = "Hello World";
page.Contents.Add(lbl);
}
doc.ProcessDocument(this.Response);
}
如果需要,我们还可以修改解析后的内容。
protected void GeneratePDF_Click(object sender, EventArgs e)
{
using(PDFDocument doc = PDFDocument.ParseDocument("~/PDFs/CodeProjectFeed.pdfx"))
{
PDFLabel lbl = new PDFLabel();
lbl.Text = "Hello World Again";
lbl.Style.Fill.Color = System.Drawing.Color.Aquamarine;
lbl.Style.Position.PositionMode = Scryber.Drawing.PositionMode.Block;
doc.Pages[0].Contents.Add(lbl);
doc.ProcessDocument(this.Response);
}
}
…但暂时先搁置手动操作。
添加一些实际内容
使用 XML 编辑器修改内容很容易,并且由于 Scryber 安装程序已将模式添加到 Visual Studio XML 模式库中,因此我们还应该获得用于描述组件的智能感知。
对于我们的 RSS feed 内容,我们将有一个描述性标题块和下面的项目组。删除“Hello World”文本,并将其替换为 <pdf:Page> <Content>
元素之间的以下内容:
<pdf:Page id="FirstPage" >
<Content>
<pdf:Div style:class="heading" >
<pdf:H1 text="This is the Title" ></pdf:H1>
<pdf:Label text="And this is going to be the description" /><BR/>
Date: <pdf:Label text="Today" /><BR/>
<pdf:Label text="CopyrightOwner" ></pdf:Label>
</pdf:Div>
</Content>
</pdf:Page>
语法类似于 HTML。保存文档并再次点击“生成”按钮,因为项目不需要重新构建。我们应该看到预期的内容已渲染到 PDF。
这是静态内容,还不错。但我们需要数据来自 feed 本身。
<rss version="2.0">
<channel>
<title>CodeProject Latest Articles</title>
<link>https://codeproject.org.cn</link>
<description>Latest Articles from CodeProject</description>
<language>en-us</language>
<image>
<title>CodeProject Latest Articles</title>
<url>https://codeproject.org.cn/App_Themes/Std/Img/logo100x30.gif</url>
<link>https://codeproject.org.cn</link>
<width>100</width>
<height>30</height>
<description>CodeProject</description>
</image>
<copyright>Copyright CodeProject, 1999-2013</copyright>
<webMaster>Webmaster@codeproject.com (Webmaster)</webMaster>
<lastBuildDate>Tue, 02 Apr 2013 09:50:10 GMT</lastBuildDate>
<ttl>20</ttl>
<generator>C# Hand-coded goodness</generator>
<item d3p1:type="item" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">
<title>Square Root algorithm for C</title>
<description>Square Root algorithm for C.</description>
<link>https://codeproject.org.cn/Articles/570700/SquareplusRootplusalgorithmplusforplusC</link>
<author>Edison Heng</author>
<category>C</category>
<pubDate>Tue, 02 Apr 2013 09:16:00 GMT</pubDate>
<subject />
<guid>https://codeproject.org.cn/Articles/570700/SquareplusRootplusalgorithmplusforplusC</guid>
</item>
.
.
.
</channel>
</rss>
在我们的页面中,添加一个指向 RSS feed URL 的 XMLDataSource
,并附带一些缓存和一个 id
属性。
<data:XMLDataSource source-path="https://codeproject.org.cn/WebServices/ArticleRSS.aspx"
cache-duration="20"
id="CPArticleSource" />
这指定了数据来源,可以是本地文件的引用或远程源。在处理之前,它甚至可以设置为代码中的其他文件,甚至是自定义加载的 XPathNavigator
到 XMLData
属性。
然后将我们的标题 div
包装在 ForEach
组件中,指定前面声明的数据源的 id 以及 feed 中根 channel 元素的 XPath
。
<data:ForEach datasource-id="CPArticleSource" select="rss/channel" >
<Template>
<pdf:Div style:class="heading" >
</pdf:Div>
</Template>
</data:ForEach>
要绑定 feed 中的数据,请使用以下格式的 XPath
表达式 {xpath:any valid xpath expression}
。因此,我们的页面内容将是:
<pdf:Page id="FirstPage" >
<Content>
<data:ForEach datasource-id="CPArticleSource" select="rss/channel" >
<Template>
<!-- start of heading -->
<pdf:Div style:class="heading" >
<pdf:H1 text="{xpath:title}" ></pdf:H1>
<pdf:Label text="{xpath:description}" /><BR/>
Date: <pdf:Label text="{xpath:lastBuildDate}" /><BR/>
<pdf:Label text="{xpath:copyright}" ></pdf:Label>
</pdf:Div>
<!-- end of heading -->
</Template>
</data:ForEach>
<!-- xml data source for code project rss feed -->
<data:XMLDataSource source-path="https://codeproject.org.cn/WebServices/ArticleRSS.aspx"
cache-duration="20"
id="CPArticleSource" />
</Content>
</pdf:Page>
保存更改,如果我们用按钮生成此内容,我们应该会看到一个类似下面的 PDF 文档。
绑定所有项目
现在,我们需要将 RSS feed 中的各个项目包含在我们的文档中。在标题 div
之后,但在模板结束之前,嵌套以下 ForEach
块:
<!-- repeating rss item blocks -->
<pdf:Div id="CPAllItems" >
<data:ForEach select="item" >
<Template>
<pdf:Div style:class="rss-item" >
<pdf:H2 text="{xpath:title}" ></pdf:H2>
<pdf:Label text="{xpath:description}" />
</pdf:Div>
</Template>
</data:ForEach>
</pdf:Div>
<!-- end of repeating rss item blocks -->
我们不需要指定 datasource-ID,因为存在一个 Scryber 将用于绑定的现有数据上下文。保存然后生成,我们应该会在文档输出中得到一组项目,所有文本都能很好地沿着线条流动并向下分布。
溢出到新页面
查看生成的 PDF,我们可以看到并非所有项目都已渲染,因为 pdf:Page
默认情况下仅限于生成单页内容。但是,有一个 pdf:Section
组件允许生成的内容流转到更多页面。如果我们将其声明更改为 section,我们应该会看到所有内容滚动到多个页面。
<Pages>
<!-- First page-->
<pdf:Section id="FirstPage" >
<Content>
<!-- Content as per the original page -->
</Content>
</pdf:Section>
</Pages>
快速查看输出,我们可以看到结果会流转到 2 页或更多页面。(不要忘记先保存。)
添加样式
我们目前有一个包含大部分所需信息的文档,但它非常难看。使用 Scryber 添加样式很容易。
所有关于如何渲染文档及其组件的信息都通过样式驱动,方式与 CSS 非常相似。大多数常见的样式值可以定义为实际元素上的属性,但将它们定义在文档的顶层或外部引用的文件中会更强大、更灵活。
让我们添加一个引用的样式集。首先选择解决方案中的 PDF 文件夹,然后选择“添加 -> 新项…”。在 Scryber 组中,选择 PDF Styles 模板并将其命名为 CPStyles.psfx。回到我们的 pdfx 文件,在文档顶部添加对这些样式的引用,并删除页面的样式定义。源路径必须相对于引用文档或相对于站点的根目录。
<Styles>
<style:Styles-Ref source="CPStyles.psfx" />
</Styles>
样式按照声明的顺序构建,并且可以根据以下标准之一来识别其适用性:applied-class
、applied-id
和 applied-type
,并且必须满足指定标准,样式才能用于任何组件。
- applied-class 根据组件上的
style:class
值进行匹配。可以在组件上指定多个值,用空格分隔。 - applied-id 根据组件的 id 进行匹配。ID 在单个文件(或模板)内必须是唯一的,但最终输出中可以有多个具有相同 ID 的组件。
- applied-type 根据组件的运行时类型进行匹配,并遵循引用代码命名空间和程序集的命名空间前缀规则。
对样式的详细分析超出了本文档的范围,但本节的其余部分将展示它们的使用方式以及它们的强大之处。在 CPStyles.psfx 中,删除任何现有的样式定义,并在 Styles
元素内添加以下内容:
<style:Style applied-type="pdf:Page" >
<style:Page size="A4" orientation="Landscape"/>
<style:Margins all="20pt" top="40pt" />
</style:Style>
<style:Style applied-class="heading" >
<style:Background color="#f90" />
<style:Fill color="black"/>
<style:Font bold="false" size="12pt" />
<style:Padding all="10pt"/>
<style:Margins bottom="20pt" />
</style:Style>
<style:Style applied-type="pdf:H1" >
<style:Fill color="white" />
<style:Font bold="false" size="30pt" />
</style:Style>
<style:Style applied-id="CPAllItems" >
<style:Columns count="2" alley-width="20pt"/>
</style:Style>
<style:Style applied-class="rss-item" >
<style:Padding all="5pt"/>
<style:Border sides="Bottom" color="#f90"/>
<style:Margins bottom="10pt"/>
<style:Font size="14pt" />
</style:Style>
<style:Style applied-type="pdf:H2" >
<style:Font size="20pt" bold="false" italic="false" />
<style:Fill color="#f90"/>
</style:Style>
我们将在稍后逐一介绍,但现在,保存并生成文档。您最终应该得到类似以下的内容:
正如我们所见,所有页面现在都是横向方向。标题 Div
有橙色背景。所有内容都填充为黑色,字体大小为 12 点。由于这些是从外部组件继承的,因此标题 Div
内的 H1
组件定义了自己的颜色,因此覆盖了黑色。在 CPAllItems
应用样式中,创建了两个列,内容向下填充一列,然后流转到下一列,最后移动到下一页。在每个 rss-item
中,我们添加了一个底部的橙色边框和一个 H2
特定的样式,用于 CodeProject 橙色文本填充颜色。
添加图像和链接
目前,它看起来相当不错,但包含 CodeProject 图像和链接到各个项目会更好。我们知道图像链接包含在 RSS feed 数据中。
<image>
<title>CodeProject Latest Articles</title>
<url>https://codeproject.org.cn/App_Themes/Std/Img/logo100x30.gif</url>
<link>https://codeproject.org.cn</link>
<width>100</width>
<height>30</height>
<description>CodeProject</description>
</image>
这可用于向我们的 PDF 添加图像。我们希望将其放在标题的右侧,而不影响其余内容的流,因此我们使用相对定位将图像放置在所需位置。由于页面是 A4 横向(宽 297mm),我们可以使用它来定义图像的放置位置。Scryber 理解英寸 (in) 和毫米 (mm),以及点 (pt)。如果未指定单位,则点是默认测量单位。在 CPStyles.psfx 中,添加:
<style:Style applied-class="right-image" >
<style:Position mode="Relative" x="240mm"
y="2mm" width="30mm"/>
</style:Style>
在标题 div
中,添加:
<pdf:Link action="Uri"
file="{xpath:image/link}" style:class="right-image" >
<pdf:Image src="{xpath:image/url}" />
</pdf:Link>
查看最终生成的 PDF 内容,我们会在右上角看到 logo,并链接回 www.codeproject.com。
下一步,我们需要从每个项目中添加一个链接到 CodeProject 站点的文章,我们可以在项目块下方添加指向文章的链接。
<pdf:Div style:h-align="Right" >
<pdf:Link action="Uri" file="{xpath:link}" new-window="true" >
<pdf:Label text="more..." />
</pdf:Link>
</pdf:Div>
右对齐是应用于块组件内内容的内联样式,而不是应用于组件本身。
您现在应该拥有一个动态加载所有内容并在 PDF 阅读器中精美渲染的完整文档。
几乎完成了,但还有一些事情需要完成。
停止提供文件,开始提供 PDF
使用 VSIX / NuGet 包的用户会自动完成此操作。 在添加包引用时,对 web.config 文件进行了修改。对于这些项目,您已经可以添加一个链接指向您的 pdfx 文档,生成的文件将下载到浏览器。现在在您的网页中尝试一下……然后您可以继续进行内容重用。
<a href='PDFs/CodeProject.pdfx' >我的 Code Project PDF</a>
对于使用 .NET 3.5、VS 2010 安装程序的用户,下面的说明将有助于实现相同的功能。
CodeProjectFeed.pdfx 和 CPStyles.psfx 是虚拟目录中的实际文件。目前,如果我们指向它们,内容将被提供。在我们的例子中,https://:3058/PDFs/CodeProjectFeed.pdfx。
虽然这目前不构成重大安全风险,但我们可能会开始引用系统上的 XML 文件或其他敏感信息。因此,需要阻止它通过 IIS 提供。最简单的方法与 ASCX 文件不通过 IIS 提供相同,即向 web.config 添加处理程序。Scryber 在安装目录(默认情况下为“C:\Program Files (x86)\Scryber\v0.8\Configuration”)中安装了一个辅助 config 文件。打开此文件,其中有许多有用的配置选项,但在底部是 <system.web>
和 <system.webServer>
部分,其中包含应用于阻止您的 Web 应用程序中内容的*.pdfx*.psfx*.pcfx*.ppfx* 文件请求的处理程序。将 httpHandlers
和 handlers
部分依次复制并添加到您的 Web 应用程序配置中的现有部分。
<system.web>
<httpHandlers>
<add path="*.psfx" verb="*" type="System.Web.HttpForbiddenHandler" />
<add path="*.ppfx" verb="*" type="System.Web.HttpForbiddenHandler" />
<add path="*.pcfx" verb="*" type="System.Web.HttpForbiddenHandler" />
<add path="*.pdfx" verb="*" type="System.Web.HttpForbiddenHandler" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<add name="Scryber.Styles" path="*.psfx" verb="*"
type="System.Web.HttpForbiddenHandler" />
<add name="Scryber.Components.Page" path="*.ppfx"
verb="*" type="System.Web.HttpForbiddenHandler" />
<add name="Scryber.Components.UserComponent" path="*.pcfx"
verb="*" type="System.Web.HttpForbiddenHandler" />
<add name="Scryber.Components.Document" path="*.pdfx"
verb="*" type="System.Web.HttpForbiddenHandler"/>
</handlers>
</system.webServer>
现在,当您导航到文档或样式文件时,您应该会收到一个被阻止的内容消息。这不会影响服务器端的内容加载或引用的内容(前提是引用的路径是相对的)。
注意:如果您尚未在使用 IIS 集成管道,则必须更改 IIS 设置,以便将对 psfx、ppfx、pcfx 和 pdfx 文件的请求传递给 .NET。
开始提供文件
如果我们能阻止内容被提供,我们也能允许内容被提供,Scryber 也支持这一点。如果您将 pdfx 在两个部分的处理程序从 HttpForbiddenHandler
更改为以下内容:
Scryber.Web.ScryberPDFHandlerFactory, Scryber.Components, Version=0.8.0.0,
Culture=neutral, PublicKeyToken=872cbeb81db952fe
所以您应该有一个httpHandlers
部分如下:
<httpHandlers>
<add path="*.psfx" verb="*"
type="System.Web.HttpForbiddenHandler"/>
<add path="*.ppfx" verb="*"
type="System.Web.HttpForbiddenHandler"/>
<add path="*.pcfx" verb="*"
type="System.Web.HttpForbiddenHandler"/>
<add path="*.pdfx" verb="*"
type="Scryber.Web.ScryberPDFHandlerFactory, Scryber.Components,
Version=0.8.0.0, Culture=neutral, PublicKeyToken=872cbeb81db952fe"/>
</httpHandlers>
以及您的
<handlers>
<add name="Scryber.Styles" path="*.psfx"
verb="*" type="System.Web.HttpForbiddenHandler"/>
<add name="Scryber.Components.Page" path="*.ppfx" verb="*"
type="System.Web.HttpForbiddenHandler"/>
<add name="Scryber.Components.UserComponent" path="*.pcfx" verb="*"
type="System.Web.HttpForbiddenHandler"/>
<add name="Scryber.Components.Document" path="*.pdfx" verb="*"
type="Scryber.Web.ScryberPDFHandlerFactory, Scryber.Components,
Version=0.8.0.0, Culture=neutral, PublicKeyToken=872cbeb81db952fe"/>
</handlers>
您应该能够直接从浏览器导航到 CodeProjectFeed.pdfx,内容将为您生成。
现在,您网站上指向 CodeProjectFeed.pdfx 的任何链接都将生成动态文档。
重用内容
Scryber 的一个巨大优势是组件的分离和引用。页面可以被引用并包含在多个文档中,组件可以被引用并在多个页面中重用。在我们的项目中,我们可以添加一个新的 CPFeedContents.pcfx PDF 用户组件模板,然后在我们的文档中引用它。
从第一个 ForEach
模板部分剪切内容,并替换为组件引用。
<pdf:Section id="FirstPage" >
<Content>
<data:ForEach datasource-id="CPArticleSource" select="rss/channel" >
<Template>
<pdf:Component-Ref source="CPFeedContents.pcfx"/>
</Template>
</data:ForEach>
<!-- xml data source for code project rss feed -->
<data:XMLDataSource source-path="https://codeproject.org.cn/WebServices/ArticleRSS.aspx"
cache-duration="20"
id="CPArticleSource" />
</Content>
</pdf:Section>
并将剪切的内容添加到组件文件中。
<pdf:UserComponent id="FeedContents"
xmlns:pdf="Scryber.Components, Scryber.Components,
Version=0.8.0.0, Culture=neutral, PublicKeyToken=872cbeb81db952fe"
xmlns:style="Scryber.Styles, Scryber.Styles,
Version=0.8.0.0, Culture=neutral, PublicKeyToken=872cbeb81db952fe"
xmlns:data="Scryber.Data, Scryber.Components,
Version=0.8.0.0, Culture=neutral, PublicKeyToken=872cbeb81db952fe" >
<Content>
<!-- start of heading -->
<pdf:Div style:class="heading" >
<pdf:H1 text="{xpath:title}" ></pdf:H1>
<pdf:Label text="{xpath:description}" /><BR/>
Date: <pdf:Label text="{xpath:lastBuildDate}" /><BR/>
<pdf:Label text="{xpath:copyright}" ></pdf:Label>
<pdf:Link action="Uri"
file="{xpath:image/link}" style:class="right-image" >
<pdf:Image src="{xpath:image/url}" />
</pdf:Link>
</pdf:Div>
<!-- end of heading -->
<!-- repeating rss item blocks -->
<pdf:Div id="CPAllItems" >
<data:ForEach select="item" >
<Template>
<pdf:Div style:class="rss-item" >
<pdf:H2 text="{xpath:title}" ></pdf:H2>
<pdf:Label text="{xpath:description}" />
<pdf:Div style:h-align="Right" >
<pdf:Link action="Uri"
file="{xpath:link}" new-window="true" >
<pdf:Label text="more..." />
</pdf:Link>
</pdf:Div>
</pdf:Div>
</Template>
</data:ForEach>
</pdf:Div>
<!-- end of repeating rss item blocks -->
</Content>
</pdf:UserComponent>
当我们再次生成时,PDF 文档在视觉上应该没有区别,但现在我们将主文档内容放在一个单独的文件中。使用相同的布局为其他 CodeProject 类别 feed 添加 5 个其他 PDF 文档将是一个简单的任务。 https://codeproject.org.cn/Info/opml.aspx。
Scryber 开发状态
目前,Scryber 绝对处于 beta 阶段。有很多“未文档化的功能”需要解决,但我们在逐步完善,如果一开始不奏效,通常有其他方法可以实现所需的功能。已在此处设置了一个论坛 here,我们非常希望在此听到有关库的问题、修复以及其他任何信息。我们还将为 Scryber 创建更多组件,例如表格、列表、图形路径以及 SQL 和对象源、安全性、表单、JavaScript 操作等。一切即将到来。文档也仅限于本文档和一个自述文件,但我们希望智能感知和模板有所帮助,并且它将随着时间的推移而增长。
关于徽章
到目前为止,您应该已经注意到您的文档的每一页上都有一个徽章——“generated by Scryber”。您可以使用 <styles:Badge>
选项来更改徽章的样式并将其移动到页面上的任何位置,但如果不深入代码,您就无法将其删除。Scryber 是开源的,可以免费使用,但我们要求在页面上显示徽章。如果您因为某种原因需要删除徽章(通常是商业原因),那么我们要求您购买许可证才能这样做。构建 Scryber 花费了很长时间,我们只触及了库可能性的表面。我们认为徽章是帮助我们继续开发和改进 Scryber 的最佳方式。
文章历史
- 1.4 - 2014 年 4 月 1 日
- 更新了安装选项,包括 VSIX 和 NuGet 包选项
- (希望)将 code project 中的文章位置更改为更易于访问的路径
- 1.3 - 2013 年 10 月 6 日
- 更新了
PDF:Link
元素,因为它不再需要内部Content
元素。
- 更新了
- 1.2 - 2013 年 4 月 23 日
- 根据读者反馈添加了最终的 config 条目,将
PDFDocument
处理封装在using
语句中以遵循最佳实践。
- 根据读者反馈添加了最终的 config 条目,将
- 1.1 - 2013 年 4 月 10 日
- 修正了拼写错误
- 1.0 - 2013 年 4 月 5 日
- 初次发表