diff --git a/funcext/TSOffice/TOfficeObj.tsf b/funcext/TSOffice/TOfficeObj.tsf
index a1058c9..f04cc39 100644
--- a/funcext/TSOffice/TOfficeObj.tsf
+++ b/funcext/TSOffice/TOfficeObj.tsf
@@ -1,4 +1,4 @@
-// Version 1.8.1
+// Version 1.8.2
Function TOfficeObj(n);
Begin
case lowercase(n) of
@@ -11143,16 +11143,33 @@ Type TParagraph = Class(DocObject, TParagraphImpl)
Begin
if ifObj(node_) then
begin
- if ifObj(node_.FirstChildElement("w:r")) then return false;
sub_node := node_.FirstChildElement();
if not ifObj(sub_node) then return true;
- // 兼容wps
sub_node2 := sub_node.NextElement();
- if sub_node.GetName() = 'w:bookmarkStart' and ifObj(sub_node2) and sub_node2.GetName() = 'w:bookmarkEnd' then
- return true;
+
+ sub_node_name := sub_node.GetName();
+ if sub_node_name = "w:r" then
+ begin
+ r_sub_node := sub_node.FirstChildElement();
+ if not ifObj(r_sub_node) then return true;
+ while ifObj(r_sub_node) do
+ begin
+ sub_node2_name := r_sub_node.GetName();
+ if sub_node2_name = 'w:t' and trim(r_sub_node.GetText()) <> '' then return fasle;
+ else if sub_node2_name in array('mc:AlternateContent', 'w:drawing', 'w:pict', 'w:object') then return false;
+ r_sub_node := r_sub_node.NextElement();
+ end
+ end
// 兼容word
- if sub_node.GetName() = 'w:pPr' and not ifObj(sub_node2) then
+ else if sub_node_name = 'w:pPr' and not ifObj(sub_node2) then
+ begin
return true;
+ end
+ // 兼容wps
+ else if sub_node_name = 'w:bookmarkStart' and ifObj(sub_node2) and sub_node2.GetName() = 'w:bookmarkEnd' then
+ begin
+ return true;
+ end
end
return false;
End;
@@ -14144,7 +14161,7 @@ Type TDocumentBody = Class(DocObject)
r[ind]['numId'] := arr[ii]['numId'];
r[ind]['ilvl'] := arr[ii]['ilvl'];
r[ind]['numArr'] := arr[ii]['numArr'];
- ind++
+ ind++;
end
End;
End;
diff --git a/funcext/TSOffice/TSDocxFile.tsf b/funcext/TSOffice/TSDocxFile.tsf
index daca7b4..6e23295 100644
--- a/funcext/TSOffice/TSDocxFile.tsf
+++ b/funcext/TSOffice/TSDocxFile.tsf
@@ -1,665 +1,668 @@
-// Version 1.8.1
-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, nil);
- End;
-
- ///构造函数,打开已经存在的docx文件
- ///alias: string,文件目录别名
- ///fname: string,文件名
- ///passwd: string,密码
- Function Create(alias, fname, passwd); overload;
- Begin
- init();
- OpenFile(alias, fname, passwd);
- End;
-
- Function Destory();
- Begin
- End;
-
- Function init();
- Begin
- DocPrId_ := -1;
- zipfile_ := new ZipFile();
- End;
-
- ///打开docx文件
- ///alias: string,文件目录别名
- ///fname: string,文件名
- ///[passwd]: string,密码
- ///返回:[err, errmsg]
- Function OpenFile(alias, fname, passwd);
- Begin
- if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
- if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile();
- [err, errmsg] := zipfile_.Open(alias, TOfficeApi().CurCodePageToGBK(fname), passwd);
- if err=0 then Begin
- document_ := new docxDocument(zipfile_);
- End;
- return array(err, errmsg);
- End;
-
- ///新建docx文件
- ///返回:[err, info]
- Function NewFile();
- Begin
- def := TOfficeTemplate('default.docx', true);
- [err, errmsg] := zipfile_.LoadFromMem(def);
- if err=0 then Begin
- document_ := new docxDocument(zipfile_);
- End;
- return array(err, errmsg);
- End;
-
- ///设置密码
- Function SetPassword(passwd)
- Begin
- zipfile_.Password := passwd;
- 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, TOfficeApi().CurCodePageToGBK(fname));
- End;
-
- ///另存为二进制流数据
- ///返回: [err, fileContent] fileContent 文件内容,为Binary数据类型
- Function SaveToMem();
- Begin
- return zipfile_.Save2Mem();
- End;
-
- ///打开二进制内容
- ///data: 二进制数据
- ///返回: [err, errmsg]
- Function LoadFromMem(data);
- Begin
- [err, errmsg] := zipfile_.LoadFromMem(data);
- if err=0 then Begin
- document_ := new docxDocument(zipfile_);
- End;
- return array(err, errmsg);
- End;
-
- ///真实文件名
- ///返回:string
- Function FileName();
- Begin
- return zipfile_.FileName();
- End;
-
- ///word文档所有段落
- ///返回:TParagraph对象数组
- Function Paragraphs();
- Begin
- return document_.Body().Paragraphs();
- End;
-
- ///word文档最后一个段落
- ///返回:TParagraph对象
- Function LastParagraph();
- Begin
- return document_.Body().LastParagraph();
- 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
- if ifstring(level) then return document_.Body().AddHeading(title, getPosNode(posOpt), level);
- 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(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 从0开始,第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
- tbl := TOfficeObj('TTable');
- tbl.SetData(self, data, IncludeHeader, IncludeIndex);
- return tbl;
- End;
-
- ///插入数据表
- ///tbl: TTable对象
- ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加表格
- ///customCell: 二维数组,自定义指定单元格的样式
- /// 如:一行一列arr[0][0] := array(twtcPr, twpPr, twrPr);其中twtcPr是twtcPr对象,twpPr是twpPr对象,twrPr是twrPr对象
- ///返回: TTable对象
- Function InsertTable(tbl, posOpt, customCell);
- Begin
- return document_.Body().InsertTable(tbl, getPosNode(posOpt), customCell);
- 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;
-
- ///获取Structured Document Tag
- ///返回:TSdt集合
- Function Sdts();
- Begin
- return document_.Body().Sdts();
- 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());
- TOfficeApi().Set('CurrentShape', p.node_);
- chart.chartFileName := 'word/charts/chart' $ o.ChartId_ $ '.xml';
- return chart;
- End;
-
- ///文档中全部的Chart图
- ///返回:TChart对象数组
- Function GetCharts();overload;
- Begin
- r := array();
- uri := '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;
-
- ///从Excel中Copy指定的chart图到文档中指定位置
- ///excelFileName:string xlsx文件名
- ///excelSheetName:string sheetname
- ///chartName:string or integer,chart图名称或当前sheet中chart图索引号
- ///Width:chart图宽度,单位cm
- ///Height:chart图高度,单位cm
- ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
- ///返回TChart对象
- Function CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, posOpt);
- Begin
- excel := new TSXlsxFile();
- [err, msg] := excel.OpenFile('', excelFileName);
- if err then return nil;
- [err, charts] := excel.GetCharts(excelSheetName);
- if err or length(charts)=0 then return nil;
- drawingObj := excel.WorkBook().GetXmlFileObj(charts[0].drawingFileName);
- if not ifObj(drawingObj) then return nil;
- node := drawingObj.FirstChildElement('xdr:wsDr').FirstChildElement('xdr:twoCellAnchor');
- ind := 0;
- chartRid := '';
- while ifObj(node) do Begin
- findChart := false;
- cNvPr := class(TSXml).GetNode(node, 'xdr:GraphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr');
- name := ifObj(cNvPr) ? cNvPr.GetAttribute('name') : '';
- if ifstring(chartName) then Begin
- if name = chartName then
- find := true;
- End
- else if ifInt(chartName) and ind = chartName then
- find := true;
- if find then Begin
- chartNode := class(TSXml).GetNode(node, 'xdr:GraphicFrame/a:graphic/a:graphicData/c:chart');
- if not ifObj(chartNode) then return nil;
- chartRid := chartNode.GetAttribute('r:id');
- break;
- End;
- ind ++;
- node := node.NextElement();
- End;
- for i:=0 to length(charts)-1 do Begin
- if charts[i].Rid = chartRid then Begin
- chart := TOfficeObj('TChart');
- chart.Width := Width;
- chart.Height := Height;
- chart.Name := name;
- chart.Type := 'line';
- chart.ShowBubbleSize := false;
- chart.ShowPercent := false;
- chart.DataTable := false;
- chart.AddSeries('test', array('line1'), array(1,2));
- chart := AddChart(chart, getPosNode(posOpt));
- xmlObj := Zip().Get(chart.chartFileName);
- xmlObj.Data := charts[i].xmlObj.Data;
- return chart;
- End;
- End;
- return nil;
- End;
-
- ///复制Word内容
- ///alias: string,文件目录别名
- ///fileName: string,文件名
- ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
- ///返回: [err, TDocxCopy对象]
- ///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组
- ///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组
- ///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组
- Function InsertFile(alias, fileName, posOpt);overload;
- Begin
- document_.AddNameSpace('w16du', 'http://schemas.microsoft.com/office/word/2023/wordml/word16du');
- docxObj := new TSDocxFile();
- [err, msg] := docxObj.OpenFile(alias, fileName);
- if err then return array(err, msg);
- copy_obj := new TDocxCopy(self, docxObj);
- copy_obj.Init();
- copy_obj.Copy(posOpt);
- return array(0, copy_obj);
- End;
-
- ///复制Word内容
- ///docxObj: TSDocxFile对象
- ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
- ///返回: [err, TDocxCopy对象]
- ///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组
- ///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组
- ///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组
- Function InsertFile(docxObj, posOpt);overload;
- Begin
- document_.AddNameSpace('w16du', 'http://schemas.microsoft.com/office/word/2023/wordml/word16du');
- copy_obj := new TDocxCopy(self, docxObj);
- copy_obj.Init();
- copy_obj.Copy(posOpt);
- return array(0, copy_obj);
- End;
-
- ///遍历文档中所有[TSTAG][/TSTAG]标签,针对每一个TAG执行tagObj.Apply()
- ///tagName:string 标签名称
- ///tagObj:TAG对象方法
- ///返回:[err,tslTagCount,errArr]: err 执行错误TAG次数,tslTagCount TAG总数,errArr 错误信息(array(('code':'代码', 'err':'错误信息')))
- Function ExecTsTag(tagName, tagObj);
- Begin
- return Body().ExecTsTag(self, tagName, tagObj);
- 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, '
-');
- 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;
- 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;
-
- ///添加自定义目录
- ///[posOpt: 段落位置],在posOpt之后新添加目录(否则在首页添加)
- ///UpperHeadingLevel:标题最高级别
- ///LowerHeadingLevel:标题最低级别
- /// 使用 UpperHeadingLevel 属性可设置起始标题级别(最高)。例如,若要设置 TOC 域语法 {TOC \o "1-3"},可将 LowerHeadingLevel 属性设为 3,并将 UpperHeadingLevel 属性设为 1。
- ///因为word、wps、openoffice等软件对于页码的计算各不相同,本功能不直接设置页码,需要用相关客户端软件打开文件后,更新目录域。
- Function AddCustomTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel);
- Begin
- content := new TTableContent(self);
- node := getPosNode(posOpt);
- content.CustomAdd(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);
- 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;
-
- ///返回:TFootnotes对象
- Function FootNotesObject();
- Begin
- if not ifObj(footnotesObj_) then
- begin
- footnotesObj_ := new TOfficeObj('TFootnoteBody');
- footnotesObj_.InitFootnotes(self.Zip());
- end
- return footnotesObj_;
- End;
-
- ///执行word文档内嵌tsl代码(执行内嵌脚本的环境字符集为GBK)
- ///返回:[err,tslFuncCount,errArr]: err 执行错误TSL代码段次数,tslFuncCount TSL代码段总数,errArr 执行TSL错误信息(array(('code':'代码', 'err':'错误信息')))
- Function ExecInnerTSL();
- Begin
- return Body().ExecInnerTSL(self);
- End;
-
- Function Zip();//兼容excel
- Begin
- return zipfile_;
- End;
-
- Function IsWord();
- Begin
- return true;
- 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
- runs := ps[i].GetRuns();
- for j:=0 to length(runs)-1 do Begin
- node := class(TSXml).GetNode(runs[j].node_, 'w:drawing/wp:inline/wp:docPr');
- if not ifObj(node) then
- node := class(TSXml).GetNode(runs[j].node_, 'w:drawing/wp:anchor/wp:docPr');
- if not ifObj(node) then
- node := class(TSXml).GetNode(runs[j].node_, 'w:pict/v:shape/v:textbox/w:txbxContent/wp:docPr');
- if not ifObj(node) then
- node := class(TSXml).GetNode(runs[j].node_, '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;
- End;
- End;
- End;
- End;
- DocPrId_ ++;
- return DocPrId_;
- End;
-
- 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对象
- styleObj_;
- footnotesObj_;
- numberingObj_;
- DocPrId_;
-End;
-
+// Version 1.8.2
+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, nil);
+ End;
+
+ ///构造函数,打开已经存在的docx文件
+ ///alias: string,文件目录别名
+ ///fname: string,文件名
+ ///passwd: string,密码
+ Function Create(alias, fname, passwd); overload;
+ Begin
+ init();
+ OpenFile(alias, fname, passwd);
+ End;
+
+ Function Destory();
+ Begin
+ End;
+
+ Function init();
+ Begin
+ DocPrId_ := -1;
+ zipfile_ := new ZipFile();
+ End;
+
+ ///打开docx文件
+ ///alias: string,文件目录别名
+ ///fname: string,文件名
+ ///[passwd]: string,密码
+ ///返回:[err, errmsg]
+ Function OpenFile(alias, fname, passwd);
+ Begin
+ if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
+ if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile();
+ [err, errmsg] := zipfile_.Open(alias, TOfficeApi().CurCodePageToGBK(fname), passwd);
+ if err=0 then Begin
+ document_ := new docxDocument(zipfile_);
+ End;
+ return array(err, errmsg);
+ End;
+
+ ///新建docx文件
+ ///返回:[err, info]
+ Function NewFile();
+ Begin
+ def := TOfficeTemplate('default.docx', true);
+ [err, errmsg] := zipfile_.LoadFromMem(def);
+ if err=0 then Begin
+ document_ := new docxDocument(zipfile_);
+ End;
+ return array(err, errmsg);
+ End;
+
+ ///设置密码
+ Function SetPassword(passwd)
+ Begin
+ zipfile_.Password := passwd;
+ 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, TOfficeApi().CurCodePageToGBK(fname));
+ End;
+
+ ///另存为二进制流数据
+ ///返回: [err, fileContent] fileContent 文件内容,为Binary数据类型
+ Function SaveToMem();
+ Begin
+ return zipfile_.Save2Mem();
+ End;
+
+ ///打开二进制内容
+ ///data: 二进制数据
+ ///返回: [err, errmsg]
+ Function LoadFromMem(data);
+ Begin
+ [err, errmsg] := zipfile_.LoadFromMem(data);
+ if err=0 then Begin
+ document_ := new docxDocument(zipfile_);
+ End;
+ return array(err, errmsg);
+ End;
+
+ ///真实文件名
+ ///返回:string
+ Function FileName();
+ Begin
+ file_name := zipfile_.FileName();
+ if TOfficeApi().IsUtf8() then
+ return AnsiToUTF8(file_name);
+ return file_name;
+ End;
+
+ ///word文档所有段落
+ ///返回:TParagraph对象数组
+ Function Paragraphs();
+ Begin
+ return document_.Body().Paragraphs();
+ End;
+
+ ///word文档最后一个段落
+ ///返回:TParagraph对象
+ Function LastParagraph();
+ Begin
+ return document_.Body().LastParagraph();
+ 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
+ if ifstring(level) then return document_.Body().AddHeading(title, getPosNode(posOpt), level);
+ 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(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 从0开始,第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
+ tbl := TOfficeObj('TTable');
+ tbl.SetData(self, data, IncludeHeader, IncludeIndex);
+ return tbl;
+ End;
+
+ ///插入数据表
+ ///tbl: TTable对象
+ ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加表格
+ ///customCell: 二维数组,自定义指定单元格的样式
+ /// 如:一行一列arr[0][0] := array(twtcPr, twpPr, twrPr);其中twtcPr是twtcPr对象,twpPr是twpPr对象,twrPr是twrPr对象
+ ///返回: TTable对象
+ Function InsertTable(tbl, posOpt, customCell);
+ Begin
+ return document_.Body().InsertTable(tbl, getPosNode(posOpt), customCell);
+ 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;
+
+ ///获取Structured Document Tag
+ ///返回:TSdt集合
+ Function Sdts();
+ Begin
+ return document_.Body().Sdts();
+ 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());
+ TOfficeApi().Set('CurrentShape', p.node_);
+ chart.chartFileName := 'word/charts/chart' $ o.ChartId_ $ '.xml';
+ return chart;
+ End;
+
+ ///文档中全部的Chart图
+ ///返回:TChart对象数组
+ Function GetCharts();overload;
+ Begin
+ r := array();
+ uri := '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;
+
+ ///从Excel中Copy指定的chart图到文档中指定位置
+ ///excelFileName:string xlsx文件名
+ ///excelSheetName:string sheetname
+ ///chartName:string or integer,chart图名称或当前sheet中chart图索引号
+ ///Width:chart图宽度,单位cm
+ ///Height:chart图高度,单位cm
+ ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
+ ///返回TChart对象
+ Function CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, posOpt);
+ Begin
+ excel := new TSXlsxFile();
+ [err, msg] := excel.OpenFile('', excelFileName);
+ if err then return nil;
+ [err, charts] := excel.GetCharts(excelSheetName);
+ if err or length(charts)=0 then return nil;
+ drawingObj := excel.WorkBook().GetXmlFileObj(charts[0].drawingFileName);
+ if not ifObj(drawingObj) then return nil;
+ node := drawingObj.FirstChildElement('xdr:wsDr').FirstChildElement('xdr:twoCellAnchor');
+ ind := 0;
+ chartRid := '';
+ while ifObj(node) do Begin
+ findChart := false;
+ cNvPr := class(TSXml).GetNode(node, 'xdr:GraphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr');
+ name := ifObj(cNvPr) ? cNvPr.GetAttribute('name') : '';
+ if ifstring(chartName) then Begin
+ if name = chartName then
+ find := true;
+ End
+ else if ifInt(chartName) and ind = chartName then
+ find := true;
+ if find then Begin
+ chartNode := class(TSXml).GetNode(node, 'xdr:GraphicFrame/a:graphic/a:graphicData/c:chart');
+ if not ifObj(chartNode) then return nil;
+ chartRid := chartNode.GetAttribute('r:id');
+ break;
+ End;
+ ind ++;
+ node := node.NextElement();
+ End;
+ for i:=0 to length(charts)-1 do Begin
+ if charts[i].Rid = chartRid then Begin
+ chart := TOfficeObj('TChart');
+ chart.Width := Width;
+ chart.Height := Height;
+ chart.Name := name;
+ chart.Type := 'line';
+ chart.ShowBubbleSize := false;
+ chart.ShowPercent := false;
+ chart.DataTable := false;
+ chart.AddSeries('test', array('line1'), array(1,2));
+ chart := AddChart(chart, getPosNode(posOpt));
+ xmlObj := Zip().Get(chart.chartFileName);
+ xmlObj.Data := charts[i].xmlObj.Data;
+ return chart;
+ End;
+ End;
+ return nil;
+ End;
+
+ ///复制Word内容
+ ///alias: string,文件目录别名
+ ///fileName: string,文件名
+ ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
+ ///返回: [err, TDocxCopy对象]
+ ///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组
+ ///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组
+ ///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组
+ Function InsertFile(alias, fileName, posOpt);overload;
+ Begin
+ document_.AddNameSpace('w16du', 'http://schemas.microsoft.com/office/word/2023/wordml/word16du');
+ docxObj := new TSDocxFile();
+ [err, msg] := docxObj.OpenFile(alias, fileName);
+ if err then return array(err, msg);
+ copy_obj := new TDocxCopy(self, docxObj);
+ copy_obj.Init();
+ copy_obj.Copy(posOpt);
+ return array(0, copy_obj);
+ End;
+
+ ///复制Word内容
+ ///docxObj: TSDocxFile对象
+ ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
+ ///返回: [err, TDocxCopy对象]
+ ///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组
+ ///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组
+ ///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组
+ Function InsertFile(docxObj, posOpt);overload;
+ Begin
+ document_.AddNameSpace('w16du', 'http://schemas.microsoft.com/office/word/2023/wordml/word16du');
+ copy_obj := new TDocxCopy(self, docxObj);
+ copy_obj.Init();
+ copy_obj.Copy(posOpt);
+ return array(0, copy_obj);
+ End;
+
+ ///遍历文档中所有[TSTAG][/TSTAG]标签,针对每一个TAG执行tagObj.Apply()
+ ///tagName:string 标签名称
+ ///tagObj:TAG对象方法
+ ///返回:[err,tslTagCount,errArr]: err 执行错误TAG次数,tslTagCount TAG总数,errArr 错误信息(array(('code':'代码', 'err':'错误信息')))
+ Function ExecTsTag(tagName, tagObj);
+ Begin
+ return Body().ExecTsTag(self, tagName, tagObj);
+ 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, '
+');
+ 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;
+ 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;
+
+ ///添加自定义目录
+ ///[posOpt: 段落位置],在posOpt之后新添加目录(否则在首页添加)
+ ///UpperHeadingLevel:标题最高级别
+ ///LowerHeadingLevel:标题最低级别
+ /// 使用 UpperHeadingLevel 属性可设置起始标题级别(最高)。例如,若要设置 TOC 域语法 {TOC \o "1-3"},可将 LowerHeadingLevel 属性设为 3,并将 UpperHeadingLevel 属性设为 1。
+ ///因为word、wps、openoffice等软件对于页码的计算各不相同,本功能不直接设置页码,需要用相关客户端软件打开文件后,更新目录域。
+ Function AddCustomTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel);
+ Begin
+ content := new TTableContent(self);
+ node := getPosNode(posOpt);
+ content.CustomAdd(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);
+ 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;
+
+ ///返回:TFootnotes对象
+ Function FootNotesObject();
+ Begin
+ if not ifObj(footnotesObj_) then
+ begin
+ footnotesObj_ := new TOfficeObj('TFootnoteBody');
+ footnotesObj_.InitFootnotes(self.Zip());
+ end
+ return footnotesObj_;
+ End;
+
+ ///执行word文档内嵌tsl代码(执行内嵌脚本的环境字符集为GBK)
+ ///返回:[err,tslFuncCount,errArr]: err 执行错误TSL代码段次数,tslFuncCount TSL代码段总数,errArr 执行TSL错误信息(array(('code':'代码', 'err':'错误信息')))
+ Function ExecInnerTSL();
+ Begin
+ return Body().ExecInnerTSL(self);
+ End;
+
+ Function Zip();//兼容excel
+ Begin
+ return zipfile_;
+ End;
+
+ Function IsWord();
+ Begin
+ return true;
+ 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
+ runs := ps[i].GetRuns();
+ for j:=0 to length(runs)-1 do Begin
+ node := class(TSXml).GetNode(runs[j].node_, 'w:drawing/wp:inline/wp:docPr');
+ if not ifObj(node) then
+ node := class(TSXml).GetNode(runs[j].node_, 'w:drawing/wp:anchor/wp:docPr');
+ if not ifObj(node) then
+ node := class(TSXml).GetNode(runs[j].node_, 'w:pict/v:shape/v:textbox/w:txbxContent/wp:docPr');
+ if not ifObj(node) then
+ node := class(TSXml).GetNode(runs[j].node_, '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;
+ End;
+ End;
+ End;
+ End;
+ DocPrId_ ++;
+ return DocPrId_;
+ End;
+
+ 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对象
+ styleObj_;
+ footnotesObj_;
+ numberingObj_;
+ DocPrId_;
+End;
+
diff --git a/funcext/TSOffice/TSXlsxFile.tsf b/funcext/TSOffice/TSXlsxFile.tsf
index d2c327c..2524de9 100644
--- a/funcext/TSOffice/TSXlsxFile.tsf
+++ b/funcext/TSOffice/TSXlsxFile.tsf
@@ -1,1053 +1,1056 @@
-// Version 1.8.1
-Type TSXlsxFile = Class
- ///Version: V1.0 2022-08-08
- ///适用于 Microsoft Excel? 2007 及以上版本创建的电子表格文档。支持 XLSX / XLSM / XLTM / XLTX 等多种文档格式。
- ///纯TSL模块实现
- ///Excel文件读写接口
-
- ///缺省构造函数
- Function Create(); overload;
- Begin
- Init();
- End;
-
- ///构造函数,打开已经存在的excel文件
- ///alias: string,文件目录别名
- ///fname: string,文件名
- Function Create(alias, fname); overload;
- Begin
- Init();
- OpenFile(alias, fname, nil);
- End;
-
- ///构造函数,打开已经存在的excel文件
- ///alias: string,文件目录别名
- ///fname: string,文件名
- ///passwd: string,密码
- Function Create(alias, fname, passwd); overload;
- Begin
- Init();
- OpenFile(alias, fname, passwd);
- End;
-
- //初始化
- Function Init();
- Begin
- zipfile_ := new ZipFile();
- End;
-
- ///打开excel文件
- ///alias: string,文件目录别名
- ///fname: string,文件名
- ///[passwd]: string,密码
- ///返回: [err, errmsg]
- Function OpenFile(alias, fname, passwd);
- Begin
- if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
- if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile();
- [err, errmsg] := zipfile_.Open(alias, TOfficeApi().CurCodePageToGBK(fname), passwd);
- if err=0 then InitVars();
- return array(err, errmsg);
- End;
-
- ///新建excel文件
- ///返回: [err, errmsg]
- Function NewFile();
- Begin
- def := TOfficeTemplate('default.xlsx', true);
- [err, errmsg] := zipfile_.LoadFromMem(def);
- if err = 0 then InitVars();
- return array(err, errmsg);
- End;
-
- ///保存文件
- ///返回: [err, info]
- Function Save();
- Begin
- return zipfile_.Save();
- End;
-
- ///设置密码
- Function SetPassword(passwd)
- Begin
- zipfile_.Password := passwd;
- End;
-
- ///打开二进制内容
- ///data: 二进制数据
- ///返回: [err, errmsg]
- Function LoadFromMem(data);
- Begin
- [err, errmsg] := zipfile_.LoadFromMem(data);
- if err = 0 then InitVars();
- return array(err, errmsg);
- End;
-
- ///保存为二进制内容
- ///返回: [err, fileContent] fileContent二进制文件内容
- Function SaveToMem();
- Begin
- return zipfile_.Save2Mem();
- End;
-
- ///另存为
- ///alias: string,文件目录别名
- ///fname: string,文件名
- ///返回: [err, info]
- Function SaveAs(alias, fname);
- Begin
- return zipfile_.Save(alias, TOfficeApi().CurCodePageToGBK(fname));
- End;
-
- ///真实文件名
- ///返回: string
- Function FileName();
- Begin
- return zipfile_.FileName();
- End;
-
- ///获取工作表列表
- ///返回: array('Sheet1','Sheet2')
- Function GetSheets();
- Begin
- sheets := workbook_.GetSheets();
- for i:=0 to length(sheets)-1 do
- sheets[i] := class(TSXml).Utf8ToCurCodePage(sheets[i]);
- return sheets;
- End;
-
- ///获取工作表数
- ///返回: integer
- Function GetSheetsCount();
- Begin
- return workbook_.GetSheetsCount();
- End;
-
- ///获取工作表名
- ///index: int,工作表索引
- ///返回: string
- Function GetSheetName(index);
- Begin
- name := workbook_.GetSheetName(index);
- return class(TSXml).Utf8ToCurCodePage(name);
- End;
-
- ///创建新sheet
- ///sheet: string,工作表名称
- Function NewSheet(sheet);overload;
- Begin
- return workbook_.NewSheet(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///在指定sheet之后插入新sheet
- ///sourceSheet: string,指定工作表名称
- ///destSheet: string,目的工作表名称
- Function NewSheet(sourceSheet, destSheet);overload;
- Begin
- return workbook_.NewSheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet));
- End;
-
- ///在指定sheet之前插入新sheet
- ///sourceSheet: string,指定工作表名称
- ///destSheet: string,目的工作表名称
- Function InsertSheet(sourceSheet, destSheet);
- Begin
- return workbook_.InsertSheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet));
- End;
-
- ///删除sheet
- ///sheet: string,工作表名称
- Function DeleteSheet(sheet);
- Begin
- return workbook_.DeleteSheet(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///设置工作表名
- ///sourceName: string, 原工作表名
- ///destName: string, 目标工作表名
- Function SetSheetName(sourceName, destName);
- Begin
- return workbook_.SetSheetName(class(TSXml).CurCodePageToUtf8(sourceName), class(TSXml).CurCodePageToUtf8(destName));
- End;
-
- ///获取总列数
- ///sheet: string,工作表名称
- ///返回: int
- Function TotalCols(sheet);
- Begin
- return workbook_.TotalCols(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///获取总行数
- ///sheet: string,工作表名称
- ///返回: int
- Function TotalRows(sheet);
- Begin
- return workbook_.TotalRows(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///获取单元格的值
- ///sheet: string,工作表名称
- ///axis: string,单元格坐标,如: "A6"
- ///返回: [err, value:string]
- Function GetCellValue(sheet, axis);
- Begin
- [err, value] := workbook_.GetCellValue(class(TSXml).CurCodePageToUtf8(sheet), axis);
- if not err then return array(err, class(TSXml).Utf8ToCurCodePage(value));
- return array(err, value);
- End;
-
- ///获取单元格数据类型
- ///sheet: string,工作表名称
- ///axis: string,单元格坐标,如: "A6"
- ///返回: type:string
- Function GetCellValueType(sheet, axis);
- Begin
- return workbook_.GetCellValueType(class(TSXml).CurCodePageToUtf8(sheet), axis);
- End;
-
- ///设置单元格的值
- ///sheet: string,工作表名称
- ///axis: string,单元格坐标,如: "A6"
- ///val: Var,可以是整数、字符串、数值型等;nil 不设置值(可以调用ClearCell方法清空单元格),只设置属性
- ///[opt:Tany],可选参数,单元格属性,可以是xml字符串或天软数组
- /// xml串:opt := '';
- /// 数组: opt := array('t':'s', 's':'1');
- /// 属性t:单元格数据类型,不设置的话,数据类型为val的数据类型;
- /// t ->('s' 共享字符串、'b' bool 类型、'e' 错误类型、'inlineStr' 内联字符串类型、nil 数字类型、'str' 公式类型)
- /// 属性s:单元格样式
- Function SetCellValue(sheet, axis, val, opt);
- Begin
- sheet_name := class(TSXml).CurCodePageToUtf8(sheet);
- value := class(TSXml).CurCodePageToUtf8(val);
- // 处理cell设定的样式
- if not ifarray(opt) then opt := array();
- workbook_.SetCellStyleOpt(sheet_name, axis, opt);
- SetCellType(sheet_name, axis, value, opt);
- return workbook_.SetCellValue(sheet_name, axis, value, opt);
- End;
-
- ///获取富文本格式
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///返回: [err, richtxt:string],参见SetCellRichText
- Function GetCellRichText(sheet, axis);
- Begin
- [err, str] := workbook_.GetCellRichText(class(TSXml).CurCodePageToUtf8(sheet), axis);
- if err then return array(err, str);
- else return array(err, class(TSXml).Utf8ToCurCodePage(str));
- End;
-
- ///设置富文本格式
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///richtext: string,xml串或富文本对象TSOfficeObj('TRichText')
- Function SetCellRichText(sheet, axis, richtext);
- Begin
- o := workbook_.GetSheetObj(class(TSXml).CurCodePageToUtf8(sheet));
- if ifObj(o) then
- begin
- xml := richtext;
- if ifObj(richtext) then
- xml := class(TSXml).Dom2Xml(richtext.Marshal());
- return o.SetCellValue(axis, xml, array('t':'s'), 'RichText');
- end
- End;
-
- ///清空单元格
- ///sheet: string,工作表名称
- ///[topLeft: string],左上角坐标,可选参数
- ///[bottomRight: string],右下角坐标,可选参数,
- /// 用法1:ClearCell(); //Clear整个sheet
- /// 用法2:ClearCell('A2'); //Clear 'A2'单元格
- /// 用法3:ClearCell('A5', 'D8'); //Clear矩形区间所有单元格
- Function ClearCell(sheet, topLeft, bottomRight);
- Begin
- return workbook_.ClearCell(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight);
- End;
-
- ///设置公式
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///formula: string,公式
- Function SetCellFormula(sheet, axis, formula);
- Begin
- return workbook_.SetCellFormula(class(TSXml).CurCodePageToUtf8(sheet), axis, class(TSXml).CurCodePageToUtf8(formula));
- End;
-
- ///获取公式
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///返回: [err, formula:string]
- Function GetCellFormula(sheet, axis);
- Begin
- return class(TSXml).Utf8ToCurCodePage(workbook_.GetCellFormula(class(TSXml).CurCodePageToUtf8(sheet), axis));
- End;
-
- ///按行赋值
- ///sheet: string,工作表名称
- ///axis: string,起始坐标,如: "A4"
- ///slice: array(),一维数组array(1,2,3,2,1,"hello")
- Function SetSheetRow(sheet, axis, slice);
- Begin
- [err, col, row] := CellNameToCoordinates(axis);
- if not err then
- begin
- for i:=0 to length(slice)-1 do
- begin
- [err, cell] := CoordinatesToCellName(col + i, row);
- SetCellValue(sheet, cell, slice[i]);
- end
- end
- End;
-
- ///插入数据表
- ///sheet: string,工作表名称
- ///axis: string,左上角坐标,如: "A4"
- ///data: table,数据表
- ///[IncludeHeader: bool] 是否包括表头,默认FALSE
- ///[IncludeIndex: bool] 是否自动添加索引号,默认FALSE
- ///返回: [err, info]
- Function InsertTable(sheet, axis, data, IncludeHeader, IncludeIndex);
- Begin
- if not ifarray(data) or length(data)=0 then
- return array(-1, "Invalid Data.");
- if FieldCount(data)=0 then Begin //数据是一维数组
- return SetSheetRow(sheet, axis, data);
- End;
- [err, colNum, rowNum] := CellNameToCoordinates(axis);
- if err then
- return array(err, colNum);
- if IncludeHeader then Begin
- fields := FieldNames(data);
- if IncludeIndex then Begin
- fields := array("Index") union fields;
- End;
- SetSheetRow(sheet, axis, fields);
- rowNum ++;
- End;
- if IncludeIndex then Begin
- for i:=0 to length(data)-1 do Begin
- [err, cell] := CoordinatesToCellName(colNum, rowNum + i);
- if err then
- return array(err, cell);
- SetCellValue(sheet, cell, i+1);
- End;
- colNum ++;
- End;
- for i:=0 to length(data)-1 do Begin
- j := 0;
- for k, v in data[i] do Begin
- [err, cell] := CoordinatesToCellName(colNum + j, rowNum + i);
- j++;
- ret := SetCellValue(sheet, cell, v);
- if not ret then return array(1, "error")
- End;
- End;
- return array(0, "OK");
- End;
-
- ///读取数据表
- ///sheet: string,工作表名称
- ///topLeft: string,左上角坐标,如: "A4"
- ///bottomRight: string,右下角坐标,如: "B8",为空获取从topLeft开始的整张表
- ///[IncludeHeader: bool] 是否包括表头,默认FALSE
- ///[IncludeIndex: bool] 是否包括索引号,默认FALSE
- ///返回: table
- Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
- Begin
- return workbook_.GetTable(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
- End;
-
- ///插入列,在指定列前插入空白列
- ///sheet: string,工作表名称
- ///col: string 列名,如: "D"
- Function InsertCol(sheet, col);
- Begin
- return workbook_.InsertCol(class(TSXml).CurCodePageToUtf8(sheet), col);
- End;
-
- ///插入行,在指定行前插入空白行
- ///sheet: string,工作表名称
- ///row: int
- Function InsertRow(sheet, row);
- Begin
- return workbook_.InsertRow(class(TSXml).CurCodePageToUtf8(sheet), row);
- End;
-
- ///删除列
- ///sheet: string,工作表名称
- ///col: string,如: "D"
- Function RemoveCol(sheet, col);
- Begin
- return workbook_.RemoveCol(class(TSXml).CurCodePageToUtf8(sheet), col);
- End;
-
- ///删除行
- ///sheet: string,工作表名称
- ///row: int
- Function RemoveRow(sheet, row);
- Begin
- return workbook_.RemoveRow(class(TSXml).CurCodePageToUtf8(sheet), row);
- End;
-
- ///设置行高度
- ///sheet: string,工作表
- ///row: int,行
- ///height: double,行高[0-409]
- Function SetRowHeight(sheet, row, height);
- Begin
- return workbook_.SetRowHeight(class(TSXml).CurCodePageToUtf8(sheet), row, height);
- End;
-
- ///获取行高度
- ///sheet: string,工作表
- ///row: int,行
- ///返回: double
- Function GetRowHeight(sheet, row);
- Begin
- return workbook_.GetRowHeight(class(TSXml).CurCodePageToUtf8(sheet), row);
- End;
-
- ///设置列宽度
- ///sheet: string,工作表
- ///startcol: string,开始列,如: "A"
- ///endcol: string,结束列,如: "D"
- ///width: double,列宽[0-255]
- Function SetColWidth(sheet, startCol, endCol, width);
- Begin
- return workbook_.SetColWidth(class(TSXml).CurCodePageToUtf8(sheet), startCol, endCol, width);
- End;
-
- ///获取列宽度
- ///sheet: string,工作表
- ///col: string,列
- ///返回: double
- Function GetColWidth(sheet, col);
- Begin
- return workbook_.GetColWidth(class(TSXml).CurCodePageToUtf8(sheet), col);
- End;
-
- ///设置工作表默认列宽
- ///sheet: string,工作表
- ///width: double
- Function SetSheetDefaultColWidth(sheet, width);
- Begin
- return workbook_.SetSheetDefaultColWidth(class(TSXml).CurCodePageToUtf8(sheet), width);
- End;
-
- ///获取工作表默认列宽
- ///sheet: string,工作表
- ///返回: double
- Function GetSheetDefaultColWidth(sheet);
- Begin
- return workbook_.GetSheetDefaultColWidth(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///添加批注
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///Author:string
- ///comment: string
- Function AddComment(sheet, axis, Author, comment);
- Begin
- o := getOj(sheet, 'xlsxComment');
- if ifObj(o) then return o.AddComment(axis, Author, comment);
- End;
-
- ///获取批注
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///返回: [author:string, comment: string]
- Function GetComment(sheet, axis);
- Begin
- author := nil;
- comment := nil;
- o := getOj(sheet, 'xlsxComment');
- if ifObj(o) then [author, comment] := o.GetComment(axis);
- return array(class(TSXml).Utf8ToCurCodePage(author), class(TSXml).Utf8ToCurCodePage(comment));
- End;
-
- ///移除批注
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- Function RemoveComment(sheet, axis);
- Begin
- o := getOj(sheet, 'xlsxComment');
- if ifObj(o) then return o.RemoveComment(axis);
- End;
-
- ///添加图表
- ///sheet: string,工作表名称
- ///range:string,图表所处矩形区域,"A5:F10"
- ///chart:TChart对象
- Function AddChart(sheet, range, chart);
- Begin
- o := getOj(sheet, 'xlsxChart');
- if ifObj(chart) then return o.AddChart(range, chart);
- End;
-
- ///获取图表列表
- ///sheet: string,工作表名称
- ///返回: [err, ChartList]
- Function GetCharts(sheet);
- Begin
- return workbook_.GetCharts(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///单元格坐标切分
- ///cell: string,单元格坐标
- ///返回: [err, col:string, row:int],"AK47" -> return array(0, "AK", 47);
- Function SplitCellName(cell);
- Begin
- return SplitCellName(cell);
- End;
-
- ///列名转索引
- ///name: string
- ///返回 [err, index:int],"AK" -> return array(0, 37);
- Function ColumnNameToNumber(name);
- Begin
- return ColumnNameToNumber(name);
- End;
-
- ///索引转列名
- ///index: int
- ///返回 [err, name:string],37 -> return array(0, "AK");
- Function ColumnNumberToName(index);
- Begin
- return ColumnNumberToName(index);
- End;
-
- ///单元格坐标转索引
- ///cell: string
- ///返回 [err, col:int, row: int] "A2" -> [1,2]
- Function CellNameToCoordinates(cell);
- Begin
- return CellNameToCoordinates(cell);
- End;
-
- ///索引转单元格坐标
- ///col: int
- ///row: int
- ///abs: bool ,true返回"$A$1"格式,false返回"A1"格式
- ///返回 [err, cell:string] [1,2,true] -> "$A$2"
- Function CoordinatesToCellName(col, row, abs);
- Begin
- return CoordinatesToCellName(col, row, abs);
- End;
-
- ///RGB与HSL色彩空间色值转换
- ///r: int
- ///g: int
- ///b: int
- ///返回: [h:double, s:double, l:double]
- Function RGBToHSL(r, g, b);
- Begin
- return RGBToHSL(r, g, b);
- End;
-
- ///HSL与RGB色彩空间色值转换
- ///h: double
- ///s: double
- ///l: double
- ///返回: [r:int, g:int, b:int]
- Function HSLToRGB(h, s, l);
- Begin
- return HSLToRGB(h, s, l);
- End;
-
- ///新建样式对象
- ///style: TStyle对象
- ///返回: styleid
- Function NewStyle(style); overload;
- Begin
- return style_.GetStyleId(style);
- End;
-
- ///新建样式对象,已存在样式的基础上返回新的样式Id
- ///style: TStyle对象
- ///oldStyleId: 已存在的styleId
- ///返回: styleid
- Function NewStyle(style, oldStyleId); overload;
- Begin
- return style_.GetStyleId(style, oldStyleId);
- End;
-
- ///获取样式对象
- ///返回: TStyle对象
- Function GetStyle(styleid);
- Begin
- return style_.GetStyle(styleid);
- End;
-
- ///设置单元格样式
- ///sheet: string,工作表名称
- ///topLeft: string,左上角坐标
- ///bottomRight: string,右下角坐标
- ///styleid: string,样式Id
- Function SetCellStyle(sheet, topLeft, bottomRight, styleid);
- Begin
- return workbook_.SetCellStyle(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight, styleid);
- End;
-
- ///获取单元格样式Id,获取到的Id可以在复制单元格样式时,作为调用 SetCellValue、或SetCellStyle 函数的参数使用。
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///返回: styleId: string
- Function GetCellStyle(sheet, axis);
- Begin
- return workbook_.GetCellStyle(class(TSXml).CurCodePageToUtf8(sheet), axis);
- End;
-
- ///设置工作表页眉页脚
- ///sheet: string,工作表名称
- ///headerFooter: THeaderFooter对象
- Function SetSheetHeaderFooter(sheet, headerFooter);
- Begin
- o := getOj(sheet, 'xlsxHeaderFooter');
- if ifObj(o) then o.SetHeaderFooter(headerFooter);
- End;
-
- ///设置工作表可见性
- ///sheet: string, 工作表名称
- ///visible: boolean
- Function SetSheetVisible(sheet, visible);
- Begin
- return workbook_.SetSheetVisible(class(TSXml).CurCodePageToUtf8(sheet), visible);
- End;
-
- ///获取工作表可见性
- ///sheet: string,工作表名称
- ///visibility: boolean
- Function GetSheetVisible(sheet);
- Begin
- return workbook_.GetSheetVisible(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///设置行可见性
- ///sheet: string,工作表
- ///row: int,行
- ///visible: boolean
- Function SetRowVisible(sheet, row, visible)
- Begin
- return workbook_.SetRowVisible(class(TSXml).CurCodePageToUtf8(sheet), row, visible);
- End;
-
- ///获取工作表行可见性
- ///sheet: string,工作表名称
- ///row: int, 行
- ///返回: [err, visible:boolean]
- Function GetRowVisble(sheet, row);
- Begin
- return workbook_.GetRowVisble(class(TSXml).CurCodePageToUtf8(sheet), row);
- End;
-
- ///设置列可见性
- ///sheet: string,工作表
- ///col: string,列,如:'A'
- ///visible: boolean
- Function SetColVisible(sheet, col, visible)
- Begin
- [err, new_col] := ColumnNameToNumber(col);
- if err then return "Col error.";
- return workbook_.SetColVisible(class(TSXml).CurCodePageToUtf8(sheet), new_col, visible);
- End;
-
- ///获取列可见性
- ///sheet: string,工作表名称
- ///col: string,列
- ///返回: [err, visible:int]
- Function GetColVisble(sheet, col);
- Begin
- [err, new_col] := ColumnNameToNumber(col);
- if err then return array(-1, col);
- return workbook_.GetColVisble(class(TSXml).CurCodePageToUtf8(sheet), new_col);
- End;
-
- ///设置工作表页边距
- ///sheet: string,工作表名称
- ///margins: TMargins 对象
- Function SetPageMargins(sheet, margins);
- Begin
- return workbook_.SetPageMargins(class(TSXml).CurCodePageToUtf8(sheet), margins);
- End;
-
- ///获取工作表页边距
- ///sheet: string,工作表名称
- ///返回: TMargins对象
- Function GetPageMargins(sheet);
- Begin
- return workbook_.GetPageMargins(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///合并单元格
- ///sheet: string,工作表名称
- ///hcell: string,左上角坐标
- ///vcell: string,右下角坐标
- Function MergeCell(sheet, hcell, vcell);
- Begin
- return workbook_.MergeCell(class(TSXml).CurCodePageToUtf8(sheet), hcell, vcell);
- End;
-
- ///取消合并单元格
- ///sheet: string,工作表名称
- ///hcell: string,左上角坐标
- ///vcell: string,右下角坐标
- Function UnMergeCell(sheet, hcell, vcell);
- Begin
- return workbook_.UnMergeCell(class(TSXml).CurCodePageToUtf8(sheet), hcell, vcell);
- End;
-
- ///新建窗口
- ///返回:windowsIndex int, 窗口索引
- Function NewSheetPane();
- Begin
- return workbook_.NewSheetView();
- End;
-
- ///设置窗格
- ///sheet: string,工作表名称
- ///windowsIndex: int,窗口索引,从0开始
- ///Pane: TPane对象
- Function SetPane(sheet, windowsIndex, Pane);
- Begin
- o := getOj(sheet, 'xlsxSheetView');
- if ifObj(o) then return o.SetPane(windowsIndex, Pane);
- End;
-
- ///设置工作表视图属性
- ///sheet: string,工作表名称
- ///windowsIndex: int,窗口索引,从0开始
- ///sheet: object,TSheetView对象
- Function SetSheetViewOptions(sheet, windowsIndex, sheetView);
- Begin
- o := getOj(sheet, 'xlsxSheetView');
- if ifObj(o) then return o.SetSheetViewOptions(windowsIndex, sheetView);
- End;
-
- ///获取工作表视图属性
- ///sheet: string,工作表名称
- ///windowsIndex: int,窗口索引,从0开始
- ///返回: object, TSheetView对象
- Function GetSheetViewOptions(sheet, windowsIndex);
- Begin
- o := getOj(sheet, 'xlsxSheetView');
- if ifObj(o) then return o.GetSheetViewOptions(windowsIndex);
- End;
-
- // TODO printerSettings1.bin?
- ///设置工作表页面布局
- ///sheet: string,工作表名称
- ///pageLayout: TPageLayout对象,属性:
- Function SetPageLayout(sheet, pageLayout);
- Begin
- o := getOj(sheet, 'xlsxPageLayout');
- if ifObj(o) then return o.SetPageLayout(class(TSXml).CurCodePageToUtf8(sheet), pageLayout);
- End;
-
- ///获取工作表页面布局
- ///sheet: string,工作表名称
- ///返回: TPageLayout对象
- Function GetPageLayout(sheet);
- Begin
- o := getOj(sheet, 'xlsxPageLayout');
- if ifObj(o) then return o.GetPageLayout();
- return "error sheet";
- End;
-
- ///设置默认工作表
- ///sheet: string,工作表名称
- Function SetDefaultSheet(sheet);
- Begin
- return workbook_.SetDefaultSheet(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///获取默认工作表
- ///返回: string,工作表名称
- Function GetDefaultSheet();
- Begin
- sheet := workbook_.GetDefaultSheet();
- return class(TSXml).Utf8ToCurCodePage(sheet);
- End;
-
- ///设置超链接
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///hyplink: THyperLink对象
- Function SetCellHyperLink(sheet, axis, hyperlink);
- Begin
- o := getOj(sheet, 'xlsxHyperLink');
- if ifObj(o) then return o.SetCellHyperLink(axis, hyperlink);
- End;
-
- // TODO 返回的结果有点问题
- ///获取超链接
- ///sheet: string,工作表名称
- ///axis: string,单元格,如"A7"
- ///返回: THyperLink对象
- Function GetCellHyperLink(sheet, axis);
- Begin
- o := getOj(sheet, 'xlsxHyperLink');
- if not ifObj(o) then return array(1, 'The sheet is not exist.');
- return o.GetCellHyperLink(axis);
- End;
-
- ///设置工作表背景图片
- ///picture: TPicture图片对象
- Function SetSheetBackground(sheet, picture);
- Begin
- o := getOj(sheet, 'xlsxImage');
- if ifObj(o) then return o.SetSheetBackground(picture);
- End;
-
- ///添加图片
- ///sheet: string, 工作表名称
- ///topLeft: string, 图片插入左上角单元格
- ///bottomRight: string, 图片插入右下角单元格
- ///picture: TPicture 图片对象
- ///format: TPictureFormat 图片设置对象
- Function AddPicture(sheet, topLeft, bottomRight, picture, format);
- Begin
- o := getOj(sheet, 'xlsxImage');
- if ifObj(o) then return o.AddPicture(topLeft, bottomRight, picture, format);
- End;
-
- ///删除图片
- ///sheet: 工作表名称
- ///topLeft: string, 图片插入左上角单元格
- ///bottomRight: string, 图片插入右下角单元格
- Function DeletePicture(sheet, topLeft, bottomRight);
- Begin
- o := getOj(sheet, 'xlsxImage');
- if ifObj(o) then return o.DeletePicture(topLeft, bottomRight);
- End;
-
- ///创建表格
- ///sheet: string,工作表名称
- ///topLeft: string, 左上角单元格
- ///bottomRight: string, 右下角单元格
- ///tableStyle: 表格选项类 TTableStyle
- ///[includeHeader: bool] 是否包括表头,默认FALSE
- Function AddTable(sheet, topLeft, bottomRight, tableStyle, includeHeader);
- Begin
- o := getOj(sheet, 'xlsxTable');
- if ifObj(o) then return o.AddTable(topLeft, bottomRight, tableStyle, includeHeader);
- End;
-
- ///指定行前插入分页符
- ///sheet: string,工作表名称
- ///row: int,行号
- Function InsertPageBreak(sheet, row);
- Begin
- return workbook_.InsertPageBreak(class(TSXml).CurCodePageToUtf8(sheet), row);
- End;
-
- ///删除指定行分页符
- ///sheet: string,工作表名称
- ///row: int,行号
- Function RemovePageBreak(sheet, row);
- Begin
- return workbook_.RemovePageBreak(class(TSXml).CurCodePageToUtf8(sheet), row);
- End;
-
- ///添加形状
- ///sheet: string,工作表名称
- ///topLeft: string, 左上角单元格
- ///bottomRight: string, 右下角单元格
- ///shapeType: string, 形状类型
- ///format: 形状设置,TShapeFormat类
- Function AddShape(sheet, topLeft, bottomRight, shapeType, format);
- Begin
- o := getOj(sheet, 'xlsxShape');
- if ifObj(o) then return o.AddShape(topLeft, bottomRight, shapeType, format);
- End;
-
- ///设置工作簿的核心属性
- ///coreProps: TCoreProperty对象
- Function SetCoreProps(coreProps);
- Begin
- o := getOj('', 'xlsxDocProps');
- if ifObj(o) then return o.SetCoreProps(coreProps);
- End;
-
- ///获取工作簿的核心属性
- ///返回: TCoreProperty对象
- Function GetCoreProps();
- Begin
- o := getOj('', 'xlsxDocProps');
- if ifObj(o) then return o.GetCoreProps();
- End;
-
- ///设置工作簿应用程序属性
- ///appProps: TAppProperty对象,属性:
- Function SetAppProps(appProps);
- Begin
- o := getOj('', 'xlsxDocProps');
- if ifObj(o) then return o.SetAppProps(appProps);
- End;
-
- ///获取应用程序属性
- ///返回: TAppProperty对象
- Function GetAppProps();
- Begin
- o := getOj('', 'xlsxDocProps');
- return o.GetAppProps();
- End;
-
- ///复制Sheet
- ///sourceSheet: 源工作表
- ///destSheet: 目的工作表,如果destSheet存在,则覆盖destSheet,否则末尾新增destSheet
- Function CopySheet(sourceSheet, destSheet);
- Begin
- return workbook_.CopySheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet));
- End;
-
- ///保护工作表
- ///sheet: 工作表
- ///protect: TProtect对象
- Function ProtectSheet(sheet, protect);
- Begin
- return workbook_.ProtectSheet(class(TSXml).CurCodePageToUtf8(sheet), protect);
- End;
-
- ///取消保护工作
- ///sheet: 工作表
- Function UnProtectSheet(sheet);
- Begin
- return workbook_.UnProtectSheet(class(TSXml).CurCodePageToUtf8(sheet));
- End;
-
- ///设置默认字体
- ///font: TFont对象
- Function SetDefaultFont(font);
- Begin
- return workbook_.SetDefaultFont(font);
- End;
-
- ///获取默认字体
- ///返回: TFont对象
- Function GetDefaultFont();
- Begin
- return workbook_.GetDefaultFont();
- End;
-
- ///设置工作簿计算选项
- ///calcPr: TCalcPr对象
- Function SetCalcOptions(calcPr);
- Begin
- return workbook_.SetCalcOptions(calcPr);
- End;
-
- ///获取工作簿计算选项
- ///返回: TCalcPr对象
- Function GetCalcOptions();
- Begin
- return workbook_.GetCalcOptions();
- End;
-
- ///创建行的分级显示,根据给定的工作表、行号和分级参数创建组
- ///sheet: string, 工作表名称
- ///row: int, 行号
- ///level: int, 分级参数
- Function SetRowOutlineLevel(sheet, row, level);
- Begin
- return workbook_.SetRowOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), row, level);
- End;
-
- ///获取行的分级参数
- ///sheet: string, 工作表名称
- ///row: int, 行号
- ///返回: int,分级参数,分级不存在返回0
- Function GetRowOutlineLevel(sheet, row);
- Begin
- return workbook_.GetRowOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), row);
- End;
-
- ///创建列的分级显示,根据给定的工作表、列名和分级参数创建组
- ///sheet: string, 工作表名称
- ///col: string, 列名
- ///level: int, 分级参数
- Function SetColOutlineLevel(sheet, col, level);
- Begin
- [err, col] := ColumnNameToNumber(col);
- if err then return "Col error.";
- return workbook_.SetColOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), col, level);
- End;
-
- ///获取列的分级参数
- ///sheet: string, 工作表名称
- ///col: string, 列名
- ///返回: int,分级参数,分级不存在返回0
- Function GetColOutlineLevel(sheet, col);
- Begin
- [err, col] := ColumnNameToNumber(col);
- if err then return "Col error.";
- return workbook_.GetColOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), col);
- End;
-
- Function WorkBook();
- Begin
- return workbook_;
- End;
-
- Function Zip();
- Begin
- return zipfile_;
- End;
-
-private
- Function InitVars();
- Begin
- workbook_ := new xlsxWorkBook(zipfile_);
- workbook_.Load();
- style_ := new xlsxStyles(self);
- End;
-
- Function SetCellType(sheet, axis, val, opt);
- Begin
- if not ifnil(opt['t']) then return;
- styleid := opt['s'] ? opt['s'] : GetCellStyle(sheet, axis);
- [t, val] := style_.GetType(styleid, val);
- if t then opt['t'] := t;
- End;
-
- Function getOj(sheet, objname);
- Begin
- sheetname := class(TSXml).CurCodePageToUtf8(sheet);
- if not ifarray(objMgr_) then objMgr_:= array();
- k := sheetname + '.' + objname;
- o := objMgr_[ k ];
- if not ifnil(o) then return o;
- case objname of
- 'xlsxComment':
- o := class(xlsxComment).NewObject(sheetname, self);
- 'xlsxChart':
- return class(xlsxChart).NewObject(sheetname, self);//不缓存xlsxChart对象
- 'xlsxHeaderFooter':
- o := class(xlsxHeaderFooter).NewObject(sheetname, self);
- 'xlsxSheetView':
- o := class(xlsxSheetView).NewObject(sheetname, self);
- 'xlsxPageLayout':
- o := class(xlsxPageLayout).NewObject(sheetname, self);
- 'xlsxDocProps':
- return class(xlsxDocProps).NewObject(self);
- 'xlsxImage':
- o := class(xlsxImage).NewObject(sheetname, self);
- 'xlsxHyperLink':
- o := class(xlsxHyperLink).NewObject(sheetname, self);
- 'xlsxShape':
- o := class(xlsxShape).NewObject(sheetname, self);
- 'xlsxTable':
- o := class(xlsxTable).NewObject(sheetname, self);
- End;
- if ifObj(o) then objMgr_[k] := o;
- return o;
- End;
-
- zipfile_; //压缩文件对象
- workbook_; //WorkBook对象
- objMgr_; //各种对象缓存、管理
- style_;
-End;
-
+// Version 1.8.2
+Type TSXlsxFile = Class
+ ///Version: V1.0 2022-08-08
+ ///适用于 Microsoft Excel? 2007 及以上版本创建的电子表格文档。支持 XLSX / XLSM / XLTM / XLTX 等多种文档格式。
+ ///纯TSL模块实现
+ ///Excel文件读写接口
+
+ ///缺省构造函数
+ Function Create(); overload;
+ Begin
+ Init();
+ End;
+
+ ///构造函数,打开已经存在的excel文件
+ ///alias: string,文件目录别名
+ ///fname: string,文件名
+ Function Create(alias, fname); overload;
+ Begin
+ Init();
+ OpenFile(alias, fname, nil);
+ End;
+
+ ///构造函数,打开已经存在的excel文件
+ ///alias: string,文件目录别名
+ ///fname: string,文件名
+ ///passwd: string,密码
+ Function Create(alias, fname, passwd); overload;
+ Begin
+ Init();
+ OpenFile(alias, fname, passwd);
+ End;
+
+ //初始化
+ Function Init();
+ Begin
+ zipfile_ := new ZipFile();
+ End;
+
+ ///打开excel文件
+ ///alias: string,文件目录别名
+ ///fname: string,文件名
+ ///[passwd]: string,密码
+ ///返回: [err, errmsg]
+ Function OpenFile(alias, fname, passwd);
+ Begin
+ if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
+ if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile();
+ [err, errmsg] := zipfile_.Open(alias, TOfficeApi().CurCodePageToGBK(fname), passwd);
+ if err=0 then InitVars();
+ return array(err, errmsg);
+ End;
+
+ ///新建excel文件
+ ///返回: [err, errmsg]
+ Function NewFile();
+ Begin
+ def := TOfficeTemplate('default.xlsx', true);
+ [err, errmsg] := zipfile_.LoadFromMem(def);
+ if err = 0 then InitVars();
+ return array(err, errmsg);
+ End;
+
+ ///保存文件
+ ///返回: [err, info]
+ Function Save();
+ Begin
+ return zipfile_.Save();
+ End;
+
+ ///设置密码
+ Function SetPassword(passwd)
+ Begin
+ zipfile_.Password := passwd;
+ End;
+
+ ///打开二进制内容
+ ///data: 二进制数据
+ ///返回: [err, errmsg]
+ Function LoadFromMem(data);
+ Begin
+ [err, errmsg] := zipfile_.LoadFromMem(data);
+ if err = 0 then InitVars();
+ return array(err, errmsg);
+ End;
+
+ ///保存为二进制内容
+ ///返回: [err, fileContent] fileContent二进制文件内容
+ Function SaveToMem();
+ Begin
+ return zipfile_.Save2Mem();
+ End;
+
+ ///另存为
+ ///alias: string,文件目录别名
+ ///fname: string,文件名
+ ///返回: [err, info]
+ Function SaveAs(alias, fname);
+ Begin
+ return zipfile_.Save(alias, TOfficeApi().CurCodePageToGBK(fname));
+ End;
+
+ ///真实文件名
+ ///返回: string
+ Function FileName();
+ Begin
+ file_name := zipfile_.FileName();
+ if TOfficeApi().IsUtf8() then
+ return AnsiToUTF8(file_name);
+ return file_name;
+ End;
+
+ ///获取工作表列表
+ ///返回: array('Sheet1','Sheet2')
+ Function GetSheets();
+ Begin
+ sheets := workbook_.GetSheets();
+ for i:=0 to length(sheets)-1 do
+ sheets[i] := class(TSXml).Utf8ToCurCodePage(sheets[i]);
+ return sheets;
+ End;
+
+ ///获取工作表数
+ ///返回: integer
+ Function GetSheetsCount();
+ Begin
+ return workbook_.GetSheetsCount();
+ End;
+
+ ///获取工作表名
+ ///index: int,工作表索引
+ ///返回: string
+ Function GetSheetName(index);
+ Begin
+ name := workbook_.GetSheetName(index);
+ return class(TSXml).Utf8ToCurCodePage(name);
+ End;
+
+ ///创建新sheet
+ ///sheet: string,工作表名称
+ Function NewSheet(sheet);overload;
+ Begin
+ return workbook_.NewSheet(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///在指定sheet之后插入新sheet
+ ///sourceSheet: string,指定工作表名称
+ ///destSheet: string,目的工作表名称
+ Function NewSheet(sourceSheet, destSheet);overload;
+ Begin
+ return workbook_.NewSheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet));
+ End;
+
+ ///在指定sheet之前插入新sheet
+ ///sourceSheet: string,指定工作表名称
+ ///destSheet: string,目的工作表名称
+ Function InsertSheet(sourceSheet, destSheet);
+ Begin
+ return workbook_.InsertSheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet));
+ End;
+
+ ///删除sheet
+ ///sheet: string,工作表名称
+ Function DeleteSheet(sheet);
+ Begin
+ return workbook_.DeleteSheet(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///设置工作表名
+ ///sourceName: string, 原工作表名
+ ///destName: string, 目标工作表名
+ Function SetSheetName(sourceName, destName);
+ Begin
+ return workbook_.SetSheetName(class(TSXml).CurCodePageToUtf8(sourceName), class(TSXml).CurCodePageToUtf8(destName));
+ End;
+
+ ///获取总列数
+ ///sheet: string,工作表名称
+ ///返回: int
+ Function TotalCols(sheet);
+ Begin
+ return workbook_.TotalCols(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///获取总行数
+ ///sheet: string,工作表名称
+ ///返回: int
+ Function TotalRows(sheet);
+ Begin
+ return workbook_.TotalRows(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///获取单元格的值
+ ///sheet: string,工作表名称
+ ///axis: string,单元格坐标,如: "A6"
+ ///返回: [err, value:string]
+ Function GetCellValue(sheet, axis);
+ Begin
+ [err, value] := workbook_.GetCellValue(class(TSXml).CurCodePageToUtf8(sheet), axis);
+ if not err then return array(err, class(TSXml).Utf8ToCurCodePage(value));
+ return array(err, value);
+ End;
+
+ ///获取单元格数据类型
+ ///sheet: string,工作表名称
+ ///axis: string,单元格坐标,如: "A6"
+ ///返回: type:string
+ Function GetCellValueType(sheet, axis);
+ Begin
+ return workbook_.GetCellValueType(class(TSXml).CurCodePageToUtf8(sheet), axis);
+ End;
+
+ ///设置单元格的值
+ ///sheet: string,工作表名称
+ ///axis: string,单元格坐标,如: "A6"
+ ///val: Var,可以是整数、字符串、数值型等;nil 不设置值(可以调用ClearCell方法清空单元格),只设置属性
+ ///[opt:Tany],可选参数,单元格属性,可以是xml字符串或天软数组
+ /// xml串:opt := '';
+ /// 数组: opt := array('t':'s', 's':'1');
+ /// 属性t:单元格数据类型,不设置的话,数据类型为val的数据类型;
+ /// t ->('s' 共享字符串、'b' bool 类型、'e' 错误类型、'inlineStr' 内联字符串类型、nil 数字类型、'str' 公式类型)
+ /// 属性s:单元格样式
+ Function SetCellValue(sheet, axis, val, opt);
+ Begin
+ sheet_name := class(TSXml).CurCodePageToUtf8(sheet);
+ value := class(TSXml).CurCodePageToUtf8(val);
+ // 处理cell设定的样式
+ if not ifarray(opt) then opt := array();
+ workbook_.SetCellStyleOpt(sheet_name, axis, opt);
+ SetCellType(sheet_name, axis, value, opt);
+ return workbook_.SetCellValue(sheet_name, axis, value, opt);
+ End;
+
+ ///获取富文本格式
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///返回: [err, richtxt:string],参见SetCellRichText
+ Function GetCellRichText(sheet, axis);
+ Begin
+ [err, str] := workbook_.GetCellRichText(class(TSXml).CurCodePageToUtf8(sheet), axis);
+ if err then return array(err, str);
+ else return array(err, class(TSXml).Utf8ToCurCodePage(str));
+ End;
+
+ ///设置富文本格式
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///richtext: string,xml串或富文本对象TSOfficeObj('TRichText')
+ Function SetCellRichText(sheet, axis, richtext);
+ Begin
+ o := workbook_.GetSheetObj(class(TSXml).CurCodePageToUtf8(sheet));
+ if ifObj(o) then
+ begin
+ xml := richtext;
+ if ifObj(richtext) then
+ xml := class(TSXml).Dom2Xml(richtext.Marshal());
+ return o.SetCellValue(axis, xml, array('t':'s'), 'RichText');
+ end
+ End;
+
+ ///清空单元格
+ ///sheet: string,工作表名称
+ ///[topLeft: string],左上角坐标,可选参数
+ ///[bottomRight: string],右下角坐标,可选参数,
+ /// 用法1:ClearCell(); //Clear整个sheet
+ /// 用法2:ClearCell('A2'); //Clear 'A2'单元格
+ /// 用法3:ClearCell('A5', 'D8'); //Clear矩形区间所有单元格
+ Function ClearCell(sheet, topLeft, bottomRight);
+ Begin
+ return workbook_.ClearCell(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight);
+ End;
+
+ ///设置公式
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///formula: string,公式
+ Function SetCellFormula(sheet, axis, formula);
+ Begin
+ return workbook_.SetCellFormula(class(TSXml).CurCodePageToUtf8(sheet), axis, class(TSXml).CurCodePageToUtf8(formula));
+ End;
+
+ ///获取公式
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///返回: [err, formula:string]
+ Function GetCellFormula(sheet, axis);
+ Begin
+ return class(TSXml).Utf8ToCurCodePage(workbook_.GetCellFormula(class(TSXml).CurCodePageToUtf8(sheet), axis));
+ End;
+
+ ///按行赋值
+ ///sheet: string,工作表名称
+ ///axis: string,起始坐标,如: "A4"
+ ///slice: array(),一维数组array(1,2,3,2,1,"hello")
+ Function SetSheetRow(sheet, axis, slice);
+ Begin
+ [err, col, row] := CellNameToCoordinates(axis);
+ if not err then
+ begin
+ for i:=0 to length(slice)-1 do
+ begin
+ [err, cell] := CoordinatesToCellName(col + i, row);
+ SetCellValue(sheet, cell, slice[i]);
+ end
+ end
+ End;
+
+ ///插入数据表
+ ///sheet: string,工作表名称
+ ///axis: string,左上角坐标,如: "A4"
+ ///data: table,数据表
+ ///[IncludeHeader: bool] 是否包括表头,默认FALSE
+ ///[IncludeIndex: bool] 是否自动添加索引号,默认FALSE
+ ///返回: [err, info]
+ Function InsertTable(sheet, axis, data, IncludeHeader, IncludeIndex);
+ Begin
+ if not ifarray(data) or length(data)=0 then
+ return array(-1, "Invalid Data.");
+ if FieldCount(data)=0 then Begin //数据是一维数组
+ return SetSheetRow(sheet, axis, data);
+ End;
+ [err, colNum, rowNum] := CellNameToCoordinates(axis);
+ if err then
+ return array(err, colNum);
+ if IncludeHeader then Begin
+ fields := FieldNames(data);
+ if IncludeIndex then Begin
+ fields := array("Index") union fields;
+ End;
+ SetSheetRow(sheet, axis, fields);
+ rowNum ++;
+ End;
+ if IncludeIndex then Begin
+ for i:=0 to length(data)-1 do Begin
+ [err, cell] := CoordinatesToCellName(colNum, rowNum + i);
+ if err then
+ return array(err, cell);
+ SetCellValue(sheet, cell, i+1);
+ End;
+ colNum ++;
+ End;
+ for i:=0 to length(data)-1 do Begin
+ j := 0;
+ for k, v in data[i] do Begin
+ [err, cell] := CoordinatesToCellName(colNum + j, rowNum + i);
+ j++;
+ ret := SetCellValue(sheet, cell, v);
+ if not ret then return array(1, "error")
+ End;
+ End;
+ return array(0, "OK");
+ End;
+
+ ///读取数据表
+ ///sheet: string,工作表名称
+ ///topLeft: string,左上角坐标,如: "A4"
+ ///bottomRight: string,右下角坐标,如: "B8",为空获取从topLeft开始的整张表
+ ///[IncludeHeader: bool] 是否包括表头,默认FALSE
+ ///[IncludeIndex: bool] 是否包括索引号,默认FALSE
+ ///返回: table
+ Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
+ Begin
+ return workbook_.GetTable(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
+ End;
+
+ ///插入列,在指定列前插入空白列
+ ///sheet: string,工作表名称
+ ///col: string 列名,如: "D"
+ Function InsertCol(sheet, col);
+ Begin
+ return workbook_.InsertCol(class(TSXml).CurCodePageToUtf8(sheet), col);
+ End;
+
+ ///插入行,在指定行前插入空白行
+ ///sheet: string,工作表名称
+ ///row: int
+ Function InsertRow(sheet, row);
+ Begin
+ return workbook_.InsertRow(class(TSXml).CurCodePageToUtf8(sheet), row);
+ End;
+
+ ///删除列
+ ///sheet: string,工作表名称
+ ///col: string,如: "D"
+ Function RemoveCol(sheet, col);
+ Begin
+ return workbook_.RemoveCol(class(TSXml).CurCodePageToUtf8(sheet), col);
+ End;
+
+ ///删除行
+ ///sheet: string,工作表名称
+ ///row: int
+ Function RemoveRow(sheet, row);
+ Begin
+ return workbook_.RemoveRow(class(TSXml).CurCodePageToUtf8(sheet), row);
+ End;
+
+ ///设置行高度
+ ///sheet: string,工作表
+ ///row: int,行
+ ///height: double,行高[0-409]
+ Function SetRowHeight(sheet, row, height);
+ Begin
+ return workbook_.SetRowHeight(class(TSXml).CurCodePageToUtf8(sheet), row, height);
+ End;
+
+ ///获取行高度
+ ///sheet: string,工作表
+ ///row: int,行
+ ///返回: double
+ Function GetRowHeight(sheet, row);
+ Begin
+ return workbook_.GetRowHeight(class(TSXml).CurCodePageToUtf8(sheet), row);
+ End;
+
+ ///设置列宽度
+ ///sheet: string,工作表
+ ///startcol: string,开始列,如: "A"
+ ///endcol: string,结束列,如: "D"
+ ///width: double,列宽[0-255]
+ Function SetColWidth(sheet, startCol, endCol, width);
+ Begin
+ return workbook_.SetColWidth(class(TSXml).CurCodePageToUtf8(sheet), startCol, endCol, width);
+ End;
+
+ ///获取列宽度
+ ///sheet: string,工作表
+ ///col: string,列
+ ///返回: double
+ Function GetColWidth(sheet, col);
+ Begin
+ return workbook_.GetColWidth(class(TSXml).CurCodePageToUtf8(sheet), col);
+ End;
+
+ ///设置工作表默认列宽
+ ///sheet: string,工作表
+ ///width: double
+ Function SetSheetDefaultColWidth(sheet, width);
+ Begin
+ return workbook_.SetSheetDefaultColWidth(class(TSXml).CurCodePageToUtf8(sheet), width);
+ End;
+
+ ///获取工作表默认列宽
+ ///sheet: string,工作表
+ ///返回: double
+ Function GetSheetDefaultColWidth(sheet);
+ Begin
+ return workbook_.GetSheetDefaultColWidth(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///添加批注
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///Author:string
+ ///comment: string
+ Function AddComment(sheet, axis, Author, comment);
+ Begin
+ o := getOj(sheet, 'xlsxComment');
+ if ifObj(o) then return o.AddComment(axis, Author, comment);
+ End;
+
+ ///获取批注
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///返回: [author:string, comment: string]
+ Function GetComment(sheet, axis);
+ Begin
+ author := nil;
+ comment := nil;
+ o := getOj(sheet, 'xlsxComment');
+ if ifObj(o) then [author, comment] := o.GetComment(axis);
+ return array(class(TSXml).Utf8ToCurCodePage(author), class(TSXml).Utf8ToCurCodePage(comment));
+ End;
+
+ ///移除批注
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ Function RemoveComment(sheet, axis);
+ Begin
+ o := getOj(sheet, 'xlsxComment');
+ if ifObj(o) then return o.RemoveComment(axis);
+ End;
+
+ ///添加图表
+ ///sheet: string,工作表名称
+ ///range:string,图表所处矩形区域,"A5:F10"
+ ///chart:TChart对象
+ Function AddChart(sheet, range, chart);
+ Begin
+ o := getOj(sheet, 'xlsxChart');
+ if ifObj(chart) then return o.AddChart(range, chart);
+ End;
+
+ ///获取图表列表
+ ///sheet: string,工作表名称
+ ///返回: [err, ChartList]
+ Function GetCharts(sheet);
+ Begin
+ return workbook_.GetCharts(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///单元格坐标切分
+ ///cell: string,单元格坐标
+ ///返回: [err, col:string, row:int],"AK47" -> return array(0, "AK", 47);
+ Function SplitCellName(cell);
+ Begin
+ return SplitCellName(cell);
+ End;
+
+ ///列名转索引
+ ///name: string
+ ///返回 [err, index:int],"AK" -> return array(0, 37);
+ Function ColumnNameToNumber(name);
+ Begin
+ return ColumnNameToNumber(name);
+ End;
+
+ ///索引转列名
+ ///index: int
+ ///返回 [err, name:string],37 -> return array(0, "AK");
+ Function ColumnNumberToName(index);
+ Begin
+ return ColumnNumberToName(index);
+ End;
+
+ ///单元格坐标转索引
+ ///cell: string
+ ///返回 [err, col:int, row: int] "A2" -> [1,2]
+ Function CellNameToCoordinates(cell);
+ Begin
+ return CellNameToCoordinates(cell);
+ End;
+
+ ///索引转单元格坐标
+ ///col: int
+ ///row: int
+ ///abs: bool ,true返回"$A$1"格式,false返回"A1"格式
+ ///返回 [err, cell:string] [1,2,true] -> "$A$2"
+ Function CoordinatesToCellName(col, row, abs);
+ Begin
+ return CoordinatesToCellName(col, row, abs);
+ End;
+
+ ///RGB与HSL色彩空间色值转换
+ ///r: int
+ ///g: int
+ ///b: int
+ ///返回: [h:double, s:double, l:double]
+ Function RGBToHSL(r, g, b);
+ Begin
+ return RGBToHSL(r, g, b);
+ End;
+
+ ///HSL与RGB色彩空间色值转换
+ ///h: double
+ ///s: double
+ ///l: double
+ ///返回: [r:int, g:int, b:int]
+ Function HSLToRGB(h, s, l);
+ Begin
+ return HSLToRGB(h, s, l);
+ End;
+
+ ///新建样式对象
+ ///style: TStyle对象
+ ///返回: styleid
+ Function NewStyle(style); overload;
+ Begin
+ return style_.GetStyleId(style);
+ End;
+
+ ///新建样式对象,已存在样式的基础上返回新的样式Id
+ ///style: TStyle对象
+ ///oldStyleId: 已存在的styleId
+ ///返回: styleid
+ Function NewStyle(style, oldStyleId); overload;
+ Begin
+ return style_.GetStyleId(style, oldStyleId);
+ End;
+
+ ///获取样式对象
+ ///返回: TStyle对象
+ Function GetStyle(styleid);
+ Begin
+ return style_.GetStyle(styleid);
+ End;
+
+ ///设置单元格样式
+ ///sheet: string,工作表名称
+ ///topLeft: string,左上角坐标
+ ///bottomRight: string,右下角坐标
+ ///styleid: string,样式Id
+ Function SetCellStyle(sheet, topLeft, bottomRight, styleid);
+ Begin
+ return workbook_.SetCellStyle(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight, styleid);
+ End;
+
+ ///获取单元格样式Id,获取到的Id可以在复制单元格样式时,作为调用 SetCellValue、或SetCellStyle 函数的参数使用。
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///返回: styleId: string
+ Function GetCellStyle(sheet, axis);
+ Begin
+ return workbook_.GetCellStyle(class(TSXml).CurCodePageToUtf8(sheet), axis);
+ End;
+
+ ///设置工作表页眉页脚
+ ///sheet: string,工作表名称
+ ///headerFooter: THeaderFooter对象
+ Function SetSheetHeaderFooter(sheet, headerFooter);
+ Begin
+ o := getOj(sheet, 'xlsxHeaderFooter');
+ if ifObj(o) then o.SetHeaderFooter(headerFooter);
+ End;
+
+ ///设置工作表可见性
+ ///sheet: string, 工作表名称
+ ///visible: boolean
+ Function SetSheetVisible(sheet, visible);
+ Begin
+ return workbook_.SetSheetVisible(class(TSXml).CurCodePageToUtf8(sheet), visible);
+ End;
+
+ ///获取工作表可见性
+ ///sheet: string,工作表名称
+ ///visibility: boolean
+ Function GetSheetVisible(sheet);
+ Begin
+ return workbook_.GetSheetVisible(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///设置行可见性
+ ///sheet: string,工作表
+ ///row: int,行
+ ///visible: boolean
+ Function SetRowVisible(sheet, row, visible)
+ Begin
+ return workbook_.SetRowVisible(class(TSXml).CurCodePageToUtf8(sheet), row, visible);
+ End;
+
+ ///获取工作表行可见性
+ ///sheet: string,工作表名称
+ ///row: int, 行
+ ///返回: [err, visible:boolean]
+ Function GetRowVisble(sheet, row);
+ Begin
+ return workbook_.GetRowVisble(class(TSXml).CurCodePageToUtf8(sheet), row);
+ End;
+
+ ///设置列可见性
+ ///sheet: string,工作表
+ ///col: string,列,如:'A'
+ ///visible: boolean
+ Function SetColVisible(sheet, col, visible)
+ Begin
+ [err, new_col] := ColumnNameToNumber(col);
+ if err then return "Col error.";
+ return workbook_.SetColVisible(class(TSXml).CurCodePageToUtf8(sheet), new_col, visible);
+ End;
+
+ ///获取列可见性
+ ///sheet: string,工作表名称
+ ///col: string,列
+ ///返回: [err, visible:int]
+ Function GetColVisble(sheet, col);
+ Begin
+ [err, new_col] := ColumnNameToNumber(col);
+ if err then return array(-1, col);
+ return workbook_.GetColVisble(class(TSXml).CurCodePageToUtf8(sheet), new_col);
+ End;
+
+ ///设置工作表页边距
+ ///sheet: string,工作表名称
+ ///margins: TMargins 对象
+ Function SetPageMargins(sheet, margins);
+ Begin
+ return workbook_.SetPageMargins(class(TSXml).CurCodePageToUtf8(sheet), margins);
+ End;
+
+ ///获取工作表页边距
+ ///sheet: string,工作表名称
+ ///返回: TMargins对象
+ Function GetPageMargins(sheet);
+ Begin
+ return workbook_.GetPageMargins(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///合并单元格
+ ///sheet: string,工作表名称
+ ///hcell: string,左上角坐标
+ ///vcell: string,右下角坐标
+ Function MergeCell(sheet, hcell, vcell);
+ Begin
+ return workbook_.MergeCell(class(TSXml).CurCodePageToUtf8(sheet), hcell, vcell);
+ End;
+
+ ///取消合并单元格
+ ///sheet: string,工作表名称
+ ///hcell: string,左上角坐标
+ ///vcell: string,右下角坐标
+ Function UnMergeCell(sheet, hcell, vcell);
+ Begin
+ return workbook_.UnMergeCell(class(TSXml).CurCodePageToUtf8(sheet), hcell, vcell);
+ End;
+
+ ///新建窗口
+ ///返回:windowsIndex int, 窗口索引
+ Function NewSheetPane();
+ Begin
+ return workbook_.NewSheetView();
+ End;
+
+ ///设置窗格
+ ///sheet: string,工作表名称
+ ///windowsIndex: int,窗口索引,从0开始
+ ///Pane: TPane对象
+ Function SetPane(sheet, windowsIndex, Pane);
+ Begin
+ o := getOj(sheet, 'xlsxSheetView');
+ if ifObj(o) then return o.SetPane(windowsIndex, Pane);
+ End;
+
+ ///设置工作表视图属性
+ ///sheet: string,工作表名称
+ ///windowsIndex: int,窗口索引,从0开始
+ ///sheet: object,TSheetView对象
+ Function SetSheetViewOptions(sheet, windowsIndex, sheetView);
+ Begin
+ o := getOj(sheet, 'xlsxSheetView');
+ if ifObj(o) then return o.SetSheetViewOptions(windowsIndex, sheetView);
+ End;
+
+ ///获取工作表视图属性
+ ///sheet: string,工作表名称
+ ///windowsIndex: int,窗口索引,从0开始
+ ///返回: object, TSheetView对象
+ Function GetSheetViewOptions(sheet, windowsIndex);
+ Begin
+ o := getOj(sheet, 'xlsxSheetView');
+ if ifObj(o) then return o.GetSheetViewOptions(windowsIndex);
+ End;
+
+ // TODO printerSettings1.bin?
+ ///设置工作表页面布局
+ ///sheet: string,工作表名称
+ ///pageLayout: TPageLayout对象,属性:
+ Function SetPageLayout(sheet, pageLayout);
+ Begin
+ o := getOj(sheet, 'xlsxPageLayout');
+ if ifObj(o) then return o.SetPageLayout(class(TSXml).CurCodePageToUtf8(sheet), pageLayout);
+ End;
+
+ ///获取工作表页面布局
+ ///sheet: string,工作表名称
+ ///返回: TPageLayout对象
+ Function GetPageLayout(sheet);
+ Begin
+ o := getOj(sheet, 'xlsxPageLayout');
+ if ifObj(o) then return o.GetPageLayout();
+ return "error sheet";
+ End;
+
+ ///设置默认工作表
+ ///sheet: string,工作表名称
+ Function SetDefaultSheet(sheet);
+ Begin
+ return workbook_.SetDefaultSheet(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///获取默认工作表
+ ///返回: string,工作表名称
+ Function GetDefaultSheet();
+ Begin
+ sheet := workbook_.GetDefaultSheet();
+ return class(TSXml).Utf8ToCurCodePage(sheet);
+ End;
+
+ ///设置超链接
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///hyplink: THyperLink对象
+ Function SetCellHyperLink(sheet, axis, hyperlink);
+ Begin
+ o := getOj(sheet, 'xlsxHyperLink');
+ if ifObj(o) then return o.SetCellHyperLink(axis, hyperlink);
+ End;
+
+ // TODO 返回的结果有点问题
+ ///获取超链接
+ ///sheet: string,工作表名称
+ ///axis: string,单元格,如"A7"
+ ///返回: THyperLink对象
+ Function GetCellHyperLink(sheet, axis);
+ Begin
+ o := getOj(sheet, 'xlsxHyperLink');
+ if not ifObj(o) then return array(1, 'The sheet is not exist.');
+ return o.GetCellHyperLink(axis);
+ End;
+
+ ///设置工作表背景图片
+ ///picture: TPicture图片对象
+ Function SetSheetBackground(sheet, picture);
+ Begin
+ o := getOj(sheet, 'xlsxImage');
+ if ifObj(o) then return o.SetSheetBackground(picture);
+ End;
+
+ ///添加图片
+ ///sheet: string, 工作表名称
+ ///topLeft: string, 图片插入左上角单元格
+ ///bottomRight: string, 图片插入右下角单元格
+ ///picture: TPicture 图片对象
+ ///format: TPictureFormat 图片设置对象
+ Function AddPicture(sheet, topLeft, bottomRight, picture, format);
+ Begin
+ o := getOj(sheet, 'xlsxImage');
+ if ifObj(o) then return o.AddPicture(topLeft, bottomRight, picture, format);
+ End;
+
+ ///删除图片
+ ///sheet: 工作表名称
+ ///topLeft: string, 图片插入左上角单元格
+ ///bottomRight: string, 图片插入右下角单元格
+ Function DeletePicture(sheet, topLeft, bottomRight);
+ Begin
+ o := getOj(sheet, 'xlsxImage');
+ if ifObj(o) then return o.DeletePicture(topLeft, bottomRight);
+ End;
+
+ ///创建表格
+ ///sheet: string,工作表名称
+ ///topLeft: string, 左上角单元格
+ ///bottomRight: string, 右下角单元格
+ ///tableStyle: 表格选项类 TTableStyle
+ ///[includeHeader: bool] 是否包括表头,默认FALSE
+ Function AddTable(sheet, topLeft, bottomRight, tableStyle, includeHeader);
+ Begin
+ o := getOj(sheet, 'xlsxTable');
+ if ifObj(o) then return o.AddTable(topLeft, bottomRight, tableStyle, includeHeader);
+ End;
+
+ ///指定行前插入分页符
+ ///sheet: string,工作表名称
+ ///row: int,行号
+ Function InsertPageBreak(sheet, row);
+ Begin
+ return workbook_.InsertPageBreak(class(TSXml).CurCodePageToUtf8(sheet), row);
+ End;
+
+ ///删除指定行分页符
+ ///sheet: string,工作表名称
+ ///row: int,行号
+ Function RemovePageBreak(sheet, row);
+ Begin
+ return workbook_.RemovePageBreak(class(TSXml).CurCodePageToUtf8(sheet), row);
+ End;
+
+ ///添加形状
+ ///sheet: string,工作表名称
+ ///topLeft: string, 左上角单元格
+ ///bottomRight: string, 右下角单元格
+ ///shapeType: string, 形状类型
+ ///format: 形状设置,TShapeFormat类
+ Function AddShape(sheet, topLeft, bottomRight, shapeType, format);
+ Begin
+ o := getOj(sheet, 'xlsxShape');
+ if ifObj(o) then return o.AddShape(topLeft, bottomRight, shapeType, format);
+ End;
+
+ ///设置工作簿的核心属性
+ ///coreProps: TCoreProperty对象
+ Function SetCoreProps(coreProps);
+ Begin
+ o := getOj('', 'xlsxDocProps');
+ if ifObj(o) then return o.SetCoreProps(coreProps);
+ End;
+
+ ///获取工作簿的核心属性
+ ///返回: TCoreProperty对象
+ Function GetCoreProps();
+ Begin
+ o := getOj('', 'xlsxDocProps');
+ if ifObj(o) then return o.GetCoreProps();
+ End;
+
+ ///设置工作簿应用程序属性
+ ///appProps: TAppProperty对象,属性:
+ Function SetAppProps(appProps);
+ Begin
+ o := getOj('', 'xlsxDocProps');
+ if ifObj(o) then return o.SetAppProps(appProps);
+ End;
+
+ ///获取应用程序属性
+ ///返回: TAppProperty对象
+ Function GetAppProps();
+ Begin
+ o := getOj('', 'xlsxDocProps');
+ return o.GetAppProps();
+ End;
+
+ ///复制Sheet
+ ///sourceSheet: 源工作表
+ ///destSheet: 目的工作表,如果destSheet存在,则覆盖destSheet,否则末尾新增destSheet
+ Function CopySheet(sourceSheet, destSheet);
+ Begin
+ return workbook_.CopySheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet));
+ End;
+
+ ///保护工作表
+ ///sheet: 工作表
+ ///protect: TProtect对象
+ Function ProtectSheet(sheet, protect);
+ Begin
+ return workbook_.ProtectSheet(class(TSXml).CurCodePageToUtf8(sheet), protect);
+ End;
+
+ ///取消保护工作
+ ///sheet: 工作表
+ Function UnProtectSheet(sheet);
+ Begin
+ return workbook_.UnProtectSheet(class(TSXml).CurCodePageToUtf8(sheet));
+ End;
+
+ ///设置默认字体
+ ///font: TFont对象
+ Function SetDefaultFont(font);
+ Begin
+ return workbook_.SetDefaultFont(font);
+ End;
+
+ ///获取默认字体
+ ///返回: TFont对象
+ Function GetDefaultFont();
+ Begin
+ return workbook_.GetDefaultFont();
+ End;
+
+ ///设置工作簿计算选项
+ ///calcPr: TCalcPr对象
+ Function SetCalcOptions(calcPr);
+ Begin
+ return workbook_.SetCalcOptions(calcPr);
+ End;
+
+ ///获取工作簿计算选项
+ ///返回: TCalcPr对象
+ Function GetCalcOptions();
+ Begin
+ return workbook_.GetCalcOptions();
+ End;
+
+ ///创建行的分级显示,根据给定的工作表、行号和分级参数创建组
+ ///sheet: string, 工作表名称
+ ///row: int, 行号
+ ///level: int, 分级参数
+ Function SetRowOutlineLevel(sheet, row, level);
+ Begin
+ return workbook_.SetRowOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), row, level);
+ End;
+
+ ///获取行的分级参数
+ ///sheet: string, 工作表名称
+ ///row: int, 行号
+ ///返回: int,分级参数,分级不存在返回0
+ Function GetRowOutlineLevel(sheet, row);
+ Begin
+ return workbook_.GetRowOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), row);
+ End;
+
+ ///创建列的分级显示,根据给定的工作表、列名和分级参数创建组
+ ///sheet: string, 工作表名称
+ ///col: string, 列名
+ ///level: int, 分级参数
+ Function SetColOutlineLevel(sheet, col, level);
+ Begin
+ [err, col] := ColumnNameToNumber(col);
+ if err then return "Col error.";
+ return workbook_.SetColOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), col, level);
+ End;
+
+ ///获取列的分级参数
+ ///sheet: string, 工作表名称
+ ///col: string, 列名
+ ///返回: int,分级参数,分级不存在返回0
+ Function GetColOutlineLevel(sheet, col);
+ Begin
+ [err, col] := ColumnNameToNumber(col);
+ if err then return "Col error.";
+ return workbook_.GetColOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), col);
+ End;
+
+ Function WorkBook();
+ Begin
+ return workbook_;
+ End;
+
+ Function Zip();
+ Begin
+ return zipfile_;
+ End;
+
+private
+ Function InitVars();
+ Begin
+ workbook_ := new xlsxWorkBook(zipfile_);
+ workbook_.Load();
+ style_ := new xlsxStyles(self);
+ End;
+
+ Function SetCellType(sheet, axis, val, opt);
+ Begin
+ if not ifnil(opt['t']) then return;
+ styleid := opt['s'] ? opt['s'] : GetCellStyle(sheet, axis);
+ [t, val] := style_.GetType(styleid, val);
+ if t then opt['t'] := t;
+ End;
+
+ Function getOj(sheet, objname);
+ Begin
+ sheetname := class(TSXml).CurCodePageToUtf8(sheet);
+ if not ifarray(objMgr_) then objMgr_:= array();
+ k := sheetname + '.' + objname;
+ o := objMgr_[ k ];
+ if not ifnil(o) then return o;
+ case objname of
+ 'xlsxComment':
+ o := class(xlsxComment).NewObject(sheetname, self);
+ 'xlsxChart':
+ return class(xlsxChart).NewObject(sheetname, self);//不缓存xlsxChart对象
+ 'xlsxHeaderFooter':
+ o := class(xlsxHeaderFooter).NewObject(sheetname, self);
+ 'xlsxSheetView':
+ o := class(xlsxSheetView).NewObject(sheetname, self);
+ 'xlsxPageLayout':
+ o := class(xlsxPageLayout).NewObject(sheetname, self);
+ 'xlsxDocProps':
+ return class(xlsxDocProps).NewObject(self);
+ 'xlsxImage':
+ o := class(xlsxImage).NewObject(sheetname, self);
+ 'xlsxHyperLink':
+ o := class(xlsxHyperLink).NewObject(sheetname, self);
+ 'xlsxShape':
+ o := class(xlsxShape).NewObject(sheetname, self);
+ 'xlsxTable':
+ o := class(xlsxTable).NewObject(sheetname, self);
+ End;
+ if ifObj(o) then objMgr_[k] := o;
+ return o;
+ End;
+
+ zipfile_; //压缩文件对象
+ workbook_; //WorkBook对象
+ objMgr_; //各种对象缓存、管理
+ style_;
+End;
+
diff --git a/funcext/TSOffice/worksheet/xlsxWorkBook.tsf b/funcext/TSOffice/worksheet/xlsxWorkBook.tsf
index 4021080..8b63676 100644
--- a/funcext/TSOffice/worksheet/xlsxWorkBook.tsf
+++ b/funcext/TSOffice/worksheet/xlsxWorkBook.tsf
@@ -1,1634 +1,1645 @@
-Type xlsxWorkBook = Class
- ///WorkBook文件操作接口
- ///规范1:类首字母小写,表示该类只在模块内部使用,不作为接口提供;
- ///规范2:成员变量首字母大写:成员可以被访问;
- ///规范3:成员变量首字母小写:不建议外部访问;
- ///规范4:成员函数首字母大写:成员函数可以被调用;
- ///规范5:成员函数首字母小写:不建议外部调用;
- ///规范6:成员变量以_结尾;
- ///规范7:字符串用单引号;
- ///规范8:模块tsf文件编码格式为UTF8
-
- ///缺省构造函数
- Function Create(z); overload;
- Begin
- zipfile_ := z;
- xmlFileObjMap_ := array();
- sheetObjMap_ := array();
- sheetPrefix_ := 'xl/worksheets/sheet';
- End;
-
- Function Load();
- Begin
- sheetsCount_ := 0;
- sheetNames_ := array();
- sheetIndexMap_ := array();
-
- //workbook.xml.rels中加载(rid -> sheet-filename)
- rids := array();
- workbook_rels := GetXmlFileObj(class(TSXml).GetFileName('workbook_rels'));
- rels := workbook_rels.FirstChildElement('Relationships').FirstChildElement('Relationship');
- while ifObj(rels) do Begin
- id := rels.GetAttribute('Id');
- target := rels.GetAttribute('Target');
- rids[ id ] := target;
- rels := rels.NextElement();
- End;
- //workbook_rels.Print();
-
- workbook := GetXmlFileObj('xl/workbook.xml');
- //workbook.Print();
- //workbook.Dump();
- //echo tostn(workbook.dom());
- node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('sheet');
- while ifObj(node) do Begin
- name := node.GetAttribute("name");
- sheetNames_[sheetsCount_]['name'] := name;
- sheetNames_[sheetsCount_]['sheetId'] := strtoint(node.GetAttribute("sheetId"));
- rid := node.GetAttribute("r:id");
- sheetNames_[sheetsCount_]['rid'] := rid;
- sheetNames_[sheetsCount_]['file'] := 'xl/' + rids[ rid ];
- sheetIndexMap_[ LowerCase(name) ] := sheetsCount_;
- sheetsCount_ ++;
- node := node.NextElement('sheet');
- End;
-
- files := zipfile_.Files();
- table_files := sselect ['FileName'] from files where AnsiContainsStr(['FileName'], 'tables/table') end;
- for i:=0 to length(table_files)-1 do
- begin
- table_xml := GetXmlFileObj(table_files[i]);
- if ifObj(table_xml) then
- begin
- display_name := table_xml.FirstChildElement('table').GetAttribute('displayName');
- TOfficeApi().Set('Table-' $ display_name, true);
- end
- end
- //echo tostn(sheetNames_);
- End;
-
- ///获取工作表列表
- Function GetSheets();
- Begin
- return sselect ['name'] from sheetNames_ end;
- End;
-
- Function GetSheetName(index);
- Begin
- name := GetSheets()[index];
- return ifString(name) ? name : '';
- End;
-
- Function TotalCols(sheet);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then return o.TotalCols();
- End;
-
- Function TotalRows(sheet);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then return o.TotalRows();
- End;
-
- Function GetCellValue(sheet, axis);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then return o.GetCellValue(axis);
- return class(ErrorMessage).Fail();
- End;
-
- Function GetCellValueType(sheet, axis);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then return o.GetAttribute(axis, '', 't')[1];
- return '';
- End;
-
- Function SetCellValue(sheet, axis, val, opt);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then
- ret := o.SetCellValue(axis, val, opt);
- return ret;
- End;
-
- Function SetCellStyleOpt(sheet, axis, opt);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then
- begin
- if ifnil(opt['s']) and not o.CellIsExists(axis) then
- begin
- [err, col, row] := CellNameToCoordinates(axis);
- xml := GetSheetXmlFile(sheet);
- cols_node := xml.FirstChildElement('worksheet').FirstChildElement('cols');
- if ifObj(cols_node) then
- begin
- col_node := cols_node.FirstChildElement();
- while ifObj(col_node) do
- begin
- min := strtoint(col_node.GetAttribute('min'));
- max := strtoint(col_node.GetAttribute('max'));
- style := col_node.GetAttribute('style');
- if col >= min and col <= max and style <> '' then
- begin
- opt['s'] := style;
- break;
- end
- col_node := col_node.NextElement();
- end
- end
- end
- end
- End;
-
- Function GetCellRichText(sheet, axis);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then return o.GetCellValue(axis, 'RichText');
- return class(ErrorMessage).Fail();
- End;
-
- Function ClearCell(sheet, topLeft, bottomRight);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then o.ClearCell(topLeft, bottomRight);
- End;
-
- Function SetCellFormula(sheet, axis, formula);
- Begin
- o := GetSheetObj(sheet);
- if not o.CellIsExists(axis) then SetCellValue(sheet, axis, '');
- if ifObj(o) then o.SetCellFormula(axis, formula);
- End;
-
- Function GetCellFormula(sheet, axis);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then return o.GetCellFormula(axis);
- return class(ErrorMessage).Fail();
- End;
-
- Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then
- begin
- data := o.Import(topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
- if not TOfficeApi().IsUtf8() then
- begin
- fields := fieldnames(data);
- if ifarray(fields) and length(fields) then
- begin
- map := array();
- for i:=0 to length(fields)-1 do
- map[fields[i]] := class(TSXml).Utf8ToCurCodePage(fields[i]);
- reindex(data, nil, map);
- end
- data::begin
- if ifstring(mcell) then mcell := class(TSXml).Utf8ToCurCodePage(mcell);
- end
- end
- return data;
- end
- End;
-
- Function NewSheet(sheet);overload;
- Begin
- lname := LowerCase(sheet);
- if ifint(sheetIndexMap_[ lname ]) then return 'The sheet already exists.';
-
- fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
- sheetId := addSheetN(fname);
- rid := setWorkbookRels();
- setWorkbookSheet(sheet, sheetId, rid);
- setDocPropsApp(sheet);
- setContentTypes(fname);
-
- sheetNames_[sheetsCount_]['name'] := sheet;
- sheetNames_[sheetsCount_]['sheetId'] := sheetId;
- sheetNames_[sheetsCount_]['rid'] := rid;
- sheetNames_[sheetsCount_]['file'] := fname;
- sheetIndexMap_[ lname ] := sheetsCount_;
- sheetsCount_ ++;
- SetDefaultSheet(sheet);
- End;
-
- Function addSheetN(fname);
- Begin
- //添加文件xl/worksheets/sheetN.xml
- sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
- sheetId := integer(sheetId) + 1;
- zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1'));
- return sheetId;
- End;
-
- Function setWorkbookRels();
- Begin
- rid := getWorkbookRelsRid();
- //设置 workbook.xml.rels
- workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
- rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship');
- rels.SetAttribute('Target', getTarget( sheetsCount_ + 1));
- rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet'));
- rels.SetAttribute('Id', rid);
- return rid;
- End;
-
- Function setWorkbookSheet(sheetName, sheetId, rid);
- Begin
- //设置 xl/workbook.xml
- workbook := GetXmlFileObj('xl/workbook.xml');
- node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
- node.SetAttribute('name', sheetName);
- node.SetAttribute('sheetId', sheetId);
- node.SetAttribute('r:id', rid);
- End;
-
- Function setDocPropsApp(sheet);
- Begin
- //设置docProps/app.xml
- app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
- properties_node := app.FirstChildElement('Properties');
- // HeadingPairs
- variant_node := class(TSXml).GetNode(properties_node, 'HeadingPairs/vt:vector/vt:variant');
- while ifObj(variant_node) do
- begin
- vt_node := variant_node.FirstChildElement('vt:i4');
- if ifObj(vt_node) then
- begin
- vt_node.SetValue(length(sheetNames_) + 1);
- break;
- end
- variant_node := variant_node.NextElement();
- end
-
- // TitlesOfParts
- vector := class(TSXml).GetNode(properties_node, 'TitlesOfParts/vt:vector');
- vector.SetAttribute('size', length(sheetNames_) + 1);
- vector.InsertEndChild('element', 'vt:lpstr', sheet);
- End;
-
- Function setContentTypes(fname);
- Begin
- //设置[Content_Types].xml
- content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
- class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
- End;
-
- Function insertWorkbookSheet(targetSheetName, newSheetName, sheetId, rId, direction);
- Begin
- workbook := GetXmlFileObj('xl/workbook.xml');
- node := class(TSXml).GetNode(workbook, 'workbook/sheets');
- sheet_node := node.FirstChildElement('sheet');
- while ifObj(sheet_node) do
- begin
- if sheet_node.GetAttribute('name') = targetSheetName then
- begin
- if direction = "after" then
- sheet_node := node.InsertAfterChild(sheet_node, 'element', 'sheet');
- else
- sheet_node := node.InsertBeforeChild(sheet_node, 'element', 'sheet');
- sheet_node.SetAttribute('name', newSheetName);
- sheet_node.SetAttribute('sheetId', sheetId);
- sheet_node.SetAttribute('r:id', rid);
- break;
- end
- sheet_node := sheet_node.NextElement();
- end
- End;
-
- Function insertDocPropsApp(targetSheetName, newSheetName, direction);
- Begin
- //设置docProps/app.xml
- app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
- properties_node := app.FirstChildElement('Properties');
- // HeadingPairs
- variant_node := class(TSXml).GetNode(properties_node, 'HeadingPairs/vt:vector/vt:variant');
- while ifObj(variant_node) do
- begin
- vt_node := variant_node.FirstChildElement('vt:i4');
- if ifObj(vt_node) then
- begin
- vt_node.SetValue(length(sheetNames_) + 1);
- break;
- end
- variant_node := variant_node.NextElement();
- end
-
- // TitlesOfParts
- vector := class(TSXml).GetNode(properties_node, 'TitlesOfParts/vt:vector');
- vector.SetAttribute('size', length(sheetNames_) + 1);
- lpstr := vector.FirstChildElement('vt:lpstr');
- while ifObj(lpstr) do
- begin
- if LowerCase(lpstr.GetText()) = LowerCase(targetSheetName) then
- begin
- if direction = 'after' then
- lpstr := vector.InsertAfterChild(lpstr, 'element', 'vt:lpstr');
- else
- lpstr := vector.InsertBeforeChild(lpstr, 'element', 'vt:lpstr');
- lpstr.SetValue(newSheetName);
- break;
- end
- lpstr := lpstr.NextElement();
- end
- End;
-
- Function NewSheet(sourceSheet, destSheet);overload;
- Begin
- lname := LowerCase(destSheet);
- if ifint(sheetIndexMap_[ lname ]) then return 'destSheet already exists.';
- sname := LowerCase(sourceSheet);
- if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists';
-
- fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
- sheetId := addSheetN(fname);
- rid := setWorkbookRels();
- insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'after');
- insertDocPropsApp(sourceSheet, destSheet, 'after');
- setContentTypes(fname);
-
- ind := sheetIndexMap_[sname];
- for i:=sheetsCount_ downto ind+2 do
- begin
- sheetNames_[i]['name'] := sheetNames_[i-1]['name'];
- sheetNames_[i]['sheetId'] := sheetNames_[i-1]['sheetId'];
- sheetNames_[i]['rid'] := sheetNames_[i-1]['rid'];
- sheetNames_[i]['file'] := sheetNames_[i-1]['file'];
- sheetIndexMap_[ LowerCase(sheetNames_[i]['name']) ] := i;
- end
- sheetIndexMap_[lname] := ind + 1;
- sheetNames_[ind+1]['name'] := destSheet;
- sheetNames_[ind+1]['sheetId'] := sheetId;
- sheetNames_[ind+1]['rid'] := rid;
- sheetNames_[ind+1]['file'] := fname;
- sheetsCount_ ++;
- SetDefaultSheet(destSheet);
- End;
-
- Function InsertSheet(sourceSheet, destSheet);
- Begin
- lname := LowerCase(destSheet);
- if ifint(sheetIndexMap_[ lname ]) then return 'destSheet already exists.';
- sname := LowerCase(sourceSheet);
- if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists';
-
- fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
- sheetId := addSheetN(fname);
- rid := setWorkbookRels();
- insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'before');
- insertDocPropsApp(sourceSheet, destSheet, 'before');
- setContentTypes(fname);
-
- ind := sheetIndexMap_[sname];
- for i:=sheetsCount_ downto ind+1 do
- begin
- sheetNames_[i]['name'] := sheetNames_[i-1]['name'];
- sheetNames_[i]['sheetId'] := sheetNames_[i-1]['sheetId'];
- sheetNames_[i]['rid'] := sheetNames_[i-1]['rid'];
- sheetNames_[i]['file'] := sheetNames_[i-1]['file'];
- sheetIndexMap_[ LowerCase(sheetNames_[i]['name']) ] := i;
- end
- sheetIndexMap_[lname] := ind;
- sheetNames_[ind]['name'] := destSheet;
- sheetNames_[ind]['sheetId'] := sheetId;
- sheetNames_[ind]['rid'] := rid;
- sheetNames_[ind]['file'] := fname;
- sheetsCount_ ++;
- SetDefaultSheet(destSheet);
- End;
-
- Function DeleteSheet(sheet);
- Begin
- if sheetsCount_ <= 1 then return 'Cant not delete the last sheet.';
- ind := sheetIndexMap_[ LowerCase(sheet) ];
- if not ifint(ind) then return ;
-
- del := sheetNames_[ind];
- defaultSheet := GetDefaultSheet();
- sheetsCount_--;
- sheetNames_ := select * from sheetNames_ where thisrowindex <> ind end;
-
- //删除文件xl/worksheets/sheetN.xml
- zipfile_.Remove(del['file']);
-
- //设置 workbook.xml.rels
- files := zipfile_.Files();
- for i:=0 to length(sheetNames_)-1 do Begin
- sheetNames_[i]['rid'] := 'rId' + inttostr(i + 1); //重置rId
- oldname := sheetNames_[i]['file'];
- fdir := ExtractFileDir(oldname);
- newname := fdir + "/" + "sheet" + inttostr(i + 1) + '.xml';
- sheetNames_[i]['file'] := newname;
- if oldname <> newname then Begin
- n := vselect thisrowindex from files where ['FileName'] = oldname end;
- if ifint(n) then Begin
- files[n]['New-FileName'] := newname; //重置文件名
- End;
- End;
- End;
- for i:=0 to length(files)-1 do Begin
- if ifstring(files[i]['New-FileName']) then Begin
- f := zipfile_.Get(i);
- f.FileName := files[i]['New-FileName'];
- End;
- End;
- workbook_rels := GetXmlFileObj(class(TSXml).GetFileName('workbook_rels'));
- parent := workbook_rels.FirstChildElement('Relationships');
- rels := parent.FirstChildElement('Relationship');
- while ifObj(rels) do Begin
- target := rels.GetAttribute('Target');
- if AnsiStartsText('worksheets/sheet', target) then Begin
- parent.DeleteChild(rels);
- break;
- End;
- rels := rels.NextElement();//删除节点
- End;
- //workbook_rels.Print();
- i := 0;
- maxRid := sheetsCount_ + 1;
- node := workbook_rels.FirstChildElement('Relationships').FirstChildElement('Relationship');
- while ifObj(node) do Begin
- target := node.GetAttribute('Target');
- if AnsiStartsText('worksheets/sheet', target) then Begin
- node.SetAttribute('Target', getTarget( i + 1));//重置文件名
- node.SetAttribute('Id', sheetNames_[i]['rid']);//重置rId
- i++;
- End
- Else Begin
- node.SetAttribute('Id', 'rId' $ maxRid);//重置rId
- maxRid ++;
- End;
- node := node.NextElement();
- End;
- //workbook_rels.Print();
-
- //设置 xl/workbook.xml
- workbook := GetXmlFileObj('xl/workbook.xml');
- workbook.FirstChildElement('workbook').FirstChildElement('sheets').DeleteChildren();
- for i:=0 to length(sheetNames_)-1 do Begin
- node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
- node.SetAttribute('name', sheetNames_[i]['name']);
- node.SetAttribute('sheetId', sheetNames_[i]['sheetId']);
- node.SetAttribute('r:id', sheetNames_[i]['rid']);
- End;
- // workbook.Print();
-
- //设置docProps/app.xml
- app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
- node := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts');
- if ifObj(node) then Begin
- vector := node.FirstChildElement('vt:vector');
- if ifObj(vector) then Begin
- vector.SetAttribute('size', sheetsCount_);
- n := vector.FirstChildElement('vt:lpstr');
- while ifObj(n) do Begin
- if n.GetText() = del['name'] then Begin
- vector.DeleteChild(n);
- break;
- End;
- n := n.NextElement();
- End;
- End;
- End;
- //app.Print();
-
- //设置[Content_Types].xml
- del_partname := '/' + sheetPrefix_ + inttostr(sheetsCount_ + 1) + '.xml';
- DeleteContentType(del_partname);
-
- //删除calcChain.xml
- zipfile_.Remove('xl/calcChain.xml');
-
- xmlFileObjMap_ := array();
-
- //设置默认工作表
- sheet_name := LowerCase(sheet);
- ind := sheetIndexMap_[sheet_name];
- for k, v in sheetIndexMap_ do
- Begin
- if v > ind then sheetIndexMap_[k] := v - 1;
- End
- reindex(sheetIndexMap_, array(sheet_name: nil));
- ind--;
- if ind < 0 then SetDefaultSheet(sheetNames_[0]['name']);
- else if ind = length(sheetIndexMap_) then SetDefaultSheet(sheetNames_[length(sheetIndexMap_)]['name']);
- else SetDefaultSheet(sheetNames_[ind]['name']);
-
- xmlFileObjMap_ := array();
-
- End;
-
- Function CopySheet(sourceSheet, destSheet);
- Begin
- ind := sheetIndexMap_[ LowerCase(sourceSheet) ];
- if not ifint(ind) then return sourceSheet $ ' does not exists.';
- dest_ind := sheetIndexMap_[ LowerCase(destSheet) ];
-
- //copy sheet
- if ifint(dest_ind) then
- begin
- fname := sheetNames_[dest_ind]['file'];
- zipfile_.Remove(fname);
- end
- else begin
- fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
- end
- sheet := GetSheetXmlFile(sourceSheet);
- zipfile_.Add(fname, sheet.Data());
- xml_file := GetXmlFileObj(fname);
- sheet_node := xml_file.FirstChildElement('worksheet').FirstChildElement('sheetviews').FirstChildElement('sheetview');
- if sheet_node.GetAttribute('tabSelected') = '1' then sheet_node.SetAttribute('tabSelected', 0);
-
- // sheetN.xml.rels
- DrawingFun := Function(obj, target, count);
- Begin
- rels_name := 'xl/drawings/_rels' + ReplaceStr(target, '../drawings', '') + '.rels';
- rels_obj := GetXmlFileObj(rels_name);
- rels_new_file := 'xl/drawings/_rels/drawing' $ count $ '.xml.rels';
- if ifObj(rels_obj) then
- begin
- zipfile_.Add(rels_new_file, rels_obj.Data());
- rels_file := GetXmlFileObj(rels_new_file);
- relationship := rels_file.FirstChildElement('Relationships').FirstChildElement('Relationship');
- while ifObj(relationship) do
- begin
- target := relationship.GetAttribute('Target');
- if AnsiContainsStr(target, 'chart') then copyFile(relationship, target, 'chartContentType');
- relationship := relationship.NextElement();
- end
- end
- End;
- sheet_rels := GetSheetRelsFile(sourceSheet);
- if ifObj(sheet_rels) then
- begin
- if ifint(dest_ind) then
- begin
- rels_name := 'xl/worksheets/_rels/' + ExtractFileName(sheetNames_[dest_ind]['file']) + '.rels';
- zipfile_.Remove(rels_name);
- end
- else begin
- rels_name := 'xl/worksheets/_rels/' + 'sheet' + inttostr(sheetsCount_ + 1) + '.xml.rels';
- end
- zipfile_.Add(rels_name, sheet_rels.Data());
- rels_file := GetXmlFileObj(rels_name);
- relationship := rels_file.FirstChildElement('Relationships').FirstChildElement('Relationship');
- while ifObj(relationship) do
- begin
- target := relationship.GetAttribute('Target');
- if AnsiContainsStr(target, 'vmlDrawing') then copyFile(relationship, target);
- else if AnsiContainsStr(target, 'drawing') then
- begin
- [count, file] := copyFile(relationship, target, 'drawingContentType');
- ##DrawingFun(relationship, target, count);
- end
- else if AnsiContainsStr(target, 'comments') then copyFile(relationship, target, 'commentContentType');
- else if AnsiContainsStr(target, 'table') then
- begin
- [count, file] := copyFile(relationship, target, 'tableContentType');
- table_xml := GetXmlFileObj(file).FirstChildElement('table');
- table_xml.SetAttribute('id', count);
- display_name := table_xml.GetAttribute('displayName');
- display_name := class(xlsxTable).GenerateValidDisplayName(display_name, count);
- table_xml.SetAttribute('displayName', display_name);
- end
- relationship := relationship.NextElement();
- end
- end
-
- if ifint(dest_ind) then return;
-
- //workbook.xml.rels
- rid := getWorkbookRelsRid();
- workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
- rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship');
- rels.SetAttribute('Target', getTarget( sheetsCount_ + 1));
- rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet'));
- rels.SetAttribute('Id', rid);
-
- //xl/workbook.xml
- sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
- sheetId := integer(sheetId) + 1;
- workbook := GetXmlFileObj('xl/workbook.xml');
- node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
- node.SetAttribute('name', destSheet);
- node.SetAttribute('sheetId', sheetId);
- node.SetAttribute('r:id', rid);
-
- //app.xml
- app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
- prop := app.FirstChildElement('Properties');
- vector := prop.FirstChildElement('TitlesOfParts').FirstChildElement('vt:vector');
- vector.SetAttribute('size', sheetsCount_+1);
- vector.InsertEndChild('element', 'vt:lpstr', destSheet);
- vector := prop.FirstChildElement('HeadingPairs').FirstChildElement('vt:vector');
- if ifObj(vector) then
- begin
- variant := vector.FirstChildElement('vt:variant').NextElement('vt:variant');
- i4 := variant.FirstChildElement('vt:i4');
- i4.SetValue(sheetsCount_ + 1);
- end
-
- //[Content_Types].xml
- content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
- class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
-
- sheetNames_[sheetsCount_]['name'] := destSheet;
- sheetNames_[sheetsCount_]['sheetId'] := sheetId;
- sheetNames_[sheetsCount_]['rid'] := rid;
- sheetNames_[sheetsCount_]['file'] := fname;
- sheetIndexMap_[ LowerCase(destSheet) ] := sheetsCount_;
- sheetsCount_ ++;
- End;
-
- Function SetSheetName(sourceName, destName);
- Begin
- ind := sheetIndexMap_[ LowerCase(destName) ];
- if ifint(ind) then return destName $ ' already exists.';
- ind := sheetIndexMap_[ LowerCase(sourceName) ];
- if not ifint(ind) then return sourceName $ ' does not exists.';
-
- //设置 xl/workbook.xml
- workbook := GetXmlFileObj('xl/workbook.xml');
- node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('Sheet');
- while ifObj(node) do
- Begin
- name := node.GetAttribute('name');
- if LowerCase(name) = LowerCase(sourceName) then
- Begin
- node.SetAttribute('name', destName);
- break;
- End
- node := node.NextElement();
- End
-
- //设置docProps/app.xml
- app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
- node := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts');
- lpstr := node.FirstChildElement('vt:vector').FirstChildElement('vt:lpstr');
- while ifObj(lpstr) do
- Begin
- if LowerCase(lpstr.GetText()) = LowerCase(sourceName) then lpstr.SetValue(destName);
- lpstr := lpstr.NextElement();
- End
-
- sheetNames_[ind]['name'] := destName;
- reindex(sheetIndexMap_, array(LowerCase(sourceName) : LowerCase(destName)));
- sheetObjMap_ := array();
- End;
-
- Function InsertCol(sheet, col);
- Begin
- sObj := GetSheetObj(sheet);
- if ifObj(sObj) then sObj.InsertCol(col);
- End;
-
- Function InsertRow(sheet, row);
- Begin
- sObj := GetSheetObj(sheet);
- if ifObj(sObj) then sObj.InsertRow(row);
- End;
-
- Function RemoveCol(sheet, col);
- Begin
- sObj := GetSheetObj(sheet);
- if ifObj(sObj) then sObj.RemoveCol(col);
- End;
-
- Function RemoveRow(sheet, row);
- Begin
- sObj := GetSheetObj(sheet);
- if ifObj(sObj) then sObj.RemoveRow(row);
- End;
-
- Function GetCharts(sheet);
- Begin
- ind := sheetIndexMap_[ LowerCase(sheet) ];
- if not ifint(ind) then return array(-1, 'The sheet is not exist.');
- //rid -> xl/worksheets/_rels/sheetN.xml.rels -> ="../drawings/drawingX.xml -> xl/drawings/_rels/drawingX.xml.rels -> charts
- rid := sheetNames_[ind]['rid'];
- id := rightstr(rid, length(rid)-3);
- rels := 'xl/worksheets/_rels/sheet' + id + '.xml.rels';
- if not FileIsExist(rels) then return array(0, array());
- xmlfile := GetXmlFileObj(rels);
- [rid2, target] := class(TSXml).FindRelationshipRid(xmlfile, '../drawings/drawing');
- if target = '' then return array(0, array());
- drawingFile := ReplaceStr(target, '..', 'xl');
- drawingRels := 'xl/drawings/_rels/' + ExtractFileName(target) + '.rels';
- if not FileIsExist(drawingRels) then return array(0, array());
- xml := GetXmlFileObj(drawingRels);
- charts := array();
- node := xml.FirstChildElement('Relationships').FirstChildElement('Relationship');
-
- while ifObj(node) do Begin
- target := node.GetAttribute('Target');
- if AnsiStartsText('../charts/chart', target) then Begin
- chartFile := ReplaceStr(target, '..', 'xl');
- chart := TOfficeObj('TChart');
- chart.Rid := node.GetAttribute('Id'); //rid
- chart.drawingFileName := drawingFile;
- setChartInfo(chartFile, chart);
- charts[i] := chart;
- i++;
- End;
- node := node.NextElement();
- End;
- if length(charts) = 0 then return array(0, array());
- chartNameMap := array();
- xml := GetXmlFileObj(drawingFile);
- node := xml.FirstChildElement('xdr:wsDr').FirstChildElement('xdr:twoCellAnchor');
- while ifObj(node) do Begin
- name := getNodeValue(node, 'xdr:graphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr', 'name');
- rid := getNodeValue(node, 'xdr:graphicFrame/a:graphic/a:graphicData/c:chart', 'r:id');
- if rid <> '' and name <> '' then
- chartNameMap[rid] := name;
- node := node.NextElement('xdr:twoCellAnchor');
- End;
- for i:=0 to length(charts)-1 do Begin
- charts[i].Name := chartNameMap[charts[i].Rid];
- End;
- return array(0, charts);
- End;
-
- Function GetSheetsCount();
- Begin
- return sheetsCount_;
- End;
-
- Function SetSheetVisible(sheet, visible);
- Begin
- workbook := GetXmlFileObj('xl/workbook.xml');
- node := class(TSXml).GetNode(workbook, 'workbook/sheets/sheet');
- if GetSheetsCount() = 1 then return "Only one sheet, can't set visible.";
- sheet := lowercase(sheet);
- default_sheet_name := lowercase(GetDefaultSheet());
- if default_sheet_name = sheet and not visible then
- begin
- ind := sheetIndexMap_[LowerCase(default_sheet_name)];
- default_name := ind = 0 ? sheetNames_[1]['name'] : sheetNames_[ind - 1]['name'];
- SetDefaultSheet(default_name);
- end
- if visible then SetDefaultSheet(sheet);
- state := visible ? "nohidden" : "hidden";
- while ifObj(node) do
- Begin
- name := lowercase(node.GetAttribute('name'));
- if name = sheet then
- Begin
- node.SetAttribute('state', state);
- break;
- End
- node := node.NextElement();
- End
- return 'Can not find sheet: ' $ sheet;
- End
-
- Function GetSheetVisible(sheet);
- Begin
- sheet_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_file) then return 0;
- sheet := lowercase(sheet);
- workbook := GetXmlFileObj('xl/workbook.xml');
- node := class(TSXml).GetNode(workbook, 'workbook/sheets/sheet');
- while ifObj(node) do
- Begin
- name := lowercase(node.GetAttribute('name'));
- if name = sheet then
- Begin
- val := node.GetAttribute('state');
- if val = 'hidden' then return 0;
- return 1;
- End
- node := node.NextElement();
- End
- return 0;
- End
-
- Function SetRowVisible(sheet, row, visible)
- Begin
- hidden := visible = 1 ? 0 : 1;
- sheet_obj := GetSheetObj(sheet);
- is_exists := sheet_obj.RowIsExists(row);
- if not is_exists then
- begin
- [err, cell] := CoordinatesToCellName(1, row);
- sheet_obj.SetCellValue(cell, '');
- end
- sheet_obj.SetAttribute(row, array("hidden": hidden));
- End
-
- Function GetRowVisble(sheet, row)
- Begin
- sheet_obj := GetSheetObj(sheet);
- if not ifObj(sheet_obj) then return class(ErrorMessage).SheetNotExist(sheet);
- is_exists := sheet_obj.RowIsExists(row);
- if is_exists then
- begin
- [err, hidden] := sheet_obj.GetAttribute(row, 'hidden');
- if not err then
- return array(0, hidden = '1' ? 0 : 1);
- end
- return array(0, 1);
- End
-
- Function SetColVisible(sheet, col, visible)
- Begin
- hidden := visible = 1 ? 0 : 1;
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return 'The Sheet is not exist.';
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- col_node := work_node.FirstChildElement('cols');
- if not ifObj(col_node) and hidden then col_node := work_node.InsertAfterChild(work_node.FirstChildElement('sheetFormatPr'),'element', 'cols');
- node := col_node.FirstChildElement('col');
- while ifObj(node) do
- Begin
- min := strtoint(node.GetAttribute('min'));
- max := strtoint(node.GetAttribute('max'));
- val := trystrtoint(node.GetAttribute('hidden'), r) ? '' : r;
- if col >= min and col <= max then
- Begin
- if hidden = val then return array(0, '');
- else Begin
- if col = min and col = max then
- node.SetAttribute('hidden', hidden);
- else if col = min then node.SetAttribute('min', col + 1);
- else if col = max then node.SetAttribute('max', col - 1);
- else Begin
- node2 := col_node.InsertAfterChild(node, node.Marshal()[0]);
- node.SetAttribute('max', col - 1);
- node2.SetAttribute('min', col + 1);
- End
- return 'ok';
- End
- End
- node := node.NextElement();
- End
- if hidden then
- Begin
- arr := array('type': 'element', 'name': 'col', 'attributes': ('min': col, 'max': col, 'width': 0, 'hidden': 1,
- 'customWidth': 1));
- col_node.InsertEndChild(arr);
- End
- return 'ok';
- End
-
- Function GetColVisble(sheet, col);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return class(ErrorMessage).SheetNotExist();
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- col_node := work_node.FirstChildElement('cols');
- if not ifObj(col_node) then return array(0, 1);
- node := col_node.FirstChildElement('col');
- while ifObj(node) do
- Begin
- min := strtoint(node.GetAttribute('min'));
- max := strtoint(node.GetAttribute('max'));
- val := node.GetAttribute('hidden');
- if col >= min and col <= max then
- Begin
- if val = '1' then return array(0, 0);
- break;
- End
- node := node.NextElement();
- End
- return array(0, 1);
- End
-
- Function SetRowHeight(sheet, row, height);
- Begin
- obj := GetSheetObj(sheet);
- if not obj.RowIsExists(row) then
- begin
- axis := CoordinatesToCellName(1, row, False)[1];
- if not obj.CellIsExists(axis) then SetCellValue(sheet, axis, '');
- end
- obj.SetAttribute(row, array('ht': height, "customHeight": 1));
- End
-
- Function GetRowHeight(sheet, row)
- Begin
- obj := GetSheetObj(sheet);
- ret := obj.GetAttribute(row, "ht");
- if !ret[0] and trystrtofloat(ret[1], height) then return height;
- sheet_xml_file := GetSheetXmlFile(sheet);
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- default_ht := work_node.FirstChildElement('sheetFormatPr').GetAttribute('defaultRowHeight');
- return strtofloat(default_ht);
- End
-
- Function SetColWidth(sheet, bCol, eCol, width);
- Begin
- startCol := bCol;
- endCol := eCol;
- startCol := ColumnNameToNumber(startCol)[1];
- endCol := ColumnNameToNumber(endCol)[1];
- if startCol > endCol then return;
- sheet_xml_file := GetSheetXmlFile(sheet);
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- col_node := class(TSXml).GetWorkSheetNode(work_node, 'cols');
- node := col_node.FirstChildElement('col');
- while ifObj(node) do
- Begin
- min := strtoint(node.GetAttribute('min'));
- max := strtoint(node.GetAttribute('max'));
- val := trystrtofloat(node.GetAttribute('width'), r) ? r : '';
- if startCol = min and endCol = max then
- begin
- node.SetAttribute('width', width);
- node.SetAttribute('customWidth', 1);
- return;
- end
- else if startCol <= min and endCol >= max then
- begin
- node.SetAttribute('width', width);
- node.SetAttribute('customWidth', 1);
- node.SetAttribute('min', startCol);
- node.SetAttribute('max', endCol);
- node := node.NextElement();
- while ifObj(node) do
- begin
- max := strtoint(node.GetAttribute('max'));
- min := strtoint(node.GetAttribute('min'));
- if max <= endCol and min <= endCol then
- begin
- delete_node := node;
- node := node.NextElement();
- col_node.DeleteChild(delete_node);
- end
- else node := node.NextElement();
- end
- return;
- end
- else if startCol >= min and endCol <= max then
- begin
- if width = val then return array(0, '');
- if min = max then
- Begin
- node.SetAttribute('width', width);
- node.SetAttribute('customWidth', 1);
- return;
- End
- else Begin
- insert_flag := 1;
- node_new := col_node.InsertAfterChild(node, node.Marshal()[0]);
- node.SetAttribute('max', startCol - 1);
- node_new.SetAttribute('min', endCol + 1);
- End
- break;
- end
- node := node.NextElement();
- End
- arr := array('type': 'element', 'name': 'col', 'attributes': ('min': startCol, 'max': endCol, 'width': width,
- 'customWidth': 1));
- if insert_flag then col_node.InsertAfterChild(node, arr);
- else col_node.InsertEndChild(arr);
- End;
-
- Function GetColWidth(sheet, col);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- col_node := work_node.FirstChildElement('cols');
- default_width := work_node.FirstChildElement('sheetFormatPr').GetAttribute('defaultColWidth');
- default_width := trystrtofloat(default_width, r) ? r: -1;
- if not ifObj(col_node) then return array(0, default_width);
-
- col_number := ColumnNameToNumber(col)[1];
- node := col_node.FirstChildElement('col');
- while ifObj(node) do
- Begin
- min := strtoint(node.GetAttribute('min'));
- max := strtoint(node.GetAttribute('max'));
- if col_number >= min and col_number <= max then
- Begin
- width := trystrtofloat(node.GetAttribute('width'), r) ? r : -1;
- if width = -1 then break;
- return width;
- End
- node := node.NextElement();
- End
- return default_width;
- End;
-
- Function SetCellStyle(sheet, topLeft, bottomRight, styleid);
- Begin
- o := GetSheetObj(sheet);
- if ifObj(o) then return o.SetCellStyle(topLeft, bottomRight, styleid);
- End;
-
- Function GetCellStyle(sheet, axis);
- Begin
- sheet_obj := GetSheetObj(sheet);
- if ifObj(sheet_obj) then
- begin
- [err, styleId] := sheet_obj.GetCellStyle(axis);
- if not err then return styleId;
- end
- return '';
- End;
-
- Function MergeCell(sheet, hcell, vcell);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return 'The sheet is not exist.';
- if hcell = vcell then return;
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- merge_node := work_node.FirstChildElement('mergeCells');
- if not ifObj(merge_node) then
- Begin
- merge_node := work_node.InsertAfterChild(work_node.FirstChildElement('sheetData'), 'element', 'mergeCells');
- count := 0;
- merge_node.SetAttribute('count', count);
- End
- else begin
- count := trystrtoint(merge_node.GetAttribute('count'), r) ? r : 0;
- end
- node := merge_node.FirstChildElement('mergeCell');
- hcell_ := SplitCellName(hcell);
- vcell_ := SplitCellName(vcell);
- hcell_int := ColumnNameToNumber(hcell_[1])[1];
- vcell_int := ColumnNameToNumber(vcell_[1])[1];
- while ifObj(node) do
- Begin
- ref := node.GetAttribute('ref');
- position := pos(':', ref);
- cell1 := SplitCellName(ref[1:position-1]);
- cell2 := SplitCellName(ref[position+1:]);
- cell1_int := ColumnNameToNumber(cell1[1])[1];
- cell2_int := ColumnNameToNumber(cell2[1])[1];
- if (hcell_[2] >= cell1[2] and hcell_[2] <= cell2[2] and hcell_int >= cell1_int and hcell_int <= cell2_int)
- or (vcell_[2] >= cell1[2] and vcell_[2] <= cell2[2] and vcell_int >= cell1_int and vcell_int <= cell2_int)
- then Begin
- merge_node.DeleteChild(node);
- break;
- End
- node := node.NextElement();
- End
- ref := hcell $ ":" $ vcell;
- node := merge_node.InsertEndChild('element', 'mergeCell');
- node.SetAttribute('ref', ref);
- merge_node.SetAttribute('count', count + 1);
-
- sheet_obj := GetSheetObj(sheet);
- style_id := GetCellStyle(sheet, hcell);
- sheet_obj.SetCellStyle(hcell, vcell, style_id = '' ? '0' : style_id);
- End
-
- Function UnMergeCell(sheet, hcell, vcell);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return 'The sheet is not exist';
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- // mergeCells节点
- merge_node := work_node.FirstChildElement('mergeCells');
- if not ifObj(merge_node) then return '';
- node := merge_node.FirstChildElement('mergeCell');
- if not ifObj(node) then return '';
- count := trystrtoint(merge_node.GetAttribute('count'), r) ? r : 0;
- hcell_ := SplitCellName(hcell);
- vcell_ := SplitCellName(vcell);
- while ifObj(node) do
- Begin
- ref := node.GetAttribute('ref');
- position := pos(':', ref);
- cell1 := SplitCellName(ref[1:position-1]);
- cell2 := SplitCellName(ref[position+1:]);
- if ref[1:position-1] = hcell and ref[position+1:] = vcell
- then Begin
- merge_node.DeleteChild(node);
- break;
- End
- node := node.NextElement();
- End
- if count - 1 <> 0 then
- merge_node.SetAttribute('count', count - 1);
- else
- work_node.DeleteChild(merge_node);
- End;
-
- Function SetSheetDefaultColWidth(sheet, width);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return;
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- node := work_node.FirstChildElement('sheetFormatPr');
- node.SetAttribute('defaultColWidth', width);
- End
-
- Function GetSheetDefaultColWidth(sheet);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return nil;
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- node := work_node.FirstChildElement('sheetFormatPr');
- flag := trystrtofloat(node.GetAttribute('defaultColWidth'), width);
- return flag ? width : nil;
- End
-
- Function SetDefaultSheet(sheet);
- Begin
- ind := sheetIndexMap_[ LowerCase(sheet) ];
- if not ifint(ind) then return 'Can not find sheet: ' $ sheet;
- for i:=0 to length(sheetnames_)-1 do
- begin
- name := sheetNames_[i]['name'];
- file := sheetNames_[i]['file'];
- xml_file := GetXmlFileObj(file);
- if AnsiContainsText(file, 'chart') then root_node := xml_file.FirstChildElement('chartsheet');
- else root_node := xml_file.FirstChildElement('worksheet');
- sheet_node := class(TSXml).GetWorkSheetNode(root_node, 'sheetViews');
- node := sheet_node.FirstChildElement('sheetView');
- if not ifObj(node) then
- begin
- node := sheet_node.InsertFirstChild('element', 'sheetView');
- node.SetAttribute('workbookViewId', 0);
- end
- if lowercase(name) = lowercase(sheet) then
- Begin
- node.SetAttribute('tabSelected', 1);
- workbook := GetXmlFileObj('xl/workbook.xml');
- view_node := class(TSXml).GetNode(workbook, 'workbook/bookViews/workbookView');
- view_node.SetAttribute('activeTab', ind);
- End
- else
- begin
- node := root_node.FirstChildElement('sheetViews').FirstChildElement('sheetView');
- node.SetAttribute('tabSelected', 0);
- end
- end
- End
-
- Function GetDefaultSheet();
- Begin
- for i:=0 to length(sheetnames_)-1 do
- begin
- name := sheetnames_[i]['name'];
- file := sheetNames_[i]['file'];
- xml_file := GetXmlFileObj(file);
- if AnsiContainsText(file, 'chart') then root_node := xml_file.FirstChildElement('chartsheet');
- else root_node := xml_file.FirstChildElement('worksheet');
- sheet_node := root_node.FirstChildElement('sheetviews').FirstChildElement('sheetview');
- if sheet_node.GetAttribute('tabSelected') = '1' then return name;
- end
- End
-
- Function ProtectSheet(sheet, protect);
- Begin
- sheet_obj := GetSheetXmlfile(sheet);
- work_node := sheet_obj.FirstChildElement('worksheet');
- sheet_protection_node := work_node.FirstChildElement('sheetProtection');
- if ifObj(sheet_protection_node) then work_node.DeleteChild(sheet_protection_node);
- prev_node := class(TSXml).GetWorkSheetPrevNode(work_node, 'sheetProtection');
- protect.Sheet := 1;
- work_node.InsertAfterChild(prev_node, protect.Marshal());
- End;
-
- Function UnProtectSheet(sheet);
- Begin
- sheet_obj := GetSheetXmlfile(sheet);
- work_node := sheet_obj.FirstChildElement('worksheet');
- sheet_protection_node := work_node.FirstChildElement('sheetProtection');
- if ifObj(sheet_protection_node) then work_node.DeleteChild(sheet_protection_node);
- End;
-
- Function GetXmlFileObj(n);
- Begin
- o := xmlFileObjMap_[ n ];
- if not ifnil(o) then return o;
- o := zipfile_.Get(n);
- xmlFileObjMap_[ n ] := o;
- return o;
- End;
-
- Function DeleteContentType(del_partname);
- Begin
- content := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
- types := content.FirstChildElement('Types');
- element := types.FirstChildElement('Override');
- while ifObj(element) do Begin
- partname := element.GetAttribute('PartName');
- if partname = del_partname then Begin
- types.DeleteChild(element);
- break;
- End;
- element := element.NextElement();
- End;
- //content.Print();
- End;
-
- Function GetRelationshipRid(sheet, prefix);
- Begin
- ind := sheetIndexMap_[ LowerCase(sheet) ];
- file := 'xl/worksheets/_rels/' + ExtractFileName(sheetNames_[ind]['file']) + '.rels';
- files := zipfile_.Files();
- isexist := vselect thisrowindex from files where ['FileName']=file end;
- if ifnil(isexist) then Begin
- zipfile_.Add(file, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet_rels'));
- end
- xmlfile := GetXmlFileObj(file);
- r := class(TSXml).FindRelationshipRid(xmlfile, prefix);
- r[2] := sheetNames_[ind]['file'];
- r[3] := file;
- return r;
- End;
-
- Function AddRelationshipRid(relsfile, target, type, rid);
- Begin
- xmlfile := GetXmlFileObj(relsfile);
- class(TSXml).AddRelationshipRid(xmlfile, target, type, rid);
- End;
-
- Function GetFilesCount(prefix);
- Begin
- files := zipfile_.Files();
- return vselect countof( ['FileName'] ) from files where AnsiStartsText(prefix, ['FileName']) end;
- End;
-
- Function FileIsExist(fname);
- Begin
- files := zipfile_.Files();
- return vselect countof( ['FileName'] ) from files where ['FileName']=fname end;
- End;
-
- Function GetSheetRelsFile(sheet);
- Begin
- ind := sheetIndexMap_[LowerCase(sheet)];
- file := 'xl/worksheets/_rels/' + ExtractFileName(sheetNames_[ind]['file']) + '.rels';
- if not FileIsExist(file) then
- zipfile_.Add(file, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet_rels'));
- return GetXmlFileObj(file);
- End;
-
- Function GetDrawingRelsFile(drawingId);
- Begin
- file := 'xl/drawings/_rels/drawing' + inttostr(drawingId) + '.xml.rels';
- if not FileIsExist(drawing_rels) then
- zipfile_.Add(file, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('drawing_rels'));
- return GetXmlFileObj(file);
- End;
-
- Function GetSheetXmlFile(sheet);
- Begin
- ind := sheetIndexMap_[ LowerCase(sheet) ];
- if not ifint(ind) then return 0;
- return GetXmlFileObj(sheetNames_[ind]['file']);
- End;
-
- Function GetSheetObj(sheet);
- Begin
- o := sheetObjMap_[sheet];
- if not ifnil(o) then return o;
-
- ind := sheetIndexMap_[ LowerCase(sheet) ];
- if not ifint(ind) then return 0;
- f := GetXmlFileObj(sheetNames_[ind]['file']);
- if not ifObj(f) then return 0;
- o := f.Sheet();
- sheetObjMap_[ sheet ] := o;
- return o;
- End;
-
- Function GetSheetDrawing(sheet);
- Begin
- [rid, drawing_file_name, sheet_file_name, relsfile] := GetRelationshipRid(sheet, '../drawings/drawing');
- if drawing_file_name = '' then
- begin
- rid ++;
- drawing_rid := GetFilesCount('xl/drawings/drawing') + 1;
- drawing_file_name := '../drawings/drawing' + inttostr(drawing_rid) + '.xml';
- drawing_xl_file_name := 'xl/drawings/drawing' + inttostr(drawing_rid) + '.xml';
- zipfile_.Add(drawing_xl_file_name, class(TSXml).XmlHeader() + '');
- AddRelationshipRid(relsfile, drawing_file_name, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', 'rId' $ inttostr(rid));
- content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
- class(TSXml).AddOverrideContentType(content_xml, '/' + drawing_xl_file_name, 'application/vnd.openxmlformats-officedocument.drawing+xml');
- sheet_xml := GetXmlFileObj(sheet_file_name);
- node := sheet_xml.FirstChildElement('worksheet');
- prev_node := class(TSXml).GetWorkSheetPrevNode(node, 'drawing');
- if ifObj(prev_node) then drawing_node := node.InsertAfterChild(prev_node, 'element', 'drawing');
- else drawing_node := node.InsertEndChild('element', 'drawing');
- drawing_node.SetAttribute('r:id', 'rId' + inttostr(rid));
- end
- else begin
- drawing_xl_file_name := 'xl/drwaings/' + ExtractFileName(drawing_file_name);
- s := RightStr(drawing_xl_file_name, length(drawing_xl_file_name) - 19);
- drawing_rid := strtoint(LeftStr(s, length(s) - 4));
- end
- return drawing_rid;
- End
-
- Function InsertPageBreak(sheet, row);
- Begin
- if row <= 1 then return 'Row must be greater than 1.';
- sheet_xml := GetSheetXmlFile(sheet);
- worksheet := sheet_xml.FirstChildElement('worksheet');
- page_node := worksheet.FirstChildElement('rowBreaks');
- tbreak := TOfficeObj('TBreak');
- tbreak.Id := row;
- if not ifObj(page_node) then
- begin
- node := class(TSXml).GetWorkSheetPrevNode(worksheet, 'rowBreaks');
- node := worksheet.InsertAfterChild(node, 'element', 'rowBreaks');
- node.SetAttribute('count', 1);
- node.SetAttribute('manualBreakCount', 1);
- node.InsertFirstChild(tbreak.Marshal());
- return '';
- end
- node := page_node.FirstChildElement('brk');
- prev := nil;
- prev_id := 0;
- while ifObj(node) do
- begin
- id := strtoint(node.GetAttribute('id'));
- if id = row then return array(0, '');
- if row > prev_id and row < id then break;
- prev_id := id;
- prev := node;
- node := node.NextElement();
- end
- if ifObj(prev) then page_node.InsertAfterChild(prev, tbreak.Marshal());
- else page_node.InsertFirstChild(tbreak.Marshal());
- count := strtoint(page_node.GetAttribute('count'));
- manual_count := strtoint(page_node.GetAttribute('manualBreakCount'));
- page_node.SetAttribute('count', count + 1);
- page_node.SetAttribute('manualBreakCount', manual_count + 1);
-
- return '';
- End;
-
- Function RemovePageBreak(sheet, row);
- Begin
- sheet_xml := GetSheetXmlFile(sheet);
- page_node := sheet_xml.FirstChildElement('worksheet').FirstChildElement('rowBreaks');
- if not ifObj(page_node) then return ;
- node := page_node.FirstChildElement('brk');
- while ifObj(node) do
- begin
- id := strtoint(node.GetAttribute('id'));
- if id = row then
- begin
- page_node.DeleteChild(node);
- return;
- end
- node := node.NextElement();
- end
- return;
- End;
-
- Function NewSheetPane();
- Begin
- workbook := GetXmlFileObj('xl/workbook.xml');
- book_view_node := workbook.FirstChildElement('workbook').FirstChildElement('bookViews');
- workbook_node := book_view_node.FirstChildElement('workbookView');
- book_view_node.InsertEndChild(workbook_node.Marshal()[0]);
- for i:=0 to length(sheetnames_)-1 do
- begin
- name := sheetNames_[i]['name'];
- xml_file := GetXmlFileObj(sheetNames_[i]['file']);
- sheet_node := xml_file.FirstChildElement('worksheet').FirstChildElement('sheetViews');
- sheet_view := sheet_node.LastChildElement('sheetView');
- view_id := strtoint(sheet_view.GetAttribute('workbookViewId')) + 1;
- view_obj := TOfficeObj('TSheetView');
- view_obj.WorkbookViewId := view_id;
- sheet_node.InsertEndChild(view_obj.Marshal());
- end
- return view_id;
- End;
-
- Function SetDefaultFont(font);
- Begin
- style_xml := GetXmlFileObj('xl/styles.xml');
- fonts_node := style_xml.FirstChildElement('styleSheet').FirstChildElement('fonts');
- first_node := fonts_node.FirstChildElement('font');
- marshal := font.Marshal();
- class(TSXml).UpdateNode(first_node, marshal['attributes'], marshal['children']);
- End;
-
- Function GetDefaultFont();
- Begin
- style_xml := GetXmlFileObj('xl/styles.xml');
- fonts_node := style_xml.FirstChildElement('styleSheet').FirstChildElement('fonts');
- first_node := fonts_node.FirstChildElement('font');
- tfont := TOfficeObj('TFont');
- tfont.RootObj := first_node;
- return tfont;
- End;
-
- Function SetCalcOptions(calcPr);
- Begin
- workbook_xml := GetXmlFileObj('xl/workbook.xml');
- workbook_node := workbook_xml.FirstChildElement('workbook');
- calc_node := workbook_node.FirstChildElement('calcPr');
- marshal := calcPr.Marshal();
- class(TSXml).UpdateNode(calc_node, marshal['attributes'], marshal['children']);
- End;
-
- Function GetCalcOptions();
- Begin
- workbook_xml := GetXmlFileObj('xl/workbook.xml');
- node := workbook_xml.FirstChildElement('workbook').FirstChildElement('calcPr');
- calcPr := TOfficeObj('TCalcPr');
- calcPr.RootObj := node;
- return calcPr;
- End;
-
- Function SetPageMargins(sheet, margins);
- Begin
- sheet_xml := GetSheetXmlFile(sheet);
- work_node := sheet_xml.FirstChild('worksheet');
- node := work_node.FirstChild('pageMargins');
- if not ifObj(node) then
- begin
- prev_node := class(TSXml).GetWorkSheetPrevNode(work_node, 'pageMargins');
- node := work_node.InsertAfterChild(prev_node, 'element', 'pageMargins');
- end
- marshal := margins.Marshal();
- class(TSXml).UpdateNode(node, marshal['attributes'], marshal['children']);
- End;
-
- Function GetPageMargins(sheet);
- Begin
- sheet_xml := GetSheetXmlFile(sheet);
- node := sheet_xml.FirstChild('worksheet').FirstChild('pageMargins');
- margins := TOfficeObj('tmargins');
- margins.RootObj := node;
- return margins;
- End;
-
- Function SetRowOutlineLevel(sheet, row, level);
- Begin
- sheet_obj := GetSheetObj(sheet);
- o := GetSheetObj(sheet);
- if not sheet_obj.RowIsExists(row) then
- begin
- [err, cell] := CoordinatesToCellName(1, row);
- sheet_obj.SetCellValue(cell, '');
- end
- sheet_obj.SetAttribute(row, array("outlineLevel": level));
- End;
-
- Function GetRowOutlineLevel(sheet, row);
- Begin
- sheet_obj := GetSheetObj(sheet);
- o := GetSheetObj(sheet);
- if not sheet_obj.RowIsExists(row) then return 0;
- [err, level] := sheet_obj.GetAttribute(row, 'outlineLevel');
- return level = '' ? 0 : strtoint(level);
- End;
-
- Function SetColOutlineLevel(sheet, col, level);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return 'The Sheet is not exist.';
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- col_node := work_node.FirstChildElement('cols');
- prev_node := class(TSXml).GetWorkSheetPrevNode(work_node, 'cols');
- if not ifObj(col_node) then col_node := work_node.InsertAfterChild(prev_node,'element', 'cols');
- node := col_node.FirstChildElement('col');
- while ifObj(node) do
- Begin
- min := strtoint(node.GetAttribute('min'));
- max := strtoint(node.GetAttribute('max'));
- if col >= min and col <= max then
- Begin
- level_val := node.GetAttribute('outlineLevel');
- if trystrtoint(level_val, r) and r = level then return;
- else Begin
- if col = min and col = max then node.SetAttribute('outlineLevel', level);
- else begin
- node2 := col_node.InsertAfterChild(node, node.Marshal()[0]);
- if col = min then
- begin
- node.SetAttribute('max', col);
- node.SetAttribute('outlineLevel', level);
- node2.SetAttribute('min', col-1);
- end
- else if col = max then
- begin
- node.SetAttribute('max', col-1);
- node2.SetAttribute('outlineLevel', level);
- node2.SetAttribute('min', col);
- end
- else begin
- node3 := col_node.InsertAfterChild(node, node.Marshal()[0]);
- node.SetAttribute('max', col - 1);
- node2.SetAttribute('min', col + 1);
- node3.SetAttribute('min', col);
- node3.SetAttribute('max', col);
- node3.SetAttribute('outlineLevel', level);
- end
- end
- return 'ok';
- End
- End
- node := node.NextElement();
- End
- arr := array('type': 'element', 'name': 'col', 'attributes': ('min': col, 'max': col, 'outlineLevel': level, 'customWidth': 1, 'width': 9.06640625));
- col_node.InsertEndChild(arr);
- return 'ok';
- End;
-
- Function GetColOutlineLevel(sheet, col);
- Begin
- sheet_xml_file := GetSheetXmlFile(sheet);
- if not ifObj(sheet_xml_file) then return "Sheet error";
- work_node := sheet_xml_file.FirstChildElement('worksheet');
- col_node := work_node.FirstChildElement('cols');
- if not ifObj(col_node) then return 0;
- node := col_node.FirstChildElement('col');
- while ifObj(node) do
- begin
- min := strtoint(node.GetAttribute('min'));
- max := strtoint(node.GetAttribute('max'));
- val := node.GetAttribute('outlineLevel');
- if col >= min and col <= max then return val = '' ? 0 : strtoint(val);
- node := node.NextElement();
- end
- return 0;
- End;
-
-
-private
- Function generateRow(c1, c2, r);
- Begin
- arr := array();
- sr := inttostr(r);
- for i:=c1 to c2 do
- Begin
- name := ColumnNumberToName(i);
- if not name[0] then name := name[1];
- name += sr;
- arr union= array(('type': 'element', 'name': 'r', 'attributes': ('r': name, 's': '1')));
- End
- return arr;
- End
-
- Function getWorkbookRelsRid();
- Begin
- workbook_rels := GetXmlFileObj(class(TSXml).GetFileName('workbook_rels'));
- [rId, find] := class(TSXml).FindRelationshipRid(workbook_rels, '');
- rId ++;
- return 'rId' $ rId;
- End;
-
- Function getTarget(i);
- Begin
- return 'worksheets/sheet' + inttostr(i) + '.xml';
- End;
-
- Function getNodeValue(nd, uri, key);
- Begin
- node := nd;
- arr := str2array(uri, '/');
- for i:=0 to length(arr)-1 do Begin
- node := node.FirstChildElement(arr[i]);
- if not ifObj(node) then return '';
- End;
- if key = '' then
- return node.FirstChildElement().GetValue();
- return node.GetAttribute(key);
- End;
-
- Function getC(chart);
- Begin
- return chart.C ? 'c:' : '';
- End;
-
- Function setChartInfo(chartFile, chart);
- Begin
- xml := GetXmlFileObj(chartFile);
- chart.xmlObj := xml;
- c := xml.FirstChildElement('c:chartSpace');
- chart.C := ifObj(c) ? true : false;
- if not ifObj(xml) then return;
- chart.Title := getNodeValue(xml, chart.C ? 'c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:t' : 'chartSpace/chart/title/tx/rich/a:p/a:r/a:t', '');
- chart.plotAreaNode := xml.FirstChildElement(getC(chart) + 'chartSpace').FirstChildElement(getC(chart) + 'chart').FirstChildElement(getC(chart) + 'plotArea');
- End;
-
- Function copyFile(obj, target, content);
- Begin
- file_name := ReplaceStr(target, '..', 'xl');
- counts := GetFilesCount(ReplaceStrByReg(file_name, "\\d+.*ml", '')) + 1;
- xml := GetXmlFileObj(file_name);
- new_file_postfix := ReplaceStrByReg(file_name[3:], "\\d+", inttostr(counts));
- zipfile_.Add('xl' + new_file_postfix, xml.Data());
- obj.SetAttribute('Target', '..' + new_file_postfix);
-
- if content then
- begin
- content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
- class(TSXml).AddOverrideContentType(content_xml, '/xl' + new_file_postfix, class(TSXml).GetTemplate(content));
- end
- return array(counts, 'xl' + new_file_postfix);
- End;
-
- sheetsCount_:integer;
- sheetNames_;
- sheetIndexMap_;
- sheetPrefix_:string;
- [weakref]zipfile_;
- xmlFileObjMap_; //XmlFile对象存储
- sheetObjMap_; //XmlSheet对象存储
-End;
+Type xlsxWorkBook = Class
+ ///WorkBook文件操作接口
+ ///规范1:类首字母小写,表示该类只在模块内部使用,不作为接口提供;
+ ///规范2:成员变量首字母大写:成员可以被访问;
+ ///规范3:成员变量首字母小写:不建议外部访问;
+ ///规范4:成员函数首字母大写:成员函数可以被调用;
+ ///规范5:成员函数首字母小写:不建议外部调用;
+ ///规范6:成员变量以_结尾;
+ ///规范7:字符串用单引号;
+ ///规范8:模块tsf文件编码格式为UTF8
+
+ ///缺省构造函数
+ Function Create(z); overload;
+ Begin
+ zipfile_ := z;
+ xmlFileObjMap_ := array();
+ sheetObjMap_ := array();
+ sheetPrefix_ := 'xl/worksheets/sheet';
+ End;
+
+ Function Load();
+ Begin
+ sheetsCount_ := 0;
+ sheetNames_ := array();
+ sheetIndexMap_ := array();
+
+ //workbook.xml.rels中加载(rid -> sheet-filename)
+ rids := array();
+ workbook_rels := GetXmlFileObj(class(TSXml).GetFileName('workbook_rels'));
+ rels := workbook_rels.FirstChildElement('Relationships').FirstChildElement('Relationship');
+ while ifObj(rels) do Begin
+ id := rels.GetAttribute('Id');
+ target := rels.GetAttribute('Target');
+ rids[ id ] := target;
+ rels := rels.NextElement();
+ End;
+ //workbook_rels.Print();
+
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ //workbook.Print();
+ //workbook.Dump();
+ //echo tostn(workbook.dom());
+ node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('sheet');
+ while ifObj(node) do Begin
+ name := node.GetAttribute("name");
+ sheetNames_[sheetsCount_]['name'] := name;
+ sheetNames_[sheetsCount_]['sheetId'] := strtoint(node.GetAttribute("sheetId"));
+ rid := node.GetAttribute("r:id");
+ sheetNames_[sheetsCount_]['rid'] := rid;
+ sheetNames_[sheetsCount_]['file'] := 'xl/' + rids[ rid ];
+ sheetIndexMap_[ LowerCase(name) ] := sheetsCount_;
+ sheetsCount_ ++;
+ node := node.NextElement('sheet');
+ End;
+
+ files := zipfile_.Files();
+ table_files := sselect ['FileName'] from files where AnsiContainsStr(['FileName'], 'tables/table') end;
+ for i:=0 to length(table_files)-1 do
+ begin
+ table_xml := GetXmlFileObj(table_files[i]);
+ if ifObj(table_xml) then
+ begin
+ display_name := table_xml.FirstChildElement('table').GetAttribute('displayName');
+ TOfficeApi().Set('Table-' $ display_name, true);
+ end
+ end
+ //echo tostn(sheetNames_);
+ End;
+
+ ///获取工作表列表
+ Function GetSheets();
+ Begin
+ return sselect ['name'] from sheetNames_ end;
+ End;
+
+ Function GetSheetName(index);
+ Begin
+ name := GetSheets()[index];
+ return ifString(name) ? name : '';
+ End;
+
+ Function TotalCols(sheet);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then return o.TotalCols();
+ End;
+
+ Function TotalRows(sheet);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then return o.TotalRows();
+ End;
+
+ Function GetCellValue(sheet, axis);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then return o.GetCellValue(axis);
+ return class(ErrorMessage).Fail();
+ End;
+
+ Function GetCellValueType(sheet, axis);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then return o.GetAttribute(axis, '', 't')[1];
+ return '';
+ End;
+
+ Function SetCellValue(sheet, axis, val, opt);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then
+ ret := o.SetCellValue(axis, val, opt);
+ return ret;
+ End;
+
+ Function SetCellStyleOpt(sheet, axis, opt);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then
+ begin
+ if ifnil(opt['s']) and not o.CellIsExists(axis) then
+ begin
+ [err, col, row] := CellNameToCoordinates(axis);
+ xml := GetSheetXmlFile(sheet);
+ cols_node := xml.FirstChildElement('worksheet').FirstChildElement('cols');
+ if ifObj(cols_node) then
+ begin
+ col_node := cols_node.FirstChildElement();
+ while ifObj(col_node) do
+ begin
+ min := strtoint(col_node.GetAttribute('min'));
+ max := strtoint(col_node.GetAttribute('max'));
+ style := col_node.GetAttribute('style');
+ if col >= min and col <= max and style <> '' then
+ begin
+ opt['s'] := style;
+ break;
+ end
+ col_node := col_node.NextElement();
+ end
+ end
+ end
+ end
+ End;
+
+ Function GetCellRichText(sheet, axis);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then return o.GetCellValue(axis, 'RichText');
+ return class(ErrorMessage).Fail();
+ End;
+
+ Function ClearCell(sheet, topLeft, bottomRight);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then o.ClearCell(topLeft, bottomRight);
+ End;
+
+ Function SetCellFormula(sheet, axis, formula);
+ Begin
+ o := GetSheetObj(sheet);
+ if not o.CellIsExists(axis) then SetCellValue(sheet, axis, '');
+ if ifObj(o) then o.SetCellFormula(axis, formula);
+ End;
+
+ Function GetCellFormula(sheet, axis);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then return o.GetCellFormula(axis);
+ return class(ErrorMessage).Fail();
+ End;
+
+ Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then
+ begin
+ data := o.Import(topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
+ if not TOfficeApi().IsUtf8() then
+ begin
+ fields := fieldnames(data);
+ if ifarray(fields) and length(fields) then
+ begin
+ map := array();
+ for i:=0 to length(fields)-1 do
+ map[fields[i]] := class(TSXml).Utf8ToCurCodePage(fields[i]);
+ reindex(data, nil, map);
+ end
+ data::begin
+ if ifstring(mcell) then mcell := class(TSXml).Utf8ToCurCodePage(mcell);
+ end
+ end
+ return data;
+ end
+ End;
+
+ Function NewSheet(sheet);overload;
+ Begin
+ lname := LowerCase(sheet);
+ if ifint(sheetIndexMap_[ lname ]) then return 'The sheet already exists.';
+
+ fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
+ sheetId := addSheetN(fname);
+ rid := setWorkbookRels();
+ setWorkbookSheet(sheet, sheetId, rid);
+ setDocPropsApp(sheet);
+ setContentTypes(fname);
+
+ sheetNames_[sheetsCount_]['name'] := sheet;
+ sheetNames_[sheetsCount_]['sheetId'] := sheetId;
+ sheetNames_[sheetsCount_]['rid'] := rid;
+ sheetNames_[sheetsCount_]['file'] := fname;
+ sheetIndexMap_[ lname ] := sheetsCount_;
+ sheetsCount_ ++;
+ SetDefaultSheet(sheet);
+ End;
+
+ Function addSheetN(fname);
+ Begin
+ //添加文件xl/worksheets/sheetN.xml
+ sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
+ sheetId := integer(sheetId) + 1;
+ zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1'));
+ return sheetId;
+ End;
+
+ Function setWorkbookRels();
+ Begin
+ rid := getWorkbookRelsRid();
+ //设置 workbook.xml.rels
+ workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
+ rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship');
+ rels.SetAttribute('Target', getTarget( sheetsCount_ + 1));
+ rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet'));
+ rels.SetAttribute('Id', rid);
+ return rid;
+ End;
+
+ Function setWorkbookSheet(sheetName, sheetId, rid);
+ Begin
+ //设置 xl/workbook.xml
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
+ node.SetAttribute('name', sheetName);
+ node.SetAttribute('sheetId', sheetId);
+ node.SetAttribute('r:id', rid);
+ End;
+
+ Function setDocPropsApp(sheet);
+ Begin
+ //设置docProps/app.xml
+ app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
+ properties_node := app.FirstChildElement('Properties');
+ // HeadingPairs
+ variant_node := class(TSXml).GetNode(properties_node, 'HeadingPairs/vt:vector/vt:variant');
+ while ifObj(variant_node) do
+ begin
+ vt_node := variant_node.FirstChildElement('vt:i4');
+ if ifObj(vt_node) then
+ begin
+ vt_node.SetValue(length(sheetNames_) + 1);
+ break;
+ end
+ variant_node := variant_node.NextElement();
+ end
+
+ // TitlesOfParts
+ vector := class(TSXml).GetNode(properties_node, 'TitlesOfParts/vt:vector');
+ vector.SetAttribute('size', length(sheetNames_) + 1);
+ vector.InsertEndChild('element', 'vt:lpstr', sheet);
+ End;
+
+ Function setContentTypes(fname);
+ Begin
+ //设置[Content_Types].xml
+ content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
+ class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
+ End;
+
+ Function insertWorkbookSheet(targetSheetName, newSheetName, sheetId, rId, direction);
+ Begin
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ node := class(TSXml).GetNode(workbook, 'workbook/sheets');
+ sheet_node := node.FirstChildElement('sheet');
+ while ifObj(sheet_node) do
+ begin
+ if sheet_node.GetAttribute('name') = targetSheetName then
+ begin
+ if direction = "after" then
+ sheet_node := node.InsertAfterChild(sheet_node, 'element', 'sheet');
+ else
+ sheet_node := node.InsertBeforeChild(sheet_node, 'element', 'sheet');
+ sheet_node.SetAttribute('name', newSheetName);
+ sheet_node.SetAttribute('sheetId', sheetId);
+ sheet_node.SetAttribute('r:id', rid);
+ break;
+ end
+ sheet_node := sheet_node.NextElement();
+ end
+ End;
+
+ Function insertDocPropsApp(targetSheetName, newSheetName, direction);
+ Begin
+ //设置docProps/app.xml
+ app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
+ properties_node := app.FirstChildElement('Properties');
+ // HeadingPairs
+ variant_node := class(TSXml).GetNode(properties_node, 'HeadingPairs/vt:vector/vt:variant');
+ while ifObj(variant_node) do
+ begin
+ vt_node := variant_node.FirstChildElement('vt:i4');
+ if ifObj(vt_node) then
+ begin
+ vt_node.SetValue(length(sheetNames_) + 1);
+ break;
+ end
+ variant_node := variant_node.NextElement();
+ end
+
+ // TitlesOfParts
+ vector := class(TSXml).GetNode(properties_node, 'TitlesOfParts/vt:vector');
+ vector.SetAttribute('size', length(sheetNames_) + 1);
+ lpstr := vector.FirstChildElement('vt:lpstr');
+ while ifObj(lpstr) do
+ begin
+ if LowerCase(lpstr.GetText()) = LowerCase(targetSheetName) then
+ begin
+ if direction = 'after' then
+ lpstr := vector.InsertAfterChild(lpstr, 'element', 'vt:lpstr');
+ else
+ lpstr := vector.InsertBeforeChild(lpstr, 'element', 'vt:lpstr');
+ lpstr.SetValue(newSheetName);
+ break;
+ end
+ lpstr := lpstr.NextElement();
+ end
+ End;
+
+ Function NewSheet(sourceSheet, destSheet);overload;
+ Begin
+ lname := LowerCase(destSheet);
+ if ifint(sheetIndexMap_[ lname ]) then return 'destSheet already exists.';
+ sname := LowerCase(sourceSheet);
+ if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists';
+
+ fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
+ sheetId := addSheetN(fname);
+ rid := setWorkbookRels();
+ insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'after');
+ insertDocPropsApp(sourceSheet, destSheet, 'after');
+ setContentTypes(fname);
+
+ ind := sheetIndexMap_[sname];
+ for i:=sheetsCount_ downto ind+2 do
+ begin
+ sheetNames_[i]['name'] := sheetNames_[i-1]['name'];
+ sheetNames_[i]['sheetId'] := sheetNames_[i-1]['sheetId'];
+ sheetNames_[i]['rid'] := sheetNames_[i-1]['rid'];
+ sheetNames_[i]['file'] := sheetNames_[i-1]['file'];
+ sheetIndexMap_[ LowerCase(sheetNames_[i]['name']) ] := i;
+ end
+ sheetIndexMap_[lname] := ind + 1;
+ sheetNames_[ind+1]['name'] := destSheet;
+ sheetNames_[ind+1]['sheetId'] := sheetId;
+ sheetNames_[ind+1]['rid'] := rid;
+ sheetNames_[ind+1]['file'] := fname;
+ sheetsCount_ ++;
+ SetDefaultSheet(destSheet);
+ End;
+
+ Function InsertSheet(sourceSheet, destSheet);
+ Begin
+ lname := LowerCase(destSheet);
+ if ifint(sheetIndexMap_[ lname ]) then return 'destSheet already exists.';
+ sname := LowerCase(sourceSheet);
+ if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists';
+
+ fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
+ sheetId := addSheetN(fname);
+ rid := setWorkbookRels();
+ insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'before');
+ insertDocPropsApp(sourceSheet, destSheet, 'before');
+ setContentTypes(fname);
+
+ ind := sheetIndexMap_[sname];
+ for i:=sheetsCount_ downto ind+1 do
+ begin
+ sheetNames_[i]['name'] := sheetNames_[i-1]['name'];
+ sheetNames_[i]['sheetId'] := sheetNames_[i-1]['sheetId'];
+ sheetNames_[i]['rid'] := sheetNames_[i-1]['rid'];
+ sheetNames_[i]['file'] := sheetNames_[i-1]['file'];
+ sheetIndexMap_[ LowerCase(sheetNames_[i]['name']) ] := i;
+ end
+ sheetIndexMap_[lname] := ind;
+ sheetNames_[ind]['name'] := destSheet;
+ sheetNames_[ind]['sheetId'] := sheetId;
+ sheetNames_[ind]['rid'] := rid;
+ sheetNames_[ind]['file'] := fname;
+ sheetsCount_ ++;
+ SetDefaultSheet(destSheet);
+ End;
+
+ Function DeleteSheet(sheet);
+ Begin
+ if sheetsCount_ <= 1 then return 'Cant not delete the last sheet.';
+ ind := sheetIndexMap_[ LowerCase(sheet) ];
+ if not ifint(ind) then return ;
+
+ del := sheetNames_[ind];
+ defaultSheet := GetDefaultSheet();
+ sheetsCount_--;
+ sheetNames_ := select * from sheetNames_ where thisrowindex <> ind end;
+
+ //删除文件xl/worksheets/sheetN.xml
+ zipfile_.Remove(del['file']);
+
+ //设置 workbook.xml.rels
+ files := zipfile_.Files();
+ for i:=0 to length(sheetNames_)-1 do Begin
+ sheetNames_[i]['rid'] := 'rId' + inttostr(i + 1); //重置rId
+ oldname := sheetNames_[i]['file'];
+ fdir := ExtractFileDir(oldname);
+ newname := fdir + "/" + "sheet" + inttostr(i + 1) + '.xml';
+ sheetNames_[i]['file'] := newname;
+ if oldname <> newname then Begin
+ n := vselect thisrowindex from files where ['FileName'] = oldname end;
+ if ifint(n) then Begin
+ files[n]['New-FileName'] := newname; //重置文件名
+ End;
+ End;
+ End;
+ for i:=0 to length(files)-1 do Begin
+ if ifstring(files[i]['New-FileName']) then Begin
+ f := zipfile_.Get(i);
+ f.FileName := files[i]['New-FileName'];
+ End;
+ End;
+ workbook_rels := GetXmlFileObj(class(TSXml).GetFileName('workbook_rels'));
+ parent := workbook_rels.FirstChildElement('Relationships');
+ rels := parent.FirstChildElement('Relationship');
+ while ifObj(rels) do Begin
+ target := rels.GetAttribute('Target');
+ if AnsiStartsText('worksheets/sheet', target) then Begin
+ parent.DeleteChild(rels);
+ break;
+ End;
+ rels := rels.NextElement();//删除节点
+ End;
+ //workbook_rels.Print();
+ i := 0;
+ maxRid := sheetsCount_ + 1;
+ node := workbook_rels.FirstChildElement('Relationships').FirstChildElement('Relationship');
+ while ifObj(node) do Begin
+ target := node.GetAttribute('Target');
+ if AnsiStartsText('worksheets/sheet', target) then Begin
+ node.SetAttribute('Target', getTarget( i + 1));//重置文件名
+ node.SetAttribute('Id', sheetNames_[i]['rid']);//重置rId
+ i++;
+ End
+ Else Begin
+ node.SetAttribute('Id', 'rId' $ maxRid);//重置rId
+ maxRid ++;
+ End;
+ node := node.NextElement();
+ End;
+ //workbook_rels.Print();
+
+ //设置 xl/workbook.xml
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ workbook.FirstChildElement('workbook').FirstChildElement('sheets').DeleteChildren();
+ for i:=0 to length(sheetNames_)-1 do Begin
+ node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
+ node.SetAttribute('name', sheetNames_[i]['name']);
+ node.SetAttribute('sheetId', sheetNames_[i]['sheetId']);
+ node.SetAttribute('r:id', sheetNames_[i]['rid']);
+ End;
+ // workbook.Print();
+
+ //设置docProps/app.xml
+ app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
+ node := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts');
+ if ifObj(node) then Begin
+ vector := node.FirstChildElement('vt:vector');
+ if ifObj(vector) then Begin
+ vector.SetAttribute('size', sheetsCount_);
+ n := vector.FirstChildElement('vt:lpstr');
+ while ifObj(n) do Begin
+ if n.GetText() = del['name'] then Begin
+ vector.DeleteChild(n);
+ break;
+ End;
+ n := n.NextElement();
+ End;
+ End;
+ End;
+ //app.Print();
+
+ //设置[Content_Types].xml
+ del_partname := '/' + sheetPrefix_ + inttostr(sheetsCount_ + 1) + '.xml';
+ DeleteContentType(del_partname);
+
+ //删除calcChain.xml
+ zipfile_.Remove('xl/calcChain.xml');
+
+ xmlFileObjMap_ := array();
+
+ //设置默认工作表
+ sheet_name := LowerCase(sheet);
+ ind := sheetIndexMap_[sheet_name];
+ for k, v in sheetIndexMap_ do
+ Begin
+ if v > ind then sheetIndexMap_[k] := v - 1;
+ End
+ reindex(sheetIndexMap_, array(sheet_name: nil));
+ ind--;
+ if ind < 0 then SetDefaultSheet(sheetNames_[0]['name']);
+ else if ind = length(sheetIndexMap_) then SetDefaultSheet(sheetNames_[length(sheetIndexMap_)]['name']);
+ else SetDefaultSheet(sheetNames_[ind]['name']);
+
+ xmlFileObjMap_ := array();
+
+ End;
+
+ Function CopySheet(sourceSheet, destSheet);
+ Begin
+ ind := sheetIndexMap_[ LowerCase(sourceSheet) ];
+ if not ifint(ind) then return sourceSheet $ ' does not exists.';
+ dest_ind := sheetIndexMap_[ LowerCase(destSheet) ];
+
+ //copy sheet
+ if ifint(dest_ind) then
+ begin
+ fname := sheetNames_[dest_ind]['file'];
+ zipfile_.Remove(fname);
+ end
+ else begin
+ fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
+ end
+ sheet := GetSheetXmlFile(sourceSheet);
+ zipfile_.Add(fname, sheet.Data());
+ xml_file := GetXmlFileObj(fname);
+ sheet_node := xml_file.FirstChildElement('worksheet').FirstChildElement('sheetviews').FirstChildElement('sheetview');
+ if sheet_node.GetAttribute('tabSelected') = '1' then sheet_node.SetAttribute('tabSelected', 0);
+
+ // sheetN.xml.rels
+ DrawingFun := Function(obj, target, count);
+ Begin
+ rels_name := 'xl/drawings/_rels' + ReplaceStr(target, '../drawings', '') + '.rels';
+ rels_obj := GetXmlFileObj(rels_name);
+ rels_new_file := 'xl/drawings/_rels/drawing' $ count $ '.xml.rels';
+ if ifObj(rels_obj) then
+ begin
+ zipfile_.Add(rels_new_file, rels_obj.Data());
+ rels_file := GetXmlFileObj(rels_new_file);
+ relationship := rels_file.FirstChildElement('Relationships').FirstChildElement('Relationship');
+ while ifObj(relationship) do
+ begin
+ target := relationship.GetAttribute('Target');
+ if AnsiContainsStr(target, 'chart') then copyFile(relationship, target, 'chartContentType');
+ relationship := relationship.NextElement();
+ end
+ end
+ End;
+ sheet_rels := GetSheetRelsFile(sourceSheet);
+ if ifObj(sheet_rels) then
+ begin
+ if ifint(dest_ind) then
+ begin
+ rels_name := 'xl/worksheets/_rels/' + ExtractFileName(sheetNames_[dest_ind]['file']) + '.rels';
+ zipfile_.Remove(rels_name);
+ end
+ else begin
+ rels_name := 'xl/worksheets/_rels/' + 'sheet' + inttostr(sheetsCount_ + 1) + '.xml.rels';
+ end
+ zipfile_.Add(rels_name, sheet_rels.Data());
+ rels_file := GetXmlFileObj(rels_name);
+ relationship := rels_file.FirstChildElement('Relationships').FirstChildElement('Relationship');
+ while ifObj(relationship) do
+ begin
+ target := relationship.GetAttribute('Target');
+ if AnsiContainsStr(target, 'vmlDrawing') then copyFile(relationship, target);
+ else if AnsiContainsStr(target, 'drawing') then
+ begin
+ [count, file] := copyFile(relationship, target, 'drawingContentType');
+ ##DrawingFun(relationship, target, count);
+ end
+ else if AnsiContainsStr(target, 'comments') then copyFile(relationship, target, 'commentContentType');
+ else if AnsiContainsStr(target, 'table') then
+ begin
+ [count, file] := copyFile(relationship, target, 'tableContentType');
+ table_xml := GetXmlFileObj(file).FirstChildElement('table');
+ table_xml.SetAttribute('id', count);
+ display_name := table_xml.GetAttribute('displayName');
+ display_name := class(xlsxTable).GenerateValidDisplayName(display_name, count);
+ table_xml.SetAttribute('displayName', display_name);
+ end
+ relationship := relationship.NextElement();
+ end
+ end
+
+ if ifint(dest_ind) then return;
+
+ //workbook.xml.rels
+ rid := getWorkbookRelsRid();
+ workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
+ rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship');
+ rels.SetAttribute('Target', getTarget( sheetsCount_ + 1));
+ rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet'));
+ rels.SetAttribute('Id', rid);
+
+ //xl/workbook.xml
+ sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
+ sheetId := integer(sheetId) + 1;
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
+ node.SetAttribute('name', destSheet);
+ node.SetAttribute('sheetId', sheetId);
+ node.SetAttribute('r:id', rid);
+
+ //app.xml
+ app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
+ prop := app.FirstChildElement('Properties');
+ vector := prop.FirstChildElement('TitlesOfParts').FirstChildElement('vt:vector');
+ vector.SetAttribute('size', sheetsCount_+1);
+ vector.InsertEndChild('element', 'vt:lpstr', destSheet);
+ vector := prop.FirstChildElement('HeadingPairs').FirstChildElement('vt:vector');
+ if ifObj(vector) then
+ begin
+ variant := vector.FirstChildElement('vt:variant').NextElement('vt:variant');
+ i4 := variant.FirstChildElement('vt:i4');
+ i4.SetValue(sheetsCount_ + 1);
+ end
+
+ //[Content_Types].xml
+ content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
+ class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
+
+ sheetNames_[sheetsCount_]['name'] := destSheet;
+ sheetNames_[sheetsCount_]['sheetId'] := sheetId;
+ sheetNames_[sheetsCount_]['rid'] := rid;
+ sheetNames_[sheetsCount_]['file'] := fname;
+ sheetIndexMap_[ LowerCase(destSheet) ] := sheetsCount_;
+ sheetsCount_ ++;
+ End;
+
+ Function SetSheetName(sourceName, destName);
+ Begin
+ ind := sheetIndexMap_[ LowerCase(destName) ];
+ if ifint(ind) then return destName $ ' already exists.';
+ ind := sheetIndexMap_[ LowerCase(sourceName) ];
+ if not ifint(ind) then return sourceName $ ' does not exists.';
+
+ //设置 xl/workbook.xml
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('Sheet');
+ while ifObj(node) do
+ Begin
+ name := node.GetAttribute('name');
+ if LowerCase(name) = LowerCase(sourceName) then
+ Begin
+ node.SetAttribute('name', destName);
+ break;
+ End
+ node := node.NextElement();
+ End
+
+ //设置docProps/app.xml
+ app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
+ node := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts');
+ lpstr := node.FirstChildElement('vt:vector').FirstChildElement('vt:lpstr');
+ while ifObj(lpstr) do
+ Begin
+ if LowerCase(lpstr.GetText()) = LowerCase(sourceName) then lpstr.SetValue(destName);
+ lpstr := lpstr.NextElement();
+ End
+
+ sheetNames_[ind]['name'] := destName;
+ reindex(sheetIndexMap_, array(LowerCase(sourceName) : LowerCase(destName)));
+ sheetObjMap_ := array();
+ End;
+
+ Function InsertCol(sheet, col);
+ Begin
+ sObj := GetSheetObj(sheet);
+ if ifObj(sObj) then sObj.InsertCol(col);
+ End;
+
+ Function InsertRow(sheet, row);
+ Begin
+ sObj := GetSheetObj(sheet);
+ if ifObj(sObj) then sObj.InsertRow(row);
+ End;
+
+ Function RemoveCol(sheet, col);
+ Begin
+ sObj := GetSheetObj(sheet);
+ if ifObj(sObj) then sObj.RemoveCol(col);
+ End;
+
+ Function RemoveRow(sheet, row);
+ Begin
+ sObj := GetSheetObj(sheet);
+ if ifObj(sObj) then sObj.RemoveRow(row);
+ End;
+
+ Function GetCharts(sheet);
+ Begin
+ ind := sheetIndexMap_[ LowerCase(sheet) ];
+ if not ifint(ind) then return array(-1, 'The sheet is not exist.');
+ //rid -> xl/worksheets/_rels/sheetN.xml.rels -> ="../drawings/drawingX.xml -> xl/drawings/_rels/drawingX.xml.rels -> charts
+ rid := sheetNames_[ind]['rid'];
+ id := rightstr(rid, length(rid)-3);
+ rels := 'xl/worksheets/_rels/sheet' + id + '.xml.rels';
+ if not FileIsExist(rels) then return array(0, array());
+ xmlfile := GetXmlFileObj(rels);
+ [rid2, target] := class(TSXml).FindRelationshipRid(xmlfile, '../drawings/drawing');
+ if target = '' then return array(0, array());
+ drawingFile := ReplaceStr(target, '..', 'xl');
+ drawingRels := 'xl/drawings/_rels/' + ExtractFileName(target) + '.rels';
+ if not FileIsExist(drawingRels) then return array(0, array());
+ xml := GetXmlFileObj(drawingRels);
+ charts := array();
+ node := xml.FirstChildElement('Relationships').FirstChildElement('Relationship');
+
+ while ifObj(node) do Begin
+ target := node.GetAttribute('Target');
+ if AnsiStartsText('../charts/chart', target) then Begin
+ chartFile := ReplaceStr(target, '..', 'xl');
+ chart := TOfficeObj('TChart');
+ chart.Rid := node.GetAttribute('Id'); //rid
+ chart.drawingFileName := drawingFile;
+ setChartInfo(chartFile, chart);
+ charts[i] := chart;
+ i++;
+ End;
+ node := node.NextElement();
+ End;
+ if length(charts) = 0 then return array(0, array());
+ chartNameMap := array();
+ xml := GetXmlFileObj(drawingFile);
+ node := xml.FirstChildElement('xdr:wsDr').FirstChildElement('xdr:twoCellAnchor');
+ while ifObj(node) do Begin
+ name := getNodeValue(node, 'xdr:graphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr', 'name');
+ rid := getNodeValue(node, 'xdr:graphicFrame/a:graphic/a:graphicData/c:chart', 'r:id');
+ if rid <> '' and name <> '' then
+ chartNameMap[rid] := name;
+ node := node.NextElement('xdr:twoCellAnchor');
+ End;
+ for i:=0 to length(charts)-1 do Begin
+ charts[i].Name := chartNameMap[charts[i].Rid];
+ End;
+ return array(0, charts);
+ End;
+
+ Function GetSheetsCount();
+ Begin
+ return sheetsCount_;
+ End;
+
+ Function SetSheetVisible(sheet, visible);
+ Begin
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ node := class(TSXml).GetNode(workbook, 'workbook/sheets/sheet');
+ if GetSheetsCount() = 1 then return "Only one sheet, can't set visible.";
+ sheet := lowercase(sheet);
+ default_sheet_name := lowercase(GetDefaultSheet());
+ if default_sheet_name = sheet and not visible then
+ begin
+ ind := sheetIndexMap_[LowerCase(default_sheet_name)];
+ default_name := ind = 0 ? sheetNames_[1]['name'] : sheetNames_[ind - 1]['name'];
+ SetDefaultSheet(default_name);
+ end
+ if visible then SetDefaultSheet(sheet);
+ state := visible ? "nohidden" : "hidden";
+ while ifObj(node) do
+ Begin
+ name := lowercase(node.GetAttribute('name'));
+ if name = sheet then
+ Begin
+ node.SetAttribute('state', state);
+ break;
+ End
+ node := node.NextElement();
+ End
+ return 'Can not find sheet: ' $ sheet;
+ End
+
+ Function GetSheetVisible(sheet);
+ Begin
+ sheet_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_file) then return 0;
+ sheet := lowercase(sheet);
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ node := class(TSXml).GetNode(workbook, 'workbook/sheets/sheet');
+ while ifObj(node) do
+ Begin
+ name := lowercase(node.GetAttribute('name'));
+ if name = sheet then
+ Begin
+ val := node.GetAttribute('state');
+ if val = 'hidden' then return 0;
+ return 1;
+ End
+ node := node.NextElement();
+ End
+ return 0;
+ End
+
+ Function SetRowVisible(sheet, row, visible)
+ Begin
+ hidden := visible = 1 ? 0 : 1;
+ sheet_obj := GetSheetObj(sheet);
+ is_exists := sheet_obj.RowIsExists(row);
+ if not is_exists then
+ begin
+ [err, cell] := CoordinatesToCellName(1, row);
+ sheet_obj.SetCellValue(cell, '');
+ end
+ sheet_obj.SetAttribute(row, array("hidden": hidden));
+ End
+
+ Function GetRowVisble(sheet, row)
+ Begin
+ sheet_obj := GetSheetObj(sheet);
+ if not ifObj(sheet_obj) then return class(ErrorMessage).SheetNotExist(sheet);
+ is_exists := sheet_obj.RowIsExists(row);
+ if is_exists then
+ begin
+ [err, hidden] := sheet_obj.GetAttribute(row, 'hidden');
+ if not err then
+ return array(0, hidden = '1' ? 0 : 1);
+ end
+ return array(0, 1);
+ End
+
+ Function SetColVisible(sheet, col, visible)
+ Begin
+ hidden := visible = 1 ? 0 : 1;
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return 'The Sheet is not exist.';
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ col_node := work_node.FirstChildElement('cols');
+ if not ifObj(col_node) and hidden then col_node := work_node.InsertAfterChild(work_node.FirstChildElement('sheetFormatPr'),'element', 'cols');
+ node := col_node.FirstChildElement('col');
+ while ifObj(node) do
+ Begin
+ min := strtoint(node.GetAttribute('min'));
+ max := strtoint(node.GetAttribute('max'));
+ val := trystrtoint(node.GetAttribute('hidden'), r) ? '' : r;
+ if col >= min and col <= max then
+ Begin
+ if hidden = val then return array(0, '');
+ else Begin
+ if col = min and col = max then
+ node.SetAttribute('hidden', hidden);
+ else if col = min then node.SetAttribute('min', col + 1);
+ else if col = max then node.SetAttribute('max', col - 1);
+ else Begin
+ node2 := col_node.InsertAfterChild(node, node.Marshal()[0]);
+ node.SetAttribute('max', col - 1);
+ node2.SetAttribute('min', col + 1);
+ End
+ return 'ok';
+ End
+ End
+ node := node.NextElement();
+ End
+ if hidden then
+ Begin
+ arr := array('type': 'element', 'name': 'col', 'attributes': ('min': col, 'max': col, 'width': 0, 'hidden': 1,
+ 'customWidth': 1));
+ col_node.InsertEndChild(arr);
+ End
+ return 'ok';
+ End
+
+ Function GetColVisble(sheet, col);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return class(ErrorMessage).SheetNotExist();
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ col_node := work_node.FirstChildElement('cols');
+ if not ifObj(col_node) then return array(0, 1);
+ node := col_node.FirstChildElement('col');
+ while ifObj(node) do
+ Begin
+ min := strtoint(node.GetAttribute('min'));
+ max := strtoint(node.GetAttribute('max'));
+ val := node.GetAttribute('hidden');
+ if col >= min and col <= max then
+ Begin
+ if val = '1' then return array(0, 0);
+ break;
+ End
+ node := node.NextElement();
+ End
+ return array(0, 1);
+ End
+
+ Function SetRowHeight(sheet, row, height);
+ Begin
+ obj := GetSheetObj(sheet);
+ if not obj.RowIsExists(row) then
+ begin
+ axis := CoordinatesToCellName(1, row, False)[1];
+ if not obj.CellIsExists(axis) then SetCellValue(sheet, axis, '');
+ end
+ obj.SetAttribute(row, array('ht': height, "customHeight": 1));
+ End
+
+ Function GetRowHeight(sheet, row)
+ Begin
+ obj := GetSheetObj(sheet);
+ ret := obj.GetAttribute(row, "ht");
+ if !ret[0] and trystrtofloat(ret[1], height) then return height;
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ default_ht := work_node.FirstChildElement('sheetFormatPr').GetAttribute('defaultRowHeight');
+ return strtofloat(default_ht);
+ End
+
+ Function SetColWidth(sheet, bCol, eCol, width);
+ Begin
+ startCol := bCol;
+ endCol := eCol;
+ startCol := ColumnNameToNumber(startCol)[1];
+ endCol := ColumnNameToNumber(endCol)[1];
+ if startCol > endCol then return;
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ col_node := class(TSXml).GetWorkSheetNode(work_node, 'cols');
+ node := col_node.FirstChildElement('col');
+ while ifObj(node) do
+ Begin
+ min := strtoint(node.GetAttribute('min'));
+ max := strtoint(node.GetAttribute('max'));
+ val := trystrtofloat(node.GetAttribute('width'), r) ? r : '';
+ if startCol = min and endCol = max then
+ begin
+ node.SetAttribute('width', width);
+ node.SetAttribute('customWidth', 1);
+ return;
+ end
+ else if startCol <= min and endCol >= max then
+ begin
+ node.SetAttribute('width', width);
+ node.SetAttribute('customWidth', 1);
+ node.SetAttribute('min', startCol);
+ node.SetAttribute('max', endCol);
+ node := node.NextElement();
+ while ifObj(node) do
+ begin
+ max := strtoint(node.GetAttribute('max'));
+ min := strtoint(node.GetAttribute('min'));
+ if max <= endCol and min <= endCol then
+ begin
+ delete_node := node;
+ node := node.NextElement();
+ col_node.DeleteChild(delete_node);
+ end
+ else node := node.NextElement();
+ end
+ return;
+ end
+ else if startCol >= min and endCol <= max then
+ begin
+ if width = val then return array(0, '');
+ if min = max then
+ Begin
+ node.SetAttribute('width', width);
+ node.SetAttribute('customWidth', 1);
+ return;
+ End
+ else Begin
+ insert_flag := 1;
+ node_new := col_node.InsertAfterChild(node, node.Marshal()[0]);
+ node.SetAttribute('max', startCol - 1);
+ node_new.SetAttribute('min', endCol + 1);
+ End
+ break;
+ end
+ node := node.NextElement();
+ End
+ arr := array('type': 'element', 'name': 'col', 'attributes': ('min': startCol, 'max': endCol, 'width': width,
+ 'customWidth': 1));
+ if insert_flag then col_node.InsertAfterChild(node, arr);
+ else col_node.InsertEndChild(arr);
+ End;
+
+ Function GetColWidth(sheet, col);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ col_node := work_node.FirstChildElement('cols');
+ default_width := work_node.FirstChildElement('sheetFormatPr').GetAttribute('defaultColWidth');
+ default_width := trystrtofloat(default_width, r) ? r: -1;
+ if not ifObj(col_node) then return array(0, default_width);
+
+ col_number := ColumnNameToNumber(col)[1];
+ node := col_node.FirstChildElement('col');
+ while ifObj(node) do
+ Begin
+ min := strtoint(node.GetAttribute('min'));
+ max := strtoint(node.GetAttribute('max'));
+ if col_number >= min and col_number <= max then
+ Begin
+ width := trystrtofloat(node.GetAttribute('width'), r) ? r : -1;
+ if width = -1 then break;
+ return width;
+ End
+ node := node.NextElement();
+ End
+ return default_width;
+ End;
+
+ Function SetCellStyle(sheet, topLeft, bottomRight, styleid);
+ Begin
+ o := GetSheetObj(sheet);
+ if ifObj(o) then return o.SetCellStyle(topLeft, bottomRight, styleid);
+ End;
+
+ Function GetCellStyle(sheet, axis);
+ Begin
+ sheet_obj := GetSheetObj(sheet);
+ if ifObj(sheet_obj) then
+ begin
+ [err, styleId] := sheet_obj.GetCellStyle(axis);
+ if not err then return styleId;
+ end
+ return '';
+ End;
+
+ Function MergeCell(sheet, hcell, vcell);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return 'The sheet is not exist.';
+ if hcell = vcell then return;
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ merge_node := work_node.FirstChildElement('mergeCells');
+ if not ifObj(merge_node) then
+ Begin
+ merge_node := work_node.InsertAfterChild(work_node.FirstChildElement('sheetData'), 'element', 'mergeCells');
+ count := 0;
+ merge_node.SetAttribute('count', count);
+ End
+ else begin
+ count := trystrtoint(merge_node.GetAttribute('count'), r) ? r : 0;
+ end
+ node := merge_node.FirstChildElement('mergeCell');
+ hcell_ := SplitCellName(hcell);
+ vcell_ := SplitCellName(vcell);
+ hcell_int := ColumnNameToNumber(hcell_[1])[1];
+ vcell_int := ColumnNameToNumber(vcell_[1])[1];
+ while ifObj(node) do
+ Begin
+ ref := node.GetAttribute('ref');
+ position := pos(':', ref);
+ cell1 := SplitCellName(ref[1:position-1]);
+ cell2 := SplitCellName(ref[position+1:]);
+ cell1_int := ColumnNameToNumber(cell1[1])[1];
+ cell2_int := ColumnNameToNumber(cell2[1])[1];
+ if (hcell_[2] >= cell1[2] and hcell_[2] <= cell2[2] and hcell_int >= cell1_int and hcell_int <= cell2_int)
+ or (vcell_[2] >= cell1[2] and vcell_[2] <= cell2[2] and vcell_int >= cell1_int and vcell_int <= cell2_int)
+ then Begin
+ merge_node.DeleteChild(node);
+ break;
+ End
+ node := node.NextElement();
+ End
+ ref := hcell $ ":" $ vcell;
+ node := merge_node.InsertEndChild('element', 'mergeCell');
+ node.SetAttribute('ref', ref);
+ merge_node.SetAttribute('count', count + 1);
+
+ sheet_obj := GetSheetObj(sheet);
+ style_id := GetCellStyle(sheet, hcell);
+ sheet_obj.SetCellStyle(hcell, vcell, style_id = '' ? '0' : style_id);
+ End
+
+ Function UnMergeCell(sheet, hcell, vcell);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return 'The sheet is not exist';
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ // mergeCells节点
+ merge_node := work_node.FirstChildElement('mergeCells');
+ if not ifObj(merge_node) then return '';
+ node := merge_node.FirstChildElement('mergeCell');
+ if not ifObj(node) then return '';
+ count := trystrtoint(merge_node.GetAttribute('count'), r) ? r : 0;
+ hcell_ := SplitCellName(hcell);
+ vcell_ := SplitCellName(vcell);
+ while ifObj(node) do
+ Begin
+ ref := node.GetAttribute('ref');
+ position := pos(':', ref);
+ cell1 := SplitCellName(ref[1:position-1]);
+ cell2 := SplitCellName(ref[position+1:]);
+ if ref[1:position-1] = hcell and ref[position+1:] = vcell
+ then Begin
+ merge_node.DeleteChild(node);
+ break;
+ End
+ node := node.NextElement();
+ End
+ if count - 1 <> 0 then
+ merge_node.SetAttribute('count', count - 1);
+ else
+ work_node.DeleteChild(merge_node);
+ End;
+
+ Function SetSheetDefaultColWidth(sheet, width);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return;
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ node := work_node.FirstChildElement('sheetFormatPr');
+ node.SetAttribute('defaultColWidth', width);
+ End
+
+ Function GetSheetDefaultColWidth(sheet);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return nil;
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ node := work_node.FirstChildElement('sheetFormatPr');
+ flag := trystrtofloat(node.GetAttribute('defaultColWidth'), width);
+ return flag ? width : nil;
+ End
+
+ Function SetDefaultSheet(sheet);
+ Begin
+ ind := sheetIndexMap_[ LowerCase(sheet) ];
+ if not ifint(ind) then return 'Can not find sheet: ' $ sheet;
+ for i:=0 to length(sheetnames_)-1 do
+ begin
+ name := sheetNames_[i]['name'];
+ file := sheetNames_[i]['file'];
+ xml_file := GetXmlFileObj(file);
+ if AnsiContainsText(file, 'chart') then root_node := xml_file.FirstChildElement('chartsheet');
+ else root_node := xml_file.FirstChildElement('worksheet');
+ sheet_node := class(TSXml).GetWorkSheetNode(root_node, 'sheetViews');
+ node := sheet_node.FirstChildElement('sheetView');
+ if not ifObj(node) then
+ begin
+ node := sheet_node.InsertFirstChild('element', 'sheetView');
+ node.SetAttribute('workbookViewId', 0);
+ end
+ if lowercase(name) = lowercase(sheet) then
+ Begin
+ node.SetAttribute('tabSelected', 1);
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ view_node := class(TSXml).GetNode(workbook, 'workbook/bookViews/workbookView');
+ view_node.SetAttribute('activeTab', ind);
+ End
+ else
+ begin
+ node := root_node.FirstChildElement('sheetViews').FirstChildElement('sheetView');
+ node.SetAttribute('tabSelected', 0);
+ end
+ end
+ End
+
+ Function GetDefaultSheet();
+ Begin
+ for i:=0 to length(sheetnames_)-1 do
+ begin
+ name := sheetnames_[i]['name'];
+ file := sheetNames_[i]['file'];
+ xml_file := GetXmlFileObj(file);
+ if AnsiContainsText(file, 'chart') then root_node := xml_file.FirstChildElement('chartsheet');
+ else root_node := xml_file.FirstChildElement('worksheet');
+ sheet_node := root_node.FirstChildElement('sheetviews').FirstChildElement('sheetview');
+ if sheet_node.GetAttribute('tabSelected') = '1' then return name;
+ end
+ End
+
+ Function ProtectSheet(sheet, protect);
+ Begin
+ sheet_obj := GetSheetXmlfile(sheet);
+ work_node := sheet_obj.FirstChildElement('worksheet');
+ sheet_protection_node := work_node.FirstChildElement('sheetProtection');
+ if ifObj(sheet_protection_node) then work_node.DeleteChild(sheet_protection_node);
+ prev_node := class(TSXml).GetWorkSheetPrevNode(work_node, 'sheetProtection');
+ protect.Sheet := 1;
+ work_node.InsertAfterChild(prev_node, protect.Marshal());
+ End;
+
+ Function UnProtectSheet(sheet);
+ Begin
+ sheet_obj := GetSheetXmlfile(sheet);
+ work_node := sheet_obj.FirstChildElement('worksheet');
+ sheet_protection_node := work_node.FirstChildElement('sheetProtection');
+ if ifObj(sheet_protection_node) then work_node.DeleteChild(sheet_protection_node);
+ End;
+
+ Function GetXmlFileObj(n);
+ Begin
+ o := xmlFileObjMap_[ n ];
+ if not ifnil(o) then return o;
+ o := zipfile_.Get(n);
+ xmlFileObjMap_[ n ] := o;
+ return o;
+ End;
+
+ Function DeleteContentType(del_partname);
+ Begin
+ content := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
+ types := content.FirstChildElement('Types');
+ element := types.FirstChildElement('Override');
+ while ifObj(element) do Begin
+ partname := element.GetAttribute('PartName');
+ if partname = del_partname then Begin
+ types.DeleteChild(element);
+ break;
+ End;
+ element := element.NextElement();
+ End;
+ //content.Print();
+ End;
+
+ Function GetRelationshipRid(sheet, prefix);
+ Begin
+ ind := sheetIndexMap_[ LowerCase(sheet) ];
+ file := 'xl/worksheets/_rels/' + ExtractFileName(sheetNames_[ind]['file']) + '.rels';
+ files := zipfile_.Files();
+ isexist := vselect thisrowindex from files where ['FileName']=file end;
+ if ifnil(isexist) then Begin
+ zipfile_.Add(file, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet_rels'));
+ end
+ xmlfile := GetXmlFileObj(file);
+ r := class(TSXml).FindRelationshipRid(xmlfile, prefix);
+ r[2] := sheetNames_[ind]['file'];
+ r[3] := file;
+ return r;
+ End;
+
+ Function AddRelationshipRid(relsfile, target, type, rid);
+ Begin
+ xmlfile := GetXmlFileObj(relsfile);
+ class(TSXml).AddRelationshipRid(xmlfile, target, type, rid);
+ End;
+
+ Function GetFilesCount(prefix);
+ Begin
+ files := zipfile_.Files();
+ return vselect countof( ['FileName'] ) from files where AnsiStartsText(prefix, ['FileName']) end;
+ End;
+
+ Function FileIsExist(fname);
+ Begin
+ files := zipfile_.Files();
+ return vselect countof( ['FileName'] ) from files where ['FileName']=fname end;
+ End;
+
+ Function GetSheetRelsFile(sheet);
+ Begin
+ ind := sheetIndexMap_[LowerCase(sheet)];
+ file := 'xl/worksheets/_rels/' + ExtractFileName(sheetNames_[ind]['file']) + '.rels';
+ if not FileIsExist(file) then
+ zipfile_.Add(file, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet_rels'));
+ return GetXmlFileObj(file);
+ End;
+
+ Function GetDrawingRelsFile(drawingId);
+ Begin
+ file := 'xl/drawings/_rels/drawing' + inttostr(drawingId) + '.xml.rels';
+ if not FileIsExist(drawing_rels) then
+ zipfile_.Add(file, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('drawing_rels'));
+ return GetXmlFileObj(file);
+ End;
+
+ Function GetSheetXmlFile(sheet);
+ Begin
+ ind := sheetIndexMap_[ LowerCase(sheet) ];
+ if not ifint(ind) then return 0;
+ return GetXmlFileObj(sheetNames_[ind]['file']);
+ End;
+
+ Function GetSheetObj(sheet);
+ Begin
+ o := sheetObjMap_[sheet];
+ if not ifnil(o) then return o;
+
+ ind := sheetIndexMap_[ LowerCase(sheet) ];
+ if not ifint(ind) then return 0;
+ f := GetXmlFileObj(sheetNames_[ind]['file']);
+ if not ifObj(f) then return 0;
+ o := f.Sheet();
+ sheetObjMap_[ sheet ] := o;
+ return o;
+ End;
+
+ Function GetSheetDrawing(sheet);
+ Begin
+ [rid, drawing_file_name, sheet_file_name, relsfile] := GetRelationshipRid(sheet, '../drawings/drawing');
+ if drawing_file_name = '' then
+ begin
+ rid ++;
+ drawing_rid := GetFilesCount('xl/drawings/drawing') + 1;
+ drawing_file_name := '../drawings/drawing' + inttostr(drawing_rid) + '.xml';
+ drawing_xl_file_name := 'xl/drawings/drawing' + inttostr(drawing_rid) + '.xml';
+ zipfile_.Add(drawing_xl_file_name, class(TSXml).XmlHeader() + '');
+ AddRelationshipRid(relsfile, drawing_file_name, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', 'rId' $ inttostr(rid));
+ content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
+ class(TSXml).AddOverrideContentType(content_xml, '/' + drawing_xl_file_name, 'application/vnd.openxmlformats-officedocument.drawing+xml');
+ sheet_xml := GetXmlFileObj(sheet_file_name);
+ node := sheet_xml.FirstChildElement('worksheet');
+ prev_node := class(TSXml).GetWorkSheetPrevNode(node, 'drawing');
+ if ifObj(prev_node) then drawing_node := node.InsertAfterChild(prev_node, 'element', 'drawing');
+ else drawing_node := node.InsertEndChild('element', 'drawing');
+ drawing_node.SetAttribute('r:id', 'rId' + inttostr(rid));
+ end
+ else begin
+ drawing_xl_file_name := 'xl/drwaings/' + ExtractFileName(drawing_file_name);
+ s := RightStr(drawing_xl_file_name, length(drawing_xl_file_name) - 19);
+ drawing_rid := strtoint(LeftStr(s, length(s) - 4));
+ end
+ return drawing_rid;
+ End
+
+ Function InsertPageBreak(sheet, row);
+ Begin
+ if row <= 1 then return 'Row must be greater than 1.';
+ sheet_xml := GetSheetXmlFile(sheet);
+ worksheet := sheet_xml.FirstChildElement('worksheet');
+ page_node := worksheet.FirstChildElement('rowBreaks');
+ tbreak := TOfficeObj('TBreak');
+ tbreak.Id := row;
+ if not ifObj(page_node) then
+ begin
+ node := class(TSXml).GetWorkSheetPrevNode(worksheet, 'rowBreaks');
+ node := worksheet.InsertAfterChild(node, 'element', 'rowBreaks');
+ node.SetAttribute('count', 1);
+ node.SetAttribute('manualBreakCount', 1);
+ node.InsertFirstChild(tbreak.Marshal());
+ return '';
+ end
+ node := page_node.FirstChildElement('brk');
+ prev := nil;
+ prev_id := 0;
+ while ifObj(node) do
+ begin
+ id := strtoint(node.GetAttribute('id'));
+ if id = row then return array(0, '');
+ if row > prev_id and row < id then break;
+ prev_id := id;
+ prev := node;
+ node := node.NextElement();
+ end
+ if ifObj(prev) then page_node.InsertAfterChild(prev, tbreak.Marshal());
+ else page_node.InsertFirstChild(tbreak.Marshal());
+ count := strtoint(page_node.GetAttribute('count'));
+ manual_count := strtoint(page_node.GetAttribute('manualBreakCount'));
+ page_node.SetAttribute('count', count + 1);
+ page_node.SetAttribute('manualBreakCount', manual_count + 1);
+
+ return '';
+ End;
+
+ Function RemovePageBreak(sheet, row);
+ Begin
+ sheet_xml := GetSheetXmlFile(sheet);
+ page_node := sheet_xml.FirstChildElement('worksheet').FirstChildElement('rowBreaks');
+ if not ifObj(page_node) then return ;
+ node := page_node.FirstChildElement('brk');
+ while ifObj(node) do
+ begin
+ id := strtoint(node.GetAttribute('id'));
+ if id = row then
+ begin
+ page_node.DeleteChild(node);
+ return;
+ end
+ node := node.NextElement();
+ end
+ return;
+ End;
+
+ Function NewSheetPane();
+ Begin
+ workbook := GetXmlFileObj('xl/workbook.xml');
+ book_view_node := workbook.FirstChildElement('workbook').FirstChildElement('bookViews');
+ workbook_node := book_view_node.FirstChildElement('workbookView');
+ book_view_node.InsertEndChild(workbook_node.Marshal()[0]);
+ for i:=0 to length(sheetnames_)-1 do
+ begin
+ name := sheetNames_[i]['name'];
+ xml_file := GetXmlFileObj(sheetNames_[i]['file']);
+ sheet_node := xml_file.FirstChildElement('worksheet').FirstChildElement('sheetViews');
+ sheet_view := sheet_node.LastChildElement('sheetView');
+ view_id := strtoint(sheet_view.GetAttribute('workbookViewId')) + 1;
+ view_obj := TOfficeObj('TSheetView');
+ view_obj.WorkbookViewId := view_id;
+ sheet_node.InsertEndChild(view_obj.Marshal());
+ end
+ return view_id;
+ End;
+
+ Function SetDefaultFont(font);
+ Begin
+ style_xml := GetXmlFileObj('xl/styles.xml');
+ fonts_node := style_xml.FirstChildElement('styleSheet').FirstChildElement('fonts');
+ first_node := fonts_node.FirstChildElement('font');
+ marshal := font.Marshal();
+ class(TSXml).UpdateNode(first_node, marshal['attributes'], marshal['children']);
+ End;
+
+ Function GetDefaultFont();
+ Begin
+ style_xml := GetXmlFileObj('xl/styles.xml');
+ fonts_node := style_xml.FirstChildElement('styleSheet').FirstChildElement('fonts');
+ first_node := fonts_node.FirstChildElement('font');
+ tfont := TOfficeObj('TFont');
+ tfont.RootObj := first_node;
+ return tfont;
+ End;
+
+ Function SetCalcOptions(calcPr);
+ Begin
+ workbook_xml := GetXmlFileObj('xl/workbook.xml');
+ workbook_node := workbook_xml.FirstChildElement('workbook');
+ calc_node := workbook_node.FirstChildElement('calcPr');
+ marshal := calcPr.Marshal();
+ class(TSXml).UpdateNode(calc_node, marshal['attributes'], marshal['children']);
+ End;
+
+ Function GetCalcOptions();
+ Begin
+ workbook_xml := GetXmlFileObj('xl/workbook.xml');
+ node := workbook_xml.FirstChildElement('workbook').FirstChildElement('calcPr');
+ calcPr := TOfficeObj('TCalcPr');
+ calcPr.RootObj := node;
+ return calcPr;
+ End;
+
+ Function SetPageMargins(sheet, margins);
+ Begin
+ sheet_xml := GetSheetXmlFile(sheet);
+ work_node := sheet_xml.FirstChild('worksheet');
+ node := work_node.FirstChild('pageMargins');
+ if not ifObj(node) then
+ begin
+ prev_node := class(TSXml).GetWorkSheetPrevNode(work_node, 'pageMargins');
+ node := work_node.InsertAfterChild(prev_node, 'element', 'pageMargins');
+ end
+ marshal := margins.Marshal();
+ class(TSXml).UpdateNode(node, marshal['attributes'], marshal['children']);
+ End;
+
+ Function GetPageMargins(sheet);
+ Begin
+ sheet_xml := GetSheetXmlFile(sheet);
+ node := sheet_xml.FirstChild('worksheet').FirstChild('pageMargins');
+ margins := TOfficeObj('tmargins');
+ margins.RootObj := node;
+ return margins;
+ End;
+
+ Function SetRowOutlineLevel(sheet, row, level);
+ Begin
+ sheet_obj := GetSheetObj(sheet);
+ o := GetSheetObj(sheet);
+ if not sheet_obj.RowIsExists(row) then
+ begin
+ [err, cell] := CoordinatesToCellName(1, row);
+ sheet_obj.SetCellValue(cell, '');
+ end
+ sheet_obj.SetAttribute(row, array("outlineLevel": level));
+ End;
+
+ Function GetRowOutlineLevel(sheet, row);
+ Begin
+ sheet_obj := GetSheetObj(sheet);
+ o := GetSheetObj(sheet);
+ if not sheet_obj.RowIsExists(row) then return 0;
+ [err, level] := sheet_obj.GetAttribute(row, 'outlineLevel');
+ return level = '' ? 0 : strtoint(level);
+ End;
+
+ Function SetColOutlineLevel(sheet, col, level);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return 'The Sheet is not exist.';
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ col_node := work_node.FirstChildElement('cols');
+ prev_node := class(TSXml).GetWorkSheetPrevNode(work_node, 'cols');
+ if not ifObj(col_node) then col_node := work_node.InsertAfterChild(prev_node,'element', 'cols');
+ node := col_node.FirstChildElement('col');
+ while ifObj(node) do
+ Begin
+ min := strtoint(node.GetAttribute('min'));
+ max := strtoint(node.GetAttribute('max'));
+ if col >= min and col <= max then
+ Begin
+ level_val := node.GetAttribute('outlineLevel');
+ if trystrtoint(level_val, r) and r = level then return;
+ else Begin
+ if col = min and col = max then node.SetAttribute('outlineLevel', level);
+ else begin
+ node2 := col_node.InsertAfterChild(node, node.Marshal()[0]);
+ if col = min then
+ begin
+ node.SetAttribute('max', col);
+ node.SetAttribute('outlineLevel', level);
+ node2.SetAttribute('min', col-1);
+ end
+ else if col = max then
+ begin
+ node.SetAttribute('max', col-1);
+ node2.SetAttribute('outlineLevel', level);
+ node2.SetAttribute('min', col);
+ end
+ else begin
+ node3 := col_node.InsertAfterChild(node, node.Marshal()[0]);
+ node.SetAttribute('max', col - 1);
+ node2.SetAttribute('min', col + 1);
+ node3.SetAttribute('min', col);
+ node3.SetAttribute('max', col);
+ node3.SetAttribute('outlineLevel', level);
+ end
+ end
+ return 'ok';
+ End
+ End
+ node := node.NextElement();
+ End
+ arr := array('type': 'element', 'name': 'col', 'attributes': ('min': col, 'max': col, 'outlineLevel': level, 'customWidth': 1, 'width': 9.06640625));
+ col_node.InsertEndChild(arr);
+ return 'ok';
+ End;
+
+ Function GetColOutlineLevel(sheet, col);
+ Begin
+ sheet_xml_file := GetSheetXmlFile(sheet);
+ if not ifObj(sheet_xml_file) then return "Sheet error";
+ work_node := sheet_xml_file.FirstChildElement('worksheet');
+ col_node := work_node.FirstChildElement('cols');
+ if not ifObj(col_node) then return 0;
+ node := col_node.FirstChildElement('col');
+ while ifObj(node) do
+ begin
+ min := strtoint(node.GetAttribute('min'));
+ max := strtoint(node.GetAttribute('max'));
+ val := node.GetAttribute('outlineLevel');
+ if col >= min and col <= max then return val = '' ? 0 : strtoint(val);
+ node := node.NextElement();
+ end
+ return 0;
+ End;
+
+
+private
+ Function generateRow(c1, c2, r);
+ Begin
+ arr := array();
+ sr := inttostr(r);
+ for i:=c1 to c2 do
+ Begin
+ name := ColumnNumberToName(i);
+ if not name[0] then name := name[1];
+ name += sr;
+ arr union= array(('type': 'element', 'name': 'r', 'attributes': ('r': name, 's': '1')));
+ End
+ return arr;
+ End
+
+ Function getWorkbookRelsRid();
+ Begin
+ workbook_rels := GetXmlFileObj(class(TSXml).GetFileName('workbook_rels'));
+ [rId, find] := class(TSXml).FindRelationshipRid(workbook_rels, '');
+ rId ++;
+ return 'rId' $ rId;
+ End;
+
+ Function getTarget(i);
+ Begin
+ return 'worksheets/sheet' + inttostr(i) + '.xml';
+ End;
+
+ Function getNodeValue(nd, uri, key);
+ Begin
+ node := nd;
+ arr := str2array(uri, '/');
+ for i:=0 to length(arr)-1 do Begin
+ node := node.FirstChildElement(arr[i]);
+ if not ifObj(node) then return '';
+ End;
+ if key = '' then
+ return node.FirstChildElement().GetValue();
+ return node.GetAttribute(key);
+ End;
+
+ Function getC(chart);
+ Begin
+ return chart.C ? 'c:' : '';
+ End;
+
+ Function setChartInfo(chartFile, chart);
+ Begin
+ xml := GetXmlFileObj(chartFile);
+ chart.xmlObj := xml;
+ c := xml.FirstChildElement('c:chartSpace');
+ chart.C := ifObj(c) ? true : false;
+ if not ifObj(xml) then return;
+ chart.Title := getNodeValue(xml, chart.C ? 'c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:t' : 'chartSpace/chart/title/tx/rich/a:p/a:r/a:t', '');
+ chart.plotAreaNode := xml.FirstChildElement(getC(chart) + 'chartSpace').FirstChildElement(getC(chart) + 'chart').FirstChildElement(getC(chart) + 'plotArea');
+ End;
+
+ Function copyFile(obj, target, content);
+ Begin
+ file_name := ReplaceStr(target, '..', 'xl');
+ counts := GetFilesCount(ReplaceStrUsingReg(file_name, "\\d+.*ml", '')) + 1;
+ xml := GetXmlFileObj(file_name);
+ new_file_postfix := ReplaceStrUsingReg(file_name[3:], "\\d+", inttostr(counts));
+ zipfile_.Add('xl' + new_file_postfix, xml.Data());
+ obj.SetAttribute('Target', '..' + new_file_postfix);
+
+ if content then
+ begin
+ content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
+ class(TSXml).AddOverrideContentType(content_xml, '/xl' + new_file_postfix, class(TSXml).GetTemplate(content));
+ end
+ return array(counts, 'xl' + new_file_postfix);
+ End;
+
+ function ReplaceStrUsingReg(s, regFromText, toText);
+ begin
+ ParseCtrls := 'r';
+ if ifnil(toText) then toText := '';
+ ParseRegExpr(RegFromText,s,ParseCtrls,toText,s2);
+ if ansicontainstext(ParseCtrls,'r') then //表替换,输出s2
+ return ifstring(s2)?s2:s;
+ else
+ return ifArray(toText) and length(toText) > 0 ? toText : array();
+ end;
+
+ sheetsCount_:integer;
+ sheetNames_;
+ sheetIndexMap_;
+ sheetPrefix_:string;
+ [weakref]zipfile_;
+ xmlFileObjMap_; //XmlFile对象存储
+ sheetObjMap_; //XmlSheet对象存储
+End;
diff --git a/更新日志.md b/更新日志.md
index 62170cd..0f86773 100644
--- a/更新日志.md
+++ b/更新日志.md
@@ -1,767 +1,781 @@
-# 更新日志
-
-## 2025-7-22
-
-### V1.8.1
-
-#### word
-
-1. **feat**: 支持同一段落多图片文字
-
-```cpp
-picture := TOfficeObj('TPicture');
-picture.Descr := '插入图像案例';
-picture.Image := TOfficeTemplate('tinysoft.gif',true);
-
-docx := new TSDocxFile();
-docx.NewFile();
-p := new TOfficeObj('TParagraph');
-r := p.AddRun();
-r.SetText("abc");
-r := p.AddRun();
-r.SetDrawing(picture, docx); // 需要一个图片对象和TSDocxFile对象
-r := p.AddRun();
-r.SetText("def");
-docx.AddParagraph(p, -1);
-```
-
-## 2025-6-23
-
-### V1.8.0
-
-#### word
-
-1. **feat**: 支持添加自定目录`TSDocxFile.AddCustomTableContent`
-2. **fix**: 修复单元格内容换行,自定义属性失效问题
-
-## 2025-2-28
-
-### V1.7.9
-
-#### word
-
-1. **feat**:支持目录自定义宽度`TOfficeApi().Get("TTableContent-tab-position")`
-2. **fix**:插入图片id计算错误,导致`office 2016 16.0.4266`版本不兼容
-
-## 2025-1-22
-
-### V1.7.8
-
-❗解决`TSDocxFile`和`TSXlsxFile`内存泄露
-
-#### word
-
-1. **fix**:`CreateTable`由于`fieldnames.tsf`差异报错
-
-## 2025-1-17
-
-### V1.7.7
-
-#### word
-
-1. **feat**:`TPicture`对象设置画布大小`CanvasWidth`、`CanvasHeight`
-2. **fix**:`InsertTable`表格数据不对齐报错
-
-#### excel
-
-1. **fix**:`SetColOutlineLevel`错误
-
-## 2025-1-8
-
-### V1.7.6
-
-#### excel
-
-1. **fix**:`SetRowHeight`单元格不存在无法设置
-2. **fix**:`Set[Get]ColVisible`修改传入的列参数
-
-## 2025-1-7
-
-### V1.7.5
-
-#### word
-
-1. 支持获取`sdt`标签类型的内容(如文本框),通过`TSDocxFile.Sdts()`获取文档的的所有`sdt`
-
-> ```pascal
-> sdts := docx.Sdts();
-> paragraphs := sdts[0].Paragraphs();
-> textbox := paragraphs[1].TextBoxs();
-> ```
-
-## 2024-12-23
-
-### V1.7.4
-
-#### word
-
-1. `insertfile`时候默认添加`w16du`命名空间
-
-## 2024-12-05
-
-### V1.7.3
-
-#### word
-
-1. `insertfile`支持 ole 对象的插入,如 wmf 公式,excel 表格
-
-## 2024-12-04
-
-### V1.7.2
-
-#### word
-
-1. 支持单元格标题生成到目录,需要额外设置`TOfficeApi().SetCellTableContent(true);`,默认是`false`
-
-## 2024-11-19
-
-### V1.7.1
-
-#### word
-
-1. 对生成目录的 xml 结构进行更改
-2. 修复引用 excel 错误
-
-#### excel
-
-1. 修复获取`comment`时候,换行符问题
-
-## 2024-7-8
-
-### V1.7.0
-
-#### excel
-
-1. 修复`CopySheet`操作后,文件报错问题
-
-## 2024-5-6
-
-### V1.6.9
-
-#### excel
-
-1. 修复单元格继承列样式失败问题
-
-## 2024-4-12
-
-### V1.6.8
-
-### excel
-
-1. 修复`SetCellValue`单元格对列样式的识别,不需要手动用格式刷对所有单元格刷新样式
-
-## 2024-3-21
-
-### V1.6.7
-
-#### word
-
-1. 新增对象的`Delete`方法,可以通过`Delete(name)`删除对象的指定属性
-2. 新增`TPageNumType`对象
-3. 扩展`TDocSection`方法,`AppendReference`和`AddReference`,前者是对已有的`Section`对象添加`TReference`对象,而后者是对新的`Section`对象添加
-
-## 2024-3-14
-
-### V1.6.6
-
-#### word
-
-1. 修复`TDocSection`节点获取错误
-2. 支持分栏自定义宽度
-
-## 2024-3-4
-
-### V1.6.5
-
-支持扩展扩展模块
-
-### excel
-
-1. 样式获取报错问题
-
-### V1.6.4
-
-#### word
-
-1. 修复`InsertTable`内容`\n`无换行问题
-2. `TextBox`支持`Clear`清空内容
-
-#### word
-
-1. 修复目录生成陷入死循环问题
-
-## 2024-2-23
-
-### V1.6.3
-
-#### word
-
-1. 修复目录生成陷入死循环问题
-
-## 2024-2-7
-
-### V1.6.2
-
-#### word
-
-1. 修复设置段落属性`TextAlignment`失败问题
-
-## 2024-2-5
-
-### V1.6.1
-
-#### excel
-
-1. 修复 excel 文件单元格原有类型与 tsl 类型转换报错问题(trystrtofloat)
-
-## 2024-2-5
-
-### V1.6.0
-
-#### excel
-
-1. 兼容 Excel2016,通过`NewSheet/InsertSheet`创建工作表不再提示修复错误
-2. 修复带有表头的二维数组通过`InsertTable`写入数据只写入表头未写入数据问题
-
-## 2024-1-31
-
-### V1.5.9
-
-修复`tsl`脚本格式为`utf8`时无法打开中文命名的文件
-
-### V1.5.8
-
-#### excel
-
-1. 支持写入单元格内容时判断当前单元格的数据类型,并以单元格数据类型为准
-
-## 2024-1-17
-
-### V1.5.7
-
-#### excel
-
-1. 修复单元格写入浮点数数据 bug,如写入 0.0,excel 某些版本打开会显示 0.00000000000000000
-
-## 2024-1-11
-
-### V1.5.6
-
-#### excel
-
-1. 修复单元格写入浮点数数据,精度丢失问题
-
-## 2023-12-29
-
-### V1.5.5
-
-#### word
-
-1. 修复生成的目录打开后重新生成页码可能产生页码失败问题
-
-#### excel
-
-1. 修复`InsertTable`与``SetCellValue`返回值问题
-
-## 2023-12-18
-
-### V1.5.4
-
-#### excel
-
-1. 优化`InsertTable`的写入效率
-
-## 2023-12-11
-
-### V1.5.3
-
-#### excel
-
-1. 支持写入单元格时且单元格不存在时,新的单元格会沿用设置的样式 ID(若有设置)
-
-## 2023-12-1
-
-### V1.5.2
-
-#### excel
-
-1. 修复公式中文转码问题
-
-## 2023-11-15
-
-### V1.5.1
-
-1. 支持识别图片中含有`0xffc2`段的 jpg 格式的大小
-
-## 2023-11-14
-
-### V1.5.0
-
-#### word
-
-1. 新增对脚注的支持,方法`TSDocxFile.FootnotesObject().Add`
-2. `TSDocxFile.InsertFile`支持脚注插入
-
-### excel
-
-1. 升级`TSXlsxFile.CopySheet`
-
-## 2023-10-16
-
-### V1.4.9
-
-#### word
-
-1. 新增`TSDocxFile.InsertFile`重载方法
-2. 新增`Table.SetStyle`方法
-
-## 2023-9-27
-
-### V1.4.8
-
-#### word
-
-1. 支持`Inserttable`时候加入对单元格样式的设置
-2. 新增`TSDocxFile.LastParagraph`方法
-3. 新增`TParagraph.PrevParagraph`方法
-4. 性能优化
-
-## 2023-9-22
-
-### V1.4.7
-
-#### word
-
-1. 修复项目符号属于样式时,生成目录无法识别出项目符号问题
-
-## 2023-9-19
-
-### V1.4.6
-
-#### word
-
-1. 修复表格存在合并单元格,生成目录报错问题
-2. 修复单元格添加 run 对象,设置样式不生效问题
-3. 修复插入 word 后,中文样式重复问题
-
-### excel
-
-🔔 重要升级:`TSExcelFile`更名为`TSXlsxFile`,建议更改,旧名字现阶段仍可使用
-
-1. 整理`NewSheet`和`SetSheetVisible`代码
-
-#### excel
-
-1. 修复`NewSheet`excel 打开遇到提示错误问题
-
-## 2023-8-30
-
-### V1.4.5
-
-#### word
-
-1. 支持生成目录后返回目录对象的所有段落内容`目录对象.Paragraphs()`
-
-#### excel
-
-1. 修复`NewSheet`excel 打开遇到提示错误问题
-
-## 2023-8-28
-
-### V1.4.4
-
-#### word
-
-1. 修复`insertFile`样式错误问题
-2. 修复`insertFile`后删除段落再新增段落位置错误问题
-3. 修复生成目录中文编码未转换导致 word 打开失败问题
-
-## 2023-8-22
-
-### V1.4.3
-
-#### word
-
-支持段落判空`TParagraph.Empty()`
-
-## 2023-8-18
-
-### V1.4.2
-
-#### word
-
-支持插入另一个 word 内容`InsertFile(alias, fileName, posOpt)`
-
-## 2023-8-11
-
-### V1.4.1
-
-#### word
-
-修复插入表格后,设置样式 ID 无法全部生效问题
-
-## 2023-8-10
-
-### V1.4.0
-
-#### word
-
-支持三级以内目录设置字体,一级目录样式 ID 为`TOC1`,二级目录样式 ID 为`TOC2`,三级为`TOC3`,通过样式设置相关属性即可修改目录字体
-
-## 2023-8-7
-
-### V1.3.9
-
-#### word
-
-支持`AddHeading`时传入样式 Id
-
-## 2023-8-2
-
-### V1.3.8
-
-#### word
-
-修复插入图片后,设置对齐失效问题
-
-## 2023-7-26
-
-### V1.3.7
-
-#### word
-
-新增表格获取高度和宽度方法`table.Height(row) table.Width(col)`
-
-## 2023-7-19
-
-### V1.3.6
-
-#### word
-
-新增对表格的行高设置`table.RowHeight(row, height)`
-
-## 2023-7-14
-
-### V1.3.5
-
-#### word
-
-段落字体大小设置调整,对齐 VBA 设置
-
-## 2023-7-11
-
-### V1.3.4
-
-修复加密 Bug
-
-## 2023-7-4
-
-### V1.3.3
-
-word/excel 新增加密方法`SetPassword`,升级`OpenFile(alias, fileName, Password)`
-
-## 2023-6-26
-
-### V1.3.2
-
-修复.net 使用 rdo2 执行失败问题,使用 rdo2 时候,需调用`TOfficeInit()`初始化
-
-#### word
-
-1. 修复 word 模板`template.docx`执行失败问题
-
-## 2023-6-20
-
-更新部署方式,不再依赖`fmt_pubkrnl_plugin.dll`,详情见[README](./README.md)
-
-## 2023-4-26
-
-### V1.3.1
-
-`office_plugin.dll`与`liboffice_plugin.so(x86_64)`动态库更新
-
-## 2023-4-18
-
-### V1.3.0
-
-支持常用图片的高宽自动识别,支持图片格式有`gif, png, jpeg/jpeg, bmp`
-
-#### excel
-
-1. 支持 excel 程序设置字体样式粗体,斜体等此类问题的返回类型,如`style.Font.Bold`返回`true 或 false`
-
-## 2023-4-4
-
-### V1.2.9
-
-#### excel
-
-1. 修复超链接兼容性
-
-## 2023-3-31
-
-### V1.2.8
-
-#### excel
-
-1. 修复由.net 客户端导出的 excel 兼容性问题,`SetColWidth`、`Set/GetDefaultSheet`、`SetSheetHeaderFooter`
-
-## 2023-3-23
-
-### V1.2.7
-
-#### excel
-
-1. `GetSheetDefaultColWidth`默认返回值改为`nil`
-
-## 2023-3-22
-
-### V1.2.6
-
-#### excel
-
-1. 修复`SetCellValue`值为`nil`未生效问题
-
-## 2023-3-20
-
-### V1.2.5
-
-#### excel
-
-1. 修复数字样式获取再赋值再获取失效问题
-2. 支持单元格填入`nil`
-3. `GetComment`获取不到批注时返回内容改为`array(nil,nil)`
-4. 修复`Set/GetColWitdh`传入变量时会被修改问题
-
-## 2023-3-16
-
-### V1.2.4
-
-#### excel
-
-1. 修复样式获取数字样式失败问题
-2. 修复边框样式对角线未生效问题
-3. 修复获取属性未正确返回属性值问题
-
-## 2023-3-9
-
-### V1.2.3
-
-#### excel
-
-1. `GetComment`获取不到批注时返回内容改为`array(nil,nil)`
-2. 新增`RemoveComment`
-3. 新增`NewStyle-overload`
-4. 修复`ClearCell`引起文件报错
-
-## 2023-3-6
-
-### V1.2.2
-
-#### excel
-
-1. 修复`InsertRow`之后原有单元格合并错误问题
-2. 新增`GetCellValueType`
-3. 新增`GetComment`
-4. 新增`GetStyle`,获取的样式修改可通过`TStyle::Apply()`方法重新写入样式
-5. 新增`SetRowOutlineLevel`、`GetRowOutlineLevel`
-6. 新增`SetColOutlineLevel`、`GetColOutlineLevel`
-
-## 2023-2-24
-
-### V1.2.1
-
-#### excel
-
-1. Get 相关方法重新 Set 后不再覆盖原有设置,影响方法如下`Set(Get)PageMargins`,`Set(Get)CellHyperLink`,`Set(Get)PageLayout`,`Set(Get)CalcOptions`,`Set(Get)DefaultFont`,`Set(Get)SheetViewOptions`
-
-## 2023-2-23
-
-### V1.2.0
-
-#### excel
-
-1. 修复`GetCaclOptions`名称错误,更改为`GetCaclOptions`
-
-## 2023-2-22
-
-### V1.1.9
-
-#### excel
-
-1. 新增`InsertSheet`
-
-### V1.1.8
-
-#### excel
-
-1. 新增`GetCalcOptios`
-
-## 2023-2-21
-
-### V1.1.7
-
-#### word
-
-1. 新增`LoadFromMem`, `SavaToMem`
-
-#### excel
-
-1. 新增`LoadFromMem`, `SavaToMem`
-2. 新增重载方法`NewSheet`
-3. 新增`SetCalcOptions`
-4. 修复删除 sheet 报错问题
-
-## 2023-2-13
-
-### V1.1.6
-
-重大更新:移除了`template`文件夹
-
-## 2023-2-3
-
-### V1.1.5
-
-#### excel
-
-1. 修复`HSLToRGB`
-2. 修复`CoordinatesToCellName`
-
-## 2023-2-2
-
-### V1.1.4
-
-#### word
-
-1. 修复文本框插入图片兼容性问题
-2. 修复项目符号问题
-
-#### excel
-
-1. 新增`GetDefaultFont`
-
-## 2023-2-1
-
-### V1.1.3
-
-#### word
-
-1. 修复获取段落样式 ID 失败问题
-2. 修复文本框添加图片失败问题
-
-#### excel
-
-1. 新增`SetDefaultFont`
-
-## 2023-1-31
-
-### V1.1.2
-
-#### word
-
-1. 完善帮助文档
-
-#### excel
-
-1. 新增`SetPane`
-2. 新增`NewSheetView`
-3. 单元格样式新增属性“文本方向”: `TAlignment::TextRotation`
-
-## 2023-1-18
-
-### V1.1.1
-
-#### word
-
-1. 兼容文本框`TextBox`
-2. 修正`TParagraph::TabStops`
-3. 修复获取图标列表失败问题
-4. 修复`TDocxStyles::Default`
-
-#### excel
-
-1. 新增`ProtectSheet`和`UnProtectSheet`
-2. 修复`SetSheetViewOptions`和`GetSheetViewOptions`
-
-## 2023-1-16
-
-### V1.1.0
-
-#### word
-
-1. 修复页眉页脚显示问题
-2. 修复表格插入列宽失败问题
-3. 优化`InsertTable`性能
-
-#### excel
-
-1. 新增`CopySheet`
-
-## 2023-1-12
-
-### V1.0.9
-
-#### word
-
-1. 新增 ExecInnerTSl
-2. 新增修改 chart 图(数据)
-3. 修复`TNumbering::AddStyleByInnerXml`错误
-4. 完善帮助文档
-
-## 2023-1-11
-
-### V1.0.8
-
-#### word
-
-1. 修复`copyFormat`和`AddTableContent`
-
-## 2023-1-10
-
-### V1.0.7
-
-#### word
-
-1. 修复 chart 图兼容性问题
-
-### V1.0.6
-
-中文自动转换 API 由`class(xlsxXml).CodePage('中文');`调整为`TOfficeApi().CodePage('中文');`
-
-## 2023-1-5
-
-### V1.0.5
-
-❗ 部署方式发生变化,简化了环境部署,详见项目[README](./README.md)
-
-#### word
-
-1. 修复 Properties 执行报错
-2. 修复格式刷
-
-#### excel
-
-1. 移除 API `JoinCellName`
-2. 修改`RGBToHSL`与`HSLToRGB`返回值
-3. 修复获取超链接`GetCellHyperLink`失败问题
-4. 修复一个对象新建文件,打开文件之后引发的 xlsx 文件错误问题
-5. 修复`GetCoreProps`,`GetAppProps`失败问题
-
-## 2022-12-30
-
-### V1.0.4
-
-#### word
-
-1. 初步支持中文
-
-#### excel
-
-1. 初步支持中文
-2. `SetSheetName`,`NewSheet` 返回值不再是`[err, errinfo]`
-3. 修复清除单元格`ClearCell`不正确问题
-4. 修复`SetCellFormula`,`GetCellFormula`问题
-5. 修复`SetRowVisible`,`GetRowVisible`,`SetSheetVisible`问题
-
-如何设置字符集(中文支持)?
-★ 用户的脚本可能是 UFT8 格式,或可能是 GBK 码格式,系统提供 API 自动设置当前字符集环境:`class(xlsxXml).CodePage('中文');` 在文件头设置该代码后,系统会自动检测当前的环境字符集
+# 更新日志
+
+## 2025-10-29
+
+### V1.8.2
+
+### word
+
+1. 升级段落判空`Empty`
+2. 修复`FileName`总是返回 gbk
+
+### excel
+
+1. 修复`CopySheet`依赖的`ReplaceStrByReg`公共方法问题,不再依赖该方法
+2. 修复`FileName`总是返回 gbk
+
+## 2025-7-22
+
+### V1.8.1
+
+#### word
+
+1. **feat**: 支持同一段落多图片文字
+
+```cpp
+picture := TOfficeObj('TPicture');
+picture.Descr := '插入图像案例';
+picture.Image := TOfficeTemplate('tinysoft.gif',true);
+
+docx := new TSDocxFile();
+docx.NewFile();
+p := new TOfficeObj('TParagraph');
+r := p.AddRun();
+r.SetText("abc");
+r := p.AddRun();
+r.SetDrawing(picture, docx); // 需要一个图片对象和TSDocxFile对象
+r := p.AddRun();
+r.SetText("def");
+docx.AddParagraph(p, -1);
+```
+
+## 2025-6-23
+
+### V1.8.0
+
+#### word
+
+1. **feat**: 支持添加自定目录`TSDocxFile.AddCustomTableContent`
+2. **fix**: 修复单元格内容换行,自定义属性失效问题
+
+## 2025-2-28
+
+### V1.7.9
+
+#### word
+
+1. **feat**:支持目录自定义宽度`TOfficeApi().Get("TTableContent-tab-position")`
+2. **fix**:插入图片 id 计算错误,导致`office 2016 16.0.4266`版本不兼容
+
+## 2025-1-22
+
+### V1.7.8
+
+❗ 解决`TSDocxFile`和`TSXlsxFile`内存泄露
+
+#### word
+
+1. **fix**:`CreateTable`由于`fieldnames.tsf`差异报错
+
+## 2025-1-17
+
+### V1.7.7
+
+#### word
+
+1. **feat**:`TPicture`对象设置画布大小`CanvasWidth`、`CanvasHeight`
+2. **fix**:`InsertTable`表格数据不对齐报错
+
+#### excel
+
+1. **fix**:`SetColOutlineLevel`错误
+
+## 2025-1-8
+
+### V1.7.6
+
+#### excel
+
+1. **fix**:`SetRowHeight`单元格不存在无法设置
+2. **fix**:`Set[Get]ColVisible`修改传入的列参数
+
+## 2025-1-7
+
+### V1.7.5
+
+#### word
+
+1. 支持获取`sdt`标签类型的内容(如文本框),通过`TSDocxFile.Sdts()`获取文档的的所有`sdt`
+
+> ```pascal
+> sdts := docx.Sdts();
+> paragraphs := sdts[0].Paragraphs();
+> textbox := paragraphs[1].TextBoxs();
+> ```
+
+## 2024-12-23
+
+### V1.7.4
+
+#### word
+
+1. `insertfile`时候默认添加`w16du`命名空间
+
+## 2024-12-05
+
+### V1.7.3
+
+#### word
+
+1. `insertfile`支持 ole 对象的插入,如 wmf 公式,excel 表格
+
+## 2024-12-04
+
+### V1.7.2
+
+#### word
+
+1. 支持单元格标题生成到目录,需要额外设置`TOfficeApi().SetCellTableContent(true);`,默认是`false`
+
+## 2024-11-19
+
+### V1.7.1
+
+#### word
+
+1. 对生成目录的 xml 结构进行更改
+2. 修复引用 excel 错误
+
+#### excel
+
+1. 修复获取`comment`时候,换行符问题
+
+## 2024-7-8
+
+### V1.7.0
+
+#### excel
+
+1. 修复`CopySheet`操作后,文件报错问题
+
+## 2024-5-6
+
+### V1.6.9
+
+#### excel
+
+1. 修复单元格继承列样式失败问题
+
+## 2024-4-12
+
+### V1.6.8
+
+### excel
+
+1. 修复`SetCellValue`单元格对列样式的识别,不需要手动用格式刷对所有单元格刷新样式
+
+## 2024-3-21
+
+### V1.6.7
+
+#### word
+
+1. 新增对象的`Delete`方法,可以通过`Delete(name)`删除对象的指定属性
+2. 新增`TPageNumType`对象
+3. 扩展`TDocSection`方法,`AppendReference`和`AddReference`,前者是对已有的`Section`对象添加`TReference`对象,而后者是对新的`Section`对象添加
+
+## 2024-3-14
+
+### V1.6.6
+
+#### word
+
+1. 修复`TDocSection`节点获取错误
+2. 支持分栏自定义宽度
+
+## 2024-3-4
+
+### V1.6.5
+
+支持扩展扩展模块
+
+### excel
+
+1. 样式获取报错问题
+
+### V1.6.4
+
+#### word
+
+1. 修复`InsertTable`内容`\n`无换行问题
+2. `TextBox`支持`Clear`清空内容
+
+#### word
+
+1. 修复目录生成陷入死循环问题
+
+## 2024-2-23
+
+### V1.6.3
+
+#### word
+
+1. 修复目录生成陷入死循环问题
+
+## 2024-2-7
+
+### V1.6.2
+
+#### word
+
+1. 修复设置段落属性`TextAlignment`失败问题
+
+## 2024-2-5
+
+### V1.6.1
+
+#### excel
+
+1. 修复 excel 文件单元格原有类型与 tsl 类型转换报错问题(trystrtofloat)
+
+## 2024-2-5
+
+### V1.6.0
+
+#### excel
+
+1. 兼容 Excel2016,通过`NewSheet/InsertSheet`创建工作表不再提示修复错误
+2. 修复带有表头的二维数组通过`InsertTable`写入数据只写入表头未写入数据问题
+
+## 2024-1-31
+
+### V1.5.9
+
+修复`tsl`脚本格式为`utf8`时无法打开中文命名的文件
+
+### V1.5.8
+
+#### excel
+
+1. 支持写入单元格内容时判断当前单元格的数据类型,并以单元格数据类型为准
+
+## 2024-1-17
+
+### V1.5.7
+
+#### excel
+
+1. 修复单元格写入浮点数数据 bug,如写入 0.0,excel 某些版本打开会显示 0.00000000000000000
+
+## 2024-1-11
+
+### V1.5.6
+
+#### excel
+
+1. 修复单元格写入浮点数数据,精度丢失问题
+
+## 2023-12-29
+
+### V1.5.5
+
+#### word
+
+1. 修复生成的目录打开后重新生成页码可能产生页码失败问题
+
+#### excel
+
+1. 修复`InsertTable`与``SetCellValue`返回值问题
+
+## 2023-12-18
+
+### V1.5.4
+
+#### excel
+
+1. 优化`InsertTable`的写入效率
+
+## 2023-12-11
+
+### V1.5.3
+
+#### excel
+
+1. 支持写入单元格时且单元格不存在时,新的单元格会沿用设置的样式 ID(若有设置)
+
+## 2023-12-1
+
+### V1.5.2
+
+#### excel
+
+1. 修复公式中文转码问题
+
+## 2023-11-15
+
+### V1.5.1
+
+1. 支持识别图片中含有`0xffc2`段的 jpg 格式的大小
+
+## 2023-11-14
+
+### V1.5.0
+
+#### word
+
+1. 新增对脚注的支持,方法`TSDocxFile.FootnotesObject().Add`
+2. `TSDocxFile.InsertFile`支持脚注插入
+
+### excel
+
+1. 升级`TSXlsxFile.CopySheet`
+
+## 2023-10-16
+
+### V1.4.9
+
+#### word
+
+1. 新增`TSDocxFile.InsertFile`重载方法
+2. 新增`Table.SetStyle`方法
+
+## 2023-9-27
+
+### V1.4.8
+
+#### word
+
+1. 支持`Inserttable`时候加入对单元格样式的设置
+2. 新增`TSDocxFile.LastParagraph`方法
+3. 新增`TParagraph.PrevParagraph`方法
+4. 性能优化
+
+## 2023-9-22
+
+### V1.4.7
+
+#### word
+
+1. 修复项目符号属于样式时,生成目录无法识别出项目符号问题
+
+## 2023-9-19
+
+### V1.4.6
+
+#### word
+
+1. 修复表格存在合并单元格,生成目录报错问题
+2. 修复单元格添加 run 对象,设置样式不生效问题
+3. 修复插入 word 后,中文样式重复问题
+
+### excel
+
+🔔 重要升级:`TSExcelFile`更名为`TSXlsxFile`,建议更改,旧名字现阶段仍可使用
+
+1. 整理`NewSheet`和`SetSheetVisible`代码
+
+#### excel
+
+1. 修复`NewSheet`excel 打开遇到提示错误问题
+
+## 2023-8-30
+
+### V1.4.5
+
+#### word
+
+1. 支持生成目录后返回目录对象的所有段落内容`目录对象.Paragraphs()`
+
+#### excel
+
+1. 修复`NewSheet`excel 打开遇到提示错误问题
+
+## 2023-8-28
+
+### V1.4.4
+
+#### word
+
+1. 修复`insertFile`样式错误问题
+2. 修复`insertFile`后删除段落再新增段落位置错误问题
+3. 修复生成目录中文编码未转换导致 word 打开失败问题
+
+## 2023-8-22
+
+### V1.4.3
+
+#### word
+
+支持段落判空`TParagraph.Empty()`
+
+## 2023-8-18
+
+### V1.4.2
+
+#### word
+
+支持插入另一个 word 内容`InsertFile(alias, fileName, posOpt)`
+
+## 2023-8-11
+
+### V1.4.1
+
+#### word
+
+修复插入表格后,设置样式 ID 无法全部生效问题
+
+## 2023-8-10
+
+### V1.4.0
+
+#### word
+
+支持三级以内目录设置字体,一级目录样式 ID 为`TOC1`,二级目录样式 ID 为`TOC2`,三级为`TOC3`,通过样式设置相关属性即可修改目录字体
+
+## 2023-8-7
+
+### V1.3.9
+
+#### word
+
+支持`AddHeading`时传入样式 Id
+
+## 2023-8-2
+
+### V1.3.8
+
+#### word
+
+修复插入图片后,设置对齐失效问题
+
+## 2023-7-26
+
+### V1.3.7
+
+#### word
+
+新增表格获取高度和宽度方法`table.Height(row) table.Width(col)`
+
+## 2023-7-19
+
+### V1.3.6
+
+#### word
+
+新增对表格的行高设置`table.RowHeight(row, height)`
+
+## 2023-7-14
+
+### V1.3.5
+
+#### word
+
+段落字体大小设置调整,对齐 VBA 设置
+
+## 2023-7-11
+
+### V1.3.4
+
+修复加密 Bug
+
+## 2023-7-4
+
+### V1.3.3
+
+word/excel 新增加密方法`SetPassword`,升级`OpenFile(alias, fileName, Password)`
+
+## 2023-6-26
+
+### V1.3.2
+
+修复.net 使用 rdo2 执行失败问题,使用 rdo2 时候,需调用`TOfficeInit()`初始化
+
+#### word
+
+1. 修复 word 模板`template.docx`执行失败问题
+
+## 2023-6-20
+
+更新部署方式,不再依赖`fmt_pubkrnl_plugin.dll`,详情见[README](./README.md)
+
+## 2023-4-26
+
+### V1.3.1
+
+`office_plugin.dll`与`liboffice_plugin.so(x86_64)`动态库更新
+
+## 2023-4-18
+
+### V1.3.0
+
+支持常用图片的高宽自动识别,支持图片格式有`gif, png, jpeg/jpeg, bmp`
+
+#### excel
+
+1. 支持 excel 程序设置字体样式粗体,斜体等此类问题的返回类型,如`style.Font.Bold`返回`true 或 false`
+
+## 2023-4-4
+
+### V1.2.9
+
+#### excel
+
+1. 修复超链接兼容性
+
+## 2023-3-31
+
+### V1.2.8
+
+#### excel
+
+1. 修复由.net 客户端导出的 excel 兼容性问题,`SetColWidth`、`Set/GetDefaultSheet`、`SetSheetHeaderFooter`
+
+## 2023-3-23
+
+### V1.2.7
+
+#### excel
+
+1. `GetSheetDefaultColWidth`默认返回值改为`nil`
+
+## 2023-3-22
+
+### V1.2.6
+
+#### excel
+
+1. 修复`SetCellValue`值为`nil`未生效问题
+
+## 2023-3-20
+
+### V1.2.5
+
+#### excel
+
+1. 修复数字样式获取再赋值再获取失效问题
+2. 支持单元格填入`nil`
+3. `GetComment`获取不到批注时返回内容改为`array(nil,nil)`
+4. 修复`Set/GetColWitdh`传入变量时会被修改问题
+
+## 2023-3-16
+
+### V1.2.4
+
+#### excel
+
+1. 修复样式获取数字样式失败问题
+2. 修复边框样式对角线未生效问题
+3. 修复获取属性未正确返回属性值问题
+
+## 2023-3-9
+
+### V1.2.3
+
+#### excel
+
+1. `GetComment`获取不到批注时返回内容改为`array(nil,nil)`
+2. 新增`RemoveComment`
+3. 新增`NewStyle-overload`
+4. 修复`ClearCell`引起文件报错
+
+## 2023-3-6
+
+### V1.2.2
+
+#### excel
+
+1. 修复`InsertRow`之后原有单元格合并错误问题
+2. 新增`GetCellValueType`
+3. 新增`GetComment`
+4. 新增`GetStyle`,获取的样式修改可通过`TStyle::Apply()`方法重新写入样式
+5. 新增`SetRowOutlineLevel`、`GetRowOutlineLevel`
+6. 新增`SetColOutlineLevel`、`GetColOutlineLevel`
+
+## 2023-2-24
+
+### V1.2.1
+
+#### excel
+
+1. Get 相关方法重新 Set 后不再覆盖原有设置,影响方法如下`Set(Get)PageMargins`,`Set(Get)CellHyperLink`,`Set(Get)PageLayout`,`Set(Get)CalcOptions`,`Set(Get)DefaultFont`,`Set(Get)SheetViewOptions`
+
+## 2023-2-23
+
+### V1.2.0
+
+#### excel
+
+1. 修复`GetCaclOptions`名称错误,更改为`GetCaclOptions`
+
+## 2023-2-22
+
+### V1.1.9
+
+#### excel
+
+1. 新增`InsertSheet`
+
+### V1.1.8
+
+#### excel
+
+1. 新增`GetCalcOptios`
+
+## 2023-2-21
+
+### V1.1.7
+
+#### word
+
+1. 新增`LoadFromMem`, `SavaToMem`
+
+#### excel
+
+1. 新增`LoadFromMem`, `SavaToMem`
+2. 新增重载方法`NewSheet`
+3. 新增`SetCalcOptions`
+4. 修复删除 sheet 报错问题
+
+## 2023-2-13
+
+### V1.1.6
+
+重大更新:移除了`template`文件夹
+
+## 2023-2-3
+
+### V1.1.5
+
+#### excel
+
+1. 修复`HSLToRGB`
+2. 修复`CoordinatesToCellName`
+
+## 2023-2-2
+
+### V1.1.4
+
+#### word
+
+1. 修复文本框插入图片兼容性问题
+2. 修复项目符号问题
+
+#### excel
+
+1. 新增`GetDefaultFont`
+
+## 2023-2-1
+
+### V1.1.3
+
+#### word
+
+1. 修复获取段落样式 ID 失败问题
+2. 修复文本框添加图片失败问题
+
+#### excel
+
+1. 新增`SetDefaultFont`
+
+## 2023-1-31
+
+### V1.1.2
+
+#### word
+
+1. 完善帮助文档
+
+#### excel
+
+1. 新增`SetPane`
+2. 新增`NewSheetView`
+3. 单元格样式新增属性“文本方向”: `TAlignment::TextRotation`
+
+## 2023-1-18
+
+### V1.1.1
+
+#### word
+
+1. 兼容文本框`TextBox`
+2. 修正`TParagraph::TabStops`
+3. 修复获取图标列表失败问题
+4. 修复`TDocxStyles::Default`
+
+#### excel
+
+1. 新增`ProtectSheet`和`UnProtectSheet`
+2. 修复`SetSheetViewOptions`和`GetSheetViewOptions`
+
+## 2023-1-16
+
+### V1.1.0
+
+#### word
+
+1. 修复页眉页脚显示问题
+2. 修复表格插入列宽失败问题
+3. 优化`InsertTable`性能
+
+#### excel
+
+1. 新增`CopySheet`
+
+## 2023-1-12
+
+### V1.0.9
+
+#### word
+
+1. 新增 ExecInnerTSl
+2. 新增修改 chart 图(数据)
+3. 修复`TNumbering::AddStyleByInnerXml`错误
+4. 完善帮助文档
+
+## 2023-1-11
+
+### V1.0.8
+
+#### word
+
+1. 修复`copyFormat`和`AddTableContent`
+
+## 2023-1-10
+
+### V1.0.7
+
+#### word
+
+1. 修复 chart 图兼容性问题
+
+### V1.0.6
+
+中文自动转换 API 由`class(xlsxXml).CodePage('中文');`调整为`TOfficeApi().CodePage('中文');`
+
+## 2023-1-5
+
+### V1.0.5
+
+❗ 部署方式发生变化,简化了环境部署,详见项目[README](./README.md)
+
+#### word
+
+1. 修复 Properties 执行报错
+2. 修复格式刷
+
+#### excel
+
+1. 移除 API `JoinCellName`
+2. 修改`RGBToHSL`与`HSLToRGB`返回值
+3. 修复获取超链接`GetCellHyperLink`失败问题
+4. 修复一个对象新建文件,打开文件之后引发的 xlsx 文件错误问题
+5. 修复`GetCoreProps`,`GetAppProps`失败问题
+
+## 2022-12-30
+
+### V1.0.4
+
+#### word
+
+1. 初步支持中文
+
+#### excel
+
+1. 初步支持中文
+2. `SetSheetName`,`NewSheet` 返回值不再是`[err, errinfo]`
+3. 修复清除单元格`ClearCell`不正确问题
+4. 修复`SetCellFormula`,`GetCellFormula`问题
+5. 修复`SetRowVisible`,`GetRowVisible`,`SetSheetVisible`问题
+
+如何设置字符集(中文支持)?
+★ 用户的脚本可能是 UFT8 格式,或可能是 GBK 码格式,系统提供 API 自动设置当前字符集环境:`class(xlsxXml).CodePage('中文');` 在文件头设置该代码后,系统会自动检测当前的环境字符集