Compare commits

..
1 Commits
Author SHA1 Message Date
csh 4a2961a635 Merge branch 'word' 2023-12-07 13:41:38 +08:00
35 changed files with 13160 additions and 14151 deletions
-4
View File
@@ -1,4 +0,0 @@
*.swp
*.swo
*.cmd
script.ps1
-3
View File
@@ -1,3 +0,0 @@
[submodule "funcext/TSOffice/Extensions/OfficeVba"]
path = funcext/TSOffice/Extensions/OfficeVba
url = https://git.mytsl.cn/tinysoft/OfficeVba.git
+1 -1
View File
@@ -73,7 +73,7 @@ content := docx.AddTableContent(paragraphTitle, 1, 3);
docx.AddPageBreak(content.node_);
v := docx.SaveAs('', 'DocxFile使用帮助.docx');
v := docx.SaveAs('', TOfficeApi().CurCodePageToGBK('DocxFile使用帮助.docx'));
println('Test Over!\n Save {}: {},time={}秒', file, v, mtoc);
//info := GetProfilerInfo(true);
+3 -5
View File
@@ -1,8 +1,8 @@
path := ExtractFileDir(ExtractFileDir(PluginPath()));
{$IFNDEF Win32}
path := path + '/funcext/TSOffice/TSXlsxFile.tsf';
{$ELSE}
path := path + '\\funcext\\TSOffice\\TSXlsxFile.tsf';
{$ELSE}
path := path + '/funcext/TSOffice/TSXlsxFile.tsf';
{$ENDIF}
funcs := LoadClassInfo(path);
@@ -43,7 +43,6 @@ PrintSuccess("GetCellRichText", testing++);
file.MergeCell('XlsxFile', 'A1', 'C1');
PrintSuccess("MergeCell", testing++);
file.SetRowHeight('XlsxFile', 1, 60);
style := TOfficeObj('TStyle');
@@ -139,7 +138,7 @@ End
file.SetColWidth('Functions', 'A', 'A', 120);
PrintSuccess('SetColWidth', testing++);
saveName := "XlsxFile使用帮助.xlsx";
saveName := utf8toansi("XlsxFile使用帮助.xlsx");
[err, errinfo] := file.SaveAs('', saveName);
if err then
return PrintError("SaveAs", err, errinfo);
@@ -321,7 +320,6 @@ Begin
excel.SetCellValue(sheet_name, cell_1, name);
excel.MergeCell(sheet_name, cell_1, cell_2);
excel.SetCellStyle(sheet_name, cell_1, cell_2, styleid1);
excel.SetRowHeight(sheet_name, row, 25);
row++;
desc_arr := obj_info[i]['Desc'];
Binary file not shown.
Binary file not shown.
+5 -12
View File
@@ -1,4 +1,4 @@
# TSOffice
# OfficePlugin
TSOffice 项目:纯 TSL 代码实现 excel、word 文件读写
@@ -6,19 +6,19 @@ TSOffice 项目:纯 TSL 代码实现 excel、word 文件读写
### windows
对应文件夹的 dll 文件按如下要求
对应文件夹的dll文件按如下要求
- `office_plugin` 放入 tsl 安装根目录下 Plugin 文件夹
可选:
- `fmt_pubkrnl_plugin` 放入 tsl 安装根目录下 Plugin 文件夹,若需要执行 Demo 文件夹下的[XlsxHelp.tsl](./Demo/XlsxHelp.tsl)和[DocxHelp.tsl](./Demo/DocxHelp.tsl),需要部署改动态库
- `fmt_pubkrnl_plugin` 放入 tsl 安装根目录下 Plugin 文件夹,若需要执行Demo文件夹下的[XlsxHelp.tsl](./Demo/XlsxHelp.tsl)和[DocxHelp.tsl](./Demo/DocxHelp.tsl),需要部署改动态库
### Linux
#### Ubuntu-18.04
根据架构选择 aarch64x86 版本将对应文件夹内容进行部署
根据架构选择aarch64x86版本将对应文件夹内容进行部署
- `liboffice_plugin` 放入 tsl 安装根目录下 Plugin 文件夹
@@ -28,14 +28,7 @@ TSOffice 项目:纯 TSL 代码实现 excel、word 文件读写
- [Word 帮助文档](./DocxFile使用帮助.docx)
## 扩展
扩展模块的内容需要通过`git submodule update --init --recursive`获取,相关帮助文档在`./funcext/TSOffice/Extensions/模块名`目录下可获取
支持的扩展:
- **OfficeVba** 可通过类 Vba 方式对 office文件 进行读写操作。
## Demo
Demo 文件夹提供了一些参考的 demo
Demo文件夹提供了一些参考的demo
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+18 -52
View File
@@ -1,4 +1,5 @@
// Version 1.8.8
// Version 1.5.2
Type TSDocxFile = Class
///Version: V1.0 2022-09-20
///适用于 Microsoft Word docx格式文件
@@ -49,7 +50,7 @@ Type TSDocxFile = Class
Begin
if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile();
[err, errmsg] := zipfile_.Open(alias, TOfficeApi().CurCodePageToGBK(fname), passwd);
[err, errmsg] := zipfile_.Open(alias, fname, passwd);
if err=0 then Begin
document_ := new docxDocument(zipfile_);
End;
@@ -87,7 +88,7 @@ Type TSDocxFile = Class
///返回: [err, info]
Function SaveAs(alias, fname);
Begin
return zipfile_.Save(alias, TOfficeApi().CurCodePageToGBK(fname));
return zipfile_.Save(alias, fname);
End;
///另存为二进制流数据
@@ -113,10 +114,7 @@ Type TSDocxFile = Class
///返回:string
Function FileName();
Begin
file_name := zipfile_.FileName();
if TOfficeApi().IsUtf8() then
return AnsiToUTF8(file_name);
return file_name;
return zipfile_.FileName();
End;
///word文档所有段落
@@ -126,7 +124,7 @@ Type TSDocxFile = Class
return document_.Body().Paragraphs();
End;
///word文档最后一个段落
///word文档最后一个段落
///返回:TParagraph对象
Function LastParagraph();
Begin
@@ -240,7 +238,7 @@ Type TSDocxFile = Class
///tbl: TTable对象
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加表格
///customCell: 二维数组,自定义指定单元格的样式
/// 如:一行一列arr[0][0] := array(twtcPr, twpPr, twrPr);其中twtcPr是twtcPr对象,twpPr是twpPr对象,twrPr是twrPr对象
/// 如:一行一列arr[0][0] := array(twtcPr, twpPr, twrPr);其中twtcPr是twtcPr对象,twpPr是twpPr对象,twrPr是twrPr对象
///返回: TTable对象
Function InsertTable(tbl, posOpt, customCell);
Begin
@@ -270,13 +268,6 @@ Type TSDocxFile = Class
return document_.Body().Sections(index);
End;
///获取Structured Document Tag
///返回:TSdt集合
Function Sdts();
Begin
return document_.Body().Sdts();
End;
///添加章节
///sessionTDocSection对象
///posOpt: 位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加章节
@@ -341,7 +332,7 @@ Type TSDocxFile = Class
///返回TChart对象
Function CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, posOpt);
Begin
excel := new TSXlsxFile();
excel := new TSExcelFile();
[err, msg] := excel.OpenFile('', excelFileName);
if err then return nil;
[err, charts] := excel.GetCharts(excelSheetName);
@@ -400,7 +391,6 @@ Type TSDocxFile = Class
///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组
Function InsertFile(alias, fileName, posOpt);overload;
Begin
document_.AddNameSpace('w16du', 'http://schemas.microsoft.com/office/word/2023/wordml/word16du');
docxObj := new TSDocxFile();
[err, msg] := docxObj.OpenFile(alias, fileName);
if err then return array(err, msg);
@@ -419,7 +409,6 @@ Type TSDocxFile = Class
///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组
Function InsertFile(docxObj, posOpt);overload;
Begin
document_.AddNameSpace('w16du', 'http://schemas.microsoft.com/office/word/2023/wordml/word16du');
copy_obj := new TDocxCopy(self, docxObj);
copy_obj.Init();
copy_obj.Copy(posOpt);
@@ -540,25 +529,6 @@ Type TSDocxFile = Class
return content;
End;
///添加自定义目录
///[posOpt: 段落位置],在posOpt之后新添加目录(否则在首页添加)
///UpperHeadingLevel:标题最高级别
///LowerHeadingLevel:标题最低级别
/// 使用 UpperHeadingLevel 属性可设置起始标题级别(最高)。例如,若要设置 TOC 域语法 {TOC \o "1-3"},可将 LowerHeadingLevel 属性设为 3,并将 UpperHeadingLevel 属性设为 1。
///因为word、wps、openoffice等软件对于页码的计算各不相同,本功能不直接设置页码,需要用相关客户端软件打开文件后,更新目录域。
Function AddCustomTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel);
Begin
content := new TTableContent(self);
node := getPosNode(posOpt);
content.CustomAdd(node, UpperHeadingLevel, LowerHeadingLevel); //标题级别
if ifObj(node) then
content.node_ := document_.Body().node_.InsertAfterChild(node, content.Marshal());
else
content.node_ := document_.Body().node_.InsertFirstChild(content.Marshal());
//AddPageBreak(content.node_);
return content;
End;
///获取全部标题列表
///UpperHeadingLevel:标题最高级别
///LowerHeadingLevel:标题最低级别
@@ -629,20 +599,16 @@ Type TSDocxFile = Class
DocPrId_ := 0;
ps := Paragraphs();
for i:=0 to length(ps)-1 do Begin
runs := ps[i].GetRuns();
for j:=0 to length(runs)-1 do Begin
node := class(TSXml).GetNode(runs[j].node_, 'w:drawing/wp:inline/wp:docPr');
if not ifObj(node) then
node := class(TSXml).GetNode(runs[j].node_, 'w:drawing/wp:anchor/wp:docPr');
if not ifObj(node) then
node := class(TSXml).GetNode(runs[j].node_, 'w:pict/v:shape/v:textbox/w:txbxContent/wp:docPr');
if not ifObj(node) then
node := class(TSXml).GetNode(runs[j].node_, 'mc:AlternateContent/mc:Choice/w:drawing/wp:anchor/wp:docPr');
if ifObj(node) then Begin
id := class(TSXml).SafeStrToIntDef(node.GetAttribute('id'), 0);
if id > DocPrId_ then
DocPrId_ := id;
End;
node := class(TSXml).GetNode(ps[i].node_, 'w:r/w:drawing/wp:inline/wp:docPr');
if not ifObj(node) then
node := class(TSXml).GetNode(ps[i].node_, 'w:r/w:pict/v:shape/v:textbox/w:txbxContent/wp:docPr');
if not ifObj(node) then
node := class(TSXml).GetNode(ps[i].node_, 'w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:anchor/wp:docPr');
if ifObj(node) then Begin
id := class(TSXml).SafeStrToIntDef(node.GetAttribute('id'), 0);
if id > DocPrId_ then
DocPrId_ := id;
break;
End;
End;
End;
+3 -31
View File
@@ -35,7 +35,7 @@ public
self.Update("end");
End;
Function Delete();overload;
Function Delete();
Begin
if ifObj(RootObj) then
begin
@@ -44,34 +44,6 @@ public
end
End;
Function Delete(name);overload;
Begin
if not ifObj(RootObj) then return false;
node := NodeUri <> '' ? class(TSXml).GetNode(RootObj, NodeUri) : RootObj;
attrs := GetAttrsEx();
lName := lowerCase(name);
r := sselect * from attrs where lName = lowerCase([0]) end;
if istable(r) then
begin
node.DeleteAttribute(r[1]);
return true;
end
children := GetChildren();
r := select * from children where lName = lowerCase(['field']) end;
if istable(r) then Begin
r := r[0];
delete_node := node.FirstChildElement(r['name']);
if ifObj(delete_node) then
begin
node.DeleteChild(delete_node);
return true;
end
End;
return false;
End;
Function GetAttrs(); virtual;
Begin
return ExtAttr;
@@ -123,7 +95,7 @@ public
begin
empty_name := children_[i]['attrEx'] ? children_[i]['attrEx'] : 'val';
if obj = 0 then arr['attributes'] := array(empty_name: 0);
else if obj = 1 then arr['attributes'] := array(empty_name: 1);
else if obj = 1 then arr['attributes'] := array(empty_name: 1)
end
else if node_type = 'pcdata' then
begin
@@ -200,7 +172,7 @@ public
if istable(r) then Begin
r := r[0];
node := node.FirstChildElement(r['name']);
if not ifObj(node) and r['nodeType'] = 'empty' and not nottransferable then return false;
if not ifObj(node) and r['nodeType'] = 'empty' then return false;
if not ifObj(node) then return nil;
if r['nodeType'] = 'pcdata' then //返回文本串
return node.GetText();
-27
View File
@@ -179,33 +179,6 @@ Type TOffice = Class
return getTextBoxImpl(p, true);
End;
///获取docx的类型
///普通段落:0
///图片段落:1
///表格:2
///其他:-1
Function GetDocxPartType(part);
Begin
if not ifObj(part) then return -1;
case part.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;
///单元格内的标题是否添加到目录
Function SetCellTableContent(flag);
Begin
hash_["CELLFLAG"] := flag;
End;
Function Set(k, v);
Begin
hash_[k] := v;
+56 -51
View File
@@ -1,4 +1,5 @@
// Version 1.8.8
// Version 1.5.2
Type TSXlsxFile = Class
///Version: V1.0 2022-08-08
///适用于 Microsoft Excel? 2007 及以上版本创建的电子表格文档。支持 XLSX / XLSM / XLTM / XLTX 等多种文档格式。
@@ -8,7 +9,7 @@ Type TSXlsxFile = Class
///缺省构造函数
Function Create(); overload;
Begin
Init();
init();
End;
///构造函数,打开已经存在的excel文件
@@ -16,7 +17,7 @@ Type TSXlsxFile = Class
///fname: string,文件名
Function Create(alias, fname); overload;
Begin
Init();
init();
OpenFile(alias, fname, nil);
End;
@@ -26,12 +27,17 @@ Type TSXlsxFile = Class
///passwd: string,密码
Function Create(alias, fname, passwd); overload;
Begin
Init();
init();
OpenFile(alias, fname, passwd);
End;
//析构函数
Function Destory();
Begin
End;
//初始化
Function Init();
Function init();
Begin
zipfile_ := new ZipFile();
End;
@@ -45,18 +51,25 @@ Type TSXlsxFile = Class
Begin
if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile();
[err, errmsg] := zipfile_.Open(alias, TOfficeApi().CurCodePageToGBK(fname), passwd);
if err=0 then InitVars();
[err, errmsg] := zipfile_.Open(alias, fname, passwd);
if err=0 then Begin
workbook_ := new xlsxWorkBook(zipfile_);
workbook_.Load();
End;
return array(err, errmsg);
End;
///新建excel文件
///返回: [err, errmsg]
///返回: [err, info]
Function NewFile();
Begin
def := TOfficeTemplate('default.xlsx', true);
[err, errmsg] := zipfile_.LoadFromMem(def);
if err = 0 then InitVars();
if err = 0 then
begin
workbook_ := new xlsxWorkBook(zipfile_);
workbook_.Load();
end
return array(err, errmsg);
End;
@@ -79,7 +92,11 @@ Type TSXlsxFile = Class
Function LoadFromMem(data);
Begin
[err, errmsg] := zipfile_.LoadFromMem(data);
if err = 0 then InitVars();
if err = 0 then
begin
workbook_ := new xlsxWorkBook(zipfile_);
workbook_.Load();
end
return array(err, errmsg);
End;
@@ -96,17 +113,14 @@ Type TSXlsxFile = Class
///返回: [err, info]
Function SaveAs(alias, fname);
Begin
return zipfile_.Save(alias, TOfficeApi().CurCodePageToGBK(fname));
return zipfile_.Save(alias, fname);
End;
///真实文件名
///返回: string
Function FileName();
Begin
file_name := zipfile_.FileName();
if TOfficeApi().IsUtf8() then
return AnsiToUTF8(file_name);
return file_name;
return zipfile_.FileName();
End;
///获取工作表列表
@@ -115,7 +129,9 @@ Type TSXlsxFile = Class
Begin
sheets := workbook_.GetSheets();
for i:=0 to length(sheets)-1 do
begin
sheets[i] := class(TSXml).Utf8ToCurCodePage(sheets[i]);
end
return sheets;
End;
@@ -223,10 +239,6 @@ Type TSXlsxFile = Class
Begin
sheet_name := class(TSXml).CurCodePageToUtf8(sheet);
value := class(TSXml).CurCodePageToUtf8(val);
// 处理cell设定的样式
if not ifarray(opt) then opt := array();
workbook_.SetCellStyleOpt(sheet_name, axis, opt);
SetCellType(sheet_name, axis, value, opt);
return workbook_.SetCellValue(sheet_name, axis, value, opt);
End;
@@ -315,14 +327,14 @@ Type TSXlsxFile = Class
Begin
if not ifarray(data) or length(data)=0 then
return array(-1, "Invalid Data.");
if mcols(data) = 0 then Begin //数据是一维数组
if FieldCount(data)=0 then Begin //数据是一维数组
return SetSheetRow(sheet, axis, data);
End;
[err, colNum, rowNum] := CellNameToCoordinates(axis);
if err then
return array(err, colNum);
if IncludeHeader then Begin
fields := mcols(data, 1);
fields := FieldNames(data);
if IncludeIndex then Begin
fields := array("Index") union fields;
End;
@@ -339,13 +351,13 @@ Type TSXlsxFile = Class
colNum ++;
End;
for i:=0 to length(data)-1 do Begin
j := 0;
for k, v in data[i] do Begin
[err, cell] := CoordinatesToCellName(colNum + j, rowNum + i);
j++;
ret := SetCellValue(sheet, cell, v);
if not ret then return array(1, "error");
End;
[err, cell] := CoordinatesToCellName(colNum, rowNum + i);
if err then
return array(err, cell);
t := sselect * from data where thisrowindex = i End;
SetSheetRow(sheet, cell, t);
if err then
return array(err, info);
End;
return array(0, "OK");
End;
@@ -565,7 +577,8 @@ Type TSXlsxFile = Class
///返回: styleid
Function NewStyle(style); overload;
Begin
return style_.GetStyleId(style);
styleObj := new xlsxStyles('', self);
return styleObj.GetStyleId(style);
End;
///新建样式对象,已存在样式的基础上返回新的样式Id
@@ -574,14 +587,16 @@ Type TSXlsxFile = Class
///返回: styleid
Function NewStyle(style, oldStyleId); overload;
Begin
return style_.GetStyleId(style, oldStyleId);
styleObj := new xlsxStyles('', self);
return styleObj.GetStyleId(style, oldStyleId);
End;
///获取样式对象
///返回: TStyle对象
Function GetStyle(styleid);
Begin
return style_.GetStyle(styleid);
styleObj := new xlsxStyles('', self);
return styleObj.GetStyle(styleid);
End;
///设置单元格样式
@@ -652,9 +667,9 @@ Type TSXlsxFile = Class
///visible: boolean
Function SetColVisible(sheet, col, visible)
Begin
[err, new_col] := ColumnNameToNumber(col);
[err, col] := ColumnNameToNumber(col);
if err then return "Col error.";
return workbook_.SetColVisible(class(TSXml).CurCodePageToUtf8(sheet), new_col, visible);
return workbook_.SetColVisible(class(TSXml).CurCodePageToUtf8(sheet), col, visible);
End;
///获取列可见性
@@ -663,9 +678,9 @@ Type TSXlsxFile = Class
///返回: [err, visible:int]
Function GetColVisble(sheet, col);
Begin
[err, new_col] := ColumnNameToNumber(col);
[err, col] := ColumnNameToNumber(col);
if err then return array(-1, col);
return workbook_.GetColVisble(class(TSXml).CurCodePageToUtf8(sheet), new_col);
return workbook_.GetColVisble(class(TSXml).CurCodePageToUtf8(sheet), col);
End;
///设置工作表页边距
@@ -967,6 +982,8 @@ Type TSXlsxFile = Class
return workbook_.GetRowOutlineLevel(class(TSXml).CurCodePageToUtf8(sheet), row);
End;
///
///创建列的分级显示,根据给定的工作表、列名和分级参数创建组
///sheet: string, 工作表名称
///col: string, 列名
@@ -1000,21 +1017,6 @@ Type TSXlsxFile = Class
End;
private
Function InitVars();
Begin
workbook_ := new xlsxWorkBook(zipfile_);
workbook_.Load();
style_ := new xlsxStyles(self);
End;
Function SetCellType(sheet, axis, val, opt);
Begin
if not ifnil(opt['t']) then return;
styleid := opt['s'] ? opt['s'] : GetCellStyle(sheet, axis);
[t, val] := style_.GetType(styleid, val);
if t then opt['t'] := t;
End;
Function getOj(sheet, objname);
Begin
sheetname := class(TSXml).CurCodePageToUtf8(sheet);
@@ -1023,8 +1025,12 @@ private
o := objMgr_[ k ];
if not ifnil(o) then return o;
case objname of
'xlsxMargin':
o := class(xlsxMargin).NewObject(sheetname, self);
'xlsxComment':
o := class(xlsxComment).NewObject(sheetname, self);
'xlsxStyles':
o := class(xlsxStyles).NewObject(sheetname, self);
'xlsxChart':
return class(xlsxChart).NewObject(sheetname, self);//不缓存xlsxChart对象
'xlsxHeaderFooter':
@@ -1051,6 +1057,5 @@ private
zipfile_; //压缩文件对象
workbook_; //WorkBook对象
objMgr_; //各种对象缓存、管理
style_;
End;
+1 -1
View File
@@ -52,7 +52,7 @@ Type TDocxChart = Class(TSChart)
return true;
End;
[weakref]docx_;
docx_;
chartId_;
rId_;
xmlObj_;
+23 -103
View File
@@ -101,56 +101,6 @@ private
end
End;
Function SetRObject(node);
Begin
image_data_node := class(TSXml).GetNode(node, 'v:shape/v:imagedata');
if not ifObj(node) then return;
rid := image_data_node.GetAttribute('r:id');
id := CopyRidContent(rid, 'media/image', 'image');
if id then image_data_node.SetAttribute('r:id', 'rId' $ id);
ole_node := class(TSXml).GetNode(node, 'o:OLEObject');
if not ifObj(node) then return;
rid := ole_node.GetAttribute('r:id');
id := CopyRidContent(rid, 'embeddings/oleObject', 'oleObject');
if id then ole_node.SetAttribute('r:id', 'rId' $ id);
End;
function CopyRidContent(id, tar_prefix, type_postfix);
begin
rels := new_docx_obj_.Zip().Get('word/_rels/document.xml.rels');
target := class(TSXml).FindRelationshipTarget(rels, id);
image_file := new_docx_obj_.Zip().Get('word/' $ target).Data();
// 比较新文件的图片在旧文件中是否存在
zip := old_docx_obj_.Zip();
xml := zip.Get('word/_rels/document.xml.rels');
files := sselect ['FileName'] from zip.Files() where AnsiStartsText('word/' $ tar_prefix, ['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;
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 := tar_prefix $ image_cnt $ '.' $ postfix;
[rid, tar] := class(TSXml).FindRelationshipRid(xml, '');
rid ++;
class(TSXml).AddRelationshipRid(xml, image_path, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' $ type_postfix, 'rId' $ rid);
zip.Add('word/' + image_path, image_file);
contentXml := zip.Get('[Content_Types].xml');
if postfix = 'wmf' then content_type := 'image/x-wmf';
else if postfix = 'bin' then content_type := 'application/vnd.openxmlformats-officedocument.oleObject';
class(TSXml).AddDefaultContentType(contentXml, postfix, content_type);
end
end
return rid;
end;
Function SetPic(node);
Begin
// 获取新文件的图片
@@ -249,9 +199,7 @@ private
Function SetDrawingInfo(drawing);
Begin
drawing_node := GetDrawingNode(drawing.node_);
graphic_node := class(TSXml).GetNode(drawing_node,'wp:inline/a:graphic/a:graphicData');
// graphic_node := class(TSXml).GetNode(drawing.node_,'w:r/w:drawing/wp:inline/a:graphic/a:graphicData');
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');
@@ -276,9 +224,6 @@ private
begin
CopyFootnote(run);
CopyStyle(run, 'w:rPr/w:rStyle');
obj_node := class(TSXml).GetNode(run, 'w:object');
if ifObj(obj_node) then
SetRObject(obj_node);
run := run.NextElement();
end
End;
@@ -343,33 +288,27 @@ private
begin
cell := table.Cell(r, c);
if not cell then continue;
node := cell.node_.FirstChildElement();
while ifObj(node) do
node := cell.node_;
if (tbl := node.FirstChildElement('w:tbl')) then
begin
name := node.GetName();
if name = 'w:tbl' then
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('TTable');
obj.Init(node);
SetTableInfo(obj);
obj := TOfficeObj('TPicture');
obj.Init(p);
SetDrawingInfo(obj);
end
else if name = "w:p" then
begin
// draw := class(TSXml).GetNode(node, 'w:r/w:drawing');
draw := GetDrawingNode(node);
if ifObj(draw) then
begin
obj := TOfficeObj('TPicture');
obj.Init(node);
SetDrawingInfo(obj);
end
else begin
obj := TOfficeObj('TParagraph');
obj.Init(node);
SetParagraphInfo(obj);
end
else begin
obj := TOfficeObj('TParagraph');
obj.Init(p);
SetParagraphInfo(obj);
end
node := node.NextElement();
end
end
end
@@ -385,7 +324,6 @@ private
case name of
'w:p':
begin
if GetDrawingNode(part.node_) then return 1;
if class(TSXml).GetNode(part.node_, 'w:r/w:drawing') then return 1;
return 0;
end
@@ -396,26 +334,6 @@ private
end
End;
function GetDrawingNode(node);
begin
r_node := node.FirstChild('w:r');
while ifObj(r_node) do
begin
sub_node := r_node.FirstChild();
sub_node_name := sub_node.GetName();
if sub_node_name = 'w:rPr' then
begin
sub_node := sub_node.NextElement();
sub_node_name := sub_node.GetName();
end
if sub_node_name = 'w:drawing' then return sub_node;
if sub_node_name = 'w:t' and sub_node.GetAttribute('xml:space') = 'preserve' then
r_node := r_node.NextElement();
else break;
end
return nil;
end;
private
old_docx_obj_;
new_docx_obj_;
@@ -525,16 +443,18 @@ private
Function GetNewName(name);
Begin
new_name := name;
count := 0;
while ifObj(old_style_obj_.GetStyle(class(TSXml).Utf8ToCurCodePage(new_name))) or style_name_map2_[new_name] do
new_name := new_name $ crc32(inttostr(random(2000000)));
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 $ crc32(inttostr(random(2000000)));
new_id := new_id $ count++;
return new_id;
End;
@@ -618,7 +538,7 @@ Type TDocxFootnoteCopy = class
End;
private
[weakref]old_footnote_obj_;
old_footnote_obj_;
new_footnote_obj_;
id_map_; // [id: footnote]
+1 -2
View File
@@ -141,7 +141,6 @@ Type TDocxStyles = Class
Begin
if ifObj(newstyle) and ifObj(newstyle.node_) then Begin
node := stylesXml_.FirstChildElement('w:styles').InsertEndChild(newstyle.node_.Marshal()[0]);
node.DeleteAttribute('w:default');
obj := TOfficeObj('TDocxStyle');
obj.StyleId := node.GetAttribute('w:styleId');
obj.Name := node.FirstChildElement('w:name').GetAttribute('w:val');
@@ -199,7 +198,7 @@ Type TDocxStyles = Class
defaultPpr_;
defaultRpr_;
private
[weakref]docx_;
docx_;
stylesXml_;
idMap_;
nameMap_;
+1 -1
View File
@@ -228,7 +228,7 @@ Type TNumbering = Class
class(TSXml).UpdateNode(o.node_, arr['attributes'], arr['children']);
End;
private
[weakref]docx_;
docx_;
numberingXml_;
maxAbstractNumId_:integer;
maxNumId_:integer;
+4 -14
View File
@@ -94,16 +94,6 @@ Type TTableContent = class
_AddTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel);
End;
///添加目录条目
///UpperHeadingLevel:标题最高级别
///LowerHeadingLevel:标题最低级别
/// 使用 UpperHeadingLevel 属性可设置起始标题级别(最高)。例如,若要设置 TOC 域语法 {TOC \o "1-3"},可将 LowerHeadingLevel 属性设为 3,并将 UpperHeadingLevel 属性设为 1。
Function CustomAdd(posOpt, UpperHeadingLevel, LowerHeadingLevel);
Begin
//自定义页码计算与word、wps有差异,不推荐使用
_AddTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel);
End;
Function _AddDefaultTableContent(UpperHeadingLevel, LowerHeadingLevel);
Begin
p := _AddItem(UpperHeadingLevel, LowerHeadingLevel, UpperHeadingLevel - 1, true);
@@ -128,13 +118,13 @@ Type TTableContent = class
tab := TOfficeObj('TTabStop');
tab.Val := 'right';
tab.leader := 'dot';
tab.Position := TOfficeApi().Get("TTableContent-tab-position") ?: 8306;
tab.Position := 8640;
p.Format.Tabs.Add(0, tab);
if first then Begin
//fldCharType
r1 := p.AddRun();
r1.fldCharType := 'begin';
r1.Dirty := 0;
r1.Dirty := 'true';
//instrText
r2 := p.AddRun();
@@ -294,7 +284,7 @@ Type TTableContent = class
if ifObj(node) then
return node.GetAttribute('w:name');
bookMarkID := docx_.Document().GetBookMarkID();
return p.AddBookMark(bookMarkID);
return p.AddBookMark();
End;
Function _CheckNodes(name);
@@ -324,7 +314,7 @@ Type TTableContent = class
End;
End;
[weakref]docx_;
docx_;
node_;
impl_;
End;
@@ -21,11 +21,6 @@ Type docxDocument = Class
return body_;
End;
Function AddNameSpace(prefix, uri);
Begin
root_.SetAttribute('xmlns:' + prefix, uri);
End;
Function GetBookMarkID();
Begin
if bookmarkid_ < 0 then Begin
+1 -1
View File
@@ -65,7 +65,7 @@ Type xlsxChart = Class(TSChart)
node := drawingXmlObj_.FirstChild('xdr:wsDr').InsertEndChild(o.Marshal());
End;
private
[weakref]excel_;
excel_;
drawingRID_:integer;
chartId_;
chartRid_;
+2 -6
View File
@@ -148,11 +148,7 @@ xmlns:v="urn:schemas-microsoft-com:vml">
comment += r_node.FirstChildElement('t').GetText();
r_node := r_node.NextElement();
end
if length(comment) > 1 then
begin
if comment[1] = "\n" then comment := comment[2:];
else if comment[1:2] = "\r\n" then comment := comment[3:];
end
if length(comment) > 1 then comment := comment[2:];
return array(author, comment);
End;
@@ -184,7 +180,6 @@ private
End;
End;
[weakref]excel_;//TSXlsxFile对象
authors_;
commentId_;
commentFileName_:string;//'../comments/comment1.xml'
@@ -192,6 +187,7 @@ private
rId_:string;
sheetName_:string;//sheet名称
sheetFileName_;//xl/worksheets/sheetN.xml
excel_;//TSExcelFile对象
commentXmlFile_;//XmlFile对象
commentObjs_;
End;
+1 -1
View File
@@ -45,5 +45,5 @@ Type xlsxDocProps = Class
End;
private
[weakref]excel_; //TSExcelFile对象
excel_; //TSExcelFile对象
End;
@@ -33,6 +33,6 @@ Type xlsxHeaderFooter = Class
private
sheet_; //XmlSheet对象
[weakref]file_; //TSExcelFile对象
file_; //TSExcelFile对象
xmlFile_; //sheet对应的xml对象
End;
+1 -1
View File
@@ -92,7 +92,7 @@ Type xlsxHyperLink = Class
End;
private
[weakref]file_; //TSExcelFile对象
file_; //TSExcelFile对象
xmlFile_;//XmlFile对象
sheetName_;
+1 -1
View File
@@ -169,6 +169,6 @@ private
private
sheetName_:string; //sheet名称
[weakref]excel_;//TSExcelFile对象
excel_;//TSExcelFile对象
zipfile_;
End;
@@ -38,7 +38,7 @@ Type xlsxPageLayout = Class
End;
private
[weakref]file_; //TSExcelFile对象
file_; //TSExcelFile对象
sheet_;//XmlSheet对象
xmlFile_; //sheet对应的xml对象
End;
+1 -1
View File
@@ -113,7 +113,7 @@ private
private
sheetName_:string; //sheet名称
[weakref]excel_;//TSExcelFile对象
excel_;//TSExcelFile对象
zipfile_;
drawingXmlObj_;
End;
+1 -1
View File
@@ -95,7 +95,7 @@ Type xlsxSheetView = Class
End;
private
[weakref]file_; //TSExcelFile对象
file_; //TSExcelFile对象
sheet_;//XmlSheet对象
xmlFile_; //sheet对应的xml对象
End;
+22 -126
View File
@@ -1,29 +1,15 @@
Type xlsxStyles = Class
Function Create(file);
Function Create(sheetobj, file);
Begin
sheet_ := sheetobj;
file_ := file;
style_xml_file_ := file_.WorkBook().GetXmlFileObj('xl/styles.xml');
style_ := array();
Init();
styleXmlFile_ := file_.WorkBook().GetXmlFileObj('xl/styles.xml');
End
Function Init();
Begin
style_node := style_xml_file_.FirstChildElement("styleSheet");
xfs_node := style_node.FirstChildElement("cellXfs");
xf_node := xfs_node.FirstChildElement("xf");
count := 0;
while ifObj(xf_node) do
begin
style_[inttostr(count++)] := xf_node;
xf_node := xf_node.NextElement("xf");
end
End
Function GetStyleId(style);overload;
Begin
node := style_xml_file_.FirstChildElement('styleSheet');
node := styleXmlFile_.FirstChildElement('styleSheet');
font_id := insertNode(node, 'fonts', style.Font);
border_id := insertNode(node, 'borders', style.Border);
fill_id := insertNode(node, 'fills', style.Fill);
@@ -54,16 +40,15 @@ Type xlsxStyles = Class
node := node.FirstChildElement('cellXfs');
count := node.GetAttribute('count');
node.InsertEndChild(xf.Marshal());
node.SetAttribute('count', strtoint(count) + 1);
xf_node := node.InsertEndChild(xf.Marshal());
style_[count] := xf_node;
return count;
End;
Function GetStyleId(newStyle, oldStyleId);overload;
Begin
if oldStyleId = '' then return nil;
style_node := style_xml_file_.FirstChildElement('styleSheet');
style_node := styleXmlFile_.FirstChildElement('styleSheet');
cellXfs_node := style_node.FirstChildElement('cellXfs');
count := strtoint(cellXfs_node.GetAttribute('count'));
Id := strtoint(oldStyleId);
@@ -104,14 +89,19 @@ Type xlsxStyles = Class
xf_node.SetAttribute('applyProtection', 1);
end
style_[inttostr(count)] := xf_node;
return inttostr(count);
End;
Function GetStyle(styleId);
Begin
xf := style_[styleId];
if not ifObj(xf) then return nil;
if styleId = '' then return nil;
style_node := styleXmlFile_.FirstChildElement('styleSheet');
cellXfs_node := style_node.FirstChildElement('cellXfs');
count := strtoint(cellXfs_node.GetAttribute('count'));
Id := strtoint(styleId);
if Id > count-1 then return nil;
xf := getNode(cellXfs_node, 'xf', Id);
attrs := xf.Attributes();
numfmt_id := trystrtoint(attrs['numFmtId'], r) ? r : 0;
font_id := trystrtoint(attrs['fontId'], r) ? r : 0;
@@ -120,7 +110,6 @@ Type xlsxStyles = Class
alignment := xf.FirstChildElement('alignment');
protection := xf.FirstChildElement('protection');
style_node := style_xml_file_.FirstChildElement('styleSheet');
style := TOfficeObj('TStyle');
numFmts_node := style_node.FirstChildElement('numFmts');
fonts_node := style_node.FirstChildElement('fonts');
@@ -138,51 +127,12 @@ Type xlsxStyles = Class
return style;
End;
Function GetNumFmtId(styleId);
class Function NewObject(sheetname, file);
Begin
node := style_[styleId];
if ifnil(node) then return '0';
numfmt := node.GetAttribute("numFmtId");
return numfmt = '' ? '' : numfmt;
End;
Function GetType(styleId, value);
Begin
numfmt_id := GetNumFmtId(styleId);
if numfmt_id = '' then return array(nil, value);
numfmt_id := strtoint(numfmt_id);
if numfmt_id = 0 then return array(nil, value);
if numfmt_id >= 1 and numfmt_id <= 10 then return array('n', value);
if numfmt_id >= 11 and numfmt_id <= 26 then
begin
if ifstring(value) and (trystrtodate(value, r) or trystrtodatetime(value, r) or trystrtotime(value, r)) then return array('n', r);
t := ifstring(value) ? 's' : 'n';
return array(t, value);
end
if numfmt_id >= 27 and numfmt_id <= 34 then return array('e', value);
if numfmt_id >= 35 and numfmt_id <= 48 then return array('n', value);
if numfmt_id = 49 then return array('s', value);
if numfmt_id >= 50 and numfmt_id <= 55 then
begin
if ifstring(value) and trystrtodate(value, r) then return array('n', r);
t := ifstring(value) ? 's' : 'n';
return array(t, value);
end
if numfmt_id >= 56 and numfmt_id <= 58 then
begin
if ifstring(value) and trystrtodatetime(value, r) then return array('n', r);
t := ifstring(value) ? 's' : 'n';
return array(t, value);
end
else begin
if ifstring(value) then
begin
if (pos("-", value) or pos("/", value)) and (trystrtodatetime(value, r) or trystrtodate(value, r)) then return array('n', r);
if trystrtofloat(value, r) then return array("n", value);
return array("s", value);
end
return array("n", value);
end
o := file.WorkBook().GetSheetObj(sheetname);
if not ifObj(o) then return 0;
styles := new xlsxStyles(o, file);
return styles;
End;
private
@@ -336,61 +286,7 @@ private
End
private
[weakref]file_; //TSXlsxFile对象
style_xml_file_;
style_; // 缓存style
sheet_; //XmlSheet对象
file_; //TSExcelFile对象
styleXmlFile_; //xmlFile对象
End;
// 参考:https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.numberingformat?view=openxml-3.0.1
// 0 General
// 1 0
// 2 0.00
// 3 #,##0
// 4 #,##0.00
// 5 整数,货币符号旁边($#,##0_);($#,##0)
// 6 带两位小数的货币符号($#,##0.00_);($#,##0.00)
// 7 整数,带括号表示负数($#,##0_);Red
// 8 带两位小数的货币符号和红色负数($#,##0.00_);Red
// 9 0%
// 10 0.00%
// 11 0.00E+00
// 12 # ?/?
// 13 # ??/??
// 14 mm-dd-yy
// 15 d-mmm-yy
// 16 d-mmm
// 17 mmm-yy
// 18 h:mm AM/PM
// 19 h:mm:ss AM/PM
// 20 h:mm
// 21 h:mm:ss
// 22 m/d/yy h:mm
// 27 [$-404]e/m/d yyyy"年"m"月"
// 28 [$-404]e"年"m"月"d"日" m"月"d"日"
// 29 [$-404]e"年"m"月"d"日" m"月"d"日"
// 30 m/d/yy m-d-yy
// 31 yyyy"年"m"月"d"日" yyyy"年"m"月"d"日"
// 32 hh"時"mm"分" h"时"mm"分"
// 33 hh"時"mm"分"ss"秒" h"时"mm"分"ss"秒"
// 34 上午/下午hh"時"mm"分" 上午/下午h"时"mm"分"
// 35 上午/下午hh"時"mm"分"ss"秒" 上午/下午h"时"mm"分"ss"秒"
// 36 [$-404]e/m/d yyyy"年"m"月"
// 37 #,##0 ;(#,##0)
// 38 #,##0 ;[Red](#,##0)
// 39 #,##0.00;(#,##0.00)
// 40 #,##0.00;[Red](#,##0.00)
// 45 mm:ss
// 46 [h]:mm:ss
// 47 mmss.0
// 48 ##0.0E+0
// 49 @(文本)
// 50 [$-404]e/m/d yyyy"年"m"月"
// 51 [$-404]e"年"m"月"d"日" m"月"d"日"
// 52 上午/下午hh"時"mm"分" yyyy"年"m"月"
// 53 上午/下午hh"時"mm"分"ss"秒" m"月"d"日"
// 54 [$-404]e"年"m"月"d"日" m"月"d"日"
// 55 上午/下午hh"時"mm"分" 上午/下午h"时"mm"分"
// 56 上午/下午hh"時"mm"分"ss"秒" 上午/下午h"时"mm"分"ss"秒"
// 57 [$-404]e/m/d yyyy"年"m"月"
// 58 [$-404]e"年"m"月"d"日" m"月"d"日"
+1 -1
View File
@@ -126,7 +126,7 @@ private
private
sheetName_:string; //sheet名称
[weakref]excel_;//TSExcelFile对象
excel_;//TSExcelFile对象
zipfile_;
tableXmlFile_;
End;
+126 -154
View File
@@ -40,7 +40,7 @@ Type xlsxWorkBook = Class
//workbook.Print();
//workbook.Dump();
//echo tostn(workbook.dom());
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('sheet');
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement();
while ifObj(node) do Begin
name := node.GetAttribute("name");
sheetNames_[sheetsCount_]['name'] := name;
@@ -50,7 +50,7 @@ Type xlsxWorkBook = Class
sheetNames_[sheetsCount_]['file'] := 'xl/' + rids[ rid ];
sheetIndexMap_[ LowerCase(name) ] := sheetsCount_;
sheetsCount_ ++;
node := node.NextElement('sheet');
node := node.NextElement();
End;
files := zipfile_.Files();
@@ -108,39 +108,7 @@ Type xlsxWorkBook = Class
Function SetCellValue(sheet, axis, val, opt);
Begin
o := GetSheetObj(sheet);
if ifObj(o) then
ret := o.SetCellValue(axis, val, opt);
return ret;
End;
Function SetCellStyleOpt(sheet, axis, opt);
Begin
o := GetSheetObj(sheet);
if ifObj(o) then
begin
if ifnil(opt['s']) and not o.CellIsExists(axis) then
begin
[err, col, row] := CellNameToCoordinates(axis);
xml := GetSheetXmlFile(sheet);
cols_node := xml.FirstChildElement('worksheet').FirstChildElement('cols');
if ifObj(cols_node) then
begin
col_node := cols_node.FirstChildElement();
while ifObj(col_node) do
begin
min := strtoint(col_node.GetAttribute('min'));
max := strtoint(col_node.GetAttribute('max'));
style := col_node.GetAttribute('style');
if col >= min and col <= max and style <> '' then
begin
opt['s'] := style;
break;
end
col_node := col_node.NextElement();
end
end
end
end
if ifObj(o) then return o.SetCellValue(axis, val, opt);
End;
Function GetCellRichText(sheet, axis);
@@ -178,7 +146,7 @@ Type xlsxWorkBook = Class
data := o.Import(topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
if not TOfficeApi().IsUtf8() then
begin
fields := mcols(data, 1);
fields := fieldnames(data);
if ifarray(fields) and length(fields) then
begin
map := array();
@@ -250,22 +218,7 @@ Type xlsxWorkBook = Class
Begin
//设置docProps/app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
properties_node := app.FirstChildElement('Properties');
// HeadingPairs
variant_node := class(TSXml).GetNode(properties_node, 'HeadingPairs/vt:vector/vt:variant');
while ifObj(variant_node) do
begin
vt_node := variant_node.FirstChildElement('vt:i4');
if ifObj(vt_node) then
begin
vt_node.SetValue(length(sheetNames_) + 1);
break;
end
variant_node := variant_node.NextElement();
end
// TitlesOfParts
vector := class(TSXml).GetNode(properties_node, 'TitlesOfParts/vt:vector');
vector := class(TSXml).GetNode(app, 'Properties/TitlesOfParts/vt:vector');
vector.SetAttribute('size', length(sheetNames_) + 1);
vector.InsertEndChild('element', 'vt:lpstr', sheet);
End;
@@ -277,64 +230,6 @@ Type xlsxWorkBook = Class
class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
End;
Function insertWorkbookSheet(targetSheetName, newSheetName, sheetId, rId, direction);
Begin
workbook := GetXmlFileObj('xl/workbook.xml');
node := class(TSXml).GetNode(workbook, 'workbook/sheets');
sheet_node := node.FirstChildElement('sheet');
while ifObj(sheet_node) do
begin
if sheet_node.GetAttribute('name') = targetSheetName then
begin
if direction = "after" then
sheet_node := node.InsertAfterChild(sheet_node, 'element', 'sheet');
else
sheet_node := node.InsertBeforeChild(sheet_node, 'element', 'sheet');
sheet_node.SetAttribute('name', newSheetName);
sheet_node.SetAttribute('sheetId', sheetId);
sheet_node.SetAttribute('r:id', rid);
break;
end
sheet_node := sheet_node.NextElement();
end
End;
Function insertDocPropsApp(targetSheetName, newSheetName, direction);
Begin
//设置docProps/app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
properties_node := app.FirstChildElement('Properties');
// HeadingPairs
variant_node := class(TSXml).GetNode(properties_node, 'HeadingPairs/vt:vector/vt:variant');
while ifObj(variant_node) do
begin
vt_node := variant_node.FirstChildElement('vt:i4');
if ifObj(vt_node) then
begin
vt_node.SetValue(length(sheetNames_) + 1);
break;
end
variant_node := variant_node.NextElement();
end
// TitlesOfParts
vector := class(TSXml).GetNode(properties_node, 'TitlesOfParts/vt:vector');
vector.SetAttribute('size', length(sheetNames_) + 1);
lpstr := vector.FirstChildElement('vt:lpstr');
while ifObj(lpstr) do
begin
if LowerCase(lpstr.GetText()) = LowerCase(targetSheetName) then
begin
if direction = 'after' then
lpstr := vector.InsertAfterChild(lpstr, 'element', 'vt:lpstr');
else
lpstr := vector.InsertBeforeChild(lpstr, 'element', 'vt:lpstr');
lpstr.SetValue(newSheetName);
break;
end
lpstr := lpstr.NextElement();
end
End;
Function NewSheet(sourceSheet, destSheet);overload;
Begin
@@ -343,12 +238,60 @@ Type xlsxWorkBook = Class
sname := LowerCase(sourceSheet);
if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists';
//添加文件xl/worksheets/sheetN.xml
sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
sheetId := integer(sheetId) + 1;
fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
sheetId := addSheetN(fname);
rid := setWorkbookRels();
insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'after');
insertDocPropsApp(sourceSheet, destSheet, 'after');
setContentTypes(fname);
zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1'));
//设置 workbook.xml.rels
rid := getWorkbookRelsRid();
workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship');
rels.SetAttribute('Target', getTarget( sheetsCount_ + 1));
rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet'));
rels.SetAttribute('Id', rid);
//workbook_rels.Print;
//设置 xl/workbook.xml
workbook := GetXmlFileObj('xl/workbook.xml');
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets');
sheet_node := node.FirstChildElement('sheet');
while ifObj(sheet_node) do
begin
if sheet_node.GetAttribute('name') = sourceSheet then
begin
sheet_node := node.InsertAfterChild(sheet_node, 'element', 'sheet');
sheet_node.SetAttribute('name', destSheet);
sheet_node.SetAttribute('sheetId', sheetId);
sheet_node.SetAttribute('r:id', rid);
break;
end
sheet_node := sheet_node.NextElement();
end
//workbook.Print();
//设置docProps/app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
//app.Print();
node := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts');
vector := node.FirstChildElement('vt:vector');
vector.SetAttribute('size', length(sheetNames_) + 1);
lpstr := vector.FirstChildElement('vt:lpstr');
while ifObj(lpstr) do
begin
if LowerCase(lpstr.GetText()) = LowerCase(sourceSheet) then
begin
lpstr := vector.InsertAfterChild(lpstr, 'element', 'vt:lpstr');
lpstr.SetValue(destSheet);
break;
end
lpstr := lpstr.NextElement();
end
//设置[Content_Types].xml
content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
ind := sheetIndexMap_[sname];
for i:=sheetsCount_ downto ind+2 do
@@ -375,12 +318,66 @@ Type xlsxWorkBook = Class
sname := LowerCase(sourceSheet);
if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists';
//添加文件xl/worksheets/sheetN.xml
sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
sheetId := integer(sheetId) + 1;
fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
sheetId := addSheetN(fname);
rid := setWorkbookRels();
insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'before');
insertDocPropsApp(sourceSheet, destSheet, 'before');
setContentTypes(fname);
zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1'));
//设置 workbook.xml.rels
rid := getWorkbookRelsRid();
workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship');
rels.SetAttribute('Target', getTarget( sheetsCount_ + 1));
rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet'));
rels.SetAttribute('Id', rid);
//workbook_rels.Print;
//设置 xl/workbook.xml
workbook := GetXmlFileObj('xl/workbook.xml');
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets');
sheet_node := node.FirstChildElement('sheet');
prev_node := nil;
while ifObj(sheet_node) do
begin
if LowerCase(sheet_node.GetAttribute('name')) = LowerCase(sourceSheet) then
begin
if ifnil(prev_node) then sheet_node := node.InsertFirstChild('element', 'sheet');
else sheet_node := node.InsertAfterChild(prev_node, 'element', 'sheet');
sheet_node.SetAttribute('name', destSheet);
sheet_node.SetAttribute('sheetId', sheetId);
sheet_node.SetAttribute('r:id', rid);
break;
end
prev_node := sheet_node;
sheet_node := sheet_node.NextElement();
end
//workbook.Print();
//设置docProps/app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
//app.Print();
node := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts');
vector := node.FirstChildElement('vt:vector');
vector.SetAttribute('size', length(sheetNames_) + 1);
lpstr := vector.FirstChildElement('vt:lpstr');
pstr := nil;
while ifObj(lpstr) do
begin
if LowerCase(lpstr.GetText()) = LowerCase(sourceSheet) then
begin
if ifnil(pstr) then lpstr := vector.InsertFirstChild('element', 'vt:lpstr');
else lpstr := vector.InsertAfterChild(pstr, 'element', 'vt:lpstr');
lpstr.SetValue(destSheet);
break;
end
pstr := lpstr;
lpstr := lpstr.NextElement();
end
//设置[Content_Types].xml
content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
ind := sheetIndexMap_[sname];
for i:=sheetsCount_ downto ind+1 do
@@ -419,8 +416,7 @@ Type xlsxWorkBook = Class
for i:=0 to length(sheetNames_)-1 do Begin
sheetNames_[i]['rid'] := 'rId' + inttostr(i + 1); //重置rId
oldname := sheetNames_[i]['file'];
fdir := ExtractFileDir(oldname);
newname := fdir + "/" + "sheet" + inttostr(i + 1) + '.xml';
newname := sheetPrefix_ + inttostr(i + 1) + '.xml';
sheetNames_[i]['file'] := newname;
if oldname <> newname then Begin
n := vselect thisrowindex from files where ['FileName'] = oldname end;
@@ -474,7 +470,7 @@ Type xlsxWorkBook = Class
node.SetAttribute('sheetId', sheetNames_[i]['sheetId']);
node.SetAttribute('r:id', sheetNames_[i]['rid']);
End;
// workbook.Print();
//workbook.Print();
//设置docProps/app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
@@ -619,17 +615,9 @@ Type xlsxWorkBook = Class
//app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
prop := app.FirstChildElement('Properties');
vector := prop.FirstChildElement('TitlesOfParts').FirstChildElement('vt:vector');
vector.SetAttribute('size', sheetsCount_+1);
vector := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts').FirstChildElement('vt:vector');
vector.SetAttribute('size', sheetId);
vector.InsertEndChild('element', 'vt:lpstr', destSheet);
vector := prop.FirstChildElement('HeadingPairs').FirstChildElement('vt:vector');
if ifObj(vector) then
begin
variant := vector.FirstChildElement('vt:variant').NextElement('vt:variant');
i4 := variant.FirstChildElement('vt:i4');
i4.SetValue(sheetsCount_ + 1);
end
//[Content_Types].xml
content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
@@ -900,11 +888,6 @@ Type xlsxWorkBook = Class
Function SetRowHeight(sheet, row, height);
Begin
obj := GetSheetObj(sheet);
if not obj.RowIsExists(row) then
begin
axis := CoordinatesToCellName(1, row, False)[1];
if not obj.CellIsExists(axis) then SetCellValue(sheet, axis, '');
end
obj.SetAttribute(row, array('ht': height, "customHeight": 1));
End
@@ -994,7 +977,7 @@ Type xlsxWorkBook = Class
col_node := work_node.FirstChildElement('cols');
default_width := work_node.FirstChildElement('sheetFormatPr').GetAttribute('defaultColWidth');
default_width := trystrtofloat(default_width, r) ? r: -1;
if not ifObj(col_node) then return nil;
if not ifObj(col_node) then return array(0, default_width);
col_number := ColumnNameToNumber(col)[1];
node := col_node.FirstChildElement('col');
@@ -1494,7 +1477,7 @@ Type xlsxWorkBook = Class
if col >= min and col <= max then
Begin
level_val := node.GetAttribute('outlineLevel');
if trystrtoint(level_val, r) and r = level then return;
if strtoint(level_val) = level then return;
else Begin
if col = min and col = max then node.SetAttribute('outlineLevel', level);
else begin
@@ -1610,9 +1593,9 @@ private
Function copyFile(obj, target, content);
Begin
file_name := ReplaceStr(target, '..', 'xl');
counts := GetFilesCount(ReplaceStrUsingReg(file_name, "\\d+.*ml", '')) + 1;
counts := GetFilesCount(ReplaceStrByReg(file_name, "\\d+.*ml", '')) + 1;
xml := GetXmlFileObj(file_name);
new_file_postfix := ReplaceStrUsingReg(file_name[3:], "\\d+", inttostr(counts));
new_file_postfix := ReplaceStrByReg(file_name[3:], "\\d+", inttostr(counts));
zipfile_.Add('xl' + new_file_postfix, xml.Data());
obj.SetAttribute('Target', '..' + new_file_postfix);
@@ -1624,22 +1607,11 @@ private
return array(counts, 'xl' + new_file_postfix);
End;
function ReplaceStrUsingReg(s, regFromText, toText);
begin
ParseCtrls := 'r';
if ifnil(toText) then toText := '';
ParseRegExpr(RegFromText,s,ParseCtrls,toText,s2);
if ansicontainstext(ParseCtrls,'r') then //表替换,输出s2
return ifstring(s2)?s2:s;
else
return ifArray(toText) and length(toText) > 0 ? toText : array();
end;
sheetsCount_:integer;
sheetNames_;
sheetIndexMap_;
sheetPrefix_:string;
[weakref]zipfile_;
zipfile_;
xmlFileObjMap_; //XmlFile对象存储
sheetObjMap_; //XmlSheet对象存储
End;
+1 -352
View File
@@ -1,356 +1,5 @@
# 更新日志
## 2026-09-10
### V1.8.8
#### word
1. 修复`InsertFile`时,图片前有空格无法识别问题
## 2026-07-30
### V1.8.7
#### word
1. 修复`InsertFile`时,样式重复导致wps/word识别失败从而渲染错误问题
## 2026-01-16
### V1.8.6
修复语法问题
#### word
1. 修复`InsertTable`多行数据插入缺失问题
## 2025-12-15
### V1.8.5
#### word
1. 修复插入word时,单元格内容不完整问题
#### excel
1. 修复`GetColWithd`返回值出现数组问题,没有设置宽度返回`nil`
## 2025-11-07
### V1.8.4
#### word
1. 修复合并单元格时候设置属性不规范
## 2025-10-30
### V1.8.3
#### word
1. `setData`移除`fieldNames`依赖
#### excel
1. `InsertTable/GetTable`移除`fieldNames` 依赖
## 2025-10-29
### V1.8.2
#### word
1. 升级段落判空`Empty`
2. 修复`FileName`总是返回 gbk
#### excel
1. 修复`CopySheet`依赖的`ReplaceStrByReg`公共方法问题,不再依赖该方法
2. 修复`FileName`总是返回 gbk
## 2025-7-22
### V1.8.1
#### word
1. **feat**: 支持同一段落多图片文字
```cpp
picture := TOfficeObj('TPicture');
picture.Descr := '';
picture.Image := TOfficeTemplate('tinysoft.gif',true);
docx := new TSDocxFile();
docx.NewFile();
p := new TOfficeObj('TParagraph');
r := p.AddRun();
r.SetText("abc");
r := p.AddRun();
r.SetDrawing(picture, docx); // 需要一个图片对象和TSDocxFile对象
r := p.AddRun();
r.SetText("def");
docx.AddParagraph(p, -1);
```
## 2025-6-23
### V1.8.0
#### word
1. **feat**: 支持添加自定目录`TSDocxFile.AddCustomTableContent`
2. **fix**: 修复单元格内容换行,自定义属性失效问题
## 2025-2-28
### V1.7.9
#### word
1. **feat**:支持目录自定义宽度`TOfficeApi().Get("TTableContent-tab-position")`
2. **fix**:插入图片 id 计算错误,导致`office 2016 16.0.4266`版本不兼容
## 2025-1-22
### V1.7.8
❗ 解决`TSDocxFile``TSXlsxFile`内存泄露
#### word
1. **fix**`CreateTable`由于`fieldnames.tsf`差异报错
## 2025-1-17
### V1.7.7
#### word
1. **feat**`TPicture`对象设置画布大小`CanvasWidth``CanvasHeight`
2. **fix**`InsertTable`表格数据不对齐报错
#### excel
1. **fix**`SetColOutlineLevel`错误
## 2025-1-8
### V1.7.6
#### excel
1. **fix**`SetRowHeight`单元格不存在无法设置
2. **fix**`Set[Get]ColVisible`修改传入的列参数
## 2025-1-7
### V1.7.5
#### word
1. 支持获取`sdt`标签类型的内容(如文本框),通过`TSDocxFile.Sdts()`获取文档的的所有`sdt`
> ```pascal
> sdts := docx.Sdts();
> paragraphs := sdts[0].Paragraphs();
> textbox := paragraphs[1].TextBoxs();
> ```
## 2024-12-23
### V1.7.4
#### word
1. `insertfile`时候默认添加`w16du`命名空间
## 2024-12-05
### V1.7.3
#### word
1. `insertfile`支持 ole 对象的插入,如 wmf 公式,excel 表格
## 2024-12-04
### V1.7.2
#### word
1. 支持单元格标题生成到目录,需要额外设置`TOfficeApi().SetCellTableContent(true);`,默认是`false`
## 2024-11-19
### V1.7.1
#### word
1. 对生成目录的 xml 结构进行更改
2. 修复引用 excel 错误
#### excel
1. 修复获取`comment`时候,换行符问题
## 2024-7-8
### V1.7.0
#### excel
1. 修复`CopySheet`操作后,文件报错问题
## 2024-5-6
### V1.6.9
#### excel
1. 修复单元格继承列样式失败问题
## 2024-4-12
### V1.6.8
### excel
1. 修复`SetCellValue`单元格对列样式的识别,不需要手动用格式刷对所有单元格刷新样式
## 2024-3-21
### V1.6.7
#### word
1. 新增对象的`Delete`方法,可以通过`Delete(name)`删除对象的指定属性
2. 新增`TPageNumType`对象
3. 扩展`TDocSection`方法,`AppendReference``AddReference`,前者是对已有的`Section`对象添加`TReference`对象,而后者是对新的`Section`对象添加
## 2024-3-14
### V1.6.6
#### word
1. 修复`TDocSection`节点获取错误
2. 支持分栏自定义宽度
## 2024-3-4
### V1.6.5
支持扩展扩展模块
### excel
1. 样式获取报错问题
### V1.6.4
#### word
1. 修复`InsertTable`内容`\n`无换行问题
2. `TextBox`支持`Clear`清空内容
#### word
1. 修复目录生成陷入死循环问题
## 2024-2-23
### V1.6.3
#### word
1. 修复目录生成陷入死循环问题
## 2024-2-7
### V1.6.2
#### word
1. 修复设置段落属性`TextAlignment`失败问题
## 2024-2-5
### V1.6.1
#### excel
1. 修复 excel 文件单元格原有类型与 tsl 类型转换报错问题(trystrtofloat)
## 2024-2-5
### V1.6.0
#### excel
1. 兼容 Excel2016,通过`NewSheet/InsertSheet`创建工作表不再提示修复错误
2. 修复带有表头的二维数组通过`InsertTable`写入数据只写入表头未写入数据问题
## 2024-1-31
### V1.5.9
修复`tsl`脚本格式为`utf8`时无法打开中文命名的文件
### V1.5.8
#### excel
1. 支持写入单元格内容时判断当前单元格的数据类型,并以单元格数据类型为准
## 2024-1-17
### V1.5.7
#### excel
1. 修复单元格写入浮点数数据 bug,如写入 0.0,excel 某些版本打开会显示 0.00000000000000000
## 2024-1-11
### V1.5.6
#### excel
1. 修复单元格写入浮点数数据,精度丢失问题
## 2023-12-29
### V1.5.5
#### word
1. 修复生成的目录打开后重新生成页码可能产生页码失败问题
#### excel
1. 修复`InsertTable`与``SetCellValue`返回值问题
## 2023-12-18
### V1.5.4
#### excel
1. 优化`InsertTable`的写入效率
## 2023-12-11
### V1.5.3
#### excel
1. 支持写入单元格时且单元格不存在时,新的单元格会沿用设置的样式 ID(若有设置)
## 2023-12-1
### V1.5.2
@@ -363,7 +12,7 @@ docx.AddParagraph(p, -1);
### V1.5.1
1. 支持识别图片中含有`0xffc2`段的 jpg 格式的大小
1. 支持识别图片中含有`0xffc2`段的jpg格式的大小
## 2023-11-14