【发布时间】:2016-06-29 07:55:46
【问题描述】:
背景:
我有一个编辑器,可以在其中编辑 HTML 文档的页眉和页脚,然后将它们合并到主文档中。 Base64 嵌入图像在主文档内容中完美运行,但在页眉或页脚中它们消失了(因此,如果我有一个带有 image1.png 的主文档,并且该文档有一个带有 image_header.png 的页眉,则将显示 image1,image_header惯于)。在我看来,标签处理器并未应用于 PageHeader html 中的元素。
我创建了一个自定义 ImageTagProcessor(下)
Public Class CustomImageTagProcessor
Inherits iTextSharp.tool.xml.html.Image
Public Overrides Function [End](ctx As IWorkerContext, tag As Tag, currentContent As IList(Of IElement)) As IList(Of IElement)
Dim attributes As IDictionary(Of String, String) = tag.Attributes
Dim src As String = String.Empty
If Not attributes.TryGetValue(iTextSharp.tool.xml.html.HTML.Attribute.SRC, src) Then
Return New List(Of IElement)(1)
End If
If String.IsNullOrEmpty(src) Then
Return New List(Of IElement)(1)
End If
If src.StartsWith("data:image/", StringComparison.InvariantCultureIgnoreCase) Then
' data:[<MIME-type>][;charset=<encoding>][;base64],<data>
Dim base64Data As String = src.Substring(src.IndexOf(",") + 1)
Dim imagedata As Byte() = Convert.FromBase64String(base64Data)
Dim image As iTextSharp.text.Image = iTextSharp.text.Image.GetInstance(imagedata)
Dim list As List(Of IElement) = New List(Of IElement)()
Dim htmlPipelineContext As pipeline.html.HtmlPipelineContext = GetHtmlPipelineContext(ctx)
list.Add(GetCssAppliers().Apply(New Chunk(DirectCast(GetCssAppliers().Apply(image, tag, htmlPipelineContext), iTextSharp.text.Image), 0, 0, True), tag, htmlPipelineContext))
Return list
Else
If File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, src)) Then
Dim imagedata As Byte() = File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, src))
Dim image As iTextSharp.text.Image = iTextSharp.text.Image.GetInstance(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, src))
Dim list As List(Of IElement) = New List(Of IElement)()
Dim htmlPipelineContext As pipeline.html.HtmlPipelineContext = GetHtmlPipelineContext(ctx)
list.Add(GetCssAppliers().Apply(New Chunk(DirectCast(GetCssAppliers().Apply(image, tag, htmlPipelineContext), iTextSharp.text.Image), 0, 0, True), tag, htmlPipelineContext))
Return list
End If
Return MyBase.[End](ctx, tag, currentContent)
End If
End Function
End Class
并通过主 PDF 生成器库中的以下代码连接
Dim tagProcessors As DefaultTagProcessorFactory = CType(Tags.GetHtmlTagProcessorFactory(), DefaultTagProcessorFactory)
tagProcessors.RemoveProcessor(HTML.Tag.IMG) ' remove the default processor
tagProcessors.AddProcessor(HTML.Tag.IMG, New CustomImageTagProcessor()) ' use our new processor
'Setup CSS
Dim cssResolver As ICSSResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(True)
cssResolver.AddCssFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "assets/css/pdf.css"), True)
'Setup Fonts
Dim xmlFontProvider As XMLWorkerFontProvider = New XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS)
xmlFontProvider.RegisterDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "assets/fonts/"))
Dim cssAppliers As CssAppliers = New CssAppliersImpl(xmlFontProvider)
Dim htmlContext As HtmlPipelineContext = New HtmlPipelineContext(cssAppliers)
htmlContext.SetAcceptUnknown(True)
htmlContext.SetTagFactory(tagProcessors)
'Setup Pipelines
Dim pdf As PdfWriterPipeline = New PdfWriterPipeline(document, writer)
Dim htmlp As HtmlPipeline = New HtmlPipeline(htmlContext, pdf)
Dim css As CssResolverPipeline = New CssResolverPipeline(cssResolver, htmlp)
在我的 HeaderFooter 类(继承自 PdfPageEventHelper)中,该类的新实例实例化为:
Public Sub New(ByVal headerHTML As String, ByVal footerHTML As String)
MyBase.New()
'< Other code not related >
Dim pdfElHandler As New PdfElementsHandler()
Using sr As TextReader = New StringReader(Me.HeaderHTML)
XMLWorkerHelper.GetInstance().ParseXHtml(pdfElHandler, sr)
End Using
headerElements = pdfElHandler.elements
Using sr As TextReader = New StringReader(Me.FooterHTML)
XMLWorkerHelper.GetInstance().ParseXHtml(pdfElHandler, sr)
End Using
footerElements = pdfElHandler.elements
headerTable = New PdfPTable(1)
headerTable = BuildElements(headerElements, "header")
footerTable = New PdfPTable(1)
footerTable = BuildElements(footerElements, "footer")
End Sub
Private Function BuildElements(tableElements As ElementList, type As String) As PdfPTable
Dim holderTable As New PdfPTable({1})
holderTable.HorizontalAlignment = Element.ALIGN_LEFT
Dim holderCell As New PdfPCell()
holderCell.Padding = 0
holderCell.UseBorderPadding = False
holderCell.Border = 0
If type = "header" Then
If Not String.IsNullOrEmpty(HeaderHTML) Then
For Each el As IElement In tableElements
holderCell.AddElement(el)
Next
Dim holderRow As New PdfPRow({holderCell})
holderTable.Rows.Add(holderRow)
End If
End If
If type = "footer" Then
If Not String.IsNullOrEmpty(FooterHTML) Then
For Each el As IElement In tableElements
holderCell.AddElement(el)
Next
Dim holderRow As New PdfPRow({holderCell})
holderTable.Rows.Add(holderRow)
End If
End If
holderTable.WidthPercentage = 100
Return holderTable
End Function
ParseXHTML 显示后 headerElements 的调试步骤:
1 Table (correct)
1 Row (correct)
2 Cells (correct)
Cell[0] Empty (not correct, there should be an image Element in here, parsed from an <img src="data:image/png;base64,xxxxxx... html element
Cell[1] Contains composite text elements (correct)
我的OnEndPage 活动看起来像:
Public Overrides Sub OnEndPage(ByVal writer As PdfWriter, ByVal document As Document)
'MyBase.OnEndPage(writer, document)
Dim pageSize As Rectangle = document.PageSize
Dim tagProcessors As DefaultTagProcessorFactory = CType(Tags.GetHtmlTagProcessorFactory(), DefaultTagProcessorFactory)
tagProcessors.RemoveProcessor(HTML.Tag.IMG) ' remove the default processor
tagProcessors.AddProcessor(HTML.Tag.IMG, New CustomImageTagProcessor()) ' use our new processor
Dim htmlContext As HtmlPipelineContext = New HtmlPipelineContext(Nothing)
htmlContext.SetAcceptUnknown(True)
htmlContext.SetTagFactory(tagProcessors)
Dim FinalMarginTop, FinalMarginBottom As Single
FinalMarginTop = Me.MarginTop
FinalMarginBottom = Me.MarginBottom
document.SetMargins(MarginLeft, MarginRight, MarginTop, MarginBottom)
If Me.UsesHeader Or Me.UsesFooter Then
Dim under As PdfContentByte = writer.DirectContent
Dim ct As New ColumnText(writer.DirectContent)
If Me.UsesHeader Then
'Create the header rectangle
Dim headerRect As New Rectangle(0, document.PageSize.Height, document.PageSize.Width, CalculatedHeaderHeight)
headerRect.Left += MarginLeft
headerRect.Right -= MarginRight ' document.RightMargin
headerRect.Top += MarginTop ' document.TopMargin
headerRect.Bottom -= MarginBottom ' document.BottomMargin
If HeaderType = EnumHeaderDisplayType.FirstPageOnly Then
If writer.PageNumber = 1 Then
ct.SetSimpleColumn(headerRect)
ct.AddElement(headerTable)
ct.Go()
FinalMarginTop = MarginTop
End If
Else
ct.SetSimpleColumn(headerRect)
ct.AddElement(headerTable)
ct.Go()
FinalMarginTop = CalculatedHeaderHeight + MarginTop
End If
End If
If Me.UsesFooter Then
Dim footerRect As New Rectangle(0, 0, pageSize.Width, CalculatedFooterHeight)
footerRect.BorderWidth = 0
footerRect.Left += document.LeftMargin
footerRect.Right -= document.RightMargin
footerRect.Top += CalculatedFooterHeight
footerRect.Bottom += document.BottomMargin
ct.SetSimpleColumn(footerRect)
ct.AddElement(footerTable)
ct.Go()
FinalMarginBottom = CalculatedFooterHeight + MarginBottom
End If
End If
End Sub
所以我认为在这个阶段需要应用自定义图像标签处理器,但我看不到在 OnEndPage 的哪个位置可以使用它。
【问题讨论】:
标签: vb.net base64 pdf-generation itext html-to-pdf