好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

使用CHM文档 阅读随笔

使用CHM文档 阅读随笔

使用CHM文档 阅读随笔

背景

  我们在开发的过程中,常常都会想记录下来一些东西,可以成文的,则以随笔的形式发布,那些不能成文的,例如某bug的解决方案,或者开发中的注意事项,甚至是某个SQL语句,以只言片语的形式记录在文章、日记里,这样,自己就能在不同的设备、终端上查看自己记录的东西。

  博客园的文章,如果不设置在首页显示的话,个人觉得查看起来不是很方便。想到自己曾做了一个 数据库CHM文档生成工具 ,于是,不管是随笔,还是文章,能否也通过CHM文档的形式查看呢。想到这里,我的需求就产生了。

效果预览

资源下载

   示例CHM文档

   源代码

开发思路

  1.得到博客内容。之前想过通过url请求的方式获取到博客正文部分,但是后台的文章或日记处理起来相对麻烦,于是采用了博客备份得到的xml文件,按照xml文件结构,定义数据结构,读取xml数据。怎么读取,这里就不细讲,如有需要,移步至 《以读取博客园随笔备份为例 将xml 序列化成json,再序列化成对象》 。

  2.遍历博客的博文,然后将博文的html内容,以html形式的存储。但是xml里存储的仅仅是博客的正文部分内容,整个页面显示框架是没有的。此时,我打开任意我的任意一篇博客,然后将网页文件全部下载到本地,对应的js或图片会存储在files文件夹中。打开下载的html,去掉页眉,侧边栏等,最后得到我们需要的模版,将关键地方使用特殊字符串占位,替换后的模版效果如图:

  利用上述模版,替换后的网页效果,仅仅包含正文部分了。

  3.得到模板化的html博客正文之后,就可以轻松的将其编译成CHM文件了。具体的编译方式很简单,下面贴一下代码:

CHM编译封装类

 #region  版权信息
 /*   ==============================================================================
   * 文 件 名: ChmHelp
   * 功能描述:Chm编译封装类
   * Copyright (c) 2013 武汉经纬视通科技有限公司
   * 创 建 人: alone
   * 创建时间: 2012/12/05 20:53:29
   * 修 改 人: alone
   * 修改时间: 2013/3/01 18:22:03
   * 修改描述: 使用hha.dll接口方法
   * 版    本: v1.0.0.0
   * ==============================================================================  */ 
 #endregion 
 using   System;
  using   System.Collections.Generic;
  using   System.Diagnostics;
  using   System.IO;
  using   System.Runtime.InteropServices;
  using   System.Text;
  using   System.Threading;
  using   Chen.Ext;
  /*   变量含义 rootPath:待编译的文件夹 rootParent:rootPath的父目录
 * 1.将rootPath目录下的文件编译成CHM。
 * 2.将编译过程中产生的hhp、hhc、hhk文件均放在rootParent下。
 * 3.基于rootParent,获取待编译文件的相对路径(相对rootParent)。
   */ 
 namespace   Chen.Common
{
      ///   <summary> 
     ///   Chm辅助类
      ///   </summary> 
     public   class   ChmHelp
    {
          #region  成员定义
         //  Chm文件保存路径 
         public   string  ChmFileName {  get ;  set  ; }
          //  Chm文件Titie 
         public   string  Title {  get ;  set  ; }
          //  编译文件夹路径 
         public   string   RootPath
        {
              get  {  return   rootPath; }
              set  
            {
                rootPath  =  Path.GetFullPath(value);
                  //  获取上级目录的完整路径
                  //  指定文件夹的父目录是作为关键字被文件的完整路径替换的,此时目录必须携带\\ 
                DirectoryInfo di =  new   DirectoryInfo(rootPath);
                rootParent  = di.Parent.FullName +  "  \\  "  ;
            }
        }
          //  默认页面 相对编译文件夹的路径 
         public   string   DefaultPage
        {
              get  //  编译时路径是相对rootParent 
             {
                  var  rootName =  Path.GetFileName(rootPath);
                  return  rootName +  "  \\  "  +  defaluePage;
            }
              set   //  赋值时路径是相对rootPath。 
             {
                defaluePage  =  value;
            }
        }
          public   int  FileCount {  get  {  return   fileCount; } }
          //  私有变量 
         private   string   rootParent;
          private   string   rootPath;
          private   string   defaluePage;
          private   int  fileCount =  0  ;
          //  CHM相关文件内容 
         private  StringBuilder hhcBody =  new   StringBuilder();
          private  StringBuilder hhpBody =  new   StringBuilder();
          private  StringBuilder hhkBody =  new   StringBuilder();
          private   bool  deleteTmp =  true  ;
          //  日志信息 
         private  StringBuilder sbMessage =  new   StringBuilder();
          public   event  Action< string >  logHandle;
          #endregion 

