diff --git a/funcext/TSOffice/TOfficeObj.tsf b/funcext/TSOffice/TOfficeObj.tsf index 3767698..83d05d8 100644 --- a/funcext/TSOffice/TOfficeObj.tsf +++ b/funcext/TSOffice/TOfficeObj.tsf @@ -1353,6 +1353,7 @@ type TChartImpl=class(NodeInfo) ,("field":"Excel","name":"Excel","obj":Excel,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"") ,("field":"chartFileName","name":"chartFileName","obj":chartFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"") ,("field":"excelFileName","name":"excelFileName","obj":excelFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"") + ,("field":"drawingFileName","name":"drawingFileName","obj":drawingFileName,"attrEx":"","nodeType":"","attrName":"", "desc":"disable", "class":"") ) union ExtNodes; End; @@ -1395,6 +1396,7 @@ type TChartImpl=class(NodeInfo) Excel; chartFileName; excelFileName; + drawingFileName; End; /////////////////////////////////////////////////////////////// @@ -13492,6 +13494,235 @@ Type TDocumentBody = Class(DocObject) return array(length(errArr), tslFuncCount, errArr); End; + Function ExecTsTag(docx, tagName, tagObj); + Begin + //表格 + t := array(); + tslTagCount := 0; + errArr := array(); + tArr := Tables(); + for i:=0 to length(tArr)-1 do Begin + col := tArr[i].Cols(); + row := tArr[i].Rows(); + for r:= 1 to row do Begin + for c:=1 to col do Begin + cell := tArr[i].Cell(r, c); + [err, cnt, err] := cell.ExecTsTag(docx, tagName, tagObj);//递归 + tslTagCount += cnt; + errArr union= err; + End; + End; + End; + + //文本框 + ps := Paragraphs(); + for i:=0 to length(ps)-1 do Begin + boxs := ps[i].TextBoxs(); + for j:=0 to length(boxs)-1 do Begin + [err, cnt, err] := boxs[j].ExecTsTag(docx, tagName, tagObj);//递归 + if cnt then + boxs[j].Apply(); + tslTagCount += cnt; + errArr union= err; + End; + End; + + //页脚、页眉 + sArr := Sections(); + tpArr := array('default','even','first'); + for i:=0 to length(sArr)-1 do Begin + for k, name in tpArr do Begin + h := sArr[i].Header(name); + if ifObj(h) then Begin + [err, cnt, err] := h.ExecTsTag(docx, tagName, tagObj);//递归 + tslTagCount += cnt; + errArr union= err; + End; + + f := sArr[i].Footer(name); + if ifObj(f) then Begin + [err, cnt, err] := f.ExecTsTag(docx, tagName, tagObj);//递归 + tslTagCount += cnt; + errArr union= err; + End; + End; + End; + + [err, cnt, err] := ExecTsTagImpl(docx, tagName, tagObj); + tslTagCount += cnt; + errArr union= err; + return array(length(errArr), tslTagCount, errArr); + End; + + Function ExecTsTagImpl(docx, tagName, tagObj); + Begin + tslTagCount := 0; + errArr := array(); + tagArr := array(); + tArr := TextArray(); + tagAttribute := ''; + tagStr := ''; + tagStatus := '';//array('','head', 'attribute', 'tail'); + tagHead := '[' + tagName; + tagTail := '[/' + tagName; + tmp := array(); + while i < length(tArr) do Begin + txt := ''; + iStep := 1; + if ifObj(tArr[i]['rNode']) then Begin + run := new TRun(tArr[i]['rNode']); + txt := run.Text(); + if class(TSXml).IsUtf8() then + txt := UTF8ToAnsi(txt); + End; + k := 1; + wz := 1; + txtLen := length(txt); + while k <= txtLen do Begin + c := txt[k]; + if c = ']' and (tagStatus = 'head' or tagStatus = 'attribute') and tagStr = tagHead then Begin //TAG头结束标志 + tagStr := ''; + tagArr := tArr[tmp['head-begin-paragraph']:i,:]; + wz++; + tagStatus := 'tail'; + tmp['head-end-paragraph'] := i; + tmp['head-end-pos'] := k++; + tmp['head-end-wz'] := wz; + tmp['head-end-txtlen'] := txtLen; + continue; + End; + case tagStatus of + '': + if c = '[' then Begin + tagStr := '['; + tmp['head-begin-paragraph'] := i; + tmp['head-begin-pos'] := k; + tmp['head-begin-wz'] := wz; + tmp['head-begin-txtlen'] := txtLen; + tagStatus := 'head'; + tagAttribute := ''; + End; + 'head': + if c = ' ' and tagStr = tagHead then Begin //找到TAG属性 + tagStatus := 'attribute'; + End + else if length(tagStr) < length(tagHead) and lowercase(c) = lowercase(tagHead[ length(tagStr) + 1 ]) then Begin + tagStr += tagHead[ length(tagStr) + 1 ]; + End + else //继续寻找TAG开始标志 + tagStatus := ''; + 'attribute': + tagAttribute += c; + 'tail': + if c=']' and tagStr = tagTail then Begin //查找到完整标签 + ind := length(tagArr) - 1; + if tagArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tagArr[ind]['rIndex'] <> tArr[i]['rIndex'] then Begin + ind ++; + tagArr[ind] := tArr[i]; + End; + + //[/tag]后 + if k < txtLen then Begin //函数后面分割为新的w:r + tArr[i]['rNode'] := run._duplicate_r(tArr[i]['rNode']); + run._adjust_r(tArr[i]['rNode'], wz, txtLen); + iStep := 0; + End; + //前[/tag] + tagInd := length(tagArr) - i + tmp['tail-begin-paragraph'] - 1; + if tmp['tail-begin-pos'] > 1 then Begin + nNode := run._duplicate_r(tagArr[tagInd]['rNode']); + run._adjust_r(tagArr[tagInd]['rNode'], 0, tmp['tail-begin-wz'] - 1); + tagArr[tagInd]['pNode'].DeleteChild(nNode); + End + else if tmp['tail-begin-pos'] = 1 then Begin + if _remove_run(tagArr[tagInd]['pNode'], tagArr[tagInd]['rNode']) then + tagArr[tagInd]['pNode'] := nil; + tagArr[tagInd]['rNode'] := nil; + End; + //删除[/TAG]尾部 + for delI := tagInd+1 to length(tagArr)-1 do Begin + if _remove_run(tagArr[delI]['pNode'], tagArr[delI]['rNode']) then + tagArr[delI]['pNode'] := nil; + tagArr[delI]['rNode'] := nil; + End; + + //[tag]后 + firstNode := tagArr[0]['rNode']; + tagInd := length(tagArr) - i + tmp['head-end-paragraph'] - 1; + //println('===============================head-end-pos={},head-end-txtlen={}',tmp['head-end-pos'] , tmp['head-end-txtlen']); + if tmp['head-end-pos'] < tmp['head-end-txtlen'] then Begin + oldNode := tagArr[tagInd]['rNode']; + tagArr[tagInd]['rNode'] := run._duplicate_r(oldNode); + run._adjust_r(tagArr[tagInd]['rNode'], tmp['head-end-wz'] - 1, tmp['head-end-txtlen']); + End + else if tmp['head-end-pos'] = tmp['head-end-txtlen'] and tagInd then Begin + if _remove_run(tagArr[tagInd]['pNode'], tagArr[tagInd]['rNode']) then + tagArr[tagInd]['pNode'] := nil; + tagArr[tagInd]['rNode'] := nil; + End; + //前[tag] + if tmp['head-begin-pos'] > 1 then Begin + run._adjust_r(firstNode, 0, tmp['head-begin-wz'] - 1); + if tmp['head-begin-paragraph'] <> tmp['head-end-paragraph'] then + tagArr[0]['rNode'] := nil; + End + else if tmp['head-begin-pos'] = 1 then Begin + if _remove_run(tagArr[0]['pNode'], firstNode) then + tagArr[0]['pNode'] := nil; + if tmp['head-begin-paragraph'] <> tmp['head-end-paragraph'] then + tagArr[0]['rNode'] := nil; + End; + for delI := 1 to tagInd-1 do Begin + if _remove_run(tagArr[delI]['pNode'], tagArr[delI]['rNode']) then + tagArr[delI]['pNode'] := nil; + tagArr[delI]['rNode'] := nil; + End; + + //执行TAG逻辑 + r := array(); + for nI := 0 to length(tagArr)-1 do Begin + if ifObj(tagArr[nI]['rNode']) then + r[length(r)] := tagArr[nI]; + End; + tagObj.Init(tagName, attribute, r); + tagObj.Apply(); + tslTagCount++; + + tagStatus := ''; + tagArr := array(); + break; + End + else if length(tagStr) < length(tagTail) and lowercase(c) = lowercase(tagTail[ length(tagStr) + 1 ]) then Begin + tagStr += tagTail[ length(tagStr) + 1 ]; + if tagStr = '[' then Begin + tmp['tail-begin-paragraph'] := i; + tmp['tail-begin-pos'] := k; + tmp['tail-begin-wz'] := wz; + tmp['tail-begin-txtlen'] := txtLen; + End; + End + else //继续寻找TAG结束标志 + tagStr := ''; + End; + if Ord(c) > 127 then Begin + k ++; + End + k ++; + wz ++; + End; + if length(tagArr) then Begin + if i and i < length(tArr) and tArr[i]['pIndex'] <> tArr[i+1]['pIndex'] then Begin + endPos ++; + End; + ind := length(tagArr) - 1; + if tagArr[ind]['pIndex'] <> tArr[i]['pIndex'] or tagArr[ind]['rIndex'] <> tArr[i]['rIndex'] then + tagArr[ind + 1] := tArr[i]; + End; + i += iStep; + End; + return array(length(errArr), tslTagCount, errArr); + End; + Function GetHeadingListImpl(docx, posOpt, UpperHeadingLevel, LowerHeadingLevel, numIds, bHeadList); Begin r := array(); @@ -13679,6 +13910,17 @@ Type TDocumentBody = Class(DocObject) End; End; + Function _remove_run(pNode, rNode); + Begin + pNode.DeleteChild(rNode); + node := pNode.FirstChildElement('w:r'); + if not ifObj(node) then Begin + pNode.Parent().DeleteChild(pNode); + return true; + End; + return false; + End; + zipfile_; lastParagraph_; document_; diff --git a/funcext/TSOffice/TSDocxFile.tsf b/funcext/TSOffice/TSDocxFile.tsf index 03f15c0..5ed6ede 100644 --- a/funcext/TSOffice/TSDocxFile.tsf +++ b/funcext/TSOffice/TSDocxFile.tsf @@ -272,6 +272,7 @@ Type TSDocxFile = Class chart.pNode := p.node_; p.Node().InsertEndChild(o.GetInnerXml()); TOfficeApi().Set('CurrentShape', p.node_); + chart.chartFileName := 'word/charts/chart' $ o.ChartId_ $ '.xml'; return chart; End; @@ -293,6 +294,74 @@ Type TSDocxFile = Class return r; End; + ///从Excel中Copy指定的chart图到文档中指定位置 + ///excelFileName:string xlsx文件名 + ///excelSheetName:string sheetname + ///chartName:string or integer,chart图名称或当前sheet中chart图索引号 + ///Width:chart图宽度,单位cm + ///Height:chart图高度,单位cm + ///posOpt: 段落位置,0 在DOCX文件开头;-1 文件尾;N 在第N段之后;XmlNode节点对象或DocObject对象 在posOpt之后新添加图片 + ///返回TChart对象 + Function CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, posOpt); + Begin + excel := new TSExcelFile(); + [err, msg] := excel.OpenFile('', excelFileName); + if err then return nil; + [err, charts] := excel.GetCharts(excelSheetName); + if err or length(charts)=0 then return nil; + drawingObj := excel.WorkBook().GetXmlFileObj(charts[0].drawingFileName); + if not ifObj(drawingObj) then return nil; + node := drawingObj.FirstChildElement('xdr:wsDr').FirstChildElement('xdr:twoCellAnchor'); + ind := 0; + chartRid := ''; + while ifObj(node) do Begin + findChart := false; + cNvPr := class(TSXml).GetNode(node, 'xdr:GraphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr'); + name := ifObj(cNvPr) ? cNvPr.GetAttribute('name') : ''; + if ifstring(chartName) then Begin + if name = chartName then + find := true; + End + else if ifInt(chartName) and ind = chartName then + find := true; + if find then Begin + chartNode := class(TSXml).GetNode(node, 'xdr:GraphicFrame/a:graphic/a:graphicData/c:chart'); + if not ifObj(chartNode) then return nil; + chartRid := chartNode.GetAttribute('r:id'); + break; + End; + ind ++; + node := node.NextElement(); + End; + for i:=0 to length(charts)-1 do Begin + if charts[i].Rid = chartRid then Begin + chart := TOfficeObj('TChart'); + chart.Width := Width; + chart.Height := Height; + chart.Name := name; + chart.Type := 'line'; + chart.ShowBubbleSize := false; + chart.ShowPercent := false; + chart.DataTable := false; + chart.AddSeries('test', array('line1'), array(1,2)); + chart := AddChart(chart, getPosNode(posOpt)); + xmlObj := Zip().Get(chart.chartFileName); + xmlObj.Data := charts[i].xmlObj.Data; + return chart; + End; + End; + return nil; + End; + + ///遍历文档中所有[TSTAG][/TSTAG]标签,针对每一个TAG执行tagObj.Apply() + ///tagName:string 标签名称 + ///tagObj:TAG对象方法 + ///返回:[err,tslTagCount,errArr]: err 执行错误TAG次数,tslTagCount TAG总数,errArr 错误信息(array(('code':'代码', 'err':'错误信息'))) + Function ExecTsTag(tagName, tagObj); + Begin + return Body().ExecTsTag(self, tagName, tagObj); + End; + ///文档中全部的批注信息 ///返回:DocComments对象 Function Comments(); diff --git a/funcext/TSOffice/TSExcelFile.tsf b/funcext/TSOffice/TSExcelFile.tsf index 4cb1e79..cbed220 100644 --- a/funcext/TSOffice/TSExcelFile.tsf +++ b/funcext/TSOffice/TSExcelFile.tsf @@ -349,10 +349,12 @@ Type TSExcelFile = Class ///sheet: string,工作表名称 ///topLeft: string,左上角坐标,如: "A4" ///bottomRight: string,右下角坐标,如: "B8",为空获取从topLeft开始的整张表 + ///[IncludeHeader: bool] 是否包括表头,默认FALSE + ///[IncludeIndex: bool] 是否包括索引号,默认FALSE ///返回: table - Function GetTable(sheet, topLeft, bottomRight); + Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle); Begin - return workbook_.GetTable(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight); + return workbook_.GetTable(class(TSXml).CurCodePageToUtf8(sheet), topLeft, bottomRight, includeHeader, includeIndex, forceSingle); End; ///插入列,在指定列前插入空白列 diff --git a/funcext/TSOffice/document/TDocxChart.tsf b/funcext/TSOffice/document/TDocxChart.tsf index 4f48740..8f6bb42 100644 --- a/funcext/TSOffice/document/TDocxChart.tsf +++ b/funcext/TSOffice/document/TDocxChart.tsf @@ -8,6 +8,7 @@ Type TDocxChart = Class(TSChart) chartId_ := 1 + vselect countof( ['FileName'] ) from docx.Zip().Files() where AnsiStartsText('word/charts/chart', ['FileName']) end; targetFileName := 'charts/chart' $ chartId_ $ '.xml'; chartFile := 'word/' + targetFileName; + chartFileName_ := chartFile; docx.Zip().Add(chartFile, GetDefaultXml()); xmlObj_ := docx.Zip().Get(chartFile); if not chartData_.DisableExcel and istable(chartData.Series) and istable(chartData.Series[0]['Categories']) and istable(chartData.Series[0]['Values']) then Begin diff --git a/funcext/TSOffice/document/TSTag.tsf b/funcext/TSOffice/document/TSTag.tsf new file mode 100644 index 0000000..e642173 --- /dev/null +++ b/funcext/TSOffice/document/TSTag.tsf @@ -0,0 +1,49 @@ +Type TSTag = Class + ///缺省构造函数 + Function Create(); overload; + Begin + End; + + Function Init(tagName, attribute, r); + Begin + tagName_ := tagName; + attribute_ := attribute; + runArr_ := r; + End; + + Function Apply(); virtual; + Begin + case tagName_ of + 'add': + _add(); + 'del': + _del(); + End; + End; + + Function _add(); + Begin + for i:=0 to length(runArr_)-1 do Begin + run := TOfficeObj('TRun'); + run.Init(runArr_[i]['rNode']); + run.Font.Size := 40; + run.Font.Color := 'FF0000'; + run.Font.Bold := true; + run.Apply(); + End; + End; + + Function _del(); + Begin + for i:=0 to length(runArr_)-1 do Begin + runArr_[i]['pNode'].DeleteChild(runArr_[i]['rNode']); + child := runArr_[i]['pNode'].FirstChildElement('w:r'); + if not ifObj(child) then + runArr_[i]['pNode'].Parent().DeleteChild(runArr_[i]['pNode']); + End; + End; + + tagName_:string; + attribute_:string; + runArr_; +End; diff --git a/funcext/TSOffice/word/_ExcelChartCopyPic.tsf b/funcext/TSOffice/word/_ExcelChartCopyPic.tsf index a7446f9..df0734d 100644 --- a/funcext/TSOffice/word/_ExcelChartCopyPic.tsf +++ b/funcext/TSOffice/word/_ExcelChartCopyPic.tsf @@ -1,3 +1,6 @@ -Function _ExcelChartCopyPic(excelFileName, excelSheetName, picType); +Function _ExcelChartCopyPic(excelFileName, excelSheetName, chartName, Width, Height); Begin + docx := TOfficeApi().GetDocument(); + chart := docx.CopyExcelChart(excelFileName, excelSheetName, chartName, Width, Height, TOfficeApi().GetCurrentPosition()); + return ifObj(chart); End; \ No newline at end of file diff --git a/funcext/TSOffice/worksheet/xlsxWorkBook.tsf b/funcext/TSOffice/worksheet/xlsxWorkBook.tsf index a73540c..5e883df 100644 --- a/funcext/TSOffice/worksheet/xlsxWorkBook.tsf +++ b/funcext/TSOffice/worksheet/xlsxWorkBook.tsf @@ -138,7 +138,7 @@ Type xlsxWorkBook = Class return class(ErrorMessage).Fail(); End; - Function GetTable(sheet, topLeft, bottomRight); + Function GetTable(sheet, topLeft, bottomRight, includeHeader, includeIndex, forceSingle); Begin o := GetSheetObj(sheet); if ifObj(o) then @@ -162,8 +162,6 @@ Type xlsxWorkBook = Class end End; - ///创建新sheet - ///sheet: string,工作表名称 Function NewSheet(sheet);overload; Begin lname := LowerCase(sheet); @@ -696,6 +694,7 @@ Type xlsxWorkBook = Class chartFile := ReplaceStr(target, '..', 'xl'); chart := TOfficeObj('TChart'); chart.Rid := node.GetAttribute('Id'); //rid + chart.drawingFileName := drawingFile; setChartInfo(chartFile, chart); charts[i] := chart; i++; @@ -1148,6 +1147,14 @@ Type xlsxWorkBook = Class Function ProtectSheet(sheet, protect); Begin + if protect.AlgorithmName and protect.Password then + begin + protect.SaltValue := nil;//protect.Password; + protect.HashValue := nil;//GetMsgDigest(protect.SaltValue, 6); + protect.SpinCount := nil; + protect.Password := nil; + protect.AlgorithmName := nil; + end sheet_obj := GetSheetXmlfile(sheet); work_node := sheet_obj.FirstChildElement('worksheet'); sheet_protection_node := work_node.FirstChildElement('sheetProtection');