2

(使用 itextsharp 5.4.3)

我创建了一个应用程序,用户可以在其中创建围绕页面主要内容的自定义 xhtml 页眉和页脚。所以生成的pdf看起来像这样:

------------------------
|  xhtml parsed Header |
------------------------
|                      |
| xhtml parsed content |
|                      |
------------------------
|  xhtml parsed Footer |
------------------------

我已经正确生成了pdf,但是我只希望标题出现在第一页上,并且能够(重新)设置剩余页面的边距。目前它仅在第一页上显示页眉(正确),但保留剩余页面的边距(不正确)而不显示页眉(正确)。所以一个 3 页生成的 pdf 看起来像

------------------------
|  xhtml parsed Header |
------------------------
|                      |
| xhtml parsed content |
|                      |
------------------------
|  Page 1              |
------------------------
------------------------
|                      |
------------------------
|                      |
| xhtml parsed content |
|                      |
------------------------
|  Page 2              |
------------------------
------------------------
|                      |
------------------------
|                      |
| xhtml parsed content |
|                      |
------------------------
|  Page 3              |
------------------------

使用 XML Parser 和 PageEvents 解析内容(生成按钮的代码)

Protected Sub btnPreview_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPreview.Click
    Dim bytes As Byte()
    Dim replaced As String = HttpUtility.HtmlDecode(letterRadEdit.Content.Replace("<br>", "<br />"))
    Dim replaced2 As String = HttpUtility.UrlDecode(replaced)
    bytes = System.Text.Encoding.UTF8.GetBytes(letterRadEdit.Content)
    Dim tagProcessor As tool.xml.html.DefaultTagProcessorFactory()

    Using input As New MemoryStream(bytes, False)
        Dim ms As New MemoryStream()
        Dim document As New iTextSharp.text.Document(iTextSharp.text.PageSize.LETTER, 36.0F, 36.0F, 52.0F, 52.0F)
        Dim headerFooter As New iTextSharpHeaderFooter()

        If Not String.IsNullOrEmpty(ddlHeaders.SelectedValue) Or Not String.IsNullOrEmpty(ddlFooters.SelectedValue) Then
            Using db As New dbEntities()
                If Not String.IsNullOrEmpty(ddlHeaders.SelectedValue) Then
                    'Get header content
                    Dim headerGuid As Guid = New Guid(ddlHeaders.SelectedValue)
                    Dim selectedheader As New LetterHeaderFooter()
                    selectedheader = (From hf In db.LetterHeaderFooters
                                        Where hf.HeadFootID = headerGuid And hf.HeadFootType = 1
                                        Select hf).FirstOrDefault()
                    Dim headerbytes As Byte()
                    headerbytes = System.Text.Encoding.UTF8.GetBytes(HttpUtility.HtmlDecode(selectedheader.HeadFootContent.Replace("<br>", "<br />").Trim()))
                    headerFooter.HeaderHTML = HttpUtility.HtmlDecode(selectedheader.HeadFootContent.Replace("<br>", "<br />").Trim())
                    headerFooter.HeaderContent = headerbytes

                    'Start building header into table
                    Dim page As New Rectangle(document.PageSize.Width - 72.0F, document.PageSize.Height)
                    Dim cellHeight As Single = document.TopMargin
                    Dim header As New PdfPTable(1)

                    header.TotalWidth = page.Width
                    Dim c As New PdfPCell()
                    c.HorizontalAlignment = Element.ALIGN_LEFT
                    c.Border = PdfPCell.BOTTOM_BORDER

                    Dim mh As SampleHandler = New SampleHandler()
                    Using sr As TextReader = New StringReader(headerFooter.HeaderHTML)
                        XMLWorkerHelper.GetInstance().ParseXHtml(mh, sr)
                    End Using
                    For Each el As IElement In mh.elements
                        c.AddElement(el)
                    Next

                    header.AddCell(c)
                    Dim startingMargin As Single = (header.TotalHeight + document.TopMargin)
                    document.SetMargins(document.LeftMargin, document.RightMargin, startingMargin, document.BottomMargin)
                    headerFooter.PageHeader = header

                End If

            End Using
        End If

        Dim writer As PdfWriter = PdfWriter.GetInstance(document, ms)
        writer.PageEvent = headerFooter
        writer.CloseStream = False
        document.Open()

        Dim htmlContext As HtmlPipelineContext = New HtmlPipelineContext(Nothing)
        htmlContext.SetAcceptUnknown(True)
        htmlContext.SetTagFactory(Tags.GetHtmlTagProcessorFactory())
        Dim cssResolver As ICSSResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(True)
        cssResolver.AddCssFile(HttpContext.Current.Server.MapPath("~/assets/css/pdf.css"), True)

        Dim pipeline As New CssResolverPipeline(cssResolver, New HtmlPipeline(htmlContext, New PdfWriterPipeline(document, writer)))
        Dim pdfworker As New XMLWorker(pipeline, True)

        Dim p As New XMLParser(True, pdfworker, New System.Text.UTF8Encoding)

        Try
            p.Parse(input)
        Catch

        Finally
            pdfworker.Close()

        End Try

        document.Close()
        ms.Position = 0

        Response.ContentType = "application/pdf"
        Response.AppendHeader("Expires", "0")
        Response.AppendHeader(
            "Cache-Control",
            "must-revalidate, post-check=0, pre-check=0"
          )
        Response.AppendHeader("Pragma", "public")
        Response.AppendHeader("content-disposition", "attachment; filename=preview.pdf")
        Response.BinaryWrite(ms.ToArray())
        ms.Flush()
    End Using