         #region  hha 方法引入 
        [DllImport(  "  hha.dll  "  )]
          private   extern   static   void  HHA_CompileHHP( string  hhpFile, CompileLog g1, CompileLog g2,  int   stack);
          delegate   bool  CompileLog( string   log);
          //  编译信息 
         private   bool  CompileLoging( string   log)
        {
              if  (logHandle !=  null  ) logHandle(log);
              return   true  ;
        }
          private   bool  CompileProcess( string   log)
        {
              return   true  ;
        }
          #endregion 

         #region  构造所需要的文件
         private   void  Create( string   path)
        {
              //  获取文件 
             var  strFileNames =  Directory.GetFiles(path);
              //  获取子目录 
             var  strDirectories =  Directory.GetDirectories(path);
              //  给该目录添加UL标记 
             if  (strFileNames.Length >  0  || strDirectories.Length >  0  )
                hhcBody.AppendLine(  "      <UL>  "  );
              //  处理获取的文件 
             foreach  ( string  filename  in   strFileNames)
            {
                  var  fileItem =  new   StringBuilder();
                fileItem.AppendLine(  "      <LI> <OBJECT type=\"text/sitemap\">  "  );
                fileItem.AppendLine(  "          <param name=\"Name\" value=\"{0}\">  "  .FormatString(Path.GetFileNameWithoutExtension(filename)));
                fileItem.AppendLine(  "          <param name=\"Local\" value=\"{0}\">  " .FormatString(filename.Replace(rootParent,  string  .Empty)));
                fileItem.AppendLine(  "          <param name=\"ImageNumber\" value=\"11\">  "  );
                fileItem.AppendLine(  "          </OBJECT>  "  );
                  //  添加文件列表到hhp 
                 hhpBody.AppendLine(filename);
                hhcBody.Append(fileItem.ToString());
                hhkBody.Append(fileItem.ToString());
                  //  记录待编译文件总数 
                fileCount++ ;
            }
              //  遍历获取的目录 
             foreach  ( string  dirname  in   strDirectories)
            {
                  if  (dirname.Contains( "  content  " ) || dirname.Contains( "  image  " ))  continue  ;
                hhcBody.AppendLine(  "      <LI> <OBJECT type=\"text/sitemap\">  "  );
                hhcBody.AppendLine(  "          <param name=\"Name\" value=\"{0}\">  "  .FormatString(Path.GetFileName(dirname)));
                hhcBody.AppendLine(  "          <param name=\"ImageNumber\" value=\"1\">  "  );
                hhcBody.AppendLine(  "          </OBJECT>  "  );
                  //  递归遍历子文件夹 
                 Create(dirname);
            }
              //  给该目录添加/UL标记 
             if  (strFileNames.Length >  0  || strDirectories.Length >  0  )
            {
                hhcBody.AppendLine(  "      </UL>  "  );
            }
        }
          private   void   CreateHHC()
        {
              var  code =  new   StringBuilder();
            code.AppendLine(  "  <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">  "  );
            code.AppendLine(  "  <HTML>  "  );
            code.AppendLine(  "  <HEAD>  "  );
            code.AppendLine(  "  <meta name=\"GENERATOR\" content=\"EasyCHM.exe  HdhCmsTestzipghost测试数据\">  "  );
            code.AppendLine(  "  <!-- Sitemap 1.0 -->  "  );
            code.AppendLine(  "  </HEAD><BODY>  "  );
            code.AppendLine(  "  <OBJECT type=\"text/site properties\">  "  );
            code.AppendLine(  "      <param name=\"ExWindow Styles\" value=\"0x200\">  "  );
            code.AppendLine(  "      <param name=\"Window Styles\" value=\"0x800025\">  "  );
            code.AppendLine(  "      <param name=\"Font\" value=\"MS Sans Serif,9,0\">  "  );
            code.AppendLine(  "  </OBJECT>  "  );
              //  遍历文件夹 构建hhc文件内容 
             code.Append(hhcBody.ToString());
            code.AppendLine(  "  </BODY></HTML>  "  );
              //  File.WriteAllText(Path.Combine(SourcePath, "chm.hhc"), code.ToString(), Encoding.GetEncoding("gb2312")); 
            File.WriteAllText( "  .//chm.hhc  " , code.ToString(), Encoding.GetEncoding( "  gb2312  "  ));
        }
          private   void   CreateHHP()
        {
              var  code =  new   StringBuilder();
            code.AppendLine(  "  [OPTIONS]  "  );
            code.AppendLine(  "  CITATION=Made by Chen  " ); //  制作人 
            code.AppendLine( "  Compatibility=1.1 or later  " ); //  版本 
            code.AppendLine( @"  Compiled file=  "  + ChmFileName); //  生成chm文件路径 
            code.AppendLine( "  Contents file=chm.HHC  " ); //  hhc文件路径 
            code.AppendLine( "  COPYRIGHT=HdhCmsTestjinwin测试数据  " ); //  版权所有 
            code.AppendLine( @"  Default topic={1}  " ); //  CHM文件的首页 
            code.AppendLine( "  Default Window=Main  " ); //  目标文件窗体控制参数,这里跳转到Windows小节中,与其一致即可 
            code.AppendLine( "  Display compile notes=Yes  " ); //  显示编译信息 
            code.AppendLine( "  Display compile progress=Yes  " ); //  显示编译进度
              //  code.AppendLine("Error log file=error.Log");  //  错误日志文件 
            code.AppendLine( "  Full-text search=Yes  " ); //  是否支持全文检索信息 
            code.AppendLine( "  Index file=chm.HHK  " ); //  hhk文件路径 
            code.AppendLine( "  Title={0}  " ); //  CHM文件标题
              //  code.AppendLine("Flat=NO");  //  编译文件不包括文件夹 
            code.AppendLine( "  Enhanced decompilation=yes  " ); //  编译文件不包括文件夹 
             code.AppendLine();
            code.AppendLine(  "  [WINDOWS]  "  );
              //  例子中使用的参数 0x20 表示只显示目录和索引 
            code.AppendLine( "  Main=\"{0}\",\"chm.hhc\",\"chm.hhk\",\"{1}\",\"{1}\",,,,,0x63520,180,0x104E, [0,0,745,509],0x0,0x0,,,,,0  "  );
              //  Easy Chm使用的参数 0x63520 表示目录索引搜索收藏夹
              //  code.AppendLine("Main=\"{0}\",\"chm.HHC\",\"chm.HHK\",\"{1}\",\"{1}\",,,,,0x63520,271,0x304E,[0,0,745,509],,,,,,,0"); 
             code.AppendLine();
            code.AppendLine(  "  [MERGE FILES]  "  );
            code.AppendLine();
            code.AppendLine(  "  [FILES]  "  );
            code.Append(hhpBody.ToString());

              //   File.WriteAllText(Path.Combine(SourcePath, "chm.hhp"), code.ToString().FormatString(Title, DefaultPage), Encoding.GetEncoding("gb2312")); 
            File.WriteAllText( "  .//chm.hhp  " , code.ToString().FormatString(Title, DefaultPage), Encoding.GetEncoding( "  gb2312  "  ));
        }
          private   void   CreateHHK()
        {
              var  code =  new   StringBuilder();
            code.AppendLine(  "  <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">  "  );
            code.AppendLine(  "  <HTML>  "  );
            code.AppendLine(  "  <HEAD>  "  );
            code.AppendLine(  "  <meta name=\"GENERATOR\" content=\"EasyCHM.exe  HdhCmsTestzipghost测试数据\">  "  );
            code.AppendLine(  "  <!-- Sitemap 1.0 -->  "  );
            code.AppendLine(  "  </HEAD><BODY>  "  );
            code.AppendLine(  "  <OBJECT type=\"text/site properties\">  "  );
            code.AppendLine(  "      <param name=\"ExWindow Styles\" value=\"0x200\">  "  );
            code.AppendLine(  "      <param name=\"Window Styles\" value=\"0x800025\">  "  );
            code.AppendLine(  "      <param name=\"Font\" value=\"MS Sans Serif,9,0\">  "  );
            code.AppendLine(  "  </OBJECT>  "  );
            code.AppendLine(  "  <UL>  "  );
              //  遍历文件夹 构建hhc文件内容 
             code.Append(hhkBody.ToString());
            code.AppendLine(  "  </UL>  "  );
            code.AppendLine(  "  </BODY></HTML>  "  );
              //  File.WriteAllText(Path.Combine(SourcePath, "chm.hhk"), code.ToString(), Encoding.GetEncoding("gb2312")); 
            File.WriteAllText( "  .//chm.hhk  " , code.ToString(), Encoding.GetEncoding( "  gb2312  "  ));
        }
          #endregion 

         ///   <summary> 
         ///   编译
          ///   </summary> 
         ///   <returns></returns> 
         public   void   Compile()
        {
              //  准备hhp hhc hhk文件 
             Create(RootPath);
            CreateHHC();
            CreateHHK();
            CreateHHP();
              var  path =  "  .//chm.hhp  "  ;
            HHA_CompileHHP(path, CompileLoging, CompileProcess,   0  );
            DeleteTmpFile();
        }
          ///   <summary> 
         ///   使用hhc.exe进行编译 暂时不使用该方式 局限性较大
          ///   </summary> 
         ///   <param name="hhpPath"></param> 
         private   void  CompileByHHC( string   hhpPath)
        {
              var  hhcPath =  string  .Empty;
              var  program =  Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
              if  (File.Exists( "  .//hhc.exe  "  ))
            {
                hhcPath  =  "  .//hhc.exe  "  ;
            }
              else   if  (File.Exists(program +  @"  \HTML Help Workshop\hhc.exe  "  ))
            {
                hhcPath  = program +  @"  \HTML Help Workshop\hhc.exe  "  ;
            }
              else   if  (File.Exists(program +  @"   (x86)\HTML Help Workshop\hhc.exe  "  ))
            {
                hhcPath  = program +  @"   (x86)\HTML Help Workshop\hhc.exe  "  ;
            }
              else  
            {
                  throw   new  Exception( "  未找到编译核心文件hhc.exe  "  );
            }
              var  process =  new  Process(); //  创建新的进程,用Process启动HHC.EXE来Compile一个CHM文件 
             try  
            {
                ProcessStartInfo processInfo  =  new   ProcessStartInfo();
                processInfo.WindowStyle  =  ProcessWindowStyle.Hidden;
                processInfo.FileName  = hhcPath;   //  调入HHC.EXE文件  
                processInfo.Arguments =  hhpPath;
                processInfo.UseShellExecute  =  false  ;
                processInfo.CreateNoWindow  =  false  ;
                process.StartInfo  =  processInfo;
                process.Start();
                process.WaitForExit();   //  组件无限期地等待关联进程退出 
                 if  (process.ExitCode ==  0  )
                {
                      throw   new  Exception( "  编译发生异常!  "  );
                }
            }
              catch   (Exception ex)
            {
                  throw   ex;
            }
              finally  
            {
                process.Close();
            }
        }
          ///   <summary> 
         ///   反编译
          ///   </summary> 
         ///   <returns></returns> 
         public   bool   DeCompile()
        {
              //  反编译时,Path作为CHM文件路径
              //  得到chm文件的绝对路径 
             string  ExtportPath =  Path.GetDirectoryName(ChmFileName);
              //  命令参数含义
              //  Path:导出的文件保存的路径
              //  ChmPath:Chm文件所在的路径 
             string  cmd =  "   -decompile   "  + ExtportPath +  "   "  + ChmFileName; //  反编译命令 
            Process p = Process.Start( "  hh.exe  " , cmd); //  调用hh.exe进行反编译 
             p.WaitForExit();
              return   true  ;
        }
          //  删除临时文件 
         private   void   DeleteTmpFile()
        {
              if   (deleteTmp)
            {
                  var  arr =  new   string [] {  "  .//chm.hhc  " ,  "  .//chm.hhp  " ,  "  .//chm.hhk  "   };
                  foreach  ( var  a  in   arr)
                {
                      if   (File.Exists(a))
                    {
                        File.Delete(a);
                    }
                }
            }
        }
    }
} 

调用示例

