Office文件的奥秘——.NET平台下不借助Office完成Word、Powerpoint等文件的分析(二)

地点大家能够取获得Word普通话本的初阶和得了地方,其实八个Word文书档案中,文字是按如下顺序存款和储蓄的:

  1. 2字节的UInt16,为其后Fib奥迪Q5gW97块中拾三位整数的个数,固定为0x000E。
  2. 28字节的FibRgW97块,包含14个UInt16。
  3. 2字节的UInt16,为后来FibRAV4gLw97块中33个人整数的个数,固定为0x0016。
  4. 88字节的FibRgLw97块,包含22个UInt32。
  5. 2字节的UInt16,为日后Fib奥德赛gFcLcb块中61人整数的个数(但Fib中华VgFcLcb实际存款和储蓄的是叁十二人整数)。

不定长的Fib劲客gFcLcb块,包蕴不定个数的叁十六个人UInt32(数量也便是上述个数的2倍),但可知至少存有18五个。

之所以,我们得以根据Fib宝马X3gLw97中得到的每一部分的篇幅以及Piece
Table中开局的岗位来赢得每一有个其他文字。

大家跟着第叁篇的代码继续,不知我们有没有翻动过Directory获取到的内容,比如上次的文书档案摘要SummaryInformation和DocumentSummaryInformation,除此之外还有特别储存文书档案内容的DirectoryEntry,比如Word的为“WordDocument”和“1Table”,PowerPoint的为“PowerPoint
Document”,Excel的为“Workbook”。

唯独需要专注的是,由于Word文档中的换行为“\r”(CLAND),而Windows中的换行符为“\r\n”(C奥迪Q5+LF),所以取得文字后需求将“\r”替换为“\r\n”,否则换行将不可能平常展现,除此之外,还有别的的片段特殊字符也须求替换或处理。

【题外话】

由于FIB中剧情实在太多了,之后的有些就不再介绍了,可是为了读取文书档案的内容我们还应有看看如下的剧情(当然也不自然都施用)。

  • 假定文书档案为Word97,该项为0x00(实际上不包涵Fib宝马7系gCswNew)。
  • 要是文书档案为Word3000-二零零二,该项为0x02。
  • 万一文书档案为Word二零零七,该项为0x05。

看上去这么多,其实大家要求的仅是fc中定义的是或不是选用Unicode存款和储蓄文本(fc中第壹3位为0则为Unicode,为1则为Ansi),以及文本相对于WordDocument的偏移量(fc中没有30人),大家率先对Piece
Element定义三个类,能够见见,3个Piece Element的大大小小实际为2 + 4 + 2 =
8字节:

附,本文全数代码下载:https://github.com/mayswind/SimpleOfficeReader

 

 1 public class PieceElement
 2 {
 3     #region 字段
 4     private UInt16 m_info;
 5     private UInt32 m_fc;
 6     private UInt16 m_prm;
 7     private Boolean m_isUnicode;
 8     #endregion
 9 
10     #region 属性
11     /// <summary>
12     /// 获取是否以Unicode形式存储文本
13     /// </summary>
14     public Boolean IsUnicode
15     {
16         get { return this.m_isUnicode; }
17     }
18 
19     /// <summary>
20     /// 获取文本偏移量
21     /// </summary>
22     public UInt32 Offset
23     {
24         get { return this.m_fc; }
25     }
26     #endregion
27 
28     #region 构造函数
29     public PieceElement(UInt16 info, UInt32 fcCompressed, UInt16 prm)
30     {
31         this.m_info = info;
32         this.m_fc = fcCompressed & 0x3FFFFFFF;//后30位
33         this.m_prm = prm;
34         this.m_isUnicode = (fcCompressed & 0x40000000) == 0;//第31位
35 
36         if (!this.m_isUnicode) this.m_fc = this.m_fc / 2;
37     }
38     #endregion
39 }

