LEInputStream in = new LEInputStream(createInputStream(0, CHM_HEADER_LENGTH)); if ( ! in.readUTF8(4).equals("ITSF") ) throw new DataFormatException("CHM file should start with \"ITSF\"");
if ( (version = in.read32()) > 3) LogUtil.getInstance().e(CHMFileByChmFile.class, "CHM header version unexpected value " + version);
int length = in.read32(); in.read32(); // -1
timestamp = in.read32(); // big-endian DWORD? // //log.info("CHM timestamp " + new Date(timestamp)); lang = in.read32(); //log.info("CHM ITSF language " + WindowsLanguageID.getLocale(lang));
long off0 = in.read64(); long len0 = in.read64(); long off1 = in.read64(); long len1 = in.read64(); // if the header length is really 0x60, read the final QWORD // or the content should be immediate after header section 1 contentOffset = (length >= CHM_HEADER_LENGTH) ? in.read64() : (off1 + len1); //log.fine("CHM content offset " + contentOffset);
/* Step 2. CHM name list: content sections */ in = new LEInputStream( getResourceAsStream("::DataSpace/NameList")); if (in == null) throw new DataFormatException("Missing ::DataSpace/NameList entry"); in.read16(); // length in 16-bit-word, = in.length() / 2 sections = new Section[in.read16()]; for (int i = 0; i < sections.length; i ++) { String name = in.readUTF16(in.read16() << 1); if ("Uncompressed".equals(name)) { sections[i] = new Section(); } else if ("MSCompressed".equals(name)) { sections[i] = new LZXCSection(); } else throw new DataFormatException("Unknown content section " + name); in.read16(); // = null }
很怀疑是不是能这样.建议 java + javascript ,不要自己去解析chm,java负责得到模块对应chm的信息,javascript实现定位到哪一页,仅供参考...
不知道打开chm和其他文件有啥区别
靠 没做过
我提供另一种思路,可以实现类似功能。
使用javahelp系统,它通过几个xml(.hs,.jhm,.xml)文件和一些html文件,实现类似chm的功能。
用到jh.jar。可以从网上搜一下相关信息,不难。
http://www.jspcn.net/htmlnews/11049400135621510.html
http://bbs.preboss.org/forum.php?mod=viewthread&tid=127725&page=1
if ( ! in.readUTF8(4).equals("ITSF") )
throw new DataFormatException("CHM file should start with \"ITSF\"");
if ( (version = in.read32()) > 3)
LogUtil.getInstance().e(CHMFileByChmFile.class, "CHM header version unexpected value " + version);
int length = in.read32();
in.read32(); // -1
timestamp = in.read32(); // big-endian DWORD?
// //log.info("CHM timestamp " + new Date(timestamp));
lang = in.read32();
//log.info("CHM ITSF language " + WindowsLanguageID.getLocale(lang));
in.readGUID(); //.equals("7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC");
in.readGUID(); //.equals("7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC");
long off0 = in.read64();
long len0 = in.read64();
long off1 = in.read64();
long len1 = in.read64(); // if the header length is really 0x60, read the final QWORD
// or the content should be immediate after header section 1
contentOffset = (length >= CHM_HEADER_LENGTH) ? in.read64() : (off1 + len1);
//log.fine("CHM content offset " + contentOffset);
/* Step 1.1 (Optional) CHM header section 0 */
in = new LEInputStream(createInputStream(off0, (int) len0)); // len0 can't exceed 32-bit
in.read32(); // 0x01FE;
in.read32(); // 0;
if ( (fileLength = in.read64()) != fileAccess.length())
LogUtil.getInstance().e(CHMFileByChmFile.class, "CHM file may be corrupted, expect file length " + fileLength);
in.read32(); // 0;
in.read32(); // 0;
/* Step 1.2 CHM header section 1: directory index header */
in = new LEInputStream(createInputStream(off1, CHM_DIRECTORY_HEADER_LENGTH));
if (! in.readUTF8(4).equals("ITSP") )
throw new DataFormatException("CHM directory header should start with \"ITSP\"");
in.read32(); // version
chunkOffset = off1 + in.read32(); // = 0x54
in.read32(); // = 0x0a
chunkSize = in.read32(); // 0x1000
quickRef = 1 + (1 << in.read32()); // = 1 + (1 << quickRefDensity )
for (int i = in.read32(); i > 1; i --) // depth of index tree, 1: no index, 2: one level of PMGI chunks
indexTree.add(new TreeMap<String, Integer>());
rootIndexChunkNo = in.read32(); // chunk number of root, -1: none
firstPMGLChunkNo = in.read32();
lastPMGLChunkNo = in.read32();
in.read32(); // = -1
totalChunks = in.read32();
int lang2 = in.read32(); // language code
//log.info("CHM ITSP language " + WindowsLanguageID.getLocale(lang2));
in.readGUID(); //.equals("5D02926A-212E-11D0-9DF9-00A0-C922-E6EC"))
in.read32(); // = x54
in.read32(); // = -1
in.read32(); // = -1
in.read32(); // = -1 if (chunkSize * totalChunks + CHM_DIRECTORY_HEADER_LENGTH != len1)
throw new DataFormatException("CHM directory list chunks size mismatch");
/* Step 2. CHM name list: content sections */
in = new LEInputStream(
getResourceAsStream("::DataSpace/NameList"));
if (in == null)
throw new DataFormatException("Missing ::DataSpace/NameList entry");
in.read16(); // length in 16-bit-word, = in.length() / 2
sections = new Section[in.read16()];
for (int i = 0; i < sections.length; i ++) {
String name = in.readUTF16(in.read16() << 1);
if ("Uncompressed".equals(name)) {
sections[i] = new Section();
} else if ("MSCompressed".equals(name)) {
sections[i] = new LZXCSection();
} else throw new DataFormatException("Unknown content section " + name);
in.read16(); // = null
}
可以把CHM文件“反编译”为一系列的HTML文件,这样便可以方便地定位到某一个文件,然后很方便地读写。
为什么非要在CHM上吊死呢
你可以把帮助内容存储成一个一个的文件,单独链接在每一个页面