''
'' This code is part of Document Solutions for PDF .NET demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports System.Linq
Imports System.Collections.Generic
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Pdf.TextMap
Imports GrapeCity.Documents.Pdf.Structure
Imports GrapeCity.Documents.Pdf.Recognition.Structure
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing
'' Find tables and read their data using structure tags.
Public Class ReadTagsTableData
Private _tf As TextFormat
Private _tfHdr As TextFormat
Private _tfPgHdr As TextFormat
Private _margin As Single = 72
Public Function CreatePDF(ByVal stream As Stream) As Integer
'' Set up some text formats:
_tf = New TextFormat() With {
.Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "NotoSans-Regular.ttf")),
.FontSize = 9,
.ForeColor = Color.Black
}
_tfHdr = New TextFormat(_tf) With {
.Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "NotoSans-Bold.ttf")),
.FontSize = 11,
.ForeColor = Color.DarkBlue
}
_tfPgHdr = New TextFormat(_tf) With {
.FontSize = 11,
.ForeColor = Color.Gray
}
'' The resulting PDF:
Dim doc = New GcPdfDocument()
Using s = File.OpenRead(Path.Combine("Resources", "PDFs", "C1Olap-QuickStart.pdf"))
Dim source = New GcPdfDocument()
source.Load(s)
PrintAllTables(doc, source)
End Using
' Save the PDF:
doc.Save(stream)
'' Done:
Return doc.Pages.Count
End Function
Private Sub PrintAllTables(doc As GcPdfDocument, source As GcPdfDocument)
'' Get the LogicalStructure and top parent element:
Dim ls As LogicalStructure = source.GetLogicalStructure()
If ls Is Nothing OrElse ls.Elements Is Nothing OrElse ls.Elements.Count = 0 Then
' No structure tags found:
Util.AddNote("No structure tags were found in the source document.", doc.Pages.Add())
Return
End If
' The root element:
Dim root As Element = ls.Elements(0)
'' Find and print all tables:
Dim tables = New List(Of (TextLayout, Page))()
root.Children.ToList().FindAll(Function(e_) e_.StructElement.Type = "Table").ForEach(Sub(t_) tables.Add(PrintTable(t_)))
' Group tables by the page they were found on:
Dim tablesByPage = tables.GroupBy(Function(t_) t_.Item2.Index)
' For each page, print all tables found on that page,
' followed by the original page for reference:
For Each tbp In tablesByPage
' The page that will contain the extracted table data:
Dim pgTables = doc.NewPage()
' The page that will contain the source page for reference:
Dim pgSrc = doc.NewPage()
' Print the original page:
tbp.First().Item2.Draw(pgSrc.Graphics, pgSrc.Bounds)
' Add a page header:
pgSrc.Graphics.DrawString($"Page {tbp.First().Item2.Index + 1} of the source PDF",
_tfPgHdr, New RectangleF(0, 0, pgSrc.Size.Width, _margin), TextAlignment.Center, ParagraphAlignment.Center, False)
Dim maxHeight As Single = pgTables.Size.Height - _margin * 2
Dim y As Single = _margin
' Print all table data. For simplicity sake we assume that all table data will fit on a single page:
For Each t In tbp
t.Item1.MaxHeight = maxHeight
t.Item1.MaxWidth = pgTables.Size.Width - _margin * 2
pgTables.Graphics.DrawTextLayout(t.Item1, New PointF(_margin, y))
maxHeight -= t.Item1.ContentHeight + _margin
y += t.Item1.ContentHeight + _margin
Next
Next
End Sub
Private Function PrintTable(e As Element) As (TextLayout, Page)
If e.Type <> "Table" Then
Throw New Exception($"Unexpected: element type must be 'Table' but it is '{e.Type}'.")
End If
Dim table = New List(Of List(Of IList(Of ITextParagraph)))()
Dim maxCols = 0
' Select all child elements with type TR - table rows:
Dim selectRows As Action(Of IReadOnlyList(Of Element)) = Nothing
selectRows = Sub(elements)
For Each ec As Element In elements
If ec.HasChildren Then
If ec.StructElement.Type = "TR" Then
Dim cells = ec.Children.ToList().FindAll(Function(e_) e_.StructElement.Type = "TD").ToArray()
maxCols = Math.Max(maxCols, cells.Length)
Dim tableCells = New List(Of IList(Of ITextParagraph))()
For Each cell In cells
tableCells.Add(cell.GetParagraphs())
Next
table.Add(tableCells)
Else
selectRows(ec.Children)
End If
End If
Next
End Sub
selectRows(e.Children)
'' Show table:
Dim sourcePage = FindPage(e.StructElement)
If sourcePage Is Nothing Then
Throw New Exception("Unexpected: could not find the default page for the table.")
End If
Dim tl = New TextLayout(72)
' Add table data to the text layout:
tl.Append($"{vbLf}Table on page {sourcePage.Index + 1} of the source document has {maxCols} column(s) and {table.Count} row(s)." & vbLf & "Data by row:", _tfHdr)
tl.AppendParagraphBreak()
Dim irow = 0
For Each row In table
Dim icol = 0
For Each cell In row
For Each para In cell
tl.Append(para.GetText())
Next
If row.IndexOf(cell) <= row.Count Then
tl.Append(vbTab)
Else
tl.AppendLine()
End If
icol += 1
Next
irow += 1
tl.AppendLine()
Next
Return (tl, sourcePage)
End Function
Private Function FindPage(se As StructElement) As Page
If se.DefaultPage IsNot Nothing Then
Return se.DefaultPage
End If
If se.HasChildren Then
For Each child In se.Children
Dim p = FindPage(child)
If p IsNot Nothing Then
Return p
End If
Next
End If
Return Nothing
End Function
End Class