/***************************************************************************
Word2CHM
Copyright (c) 2010 sinosoft , written by yuans.
http://www.sinoreport.net
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
****************************************************************************/
using System;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System.Diagnostics;
namespace Word2CHM.CHMDom
{
/// <summary>
/// CHM Document
/// </summary>
/// <remarks>
/// Root of CHM Dom
/// </remarks>
[Serializable()]
public class CHMDocument
{
/// <summary>
/// Initialize
/// </summary>
public CHMDocument()
{
}
private string strFileName = null;
/// <summary>
/// File name
/// </summary>
public string FileName
{
get
{
return strFileName;
}
set
{
strFileName = value;
}
}
private string strName = "Help";
/// <summary>
/// name
/// </summary>
public string Name
{
get { return strName; }
set { strName = value; }
}
private string strWorkDirectory = null;
/// <summary>
/// Work directory
/// </summary>
public string WorkDirectory
{
get
{
return strWorkDirectory;
}
set
{
strWorkDirectory = value;
}
}
private bool bolAutoIndex = true;
/// <summary>
/// Auto create index
/// </summary>
[System.ComponentModel.DefaultValue(true)]
public bool AutoIndex
{
get
{
return bolAutoIndex;
}
set
{
bolAutoIndex = value;
}
}
private bool bolFullTextSearch = true;
/// <summary>
/// Whether full text search
/// </summary>
[System.ComponentModel.DefaultValue(true)]
public bool FullTextSearch
{
get
{
return bolFullTextSearch;
}
set
{
bolFullTextSearch = value;
}
}
private bool bolBinaryIndex = true;
/// <summary>
/// Whether create binary index
/// </summary>
[System.ComponentModel.DefaultValue(true)]
public bool BinaryIndex
{
get
{
return bolBinaryIndex;
}
set
{
bolBinaryIndex = value;
}
}
private bool bolBinaryToc = true;
/// <summary>
/// Whether create binary toc
/// </summary>
[System.ComponentModel.DefaultValue(true)]
public bool BinaryToc
{
get
{
return bolBinaryToc;
}
set
{
bolBinaryToc = value;
}
}
private bool bolModified = false;
/// <summary>
/// document modified flag
/// </summary>
[System.Xml.Serialization.XmlIgnore()]
public bool Modified
{
get
{
return bolModified;
}
set
{
bolModified = value;
}
}
private string strDefaultTopic = null;
/// <summary>
/// default topic
/// </summary>
public string DefaultTopic
{
get
{
return strDefaultTopic;
}
set
{
strDefaultTopic = value;
}
}
private string strTitle = "New help";
/// <summary>
/// Title
/// </summary>
public string Title
{
get
{
return strTitle;
}
set
{
strTitle = value;
}
}
private string strHelpHeaderHtml = null;
/// <summary>
/// Header html source code
/// </summary>
[System.ComponentModel.Bindable(false)]
[System.Xml.Serialization.XmlElement()]
public string HelpHeaderHtml
{
get
{
return strHelpHeaderHtml;
}
set
{
strHelpHeaderHtml = value;
}
}
private string strHelpFooterHtml = null;
/// <summary>
/// Footer html source code
/// </summary>
[System.ComponentModel.Bindable(false)]
[System.Xml.Serialization.XmlElement()]
public string HelpFooterHtml
{
get
{
return strHelpFooterHtml;
}
set
{
strHelpFooterHtml = value;
}
}
private CHMNodeList myNodes = new CHMNodeList();
/// <summary>
/// Child nodes
/// </summary>
[System.Xml.Serialization.XmlArray()]
[System.Xml.Serialization.XmlArrayItem("Node", typeof(CHMNode))]
[System.ComponentModel.Browsable(false)]
public CHMNodeList Nodes
{
get
{
return myNodes;
}
set
{
myNodes = value;
}
}
private StringList myFiles = new StringList();
/// <summary>
/// File list
/// </summary>
[System.Xml.Serialization.XmlArray()]
[System.Xml.Serialization.XmlArrayItem("File", typeof(string))]
[System.ComponentModel.Browsable(false)]
public StringList Files
{
get
{
return myFiles;
}
set
{
myFiles = value;
}
}
protected string strOutputText = "";
/// <summary>
/// Output text during compile
/// </summary>
[System.ComponentModel.Browsable(false)]
public string OutputText
{
get
{
return strOutputText;
}
}
/// <summary>
/// Get relative path
/// </summary>
/// <param name="strPath">Path</param>
/// <returns>Relative path</returns>
public string GetRelPath(string strPath)
{
if (HasContent(strFileName) == false)
return strPath;
string path = strWorkDirectory;
path = path.ToLower();
string path2 = strPath.ToLower();
if (path2.StartsWith(path))
{
path2 = strPath.Substring(path.Length);
if (path2.StartsWith("/") || path2.StartsWith("\\"))
path2 = path2.Substring(1);
return path2;
}
return strPath;
}
/// <summary>
/// Get absolute path
/// </summary>
/// <param name="strPath">Path</param>
/// <returns>Absolute path</returns>
public string GetAbsPath(string strPath)
{
if (HasContent(strPath) == false)
return strPath;
if (HasContent(strWorkDirectory) == false)
return strPath;
if (System.IO.Path.IsPathRooted(strPath))
return strPath;
else
return System.IO.Path.Combine(strWorkDirectory, strPath);
}
/// <summary>
/// Get all node in this document
/// </summary>
/// <returns>node list</returns>
public CHMNodeList GetAllNodes()
{
CHMNodeList list = new CHMNodeList();
InnerGetNodes(list, myNodes);
return list;
}
private void InnerGetNodes(CHMNodeList list, CHMNodeList list2)
{
foreach (CHMNode node in list2)
{
list.Add(node);
InnerGetNodes(list, node.Nodes);
}
}
/// <summary>
/// judge a string has content
/// </summary>
/// <param name="strData">string</param>
/// <returns>has content</returns>
public static bool HasContent(string strData)
{
if (strData != null && strData.Length > 0)
{
foreach (char c in strData)
{
if (Char.IsWhiteSpace(c) == false)
return true;
}
}
return false;
}// bool HasContent()
public CHMNode GetNodeByFileName(string fileName)
{
return GetNodeByFileName(this.Nodes, fileName);
}
private CHMNode GetNodeByFileName(CHMNodeList nodes, string fileName)
{
foreach (CHMNode node in nodes)
{
if (string.Compare(node.Local, fileName, true) == 0)
{
return node;
}
if (node.Nodes != null && node.Nodes.Count > 0)
{
CHMNode node2 = GetNodeByFileName(node.Nodes, fileName);
if (node2 != null)
{
return node2;
}
}
}
return null;
}
/// <summary>
/// Save document
/// </summary>
/// <param name="filename">File name</param>
public void Save(string filename)
{
XmlSerializer ser = new XmlSerializer(typeof(CHMDocument));
XmlTextWriter writer = new XmlTextWriter(
filename, System.Text.Encoding.UTF8);
writer.Indentation = 1;
writer.IndentChar = '\t';
writer.Formatting = System.Xml.Formatting.Indented;
ser.Serialize(writer, this);
writer.Close();
strFileName = filename;
this.bolModified = false;
}
/// <summary>
/// Load document
/// </summary>
/// <param name="fileName">File name</param>
/// <returns>document</returns>
public static CHMDocument Load(string fileName)
{
XmlSerializer ser = new XmlSerializer(typeof(CHMDocument));
using (System.IO.FileStream stream = new System.IO.FileStream(
fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
CHMDocument doc = (CHMDocument)ser.Deserialize(stream);
return doc;
}
}
/// <summary>
/// Compile to chm file
/// </summary>
/// <returns>���ɵ�CHM�ļ���</returns>
public string CompileProject(string compilerExeFileName, bool deleteTempFile)
{
strOutputText = "";
if (System.IO.File.Exists(compilerExeFileName) == false)
{
throw new System.IO.FileNotFoundException(compilerExeFileName);
}
string strHHP = System.IO.Path.Combine(this.WorkDirectory, strName + ".hhp");
string strHHC = System.IO.Path.Combine(this.WorkDirectory, strName + ".hhc");
string strCHM = System.IO.Path.Combine(this.WorkDirectory, strName + ".chm");
if (System.IO.File.Exists(strCHM))
{
System.IO.File.Delete(strCHM);
}
string DefaultTopic = null;
CHMNodeList nodes = this.GetAllNodes();
foreach (CHMNode node in nodes)
{
if (HasContent(node.Local))
{
DefaultTopic = node.Local;
break;
}
}
// Generate hhp file
using (System.IO.StreamWriter myWriter = new System.IO.StreamWriter(
strHHP,
false,
System.Text.Encoding.GetEncoding(936)))
{
myWriter.WriteLine("[OPTIONS]");
myWriter.WriteLine("Compiled file=" + System.IO.Path.GetFileName(strCHM));
myWriter.WriteLine("Contents file=" + System.IO.Path.GetFileName(strHHC));
myWriter.WriteLine("Default topic=" + this.DefaultTopic);
myWriter.WriteLine("Default Window=main");
myWriter.WriteLine("Display compile progress=yes");
myWriter.WriteLine("Full-text search=" + (this.FullTextSearch ? "Yes" : "No"));
myWriter.WriteLine("Binary TOC=" + (this.BinaryToc ? "Yes" : "No"));
myWriter.WriteLine("Auto Index=" + (this.AutoIndex ? "Yes" : "No"));
myWriter.WriteLine("Binary Index=" + (this.BinaryIndex ? "Yes" : "No"));
//myWriter.WriteLine("Index file=" + System.IO.Path.GetFileName( strIndexFile ));
//
//myWriter.WriteLine("Language=0x804����(�й�)");
//
myWriter.WriteLine("Title=" + this.Title);
myWriter.WriteLine("[FILES]");
foreach (CHMNode node in nodes)
{
if (HasContent(node.Local))
{
if (myFiles.Contains(node.Local) == false)
{
myFiles.Add(node.Local);
}
}
}
foreach (string fileName in myFiles)
{
myWriter.WriteLine(fileName);
}
}
// Generate hhc file
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.AppendChild(doc.CreateElement("hhc"));
ToHHCXMLElement(this.myNodes, doc.DocumentElement);
using (System.IO.StreamWriter myWriter = new System.IO.StreamWriter(
strHHC,
false,
System.Text.Encoding.GetEncoding(936)))
{
myWriter.Write(doc.DocumentElement.InnerXml);
}
// Compile project , generate chm file
ProcessStartInfo start = new ProcessStartInfo(compilerExeFileName, "\"" + strHHP + "\"");
start.UseShellExecute = false;
start.CreateNoWindow = true;
start.RedirectStandardOutput = true;
start.WindowStyle = System.Diagnostics.ProcessWindowStyle.Minimized;
System.Diagnostics.Process proc = System.Diagnostics.Process.Start(start);
proc.PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal;
this.strOutputText = proc.StandardOutput.ReadToEnd();
// Delete template file
if (deleteTempFile)
{
System.IO.File.Delete(strHHP);
System.IO.File.Delete(strHHC);
}
if (System.IO.File.Exists(strCHM))
return strCHM;
else
return null;
}
private void AddParamElement(System.Xml.XmlElement element, string strName, string strValue)
{
System.Xml.XmlElement e = element.OwnerDocument.CreateElement("param");
e.SetAttribute("name", strName);
e.SetAttribute("value", strValue);
element.AppendChild(e);
}
public void LoadHHP(string fileName)
{
string session = null;
myNodes.Clear();
myFiles.Clear();
this.strDefaultTopic = "";
this.strTitle = "Help";
using (StreamReader reader = new StreamReader(
fileName,
System.Text.Encoding.GetEncoding(936), true))
{
string line = reader.ReadLine();
while (line != null)
{
line = line.Trim();
if (line.StartsWith("[") && line.EndsWith("]"))
{
session = line.Substring(1, line.Length - 2).Trim();
}
else
{
if (session == "FILES")
{
if (line.Length > 0)
{
myFiles.Add(line);
}
}
else if (session == "OPTIONS")
{
int index = line.IndexOf("=");
if (index > 0)
{
string optName = line.Substring(0, index).Trim();
string optValue = line.Substring(index + 1).Trim();
switch (optName)
{
case "Title":
this.strTitle = optValue;
break;
case "Default topic":
this.strDefaultTopic = optValue;
break;
case "Contents file":
string fn = optValue;
if (System.IO.Path.IsPathRooted(fn) == false)
{
string dir = System.IO.Path.GetDirectoryName(fileName);
fn = System.IO.Path.Combine(dir, fn);
}
LoadHHCXML(fn);
break;
}
}
}
}
line = reader.ReadLine();
}//while
}//using
strFileName = fileName;
strWorkDirectory = System.IO.Path.GetDirectoryName(strFileName);
strName = System.IO.Path.GetFileNameWithoutExtension(strFileName);
}
/// <summary>
/// Load HHC file in xml format , set document's content
/// </summary>
/// <param name="strFileName">HHC file</param>
public void LoadHHCXML(string strFileName)
{
string xml = null;
using (System.IO.StreamReader reader = new System.IO.StreamReader(
strFileName,
System.Text.Encoding.GetEncoding(936)))
{
xml = reader.ReadToEnd();
}
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(xml);
myNodes.Clear();
this.strFileName = strFileName;
strWorkDirectory = System.IO.Path.GetDirectoryName(strFileName);
strName = System.IO.Path.GetFileNameWithoutExtension(strFileName);
LoadHHCXML(doc.DocumentElement, myNodes);
}
/// <summary>
/// Load document node form HHC xml element
/// </summary>
/// <param name="RootElement">XML element</param>
/// <param name="list">node list</param>
private void LoadHHCXML(System.Xml.XmlElement RootElement, CHMNodeList list)
{
//System.Xml.XmlElement[] elements = GetChildElement( RootElement );
CHMNode LastNode = null;
foreach (System.Xml.XmlNode node in RootElement.ChildNodes)
{
if (string.Compare(node.Name, "li", true) == 0)
{
CHMNode NewNode = new CHMNode();
foreach (System.Xml.XmlNode node2 in node.ChildNodes)
{
foreach (System.Xml.XmlNode node3 in node2.ChildNodes)
{
if (string.Compare(node3.Name, "param", true) == 0)
{
System.Xml.XmlElement e = (System.Xml.XmlElement)node3;
if (e.GetAttribute("name") == "Name")
NewNode.Name = e.GetAttribute("value");
else if (e.GetAttribute("name") == "Local")
NewNode.Local = e.GetAttribute("value");
else if (e.GetAttribute("name") == "ImageNumber")
NewNode.ImageNumber = e.GetAttribute("value");
}
}
}
list.Add(NewNode);
LastNode = NewNode;
}
else if (string.Compare(node.Name, "ul", true) == 0)
{
if (LastNode != null)
{
LoadHHCXML((System.Xml.XmlElement)node, LastNode.Nodes);
}
}
}
}
private void ToHHCXMLElement(CHMNodeList nodes, System.Xml.XmlElement RootElement)
{
if (nodes == null || nodes.Count == 0)
return;
System.Xml.XmlDocument doc = RootElement.OwnerDocument;
System.Xml.XmlElement ulElement = doc.CreateElement("UL");
RootElement.AppendChild(ulElement);
foreach (CHMNode node in nodes)
{
System.Xml.XmlElement liElement = doc.CreateElement("LI");
ulElement.AppendChild(liElement);
System.Xml.XmlElement objElement = doc.CreateElement("OBJECT");
liElement.AppendChild(objElement);
objElement.SetAttribute("type", "text/sitemap");
AddParamElement(objElement, "Name", node.Name);
if (HasContent(node.Local))
{
AddParamElement(objElement, "Local", node.Local.Replace('\\', '/'));
}
if (HasContent(node.ImageNumber))
{
AddParamElement(objElement, "ImageNumber", node.ImageNumber);
}
if (node.Nodes.Count > 0)
{
ToHHCXMLElement(node.Nodes, ulElement);
}
}
}
/// <summary>
/// load html which gnerate MS Word in filter format
/// </summary>
/// <param name="fileName"></param>
public void LoadWordHtml(string fileName)
{
//Clear nodes
myNodes.Clear();
//Clear files
myFiles.Clear();
string strDir = System.IO.Path.GetDirectoryName(fileName);
string strHtml = null;
System.Text.Encoding encoding = System.Text.Encoding.Default;
using (StreamReader reader = new StreamReader(fileName, encoding, true))
{
//set content encoding
encoding = reader.CurrentEncoding;
//read HTML source code
strHtml = reader.ReadToEnd();
}
int index = strHtml.IndexOf("<body");
string strHeader = strHtml.Substring(0, index);
string strHeader1 = strHeader;
string strHeader2 = null;
index = strHeader.IndexOf("<title>");
if (index > 0)
{
strHeader1 = strHeader.Substring(0, index);
int indexEndTitle = strHeader.IndexOf("</title>");
strHeader2 = strHeader.Substring(indexEndTitle + 8);
// read title
this.strTitle = strHeader.Substring(index + 7, indexEndTitle - index - 6 - 1);
}
else
{
strTitle = System.IO.Path.GetFileNameWithoutExtension(fileName);
}
index = strHtml.IndexOf(">", index);
string strBody = strHtml.Substring(index + 1);
index = strBody.LastIndexOf("</body>");
strBody = strBody.Substring(0, index);
index = strBody.IndexOf("<div");
if (index >= 0)
{
index = strBody.IndexOf(">", index + 1);
strBody = strBody.Substring(index + 1);
index = strBody.LastIndexOf("</div>");
strBody = strBody.Substring(0, index);
}
//Split html document by tag <h>
index = strBody.IndexOf("<h");
if (index >= 0)
{
strBody = strBody.Substring(index);
}
else
{
strBody = "";
}
strBody = strBody.Trim();
int lastLevel = 1;
int lastNativeLevel = 1;
while (strBody.Length > 0)
{
int Nativelevel = Convert.ToInt32(strBody.Substring(2, 1));
int level = Nativelevel;
if (lastNativeLevel == Nativelevel)
{
level = lastLevel;
}
else
{
if (level > lastLevel + 1)
{
level = lastLevel + 1;
}
}
lastNativeLevel = Nativelevel;
lastLevel = level;
int index2 = strBody.IndexOf(">");
int index3 = strBody.IndexOf("</h" + Nativelevel + ">");
//read text in <h</h> as topic title
string strTitle = strBody.Substring(index2 + 1, index3 - index2 - 1);
while (strTitle.IndexOf("<") >= 0)
{
int index4 = strTitle.IndexOf("<");
int index5 = strTitle.IndexOf(">", index4);
strTitle = strTitle.Remove(index4, index5 - index4 + 1);
}
strBody = strBody.Substring(index3 + 5);
index = strBody.IndexOf("<h");
if (index == -1)
{
index = strBody.Length;
}
//read topic content
string strContent = strBody.Substring(0, index);
// add node to chm document DOM tree
CHMNode currentNode = null;
if (this.Nodes.Count == 0 || level == 1)
{
//create node
currentNode = new CHMNode();
this.Nodes.Add(currentNode);
}
else
{
CHMNode parentNode = this.Nodes.LastNode;
while (true)
{
if (parentNode.Nodes.Count == 0)
break;
if (parentNode.Level == level - 1)
{
break;
}
parentNode = parentNode.Nodes.LastNode;
}
currentNode = new CHMNode();
//add child node
parentNode.Nodes.Add(currentNode);
}
//set node's name
currentNode.Name = strTitle;
strContent = strContent.Trim();
if (strContent.Length > 0)
{
string strHtmlFileName = "";
CHMNode node = currentNode;
while (node != null)
{
int NodeIndex = node.Index;
if (node.Parent == null)
NodeIndex = this.Nodes.IndexOf(node);
if (strHtmlFileName.Length > 0)
strHtmlFileName = NodeIndex + "-" + strHtmlFileName;
else
strHtmlFileName = NodeIndex.ToString();
node = node.Parent;
}
strHtmlFileName = "File" + strHtmlFileName + ".html";
currentNode.Local = strHtmlFileName;
myFiles.Add(strHtmlFileName);
strHtmlFileName = System.IO.Path.Combine(strDir, strHtmlFileName);
//Generate topic html file
using (StreamWriter writer = new StreamWriter(strHtmlFileName, false, encoding))
{
if (strHeader2 != null)
{
//write header html source
writer.Write(strHeader1);
writer.Write("<title>" + strTitle + "</title>");
writer.Write(strHeader2);
}
else
{
writer.Write(strHeader);
}
writer.WriteLine("<body style=' margin: 0px 0px 0px 0px; padding: 0px 0px 0px 0px;font-family: Verdana, Arial, Helvetica, sans-serif;' >");
string header = this.HelpHeaderHtml;
if (header != null)
{
//write header html source code
header = header.Replace("@Title", strTitle);
writer.WriteLine(header);
}
//write html content
writer.WriteLine(strContent);
//write footer html source
writer.WriteLine(this.HelpFooterHtml);
writer.WriteLine("</body>");
writer.WriteLine("</html>");
}
}
if (index == strBody.Length)
{
break;
}
else
{
strBody = strBody.Substring(index);
}
}//while
//write html file
string strFilesDir = System.IO.Path.ChangeExtension(fileName, "files");
if (System.IO.Directory.Exists(strFilesDir))
{
string dirName = System.IO.Path.GetFileName(strFilesDir);
foreach (string name in System.IO.Directory.GetFiles(strFilesDir))
{
string name2 = System.IO.Path.GetFileName(name);
name2 = System.IO.Path.Combine(dirName, name2);
myFiles.Add(name2);
}
}
strFileName = fileName;
//set work path
strWorkDirectory = System.IO.Path.GetDirectoryName(strFileName);
// set document's name
strName = System.IO.Path.GetFileNameWithoutExtension(strFileName);
}
}//public class CHMDocument
}