用于排版文档(PDF 或类似文件)的 .NET 库?

Posted

技术标签:

【中文标题】用于排版文档(PDF 或类似文件)的 .NET 库?【英文标题】:.NET library for typesetting documents (PDF or similar)? 【发布时间】:2011-08-03 11:51:12 【问题描述】:

编辑:更好的解释: 在为这个问题设置赏金之前,我想更清楚地说明我需要什么:

我需要 .NET 库来生成可打印文档。用户应该能够使用我的应用程序或使用外部工具(如 Adob​​e Acrobat 阅读器)打印他们看到的完全相同的文档。它不一定是生成PDF文档的库,任何满足上述条件的文档格式都可以。

我需要该库来支持以下场景:

场景 1:

    我以某种字体和颜色创建文本。 如果打印此文本,我向图书馆询问宽度。 我根据 2 中的信息计算此文本的位置(页面上的 [X,Y] 坐标),并让库在页面上的此位置打印。

场景 2:

    我创建了一个文本,其中部分内容为上标。文本的所有部分(普通和上标)都使用相同(但可变)的字体。 我问图书馆如果这个文本打印时的宽度是多少。我得到的正确答案也考虑了普通文本和上标之间的kerning。 我在应打印此文本的页面上计算位置([X,Y] 坐标)(使用步骤 2 中的宽度)。我让图书馆在这个位置打印它。打印在页面上的宽度与库在上一步中返回的宽度完全相同。

第二种情况的注意事项:我有一些部分在上标中的文本 - 例如AAAv-superscript textBBB(其中 大括号中的文本是上标)。库需要能够使用正确的kerning 打印此文本。如果没有正确的字距调整,最后一个 A 和第一个上标 v 之间的间距与最后一个上标 t 和第一个 B 之间的间距相同。对于用户来说,A 和上标v 之间似乎有一个空格,但最后一个上标字母之后没有空格。所以文字看起来很难看。如果库要正确处理此问题,它将有方法一次打印整个文本AAAv-superscript textBBB,并指定其中的一部分为上标。然后它将在普通文本和上标之间使用正确的字距调整。

场景 3:

    我想在页面的精确点上打印由线条、圆圈、实心圆圈、字母和贝塞尔曲线组成的图片。我需要指定线条和圆圈的宽度。所有形状都需要以像素精度打印。

图书馆应该是免费的,而不是 GPL(LGPL 是可以的)。有什么可以让我做我需要的事情吗?可以用 iTextSharp(LGPL 而非 AGPL 的 4.1.6 版)来完成吗?或者也许是固定文件?感谢您的任何建议。


原问题:

