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

7

编辑:更好的解释:

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

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

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

场景1:

  1. 我创建了一些字体和颜色的文本。
  2. 我询问库打印此文本时的宽度。
  3. 我根据第2步的信息计算出此文本在页面上的位置([X,Y]坐标),并让库在此位置打印。

场景2:

  1. 我创建了一个文本,其中一些部分为上标。所有文本部分(正常和上标)都采用相同的(但可变的)字体。
  2. 我询问库打印此文本时的宽度。我得到正确的答案,该答案还考虑了正常文本和上标之间的字距
  3. 我计算出此文本应在页面上打印的位置(使用步骤2中的宽度)。我让库在此位置打印它。在页面上打印,其宽度与先前步骤中库返回的宽度完全相同。

第二个场景的注意事项:我有一些部分为上标的文本 - 例如AAA{v-上标文本}BBB(其中{}括号中的文本为上标)。该库需要能够使用正确的字距打印此文本。如果没有正确的字距,最后一个A和第一个上标v之间将有相同的间隔,就像最后一个上标t和第一个B之间一样。对于用户来说,看起来A和上标v之间有一个空格,但是最后一个上标字母之后没有空格。因此,文本看起来很丑陋。如果库要正确处理此问题,它将具有一种方法,可以一次打印整个文本AAA{v-上标文本}BBB并指定其中一部分为上标。然后它将在正常文本和上标之间使用正确的字距。

场景3:

  1. 我想要打印一个由线条、圆、填充圆、字母和贝塞尔曲线组成的图片,放在页面上的确切位置。我需要指定线条和圆的宽度。所有形状都需要以像素精度打印。
