NumberedList2.vb
'' 
'' このコードは、DioDocs for PDF のサンプルの一部として提供されています。
'' © MESCIUS inc. All rights reserved.
'' 
Imports System.IO
Imports System.Drawing
Imports System.Text
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Drawing

'' このサンプルは、DsPdf で異なるスタイルの番号付きリストを描画する方法を示します。
'' NumberedList も参照してください。
Public Class NumberedList2
    '' サンプルで使用されているページレイアウト定数をカプセル化します。
    Private Structure Layout
        '' ページ外周の余白。
        Public Shared Margin As Single = 72.0F
        '' 項目番号を基準にしたリストオフセット。
        Public Shared ListOffset As Single = 24.0F
        '' リストレベルインデント。
        Public Shared ListIndent As Single = 30.0F
    End Structure

    '' シンプルなツリータイプを定義します。
    Private Class Node
        Public Sub New(ByVal txt As String)
            Text = txt
        End Sub
        Public Property Text As String
        Public Property Children As List(Of Node) = New List(Of Node)()
    End Class

    '' ノードのリストを番号付きリストとして描画します。
    Private Function RenderNodes(ByRef page As Page, ByVal pt As PointF, ByVal nodes As List(Of Node), ByVal level As Integer) As PointF

        Dim tlBullet = page.Graphics.CreateTextLayout()
        tlBullet.DefaultFormat.FontName = "Yu Gothic"
        tlBullet.MarginLeft = Layout.ListIndent * level

        Dim tlText = New TextLayout(72)
        tlText.DefaultFormat.FontName = "Yu Gothic"
        tlText.MarginLeft = Layout.ListOffset + Layout.ListIndent * level

        For i = 0 To nodes.Count - 1
            Dim g = page.Graphics
            '' 項目テキストを準備します。
            tlText.Clear()
            tlText.Append(nodes(i).Text)
            tlText.PerformLayout(True)
            If pt.Y + tlText.ContentHeight > page.Size.Height - Layout.Margin Then
                page = page.Doc.NewPage()
                g = page.Graphics
                pt.Y = Layout.Margin
            End If
            '' 項目番号を準備します。
            tlBullet.Clear()
            tlBullet.Append(ItemIdxToString(i, level, tlBullet))
            tlBullet.PerformLayout(True)
            '' 項目を描画します。
            g.DrawTextLayout(tlBullet, pt)
            g.DrawTextLayout(tlText, pt)
            '' 挿入位置を拡張します。
            pt.Y += tlText.ContentHeight
            '' 子ノードを描画します。
            If nodes(i).Children.Count > 0 Then
                pt = RenderNodes(page, pt, nodes(i).Children, level + 1)
            End If
        Next
        Return pt
    End Function

    '' アラビア語 -> ラテン文字 -> ローマ数字のループで、
    '' 項目インデックスを項目番号表現に変換します。
    '' ローマ数字は右揃え、その他は左揃えです。
    Private Function ItemIdxToString(ByVal itemIdx As Integer, ByVal level As Integer, ByVal tl As TextLayout) As String

        Select Case (level Mod 3)
            Case 0
                tl.MarginLeft = Layout.ListIndent * level
                tl.MaxWidth = Nothing
                tl.TextAlignment = TextAlignment.Leading
                Return $"{itemIdx + 1}."
            Case 1
                tl.MarginLeft = Layout.ListIndent * level
                tl.MaxWidth = Nothing
                tl.TextAlignment = TextAlignment.Leading
                Return $"{ChrW(AscW("a") + itemIdx)}."
            Case 2
                tl.MarginLeft = 0
                Dim font = tl.DefaultFormat.Font
                tl.MaxWidth = Layout.ListIndent * level + tl.DefaultFormat.FontSize
                tl.TextAlignment = TextAlignment.Trailing
                Return $"{IntToRoman(itemIdx + 1)}."
            Case Else
                Throw New Exception("Unexpected.")
        End Select
    End Function

    '' http://dotnet-snippets.com/snippet/roman-numerals/667 より
    Private Function IntToRoman(ByVal number As Integer) As String

        Dim result = New StringBuilder()
        Dim digitsValues = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000}
        Dim romanDigits = {"I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"}
        While number > 0
            For i = digitsValues.Count() - 1 To 0 Step -1
                If (number / digitsValues(i) >= 1) Then
                    number -= digitsValues(i)
                    result.Append(romanDigits(i).ToLower())
                    Exit For
                End If
            Next
        End While
        Return result.ToString()
    End Function

    '' メインエントリポイントです。
    Function CreatePDF(ByVal stream As Stream) As Integer
        Dim doc = New GcPdfDocument()
        Dim page = doc.NewPage()
        RenderNodes(page, New PointF(Layout.Margin, Layout.Margin), _cities.Children, 0)
        ''
        '' PDF ドキュメントを保存します。
        doc.Save(stream)
        Return doc.Pages.Count
    End Function

    '' サンプルツリーデータです。
    Private _cities As Node = New Node("政令指定都市") With {
    .Children = New List(Of Node)() From {
        New Node("北海道・東北") With {
            .Children = New List(Of Node)() From {
                New Node("札幌市") With {
                    .Children = New List(Of Node)() From {
                        New Node("札幌市中央区"),
                        New Node("札幌市北区"),
                        New Node("札幌市東区"),
                        New Node("札幌市白石区"),
                        New Node("札幌市豊平区"),
                        New Node("札幌市南区"),
                        New Node("札幌市西区"),
                        New Node("札幌市厚別区"),
                        New Node("札幌市手稲区"),
                        New Node("札幌市清田区")
                    }
                },
                New Node("仙台市") With {
                    .Children = New List(Of Node)() From {
                        New Node("仙台市青葉区"),
                        New Node("仙台市宮城野区"),
                        New Node("仙台市若林区"),
                        New Node("仙台市太白区"),
                        New Node("仙台市泉区")
                    }
                }
            }
        },
        New Node("関東") With {
            .Children = New List(Of Node)() From {
                New Node("さいたま市") With {
                    .Children = New List(Of Node)() From {
                        New Node("さいたま市西区"),
                        New Node("さいたま市北区"),
                        New Node("さいたま市大宮区"),
                        New Node("さいたま市見沼区"),
                        New Node("さいたま市中央区"),
                        New Node("さいたま市桜区"),
                        New Node("さいたま市浦和区"),
                        New Node("さいたま市南区"),
                        New Node("さいたま市緑区"),
                        New Node("さいたま市岩槻区")
                    }
                },
                New Node("千葉市") With {
                    .Children = New List(Of Node)() From {
                        New Node("千葉市中央区"),
                        New Node("千葉市花見川区"),
                        New Node("千葉市稲毛区"),
                        New Node("千葉市若葉区"),
                        New Node("千葉市緑区"),
                        New Node("千葉市美浜区")
                    }
                },
                New Node("横浜市") With {
                    .Children = New List(Of Node)() From {
                        New Node("横浜市鶴見区"),
                        New Node("横浜市神奈川区"),
                        New Node("横浜市西区"),
                        New Node("横浜市中区"),
                        New Node("横浜市南区"),
                        New Node("横浜市保土ケ谷区"),
                        New Node("横浜市磯子区"),
                        New Node("横浜市金沢区"),
                        New Node("横浜市港北区"),
                        New Node("横浜市戸塚区"),
                        New Node("横浜市港南区"),
                        New Node("横浜市旭区"),
                        New Node("横浜市緑区"),
                        New Node("横浜市瀬谷区"),
                        New Node("横浜市栄区"),
                        New Node("横浜市泉区"),
                        New Node("横浜市青葉区"),
                        New Node("横浜市都筑区")
                    }
                },
                New Node("川崎市") With {
                    .Children = New List(Of Node)() From {
                        New Node("川崎市川崎区"),
                        New Node("川崎市幸区"),
                        New Node("川崎市中原区"),
                        New Node("川崎市高津区"),
                        New Node("川崎市多摩区"),
                        New Node("川崎市宮前区"),
                        New Node("川崎市麻生区")
                    }
                },
                New Node("相模原市") With {
                    .Children = New List(Of Node)() From {
                        New Node("相模原市緑区"),
                        New Node("相模原市中央区"),
                        New Node("相模原市南区")
                    }
                }
            }
        },
        New Node("甲信越") With {
            .Children = New List(Of Node)() From {
                New Node("新潟市") With {
                    .Children = New List(Of Node)() From {
                        New Node("新潟市北区"),
                        New Node("新潟市東区"),
                        New Node("新潟市中央区"),
                        New Node("新潟市江南区"),
                        New Node("新潟市秋葉区"),
                        New Node("新潟市南区"),
                        New Node("新潟市西区"),
                        New Node("新潟市西蒲区")
                    }
                },
                New Node("静岡市") With {
                    .Children = New List(Of Node)() From {
                        New Node("静岡市葵区"),
                        New Node("静岡市駿河区"),
                        New Node("静岡市清水区")
                    }
                },
                New Node("浜松市") With {
                    .Children = New List(Of Node)() From {
                        New Node("浜松市中区"),
                        New Node("浜松市東区"),
                        New Node("浜松市西区"),
                        New Node("浜松市南区"),
                        New Node("浜松市北区"),
                        New Node("浜松市浜北区"),
                        New Node("浜松市天竜区")
                    }
                }
            }
        },
        New Node("中部・近畿") With {
            .Children = New List(Of Node)() From {
                New Node("名古屋市") With {
                    .Children = New List(Of Node)() From {
                        New Node("名古屋市千種区"),
                        New Node("名古屋市東区"),
                        New Node("名古屋市北区"),
                        New Node("名古屋市西区"),
                        New Node("名古屋市中村区"),
                        New Node("名古屋市中区"),
                        New Node("名古屋市昭和区"),
                        New Node("名古屋市瑞穂区"),
                        New Node("名古屋市熱田区"),
                        New Node("名古屋市中川区"),
                        New Node("名古屋市港区"),
                        New Node("名古屋市南区"),
                        New Node("名古屋市守山区"),
                        New Node("名古屋市緑区"),
                        New Node("名古屋市名東区"),
                        New Node("名古屋市天白区")
                    }
                },
                New Node("京都市") With {
                    .Children = New List(Of Node)() From {
                        New Node("京都市北区"),
                        New Node("京都市上京区"),
                        New Node("京都市左京区"),
                        New Node("京都市中京区"),
                        New Node("京都市東山区"),
                        New Node("京都市下京区"),
                        New Node("京都市南区"),
                        New Node("京都市右京区"),
                        New Node("京都市伏見区"),
                        New Node("京都市山科区"),
                        New Node("京都市西京区")
                    }
                },
                New Node("大阪市") With {
                    .Children = New List(Of Node)() From {
                        New Node("大阪市都島区"),
                        New Node("大阪市福島区"),
                        New Node("大阪市此花区"),
                        New Node("大阪市西区"),
                        New Node("大阪市港区"),
                        New Node("大阪市大正区"),
                        New Node("大阪市天王寺区"),
                        New Node("大阪市浪速区"),
                        New Node("大阪市西淀川区"),
                        New Node("大阪市東淀川区"),
                        New Node("大阪市東成区"),
                        New Node("大阪市生野区"),
                        New Node("大阪市旭区"),
                        New Node("大阪市城東区"),
                        New Node("大阪市阿倍野区"),
                        New Node("大阪市住吉区"),
                        New Node("大阪市東住吉区"),
                        New Node("大阪市西成区"),
                        New Node("大阪市淀川区"),
                        New Node("大阪市鶴見区"),
                        New Node("大阪市住之江区"),
                        New Node("大阪市平野区"),
                        New Node("大阪市北区"),
                        New Node("大阪市中央区")
                    }
                },
                New Node("堺市") With {
                    .Children = New List(Of Node)() From {
                        New Node("堺市堺区"),
                        New Node("堺市中区"),
                        New Node("堺市東区"),
                        New Node("堺市西区"),
                        New Node("堺市南区"),
                        New Node("堺市北区"),
                        New Node("堺市美原区")
                    }
                },
                New Node("神戸市") With {
                    .Children = New List(Of Node)() From {
                        New Node("神戸市東灘区"),
                        New Node("神戸市灘区"),
                        New Node("神戸市兵庫区"),
                        New Node("神戸市長田区"),
                        New Node("神戸市須磨区"),
                        New Node("神戸市垂水区"),
                        New Node("神戸市北区"),
                        New Node("神戸市中央区"),
                        New Node("神戸市西区")
                    }
                }
            }
        },
        New Node("中国・四国・九州") With {
            .Children = New List(Of Node)() From {
                New Node("岡山市") With {
                    .Children = New List(Of Node)() From {
                        New Node("岡山市北区"),
                        New Node("岡山市中区"),
                        New Node("岡山市東区"),
                        New Node("岡山市南区")
                    }
                },
                New Node("広島市") With {
                    .Children = New List(Of Node)() From {
                        New Node("広島市中区"),
                        New Node("広島市東区"),
                        New Node("広島市南区"),
                        New Node("広島市西区"),
                        New Node("広島市安佐南区"),
                        New Node("広島市安佐北区"),
                        New Node("広島市安芸区"),
                        New Node("広島市佐伯区")
                    }
                },
                New Node("北九州市") With {
                    .Children = New List(Of Node)() From {
                        New Node("北九州市門司区"),
                        New Node("北九州市若松区"),
                        New Node("北九州市戸畑区"),
                        New Node("北九州市小倉北区"),
                        New Node("北九州市小倉南区"),
                        New Node("北九州市八幡東区"),
                        New Node("北九州市八幡西区")
                    }
                },
                New Node("福岡市") With {
                    .Children = New List(Of Node)() From {
                        New Node("福岡市東区"),
                        New Node("福岡市博多区"),
                        New Node("福岡市中央区"),
                        New Node("福岡市南区"),
                        New Node("福岡市西区"),
                        New Node("福岡市城南区"),
                        New Node("福岡市早良区")
                    }
                },
                New Node("熊本市") With {
                    .Children = New List(Of Node)() From {
                        New Node("熊本市中央区"),
                        New Node("熊本市東区"),
                        New Node("熊本市西区"),
                        New Node("熊本市南区"),
                        New Node("熊本市北区")
                    }
                }
            }
        }
    }
    }
End Class