466 lines
15 KiB
Plaintext
466 lines
15 KiB
Plaintext
// Version 1.0.6
|
||
|
||
Type TSDocxFile = Class
|
||
///Version: V1.0 2022-09-20
|
||
///适用于 Microsoft Word docx格式文件
|
||
///纯TSL模块实现
|
||
///Word(docx)文件读写接口
|
||
|
||
///缺省构造函数
|
||
Function Create(); overload;
|
||
Begin
|
||
init();
|
||
End;
|
||
|
||
///构造函数,打开已经存在的docx文件
|
||
///alias: string,文件目录别名
|
||
///fname: string,文件名
|
||
Function Create(alias, fname); overload;
|
||
Begin
|
||
init();
|
||
OpenFile(alias, fname);
|
||
End;
|
||
|
||
Function Destory();
|
||
Begin
|
||
End;
|
||
|
||
Function init();
|
||
Begin
|
||
DocPrId_ := -1;
|
||
zipfile_ := new ZipFile();
|
||
xml_ := new TSXml();
|
||
End;
|
||
|
||
///打开docx文件
|
||
///alias: string,文件目录别名
|
||
///fname: string,文件名
|
||
///返回:[err, errmsg]
|
||
Function OpenFile(alias, fname);
|
||
Begin
|
||
if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
|
||
[err, errmsg] := zipfile_.Open(alias, fname);
|
||
if err=0 then Begin
|
||
document_ := new docxDocument(zipfile_, xml_);
|
||
End;
|
||
return array(err, errmsg);
|
||
End;
|
||
|
||
///新建docx文件
|
||
///返回:[err, info]
|
||
Function NewFile();
|
||
Begin
|
||
path := GetPath();
|
||
if path[1] = '/' then
|
||
defaultFileName := path + '/funcext/TSOffice/template/default.docx';
|
||
else
|
||
defaultFileName := path + '\\funcext\\TSOffice\\template\\default.docx';
|
||
return OpenFile('', defaultFileName);
|
||
End;
|
||
|
||
///保存文件
|
||
///返回: [err, info]
|
||
Function Save();
|
||
Begin
|
||
return zipfile_.Save();
|
||
End;
|
||
|
||
///另存为
|
||
///alias: string,文件目录别名
|
||
///fname: string,文件名
|
||
///返回: [err, info]
|
||
Function SaveAs(alias, fname);
|
||
Begin
|
||
return zipfile_.Save(alias, fname);
|
||
End;
|
||
|
||
///真实文件名
|
||
///返回:string
|
||
Function FileName();
|
||
Begin
|
||
return zipfile_.FileName();
|
||
End;
|
||
|
||
///word文档所有段落
|
||
///返回:TParagraph对象数组
|
||
Function Paragraphs();
|
||
Begin
|
||
return document_.Body().Paragraphs();
|
||
End;
|
||
|
||
///添加新段落
|
||
///paragraph: TParagraph对象
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加段落
|
||
///[styleId]: 样式ID(integer或string)
|
||
///返回TParagraph对象
|
||
Function AddParagraph(paragraph, posOpt, styleId);
|
||
Begin
|
||
return document_.Body().AddParagraph(paragraph, getPosNode(posOpt), styleId);
|
||
End;
|
||
|
||
///添加标题
|
||
///title: string
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加段落
|
||
///level: int 标题级别(0-9)
|
||
///返回TParagraph对象
|
||
Function AddHeading(title, posOpt, level);
|
||
Begin
|
||
styleName := level = 0 ? 'Title' : 'Heading ' $ level;
|
||
style := StyleObject().GetStyle(styleName);
|
||
if not ifObj(style) and ifInt(level) and level >= 0 and level <= 9 then
|
||
style := StyleObject().AddDefaultStyle(GetPath(), styleName);
|
||
return document_.Body().AddHeading(title, getPosNode(posOpt), ifObj(style) ? style.StyleId : nil);
|
||
End;
|
||
|
||
///插入分页符
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加段落
|
||
///返回TParagraph对象
|
||
Function AddPageBreak(posOpt);
|
||
Begin
|
||
return document_.Body().AddBreak(getPosNode(posOpt), 'page');
|
||
End;
|
||
|
||
///插入换行符
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加段落
|
||
///返回TParagraph对象
|
||
Function AddLineBreak(posOpt);
|
||
Begin
|
||
return document_.Body().AddBreak(getPosNode(posOpt), '');
|
||
End;
|
||
|
||
///插入分栏符
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加段落
|
||
///返回TParagraph对象
|
||
Function AddColumnBreak(posOpt);
|
||
Begin
|
||
return document_.Body().AddBreak(getPosNode(posOpt), 'column');
|
||
End;
|
||
|
||
///删除指定段落
|
||
///posOpt: 段落位置,0 DOCX文件开头;-1 文件尾;N 第N段;posOpt段落
|
||
///返回:true
|
||
Function DelParagraph(posOpt);
|
||
Begin
|
||
return document_.Body().DelParagraph(getPosNode(posOpt));
|
||
End;
|
||
|
||
///word文档所有内容的文本串
|
||
///返回:string
|
||
Function Text();
|
||
Begin
|
||
return document_.Body().Text();
|
||
End;
|
||
|
||
//word文档所有内容的文本串数组,包含段落信息
|
||
//返回:array(("pNode":nodeObj, "pIndex":p, "rNode":nodeObj, "rIndex":r))
|
||
Function TextArray();
|
||
Begin
|
||
return document_.Body().TextArray();
|
||
End;
|
||
|
||
Function Body();
|
||
Begin
|
||
return document_.Body();
|
||
End;
|
||
|
||
///word文档所有表格个数
|
||
///返回:int
|
||
Function TablesCount();
|
||
Begin
|
||
return document_.Body().TablesCount();
|
||
End;
|
||
|
||
///word文档指定表格
|
||
///n: int 第n个表格
|
||
///返回:TTable对象
|
||
Function GetTable(n);
|
||
Begin
|
||
return document_.Body().GetTable(n);
|
||
End;
|
||
|
||
///创建数据表
|
||
///data: table,数据表
|
||
///[IncludeHeader: bool] 是否包括表头,默认FALSE
|
||
///[IncludeIndex: bool] 是否自动添加索引号,默认FALSE
|
||
///返回: TTable对象
|
||
Function CreateTable(data, IncludeHeader, IncludeIndex);
|
||
Begin
|
||
return document_.Body().CreateTable(data, IncludeHeader, IncludeIndex);
|
||
End;
|
||
|
||
///插入数据表
|
||
///tbl: TTable对象
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加表格
|
||
///返回: TTable对象
|
||
Function InsertTable(tbl, posOpt);
|
||
Begin
|
||
return document_.Body().InsertTable(tbl, getPosNode(posOpt));
|
||
End;
|
||
|
||
///返回CoreProperties对象
|
||
Function Properties();
|
||
Begin
|
||
core := TOfficeObj('TCoreProperties');
|
||
core.node_ := zipfile_.Get('docProps/core.xml').FirstChildElement('cp:coreProperties');
|
||
return core;
|
||
End;
|
||
|
||
///返回TDocSection集合
|
||
Function Sections();overload;
|
||
Begin
|
||
return document_.Body().Sections();
|
||
End;
|
||
|
||
///提供对节和页面设置设置的访问
|
||
///还提供对页眉和页脚的访问
|
||
///index:integer 章节索引
|
||
///返回:TDocSection对象
|
||
Function Sections(index);overload;
|
||
Begin
|
||
return document_.Body().Sections(index);
|
||
End;
|
||
|
||
///添加章节
|
||
///session:TDocSection对象
|
||
///posOpt: 位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加章节
|
||
///返回:TDocSection对象
|
||
Function AddSection(session, posOpt);overload;
|
||
Begin
|
||
return document_.Body().AddSection(session, getPosNode(posOpt));
|
||
End;
|
||
|
||
///插入图片
|
||
///picture: TPicture对象
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
|
||
///返回:TPicture对象
|
||
Function AddPicture(picture, posOpt);
|
||
Begin
|
||
picture.Run.Drawing.WInline.ID := GetDocPrId();
|
||
return document_.Body().AddPicture(picture, getPosNode(posOpt));
|
||
End;
|
||
|
||
///插入图表
|
||
///chart:TChart对象
|
||
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图表
|
||
///返回: TChart对象
|
||
Function AddChart(chart, posOpt);
|
||
Begin
|
||
o := new TDocxChart(self, chart);
|
||
p := TOfficeObj('TParagraph');
|
||
p.Format.rPr.Lang := 'zh-CN';
|
||
p := AddParagraph(p, getPosNode(posOpt), nil);
|
||
chart.pNode := p.node_;
|
||
p.Node().InsertEndChild(o.GetInnerXml());
|
||
return chart;
|
||
End;
|
||
|
||
///文档中全部的Chart图
|
||
///返回:TChart对象数组
|
||
Function GetCharts();overload;
|
||
Begin
|
||
r := array();
|
||
uri := 'w:p/w:r/w:drawing/wp:inline/a:graphic/a:graphicData/c:chart';
|
||
ps := Paragraphs();
|
||
for i:=0 to length(ps)-1 do Begin
|
||
node := class(TSXml).GetNode(ps[i].node_, uri);
|
||
if ifObj(node) then Begin
|
||
chart := TOfficeObj('TChart');
|
||
chart.Init(self, ps[i].node_, node);
|
||
r[length(r)] := chart;
|
||
End;
|
||
End;
|
||
return r;
|
||
End;
|
||
|
||
///文档中全部的批注信息
|
||
///返回:DocComments对象
|
||
Function Comments();
|
||
Begin
|
||
return document_.Body().Comments();
|
||
End;
|
||
|
||
///创建新的批注对象
|
||
///返回:TDocComment对象
|
||
Function NewComment(author, txt);
|
||
Begin
|
||
file := 'word/comments.xml';
|
||
files := zipfile_.Files();
|
||
isexist := vselect thisrowindex from files where ['FileName']=file end;
|
||
if ifnil(isexist) then Begin
|
||
zipfile_.Add(file, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||
<w:comments xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpsCustomData="http://www.wps.cn/officeDocument/2013/wpsCustomData" mc:Ignorable="w14 w15 wp14"></w:comments>');
|
||
rels := 'word/_rels/document.xml.rels';
|
||
xmlfile := zipfile_.Get(rels);
|
||
[rId, target] := class(TSXml).FindRelationshipRid(xmlfile, '');
|
||
rId ++;
|
||
class(TSXml).AddRelationshipRid(xmlfile, 'comments.xml', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments', 'rId' $ rId);
|
||
contentType := zipfile_.Get('[Content_Types].xml');
|
||
class(TSXml).AddOverrideContentType(contentType, '/word/comments.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml');
|
||
End
|
||
xmlfile := zipfile_.Get(file);
|
||
id := 0;
|
||
node := xmlfile.FirstChildElement('w:comments').FirstChildElement('w:comment');
|
||
while ifObj(node) do Begin
|
||
wId := node.GetAttribute('w:id');
|
||
if strtoint(wId) >= id then
|
||
id := strtoint(wId) + 1;
|
||
node := node.NextElement('w:comment');
|
||
End;
|
||
c := TOfficeObj('TDocComment');
|
||
c.p.Run.SetText( txt );
|
||
c.Author := author;
|
||
if author and not class(TSXml).IsUtf8() then
|
||
c.Author := class(TSXml).CurCodePageToUtf8(author);
|
||
c.ID := id;
|
||
xmlfile.FirstChildElement('w:comments').InsertEndChild(c.Marshal());
|
||
return c;
|
||
End;
|
||
|
||
///格式刷:段落格式 + 字体格式
|
||
///fromParagraph:源段落
|
||
///toParagraph:目标段落
|
||
Function CopyFormat(fromParagraph, toParagraph);
|
||
Begin
|
||
toParagraph.ClearFormat();//清除目标段落格式、字体格式
|
||
CopyParagraphFormat(fromParagraph, toParagraph);//段落格式
|
||
//字体格式
|
||
if not ifObj(fromParagraph) then
|
||
return;
|
||
fromRun := fromParagraph.GetRun(0);
|
||
if ifObj(fromRun) then Begin
|
||
runs := toParagraph.GetRuns();
|
||
for i:=0 to length(runs)-1 do Begin
|
||
runs[i].CopyFontFormat(fromRun);
|
||
End;
|
||
End;
|
||
End;
|
||
|
||
///格式刷:仅段落格式
|
||
///fromParagraph:源段落
|
||
///toParagraph:目标段落
|
||
Function CopyParagraphFormat(fromParagraph, toParagraph);
|
||
Begin
|
||
pPr := toParagraph.node_.FirstChildElement('w:pPr');
|
||
//清除段落格式
|
||
if ifObj(pPr) then
|
||
toParagraph.node_.DeleteChild(pPr);
|
||
pPr := ifObj(fromParagraph) ? fromParagraph.node_.FirstChildElement('w:pPr') : nil;
|
||
if ifObj(pPr) then Begin //复制段落格式
|
||
arr := pPr.Marshal();
|
||
toParagraph.node_.InsertFirstChild(arr[0]);
|
||
End;
|
||
End;
|
||
|
||
///格式刷:仅字体格式
|
||
///fromRun:源段落
|
||
///toRun:目标段落
|
||
Function CopyFontFormat(fromRun, toRun);
|
||
Begin
|
||
toRun.CopyFontFormat(fromRun);
|
||
End;
|
||
|
||
///添加目录
|
||
///[posOpt: 段落位置],在posOpt之后新添加目录(否则在首页添加)
|
||
///UpperHeadingLevel:标题最高级别
|
||
///LowerHeadingLevel:标题最低级别
|
||
/// 使用 UpperHeadingLevel 属性可设置起始标题级别(最高)。例如,若要设置 TOC 域语法 {TOC \o "1-3"},可将 LowerHeadingLevel 属性设为 3,并将 UpperHeadingLevel 属性设为 1。
|
||
///因为word、wps、openoffice等软件对于页码的计算各不相同,本功能不直接设置页码,需要用相关客户端软件打开文件后,更新目录域。
|
||
Function AddTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel);
|
||
Begin
|
||
content := new TTableContent(self);
|
||
content.SetDefaultFormat(); //缺省目录格式
|
||
node := getPosNode(posOpt);
|
||
content.Add(node, UpperHeadingLevel, LowerHeadingLevel); //标题级别
|
||
if ifObj(node) then
|
||
content.node_ := document_.Body().node_.InsertAfterChild(node, content.Marshal());
|
||
else
|
||
content.node_ := document_.Body().node_.InsertFirstChild(content.Marshal());
|
||
AddPageBreak(content.node_);
|
||
return content;
|
||
End;
|
||
|
||
///获取全部标题列表
|
||
///UpperHeadingLevel:标题最高级别
|
||
///LowerHeadingLevel:标题最低级别
|
||
///返回:array((("Level":level,"Paragraph":"object","Text":title));
|
||
Function GetHeadingList(UpperHeadingLevel, LowerHeadingLevel);
|
||
Begin
|
||
return document_.Body().GetHeadingListImpl(self, nil, UpperHeadingLevel, LowerHeadingLevel, nil, true);
|
||
End;
|
||
|
||
///返回Document对象
|
||
Function Document();
|
||
Begin
|
||
return document_;
|
||
End;
|
||
|
||
///返回:TDocxStyles对象
|
||
Function StyleObject();
|
||
Begin
|
||
if not ifObj(styleObj_) then
|
||
styleObj_ := new TDocxStyles(self);
|
||
return styleObj_;
|
||
End;
|
||
|
||
///返回:TNumbering对象
|
||
Function NumberingObject();
|
||
Begin
|
||
if not ifObj(numberingObj_) then
|
||
numberingObj_ := new TNumbering(self);
|
||
return numberingObj_;
|
||
End;
|
||
|
||
///执行word文档内嵌tsl代码
|
||
///返回:[err,tslFuncCount,errArr]: err 执行错误TSL代码段次数,tslFuncCount TSL代码段总数,errArr 执行TSL错误信息(包括代码、错误信息)
|
||
Function ExecInnerTSL();
|
||
Begin
|
||
return Body().ExecInnerTSL(self);
|
||
End;
|
||
|
||
Function ZipObject();
|
||
Begin
|
||
return zipfile_;
|
||
End;
|
||
|
||
Function GetPath();
|
||
Begin
|
||
return ExtractFileDir(ExtractFileDir(PluginPath()));
|
||
End;
|
||
|
||
Function GetDocPrId();
|
||
Begin
|
||
if DocPrId_ < 0 then Begin
|
||
DocPrId_ := 0;
|
||
ps := Paragraphs();
|
||
for i:=0 to length(ps)-1 do Begin
|
||
node := class(TSXml).GetNode(ps[i].node_, 'w:r/w:drawing/wp:inline/wp:docPr');
|
||
if not ifObj(node) then
|
||
node := class(TSXml).GetNode(ps[i].node_, 'w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:anchor/wp:docPr');
|
||
if ifObj(node) then Begin
|
||
id := class(TSXml).SafeStrToIntDef(node.GetAttribute('id'), 0);
|
||
if id > DocPrId_ then
|
||
DocPrId_ := id;
|
||
break;
|
||
End;
|
||
End;
|
||
End;
|
||
DocPrId_ ++;
|
||
return DocPrId_;
|
||
End;
|
||
private
|
||
Function getPosNode(posOpt);
|
||
Begin
|
||
node := posOpt;
|
||
if ifObj(node) and not (node is Class(XmlNode)) then
|
||
node := node.Node();
|
||
return node;
|
||
End;
|
||
private
|
||
zipfile_; //压缩文件对象
|
||
document_; //Document对象
|
||
xml_; //TSXml对象
|
||
styleObj_;
|
||
numberingObj_;
|
||
DocPrId_;
|
||
End;
|