vb.net und die Pdf Erstellung

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    vb.net und die Pdf Erstellung

    Holladiho
    Ich quäle mich nun seid einiger Zeit mit dem Thema pdf Erstellung herum, und habe verschiedene dlls ausprobiert - und das Thema dann erstmal beiseite geschobben.
    Nun ist die Erstellung der pdf das Einzige, was an meinem Programm zur Fertigstellung fehlt. Also komme ich nicht mehr drumherum.
    Durch Zufall bin ich auf folgenden Blog gestoßen: emoreau.com/Entries/Articles/2…-PDF-from-Net-easily.aspx

    Diese library, bzw. das Demoprojekt dazu habe ich zumindest Ansatzweise (echt nur im Ansatz) verstanden. Jedoch verstehe ich die Positionierung des Headers und des Footers nicht.
    Auch die Erstellung des Headers und Footers ist mir nicht ganz begreiflich.
    Hier erstmal der Code des Demoprojektes:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports PdfFileWriter
    3. Public Class Form1
    4. Inherits Form
    5. Private _document As PdfDocument
    6. Private _page As PdfPage
    7. Private _contents As PdfContents
    8. Private _normalFont As PdfFont
    9. Private _tableTitleFont As PdfFont
    10. Private _pageNumber As Integer
    11. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    12. listBox1.Items.Add("Starting the process")
    13. 'create a source of test data
    14. listBox1.Items.Add("Creating the data source...")
    15. Dim dt As DataTable = CreateDataTable()
    16. dataGridView1.DataSource = dt
    17. 'Create empty document and some fonts
    18. listBox1.Items.Add("Creating the document and some properties...")
    19. _document = New PdfDocument(PaperType.Letter, False, UnitOfMeasure.Inch, "Test.pdf")
    20. _normalFont = PdfFont.CreatePdfFont(_document, "Arial", FontStyle.Regular, True)
    21. _tableTitleFont = PdfFont.CreatePdfFont(_document, "Times New Roman", FontStyle.Bold, True)
    22. 'Generate the PDF file
    23. listBox1.Items.Add("Generate the PDF file...")
    24. GeneratePDF(dt)
    25. 'Complete the process
    26. listBox1.Items.Add("Closing the file...")
    27. _document.CreateFile()
    28. 'start default PDF reader and display the file
    29. listBox1.Items.Add("Opening the newly generated file...")
    30. Process.Start("Test.pdf")
    31. listBox1.Items.Add("Process is completed")
    32. End Sub
    33. Private Function CreateDataTable() As DataTable
    34. Using dt As DataTable = New DataTable("Products")
    35. dt.Columns.Add("ID", GetType(Integer))
    36. dt.Columns.Add("Code", GetType(String))
    37. dt.Columns.Add("Description", GetType(String))
    38. dt.Columns.Add("Price", GetType(Decimal))
    39. Dim rand As Random = New Random()
    40. For i As Integer = 1 To 222
    41. dt.LoadDataRow(New Object() {i, "Item" & i.ToString("0000"), $"Description of item {i}", Math.Round(100 * rand.NextDouble(), 2)}, True)
    42. Next
    43. Return dt
    44. End Using
    45. End Function
    46. Public Sub GeneratePDF(ByVal pDataTable As DataTable)
    47. Const columnID As Integer = 0
    48. Const columnCode As Integer = 1
    49. Const columnDesc As Integer = 2
    50. Const columnPrice As Integer = 3
    51. ' Add new page
    52. _page = New PdfPage(_document)
    53. ' Add contents to page
    54. _contents = New PdfContents(_page)
    55. ' create table
    56. Dim table As PdfTable = New PdfTable(_page, _contents, _normalFont, 9.0)
    57. table.SetColumnWidth(1.0, 1.0, 2.0, 1.0)
    58. ' event handlers for headers and footers
    59. AddHandler table.TableStartEvent, AddressOf CreateTableStart
    60. AddHandler table.TableEndEvent, AddressOf CreateTableEnd
    61. table.HeaderOnEachPage = True
    62. ' set all borders
    63. table.Borders.SetAllBorders(0.012, Color.DarkGray, 0.0025, Color.DarkGray)
    64. ' define the header
    65. table.DefaultHeaderStyle.Alignment = ContentAlignment.MiddleCenter
    66. table.Header(columnID).Value = "ID"
    67. table.Header(columnCode).Value = "Code"
    68. table.Header(columnDesc).Value = "Description"
    69. table.Header(columnPrice).Value = "Price"
    70. ' make some changes to default cell style
    71. table.DefaultCellStyle.Alignment = ContentAlignment.MiddleLeft
    72. ' create private styles for price column
    73. Dim normalPriceStyle As PdfTableStyle = table.CellStyle
    74. normalPriceStyle.Alignment = ContentAlignment.MiddleRight
    75. normalPriceStyle.Format = "#,##0.00"
    76. Dim highPriceStyle As PdfTableStyle = table.CellStyle
    77. highPriceStyle.BackgroundColor = Color.Red
    78. highPriceStyle.ForegroundColor = Color.White
    79. highPriceStyle.Alignment = ContentAlignment.MiddleRight
    80. highPriceStyle.Format = "#,##0.00"
    81. 'loop through each rows
    82. For Each row As DataRow In pDataTable.Rows
    83. table.Cell(columnID).Value = row("ID")
    84. table.Cell(columnCode).Value = row("Code")
    85. table.Cell(columnDesc).Value = row("Description")
    86. Dim decPrice As Decimal
    87. Decimal.TryParse(row("Price").ToString(), decPrice)
    88. table.Cell(columnPrice).Value = decPrice
    89. table.Cell(columnPrice).Style = If(decPrice > 80, highPriceStyle, normalPriceStyle)
    90. table.DrawRow()
    91. Next
    92. table.Close()
    93. End Sub
    94. Private Sub CreateTableStart(ByVal pPdfTable As PdfTable, ByVal pTableStartPos As Double)
    95. 'Header mitte
    96. Dim posX As Double = 0.5 * (pPdfTable.TableArea.Left + pPdfTable.TableArea.Right)
    97. Dim posY As Double = pTableStartPos + _tableTitleFont.Descent(16.0) + 0.05
    98. pPdfTable.Contents.DrawText(_tableTitleFont, 16.0, posX, posY, TextJustify.Center, DrawStyle.Normal, Color.Blue, "Header Mitte")
    99. 'Header links
    100. posX = pPdfTable.TableArea.Left
    101. posY = pTableStartPos + _tableTitleFont.Descent(16.0) + 0.05
    102. pPdfTable.Contents.DrawText(_tableTitleFont, 16.0, posX, posY, TextJustify.Center, DrawStyle.Normal, Color.Blue, "Header Links")
    103. 'Header rechts
    104. posX = pPdfTable.TableArea.Right
    105. posY = pTableStartPos + _tableTitleFont.Descent(16.0) + 0.7
    106. pPdfTable.Contents.DrawText(_tableTitleFont, 16.0, posX, posY, TextJustify.Center, DrawStyle.Normal, Color.Blue, "Header rechts")
    107. End Sub
    108. Private Sub CreateTableEnd(ByVal pPdfTable As PdfTable, ByVal pTableEndPos As Double)
    109. Dim posX As Double = pPdfTable.TableArea.Left
    110. Dim posY As Double = pTableEndPos - _tableTitleFont.Ascent(12.0) + 0.5
    111. _pageNumber += 1
    112. pPdfTable.Contents.DrawText(_tableTitleFont, 12.0, posX, posY, TextJustify.Left, DrawStyle.Normal, Color.Black, "page " & _pageNumber)
    113. End Sub
    114. End Class


    Meine Versuche der Headerausrichtung, findet ihr in der vorletzen Sub - CreateTableStart - hier verstehe ich nicht, wie die PosY auf dem Blatt dargestellt wird.
    1. Frage Wenn ich z.B. reinschreibe, bedeutet das doch 2,5cm, müsste also mit cm Abstand vom oberen Rand dargestellt werden. WIrds aber nicht - ist unten. Warum?
    Auf der Codesource Seite des Projektes heißt es:
    2.1. Coordinate system and Unit of Measure
    The PDF coordinate system origin is at the bottom left corner of the page. The X-axis is pointing to the right. The Y-axis is pointing in upward direction.

    Der Punkt 0,0 müsste doch demnach die obere linke ecke sein. PosY = 1 sind dann also 2,54cm vom linken Rand. Stimmt aber nich.

    2. Frage, Wie stelle ich es an eine zweite Headerzeile direkt unter der ersten darzustellen? Muss ich hier die Position angeben, oder gibt es auch etwas wie einen Zeilenumbruch?

    3.Frage: Wie funktioniert die Headererstellung an sich? Mir ist die Sache mit dem Event nicht ganz begreiflich.
    Dateien

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „DerSmurf“ ()

    @DerSmurf Der druckt genau da hin, wohin er drucken soll.

    VB.NET-Quellcode

    1. posY = pTableStartPos + _tableTitleFont.Descent(16.0) + 0.7
    machst Du

    VB.NET-Quellcode

    1. posY = pTableStartPos + _tableTitleFont.Descent(16.0) + 0.07
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ah. Da steht bottom left. Sorry, hatte ich überlesen.
    Wie bekomme ich nun eine zweite Header Zeile? Muss ich die Höhe der ersten errechnen und direkt darunter eine zweite einfügen, oder geht das einfacher?
    Und könnte mir jemand das erstellen der Header und Footer aus dem Beispiel erklären? Ich habe noch nicht ganz kapiert, was da passiert.
    @DerSmurf Header und Footer sind doch nur Namen für Druckbereiche bzw. beim Druck auszusparende Bereiche.
    Ich sehe da die Property MinHeaderHeight:
    Du legst fest, dass Zeile 1 bei posY1 = ZEILE_1
    und Zeile 2 bei posY2 = ZEILE_2 steht.
    Der normale Text beginnt dann einfach bei posY3 >= MinHeaderHeight.
    Für den Footer sehe ich da kein Äquivalent. :/
    ===
    Gibt es zu der DLL eine XML, in der die Parameter beschrieben sind?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Holladiho
    Ich habe mir nun die gesamte Beschreibung der dll auf Codeproject zu Gemüte geführt. Aus verständnissgründen drei mal.
    Einiges ist mir klar geworden.
    So habe ich z.B. das Format meiner pdf auf Dina4 und die "UnitofMeasure" auf mm gestellt:
    _document = New PdfDocument(PaperType.A4, False, UnitOfMeasure.mm, "Test.pdf")
    Dann hat mir Google verraten, dass ein DinA4 Zettel die Maße 210mm x 297mm hat. Damit habe ich nun Header und Footer hübsch ausgerichtet:
    (Code ist noch schlect - das ganze Zeilenweise aufzubauen, sollte wohl besser sein - aber geht erstmal):

    VB.NET-Quellcode

    1. Private Sub CreateTableStart(ByVal pPdfTable As PdfTable, ByVal pTableStartPos As Double)
    2. 'Header mitte / 1. Zeile
    3. Dim posX As Double = 105 ' 0.5 * (pPdfTable.TableArea.Left + pPdfTable.TableArea.Right)
    4. Dim posY As Double = 290
    5. pPdfTable.Contents.DrawText(_normalFont, 10.0, posX, posY, TextJustify.Center, DrawStyle.Normal, Color.Black, "Tel. 02710 - 20072510")
    6. 'Header mitte / 2. Zeile
    7. posX = 105 '0.5 * (pPdfTable.TableArea.Left + pPdfTable.TableArea.Right)
    8. posY = 290 - 4
    9. pPdfTable.Contents.DrawText(_normalFont, 10.0, posX, posY, TextJustify.Center, DrawStyle.Normal, Color.Black, "Ansprechp. Max Musterkerl")
    10. 'Header links / 1.Zeile
    11. posX = 5
    12. posY = 290
    13. pPdfTable.Contents.DrawText(_normalFont, 10.0, posX, posY, TextJustify.Left, DrawStyle.Normal, Color.Black, "Max Musterfirma")
    14. 'Header links / 2.Zeile
    15. posX = 5
    16. posY = 290 - 4
    17. pPdfTable.Contents.DrawText(_normalFont, 10.0, posX, posY, TextJustify.Left, DrawStyle.Normal, Color.Black, "Musterstraße 27")
    18. 'Header links / 3. Zeile
    19. posX = 5
    20. posY = 290 - 8
    21. pPdfTable.Contents.DrawText(_normalFont, 10.0, posX, posY, TextJustify.Left, DrawStyle.Normal, Color.Black, "27101 Musterstadt")
    22. 'Header rechts / 1. Zeile
    23. posX = 205 'pPdfTable.TableArea.Right
    24. posY = 290
    25. pPdfTable.Contents.DrawText(_normalFont, 10.0, posX, posY, TextJustify.Right, DrawStyle.Normal, Color.Black, "K.Nr 2710")
    26. End Sub
    27. Private Sub CreateTableEnd(ByVal pPdfTable As PdfTable, ByVal pTableEndPos As Double)
    28. Dim posX As Double = 205 'pPdfTable.TableArea.Left
    29. Dim posY As Double = 4 'pTableEndPos - _normalFont.Ascent(12.0) + 0.5
    30. _pageNumber += 1
    31. pPdfTable.Contents.DrawText(_normalFont, 10.0, posX, posY, TextJustify.Right, DrawStyle.Normal, Color.Black, "Seite " & _pageNumber)
    32. End Sub


    Nun möchte ich noch (wenn mit nicht zu viel Aufwand möglich - das pdf Thema bringt mich allmählich zur Weißglut) eine Gesamtseitenzahl einfügen.
    Also nicht nur "Seite 1", sondern Seite 1 von [GesamtzahlDerSeiten]. Um dies umzusetzen, müsste ich aber die Erstellung von Header und Footer verstehen. Dabei brauche ich Hilfe, denn das raffe ich nicht.
    Also diese Stelle in der Sub GeneratePdf:

    VB.NET-Quellcode

    1. ' event handlers for headers and footers
    2. AddHandler table.TableStartEvent, AddressOf CreateTableStart
    3. AddHandler table.TableEndEvent, AddressOf CreateTableEnd
    4. table.HeaderOnEachPage = True

    Hier werden zwei Events erzeugt, "TableStartEvent" und "TableEndEvent". Aber es wird ja nur eine Table im Code erzeugt (das erstellen neuer Seiten, wenn Seite voll, scheint ja automatisch zu passieren).
    Aber was genau passiert hier an dieser Stelle Code? Und warum macht dieser Code einen Header und einen Footer auf jede Seite?
    Und warum wird nicht ein entsprechends Event erstellt, dass HEader und Footer erstellt, wenn eine neue pdf Seite erstellt wird? Wäre doch irgendwie logischer?
    Dateien
    @DerSmurf Das ist so ne Sache.
    Ich arbeite lieber mit der .NET-Funktionalität: Drucken mehrseitiger Dokumente
    da kannst Du im PreView den Ausdruck ansehen und während der Entwicklung elegant manipulieren.
    Beim Druck in die Vorschau kannst Du die Anzahl der Seiten ermitteln und beim finalen Druck mit ausgeben.
    Dann druckst Du in eine PDF und feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Huhu
    Danke für den Link und Danke für dieses - sehr anfängerfreundliche - Tutorial.
    Dieses werde ich an anderer Stelle in mein Programm einbauen, und zwar zum Drucken meiner EinnahmeStatistik. Letzlich zum Druck eines DGV.
    An dieser Stelle wäre der PrintPreviewDialog allerdings eher störend.
    Da das Programm an dieser Stelle immer eine pdf Drucken soll (um Mailversand), und der User sich diese in den seltensten Fällen ernsthaft ansieht.