Private Doc As New PrintDocument()
This is where the problem started.
AddHandler Doc.PrintPage, AddressOf Print_PrintPage
This is where you nailed yourself. The As New
syntax is pretty handy, but it will create an object just once. Problem is, only the first call to printInvoice creates the PrintDocument object. But every time you call printInvoice(), you add another PrintPage event handler to the same object. The net effect is that the second time you print, your PrintPage event handler will run twice for each page. The third time it will run three times. Etcetera. The Dispose() method doesn't otherwise do anything, a PrintDocument doesn't use disposable resources. Setting it to Nothing would fix the problem.
You fix this by just creating a new PrintDocument object every time you print. So
Private Doc As PrintDocument
Public Sub printInvoice()
Try
copy = 1
Doc = new PrintDocument()
AddHandler Doc.PrintPage, AddressOf Print_PrintPage
ItemsRowCount = Invoice.dgvInvoiceItems.RowCount
Doc.Print()
'' etc...
Further improve this code by making "Doc" a local variable. Or moving the code into a class.
Note how your ItemsRowCount variable has the same problem. It might be initialized too early, storing the wrong row count. And if you print again, later, with a different invoice then you'll definitely get the wrong number of rows.
Be careful with global variables, they have a knack for causing problems like this.