itextsharp change from pdfwriter to pdfstamper to keep bookmarks in PDF - wpf

I am using iTextSharp to merge PDF documents together. My problem is I am trying to merge a large PDF which contains bookmarks. My current function is using PdfWriter to merge the documents. I know PdfStamper will work, but I cannot figure out how to change the function to work properly.
When I change PdfWriter to PdfStamper in the example below I get an error.
Code Example:
writer = PdfStamper.GetInstance(pdfDoc, New FileStream(outputPath, FileMode.OpenOrCreate))
Error Message:
'GetInstance' is not a member of 'iTextSharp.text.pdf.PdfStamper'
Here is the entire function:
Public Shared Function MergePdfFiles(ByVal pdfFiles() As String, ByVal outputPath As String) As Boolean
Dim result As Boolean = False
Dim pdfCount As Integer = 0
Dim f As Integer = 0
Dim fName As String
Dim reader As iTextSharp.text.pdf.PdfReader = Nothing
Dim pageCount As Integer = 0
Dim pdfDoc As iTextSharp.text.Document = Nothing
Dim writer As PdfWriter = Nothing
Dim cb As PdfContentByte = Nothing
Dim page As PdfImportedPage = Nothing
Dim rotation As Integer = 0
Try
pdfCount = pdfFiles.Length
If pdfCount > 1 Then
fName = pdfFiles(f)
reader = New iTextSharp.text.pdf.PdfReader(fName)
pageCount = reader.NumberOfPages
pdfDoc = New iTextSharp.text.Document(reader.GetPageSizeWithRotation(1), 18, 18, 18, 18)
writer = PdfWriter.GetInstance(pdfDoc, New FileStream(outputPath, FileMode.OpenOrCreate))
With pdfDoc
.Open()
End With
cb = writer.DirectContent
While f < pdfCount
Dim i As Integer = 0
While i < pageCount
i += 1
pdfDoc.SetPageSize(reader.GetPageSizeWithRotation(i))
pdfDoc.NewPage()
page = writer.GetImportedPage(reader, i)
rotation = reader.GetPageRotation(i)
If rotation = 90 Then
cb.AddTemplate(page, 0, -1.0F, 1.0F, 0, 0, reader.GetPageSizeWithRotation(i).Height)
ElseIf rotation = 270 Then
cb.AddTemplate(page, 0, 1.0F, -1.0F, 0, reader.GetPageSizeWithRotation(i).Width + 60, -30)
Else
cb.AddTemplate(page, 1.0F, 0, 0, 1.0F, 0, 0)
End If
End While
f += 1
If f < pdfCount Then
fName = pdfFiles(f)
reader = New iTextSharp.text.pdf.PdfReader(fName)
pageCount = reader.NumberOfPages
End If
End While
pdfDoc.Close()
result = True
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
Return False
End Try
Return result
End Function

You can't just change PdfWriter to PdfStamper. You need to create a stamper with a reader and an output stream:
PdfReader reader = new PdfReader(pathToSrc);
PdfStamper.GetInstance(reader, New FileStream(outputPath, FileMode.OpenOrCreate));
// do stuff
stamper.Close();
You don't need a Document instance if you use PdfStamper; you only need to read the documentation more closely.
All of the above is useless for you, because PdfStamper is the class to use when you manipulate a single file. If you want to merge different file, you need to use PdfCopy or PdfSmartCopy.
Please take a look at the ConcatenateBookmarks example. There's a C# example at the bottom of the page if you don't understand Java.
Browse the official iText website if you have further questions.

Related

creating an array of controls with unknown size at vb.net

So
I need to create an array of labels and I dont know the final size of the array.
I declare it in the class section
Dim myPoints() As Label
in the program I fill the array
Dim l As New Label
l.Width = 4
l.Height = l.Width
l.BackColor = Color.Red
l.Visible = True
l.Left = pointA.X - 2
l.Top = pointA.Y - 2
l.Name = CStr(i)
myPoints(i) = New Label
myPoints(i) = l
AddHandler l.Click, AddressOf l_Click
Panel1.Controls.Add(myPoints(i))
when I run the program I get object reference not set Error
is there a way to to do it with no declaration of the array size ?
Arrays are fixed length collections. If you need a collection that can grow and/or shrink, then use a List(Of T) (documentation)
Dim myPoints = New List(Of Label)()
Dim l = New Label() With {
.Width 4,
.Height = .Width,
.BackColor = Color.Red,
.Visible = True,
.Left = pointA.X - 2,
.Top = pointA.Y - 2,
.Name = i.ToString()
}
myPoints.Add(l)

VB.NET project threading issue

