GoodsReturnForm.vb
''
'' This code is part of Document Solutions for PDF demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports GrapeCity.Documents.Common
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Pdf.Annotations
Imports GrapeCity.Documents.Pdf.AcroForms
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing

'' Creates a "Goods return or exchange form" AcroForm with multiple input fields And a complex layout.
Public Class GoodsReturnForm
    '' Page margins:
    Const MarginLeft = 32.0F
    Const MarginTop = 32.0F
    Const MarginRight = 32.0F
    Const MarginBottom = 32.0F
    ''
    Const TableCaptionHeight = 20.0F
    ReadOnly TableSampleHeight As Single = Textbox.Height
    Const SmallTextVOff As Single = -0.5F
    '' Section delimiting line:
    Dim CaptionLineThickness As Single = 2.5F
    '' Struct to hold a text style:
    Private Structure TextStyle
        Public Property Font As GCTEXT.Font
        Public Property FontSize As Single
        Public Property ForeColor As Color
        Public Property GlyphAdvanceFactor As Single
    End Structure
    '' Various styles used throughout the form:
    Shared TsTitle As TextStyle = New TextStyle() With {
            .Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "SitkaB.ttc")),
            .FontSize = 30,
            .ForeColor = Color.FromArgb(&HFF, &H3B, &H5C, &HAA),
            .GlyphAdvanceFactor = 0.93F
        }
    Shared TsCaption As TextStyle = New TextStyle() With {
        .Font = TsTitle.Font,
        .FontSize = 14,
        .ForeColor = Color.FromArgb(&HFF, &H3B, &H5C, &HAA),
        .GlyphAdvanceFactor = 0.93F
    }
    Shared TsBold As TextStyle = New TextStyle() With {
        .Font = GrapeCity.Documents.Text.Font.FromFile(Path.Combine("Resources", "Fonts", "arialbd.ttf")),
        .FontSize = 9,
        .ForeColor = Color.Black,
        .GlyphAdvanceFactor = 1
    }
    Shared TsNormal As TextStyle = New TextStyle() With {
        .Font = GrapeCity.Documents.Text.Font.FromFile(Path.Combine("Resources", "Fonts", "arial.ttf")),
        .FontSize = 8.0F,
        .ForeColor = Color.Black,
        .GlyphAdvanceFactor = 0.922F
    }
    Shared TsSmall As TextStyle = New TextStyle() With {
        .Font = TsNormal.Font,
        .FontSize = 5,
        .ForeColor = Color.FromArgb(&HF, &HF, &HF),
        .GlyphAdvanceFactor = 1.1F
    }
    '' Input field styles:
    Private Structure Textbox
        Public Shared Property Font As GCTEXT.Font = TsNormal.Font
        Public Shared Property FontSize As Single = 12
        Public Shared Property Height As Single
        Public Shared Property BaselineOffset As Single
        Public Shared Property LabelSpacing As Single = 2
    End Structure
    Private Structure Checkbox
        Public Shared Font As GCTEXT.Font = TsNormal.Font
        Public Shared FontSize As Single = TsNormal.FontSize - 2
        Public Shared Height As Single
        Public Shared BaselineOffset As Single
        Public Shared LabelSpacing As Single = 3
    End Structure
    '' The document being created:
    Private _doc As GcPdfDocument
    '' Insertion point:
    Private _ip As PointF = New PointF(MarginLeft, MarginTop)
    '' If non-null, DrawText use this to align text to last baseline:
    Private _lastBaselineOffset As Single? = Nothing
    '' Shortcuts to current values:
    Private ReadOnly Property CurrPageIdx As Integer
        Get
            Return _doc.Pages.Count - 1
        End Get
    End Property
    Private ReadOnly Property CurrPage As Page
        Get
            Return _doc.Pages(CurrPageIdx)
        End Get
    End Property

    Private ReadOnly Property CurrGraphics As GcGraphics
        Get
            Return CurrPage.Graphics
        End Get
    End Property
    '' Static ctor:
    Shared Sub New()
        '' Init Textbox:
        Dim tl = New TextLayout(72)
        tl.Append("Qwerty")
        tl.DefaultFormat.Font = Textbox.Font
        tl.DefaultFormat.FontSize = Textbox.FontSize
        tl.PerformLayout(True)
        Textbox.Height = tl.ContentHeight
        Textbox.BaselineOffset = tl.Lines(0).GlyphRuns(0).BaselineOffset
        '' Init Checkbox:
        tl.Clear()
        tl.Append("Qwerty")
        tl.DefaultFormat.Font = Checkbox.Font
        tl.DefaultFormat.FontSize = Checkbox.FontSize
        tl.PerformLayout(True)
        Checkbox.Height = tl.ContentHeight
        Checkbox.BaselineOffset = tl.Lines(0).GlyphRuns(0).BaselineOffset
    End Sub
    '' The main entry point:
    Function CreatePDF(ByVal stream As Stream) As Integer
        Acme()
        _doc.Save(stream)
        Return _doc.Pages.Count
    End Function
    '' Sets or advances the insertion point vertically:
    Private Sub SetY(ByVal abs As Single?, ByVal offset As Single?)
        If (abs.HasValue) Then
            _ip.Y = abs.Value
        End If
        If (offset.HasValue) Then
            _ip.Y += offset.Value
        End If
        _lastBaselineOffset = Nothing
    End Sub
    '' Creates the PDF form:
    Private Sub Acme()
        _doc = New GcPdfDocument()
        _doc.NewPage()
        Dim pageWidth = CurrPage.Size.Width

        '' Main caption:
        SetY(Nothing, -2)
        Dim cr = DrawText("ACME Inc.", TsTitle)
        SetY(Nothing, _lastBaselineOffset - CaptionLineThickness / 2)
        DrawGreenLine(MarginLeft, cr.Left - CaptionLineThickness)
        DrawGreenLine(cr.Right + CaptionLineThickness, pageWidth - MarginRight)

        '' 'return and exchange form':
        SetY(cr.Bottom, 10)
        cr = DrawText("Return and Exchange Form", TsCaption)

        SetY(Nothing, CaptionLineThickness + 14)
        cr = DrawText("Please type in the appropriate information below, then print this form.", TsBold)
        _ip.X = pageWidth - 150
        cr = DrawText("Have Any Questions?", TsBold)

        SetY(Nothing, 10)
        _ip.X = MarginLeft
        cr = DrawText("(Or you may print the form and complete it by hand.)", TsNormal)
        _ip.X = pageWidth - 150
        cr = DrawText("Please call us at 800-123-4567.", TsNormal)

        '' Step 1 - line 1:
        SetY(Nothing, 18)
        _ip.X = MarginLeft
        cr = DrawText("Step 1", TsCaption)
        _ip.X = cr.Right + 10
        cr = DrawText("Original Order #", TsBold)
        _ip.X = cr.Right + 4
        cr = DrawText("(if available):", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(120)
        _ip.X = cr.Right + 6
        cr = DrawText("Estimated Order Date:", TsBold)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        SetY(Nothing, 17)
        DrawGreenLine()
        '' Step 1 - line 2:
        SetY(Nothing, 10)
        _ip.X = MarginLeft
        cr = DrawText("Originally Purchased by:", TsBold)
        _ip.X = cr.Right + 20
        cr = DrawCheckbox("Address Change")
        Dim col1right = pageWidth / 2 - 10
        Dim col2left = col1right + 20
        _ip.X = col2left
        cr = DrawText("Send Refund or Exchange to:", TsBold)
        _ip.X = cr.Right + 2
        cr = DrawText("(If different from left)", TsNormal)
        '' Step 1 - line 3:
        SetY(cr.Bottom, 10)
        _ip.X = MarginLeft
        cr = DrawText("Name:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(col1right - _ip.X)
        _ip.X = col2left
        cr = DrawText("Name:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        '' Step 1 - line 4:
        SetY(cr.Bottom, 4 + 4)
        _ip.X = MarginLeft
        cr = DrawText("Address:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(col1right - _ip.X)
        _ip.X = col2left
        cr = DrawText("Address:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        '' Step 1 - line 5:
        SetY(cr.Bottom, 4 + 0.5F)
        _ip.X = MarginLeft
        cr = DrawTextbox(col1right - _ip.X)
        _ip.X = col2left
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        '' Step 1 - line 6 (city state zip):
        SetY(cr.Bottom, 4 + 0.5F)
        _ip.X = MarginLeft
        cr = DrawTextbox(160)
        _ip.X = cr.Right + 4
        Dim oState = _ip.X - MarginLeft
        cr = DrawTextbox(40)
        _ip.X = cr.Right + 4
        Dim oZip = _ip.X - MarginLeft
        cr = DrawTextbox(col1right - _ip.X)
        ''
        _ip.X = col2left
        cr = DrawTextbox(160)
        _ip.X = cr.Right + 4
        cr = DrawTextbox(40)
        _ip.X = cr.Right + 4
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        '' small text
        SetY(cr.Bottom, SmallTextVOff)
        _ip.X = MarginLeft
        cr = DrawText("(City)", TsSmall)
        _ip.X = MarginLeft + oState
        cr = DrawText("(State)", TsSmall)
        _ip.X = MarginLeft + oZip
        cr = DrawText("(Zip)", TsSmall)
        ''
        _ip.X = col2left
        cr = DrawText("(City)", TsSmall)
        _ip.X = col2left + oState
        cr = DrawText("(State)", TsSmall)
        _ip.X = col2left + oZip
        cr = DrawText("(Zip)", TsSmall)
        '' Step 1 - line 7 (daytime):
        SetY(cr.Bottom, 4 - 0.5F)
        _ip.X = MarginLeft
        cr = DrawText("Phone: (", TsNormal)
        _ip.X = cr.Right
        cr = DrawTextbox(30)
        _ip.X = cr.Right
        cr = DrawText(")", TsNormal)
        _ip.X += 3
        cr = DrawTextbox(80)
        Dim oDay = cr.Left - MarginLeft + 10
        '' (evening)
        _ip.X = cr.Right + 3
        cr = DrawText("(", TsNormal)
        _ip.X = cr.Right
        cr = DrawTextbox(30)
        _ip.X = cr.Right
        cr = DrawText(")", TsNormal)
        _ip.X += 3
        cr = DrawTextbox(col1right - _ip.X)
        Dim oEve = cr.Left - MarginLeft + 10
        '' 
        _ip.X = col2left
        cr = DrawText("Phone: (", TsNormal)
        _ip.X = cr.Right
        cr = DrawTextbox(30)
        _ip.X = cr.Right
        cr = DrawText(")", TsNormal)
        _ip.X += 3
        cr = DrawTextbox(80)
        '' (evening)
        _ip.X = cr.Right + 3
        cr = DrawText("(", TsNormal)
        _ip.X = cr.Right
        cr = DrawTextbox(30)
        _ip.X = cr.Right
        cr = DrawText(")", TsNormal)
        _ip.X += 3
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        '' small text
        SetY(cr.Bottom, SmallTextVOff)
        _ip.X = MarginLeft + oDay
        cr = DrawText("(Daytime)", TsSmall)
        _ip.X = MarginLeft + oEve
        cr = DrawText("(Evening)", TsSmall)
        _ip.X = col2left + oDay
        cr = DrawText("(Daytime)", TsSmall)
        _ip.X = col2left + oEve
        cr = DrawText("(Evening)", TsSmall)
        '' Step 1 - email
        SetY(cr.Bottom, 4 - 0.5F)
        _ip.X = MarginLeft
        cr = DrawText("Email Address:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(col1right - _ip.X)
        _ip.X = col2left
        cr = DrawText("Email Address:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        '' Options:
        SetY(Nothing, 16)
        _ip.X = MarginLeft
        cr = DrawText("Please select one of the following options:", TsBold)
        SetY(cr.Bottom, 2)
        cr = DrawCheckbox("Exchange for another item(s).")
        SetY(cr.Bottom, 2)
        cr = DrawCheckbox("Send me an ACME Gift Card for the amount of the refund.")
        SetY(cr.Bottom, 2)
        cr = DrawCheckbox("Reimburse my original method of payment. " +
            "(Gift recipients who select this option will receive a merchandise only gift card.)")

        '' Step 2
        SetY(Nothing, 18)
        _ip.X = MarginLeft
        cr = DrawText("Step 2–Returns", TsCaption)
        _ip.X = cr.Right + 10
        cr = DrawText("In the form below please indicate the item(s) you are returning, " +
            "including a reason code.", TsNormal)
        SetY(Nothing, 17)
        DrawGreenLine()
        SetY(Nothing, 10)
        cr = DrawReturnsTable()
        SetY(cr.Bottom, 10)
        cr = DrawReasonCodes()

        '' Step 3
        SetY(Nothing, 25)
        _ip.X = MarginLeft
        cr = DrawText("Step 3–Exchanges", TsCaption)
        _ip.X = cr.Right + 10
        SetY(Nothing, -5)
        cr = DrawText(
            "For the fastest service, call Customer Service at 800-123-4567 to request a QuickExchange " +
            "or place a new order online or by phone. We'll ship your new item right away. " +
            "Note: If you use our QuickExchange option, you do not need to fill out Step 3.",
            TsNormal)
        SetY(Nothing, 22)
        DrawGreenLine()

        SetY(Nothing, 10)
        cr = DrawExchangesTable()

        '' Step 4
        SetY(Nothing, 18)
        _ip.X = MarginLeft
        cr = DrawText("Step 4", TsCaption)
        SetY(Nothing, 17)
        DrawGreenLine()

        SetY(Nothing, 10)
        _ip.X = MarginLeft
        Dim oCc = col2left - 30
        cr = DrawText("Method of Payment:", TsBold)
        _ip.X = oCc
        cr = DrawText("Credit Card Information:", TsBold)
        SetY(cr.Bottom, 2)
        _ip.X = MarginLeft
        cr = DrawText("If the total of your exchange or new order exceeds the value of your" + vbCrLf +
            "return, please provide a method of payment. (Select one)", TsNormal)
        _ip.X = oCc
        cr = DrawCheckbox("ACME® Visa®")
        Dim oCcOff = 90
        _ip.X += oCcOff
        cr = DrawCheckbox("MasterCard®")
        _ip.X += oCcOff
        cr = DrawCheckbox("JCB Card™")

        SetY(cr.Bottom, 2)
        _ip.X = oCc
        cr = DrawCheckbox("VISA")
        _ip.X += oCcOff
        cr = DrawCheckbox("American Express")
        _ip.X += oCcOff
        cr = DrawCheckbox("Discover®/Novus® Cards")

        SetY(cr.Bottom, 4)
        _ip.X = MarginLeft
        cr = DrawCheckbox("Credit Card")
        SetY(cr.Bottom, 2)
        cr = DrawCheckbox("Check or Money Order enclosed")
        SetY(cr.Bottom, 2)
        cr = DrawCheckbox("Gift Card, Gift Certificate or ACME Visa coupon dollars." + vbCrLf +
            "Enter # below (for Gift Cards, please include PIN).")
        _ip.X = oCc
        cr = DrawText("Card Number:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(180)
        _ip.X = cr.Right + 4
        cr = DrawTextbox(pageWidth - MarginRight - _ip.X)
        '' small text
        SetY(cr.Bottom, SmallTextVOff)
        _ip.X = cr.Left
        cr = DrawText("Exp. Date (MM/YY)", TsSmall)

        SetY(cr.Bottom, 10)
        _ip.X = MarginLeft
        cr = DrawText("Number:", TsNormal)
        _ip.X = cr.Right + Textbox.LabelSpacing
        cr = DrawTextbox(140)
        Dim tbBottom = cr.Bottom
        _ip.X = cr.Right + 4
        cr = DrawTextbox(60)
        Dim oPin = cr.Left
        _ip.X = oCc
        cr = DrawText("Signature:", TsNormal)
        CurrGraphics.DrawLine(New PointF(cr.Right, cr.Bottom),
            New PointF(pageWidth - MarginRight, cr.Bottom), Color.Black, 0.5F)
        '' small text
        SetY(tbBottom, SmallTextVOff)
        _ip.X = oPin
        cr = DrawText("PIN", TsSmall)
    End Sub

    Private Sub DrawGreenLine(Optional ByVal from_ As Single? = Nothing, Optional ByVal to_ As Single? = Nothing)
        Dim page = CurrPage
        If Not from_.HasValue Then
            from_ = MarginLeft
        End If
        If Not to_.HasValue Then
            to_ = page.Size.Width - MarginRight
        End If
        Dim g = page.Graphics
        Dim pen = New GCDRAW.Pen(TsTitle.ForeColor, CaptionLineThickness)
        g.DrawLine(New PointF(from_.Value, _ip.Y), New PointF(to_.Value, _ip.Y), pen)
    End Sub

    Private Function DrawText(ByVal text As String, ByVal ts As TextStyle) As RectangleF
        Dim page = CurrPage
        Dim tl = page.Graphics.CreateTextLayout()
        tl.MaxWidth = page.Size.Width - MarginRight - _ip.X
        If ts.FontSize = TsTitle.FontSize Then
            tl.TextAlignment = TextAlignment.Center
        End If
        tl.DefaultFormat.Font = ts.Font
        tl.DefaultFormat.FontSize = ts.FontSize
        tl.DefaultFormat.GlyphAdvanceFactor = ts.GlyphAdvanceFactor
        tl.DefaultFormat.ForeColor = ts.ForeColor
        tl.Append(text)
        tl.PerformLayout(True)
        Dim line = tl.Lines(tl.Lines.Count - 1)
        Dim run = line.GlyphRuns(0)
        Dim baselineOffset = run.BaselineOffset
        Dim p = If(_lastBaselineOffset.HasValue, New PointF(_ip.X, _ip.Y + _lastBaselineOffset.Value - baselineOffset), _ip)
        page.Graphics.DrawTextLayout(tl, p)
        If Not _lastBaselineOffset.HasValue Then
            _lastBaselineOffset = baselineOffset ''#34 within one 'line', keep imports the first offset
        End If
        Return New RectangleF(_ip.X + tl.ContentX, _ip.Y + tl.ContentY, tl.ContentWidth, tl.ContentHeight)
    End Function

    Private Function DrawTextbox(ByVal width As Single, Optional ByVal inTable As Boolean = False) As RectangleF
        Dim fld = New TextField()
        fld.Widget.Page = CurrPage
        Dim p = If(_lastBaselineOffset.HasValue, New PointF(_ip.X, _ip.Y + _lastBaselineOffset.Value - Textbox.BaselineOffset), _ip)
        fld.Widget.Rect = New RectangleF(p.X, p.Y, width, Textbox.Height)
        If inTable Then
            fld.Widget.Border = Nothing
        Else
            fld.Widget.Border.Style = BorderStyle.Underline
        End If
        fld.Widget.DefaultAppearance.Font = Textbox.Font
        fld.Widget.DefaultAppearance.FontSize = Textbox.FontSize
        _doc.AcroForm.Fields.Add(fld)
        If Not _lastBaselineOffset.HasValue Then
            _lastBaselineOffset = Textbox.BaselineOffset
        End If
        Return fld.Widget.Rect
    End Function

    Private Function DrawCheckbox(ByVal text As String) As RectangleF
        Dim fld = New CheckBoxField()
        fld.Widget.Page = CurrPage
        Dim p = If(_lastBaselineOffset.HasValue, New PointF(_ip.X, _ip.Y + _lastBaselineOffset.Value - Checkbox.BaselineOffset), _ip)
        fld.Widget.Rect = New RectangleF(p.X, p.Y, Checkbox.Height, Checkbox.Height)
        _doc.AcroForm.Fields.Add(fld)
        If Not _lastBaselineOffset.HasValue Then
            _lastBaselineOffset = Checkbox.BaselineOffset
        End If
        Dim pSave = _ip
        _ip.X = fld.Widget.Rect.Right + Checkbox.LabelSpacing
        Dim r = DrawText(Text, TsNormal)
        _ip = pSave
        Return New RectangleF(fld.Widget.Rect.X, r.Y, r.Right - fld.Widget.Rect.Left, r.Height)
    End Function

    Private Function DrawReturnsTable() As RectangleF
        Dim widths As Single() = {
            55,
            60,
            60,
            35,
            35,
            200,
            50,
            0
        }
        Dim captions As String() = {
            "Reason Code",
            "Item #",
            "Color",
            "Size",
            "Quantity",
            "Item Name",
            "Pirce",
            "Total"
        }
        Dim samples As String() = {
            "23",
            "KK123456",
            "Navy",
            "8",
            "1",
            "Example Item Only",
            "59.00",
            "59.00"
        }
        Return DrawTable(widths, captions, samples, 4)
    End Function

    Private Function DrawExchangesTable() As RectangleF
        '' This table has two special extra titles spanning two tolumns.
        '' To achieve this, we:
        '' - force the column titles in those 4 columns to print as '2nd paragraph',
        ''   thus leaving an empty line for the span title
        '' - print the span titles here as a special case.
        Dim widths As Single() = {
            50,
            25,
            25,
            25,
            25,
            60,
            150,
            50,
            40,
            25,
            35,
            0
        }
        Dim captions As String() = {
            "Item",
            "Style",
            vbCrLf + "1st",
            vbCrLf + "2nd",
            "Size",
            "Sleeve Length" + vbCrLf + "& Inseam",
            "Item Name",
            vbCrLf + "Characters",
            vbCrLf + "Style",
            "Qty.",
            "Price",
            "Total"
        }
        Dim samples As String() = {
            "LH123456",
            "Plain",
            "Tan",
            "Olive",
            "8",
            "28",
            "Example Item Only",
            "Amanda",
            "Block",
            "1",
            "49.95",
            "49.95"
        }

        Dim cr = DrawTable(widths, captions, samples, 4)

        '' print 2 spanning titles:
        Dim g = CurrGraphics
        Dim tl = g.CreateTextLayout()
        tl.ParagraphAlignment = ParagraphAlignment.Near
        tl.TextAlignment = TextAlignment.Center
        tl.DefaultFormat.Font = TsNormal.Font
        tl.DefaultFormat.FontSize = TsNormal.FontSize
        tl.DefaultFormat.GlyphAdvanceFactor = TsNormal.GlyphAdvanceFactor
        tl.DefaultFormat.ForeColor = Color.White
        tl.WrapMode = WrapMode.NoWrap
        '' Color Choice
        Dim width = widths(2) + widths(3)
        tl.MaxWidth = width
        tl.Append("Color Choice")
        tl.PerformLayout(True)
        Dim pt = New PointF(cr.Left + widths(0) + widths(1), cr.Top)
        g.DrawTextLayout(tl, pt)
        Dim pen = New GCDRAW.Pen(Color.White, 0.5F)
        Dim pt1 = New PointF(pt.X + 0.5F, pt.Y + TableCaptionHeight / 2)
        Dim pt2 = New PointF(pt1.X + width, pt1.Y)
        g.DrawLine(pt1, pt2, pen)
        pt1 = New PointF(pt.X + widths(2) + 0.5F, pt.Y + TableCaptionHeight / 2)
        pt2 = New PointF(pt1.X, pt.Y + TableCaptionHeight)
        g.DrawLine(pt1, pt2, pen)
        pt1 = New PointF(pt.X + 0.5F, pt.Y)
        pt2 = New PointF(pt1.X, pt.Y + TableCaptionHeight)
        g.DrawLine(pt1, pt2, pen)
        pt1 = New PointF(pt.X + width + 0.5F, pt.Y)
        pt2 = New PointF(pt1.X, pt.Y + TableCaptionHeight)
        g.DrawLine(pt1, pt2, pen)
        '' Monogramming
        width = widths(7) + widths(8)
        tl.Inlines.Clear()
        tl.MaxWidth = width
        tl.Append("Monogramming")
        tl.PerformLayout(True)
        pt = New PointF(cr.Left + widths(0) + widths(1) + widths(2) + widths(3) + widths(4) + widths(5) + widths(6), cr.Top)
        g.DrawTextLayout(tl, pt)
        pt1 = New PointF(pt.X + 0.5F, pt.Y + TableCaptionHeight / 2)
        pt2 = New PointF(pt1.X + width, pt1.Y)
        g.DrawLine(pt1, pt2, pen)
        pt1 = New PointF(pt.X + widths(7) + 0.5F, pt.Y + TableCaptionHeight / 2)
        pt2 = New PointF(pt1.X, pt.Y + TableCaptionHeight)
        g.DrawLine(pt1, pt2, pen)
        pt1 = New PointF(pt.X + 0.5F, pt.Y)
        pt2 = New PointF(pt1.X, pt.Y + TableCaptionHeight)
        g.DrawLine(pt1, pt2, pen)
        pt1 = New PointF(pt.X + width + 0.5F, pt.Y)
        pt2 = New PointF(pt1.X, pt.Y + TableCaptionHeight)
        g.DrawLine(pt1, pt2, pen)

        Return cr
    End Function

    Private Function DrawTable(ByVal widths As Single(), ByVal captions As String(), ByVal samples As String(), ByVal rowCount As Integer) As RectangleF
        Debug.Assert(captions.Length = widths.Length AndAlso samples.Length = widths.Length)

        Dim ipSave = _ip
        Dim p = New GCDRAW.Pen(Color.Black, 0.5F)

        Dim g = CurrGraphics
        Dim tl = g.CreateTextLayout()
        tl.ParagraphAlignment = ParagraphAlignment.Center
        tl.TextAlignment = TextAlignment.Center
        tl.DefaultFormat.Font = TsNormal.Font
        tl.DefaultFormat.FontSize = TsNormal.FontSize
        tl.DefaultFormat.GlyphAdvanceFactor = TsNormal.GlyphAdvanceFactor
        tl.DefaultFormat.ForeColor = Color.White
        tl.WrapMode = WrapMode.NoWrap
        tl.MaxHeight = TableCaptionHeight
        Dim totW = 0F
        For i = 0 To widths.Length - 1
            If i = widths.Length - 1 Then
                widths(i) = CurrPage.Size.Width - MarginLeft - MarginRight - totW - 1
                totW += 1
            End If
            totW += widths(i)
        Next
        g.FillRectangle(New RectangleF(MarginLeft, _ip.Y, totW, TableCaptionHeight), Color.Black)
        Dim pt = New PointF(MarginLeft, _ip.Y)
        For i = 0 To widths.Length - 1
            tl.MaxWidth = widths(i)
            tl.Append(captions(i))
            tl.PerformLayout(True)
            g.DrawTextLayout(tl, pt)
            pt.X = pt.X + widths(i)
            tl.Inlines.Clear()
        Next
        tl.DefaultFormat.ForeColor = Color.Teal
        tl.MaxHeight = TableSampleHeight
        pt = New PointF(MarginLeft, _ip.Y + TableCaptionHeight)
        For i = 0 To widths.Length - 1
            tl.MaxWidth = widths(i)
            tl.Append(samples(i))
            tl.PerformLayout(True)
            g.DrawTextLayout(tl, pt)
            pt.X = pt.X + widths(i)
            tl.Inlines.Clear()
        Next
        SetY(_ip.Y + TableCaptionHeight + TableSampleHeight, 0.5F)
        For row = 0 To rowCount - 1
            _ip.X = MarginLeft + 1
            For i = 0 To widths.Length - 1
                Dim cr = DrawTextbox(widths(i) - 1, True)
                _ip.X = cr.Right + 1
            Next
            g.DrawLine(New PointF(MarginLeft, _ip.Y - 0.5F), New PointF(MarginLeft + totW, _ip.Y - 0.5F), p)
            SetY(Nothing, Textbox.Height + 1)
        Next
        Dim totH = TableCaptionHeight + TableSampleHeight + (Textbox.Height + 1) * rowCount
        _ip.X = MarginLeft + 0.5F
        For i = 0 To widths.Length - 2
            _ip.X += widths(i)
            g.DrawLine(New PointF(_ip.X, ipSave.Y), New PointF(_ip.X, ipSave.Y + totH), p)
        Next

        Dim rect = New RectangleF(MarginLeft, ipSave.Y, totW, totH)
        g.DrawRectangle(rect, p)

        Return rect
    End Function

    Private Function DrawReasonCodes() As RectangleF
        Dim startX = 150.0F
        Dim capOff = 16.0F
        Dim colOff = 110.0F
        Dim ipSave = _ip

        _ip.X = startX
        Dim cr = DrawText("01", TsNormal)
        _ip.X += capOff
        cr = DrawText("Unsatisfactory", TsNormal)
        _ip.X = startX + colOff
        cr = DrawText("33", TsNormal)
        _ip.X += capOff
        cr = DrawText("Did not like color", TsNormal)
        _ip.X = startX + colOff * 2
        cr = DrawText("23", TsNormal)
        _ip.X += capOff
        cr = DrawText("Ordered wrong size", TsNormal)
        _ip.X = startX + colOff * 3
        cr = DrawText("51", TsNormal)
        _ip.X += capOff
        cr = DrawText("Shipping damage", TsNormal)
        SetY(Nothing, TsNormal.FontSize + 2)
        _ip.X = startX
        cr = DrawText("02", TsNormal)
        _ip.X += capOff
        cr = DrawText("Defective construction", TsNormal)
        _ip.X = startX + colOff
        cr = DrawText("21", TsNormal)
        _ip.X += capOff
        cr = DrawText("Too small", TsNormal)
        _ip.X = startX + colOff * 2
        cr = DrawText("25", TsNormal)
        _ip.X += capOff
        cr = DrawText("Too short", TsNormal)
        _ip.X = startX + colOff * 3
        cr = DrawText("52", TsNormal)
        _ip.X += capOff
        cr = DrawText("Wrong item shipped", TsNormal)

        _ip.X = MarginLeft + 10
        cr = DrawText("Reason Codes", TsBold)
        Dim lineX = cr.Right + 20

        SetY(Nothing, TsNormal.FontSize + 2)
        _ip.X = startX
        cr = DrawText("31", TsNormal)
        _ip.X += capOff
        cr = DrawText("Did not like styling", TsNormal)
        _ip.X = startX + colOff
        cr = DrawText("22", TsNormal)
        _ip.X += capOff
        cr = DrawText("Too large", TsNormal)
        _ip.X = startX + colOff * 2
        cr = DrawText("36", TsNormal)
        _ip.X += capOff
        cr = DrawText("Too long", TsNormal)

        Dim rect = New RectangleF(MarginLeft, ipSave.Y, CurrPage.Size.Width, cr.Bottom - ipSave.Y)
        CurrGraphics.DrawLine(lineX, rect.Top, lineX, rect.Bottom, Color.Black, 0.5F)

        Return rect
    End Function
End Class