/// <summary> /// Create a companion file for a topic /// </summary> /// <param name="filename">The companion filename.</param> /// <param name="topic">The topic</param> public static void CreateCompanionFile(string filename, Topic topic) { XmlWriterSettings settings = new XmlWriterSettings(); XmlWriter writer = null; try { settings.Indent = true; settings.CloseOutput = true; writer = XmlWriter.Create(filename, settings); writer.WriteStartDocument(); writer.WriteStartElement("metadata"); writer.WriteAttributeString("fileAssetGuid", topic.Id.ToString()); writer.WriteAttributeString("assetTypeId", "CompanionFile"); writer.WriteStartElement("topic"); writer.WriteAttributeString("id", topic.Id.ToString()); writer.WriteStartElement("title"); if(!String.IsNullOrEmpty(topic.Title)) writer.WriteValue(topic.Title); else writer.WriteValue(Path.GetFileNameWithoutExtension(filename)); writer.WriteEndElement(); // </title> writer.WriteStartElement("tableOfContentsTitle"); if(!String.IsNullOrEmpty(topic.Title)) writer.WriteValue(topic.Title); else writer.WriteValue(Path.GetFileNameWithoutExtension(filename)); writer.WriteEndElement(); // </tableOfContentsTitle> foreach(MSHelpAttr attr in topic.HelpAttributes) { writer.WriteStartElement("attribute"); writer.WriteAttributeString("name", attr.AttributeName); writer.WriteValue(attr.AttributeValue); writer.WriteEndElement(); } foreach(MSHelpKeyword kw in topic.HelpKeywords) { writer.WriteStartElement("keyword"); writer.WriteAttributeString("index", kw.Index); writer.WriteValue(kw.Term); writer.WriteEndElement(); } writer.WriteEndElement(); // </topic> writer.WriteEndElement(); // </metadata> writer.WriteEndDocument(); } finally { if(writer != null) writer.Close(); } }
/// <summary> /// This is called to convert a single topic and its children /// </summary> /// <param name="topic">The topic to convert</param> public void ConvertTopic(Topic topic) { StringBuilder sb = new StringBuilder(); List<string> extras = new List<string>(); string destFile, tagName, body = topic.Body; Regex reRemoveExtra; if(topic.SourceFile != null) { // Save the topic to the destination folder destFile = Path.Combine(destPath, topic.SourceFile.Path.Substring( sourcePath.Length + 1)); destFile = Path.ChangeExtension(destFile, ".aml"); if(!Directory.Exists(Path.GetDirectoryName(destFile))) Directory.CreateDirectory(Path.GetDirectoryName(destFile)); this.ReportProgress("{0} -> {1}", topic.SourceFile, destFile); currentTopic = topic; if(!String.IsNullOrEmpty(body)) { markupSections.Clear(); sectionCount = 0; foreach(Match m in reAllTags.Matches(body)) { tagName = m.Groups["Tag"].Value.ToLower( CultureInfo.InvariantCulture); if(!conversionRules.ContainsKey(tagName) && extras.IndexOf(tagName) == -1) { if(sb.Length != 0) sb.Append("|"); sb.Append(tagName); extras.Add(tagName); this.ReportProgress(" Warning: Found unknown tag " + "'{0}' which will be removed", tagName); } } // Replace unrecognized entities body = reReplaceEntity.Replace(body, matchEntity); // Replace markup wrappers with a placeholder body = reMarkupWrapper.Replace(body, matchMarkupWrapper); // Remove tags with no MAML equivalent body = reRemoveTag.Replace(body, String.Empty); if(sb.Length != 0) { sb.Insert(0, @"<\s*/?\s*("); sb.Append(@")\s*?((\s|/)[^>]*?)?>"); reRemoveExtra = new Regex(sb.ToString(), RegexOptions.IgnoreCase | RegexOptions.Singleline); body = reRemoveExtra.Replace(body, String.Empty); } // Replace tokens body = reReplaceTokens.Replace(body, matchToken); // Replace tags with their MAML equivalent body = reReplaceTag.Replace(body, matchReplace); // Update <code> tags with the appropriate MAML tag body = reReplaceCode.Replace(body, matchCode); // Replace <see> tags with <codeEntityReference> tags body = reReplaceSee.Replace(body, matchSee); // Replace <a> tags with an appropriate MAML link body = reReplaceAnchor.Replace(body, matchAnchor); // Replace <img> tags with an appropriate MAML media link body = reReplaceImage.Replace(body, matchImage); // Wrap heading tags (h1-h6) in sections isFirstHeading = true; body = reReplaceHeading.Replace(body, matchHeading); if(!isFirstHeading) body += "\r\n </content>\r\n</section>\r\n"; // If wanted, move the leading text before the first section tag into an introduction // element. This doesn't always produce good results across all topics depending on how // they are formatted so it is optional. if(replaceIntro) body = reReplaceIntroduction.Replace(body, matchIntroduction); // Put the markup wrappers back in the body body = reReplaceMarker.Replace(body, matchMarker); topic.Body = body; // The converted HTML may not be valid XML so we'll write it // out as text rather than using an XML document object. using(StreamWriter sw = new StreamWriter(destFile, false, Encoding.UTF8)) { sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); sw.WriteLine("<topic id=\"{0}\" revisionNumber=\"{1}\">", topic.Id, topic.RevisionNumber); //DR[12-07-27]: sw.WriteLine(" <developerConceptualDocument\r\n" + " xmlns=\"http://ddue.schemas.microsoft.com/authoring/2003/5\"\r\n" + " xmlns:xlink=\"http://www.w3.org/1999/xlink\">\r\n"); // If it had an abstract, write that out if(!String.IsNullOrEmpty(topic.TopicAbstract)) sw.WriteLine(" <summary abstract=\"true\">\r\n" + topic.TopicAbstract + "\r\n </summary>"); // Insert a default introduction if leading text was not moved above if(!replaceIntro) sw.WriteLine(" <introduction>\r\n <para>TODO: " + "Move introduction text here</para>\r\n " + "</introduction>"); sw.WriteLine(topic.Body); sw.WriteLine(" <relatedTopics>\r\n </relatedTopics>\r\n"); sw.WriteLine(" </developerConceptualDocument>\r\n</topic>"); } if(createCompanionFile) CreateCompanionFile(Path.ChangeExtension(destFile, ".cmp"), topic); } } foreach(Topic t in topic.Subtopics) this.ConvertTopic(t); }