   //  编译CHM文档 
            ChmHelp chm =  new   ChmHelp();
            chm.RootPath  =  "  .//cnblogs  "  ;
            chm.ChmFileName  =Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop ),channel.title+ "  .chm  "  );
            chm.DefaultPage  =  "  博文目录.html  "  ;
            chm.Title  =  channel.title;
            chm.Compile(); 

  到这里基本上就已经完成了,但是有一点需要改进。博客中插入的图片,是url地址,如果阅读该chm文件时,电脑没有联网,此时是影响阅读的。此时我们的工作就应该是下载博客正文所使用的图片,存储到本地,编译到CHM文件中。

  4.首先提取博客正文的url链接,并下载图片。图片分两种,手动插入的图片和博客园显示代码时使用的图片(展开代码,折叠代码,复制代码的图标),前者下载时需防止图片命名重复导致覆盖,后者如果已经下载,则无需重复下载。下载成功后,对博客正文进行替换,将引用图片的url链接替换成本地的相对途径。

         ///   <summary> 
         ///   提取网页文件中的图片链接
          ///   </summary> 
         ///   <param name="sHtmlText">  html  </param> 
         ///   <returns></returns> 
         public   static   string [] GetHtmlImageUrls( string   sHtmlText)
        {
              //   定义正则表达式用来匹配 img 标签      
            Regex regImg =  new  Regex( @"  <img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r\n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>  "  , RegexOptions.IgnoreCase);
              //   搜索匹配的字符串       
            MatchCollection matches =  regImg.Matches(sHtmlText);
              int  i =  0  ;
              string [] sUrlList =  new   string  [matches.Count];
              //   取得匹配项列表         
             foreach  (Match match  in   matches)
                sUrlList[i ++] = match.Groups[ "  imgUrl  "  ].Value;
              return   sUrlList;
        } 

  到这里,基本上全部完成了。

  程序使用的模版文件,全在content文件夹下,如果有朋友使用到自定义css,可以手动的更改模版。

下节预告

  我们既然能将自己的博客备份得到的xml文件,转换成chm文件,我们也同样可以将某个人的博客随笔备份成chm文件,参考 《一键构造你的博客目录》 ,它可以得到某个博客下的所有随笔链接,既然能够得到链接,我们就可以等到博客的正文,因此我们同样将其转换成CHM文件。

  以 啊汉 的博客为例, 示例文档下载 ,效果图预览:

  



   如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的 【 推荐 】按钮。

   感谢阅读,希望这篇文章能给你带来帮助!

 

分类:  C#

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于使用CHM文档 阅读随笔的详细内容...

  阅读:42次