首先,虽然 HTML 和 PDF 是在同一时间创建的,但它们并不相关。HTML 旨在传达更高级别的信息,例如段落和表格。虽然有一些方法可以控制它,但最终要由浏览器来绘制这些更高级别的概念。PDF 旨在传达文档,并且无论呈现在何处,文档都必须“看起来”相同。
在 HTML 文档中,您可能有一个 100% 宽的段落,根据显示器的宽度,可能需要 2 行或 10 行,打印时可能需要 7 行,在手机上查看时可能走20行。然而,PDF 文件必须独立于渲染设备,因此无论您的屏幕大小如何,它都必须始终以完全相同的方式渲染。
由于上述要求,PDF 不支持“表格”或“段落”之类的抽象内容。PDF 支持三种基本的东西:文本、线条/形状和图像。(还有其他的东西,比如注释和电影,但我在这里尽量保持简单。)在 PDF 中,你不会说“这是一段,浏览器做你的事!”。相反,您会说,“使用这个确切的字体在这个确切的 X、Y 位置绘制这个文本,不用担心,我之前已经计算了文本的宽度,所以我知道它都适合这条线”。您也不会说“这是一张桌子”,而是说“在这个确切位置绘制此文本,然后在我的另一个确切位置绘制一个矩形”
其次,iText 和 iTextSharp 解析 HTML 和 CSS。而已。ASP.Net、MVC、Razor、Struts、Spring 等都是 HTML 框架,但 iText/iTextSharp 100% 不知道它们。与 DataGridViews、Repeater、模板、视图等相同,它们都是特定于框架的抽象。从您选择的框架中获取 HTML是您的责任,iText 不会帮助您。如果你得到一个例外,The document has no pages
或者你认为“iText 没有解析我的 HTML”,那么几乎可以肯定你实际上 没有 HTML,你只是认为你有。
第三,已经存在多年的内置类HTMLWorker
被替换为XMLWorker
(Java / .Net)。零工作正在进行HTMLWorker
,不支持 CSS 文件,仅对最基本的 CSS 属性提供有限支持,实际上在某些标签上中断。如果您在此文件中没有看到HTML 属性或 CSS 属性和值,那么它可能不受HTMLWorker
. XMLWorker
有时可能会更复杂,但这些复杂性也使其 更具 可扩展性。
下面是 C# 代码,展示了如何将 HTML 标记解析为 iText 抽象,这些抽象会自动添加到您正在处理的文档中。C# 和 Java 非常相似,因此转换它应该相对容易。Example #1 使用内置的HTMLWorker
来解析 HTML 字符串。由于仅支持内联样式,因此class="headline"
会被忽略,但其他所有内容都应该可以正常工作。Example #2 与第一个相同,只是它使用了它XMLWorker
。Example #3 还解析了简单的 CSS 示例。
//Create a byte array that will eventually hold our final PDF
Byte[] bytes;
//Boilerplate iTextSharp setup here
//Create a stream that we can write to, in this case a MemoryStream
using (var ms = new MemoryStream()) {
//Create an iTextSharp Document which is an abstraction of a PDF but **NOT** a PDF
using (var doc = new Document()) {
//Create a writer that's bound to our PDF abstraction and our stream
using (var writer = PdfWriter.GetInstance(doc, ms)) {
//Open the document for writing
doc.Open();
//Our sample HTML and CSS
var example_html = @"<p>This <em>is </em><span class=""headline"" style=""text-decoration: underline;"">some</span> <strong>sample <em> text</em></strong><span style=""color: red;"">!!!</span></p>";
var example_css = @".headline{font-size:200%}";
/**************************************************
* Example #1 *
* *
* Use the built-in HTMLWorker to parse the HTML. *
* Only inline CSS is supported. *
* ************************************************/
//Create a new HTMLWorker bound to our document
using (var htmlWorker = new iTextSharp.text.html.simpleparser.HTMLWorker(doc)) {
//HTMLWorker doesn't read a string directly but instead needs a TextReader (which StringReader subclasses)
using (var sr = new StringReader(example_html)) {
//Parse the HTML
htmlWorker.Parse(sr);
}
}
/**************************************************
* Example #2 *
* *
* Use the XMLWorker to parse the HTML. *
* Only inline CSS and absolutely linked *
* CSS is supported *
* ************************************************/
//XMLWorker also reads from a TextReader and not directly from a string
using (var srHtml = new StringReader(example_html)) {
//Parse the HTML
iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
}
/**************************************************
* Example #3 *
* *
* Use the XMLWorker to parse HTML and CSS *
* ************************************************/
//In order to read CSS as a string we need to switch to a different constructor
//that takes Streams instead of TextReaders.
//Below we convert the strings into UTF8 byte array and wrap those in MemoryStreams
using (var msCss = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(example_css))) {
using (var msHtml = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(example_html))) {
//Parse the HTML
iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msHtml, msCss);
}
}
doc.Close();
}
}
//After all of the PDF "stuff" above is done and closed but **before** we
//close the MemoryStream, grab all of the active bytes from the stream
bytes = ms.ToArray();
}
//Now we just need to do something with those bytes.
//Here I'm writing them to disk but if you were in ASP.Net you might Response.BinaryWrite() them.
//You could also write the bytes to a database in a varbinary() column (but please don't) or you
//could pass them to another function for further PDF processing.
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
System.IO.File.WriteAllBytes(testFile, bytes);
2017年的更新
对于 HTML 到 PDF 的需求,有好消息。正如这个答案所示,W3C 标准css-break-3将解决这个问题......这是一个候选推荐,计划在今年经过测试后变成最终推荐。
正如print-css.rocks 所示,有一些解决方案不那么标准,带有 C# 插件。