using System;
using System.Collections.Generic;
using System.IO;
using System.Web.UI;
using Autodesk.Revit.DB;
namespace Revit.SDK.Samples.ScheduleToHTML.CS
{
///
/// A class that can export a schedule to HTML.
///
class ScheduleHTMLExporter
{
///
/// Constructs a new instance of the schedule exporter operating on the input schedule.
///
/// The schedule to be exported.
public ScheduleHTMLExporter(ViewSchedule input)
{
theSchedule = input;
}
///
/// Exports the schedule to formatted HTML.
///
/// true if the export is being run interactively, false for journal playback.
/// String to contain message to display if the export fails.
/// true if HTML exported without error, false otherswise.
public bool ExportToHTML(bool bInteractive, ref string errMessage)
{
// Setup file location in temp directory
string folder = Environment.GetEnvironmentVariable("TEMP");
string htmlFile = System.IO.Path.Combine(folder, ReplaceIllegalCharacters(theSchedule.Name) + ".html");
// Initialize StringWriter instance, but handle any io exceptions and close as appropriate.
StreamWriter stringWriter = null;
try
{
stringWriter = new StreamWriter(htmlFile);
// Put HtmlTextWriter in using block because it needs to call Dispose.
using (writer = new HtmlTextWriter(stringWriter))
{
writer.AddAttribute(HtmlTextWriterAttribute.Align, "center");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
//Write schedule header
WriteHeader();
//Write schedule body
WriteBody();
writer.RenderEndTag();
}
}
catch(System.IO.IOException e)
{
// set error message and return failure, finally will close stringWriter if necessary.
errMessage = "Exception occured generating HTML: " + e.Message + " Command canceled.";
return false;
}
finally
{
if (stringWriter != null)
stringWriter.Close();
}
// Show the created file, but only if in interactive mode.
if (bInteractive)
System.Diagnostics.Process.Start(htmlFile);
return true;
}
///
/// Writes the header section of the table to the HTML file.
///
private void WriteHeader()
{
// Clear written cells
writtenCells.Clear();
// Start table to represent the header
writer.AddAttribute(HtmlTextWriterAttribute.Border, "1");
writer.RenderBeginTag(HtmlTextWriterTag.Table);
// Get header section and write each cell
headerSection = theSchedule.GetTableData().GetSectionData(SectionType.Header);
int numberOfRows = headerSection.NumberOfRows;
int numberOfColumns = headerSection.NumberOfColumns;
for (int iRow = headerSection.FirstRowNumber; iRow < numberOfRows; iRow++)
{
WriteHeaderSectionRow(iRow, numberOfColumns);
}
// Close header table
writer.RenderEndTag();
}
///
/// Writes the body section of the table to the HTML file.
///
private void WriteBody()
{
// Clear written cells
writtenCells.Clear();
// Write the start of the body table
writer.AddAttribute(HtmlTextWriterAttribute.Border, "1");
writer.RenderBeginTag(HtmlTextWriterTag.Table);
// Get body section and write contents
bodySection = theSchedule.GetTableData().GetSectionData(SectionType.Body);
int numberOfRows = bodySection.NumberOfRows;
int numberOfColumns = bodySection.NumberOfColumns;
for (int iRow = bodySection.FirstRowNumber; iRow < numberOfRows; iRow++)
{
WriteBodySectionRow(iRow, numberOfColumns);
}
// Close the table
writer.RenderEndTag();
}
///
/// Gets the Color value formatted for HTML (#XXXXXX) output.
///
/// he color.
/// The color string.
private static String GetColorHtmlString(Color color)
{
return String.Format("#{0}{1}{2}", color.Red.ToString("X"), color.Green.ToString("X"), color.Blue.ToString("X"));
}
///
/// A predefined color value used for comparison.
///
private static Color Black
{
get
{
return new Color(0, 0, 0);
}
}
///
/// A predefined color value used for comparison.
///
private static Color White
{
get
{
return new Color(255, 255, 255);
}
}
///
/// Compares two colors.
///
/// The first color.
/// The second color.
/// True if the colors are equal, false otherwise.
private bool ColorsEqual(Color color1, Color color2)
{
return color1.Red == color2.Red && color1.Green == color2.Green && color1.Blue == color2.Blue;
}
///
/// Gets the HTML string representing this horizontal alignment.
///
/// The horizontal alignment.
/// The related string.
private static String GetAlignString(HorizontalAlignmentStyle style)
{
switch (style)
{
case HorizontalAlignmentStyle.Left:
return "left";
case HorizontalAlignmentStyle.Center:
return "center";
case HorizontalAlignmentStyle.Right:
return "right";
}
return "";
}
///
/// Writes a row of the header.
///
/// The row number.
/// The number of columns to write.
private void WriteHeaderSectionRow(int iRow, int numberOfColumns)
{
WriteSectionRow(SectionType.Header, headerSection, iRow, numberOfColumns);
}
///
/// Writes a row of the body.
///
/// The row number.
/// The number of columns to write.
private void WriteBodySectionRow(int iRow, int numberOfColumns)
{
WriteSectionRow(SectionType.Body, bodySection, iRow, numberOfColumns);
}
///
/// Writes a row of a table section.
///
/// The row number.
/// The number of columns to write.
/// The section type.
/// The table section data.
private void WriteSectionRow(SectionType secType, TableSectionData data, int iRow, int numberOfColumns)
{
// Start the table row tag.
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
// Loop over the table section row.
for (int iCol = data.FirstColumnNumber; iCol < numberOfColumns; iCol++)
{
// Skip already written cells
if (writtenCells.Contains(new Tuple(iRow, iCol)))
continue;
// Get style
TableCellStyle style = data.GetTableCellStyle(iRow, iCol);
int numberOfStyleTags = 1;
// Merged cells
TableMergedCell mergedCell = data.GetMergedCell(iRow, iCol);
// If merged cell spans multiple columns
if (mergedCell.Left != mergedCell.Right)
{
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, (mergedCell.Right - mergedCell.Left + 1).ToString());
}
// If merged cell spans multiple rows
if (mergedCell.Top != mergedCell.Bottom)
{
writer.AddAttribute(HtmlTextWriterAttribute.Rowspan, (mergedCell.Bottom - mergedCell.Top + 1).ToString());
}
// Remember all written cells related to the merge
for (int iMergedRow = mergedCell.Top; iMergedRow <= mergedCell.Bottom; iMergedRow++)
{
for (int iMergedCol = mergedCell.Left; iMergedCol <= mergedCell.Right; iMergedCol++)
{
writtenCells.Add(new Tuple(iMergedRow, iMergedCol));
}
}
// Write formatting attributes for the upcoming cell
// Background color
if (!ColorsEqual(style.BackgroundColor, White))
{
writer.AddAttribute(HtmlTextWriterAttribute.Bgcolor, GetColorHtmlString(style.BackgroundColor));
}
// Horizontal alignment
writer.AddAttribute(HtmlTextWriterAttribute.Align, GetAlignString(style.FontHorizontalAlignment));
// Write cell tag
writer.RenderBeginTag(HtmlTextWriterTag.Td);
// Write subtags for the cell
// Underline
if (style.IsFontUnderline)
{
writer.RenderBeginTag(HtmlTextWriterTag.U);
numberOfStyleTags++;
}
//Italic
if (style.IsFontItalic)
{
writer.RenderBeginTag(HtmlTextWriterTag.I);
numberOfStyleTags++;
}
//Bold
if (style.IsFontBold)
{
writer.RenderBeginTag(HtmlTextWriterTag.B);
numberOfStyleTags++;
}
// Write cell text
String cellText = theSchedule.GetCellText(secType, iRow, iCol);
if (cellText.Length > 0)
{
writer.Write(cellText);
}
else
{
writer.Write(" ");
}
// Close open style tags & cell tag
for (int i = 0; i < numberOfStyleTags; i++)
{
writer.RenderEndTag();
}
}
// Close row tag
writer.RenderEndTag();
}
///
/// An utility method to replace illegal characters of the Schedule name when creating the HTML file name.
///
/// The Schedule name.
/// The updated string without illegal characters.
private static string ReplaceIllegalCharacters(string stringWithIllegalChar)
{
char[] illegalChars = System.IO.Path.GetInvalidFileNameChars();
string updated = stringWithIllegalChar;
foreach (char ch in illegalChars)
{
updated = updated.Replace(ch, '_');
}
return updated;
}
///
/// The writer for the HTML file.
///
private HtmlTextWriter writer { get; set; }
///
/// The schedule being exported.
///
private ViewSchedule theSchedule;
///
/// The body section of the table.
///
private TableSectionData bodySection;
///
/// The header section of the table.
///
private TableSectionData headerSection;
///
/// A collection of cells which have already been output. This is needed to deal with
/// cell merging - each cell should be written only once even as all the cells are iterated in
/// order.
///
List> writtenCells = new List>();
}
}