图片 1图片 2View Code

  1. Fib翼虎gW97中的十六个UInt16,为文书档案的言语(lidFE),比如0x0804为简体普通话。若是文书档案是Unicode存款和储蓄的当然无所谓,假使是ANSI码存款和储蓄的那么就必要得到这几个了。
  2. FibSportagegLw97中的第3个Int32,为Word Document中有意义的字节数(即Word
    Document之后的字节数都得以忽略)。
  3. Fib卡宴gLw97中的第陆个Int32,为文档中正文(Main document)的总篇幅。
  4. FibRAV4gLw97中的第⑥个Int32,为文书档案中页脚(Footnote
    subdocument)的总字数。
  5. FibMuranogLw97中的第5个Int32,为文档中页眉(Header
    subdocument)的总篇幅。
  6. Fib本田CR-VgLw97中的第7个Int32,为文书档案中批注(Comment
    subdocument)的总字数。
  7. FibOdysseygLw97中的第1三个Int32,为文书档案中尾注(Endnote
    subdocument)的总篇幅。
  8. FibCRUISERgLw97中的第⑨个Int32,为文书档案普通话本框(Textbox
    subdocument)的总篇幅。
  9. Fib汉兰达gLw97中的第33个Int32,为文书档案中页眉文本框(Textbox Subdocument of
    the header)的总字数。
  10. Fib科雷傲gFcLcb中的第肆8个UInt32,为Piece Table在Table
    Stream中的偏移(fcClx)。
  11. FibMuranogFcLcb中的第肆7个UInt32,为Piece Table的字节数(lcbClx)。

本还想周三晚间发出去,结果要么没写完。希望这一次的文章能对大家有用。要是你认为好就点下推荐呗。

【后记】

看完FIB结构后大家先来看下nFib与公事版本对应的情景:

 

图片 3图片 4View Code

Table
Stream其实正是1Table要么0Table的总称,具体文字存在卓殊Table中还要依照FIB中的消息。由于复合文件是以2个个Sector形式储存的,所以大家率先要求得到文字存款和储蓄在如何个Sector中。实际上,文本的仓库储存是由Piece
Element(临时这么叫吧)控制着,包涵是或不是启用Unicode、每块的职位等等,这几个剧情都存放于Table
Stream中的Piece Table中,Piece Table相对Table
Stream的偏移量能够从FIB中获得到。

 

  1. 从000H到000H的1字节Byte,是Piece Table的标识,为固定的0x02。
  2. 从001H到004H的4字节UInt32,是Piece
    Table的大大小小(即存款和储蓄文字的Sector的数额)。
    法定给了四个Piece Table中个数的总结公式
    图片 5

    其间,cbPlc即Piece Table的轻重缓急,cbData为1个Piece
    Element的大大小小,所以Piece Table中的个数实际为n = (size – 4) / 12。

  3. 之后4*(n + 1)个字节,是各类Piece
    Element存款和储蓄的文件的发端地方(甘休地方即下1个的起头地方)。
  4. 之后8*n个字节,是每一种Piece Element的相干消息。
  1. 0x00C1(nFib)表示文件为Word97(恐怕为更高版本的文书档案)。
  2. 0x00D9(nFibNew)表示文件为Word2000。
  3. 0x0101(nFibNew)表示文件为Word二〇〇〇。
  4. 0x010C(nFibNew)表示文件为Word贰零零肆。
  5. 0x0112(nFibNew)表示文件为Word2006。
 1 private void ReadTableStream()
 2 {
 3     DirectoryEntry entry = this.m_dirRootEntry.GetChild((this.m_is1Table ? "1Table" : "0Table"));
 4 
 5     if (entry == null)
 6     {
 7         return;
 8     }
 9 
10     Int64 pieceTableStart = this.GetSectorOffset(entry.SectorID) + this.m_fcClx;
11     Int64 pieceTableEnd = pieceTableStart + this.m_lcbClx;
12     this.m_stream.Seek(pieceTableStart, SeekOrigin.Begin);
13 
14     Byte clxt = this.m_reader.ReadByte();
15     Int32 prcLen = 0;
16 
17     //判断如果是Prc不是Pcdt
18     while (clxt == 0x01 && this.m_stream.Position < pieceTableEnd)
19     {
20         this.m_stream.Seek(prcLen, SeekOrigin.Current);
21         clxt = this.m_reader.ReadByte();
22         prcLen = this.m_reader.ReadInt32();
23     }
24 
25     if (clxt != 0x02)
26     {
27         throw new Exception("该文件不存在内容!");
28     }
29 
30     UInt32 size = this.m_reader.ReadUInt32();
31     UInt32 count = (size - 4) / 12;
32 
33     this.m_lstPieceStartPosition = new List<UInt32>();
34     this.m_lstPieceEndPosition = new List<UInt32>();
35     this.m_lstPieceElement = new List<PieceElement>();
36 
37     for (Int32 i = 0; i < count; i++)
38     {
39         this.m_lstPieceStartPosition.Add(this.m_reader.ReadUInt32());
40         this.m_lstPieceEndPosition.Add(this.m_reader.ReadUInt32());
41         this.m_stream.Seek(-4, SeekOrigin.Current);
42     }
43 
44     this.m_stream.Seek(4, SeekOrigin.Current);
45 
46     for (Int32 i = 0; i < count; i++)
47     {
48         UInt16 info = this.m_reader.ReadUInt16();
49         UInt32 fcCompressed = this.m_reader.ReadUInt32();
50         UInt16 prm = this.m_reader.ReadUInt16();
51 
52         this.m_lstPieceElement.Add(new PieceElement(info, fcCompressed, prm));
53     }
54 }

