//===================================================================== /// <summary> /// Constructor /// </summary> public MainWindow() { InitializeComponent(); cboLanguage.SelectedIndex = 4; txtSampleText.Text = File.ReadAllText(@"..\..\MainWindow.xaml"); // Load the template flow document flowTemplate = File.ReadAllText(@"..\..\..\ColorizerLibrary\Template\DocumentTemplate.xaml"); // Create the code colorizer codeColorizer = new CodeColorizer(); codeColorizer.Init(); }
private void Page_Load(object sender, System.EventArgs e) { try { log4net.Config.DOMConfigurator.Configure(); if (Log.IsInfoEnabled) Log.Info("Load page"); Colorizer = ColorizerLibrary.Config.DOMConfigurator.Configure(); TextColorizerControl1.SyntaxEngine = Colorizer; } catch( Exception exception) { Log.Fatal( exception ); throw exception; } }
protected void Page_Load(object sender, System.EventArgs e) { if(!Page.IsPostBack) { // In this case, we'll use a static instance to improve // performance with repeated use. syntaxEngine = new CodeColorizer(); syntaxEngine.Init(); string demoSource = Server.MapPath("Default.aspx.cs"); using(StreamReader sr = new StreamReader(demoSource)) { txtContent.Text = sr.ReadToEnd(); } } }
/// <summary> /// Method referencing type in third-party assembly /// </summary> /// <param name="colorizer">Colorizer</param> /// <param name="language">Colorizer language</param> public void SetColorizer(CodeColorizer colorizer, string language) { }
/// <summary> /// Method referencing type in third-party assembly /// </summary> /// <param name="colorizer">Colorizer</param> public void SetColorizer(CodeColorizer colorizer) { }
/// <summary> /// The parameter is a type in a referenced assembly, not documented, but should still show up in /// the TOC title and topic title. /// </summary> /// <param name="colorizer">Colorizer</param> /// <param name="language">Colorizer language</param> public ReferenceTest(CodeColorizer colorizer, string language) { }
//===================================================================== /// <inheritdoc /> /// <remarks>See the <see cref="CodeBlockComponent"/> class topic for an example of the configuration and /// usage.</remarks> /// <exception cref="ConfigurationErrorsException">This is thrown if an error is detected in the /// configuration.</exception> public override void Initialize(XPathNavigator configuration) { XPathNavigator nav; string value = null; bool allowMissingSource = false, useDefaultTitle = false; outputPaths = new List<string>(); colorizedCodeBlocks = new Dictionary<string, XmlNode>(); Assembly asm = Assembly.GetExecutingAssembly(); FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location); base.WriteMessage(MessageLevel.Info, "[{0}, version {1}]\r\n Code Block Component. " + "{2}.\r\n Portions copyright (c) 2003, Jonathan de Halleux, All rights reserved.\r\n" + " http://SHFB.CodePlex.com", fvi.ProductName, fvi.ProductVersion, fvi.LegalCopyright); // The <basePath> element is optional. If not set, it will assume the current folder as the base // path for source references with relative paths. nav = configuration.SelectSingleNode("basePath"); if(nav != null) basePath = nav.GetAttribute("value", String.Empty); if(String.IsNullOrEmpty(basePath)) basePath = Directory.GetCurrentDirectory(); if(basePath[basePath.Length - 1] != '\\') basePath += @"\"; // Get the output paths foreach(XPathNavigator path in configuration.Select("outputPaths/path")) { value = path.GetAttribute("value", String.Empty); if(value[value.Length - 1] != '\\') value += @"\"; if(!Directory.Exists(value)) Directory.CreateDirectory(value); outputPaths.Add(value); // The Open XML format doesn't support all features and requires a custom transformation if(value.IndexOf("OpenXML", StringComparison.OrdinalIgnoreCase) != -1) isOpenXml = true; } if(outputPaths.Count == 0) throw new ConfigurationErrorsException("You must specify at least one <path> element in the " + "<outputPaths> element. You may need to delete and re-add the component to the project " + "to obtain updated configuration settings."); // The <allowMissingSource> element is optional. If not set, missing source files generate an error. nav = configuration.SelectSingleNode("allowMissingSource"); if(nav != null) { value = nav.GetAttribute("value", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out allowMissingSource)) throw new ConfigurationErrorsException("You must specify a Boolean value for the " + "<allowMissingSource> 'value' attribute."); } if(!allowMissingSource) messageLevel = MessageLevel.Error; else messageLevel = MessageLevel.Warn; // The <removeRegionMarkers> element is optional. If not set, region markers in imported code are // left alone. nav = configuration.SelectSingleNode("removeRegionMarkers"); if(nav != null) { value = nav.GetAttribute("value", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out removeRegionMarkers)) throw new ConfigurationErrorsException("You must specify a Boolean value for the " + "<removeRegionMarkers> 'value' attribute."); } // The <colorizer> element is required and defines the defaults for the code colorizer nav = configuration.SelectSingleNode("colorizer"); if(nav == null) throw new ConfigurationErrorsException("You must specify a <colorizer> element to define the " + "code colorizer options."); // The file and URL values are all required syntaxFile = nav.GetAttribute("syntaxFile", String.Empty); styleFile = nav.GetAttribute("styleFile", String.Empty); stylesheet = nav.GetAttribute("stylesheet", String.Empty); scriptFile = nav.GetAttribute("scriptFile", String.Empty); if(String.IsNullOrEmpty(syntaxFile)) throw new ConfigurationErrorsException("You must specify a 'syntaxFile' attribute on the " + "<colorizer> element."); if(String.IsNullOrEmpty(styleFile)) throw new ConfigurationErrorsException("You must specify a 'styleFile' attribute on the " + "<colorizer> element."); if(String.IsNullOrEmpty(stylesheet) && !isOpenXml) throw new ConfigurationErrorsException("You must specify a 'stylesheet' attribute on the " + "<colorizer> element"); if(String.IsNullOrEmpty(scriptFile) && !isOpenXml) throw new ConfigurationErrorsException("You must specify a 'scriptFile' attribute on the " + "<colorizer> element"); // The syntax and style files must also exist. The "copy" image URL is just a location and it // doesn't have to exist yet. syntaxFile = Path.GetFullPath(syntaxFile); styleFile = Path.GetFullPath(styleFile); if(!File.Exists(syntaxFile)) throw new ConfigurationErrorsException("The specified syntax file could not be found: " + syntaxFile); if(!File.Exists(styleFile)) throw new ConfigurationErrorsException("The specified style file could not be found: " + styleFile); if(!isOpenXml) { stylesheet = Path.GetFullPath(stylesheet); scriptFile = Path.GetFullPath(scriptFile); if(!File.Exists(stylesheet)) throw new ConfigurationErrorsException("Could not find style sheet file: " + stylesheet); if(!File.Exists(scriptFile)) throw new ConfigurationErrorsException("Could not find script file: " + scriptFile); } // Optional attributes defaultLanguage = nav.GetAttribute("language", String.Empty); if(String.IsNullOrEmpty(defaultLanguage)) defaultLanguage = "none"; value = nav.GetAttribute("numberLines", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out numberLines)) throw new ConfigurationErrorsException("You must specify a Boolean value for the " + "'numberLines' attribute."); value = nav.GetAttribute("outlining", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out outliningEnabled)) throw new ConfigurationErrorsException("You must specify a Boolean value for the " + "'outlining' attribute."); value = nav.GetAttribute("keepSeeTags", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out keepSeeTags)) throw new ConfigurationErrorsException("You must specify a Boolean value for the " + "'keepSeeTags' attribute."); value = nav.GetAttribute("tabSize", String.Empty); if(!String.IsNullOrEmpty(value) && !Int32.TryParse(value, out defaultTabSize)) throw new ConfigurationErrorsException("You must specify an integer value for the 'tabSize' " + "attribute."); value = nav.GetAttribute("defaultTitle", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out useDefaultTitle)) throw new ConfigurationErrorsException("You must specify a Boolean value for the " + "'defaultTitle' attribute."); value = nav.GetAttribute("disabled", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out isDisabled)) throw new ConfigurationErrorsException("You must specify a Boolean value for the " + "'disabled' attribute."); if(isOpenXml) { numberLines = outliningEnabled = false; // If the default transform is specified, switch to the Open XML version. This can happen if // the user adds the code block component to their project to override the default settings. string defaultTransform = Path.Combine(Path.GetDirectoryName(asm.Location), @"PresentationStyles\Colorizer\highlight.xsl"); if(styleFile.Equals(defaultTransform, StringComparison.OrdinalIgnoreCase)) { styleFile = Path.Combine(Path.GetDirectoryName(defaultTransform), "highlight_openxml.xsl"); if(!File.Exists(styleFile)) throw new ConfigurationErrorsException("The specified style file could not be found: " + styleFile); } } // Initialize the code colorizer colorizer = new CodeColorizer(syntaxFile, styleFile); colorizer.UseDefaultTitle = useDefaultTitle; colorizer.TabSize = defaultTabSize; // Create the XPath queries context = new CustomContext(); context.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/5"); referenceRoot = XPathExpression.Compile("document/comments"); referenceCode = XPathExpression.Compile("//code"); nestedRefCode = XPathExpression.Compile("code"); conceptualRoot = XPathExpression.Compile("document/topic"); conceptualCode = XPathExpression.Compile("//ddue:code"); conceptualCode.SetContext(context); nestedConceptCode = XPathExpression.Compile("ddue:code"); nestedConceptCode.SetContext(context); // Hook up the event handler to complete the process after the topic is transformed to HTML base.BuildAssembler.ComponentEvent += TransformComponent_TopicTransformed; }
/// <summary> /// Constructor /// </summary> /// <param name="assembler">A reference to the build assembler.</param> /// <param name="configuration">The configuration information</param> /// <remarks><b>NOTE:</b> This component must be paired with the /// <see cref="PostTransformComponent"/>. See the /// <see cref="CodeBlockComponent"/> class topic for an example of the /// configuration and usage.</remarks> /// <exception cref="ConfigurationErrorsException">This is thrown if /// an error is detected in the configuration.</exception> public CodeBlockComponent(BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { XPathNavigator nav; string value = null; bool allowMissingSource = false, useDefaultTitle = false; Assembly asm = Assembly.GetExecutingAssembly(); FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location); base.WriteMessage(MessageLevel.Info, String.Format( CultureInfo.InvariantCulture, "\r\n [{0}, version {1}]\r\n Code Block Component. " + "{2}.\r\n Portions copyright (c) 2003, Jonathan de " + "Halleux, All rights reserved.\r\n" + " http://SHFB.CodePlex.com", fvi.ProductName, fvi.ProductVersion, fvi.LegalCopyright)); // The <basePath> element is optional. If not set, it will assume // the current folder as the base path for source references with // relative paths. nav = configuration.SelectSingleNode("basePath"); if(nav != null) basePath = nav.GetAttribute("value", String.Empty); if(String.IsNullOrEmpty(basePath)) basePath = Directory.GetCurrentDirectory(); if(basePath[basePath.Length - 1] != '\\') basePath += @"\"; // The <languageFilter> element is optional. If not set, language // filtering is enabled by default. nav = configuration.SelectSingleNode("languageFilter"); if(nav != null) { value = nav.GetAttribute("value", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out languageFilter)) throw new ConfigurationErrorsException("You must specify " + "a Boolean value for the <languageFilter> 'value' " + "attribute."); } else languageFilter = true; // The <allowMissingSource> element is optional. If not set, // missing source files generate an error. nav = configuration.SelectSingleNode("allowMissingSource"); if(nav != null) { value = nav.GetAttribute("value", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out allowMissingSource)) throw new ConfigurationErrorsException("You must specify " + "a Boolean value for the <allowMissingSource> 'value' " + "attribute."); } if(!allowMissingSource) messageLevel = MessageLevel.Error; else messageLevel = MessageLevel.Warn; // The <removeRegionMarkers> element is optional. If not set, // region markers in imported code are left alone. nav = configuration.SelectSingleNode("removeRegionMarkers"); if(nav != null) { value = nav.GetAttribute("value", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out removeRegionMarkers)) throw new ConfigurationErrorsException("You must specify " + "a Boolean value for the <removeRegionMarkers> 'value' " + "attribute."); } // The <colorizer> element is required and defines the defaults // for the code colorizer. nav = configuration.SelectSingleNode("colorizer"); if(nav == null) throw new ConfigurationErrorsException("You must specify " + "a <colorizer> element to define the code colorizer " + "options."); // The syntax configuration file and XSLT style file are required syntaxFile = nav.GetAttribute("syntaxFile", String.Empty); styleFile = nav.GetAttribute("styleFile", String.Empty); copyImage = nav.GetAttribute("copyImageUrl", String.Empty); if(String.IsNullOrEmpty(syntaxFile)) throw new ConfigurationErrorsException("You must specify " + "a 'syntaxFile' attribute on the <colorizer> element."); if(String.IsNullOrEmpty(styleFile)) throw new ConfigurationErrorsException("You must specify " + "a 'styleFile' attribute on the <colorizer> element."); if(String.IsNullOrEmpty(copyImage)) throw new ConfigurationErrorsException("You must specify " + "a 'copyImageUrl' attribute on the <colorizer> element."); // The syntax and style files must also exist. The "copy" image // is just a location and it doesn't have to exist yet. if(!File.Exists(syntaxFile)) throw new ConfigurationErrorsException("The specified " + "syntax file could not be found: " + syntaxFile); if(!File.Exists(styleFile)) throw new ConfigurationErrorsException("The specified " + "style file could not be found: " + styleFile); // Optional attributes defaultLanguage = nav.GetAttribute("language", String.Empty); if(String.IsNullOrEmpty(defaultLanguage)) defaultLanguage = "none"; value = nav.GetAttribute("numberLines", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out numberLines)) throw new ConfigurationErrorsException("You must specify " + "a Boolean value for the 'numberLines' attribute."); value = nav.GetAttribute("outlining", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out outliningEnabled)) throw new ConfigurationErrorsException("You must specify " + "a Boolean value for the 'outlining' attribute."); value = nav.GetAttribute("keepSeeTags", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out keepSeeTags)) throw new ConfigurationErrorsException("You must specify " + "a Boolean value for the 'keepSeeTags' attribute."); value = nav.GetAttribute("tabSize", String.Empty); if(!String.IsNullOrEmpty(value) && !Int32.TryParse(value, out defaultTabSize)) throw new ConfigurationErrorsException("You must specify " + "an integer value for the 'tabSize' attribute."); value = nav.GetAttribute("defaultTitle", String.Empty); if(!String.IsNullOrEmpty(value) && !Boolean.TryParse(value, out useDefaultTitle)) throw new ConfigurationErrorsException("You must specify " + "a Boolean value for the 'defaultTitle' attribute."); // Initialize the code colorizer colorizer = new CodeColorizer(syntaxFile, styleFile); colorizer.UseDefaultTitle = useDefaultTitle; colorizer.CopyImageUrl = copyImageLocation = copyImage; colorizer.TabSize = defaultTabSize; copyText = colorizer.CopyText; // Get the language filters in effect. If a code block uses // a language without a filter, it won't be have the attributes // added to it so that it stays visible all the time. nav = configuration.Clone(); nav.MoveToParent(); filtersPresent = new List<string>(); foreach(XPathNavigator filter in nav.Select("//language/@name")) filtersPresent.Add(filter.Value); // Create the XPath queries context = new CustomContext(); context.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/5"); referenceRoot = XPathExpression.Compile("document/comments"); referenceCode = XPathExpression.Compile("//code"); nestedRefCode = XPathExpression.Compile("code"); conceptualRoot = XPathExpression.Compile("document/topic"); conceptualCode = XPathExpression.Compile("//ddue:code"); conceptualCode.SetContext(context); nestedConceptCode = XPathExpression.Compile("ddue:code"); nestedConceptCode.SetContext(context); }
/// <summary> /// This is used to transform a *.topic file into a *.html file using /// an XSLT transformation based on the presentation style. /// </summary> /// <param name="sourceFile">The source topic filename</param> private void XslTransform(string sourceFile) { TocEntry tocInfo; XmlReader reader = null; XmlWriter writer = null; XsltSettings settings; XmlReaderSettings readerSettings; XmlWriterSettings writerSettings; Encoding enc = Encoding.Default; FileItemCollection transforms; string content; string sourceStylesheet, destFile = Path.ChangeExtension(sourceFile, ".html"); try { readerSettings = new XmlReaderSettings(); readerSettings.ProhibitDtd = false; readerSettings.CloseInput = true; // Create the transform on first use if(xslTransform == null) { transforms = new FileItemCollection(project, BuildAction.TopicTransform); if(transforms.Count != 0) { if(transforms.Count > 1) this.ReportWarning("BE0011", "Multiple topic " + "transformations found. Using '{0}'", transforms[0].FullPath); sourceStylesheet = transforms[0].FullPath; } else sourceStylesheet = templateFolder + presentationParam + ".xsl"; xslStylesheet = workingFolder + Path.GetFileName(sourceStylesheet); tocInfo = BuildProcess.GetTocInfo(sourceStylesheet); // The stylesheet may contain shared content items so we // must resolve it this way rather than using // TransformTemplate. this.ResolveLinksAndCopy(sourceStylesheet, xslStylesheet, tocInfo); xslTransform = new XslCompiledTransform(); settings = new XsltSettings(true, true); xslArguments = new XsltArgumentList(); xslTransform.Load(XmlReader.Create(xslStylesheet, readerSettings), settings, new XmlUrlResolver()); } this.ReportProgress("Applying XSL transformation '{0}' to '{1}'.", xslStylesheet, sourceFile); reader = XmlReader.Create(sourceFile, readerSettings); writerSettings = xslTransform.OutputSettings.Clone(); writerSettings.CloseOutput = true; writerSettings.Indent = false; writer = XmlWriter.Create(destFile, writerSettings); xslArguments.Clear(); xslArguments.AddParam("pathToRoot", String.Empty, pathToRoot); xslTransform.Transform(reader, xslArguments, writer); } catch(Exception ex) { throw new BuilderException("BE0017", String.Format( CultureInfo.InvariantCulture, "Unexpected error " + "using '{0}' to transform additional content file '{1}' " + "to '{2}'. The error is: {3}\r\n{4}", xslStylesheet, sourceFile, destFile, ex.Message, (ex.InnerException == null) ? String.Empty : ex.InnerException.Message)); } finally { if(reader != null) reader.Close(); if(writer != null) { writer.Flush(); writer.Close(); } } // The source topic file is deleted as the transformed file // takes its place. File.Delete(sourceFile); // <span> and <script> tags cannot be self-closing if empty. // The template may contain them correctly but when written out // as XML, they get converted to self-closing tags which breaks // them. To fix them, convert them to full start and close tags. content = BuildProcess.ReadWithEncoding(destFile, ref enc); content = reSpanScript.Replace(content, "<$1$2></$1>"); // An XSL transform might have added tags and include items that // need replacing so run it through those options if needed. tocInfo = BuildProcess.GetTocInfo(destFile); // Expand <code> tags if necessary if(tocInfo.HasCodeBlocks) content = reCodeBlock.Replace(content, codeBlockMatchEval); // Colorize <pre> tags if necessary if(tocInfo.NeedsColorizing || tocInfo.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"); tocInfo.HasProjectTags = true; } // Use a regular expression to find and replace all tags with // cref attributes with a link to the help file content. This // needs to happen after the code block processing as they // may contain <see> tags that need to be resolved. if(tocInfo.HasLinks || tocInfo.HasCodeBlocks) content = reResolveLinks.Replace(content, linkMatchEval); // Replace project option tags with project option values if(tocInfo.HasProjectTags) { // Project tags can be nested while(reProjectTags.IsMatch(content)) 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 copy the additional content files and build a /// list of them for the help file project. /// </summary> /// <remarks>Note that for wilcard content items, the folders are /// copied recursively.</remarks> protected void CopyAdditionalContent() { Dictionary<string, TocEntryCollection> tocItems = new Dictionary<string, TocEntryCollection>(); TocEntryCollection parentToc; TocEntry tocEntry, tocFolder; FileItemCollection contentItems; string projectPath, source, filename, dirName; string[] parts; int part; this.ReportProgress(BuildStep.CopyAdditionalContent, "Copying additional content files..."); if(this.ExecutePlugIns(ExecutionBehaviors.InsteadOf)) return; // A plug-in might add or remove additional content so call // them before checking to see if there is anything to copy. this.ExecutePlugIns(ExecutionBehaviors.Before); if(!project.HasItems(BuildAction.Content) && !project.HasItems(BuildAction.SiteMap)) { this.ReportProgress("No additional content to copy"); this.ExecutePlugIns(ExecutionBehaviors.After); return; } toc = new TocEntryCollection(); tocItems.Add(String.Empty, toc); // Now copy the content files contentItems = new FileItemCollection(project, BuildAction.Content); projectPath = FolderPath.TerminatePath(Path.GetDirectoryName(originalProjectName)); foreach(FileItem fileItem in contentItems) { source = fileItem.Include; dirName = Path.GetDirectoryName(fileItem.Link.ToString().Substring(projectPath.Length)); filename = Path.Combine(dirName, Path.GetFileName(source)); if(source.EndsWith(".htm", StringComparison.OrdinalIgnoreCase) || source.EndsWith(".html", StringComparison.OrdinalIgnoreCase) || source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) { tocEntry = BuildProcess.GetTocInfo(source); // Exclude the page if so indicated via the item metadata if(fileItem.ExcludeFromToc) tocEntry.IncludePage = false; // .topic files get transformed into .html files if(source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) filename = Path.ChangeExtension(filename, ".html"); tocEntry.SourceFile = new FilePath(source, project); tocEntry.DestinationFile = filename; // Figure out where to add the entry parts = tocEntry.DestinationFile.Split('\\'); pathToRoot = String.Empty; parentToc = toc; for(part = 0; part < parts.Length - 1; part++) { pathToRoot += parts[part] + @"\"; // Create place holders if necessary if(!tocItems.TryGetValue(pathToRoot, out parentToc)) { tocFolder = new TocEntry(project); tocFolder.Title = parts[part]; if(part == 0) toc.Add(tocFolder); else tocItems[String.Join(@"\", parts, 0, part) + @"\"].Add(tocFolder); parentToc = tocFolder.Children; tocItems.Add(pathToRoot, parentToc); } } parentToc.Add(tocEntry); if(tocEntry.IncludePage && tocEntry.IsDefaultTopic) defaultTopic = tocEntry.DestinationFile; } else tocEntry = null; this.EnsureOutputFoldersExist(dirName); foreach(string baseFolder in this.HelpFormatOutputFolders) { // If the file contains items that need to be resolved, // it is handled separately. if(tocEntry != null && (tocEntry.HasLinks || tocEntry.HasCodeBlocks || tocEntry.NeedsColorizing || tocEntry.HasProjectTags || source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase))) { // Figure out the path to the root if needed parts = tocEntry.DestinationFile.Split('\\'); pathToRoot = String.Empty; for(part = 0; part < parts.Length - 1; part++) pathToRoot += "../"; this.ResolveLinksAndCopy(source, baseFolder + filename, tocEntry); } else { this.ReportProgress("{0} -> {1}{2}", source, baseFolder, filename); // All attributes are turned off so that we can delete it later File.Copy(source, baseFolder + filename, true); File.SetAttributes(baseFolder + filename, FileAttributes.Normal); } } } // Remove excluded nodes, merge folder item info into the root // nodes, and sort the items. If a site map isn't defined, this // will define the layout of the items. toc.RemoveExcludedNodes(null); toc.Sort(); codeColorizer = null; sharedContent = sharedBuilderContent = styleContent = null; this.ExecutePlugIns(ExecutionBehaviors.After); }
/// <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> internal void ResolveLinksAndCopy(string sourceFile, string destFile, TocEntry entry) { Encoding enc = Encoding.Default; string content, script, syntaxFile; int pos; // For topics, change the extenstion back to ".topic". It's // ".html" in the TOC as that's what it ends up as after // transformation. if(sourceFile.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) destFile = Path.ChangeExtension(destFile, ".topic"); 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); // 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 // unless it's going to be transformed. In which case, the // links should be in the XSL stylesheet. if(!sourceFile.EndsWith(".topic", StringComparison.OrdinalIgnoreCase) && !sourceFile.EndsWith(".xsl", StringComparison.OrdinalIgnoreCase)) { script = String.Format(CultureInfo.InvariantCulture, "<link type='text/css' rel='stylesheet' href='{0}styles/highlight.css' />" + "<script type='text/javascript' src='{0}scripts/highlight.js'></script>", pathToRoot); pos = content.IndexOf("</head>", StringComparison.Ordinal); // Create a <head> section if one doesn't exist if(pos == -1) { script = "<head>" + script + "</head>"; pos = content.IndexOf("<html>", StringComparison.Ordinal); if(pos != -1) pos += 6; else pos = 0; } content = content.Insert(pos, script); } // Copy the colorizer files if not already there this.EnsureOutputFoldersExist("icons"); this.EnsureOutputFoldersExist("styles"); this.EnsureOutputFoldersExist("scripts"); foreach(string baseFolder in this.HelpFormatOutputFolders) if(!File.Exists(baseFolder + @"styles\highlight.css")) { syntaxFile = baseFolder + @"styles\highlight.css"; File.Copy(shfbFolder + @"Colorizer\highlight.css", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); syntaxFile = baseFolder + @"scripts\highlight.js"; File.Copy(shfbFolder + @"Colorizer\highlight.js", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); // Always copy the image files, they may be different. Also, delete the // destination file first if it exists as the filename casing may be different. syntaxFile = baseFolder + @"icons\CopyCode.gif"; if(File.Exists(syntaxFile)) { File.SetAttributes(syntaxFile, FileAttributes.Normal); File.Delete(syntaxFile); } File.Copy(shfbFolder + @"Colorizer\CopyCode.gif", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); syntaxFile = baseFolder + @"icons\CopyCode_h.gif"; if(File.Exists(syntaxFile)) { File.SetAttributes(syntaxFile, FileAttributes.Normal); File.Delete(syntaxFile); } File.Copy(shfbFolder + @"Colorizer\CopyCode_h.gif", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); } } // Use a regular expression to find and replace all tags with // cref attributes with a link to the help file content. This // needs to happen after the code block processing as they // may contain <see> tags that need to be resolved. if(entry.HasLinks || entry.HasCodeBlocks) content = reResolveLinks.Replace(content, linkMatchEval); // Replace project option tags with project option values if(entry.HasProjectTags) { // Project tags can be nested while(reProjectTags.IsMatch(content)) 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); } // Transform .topic files into .html files if(sourceFile.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) this.XslTransform(destFile); }