add `TSTag` and wordtemplate

This commit is contained in:
csh 2023-05-17 17:25:21 +08:00
parent c536c4d531
commit 10ef45f1a0
7 changed files with 379 additions and 6 deletions

View File

@ -1353,6 +1353,7 @@ type TChartImpl=class(NodeInfo)
,("field":"Excel","name":"Excel","obj":Excel,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"") ,("field":"Excel","name":"Excel","obj":Excel,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"")
,("field":"chartFileName","name":"chartFileName","obj":chartFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"") ,("field":"chartFileName","name":"chartFileName","obj":chartFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"")
,("field":"excelFileName","name":"excelFileName","obj":excelFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"") ,("field":"excelFileName","name":"excelFileName","obj":excelFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"")
,("field":"drawingFileName","name":"drawingFileName","obj":drawingFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"")
) union ExtNodes; ) union ExtNodes;
End; End;
@ -1395,6 +1396,7 @@ type TChartImpl=class(NodeInfo)
Excel; Excel;
chartFileName; chartFileName;
excelFileName; excelFileName;
drawingFileName;
End; End;
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
@ -13492,6 +13494,235 @@ Type TDocumentBody = Class(DocObject)
return array(length(errArr), tslFuncCount, errArr); return array(length(errArr), tslFuncCount, errArr);
End; End;
Function ExecTsTag(docx, tagName, tagObj);
Begin
//表格
t := array();
tslTagCount := 0;
errArr := array();
tArr := Tables();
for i:=0 to length(tArr)-1 do Begin
col := tArr[i].Cols();
row := tArr[i].Rows();
for r:= 1 to row do Begin
for c:=1 to col do Begin
cell := tArr[i].Cell(r, c);
[err, cnt, err] := cell.ExecTsTag(docx, tagName, tagObj);//递归
tslTagCount += cnt;
errArr union= err;
End;
End;
End;
//文本框
ps := Paragraphs();
for i:=0 to length(ps)-1 do Begin
boxs := ps[i].TextBoxs();
for j:=0 to length(boxs)-1 do Begin
[err, cnt, err] := boxs[j].ExecTsTag(docx, tagName, tagObj);//递归
if cnt then
boxs[j].Apply();
tslTagCount += cnt;
errArr union= err;
End;
End;
//页脚、页眉
sArr := Sections();
tpArr := array('default','even','first');
for i:=0 to length(sArr)-1 do Begin
for k, name in tpArr do Begin
h := sArr[i].Header(name);
if ifObj(h) then Begin
[err, cnt, err] := h.ExecTsTag(docx, tagName, tagObj);//递归
tslTagCount += cnt;
errArr union= err;
End;
f := sArr[i].Footer(name);
if ifObj(f) then Begin
[err, cnt, err] := f.ExecTsTag(docx, tagName, tagObj);//递归
tslTagCount += cnt;
errArr union= err;
End;
End;
End;
[err, cnt, err] := ExecTsTagImpl(docx, tagName, tagObj);
tslTagCount += cnt;
errArr union= err;
return array(length(errArr), tslTagCount, errArr);
End;
Function ExecTsTagImpl(docx, tagName, tagObj);
Begin
tslTagCount := 0;
errArr := array();
tagArr := array();
tArr := TextArray();
tagAttribute := '';
tagStr := '';
tagStatus := '';//array('','head', 'attribute', 'tail');
tagHead := '[' + tagName;
tagTail := '[/' + tagName;
tmp := array();
while i < length(tArr) do Begin
txt := '';
iStep := 1;
if ifObj(tArr[i]['rNode']) then Begin
run := new TRun(tArr[i]['rNode']);
txt := run.Text();
if class(TSXml).IsUtf8() then
txt := UTF8ToAnsi(txt);
End;
k := 1;
wz := 1;
txtLen := length(txt);
while k <= txtLen do Begin
c := txt[k];
if c = ']' and (tagStatus = 'head' or tagStatus = 'attribute') and tagStr = tagHead then Begin //TAG头结束标志
tagStr := '';
tagArr := tArr[tmp['head-begin-paragraph']:i,:];
wz++;
tagStatus := 'tail';
tmp['head-end-paragraph'] := i;
tmp['head-end-pos'] := k++;
tmp['head-end-wz'] := wz;
tmp['head-end-txtlen'] := txtLen;
continue;
End;
case tagStatus of
'':
if c = '[' then Begin
tagStr := '[';
tmp['head-begin-paragraph'] := i;
tmp['head-begin-pos'] := k;
tmp['head-begin-wz'] := wz;
tmp['head-begin-txtlen'] := txtLen;
tagStatus := 'head';
tagAttribute := '';
End;
'head':
if c = ' ' and tagStr = tagHead then Begin //找到TAG属性
tagStatus := 'attribute';
End
else if length(tagStr) < length(tagHead) and lowercase(c) = lowercase(tagHead[ length(tagStr) + 1 ]) then Begin
tagStr += tagHead[ length(tagStr) + 1 ];
End
else //继续寻找TAG开始标志
tagStatus := '';
'attribute':
tagAttribute += c;
'tail':
if c=']' and tagStr = tagTail then Begin //查找到完整标签
ind := length(tagArr) - 1;
if tagArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tagArr[ind]['rIndex'] <> tArr[i]['rIndex'] then Begin
ind ++;
tagArr[ind] := tArr[i];
End;
//[/tag]后
if k < txtLen then Begin //函数后面分割为新的w:r
tArr[i]['rNode'] := run._duplicate_r(tArr[i]['rNode']);
run._adjust_r(tArr[i]['rNode'], wz, txtLen);
iStep := 0;
End;
//前[/tag]
tagInd := length(tagArr) - i + tmp['tail-begin-paragraph'] - 1;
if tmp['tail-begin-pos'] > 1 then Begin
nNode := run._duplicate_r(tagArr[tagInd]['rNode']);
run._adjust_r(tagArr[tagInd]['rNode'], 0, tmp['tail-begin-wz'] - 1);
tagArr[tagInd]['pNode'].DeleteChild(nNode);
End
else if tmp['tail-begin-pos'] = 1 then Begin
if _remove_run(tagArr[tagInd]['pNode'], tagArr[tagInd]['rNode']) then
tagArr[tagInd]['pNode'] := nil;
tagArr[tagInd]['rNode'] := nil;
End;
//删除[/TAG]尾部
for delI := tagInd+1 to length(tagArr)-1 do Begin
if _remove_run(tagArr[delI]['pNode'], tagArr[delI]['rNode']) then
tagArr[delI]['pNode'] := nil;
tagArr[delI]['rNode'] := nil;
End;
//[tag]后
firstNode := tagArr[0]['rNode'];
tagInd := length(tagArr) - i + tmp['head-end-paragraph'] - 1;
//println('===============================head-end-pos={},head-end-txtlen={}',tmp['head-end-pos'] , tmp['head-end-txtlen']);
if tmp['head-end-pos'] < tmp['head-end-txtlen'] then Begin
oldNode := tagArr[tagInd]['rNode'];
tagArr[tagInd]['rNode'] := run._duplicate_r(oldNode);
run._adjust_r(tagArr[tagInd]['rNode'], tmp['head-end-wz'] - 1, tmp['head-end-txtlen']);
End
else if tmp['head-end-pos'] = tmp['head-end-txtlen'] and tagInd then Begin
if _remove_run(tagArr[tagInd]['pNode'], tagArr[tagInd]['rNode']) then
tagArr[tagInd]['pNode'] := nil;
tagArr[tagInd]['rNode'] := nil;
End;
//前[tag]
if tmp['head-begin-pos'] > 1 then Begin
run._adjust_r(firstNode, 0, tmp['head-begin-wz'] - 1);
if tmp['head-begin-paragraph'] <> tmp['head-end-paragraph'] then
tagArr[0]['rNode'] := nil;
End
else if tmp['head-begin-pos'] = 1 then Begin
if _remove_run(tagArr[0]['pNode'], firstNode) then
tagArr[0]['pNode'] := nil;
if tmp['head-begin-paragraph'] <> tmp['head-end-paragraph'] then
tagArr[0]['rNode'] := nil;
End;
for delI := 1 to tagInd-1 do Begin
if _remove_run(tagArr[delI]['pNode'], tagArr[delI]['rNode']) then
tagArr[delI]['pNode'] := nil;
tagArr[delI]['rNode'] := nil;
End;
//执行TAG逻辑
r := array();
for nI := 0 to length(tagArr)-1 do Begin
if ifObj(tagArr[nI]['rNode']) then
r[length(r)] := tagArr[nI];
End;
tagObj.Init(tagName, attribute, r);
tagObj.Apply();
tslTagCount++;
tagStatus := '';
tagArr := array();
break;
End
else if length(tagStr) < length(tagTail) and lowercase(c) = lowercase(tagTail[ length(tagStr) + 1 ]) then Begin
tagStr += tagTail[ length(tagStr) + 1 ];
if tagStr = '[' then Begin
tmp['tail-begin-paragraph'] := i;
tmp['tail-begin-pos'] := k;
tmp['tail-begin-wz'] := wz;
tmp['tail-begin-txtlen'] := txtLen;
End;
End
else //继续寻找TAG结束标志
tagStr := '';
End;
if Ord(c) > 127 then Begin
k ++;
End
k ++;
wz ++;
End;
if length(tagArr) then Begin
if i and i < length(tArr) and tArr[i]['pIndex'] <> tArr[i+1]['pIndex'] then Begin
endPos ++;
End;
ind := length(tagArr) - 1;
if tagArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tagArr[ind]['rIndex'] <> tArr[i]['rIndex'] then
tagArr[ind + 1] := tArr[i];
End;
i += iStep;
End;
return array(length(errArr), tslTagCount, errArr);
End;
Function GetHeadingListImpl(docx, posOpt, UpperHeadingLevel, LowerHeadingLevel, numIds, bHeadList); Function GetHeadingListImpl(docx, posOpt, UpperHeadingLevel, LowerHeadingLevel, numIds, bHeadList);
Begin Begin
r := array(); r := array();
@ -13679,6 +13910,17 @@ Type TDocumentBody = Class(DocObject)
End; End;
End; End;
Function _remove_run(pNode, rNode);
Begin
pNode.DeleteChild(rNode);
node := pNode.FirstChildElement('w:r');
if not ifObj(node) then Begin
pNode.Parent().DeleteChild(pNode);
return true;
End;
return false;
End;
zipfile_; zipfile_;
lastParagraph_; lastParagraph_;
document_; document_;

