'
' Block starter:
' Import a PDF page containing Blocks and fill text and image
' Blocks with some data. For each recipient of the simulated
' mailing a separate page with personalized information is
' generated.
'
' The Textflow Blocks are processed with variable text lengths in mind:
' if a Block doesn't fully use its vertical space or requires excess
' space, the next Block is moved up or down accordingly.
'
' Required software: PDFlib Personalization Server (PPS)
' Required data: Block PDF (template), images, font
'

Imports System
Imports System.Text
Imports PDFlib_dotnet


Structure Block                ' Description of a single text or image Block
    Public name As String
    Public contents() As String

    ' Constructor
    Public Sub New(ByVal s_name As String, ByVal s_contents() As String)
        ' Block name
        name = s_name
        ' text Block: array with a string for each recipient
        ' image Block: array with image file name for each recipient
        contents = s_contents
    End Sub
End Structure

Module starter_block
    Sub Main()

        ' This is where the data files are. Adjust as necessary.
        Dim searchpath As String = "../../../data"
        ' By default annotations are also imported. In some cases this
        ' requires the Noto fonts for creating annotation appearance streams.
        ' We therefore set the searchpath to also point to the font directory.
        '
        Dim fontpath = "../../../../resource/font"
        Dim outfile As String = "starter_block.pdf"
        Dim infile As String = "block_template.pdf"

        Dim p As PDFlib = Nothing

        ' Names and contents of text  Blocks
        Dim TextBlocks() As Block = { _
            New Block( _
                "recipient", _
                 { _
                    "Mr Maurizio Moroni" & Environment.NewLine & "Strada Provinciale 124" & _
                    Environment.NewLine & "Reggio Emilia", _
 _
                    "Ms Dominique Perrier" & Environment.NewLine & "25, Rue Lauriston" & _
                    Environment.NewLine & "Paris", _
 _
                    "Mr Liu Wong" & Environment.NewLine & "55 Grizzly Peak Rd." & _
                    Environment.NewLine & "Butte" _
                }), _
 _
            New Block( _
                "announcement", _
                { _
                    "Dear Paper Planes Fan," & Environment.NewLine & Environment.NewLine & _
                    "we are happy to present our <fillcolor=red>best price offer" & _
                    "<fillcolor=black> especially for you:" & Environment.NewLine, _
 _
                    "Bonjour," & Environment.NewLine & Environment.NewLine & _
                    "here comes your personal <fillcolor=red>best price offer:" & Environment.NewLine, _
 _
                    "Dear Paper Folder," & Environment.NewLine & Environment.NewLine & _
                    "here's another exciting new paper plane especially for you. " & _
                    "We can supply this <fillcolor=red>best price offer" & _
                    "<fillcolor=black> only for a limited time and in limited quantity. " & _
                    "Don't hesitate and order your new plane today!" & Environment.NewLine _
                }), _
 _
            New Block("specialoffers", _
                { _
                    "<fillcolor=red>Long Distance Glider<fillcolor=black>" & Environment.NewLine & _
                    "With this paper rocket you can send all your " & _
                    "messages even when sitting in a hall or in the cinema pretty near " & _
                    "the back." & Environment.NewLine, _
 _
                    "<fillcolor=red>Giant Wing<fillcolor=black>" & Environment.NewLine & _
                    "An unbelievable sailplane! It is amazingly robust and " & _
                    "can even do aerobatics. But it is best suited to gliding." & Environment.NewLine & _
                    "This paper arrow can be thrown with big swing. " & _
                    "We launched it from the roof of a hotel. It stayed in the air a " & _
                    "long time and covered a considerable distance." & Environment.NewLine, _
 _
                    "<fillcolor=red>Super Dart<fillcolor=black>" & Environment.NewLine & _
                    "The super dart can fly giant loops with a radius of 4 " & _
                    "or 5 meters and cover very long distances. Its heavy cone point is " & _
                    "slightly bowed upwards to get the lift required for loops." & Environment.NewLine _
                }), _
 _
            New Block("goodbye", _
                { _
                    "Visit us on our Web site at <fillcolor=blue>www.kraxi.com<fillcolor=black>!" & _
                     Environment.NewLine & Environment.NewLine & _
                    "Yours sincerely," & Environment.NewLine & "Victor Kraxi", _
 _
                    "Make sure to order quickly at <fillcolor=blue>www.kraxi.com<fillcolor=black> " & _
                    "since we will soon run out of stock!" & Environment.NewLine & Environment.NewLine & _
                    "Yours sincerely," & Environment.NewLine & "Victor Kraxi", _
 _
                    "Make sure to order soon at <fillcolor=blue>www.kraxi.com<fillcolor=black>!" & _
                    Environment.NewLine & Environment.NewLine & _
                    "Your friendly staff at Kraxi Systems Paper Planes" _
                }) _
            }

        ' Names and contents of image Block(s)
        Dim ImageBlocks() As Block = { _
            New Block("icon", _
                { _
                    "plane1.png", _
                    "plane2.png", _
                    "plane3.png" _
                }) _
        }

        Try
            ' create a new PDFlib object
            p = New PDFlib()

            Dim no_of_input_pages As Integer
            Dim indoc As Integer
            Dim pageno As Integer
            Dim recipient As Integer
            Dim number_of_recipients As Integer = 3

            ' This means we must check return values of load_font() etc.
            ' Set the search path for fonts and images etc.
            p.set_option("errorpolicy=return SearchPath={{" & searchpath & "}} escapesequence=true")

            p.set_option("SearchPath={{" & searchpath & "}}")

            If (p.begin_document(outfile,
                "destination={type=fitwindow} pagelayout=singlepage") = -1) Then
                Throw New Exception("Error: " & p.get_errmsg())
                Return
            End If


            p.set_info("Creator", "PDFlib starter sample")
            p.set_info("Title", "starter_block")

            ' Open the Block template with PDFlib Blocks
            indoc = p.open_pdi_document(infile, "")
            If (indoc = -1) Then
                Throw New Exception("Error: " & p.get_errmsg())
            End If

            no_of_input_pages = CInt(p.pcos_get_number(indoc, "length:pages"))

            ' Preload all pages of the Block template. We assume a small
            ' number of input pages and a large number of generated output
            ' pages. Therefore it makes sense to keep the input pages
            ' open instead of opening them again for each recipient.
            ' Add 1 because we use the 1-based page number as array index.

            Dim pagehandles(no_of_input_pages) As Integer

            For pageno = 1 To no_of_input_pages Step 1
                ' Open template page and prepare it for cloning the page size
                pagehandles(pageno) = p.open_pdi_page(indoc, pageno, "cloneboxes")

                If (pagehandles(pageno) = -1) Then
                    Throw New Exception("Error: " & p.get_errmsg())
                End If
            Next

            ' For each recipient: place template page(s) and fill Blocks
            For recipient = 0 To number_of_recipients - 1 Step 1

                ' Process all pages of the template document
                For pageno = 1 To no_of_input_pages Step 1
                    ' Accumulated unused or excess space in Textflow Blocks:
                    ' - if positive, the previous Blocks didn't use their space, so
                    '   we move up the Block
                    ' - if negative, the previous Blocks used excess space, so
                    '   we move down the Block
                    Dim accumulated_offset_y As Double
                    accumulated_offset_y = 0

                    ' Start the next output page. The page size will be
                    ' replaced with the cloned size of the input page.
                    p.begin_page_ext(0, 0, "width=a4.width height=a4.height")

                    ' Place the imported page on the output page, and clone all
                    ' page boxes which are present in the input page; this will
                    ' override the dummy size used in begin_page_ext().
                    p.fit_pdi_page(pagehandles(pageno), 0, 0, "cloneboxes")

                    ' Process all Textflow Blocks
                    For Each textblock As Block In TextBlocks

                        ' The Textflow Blocks in the template use "fitmethod=nofit"
                        ' which means we allow the Textflow to overflow the Block.
                        Dim baseopt As String = ""
                        Dim optlist As String = baseopt

                        ' pCOS path for the current Block 
                        Dim blockpath As String = "pages[" & (pageno - 1) & "]/blocks/" & textblock.name

                        ' Retrieve y coordinate of the Block's lower left corner
                        Dim lly As Double = p.pcos_get_number(indoc, blockpath & "/Rect[1]")

                        ' Adjust the vertical Block position by accumulated offset
                        ' and make sure we don't fall off the bottom page edge.
                        ' Similarly we could adjust the horizontal position.
                        Dim adjusted_lly As Double
                        adjusted_lly = lly + accumulated_offset_y
                        If (adjusted_lly < 0) Then
                            Throw New Exception("Error for recipient " & recipient & " (input page " & pageno & "): " &
                                "Too much text in Textflow Blocks")
                        End If

                        ' The "refpoint" option overrides the Blocks's original
                        ' position. We use relative coordinates (suffix "r") to move
                        ' the Block up or down if the previous Blocks didn't use up
                        ' their area or exceeded the Block area.
                        optlist += " refpoint={ 0r " & accumulated_offset_y & "r }"

                        ' Create a matchbox for the Block contents, using the Block name as matchbox name
                        optlist += " matchbox={name={" & textblock.name & "}}"

                        ' Fill the text Block.
                        If (p.fill_textblock(pagehandles(pageno), textblock.name,
                            textblock.contents(recipient), optlist) = -1) Then
                            Console.WriteLine("Warning: " & p.get_errmsg() & Environment.NewLine)
                        Else
                            ' Calculate the height which wasn't used inside the Block
                            ' or was used in excess outside the Block. This will be used
                            ' for adjusting the position of the next Block.

                            If (p.info_matchbox(textblock.name, 1, "exists") = 1) Then
                                ' We successfully filled a Textflow Block. Retrieve the bottom edge
                                ' of the matchbox which gives the vertical end position of the contents...
                                Dim content_lly As Double = p.info_matchbox(textblock.name, 1, "y1")     ' (x1, y1) = lower left corner

                                ' ...and use the distance from the bottom edge of the Block as offset
                                accumulated_offset_y += (content_lly - adjusted_lly)
                            Else
                                ' If the Block is empty, no corresponding matchbox exists.
                                ' We use the Block height as offset to skip the whole Block,
                                ' not taking into account possible space between the Blocks.
                                Dim ury As Double = p.pcos_get_number(indoc, blockpath + "/Rect[3]")
                                accumulated_offset_y += (ury - lly)
                            End If

                        End If
                    Next

                    ' Process all image Blocks
                    For Each imageblock As Block In ImageBlocks
                        Dim image As Integer = p.load_image("auto", imageblock.contents(recipient), "")
                        If (image = -1) Then
                            Throw New Exception("Error: " & p.get_errmsg())
                        End If

                        ' Fill image Block
                        If (p.fill_imageblock(pagehandles(pageno), imageblock.name, image, "") = -1) Then
                            Console.WriteLine("Warning: " & p.get_errmsg())
                            p.close_image(image)
                        End If
                    Next

                    p.end_page_ext("")
                Next
            Next

            ' Close the preloaded template pages
            For pageno = 1 To no_of_input_pages Step 1
                p.close_pdi_page(pagehandles(pageno))
            Next
            p.close_pdi_document(indoc)

            p.end_document("")

        Catch e As PDFlibException
            Console.Error.WriteLine("PDFlib exception occurred:")
            Console.Error.WriteLine("[{0}] {1}: {2}", e.get_errnum(), e.get_apiname(), e.get_errmsg)
        Catch e As System.Exception
            Console.Error.WriteLine(e.ToString())
        Finally
            If Not p Is Nothing Then
                p.Dispose()
                p = Nothing
            End If
        End Try
    End Sub
End Module
