Imports C1.Web.Api.Storage
Imports System.IO
Imports C1.C1Pdf
Imports System.Drawing

Public Class CustomPdfStorageProvider
    Implements IStorageProvider
    Private _root As String

    Public Sub New()
        _root = GetFullRoot()
    End Sub

    Private Shared Function GetFullRoot() As String
        Dim applicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
        Dim fullRoot = Path.GetFullPath(applicationBase)
        If Not fullRoot.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) Then
            ' When we do matches in GetFullPath, we want to only match full directory names.
            fullRoot += Path.DirectorySeparatorChar
        End If
        Return fullRoot
    End Function

    Public Function GetDirectoryStorage(name As String, Optional args As NameValueCollection = Nothing) As IDirectoryStorage Implements IStorageProvider.GetDirectoryStorage
        Throw New NotImplementedException()
    End Function

    Public Function GetFileStorage(name As String, Optional args As NameValueCollection = Nothing) As IFileStorage Implements IStorageProvider.GetFileStorage
        Return New StreamFileStorage(CreatePdf(name))
    End Function

    Private Function GetFullPath(name As String) As String
        Return Path.Combine(_root, name)
    End Function

    Private Function CreatePdf(name As String) As Stream
        Select Case name
            Case "TextFlow.pdf"
                Return CreateTextFlowPdf()
            Case "TextPosition.pdf"
                Return CreateTextPositionPdf()
            Case "DefaultDocument.pdf"
                Dim stream = New FileStream(GetFullPath("Content/DefaultDocument.pdf"), FileMode.Open, FileAccess.Read)
                Return stream
        End Select

        Return Nothing
    End Function

    Private Function GetPageRect(c1pdf As C1PdfDocument) As RectangleF
        Dim rcPage As RectangleF = c1pdf.PageRectangle
        rcPage.Inflate(-72, -72)
        Return rcPage
    End Function

    Private Function RenderParagraph(c1pdf As C1PdfDocument, text As String, font As Font, rcPage As RectangleF, rc As RectangleF, outline As Boolean, linkTarget As Boolean) As RectangleF
        ' if it won't fit this page, do a page break
        rc.Height = c1pdf.MeasureString(text, font, rc.Width).Height
        If rc.Bottom > rcPage.Bottom Then
            c1pdf.NewPage()
            rc.Y = rcPage.Top
        End If

        ' draw the string
        c1pdf.DrawString(text, font, Brushes.Black, rc)

        ' show bounds (mainly to check word wrapping)
        'c1pdf.DrawRectangle(Pens.Sienna, rc);

        ' add headings to outline
        If outline Then
            c1pdf.DrawLine(Pens.Black, rc.X, rc.Y, rc.Right, rc.Y)
            c1pdf.AddBookmark(text, 0, rc.Y)
        End If

        ' add link target
        If linkTarget Then
            c1pdf.AddTarget(text, rc)
        End If

        ' update rectangle for next time
        rc.Offset(0, rc.Height)
        Return rc
    End Function

    Private Function CreateTextFlowPdf() As Stream
        ' load long string from resource file
        Dim text As String = "Resource not found..."

        Dim path As String = GetFullPath("Content/flow.txt")
        Dim sr As New StreamReader(path)
        text = sr.ReadToEnd()

        text = text.Replace(vbTab, "   ")
        text = String.Format("{0}" & vbCr & vbLf & vbCr & vbLf & "---oOoOoOo---" & vbCr & vbLf & vbCr & vbLf & "{0}", text)

        ' create pdf document
        Dim c1pdf As New C1PdfDocument()
        c1pdf.DocumentInfo.Title = "Text Flow"

        ' add title
        Dim titleFont As New Font("Tahoma", 24, FontStyle.Bold)
        Dim bodyFont As New Font("Tahoma", 9)
        Dim rcPage As RectangleF = GetPageRect(c1pdf)
        Dim rc As RectangleF = RenderParagraph(c1pdf, c1pdf.DocumentInfo.Title, titleFont, rcPage, rcPage, False, False)
        rc.Y += titleFont.Size + 6
        rc.Height = rcPage.Height - rc.Y

        ' create two columns for the text
        Dim rcLeft As RectangleF = rc
        rcLeft.Width = rcPage.Width / 2 - 12
        rcLeft.Height = 300
        rcLeft.Y = (rcPage.Y + rcPage.Height - rcLeft.Height) / 2
        Dim rcRight As RectangleF = rcLeft
        rcRight.X = rcPage.Right - rcRight.Width

        ' start with left column
        rc = rcLeft

        ' render string spanning columns and pages
        While True
            ' render as much as will fit into the rectangle
            rc.Inflate(-3, -3)
            Dim nextChar As Integer = c1pdf.DrawString(text, bodyFont, Brushes.Black, rc)
            rc.Inflate(+3, +3)
            c1pdf.DrawRectangle(Pens.Silver, rc)

            ' break when done
            If nextChar >= text.Length Then
                Exit While
            End If

            ' get rid of the part that was rendered
            text = text.Substring(nextChar)

            ' switch to right-side rectangle
            If rc.Left = rcLeft.Left Then
                rc = rcRight
            Else
                ' switch to left-side rectangle on the next page
                c1pdf.NewPage()
                rc = rcLeft
            End If
        End While

        ' save and show pdf document
        Dim stream = New MemoryStream()
        c1pdf.Save(stream)
        stream.Position = 0
        Return stream
    End Function

    Private Function CreateTextPositionPdf() As Stream
        ' create pdf document
        Dim c1pdf As New C1PdfDocument()
        c1pdf.DocumentInfo.Title = "Text Alignment"

        ' create objects
        Dim sf As New StringFormat()
        Dim titleFont As New Font("Tahoma", 24, FontStyle.Bold)
        Dim font As New Font("Tahoma", 10)
        Dim rcPage As RectangleF = GetPageRect(c1pdf)

        ' render title
        RenderParagraph(c1pdf, c1pdf.DocumentInfo.Title, titleFont, rcPage, rcPage, False, False)

        ' draw cross-hair
        Dim pen As New Pen(Color.Black, 0.1F)
        Dim center As New PointF(rcPage.X + rcPage.Width / 2, rcPage.Y + rcPage.Height / 2)
        c1pdf.DrawLine(pen, center.X, rcPage.Y, center.X, rcPage.Bottom)
        c1pdf.DrawLine(pen, rcPage.X, center.Y, rcPage.Right, center.Y)

        ' draw some text
        Dim text As String = "This is some sample text that will be rendered in rectangles on the page."

        sf.Alignment = StringAlignment.Far
        sf.LineAlignment = StringAlignment.Far
        Dim rc As New RectangleF(center.X - 100, center.Y - 100, 100, 100)
        c1pdf.FillRectangle(Brushes.LightGoldenrodYellow, rc)
        c1pdf.DrawString(Convert.ToString("TOP LEFT: ") & text, font, Brushes.Black, rc, sf)
        c1pdf.DrawRectangle(Pens.Black, rc)

        sf.LineAlignment = StringAlignment.Near
        rc.Offset(0, rc.Height)
        c1pdf.FillRectangle(Brushes.LightSalmon, rc)
        c1pdf.DrawString(Convert.ToString("BOTTOM LEFT: ") & text, font, Brushes.Black, rc, sf)
        c1pdf.DrawRectangle(Pens.Black, rc)

        sf.Alignment = StringAlignment.Near
        rc.Offset(rc.Width, 0)
        c1pdf.FillRectangle(Brushes.LightSeaGreen, rc)
        c1pdf.DrawString(Convert.ToString("BOTTOM RIGHT: ") & text, font, Brushes.Black, rc, sf)
        c1pdf.DrawRectangle(Pens.Black, rc)

        sf.LineAlignment = StringAlignment.Far
        rc.Offset(0, -rc.Height)
        c1pdf.FillRectangle(Brushes.LightSteelBlue, rc)
        c1pdf.DrawString(Convert.ToString("TOP RIGHT: ") & text, font, Brushes.Black, rc, sf)
        c1pdf.DrawRectangle(Pens.Black, rc)

        ' render some rtf as well
        Dim qbf As String = "The quick brown fox jumped over the lazy dog. "
        qbf = Convert.ToString(Convert.ToString(qbf & qbf) & qbf) & qbf
        text = (Convert.ToString((Convert.ToString((Convert.ToString("This is some {\b RTF} text. It will render into the rectangle as usual.\par\par" + "\qr And {\i this paragraph} will be {\b\i right-aligned}. {\fs12 ") & qbf) + "\par\par}" + "\qc And {\i this paragraph} will be {\b\i center-aligned}. {\fs12 ") & qbf) + "\par\par}" + "\qj And {\i this paragraph} will be {\b\i justified}. {\fs12 ") & qbf) + "\par\par Done.}"
        rc.Location = New PointF(rcPage.X + 12, rcPage.Y + 50)
        rc.Size = c1pdf.MeasureStringRtf(text, font, rc.Width * 2)
        c1pdf.FillRectangle(Brushes.DarkBlue, rc)
        c1pdf.DrawStringRtf(text, font, Brushes.White, rc)
        c1pdf.DrawRectangle(Pens.Black, rc)

        ' and some rtf with bullets
        text = "{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0 Arial;}{\f1\fnil\fcharset2 Symbol;}}" + "\viewkind4\uc1\pard\f0\fs20 Here's a bullet list:\par" + "\par" + "\pard{\pntext\f1\'B7\tab}{\*\pn\pnlvlblt\pnf1\pnindent0{\pntxtb\'B7}}\fi-360\li360 Item 1\par" + "{\pntext\f1\'B7\tab}Item 2\par" + "{\pntext\f1\'B7\tab}Item 3\par" + "{\pntext\f1\'B7\tab}Item 4\par" + "\pard\par" + "}"
        rc.Location = New PointF(rcPage.X + 250, rcPage.Y + 50)
        rc.Size = c1pdf.MeasureStringRtf(text, font, rc.Width)
        c1pdf.DrawStringRtf(text, font, Brushes.White, rc)
        c1pdf.DrawRectangle(Pens.Black, rc)

        ' box the whole thing
        pen = New Pen(Brushes.Black, 1)
        pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot
        c1pdf.DrawRectangle(pen, rcPage)

        ' save and show pdf document
        Dim stream = New MemoryStream()
        c1pdf.Save(stream)
        stream.Position = 0
        Return stream
    End Function
End Class