diff --git a/DocxFile使用帮助.docx b/DocxFile使用帮助.docx index 5c0f868..b5f8fbb 100644 Binary files a/DocxFile使用帮助.docx and b/DocxFile使用帮助.docx differ diff --git a/XlsxFile使用帮助.xlsx b/XlsxFile使用帮助.xlsx index c410454..f2b6b56 100644 Binary files a/XlsxFile使用帮助.xlsx and b/XlsxFile使用帮助.xlsx differ diff --git a/funcext/TSOffice/TOfficeObj.tsf b/funcext/TSOffice/TOfficeObj.tsf index d308c3a..ca0f457 100644 --- a/funcext/TSOffice/TOfficeObj.tsf +++ b/funcext/TSOffice/TOfficeObj.tsf @@ -1,4 +1,4 @@ -// Version 1.4.9 +// Version 1.5.0 Function TOfficeObj(n); Begin @@ -213,6 +213,10 @@ Begin return new TText(); "trun": return new TRun(); + "tfootnoteimpl": + return new TFootnoteImpl(); + "tfootnotebody": + return new TFootnoteBody(); "tbookmark": return new TBookMark(); "tfldsimple": @@ -5504,7 +5508,7 @@ type TBr=class(NodeInfo) Function GetAttrs(); override; Begin - return array(("Type", "w:type", Type, "")) union ExtAttr; + return array(("Type", "w:type", Type, ""),("Clear", "w:left", Clear, "")) union ExtAttr; End; Function GetChildren(); override; @@ -5514,6 +5518,7 @@ type TBr=class(NodeInfo) //Attributes Type; + Clear; //Nodes End; @@ -5724,21 +5729,39 @@ type TwrPr=class(NodeInfo) ,('field':'Bold','name':'w:b','obj':Bold,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'黑体', 'class':'') ,('field':'noProof','name':'w:noProof','obj':noProof,'attrEx':'','nodeType':'empty','attrName':'', 'desc':'不检查拼写或语法)', 'class':'') ,('field':'Color','name':'w:color','obj':Color,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'字体颜色', 'class':'') - ,('field':'bCs','name':'w:bCs','obj':bCs,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'复杂脚本黑体', 'class':'') + ,('field':'bCs','name':'w:bCs','obj':bCs,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'黑体,适用于从右到左语言文本', 'class':'') + ,('field':'Caps','name':'w:caps','obj':Caps,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'首字母大写', 'class':'') + ,('field':'SmallCaps','name':'w:smallCaps','obj':SmallCaps,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'首字母小写', 'class':'') + ,('field':'Emboss','name':'w:emboss','obj':Emboss,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'浮雕效果', 'class':'') + ,('field':'Em','name':'w:em','obj':Em,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'强调标志的类型,取值:"comma", "dot", "circle", "underDot", "none"', 'class':'') + ,('field':'Imprint','name':'w:imprint','obj':Imprint,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'刻雕效果', 'class':'') + ,('field':'Vanish','name':'w:vanish','obj':Vanish,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'隐藏', 'class':'') ,('field':'Italic','name':'w:i','obj':Italic,'attrEx':'','nodeType':'empty','attrName':'', 'desc':'斜体', 'class':'') + ,('field':'ICs','name':'w:iCs','obj':ICs,'attrEx':'','nodeType':'empty','attrName':'', 'desc':'斜体,适用于从右到左语言文本', 'class':'') ,('field':'Strike','name':'w:strike','obj':Strike,'attrEx':'','nodeType':'empty','attrName':'', 'desc':'单个删除线', 'class':'') ,('field':'dStrike','name':'w:dstrike','obj':dStrike,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'两个删除线', 'class':'') ,('field':'kern','name':'w:kern','obj':kern,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'字体紧排', 'class':'') ,('field':'Sz','name':'w:sz','obj':Sz,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'字体大小', 'class':'') ,('field':'szCs','name':'w:szCs','obj':szCs,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'复杂脚本字体大小', 'class':'') + ,('field':'Ligatures','name':'w14:ligatures','obj':Ligatures,'attrEx':'w14:val','nodeType':'','attrName':'', 'desc':'连字方式', 'class':'') ,('field':'U','name':'w:u','obj':U,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'下划线', 'class':'') - ,('field':'vertAlign','name':'w:vertAlign','obj':vertAlign,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'对齐方式.', 'class':'') + ,('field':'UColor','name':'w:u','obj':UColor,'attrEx':'w:color','nodeType':'','attrName':'', 'desc':'下划线颜色', 'class':'') + ,('field':'vertAlign','name':'w:vertAlign','obj':vertAlign,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'对齐方式.取值:"subscript", "superscript"', 'class':'') ,('field':'eastAsia','name':'w:lang','obj':eastAsia,'attrEx':'w:eastAsia','nodeType':'','attrName':'', 'desc':'处理使用东亚字符的运行内容时应使用的语言.', 'class':'') ,('field':'Lang','name':'w:lang','obj':Lang,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'处理使用拉丁字符的本次运行内容时用于检查拼写和语法的语言.', 'class':'') ,('field':'bidi','name':'w:lang','obj':bidi,'attrEx':'w:bidi','nodeType':'','attrName':'', 'desc':'处理使用复杂脚本字符的运行内容时应使用的语言.', 'class':'') ,('field':'Del','name':Del.NodeName,'obj':Del,'attrEx':'','nodeType':'','attrName':'', 'desc':'disable', 'class':'TOptInfo') ,('field':'StyleId','name':'w:rStyle','obj':StyleId,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'disable', 'class':'') ,('field':'WebHidden','name':'w:webHidden','obj':WebHidden,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'disable', 'class':'') + ,('field':'ContextualAlternates','name':'w14:cntxtAlts','obj':ContextualAlternates,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'对指定的字体启用上下文替代字', 'class':'') + ,('field':'SnapToGrid','name':'w:snapToGrid','obj':SnapToGrid,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'是否忽略选定文本每行中的字符数', 'class':'') + ,('field':'NumForm','name':'w14:numForm','obj':NumForm,'attrEx':'w14:val','nodeType':'','attrName':'', 'desc':'OpenType 字体的数字形式', 'class':'') + ,('field':'NumSpacing','name':'w14:numSpacing','obj':NumSpacing,'attrEx':'w14:val','nodeType':'','attrName':'', 'desc':'字体的数字间距', 'class':'') + ,('field':'OutLine','name':'w:outline','obj':OutLine,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'镂空字体', 'class':'') + ,('field':'W','name':'w:w','obj':W,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'字体的缩放百分比,取值0~600', 'class':'') + ,('field':'Position','name':'w:position','obj':Position,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'文本相对于基准线的位置', 'class':'') + ,('field':'Shadow','name':'w:shadow','obj':Shadow,'attrEx':'w:val','nodeType':'empty','attrName':'', 'desc':'阴影字体', 'class':'') + ,('field':'Spacing','name':'w:spacing','obj':Spacing,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'字符间距', 'class':'') ) union ExtNodes; End; @@ -5766,7 +5789,7 @@ type TwrPr=class(NodeInfo) End; Function readSize(); Begin - return int(Sz / 2); + return Value("Sz") / 2; End; //Attributes @@ -5777,13 +5800,22 @@ type TwrPr=class(NodeInfo) noProof; Color; bCs; + Caps; + SmallCaps; + Emboss; + Em; + Imprint; + Vanish; Italic; + ICs; Strike; dStrike; kern; Sz; szCs; + Ligatures; U; + UColor; vertAlign; eastAsia; Lang; @@ -5791,6 +5823,15 @@ type TwrPr=class(NodeInfo) Del; StyleId; WebHidden; + ContextualAlternates; + SnapToGrid; + NumForm; + NumSpacing; + OutLine; + W; + Position; + Shadow; + Spacing; End; /////////////////////////////////////////////////////////////// @@ -7051,6 +7092,9 @@ type TRunImpl=class(NodeInfo) ,('field':'T','name':'w:t','obj':T,'attrEx':'','nodeType':'pcdata','attrName':'', 'desc':'', 'class':'') ,('field':'Space','name':'w:t','obj':Space,'attrEx':'xml:space','nodeType':'','attrName':'', 'desc':'', 'class':'') ,('field':'Drawing','name':Drawing.NodeName,'obj':Drawing,'attrEx':'','nodeType':'','attrName':'', 'desc':'', 'class':'TwDrawing') + ,('field':'FootnoteReference','name':'w:footnoteReference','obj':FootnoteReference,'attrEx':'w:id','nodeType':'','attrName':'', 'desc':'', 'class':'') + ,('field':'Separator','name':'w:separator','obj':Separator,'attrEx':'','nodeType':'empty','attrName':'', 'desc':'', 'class':'') + ,('field':'ContinuationSeparator','name':'w:continuationSeparator','obj':ContinuationSeparator,'attrEx':'','nodeType':'empty','attrName':'', 'desc':'', 'class':'') ) union ExtNodes; End; @@ -7069,6 +7113,91 @@ type TRunImpl=class(NodeInfo) T; Space; Drawing; + FootnoteReference; + Separator; + ContinuationSeparator; +End; + +/////////////////////////////////////////////////////////////// +/// TFootnoteImpl +/////////////////////////////////////////////////////////////// +type TFootnoteImpl=class(NodeInfo) + Function Create(); overload; + Begin + Create(nil, 'w:footnote'); + End; + + Function Create(p, name); overload; + Begin + Class(NodeInfo).Create(p, name); + Init(); + End; + + Function Init(); + Begin + //TODO... + End; + + Function InitRootNode(node); + Begin + RootObj := node; + End; + + Function GetAttrs(); override; + Begin + return array(("Type", "w:type", Type, ""),("Id", "w:id", Id, "")) union ExtAttr; + End; + + Function GetChildren(); override; + Begin + return ExtNodes; + End; + + //Attributes + Type; + Id; + + //Nodes +End; + +/////////////////////////////////////////////////////////////// +/// TFootnoteBody +/////////////////////////////////////////////////////////////// +type TFootnoteBody=class(NodeInfo, TFootnotes) + Function Create(); overload; + Begin + Create(nil, 'w:footnote'); + End; + + Function Create(p, name); overload; + Begin + Class(NodeInfo).Create(p, name); + Init(); + End; + + Function Init(); + Begin + //TODO... + End; + + Function InitRootNode(node); + Begin + RootObj := node; + End; + + Function GetAttrs(); override; + Begin + return ExtAttr; + End; + + Function GetChildren(); override; + Begin + return ExtNodes; + End; + + //Attributes + + //Nodes End; /////////////////////////////////////////////////////////////// @@ -8592,7 +8721,7 @@ type TwTblPr=class(NodeInfo) Begin return array(('field':'StyleID','name':'w:tblStyle','obj':StyleID,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'样式ID', 'class':'') ,('field':'Width','name':'w:tblW','obj':Width,'attrEx':'w:w','nodeType':'','attrName':'', 'desc':'表格宽度', 'class':'') - ,('field':'WidthType','name':'w:tblW','obj':WidthType,'attrEx':'w:type','nodeType':'','attrName':'', 'desc':'宽度属性的单位。如"auto"表示根据内容调整,此时需要设置"width=0"; "pct"是按比例设置,转换比例是50, 10%时候需要width为500; "dax"表示自定义宽度,此时width单位是twips,即1cm=567twips', 'class':'') + ,('field':'WidthType','name':'w:tblW','obj':WidthType,'attrEx':'w:type','nodeType':'','attrName':'', 'desc':'宽度属性的单位。如"auto"表示根据内容调整,此时需要设置"width=0"; "pct"是按比例设置,转换比例是50, 10%时候需要width为500; "dxa"表示自定义宽度,此时width单位是twips,即1cm=567twips', 'class':'') ,('field':'Val','name':'w:tblLook','obj':Val,'attrEx':'w:val','nodeType':'','attrName':'', 'desc':'表格条件格式样式:表格样式最多可以指定六种不同的可选条件格式,如首行、尾行、首列、尾列等(16进制short)', 'class':'') ,('field':'NoVBand','name':'w:tblLook','obj':NoVBand,'attrEx':'w:noVBand','nodeType':'','attrName':'', 'desc':'disable', 'class':'') ,('field':'NoHBand','name':'w:tblLook','obj':NoHBand,'attrEx':'w:noHBand','nodeType':'','attrName':'', 'desc':'disable', 'class':'') @@ -10767,12 +10896,42 @@ Type TParagraph = Class(DocObject, TParagraphImpl) ///在段落对象后面追加TRun对象 ///返回TRun对象 - Function AppendRun(); + Function AppendRun();overload; Begin node := node_.InsertEndChild('element','w:r'); node.InsertFirstChild('element', 'w:rPr'); return new TRun(node); End; + + ///在段落某个TRun对象之后添加TRun对象 + ///run:TRun对象 + ///返回TRun对象 + Function AppendRun(run);overload; + Begin + node := node_.InsertAfterChild(run.Root(), 'element','w:r'); + node.InsertFirstChild('element', 'w:rPr'); + return new TRun(node); + End; + + ///在段落对象开头添加TRun对象 + ///返回TRun对象 + Function PrependRun();overload; + Begin + node := node_.FirstChildElement('w:r'); + node := node_.InsertBeforeChild(node, 'element', 'w:r'); + node.InsertFirstChild('element', 'w:rPr'); + return new TRun(node); + End; + + ///在段落某个TRun对象之前添加TRun对象 + ///run:TRun对象 + ///返回TRun对象 + Function PrependRun(run);overload; + Begin + node := node_.InsertBeforeChild(run.Root(), 'element', 'w:r'); + node.InsertFirstChild('element', 'w:rPr'); + return new TRun(node); + End; ///段落中全部的修改标记(修订删除、修订插入) ///返回:TRevision对象列表,array(TRevision...); @@ -14179,6 +14338,97 @@ Type TRow = Class(DocObject, TwTr) End; End; +Type TFootnote = Class(TDocumentBody, TFootnoteImpl) + Function Create();overload; + Begin + Create(nil); + End; + + Function Create(node);overload; + Begin + Class(TDocumentBody).Create(node); + Create(node, 'w:footnote'); + node_ := node; + End; + + Function Create(pNode, name);overload; + Begin + Class(TFootnoteImpl).Create(nil, name); + InitRootNode(pNode); + End; + + node_; +End; + +Type TFootnotes = Class + Function InitFootnotes(zip); + Begin + zip_ := zip; + file := 'word/footnotes.xml'; + footnotesXml_ := zip.Get(file); + idMap_ := array(); + maxId_ := 0; + if not ifObj(footnotesXml_) then + begin + content := ''; + zip_.Add(file, content); + footnotesXml_ := zip.Get(file); + // content_type + ctXml := zip_.Get('[Content_Types].xml'); + class(TSXml).AddOverrideContentType(ctXml, '/word/footnotes.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml'); + // document.xml.rels + rels := zip_.Get('word/_rels/document.xml.rels'); + [maxRid, target, id] := class(TSXml).FindRelationshipRid(rels, ''); + maxRid++; + class(TSXml).AddRelationshipRid(rels, 'footnotes.xml', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes', 'rId' $ maxRid); + end + footnotesNode_ := footnotesXml_.FirstChildElement("w:footnotes"); + node := footnotesNode_.FirstChildElement('w:footnote'); + while ifObj(node) do + begin + obj := new TFootnote(node); + id := obj.Value('Id', true); + idMap_[id] := obj; + id := StrToIntDef(id, 0); + if id > maxId_ then maxId_ := id; + node := node.NextElement(); + end + End; + + Function Add(); + Begin + node := footnotesNode_.InsertEndChild('element', 'w:footnote'); + node.SetAttribute('w:id', ++maxId_); + obj := new TFootnote(node); + obj.Id := maxId_; + idMap_[maxId_] := obj; + return obj; + End; + + Function GetFootnote(id); + Begin + return idMap_[id]; + End; + + Function CopyFootnote(obj); + Begin + marshal := obj.Root().Marshal()[0]; + node := footnotesNode_.InsertEndChild(marshal); + node.SetAttribute('w:id', ++maxId_); + obj := new TFootnote(node); + obj.Id := maxId_; + idMap_[maxId_] := obj; + return obj; + End; + +private + zip_; + footnotesXml_; + footnotesNode_; + idMap_; + maxId_; +End; + //w:tc Type TCell = Class(TDocumentBody, TWTc) Function Create();overload; @@ -14331,6 +14581,11 @@ Type TTable = Class(DocObject, TTableImpl) End; InitRootNode(node); End; + + Function GetCell(r, c); + Begin + return cells_[r-1, c-1]; + End; ///设置表格数据 ///data: table,数据表 diff --git a/funcext/TSOffice/TSDocxFile.tsf b/funcext/TSOffice/TSDocxFile.tsf index 4940dfc..805e0ba 100644 --- a/funcext/TSOffice/TSDocxFile.tsf +++ b/funcext/TSOffice/TSDocxFile.tsf @@ -1,4 +1,4 @@ -// Version 1.4.9 +// Version 1.5.0 Type TSDocxFile = Class ///Version: V1.0 2022-09-20 @@ -559,6 +559,17 @@ Type TSDocxFile = Class 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':'错误信息'))) @@ -616,6 +627,7 @@ private zipfile_; //压缩文件对象 document_; //Document对象 styleObj_; + footnotesObj_; numberingObj_; DocPrId_; End; diff --git a/funcext/TSOffice/TSExcelFile.tsf b/funcext/TSOffice/TSExcelFile.tsf index 5aa4034..079edce 100644 --- a/funcext/TSOffice/TSExcelFile.tsf +++ b/funcext/TSOffice/TSExcelFile.tsf @@ -1,4 +1,4 @@ -// Version 1.4.9 +// Version 1.5.0 Type TSExcelFile = Class ///Version: V1.0 2022-08-08 @@ -915,7 +915,7 @@ Type TSExcelFile = Class ///复制Sheet ///sourceSheet: 源工作表 - ///destSheet: 目的工作表 + ///destSheet: 目的工作表,如果destSheet存在,则覆盖destSheet,否则末尾新增destSheet Function CopySheet(sourceSheet, destSheet); Begin return workbook_.CopySheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet)); diff --git a/funcext/TSOffice/TSUtils/NodeInfo.tsf b/funcext/TSOffice/TSUtils/NodeInfo.tsf index 29906a0..da21e20 100644 --- a/funcext/TSOffice/TSUtils/NodeInfo.tsf +++ b/funcext/TSOffice/TSUtils/NodeInfo.tsf @@ -18,17 +18,31 @@ public return RootObj; End - Function Update(); + Function Update(position);overload; Begin if ifObj(RootObj) then Begin arr := Marshal(); if length(arr['attributes']) or length(arr['children']) then Begin - curNode := class(TSXml).GetNode(RootObj, NodeUri, 'end'); + curNode := class(TSXml).GetNode(RootObj, NodeUri, position); if ifObj(curNode) then class(TSXml).UpdateNode(curNode, arr['attributes'], arr['children']); End; End; End; + + Function Update();overload; + Begin + self.Update("end"); + End; + + Function Delete(); + Begin + if ifObj(RootObj) then + begin + curNode := class(TSXml).GetNode(RootObj, NodeUri); + if ifObj(curNode) then curNode.DeleteChildren(); + end + End; Function GetAttrs(); virtual; Begin diff --git a/funcext/TSOffice/TSUtils/TSXml.tsf b/funcext/TSOffice/TSUtils/TSXml.tsf index b045b89..01be3a3 100644 --- a/funcext/TSOffice/TSUtils/TSXml.tsf +++ b/funcext/TSOffice/TSUtils/TSXml.tsf @@ -136,7 +136,7 @@ Type TSXml = Class End; return node; End; - + class Function RemoveNode(obj, r); Begin arr := str2array(r, '/'); @@ -151,6 +151,21 @@ Type TSXml = Class nodeArr[cnt - 2].DeleteChild(nodeArr[cnt - 1]); End; + class Function GetNodeN(xmlnode, name, count); + Begin + arr := str2array(name, '/'); + node := xmlnode; + for i:=0 to length(arr)-1 do + begin + node := node.FirstChild(arr[i]); + if not ifObj(node) then return nil; + end + last := arr[length(arr)-1]; + for i:=2 to count do + node := node.NextElement(last); + return node; + End; + ///prefix为空——查找最大rId,prefix非空——查找指定Target class Function FindRelationshipRid(xmlfile, prefix); Begin diff --git a/funcext/TSOffice/TSXlsxFile.tsf b/funcext/TSOffice/TSXlsxFile.tsf index 618084f..26e2e32 100644 --- a/funcext/TSOffice/TSXlsxFile.tsf +++ b/funcext/TSOffice/TSXlsxFile.tsf @@ -1,4 +1,4 @@ -// Version 1.4.9 +// Version 1.5.0 Type TSXlsxFile = Class ///Version: V1.0 2022-08-08 @@ -915,7 +915,7 @@ Type TSXlsxFile = Class ///复制Sheet ///sourceSheet: 源工作表 - ///destSheet: 目的工作表 + ///destSheet: 目的工作表,如果destSheet存在,则覆盖destSheet,否则末尾新增destSheet Function CopySheet(sourceSheet, destSheet); Begin return workbook_.CopySheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet)); diff --git a/funcext/TSOffice/document/TDocxCopy.tsf b/funcext/TSOffice/document/TDocxCopy.tsf index 089c01b..6cdbd48 100644 --- a/funcext/TSOffice/document/TDocxCopy.tsf +++ b/funcext/TSOffice/document/TDocxCopy.tsf @@ -9,6 +9,7 @@ Type TDocxCopy = class copy_drawing_ := array(); style_copy_obj_ := new TDocxStyleCopy(oldObj.StyleObject(), newObj.StyleObject()); number_copy_obj_ := new TDocxNumberCopy(oldObj, newObj); + footnote_copy_obj_ := new TDocxFootnoteCopy(oldObj, newObj); End; Function Init(); @@ -110,6 +111,7 @@ private // 比较新文件的图片在旧文件中是否存在 zip := old_docx_obj_.Zip(); + xml := zip.Get('word/_rels/document.xml.rels'); files := sselect ['FileName'] from zip.Files() where AnsiStartsText('word/media/image', ['FileName']) end; for i:=0 to length(files)-1 do Begin if zip.Diff(files[i], image_file) = 0 then Begin @@ -117,7 +119,6 @@ private [maxRid, imageFile, rid] := class(TSXml).FindRelationshipRid(xml, prefix); End; End; - xml := zip.Get('word/_rels/document.xml.rels'); if rid = 0 then begin image_cnt := length(files) + 1; @@ -131,9 +132,9 @@ private zip.Add('word/' + image_path, image_file); contentXml := zip.Get('[Content_Types].xml'); class(TSXml).AddDefaultContentType(contentXml, postfix, 'image/' $ postfix); - node.SetAttribute('r:embed', 'rId' $ rid); end end + node.SetAttribute('r:embed', 'rId' $ rid); End; Function SetChart(node); @@ -210,21 +211,64 @@ private Function SetParagraphInfo(paragraph); Begin - style := class(TSXml).GetNode(paragraph.node_, 'w:pPr/w:pStyle'); + CopyStyle(paragraph.node_, 'w:pPr/w:pStyle'); + CopyNumber(paragraph.node_, 'w:pPr/w:numPr/w:numId'); + SetRunsInfo(paragraph.node_); + DeleteComment(paragraph); // 删除批注 + End; + + Function SetRunsInfo(node); + Begin + run := node.FirstChildElement('w:r'); + while ifObj(run) do + begin + CopyFootnote(run); + CopyStyle(run, 'w:rPr/w:rStyle'); + run := run.NextElement(); + end + End; + + Function CopyStyle(node, path); + Begin + style := class(TSXml).GetNode(node, path); if ifObj(style) then begin styleid := style.GetAttribute('w:val'); new_id := style_copy_obj_.GetStyleNewId(styleid); if new_id then style.SetAttribute('w:val', new_id); end - numpr := class(TSXml).GetNode(paragraph.node_, 'w:pPr/w:numPr/w:numId'); + End; + + Function CopyNumber(node, path); + Begin + numpr := class(TSXml).GetNode(node, path); if ifObj(numpr) then begin id := numpr.GetAttribute('w:val'); numberid := number_copy_obj_.CopyNumbering(id); numpr.SetAttribute('w:val', numberid); end - DeleteComment(paragraph); // 删除批注 + End; + + Function CopyFootnote(node); + Begin + footnote := node.FirstChildElement('w:footnoteReference'); + if not ifObj(footnote) then return; + id := footnote.GetAttribute('w:id'); + if id then + begin + obj := footnote_copy_obj_.CopyFootnote(id); + footnote.SetAttribute('w:id', obj.Id); + parts := obj.Parts(); + for i:=0 to length(parts)-1 do + begin + case GetPartType(parts[i]) of + 0: SetParagraphInfo(parts[i]); + 1: SetDrawingInfo(parts[i]); + 2: SetTableInfo(parts[i]); + end; + end + end End; Function SetTableInfo(table); @@ -295,6 +339,7 @@ private new_docx_obj_; style_copy_obj_; number_copy_obj_; + footnote_copy_obj_; copy_table_; copy_paragraph_; @@ -461,3 +506,40 @@ private id_map_; // [id: styleobj] End; + +Type TDocxFootnoteCopy = class + + Function Create(oldObj, newObj); + Begin + footnotexml := newObj.Zip().Get('word/footnotes.xml'); + if ifObj(footnotexml) then + begin + old_footnote_obj_ := oldObj.FootNotesObject(); + new_footnote_obj_ := newObj.FootNotesObject(); + end + else begin + old_footnote_obj_ := nil; + new_footnote_obj_ := nil; + end + id_map_ := array(); + End; + + Function CopyFootnote(id); + Begin + if ifObj(old_footnote_obj_) and ifObj(new_footnote_obj_) then + begin + if (obj := new_footnote_obj_.GetFootnote(id)) and not id_map_[id] then + begin + footnote_obj := old_footnote_obj_.CopyFootnote(obj); + id_map_[id] := footnote_obj; + end + return id_map_[id]; + end + End; + +private + old_footnote_obj_; + new_footnote_obj_; + + id_map_; // [id: footnote] +End; diff --git a/funcext/TSOffice/document/docxDocument.tsf b/funcext/TSOffice/document/docxDocument.tsf index 31550c0..f0a1bcb 100644 --- a/funcext/TSOffice/document/docxDocument.tsf +++ b/funcext/TSOffice/document/docxDocument.tsf @@ -10,6 +10,7 @@ Type docxDocument = Class bodyNode_ := root_.FirstChildElement('w:body'); body_ := TOfficeObj('TwBody'); body_.InitNode(bodyNode_); + body_.InitRootNode(bodyNode_); body_.zipfile_ := z; body_.document_ := self; bookmarkid_ := -1; diff --git a/funcext/TSOffice/worksheet/xlsxWorkBook.tsf b/funcext/TSOffice/worksheet/xlsxWorkBook.tsf index fb73d9f..bda017b 100644 --- a/funcext/TSOffice/worksheet/xlsxWorkBook.tsf +++ b/funcext/TSOffice/worksheet/xlsxWorkBook.tsf @@ -519,15 +519,19 @@ Type xlsxWorkBook = Class Function CopySheet(sourceSheet, destSheet); Begin - ind := sheetIndexMap_[ LowerCase(destSheet) ]; - if ifint(ind) then return destSheet $ ' already exists.'; ind := sheetIndexMap_[ LowerCase(sourceSheet) ]; if not ifint(ind) then return sourceSheet $ ' does not exists.'; + dest_ind := sheetIndexMap_[ LowerCase(destSheet) ]; //copy sheet - sheetId := vselect maxof(['sheetId']) from sheetNames_ end; - sheetId := integer(sheetId) + 1; - fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml'; + 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); @@ -556,7 +560,14 @@ Type xlsxWorkBook = Class sheet_rels := GetSheetRelsFile(sourceSheet); if ifObj(sheet_rels) then begin - rels_name := 'xl/worksheets/_rels/' + 'sheet' + inttostr(sheetsCount_ + 1) + '.xml.rels'; + 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'); @@ -583,6 +594,8 @@ Type xlsxWorkBook = Class end end + if ifint(dest_ind) then return; + //workbook.xml.rels rid := getWorkbookRelsRid(); workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels'); @@ -592,6 +605,8 @@ Type xlsxWorkBook = Class 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); @@ -734,7 +749,7 @@ Type xlsxWorkBook = Class 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."; + 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 diff --git a/更新日志.md b/更新日志.md index 1c7663c..bf204a7 100644 --- a/更新日志.md +++ b/更新日志.md @@ -1,5 +1,18 @@ # 更新日志 +## 2023-11-14 + +### V1.5.0 + +#### word + +1. 新增对脚注的支持,方法`TSDocxFile.FootnotesObject().Add` +2. `TSDocxFile.InsertFile`支持脚注插入 + +### excel + +1. 升级`TSXlsxFile.CopySheet` + ## 2023-10-16 ### V1.4.9