Search code examples
arraysvb.netpdffor-looppdfsharp

VB.Net, how to detect end of page and continue writing on next page


I have a VB.NET program that uses PDF Sharp to dump information to a PDF file. The problem i'm having is I can't figure out how to jump to the next page if ypoint (length of the page) is at a certain value. Indicated by ** before the if ** at the end if. Below is a section of my pdf sharp dump. I thought I could use an if then statement to increase a counter and add the variable to the end of the pdfpage... for example

pdfpage(ypointcounter)....

This doesn't work. You'll see I tried separating the top portion (my heading), because I don't want to repeat that on the next page, and is not part of my loop through to dump the info from multiple datasets. I know I can insert a new page, and I also know how to write to the second page. What i'd like to do is setup the program, inside my loop, to automatically create the next page, and begin writing to it if i'm at the end of a page. I assume I have to setup some sort of array?

Any Help?

My code is:

Try
yPoint = 0

Dim pdf As PdfDocument = New PdfDocument
pdf.Info.Title = "Last Hour Comparison"
Dim pdfPage As PdfPage = pdf.AddPage
Dim pdfPage1 As PdfPage = pdf.AddPage
Dim graph As XGraphics = XGraphics.FromPdfPage(pdfPage1)
Dim fontheader As XFont = New XFont("Arial", 12, XFontStyle.Bold) ' I used this as a header.  Basically-
'Just made the font bigger and style is BOLD
Dim font As XFont = New XFont("Arial", 11, XFontStyle.Regular)

'This sets the Header----------------------------------------------------------------------------

graph.DrawString("Test Last Hour Comparison for week of " & Weekstart, fontheader, XBrushes.Black, _
   New XRect(20, yPoint, pdfPage.Width.Point, pdfPage.Height.Point), XStringFormats.TopLeft)
yPoint = yPoint + 40

'---------------------------------
'This is for building the Report ---------------------------------------------------------

'If ds.Tables(0).Rows.Count > 0 Then
graph.DrawString("Location", font, XBrushes.Black, _
    New XRect(20, yPoint, pdfPage.Width.Point, pdfPage.Height.Point), XStringFormats.TopLeft)

graph.DrawString("Date", font, XBrushes.Black, _
    New XRect(120, yPoint, pdfPage.Width.Point, pdfPage.Height.Point), XStringFormats.TopLeft)

graph.DrawString("# 6-7 Visits ", font, XBrushes.Black, _
New XRect(280, yPoint, pdfPage.Width.Point, pdfPage.Height.Point), XStringFormats.TopLeft)

graph.DrawString("# 7-8 Visits ", font, XBrushes.Black, _
    New XRect(340, yPoint, pdfPage.Width.Point, pdfPage.Height.Point), XStringFormats.TopLeft)

graph.DrawString("% Change ", font, XBrushes.Black, _
    New XRect(410, yPoint, pdfPage.Width.Point, pdfPage.Height.Point), XStringFormats.TopLeft)
Dim x As Integer
x = 0
LocationCounter = 1
ypointcounter = 0
**For i = 0 To 3
    GetLocation()
    yPoint = yPoint + 20
    If yPoint > 840 Then
        yPoint = 20
        ypointcounter = ypointcounter + 1


    End If**

    graph.DrawString(LocationName, font, XBrushes.Black, _
    New XRect(20, yPoint, pdfPage(ypointcounter).Width.Point, pdfPage(ypointcounter).Height.Point), XStringFormats.TopLeft)
    dtstartdate = Today.AddDays(-6)
    firsthourweektotal = 0
    secondhourweektotal = 0
    For z = 0 To 6
        firsthour = ds.Tables(0).Rows(x).Item(0)
        secondhour = ds2.Tables(0).Rows(x).Item(0)
        firsthourweektotal = firsthourweektotal + ds.Tables(0).Rows(x).Item(0)
        secondhourweektotal = secondhourweektotal + ds2.Tables(0).Rows(x).Item(0)
        If ds.Tables(0).Rows(x).Item(0) = 0 Then
            Percentage = 0
        Else
            Percentage = Math.Round(ds2.Tables(0).Rows(x).Item(0) / ds.Tables(0).Rows(x).Item(0) * 100, 2)
        End If

        graph.DrawString(dtstartdate.DayOfWeek.ToString & ", " & dtstartdate.Month & "/" & dtstartdate.Day, font, XBrushes.Black, _
        New XRect(110, yPoint, pdfPage(ypointcounter).Width.Point, pdfPage(ypointcounter).Height.Point), XStringFormats.TopLeft)

        graph.DrawString(firsthour, font, XBrushes.Black, _
        New XRect(288, yPoint, pdfPage(ypointcounter).Width.Point, pdfPage(ypointcounter).Height.Point), XStringFormats.TopLeft)

        graph.DrawString(secondhour, font, XBrushes.Black, _
        New XRect(352, yPoint, pdfPage(ypointcounter).Width.Point, pdfPage(ypointcounter).Height.Point), XStringFormats.TopLeft)

        graph.DrawString(Percentage, font, XBrushes.Black, _
        New XRect(418, yPoint, pdfPage(ypointcounter).Width.Point, pdfPage(ypointcounter).Height.Point), XStringFormats.TopLeft)
        dtstartdate = dtstartdate.AddDays(1)
        yPoint = yPoint + 20
        x = x + 1
    Next

