Search code examples
c#gembox-presentation

Split table to multiple slides in PowerPoint file


I'm using GemBox.Presentation and I'm creating a large table in my PPTX file. Similar to this example, e.g.:

PresentationDocument presentation = new PresentationDocument();
Slide slide = presentation.Slides.AddNew(SlideLayoutType.Custom);

int rowCount = 100;

int columnCount = 4;
int columnWidth = 5;

Table table = slide.Content.AddTable(1, 1, columnCount * columnWidth, 0, LengthUnit.Centimeter);

for (int i = 0; i < columnCount; i++)
    table.Columns.AddNew(Length.From(5, LengthUnit.Centimeter));

for (int r = 0; r < rowCount; r++)
{
    TableRow row = table.Rows.AddNew(0);
    for (int c = 0; c < columnCount; c++)
    {
        TableCell cell = row.Cells.AddNew();
        TextParagraph paragraph = cell.Text.AddParagraph();
        TextRun run = paragraph.AddRun(string.Format("Cell {0}-{1}", r + 1, c + 1));
    }
}

presentation.Save("output.pptx");

As expected, the table doesn't fit on the slide:

PowerPoint slide with large table that doesn't fit

So I need to split this table into multiple tables or multiple slides so that each table fits on its slide and all rows are visible.

How can I do that?
How can I find if the new TableRow will exceed the Slide height?


Solution

  • If you have dynamic row heights (for instance, some cells may contain multiple lines of text), then you'll need to paginate the content.

    For example, see the following:

    table.Frame.FormatDrawing(new PaginatorOptions() { UpdateTableRowHeights = true });
    
    DrawingLayout tableLayout = table.Frame.Layout;
    double maxHeight = presentation.SlideSize.Height - tableLayout.Top;
    double currentHeight = 0;
    
    TableRowCollection sourceRows = table.Rows;
    TableRowCollection newRows = null;
    int currentRowIndex = 0;
    
    // Split the main table into multiple new tables based on the row heights.
    while (currentRowIndex < sourceRows.Count)
    {
        currentHeight += sourceRows[currentRowIndex].Height;
    
        // Create new slide with new table.
        if (currentHeight > maxHeight)
        {
            currentHeight = sourceRows[currentRowIndex].Height;
    
            Slide newSlide = presentation.Slides.AddNew(SlideLayoutType.Blank);
            Table newTable = newSlide.Content.AddTable(tableLayout.Left, tableLayout.Top, tableLayout.Width, 0);
    
            foreach (var column in table.Columns)
                newTable.Columns.AddClone(column);
            
            newRows = newTable.Rows;
        }
    
        // Move row from the main table to a new table.
        if (newRows != null)
        {
            newRows.AddClone(sourceRows[currentRowIndex]);
            sourceRows.RemoveAt(currentRowIndex);
        }
        else
        {
            ++currentRowIndex;
        }
    }
    
    presentation.Save("output.pptx");
    

    If you have constant row heights, like shown in your screenshot, then you can simplify this.

    For example, like the following:

    int rowsPerSlide = 16;
    
    TableRowCollection rows = table.Rows;
    DrawingLayout layout = table.Frame.Layout;
    
    for (int t = 1, tablesCount = (int)Math.Ceiling(rows.Count / (double)rowsPerSlide); t < tablesCount; t++)
    {
        Slide newSlide = presentation.Slides.AddNew(SlideLayoutType.Blank);
        Table newTable = newSlide.Content.AddTable(layout.Left, layout.Top, layout.Width, 0);
    
        foreach (var column in table.Columns)
            newTable.Columns.AddClone(column);
    
        for (int r = rowsPerSlide, rowsCount = Math.Min(rowsPerSlide * 2, rows.Count); r < rowsCount; r++)
        {
            newTable.Rows.AddClone(rows[rowsPerSlide]);
            rows.RemoveAt(rowsPerSlide);
        }
    }
    
    presentation.Save("output.pptx");