/// <summary> /// This is used to extract table of content information from a file /// that will appear in the help file's table of content. /// </summary> /// <param name="filename">The file from which to extract the /// information</param> /// <returns>The table of content entry</returns> private static TocEntry GetTocInfo(string filename) { TocEntry tocEntry; Encoding enc = Encoding.Default; string content; content = BuildProcess.ReadWithEncoding(filename, ref enc); tocEntry = new TocEntry(); tocEntry.IncludePage = !reTocExclude.IsMatch(content); tocEntry.IsDefaultTopic = reIsDefaultTopic.IsMatch(content); Match m = reSortOrder.Match(content); if (m.Success) { tocEntry.SortOrder = Convert.ToInt32( m.Groups["SortOrder"].Value, CultureInfo.InvariantCulture); } // Get the page title if possible. If not found, use the filename // without the path or extension as the page title. m = rePageTitle.Match(content); if (!m.Success) { tocEntry.Title = Path.GetFileNameWithoutExtension(filename); } else { tocEntry.Title = HttpUtility.HtmlDecode( m.Groups["Title"].Value).Replace("\r", "").Replace("\n", ""); } // Since we've got the file loaded, see if there are links // that need to be resolved when the file is copied, if it // contains <pre> blocks that should be colorized, or if it // contains tags or shared content items that need replacing. tocEntry.HasLinks = reResolveLinks.IsMatch(content); tocEntry.HasCodeBlocks = reCodeBlock.IsMatch(content); tocEntry.NeedsColorizing = reColorizeCheck.IsMatch(content); tocEntry.HasProjectTags = (reProjectTags.IsMatch(content) || reSharedContent.IsMatch(content)); return(tocEntry); }
/// <summary> /// This is called to fixup the comments for C++ compiler generated /// XML comments files. /// </summary> /// <remarks>The C++ compiler generates method signatures that differ /// from the other .NET compilers for methods that take generics as /// parameters. These methods fail to get documented as they do not /// match the output of <b>MRefBuilder</b>. The C# and VB.NET /// compilers generate names that do match it and this option is not /// needed for comments files generated by them.</remarks> public void FixupComments() { this.Save(); comments = null; members = null; enc = Encoding.UTF8; // Read it with the appropriate encoding string content = BuildProcess.ReadWithEncoding( sourcePath, ref enc); // Strip out "`" followed by digits and "^" in member names content = reFixupComments1.Replace(content, "$1"); content = reFixupComments2.Replace(content, "$1"); // Write the file back out using its original encoding using (StreamWriter sw = new StreamWriter(sourcePath, false, enc)) { sw.Write(content); } }
/// <summary> /// This is called to generate the HTML table of content when /// creating the website output. /// </summary> /// <returns>The HTML to insert for the table of content.</returns> protected string GenerateHtmlToc() { XmlDocument toc; Encoding enc = Encoding.Default; StringBuilder sb = new StringBuilder(2048); string content; // When reading the file, use the default encoding but detect the // encoding if byte order marks are present. content = BuildProcess.ReadWithEncoding(workingFolder + project.HtmlHelpName + ".hhc", ref enc); toc = new XmlDocument(); toc.LoadXml(content); // Get the TOC entries from the first UL tag in the body XmlNodeList entries = toc.ChildNodes[0].ChildNodes[0]. ChildNodes[0].ChildNodes; this.AppendTocEntry(entries, sb); return(sb.ToString()); }
/// <summary> /// This is called to load an additional content file, resolve links /// to namespace content and copy it to the output folder. /// </summary> /// <param name="sourceFile">The source filename to copy</param> /// <param name="destFile">The destination filename</param> /// <param name="entry">The entry being resolved.</param> private void ResolveLinksAndCopy(string sourceFile, string destFile, TocEntry entry) { Encoding enc = Encoding.Default; string content, script, syntaxFile; int pos; this.ReportProgress("{0} -> {1}", sourceFile, destFile); // When reading the file, use the default encoding but detect the // encoding if byte order marks are present. content = BuildProcess.ReadWithEncoding(sourceFile, ref enc); // Use a regular expression to find and replace all <see> // tags with link to the help file content. if (entry.HasLinks) { content = reResolveLinks.Replace(content, linkMatchEval); } // Expand <code> tags if necessary if (entry.HasCodeBlocks) { content = reCodeBlock.Replace(content, codeBlockMatchEval); } // Colorize <pre> tags if necessary if (entry.NeedsColorizing || entry.HasCodeBlocks) { // Initialize code colorizer on first use if (codeColorizer == null) { codeColorizer = new CodeColorizer( shfbFolder + @"Colorizer\highlight.xml", shfbFolder + @"Colorizer\highlight.xsl"); } // Set the path the "Copy" image codeColorizer.CopyImageUrl = pathToRoot + "icons/CopyCode.gif"; // Colorize it and replace the "Copy" literal text with the // shared content include item so that it gets localized. content = codeColorizer.ProcessAndHighlightText(content); content = content.Replace(codeColorizer.CopyText + "</span", "<include item=\"copyCode\"/></span"); entry.HasProjectTags = true; // Add the links to the colorizer stylesheet and script files script = String.Format(CultureInfo.InvariantCulture, "<link type='text/css' rel='stylesheet' " + "href='{0}html/highlight.css' />" + "<script type='text/javascript' " + "src='{0}html/highlight.js'></script>", pathToRoot); pos = content.IndexOf("</head>"); // Create a <head> section if one doesn't exist if (pos == -1) { script = "<head>" + script + "</head>"; pos = content.IndexOf("<html>"); if (pos != -1) { pos += 6; } else { pos = 0; } } content = content.Insert(pos, script); // Copy the colorizer files if not already there if (!File.Exists(workingFolder + @"Output\html\highlight.css")) { syntaxFile = shfbFolder + @"Colorizer\highlight.css"; File.Copy(syntaxFile, workingFolder + @"Output\html\highlight.css"); File.SetAttributes(syntaxFile, FileAttributes.Normal); syntaxFile = shfbFolder + @"Colorizer\highlight.js"; File.Copy(syntaxFile, workingFolder + @"Output\html\highlight.js"); File.SetAttributes(syntaxFile, FileAttributes.Normal); // This one may exist as the default presentation styles // contain an image by this name. syntaxFile = shfbFolder + @"Colorizer\CopyCode.gif"; File.Copy(syntaxFile, workingFolder + @"Output\icons\CopyCode.gif", true); File.SetAttributes(syntaxFile, FileAttributes.Normal); } } // Replace project option tags with project option values if (entry.HasProjectTags) { content = reProjectTags.Replace(content, fieldMatchEval); // Shared content items can be nested while (reSharedContent.IsMatch(content)) { content = reSharedContent.Replace(content, contentMatchEval); } } // Write the file back out with the appropriate encoding using (StreamWriter sw = new StreamWriter(destFile, false, enc)) { sw.Write(content); } }
/// <summary> /// This is called to determine the default topic for the help file /// and insert any additional table of content entries for the /// additional content files. /// </summary> /// <param name="format">The format of the table of content /// (HtmlHelp1x, HtmlHelp2x, or Website).</param> /// <remarks>In the absence of an additional content item with a /// default topic indicator, the default page is determined by /// extracting the first entry from the generated table of contents /// file. If an additional content item with a default topic indicator /// has been specified, it will be the used instead. The default /// topic is not used by HTML Help 2.x files.</remarks> /// <exception cref="ArgumentException">This is thrown if the /// format is not <b>HtmlHelp1x</b>, <b>HtmlHelp2x</b>, or /// <b>Website</b>.</exception> protected void UpdateTableOfContent(HelpFileFormat format) { Encoding enc = Encoding.Default; string tocFile, content; bool tocChanged = false; int endTagPos; if (format != HelpFileFormat.HtmlHelp1x && format != HelpFileFormat.HtmlHelp2x && format != HelpFileFormat.Website) { throw new ArgumentException("The format specified must be " + "a single format, not a combination", "format"); } // HTML 2.x or HTML 1.x/website if (format != HelpFileFormat.HtmlHelp2x) { tocFile = workingFolder + project.HtmlHelpName + ".hhc"; } else { tocFile = workingFolder + project.HtmlHelpName + ".hxt"; } // When reading the file, use the default encoding but detect the // encoding if byte order marks are present. content = BuildProcess.ReadWithEncoding(tocFile, ref enc); // We only need the default page for HTML Help 1.x files and // websites. if (format != HelpFileFormat.HtmlHelp2x) { // Don't bother if an explicit default has been specified if (defaultTopic == null) { Match m = reExtractDefTopic.Match(content); if (!m.Success) { throw new BuilderException("Unable to determine " + "default page in " + tocFile); } defaultTopic = m.Groups["Filename"].Value; } // Remove the root namespace container if not wanted and it // is there. if (!project.RootNamespaceContainer && re1xRootEntry.IsMatch(content)) { tocChanged = true; content = re1xRootEntry.Replace(content, String.Empty, 1); endTagPos = content.LastIndexOf("</UL>"); if (endTagPos != -1) { content = content.Remove(endTagPos, 5); } } // Reset the encoding as HTML 1.x does not appear to // support UTF-8 encoding in the table of content. enc = Encoding.Default; tocChanged = true; } else { // Remove the root namespace container if not wanted and // it is there. if (!project.RootNamespaceContainer && content.LastIndexOf("</HelpTOCNode>") != -1) { tocChanged = true; content = re2xRootEntry.Replace(content, String.Empty, 1); endTagPos = content.LastIndexOf("</HelpTOCNode>"); content = content.Remove(endTagPos, 14); } } // Update and save table of content with additional items if (tocChanged || (toc != null && toc.Count != 0)) { // The additional entries can go in ahead of the namespace // documentation entries or after them. if (toc != null && toc.Count != 0) { if (format != HelpFileFormat.HtmlHelp2x) { if (project.ContentPlacement == ContentPlacement.AboveNamespaces) { content = content.Insert(content.IndexOf( "<UL>") + 6, toc.ToString()); } else { content = content.Insert(content.LastIndexOf( "</UL>"), toc.ToString()); } } else { if (project.ContentPlacement == ContentPlacement.AboveNamespaces) { content = content.Insert(content.IndexOf( "\"1.0\">") + 8, toc.ToString(HelpFileFormat.HtmlHelp2x)); } else { content = content.Insert(content.IndexOf( "</HelpTOC>"), toc.ToString(HelpFileFormat.HtmlHelp2x)); } } } // We'll parse the HTML TOC as an XML file later on to // generate the tree view TOC for the website. This // requires some fix-ups to the <param> tags and the // removal of the DOCTYPE tag. if (format == HelpFileFormat.Website) { content = reParamFixUp.Replace(content, "<param$1/>"); content = reEncodeLeft.Replace(content, "$1<$3"); content = reEncodeRight.Replace(content, "$1>$3"); content = content.Remove(0, content.IndexOf("<HTML>")); } // Write the file back out with the appropriate encoding using (StreamWriter sw = new StreamWriter(tocFile, false, enc)) { sw.Write(content); } } }
/// <summary> /// Transform the specified template by inserting the necessary /// values into the place holders and saving it to the working folder. /// </summary> /// <param name="template">The template to transform</param> /// <param name="sourceFolder">The folder where the template is /// located</param> /// <param name="destFolder">The folder in which to save the /// transformed file</param> /// <returns>The path to the transformed file</returns> protected string TransformTemplate(string template, string sourceFolder, string destFolder) { Encoding enc = Encoding.Default; string templateText, transformedFile; if (template == null) { throw new ArgumentNullException("template"); } if (sourceFolder == null) { throw new ArgumentNullException("sourceFolder"); } if (destFolder == null) { throw new ArgumentNullException("destFolder"); } if (!sourceFolder.EndsWith(@"\")) { sourceFolder += @"\"; } if (!destFolder.EndsWith(@"\")) { destFolder += @"\"; } try { // When reading the file, use the default encoding but // detect the encoding if byte order marks are present. templateText = BuildProcess.ReadWithEncoding( sourceFolder + template, ref enc); // Use a regular expression to find and replace all field // tags with a matching value from the project. templateText = reField.Replace(templateText, fieldMatchEval); transformedFile = destFolder + template; // Write the file back out using its original encoding using (StreamWriter sw = new StreamWriter(transformedFile, false, enc)) { sw.Write(templateText); } } catch (Exception ex) { throw new BuilderException(String.Format( CultureInfo.CurrentCulture, "Unable to transform template '{0}': {1}", template, ex.Message), ex); } return(transformedFile); }