This commit is contained in:
csh 2023-06-20 18:17:39 +08:00
parent 0e21645b2a
commit c706d2d8d0
11 changed files with 516 additions and 108 deletions

View File

@ -8,9 +8,12 @@ TSOffice 项目:纯 TSL 代码实现 excel、word 文件读写
对应文件夹的dll文件按如下要求 对应文件夹的dll文件按如下要求
- `fmt_pubkrnl_plugin` 放入 tsl 安装根目录下 Plugin 文件夹
- `office_plugin` 放入 tsl 安装根目录下 Plugin 文件夹 - `office_plugin` 放入 tsl 安装根目录下 Plugin 文件夹
可选:
- `fmt_pubkrnl_plugin` 放入 tsl 安装根目录下 Plugin 文件夹若需要执行Demo文件夹下的[ExcelHelp.tsl](./Demo/ExcelHelp.tsl)和[WordHelp.tsl](./Demo/WordHelp.tsl),需要部署改动态库
### Linux ### Linux
根据架构选择aarch64或x86版本将对应文件夹内容进行部署 根据架构选择aarch64或x86版本将对应文件夹内容进行部署

View File

@ -1,10 +1,12 @@
// Version 1.3.0 // Version 1.3.2
Function TOfficeObj(n); Function TOfficeObj(n);
Begin Begin
case lowercase(n) of case lowercase(n) of
"nodeinfo": "nodeinfo":
return new NodeInfo(""); return new NodeInfo("");
"trange":
return new TRange("");
"tfont": "tfont":
return new TFont(); return new TFont();
"tcomment": "tcomment":
@ -1353,6 +1355,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 +1398,7 @@ type TChartImpl=class(NodeInfo)
Excel; Excel;
chartFileName; chartFileName;
excelFileName; excelFileName;
drawingFileName;
End; End;
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
@ -9933,7 +9937,7 @@ type TProtect=class(NodeInfo)
Function GetAttrs(); override; Function GetAttrs(); override;
Begin Begin
HandleAttrs(); HandleAttrs();
return array(("Sheet", "sheet", Sheet, ""),("EditObjects", "objects", EditObjects, ""),("EditScenarios", "scenarios", EditScenarios, ""),("FormatCells", "formatCells", FormatCells, ""),("FormatColumns", "formatColumns", FormatColumns, ""),("FormatRows", "formatRows", FormatRows, ""),("InsertColumns", "insertColumns", InsertColumns, ""),("InsertRows", "insertRows", InsertRows, ""),("InsertHyperlinks", "insertHyperlinks", InsertHyperlinks, ""),("DeleteColumns", "deleteColumns", DeleteColumns, ""),("DeleteRows", "deleteRows", DeleteRows, ""),("SelectLockedCells", "selectLockedCells", SelectLockedCells, ""),("Sort", "sort", Sort, ""),("AutoFilter", "autoFilter", AutoFilter, ""),("PivotTables", "pivotTables", PivotTables, ""),("SelectUnLockedCells", "selectUnlockedCells", SelectUnLockedCells, "")) union ExtAttr; return array(("Password", "password", Password, ""),("AlgorithmName", "algorithmName", AlgorithmName, ""),("HashValue", "hashValue", HashValue, ""),("SaltValue", "saltValue", SaltValue, ""),("SpinCount", "spinCount", SpinCount, ""),("Sheet", "sheet", Sheet, ""),("EditObjects", "objects", EditObjects, ""),("EditScenarios", "scenarios", EditScenarios, ""),("FormatCells", "formatCells", FormatCells, ""),("FormatColumns", "formatColumns", FormatColumns, ""),("FormatRows", "formatRows", FormatRows, ""),("InsertColumns", "insertColumns", InsertColumns, ""),("InsertRows", "insertRows", InsertRows, ""),("InsertHyperlinks", "insertHyperlinks", InsertHyperlinks, ""),("DeleteColumns", "deleteColumns", DeleteColumns, ""),("DeleteRows", "deleteRows", DeleteRows, ""),("SelectLockedCells", "selectLockedCells", SelectLockedCells, ""),("Sort", "sort", Sort, ""),("AutoFilter", "autoFilter", AutoFilter, ""),("PivotTables", "pivotTables", PivotTables, ""),("SelectUnLockedCells", "selectUnlockedCells", SelectUnLockedCells, "")) union ExtAttr;
End; End;
Function GetChildren(); override; Function GetChildren(); override;
@ -9943,6 +9947,11 @@ type TProtect=class(NodeInfo)
End; End;
//Attributes //Attributes
Password;
AlgorithmName;
HashValue;
SaltValue;
SpinCount;
Sheet; Sheet;
EditObjects; EditObjects;
EditScenarios; EditScenarios;
@ -10149,11 +10158,20 @@ End;
///DOCX文档实现 ///DOCX文档实现
Type TRange = Class Type TRange = Class
Function Create(t); Function Create();overload;
Begin
Create(nil);
End;
Function Create(t);overload;
Begin Begin
//array(("pNode":nodeObj, "pIndex":p, "rNode":nodeObj, "rIndex":r)) //array(("pNode":nodeObj, "pIndex":p, "rNode":nodeObj, "rIndex":r))
Init(t);
End;
Function Init(t);
Begin
RunArr_ := t; RunArr_ := t;
rPr_ := new TwrPr();
End; End;
///清除全部选中内容 ///清除全部选中内容
@ -10165,6 +10183,8 @@ Type TRange = Class
Property Font read readFont; Property Font read readFont;
Function readFont(); Function readFont();
Begin Begin
if ifNil(rPr_) then
rPr_ := new TwrPr();
return rPr_; return rPr_;
End; End;
@ -10186,6 +10206,7 @@ Type TRange = Class
///应用字体样式 ///应用字体样式
Function Apply(); Function Apply();
Begin Begin
if not ifObj(rPr_) then return;
arr := rPr_.Marshal(); arr := rPr_.Marshal();
if length(arr['attributes']) or length(arr['children']) then Begin if length(arr['attributes']) or length(arr['children']) then Begin
for i:=0 to length(RunArr_)-1 do Begin for i:=0 to length(RunArr_)-1 do Begin
@ -10197,11 +10218,9 @@ Type TRange = Class
Function _clear(first); Function _clear(first);
Begin Begin
for i:=first to length(RunArr_)-1 do Begin
RunArr_[i]['pNode'].DeleteChild(RunArr_[i]['rNode']);
End;
for i:=first to length(RunArr_)-1 do Begin for i:=first to length(RunArr_)-1 do Begin
pNode := RunArr_[i]['pNode']; pNode := RunArr_[i]['pNode'];
pNode.DeleteChild(RunArr_[i]['rNode']);
rNode := pNode.FirstChildElement('w:r'); rNode := pNode.FirstChildElement('w:r');
if not ifObj(rNode) then if not ifObj(rNode) then
pNode.Parent().DeleteChild(pNode); pNode.Parent().DeleteChild(pNode);
@ -11323,20 +11342,22 @@ Type TPicture = Class(DocObject, TPictureImpl)
ETU := 360045;//1cm单位 ETU := 360045;//1cm单位
maxX := 17.0;//水平方向17cm maxX := 17.0;//水平方向17cm
maxY := 23.0;//垂直方向23cm maxY := 23.0;//垂直方向23cm
imageW := image.Width() / 28.346 * ETU;
imageH := image.Height() / 28.346 * ETU;
if Width <= 0 and Height <= 0 then Begin //图像缺省大小 if Width <= 0 and Height <= 0 then Begin //图像缺省大小
widthVal := image.Width(); widthVal := imageW;
heightVal := image.Height(); heightVal := imageH;
End End
else Begin //用户设置了图片尺寸 else Begin //用户设置了图片尺寸
widthVal := ETU * Width; widthVal := ETU * Width;
heightVal := ETU * Height; heightVal := ETU * Height;
if Width <= 0 and image.Height() and image.Width() then Begin //按照图片比例自动缩放 if Width <= 0 and imageH and imageW then Begin //按照图片比例自动缩放
scaling_factor := image.Width() / image.Height(); scaling_factor := imageW / imageH;
widthVal := round(heightVal * scaling_factor); widthVal := round(heightVal * scaling_factor);
End; End;
if Height <= 0 and image.Height() and image.Width() then Begin //按照图片比例自动缩放 if Height <= 0 and imageH and imageW then Begin //按照图片比例自动缩放
scaling_factor := image.Width() / image.Height(); scaling_factor := imageW / imageH;
heightVal := round(widthVal / scaling_factor); heightVal := round(widthVal / scaling_factor);
End; End;
End; End;
@ -11361,7 +11382,7 @@ Type TPicture = Class(DocObject, TPictureImpl)
heightVal *= ratio; heightVal *= ratio;
widthVal *= ratio; widthVal *= ratio;
End; End;
//println('w={}-{},maxx={}, h={}-{},maxh={},ratio={}',integer(image.Width()),integer(widthVal),15 * ETU, integer(image.Height()),integer(heightVal),23 * ETU,ratio); //println('w={}-{},maxx={}, h={}-{},maxh={},ratio={}',integer(imageW),integer(widthVal),15 * ETU, integer(imageH),integer(heightVal),23 * ETU,ratio);
return array(integer(widthVal), integer(heightVal)); return array(integer(widthVal), integer(heightVal));
End; End;
@ -12112,7 +12133,7 @@ private
cnt := length(data); cnt := length(data);
//[err, axis] := ColumnNumberToName(2 * ind + 2); //[err, axis] := ColumnNumberToName(2 * ind + 2);
[err, axis] := ColumnNumberToName(ind + 2); [err, axis] := ColumnNumberToName(ind + 2);
Val.NumRef.F := fmt('Sheet1!${}$2:${}${}',axis, axis, cnt+1); Val.NumRef.F := format('Sheet1!$%s$2:$%s$%d', axis, axis, cnt+1);
Val.NumRef.NumCache.PtCount := cnt; Val.NumRef.NumCache.PtCount := cnt;
Val.NumRef.NumCache.formatCode := 'General'; Val.NumRef.NumCache.formatCode := 'General';
for i:=0 to cnt-1 do Begin for i:=0 to cnt-1 do Begin
@ -12141,8 +12162,7 @@ private
else if istable(Categories) then Begin //word 图表 else if istable(Categories) then Begin //word 图表
cnt := length(Categories); cnt := length(Categories);
//[err, axis] := ColumnNumberToName(2 * ind + 1); //[err, axis] := ColumnNumberToName(2 * ind + 1);
//cat.StrRef.F := fmt('Sheet1!${}$2:${}${}',axis, axis, cnt+1); cat.StrRef.F := format('Sheet1!$A$2:$A$%d', cnt+1);
cat.StrRef.F := fmt('Sheet1!$A$2:$A${}', cnt+1);
cat.StrRef.StrCache.PtCount := cnt; cat.StrRef.StrCache.PtCount := cnt;
for i:=0 to cnt-1 do Begin for i:=0 to cnt-1 do Begin
Tpt := TOfficeObj('Tpt'); Tpt := TOfficeObj('Tpt');
@ -12946,6 +12966,7 @@ Type TDocumentBody = Class(DocObject)
Function InsertTable(tbl, posOpt); Function InsertTable(tbl, posOpt);
Begin Begin
addPart(posOpt, tbl); addPart(posOpt, tbl);
TOfficeApi().Set('CurrentTable', tbl.node_);
tblBorders := class(TSXml).GetNode(tbl.node_, 'w:tblPr/w:tblBorders'); tblBorders := class(TSXml).GetNode(tbl.node_, 'w:tblPr/w:tblBorders');
tblCellMar := class(TSXml).GetNode(tbl.node_, 'w:tblPr/w:tblCellMar'); tblCellMar := class(TSXml).GetNode(tbl.node_, 'w:tblPr/w:tblCellMar');
if ifObj(tblBorders) or ifObj(tblCellMar) then Begin if ifObj(tblBorders) or ifObj(tblCellMar) then Begin
@ -13107,6 +13128,7 @@ Type TDocumentBody = Class(DocObject)
p := new TPicture(picture.node_); p := new TPicture(picture.node_);
_set_lastParagraph_(posOpt, picture.node_); _set_lastParagraph_(posOpt, picture.node_);
TOfficeApi().Set('CurrentShape', picture.node_);
return p; return p;
End; End;
@ -13268,8 +13290,9 @@ Type TDocumentBody = Class(DocObject)
begParagraphIndex := -1; begParagraphIndex := -1;
begPos := 0; begPos := 0;
begTxtLen := 0; begTxtLen := 0;
for i:=0 to length(tArr)-1 do Begin while i < length(tArr) do Begin
txt := ''; txt := '';
iStep := 1;
if ifObj(tArr[i]['rNode']) then Begin if ifObj(tArr[i]['rNode']) then Begin
run := new TRun(tArr[i]['rNode']); run := new TRun(tArr[i]['rNode']);
txt := run.Text(); txt := run.Text();
@ -13342,7 +13365,8 @@ Type TDocumentBody = Class(DocObject)
flag := '[TSL]'; flag := '[TSL]';
'[TSL][/TSL': '[TSL][/TSL':
if c = ']' then Begin if c = ']' then Begin
curParagraphNode := tslArr[0]['pNode']; tslBegParagraphNode := tslArr[0]['pNode'];
tslEndParagraphNode := tArr[i]['pNode'];
ind := length(tslArr) - 1; ind := length(tslArr) - 1;
if tslArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tslArr[ind]['rIndex'] <> tArr[i]['rIndex'] then Begin if tslArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tslArr[ind]['rIndex'] <> tArr[i]['rIndex'] then Begin
ind ++; ind ++;
@ -13359,7 +13383,7 @@ Type TDocumentBody = Class(DocObject)
oldNode := prevRun; oldNode := prevRun;
tArr[i]['rNode'] := run._duplicate_r(oldNode); tArr[i]['rNode'] := run._duplicate_r(oldNode);
run._adjust_r(tArr[i]['rNode'], wz, txtLen); run._adjust_r(tArr[i]['rNode'], wz, txtLen);
i--; iStep := 0;
End; End;
curRunNode := tslArr[0]['rNode']; curRunNode := tslArr[0]['rNode'];
curRun := new TRun(curRunNode); curRun := new TRun(curRunNode);
@ -13382,92 +13406,76 @@ Type TDocumentBody = Class(DocObject)
code := leftstr(code, lengthW(code) - 5 - endPos); code := leftstr(code, lengthW(code) - 5 - endPos);
CodePage := TOfficeApi().Get('CodePage'); CodePage := TOfficeApi().Get('CodePage');
TOfficeApi().Set('Docx', docx); TOfficeApi().Set('Docx', docx);
TOfficeApi().Set('CurrentParagraph', curParagraphNode); TOfficeApi().Set('CurrentParagraph', tslBegParagraphNode);
TOfficeApi().Set('CurrentPosition', tslBegParagraphNode);
TOfficeApi().Set('CurrentRun', curRunNode); TOfficeApi().Set('CurrentRun', curRunNode);
TOfficeApi().Set('CodePage', 'gbk'); TOfficeApi().Set('CodePage', 'gbk');
try try
tslFuncCount ++; tslFuncCount ++;
echo format('run code=[%s]\n',code);
if code <> '' then if code <> '' then
str := eval(&code); eval(&code);
except except
println('run code={},err={}', code, ExceptObject.ErrInfo); echo format('run code=%s,err=%s\n', code, ExceptObject.ErrInfo);
errArr[ length(errArr) ] := array('code':code, 'err':ExceptObject.ErrInfo); errArr[ length(errArr) ] := array('code':code, 'err':ExceptObject.ErrInfo);
str := '';
End; End;
tNode := curRun.node_.FirstChildElement('w:t'); tNode := curRun.node_.FirstChildElement('w:t');
if not ifObj(tNode) then Begin //没有在外部插入文字 if not ifObj(tNode) then Begin //没有在外部插入文字
if not ifstring(str) or str = '' then Begin //删除w:r tslBegParagraphNode.DeleteChild(curRunNode);
curParagraphNode.DeleteChild(curRunNode); //兼容WordTemplate函数不删除空段落TSL脚本段落
hasrNode := curParagraphNode.FirstChildElement('w:r'); //hasrNode := tslBegParagraphNode.FirstChildElement('w:r');
if not ifObj(hasrNode) then Begin//删除空段落 //if not ifObj(hasrNode) then Begin//删除空段落
curParagraphNode.Parent().DeleteChild(curParagraphNode); // tslBegParagraphNode.Parent().DeleteChild(tslBegParagraphNode);
curParagraphNode := nil; // tslBegParagraphNode := nil;
//End;
End; End;
End //段落炸裂
else Begin curPosotion := TOfficeApi().Get('CurrentPosition');
str := AnsiToUTF8(str); if i+iStep < length(tArr) and tArr[i+iStep]['pNode'] = tslEndParagraphNode and tslBegParagraphNode <> curPosotion then Begin
lines := str2array(str, '\r\n'); data := tslEndParagraphNode.Marshal();
lineCnt := length(lines); NewParagraphNode := tslEndParagraphNode.Parent().InsertAfterChild(curPosotion, data[0]);//复制段落
if lineCnt = 1 then Begin //单行 rmvArr := array();
curRun.SetText(str, true); cnt := 0;
End
else Begin //多行
curRun.SetText(lines[0], true);
nextRun := curRun.node_.NextElement('w:r');
if ifObj(nextRun) then Begin //段落炸裂
curLine := 0; curLine := 0;
prevRNode := curRunNode; prevRNode := tArr[i+iStep]['rNode'].PrevElement('w:r');
while ifObj(prevRNode) do Begin while ifObj(prevRNode) do Begin
curLine ++; curLine ++;
prevRNode := prevRNode.PrevElement('w:r'); prevRNode := prevRNode.PrevElement('w:r');
End; End;
data := curParagraphNode.Marshal(); rmvN := 0;
LastParagraphNode := curParagraphNode.Parent().InsertAfterChild(curParagraphNode, data[0]);//复制段落 nextRNode := tArr[i+iStep]['rNode'];
rmvArr := array(); while ifObj(nextRNode) do Begin
cnt := 0; rmvArr[rmvN++] := nextRNode;
rNode := LastParagraphNode.FirstChildElement('w:r'); nextRNode := nextRNode.NextElement('w:r');
End;
rNode := NewParagraphNode.FirstChildElement('w:r');
nI := 0;
rIndex := 0;
while ifObj(rNode) do Begin while ifObj(rNode) do Begin
cnt ++; cnt ++;
if cnt < curLine then if cnt <= curLine then Begin
rmvArr[rmvN++] := rNode; rmvArr[rmvN++] := rNode;
else if cnt = curLine then Begin End
rNode.ClearText(); else Begin
rNode.SetText(lines[lineCnt-1], true); tArr[i+iStep+nI]['pNode'] := NewParagraphNode;
lineCnt--; tArr[i+iStep+nI]['rNode'] := rNode;
tArr[i+iStep+nI]['rIndex'] := rIndex++;
nr := new TRun(tArr[i+iStep+nI]['rNode']);
nI ++;
End; End;
rNode := rNode.NextElement('w:r'); rNode := rNode.NextElement('w:r');
End; End;
cnt := 0;
rNode := curParagraphNode.FirstChildElement('w:r');
while ifObj(rNode) do Begin
cnt ++;
if cnt > curLine then
rmvArr[rmvN++] := rNode;
rNode := rNode.NextElement('w:r');
End;
//删除重复的run //删除重复的run
for rmvN := 0 to rmvN < length(rmvArr)-1 do begin for rmvN := 0 to length(rmvArr)-1 do begin
rmvArr[rmvN].Parent().DeleteChild(rmvArr[rmvN]); rmvArr[rmvN].Parent().DeleteChild(rmvArr[rmvN]);
End; End;
End;//段落炸裂
curParagraph := new TParagraph(curParagraphNode);
prev := curParagraph;
for nP:=1 to lineCnt - 1 do Begin //新段落
if lines[nP]='' then continue;
p := new TParagraph();
p.Run.SetText(lines[nP], true);
prev := docx.AddParagraph(p, prev.Node());
docx.CopyFormat(curParagraph, prev);//格式刷
End;
End;
End;
End; End;
TOfficeApi().Set('CodePage', CodePage); TOfficeApi().Set('CodePage', CodePage);
flag := ''; flag := '';
code := nil; code := nil;
tslArr := array(); tslArr := array();
continue; break;
End End
else else
flag := '[TSL]'; flag := '[TSL]';
@ -13492,10 +13500,232 @@ Type TDocumentBody = Class(DocObject)
if tslArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tslArr[ind]['rIndex'] <> tArr[i]['rIndex'] then if tslArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tslArr[ind]['rIndex'] <> tArr[i]['rIndex'] then
tslArr[ind + 1] := tArr[i]; tslArr[ind + 1] := tArr[i];
End; End;
i += iStep;
End; End;
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);
tmp['tag-end'] := tagInd;
End
else if tmp['tail-begin-pos'] = 1 then Begin
tmp['tag-end'] := tagInd - 1;
End;
//[tag]后
firstNode := tagArr[0]['rNode'];
tagInd := length(tagArr) - i + tmp['head-end-paragraph'] - 1;
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']);
tmp['tag-beg'] := tagInd;
if tmp['head-begin-paragraph'] <> tmp['head-end-paragraph'] then
tagArr[tagInd]['pNode'].DeleteChild(oldNode);
End
else if tmp['head-end-pos'] = tmp['head-end-txtlen'] and tagInd then Begin
tmp['tag-beg'] := tagInd + 1;
End;
//前[tag]
if tmp['head-begin-pos'] > 1 then Begin
run._adjust_r(firstNode, 0, tmp['head-begin-wz'] - 1);
if firstNode = tagArr[0]['rNode'] then
tagArr[0]['rNode'] := nil;
End
else if tagInd=0 and tmp['tag-beg']=0 then
tagArr[0]['pNode'].DeleteChild(firstNode);
//执行TAG逻辑
r := array();
for nI := 0 to length(tagArr)-1 do Begin
if not ifObj(tagArr[nI]['rNode']) then continue;
if nI >= tmp['tag-beg'] and nI <= tmp['tag-end'] then
r[length(r)] := tagArr[nI];//标签中间文字内容
else Begin //删除标签
tagArr[nI]['pNode'].DeleteChild(tagArr[nI]['rNode']);
node := tagArr[nI]['pNode'].FirstChildElement('w:r');
if not ifObj(node) then Begin
tagArr[nI]['pNode'].Parent().DeleteChild(tagArr[nI]['pNode']);
End;
End;
End;
tagObj.Init(tagName, tagAttribute, 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();
@ -13625,6 +13855,7 @@ Type TDocumentBody = Class(DocObject)
else Begin else Begin
raise 'Invalid input param.'; raise 'Invalid input param.';
End; End;
TOfficeApi().Set('CurrentPosition', o.node_);
End; End;
Function findNode(posOpt, b); Function findNode(posOpt, b);
@ -14131,25 +14362,28 @@ Type TTable = Class(DocObject, TTableImpl)
///设置表格数据 ///设置表格数据
///data: table数据表 ///data: table数据表
///[IncludeHeader: bool] 是否包括表头默认FALSE ///[includeHeader: bool] 是否包括表头默认FALSE
///[IncludeIndex: bool] 是否自动添加索引号默认FALSE ///[includeIndex: bool] 是否自动添加索引号默认FALSE
Function SetData(docx, data, IncludeHeader, IncludeIndex); Function SetData(docx, data, includeHeader, includeIndex);
Begin Begin
if not IsTable(data) then if not IsTable(data) then
raise "Invalid Data."; raise "Invalid Data.";
[rowcount,colcount] := mSize(data);//一维数组
if colcount = 0 then
data := array(data);
fields := FieldNames(data); fields := FieldNames(data);
if IsTable(fields) then Begin if IsTable(fields) then Begin
r := nils(length(data),length(fields)); r := nils(length(data),length(fields));
r[:,:] := data; r[:,:] := data;
data := r; data := r;
End; End;
if IncludeIndex then Begin if includeIndex then Begin
n := length(data)-1; n := length(data)-1;
r := array(0->n); r := array(0->n);
data := `r | data; data := `r | data;
End; End;
if IncludeHeader and IsTable(fields) then Begin if includeHeader and IsTable(fields) then Begin
if IncludeIndex then if includeIndex then
fields := array("Index") union fields; fields := array("Index") union fields;
data := array(fields) union data; data := array(fields) union data;
End; End;

View File

@ -1,4 +1,4 @@
// Version 1.3.0 // Version 1.3.2
Type TSDocxFile = Class Type TSDocxFile = Class
///Version: V1.0 2022-09-20 ///Version: V1.0 2022-09-20
@ -271,6 +271,8 @@ Type TSDocxFile = Class
p := AddParagraph(p, getPosNode(posOpt), nil); p := AddParagraph(p, getPosNode(posOpt), nil);
chart.pNode := p.node_; chart.pNode := p.node_;
p.Node().InsertEndChild(o.GetInnerXml()); p.Node().InsertEndChild(o.GetInnerXml());
TOfficeApi().Set('CurrentShape', p.node_);
chart.chartFileName := 'word/charts/chart' $ o.ChartId_ $ '.xml';
return chart; return chart;
End; End;
@ -292,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

@ -1,4 +1,4 @@
// Version 1.3.0 // Version 1.3.2
Type TSExcelFile = Class Type TSExcelFile = Class
///Version: V1.0 2022-08-08 ///Version: V1.0 2022-08-08
@ -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

@ -73,6 +73,11 @@ Type TOffice = Class
hash_['Paragraph-Node-' + name] := hash_['CurrentParagraph']; hash_['Paragraph-Node-' + name] := hash_['CurrentParagraph'];
End; End;
Function GetCurrentPosition()
Begin
return hash_['CurrentPosition'];
End;
///获取当前TSL代码段所在段落 ///获取当前TSL代码段所在段落
///返回TParagraph对象 ///返回TParagraph对象
Function GetCurrentParagraph(); Function GetCurrentParagraph();

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
@ -29,16 +30,16 @@ Type TDocxChart = Class(TSChart)
Function GetInnerXml(); Function GetInnerXml();
Begin Begin
ETU := 360045;//1cm单位 ETU := 360045;//1cm单位
return fmt('<w:r> return format('<w:r>
<w:drawing> <w:drawing>
<wp:inline> <wp:inline>
<wp:extent cx="{}" cy="{}"/> <wp:extent cx="%d" cy="%d"/>
<wp:effectExtent l="0" t="0" r="19050" b="19050"/> <wp:effectExtent l="0" t="0" r="19050" b="19050"/>
<wp:docPr id="{}" name="{}"/> <wp:docPr id="%d" name="%s"/>
<wp:cNvGraphicFramePr /> <wp:cNvGraphicFramePr />
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"> <a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart"> <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart">
<c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId{}"/> <c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId%d"/>
</a:graphicData> </a:graphicData>
</a:graphic> </a:graphic>
</wp:inline> </wp:inline>

View File

@ -0,0 +1,72 @@
Type TSTag = Class
///缺省构造函数
Function Create(); overload;
Begin
End;
Function Init(tagName, attribute, r);
Begin
tagName_ := tagName;
attribute_ := attribute;
runArr_ := r;
range_ := nil;
map_ := nil;
End;
Property Range read readRange;
Function readRange();
Begin
if ifNil(range_) then Begin
range_ := TOfficeObj('TRange');
range_.Init(runArr_);
End;
return range_;
End;
///获取TAG中属性 [font size=12]...[/font]
/// tag.GetAttribute('size');
///返回string
Function GetAttribute(key);
Begin
if not ifArray(map_) then Begin
map_ := array();
//println('attribute={}',attribute_);
arr := Str2Array(attribute_, ' ');
for i := 0 to length(arr)-1 do Begin
kv := Str2Array(arr[i], '=');
if length(kv)=2 then
map_[kv[0]] := kv[1];
End;
End;
return map_[key];
End;
Function Apply(); virtual;
Begin
case tagName_ of
'add':
_add();
'del':
_del();
End;
End;
Function _add();
Begin
Range.Font.Size := 40;
Range.Font.Color := 'FF0000';
Range.Font.Bold := true;
Range.Apply();
End;
Function _del();
Begin
Range.Clear();
End;
tagName_:string;
attribute_:string;
runArr_;
range_;
map_;
End;

View File

@ -124,7 +124,7 @@ xmlns:v="urn:schemas-microsoft-com:vml">
if ifObj(node) then Begin if ifObj(node) then Begin
[err, col, row] := excel_.CellNameToCoordinates(cell); [err, col, row] := excel_.CellNameToCoordinates(cell);
clientData := TOfficeObj('TClientData'); clientData := TOfficeObj('TClientData');
clientData.Anchor := fmt('{},23,{},0,{},{},{},5',col, row, 1+col+lineCount, colCount+col-1, 1+row+lineCount); clientData.Anchor := format('%d,23,%d,0,%d,%d,%d,5',col, row, 1+col+lineCount, colCount+col-1, 1+row+lineCount);
clientData.Row := row - 1; clientData.Row := row - 1;
ClientData.Column := col - 1; ClientData.Column := col - 1;
node.UnMarshal(clientData.Marshal()); node.UnMarshal(clientData.Marshal());

View File

@ -101,7 +101,7 @@ private
name := "列" $ col_index++; name := "列" $ col_index++;
excel_.WorkBook().SetCellValue(sheetName_, cell, name); excel_.WorkBook().SetCellValue(sheetName_, cell, name);
end end
table_str += fmt('<tableColumn id="{}" name="{}" />', id++, name); table_str += format('<tableColumn id="%d" name="%s" />', id, name);
end end
table_str += "</tableColumns>"; table_str += "</tableColumns>";

View File

@ -138,14 +138,30 @@ 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 return o.Import(topLeft, bottomRight); 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; End;
///创建新sheet
///sheet: string工作表名称
Function NewSheet(sheet);overload; Function NewSheet(sheet);overload;
Begin Begin
lname := LowerCase(sheet); lname := LowerCase(sheet);
@ -678,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++;

View File

@ -1,5 +1,9 @@
# 更新日志 # 更新日志
## 2023-6-20
更新部署方式,不再依赖`fmt_pubkrnl_plugin.dll`,详情见[README](./README.md)
## 2023-4-26 ## 2023-4-26
### V1.3.1 ### V1.3.1