593 lines
29 KiB
C#
593 lines
29 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using Excel = Microsoft.Office.Interop.Excel;
|
||
|
||
namespace DwgExtractorManual.Models
|
||
{
|
||
/// <summary>
|
||
/// Excel <20><>Ʈ<EFBFBD><C6AE> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>۾<EFBFBD><DBBE><EFBFBD> <20><><EFBFBD><EFBFBD>ϴ<EFBFBD> Ŭ<><C5AC><EFBFBD><EFBFBD>
|
||
/// </summary>
|
||
internal class ExcelDataWriter
|
||
{
|
||
private readonly ExcelManager excelManager;
|
||
|
||
public ExcelDataWriter(ExcelManager excelManager)
|
||
{
|
||
this.excelManager = excelManager ?? throw new ArgumentNullException(nameof(excelManager));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Title Block <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Excel <20><>Ʈ<EFBFBD><C6AE> <20><><EFBFBD>
|
||
/// </summary>
|
||
public void WriteTitleBlockData(List<TitleBlockRowData> titleBlockRows)
|
||
{
|
||
if (excelManager.TitleBlockSheet == null || titleBlockRows == null || titleBlockRows.Count == 0)
|
||
return;
|
||
|
||
int currentRow = 2; // <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
|
||
foreach (var row in titleBlockRows)
|
||
{
|
||
excelManager.TitleBlockSheet.Cells[currentRow, 1] = row.Type;
|
||
excelManager.TitleBlockSheet.Cells[currentRow, 2] = row.Name;
|
||
excelManager.TitleBlockSheet.Cells[currentRow, 3] = row.Tag;
|
||
excelManager.TitleBlockSheet.Cells[currentRow, 4] = row.Prompt;
|
||
excelManager.TitleBlockSheet.Cells[currentRow, 5] = row.Value;
|
||
excelManager.TitleBlockSheet.Cells[currentRow, 6] = row.Path;
|
||
excelManager.TitleBlockSheet.Cells[currentRow, 7] = row.FileName;
|
||
currentRow++;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Text Entity <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Excel <20><>Ʈ<EFBFBD><C6AE> <20><><EFBFBD>
|
||
/// </summary>
|
||
public void WriteTextEntityData(List<TextEntityRowData> textEntityRows)
|
||
{
|
||
if (excelManager.TextEntitiesSheet == null || textEntityRows == null || textEntityRows.Count == 0)
|
||
return;
|
||
|
||
int currentRow = 2; // <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
|
||
foreach (var row in textEntityRows)
|
||
{
|
||
excelManager.TextEntitiesSheet.Cells[currentRow, 1] = row.Type;
|
||
excelManager.TextEntitiesSheet.Cells[currentRow, 2] = row.Layer;
|
||
excelManager.TextEntitiesSheet.Cells[currentRow, 3] = row.Text;
|
||
excelManager.TextEntitiesSheet.Cells[currentRow, 4] = row.Path;
|
||
excelManager.TextEntitiesSheet.Cells[currentRow, 5] = row.FileName;
|
||
currentRow++;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Note 엔터티 데이터를 Excel 시트에 쓰기 (테이블 및 셀 병합 포함)
|
||
/// </summary>
|
||
public void WriteNoteEntityData(List<NoteEntityInfo> noteEntityRows)
|
||
{
|
||
if (excelManager.NoteEntitiesSheet == null || noteEntityRows == null || noteEntityRows.Count == 0)
|
||
return;
|
||
|
||
int excelRow = 2; // 헤더 다음 행부터 시작
|
||
|
||
foreach (var note in noteEntityRows)
|
||
{
|
||
// 기본 Note 정보 쓰기
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 1] = note.Type;
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 2] = note.Layer;
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 3] = note.Text;
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 4] = note.X;
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 5] = note.Y;
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 6] = note.SortOrder;
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 8] = note.Path;
|
||
excelManager.NoteEntitiesSheet.Cells[excelRow, 9] = note.FileName;
|
||
|
||
int tableRowCount = 0;
|
||
if (note.Cells != null && note.Cells.Count > 0)
|
||
{
|
||
// 테이블 데이터 처리
|
||
foreach (var cell in note.Cells)
|
||
{
|
||
int startRow = excelRow + cell.Row;
|
||
int startCol = 7 + cell.Column; // G열부터 시작
|
||
int endRow = startRow + cell.RowSpan - 1;
|
||
int endCol = startCol + cell.ColumnSpan - 1;
|
||
|
||
Excel.Range cellRange = excelManager.NoteEntitiesSheet.Range[
|
||
excelManager.NoteEntitiesSheet.Cells[startRow, startCol],
|
||
excelManager.NoteEntitiesSheet.Cells[endRow, endCol]];
|
||
|
||
// 병합 먼저 수행
|
||
if (cell.RowSpan > 1 || cell.ColumnSpan > 1)
|
||
{
|
||
cellRange.Merge();
|
||
}
|
||
|
||
// 값 설정 및 서식 지정
|
||
cellRange.Value = cell.CellText;
|
||
cellRange.VerticalAlignment = Excel.XlVAlign.xlVAlignCenter;
|
||
cellRange.HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter;
|
||
}
|
||
|
||
// 이 테이블이 차지하는 총 행 수를 계산
|
||
tableRowCount = note.Cells.Max(c => c.Row + c.RowSpan);
|
||
}
|
||
|
||
// 다음 Note를 기록할 위치로 이동
|
||
// 테이블이 있으면 테이블 높이만큼, 없으면 한 칸만 이동
|
||
excelRow += (tableRowCount > 0) ? tableRowCount : 1;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Excel <20><>Ʈ<EFBFBD><C6AE> <20><><EFBFBD>
|
||
/// </summary>
|
||
public void WriteMappingDataToExcel(Dictionary<string, Dictionary<string, (string, string, string, string)>> mappingData)
|
||
{
|
||
try
|
||
{
|
||
if (excelManager.MappingSheet == null || mappingData == null)
|
||
return;
|
||
|
||
int currentRow = 2; // <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
|
||
Debug.WriteLine($"[DEBUG] Writing mapping data to Excel. Total files: {mappingData.Count}");
|
||
|
||
foreach (var fileEntry in mappingData)
|
||
{
|
||
string fileName = fileEntry.Key;
|
||
var fileMappingData = fileEntry.Value;
|
||
|
||
Debug.WriteLine($"[DEBUG] Processing file: {fileName}, entries: {fileMappingData.Count}");
|
||
|
||
foreach (var mapEntry in fileMappingData)
|
||
{
|
||
string mapKey = mapEntry.Key;
|
||
(string aiLabel, string dwgTag, string attValue, string pdfValue) = mapEntry.Value;
|
||
|
||
if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(mapKey))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
try
|
||
{
|
||
// <20><>ġ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ<EFBFBD><C6AE> <20><><EFBFBD><EFBFBD> <20>迭 <20><><EFBFBD>
|
||
object[,] rowData = new object[1, 6];
|
||
rowData[0, 0] = fileName;
|
||
rowData[0, 1] = mapKey;
|
||
rowData[0, 2] = aiLabel ?? "";
|
||
rowData[0, 3] = dwgTag ?? "";
|
||
rowData[0, 4] = attValue ?? "";
|
||
rowData[0, 5] = pdfValue ?? "";
|
||
|
||
Excel.Range range = excelManager.MappingSheet.Range[
|
||
excelManager.MappingSheet.Cells[currentRow, 1],
|
||
excelManager.MappingSheet.Cells[currentRow, 6]];
|
||
range.Value = rowData;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"? Error writing row {currentRow}: {ex.Message}");
|
||
}
|
||
|
||
currentRow++;
|
||
}
|
||
}
|
||
|
||
Debug.WriteLine($"[DEBUG] Mapping data written to Excel. Total rows: {currentRow - 2}");
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"? <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Excel <20><><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>: {ex.Message}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Excel <20><><EFBFBD><EFBFBD> <20><>Ʈ<EFBFBD><C6AE><EFBFBD><EFBFBD> FileName<6D><65> AILabel<65><6C> <20><>Ī<EFBFBD>Ǵ<EFBFBD> <20><><EFBFBD><EFBFBD> ã<><C3A3> Pdf_value<75><65> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ
|
||
/// </summary>
|
||
public bool UpdateExcelRow(string fileName, string aiLabel, string pdfValue)
|
||
{
|
||
try
|
||
{
|
||
if (excelManager.MappingSheet == null)
|
||
return false;
|
||
|
||
Excel.Range usedRange = excelManager.MappingSheet.UsedRange;
|
||
if (usedRange == null) return false;
|
||
|
||
int lastRow = usedRange.Rows.Count;
|
||
|
||
for (int row = 2; row <= lastRow; row++)
|
||
{
|
||
var cellFileName = ((Excel.Range)excelManager.MappingSheet.Cells[row, 1]).Value?.ToString() ?? "";
|
||
var cellAiLabel = ((Excel.Range)excelManager.MappingSheet.Cells[row, 3]).Value?.ToString() ?? "";
|
||
|
||
if (string.Equals(cellFileName.Trim(), fileName.Trim(), StringComparison.OrdinalIgnoreCase) &&
|
||
string.Equals(cellAiLabel.Trim(), aiLabel.Trim(), StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
excelManager.MappingSheet.Cells[row, 6] = pdfValue;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"? Excel <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ <20><> <20><><EFBFBD><EFBFBD>: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// DWG <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ũ<EFBFBD><C5A9><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϰ<EFBFBD> <20><><EFBFBD><EFBFBD> (PDF <20>÷<EFBFBD> <20><><EFBFBD><EFBFBD>)
|
||
/// </summary>
|
||
public void SaveDwgOnlyMappingWorkbook(Dictionary<string, Dictionary<string, (string, string, string, string)>> mappingData, string resultFolderPath)
|
||
{
|
||
try
|
||
{
|
||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||
string savePath = System.IO.Path.Combine(resultFolderPath, $"{timestamp}_DwgOnly_Mapping.xlsx");
|
||
|
||
Debug.WriteLine($"[DEBUG] DWG <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ũ<EFBFBD><C5A9> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>: {savePath}");
|
||
|
||
var dwgOnlyWorkbook = excelManager.CreateNewWorkbook();
|
||
var dwgOnlyWorksheet = (Excel.Worksheet)dwgOnlyWorkbook.Worksheets[1];
|
||
dwgOnlyWorksheet.Name = "DWG Mapping Data";
|
||
|
||
// <20><><EFBFBD> <20><><EFBFBD><EFBFBD> (PDF Value <20>÷<EFBFBD> <20><><EFBFBD><EFBFBD>)
|
||
dwgOnlyWorksheet.Cells[1, 1] = "<22><><EFBFBD>ϸ<EFBFBD>";
|
||
dwgOnlyWorksheet.Cells[1, 2] = "Map Key";
|
||
dwgOnlyWorksheet.Cells[1, 3] = "AI Label";
|
||
dwgOnlyWorksheet.Cells[1, 4] = "DWG Tag";
|
||
dwgOnlyWorksheet.Cells[1, 5] = "DWG Value";
|
||
|
||
// <20><><EFBFBD> <20><>Ÿ<EFBFBD><C5B8> <20><><EFBFBD><EFBFBD>
|
||
var headerRange = dwgOnlyWorksheet.Range["A1:E1"];
|
||
headerRange.Font.Bold = true;
|
||
headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
|
||
headerRange.Borders.LineStyle = Excel.XlLineStyle.xlContinuous;
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>Է<EFBFBD>
|
||
int totalRows = mappingData.Sum(f => f.Value.Count);
|
||
if (totalRows > 0)
|
||
{
|
||
object[,] data = new object[totalRows, 5];
|
||
int row = 0;
|
||
|
||
foreach (var fileEntry in mappingData)
|
||
{
|
||
string fileName = fileEntry.Key;
|
||
foreach (var mapEntry in fileEntry.Value)
|
||
{
|
||
string mapKey = mapEntry.Key;
|
||
var (aiLabel, dwgTag, dwgValue, pdfValue) = mapEntry.Value;
|
||
|
||
data[row, 0] = fileName;
|
||
data[row, 1] = mapKey;
|
||
data[row, 2] = aiLabel;
|
||
data[row, 3] = dwgTag;
|
||
data[row, 4] = dwgValue;
|
||
|
||
row++;
|
||
}
|
||
}
|
||
|
||
Excel.Range dataRange = dwgOnlyWorksheet.Range[
|
||
dwgOnlyWorksheet.Cells[2, 1],
|
||
dwgOnlyWorksheet.Cells[totalRows + 1, 5]];
|
||
dataRange.Value = data;
|
||
}
|
||
|
||
dwgOnlyWorksheet.Columns.AutoFit();
|
||
excelManager.SaveWorkbookAs(dwgOnlyWorkbook, savePath);
|
||
|
||
Debug.WriteLine($"? DWG <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ũ<EFBFBD><C5A9> <20><><EFBFBD><EFBFBD> <20>Ϸ<EFBFBD>: {System.IO.Path.GetFileName(savePath)}");
|
||
|
||
dwgOnlyWorkbook.Close(false);
|
||
System.GC.Collect();
|
||
System.GC.WaitForPendingFinalizers();
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"? DWG <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ũ<EFBFBD><C5A9> <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>: {ex.Message}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Height <20><><EFBFBD>ĵ<EFBFBD> Excel <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
/// </summary>
|
||
public void WriteHeightSortedData(List<TextEntityInfo> textEntities, Excel.Worksheet worksheet, string fileName)
|
||
{
|
||
// <20><><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
worksheet.Cells[1, 1] = "Height";
|
||
worksheet.Cells[1, 2] = "Type";
|
||
worksheet.Cells[1, 3] = "Layer";
|
||
worksheet.Cells[1, 4] = "Tag";
|
||
worksheet.Cells[1, 5] = "FileName";
|
||
worksheet.Cells[1, 6] = "Text";
|
||
|
||
// <20><><EFBFBD> <20><>Ÿ<EFBFBD><C5B8> <20><><EFBFBD><EFBFBD>
|
||
var headerRange = worksheet.Range["A1:F1"];
|
||
headerRange.Font.Bold = true;
|
||
headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD> ExtractTextEntitiesWithHeight<68><74><EFBFBD><EFBFBD> <20>̹<EFBFBD> <20><><EFBFBD>ĵǾ<C4B5><C7BE><EFBFBD><EFBFBD>Ƿ<EFBFBD> <20>ٽ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ʽ<EFBFBD><CABD>ϴ<EFBFBD>.
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>Է<EFBFBD>
|
||
int row = 2;
|
||
foreach (var entity in textEntities) // sortedEntities<65><73> textEntities<65><73> <20><><EFBFBD><EFBFBD>
|
||
{
|
||
worksheet.Cells[row, 1] = entity.Height;
|
||
worksheet.Cells[row, 2] = entity.Type;
|
||
worksheet.Cells[row, 3] = entity.Layer;
|
||
worksheet.Cells[row, 4] = entity.Tag;
|
||
worksheet.Cells[row, 5] = fileName;
|
||
worksheet.Cells[row, 6] = entity.Text;
|
||
row++;
|
||
}
|
||
|
||
worksheet.Columns.AutoFit();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Note 엔티티들을 Excel 워크시트에 기록합니다 (기존 데이터 아래에 추가).
|
||
/// CellBoundary 데이터를 사용하여 병합된 셀의 텍스트를 적절히 처리합니다.
|
||
/// </summary>
|
||
public void WriteNoteEntities(List<NoteEntityInfo> noteEntities, Excel.Worksheet worksheet, string fileName)
|
||
{
|
||
if (noteEntities == null || noteEntities.Count == 0)
|
||
{
|
||
Debug.WriteLine("[DEBUG] Note 엔티티가 없습니다.");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 현재 워크시트의 마지막 사용된 행 찾기
|
||
Excel.Range usedRange = null;
|
||
int lastRow = 1;
|
||
|
||
try
|
||
{
|
||
usedRange = worksheet.UsedRange;
|
||
lastRow = usedRange?.Rows.Count ?? 1;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"[DEBUG] UsedRange 접근 오류, 기본값 사용: {ex.Message}");
|
||
lastRow = 1;
|
||
}
|
||
|
||
int startRow = lastRow + 2; // 한 줄 띄우고 시작
|
||
Debug.WriteLine($"[DEBUG] Note 데이터 기록 시작: {startRow}행부터 {noteEntities.Count}개 항목");
|
||
|
||
// Note 섹션 헤더 추가 (표 컬럼 포함)
|
||
try
|
||
{
|
||
worksheet.Cells[startRow - 1, 1] = "=== Notes (with Cell Boundary Tables) ===";
|
||
worksheet.Cells[startRow - 1, 2] = "";
|
||
worksheet.Cells[startRow - 1, 3] = "";
|
||
worksheet.Cells[startRow - 1, 4] = "";
|
||
worksheet.Cells[startRow - 1, 5] = "";
|
||
worksheet.Cells[startRow - 1, 6] = "";
|
||
|
||
// 표 컬럼 헤더 추가 (G열부터 최대 20개 컬럼)
|
||
for (int col = 7; col <= 26; col++) // G~Z열 (20개 컬럼)
|
||
{
|
||
worksheet.Cells[startRow - 1, col] = $"Table Col {col - 6}";
|
||
var tableHeaderCell = (Excel.Range)worksheet.Cells[startRow - 1, col];
|
||
tableHeaderCell.Font.Bold = true;
|
||
tableHeaderCell.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
|
||
tableHeaderCell.Font.Size = 9; // 작은 폰트로 설정
|
||
}
|
||
|
||
// 헤더 스타일 적용 (개별 셀로 처리)
|
||
var headerCell = (Excel.Range)worksheet.Cells[startRow - 1, 1];
|
||
headerCell.Font.Bold = true;
|
||
headerCell.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightYellow);
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"[DEBUG] Note 헤더 작성 오류: {ex.Message}");
|
||
}
|
||
|
||
// Note 데이터 입력 (CellBoundary 데이터 사용)
|
||
int row = startRow;
|
||
try
|
||
{
|
||
foreach (var noteEntity in noteEntities)
|
||
{
|
||
// 기본 Note 정보 입력 (F열까지)
|
||
worksheet.Cells[row, 1] = 0; // Height는 0으로 설정
|
||
worksheet.Cells[row, 2] = noteEntity.Type ?? "";
|
||
worksheet.Cells[row, 3] = noteEntity.Layer ?? "";
|
||
worksheet.Cells[row, 4] = ""; // Tag는 빈 값
|
||
worksheet.Cells[row, 5] = fileName ?? "";
|
||
worksheet.Cells[row, 6] = noteEntity.Text ?? ""; // 일반 텍스트만 (표 데이터 제외)
|
||
|
||
int currentRow = row; // 현재 처리 중인 행 번호
|
||
|
||
// CellBoundary 데이터가 있으면 G열부터 테이블 데이터 처리
|
||
if (noteEntity.CellBoundaries != null && noteEntity.CellBoundaries.Count > 0)
|
||
{
|
||
Debug.WriteLine($"[DEBUG] CellBoundary 데이터 처리: Row {row}, 셀 수={noteEntity.CellBoundaries.Count}");
|
||
|
||
// CellBoundary의 각 셀을 해당 위치에 직접 배치
|
||
int maxTableRow = 0;
|
||
foreach (var cellBoundary in noteEntity.CellBoundaries)
|
||
{
|
||
var (cellRow, cellCol) = ParseRowColFromLabel(cellBoundary.Label);
|
||
Debug.WriteLine($"[DEBUG] CellBoundary 처리: {cellBoundary.Label} → Row={cellRow}, Col={cellCol}, Text='{cellBoundary.CellText}'");
|
||
|
||
if (cellRow > 0 && cellCol > 0)
|
||
{
|
||
// Excel에서 테이블 위치 계산:
|
||
// R1 → Note의 현재 행 (currentRow)
|
||
// R2 → Note의 다음 행 (currentRow + 1)
|
||
// C1 → G열(7), C2 → H열(8)
|
||
int excelRow = currentRow + (cellRow - 1); // R1=currentRow, R2=currentRow+1, ...
|
||
int excelCol = 7 + (cellCol - 1); // C1=G열(7), C2=H열(8), ...
|
||
|
||
Debug.WriteLine($"[DEBUG] Excel 위치: {cellBoundary.Label} → Excel[{excelRow},{excelCol}]");
|
||
|
||
// Excel 범위 체크 (최대 20개 컬럼까지)
|
||
if (excelCol <= 26) // Z열까지
|
||
{
|
||
// CellText가 비어있어도 일단 배치해보기 (디버그용)
|
||
var cellValue = string.IsNullOrEmpty(cellBoundary.CellText) ? "[빈셀]" : cellBoundary.CellText;
|
||
worksheet.Cells[excelRow, excelCol] = cellValue;
|
||
Debug.WriteLine($"[DEBUG] ✅ 셀 배치 완료: {cellBoundary.Label} → Excel[{excelRow},{excelCol}] = '{cellValue}'");
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"[DEBUG] ❌ Excel 컬럼 범위 초과: {excelCol} > 26");
|
||
}
|
||
|
||
// 테이블이 차지하는 최대 행 수 추적
|
||
maxTableRow = Math.Max(maxTableRow, cellRow);
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"[DEBUG] ❌ 잘못된 Row/Col: {cellBoundary.Label} → Row={cellRow}, Col={cellCol}");
|
||
}
|
||
}
|
||
|
||
// 테이블이 여러 행을 차지하는 경우 currentRow 업데이트
|
||
if (maxTableRow > 1)
|
||
{
|
||
currentRow += (maxTableRow - 1);
|
||
Debug.WriteLine($"[DEBUG] 테이블 행 수만큼 currentRow 업데이트: +{maxTableRow - 1} → {currentRow}");
|
||
}
|
||
}
|
||
else if (!string.IsNullOrEmpty(noteEntity.TableCsv))
|
||
{
|
||
// 기존 TableCsv 방식 (백업용)
|
||
var tableRows = noteEntity.TableCsv.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||
Debug.WriteLine($"[DEBUG] 기존 표 데이터 처리: Row {row}, 표 행 수={tableRows.Length}");
|
||
|
||
for (int tableRowIndex = 0; tableRowIndex < tableRows.Length; tableRowIndex++)
|
||
{
|
||
var tableCells = tableRows[tableRowIndex].Split(',');
|
||
|
||
// 각 셀을 G열부터 배치 (최대 15개 컬럼까지)
|
||
for (int cellIndex = 0; cellIndex < Math.Min(tableCells.Length, 15); cellIndex++)
|
||
{
|
||
var cellValue = tableCells[cellIndex].Trim().Trim('"'); // 따옴표 제거
|
||
worksheet.Cells[currentRow, 7 + cellIndex] = cellValue; // G열(7)부터 시작
|
||
}
|
||
|
||
// 표의 첫 번째 행이 아니면 새로운 Excel 행 추가
|
||
if (tableRowIndex > 0)
|
||
{
|
||
currentRow++;
|
||
// 새로운 행에는 기본 Note 정보 복사 (Type, Layer 등)
|
||
worksheet.Cells[currentRow, 1] = 0;
|
||
worksheet.Cells[currentRow, 2] = noteEntity.Type ?? "";
|
||
worksheet.Cells[currentRow, 3] = noteEntity.Layer ?? "";
|
||
worksheet.Cells[currentRow, 4] = "";
|
||
worksheet.Cells[currentRow, 5] = fileName ?? "";
|
||
worksheet.Cells[currentRow, 6] = "(continued)"; // 연속 표시
|
||
}
|
||
|
||
Debug.WriteLine($"[DEBUG] 표 행 {tableRowIndex + 1}/{tableRows.Length}: Excel Row {currentRow}, 셀 수={tableCells.Length}");
|
||
}
|
||
}
|
||
|
||
// "NOTE" 타입인 경우 행 배경색 변경 (표 영역 포함)
|
||
if (noteEntity.Type == "Note")
|
||
{
|
||
Excel.Range noteRowRange = worksheet.Range[worksheet.Cells[row, 1], worksheet.Cells[currentRow, 26]]; // Z열까지
|
||
noteRowRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightYellow);
|
||
noteRowRange.Font.Bold = true;
|
||
}
|
||
|
||
Debug.WriteLine($"[DEBUG] Excel 기록: Row {row}~{currentRow}, Order {noteEntity.SortOrder}, Type {noteEntity.Type}, Pos({noteEntity.X:F1},{noteEntity.Y:F1}), Text: '{noteEntity.Text}', HasCellBoundaries: {noteEntity.CellBoundaries?.Count > 0} (Count: {noteEntity.CellBoundaries?.Count ?? 0}), HasTableCsv: {!string.IsNullOrEmpty(noteEntity.TableCsv)}");
|
||
|
||
// CellBoundaries 상세 디버그
|
||
if (noteEntity.CellBoundaries != null && noteEntity.CellBoundaries.Count > 0)
|
||
{
|
||
Debug.WriteLine($"[DEBUG] CellBoundaries 상세:");
|
||
foreach (var cb in noteEntity.CellBoundaries.Take(5)) // 처음 5개만 출력
|
||
{
|
||
Debug.WriteLine($"[DEBUG] {cb.Label}: '{cb.CellText}'");
|
||
}
|
||
}
|
||
|
||
// 다음 Note는 현재 행의 다음 행부터 시작
|
||
row = currentRow + 1;
|
||
}
|
||
|
||
Debug.WriteLine($"[DEBUG] Note 데이터 기록 완료: {row - startRow}개 항목");
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"[DEBUG] Note 데이터 입력 오류: {ex.Message}");
|
||
Debug.WriteLine($"[DEBUG] 처리된 행: {row - startRow}개");
|
||
}
|
||
|
||
// AutoFit 시도 (오류 발생시 무시)
|
||
try
|
||
{
|
||
worksheet.Columns.AutoFit();
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"[DEBUG] AutoFit 오류 (무시됨): {ex.Message}");
|
||
}
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.WriteLine($"❌ WriteNoteEntities 전체 오류: {ex.Message}");
|
||
Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
|
||
throw; // 상위로 예외 전파
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 라벨에서 Row, Col 정보를 파싱합니다.
|
||
/// 예: "R1C2" → (1, 2) 또는 "R2C2→R3C4" → (2, 2) (시작 위치 사용)
|
||
/// </summary>
|
||
private (int row, int col) ParseRowColFromLabel(string label)
|
||
{
|
||
try
|
||
{
|
||
if (string.IsNullOrEmpty(label))
|
||
return (0, 0);
|
||
|
||
// "R2C2→R3C4" 형태인 경우 시작 부분만 사용
|
||
var startPart = label;
|
||
if (label.Contains("→"))
|
||
{
|
||
startPart = label.Split('→')[0];
|
||
}
|
||
|
||
var rIndex = startPart.IndexOf('R');
|
||
var cIndex = startPart.IndexOf('C');
|
||
|
||
if (rIndex >= 0 && cIndex > rIndex)
|
||
{
|
||
var rowStr = startPart.Substring(rIndex + 1, cIndex - rIndex - 1);
|
||
var colStr = startPart.Substring(cIndex + 1);
|
||
|
||
if (int.TryParse(rowStr, out int row) && int.TryParse(colStr, out int col))
|
||
{
|
||
return (row, col);
|
||
}
|
||
}
|
||
|
||
return (0, 0);
|
||
}
|
||
catch
|
||
{
|
||
return (0, 0);
|
||
}
|
||
}
|
||
}
|
||
} |