LongTable.cs
//
// This code is part of Document Solutions for PDF demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Linq;
using System.Collections.Generic;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Layout;
using GrapeCity.Documents.Layout.Composition;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;
namespace DsPdfWeb.Demos
{
// This demo creates a table with variable row height and spanning many pages,
// using classes from the GrapeCity.Documents.Layout.Composition namespace.
public class LongTable
{
string[] _headers;
TextFormat _thFormat;
TextFormat _tdFormat;
float _resolution;
readonly List<LayoutRect> _rows = new();
readonly List<LayoutRect> _columns = new();
readonly List<Visual> _cells = new();
readonly List<TextLayout> _texts = new();
GcPdfGraphics _g;
View _pageView;
Layer _textLayer;
LayoutRect _bounds;
Surface _sf;
public int CreatePDF(Stream stream)
{
var doc = new GcPdfDocument();
var page = doc.NewPage();
var pageSize = page.Size;
_g = page.Graphics;
_sf = new Surface();
_pageView = _sf.CreateView(pageSize.Width, pageSize.Height);
_textLayer = _pageView.CreateSubLayer();
_resolution = _g.Resolution;
var marginsVisual = _pageView.CreateVisual((g, v) =>
{
g.FillRectangle(v.AsRectF(), Color.FromArgb(0xFF, 0xF8, 0xF8));
});
_bounds = marginsVisual.LayoutRect;
_bounds.AnchorInflate(null, -_resolution);
_sf.PerformLayout();
var noteBounds = AddNote("We create a table with a large number of variable height rows. " +
"The table starts at a specified position on the first page, and spans multiple pages.");
var fc = FontCollection.SystemFonts;
_thFormat = new TextFormat
{
Font = fc.FindFamilyName("Trebuchet MS", true) ?? fc.FindFamilyName("Arial", true),
FontBold = true,
ForeColor = Color.White
};
_tdFormat = new TextFormat
{
Font = fc.FindFamilyName("Trebuchet MS") ?? fc.FindFamilyName("Arial")
};
_headers = new string[] { "#", "Lorem", "Ipsum" };
CreateColumns();
var headerRowRect = CreateHeaderRow();
headerRowRect.SetTop(noteBounds, AnchorParam.Bottom, _resolution / 2);
FillHeaderRow(headerRowRect);
var prevRowRect = headerRowRect;
var numberOfRows = Common.Util.NewRandom().Next(100, 500);
for (int i = 0; i < numberOfRows; i++)
{
var row = CreateRow((i & 1) != 0);
var rc = row.LayoutRect;
rc.SetTop(prevRowRect, AnchorParam.Bottom);
if (!FillRow(i, rc))
{
row.Detach();
CreateTableGrid();
_sf.Render(_g);
_columns.Clear();
_rows.Clear();
page = doc.NewPage();
pageSize = page.Size;
_g = page.Graphics;
_sf = new Surface();
_pageView = _sf.CreateView(pageSize.Width, pageSize.Height);
_textLayer = _pageView.CreateSubLayer();
marginsVisual = _pageView.CreateVisual((g, v) =>
{
g.FillRectangle(v.AsRectF(), Color.FromArgb(0xFF, 0xF8, 0xF8));
});
_bounds = marginsVisual.LayoutRect;
_bounds.AnchorInflate(null, -_resolution);
_sf.PerformLayout();
CreateColumns();
headerRowRect = CreateHeaderRow();
headerRowRect.SetTop(_bounds, AnchorParam.Top);
FillHeaderRow(headerRowRect);
row = CreateRow((i & 1) != 0);
rc = row.LayoutRect;
rc.SetTop(headerRowRect, AnchorParam.Bottom);
RestoreRow(rc);
}
prevRowRect = rc;
}
CreateTableGrid();
_sf.Render(_g);
doc.Save(stream);
return doc.Pages.Count;
}
class TableGridInfo
{
public TableGridInfo(LayoutRect[] columns, LayoutRect[] rows)
{
Columns = columns;
Rows = rows;
}
public readonly LayoutRect[] Columns;
public readonly LayoutRect[] Rows;
}
void CreateTableGrid()
{
var grid = _pageView.CreateVisual(false);
grid.Tag = new TableGridInfo(_columns.ToArray(), _rows.ToArray());
grid.Draw = (g, v) =>
{
var pen = new GCDRAW::Pen(Color.FromArgb(0xDD, 0xDD, 0xDD), 1);
var tgi = (TableGridInfo)v.Tag;
int lastColumnIndex = tgi.Columns.Length - 1;
int lastRowIndex = tgi.Rows.Length - 1;
float xMin = tgi.Columns[0].P0X;
float yMin = tgi.Rows[0].P0Y;
float xMax = tgi.Columns[lastColumnIndex].P1X;
float yMax = tgi.Rows[lastRowIndex].P2Y;
g.DrawRectangle(new RectangleF(xMin, yMin, xMax - xMin, yMax - yMin), pen);
for (int i = 0; i < lastColumnIndex; i++)
{
float x = tgi.Columns[i].P1X;
g.DrawLine(new PointF(x, yMin), new PointF(x, yMax), pen);
}
for (int i = 0; i < lastRowIndex; i++)
{
float y = tgi.Rows[i].P2Y;
g.DrawLine(new PointF(xMin, y), new PointF(xMax, y), pen);
}
};
}
void CreateColumns()
{
LayoutRect column1 = _pageView.CreateSpace().LayoutRect;
LayoutRect column2 = _pageView.CreateSpace().LayoutRect;
LayoutRect column3 = _pageView.CreateSpace().LayoutRect;
column1.AnchorTopBottom(_bounds, 0, 0);
column2.AnchorTopBottom(_bounds, 0, 0);
column3.AnchorTopBottom(_bounds, 0, 0);
column1.SetLeft(_bounds, AnchorParam.Left);
column2.SetLeftAndOpposite(column1, AnchorParam.Right);
column3.SetLeftAndOpposite(column2, AnchorParam.Right);
column3.SetRight(_bounds, AnchorParam.Right);
column1.SetStarWidth(1);
column2.SetStarWidth(3);
column3.SetStarWidth(3);
_sf.PerformLayout();
_columns.Add(column1);
_columns.Add(column2);
_columns.Add(column3);
}
LayoutRect CreateHeaderRow()
{
var rc = _pageView.CreateVisual((g, v) =>
{
g.FillRectangle(v.AsRectF(), Color.FromArgb(0x33, 0x77, 0xFF));
}).LayoutRect;
rc.AnchorLeftRight(_bounds, 0, 0);
return rc;
}
void FillHeaderRow(LayoutRect headerRowRect)
{
for (int i = 0; i < _columns.Count; i++)
{
var column = _columns[i];
var tl = new TextLayout(_resolution)
{
MaxWidth = column.Width,
TextAlignment = TextAlignment.Center,
MarginLeft = 8,
MarginRight = 8,
MarginTop = 12
};
tl.Append(_headers[i], _thFormat);
tl.PerformLayout();
var cell = _textLayer.CreateVisual();
cell.Tag = tl;
cell.Draw = (g, v) =>
{
g.DrawTextLayout((TextLayout)v.Tag, new PointF(0, 0));
};
var rc = cell.LayoutRect;
rc.AnchorLeftRight(column, 0, 0);
rc.SetTop(headerRowRect, AnchorParam.Top, 0);
rc.SetHeight(tl.ContentHeight + 12 + 12);
headerRowRect.AppendMinBottom(rc, AnchorParam.Bottom);
}
_rows.Add(headerRowRect);
_sf.PerformLayout();
}
Visual CreateRow(bool even)
{
var row = _pageView.CreateVisual();
if (even)
row.Draw = (g, v) => g.FillRectangle(v.AsRectF(), Color.FromArgb(0xF2, 0xF2, 0xF2));
else
row.Draw = (g, v) => g.FillRectangle(v.AsRectF(), Color.White);
row.LayoutRect.AnchorLeftRight(_bounds, 0, 0);
return row;
}
bool FillRow(int index, LayoutRect rowRect)
{
_cells.Clear();
_texts.Clear();
for (int i = 0; i < _columns.Count; i++)
{
var column = _columns[i];
var tl = new TextLayout(_resolution)
{
MaxWidth = column.Width,
MarginAll = 8
};
if (i == 0)
{
tl.TextAlignment = TextAlignment.Center;
tl.Append((index + 1).ToString(), _tdFormat);
}
else
{
// Add random data:
tl.Append(Common.Util.LoremIpsum(1, 1, 2, 1, 15), _tdFormat);
}
tl.PerformLayout();
_texts.Add(tl);
var cell = _textLayer.CreateVisual();
cell.Tag = tl;
cell.Draw = (g, v) =>
{
g.DrawTextLayout((TextLayout)v.Tag, new PointF(0, 0));
};
var rc = cell.LayoutRect;
rc.AnchorLeftRight(column, 0, 0);
rc.SetTop(rowRect, AnchorParam.Top, 0);
rc.SetHeight(tl.ContentHeight + 8 + 8);
rowRect.AppendMinBottom(rc, AnchorParam.Bottom);
_cells.Add(cell);
}
_sf.PerformLayout();
if (rowRect.P2Y > _bounds.P2Y)
{
for (int i = 0; i < _cells.Count; i++)
{
_cells[i].Detach();
}
_cells.Clear();
return false;
}
_rows.Add(rowRect);
return true;
}
void RestoreRow(LayoutRect rowRect)
{
for (int i = 0; i < _columns.Count; i++)
{
var tl = _texts[i];
var cell = _textLayer.CreateVisual();
cell.Tag = tl;
cell.Draw = (g, v) =>
{
g.DrawTextLayout((TextLayout)v.Tag, new PointF(0, 0));
};
var rc = cell.LayoutRect;
rc.AnchorLeftRight(_columns[i], 0, 0);
rc.SetTop(rowRect, AnchorParam.Top, 0);
rc.SetHeight(tl.ContentHeight + 8 + 8);
rowRect.AppendMinBottom(rc, AnchorParam.Bottom);
}
_sf.PerformLayout();
_rows.Add(rowRect);
}
LayoutRect AddNote(string text)
{
var pad = _resolution / 8f;
var tl = new TextLayout(_resolution)
{
MaxWidth = _bounds.Width,
MaxHeight = _bounds.Height,
MarginAll = pad
};
tl.Append(text, new TextFormat { Font = StandardFonts.Helvetica });
tl.PerformLayout();
var noteVisual = _pageView.CreateVisual();
noteVisual.Tag = tl;
noteVisual.Draw = (g, v) =>
{
var rect = v.AsRectF();
g.FillRectangle(rect, Color.FromArgb(213, 221, 240));
g.DrawRectangle(rect, Color.FromArgb(59, 92, 170), 0.5f);
g.DrawTextLayout((TextLayout)v.Tag, new PointF(0, 0));
};
var textRect = tl.ContentRectangle;
textRect.Inflate(pad, pad);
var rc = noteVisual.LayoutRect;
rc.AnchorTopLeft(_bounds, 0, 0, textRect.Width, textRect.Height);
return rc;
}
}
}