TSOffice/funcext/TSOffice/TSDocxFile.tsf

466 lines
15 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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]: 样式IDinteger或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;
///提供对节和页面设置设置的访问
///还提供对页眉和页脚的访问
///indexinteger 章节索引
///返回TDocSection对象
Function Sections(index);overload;
Begin
return document_.Body().Sections(index);
End;
///添加章节
///sessionTDocSection对象
///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;