Solution

  • You have sort of boxed yourself in with the idea of the arrays. It sounds like what you really want is a way to start a new page and redraw the header on the fly when YPosition gets to a certain point. Note: With such questions we are guessing at the result, format, layout and grouping based on what it looks like the code is doing. For example, it is not entirely clear if the report is grouped by location or date. That should not matter since the concept is the same.

    To do this, I wrote a class of primitives to manage printing in a loop, creating a new page when desired. My test data was a Dictionary(Of String, List(of VisitInfo)), but as I said, I think I got that inside out (based on "comparison" in the header). I also cleaned up the code to Dispose of resources, such as XGraphics objects which are leaking in yours.

    Public Class PDFReport
    
    Private filePath As String
    Private pdf As PdfDocument              ' this also needs to be disposed of
    Private hFont As XFont
    Private tFont As XFont
    
    ' the current page, current Y
    Private pdfPage As PdfPage
    Private CurrentY As Integer
    
    Public Sub New(fPath As String)
        filePath = fPath
    
        pdf = New PdfDocument()
        pdf.Info.Title = "Last Hour Comparison"
    
        hFont = New XFont("Arial", 14, XFontStyle.Bold)
        tFont = New XFont("Arial", 11, XFontStyle.Regular)
    End Sub
    
    ' this is specialized to print the dictionary of data
    Public Sub WriteDoc(col As Dictionary(Of String, List(Of Visit)))
    
        ' skip page  ??  in OP
        pdf.AddPage()
    
        Dim vl As List(Of Visit)
    
        ' the string key is the location name
        For Each kvp As KeyValuePair(Of String, List(Of Visit)) In col    
    
            vl = kvp.Value
    
            ' this always starts a new page when the location
            ' changes even if there is room.
            ' otherwise, you could just call DrawHeader
            StartNewPage(vl(0).Location, vl(0).VDate.ToShortDateString)
    
            For Each v As Visit In vl
    
                If CurrentY >= 780 Then
                    StartNewPage(v.Location, v.VDate.ToShortDateString)
    
                End If
                PrintLine(v)
            Next
    
        Next
    
        pdf.Save(filePath)
        pdf.Dispose()        ' dispose of resource
    
    End Sub
    
    ' prints one line of data, increments Y 
    Private Sub PrintLine(v As Visit)
        Using g As XGraphics = XGraphics.FromPdfPage(pdfPage)
    
            g.DrawString(String.Format("{0}, {1}/{2}",
                                       v.VDate.DayOfWeek.ToString,
                                       v.VDate.Month, v.VDate.Day),
                         tFont, XBrushes.Black,
                         New XRect(110, CurrentY, pdfPage.Width.Point,
                                   pdfPage.Height.Point),
                         XStringFormats.TopLeft)
    
            g.DrawString(v.Foo.ToString, tFont, XBrushes.Black,
            New XRect(288, CurrentY, pdfPage.Width.Point, pdfPage.Height.Point),
            XStringFormats.TopLeft)
    
            g.DrawString(v.Bar.ToString, tFont, XBrushes.Black,
            New XRect(352, CurrentY, pdfPage.Width.Point, pdfPage.Height.Point),
            XStringFormats.TopLeft)
    
            CurrentY += 20
        End Using         ' dispose of XGraphic object
    
    
    End Sub
    
    ' called when a new page is needed
    ' updates the pdf object var, Resets CurrentY to top of page
    ' and draws the header
    Private Sub StartNewPage(loc As String, dt As String)
        pdfPage = pdf.AddPage()
        CurrentY = 20
        DrawHeader(loc, dt)
    End Sub
    
    Private Sub DrawHeader(loc As String, WeekStart As String)
    
        Using g As XGraphics = XGraphics.FromPdfPage(pdfPage)
    
            g.DrawString("Test Last Hour Comparison for week of " & WeekStart, hFont,
                         XBrushes.Black,
                         New XRect(20, CurrentY, pdfPage.Width.Point, 
                                   pdfPage.Height.Point),
                         XStringFormats.TopLeft)
    
            CurrentY += 40
            g.DrawString(loc, tFont, XBrushes.Black,
                New XRect(20, CurrentY, pdfPage.Width.Point,
                          pdfPage.Height.Point),
                XStringFormats.TopLeft)
    
            g.DrawString("Date", tFont, XBrushes.Black,
                New XRect(120, CurrentY, pdfPage.Width.Point,
                          pdfPage.Height.Point),
                XStringFormats.TopLeft)
    
            g.DrawString("# 6-7 Visits ", tFont, XBrushes.Black,
            New XRect(280, CurrentY, pdfPage.Width.Point,
                      pdfPage.Height.Point),
            XStringFormats.TopLeft)
    
            g.DrawString("# 7-8 Visits ", tFont, XBrushes.Black,
                New XRect(340, CurrentY, pdfPage.Width.Point,
                          pdfPage.Height.Point),
                XStringFormats.TopLeft)
    
            g.DrawString("% Change ", tFont, XBrushes.Black, _
                New XRect(410, CurrentY, pdfPage.Width.Point,
                          pdfPage.Height.Point),
                XStringFormats.TopLeft)
    
        End Using        ' dispose of XGraphic object
    
        CurrentY += 20
    
    End Sub
    
    End Class
    

    In a nutshell, drawing the header/caption is encapsulated in one procedure, starting a new page in another and printing a line of data in another. WriteDoc, which is just a control procedure, loops thru the data simply feeding the current data point (a List item in mine, a ds item in yours) to PrintLine. That control procedure itself wont do you much good. It exists mainly to show how to orchestrate calls to the local methods described to get the result.

    When it sees CurrentY is beyond a certain point, it pauses from feeding lines to call StartNewPage, then continues. The test data does not matter, but calling it from a button click is simple:

    Dim pdf As New PDFReport("C:\Temp\Visits.pdf")
    pdf.WriteDoc(col)     ' col is my dictionary of fake data
    

    Output/page break in flow:

    enter image description here

    Works on My MachineTM

    link to larger image