GoodsReturnForm.vb
'' 
'' このコードは、DioDocs for PDF のサンプルの一部として提供されています。
'' Copyright (c) GrapeCity 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

'' 複数の入力項目と複雑なレイアウトを持つ「商品返品または交換フォーム」 AcroForm を作成します。
Public Class GoodsReturnForm
    '' ページの余白。
    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
    '' 区切り線。
    Dim CaptionLineThickness As Single = 2.5F
    '' テキストスタイルを保持する構造体。
    Private Structure TextStyle
        Public Property Font As Font
        Public Property FontSize As Single
        Public Property ForeColor As Color
        Public Property GlyphAdvanceFactor As Single
    End Structure
    '' フォーム全体で使用されるさまざまなスタイル。
    Shared TsTitle As TextStyle = New TextStyle() With {
            .Font = GrapeCity.Documents.Text.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
    }
    '' 入力フィールドスタイル。
    Private Structure Textbox
        Public Shared Property Font As 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 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
    '' 作成されるドキュメント。
    Private _doc As GcPdfDocument
    '' 挿入位置。
    Private _ip As PointF = New PointF(MarginLeft, MarginTop)
    '' null でない場合、DrawText はテキストを最後のベースラインに揃えるためにこれを使用します
    Private _lastBaselineOffset As Single? = Nothing
    '' 現在の値へのショートカット。
    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
    '' 静的コンストラクタ。
    Shared Sub New()
        '' テキストボックスの初期化。
        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
        '' チェックボックスの初期化。
        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
    '' メインエントリポイント。
    Function CreatePDF(ByVal stream As Stream) As Integer
        Acme()
        _doc.Save(stream)
        Return _doc.Pages.Count
    End Function
    '' 挿入位置を垂直に設定または進めます。
    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
    '' PDF フォームを作成します。
    Private Sub Acme()
        _doc = New GcPdfDocument()
        _doc.NewPage()
        Dim pageWidth = CurrPage.Size.Width

        '' メインキャプション。
        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)

        '' '返品と交換フォーム'。
        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 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 '' 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.TextFormat.Font = Textbox.Font
        fld.Widget.TextFormat.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
        '' この表には、2つの列にまたがる2つの特別なタイトルがあります。
        '' これを実現するには、次のようにします。
        '' - これらの4つの列の列タイトルを「第2段落」として印刷し、スパンタイトルに
        ''   空の行を残します。
        '' - スパンタイトルを特別な場合としてここに表示します。
        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 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 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