ReadTagsTableData.vb
C# VB
'''' This code is part of Document Solutions for PDF .NET demos.'' Copyright (c) MESCIUS inc. All rights reserved.''Imports System.IOImports System.DrawingImports System.LinqImports System.Collections.GenericImports GrapeCity.Documents.PdfImports GrapeCity.Documents.TextImports GrapeCity.Documents.Pdf.TextMapImports GrapeCity.Documents.Pdf.StructureImports GrapeCity.Documents.Pdf.Recognition.StructureImports GCTEXT = GrapeCity.Documents.Text
'' 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 FunctionEnd Class