我需要在 .NET (C#) 应用程序中为用户排版复杂的文档。这些文件的主要用途是打印。

文档将包含文本和简单生成的图形。文本和图形的布局会很复杂,需要计算(换句话说,文档中的文本位置需要由我的代码控制,它不会由所选库自动完成)。

这是我的 API 要求:

    函数返回给定字符串的精确宽度和文本应该出现的给定字体 能够将文本定位到页面上的确切位置 在超级脚本中包含部分文本 函数获取某些文本的确切宽度,其中部分文本包含在上标中 能够添加图片甚至更好的选项来绘制简单的图形(给定粗细的线,给定半径/直径的实心圆)

它不必是用于创建 PDF 文档的库 - 任何其他“所见即所得”的文档格式也可以。如果有可以显示此类文档的 WPF 组件,那将是一个优势。 我知道有 iTextSharp 但并不容易实现 4. 使用 iTextSharp 的要求。 当然一些满足上述要求的 PDF 生成库也将是很好的解决方案。

感谢您的任何建议,我也很乐意提供详细信息或更清晰的解释。

【问题讨论】:

你看过 XPS/XpsDocumentWriter/etc。还没有? @Logan Capaldo:这个名字对我来说听起来很熟悉......如果它是具有本机 WPF 控件来显示它的格式,而不是我前段时间看到的。但我不知道是否有简单的方法可以在 WPF 中生成此类文档。我在这里有一个没有答案的问题:***.com/questions/4634445/how-to-work-with-fixedpage。 @Logan Capaldo:但是 XPS 文档不是固定的吗?所以它不会显示与将要打印的完全相同的布局? @drasto:***说“与 Adob​​e Systems 的 PDF 格式一样,XPS 是一种固定布局的文档格式,旨在保持文档保真度,提供独立于设备的文档外观。”看起来像你要求的。 再次编辑我的答案。 【参考方案1】:

您可以查看Docotic.Pdf Library(免责声明:我为 Bit Miracle 工作)。 它具有用户友好的 API 和一组很好的示例,您可以在线查看或在示例查看器应用程序中运行。

它也满足您的要求:

函数返回给定字符串的精确宽度和文本应该出现的给定字体

您可以为此使用PdfCanvas.MeasureText() 方法。

能够将文本定位到页面上的确切位置

有许多重载允许在任意位置或区域显示文本。http://bitmiracle.com/pdf-library/help/pdfcanvas.drawstring.aspxhttp://bitmiracle.com/pdf-library/help/pdfcanvas.drawtext.aspx

在超级脚本中包含部分文本

PdfCanvas.TextRise 属性允许显示上标文本。您可以将它与 PdfCanvas.FontSize 属性结合使用来控制上标文本的大小。 样本: http://bitmiracle.com/pdf-library/help/text-rise.aspx

函数获取某些文本的确切宽度,其中部分文本包含在上标中

如果您对上标文本使用不同的字体或字体大小,则直接调用 PdfCanvas.MeasureText() 方法将产生不正确的结果。

有一些解决方法:

-分别测量用不同字体绘制的字符串的每个部分,然后对所有宽度求和。

-如果你想得到绘制文本的宽度,你可以简单地从绘制后的最终文本位置中减去初始文本位置。

能够添加图片甚至更好的选项来绘制简单的图形(给定粗细的线,给定半径/直径的实心圆)

支持。看看这些样本:https://github.com/BitMiracle/Docotic.Pdf.Samples/tree/master/Samples/Graphicshttps://github.com/BitMiracle/Docotic.Pdf.Samples/tree/master/Samples/Images


PDF 查看器组件在我们的计划中,但目前 Docotic.Pdf 不提供此类功能。

更新:您现在可以使用 Docotic.Pdf 栅格化、渲染或打印 PDF 文档。看看下面的文章:https://bitmiracle.com/pdf-library/convert-pdf-to-image.aspxhttps://bitmiracle.com/pdf-library/draw-print-pdf.aspx

【讨论】:

我是否可以在某个地方看到您提供的上标示例的输出:bitmiracle.com/pdf-library/help/text-rise.aspx?你有免费试用/演示/GPL 版本或我可以在付费前试用的东西吗? 当然。你可以download and evaluate Docotic。它带有用于 Visual Studio 2005-2010 的示例查看器和一组示例。评估版有一些限制,可以阅读详情here【参考方案2】:

与其寻找其他库,不如寻找一种更好的方法来计算混合大小/样式文本的宽度?

float width = ColumText.getWidth(phrase);

Phrase 使用各种文本布局功能和属性扩展了 ArrayList。段落扩展短语。每个块都有一个特定的Font,具有自己的颜色、大小和基础 PDF 字体。每个块都有自己的“文本上升”来调整其基线。

您使用的是哪个版本的 iText? ColumnText 已经存在很长时间了。


所以你想在普通字母和上标字母之间调整字距?对我来说,这听起来不是一个好主意。字距调整是一种调整,例如,“T”可以突出“j”。字距调整值假定共享基线和字体大小。您永远不会共享基线,并且在处理上标文本时几乎肯定会有不同的字体大小。即使您认为使用这些值是一个好主意(我不同意),您是在基本文本的磅值还是上标的字体大小中调整字距?

我的观点是,我认为您的目标(紧缩上标/下标文本边界与普通文本)将导致更糟糕的布局,而不是更好。

还是我误会了你?让我重新阅读您的评论:

但是当您还想在页面上放置包含上标的文本时,您需要使用 PdfContentByte。

不完全确定你的意思。如果您想将文本放置在页面上的任意位置,是的,您几乎需要使用PdfContentByte

使用它时,我没有找到一次打印包含上标的复杂文本的方法。

给定“显示文本”命令中的所有文本必须共享相同的字体/大小/颜色/等。这就是 PDF 的工作方式,它不是 iText 的一些限制。

我认为它只允许一次打印一个文本块。

正确。

所以我不能在考虑字距调整的情况下使用上标测量文本,然后将其放在页面上的某个位置

您需要将不同块的所有宽度相加。我不敢相信普通文本和上标文本之间的字距调整是个好主意,但是显示问题的 PDF 示例可能会说服我。

在我看来你需要使用BaseFont.getWidthPointKerned(String text, float fontSize)。上标、下标或法线,都是关于磅值和字体的。如果您绝对坚持,您可以使用BaseFont.getKerning(int c1, int c2) 获取任何两个字母之间的字距值在同一字体内,并使用它来确定块间字距。


使用 iText 和 PdfContentByte 绘制文本的另一种方法是使用 ColumnText。我相信 iText 在布局段落时使用 ColumnText,但我必须查看代码才能确定。

无论如何,您的代码可能如下所示:

ColumnText colTx = new ColumnText(contByte);
// paragraphs are phrases.
colTx.addText(phraseWithSuperAndSubScriptStuff);
colTx.setSimpleColumn(llx, lly, urx, ury);
colTx.go();

【讨论】:

@Mark Storer:我对 iTextSharp 的问题是,当您希望在上标中的文本后面跟随不在上标中的文本时。当您以标准方式使用 iTextSharp 时,它会进行正确的字距调整。但是,当您还想在页面上放置包含上标的文本时,您需要使用PdfContentByte。使用它时,我没有找到一次打印包含上标的复杂文本的方法。我认为它只允许同时打印一个文本块。所以我不能用上标测量文本,考虑到字距调整,然后把它放在页面上的某个位置...... ...无需将其拆分为块并在页面上打印单独的块。但是这样我就可以放松这些块之间的字距(在上标和非上标文本之间的边界)。因此结果是文本看起来很糟糕(上标之前没有字距调整)但我可以测量它并在指定位置打印 - 或者它看起来不错并且我可以测量它但我无法在页面上的自定义位置打印它(我不能将ColumTextPdfContentByte 一起使用)。我可以下载没有问题的最新版本的 iTextSharp。现在我使用的是 1 岁左右的版本。 @Mark Storer 现在我才提到你编辑了你的答案。您正确理解了我的 cmets。有一个错字,但你没看错。关于普通文本和上标文本之间的字距调整:上次我测试如何使用PdfContentByte 打印包含普通和上标部分的文本(以便我可以在任意位置打印它)我做了以下操作:打印普通文本Normal text[30, 30]。使用BaseFont.getWidthPointKerned 测量Normal text。假设返回值为101。所以我使用相同的方法在 [131, 30] 处打印了上标部分 superscript text... ...字体系列,但尺寸较小,带有一些文字上升设置。但结果看起来并不好。特别是它与我不使用 PdfContentByte 时产生的结果不同,但 Paragraph 由 2 个块组成(Normal textsuperscript text 其中第二个块设置为上标 - 有一种方法可以这样做,但我不记得方法名称......)。使用Paragraph 而不是PdfContentByte 时产生的结果看起来不错,这实际上是我想要的想法。 Normal textsuperscript text 之间的差距较小。我以为那是因为…… ...Paragraph 使用字距调整来计算这些块之间的距离(Normal textsuperscript text)。但是Paragraph 不能与PdfContentByte 一起使用,至少我没有找到办法做到这一点。所以我不能在任意位置打印Paragraph。这就是为什么我想要在普通文本和上标之间调整字距。如果因为其他原因而出现丑陋的文字,我只想在任意位置获得相同的结果(如Paragraph)。【参考方案3】:

Here is an article 在 MSDN 上构造 FixedDocument 对象。

如果您正在使用 WPF,并且想要创建打印质量文档,那么您应该学习内置的 FixedDocument 和 XPS 技术。而且由于您最终可以访问对象模型中的整个 FixedDocument,因此它也可能会告诉您您的宽度数。我还没有尝试过。

【讨论】:

在我决定使用它之前,我需要知道它可以做我需要做的事情。所以我需要知道如何获取文本宽度信息以及如何排版超级脚本。以及如何测量超级脚本文本。【参考方案4】:

我认为你可能想多了你的问题,而 WPF 有很好的打印能力。 您可以以非常简单的方式完全按照您在屏幕上看到的任何视觉对象(视觉对象几乎是所有 WPF 类)打印它们。这里有一个很好的教程:http://www.switchonthecode.com/tutorials/printing-in-wpf

【讨论】:

看起来不错,但如何在 WPF 中添加一些带有上标的文本?我认为不可能有一个Label 在上标(和紧缩)中包含部分文本。那么我将如何测量文本呢?只需获取AcctualWidthLabel 的值? 嗨,我自己没有尝试过,但我认为这里有一个上标与普通文本混合的解决方案:***.com/questions/2095583/…。对于宽度,是的,它或多或少是那么容易。 WPF 可以测量任何视觉对象的宽度,您可以从它的属性中获得它。我编写了类似的代码,但这里没有。稍后我会在家里检查它。祝你好运! @tinhou:很好的链接!再次,我越来越喜欢这个......我几乎决定使用iTextSharp 并尝试让 Mark Storer 的建议起作用,但现在我正在重新考虑它。您可以提供的任何代码都会非常有帮助。视觉效果也可以转换为FixedDocument 对吗? 你好,drasto。我认为你应该试试这个,很快就会看到它有多容易。虽然,错字变体有一些限制:首先,您需要 opentype 字体(如 palatino)。你可以在这里阅读描述:msdn.microsoft.com/en-us/library/… 不幸的是,有一个可怕的错误:connect.microsoft.com/VisualStudio/feedback/details/545057/… 我还检查了我的代码,我只是使用了 FormattedText 的宽度属性 最后,由于您将使用通用 Windows 打印窗口,您可以选择“打印”到 XPS 文档。 (我在打印到 xps 时遇到了一些边距问题,但我从未解决过这些问题,因为我的应用程序是用于真正的打印,而且运行良好)。 (对不起,我意识到我写得非常糟糕和短!)【参考方案5】:

听起来您需要获得一些 GDI+ 方面的经验。

我曾在一家抵押贷款公司工作,他们非常注重报告的布局方式。降低到像素精度。不幸的是,GDI+ 不能很好地测量 Text。因此,您更好的选择是使用 windows API

[DllImport("gdi32.dll")]
static extern bool GetTextExtentPoint(IntPtr hdc, string lpString, 
                                      int cbString, ref Size lpSize);

您传递您正在绘制的位图的句柄。更准确。

您也可以考虑使用 TextRenderer

http://msdn.microsoft.com/en-us/library/system.windows.forms.textrenderer(v=VS.80).aspx

我无法断言它的准确性。

【讨论】:

+1 谢谢,它看起来不错,但在其他答案的情况下,我看不到如何从 GDI+ 的答案中执行方案 2。您提供了有关如何测量和打印普通文本、如何测量和打印上标文本的答案,但无法将它们一起测量和打印并紧缩在一起(请参阅场景 2 的注释)。如果有我看不到的方法,请提供简单的代码示例/方法链接/说明如何操作。 我猜文本的区别在于(超级/子)脚本文本的大小与普通文本不同。 GDI+ 为您提供绘画工具。您必须解析请求并测量您的字符串是什么,然后您可以将它们绘制到您想要的位置。我的建议是,绘制第一组普通文本,然后绘制上标或下标文本,最后绘制下一组普通文本。我认为这是任何其他应用程序都会做的事情。但这将使您对您的产品拥有最大的控制权。【参考方案6】:

我们的产品PDFOne .NET 可能适合您的要求。它带有免版税的商业许可。

    返回给定字符串和文本应显示的字体的精确宽度的函数您可以为此使用 PDFFont.GetTextWidth() 方法。 能够将文本定位到页面上的确切位置您可以为此使用众多 PDFDocument.WriteText() 重载之一 在超级脚本中包含部分文本PDF 在其文本输出中没有任何这个概念。在我们看来,超字符串只是另一个具有不同字体大小和位置的字符串。您只需要为此调用另一个文本输出。 获取某些文本的确切宽度的函数,该文本的某些部分在上标中请参考之前的回复。 能够添加图片甚至更好的选项来绘制简单图形(给定粗细的线,给定半径/直径的实心圆) - PDFOne .NET 具有渲染图像、正方形、矩形、圆弧、Bezeir 曲线、椭圆、圆形、矩形、折线、多边形、矩形、水印、图章、多种类型的注释......

PDFOne .NET 还附带了一个 PDF 打印机组件和 PDF 查看器组件。

免责声明:我为 Gnostice 工作。

【讨论】:

+1 好答案。但是,当您在 3. 中写入时,显示为上标的只是另一个位置的另一个文本,我认为这不是我想要的。如果我只添加较小的文本作为上标,它会看起来很难看,因为普通文本和上标之间不会有字距调整。考虑示例A^vA 和上标v 之间会有很多空格。在另一种情况下E^b 的空间将比前一种情况少得多。所以我需要在没有字距调整信息的情况下正确定位上标。然后我还需要这些信息来衡量它。 我必须更正自己关于上标和下标文本输出的问题。我将此问题提交给我们的开发人员(后回答),他们说 PDF 不需要单独的文本输出,即它可以通过单个文本输出来实现。我们将在即将发布的版本中添加对此类字符串的支持。目前,我们的产品需要单独的文本输出。【参考方案7】:
    乳胶:LaTeX for PDF generation in production 对于像 Aspose 这样的付费库:http://www.aspose.com/categories/.net-components/aspose.pdf-for-.net/default.aspx 带有良好 CSS 的纯 html 也可以很好地工作。 微软开放 XML:http://msdn.microsoft.com/en-us/library/bb448854.aspx

就其价值而言,我认为 iTextSharp 是其中最简单的一个。你可以在所有这些中做你想做的,但它们各有优缺点。

【讨论】:

Latex:我需要以编程方式访问文档中的字符串宽度以计算布局。乳胶无法做到这一点。 Html:我在哪里得到字符串的宽度?打开 XML - 请参阅以前的 cmets 到原始问题。 我远不是 LaTeX 专家,但我很确定你可以做任何你想做的事。您可以指定节点的宽度,如下所述:tex.stackexchange.com/questions/7660/… Html:使用 css 控制字体并根据长度和字体信息计算字符串的宽度。 Open XML:我链接的SDK非常丰富,相信你可以做你需要的。就像我说的,你可以用所有的库做你需要的事情,这可能需要一些创造力和努力。我认为这里没有简单的出路。 我很清楚 LaTeX 能做什么,不能做什么。您可以在 LaTeX 本身中使用某些字符串的信息宽度,但不能从 .NET 代码外部访问它。我不能让 LaTeX 在没有 .NET 的情况下单独进行布局,因为我的布局对于 LaTeX 来说太复杂了——我需要全功能的编程语言来计算它。不,你不能在 LaTeX 中做任何你想做的事情。这是你不能做的:***.com/questions/2599644/…。这就是我不能使用它的原因。 我仍然不明白如何在 Html 中获取字符串的宽度...基于哪些字体信息?如果我使用 .NET 字体中的信息,宽度将永远不会与 html 中显示的相同。我不是 100% 确定,但我敢打赌,至少有不同的字距调整……而且 Html 对于程序化生成的文档和打印都不是很好的解决方案。 如,您可以测量 font-size: 12px courier new "hello world" 字符串的宽度,然后以此为基础进行计算。无论如何,我给出了其他几个选择。希望他们有所帮助。

以上是关于用于排版文档(PDF 或类似文件)的 .NET 库?的主要内容,如果未能解决你的问题,请参考以下文章

spire.pdf for net pdf页面排版怎么设置

用于在 Android 应用程序中查看或阅读文档的开源库? [关闭]

pdf原来的文件 排版很好,但是用abbyy 转了之后,排版就变得十分糟糕了,怎么办?

如何使用 SWIFT 修改文档或 PDF 中的选定字段

PDF转Word之后排版变了该怎么办?你知道哪些解决的小妙招?

如何从pdf文件中查找页眉页脚