iTextSharp将不同的边距应用于不同的页面

时间:2013-11-14 10:00:16

标签: vb.net layout pdf-generation itextsharp

(使用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中更改页边距,但都没有任何结果。我查看了上一个问题“How do I change the margin for the second page in a PDF using iTextsharp?”,但看不到我想(或应该)添加page.NewPage()NewPage在页面事件中的位置?

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

1 个答案:

答案 0 :(得分:2)

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

If writer.PageNumber > 1 Then

应该是

If writer.PageNumber = 1 Then

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