");
bool bDefaultTopic = true;
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.Name == "topic")
{
_indentCount = reader.Depth;
fileAttr = reader.GetAttribute("file") + ".htm";
if (titleTable.Contains(fileAttr))
titleValue = (string)titleTable[fileAttr];
else
titleValue = String.Empty;
WriteHhcLine(sw, "
");
WriteHhcLine(sw, " ");
if (reader.IsEmptyElement)
{
WriteHhcLine(sw, "
");
}
}
break;
case XmlNodeType.EndElement:
if (reader.Name == "topic")
{
_indentCount = reader.Depth;
WriteHhcLine(sw, "");
}
break;
default:
//Console.WriteLine(reader.Name);
break;
}
}
sw.WriteLine(" ");
sw.WriteLine("");
}
}
private void WriteHhcLine(TextWriter writer, string value)
{
//write correct indent space
writer.WriteLine();
for (int i = 0; i < _indentCount - 1; i++)
writer.Write(" ");
writer.Write(value);
}
private void WriteHhk()
{
int iPrefix = _args.outputDirectory.Length + 1;
bool isIndent = false;
InsertSeealsoIndice();
using (StreamWriter sw = new StreamWriter(String.Format("{0}\\{1}.hhk", _args.outputDirectory, _args.projectName), false, Encoding.GetEncoding(_lang.CodePage)))
{
sw.WriteLine("");
sw.WriteLine("");
sw.WriteLine(" ");
sw.WriteLine("
");
foreach (KKeywordInfo ki in kkwdTable)
{
if (!string.IsNullOrEmpty(ki.MainEntry))
{
string kwdValue = ki.MainEntry;
if (!string.IsNullOrEmpty(ki.SubEntry))
{
if (!isIndent)
{
isIndent = true;
sw.WriteLine("
");
sw.WriteLine(" ");
sw.WriteLine("");
}
}
///
/// In hhp.template, {0} is projectName, {1} is defalutTopic, {2}:Language, {3}:Title
///
private void WriteHhp()
{
string hhpFile = String.Format("{0}\\{1}.hhp", _args.outputDirectory, _args.projectName);
Encoding ei = Encoding.GetEncoding(_lang.CodePage);
using (FileStream writer = File.OpenWrite(hhpFile))
{
string var0 = _args.projectName;
string var1 = _defaultTopic;
string var2 = _lang.Name;
string var3 = GetChmTitle();
XPathNodeIterator iter = _config.CreateNavigator().Select("/configuration/hhpTemplate/line");
while (iter.MoveNext())
{
String line = iter.Current.Value;
AddText(writer, String.Format(line, var0, var1, var2, var3), ei);
AddText(writer, "\r\n", ei);
}
}
}
private void AddText(FileStream fs, string value, Encoding ei)
{
byte[] info = ei.GetBytes(value);
fs.Write(info, 0, info.Length);
}
private void WriteHtmls()
{
string _outhtmldir = _args.outputDirectory + _args.htmlDirectory.Substring(_args.htmlDirectory.LastIndexOf('\\'));
HxsChmConverter converter = new HxsChmConverter(_args.htmlDirectory, _outhtmldir, titleTable, kkwdTable, _args.metadata);
converter.Process();
}
}
///
/// Convert hxs-ready html page to chm-ready page
/// 1. strip of xmlisland;
/// 2. link tiltle ==> link title
///
internal class HxsChmConverter
{
private string _currentFile;
private string _currentTitle;
private string _htmlDir;
List < KKeywordInfo > _kkeywords;
private bool _metadata;
private string _outputDir;
Hashtable _titles;
private int _topicCount = 0;
public HxsChmConverter(string htmlDir, string outputDir, Hashtable titles, List < KKeywordInfo > kkeywords, bool metadata)
{
_htmlDir = htmlDir;
_outputDir = outputDir;
_titles = titles;
_kkeywords = kkeywords;
_metadata = metadata;
}
public void Process()
{
_topicCount = 0;
ProcessDirectory(_htmlDir, _outputDir);
Console.WriteLine("Processed {0} files.", _topicCount);
}
private void ProcessDirectory(string srcDir, string destDir)
{
if (!Directory.Exists(destDir))
{
Directory.CreateDirectory(destDir);
}
string[] fileEntries = Directory.GetFiles(srcDir);
foreach (string fileName in fileEntries)
{
string destFile = destDir + fileName.Substring(fileName.LastIndexOf('\\'));
FileInfo fi = new FileInfo(fileName);
string extion = fi.Extension.ToLower();
//process .htm and .html files, just copy other files, like css, gif. TFS DCR 318537
if (extion == ".htm" || extion == ".html")
{
try
{
ProcessFile(fileName, destFile);
}
/*
catch (XmlException ex)
{
ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("Invalid XML file {0}", fileName));
ConsoleApplication.WriteMessage(LogLevel.Error, ex.Message);
_stop = true;
return;
}
*/
catch (Exception)
{
ConsoleApplication.WriteMessage(LogLevel.Error, String.Format("failed to process file {0}", fileName));
throw;
}
}
else
File.Copy(fileName, destFile, true);
}
// Recurse into subdirectories of this directory.
string[] subdirectoryEntries = Directory.GetDirectories(srcDir, "*", SearchOption.TopDirectoryOnly);
foreach (string subdirectory in subdirectoryEntries)
{
DirectoryInfo di = new DirectoryInfo(subdirectory);
string newSubdir = destDir + "\\" + di.Name;
ProcessDirectory(subdirectory, newSubdir);
}
}
private void ProcessFile(string srcFile, string destFile)
{
//Console.WriteLine("Processing:{0}",srcFile);
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
settings.IgnoreWhitespace = false;
settings.IgnoreComments = true;
XmlReader reader = XmlReader.Create(srcFile, settings);
XmlWriterSettings settings2 = new XmlWriterSettings();
settings2.Indent = false;
settings2.IndentChars = "\t";
settings2.OmitXmlDeclaration = true;
XmlWriter writer = XmlWriter.Create(destFile, settings2);
_currentTitle = String.Empty;
_currentFile = destFile;
_topicCount++;
while (reader.Read())
{
if (_metadata == false && reader.Name.ToLower() == "xml" && reader.NodeType == XmlNodeType.Element)
{
//skip xml data island
reader.ReadOuterXml();
}
switch (reader.NodeType)
{
case XmlNodeType.Element:
string elementName = reader.Name.ToLower();
//skip node,
if (elementName == "mshelp:link")
{
writer.WriteStartElement("span");
writer.WriteAttributeString("class", "nolink");
reader.MoveToContent();
}
else
{
if (!String.IsNullOrEmpty(reader.Prefix))
writer.WriteStartElement(reader.Prefix, reader.LocalName, null);
else
writer.WriteStartElement(reader.Name);
if (reader.HasAttributes)
{
while (reader.MoveToNextAttribute())
{
if (!String.IsNullOrEmpty(reader.Prefix))
writer.WriteAttributeString(reader.Prefix, reader.LocalName, null, reader.Value);
else
//If we write the following content to output file, we will get xmlexception saying the 2003/5 namespace is redefined. So hard code to skip "xmlns".
//
My.Computer.FileSystem.RenameFile(
if (!(reader.Depth > 2 && reader.Name.StartsWith("xmlns")))
writer.WriteAttributeString(reader.Name, reader.Value);
}
// Move the reader back to the element node.
reader.MoveToElement();
}
//read html/head/title, save it to _currentTitle
if (reader.Depth == 2 && elementName == "title")
{
if (!reader.IsEmptyElement) //skip node, fix bug 425406
{
reader.Read();
if (reader.NodeType == XmlNodeType.Text)
{
_currentTitle = reader.Value;
writer.WriteRaw(reader.Value);
}
}
}
if (reader.IsEmptyElement)
writer.WriteEndElement();
}
break;
case XmlNodeType.Text:
writer.WriteValue(reader.Value);
break;
case XmlNodeType.EndElement:
writer.WriteFullEndElement();
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
writer.WriteWhitespace(reader.Value);
break;
default:
//Console.WriteLine(reader.Name);
break;
}
}
writer.Close();
ReadXmlIsland(srcFile);
_titles.Add(destFile.Substring(destFile.LastIndexOf("\\") + 1), _currentTitle);
}
///
/// As XmlReader is forward only and we added support for leaving xmlisland data.
/// We have to use another xmlreader to find TocTile, keywords etc.
///
///
private void ReadXmlIsland(string filename)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
settings.IgnoreWhitespace = false;
settings.IgnoreComments = true;
XmlReader reader = XmlReader.Create(filename, settings);
//Fix TFS bug 289403: search if there is comma in k keyword except those in () or <>.
//sample1: "StoredNumber (T1,T2) class, about StoredNumber (T1,T2) class";
//sample2: "StoredNumber class, about StoredNumber class";
Regex r = new Regex(@",([^\)\>]+|([^\<\>]*\<[^\<\>]*\>[^\<\>]*)?|([^\(\)]*\([^\(\)]*\)[^\(\)]*)?)$");
while (reader.Read())
{
if (reader.IsStartElement())
{
if (reader.Name.ToLower() == "mshelp:toctitle")
{
string titleAttr = reader.GetAttribute("Title");
if (!String.IsNullOrEmpty(titleAttr))
_currentTitle = titleAttr;
}
if (reader.Name.ToLower() == "mshelp:keyword")
{
string indexType = reader.GetAttribute("Index");
if (indexType == "K")
{
KKeywordInfo kkwdinfo = new KKeywordInfo();
string kkeyword = reader.GetAttribute("Term");
if (!string.IsNullOrEmpty(kkeyword))
{
kkeyword = ChmBuilder.ReplaceMarks(kkeyword);
Match match = r.Match(kkeyword);
if (match.Success)
{
kkwdinfo.MainEntry = kkeyword.Substring(0, match.Index);
kkwdinfo.SubEntry = kkeyword.Substring(match.Index + 1).TrimStart(new char[] { ' ' });
}
else
{
kkwdinfo.MainEntry = kkeyword;
}
kkwdinfo.File = _currentFile;
_kkeywords.Add(kkwdinfo);
}
}
}
}
if (reader.NodeType == XmlNodeType.EndElement)
{
if (reader.Name == "xml")
return;
}
}
}
}
}