If ServerVersion > localVersion Then
Net.ServicePointManager.DefaultConnectionLimit = 20
Dim url As New Uri(sUrlToReadFileFrom)
Dim request As System.Net.HttpWebRequest = CType(System.Net.WebRequest.Create(url), System.Net.HttpWebRequest)
Dim response As System.Net.HttpWebResponse = CType(request.GetResponse(), System.Net.HttpWebResponse)
response.Close()
Dim iSize As Int64 = response.ContentLength
Dim iRunningByteTotal As Int64 = 0
Using client As New System.Net.WebClient()
Using streamRemote As System.IO.Stream = client.OpenRead(New Uri(sUrlToReadFileFrom))
Using streamLocal As Stream = New FileStream(sFilePathToWriteFileTo, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None)
Dim iByteSize As Integer = 0
Dim byteBuffer(iSize - 1) As Byte
iByteSize = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)
Do While iByteSize > 0
streamLocal.Write(byteBuffer, 0, iByteSize)
iRunningByteTotal += iByteSize
Dim dIndex As Double = CDbl(iRunningByteTotal)
Dim dTotal As Double = CDbl(byteBuffer.Length)
Dim dProgressPercentage As Double = (dIndex / dTotal)
Dim iProgressPercentage As Integer = CInt(Math.Truncate(dProgressPercentage * 100))
bgDownloader.ReportProgress(iProgressPercentage)
iByteSize = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)
Loop
streamLocal.Close()
End Using
streamRemote.Close()
End Using
End If
Using the above code in a BackgroundWorker on a VB.NET WPF Project. An error occurs when I try to start the code.
The calling thread cannot access this object because a different thread owns it.
This code works perfectly in WinForms project without any edits/modifications.
Thanks to #Cruled for providing the clue. It has been fixed.
What I did is add invokes on all things needs updating.

Put form image into array (VB .net)

I put two solid circles (shapes) onto a form, change their colors, and move them around. When I get them into the right position, I want to capture the full-color image of the form and put it into an array as quickly as possible. How can I do this?
Here is how I draw the objects:
I get an OvalShape from VB PowerPacks in VB2010Express and draw it as a circle on the form. After that, I adjust it in code:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
OccultSize1 = 100
OccultSize2 = 50
OvalShape1.Width = OccultSize1
OvalShape1.Height = OccultSize1
OvalShape2.Width = OccultSize2
OvalShape2.Height = OccultSize2
OvalShape1.Left() = 155 - OvalShape1.Width / 2
OvalShape1.Top() = 153 - OvalShape1.Height / 2
OvalShape2.Left() = 155 - OvalShape2.Width / 2
OvalShape2.Top = 85 'temporary
OvalShape1.FillColor = Color.Yellow
OvalShape1.BorderColor = Color.Yellow
OvalShape2.FillColor = Color.Blue
OvalShape2.BorderColor = OvalShape2.FillColor
Label1.Text = "100"
Label2.Text = "50"
Label3.Text = "xxx"
tbHue.Value = OvalShape2.FillColor.GetHue
RadioButton1.Checked = False
RadioButton2.Checked = True
LineShape1.Visible = False
LineShape2.Visible = True
OvalShape3.Visible = False
End Sub
Here is what I have tried with DrawToBitmap (copied most of it from Stack Overflow): Button1.Click is my addition.
Private Function GetFormImage(ByRef GetBitmap As String) As Bitmap
' Make the bitmap.
Dim wid As Integer = Me.Width
Dim hgt As Integer = Me.Height
Dim bm As New Bitmap(wid, hgt)
' Draw the form onto the bitmap.
Me.DrawToBitmap(bm, New Rectangle(0, 0, wid, hgt))
bm.Save("E:\rhtempsave\bm.bmp")
' Make a smaller bitmap without borders.
wid = 240 'Me.ClientSize.Width
hgt = 320 'Me.ClientSize.Height
Dim bm2 As New Bitmap(wid, hgt)
' Get the offset from the window's corner to its client
' area's corner.
Dim pt As New Point(0, 0)
pt = PointToScreen(pt)
Dim dx As Integer = 37 'pt.X - Me.Left
Dim dy As Integer = 10 'pt.Y - Me.Top
' Copy the part of the original bitmap that we want
' into the bitmap.
Dim gr As Graphics = Graphics.FromImage(bm2)
gr.DrawImage(bm, 0, 0, New Rectangle(dx, dy, wid, hgt), _
GraphicsUnit.Pixel)
bm2.Save("E:\rhtempsave\bm2.bmp")
Return bm2
End Function
This is bm2.
For some reason, the bitmaps appeared on my disk today, but not yesterday. The only thing I did in the meantime was shutdown and restart my computer. Strangely, the bmps are not readable by Photoshop, Photoshop Elements or Win10's photo reader yet they show up as icons in the disk file dialog. Probably a header problem. I think I'm good to go with the next step of putting the bmp into an array for later retrieval. Thanks. I'm very grateful for your prompt response.

Compare String Arrays of Different Sizes

