diff --git a/DocxFile使用帮助.docx b/DocxFile使用帮助.docx index c866fd3..be5fcef 100644 Binary files a/DocxFile使用帮助.docx and b/DocxFile使用帮助.docx differ diff --git a/Windows-X64/office_plugin.dll b/Windows-X64/office_plugin.dll index 2028291..9a686e3 100644 Binary files a/Windows-X64/office_plugin.dll and b/Windows-X64/office_plugin.dll differ diff --git a/funcext/TSOffice/TOfficeObj.tsf b/funcext/TSOffice/TOfficeObj.tsf index 14db5f0..faecc41 100644 --- a/funcext/TSOffice/TOfficeObj.tsf +++ b/funcext/TSOffice/TOfficeObj.tsf @@ -1,4 +1,4 @@ -// Version 1.4.1 +// Version 1.4.2 Function TOfficeObj(n); Begin @@ -12691,7 +12691,7 @@ Type TSectPr = Class LineRatio := 1.0; if ifObj(pPr) then Begin if pPr.LineSpacingRule = 'exact' and pPr.LineSpacing then //固定行距 - defaultLineHeight := _pPr.LineSpacing; + defaultLineHeight := pPr.LineSpacing; else if pPr.LineSpacing then Begin LineRatio := pPr.LineSpacing / 240; //LineRatio := 1.0; @@ -12746,7 +12746,8 @@ Type TSectPr = Class n += cnt; if newLine then Begin _addLine(startPage, curPageNo, curLinePos, LineHeight + (linesCnt ? 0 : more), before, linesCnt ? false : true, t); //加入一行 - PageLine.CurrentLine += _getHanging(linesCnt+1, pPr); + if ifObj(pPr) then + PageLine.CurrentLine += _getHanging(linesCnt+1, pPr); linesCnt ++; End; End; @@ -12945,6 +12946,32 @@ Type TDocumentBody = Class(DocObject) _set_lastParagraph_(posOpt, p.node_);//设置最后一个段落 return p; End; + + ///复制w:p节点内容 + ///paragraphObj: TParagraph对象 + ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;DocumentPart对象 在posOpt之后新添加段落 + ///返回TParagraph对象 + Function CopyWp(paragraphObj, posOpt); + Begin + addPart(posOpt, paragraphObj, true); + p := new TParagraph(paragraphObj.node_); + //复制段落字体属性 + p.CopyRunFormat(false, nil); + _set_lastParagraph_(posOpt, p.node_);//设置最后一个段落 + return p; + End; + + ///复制w:tbl节点内容 + ///ttable: TTable对象 + ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;DocumentPart对象 在posOpt之后新添加段落 + ///返回TTable对象 + Function CopyWtbl(table, posOpt); + Begin + addPart(posOpt, table, true); + p := new TTable(table.node_); + _set_lastParagraph_(posOpt, p.node_); + return p; + End; ///删除指定段落 ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 第N段;posOpt段落 @@ -13927,13 +13954,14 @@ Type TDocumentBody = Class(DocObject) return id; End; - Function addPart(posOpt, o); + Function addPart(posOpt, o, flag); Begin node := posOpt; if ifObj(posOpt) and not (posOpt is Class(XmlNode)) then node := posOpt.Node(); node := findNode(node, true); - nodeArr := o.Marshal(); + if flag then nodeArr := o.node_.Marshal()[0]; + else nodeArr := o.Marshal(); if ifObj(node) then Begin o.node_ := node_.InsertAfterChild(node, nodeArr); //XmlNode节点对象或DocObject对象 End diff --git a/funcext/TSOffice/TSDocxFile.tsf b/funcext/TSOffice/TSDocxFile.tsf index b50895d..48cc169 100644 --- a/funcext/TSOffice/TSDocxFile.tsf +++ b/funcext/TSOffice/TSDocxFile.tsf @@ -1,4 +1,4 @@ -// Version 1.4.1 +// Version 1.4.2 Type TSDocxFile = Class ///Version: V1.0 2022-09-20 @@ -371,6 +371,24 @@ Type TSDocxFile = Class End; return nil; End; + + ///复制Word内容 + ///docxObj: TSDocxFile对象 + ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片 + ///返回: [err, TDocxCopy对象] + ///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组 + ///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组 + ///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组 + Function InsertFile(alias, fileName, posOpt); + Begin + 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; ///遍历文档中所有[TSTAG][/TSTAG]标签,针对每一个TAG执行tagObj.Apply() ///tagName:string 标签名称 @@ -561,8 +579,8 @@ Type TSDocxFile = Class DocPrId_ ++; return DocPrId_; End; -private - Function getPosNode(posOpt); + + Function GetPosNode(posOpt); Begin node := posOpt; if ifObj(node) and not (node is Class(XmlNode)) then diff --git a/funcext/TSOffice/TSExcelFile.tsf b/funcext/TSOffice/TSExcelFile.tsf index 120d73c..0542693 100644 --- a/funcext/TSOffice/TSExcelFile.tsf +++ b/funcext/TSOffice/TSExcelFile.tsf @@ -1,4 +1,4 @@ -// Version 1.4.1 +// Version 1.4.2 Type TSExcelFile = Class ///Version: V1.0 2022-08-08 diff --git a/funcext/TSOffice/document/TDocxCopy.tsf b/funcext/TSOffice/document/TDocxCopy.tsf new file mode 100644 index 0000000..dc00c20 --- /dev/null +++ b/funcext/TSOffice/document/TDocxCopy.tsf @@ -0,0 +1,447 @@ +Type TDocxCopy = class + + Function Create(oldObj, newObj) + Begin + old_docx_obj_ := oldObj; + new_docx_obj_ := newObj; + copy_table_ := array(); + copy_paragraph_ := array(); + copy_drawing_ := array(); + style_copy_obj_ := new TDocxStyleCopy(oldObj.StyleObject(), newObj.StyleObject()); + number_copy_obj_ := new TDocxNumberCopy(oldObj, newObj); + End; + + Function Init(); + Begin + style_copy_obj_.Init(); + End; + + ///从posOpt位置开始复制word + ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片 + Function Copy(posOpt); + Begin + // 复制所有的样式 + style_copy_obj_.CopyStyle(); + + parts := new_docx_obj_.Body().Parts(); + pos := old_docx_obj_.GetPosNode(posOpt); + for i:=0 to length(parts)-1 do + begin + case GetPartType(parts[i]) of + 0: pos := CopyParagraph(parts[i], pos); + 1: pos := CopyDrawing(parts[i], pos); + 2: pos := CopyTable(parts[i], pos); + end; + end + End; + + Function CopyParagraph(obj, pos); + Begin + paragraph := old_docx_obj_.Body().CopyWp(obj, pos); + SetParagraphInfo(paragraph); + copy_paragraph_[length(copy_paragraph_)] := paragraph; + return paragraph; + End; + + Function CopyDrawing(obj, pos); + Begin + paragraph := old_docx_obj_.Body().CopyWp(obj, pos); + SetDrawingInfo(paragraph); + copy_drawing_[length(copy_drawing_)] := paragraph; + return paragraph; + End; + + Function CopyTable(obj, pos); + Begin + table := old_docx_obj_.Body().CopyWtbl(obj, pos); + SetTableInfo(table); + copy_table_[length(copy_table_)] := table; + return table; + End; + + Function GetCopiedTable(); + Begin + return copy_table_; + End; + + Function GetCopiedParagraph(); + Begin + return copy_paragraph_; + End; + + Function GetCopiedDrawing(); + Begin + return copy_drawing_; + End; + +private + + Function DeleteComment(paragraph); + Begin + node := paragraph.node_.FirstChildElement('w:commentRangeStart'); + while ifObj(node) do + begin + name := node.GetName(); + if name = "w:commentRangeStart" or name = "w:commentRangeEnd" then + begin + delete_node := node; + node := node.NextElement(); + paragraph.node_.DeleteChild(delete_node); + if node.FirstChildElement('w:commentReference') then + begin + delete_node := node; + node := node.NextElement(); + paragraph.node_.DeleteChild(delete_node); + end + end + else begin + node := node.NextElement(); + end + end + End; + + Function SetPic(node); + Begin + // 获取新文件的图片 + rembed := node.GetAttribute('r:embed'); + rels := new_docx_obj_.Zip().Get('word/_rels/document.xml.rels'); + target := class(TSXml).FindRelationshipTarget(rels, rembed); + image_file := new_docx_obj_.Zip().Get('word/' $ target).Data(); + + // 比较新文件的图片在旧文件中是否存在 + zip := old_docx_obj_.Zip(); + 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 + prefix := ReplaceStr(files[i], 'word/', ''); + [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; + if ParseRegExpr("\\w+$", target, "", result, Mpos, Mlen) then + begin + postfix := result[0][0]; + image_path := "media/image" $ image_cnt $ '.' $ postfix; + [rid, tar] := class(TSXml).FindRelationshipRid(xml, ''); + rid ++; + class(TSXml).AddRelationshipRid(xml, image_path, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', 'rId' $ rid); + 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 + End; + + Function SetChart(node); + Begin + rid := node.GetAttribute('r:id'); + rels := new_docx_obj_.Zip().Get('word/_rels/document.xml.rels'); + target := class(TSXml).FindRelationshipTarget(rels, rid); + new_zip := new_docx_obj_.Zip(); + chart_file := new_zip.Get('word/' $ target); + + // 复制charN.xml + zip := old_docx_obj_.Zip(); + files := sselect ['FileName'] from zip.Files() where AnsiStartsText('word/charts/chart', ['FileName']) end; + new_chart_file := "charts/chart" $ (length(files) + 1) $ ".xml"; + xml := zip.Get('word/_rels/document.xml.rels'); + [new_rid, tar] := class(TSXml).FindRelationshipRid(xml, ''); + new_rid++; + class(TSXml).AddRelationshipRid(xml, new_chart_file, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart", 'rId' $ new_rid); + zip.Add('word/' + new_chart_file, chart_file.Data()); + contentXml := zip.Get('[Content_Types].xml'); + class(TSXml).AddOverrideContentType(contentXml, '/word/' + new_chart_file, 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml'); + node.SetAttribute('r:id', 'rId' $ new_rid); + + // 复制charN.xml.rels + chart_rels := "word/charts/_rels" + target[pos('/', target):] $ ".rels"; + chart_rels_xml := new_zip.Get(chart_rels); + if ifObj(chart_rels_xml) then + begin + zip.Add('word/charts/_rels/chart' $ (length(files) + 1) $ ".xml.rels", chart_rels_xml.Data()); + relationship := chart_rels_xml.FirstChildElement('Relationships').FirstChildElement('Relationship'); + while ifObj(relationship) do + begin + target := relationship.GetAttribute('Target'); + target_path := AnsiReplaceText(target, '..', 'word'); + file := new_zip.Get(target_path); + if ifObj(file) then + begin + [new_file, filetype] := GetNewTargetFileName(zip, target_path); + zip.Add(new_file, file.Data()); + if filetype then Class(TSXml).AddDefaultContentType(contentXml, filetype[2:], 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + end + relationship := relationship.NextElement(); + end; + end + End; + + Function GetNewTargetFileName(zip, fileName); + Begin + files := zip.Files()[:, 'FileName']; + ret := ParseRegExpr("(.*/)(\\w+)(\.\\w+)$", fileName, "", result, Mpos, Mlen); + count := 0; + while fileName in files do + begin + if ret then + fileName := result[0][1] $ result[0][2] $ "tinysoft" $ count $ result[0][3]; + else fileName := fileName $ "tinysoft" $ count; + count++; + end + if ret then filetype := result[0][3]; + return array(fileName, filetype); + End; + + Function SetDrawingInfo(drawing); + Begin + graphic_node := class(TSXml).GetNode(drawing.node_,'w:r/w:drawing/wp:inline/a:graphic/a:graphicData'); + if ifObj(graphic_node) then + begin + node := class(TSXml).GetNode(graphic_node,'pic:pic/pic:blipFill/a:blip'); + if ifObj(node) then SetPic(node); + node := class(TSXml).GetNode(graphic_node, 'c:chart'); + if ifObj(node) then SetChart(node); + end; + End; + + Function SetParagraphInfo(paragraph); + Begin + style := class(TSXml).GetNode(paragraph.node_, 'w:pPr/w:pStyle'); + 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'); + if ifObj(numpr) then + begin + id := numpr.GetAttribute('w:val'); + numberid := number_copy_obj_.CopyNumbering(id); + numpr.SetAttribute('w:val', id); + end + DeleteComment(paragraph); // 删除批注 + End; + + Function SetTableInfo(table); + Begin + style := class(TSXml).GetNode(table.node_, 'w:tblPr/w:tblStyle'); + if ifObj(style) then + begin + id := style.GetAttribute('w:val'); + new_id := style_copy_obj_.GetStyleNewId(id); + if new_id then style.SetAttribute('w:val', new_id); + end + col := table.Cols(); + row := table.Rows(); + for r:=1 to row do + begin + for c:=1 to col do + begin + node := table.Cell(r, c).node_; + if (tbl := node.FirstChildElement('w:tbl')) then + begin + obj := TOfficeObj('TTable'); + obj.Init(tbl); + SetTableInfo(obj); + end + else if (p := node.FirstChildElement('w:p')) then + begin + draw := class(TSXml).GetNode(p, 'w:r/w:drawing'); + if ifObj(draw) then + begin + obj := TOfficeObj('TPicture'); + obj.Init(p); + SetDrawingInfo(obj); + end + else begin + obj := TOfficeObj('TParagraph'); + obj.Init(p); + SetParagraphInfo(obj); + end + end + end + end + End; + + /// 普通段落: 0 + /// 图片段落: 1 + /// 表格: 2 + /// 其他类型暂不复制 + Function GetPartType(part); + Begin + name := part.name_; + case name of + 'w:p': + begin + if class(TSXml).GetNode(part.node_, 'w:r/w:drawing') then return 1; + return 0; + end + 'w:tbl': + return 2; + else + return -1; + end + End; + +private + old_docx_obj_; + new_docx_obj_; + style_copy_obj_; + number_copy_obj_; + + copy_table_; + copy_paragraph_; + copy_drawing_; +End; + +Type TDocxStyleCopy = class + + Function Create(oldObj, newObj); + Begin + old_style_obj_ := oldObj; + new_style_obj_ := newObj; + style_id_map_ := array(); + style_name_map_ := array(); + style_id_map2_ := array(); + style_name_map2_ := array(); + id_map_ := array(); + End; + + Function Init(); + Begin + id_styles := new_style_obj_.Styles(); + for id, obj in id_styles do + begin + new_obj := obj; + id_map_[id] := new_obj; + SetId(new_obj, id); + SetName(new_obj); + end + End; + + Function CopyStyle(); + Begin + for id, obj in id_map_ do + begin + SetBasedOn(obj); + SetLink(obj); + old_style_obj_.CopyStyle(obj); + end; + End; + + Function GetStyleNewId(oldId) + Begin + if ifnumber(oldId) then oldId := tostring(oldId); + return style_id_map_[oldId]; + End; + +private + + Function SetId(obj, id); + Begin + new_id := GetNewId(id); + obj.node_.SetAttribute('w:styleId', new_id); + style_id_map_[id] := new_id; + style_id_map2_[new_id] := id; + End; + + Function SetName(obj); + Begin + name_node := obj.node_.FirstChildElement('w:name'); + name := name_node.GetAttribute('w:val'); + new_name := GetNewName(name); + name_node.SetAttribute('w:val', new_name); + style_name_map_[name] := new_name; + style_name_map2_[new_name] := name; + End; + + Function SetBasedOn(obj); + Begin + basedon := obj.node_.FirstChildElement('w:basedOn'); + if ifObj(basedon) then + begin + val := basedon.GetAttribute('w:val'); + if style_id_map_[val] then basedOn.SetAttribute('w:val', style_id_map_[val]); + end + End; + + Function SetLink(obj); + Begin + link := obj.node_.FirstChildElement('w:link'); + if ifObj(link) then + begin + val := link.GetAttribute('w:val'); + if style_id_map_[val] then link.SetAttribute('w:val', style_id_map_[val]); + end + End; + + Function GetNewName(name); + Begin + new_name := name; + count := 0; + while ifObj(old_style_obj_.GetStyle(new_name)) or style_name_map2_[new_name] do + new_name := new_name $ count++; + return new_name; + End; + + Function GetNewId(id); + Begin + new_id := id; + count := 0; + while ifObj(old_style_obj_.GetStyleById(new_id)) or style_id_map2_[new_id] do + new_id := new_id $ count++; + return new_id; + End; + +private + old_style_obj_; + new_style_obj_; + + style_id_map_; // [old_id: new_id]; + style_name_map_; // [old_name: new_name]; + style_id_map2_; // [new_id: old_id]; + style_name_map2_; // [old_name: new_name]; + + id_map_; // [id: styleobj] +End; + +Type TDocxNumberCopy = class + + Function Create(oldObj, newObj); + Begin + numberingxml := newObj.Zip().Get('word/numbering.xml'); + if ifObj(numberingxml) then + begin + old_number_obj_ := oldObj.NumberingObject(); + new_number_obj_ := newObj.NumberingObject(); + end + else begin + old_number_obj_ := nil; + new_number_obj_ := nil; + end + number_id_map_ := array(); + id_map_ := array(); + End; + + Function CopyNumbering(number); + Begin + if ifObj(old_number_obj_) and ifObj(new_number_obj_) then + begin + if (obj := new_number_obj_.NumberStyle(number)) then + begin + return old_number_obj_.CopyNumber(obj); + end + end; + End; + +private + old_number_obj_; + new_number_obj_; + + id_map_; // [id: styleobj] +End; diff --git a/funcext/TSOffice/document/TDocxStyles.tsf b/funcext/TSOffice/document/TDocxStyles.tsf index 8f7ec48..3a58155 100644 --- a/funcext/TSOffice/document/TDocxStyles.tsf +++ b/funcext/TSOffice/document/TDocxStyles.tsf @@ -133,6 +133,23 @@ Type TDocxStyles = Class return nil; End; + ///复制样式 + ///newstyle:样式节点TDocxStyle对象 + ///返回:TDocxStyle对象 + Function CopyStyle(newstyle); + Begin + if ifObj(newstyle) and ifObj(newstyle.node_) then Begin + node := stylesXml_.FirstChildElement('w:styles').InsertEndChild(newstyle.node_.Marshal()[0]); + obj := TOfficeObj('TDocxStyle'); + obj.StyleId := node.GetAttribute('w:styleId'); + obj.Name := node.FirstChildElement('w:name').GetAttribute('w:val'); + obj.Init(node); + _addStyle(obj); + return obj; + End; + return nil; + End; + ///插入新的LatentStyle段落样式 ///o:TDocxStyle对象 ///返回:TDocxStyle对象 @@ -185,4 +202,4 @@ private idMap_; nameMap_; maxStyleId_:integer; -End; \ No newline at end of file +End; diff --git a/funcext/TSOffice/document/TNumbering.tsf b/funcext/TSOffice/document/TNumbering.tsf index cbfb2bf..0b2206c 100644 --- a/funcext/TSOffice/document/TNumbering.tsf +++ b/funcext/TSOffice/document/TNumbering.tsf @@ -76,6 +76,19 @@ Type TNumbering = Class numIdMap_[''$num.numId] := num.abstractNumId; return num.numId; End; + + ///复制number样式 + ///numberObj: 样式xmlNode + Function CopyNumber(numberObj); + Begin + obj := AddStyle(numberObj, true); + num := TOfficeObj('TNumber'); + num.numId := maxNumId_++; + num.abstractNumId := obj.Id; + numberingXml_.FirstChildElement('w:numbering').InsertEndChild(num.Marshal()); + numIdMap_[''$num.numId] := num.abstractNumId; + return num.numId; + End; ///根据numId获取TNumStyle对象(已存在) ///返回:TNumStyle对象 @@ -148,14 +161,16 @@ Type TNumbering = Class ///新添加项目样式 ///o:TNumStyle对象 ///返回:TNumStyle对象 - Function AddStyle(o); + Function AddStyle(o, flag); Begin if ifObj(o) then Begin o.abstractNumId := maxAbstractNumId_++; + if flag then marshal := o.node_.Marshal()[0]; + else marshal := o.Marshal(); if ifObj(lastAbstractNumStyle_) then - node := numberingXml_.FirstChildElement('w:numbering').InsertAfterChild(lastAbstractNumStyle_, o.Marshal()); + node := numberingXml_.FirstChildElement('w:numbering').InsertAfterChild(lastAbstractNumStyle_, marshal); else - node := numberingXml_.FirstChildElement('w:numbering').InsertFirstChild(o.Marshal()); + node := numberingXml_.FirstChildElement('w:numbering').InsertFirstChild(marshal); lastAbstractNumStyle_ := node; o := TOfficeObj('TNumStyle'); o.Init(node); diff --git a/更新日志.md b/更新日志.md index 76cc571..8c5459a 100644 --- a/更新日志.md +++ b/更新日志.md @@ -1,5 +1,13 @@ # 更新日志 +## 2023-8-18 + +### V1.4.2 + +#### word + +支持插入另一个word内容`InsertFile(alias, fileName, posOpt)` + ## 2023-8-11 ### V1.4.1