该库应该是免费的,不受 GPL(LGPL 可以)。有没有什么东西可以满足我的需求?可以使用 iTextSharp(版本为 LGPL 的 4.1.6)吗?或者使用 Fixed document?感谢任何建议。
原始问题:
我需要在 .NET(C#)应用程序中排版复杂文档,主要用途是打印。文档将包含文本和简单生成的图形。文本和图形的布局将是复杂的,并且需要计算(换句话说,文档中的文本位置需要由我的代码控制,而不是由选定的库自动完成)。
以下是我的 API 要求:
1.返回给定字符串和给定字体的精确宽度的函数 2.能够将文本精确定位于页面上的确切位置 3.使一部分文本成为上标 4.获取某些文本的确切宽度,其中一部分文本为上标 5.能够添加图片或更好的选择是绘制简单的图形(给定厚度的线条、给定半径/直径的填充圆)
它不必是创建 PDF 文档的库 - 任何其他“所见即所得的将被打印”文档格式也可以做到。如果有 WPF 组件可以显示此类文档,则具有优势。当然,满足上述要求的某些 PDF 生成库也是很好的解决方案。
谢谢任何建议,我也很乐意提供详细信息或更清晰的解释。

1
你看过XPS / XpsDocumentWriter /等吗? - Logan Capaldo
@Logan Capaldo:这个名字听起来很熟悉...如果它是那种具有本地WPF控件以显示它的格式,那么我曾经在一段时间内对它进行了研究。但我没有找到在WPF中生成这样的文档的简单方法。我在这里提出了一个没有答案的问题:http://stackoverflow.com/questions/4634445/how-to-work-with-fixedpage。 - Rasto
1
@drasto:维基百科说:“与Adobe Systems的PDF格式类似,XPS是一种固定布局文档格式,旨在保持文档真实性,并提供设备独立的文档外观。”看起来这正是你所需要的。 - R. Martinho Fernandes
我进行了一些搜索,似乎没有任何教程或资源可以指导如何生成文档。有一些关于如何从某些“FixedPage”创建XPS文档的SO答案,但我仍然不知道如何在.NET中创建带有某些内容的“FixedPage”。我的问题http://stackoverflow.com/questions/4634445/how-to-work-with-fixedpage 仍未得到解答。 - Rasto
1
再次编辑我的回答。 - Mark Storer
显示剩余2条评论
7个回答

3
您可以查看Docotic.Pdf Library(免责声明:我为Bit Miracle工作)。 它具有用户友好的API和良好的示例集,您可以在线查看或在示例查看器应用程序中运行。
此外,它满足您的要求:
  • 返回给定字符串和给定字体中文本应出现的确切宽度的函数
您可以使用PdfCanvas.MeasureText()方法实现此功能。
  • 能够将文本定位到页面上的确切位置
有许多重载函数可使文本显示在任意位置或区域。
http://bitmiracle.com/pdf-library/help/pdfcanvas.drawstring.aspx
http://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/Graphics
https://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.aspx
https://bitmiracle.com/pdf-library/draw-print-pdf.aspx

有没有地方可以查看您提供的上标示例的输出:http://bitmiracle.com/pdf-library/help/text-rise.aspx?在支付之前,您是否有免费试用/演示/GPL版本或其他可以尝试的版本? - Rasto
2
当然可以。您可以下载并评估Docotic。它附带有用于Visual Studio 2005-2010的示例查看器和示例集。评估版本有一些限制,您可以在此处阅读详细信息。 - Vitaliy Shibaev

2
与其寻找其他库,不如寻找更好的计算混合大小/样式文本宽度的方法,你觉得呢?
float width = ColumText.getWidth(phrase);

“Phrase”通过各种文本布局功能和属性扩展了ArrayList。 “Paragraph”扩展了“Phrase”。每个块都有特定的“字体”,具有自己的颜色,大小和基础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。在使用它时,我没有找到一次打印包含上标的复杂文本的方法。我认为它只允许一次打印一个文本块。因此,我无法考虑上标测量文本并考虑字距,然后将其放置在页面上的某个位置... - Rasto
但是这样做会导致我失去这些块之间的字距(在上标和非上标文本之间的边界处)。因此,结果要么看起来很糟糕(没有上标前的字距),但我可以测量它并在指定位置打印 - 要么看起来很好,我可以测量它,但我不能将其打印在页面上的自定义位置上(我不能使用 ColumTextPdfContentByte)。我可以下载最新版本的 iTextSharp,这不是问题。现在我正在使用大约一年前的版本。 - Rasto
@Mark Storer,现在我才提到你编辑了你的答案。你正确理解了我的评论。有一个错别字,但你弄对了。关于正常文本和上标文本之间的字距:上次我在测试如何使用PdfContentByte打印包含正常文本和上标部分的文本(以便我可以在任意位置打印它)时,我做了以下操作:在[30, 30]处打印正常文本“Normal text”。使用BaseFont.getWidthPointKerned测量“Normal text”的大小。假设返回值为101。因此,我使用相同的方法在[131, 30]处打印上标部分“superscript text”... - Rasto
...使用了一种新的方法,即使用PdfContentByte来设置字体族但较小的大小,并设置了一些文本上升。但结果看起来不好。特别是与我没有使用PdfContentByte而是由两个块(“普通文本”和“上标文本”,其中第二个块被设置为上标 - 有一种方法可以做到这一点,但我不记得方法名称...)组成的“段落”产生的结果不同。使用Paragraph而不是PdfContentByte时产生的结果看起来很好,实际上这正是我想要的。在“普通文本”和“上标文本”之间有较小的间隙。我认为这是因为... - Rasto
“Paragraph”使用字距来计算这些块(“普通文本”和“上标文本”)之间的距离。但是,至少我没有找到一种方法可以在“PdfContentByte”中使用“Paragraph”。因此,我无法在任意位置打印“段落”。这就是为什么我想要在普通文本和上标之间使用字距。如果丑陋的文本出现是由于其他原因,我只想在任意位置获得与“段落”相同的结果。 - Rasto
@Mark Storer:好的,我想颁发悬赏,你最后的编辑很可能是我正在寻找的解决方案。我没有时间测试它是否真正做到了我想要的 - 悬赏还有8分钟就结束了。我稍后会测试你的建议和其他人的建议,并可能接受某些答案或设置新的悬赏,如果问题没有解决。谢谢你,也感谢所有其他发布答案的人。 - Rasto

1

这里有一篇文章在MSDN上关于构建FixedDocument对象的。

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


在我决定使用它之前,我需要知道它是否能够完成我需要做的事情。因此,我需要了解如何获取文本宽度信息,以及如何排版上标。还要了解如何测量上标文本。 - Rasto

1

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

  1. 给定字符串和文本应出现的字体,返回确切宽度的函数
    您可以使用PDFFont.GetTextWidth()方法。
  2. 将文本定位到页面上的确切位置的能力
    您可以使用众多PDFDocument.WriteText()重载之一来实现这一点。
  3. 在文本中有一部分是上标的能力
    PDF在其文本输出中没有这个概念。对我们而言,所谓的上标只是另一个具有不同字体大小和位置的字符串。您只需要为此调用另一个textout即可。
  4. 获取一些文本的确切宽度,其中一部分是上标的函数
    参考先前的回答。
  5. 能够添加图片,或者更好的选项是绘制简单的图形(具有给定粗细的线条、具有给定半径/直径的填充圆) - PDFOne .NET具有渲染图像、正方形、矩形、弧形、Bezeir曲线、椭圆、圆形、矩形、折线、多边形、矩形、水印、标记、几种类型的注释等功能。

PDFOne .NET还配备了PDF打印机组件和PDF查看器组件。

免责声明:我为Gnostice工作。


+1个好答案。但是当您在3中写入上标时,所显示的内容只是另一个位置上的文本,我认为这不是我想要的。如果我只添加稍微向上偏移的较小文本作为上标,它看起来会很丑,因为普通文本和上标之间没有字距。考虑到例子A ^ v,在普通文本和上标之间将有很多空间。在另一种情况下E ^ b,与前一种情况相比,将会有更少的空间。因此,我需要具有字距信息才能正确定位上标。然后我还需要使用该信息进行测量。 - Rasto
1
我必须更正自己关于上标和下标文本输出的问题。我将这个问题转交给了我们的开发人员(回答后),他们说PDF不需要单独的文本输出,即可以通过一个文本输出来实现。在即将推出的版本中,我们将会增加对这样的字符串的支持。目前,我们的产品需要单独的文本输出。 - BZ1

1

我认为你可能在过度思考问题,虽然WPF具有出色的打印能力。 你可以以非常简单的方式打印任何可视化元素(几乎所有WPF类都是可视化元素),并将它们完全按照屏幕上的显示效果打印出来。这里有一个很好的教程:http://www.switchonthecode.com/tutorials/printing-in-wpf


1
看起来不错,但我怎么在WPF中添加一些带上标的文本?我不认为可以使 Label 的某些文本部分成为上标(并且加入字距)。那么我该如何测量文本大小呢?只需获取 LabelAcctualWidth 值吗? - Rasto
1
嗨,我自己没有尝试过,但我认为这里有一个解决上标与普通文本混合的方案:https://dev59.com/kXI95IYBdhLWcg3w7CfL。 至于宽度,是的,它更或多或少很容易。WPF可以测量任何可视化对象的宽度,并且您可以从其属性中获取它。我编写了类似的代码,但我现在没有它。我晚些时候回家后会检查一下。 祝你好运! - Martín Coll
1
你好,Drasto。我认为你应该尝试一下这个,很快就能看出它有多容易。虽然,有一些拼写变体的限制:首先,你需要OpenType字体(如Palatino)。你可以在这里阅读描述:http://msdn.microsoft.com/en-us/library/system.windows.documents.typography.variants.aspx 不幸的是,还有一个可怕的错误:http://connect.microsoft.com/VisualStudio/feedback/details/545057/typography-variants-superscript-and-subscript-bug-with-net-4-0 我还检查了我的代码,我只使用了FormattedText的宽度属性。 - Martín Coll
1
最后,由于您将使用常见的Windows打印窗口,您可以选择“打印”到XPS文档。(我在打印到XPS时遇到了一些边距问题,但我从未解决过,因为我的应用程序是用于实际打印的,并且效果很好)。 (抱歉,我意识到我写得非常糟糕和简短!) - Martín Coll
@tinchou 我不明白 FormattedText 是什么,也不知道如何使用它。我看过一些例子,但它们看起来非常困难,我无法理解。似乎这是一个非常低级的 API,而我通常会尽量避免使用它。 - Rasto
显示剩余2条评论

1

听起来你需要在GDI+方面积累一些经验。

我曾在一家抵押贷款公司工作,他们非常注重报告的布局。甚至到像素精度的程度。不幸的是,GDI+在测量文本方面表现并不好。因此,你最好使用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的注释)。如果有我没看到的方法,请提供简单的代码示例/方法链接/描述如何实现。 - Rasto
我猜测文本的不同之处在于(上/下)标文本的大小与普通文本不同。 GDI+为您提供绘制工具。您必须解析请求并测量您的字符串,然后可以将它们绘制到想要的位置。我的建议是,先绘制第一组普通文本,然后是上标或下标文本,最后是下一组普通文本。我认为这是任何其他应用程序都会做的。但这将使您对产品拥有最大的控制权。 - The Lazy Coder

