{*******************************************************} { } { Tocsg.Export } { } { Copyright (C) 2023 kku } { } {*******************************************************} unit Tocsg.Export; interface uses System.SysUtils, Winapi.Windows, System.Classes, Winapi.ActiveX, VirtualTrees; const xlCsv = 6; xlXls = 43; xlHtml = 44; xlNxl = -2003; xlCell = -4143; xlText = -4158; xlLeft = -4131; xlRight = -4152; xlCenter = -4108; xlQualityStandard = $00000000; xlQualityMinimum = $00000001; xlTypePDF = $00000000; xlTypeXPS = $00000001; type TFnExportExpert = reference to procedure(vExcel: Variant); XlFixedFormatType = TOleEnum; XlFixedFormatQuality = TOleEnum; TExportTargetKind = (etkAll, etkSelected, etkChecked); function ExportCSV_VT(vt: TVirtualStringTree; sExportPath: String; aEncoding: TEncoding; aExpTgKind: TExportTargetKind = etkAll; pRootNode: PVirtualNode = nil): Boolean; function ExportCSV_VT_Div(vt: TVirtualStringTree; sExportPath: String; aEncoding: TEncoding; nDivCount: Integer; aExpTgKind: TExportTargetKind = etkAll; bHitsCount: Boolean = false): Boolean; function ExportXLS_VT(vt: TVirtualStringTree; const sSheetCaption, sExportPath: String; FnExportExpert: TFnExportExpert = nil; aExpTgKind: TExportTargetKind = etkAll): Boolean; function ExportXLS_VT_Div(vt: TVirtualStringTree; const sSheetCaption, sExportPath: String; nDivCount: Integer; FnExportExpert: TFnExportExpert = nil; aExpTgKind: TExportTargetKind = etkAll): Boolean; function ExportHanCell_VT(vt: TVirtualStringTree; const sSheetCaption, sExportPath: String; aExpTgKind: TExportTargetKind = etkAll): Boolean; function ExportXlsFromCsv(const sCsvPath, sOutPath: String; aCsvEncoding: TEncoding): Boolean; implementation uses System.Win.ComObj, System.Variants, Vcl.Graphics, Tocsg.Safe, Tocsg.Exception, Tocsg.VTUtil, VirtualTrees.Types, Tocsg.Strings, Tocsg.Path; function ExportCSV_VT(vt: TVirtualStringTree; sExportPath: String; aEncoding: TEncoding; aExpTgKind: TExportTargetKind = etkAll; pRootNode: PVirtualNode = nil): Boolean; var pNode: PVirtualNode; StrList: TStringList; sData: String; i: Integer; begin Result := false; Guard(StrList, TStringList.Create); sData := ''; for i := 0 to vt.Header.Columns.Count - 1 do begin if coVisible in vt.Header.Columns[i].Options then SumString(sData, '"' + vt.Header.Columns[i].Text + '"', ','); end; StrList.Add(sData); case aExpTgKind of etkAll : pNode := vt.GetFirst; etkSelected : pNode := vt.GetFirstSelected; etkChecked : pNode := vt.GetFirstChecked; else pNOde := nil; end; while pNode <> nil do begin sData := ''; if (pRootNode = nil) or (pRootNode = pNode.Parent) then begin for i := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[i].Options then SumString(sData, '"' + StringReplace(vt.Text[pNode, i], '"', '', [rfReplaceAll]) + '"', ','); if sData <> '' then StrList.Add(sData); end; case aExpTgKind of etkAll : pNode := vt.GetNext(pNode); etkSelected : pNode := vt.GetNextSelected(pNode); etkChecked : pNode := vt.GetNextChecked(pNode); end; end; if GetFileExt(sExportPath).ToUpper <> 'CSV' then sExportPath := sExportPath + '.csv'; StrList.SaveToFile(sExportPath, aEncoding); Result := true; end; function ExportCSV_VT_Div(vt: TVirtualStringTree; sExportPath: String; aEncoding: TEncoding; nDivCount: Integer; aExpTgKind: TExportTargetKind = etkAll; bHitsCount: Boolean = false): Boolean; var pNode: PVirtualNode; StrList: TStringList; sExt, sExcludeExtPath, sSavePath, sData: String; i, nFileNum, nAddedCnt: Integer; procedure InitData; var c: Integer; begin nAddedCnt := 0; StrList.Clear; sData := ''; for c := 0 to vt.Header.Columns.Count - 1 do begin if coVisible in vt.Header.Columns[c].Options then SumString(sData, vt.Header.Columns[c].Text, ','); end; StrList.Add(sData); end; procedure SaveData; begin Inc(nFileNum); sSavePath := Format('%s - %.3d%s', [sExcludeExtPath, nFileNum, sExt]); StrList.SaveToFile(sSavePath, aEncoding); InitData; end; begin Result := false; if GetFileExt(sExportPath).ToUpper <> 'CSV' then sExportPath := sExportPath + '.csv'; sExt := ExtractFileExt(sExportPath); sExcludeExtPath := CutFileExt(sExportPath); nFileNum := 0; case aExpTgKind of etkAll : pNode := vt.GetFirst; etkSelected : pNode := vt.GetFirstSelected; etkChecked : pNode := vt.GetFirstChecked; else pNode := nil; end; Guard(StrList, TStringList.Create); InitData; while pNode <> nil do begin sData := ''; for i := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[i].Options then SumString(sData, '"' + StringReplace(vt.Text[pNode, i], '"', '', [rfReplaceAll]) + '"', ','); if sData <> '' then begin StrList.Add(sData); Inc(nAddedCnt); if nAddedCnt >= nDivCount then SaveData; end; case aExpTgKind of etkAll : pNode := vt.GetNext(pNode); etkSelected : pNode := vt.GetNextSelected(pNode); etkChecked : pNode := vt.GetNextChecked(pNode); end; end; if nAddedCnt > 0 then SaveData; Result := true; end; function ExportXLS_VT(vt: TVirtualStringTree; const sSheetCaption, sExportPath: String; FnExportExpert: TFnExportExpert = nil; aExpTgKind: TExportTargetKind = etkAll): Boolean; var vExcel, vWorkBook: Variant; vArrData: OleVariant; i, nRow, nCol: Integer; pNode: PVirtualNode; dwRecordCnt, dwColumnCnt: DWORD; ClassID: TCLSID; begin Result := false; if not Succeeded(CLSIDFromProgID(PWideChar(WideString('Excel.Application')), ClassID)) then exit; try vExcel := CreateOleObject('Excel.Application'); except on E: Exception do begin ETgException.TraceException(E, 'ExportXLS_VT() .. fail CreateOleObject(Excel.Application) ..'); exit; end; end; try try vWorkBook := vExcel.WorkBooks.Add; if Assigned(FnExportExpert) then FnExportExpert(vExcel); dwRecordCnt := 0; case aExpTgKind of etkAll : dwRecordCnt := VT_CountTotalNode(vt); etkSelected : dwRecordCnt := vt.SelectedCount; etkChecked : dwRecordCnt := VT_CountVisibleCheckedNode(vt); end; // 엑셀 내보내기는 60000 미만으로 해야한다 if dwRecordCnt > 60000 then exit; dwColumnCnt := 0; for nCol := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[nCol].Options then Inc(dwColumnCnt); nRow := 0; vArrData := VarArrayCreate([0, dwRecordCnt, 0, dwColumnCnt - 1], varVariant); i := 0; for nCol := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[nCol].Options then begin vArrData[nRow, i] := vt.Header.Columns[nCol].Text; Inc(i); end; case aExpTgKind of etkAll : pNode := vt.GetFirst; etkSelected : pNode := vt.GetFirstSelected; etkChecked : pNode := vt.GetFirstChecked; else pNode := nil; end; while pNode <> nil do begin Inc(nRow); i := 0; for nCol := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[nCol].Options then begin vArrData[nRow, i] := vt.Text[pNode, nCol]; Inc(i); end; case aExpTgKind of etkAll : pNode := vt.GetNext(pNode); etkSelected : pNode := vt.GetNextSelected(pNode); etkChecked : pNode := vt.GetNextChecked(pNode); end; end; vWorkBook := vExcel.WorkSheets.Add; vExcel.Sheets[1].Name := DeleteChars(':\/?*[]', sSheetCaption); vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[1, dwColumnCnt]].Select; vExcel.Selection.Interior.ColorIndex := 15; vExcel.Selection.Interior.Pattern := 1; vExcel.Selection.Borders.LineStyle := 1; vExcel.Selection.Font.Name := 'Tahoma';//'굴림'; vExcel.Selection.Font.Size := 10; vExcel.Selection.Font.Bold := True; vExcel.Selection.Font.Color := clBlack; // 가로 타이틀 정렬 vExcel.Selection.VerticalAlignment := 2; vExcel.Selection.HorizontalAlignment := 3; vExcel.Range[vExcel.Cells[2, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.Font.Size := 10; vExcel.Selection.Font.Name := 'Tahoma';//'굴림'; vExcel.Selection.VerticalAlignment := 1; vExcel.Selection.HorizontalAlignment := 1; vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].NumberFormatLocal := WideString('@'); // 문자열 강제 지정 vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Value := vArrData; vExcel.Range[vExcel.Cells[2, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.VerticalAlignment := 2; vExcel.Selection.HorizontalAlignment := 2; vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.Columns.AutoFit; finally vExcel.DisplayAlerts := False; vExcel.Visible := False; vWorkBook.Saveas(sExportPath); // vWorkBook.ExportAsFixedFormat(xlTypePDF, sExportPath); vExcel.Quit; vExcel := Unassigned; end; except on E: Exception do begin ETgException.TraceException(E, 'ExportXLS_VT() ..'); exit; end; end; Result := true; end; function ExportXLS_VT_Div(vt: TVirtualStringTree; const sSheetCaption, sExportPath: String; nDivCount: Integer; FnExportExpert: TFnExportExpert = nil; aExpTgKind: TExportTargetKind = etkAll): Boolean; var vExcel, vWorkBook: Variant; vArrData: OleVariant; i, nRow, nCol, nFileNum: Integer; pNode: PVirtualNode; dwRecordCnt, dwColumnCnt: DWORD; sExt, sSavePath, sExcludeExtPath: String; function InitData: Boolean; var c: Integer; begin Result := false; try vExcel := CreateOleObject('Excel.Application'); except on E: Exception do begin ETgException.TraceException(E, 'Fail .. ExportXLS_VT_Div() .. fail CreateOleObject(Excel.Application) ..'); exit; end; end; try vWorkBook := vExcel.WorkBooks.Add; if Assigned(FnExportExpert) then FnExportExpert(vExcel); case aExpTgKind of etkAll : dwRecordCnt := VT_CountTotalNode(vt); etkSelected : dwRecordCnt := vt.SelectedCount; etkChecked : dwRecordCnt := VT_CountVisibleCheckedNode(vt); end; dwColumnCnt := 0; for c := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[c].Options then Inc(dwColumnCnt); nRow := 0; vArrData := VarArrayCreate([0, dwRecordCnt, 0, dwColumnCnt - 1], varVariant); i := 0; for c := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[c].Options then begin vArrData[nRow, i] := vt.Header.Columns[c].Text; Inc(i); end; except on E: Exception do begin ETgException.TraceException(E, 'Fail .. ExportXLS_VT_Div() .. InitData()'); exit; end; end; Result := true; end; function SaveData: Boolean; begin vWorkBook := vExcel.WorkSheets.Add; vExcel.Sheets[1].Name := DeleteChars(':\/?*[]', sSheetCaption); vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[1, dwColumnCnt]].Select; vExcel.Selection.Interior.ColorIndex := 15; vExcel.Selection.Interior.Pattern := 1; vExcel.Selection.Borders.LineStyle := 1; vExcel.Selection.Font.Name := 'Tahoma';//'굴림'; vExcel.Selection.Font.Size := 10; vExcel.Selection.Font.Bold := True; vExcel.Selection.Font.Color := clBlack; // 가로 타이틀 정렬 vExcel.Selection.VerticalAlignment := 2; vExcel.Selection.HorizontalAlignment := 3; vExcel.Range[vExcel.Cells[2, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.Font.Size := 10; vExcel.Selection.Font.Name := 'Tahoma';//'굴림'; vExcel.Selection.VerticalAlignment := 1; vExcel.Selection.HorizontalAlignment := 1; vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].NumberFormatLocal := WideString('@'); // 문자열 강제 지정 vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Value := vArrData; vExcel.Range[vExcel.Cells[2, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.VerticalAlignment := 2; vExcel.Selection.HorizontalAlignment := 2; vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.Columns.AutoFit; Inc(nFileNum); sSavePath := Format('%s - %.3d.%s', [sExcludeExtPath, nFileNum, sExt]); vExcel.DisplayAlerts := False; vExcel.Visible := False; vWorkBook.Saveas(sSavePath); vExcel.Quit; vExcel := Unassigned; Result := InitData; end; begin Result := false; sExt := ExtractFileExt(sExportPath); sExcludeExtPath := CutFileExt(sExportPath); if not InitData then exit; try pNode := nil; try case aExpTgKind of etkAll : pNode := vt.GetFirst; etkSelected : pNode := vt.GetFirstSelected; etkChecked : pNode := vt.GetFirstChecked; end; while pNode <> nil do begin Inc(nRow); i := 0; for nCol := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[nCol].Options then begin vArrData[nRow, i] := vt.Text[pNode, nCol]; Inc(i); end; if nRow >= nDivCount then if not SaveData then exit; case aExpTgKind of etkAll : pNode := vt.GetNext(pNode); etkSelected : pNode := vt.GetNextSelected(pNode); etkChecked : pNode := vt.GetNextChecked(pNode); end; end; if nRow > 1 then if not SaveData then exit; finally vExcel.Quit; vExcel := Unassigned; end; except on E: Exception do begin ETgException.TraceException(E, 'ExportXLS_VT_Div() ..'); exit; end; end; Result := true; end; function ExportHanCell_VT(vt: TVirtualStringTree; const sSheetCaption, sExportPath: String; aExpTgKind: TExportTargetKind = etkAll): Boolean; var vHancell: OleVariant; vWorkBook, vWorkSheet: Variant; i, nRow, nCol: Integer; pNode: PVirtualNode; begin Result := false; try vHancell := CreateOleObject('HCell.Application'); // 한컴 오피스 네오 체험판의 한셀만 설치되어 있다면 작동하지 않음 except on E: Exception do begin ETgException.TraceException(E, 'ExportHanCell_VT() .. fail CreateOleObject(HCell.Application) ..'); exit; end; end; try vHancell.visible := False; vWorkBook := vHancell.Workbooks.Add; try vWorkSheet := vWorkBook.WorkSheets.Add; except vWorkSheet := vWorkBook.Sheets.Add; end; vWorkSheet.Name := sSheetCaption; pNode := nil; try i := 0; for nCol := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[nCol].Options then begin vWorkSheet.Cells[1, 1 + i].Value := vt.Header.Columns[nCol].Text; Inc(i); end; case aExpTgKind of etkAll : pNode := vt.GetFirst; etkSelected : pNode := vt.GetFirstSelected; etkChecked : pNode := vt.GetFirstChecked; end; nRow := 0; while pNode <> nil do begin i := 0; for nCol := 0 to vt.Header.Columns.Count - 1 do if coVisible in vt.Header.Columns[nCol].Options then begin vWorkSheet.Cells[2 + nRow, 1 + i].Value := vt.Text[pNode, nCol]; Inc(i); end; case aExpTgKind of etkAll : pNode := vt.GetNext(pNode); etkSelected : pNode := vt.GetNextSelected(pNode); etkChecked : pNode := vt.GetNextChecked(pNode); end; Inc(nRow); end; vWorkBook.SaveAs(OleVariant(sExportPath)); finally vHancell.workbooks.close; vHancell.Quit; vHancell := Unassigned; end; except on E: Exception do begin ETgException.TraceException(E, 'ExportXLS_VT() ..'); exit; end; end; Result := true; end; function ExportXlsFromCsv(const sCsvPath, sOutPath: String; aCsvEncoding: TEncoding): Boolean; var CsvStrList, ColumnList: TStringList; vExcel, vWorkBook: Variant; vArrData: OleVariant; i, nRow, nCol: Integer; dwRecordCnt, dwColumnCnt: DWORD; begin Result := false; if not FileExists(sCsvPath) then exit; Guard(CsvStrList, TStringList.Create); CsvStrList.LoadFromFile(sCsvPath, aCsvEncoding); if CsvStrList.Count = 0 then exit; Guard(ColumnList, TStringList.Create); try vExcel := CreateOleObject('Excel.Application'); except on E: Exception do begin ETgException.TraceException(E, 'ExportXLS_fromCVS() .. fail CreateOleObject(Excel.Application) ..'); exit; end; end; try try vWorkBook := vExcel.WorkBooks.Add; dwRecordCnt := CsvStrList.Count; SplitString2(CsvStrList[CsvStrList.Count-1], ',', ColumnList); dwColumnCnt := ColumnList.Count; vArrData := VarArrayCreate([0, dwRecordCnt, 0, dwColumnCnt-1], varVariant); for nRow := 0 to dwRecordCnt - 1 do begin i := 0; SplitString2(CsvStrList[nRow], ',', ColumnList); for nCol := 0 to ColumnList.Count - 1 do begin vArrData[nRow, i] := ColumnList[nCol]; Inc(i); end; end; vWorkBook := vExcel.WorkSheets.Add; vExcel.Sheets[1].Name := 'Untitle'; vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].NumberFormatLocal := WideString('@'); // 문자열 강제 지정 vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Value := vArrData; vExcel.Range[vExcel.Cells[2, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.VerticalAlignment := 2; vExcel.Selection.HorizontalAlignment := 2; vExcel.Range[vExcel.Cells[1, 1], vExcel.Cells[dwRecordCnt + 1, dwColumnCnt]].Select; vExcel.Selection.Columns.AutoFit; finally vExcel.DisplayAlerts := False; vExcel.Visible := False; vWorkBook.Saveas(sOutPath); vExcel.Quit; vExcel := Unassigned; end; except on E: Exception do begin ETgException.TraceException(E, 'ExportXLS_VT() ..'); exit; end; end; Result := true; end; end.