如上这几个新闻大家得以编写制定如下代码获取:

【系列索引】 

Piece Table新闻我们能够编写制定如下代码获取:

对于三个FIB,官方文书档案中身为可变长的,在那之中FIB中最起首的为一定32字节长的FibBase:

那FibBase之后吧?其实FIB包罗众多的内容,从FibBase起初按梯次分别是:

 

图片 6

我们先从WordDocument说起。不知大家发现了从未有过,其实无论是哪个Word文件,WordDocument这几个DirectoryEntry的SectorID总是0,也正是说,WordDocument其实便是Header之后的率先个Sector。对于WordDocument,其最根本的应当是中间含有的FIB(File
Information
Block)了,FIB位于WordDocument的最先,其含有着Word文件丰盛首要的参数,诸如文件的加密方法、文字的编码等等。

 

【一、WordDocument和FIB】

  1. 从000H到001H的2字节UInt16,是定点为0xA5EC,注脚文书档案为Word二进制文件。
  2. 从002H到003H的2字节UInt16,是Word格式的本子(nFib),但实际那里一般为0xC1,即Word97的格式,真实的版本在今后会油不过生。
  3. 从00AH到00BH的2字节UInt16,其实那么些UInt16实际上被分成了13片段,除了第4片段占了4bit外,别的12部分各站1bit,计算16bit,我们能够透过位运算分别读取每一bit的值,比如Boolean
    isDot = ((n & 0x1) ==
    1),就能够读取最低位是或不是为真了。插张图来表明下13片段是怎么着分配的,最左为UInt16的最低位。
    图片 7

    • A(第0位),为文书档案是不是是.Dot文件(Word模板文件)
    • B(第三位),没明白那一个人存的是哪些。
    • C(第一位),为文书档案是还是不是是复杂格式(火速保存时生成的格式)。
    • D(第四位),为文书档案是或不是带有图表。
    • E(第六-五人),当nFib小于0x00D9时为快捷保存(Quick
      Save)的次数,当大于0x00D9时一贯为0x0F。
    • F(第7位),为文书档案是还是不是加密。
    • G(第6个人),为1时文字存款和储蓄于1Table,为0时文字存款和储蓄于0Table。
    • H(第5位),为是还是不是“提出以只读情势打开文书档案”(保存时选取“工具”->“常规选项”能够安装该属性)。
    • I(第叁一个人),为是还是不是有写拥戴密码。
    • J(第三三位),为固定值1。
    • K(第3四个人),为是或不是要用应用程序的言语暗许值覆盖段落格式中定义的言语和字体。
    • L(第③3个人),为文书档案语言是或不是为东南亚语言。
    • M(第叁7位),当文书档案加密时,文书档案假使运用XO瑞虎混淆则为1,不然为0;文书档案不加密时大意该属性。
  4. 从00CH到00DH的2字节UInt16,为稳定的0x00BF或0x00C1(有些语言的Word97会为0x00C1)
  5. 从00EH到011H的4字节UInt32,当文书档案加密并且混淆,则为混淆的密钥;假使加密不混淆,则为加密头的长短;不然应置0。
  6. 从012H到012H的1字节Byte,应当置0,并且忽略。
  7. 从013H到013H的1字节Byte,被分割为6局地,除了第六局地占3bit之外,别的各占1bit。
    • 第一位,必须置0,并且忽略。
    • 第一位,通过右键菜单->新建->新建Word文件成立的空文件为1,其他应当为0。
    • 第陆位,为是不是要用应用程序的暗许值覆盖页面中的页面大小、页面方向、页边距等。
    • 第伍位和第⑤个人,未定义,应当忽略。
    • 第五-8个人,未定义,应当忽略。
  8. 从014H到015H和016H到017H的各2字节,应当置0,并且忽略。
  9. 从018H到01BH和01CH到01FH的各4字节,未定义,应当忽略。

