From 522e52ed461c9c2db7bd17d74371ef8cd909c19f Mon Sep 17 00:00:00 2001 From: csh Date: Wed, 31 Jan 2024 09:52:10 +0800 Subject: [PATCH] v1.5.8 --- funcext/TSOffice/TOfficeObj.tsf | 2 +- funcext/TSOffice/TSDocxFile.tsf | 2 +- funcext/TSOffice/TSXlsxFile.tsf | 59 ++++----- funcext/TSOffice/worksheet/xlsxStyles.tsf | 152 ++++++++++++++++++---- 更新日志.md | 14 +- 5 files changed, 171 insertions(+), 58 deletions(-) diff --git a/funcext/TSOffice/TOfficeObj.tsf b/funcext/TSOffice/TOfficeObj.tsf index 5a7c5ed..63935ab 100644 --- a/funcext/TSOffice/TOfficeObj.tsf +++ b/funcext/TSOffice/TOfficeObj.tsf @@ -1,4 +1,4 @@ -// Version 1.5.7 +// Version 1.5.8 Function TOfficeObj(n); Begin case lowercase(n) of diff --git a/funcext/TSOffice/TSDocxFile.tsf b/funcext/TSOffice/TSDocxFile.tsf index 5936089..7779f62 100644 --- a/funcext/TSOffice/TSDocxFile.tsf +++ b/funcext/TSOffice/TSDocxFile.tsf @@ -1,4 +1,4 @@ -// Version 1.5.7 +// Version 1.5.8 Type TSDocxFile = Class ///Version: V1.0 2022-09-20 ///适用于 Microsoft Word docx格式文件 diff --git a/funcext/TSOffice/TSXlsxFile.tsf b/funcext/TSOffice/TSXlsxFile.tsf index f284a89..ddc2e27 100644 --- a/funcext/TSOffice/TSXlsxFile.tsf +++ b/funcext/TSOffice/TSXlsxFile.tsf @@ -1,4 +1,4 @@ -// Version 1.5.7 +// Version 1.5.8 Type TSXlsxFile = Class ///Version: V1.0 2022-08-08 ///适用于 Microsoft Excel? 2007 及以上版本创建的电子表格文档。支持 XLSX / XLSM / XLTM / XLTX 等多种文档格式。 @@ -8,7 +8,7 @@ Type TSXlsxFile = Class ///缺省构造函数 Function Create(); overload; Begin - init(); + Init(); End; ///构造函数,打开已经存在的excel文件 @@ -16,7 +16,7 @@ Type TSXlsxFile = Class ///fname: string,文件名 Function Create(alias, fname); overload; Begin - init(); + Init(); OpenFile(alias, fname, nil); End; @@ -26,17 +26,12 @@ 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; @@ -51,24 +46,17 @@ Type TSXlsxFile = Class 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, fname, passwd); - if err=0 then Begin - workbook_ := new xlsxWorkBook(zipfile_); - workbook_.Load(); - End; + if err=0 then InitVars(); return array(err, errmsg); End; ///新建excel文件 - ///返回: [err, info] + ///返回: [err, errmsg] Function NewFile(); Begin def := TOfficeTemplate('default.xlsx', true); [err, errmsg] := zipfile_.LoadFromMem(def); - if err = 0 then - begin - workbook_ := new xlsxWorkBook(zipfile_); - workbook_.Load(); - end + if err = 0 then InitVars(); return array(err, errmsg); End; @@ -91,11 +79,7 @@ Type TSXlsxFile = Class Function LoadFromMem(data); Begin [err, errmsg] := zipfile_.LoadFromMem(data); - if err = 0 then - begin - workbook_ := new xlsxWorkBook(zipfile_); - workbook_.Load(); - end + if err = 0 then InitVars(); return array(err, errmsg); End; @@ -128,9 +112,7 @@ 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; @@ -238,6 +220,8 @@ Type TSXlsxFile = Class Begin sheet_name := class(TSXml).CurCodePageToUtf8(sheet); value := class(TSXml).CurCodePageToUtf8(val); + // 处理cell设定的样式 + SetCellType(sheet, axis, value, opt); return workbook_.SetCellValue(sheet_name, axis, value, opt); End; @@ -1012,6 +996,22 @@ 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 ifarray(opt) then opt := array(); + if not ifnil(opt['t']) then return; + styleid := 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); @@ -1020,12 +1020,8 @@ 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': @@ -1052,5 +1048,6 @@ private zipfile_; //压缩文件对象 workbook_; //WorkBook对象 objMgr_; //各种对象缓存、管理 + style_; End; diff --git a/funcext/TSOffice/worksheet/xlsxStyles.tsf b/funcext/TSOffice/worksheet/xlsxStyles.tsf index 35e29f0..7f8f16c 100644 --- a/funcext/TSOffice/worksheet/xlsxStyles.tsf +++ b/funcext/TSOffice/worksheet/xlsxStyles.tsf @@ -1,15 +1,29 @@ Type xlsxStyles = Class - Function Create(sheetobj, file); + Function Create(file); Begin - sheet_ := sheetobj; 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 + Function GetStyleId(style);overload; Begin - node := styleXmlFile_.FirstChildElement('styleSheet'); + node := style_xml_file_.FirstChildElement('styleSheet'); font_id := insertNode(node, 'fonts', style.Font); border_id := insertNode(node, 'borders', style.Border); fill_id := insertNode(node, 'fills', style.Fill); @@ -40,15 +54,16 @@ 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 := styleXmlFile_.FirstChildElement('styleSheet'); + style_node := style_xml_file_.FirstChildElement('styleSheet'); cellXfs_node := style_node.FirstChildElement('cellXfs'); count := strtoint(cellXfs_node.GetAttribute('count')); Id := strtoint(oldStyleId); @@ -89,19 +104,14 @@ Type xlsxStyles = Class xf_node.SetAttribute('applyProtection', 1); end + style_[inttostr(count)] := xf_node; return inttostr(count); End; Function GetStyle(styleId); Begin - 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); + xf := style_[styleId]; + if not ifObj(xf) then return nil; attrs := xf.Attributes(); numfmt_id := trystrtoint(attrs['numFmtId'], r) ? r : 0; font_id := trystrtoint(attrs['fontId'], r) ? r : 0; @@ -110,6 +120,7 @@ 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'); @@ -127,12 +138,51 @@ Type xlsxStyles = Class return style; End; - class Function NewObject(sheetname, file); + Function GetNumFmtId(styleId); Begin - o := file.WorkBook().GetSheetObj(sheetname); - if not ifObj(o) then return 0; - styles := new xlsxStyles(o, file); - return styles; + 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)) 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', trystrtofloat(value, r) ? r : 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 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; private @@ -286,7 +336,65 @@ private End private - sheet_; //XmlSheet对象 - file_; //TSExcelFile对象 - styleXmlFile_; //xmlFile对象 + file_; //TSXlsxFile对象 + style_xml_file_; + style_; // 缓存style End; + +// {* +// 也可参考: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.numberingformat?view=openxml-3.0.1 +// numfmtid为1表示数值格式,即显示为整数或小数,且根据系统设置或区域设置来决定小数点、千位分隔符和负数的显示方式。 +// numfmtid为2表示货币格式,即显示为带有货币符号的数值,且根据系统设置或区域设置来决定货币符号、小数点、千位分隔符和负数的显示方式。 +// numfmtid为3表示会计格式,即显示为带有货币符号和千位分隔符的数值,且根据系统设置或区域设置来决定货币符号、小数点和负数的显示方式。会计格式的特点是货币符号和数值之间有一定的空格,且小数位数固定为2。 +// numfmtid为4表示百分比格式,即显示为带有百分号的数值,且根据系统设置或区域设置来决定小数点和负数的显示方式。 +// numfmtid为5表示千位分隔符格式,即显示为带有千位分隔符的数值。 +// numfmtid为6表示负数红色格式,即显示为带有负号和红色字体的数值。 +// numfmtid为7表示千位分隔符格式,即显示为带有千位分隔符的数值,且根据系统设置或区域设置来决定小数点和负数的显示方式。 +// numfmtid为8表示负数红色格式,即显示为带有负号和红色字体的数值,且根据系统设置或区域设置来决定小数点和千位分隔符的显示方式。 +// numfmtid为9表示分数格式,即显示为分数形式的数值,且根据系统设置或区域设置来决定负数的显示方式。分数格式的特点是分子和分母都是一位数,例如1/2。 +// numfmtid为10表示指数格式,即显示为带有小数点和指数的数值。 +// numfmtid为11表示日期时间格式,即显示为年/月/日 时:分的形式。 +// numfmtid为12表示时间格式,即显示为时:分:秒的形式。 +// numfmtid为13表示时间格式,即显示为上午/下午 时:分的形式。 +// numfmtid为16表示日期格式,即显示为月/日/年的形式。 +// numfmtid为17表示日期格式,即显示为日-月-年的形式。 +// numfmtid为18表示日期格式,即显示为年-月-日的形式。 +// numfmtid为19表示日期格式,即显示为月-年的形式。 +// numfmtid为20表示日期格式,即显示为年-月的形式。 +// numfmtid为21表示日期时间格式,即显示为年/月/日 时:分:秒的形式。 +// numfmtid为22表示日期时间格式,即显示为年/月/日 上午/下午 时:分的形式。 +// numfmtid为23表示时间格式,即显示为时:分的形式,带有前导零。 +// numfmtid为24表示时间格式,即显示为时:分:秒的形式,带有前导零。 +// numfmtid为25表示时间格式,即显示为上午/下午 时:分的形式,带有前导零。 +// numfmtid为26表示时间格式,即显示为上午/下午 时:分:秒的形式,带有前导零。 +// numfmtid为27表示错误值格式,即显示为#NULL!。 +// numfmtid为28表示错误值格式,即显示为#DIV/0!。 +// numfmtid为29表示错误值格式,即显示为#VALUE!。 +// numfmtid为30表示错误值格式,即显示为#REF!。 +// numfmtid为31表示错误值格式,即显示为#NAME?。 +// numfmtid为32表示错误值格式,即显示为#NUM!。 +// numfmtid为33表示错误值格式,即显示为#N/A。 +// numfmtid为34表示错误值格式,即显示为#GETTING_DATA。 +// numfmtid为36表示货币格式,即显示为带有货币符号和负数红色的数值。 +// numfmtid为38表示会计格式,即显示为带有货币符号和负数红色的数值,且小数位数为2。 +// numfmtid为39表示会计格式,即显示为带有货币符号和负数红色的数值,且小数位数为0。 +// numfmtid为40表示会计格式,即显示为带有货币符号和负数红色的数值,且小数位数为4。 +// numfmtid为41表示货币格式,即显示为带有货币符号和千位分隔符的数值,且小数位数为2。 +// numfmtid为42表示货币格式,即显示为带有货币符号和千位分隔符的数值,且小数位数为0。 +// numfmtid为43表示货币格式,即显示为带有货币符号和千位分隔符的数值,且小数位数为4。 +// numfmtid为44表示货币格式,即显示为带有货币符号和千位分隔符的数值,且负数红色,且小数位数为2。 +// numfmtid为45表示货币格式,即显示为带有货币符号和千位分隔符的数值,且负数红色,且小数位数为0。 +// numfmtid为46表示货币格式,即显示为带有货币符号和千位分隔符的数值,且负数红色,且小数位数为4。 +// numfmtid为47表示会计格式,即显示为带有货币符号和千位分隔符的数值,且小数位数为2。 +// numfmtid为48表示会计格式,即显示为带有货币符号和千位分隔符的数值,且小数位数为0。 +// numfmtid为49表示文本格式,即显示为文本。 +// numfmtid为50表示日期格式,即显示为年/月/日的形式,带有前导零。 +// numfmtid为51表示日期格式,即显示为月/日/年的形式,带有前导零。 +// numfmtid为52表示日期格式,即显示为日-月-年的形式,带有前导零。 +// numfmtid为53表示日期格式,即显示为年-月-日的形式,带有前导零。 +// numfmtid为54表示日期格式,即显示为月-年的形式,带有前导零。 +// numfmtid为55表示日期格式,即显示为年-月的形式,带有前导零。 +// numfmtid为56表示日期时间格式,即显示为年/月/日 时:分:秒的形式,带有前导零。 +// numfmtid为57表示日期时间格式,即显示为年/月/日 上午/下午 时:分的形式,带有前导零。 +// numfmtid为58表示日期时间格式,即显示为年/月/日 上午/下午 时:分:秒的形式,带有前导零。 +// *} diff --git a/更新日志.md b/更新日志.md index e050f30..9d4fd25 100644 --- a/更新日志.md +++ b/更新日志.md @@ -1,12 +1,20 @@ # 更新日志 +## 2023-1-18 + +### V1.5.8 + +#### excel + +1. 支持写入单元格内容时判断当前单元格的数据类型,并以单元格数据类型为准 + ## 2023-1-17 ### V1.5.7 #### excel -1. 修复单元格写入浮点数数据bug,如写入0.0,excel某些版本打开会显示0.00000000000000000 +1. 修复单元格写入浮点数数据 bug,如写入 0.0,excel 某些版本打开会显示 0.00000000000000000 ## 2023-1-11 @@ -42,7 +50,7 @@ #### excel -1. 支持写入单元格时且单元格不存在时,新的单元格会沿用设置的样式ID(若有设置) +1. 支持写入单元格时且单元格不存在时,新的单元格会沿用设置的样式 ID(若有设置) ## 2023-12-1 @@ -56,7 +64,7 @@ ### V1.5.1 -1. 支持识别图片中含有`0xffc2`段的jpg格式的大小 +1. 支持识别图片中含有`0xffc2`段的 jpg 格式的大小 ## 2023-11-14