예제 #1
0
        //=====================================================================

        /// <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();
        }
예제 #2
0
        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;
            }
        }
예제 #3
0
        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();
                }
            }
        }
예제 #4
0
 /// <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)
 {
 }
예제 #5
0
 /// <summary>
 /// Method referencing type in third-party assembly
 /// </summary>
 /// <param name="colorizer">Colorizer</param>
 public void SetColorizer(CodeColorizer colorizer)
 {
 }
예제 #6
0
 /// <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)
 {
 }
예제 #7
0
        //=====================================================================

        /// <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);
        }