【作品索引】

  1. 正文内容(Main document)
  2. 脚注(Footnote subdocument)
  3. 页眉和页脚(Header subdocument)
  4. 批注(Comment subdocument)
  5. 尾注(Endnote subdocument)
  6. 文本框(Textbox subdocument)
  7. 页眉文本框(Textbox Subdocument of the header)

然后大家来看Piece Table,其结构为:

上篇小说很荣幸被NPOI的大神回复了,同时也修正了作者一个难点,正是NPOI其实是有doc文件的分析,只不过一向未曾跟随正式版揭橥过,要获取这有的代码,能够活动CodePlex(http://npoi.codeplex.com/),访问在SourceCode中的NPOI.ScratchPad中即可看出。给大家造成的诸多不便在此表示抱歉。

【③ 、正式得到文本内容】

  1. WordDocument和FIB
  2. Table Stream中的Piece Table
  3. 正式获得文字内容
  4. 连锁链接

之所以我们编辑如下代码获取:

 

【二、Table Stream中的Piece Table】

【肆 、相关链接】

图片 8图片 9View Code

  1. Office文件的精深——.NET平台下不借助Office完成Word、Powerpoint等公事的解析(一) 获得Office二进制文书档案的DocumentSummaryInformation以及SummaryInformation
  2. Office文件的奥秘——.NET平台下不借助Office达成Word、Powerpoint等公事的辨析(二) 得到Word二进制文档(.doc)的文字内容(包涵正文、页眉、页脚、批注等等)
  3. Office文件的深邃——.NET平台下不借助Office完成Word、Powerpoint等公事的解析(三)
    详尽介绍Office二进制文档中的存款和储蓄结构,以及获得PowerPoint二进制文书档案(.ppt)的文字内容
  4. Office文件的深邃——.NET平台下不借助Office达成Word、Powerpoint等文件的分析(完)
    介绍Office Open
    XML文书档案(.docx、.pptx)怎样开始展览分析以及解析Office文件常见开源类库

2字节的UInt16,为其后Fib陆风X8gCswNew块中14位整数的个数。

  1 #region 字段
  2 private UInt16 m_nFib;
  3 private Boolean m_isComplexFile;
  4 private Boolean m_hasPictures;
  5 private Boolean m_isEncrypted;
  6 private Boolean m_is1Table;
  7 
  8 private UInt16 m_lidFE;
  9 
 10 private Int32 m_cbMac;
 11 private Int32 m_ccpText;
 12 private Int32 m_ccpFtn;
 13 private Int32 m_ccpHdd;
 14 private Int32 m_ccpAtn;
 15 private Int32 m_ccpEdn;
 16 private Int32 m_ccpTxbx;
 17 private Int32 m_ccpHdrTxbx;
 18 
 19 private UInt32 m_fcClx;
 20 private UInt32 m_lcbClx;
 21 #endregion
 22 
 23 #region 读取WordDocument
 24 private void ReadWordDocument()
 25 {
 26     DirectoryEntry entry = this.m_dirRootEntry.GetChild("WordDocument");
 27 
 28     if (entry == null)
 29     {
 30         return;
 31     }
 32 
 33     Int64 entryStart = this.GetSectorOffset(entry.SectorID);
 34     this.m_stream.Seek(entryStart, SeekOrigin.Begin);
 35 
 36     this.ReadFileInformationBlock();
 37 }
 38 
 39 #region 读取FileInformationBlock
 40 private void ReadFileInformationBlock()
 41 {
 42     this.ReadFibBase();
 43     this.ReadFibRgW97();
 44     this.ReadFibRgLw97();
 45     this.ReadFibRgFcLcb();
 46     this.ReadFibRgCswNew();
 47 }
 48 
 49 #region FibBase
 50 private void ReadFibBase()
 51 {
 52     UInt16 wIdent = this.m_reader.ReadUInt16();
 53     if (wIdent != 0xA5EC)
 54     {
 55         throw new Exception("该文件不是Word文件!");
 56     }
 57 
 58     this.m_nFib = this.m_reader.ReadUInt16();
 59     this.m_reader.ReadUInt16();//unused
 60     this.m_reader.ReadUInt16();//lid
 61     this.m_reader.ReadUInt16();//pnNext
 62 
 63     UInt16 flags = this.m_reader.ReadUInt16();
 64     this.m_isComplexFile = this.GetBitFromInteger(flags, 2);
 65     this.m_hasPictures = this.GetBitFromInteger(flags, 3);
 66     this.m_isEncrypted = this.GetBitFromInteger(flags, 8);
 67     this.m_is1Table = this.GetBitFromInteger(flags, 9);
 68 
 69     if (this.m_isComplexFile)
 70     {
 71         throw new Exception("不支持复杂文件的读取!");
 72     }
 73 
 74     if (this.m_isEncrypted)
 75     {
 76         throw new Exception("不支持加密文件的读取!");
 77     }
 78 
 79     this.m_stream.Seek(32 - 12, SeekOrigin.Current);
 80 }
 81 #endregion
 82 
 83 #region FibRgW97
 84 private void ReadFibRgW97()
 85 {
 86     UInt16 count = this.m_reader.ReadUInt16();
 87 
 88     if (count != 0x000E)
 89     {
 90         throw new Exception("FibRgW97长度错误!");
 91     }
 92 
 93     this.m_stream.Seek(26, SeekOrigin.Current);
 94     this.m_lidFE = this.m_reader.ReadUInt16();
 95 }
 96 #endregion
 97 
 98 #region FibRgLw97
 99 private void ReadFibRgLw97()
100 {
101     UInt16 count = this.m_reader.ReadUInt16();
102 
103     if (count != 0x0016)
104     {
105         throw new Exception("FibRgLw97长度错误!");
106     }
107 
108     this.m_cbMac = this.m_reader.ReadInt32();
109     this.m_reader.ReadInt32();//reserved1
110     this.m_reader.ReadInt32();//reserved2
111     this.m_ccpText = this.m_reader.ReadInt32();
112     this.m_ccpFtn = this.m_reader.ReadInt32();
113     this.m_ccpHdd = this.m_reader.ReadInt32();
114     this.m_reader.ReadInt32();//reserved3
115     this.m_ccpAtn = this.m_reader.ReadInt32();
116     this.m_ccpEdn = this.m_reader.ReadInt32();
117     this.m_ccpTxbx = this.m_reader.ReadInt32();
118     this.m_ccpHdrTxbx = this.m_reader.ReadInt32();
119 
120     this.m_stream.Seek(44, SeekOrigin.Current);
121 }
122 #endregion
123 
124 #region FibRgFcLcb
125 private void ReadFibRgFcLcb()
126 {
127     UInt16 count = this.m_reader.ReadUInt16();
128     this.m_stream.Seek(66 * 4, SeekOrigin.Current);
129 
130     this.m_fcClx = this.m_reader.ReadUInt32();
131     this.m_lcbClx = this.m_reader.ReadUInt32();
132 
133     this.m_stream.Seek((count * 2 - 68) * 4, SeekOrigin.Current);
134 }
135 #endregion
136 
137 #region FibRgCswNew
138 private void ReadFibRgCswNew()
139 {
140     UInt16 count = this.m_reader.ReadUInt16();
141     this.m_nFib = this.m_reader.ReadUInt16();
142     this.m_stream.Seek((count - 1) * 2, SeekOrigin.Current);
143 }
144 #endregion
145 #endregion
146 #endregion
147 
148 private Boolean GetBitFromInteger(Int32 integer, Int32 bitIndex)
149 {
150     Int32 num = (Int32)Math.Pow(2, bitIndex);
151     return (integer & num) == num;
152 }

 

 

图片 10图片 11View Code

  1 #region 字段
  2 private String m_paragraphText;
  3 private String m_footnoteText;
  4 private String m_headerText;
  5 private String m_commentText;
  6 private String m_endnoteText;
  7 private String m_textboxText;
  8 private String m_headerTextboxText;
  9 #endregion
 10 
 11 #region 属性
 12 /// <summary>
 13 /// 获取文档正文内容
 14 /// </summary>
 15 public String ParagraphText
 16 {
 17     get { return this.m_paragraphText; }
 18 }
 19 
 20 /// <summary>
 21 /// 获取文档页脚内容
 22 /// </summary>
 23 public String FootnoteText
 24 {
 25     get { return this.m_footnoteText; }
 26 }
 27 
 28 /// <summary>
 29 /// 获取文档页眉内容
 30 /// </summary>
 31 public String HeaderText
 32 {
 33     get { return this.m_headerText; }
 34 }
 35 
 36 /// <summary>
 37 /// 获取文档批注内容
 38 /// </summary>
 39 public String CommentText
 40 {
 41     get { return this.m_commentText; }
 42 }
 43 
 44 /// <summary>
 45 /// 获取文档尾注内容
 46 /// </summary>
 47 public String EndnoteText
 48 {
 49     get { return this.m_endnoteText; }
 50 }
 51 
 52 /// <summary>
 53 /// 获取文档文本框内容
 54 /// </summary>
 55 public String TextboxText
 56 {
 57     get { return this.m_textboxText; }
 58 }
 59 
 60 /// <summary>
 61 /// 获取文档页眉文本框内容
 62 /// </summary>
 63 public String HeaderTextboxText
 64 {
 65     get { return this.m_headerTextboxText; }
 66 }
 67 #endregion
 68 
 69 #region 读取文本内容
 70 private void ReadPieceText()
 71 {
 72     StringBuilder sb = new StringBuilder();
 73     DirectoryEntry entry = this.m_dirRootEntry.GetChild("WordDocument");
 74 
 75     for (Int32 i = 0; i < this.m_lstPieceElement.Count; i++)
 76     {
 77         Int64 pieceStart = this.GetSectorOffset(entry.SectorID) + this.m_lstPieceElement[i].Offset;
 78         this.m_stream.Seek(pieceStart, SeekOrigin.Begin);
 79 
 80         Int32 length = (Int32)((this.m_lstPieceElement[i].IsUnicode ? 2 : 1) * (this.m_lstPieceEndPosition[i] - this.m_lstPieceStartPosition[i]));
 81         Byte[] data = this.m_reader.ReadBytes(length);
 82         String content = GetString(this.m_lstPieceElement[i].IsUnicode, data);
 83         sb.Append(content);
 84     }
 85 
 86     String allContent = sb.ToString();
 87     Int32 paragraphEnd = this.m_ccpText;
 88     Int32 footnoteEnd = paragraphEnd + this.m_ccpFtn;
 89     Int32 headerEnd = footnoteEnd + this.m_ccpHdd;
 90     Int32 commentEnd = headerEnd + this.m_ccpAtn;
 91     Int32 endnoteEnd = commentEnd + this.m_ccpEdn;
 92     Int32 textboxEnd = endnoteEnd + this.m_ccpTxbx;
 93     Int32 headerTextboxEnd = textboxEnd + this.m_ccpHdrTxbx;
 94 
 95     this.m_paragraphText = allContent.Substring(0, this.m_ccpText);
 96     this.m_footnoteText = allContent.Substring(paragraphEnd, this.m_ccpFtn);
 97     this.m_headerText = allContent.Substring(footnoteEnd, this.m_ccpHdd);
 98     this.m_commentText = allContent.Substring(headerEnd, this.m_ccpAtn);
 99     this.m_endnoteText = allContent.Substring(commentEnd, this.m_ccpEdn);
100     this.m_textboxText = allContent.Substring(endnoteEnd, this.m_ccpTxbx);
101     this.m_headerTextboxText = allContent.Substring(textboxEnd, this.m_ccpHdrTxbx);
102 }
103 #endregion
104 
105 private String GetString(Boolean isUnicode, Byte[] data)
106 {
107     if (isUnicode)
108     {
109         return Encoding.Unicode.GetString(data);
110     }
111     else
112     {
113         return Encoding.GetEncoding("Windows-1252").GetString(data);
114     }
115 }
  • 假如文书档案为Word97,该项为0x005D。
  • 假定文书档案为Word两千,该项为0x006C。
  • 若是文书档案为Word二零零一,该项为0x0088。
  • 万一文书档案为Word2000,该项为0x00奥迪A8。
  • 一经文书档案为Word二〇〇七,该项为0x00B7。

譬如正文内容的岗位为[0, ccpText],页脚的职位为[ccpText + 1, ccpText +
1 + ccpFtn]……

不定长的Fib途观gCswNew块,首先是定点长度的UInt16即Word文书档案的真实性版本nFibNew,然后一个UInt16象征文书档案在完全存档后高速存档的次数,之后假诺是Word二零零七则还有一个UInt16文书档案说没有定义且需求忽略(大囧)。

*1、Microsoft Open
Specifications:http://www.microsoft.com/openspecifications/en/us/programs/osp/default.aspx
二 、用PHP读取MS
Word(.doc)中的文字:https://imethan.com/post-2009-10-06-17-59.html **
三 、Office檔案格式:http://www.programmer-club.com.tw/ShowSameTitleN/general/2681.html** 4、LAOLA file
system:http://stuff.mit.edu/afs/athena/astaff/project/mimeutils/share/laola/guide.html*

关于Piece Element,官方是那般描述的:

相关文章