End Sub

iTextSharpHeaderFooter 类如下

Public Class iTextSharpHeaderFooter
    Inherits PdfPageEventHelper

    Private _HeaderStream As Byte()
    Private _FooterStream As Byte()
    Private _HeaderHTML As String
    Private _FooterHTML As String
    Private _headerPdf As Document
    Private _footerPdf As Document
    Private _usesHeader As Boolean
    Private _pageHeader, _pageFooter As PdfPTable

    'This is the contentbyte object of the writer
    Dim cb As PdfContentByte

    ' we will put the final number of pages in a template
    Dim template As PdfTemplate

    ' this is the BaseFont we are going to use for the header / footer
    Dim bf As BaseFont = Nothing

    ' This keeps track of the creation time
    Dim PrintTime As DateTime = DateTime.Now

    Public Property HeaderContent() As Byte()
        Get
            Return _HeaderStream
        End Get
        Set(ByVal value As Byte())
            _HeaderStream = value
        End Set
    End Property
    Public Property FooterContent() As Byte()
        Get
            Return _FooterStream
        End Get
        Set(ByVal value As Byte())
            _FooterStream = value
        End Set
    End Property

    Public Property HeaderHTML() As String
        Get
            Return _HeaderHTML
        End Get
        Set(ByVal value As String)
            _HeaderHTML = value
        End Set
    End Property
    Public Property FooterHTML() As String
        Get
            Return _FooterHTML
        End Get
        Set(ByVal value As String)
            _FooterHTML = value
        End Set
    End Property

    Public Property letterHeader() As Document
        Get
            Return _headerPdf
        End Get
        Set(ByVal value As Document)
            _headerPdf = value
        End Set
    End Property
    Public Property letterFooter() As Document
        Get
            Return _footerPdf
        End Get
        Set(ByVal value As Document)
            _footerPdf = value
        End Set
    End Property

    Public Property UsesHeader() As Boolean
        Get
            Return _usesHeader
        End Get
        Set(ByVal value As Boolean)
            _usesHeader = value
        End Set
    End Property

    Public Property PageHeader() As PdfPTable
        Get
            Return _pageHeader
        End Get
        Set(ByVal value As PdfPTable)
            _pageHeader = value
        End Set
    End Property

    Public Property PageFooter() As PdfPTable
        Get
            Return _pageFooter
        End Get
        Set(ByVal value As PdfPTable)
            _pageFooter = value
        End Set
    End Property


    ' we override the onOpenDocument method
    Public Overrides Sub OnOpenDocument(ByVal writer As PdfWriter, ByVal document As Document)
        MyBase.OnOpenDocument(writer, document)
        Try
            PrintTime = DateTime.Now
        Catch de As DocumentException
        Catch ioe As System.IO.IOException
        End Try

    End Sub

    Public Overrides Sub OnStartPage(ByVal writer As PdfWriter, ByVal document As Document)
        MyBase.OnStartPage(writer, document)
    End Sub


    Public Overrides Sub OnEndPage(ByVal writer As PdfWriter, ByVal document As Document)
        MyBase.OnEndPage(writer, document)

        Dim pageSize As Rectangle = document.PageSize

        Dim htmlContext As HtmlPipelineContext = New HtmlPipelineContext(Nothing)
        htmlContext.SetAcceptUnknown(True)
        htmlContext.SetTagFactory(Tags.GetHtmlTagProcessorFactory())

        If Not HeaderContent Is Nothing And HeaderContent.Length > 0 And writer.PageNumber < 2 Then
            Dim page As New Rectangle(document.PageSize.Width - 72.0F, document.PageSize.Height)
            PageHeader.WriteSelectedRows(0, -1, 0, -1, document.LeftMargin, page.Height, writer.DirectContent)
        End If
        If writer.PageNumber > 1 Then
            document.SetPageSize(New Rectangle(36.0F, 36.0F, 52.0F, PageFooter.TotalHeight))
        End If


        If Not FooterContent Is Nothing And FooterContent.Length > 0 Then
            Dim page As New Rectangle(document.PageSize.Width - 72.0F, document.PageSize.Height)
            PageFooter.WriteSelectedRows(0, -1, 0, -1, document.LeftMargin, PageFooter.TotalHeight, writer.DirectContent)
        End If


        Dim fontSize As Integer = 160
        Dim xPosition As Integer = 300
        Dim yPosition As Integer = 400
        Dim angle As Integer = 45
        Dim under As PdfContentByte = writer.DirectContentUnder
        Dim baseFont As BaseFont = baseFont.CreateFont(baseFont.HELVETICA, baseFont.WINANSI, baseFont.EMBEDDED)
        under.BeginText()
        under.SetColorFill(BaseColor.LIGHT_GRAY)
        under.SetFontAndSize(baseFont, fontSize)
        under.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "Preview", xPosition, yPosition, angle)
        under.EndText()


    End Sub


    Public Overrides Sub OnCloseDocument(ByVal writer As PdfWriter, ByVal document As Document)
        MyBase.OnCloseDocument(writer, document)
    End Sub
End Class

我曾尝试更改 OnEndPage 和 OnStartPage 中的页边距,但都没有任何结果。我查看了上一个问题“如何使用 iTextsharp 更改 PDF 中第二页的边距? ”但看不到我将(或应该)添加 page.NewPage() 页面上的哪个位置事件?

(作为一个子问题,我的 pdf 在 Acrobat Reader X 中关闭时一直说“你想保存更改吗”,我查看了之前的 SO 问题,它说使用 ToArray() 而不是 ToBuffer() ,我错过了什么吗?)

4

1 回答 1

3

我相信我已经解决了这个问题,OnEndPage 函数的一部分:

If writer.PageNumber > 1 Then

应该

If writer.PageNumber = 1 Then

正如我所读到的那样,任何页边距都会应用于后续页面。这意味着第 3 页的页边距将更改,因为页码为 2,因此我需要在第 1 页而不是第 2 页的末尾设置页边距。

于 2013-11-14T10:39:43.937 回答