using System; using System.Collections.Generic; using System.Web.UI; using System.IO; 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; /// /// 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>(); } }