From a2c3905cd003a953bfddef1a067a43ab6889e2e0 Mon Sep 17 00:00:00 2001 From: csh Date: Wed, 29 Oct 2025 15:47:51 +0800 Subject: [PATCH] v1.8.2 --- funcext/TSOffice/TOfficeObj.tsf | 31 +- funcext/TSOffice/TSDocxFile.tsf | 1333 ++++---- funcext/TSOffice/TSXlsxFile.tsf | 2109 ++++++------ funcext/TSOffice/worksheet/xlsxWorkBook.tsf | 3279 ++++++++++--------- 更新日志.md | 1548 ++++----- 5 files changed, 4174 insertions(+), 4126 deletions(-) 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('中文');` 在文件头设置该代码后,系统会自动检测当前的环境字符集