View File

@ -272,6 +272,7 @@ Type TSDocxFile = Class
chart.pNode := p.node_; chart.pNode := p.node_;
p.Node().InsertEndChild(o.GetInnerXml()); p.Node().InsertEndChild(o.GetInnerXml());
TOfficeApi().Set('CurrentShape', p.node_); TOfficeApi().Set('CurrentShape', p.node_);
chart.chartFileName := 'word/charts/chart' $ o.ChartId_ $ '.xml';
return chart; return chart;
End; End;
@ -293,6 +294,74 @@ Type TSDocxFile = Class
return r; return r;
End; End;
///从Excel中Copy指定的chart图到文档中指定位置
///excelFileNamestring xlsx文件名
///excelSheetNamestring sheetname
///chartNamestring or integerchart图名称或当前sheet中chart图索引号
///Widthchart图宽度单位cm
///Heightchart图高度单位cm
///posOpt: 段落位置0 在DOCX文件开头-1 文件尾N 在第N段之后XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
///返回TChart对象
Function CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, posOpt);
Begin
excel := new TSExcelFile();
[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;
///遍历文档中所有[TSTAG][/TSTAG]标签针对每一个TAG执行tagObj.Apply()
///tagNamestring 标签名称
///tagObjTAG对象方法
///返回:[err,tslTagCount,errArr]: err 执行错误TAG次数tslTagCount TAG总数errArr 错误信息array(('code':'代码', 'err':'错误信息'))
Function ExecTsTag(tagName, tagObj);
Begin
return Body().ExecTsTag(self, tagName, tagObj);
End;
///文档中全部的批注信息 ///文档中全部的批注信息
///返回DocComments对象 ///返回DocComments对象
Function Comments(); Function Comments();

View File

@ -349,10 +349,12 @@ Type TSExcelFile = Class
///sheet: string工作表名称 ///sheet: string工作表名称
///topLeft: string左上角坐标如: "A4" ///topLeft: string左上角坐标如: "A4"
///bottomRight: string右下角坐标如: "B8"为空获取从topLeft开始的整张表 ///bottomRight: string右下角坐标如: "B8"为空获取从topLeft开始的整张表
///[IncludeHeader: bool] 是否包括表头默认FALSE
///[IncludeIndex: bool] 是否包括索引号默认FALSE
///返回: table ///返回: table
Function GetTable(sheet, topLeft, bottomRight); Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
Begin Begin
return workbook_.GetTable(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight); return workbook_.GetTable(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
End; End;
///插入列,在指定列前插入空白列 ///插入列,在指定列前插入空白列

View File

@ -8,6 +8,7 @@ Type TDocxChart = Class(TSChart)
chartId_ := 1 + vselect countof( ['FileName'] ) from docx.Zip().Files() where AnsiStartsText('word/charts/chart', ['FileName']) end; chartId_ := 1 + vselect countof( ['FileName'] ) from docx.Zip().Files() where AnsiStartsText('word/charts/chart', ['FileName']) end;
targetFileName := 'charts/chart' $ chartId_ $ '.xml'; targetFileName := 'charts/chart' $ chartId_ $ '.xml';
chartFile := 'word/' + targetFileName; chartFile := 'word/' + targetFileName;
chartFileName_ := chartFile;
docx.Zip().Add(chartFile, GetDefaultXml()); docx.Zip().Add(chartFile, GetDefaultXml());
xmlObj_ := docx.Zip().Get(chartFile); xmlObj_ := docx.Zip().Get(chartFile);
if not chartData_.DisableExcel and istable(chartData.Series) and istable(chartData.Series[0]['Categories']) and istable(chartData.Series[0]['Values']) then Begin if not chartData_.DisableExcel and istable(chartData.Series) and istable(chartData.Series[0]['Categories']) and istable(chartData.Series[0]['Values']) then Begin

View File

@ -0,0 +1,49 @@
Type TSTag = Class
///缺省构造函数
Function Create(); overload;
Begin
End;
Function Init(tagName, attribute, r);
Begin
tagName_ := tagName;
attribute_ := attribute;
runArr_ := r;
End;
Function Apply(); virtual;
Begin
case tagName_ of
'add':
_add();
'del':
_del();
End;
End;
Function _add();
Begin
for i:=0 to length(runArr_)-1 do Begin
run := TOfficeObj('TRun');
run.Init(runArr_[i]['rNode']);
run.Font.Size := 40;
run.Font.Color := 'FF0000';
run.Font.Bold := true;
run.Apply();
End;
End;
Function _del();
Begin
for i:=0 to length(runArr_)-1 do Begin
runArr_[i]['pNode'].DeleteChild(runArr_[i]['rNode']);
child := runArr_[i]['pNode'].FirstChildElement('w:r');
if not ifObj(child) then
runArr_[i]['pNode'].Parent().DeleteChild(runArr_[i]['pNode']);
End;
End;
tagName_:string;
attribute_:string;
runArr_;
End;

View File

@ -1,3 +1,6 @@
Function _ExcelChartCopyPic(excelFileName, excelSheetName, picType); Function _ExcelChartCopyPic(excelFileName, excelSheetName, chartName, Width, Height);
Begin Begin
docx := TOfficeApi().GetDocument();
chart := docx.CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, TOfficeApi().GetCurrentPosition());
return ifObj(chart);
End; End;

View File

@ -138,7 +138,7 @@ Type xlsxWorkBook = Class
return class(ErrorMessage).Fail(); return class(ErrorMessage).Fail();
End; End;
Function GetTable(sheet, topLeft, bottomRight); Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
Begin Begin
o := GetSheetObj(sheet); o := GetSheetObj(sheet);
if ifObj(o) then if ifObj(o) then
@ -162,8 +162,6 @@ Type xlsxWorkBook = Class
end end
End; End;
///创建新sheet
///sheet: string工作表名称
Function NewSheet(sheet);overload; Function NewSheet(sheet);overload;
Begin Begin
lname := LowerCase(sheet); lname := LowerCase(sheet);
@ -696,6 +694,7 @@ Type xlsxWorkBook = Class
chartFile := ReplaceStr(target, '..', 'xl'); chartFile := ReplaceStr(target, '..', 'xl');
chart := TOfficeObj('TChart'); chart := TOfficeObj('TChart');
chart.Rid := node.GetAttribute('Id'); //rid chart.Rid := node.GetAttribute('Id'); //rid
chart.drawingFileName := drawingFile;
setChartInfo(chartFile, chart); setChartInfo(chartFile, chart);
charts[i] := chart; charts[i] := chart;
i++; i++;
@ -1148,6 +1147,14 @@ Type xlsxWorkBook = Class
Function ProtectSheet(sheet, protect); Function ProtectSheet(sheet, protect);
Begin Begin
if protect.AlgorithmName and protect.Password then
begin
protect.SaltValue := nil;//protect.Password;
protect.HashValue := nil;//GetMsgDigest(protect.SaltValue, 6);
protect.SpinCount := nil;
protect.Password := nil;
protect.AlgorithmName := nil;
end
sheet_obj := GetSheetXmlfile(sheet); sheet_obj := GetSheetXmlfile(sheet);
work_node := sheet_obj.FirstChildElement('worksheet'); work_node := sheet_obj.FirstChildElement('worksheet');
sheet_protection_node := work_node.FirstChildElement('sheetProtection'); sheet_protection_node := work_node.FirstChildElement('sheetProtection');