0

1
Latex:我需要以编程方式访问将在我的文档中的字符串的宽度,以计算布局。这不能用latex完成。Html:我从哪里获取字符串的宽度?Open XML-请参见原始问题的先前评论。 - Rasto
我并不是LaTeX的专家,但我相信你可以做任何你想做的事情。你可以像这里讨论的那样指定节点的宽度:http://tex.stackexchange.com/questions/7660/tikz-applying-width-to-nodes-programmatically-and-undefined-control-sequence-pHTML:使用CSS控制字体,并根据长度和字体信息计算字符串的宽度。Open XML:我链接的SDK非常丰富,我相信你可以做你需要的事情。就像我说的,你可以用所有的库来做你需要的事情,可能需要一些创造力和努力。我认为这里没有简单的方法。 - Milimetric
1
我非常清楚LaTeX可以做什么,也知道它不能做什么。你可以在LaTeX中使用某些字符串的信息宽度,但无法从.NET代码外部访问它。我不能让LaTeX独自完成布局,因为我的布局对于LaTeX来说太复杂了 - 我需要一个完整功能的编程语言来计算它。而且,不,你不能在LaTeX中做任何想做的事情。这是你不能做的事情:http://stackoverflow.com/questions/2599644/how-to-keep-material-on-one-double-page-in-latex 。这就是我不能使用它的原因。 - Rasto
我仍然不明白如何获取Html中字符串的宽度...基于哪种字体信息?如果我使用.NET字体的信息,宽度将永远不会与Html中显示的宽度相同。我不是100%确定,但我敢打赌至少有不同的字距...而且Html对于编程生成的文档和打印来说都不是一个好的解决方案。 - Rasto
你可以测量字体大小为12px Courier New的“hello world”字符串的宽度,然后基于此进行计算。无论如何,我提供了其他几个选项。希望它们有所帮助。 - Milimetric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接