I have two string arrays that I want to compare to each other. The one array (sProdList) will always be a set size (16 values), the other (sProd) might be 16, but it might be more or less values. I want to see if sProd contains any of the values in sProdList.
I'm using the below LINQ to try this out:
If Array.Exists(sProd, Function(x As String) sProdList.Contains(x)) Then
The idea being that if the current value of sProd is in sProdList it goes into the If statement, otherwise it skips over it.
When debugging my code I see that the value of x is 24, I'm not sure where it's getting this value from as neither of my arrays have 24 items.
Code is below, any help would be appreciated:
'Separate out Product Names
Dim sProd() As String = {}
Dim sInst() As String = {}
Dim sProdList() As String = {"Auto", "Chequing", "GIC", "Mutual Funds", "Real Estate", "RIF", "RSP", "Savings", "Shares", "Stock", "TFSA", "CCard", "Guar", "Loan", "Loc", "Mortgage"}
sProd = objNode.InnerText.Split(",")
objNode = Nothing
objNode = objXMl.SelectSingleNode("//Inst")
sInst = objNode.InnerText.Split(",")
Dim instPos As Integer = 0
For Each st As String In sProdList
If Array.Exists(sProd, Function(x As String) sProdList.Contains(x)) Then
If sInst(instPos) = "PCCU" Then
img = My.Resources.check
ElseIf sInst(instPos) <> "PCCU" OrElse sInst(instPos) = "" Then
img = My.Resources.exclamation
Else
img = My.Resources.redX
End If
dt.Rows.Add(New Object() {st, imageToByteArray(img)})
instPos += 1
End If
Next
dgProduct.DataSource = dt
You are iterating over sProdList but checking the whole list each time.
You can just use the Linq Contains method to see if sProd contains the string:
For Each st As String In sProdList
If sProd.Contains(st) Then
....
You are iterating sProdList, so you don't want to compare all of them within the loop, but you can use .Intersect to change to loop to just loop over the ones that are in both like this:
For Each st As String In sProdList.Intersect(sProd)
If sInst(instPos) = "PCCU" Then
img = My.Resources.check
ElseIf sInst(instPos) <> "PCCU" OrElse sInst(instPos) = "" Then
img = My.Resources.exclamation
Else
img = My.Resources.redX
End If
dt.Rows.Add(New Object() {st, imageToByteArray(img)})
instPos += 1
Next If
Also, you can further use LINQ to do create your query and feed that directly to dgProduct.DataSource rather than using DataTable as a wrapper like this:
Dim imgs = sInst.Select(Function(i) imageToByteArray(If(i = "PCCU", My.Resources.check, If((i <> "PCCU" OrElse i = ""), My.Resources.exclamation, My.Resources.redX))))
dgProduct.DataSource = sProdList.Intersect(sProd).Zip(imgs, Function(product, image) New From { product, image })

Import CSV into DataGrid

In winForms adding a CSV to a DataGrid was quite easy. I am now trying to add this to a Silverlight DataGrid. Here is my attempt - which yields 3 columns Capacity|Count|Items - mind you the values are correct 83|83|_ on each row. There are 83 rows, but the columns should be 23 with diff values in each. Thanks for looking and enjoy your bounty!
Code:
Try
Dim ofd As New OpenFileDialog
If ofd.ShowDialog Then
If IO.File.Exists(ofd.File.FullName) Then
Dim srsCol As New List(Of List(Of String))
Using fs As IO.FileStream = ofd.File.OpenRead
Using sr As New IO.StreamReader(fs)
While Not sr.Peek = -1
srsCol.Add(New List(Of String)(sr.ReadLine.Split(","c).ToList))
End While
End Using
End Using
dgStaff.ItemsSource = srsCol
End If
End If
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
I decided to use the BindableDataGrid from CodePlex Since the binding is being set dynamically I had to come up with a Random string generator and assign that for the binding and all is well.
csvDs.Tables.Clear()
Try
Dim ofd As New OpenFileDialog
If ofd.ShowDialog Then
If IO.File.Exists(ofd.File.FullName) Then
csvDs.Tables.Add(csvDt)
Using fs As IO.FileStream = ofd.File.OpenRead
Using sr As New IO.StreamReader(fs)
Dim i As Integer
While Not sr.EndOfStream
If i = 0 Then
Dim cols = sr.ReadLine.Split(","c)
For ii As Integer = 0 To cols.Count - 1
Dim rndValue As String = RndColName()
Dim col As New BindableDataGrid.Data.DataColumn(rndValue)
rndValues.Add(rndValue)
col.DataType = GetType(System.String)
col.Caption = ii.ToString
col.ReadOnly = True
col.AllowReorder = False
col.AllowResize = False
col.AllowSort = False
csvDt.Columns.Add(col)
AddItemsToCb(ii)
Next
Dim row As New BindableDataGrid.Data.DataRow
For _i As Integer = 0 To cols.Count - 1
Dim s As String = cols(_i).Replace("""", String.Empty)
row(rndValues(_i)) = s
csvValues.Add(s)
Next
csvDt.Rows.Add(row)
Else
Dim cols = sr.ReadLine.Split(","c)
Dim row As New BindableDataGrid.Data.DataRow
For _i As Integer = 0 To cols.Count - 1
row(rndValues(_i)) = cols(_i).Replace("""", String.Empty)
Next
csvDt.Rows.Add(row)
End If
i += 1
End While
End Using
End Using
dgStaff.DataSource = csvDs
dgStaff.DataMember = "csvTable"
dgStaff.DataBind()

Resources