Skip to main content
RSS feed Subscribe to feed


Skip Navigation LinksHome  Extending the Spotfire Platform  Creating Extensions to the Spotfire Platform  Creating a Tool  How to Create an Export Tool  Export Tool Feature: Table Plot Rendering as HTML for Improved Printing

©Spotfire 2011

Export Tool Feature: Table Plot Rendering as HTML for Improved Printing

The simple export tool renders table plots the easiest way, as images, resulting in WYSIWYG. Still, the images contain plenty of text that may be viewed and printed better if rendered as HTML.

The CustomWebTableRenderer class uses the Spotfire API to render tables as HTML.

In the loop over all visualizations in the HTMLPrintTool class, the same trick as for the text area is used to choose the right rendering code, for instance:

CrossTablePlot crossTablePlot = visual.As<CrossTablePlot>();
else if (crossTablePlot != null)
    using (TableLayout layout = crossTablePlot.CreateLayout())
        // This visual is a CrossTable - render it as html.
        CustomWebTableRenderer renderer = new CustomWebTableRenderer(
            visualArea.Size, layout, settings, delegate(Image image)
                return HTMLPrintToolHelper.ImagePart(
                    settings.FilesFoldername, image, result);

Just as for the text area, a delegate to handle images inside the table is needed.

Making the code capable of handling frozen columns when the user has scrolled the table, requires complicated rendering code. The API enables the retrieval of cell collections for each region:

StringBuilder sb = new StringBuilder();
Rectangle bodyArea = new Rectangle(scroll.X + frozen.X, scroll.Y + frozen.Y, bodyWidth, bodyHeight);
Rectangle renderedBodyArea = new Rectangle(frozen.X, frozen.Y, bodyWidth, bodyHeight);
Rectangle topLeftArea = new Rectangle(0, 0, frozen.X, frozen.Y);
Rectangle topRightArea = new Rectangle(
    scroll.X + frozen.X, 0, bodyWidth, this.layout.FrozenRowsHeight);

Rectangle bottomLeftArea = new Rectangle(0, scroll.Y + frozen.Y, frozen.X, bodyHeight);

// Retrieve the needed cell sets.
IEnumerable<TableCell> topLeftCells = this.layout.GetCells(this.layout.GetCellRange(topLeftArea));
IEnumerable<TableCell> topRightCells = this.layout.GetCells(this.layout.GetCellRange(topRightArea));
IEnumerable<TableCell> bottomLeftCells = this.layout.GetCells(this.layout.GetCellRange(bottomLeftArea));
IEnumerable<TableCell> bodyCells = this.layout.GetCells(this.layout.GetCellRange(bodyArea));
sb.Append(RenderBody(bodyCells, renderedBodyArea));

The rendering method for the body cells, RenderBody, returns HTML that is appended to the StringBuilder builder sb. It loops over the collection of body cells to render them in the right positions as DIVs:

foreach (TableCell cell in cells)
    if (cell.ColumnIndex >= renderedLeft && cell.ColumnIndex <= renderedRight &&
        cell.RowIndex >= renderedTop && cell.RowIndex <= renderedBottom)

Some special handling is performed, to cut all truncated cell and in some cases for proper alignment, to move cells up and to the left.

        int left = cell.Bounds.Left - this.scroll.X - renderedPLeft + this.frozen.X,
            top = cell.Bounds.Top - this.scroll.Y - renderedPTop + this.frozen.Y;
        Rectangle bounds = new Rectangle(left, top, cell.Bounds.Width, cell.Bounds.Height);

        result += RenderCell(cell, bounds);

RenderCell returns the HTML for the cell, including colors, fonts, and so forth. Further details can be seen in the full listing.

The fact that the frozen columns and rows may span over more than one cell is a complicating factor. In these cases the TableCell collecting will contain many items for each rendered cell. Refer to the RenderHeading method for the handling of this problem. Also note that rows or columns that do not completely fit into the area will be skipped.