Compare commits

64 Commits
Author SHA1 Message Date
csh 19b8614c93 v1.8.8 2026-09-10 15:03:04 +08:00
csh 3df8ce2be0 v1.8.7 2026-07-30 14:05:16 +08:00
csh 6b0f11cebb v1.8.6 2026-01-16 14:32:22 +08:00
csh bbe0821f68 v1.8.5 2025-12-15 13:28:02 +08:00
csh ea528f3094 v1.8.4 2025-11-07 14:24:12 +08:00
csh 95a00e7002 v1.8.3 2025-10-30 15:58:12 +08:00
csh 2b62e4e1a5 v1.8.3 2025-10-30 15:50:10 +08:00
csh a2c3905cd0 v1.8.2 2025-10-29 15:47:51 +08:00
csh ca03e53285 v1.8.1 2025-07-22 16:35:36 +08:00
csh d57631a19a v1.8.0 2025-06-23 10:50:55 +08:00
csh c05b646104 v1.7.9 2025-02-28 13:41:23 +08:00
csh 39b7549665 v1.7.8 2025-01-22 13:58:18 +08:00
csh 93533c41a0 v1.7.7 2025-01-17 18:33:30 +08:00
csh 0f07603d73 v1.7.6 2025-01-08 15:31:04 +08:00
csh 3c16ce510a v1.7.5 2025-01-07 10:56:24 +08:00
csh 3243db0e2d v1.7.4-patch1 2024-12-23 14:17:49 +08:00
csh fdd2bd0fef v1.7.4 2024-12-23 11:07:05 +08:00
csh 2d423ca1fb v1.7.3 2024-12-05 16:37:20 +08:00
csh 0e35cc0463 v1.7.2 2024-12-04 09:50:13 +08:00
csh c7b7971d9a v1.7.1 2024-11-19 18:26:49 +08:00
csh 066a8f6562 v1.70-patch3 2024-07-31 13:30:20 +08:00
csh 9e04cc2cff v1.7.0-patch2 2024-07-26 14:11:22 +08:00
csh bb8d3591ac v1.7.0-patch1 2024-07-22 11:06:49 +08:00
csh 037cb41560 v1.7.0 2024-07-18 17:26:41 +08:00
csh 5469ca318d v1.6.9 2024-05-06 15:12:37 +08:00
csh c3432d1812 v1.6.8-patch1 2024-04-17 16:22:48 +08:00
csh 53d83f91ae v1.6.8 2024-04-12 14:14:47 +08:00
csh 893a3a928b v1.6.7-patch1 2024-03-25 10:15:28 +08:00
csh 72554969bb v1.6.7 2024-03-21 16:34:38 +08:00
csh fa8800e20d v.1.6.6-patch2 2024-03-15 10:27:28 +08:00
csh 92257c380f v1.6.6-patch1 2024-03-14 14:09:55 +08:00
csh fb22cf9b0a v1.6.6 2024-03-14 10:18:16 +08:00
csh a3df0d5df9 v1.6.5 2024-03-04 16:14:03 +08:00
csh b4ef4a531c v1.6.4 2024-03-04 14:11:34 +08:00
csh f499526f04 v1.6.3 2024-02-23 11:19:29 +08:00
csh c1a93bb16e v1.6.2 2024-02-07 15:59:35 +08:00
csh 03e101fa46 v1.6.1 2024-02-06 11:28:13 +08:00
csh 1231868aa7 v1.6.0 2024-02-05 16:15:22 +08:00
csh 6d6281229c v1.5.9 2024-01-31 17:39:37 +08:00
csh 522e52ed46 v1.5.8 2024-01-31 09:52:10 +08:00
csh a774f57335 v1.5.7-patch1 2024-01-30 18:03:53 +08:00
csh 800f91870d v1.5.7 2024-01-17 17:10:16 +08:00
csh e5d011e59b v1.5.6-patch1 2024-01-12 09:11:38 +08:00
csh d588ac67d4 v1.5.6 2024-01-11 15:00:26 +08:00
csh dc7d48c318 v1.5.5 2023-12-29 10:11:50 +08:00
csh fcb2a8e7b3 v1.5.4 2023-12-18 11:33:26 +08:00
csh 91f3c5af49 v1.5.3-patch1 2023-12-15 15:14:44 +08:00
csh c16566d2f1 v1.5.3 2023-12-11 15:48:41 +08:00
csh 3607dab833 v1.5.2-patch2 2023-12-07 14:04:28 +08:00
csh 9f660c09f6 v1.5.2-patch1 2023-12-05 15:31:49 +08:00
csh 1e9b8f3a12 v1.5.2 2023-12-01 10:42:08 +08:00
csh b617f235fa v1.5.1 2023-11-15 10:51:24 +08:00
csh ee7b7164ce v1.5.0 2023-11-14 11:38:27 +08:00
csh aab0a498c6 v1.4.9-patch2 2023-10-19 14:47:21 +08:00
csh 5172a78e3e v1.4.9-patch1 2023-10-17 16:20:41 +08:00
csh de0af521bc v1.4.9 2023-10-16 17:35:16 +08:00
csh 8cd87118d5 v1.4.8-patch1 2023-09-28 16:22:33 +08:00
csh 9c917cfb70 v1.4.8 2023-09-27 14:50:53 +08:00
csh d72d88d68a v1.4.7 2023-09-22 09:42:31 +08:00
csh 5f1064794d v1.4.6-patch3 2023-09-21 09:22:15 +08:00
csh 493691b930 v1.4.6-patch2 2023-09-20 09:14:36 +08:00
csh f3094c148c v1.4.6-patch1 2023-09-19 17:00:37 +08:00
csh addc41144e v1.4.6 2023-09-19 10:10:29 +08:00
csh 8aa050461c v1.4.5 2023-08-30 15:43:03 +08:00
39 changed files with 15003 additions and 12480 deletions
+4
View File
@@ -0,0 +1,4 @@
*.swp
*.swo
*.cmd
script.ps1
+3
View File
@@ -0,0 +1,3 @@
[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_); docx.AddPageBreak(content.node_);
v := docx.SaveAs('', TOfficeApi().CurCodePageToGBK('DocxFile使用帮助.docx')); v := docx.SaveAs('', 'DocxFile使用帮助.docx');
println('Test Over!\n Save {}: {},time={}秒', file, v, mtoc); println('Test Over!\n Save {}: {},time={}秒', file, v, mtoc);
//info := GetProfilerInfo(true); //info := GetProfilerInfo(true);
+25 -23
View File
@@ -1,28 +1,28 @@
path := ExtractFileDir(ExtractFileDir(PluginPath())); path := ExtractFileDir(ExtractFileDir(PluginPath()));
{$IFNDEF Win32} {$IFNDEF Win32}
path := path + '\\funcext\\TSOffice\\TSExcelFile.tsf'; path := path + '/funcext/TSOffice/TSXlsxFile.tsf';
{$ELSE} {$ELSE}
path := path + '/funcext/TSOffice/TSExcelFile.tsf'; path := path + '\\funcext\\TSOffice\\TSXlsxFile.tsf';
{$ENDIF} {$ENDIF}
funcs := LoadClassInfo(path); funcs := LoadClassInfo(path);
testing := 1; testing := 1;
file := new TSExcelFile(); file := new TSXlsxFile();
[err, errinfo] := file.NewFile(); [err, errinfo] := file.NewFile();
if err then if err then
return PrintError("NewFile", err, errinfo); return PrintError("NewFile", err, errinfo);
PrintSuccess("NewFile", testing++); PrintSuccess("NewFile", testing++);
file.SetSheetName('Sheet1', 'ExcelFile'); file.SetSheetName('Sheet1', 'XlsxFile');
PrintSuccess("SetSheetName", testing++); PrintSuccess("SetSheetName", testing++);
file.NewSheet("Functions"); file.NewSheet("Functions");
PrintSuccess("NewSheet", testing++); PrintSuccess("NewSheet", testing++);
file.SetDefaultSheet('ExcelFile'); file.SetDefaultSheet('TSXlsxFile');
richtext := TOfficeObj('TRichText'); richtext := TOfficeObj('TRichText');
txt1 := richtext.AddText('TSExcelFile类 '); txt1 := richtext.AddText('TSXlsxFile类 ');
txt1.Font.Size := 35; txt1.Font.Size := 35;
txt1.Font.Color := "FF0000"; txt1.Font.Color := "FF0000";
txt1.Font.Bold := 1; txt1.Font.Bold := 1;
@@ -34,15 +34,16 @@ txt2.Font.Bold := 1;
txt2.Font.Italic := 1; txt2.Font.Italic := 1;
txt2.Font.Color := "FFC0CB"; txt2.Font.Color := "FFC0CB";
file.SetCellRichText('ExcelFile', 'A1', richtext); file.SetCellRichText('XlsxFile', 'A1', richtext);
PrintSuccess("SetCellRichText", testing++); PrintSuccess("SetCellRichText", testing++);
[err, richText] := file.GetCellRichText('ExcelFile', 'A1'); [err, richText] := file.GetCellRichText('XlsxFile', 'A1');
if err then if err then
return PrintError("GetCellRichText", err, errinfo); return PrintError("GetCellRichText", err, errinfo);
PrintSuccess("GetCellRichText", testing++); PrintSuccess("GetCellRichText", testing++);
file.MergeCell('ExcelFile', 'A1', 'C1'); file.MergeCell('XlsxFile', 'A1', 'C1');
PrintSuccess("MergeCell", testing++); PrintSuccess("MergeCell", testing++);
file.SetRowHeight('XlsxFile', 1, 60);
style := TOfficeObj('TStyle'); style := TOfficeObj('TStyle');
@@ -65,15 +66,15 @@ style.Fill.Gradient.Stop2.Color := "E0EBF5";
styleId := file.NewStyle(style); styleId := file.NewStyle(style);
PrintSuccess("NewStyle", testing++); PrintSuccess("NewStyle", testing++);
file.SetCellStyle("ExcelFile", 'A1', 'C1', styleId); file.SetCellStyle("XlsxFile", 'A1', 'C1', styleId);
file.SetCellValue("ExcelFile", "A3", "索引"); file.SetCellValue("XlsxFile", "A3", "索引");
file.SetCellValue("ExcelFile", "B3", "函数名称"); file.SetCellValue("XlsxFile", "B3", "函数名称");
file.SetCellValue("ExcelFile", "C3", "功能"); file.SetCellValue("XlsxFile", "C3", "功能");
file.SetColWidth("ExcelFile", "A", "A", 6); file.SetColWidth("XlsxFile", "A", "A", 6);
file.SetColWidth("ExcelFile", "B", "B", 55); file.SetColWidth("XlsxFile", "B", "B", 55);
file.SetColWidth("ExcelFile", "C", "C", 100); file.SetColWidth("XlsxFile", "C", "C", 100);
file.SetCellStyle("ExcelFile", "A3", "C3", styleId); file.SetCellStyle("XlsxFile", "A3", "C3", styleId);
style2 := TOfficeObj("TStyle"); style2 := TOfficeObj("TStyle");
@@ -108,9 +109,9 @@ Begin
[err0, cell0] := file.CoordinatesToCellName(1, i+4); [err0, cell0] := file.CoordinatesToCellName(1, i+4);
[err1, cell1] := file.CoordinatesToCellName(2, i+4); [err1, cell1] := file.CoordinatesToCellName(2, i+4);
[err2, cell2] := file.CoordinatesToCellName(3, i+4); [err2, cell2] := file.CoordinatesToCellName(3, i+4);
file.SetCellValue("ExcelFile", cell0, i+1); file.SetCellValue("XlsxFile", cell0, i+1);
file.SetCellValue("ExcelFile", cell1, funcs[i]['funcname']); file.SetCellValue("XlsxFile", cell1, funcs[i]['funcname']);
file.SetCellValue("ExcelFile", cell2, funcs[i]['name']); file.SetCellValue("XlsxFile", cell2, funcs[i]['name']);
str := funcs[i]['name'] + "\nFunction " + funcs[i]['funcname'] + funcs[i]['comment']; str := funcs[i]['name'] + "\nFunction " + funcs[i]['funcname'] + funcs[i]['comment'];
arr := str2array(str, "\n"); arr := str2array(str, "\n");
@@ -130,15 +131,15 @@ Begin
hyperlink.LinkType := "location"; hyperlink.LinkType := "location";
hyperlink.LinkUrl := "Functions!" $ c1; hyperlink.LinkUrl := "Functions!" $ c1;
file.SetCellHyperLink("ExcelFile", cell1, hyperlink); file.SetCellHyperLink("XlsxFile", cell1, hyperlink);
file.SetCellStyle("ExcelFile", cell1, cell1, styleId4); file.SetCellStyle("XlsxFile", cell1, cell1, styleId4);
End End
file.SetColWidth('Functions', 'A', 'A', 120); file.SetColWidth('Functions', 'A', 'A', 120);
PrintSuccess('SetColWidth', testing++); PrintSuccess('SetColWidth', testing++);
saveName := utf8toansi("ExcelFile使用帮助.xlsx"); saveName := "XlsxFile使用帮助.xlsx";
[err, errinfo] := file.SaveAs('', saveName); [err, errinfo] := file.SaveAs('', saveName);
if err then if err then
return PrintError("SaveAs", err, errinfo); return PrintError("SaveAs", err, errinfo);
@@ -320,6 +321,7 @@ Begin
excel.SetCellValue(sheet_name, cell_1, name); excel.SetCellValue(sheet_name, cell_1, name);
excel.MergeCell(sheet_name, cell_1, cell_2); excel.MergeCell(sheet_name, cell_1, cell_2);
excel.SetCellStyle(sheet_name, cell_1, cell_2, styleid1); excel.SetCellStyle(sheet_name, cell_1, cell_2, styleid1);
excel.SetRowHeight(sheet_name, row, 25);
row++; row++;
desc_arr := obj_info[i]['Desc']; desc_arr := obj_info[i]['Desc'];
Binary file not shown.
Binary file not shown.
Binary file not shown.
+12 -3
View File
@@ -1,4 +1,4 @@
# OfficePlugin # TSOffice
TSOffice 项目:纯 TSL 代码实现 excel、word 文件读写 TSOffice 项目:纯 TSL 代码实现 excel、word 文件读写
@@ -12,20 +12,29 @@ TSOffice 项目:纯 TSL 代码实现 excel、word 文件读写
可选: 可选:
- `fmt_pubkrnl_plugin` 放入 tsl 安装根目录下 Plugin 文件夹,若需要执行Demo文件夹下的[ExcelHelp.tsl](./Demo/ExcelHelp.tsl)和[WordHelp.tsl](./Demo/WordHelp.tsl),需要部署改动态库 - `fmt_pubkrnl_plugin` 放入 tsl 安装根目录下 Plugin 文件夹,若需要执行 Demo 文件夹下的[XlsxHelp.tsl](./Demo/XlsxHelp.tsl)和[DocxHelp.tsl](./Demo/DocxHelp.tsl),需要部署改动态库
### Linux ### Linux
#### Ubuntu-18.04
根据架构选择 aarch64 或 x86 版本将对应文件夹内容进行部署 根据架构选择 aarch64 或 x86 版本将对应文件夹内容进行部署
- `liboffice_plugin` 放入 tsl 安装根目录下 Plugin 文件夹 - `liboffice_plugin` 放入 tsl 安装根目录下 Plugin 文件夹
## 帮助文档 ## 帮助文档
- [Excel 帮助文档](./ExcelFile使用帮助.xlsx) - [Excel 帮助文档](./XlsxFile使用帮助.xlsx)
- [Word 帮助文档](./DocxFile使用帮助.docx) - [Word 帮助文档](./DocxFile使用帮助.docx)
## 扩展
扩展模块的内容需要通过`git submodule update --init --recursive`获取,相关帮助文档在`./funcext/TSOffice/Extensions/模块名`目录下可获取
支持的扩展:
- **OfficeVba** 可通过类 Vba 方式对 office文件 进行读写操作。
## 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
+88 -17
View File
@@ -1,5 +1,4 @@
// Version 1.4.4 // Version 1.8.8
Type TSDocxFile = Class Type TSDocxFile = Class
///Version: V1.0 2022-09-20 ///Version: V1.0 2022-09-20
///适用于 Microsoft Word docx格式文件 ///适用于 Microsoft Word docx格式文件
@@ -50,7 +49,7 @@ Type TSDocxFile = Class
Begin Begin
if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.'); if not ifObj(zipfile_) then return array(-1, 'Create ZipFile object fail.');
if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile(); if zipfile_.FilesCount() > 0 then zipfile_ := new ZipFile();
[err, errmsg] := zipfile_.Open(alias, fname, passwd); [err, errmsg] := zipfile_.Open(alias, TOfficeApi().CurCodePageToGBK(fname), passwd);
if err=0 then Begin if err=0 then Begin
document_ := new docxDocument(zipfile_); document_ := new docxDocument(zipfile_);
End; End;
@@ -88,7 +87,7 @@ Type TSDocxFile = Class
///返回: [err, info] ///返回: [err, info]
Function SaveAs(alias, fname); Function SaveAs(alias, fname);
Begin Begin
return zipfile_.Save(alias, fname); return zipfile_.Save(alias, TOfficeApi().CurCodePageToGBK(fname));
End; End;
///另存为二进制流数据 ///另存为二进制流数据
@@ -114,7 +113,10 @@ Type TSDocxFile = Class
///返回:string ///返回:string
Function FileName(); Function FileName();
Begin Begin
return zipfile_.FileName(); file_name := zipfile_.FileName();
if TOfficeApi().IsUtf8() then
return AnsiToUTF8(file_name);
return file_name;
End; End;
///word文档所有段落 ///word文档所有段落
@@ -124,6 +126,13 @@ Type TSDocxFile = Class
return document_.Body().Paragraphs(); return document_.Body().Paragraphs();
End; End;
///word文档最后一个段落
///返回:TParagraph对象
Function LastParagraph();
Begin
return document_.Body().LastParagraph();
End;
///添加新段落 ///添加新段落
///paragraph: TParagraph对象 ///paragraph: TParagraph对象
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加段落 ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加段落
@@ -230,10 +239,12 @@ Type TSDocxFile = Class
///插入数据表 ///插入数据表
///tbl: TTable对象 ///tbl: TTable对象
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加表格 ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加表格
///customCell: 二维数组,自定义指定单元格的样式
/// 如:一行一列arr[0][0] := array(twtcPr, twpPr, twrPr);其中twtcPr是twtcPr对象,twpPr是twpPr对象,twrPr是twrPr对象
///返回: TTable对象 ///返回: TTable对象
Function InsertTable(tbl, posOpt); Function InsertTable(tbl, posOpt, customCell);
Begin Begin
return document_.Body().InsertTable(tbl, getPosNode(posOpt)); return document_.Body().InsertTable(tbl, getPosNode(posOpt), customCell);
End; End;
///返回CoreProperties对象 ///返回CoreProperties对象
@@ -259,6 +270,13 @@ Type TSDocxFile = Class
return document_.Body().Sections(index); return document_.Body().Sections(index);
End; End;
///获取Structured Document Tag
///返回:TSdt集合
Function Sdts();
Begin
return document_.Body().Sdts();
End;
///添加章节 ///添加章节
///sessionTDocSection对象 ///sessionTDocSection对象
///posOpt: 位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加章节 ///posOpt: 位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加章节
@@ -323,7 +341,7 @@ Type TSDocxFile = Class
///返回TChart对象 ///返回TChart对象
Function CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, posOpt); Function CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, posOpt);
Begin Begin
excel := new TSExcelFile(); excel := new TSXlsxFile();
[err, msg] := excel.OpenFile('', excelFileName); [err, msg] := excel.OpenFile('', excelFileName);
if err then return nil; if err then return nil;
[err, charts] := excel.GetCharts(excelSheetName); [err, charts] := excel.GetCharts(excelSheetName);
@@ -372,6 +390,26 @@ Type TSDocxFile = Class
return nil; return nil;
End; End;
///复制Word内容
///alias: string,文件目录别名
///fileName: string,文件名
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
///返回: [err, TDocxCopy对象]
///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组
///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组
///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);
copy_obj := new TDocxCopy(self, docxObj);
copy_obj.Init();
copy_obj.Copy(posOpt);
return array(0, copy_obj);
End;
///复制Word内容 ///复制Word内容
///docxObj: TSDocxFile对象 ///docxObj: TSDocxFile对象
///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片 ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片
@@ -379,11 +417,9 @@ Type TSDocxFile = Class
///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组 ///TDocxCopy.GetCopiedTable() 返回复制的对象表格数组
///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组 ///TDocxCopy.GetCopiedParagraph() 返回复制对象的段落数组
///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组 ///TDocxCopy.GetCopiedDrawing() 返回复制对象的图表数组
Function InsertFile(alias, fileName, posOpt); Function InsertFile(docxObj, posOpt);overload;
Begin Begin
docxObj := new TSDocxFile(); document_.AddNameSpace('w16du', 'http://schemas.microsoft.com/office/word/2023/wordml/word16du');
[err, msg] := docxObj.OpenFile(alias, fileName);
if err then return array(err, msg);
copy_obj := new TDocxCopy(self, docxObj); copy_obj := new TDocxCopy(self, docxObj);
copy_obj.Init(); copy_obj.Init();
copy_obj.Copy(posOpt); copy_obj.Copy(posOpt);
@@ -504,13 +540,32 @@ Type TSDocxFile = Class
return content; return content;
End; 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:标题最高级别 ///UpperHeadingLevel:标题最高级别
///LowerHeadingLevel:标题最低级别 ///LowerHeadingLevel:标题最低级别
///返回:array((("Level":level,"Paragraph":"object","Text":title)); ///返回:array((("Level":level,"Paragraph":"object","Text":title));
Function GetHeadingList(UpperHeadingLevel, LowerHeadingLevel); Function GetHeadingList(UpperHeadingLevel, LowerHeadingLevel);
Begin Begin
return document_.Body().GetHeadingListImpl(self, nil, UpperHeadingLevel, LowerHeadingLevel, nil, true); return document_.Body().GetHeadingListImpl(self, nil, UpperHeadingLevel, LowerHeadingLevel, nil);
End; End;
//返回Document对象 //返回Document对象
@@ -535,6 +590,17 @@ Type TSDocxFile = Class
return numberingObj_; return numberingObj_;
End; End;
///返回:TFootnotes对象
Function FootNotesObject();
Begin
if not ifObj(footnotesObj_) then
begin
footnotesObj_ := new TOfficeObj('TFootnoteBody');
footnotesObj_.InitFootnotes(self.Zip());
end
return footnotesObj_;
End;
///执行word文档内嵌tsl代码(执行内嵌脚本的环境字符集为GBK) ///执行word文档内嵌tsl代码(执行内嵌脚本的环境字符集为GBK)
///返回:[err,tslFuncCount,errArr]: err 执行错误TSL代码段次数,tslFuncCount TSL代码段总数,errArr 执行TSL错误信息(array(('code':'代码', 'err':'错误信息')) ///返回:[err,tslFuncCount,errArr]: err 执行错误TSL代码段次数,tslFuncCount TSL代码段总数,errArr 执行TSL错误信息(array(('code':'代码', 'err':'错误信息'))
Function ExecInnerTSL(); Function ExecInnerTSL();
@@ -563,16 +629,20 @@ Type TSDocxFile = Class
DocPrId_ := 0; DocPrId_ := 0;
ps := Paragraphs(); ps := Paragraphs();
for i:=0 to length(ps)-1 do Begin for i:=0 to length(ps)-1 do Begin
node := class(TSXml).GetNode(ps[i].node_, 'w:r/w:drawing/wp:inline/wp:docPr'); 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 if not ifObj(node) then
node := class(TSXml).GetNode(ps[i].node_, 'w:r/w:pict/v:shape/v:textbox/w:txbxContent/wp:docPr'); node := class(TSXml).GetNode(runs[j].node_, 'w:drawing/wp:anchor/wp:docPr');
if not ifObj(node) then if not ifObj(node) then
node := class(TSXml).GetNode(ps[i].node_, 'w:r/mc:AlternateContent/mc:Choice/w:drawing/wp:anchor/wp:docPr'); 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 if ifObj(node) then Begin
id := class(TSXml).SafeStrToIntDef(node.GetAttribute('id'), 0); id := class(TSXml).SafeStrToIntDef(node.GetAttribute('id'), 0);
if id > DocPrId_ then if id > DocPrId_ then
DocPrId_ := id; DocPrId_ := id;
break; End;
End; End;
End; End;
End; End;
@@ -591,6 +661,7 @@ private
zipfile_; //压缩文件对象 zipfile_; //压缩文件对象
document_; //Document对象 document_; //Document对象
styleObj_; styleObj_;
footnotesObj_;
numberingObj_; numberingObj_;
DocPrId_; DocPrId_;
End; End;
+2 -2
View File
@@ -1,4 +1,4 @@
// Version 1.4.4 // Version 1.5.0
Type TSExcelFile = Class Type TSExcelFile = Class
///Version: V1.0 2022-08-08 ///Version: V1.0 2022-08-08
@@ -915,7 +915,7 @@ Type TSExcelFile = Class
///复制Sheet ///复制Sheet
///sourceSheet: 源工作表 ///sourceSheet: 源工作表
///destSheet: 目的工作表 ///destSheet: 目的工作表,如果destSheet存在,则覆盖destSheet,否则末尾新增destSheet
Function CopySheet(sourceSheet, destSheet); Function CopySheet(sourceSheet, destSheet);
Begin Begin
return workbook_.CopySheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet)); return workbook_.CopySheet(class(TSXml).CurCodePageToUtf8(sourceSheet), class(TSXml).CurCodePageToUtf8(destSheet));
+49 -27
View File
@@ -18,25 +18,59 @@ public
return RootObj; return RootObj;
End End
Function Update(); Function Update(position);overload;
Begin Begin
if ifObj(RootObj) then Begin if ifObj(RootObj) then Begin
arr := Marshal(); arr := Marshal();
if length(arr['attributes']) or length(arr['children']) then Begin if length(arr['attributes']) or length(arr['children']) then Begin
curNode := class(TSXml).GetNode(RootObj, NodeUri); curNode := class(TSXml).GetNode(RootObj, NodeUri, position);
if ifObj(curNode) then if ifObj(curNode) then
class(TSXml).UpdateNode(curNode, arr['attributes'], arr['children']); class(TSXml).UpdateNode(curNode, arr['attributes'], arr['children']);
End; End;
End; End;
End; End;
Function HandleAttrs(); virtual; Function Update();overload;
Begin Begin
End self.Update("end");
End;
Function HandleChildren(); virtual; Function Delete();overload;
Begin Begin
End if ifObj(RootObj) then
begin
curNode := class(TSXml).GetNode(RootObj, NodeUri);
if ifObj(curNode) then curNode.DeleteChildren();
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; Function GetAttrs(); virtual;
Begin Begin
@@ -71,27 +105,16 @@ public
children_ := nil; children_ := nil;
GetChildrenEx(); //优化为变量,减少数据COPY2022-12-10 GetChildrenEx(); //优化为变量,减少数据COPY2022-12-10
child_arr := array(); child_arr := array();
name_map := array();
len := 0; len := 0;
for i:=0 to length(children_)-1 do for i:=0 to length(children_)-1 do
begin begin
node_type := children_[i]['nodeType']; node_type := children_[i]['nodeType'];
obj := children_[i]['obj']; obj := children_[i]['obj'];
if ifnil(obj) then continue; if ifnil(obj) then continue;
//find := select thisrowindex as "rowindex_", * from child_arr where ['name'] = children_[i]['name'] end; //优化为循环,性能提高15-20%2022-12-20 if not ifObj(obj) and not ifnil(name_map[children_[i]['name']]) and children_[i]['attrEx'] <> '' then
find := array();
for j:=length(child_arr)-1 downto 0 do Begin //支持列表模式(如多节点:<w:t xml:space=""></w:t>),查找最近的节点
if child_arr[j]['name'] = children_[i]['name'] then Begin
find[0] := child_arr[j];
find[0]['rowindex_'] := j;
break;
End;
End;
if not ifnil(obj) and not ifObj(obj) and istable(find) and ifstring(children_[i]['attrEx']) and children_[i]['attrEx'] <> '' then
begin begin
if not ifarray(find[0]['attributes']) then find[0]['attributes'] := array(); child_arr[name_map[children_[i]['name']]]['attributes'] union=array(children_[i]['attrEx'] : obj);
key := children_[i]['attrEx'];
find[0]['attributes'] union= array(key : obj);
child_arr[find[0]['rowindex_']]['attributes'] := find[0]['attributes'];
continue; continue;
end end
@@ -100,7 +123,7 @@ public
begin begin
empty_name := children_[i]['attrEx'] ? children_[i]['attrEx'] : 'val'; empty_name := children_[i]['attrEx'] ? children_[i]['attrEx'] : 'val';
if obj = 0 then arr['attributes'] := array(empty_name: 0); 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 end
else if node_type = 'pcdata' then else if node_type = 'pcdata' then
begin begin
@@ -118,7 +141,7 @@ public
key := children_[i]['attrName'] ? children_[i]['attrName'] : children_[i]['attrEx'] ? children_[i]['attrEx'] : 'val'; key := children_[i]['attrName'] ? children_[i]['attrName'] : children_[i]['attrEx'] ? children_[i]['attrEx'] : 'val';
arr['attributes'] := array(key : obj); arr['attributes'] := array(key : obj);
end end
name_map[children_[i]['name']] := len;
child_arr[len++] := arr; child_arr[len++] := arr;
end end
@@ -154,7 +177,7 @@ public
return 1; return 1;
End End
Function Value(name); Function Value(name, nottransferable);
Begin Begin
if not ifObj(RootObj) then return nil; if not ifObj(RootObj) then return nil;
if NodeUri <> '' then if NodeUri <> '' then
@@ -169,7 +192,7 @@ public
if istable(r) then if istable(r) then
begin begin
value := node.GetAttribute(r[1]); value := node.GetAttribute(r[1]);
return trystrtofloat(value, r) ? r : value; return nottransferable ? value : trystrtofloat(value, r) ? r : value;
end end
children := GetChildren(); children := GetChildren();
@@ -177,7 +200,7 @@ public
if istable(r) then Begin if istable(r) then Begin
r := r[0]; r := r[0];
node := node.FirstChildElement(r['name']); node := node.FirstChildElement(r['name']);
if not ifObj(node) and r['nodeType'] = 'empty' then return false; if not ifObj(node) and r['nodeType'] = 'empty' and not nottransferable then return false;
if not ifObj(node) then return nil; if not ifObj(node) then return nil;
if r['nodeType'] = 'pcdata' then //返回文本串 if r['nodeType'] = 'pcdata' then //返回文本串
return node.GetText(); return node.GetText();
@@ -186,7 +209,7 @@ public
key := r['attrName'] ? r['attrName'] : r['attrEx'] ? r['attrEx'] : 'val'; key := r['attrName'] ? r['attrName'] : r['attrEx'] ? r['attrEx'] : 'val';
value := node.GetAttribute(key); value := node.GetAttribute(key);
if r['nodeType'] = 'empty' and value = '' then return true; if r['nodeType'] = 'empty' and value = '' then return true;
return trystrtofloat(value, r) ? r : value; return nottransferable ? value : trystrtofloat(value, r) ? r : value;
End; End;
return nil; return nil;
End; End;
@@ -269,7 +292,6 @@ public
NodeName; NodeName;
ExtAttr; ExtAttr;
ExtNodes; ExtNodes;
//Parent;
ReplaceArr; ReplaceArr;
RootObj; // xml node RootObj; // xml node
NodeUri:string; NodeUri:string;
+27
View File
@@ -179,6 +179,33 @@ Type TOffice = Class
return getTextBoxImpl(p, true); return getTextBoxImpl(p, true);
End; 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); Function Set(k, v);
Begin Begin
hash_[k] := v; hash_[k] := v;
+5 -3
View File
@@ -71,12 +71,14 @@ Type TSImage = Class
Begin Begin
des1 := 0xff; des1 := 0xff;
des2 := 0xc0; des2 := 0xc0;
des3 := 0xc2;
flag := 1; flag := 1;
while flag do while flag do
begin begin
stringObj_.Read(byte1, 1); r1 := stringObj_.Read(byte1, 1);
stringObj_.Read(byte2, 1); r2 := stringObj_.Read(byte2, 1);
if byte1 = des1 and byte2 = des2 then flag := 0; if r1 = 0 or r2 = 0 then break;
if byte1 = des1 and (byte2 = des2 or byte2 = des3) then flag := 0;
end end
if flag then return array(0, 0); if flag then return array(0, 0);
stringObj_.Read(tmp, 3); stringObj_.Read(tmp, 3);
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -52,7 +52,7 @@ Type TDocxChart = Class(TSChart)
return true; return true;
End; End;
docx_; [weakref]docx_;
chartId_; chartId_;
rId_; rId_;
xmlObj_; xmlObj_;
+199 -24
View File
@@ -9,6 +9,7 @@ Type TDocxCopy = class
copy_drawing_ := array(); copy_drawing_ := array();
style_copy_obj_ := new TDocxStyleCopy(oldObj.StyleObject(), newObj.StyleObject()); style_copy_obj_ := new TDocxStyleCopy(oldObj.StyleObject(), newObj.StyleObject());
number_copy_obj_ := new TDocxNumberCopy(oldObj, newObj); number_copy_obj_ := new TDocxNumberCopy(oldObj, newObj);
footnote_copy_obj_ := new TDocxFootnoteCopy(oldObj, newObj);
End; End;
Function Init(); Function Init();
@@ -21,7 +22,7 @@ Type TDocxCopy = class
Function Copy(posOpt); Function Copy(posOpt);
Begin Begin
// 复制所有的样式 // 复制所有的样式
style_copy_obj_.CopyStyle(); style_copy_obj_.CopyStyle(number_copy_obj_);
parts := new_docx_obj_.Body().Parts(); parts := new_docx_obj_.Body().Parts();
pos := old_docx_obj_.GetPosNode(posOpt); pos := old_docx_obj_.GetPosNode(posOpt);
@@ -100,6 +101,56 @@ private
end end
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); Function SetPic(node);
Begin Begin
// 获取新文件的图片 // 获取新文件的图片
@@ -110,6 +161,7 @@ private
// 比较新文件的图片在旧文件中是否存在 // 比较新文件的图片在旧文件中是否存在
zip := old_docx_obj_.Zip(); zip := old_docx_obj_.Zip();
xml := zip.Get('word/_rels/document.xml.rels');
files := sselect ['FileName'] from zip.Files() where AnsiStartsText('word/media/image', ['FileName']) end; files := sselect ['FileName'] from zip.Files() where AnsiStartsText('word/media/image', ['FileName']) end;
for i:=0 to length(files)-1 do Begin for i:=0 to length(files)-1 do Begin
if zip.Diff(files[i], image_file) = 0 then Begin if zip.Diff(files[i], image_file) = 0 then Begin
@@ -117,7 +169,6 @@ private
[maxRid, imageFile, rid] := class(TSXml).FindRelationshipRid(xml, prefix); [maxRid, imageFile, rid] := class(TSXml).FindRelationshipRid(xml, prefix);
End; End;
End; End;
xml := zip.Get('word/_rels/document.xml.rels');
if rid = 0 then if rid = 0 then
begin begin
image_cnt := length(files) + 1; image_cnt := length(files) + 1;
@@ -131,9 +182,9 @@ private
zip.Add('word/' + image_path, image_file); zip.Add('word/' + image_path, image_file);
contentXml := zip.Get('[Content_Types].xml'); contentXml := zip.Get('[Content_Types].xml');
class(TSXml).AddDefaultContentType(contentXml, postfix, 'image/' $ postfix); class(TSXml).AddDefaultContentType(contentXml, postfix, 'image/' $ postfix);
end
end
node.SetAttribute('r:embed', 'rId' $ rid); node.SetAttribute('r:embed', 'rId' $ rid);
end
end
End; End;
Function SetChart(node); Function SetChart(node);
@@ -198,7 +249,9 @@ private
Function SetDrawingInfo(drawing); Function SetDrawingInfo(drawing);
Begin Begin
graphic_node := class(TSXml).GetNode(drawing.node_,'w:r/w:drawing/wp:inline/a:graphic/a:graphicData'); 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');
if ifObj(graphic_node) then if ifObj(graphic_node) then
begin begin
node := class(TSXml).GetNode(graphic_node,'pic:pic/pic:blipFill/a:blip'); node := class(TSXml).GetNode(graphic_node,'pic:pic/pic:blipFill/a:blip');
@@ -210,21 +263,67 @@ private
Function SetParagraphInfo(paragraph); Function SetParagraphInfo(paragraph);
Begin Begin
style := class(TSXml).GetNode(paragraph.node_, 'w:pPr/w:pStyle'); CopyStyle(paragraph.node_, 'w:pPr/w:pStyle');
CopyNumber(paragraph.node_, 'w:pPr/w:numPr/w:numId');
SetRunsInfo(paragraph.node_);
DeleteComment(paragraph); // 删除批注
End;
Function SetRunsInfo(node);
Begin
run := node.FirstChildElement('w:r');
while ifObj(run) do
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;
Function CopyStyle(node, path);
Begin
style := class(TSXml).GetNode(node, path);
if ifObj(style) then if ifObj(style) then
begin begin
styleid := style.GetAttribute('w:val'); styleid := style.GetAttribute('w:val');
new_id := style_copy_obj_.GetStyleNewId(styleid); new_id := style_copy_obj_.GetStyleNewId(styleid);
if new_id then style.SetAttribute('w:val', new_id); if new_id then style.SetAttribute('w:val', new_id);
end end
numpr := class(TSXml).GetNode(paragraph.node_, 'w:pPr/w:numPr/w:numId'); End;
Function CopyNumber(node, path);
Begin
numpr := class(TSXml).GetNode(node, path);
if ifObj(numpr) then if ifObj(numpr) then
begin begin
id := numpr.GetAttribute('w:val'); id := numpr.GetAttribute('w:val');
numberid := number_copy_obj_.CopyNumbering(id); numberid := number_copy_obj_.CopyNumbering(id);
numpr.SetAttribute('w:val', id); numpr.SetAttribute('w:val', numberid);
end
End;
Function CopyFootnote(node);
Begin
footnote := node.FirstChildElement('w:footnoteReference');
if not ifObj(footnote) then return;
id := footnote.GetAttribute('w:id');
if id then
begin
obj := footnote_copy_obj_.CopyFootnote(id);
footnote.SetAttribute('w:id', obj.Id);
parts := obj.Parts();
for i:=0 to length(parts)-1 do
begin
case GetPartType(parts[i]) of
0: SetParagraphInfo(parts[i]);
1: SetDrawingInfo(parts[i]);
2: SetTableInfo(parts[i]);
end;
end
end end
DeleteComment(paragraph); // 删除批注
End; End;
Function SetTableInfo(table); Function SetTableInfo(table);
@@ -244,28 +343,34 @@ private
begin begin
cell := table.Cell(r, c); cell := table.Cell(r, c);
if not cell then continue; if not cell then continue;
node := cell.node_; node := cell.node_.FirstChildElement();
if (tbl := node.FirstChildElement('w:tbl')) then while ifObj(node) do
begin
name := node.GetName();
if name = 'w:tbl' then
begin begin
obj := TOfficeObj('TTable'); obj := TOfficeObj('TTable');
obj.Init(tbl); obj.Init(node);
SetTableInfo(obj); SetTableInfo(obj);
end end
else if (p := node.FirstChildElement('w:p')) then else if name = "w:p" then
begin begin
draw := class(TSXml).GetNode(p, 'w:r/w:drawing'); // draw := class(TSXml).GetNode(node, 'w:r/w:drawing');
draw := GetDrawingNode(node);
if ifObj(draw) then if ifObj(draw) then
begin begin
obj := TOfficeObj('TPicture'); obj := TOfficeObj('TPicture');
obj.Init(p); obj.Init(node);
SetDrawingInfo(obj); SetDrawingInfo(obj);
end end
else begin else begin
obj := TOfficeObj('TParagraph'); obj := TOfficeObj('TParagraph');
obj.Init(p); obj.Init(node);
SetParagraphInfo(obj); SetParagraphInfo(obj);
end end
end end
node := node.NextElement();
end
end end
end end
End; End;
@@ -280,6 +385,7 @@ private
case name of case name of
'w:p': 'w:p':
begin begin
if GetDrawingNode(part.node_) then return 1;
if class(TSXml).GetNode(part.node_, 'w:r/w:drawing') then return 1; if class(TSXml).GetNode(part.node_, 'w:r/w:drawing') then return 1;
return 0; return 0;
end end
@@ -290,11 +396,32 @@ private
end end
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 private
old_docx_obj_; old_docx_obj_;
new_docx_obj_; new_docx_obj_;
style_copy_obj_; style_copy_obj_;
number_copy_obj_; number_copy_obj_;
footnote_copy_obj_;
copy_table_; copy_table_;
copy_paragraph_; copy_paragraph_;
@@ -326,11 +453,12 @@ Type TDocxStyleCopy = class
end end
End; End;
Function CopyStyle(); Function CopyStyle(numberobj);
Begin Begin
for id, obj in id_map_ do for id, obj in id_map_ do
begin begin
SetBasedOn(obj); SetBasedOn(obj);
SetNumId(obj, numberobj);
SetLink(obj); SetLink(obj);
old_style_obj_.CopyStyle(obj); old_style_obj_.CopyStyle(obj);
end; end;
@@ -382,21 +510,31 @@ private
end end
End; End;
Function SetNumId(obj, numberobj);
Begin
if not ifObj(numberobj) then return;
numPr := class(TSXml).GetNode(obj.node_, 'w:pPr/w:numPr/w:numId');
if ifObj(numPr) then
begin
id := numpr.GetAttribute('w:val');
numberid := numberobj.CopyNumbering(id);
numpr.SetAttribute('w:val', numberid);
end
End;
Function GetNewName(name); Function GetNewName(name);
Begin Begin
new_name := name; new_name := name;
count := 0; while ifObj(old_style_obj_.GetStyle(class(TSXml).Utf8ToCurCodePage(new_name))) or style_name_map2_[new_name] do
while ifObj(old_style_obj_.GetStyle(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; return new_name;
End; End;
Function GetNewId(id); Function GetNewId(id);
Begin Begin
new_id := id; new_id := id;
count := 0;
while ifObj(old_style_obj_.GetStyleById(new_id)) or style_id_map2_[new_id] do while ifObj(old_style_obj_.GetStyleById(new_id)) or style_id_map2_[new_id] do
new_id := new_id $ count++; new_id := new_id $ crc32(inttostr(random(2000000)));
return new_id; return new_id;
End; End;
@@ -437,9 +575,9 @@ Type TDocxNumberCopy = class
begin begin
number_obj := old_number_obj_.CopyNumber(obj); number_obj := old_number_obj_.CopyNumber(obj);
id_map_[number] := number_obj; id_map_[number] := number_obj;
return number_obj;
end end
end; return id_map_[number];
end
End; End;
private private
@@ -448,3 +586,40 @@ private
id_map_; // [id: styleobj] id_map_; // [id: styleobj]
End; End;
Type TDocxFootnoteCopy = class
Function Create(oldObj, newObj);
Begin
footnotexml := newObj.Zip().Get('word/footnotes.xml');
if ifObj(footnotexml) then
begin
old_footnote_obj_ := oldObj.FootNotesObject();
new_footnote_obj_ := newObj.FootNotesObject();
end
else begin
old_footnote_obj_ := nil;
new_footnote_obj_ := nil;
end
id_map_ := array();
End;
Function CopyFootnote(id);
Begin
if ifObj(old_footnote_obj_) and ifObj(new_footnote_obj_) then
begin
if (obj := new_footnote_obj_.GetFootnote(id)) and not id_map_[id] then
begin
footnote_obj := old_footnote_obj_.CopyFootnote(obj);
id_map_[id] := footnote_obj;
end
return id_map_[id];
end
End;
private
[weakref]old_footnote_obj_;
new_footnote_obj_;
id_map_; // [id: footnote]
End;
+2 -1
View File
@@ -141,6 +141,7 @@ Type TDocxStyles = Class
Begin Begin
if ifObj(newstyle) and ifObj(newstyle.node_) then Begin if ifObj(newstyle) and ifObj(newstyle.node_) then Begin
node := stylesXml_.FirstChildElement('w:styles').InsertEndChild(newstyle.node_.Marshal()[0]); node := stylesXml_.FirstChildElement('w:styles').InsertEndChild(newstyle.node_.Marshal()[0]);
node.DeleteAttribute('w:default');
obj := TOfficeObj('TDocxStyle'); obj := TOfficeObj('TDocxStyle');
obj.StyleId := node.GetAttribute('w:styleId'); obj.StyleId := node.GetAttribute('w:styleId');
obj.Name := node.FirstChildElement('w:name').GetAttribute('w:val'); obj.Name := node.FirstChildElement('w:name').GetAttribute('w:val');
@@ -198,7 +199,7 @@ Type TDocxStyles = Class
defaultPpr_; defaultPpr_;
defaultRpr_; defaultRpr_;
private private
docx_; [weakref]docx_;
stylesXml_; stylesXml_;
idMap_; idMap_;
nameMap_; nameMap_;
+1 -1
View File
@@ -228,7 +228,7 @@ Type TNumbering = Class
class(TSXml).UpdateNode(o.node_, arr['attributes'], arr['children']); class(TSXml).UpdateNode(o.node_, arr['attributes'], arr['children']);
End; End;
private private
docx_; [weakref]docx_;
numberingXml_; numberingXml_;
maxAbstractNumId_:integer; maxAbstractNumId_:integer;
maxNumId_:integer; maxNumId_:integer;
+39 -9
View File
@@ -94,6 +94,16 @@ Type TTableContent = class
_AddTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel); _AddTableContent(posOpt, UpperHeadingLevel, LowerHeadingLevel);
End; 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); Function _AddDefaultTableContent(UpperHeadingLevel, LowerHeadingLevel);
Begin Begin
p := _AddItem(UpperHeadingLevel, LowerHeadingLevel, UpperHeadingLevel - 1, true); p := _AddItem(UpperHeadingLevel, LowerHeadingLevel, UpperHeadingLevel - 1, true);
@@ -118,13 +128,13 @@ Type TTableContent = class
tab := TOfficeObj('TTabStop'); tab := TOfficeObj('TTabStop');
tab.Val := 'right'; tab.Val := 'right';
tab.leader := 'dot'; tab.leader := 'dot';
tab.Position := 8640; tab.Position := TOfficeApi().Get("TTableContent-tab-position") ?: 8306;
p.Format.Tabs.Add(0, tab); p.Format.Tabs.Add(0, tab);
if first then Begin if first then Begin
//fldCharType //fldCharType
r1 := p.AddRun(); r1 := p.AddRun();
r1.fldCharType := 'begin'; r1.fldCharType := 'begin';
r1.Dirty := 'true'; r1.Dirty := 0;
//instrText //instrText
r2 := p.AddRun(); r2 := p.AddRun();
@@ -142,7 +152,7 @@ Type TTableContent = class
Begin Begin
///获取标题列表 array((("Level":level,"Paragraph":"object","Text":title,"numId":,"ilvl":,"numArr":)); ///获取标题列表 array((("Level":level,"Paragraph":"object","Text":title,"numId":,"ilvl":,"numArr":));
numMap := array(); numMap := array();
r := docx_.Document().Body().GetHeadingListImpl(docx_, posOpt, UpperHeadingLevel, LowerHeadingLevel, numMap, true); r := docx_.Document().Body().GetHeadingListImpl(docx_, nil, UpperHeadingLevel, LowerHeadingLevel, numMap);
for i:=0 to length(r)-1 do Begin for i:=0 to length(r)-1 do Begin
p := _AddItem(UpperHeadingLevel, LowerHeadingLevel, r[i]['Level'], i = 0 ? true: false); p := _AddItem(UpperHeadingLevel, LowerHeadingLevel, r[i]['Level'], i = 0 ? true: false);
//fldCharType //fldCharType
@@ -160,9 +170,9 @@ Type TTableContent = class
if ifObj(style) then if ifObj(style) then
numStr := style.GetText(r[i]['ilvl'], r[i]['numArr']); numStr := style.GetText(r[i]['ilvl'], r[i]['numArr']);
End End
if numStr <> '' then run_t := numStr + ' ' + r[i]['Text']; run_t := class(TSXml).CurCodePageToUtf8(r[i]['Text']);
else run_t := r[i]['Text']; if numStr <> '' then run_t := numStr + ' ' + run_t;
r1.SetText(run_t); r1.SetText(run_t, 1);
r2 := h.AddRun(); r2 := h.AddRun();
r2.rPr.noProof := true; r2.rPr.noProof := true;
@@ -203,7 +213,6 @@ Type TTableContent = class
r8.fldCharType := 'end'; r8.fldCharType := 'end';
_AddStdContent(p); _AddStdContent(p);
End; End;
p2 := TOfficeObj('TParagraph'); p2 := TOfficeObj('TParagraph');
@@ -221,6 +230,26 @@ Type TTableContent = class
Begin Begin
End; End;
Function Paragraphs();
Begin
node := class(TSXml).GetNode(docx_.Document().Body().Node(), 'w:sdt/w:sdtContent');
r := array();
if ifObj(node) then
begin
twbody := TOfficeObj('TwBody');
twbody.Create(nil, 'w:sdtContent');
twbody.InitRootNode(node);
twbody.InitNode(node);
parts := twbody.Paragraphs();
for i:=0 to length(parts)-1 do
begin
r[i] := TOfficeObj('TParagraph');
r[i].Init(parts[i].node_);
end
end
return r;
End;
Function Marshal(); Function Marshal();
Begin Begin
return impl_.Marshal(); return impl_.Marshal();
@@ -265,7 +294,7 @@ Type TTableContent = class
if ifObj(node) then if ifObj(node) then
return node.GetAttribute('w:name'); return node.GetAttribute('w:name');
bookMarkID := docx_.Document().GetBookMarkID(); bookMarkID := docx_.Document().GetBookMarkID();
return p.AddBookMark(); return p.AddBookMark(bookMarkID);
End; End;
Function _CheckNodes(name); Function _CheckNodes(name);
@@ -295,7 +324,8 @@ Type TTableContent = class
End; End;
End; End;
docx_; [weakref]docx_;
node_; node_;
impl_; impl_;
End; End;
+7 -1
View File
@@ -10,6 +10,7 @@ Type docxDocument = Class
bodyNode_ := root_.FirstChildElement('w:body'); bodyNode_ := root_.FirstChildElement('w:body');
body_ := TOfficeObj('TwBody'); body_ := TOfficeObj('TwBody');
body_.InitNode(bodyNode_); body_.InitNode(bodyNode_);
body_.InitRootNode(bodyNode_);
body_.zipfile_ := z; body_.zipfile_ := z;
body_.document_ := self; body_.document_ := self;
bookmarkid_ := -1; bookmarkid_ := -1;
@@ -20,6 +21,11 @@ Type docxDocument = Class
return body_; return body_;
End; End;
Function AddNameSpace(prefix, uri);
Begin
root_.SetAttribute('xmlns:' + prefix, uri);
End;
Function GetBookMarkID(); Function GetBookMarkID();
Begin Begin
if bookmarkid_ < 0 then Begin if bookmarkid_ < 0 then Begin
@@ -29,7 +35,7 @@ Type docxDocument = Class
markStart := p.FirstChildElement('w:bookmarkStart'); markStart := p.FirstChildElement('w:bookmarkStart');
if ifObj(markStart) then Begin if ifObj(markStart) then Begin
id := markStart.GetAttribute('w:id'); id := markStart.GetAttribute('w:id');
if ifstring(id) then Begin if ifstring(id) and id <> '' then Begin
iVal := strtoint(id); iVal := strtoint(id);
if bookmarkid_ <= iVal then if bookmarkid_ <= iVal then
bookmarkid_ := iVal + 1; bookmarkid_ := iVal + 1;
+1 -1
View File
@@ -65,7 +65,7 @@ Type xlsxChart = Class(TSChart)
node := drawingXmlObj_.FirstChild('xdr:wsDr').InsertEndChild(o.Marshal()); node := drawingXmlObj_.FirstChild('xdr:wsDr').InsertEndChild(o.Marshal());
End; End;
private private
excel_; [weakref]excel_;
drawingRID_:integer; drawingRID_:integer;
chartId_; chartId_;
chartRid_; chartRid_;
+6 -2
View File
@@ -148,7 +148,11 @@ xmlns:v="urn:schemas-microsoft-com:vml">
comment += r_node.FirstChildElement('t').GetText(); comment += r_node.FirstChildElement('t').GetText();
r_node := r_node.NextElement(); r_node := r_node.NextElement();
end end
if length(comment) > 1 then comment := comment[2:]; 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
return array(author, comment); return array(author, comment);
End; End;
@@ -180,6 +184,7 @@ private
End; End;
End; End;
[weakref]excel_;//TSXlsxFile对象
authors_; authors_;
commentId_; commentId_;
commentFileName_:string;//'../comments/comment1.xml' commentFileName_:string;//'../comments/comment1.xml'
@@ -187,7 +192,6 @@ private
rId_:string; rId_:string;
sheetName_:string;//sheet名称 sheetName_:string;//sheet名称
sheetFileName_;//xl/worksheets/sheetN.xml sheetFileName_;//xl/worksheets/sheetN.xml
excel_;//TSExcelFile对象
commentXmlFile_;//XmlFile对象 commentXmlFile_;//XmlFile对象
commentObjs_; commentObjs_;
End; End;
+1 -1
View File
@@ -45,5 +45,5 @@ Type xlsxDocProps = Class
End; End;
private private
excel_; //TSExcelFile对象 [weakref]excel_; //TSExcelFile对象
End; End;
@@ -33,6 +33,6 @@ Type xlsxHeaderFooter = Class
private private
sheet_; //XmlSheet对象 sheet_; //XmlSheet对象
file_; //TSExcelFile对象 [weakref]file_; //TSExcelFile对象
xmlFile_; //sheet对应的xml对象 xmlFile_; //sheet对应的xml对象
End; End;
+1 -1
View File
@@ -92,7 +92,7 @@ Type xlsxHyperLink = Class
End; End;
private private
file_; //TSExcelFile对象 [weakref]file_; //TSExcelFile对象
xmlFile_;//XmlFile对象 xmlFile_;//XmlFile对象
sheetName_; sheetName_;
+1 -1
View File
@@ -169,6 +169,6 @@ private
private private
sheetName_:string; //sheet名称 sheetName_:string; //sheet名称
excel_;//TSExcelFile对象 [weakref]excel_;//TSExcelFile对象
zipfile_; zipfile_;
End; End;
@@ -38,7 +38,7 @@ Type xlsxPageLayout = Class
End; End;
private private
file_; //TSExcelFile对象 [weakref]file_; //TSExcelFile对象
sheet_;//XmlSheet对象 sheet_;//XmlSheet对象
xmlFile_; //sheet对应的xml对象 xmlFile_; //sheet对应的xml对象
End; End;
+1 -1
View File
@@ -113,7 +113,7 @@ private
private private
sheetName_:string; //sheet名称 sheetName_:string; //sheet名称
excel_;//TSExcelFile对象 [weakref]excel_;//TSExcelFile对象
zipfile_; zipfile_;
drawingXmlObj_; drawingXmlObj_;
End; End;
+1 -1
View File
@@ -95,7 +95,7 @@ Type xlsxSheetView = Class
End; End;
private private
file_; //TSExcelFile对象 [weakref]file_; //TSExcelFile对象
sheet_;//XmlSheet对象 sheet_;//XmlSheet对象
xmlFile_; //sheet对应的xml对象 xmlFile_; //sheet对应的xml对象
End; End;
+126 -22
View File
@@ -1,15 +1,29 @@
Type xlsxStyles = Class Type xlsxStyles = Class
Function Create(sheetobj, file); Function Create(file);
Begin Begin
sheet_ := sheetobj;
file_ := file; file_ := file;
styleXmlFile_ := file_.WorkBook().GetXmlFileObj('xl/styles.xml'); style_xml_file_ := file_.WorkBook().GetXmlFileObj('xl/styles.xml');
style_ := array();
Init();
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 End
Function GetStyleId(style);overload; Function GetStyleId(style);overload;
Begin Begin
node := styleXmlFile_.FirstChildElement('styleSheet'); node := style_xml_file_.FirstChildElement('styleSheet');
font_id := insertNode(node, 'fonts', style.Font); font_id := insertNode(node, 'fonts', style.Font);
border_id := insertNode(node, 'borders', style.Border); border_id := insertNode(node, 'borders', style.Border);
fill_id := insertNode(node, 'fills', style.Fill); fill_id := insertNode(node, 'fills', style.Fill);
@@ -40,15 +54,16 @@ Type xlsxStyles = Class
node := node.FirstChildElement('cellXfs'); node := node.FirstChildElement('cellXfs');
count := node.GetAttribute('count'); count := node.GetAttribute('count');
node.InsertEndChild(xf.Marshal());
node.SetAttribute('count', strtoint(count) + 1); node.SetAttribute('count', strtoint(count) + 1);
xf_node := node.InsertEndChild(xf.Marshal());
style_[count] := xf_node;
return count; return count;
End; End;
Function GetStyleId(newStyle, oldStyleId);overload; Function GetStyleId(newStyle, oldStyleId);overload;
Begin Begin
if oldStyleId = '' then return nil; if oldStyleId = '' then return nil;
style_node := styleXmlFile_.FirstChildElement('styleSheet'); style_node := style_xml_file_.FirstChildElement('styleSheet');
cellXfs_node := style_node.FirstChildElement('cellXfs'); cellXfs_node := style_node.FirstChildElement('cellXfs');
count := strtoint(cellXfs_node.GetAttribute('count')); count := strtoint(cellXfs_node.GetAttribute('count'));
Id := strtoint(oldStyleId); Id := strtoint(oldStyleId);
@@ -89,19 +104,14 @@ Type xlsxStyles = Class
xf_node.SetAttribute('applyProtection', 1); xf_node.SetAttribute('applyProtection', 1);
end end
style_[inttostr(count)] := xf_node;
return inttostr(count); return inttostr(count);
End; End;
Function GetStyle(styleId); Function GetStyle(styleId);
Begin Begin
if styleId = '' then return nil; xf := style_[styleId];
style_node := styleXmlFile_.FirstChildElement('styleSheet'); if not ifObj(xf) then return nil;
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(); attrs := xf.Attributes();
numfmt_id := trystrtoint(attrs['numFmtId'], r) ? r : 0; numfmt_id := trystrtoint(attrs['numFmtId'], r) ? r : 0;
font_id := trystrtoint(attrs['fontId'], r) ? r : 0; font_id := trystrtoint(attrs['fontId'], r) ? r : 0;
@@ -110,6 +120,7 @@ Type xlsxStyles = Class
alignment := xf.FirstChildElement('alignment'); alignment := xf.FirstChildElement('alignment');
protection := xf.FirstChildElement('protection'); protection := xf.FirstChildElement('protection');
style_node := style_xml_file_.FirstChildElement('styleSheet');
style := TOfficeObj('TStyle'); style := TOfficeObj('TStyle');
numFmts_node := style_node.FirstChildElement('numFmts'); numFmts_node := style_node.FirstChildElement('numFmts');
fonts_node := style_node.FirstChildElement('fonts'); fonts_node := style_node.FirstChildElement('fonts');
@@ -127,12 +138,51 @@ Type xlsxStyles = Class
return style; return style;
End; End;
class Function NewObject(sheetname, file); Function GetNumFmtId(styleId);
Begin Begin
o := file.WorkBook().GetSheetObj(sheetname); node := style_[styleId];
if not ifObj(o) then return 0; if ifnil(node) then return '0';
styles := new xlsxStyles(o, file); numfmt := node.GetAttribute("numFmtId");
return styles; 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
End; End;
private private
@@ -286,7 +336,61 @@ private
End End
private private
sheet_; //XmlSheet对象 [weakref]file_; //TSXlsxFile对象
file_; //TSExcelFile对象 style_xml_file_;
styleXmlFile_; //xmlFile对象 style_; // 缓存style
End; 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 private
sheetName_:string; //sheet名称 sheetName_:string; //sheet名称
excel_;//TSExcelFile对象 [weakref]excel_;//TSExcelFile对象
zipfile_; zipfile_;
tableXmlFile_; tableXmlFile_;
End; End;
+224 -183
View File
@@ -40,7 +40,7 @@ Type xlsxWorkBook = Class
//workbook.Print(); //workbook.Print();
//workbook.Dump(); //workbook.Dump();
//echo tostn(workbook.dom()); //echo tostn(workbook.dom());
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement(); node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('sheet');
while ifObj(node) do Begin while ifObj(node) do Begin
name := node.GetAttribute("name"); name := node.GetAttribute("name");
sheetNames_[sheetsCount_]['name'] := name; sheetNames_[sheetsCount_]['name'] := name;
@@ -50,7 +50,7 @@ Type xlsxWorkBook = Class
sheetNames_[sheetsCount_]['file'] := 'xl/' + rids[ rid ]; sheetNames_[sheetsCount_]['file'] := 'xl/' + rids[ rid ];
sheetIndexMap_[ LowerCase(name) ] := sheetsCount_; sheetIndexMap_[ LowerCase(name) ] := sheetsCount_;
sheetsCount_ ++; sheetsCount_ ++;
node := node.NextElement(); node := node.NextElement('sheet');
End; End;
files := zipfile_.Files(); files := zipfile_.Files();
@@ -108,7 +108,39 @@ Type xlsxWorkBook = Class
Function SetCellValue(sheet, axis, val, opt); Function SetCellValue(sheet, axis, val, opt);
Begin Begin
o := GetSheetObj(sheet); o := GetSheetObj(sheet);
if ifObj(o) then return o.SetCellValue(axis, val, opt); 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
End; End;
Function GetCellRichText(sheet, axis); Function GetCellRichText(sheet, axis);
@@ -146,7 +178,7 @@ Type xlsxWorkBook = Class
data := o.Import(topLeft, bottomRight, includeHeader, includeIndex, forceSingle); data := o.Import(topLeft, bottomRight, includeHeader, includeIndex, forceSingle);
if not TOfficeApi().IsUtf8() then if not TOfficeApi().IsUtf8() then
begin begin
fields := fieldnames(data); fields := mcols(data, 1);
if ifarray(fields) and length(fields) then if ifarray(fields) and length(fields) then
begin begin
map := array(); map := array();
@@ -166,62 +198,142 @@ Type xlsxWorkBook = Class
Begin Begin
lname := LowerCase(sheet); lname := LowerCase(sheet);
if ifint(sheetIndexMap_[ lname ]) then return 'The sheet already exists.'; if ifint(sheetIndexMap_[ lname ]) then return 'The sheet already exists.';
rid := getWorkbookRelsRid();
fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
sheetId := addSheetN(fname);
rid := setWorkbookRels();
setWorkbookSheet(sheet, sheetId, rid);
setDocPropsApp(sheet);
setContentTypes(fname);
sheetNames_[sheetsCount_]['name'] := sheet;
sheetNames_[sheetsCount_]['sheetId'] := sheetId;
sheetNames_[sheetsCount_]['rid'] := rid;
sheetNames_[sheetsCount_]['file'] := fname;
sheetIndexMap_[ lname ] := sheetsCount_;
sheetsCount_ ++;
SetDefaultSheet(sheet);
End;
Function addSheetN(fname);
Begin
//添加文件xl/worksheets/sheetN.xml //添加文件xl/worksheets/sheetN.xml
sheetId := vselect maxof(['sheetId']) from sheetNames_ end; sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
sheetId := integer(sheetId) + 1; sheetId := integer(sheetId) + 1;
fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1')); zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1'));
return sheetId;
End;
Function setWorkbookRels();
Begin
rid := getWorkbookRelsRid();
//设置 workbook.xml.rels //设置 workbook.xml.rels
workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels'); workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship'); rels := workbook_rels.FirstChildElement('Relationships').InsertEndChild('element', 'Relationship');
rels.SetAttribute('Target', getTarget( sheetsCount_ + 1)); rels.SetAttribute('Target', getTarget( sheetsCount_ + 1));
rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet')); rels.SetAttribute('Type', class(TSXml).GetTemplate('RelationshipWorkSheet'));
rels.SetAttribute('Id', rid); rels.SetAttribute('Id', rid);
//workbook_rels.Print; return rid;
End;
Function setWorkbookSheet(sheetName, sheetId, rid);
Begin
//设置 xl/workbook.xml //设置 xl/workbook.xml
workbook := GetXmlFileObj('xl/workbook.xml'); workbook := GetXmlFileObj('xl/workbook.xml');
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet'); node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
node.SetAttribute('name', sheet); node.SetAttribute('name', sheetName);
node.SetAttribute('sheetId', sheetId); node.SetAttribute('sheetId', sheetId);
node.SetAttribute('r:id', rid); node.SetAttribute('r:id', rid);
//workbook.Print(); End;
Function setDocPropsApp(sheet);
Begin
//设置docProps/app.xml //设置docProps/app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app')); app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
//app.Print(); properties_node := app.FirstChildElement('Properties');
node := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts'); // HeadingPairs
if not ifObj(node) then Begin variant_node := class(TSXml).GetNode(properties_node, 'HeadingPairs/vt:vector/vt:variant');
node := app.FirstChildElement('Properties').InsertEndChild('element','TitlesOfParts'); while ifObj(variant_node) do
End; begin
vector := node.FirstChildElement('vt:vector'); vt_node := variant_node.FirstChildElement('vt:i4');
if not ifObj(vector) then Begin if ifObj(vt_node) then
vector := node.InsertEndChild('element', 'vt:vector'); begin
vector.SetAttribute('baseType', 'lpstr'); vt_node.SetValue(length(sheetNames_) + 1);
vector.SetAttribute('size', length(sheetNames_) + 1); break;
for i:=0 to length(sheetNames_)-1 do Begin end
vector.InsertEndChild('element', 'vt:lpstr', sheetNames_[i]['name']); variant_node := variant_node.NextElement();
End; end
End
else // TitlesOfParts
vector := class(TSXml).GetNode(properties_node, 'TitlesOfParts/vt:vector');
vector.SetAttribute('size', length(sheetNames_) + 1); vector.SetAttribute('size', length(sheetNames_) + 1);
vector.InsertEndChild('element', 'vt:lpstr', sheet); vector.InsertEndChild('element', 'vt:lpstr', sheet);
//app.Print(); End;
Function setContentTypes(fname);
Begin
//设置[Content_Types].xml //设置[Content_Types].xml
content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types')); content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType')); class(TSXml).AddOverrideContentType(content_xml, '/' + fname, class(TSXml).GetTemplate('sheetContentType'));
End;
sheetNames_[sheetsCount_]['name'] := sheet; Function insertWorkbookSheet(targetSheetName, newSheetName, sheetId, rId, direction);
sheetNames_[sheetsCount_]['sheetId'] := sheetId; Begin
sheetNames_[sheetsCount_]['rid'] := rid; workbook := GetXmlFileObj('xl/workbook.xml');
sheetNames_[sheetsCount_]['file'] := fname; node := class(TSXml).GetNode(workbook, 'workbook/sheets');
sheetIndexMap_[ LowerCase(sheet) ] := sheetsCount_; sheet_node := node.FirstChildElement('sheet');
sheetsCount_ ++; while ifObj(sheet_node) do
SetDefaultSheet(sheet); 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; End;
Function NewSheet(sourceSheet, destSheet);overload; Function NewSheet(sourceSheet, destSheet);overload;
@@ -231,60 +343,12 @@ Type xlsxWorkBook = Class
sname := LowerCase(sourceSheet); sname := LowerCase(sourceSheet);
if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists'; 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'; fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1')); sheetId := addSheetN(fname);
rid := setWorkbookRels();
//设置 workbook.xml.rels insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'after');
rid := getWorkbookRelsRid(); insertDocPropsApp(sourceSheet, destSheet, 'after');
workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels'); setContentTypes(fname);
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]; ind := sheetIndexMap_[sname];
for i:=sheetsCount_ downto ind+2 do for i:=sheetsCount_ downto ind+2 do
@@ -311,66 +375,12 @@ Type xlsxWorkBook = Class
sname := LowerCase(sourceSheet); sname := LowerCase(sourceSheet);
if not ifint(sheetIndexMap_[ sname ]) then return 'sourceSheet does not exists'; 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'; fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
zipfile_.Add(fname, class(TSXml).XmlHeader() + class(TSXml).GetTemplate('sheet1')); sheetId := addSheetN(fname);
rid := setWorkbookRels();
//设置 workbook.xml.rels insertWorkbookSheet(sourceSheet, destSheet, sheetId, rid, 'before');
rid := getWorkbookRelsRid(); insertDocPropsApp(sourceSheet, destSheet, 'before');
workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels'); setContentTypes(fname);
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]; ind := sheetIndexMap_[sname];
for i:=sheetsCount_ downto ind+1 do for i:=sheetsCount_ downto ind+1 do
@@ -409,7 +419,8 @@ Type xlsxWorkBook = Class
for i:=0 to length(sheetNames_)-1 do Begin for i:=0 to length(sheetNames_)-1 do Begin
sheetNames_[i]['rid'] := 'rId' + inttostr(i + 1); //重置rId sheetNames_[i]['rid'] := 'rId' + inttostr(i + 1); //重置rId
oldname := sheetNames_[i]['file']; oldname := sheetNames_[i]['file'];
newname := sheetPrefix_ + inttostr(i + 1) + '.xml'; fdir := ExtractFileDir(oldname);
newname := fdir + "/" + "sheet" + inttostr(i + 1) + '.xml';
sheetNames_[i]['file'] := newname; sheetNames_[i]['file'] := newname;
if oldname <> newname then Begin if oldname <> newname then Begin
n := vselect thisrowindex from files where ['FileName'] = oldname end; n := vselect thisrowindex from files where ['FileName'] = oldname end;
@@ -512,15 +523,19 @@ Type xlsxWorkBook = Class
Function CopySheet(sourceSheet, destSheet); Function CopySheet(sourceSheet, destSheet);
Begin Begin
ind := sheetIndexMap_[ LowerCase(destSheet) ];
if ifint(ind) then return destSheet $ ' already exists.';
ind := sheetIndexMap_[ LowerCase(sourceSheet) ]; ind := sheetIndexMap_[ LowerCase(sourceSheet) ];
if not ifint(ind) then return sourceSheet $ ' does not exists.'; if not ifint(ind) then return sourceSheet $ ' does not exists.';
dest_ind := sheetIndexMap_[ LowerCase(destSheet) ];
//copy sheet //copy sheet
sheetId := vselect maxof(['sheetId']) from sheetNames_ end; if ifint(dest_ind) then
sheetId := integer(sheetId) + 1; begin
fname := sheetNames_[dest_ind]['file'];
zipfile_.Remove(fname);
end
else begin
fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml'; fname := sheetPrefix_ $ inttostr(sheetsCount_ + 1) $ '.xml';
end
sheet := GetSheetXmlFile(sourceSheet); sheet := GetSheetXmlFile(sourceSheet);
zipfile_.Add(fname, sheet.Data()); zipfile_.Add(fname, sheet.Data());
xml_file := GetXmlFileObj(fname); xml_file := GetXmlFileObj(fname);
@@ -549,7 +564,14 @@ Type xlsxWorkBook = Class
sheet_rels := GetSheetRelsFile(sourceSheet); sheet_rels := GetSheetRelsFile(sourceSheet);
if ifObj(sheet_rels) then if ifObj(sheet_rels) then
begin begin
if ifint(dest_ind) then
begin
rels_name := 'xl/worksheets/_rels/' + ExtractFileName(sheetNames_[dest_ind]['file']) + '.rels';
zipfile_.Remove(rels_name);
end
else begin
rels_name := 'xl/worksheets/_rels/' + 'sheet' + inttostr(sheetsCount_ + 1) + '.xml.rels'; rels_name := 'xl/worksheets/_rels/' + 'sheet' + inttostr(sheetsCount_ + 1) + '.xml.rels';
end
zipfile_.Add(rels_name, sheet_rels.Data()); zipfile_.Add(rels_name, sheet_rels.Data());
rels_file := GetXmlFileObj(rels_name); rels_file := GetXmlFileObj(rels_name);
relationship := rels_file.FirstChildElement('Relationships').FirstChildElement('Relationship'); relationship := rels_file.FirstChildElement('Relationships').FirstChildElement('Relationship');
@@ -576,6 +598,8 @@ Type xlsxWorkBook = Class
end end
end end
if ifint(dest_ind) then return;
//workbook.xml.rels //workbook.xml.rels
rid := getWorkbookRelsRid(); rid := getWorkbookRelsRid();
workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels'); workbook_rels := GetXmlFileObj('xl/_rels/workbook.xml.rels');
@@ -585,6 +609,8 @@ Type xlsxWorkBook = Class
rels.SetAttribute('Id', rid); rels.SetAttribute('Id', rid);
//xl/workbook.xml //xl/workbook.xml
sheetId := vselect maxof(['sheetId']) from sheetNames_ end;
sheetId := integer(sheetId) + 1;
workbook := GetXmlFileObj('xl/workbook.xml'); workbook := GetXmlFileObj('xl/workbook.xml');
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet'); node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').InsertEndChild('element','sheet');
node.SetAttribute('name', destSheet); node.SetAttribute('name', destSheet);
@@ -593,9 +619,17 @@ Type xlsxWorkBook = Class
//app.xml //app.xml
app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app')); app := GetXmlFileObj(class(TSXml).GetFileName('docProps_app'));
vector := app.FirstChildElement('Properties').FirstChildElement('TitlesOfParts').FirstChildElement('vt:vector'); prop := app.FirstChildElement('Properties');
vector.SetAttribute('size', sheetId); vector := prop.FirstChildElement('TitlesOfParts').FirstChildElement('vt:vector');
vector.SetAttribute('size', sheetsCount_+1);
vector.InsertEndChild('element', 'vt:lpstr', destSheet); 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_Types].xml
content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types')); content_xml := GetXmlFileObj(class(TSXml).GetFileName('Content_Types'));
@@ -726,23 +760,25 @@ Type xlsxWorkBook = Class
Function SetSheetVisible(sheet, visible); Function SetSheetVisible(sheet, visible);
Begin Begin
workbook := GetXmlFileObj('xl/workbook.xml'); workbook := GetXmlFileObj('xl/workbook.xml');
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('sheet'); node := class(TSXml).GetNode(workbook, 'workbook/sheets/sheet');
hidden := visible = 1 ? 0 : 1; if GetSheetsCount() = 1 then return "Only one sheet, can't set visible.";
default_sheet_name := GetDefaultSheet(); sheet := lowercase(sheet);
if default_sheet_name = sheet then default_sheet_name := lowercase(GetDefaultSheet());
if default_sheet_name = sheet and not visible then
begin begin
ind := sheetIndexMap_[LowerCase(default_sheet_name)]; ind := sheetIndexMap_[LowerCase(default_sheet_name)];
if ind = 0 then default_name := sheetNames_[ind + 1]['name']; default_name := ind = 0 ? sheetNames_[1]['name'] : sheetNames_[ind - 1]['name'];
else default_name := sheetNames_[ind - 1]['name'];
SetDefaultSheet(default_name); SetDefaultSheet(default_name);
end end
if visible then SetDefaultSheet(sheet);
state := visible ? "nohidden" : "hidden";
while ifObj(node) do while ifObj(node) do
Begin Begin
name := node.GetAttribute('name'); name := lowercase(node.GetAttribute('name'));
if name = sheet then if name = sheet then
Begin Begin
if hidden then node.SetAttribute('state', 'hidden'); node.SetAttribute('state', state);
else node.DeleteAttribute('state'); break;
End End
node := node.NextElement(); node := node.NextElement();
End End
@@ -753,11 +789,12 @@ Type xlsxWorkBook = Class
Begin Begin
sheet_file := GetSheetXmlFile(sheet); sheet_file := GetSheetXmlFile(sheet);
if not ifObj(sheet_file) then return 0; if not ifObj(sheet_file) then return 0;
sheet := lowercase(sheet);
workbook := GetXmlFileObj('xl/workbook.xml'); workbook := GetXmlFileObj('xl/workbook.xml');
node := workbook.FirstChildElement('workbook').FirstChildElement('sheets').FirstChildElement('sheet'); node := class(TSXml).GetNode(workbook, 'workbook/sheets/sheet');
while ifObj(node) do while ifObj(node) do
Begin Begin
name := node.GetAttribute('name'); name := lowercase(node.GetAttribute('name'));
if name = sheet then if name = sheet then
Begin Begin
val := node.GetAttribute('state'); val := node.GetAttribute('state');
@@ -863,6 +900,11 @@ Type xlsxWorkBook = Class
Function SetRowHeight(sheet, row, height); Function SetRowHeight(sheet, row, height);
Begin Begin
obj := GetSheetObj(sheet); 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)); obj.SetAttribute(row, array('ht': height, "customHeight": 1));
End End
@@ -952,7 +994,7 @@ Type xlsxWorkBook = Class
col_node := work_node.FirstChildElement('cols'); col_node := work_node.FirstChildElement('cols');
default_width := work_node.FirstChildElement('sheetFormatPr').GetAttribute('defaultColWidth'); default_width := work_node.FirstChildElement('sheetFormatPr').GetAttribute('defaultColWidth');
default_width := trystrtofloat(default_width, r) ? r: -1; default_width := trystrtofloat(default_width, r) ? r: -1;
if not ifObj(col_node) then return array(0, default_width); if not ifObj(col_node) then return nil;
col_number := ColumnNameToNumber(col)[1]; col_number := ColumnNameToNumber(col)[1];
node := col_node.FirstChildElement('col'); node := col_node.FirstChildElement('col');
@@ -1104,29 +1146,17 @@ Type xlsxWorkBook = Class
node := sheet_node.InsertFirstChild('element', 'sheetView'); node := sheet_node.InsertFirstChild('element', 'sheetView');
node.SetAttribute('workbookViewId', 0); node.SetAttribute('workbookViewId', 0);
end end
if name = sheet then if lowercase(name) = lowercase(sheet) then
Begin Begin
node.SetAttribute('tabselected', 1); node.SetAttribute('tabSelected', 1);
workbook := GetXmlFileObj('xl/workbook.xml'); workbook := GetXmlFileObj('xl/workbook.xml');
workbook_node := workbook.FirstChildElement('workbook'); view_node := class(TSXml).GetNode(workbook, 'workbook/bookViews/workbookView');
book_view_node := workbook_node.FirstChildElement('bookViews');
if not ifObj(book_view_node) then
begin
prev_node := class(TSXml).GetWorkBookPrevNode(workbook_node, 'bookViews');
if not ifObj(prev_node) then book_view_node := workbook_node.InsertFirstChild('element', 'bookViews');
else book_view_node := workbook_node.InsertAfterChild(prev_node, 'element', 'bookViews');
end;
view_node := book_view_node.FirstChildElement('workbookView');
if not ifObj(view_node) then
begin
view_node := book_view_node.InsertFirstChild('element', 'workbookView');
end
view_node.SetAttribute('activeTab', ind); view_node.SetAttribute('activeTab', ind);
End End
else else
begin begin
node := root_node.FirstChildElement('sheetViews').FirstChildElement('sheetView'); node := root_node.FirstChildElement('sheetViews').FirstChildElement('sheetView');
node.SetAttribute('tabselected', 0); node.SetAttribute('tabSelected', 0);
end end
end end
End End
@@ -1464,7 +1494,7 @@ Type xlsxWorkBook = Class
if col >= min and col <= max then if col >= min and col <= max then
Begin Begin
level_val := node.GetAttribute('outlineLevel'); level_val := node.GetAttribute('outlineLevel');
if strtoint(level_val) = level then return; if trystrtoint(level_val, r) and r = level then return;
else Begin else Begin
if col = min and col = max then node.SetAttribute('outlineLevel', level); if col = min and col = max then node.SetAttribute('outlineLevel', level);
else begin else begin
@@ -1580,9 +1610,9 @@ private
Function copyFile(obj, target, content); Function copyFile(obj, target, content);
Begin Begin
file_name := ReplaceStr(target, '..', 'xl'); file_name := ReplaceStr(target, '..', 'xl');
counts := GetFilesCount(ReplaceStrByReg(file_name, "\\d+.*ml", '')) + 1; counts := GetFilesCount(ReplaceStrUsingReg(file_name, "\\d+.*ml", '')) + 1;
xml := GetXmlFileObj(file_name); xml := GetXmlFileObj(file_name);
new_file_postfix := ReplaceStrByReg(file_name[3:], "\\d+", inttostr(counts)); new_file_postfix := ReplaceStrUsingReg(file_name[3:], "\\d+", inttostr(counts));
zipfile_.Add('xl' + new_file_postfix, xml.Data()); zipfile_.Add('xl' + new_file_postfix, xml.Data());
obj.SetAttribute('Target', '..' + new_file_postfix); obj.SetAttribute('Target', '..' + new_file_postfix);
@@ -1594,11 +1624,22 @@ private
return array(counts, 'xl' + new_file_postfix); return array(counts, 'xl' + new_file_postfix);
End; 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; sheetsCount_:integer;
sheetNames_; sheetNames_;
sheetIndexMap_; sheetIndexMap_;
sheetPrefix_:string; sheetPrefix_:string;
zipfile_; [weakref]zipfile_;
xmlFileObjMap_; //XmlFile对象存储 xmlFileObjMap_; //XmlFile对象存储
sheetObjMap_; //XmlSheet对象存储 sheetObjMap_; //XmlSheet对象存储
End; End;
+438
View File
@@ -1,5 +1,443 @@
# 更新日志 # 更新日志
## 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
#### excel
1. 修复公式中文转码问题
## 2023-11-15
### V1.5.1
1. 支持识别图片中含有`0xffc2`段的 jpg 格式的大小
## 2023-11-14
### V1.5.0
#### word
1. 新增对脚注的支持,方法`TSDocxFile.FootnotesObject().Add`
2. `TSDocxFile.InsertFile`支持脚注插入
### excel
1. 升级`TSXlsxFile.CopySheet`
## 2023-10-16
### V1.4.9
#### word
1. 新增`TSDocxFile.InsertFile`重载方法
2. 新增`Table.SetStyle`方法
## 2023-9-27
### V1.4.8
#### word
1. 支持`Inserttable`时候加入对单元格样式的设置
2. 新增`TSDocxFile.LastParagraph`方法
3. 新增`TParagraph.PrevParagraph`方法
4. 性能优化
## 2023-9-22
### V1.4.7
#### word
1. 修复项目符号属于样式时,生成目录无法识别出项目符号问题
## 2023-9-19
### V1.4.6
#### word
1. 修复表格存在合并单元格,生成目录报错问题
2. 修复单元格添加 run 对象,设置样式不生效问题
3. 修复插入 word 后,中文样式重复问题
### excel
🔔 重要升级:`TSExcelFile`更名为`TSXlsxFile`,建议更改,旧名字现阶段仍可使用
1. 整理`NewSheet``SetSheetVisible`代码
#### excel
1. 修复`NewSheet`excel 打开遇到提示错误问题
## 2023-8-30
### V1.4.5
#### word
1. 支持生成目录后返回目录对象的所有段落内容`目录对象.Paragraphs()`
#### excel
1. 修复`NewSheet`excel 打开遇到提示错误问题
## 2023-8-28 ## 2023-8-28
### V1.4.4 ### V1.4.4