This commit is contained in:
csh 2023-08-18 16:49:12 +08:00
parent 8d63ecd7b3
commit b59c869ae2
9 changed files with 546 additions and 13 deletions

Binary file not shown.

Binary file not shown.

View File

@ -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,6 +12746,7 @@ Type TSectPr = Class
n += cnt;
if newLine then Begin
_addLine(startPage, curPageNo, curLinePos, LineHeight + (linesCnt ? 0 : more), before, linesCnt ? false : true, t); //加入一行
if ifObj(pPr) then
PageLine.CurrentLine += _getHanging(linesCnt+1, pPr);
linesCnt ++;
End;
@ -12946,6 +12947,32 @@ Type TDocumentBody = Class(DocObject)
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段落
///返回true
@ -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

View File

@ -1,4 +1,4 @@
// Version 1.4.1
// Version 1.4.2
Type TSDocxFile = Class
///Version: V1.0 2022-09-20
@ -372,6 +372,24 @@ Type TSDocxFile = Class
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()
///tagNamestring 标签名称
///tagObjTAG对象方法
@ -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

View File

@ -1,4 +1,4 @@
// Version 1.4.1
// Version 1.4.2
Type TSExcelFile = Class
///Version: V1.0 2022-08-08

View File

@ -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;

View File

@ -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段落样式
///oTDocxStyle对象
///返回TDocxStyle对象

View File

@ -77,6 +77,19 @@ Type TNumbering = Class
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对象
Function NumberStyle(numId);overload;
@ -148,14 +161,16 @@ Type TNumbering = Class
///新添加项目样式
///oTNumStyle对象
///返回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);

View File

@ -1,5 +1,13 @@
# 更新日志
## 2023-8-18
### V1.4.2
#### word
支持插入另一个word内容`InsertFile(alias, fileName, posOpt)`
## 2023-8-11
### V1.4.1