diff --git a/Models/AppSettings.cs b/Models/AppSettings.cs
new file mode 100644
index 0000000..e69de29
diff --git a/Models/DwgDataExtractor.cs b/Models/DwgDataExtractor.cs
new file mode 100644
index 0000000..c000cc0
--- /dev/null
+++ b/Models/DwgDataExtractor.cs
@@ -0,0 +1,397 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using Teigha.DatabaseServices;
+using Teigha.Runtime;
+
+namespace DwgExtractorManual.Models
+{
+ ///
+ /// DWG Ͽ ؽƮ ƼƼ ϴ Ŭ
+ ///
+ internal class DwgDataExtractor
+ {
+ private readonly FieldMapper fieldMapper;
+
+ public DwgDataExtractor(FieldMapper fieldMapper)
+ {
+ this.fieldMapper = fieldMapper ?? throw new ArgumentNullException(nameof(fieldMapper));
+ }
+
+ ///
+ /// DWG Ͽ Ͽ ExcelRowData Ʈ ȯ
+ ///
+ public DwgExtractionResult ExtractFromDwgFile(string filePath, IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ var result = new DwgExtractionResult();
+
+ if (!File.Exists(filePath))
+ {
+ Debug.WriteLine($"? ʽϴ: {filePath}");
+ return result;
+ }
+
+ try
+ {
+ progress?.Report(0);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var database = new Database(false, true))
+ {
+ database.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
+ progress?.Report(10);
+
+ using (var tran = database.TransactionManager.StartTransaction())
+ {
+ var bt = tran.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
+ using (var btr = tran.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord)
+ {
+ int totalEntities = btr.Cast().Count();
+ int processedCount = 0;
+ var fileName = Path.GetFileNameWithoutExtension(database.Filename);
+
+ if (string.IsNullOrEmpty(fileName))
+ {
+ fileName = "Unknown_File";
+ }
+
+ foreach (ObjectId entId in btr)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var ent = tran.GetObject(entId, OpenMode.ForRead) as Entity)
+ {
+ string layerName = GetLayerName(ent.LayerId, tran, database);
+
+ ProcessEntity(ent, tran, database, layerName, fileName, result);
+ }
+
+ processedCount++;
+ double currentProgress = 10.0 + (double)processedCount / totalEntities * 80.0;
+ progress?.Report(Math.Min(currentProgress, 90.0));
+ }
+ }
+
+ tran.Commit();
+ }
+ }
+
+ progress?.Report(100);
+ return result;
+ }
+ catch (OperationCanceledException)
+ {
+ Debug.WriteLine("? ۾ ҵǾϴ.");
+ progress?.Report(0);
+ return result;
+ }
+ catch (Teigha.Runtime.Exception ex)
+ {
+ progress?.Report(0);
+ Debug.WriteLine($"? DWG ó Teigha : {ex.Message}");
+ return result;
+ }
+ catch (System.Exception ex)
+ {
+ progress?.Report(0);
+ Debug.WriteLine($"? Ϲ : {ex.Message}");
+ return result;
+ }
+ }
+
+ ///
+ /// DWG Ͽ ؽƮ ƼƼ Ͽ Height Բ ȯմϴ.
+ ///
+ public List ExtractTextEntitiesWithHeight(string filePath)
+ {
+ var attRefEntities = new List();
+ var otherTextEntities = new List();
+
+ try
+ {
+ using (var database = new Database(false, true))
+ {
+ database.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
+
+ using (var tran = database.TransactionManager.StartTransaction())
+ {
+ var bt = tran.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
+ using (var btr = tran.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord)
+ {
+ foreach (ObjectId entId in btr)
+ {
+ using (var ent = tran.GetObject(entId, OpenMode.ForRead) as Entity)
+ {
+ string layerName = GetLayerName(ent.LayerId, tran, database);
+
+ // AttributeReference ó
+ if (ent is BlockReference blr)
+ {
+ foreach (ObjectId attId in blr.AttributeCollection)
+ {
+ using (var attRef = tran.GetObject(attId, OpenMode.ForRead) as AttributeReference)
+ {
+ if (attRef != null)
+ {
+ var textString = attRef.TextString == null ? "" : attRef.TextString;
+ attRefEntities.Add(new TextEntityInfo
+ {
+ Height = attRef.Height,
+ Type = "AttRef",
+ Layer = layerName,
+ Tag = attRef.Tag,
+ Text = textString,
+ });
+ }
+ }
+ }
+ }
+ // DBText ó
+ else if (ent is DBText dbText)
+ {
+ otherTextEntities.Add(new TextEntityInfo
+ {
+ Height = dbText.Height,
+ Type = "DBText",
+ Layer = layerName,
+ Tag = "",
+ Text = dbText.TextString
+ });
+ }
+ // MText ó
+ else if (ent is MText mText)
+ {
+ otherTextEntities.Add(new TextEntityInfo
+ {
+ Height = mText.Height,
+ Type = "MText",
+ Layer = layerName,
+ Tag = "",
+ Text = mText.Contents
+ });
+ }
+ }
+ }
+ }
+
+ tran.Commit();
+ }
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? ؽƮ ƼƼ ({Path.GetFileName(filePath)}): {ex.Message}");
+ }
+
+ var sortedAttRefEntities = attRefEntities.OrderByDescending(e => e.Height).ToList();
+ var sortedOtherTextEntities = otherTextEntities.OrderByDescending(e => e.Height).ToList();
+
+ sortedAttRefEntities.AddRange(sortedOtherTextEntities);
+ return sortedAttRefEntities;
+ }
+
+ private void ProcessEntity(Entity ent, Transaction tran, Database database, string layerName, string fileName, DwgExtractionResult result)
+ {
+ // AttributeDefinition
+ if (ent is AttributeDefinition attDef)
+ {
+ var titleBlockRow = new TitleBlockRowData
+ {
+ Type = attDef.GetType().Name,
+ Name = attDef.BlockName,
+ Tag = attDef.Tag,
+ Prompt = attDef.Prompt,
+ Value = attDef.TextString,
+ Path = database.Filename,
+ FileName = Path.GetFileName(database.Filename)
+ };
+
+ result.TitleBlockRows.Add(titleBlockRow);
+
+ //
+ AddMappingData(fileName, attDef.Tag, attDef.TextString, result);
+ }
+ // BlockReference AttributeReference
+ else if (ent is BlockReference blr)
+ {
+ foreach (ObjectId attId in blr.AttributeCollection)
+ {
+ using (var attRef = tran.GetObject(attId, OpenMode.ForRead) as AttributeReference)
+ {
+ if (attRef != null && attRef.TextString.Trim() != "")
+ {
+ var titleBlockRow = new TitleBlockRowData
+ {
+ Type = attRef.GetType().Name,
+ Name = blr.Name,
+ Tag = attRef.Tag,
+ Prompt = GetPromptFromAttributeReference(tran, blr, attRef.Tag),
+ Value = attRef.TextString,
+ Path = database.Filename,
+ FileName = Path.GetFileName(database.Filename)
+ };
+
+ result.TitleBlockRows.Add(titleBlockRow);
+
+ //
+ var aiLabel = fieldMapper.ExpresswayToAilabel(attRef.Tag);
+ if (aiLabel != null)
+ {
+ AddMappingData(fileName, attRef.Tag, attRef.TextString, result);
+ }
+ }
+ }
+ }
+ }
+ // DBText ƼƼ ( Ʈ)
+ else if (ent is DBText dbText)
+ {
+ var textEntityRow = new TextEntityRowData
+ {
+ Type = "DBText",
+ Layer = layerName,
+ Text = dbText.TextString,
+ Path = database.Filename,
+ FileName = Path.GetFileName(database.Filename)
+ };
+
+ result.TextEntityRows.Add(textEntityRow);
+ }
+ // MText ƼƼ ( Ʈ)
+ else if (ent is MText mText)
+ {
+ var textEntityRow = new TextEntityRowData
+ {
+ Type = "MText",
+ Layer = layerName,
+ Text = mText.Contents,
+ Path = database.Filename,
+ FileName = Path.GetFileName(database.Filename)
+ };
+
+ result.TextEntityRows.Add(textEntityRow);
+ }
+ }
+
+ private void AddMappingData(string fileName, string tag, string attValue, DwgExtractionResult result)
+ {
+ var aiLabel = fieldMapper.ExpresswayToAilabel(tag);
+ var mapKey = fieldMapper.AilabelToDocAiKey(aiLabel);
+
+ if (!string.IsNullOrEmpty(aiLabel))
+ {
+ var finalMapKey = mapKey ?? aiLabel;
+ result.AddMappingData(fileName, finalMapKey, aiLabel, tag, attValue, "");
+ }
+ else
+ {
+ var finalMapKey = mapKey ?? tag;
+ if (!string.IsNullOrEmpty(finalMapKey))
+ {
+ result.AddMappingData(fileName, finalMapKey, tag, tag, attValue, "");
+ }
+ }
+ }
+
+ private string? GetPromptFromAttributeReference(Transaction tr, BlockReference blockref, string tag)
+ {
+ string? prompt = null;
+
+ BlockTableRecord? blockDef = tr.GetObject(blockref.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
+ if (blockDef == null) return null;
+
+ foreach (ObjectId objId in blockDef)
+ {
+ AttributeDefinition? attDef = tr.GetObject(objId, OpenMode.ForRead) as AttributeDefinition;
+ if (attDef != null)
+ {
+ if (attDef.Tag.Equals(tag, System.StringComparison.OrdinalIgnoreCase))
+ {
+ prompt = attDef.Prompt;
+ break;
+ }
+ }
+ }
+
+ return prompt;
+ }
+
+ private string GetLayerName(ObjectId layerId, Transaction transaction, Database database)
+ {
+ try
+ {
+ using (var layerTableRecord = transaction.GetObject(layerId, OpenMode.ForRead) as LayerTableRecord)
+ {
+ return layerTableRecord?.Name ?? "";
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"Layer ̸ : {ex.Message}");
+ return "";
+ }
+ }
+ }
+
+ ///
+ /// DWG Ŭ
+ ///
+ public class DwgExtractionResult
+ {
+ public List TitleBlockRows { get; set; } = new List();
+ public List TextEntityRows { get; set; } = new List();
+ public Dictionary> FileToMapkeyToLabelTagValuePdf { get; set; }
+ = new Dictionary>();
+
+ public void AddMappingData(string fileName, string mapKey, string aiLabel, string dwgTag, string dwgValue, string pdfValue)
+ {
+ if (!FileToMapkeyToLabelTagValuePdf.ContainsKey(fileName))
+ {
+ FileToMapkeyToLabelTagValuePdf[fileName] = new Dictionary();
+ }
+
+ FileToMapkeyToLabelTagValuePdf[fileName][mapKey] = (aiLabel, dwgTag, dwgValue, pdfValue);
+ }
+ }
+
+ ///
+ /// Title Block
+ ///
+ public class TitleBlockRowData
+ {
+ public string Type { get; set; } = "";
+ public string Name { get; set; } = "";
+ public string Tag { get; set; } = "";
+ public string Prompt { get; set; } = "";
+ public string Value { get; set; } = "";
+ public string Path { get; set; } = "";
+ public string FileName { get; set; } = "";
+ }
+
+ ///
+ /// Text Entity
+ ///
+ public class TextEntityRowData
+ {
+ public string Type { get; set; } = "";
+ public string Layer { get; set; } = "";
+ public string Text { get; set; } = "";
+ public string Path { get; set; } = "";
+ public string FileName { get; set; } = "";
+ }
+
+ ///
+ /// ؽƮ ƼƼ Ŭ
+ ///
+ public class TextEntityInfo
+ {
+ public double Height { get; set; }
+ public string Type { get; set; } = "";
+ public string Layer { get; set; } = "";
+ public string Tag { get; set; } = "";
+ public string Text { get; set; } = "";
+ }
+}
\ No newline at end of file
diff --git a/Models/ExcelDataWriter.cs b/Models/ExcelDataWriter.cs
new file mode 100644
index 0000000..829650f
--- /dev/null
+++ b/Models/ExcelDataWriter.cs
@@ -0,0 +1,279 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Excel = Microsoft.Office.Interop.Excel;
+
+namespace DwgExtractorManual.Models
+{
+ ///
+ /// Excel Ʈ ۾ ϴ Ŭ
+ ///
+ internal class ExcelDataWriter
+ {
+ private readonly ExcelManager excelManager;
+
+ public ExcelDataWriter(ExcelManager excelManager)
+ {
+ this.excelManager = excelManager ?? throw new ArgumentNullException(nameof(excelManager));
+ }
+
+ ///
+ /// Title Block Excel Ʈ
+ ///
+ public void WriteTitleBlockData(List titleBlockRows)
+ {
+ if (excelManager.TitleBlockSheet == null || titleBlockRows == null || titleBlockRows.Count == 0)
+ return;
+
+ int currentRow = 2; //
+
+ 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++;
+ }
+ }
+
+ ///
+ /// Text Entity Excel Ʈ
+ ///
+ public void WriteTextEntityData(List textEntityRows)
+ {
+ if (excelManager.TextEntitiesSheet == null || textEntityRows == null || textEntityRows.Count == 0)
+ return;
+
+ int currentRow = 2; //
+
+ 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++;
+ }
+ }
+
+ ///
+ /// Excel Ʈ
+ ///
+ public void WriteMappingDataToExcel(Dictionary> mappingData)
+ {
+ try
+ {
+ if (excelManager.MappingSheet == null || mappingData == null)
+ return;
+
+ int currentRow = 2; //
+
+ 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
+ {
+ // ġ Ʈ 迭
+ 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($"? Excel : {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Excel Ʈ FileName AILabel ĪǴ ã Pdf_value Ʈ
+ ///
+ 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 = excelManager.MappingSheet.Cells[row, 1]?.Value?.ToString() ?? "";
+ var cellAiLabel = 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 Ʈ : {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// DWG ũ ϰ (PDF ÷ )
+ ///
+ public void SaveDwgOnlyMappingWorkbook(Dictionary> 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 ũ : {savePath}");
+
+ var dwgOnlyWorkbook = excelManager.CreateNewWorkbook();
+ var dwgOnlyWorksheet = (Excel.Worksheet)dwgOnlyWorkbook.Worksheets[1];
+ dwgOnlyWorksheet.Name = "DWG Mapping Data";
+
+ // (PDF Value ÷ )
+ dwgOnlyWorksheet.Cells[1, 1] = "ϸ";
+ dwgOnlyWorksheet.Cells[1, 2] = "Map Key";
+ dwgOnlyWorksheet.Cells[1, 3] = "AI Label";
+ dwgOnlyWorksheet.Cells[1, 4] = "DWG Tag";
+ dwgOnlyWorksheet.Cells[1, 5] = "DWG Value";
+
+ // Ÿ
+ 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;
+
+ // Է
+ 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 ũ Ϸ: {System.IO.Path.GetFileName(savePath)}");
+
+ dwgOnlyWorkbook.Close(false);
+ System.GC.Collect();
+ System.GC.WaitForPendingFinalizers();
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? DWG ũ : {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Height ĵ Excel
+ ///
+ public void WriteHeightSortedData(List textEntities, Excel.Worksheet worksheet, string fileName)
+ {
+ //
+ 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";
+
+ // Ÿ
+ var headerRange = worksheet.Range["A1:F1"];
+ headerRange.Font.Bold = true;
+ headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
+
+ // ʹ ExtractTextEntitiesWithHeight ̹ ĵǾǷ ٽ ʽϴ.
+
+ // Է
+ int row = 2;
+ foreach (var entity in textEntities) // sortedEntities textEntities
+ {
+ 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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Models/ExcelManager.cs b/Models/ExcelManager.cs
new file mode 100644
index 0000000..cefa558
--- /dev/null
+++ b/Models/ExcelManager.cs
@@ -0,0 +1,309 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using Excel = Microsoft.Office.Interop.Excel;
+
+namespace DwgExtractorManual.Models
+{
+ ///
+ /// Excel COM ü ⺻ ۾ ϴ Ŭ
+ ///
+ internal class ExcelManager : IDisposable
+ {
+ public Excel.Application? ExcelApplication { get; private set; }
+ public Excel.Workbook? TitleBlockWorkbook { get; private set; }
+ public Excel.Workbook? MappingWorkbook { get; private set; }
+
+ public Excel.Worksheet? TitleBlockSheet { get; private set; }
+ public Excel.Worksheet? TextEntitiesSheet { get; private set; }
+ public Excel.Worksheet? MappingSheet { get; private set; }
+
+ ///
+ /// Excel ø̼ ũƮ ʱȭ
+ ///
+ public void InitializeExcel()
+ {
+ try
+ {
+ var excelApp = new Excel.Application();
+ ExcelApplication = excelApp;
+ ExcelApplication.Visible = false; // WPF ó
+ Excel.Workbook workbook = excelApp.Workbooks.Add();
+ TitleBlockWorkbook = workbook;
+
+ // Title Block Sheet (⺻ Sheet1)
+ TitleBlockSheet = (Excel.Worksheet)workbook.Sheets[1];
+ TitleBlockSheet.Name = "Title Block";
+ SetupTitleBlockHeaders();
+
+ // Text Entities Sheet ߰
+ TextEntitiesSheet = (Excel.Worksheet)workbook.Sheets.Add();
+ TextEntitiesSheet.Name = "Text Entities";
+ SetupTextEntitiesHeaders();
+
+ // Ϳ ũ Ʈ
+ MappingWorkbook = excelApp.Workbooks.Add();
+ MappingSheet = (Excel.Worksheet)MappingWorkbook.Sheets[1];
+ MappingSheet.Name = "Mapping Data";
+ SetupMappingHeaders();
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"Excel ʱȭ : {ex.Message}");
+ ReleaseExcelObjects();
+ throw;
+ }
+ }
+
+ ///
+ /// Excel Ʈ
+ ///
+ public bool OpenExistingFile(string excelFilePath)
+ {
+ try
+ {
+ if (!File.Exists(excelFilePath))
+ {
+ Debug.WriteLine($"? Excel ʽϴ: {excelFilePath}");
+ return false;
+ }
+
+ if (ExcelApplication == null)
+ {
+ ExcelApplication = new Excel.Application();
+ ExcelApplication.Visible = false;
+ }
+
+ MappingWorkbook = ExcelApplication.Workbooks.Open(excelFilePath);
+ MappingSheet = (Excel.Worksheet)MappingWorkbook.Sheets["Mapping Data"];
+
+ if (MappingSheet == null)
+ {
+ Debug.WriteLine("? 'Mapping Data' Ʈ ã ϴ.");
+ return false;
+ }
+
+ return true;
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? Excel : {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// ο ũ (Height Ŀ)
+ ///
+ public Excel.Workbook CreateNewWorkbook()
+ {
+ if (ExcelApplication == null)
+ {
+ ExcelApplication = new Excel.Application();
+ ExcelApplication.Visible = false;
+ }
+ return ExcelApplication.Workbooks.Add();
+ }
+
+ // Title Block Ʈ
+ private void SetupTitleBlockHeaders()
+ {
+ if (TitleBlockSheet == null) return;
+
+ TitleBlockSheet.Cells[1, 1] = "Type"; // : AttributeReference, AttributeDefinition
+ TitleBlockSheet.Cells[1, 2] = "Name"; // BlockReference ̸ Ǵ BlockDefinition ̸
+ TitleBlockSheet.Cells[1, 3] = "Tag"; // Attribute Tag
+ TitleBlockSheet.Cells[1, 4] = "Prompt"; // Attribute Prompt
+ TitleBlockSheet.Cells[1, 5] = "Value"; // Attribute (TextString)
+ TitleBlockSheet.Cells[1, 6] = "Path"; // DWG ü
+ TitleBlockSheet.Cells[1, 7] = "FileName"; // DWG ̸
+
+ // Ÿ
+ Excel.Range headerRange = TitleBlockSheet.Range["A1:G1"];
+ headerRange.Font.Bold = true;
+ headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
+ }
+
+ // Text Entities Ʈ
+ private void SetupTextEntitiesHeaders()
+ {
+ if (TextEntitiesSheet == null) return;
+
+ TextEntitiesSheet.Cells[1, 1] = "Type"; // DBText, MText
+ TextEntitiesSheet.Cells[1, 2] = "Layer"; // Layer ̸
+ TextEntitiesSheet.Cells[1, 3] = "Text"; // ؽƮ
+ TextEntitiesSheet.Cells[1, 4] = "Path"; // DWG ü
+ TextEntitiesSheet.Cells[1, 5] = "FileName"; // DWG ̸
+
+ // Ÿ
+ Excel.Range headerRange = TextEntitiesSheet.Range["A1:E1"];
+ headerRange.Font.Bold = true;
+ headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGreen);
+ }
+
+ // Ʈ
+ private void SetupMappingHeaders()
+ {
+ if (MappingSheet == null) return;
+
+ MappingSheet.Cells[1, 1] = "FileName"; // ̸
+ MappingSheet.Cells[1, 2] = "MapKey"; // Ű
+ MappingSheet.Cells[1, 3] = "AILabel"; // AI
+ MappingSheet.Cells[1, 4] = "DwgTag"; // DWG Tag
+ MappingSheet.Cells[1, 5] = "Att_value"; // DWG
+ MappingSheet.Cells[1, 6] = "Pdf_value"; // PDF ( )
+
+ // Ÿ
+ Excel.Range headerRange = MappingSheet.Range["A1:F1"];
+ headerRange.Font.Bold = true;
+ headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightYellow);
+ }
+
+ ///
+ /// ũ
+ ///
+ public bool SaveWorkbook(Excel.Workbook? workbook = null)
+ {
+ try
+ {
+ if (workbook != null)
+ {
+ workbook.Save();
+ return true;
+ }
+
+ if (MappingWorkbook != null)
+ {
+ MappingWorkbook.Save();
+ return true;
+ }
+
+ if (TitleBlockWorkbook != null)
+ {
+ TitleBlockWorkbook.Save();
+ return true;
+ }
+
+ return false;
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? Excel : {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// ũ ο
+ ///
+ public void SaveWorkbookAs(Excel.Workbook? workbook, string savePath)
+ {
+ if (workbook == null) return;
+
+ string? directory = Path.GetDirectoryName(savePath);
+ if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ workbook.SaveAs(savePath,
+ FileFormat: Excel.XlFileFormat.xlOpenXMLWorkbook,
+ AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
+ }
+
+ ///
+ /// Excel Ʈ ִ ȿ ̸ մϴ.
+ ///
+ public string GetValidSheetName(string originalName)
+ {
+ if (string.IsNullOrEmpty(originalName))
+ return "Sheet";
+
+ // Excel Ʈ ʴ
+ string validName = originalName;
+ char[] invalidChars = { '\\', '/', '?', '*', '[', ']', ':' };
+
+ foreach (char c in invalidChars)
+ {
+ validName = validName.Replace(c, '_');
+ }
+
+ // 31ڷ (Excel Ʈ ִ )
+ if (validName.Length > 31)
+ {
+ validName = validName.Substring(0, 31);
+ }
+
+ return validName;
+ }
+
+ public void CloseWorkbooks()
+ {
+ if (TitleBlockWorkbook != null)
+ {
+ try { TitleBlockWorkbook.Close(false); }
+ catch { }
+ }
+ if (MappingWorkbook != null)
+ {
+ try { MappingWorkbook.Close(false); }
+ catch { }
+ }
+ if (ExcelApplication != null)
+ {
+ try { ExcelApplication.Quit(); }
+ catch { }
+ }
+ }
+
+ private void ReleaseExcelObjects()
+ {
+ ReleaseComObject(TitleBlockSheet);
+ ReleaseComObject(TextEntitiesSheet);
+ ReleaseComObject(MappingSheet);
+ ReleaseComObject(TitleBlockWorkbook);
+ ReleaseComObject(MappingWorkbook);
+ ReleaseComObject(ExcelApplication);
+
+ TitleBlockSheet = null;
+ TextEntitiesSheet = null;
+ MappingSheet = null;
+ TitleBlockWorkbook = null;
+ MappingWorkbook = null;
+ ExcelApplication = null;
+ }
+
+ private void ReleaseComObject(object? obj)
+ {
+ try
+ {
+ if (obj != null && Marshal.IsComObject(obj))
+ {
+ Marshal.ReleaseComObject(obj);
+ }
+ }
+ catch (System.Exception)
+ {
+ //
+ }
+ }
+
+ public void Dispose()
+ {
+ try
+ {
+ Debug.WriteLine("[DEBUG] ExcelManager Dispose ");
+ CloseWorkbooks();
+ ReleaseExcelObjects();
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Debug.WriteLine("[DEBUG] ExcelManager Dispose Ϸ");
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"[DEBUG] ExcelManager Dispose : {ex.Message}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Models/ExportExcel.cs b/Models/ExportExcel.cs
index c3a2e33..7149a9b 100644
--- a/Models/ExportExcel.cs
+++ b/Models/ExportExcel.cs
@@ -3,48 +3,37 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Runtime.InteropServices; // COM 객체 해제를 위해 필요
using System.Threading;
using System.Threading.Tasks;
-using Teigha.DatabaseServices;
-using Teigha.Geometry;
using Teigha.Runtime;
-using Excel = Microsoft.Office.Interop.Excel;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace DwgExtractorManual.Models
{
///
- /// DWG 파일에서 Excel로 데이터 내보내기 클래스
- /// AttributeReference, AttributeDefinition, DBText, MText 추출 지원
+ /// DWG 파일에서 Excel로 데이터 내보내기 메인 클래스
+ /// 리팩토링된 구조로 각 기능별 클래스를 조합하여 사용
///
internal class ExportExcel : IDisposable
{
- // ODA 서비스 객체 (managed by singleton)
- private Services appServices;
+ // 컴포넌트들
+ private readonly ExcelManager excelManager;
+ private readonly DwgDataExtractor dwgExtractor;
+ private readonly JsonDataProcessor jsonProcessor;
+ private readonly ExcelDataWriter excelWriter;
+ private readonly FieldMapper fieldMapper;
- // Excel COM 객체들
- private Excel.Application excelApplication;
- private Excel.Workbook workbook1;
- private Excel.Worksheet titleBlockSheet; // Title Block용 시트
- private Excel.Worksheet textEntitiesSheet; // Text Entities용 시트
- private Excel.Workbook mappingWorkbook; // 매핑 데이터용 워크북
- private Excel.Worksheet mappingSheet; // 매핑 데이터용 시트
+ // ODA 서비스 관리
+ private Services? appServices;
+
+ // 매핑 데이터 저장용
+ private Dictionary> FileToMapkeyToLabelTagValuePdf
+ = new Dictionary>();
readonly List MapKeys;
-
- // 각 시트의 현재 행 번호
- private int titleBlockCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작
- private int textEntitiesCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작
- private int mappingDataCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작
-
- // 매핑 데이터 저장용 딕셔너리 - (AILabel, DwgTag, DwgValue, PdfValue)
- private Dictionary> FileToMapkeyToLabelTagValuePdf = new Dictionary>();
-
- private FieldMapper fieldMapper;
- // 생성자: ODA 및 Excel 초기화
+ ///
+ /// 생성자: 모든 컴포넌트 초기화
+ ///
public ExportExcel()
{
try
@@ -54,36 +43,303 @@ namespace DwgExtractorManual.Models
Debug.WriteLine("✅ FieldMapper 로딩 성공");
MapKeys = fieldMapper.GetAllDocAiKeys();
Debug.WriteLine($"📊 총 DocAI 키 개수: {MapKeys?.Count ?? 0}");
-
+
// 매핑 테스트 (디버깅용)
TestFieldMapper();
+
+ Debug.WriteLine("🔄 ODA 초기화 중...");
+ InitializeTeighaServices();
+
+ // 컴포넌트들 초기화
+ excelManager = new ExcelManager();
+ dwgExtractor = new DwgDataExtractor(fieldMapper);
+ jsonProcessor = new JsonDataProcessor();
+ excelWriter = new ExcelDataWriter(excelManager);
+
+ Debug.WriteLine("🔄 Excel 초기화 중...");
+ excelManager.InitializeExcel();
}
catch (System.Exception ex)
{
- Debug.WriteLine($"❌ FieldMapper 로딩 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
- if (ex.InnerException != null)
- {
- Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
- }
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
+ Debug.WriteLine($"❌ ExportExcel 초기화 오류: {ex.Message}");
throw;
}
-
- Debug.WriteLine("🔄 ODA 초기화 중...");
- InitializeTeighaServices();
- Debug.WriteLine("🔄 Excel 초기화 중...");
- InitializeExcel();
}
- // 필드 매퍼 테스트
+ ///
+ /// 단일 DWG 파일에서 데이터를 추출하여 Excel에 추가
+ ///
+ public bool ExportDwgToExcel(string filePath, IProgress? progress = null, CancellationToken cancellationToken = default)
+ {
+ Debug.WriteLine($"[DEBUG] ExportDwgToExcel 시작: {filePath}");
+
+ if (excelManager.ExcelApplication == null)
+ {
+ Debug.WriteLine("❌ Excel이 초기화되지 않았습니다.");
+ return false;
+ }
+
+ if (!File.Exists(filePath))
+ {
+ Debug.WriteLine($"❌ 파일이 존재하지 않습니다: {filePath}");
+ return false;
+ }
+
+ try
+ {
+ // DWG 데이터 추출
+ var extractionResult = dwgExtractor.ExtractFromDwgFile(filePath, progress, cancellationToken);
+
+ if (extractionResult == null)
+ {
+ return false;
+ }
+
+ // Excel에 데이터 기록
+ excelWriter.WriteTitleBlockData(extractionResult.TitleBlockRows);
+ excelWriter.WriteTextEntityData(extractionResult.TextEntityRows);
+
+ // 매핑 데이터 병합
+ foreach (var fileEntry in extractionResult.FileToMapkeyToLabelTagValuePdf)
+ {
+ if (!FileToMapkeyToLabelTagValuePdf.ContainsKey(fileEntry.Key))
+ {
+ FileToMapkeyToLabelTagValuePdf[fileEntry.Key] = new Dictionary();
+ }
+
+ foreach (var mapEntry in fileEntry.Value)
+ {
+ FileToMapkeyToLabelTagValuePdf[fileEntry.Key][mapEntry.Key] = mapEntry.Value;
+ }
+ }
+
+ // 매핑 데이터를 Excel에 기록
+ excelWriter.WriteMappingDataToExcel(FileToMapkeyToLabelTagValuePdf);
+
+ return true;
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"❌ ExportDwgToExcel 오류: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 기존 Excel 파일을 열어 JSON 파일의 PDF 분석 결과로 업데이트
+ ///
+ public bool UpdateExistingExcelWithJson(string excelFilePath, string jsonFilePath)
+ {
+ try
+ {
+ Debug.WriteLine($"[DEBUG] 기존 Excel 파일 업데이트 시작: {excelFilePath}");
+
+ if (!excelManager.OpenExistingFile(excelFilePath))
+ {
+ return false;
+ }
+
+ Debug.WriteLine("✅ 기존 Excel 파일 열기 성공");
+ return UpdateMappingSheetFromJson(jsonFilePath);
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"❌ 기존 Excel 파일 업데이트 중 오류: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// JSON 파일에서 PDF 분석 결과를 읽어 Excel 매핑 시트 업데이트
+ ///
+ public bool UpdateMappingSheetFromJson(string jsonFilePath)
+ {
+ if (!File.Exists(jsonFilePath))
+ {
+ Debug.WriteLine($"❌ JSON 파일이 존재하지 않습니다: {jsonFilePath}");
+ return false;
+ }
+
+ // JSON 처리를 통해 매핑 데이터를 업데이트하고, Excel에 반영
+ return jsonProcessor.UpdateMappingDataFromJson(FileToMapkeyToLabelTagValuePdf, jsonFilePath) &&
+ UpdateExcelFromMappingData();
+ }
+
+ ///
+ /// DWG 파일들을 처리하여 Height 순으로 정렬된 Excel 파일 생성
+ ///
+ public void ExportDwgToExcelHeightSorted(string[] dwgFiles, string resultFolder)
+ {
+ try
+ {
+ Debug.WriteLine($"[DEBUG] Height 정렬 Excel 생성 시작: {dwgFiles.Length}개 파일");
+
+ string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
+ string savePath = Path.Combine(resultFolder, $"{timestamp}_HeightSorted.xlsx");
+
+ var heightSortedWorkbook = excelManager.CreateNewWorkbook();
+ bool firstSheetProcessed = false;
+
+ foreach (string dwgFile in dwgFiles)
+ {
+ if (!File.Exists(dwgFile))
+ {
+ continue;
+ }
+
+ string fileName = Path.GetFileNameWithoutExtension(dwgFile);
+
+ try
+ {
+ var worksheet = firstSheetProcessed ?
+ heightSortedWorkbook.Worksheets.Add() :
+ (Microsoft.Office.Interop.Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
+
+ worksheet.Name = excelManager.GetValidSheetName(fileName);
+ firstSheetProcessed = true;
+
+ var textEntities = dwgExtractor.ExtractTextEntitiesWithHeight(dwgFile);
+ excelWriter.WriteHeightSortedData(textEntities, worksheet, fileName);
+
+ Debug.WriteLine($"[DEBUG] {fileName} 시트 완료: {textEntities.Count}개 엔티티");
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"❌ {fileName} 처리 중 오류: {ex.Message}");
+ continue;
+ }
+ }
+
+ if (!firstSheetProcessed)
+ {
+ var defaultSheet = (Microsoft.Office.Interop.Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
+ defaultSheet.Name = "No_DWG_Files";
+ defaultSheet.Cells[1, 1] = "No DWG files found in this folder";
+ }
+
+ excelManager.SaveWorkbookAs(heightSortedWorkbook, savePath);
+ heightSortedWorkbook.Close(false);
+
+ Debug.WriteLine($"✅ Height 정렬 Excel 파일 저장 완료: {Path.GetFileName(savePath)}");
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"❌ Height 정렬 Excel 생성 중 오류: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// 모든 DWG 파일들을 하나의 Excel 파일로 처리하여 Height 순으로 정렬
+ ///
+ public void ExportAllDwgToExcelHeightSorted(List<(string filePath, string folderName)> allDwgFiles, string savePath)
+ {
+ try
+ {
+ Debug.WriteLine($"[DEBUG] 단일 Excel 파일로 Height 정렬 생성 시작: {allDwgFiles.Count}개 파일");
+
+ var heightSortedWorkbook = excelManager.CreateNewWorkbook();
+ bool firstSheetProcessed = false;
+
+ foreach (var (filePath, folderName) in allDwgFiles)
+ {
+ if (!File.Exists(filePath))
+ {
+ continue;
+ }
+
+ string fileName = Path.GetFileNameWithoutExtension(filePath);
+
+ try
+ {
+ var worksheet = firstSheetProcessed ?
+ heightSortedWorkbook.Worksheets.Add() :
+ (Microsoft.Office.Interop.Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
+
+ worksheet.Name = excelManager.GetValidSheetName(fileName);
+ firstSheetProcessed = true;
+
+ var textEntities = dwgExtractor.ExtractTextEntitiesWithHeight(filePath);
+ excelWriter.WriteHeightSortedData(textEntities, worksheet, fileName);
+
+ Debug.WriteLine($"[DEBUG] {fileName} 시트 완료: {textEntities.Count}개 엔티티");
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"❌ {fileName} 처리 중 오류: {ex.Message}");
+ continue;
+ }
+ }
+
+ if (!firstSheetProcessed)
+ {
+ var defaultSheet = (Microsoft.Office.Interop.Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
+ defaultSheet.Name = "No_DWG_Files";
+ defaultSheet.Cells[1, 1] = "No DWG files found in any folder";
+ }
+
+ excelManager.SaveWorkbookAs(heightSortedWorkbook, savePath);
+ heightSortedWorkbook.Close(false);
+
+ Debug.WriteLine($"✅ 단일 Height 정렬 Excel 파일 저장 완료: {Path.GetFileName(savePath)}");
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"❌ 단일 Height 정렬 Excel 생성 중 오류: {ex.Message}");
+ throw;
+ }
+ }
+
+ // Helper methods and legacy support methods
+ public bool SaveExcel() => excelManager.SaveWorkbook();
+
+ public void SaveMappingWorkbookOnly(string savePath) => excelManager.SaveWorkbookAs(excelManager.MappingWorkbook, savePath);
+
+ public void SaveDwgOnlyMappingWorkbook(string resultFolderPath) => excelWriter.SaveDwgOnlyMappingWorkbook(FileToMapkeyToLabelTagValuePdf, resultFolderPath);
+
+ public void SaveAndCloseExcel(string savePath)
+ {
+ try
+ {
+ excelManager.SaveWorkbookAs(excelManager.TitleBlockWorkbook, savePath);
+
+ if (excelManager.MappingWorkbook != null)
+ {
+ string directory = Path.GetDirectoryName(savePath) ?? "";
+ string mappingPath = Path.Combine(directory, Path.GetFileNameWithoutExtension(savePath) + "_Mapping.xlsx");
+ excelManager.SaveWorkbookAs(excelManager.MappingWorkbook, mappingPath);
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"Excel 파일 저장 중 오류 발생: {ex.Message}");
+ }
+ finally
+ {
+ excelManager.CloseWorkbooks();
+ }
+ }
+
+ public void CloseExcelObjectsWithoutSaving() => excelManager.CloseWorkbooks();
+
+ public void SaveMappingDictionary(string filePath) => jsonProcessor.SaveMappingDictionary(FileToMapkeyToLabelTagValuePdf, filePath);
+
+ public void LoadMappingDictionary(string filePath)
+ {
+ FileToMapkeyToLabelTagValuePdf = jsonProcessor.LoadMappingDictionary(filePath);
+ }
+
+ public void WriteCompleteMapping() => excelWriter.WriteMappingDataToExcel(FileToMapkeyToLabelTagValuePdf);
+
+ public void UpdateWithPdfData(string jsonFilePath) => jsonProcessor.UpdateMappingDataFromJson(FileToMapkeyToLabelTagValuePdf, jsonFilePath);
+
+ public void ClearAccumulatedData() => FileToMapkeyToLabelTagValuePdf.Clear();
+
+ // Private helper methods
private void TestFieldMapper()
{
Debug.WriteLine("[DEBUG] Testing field mapper...");
-
- // 몇 가지 알려진 expressway 필드로 테스트
- var testFields = new[] { "TD_DNAME_MAIN", "TD_DWGNO", "TD_DWGCODE", "TB_MTITIL"};
+ var testFields = new[] { "TD_DNAME_MAIN", "TD_DWGNO", "TD_DWGCODE", "TB_MTITIL" };
foreach (var field in testFields)
{
@@ -93,7 +349,6 @@ namespace DwgExtractorManual.Models
}
}
- // Teigha 서비스 초기화 (싱글톤 사용)
private void InitializeTeighaServices()
{
try
@@ -109,1829 +364,37 @@ namespace DwgExtractorManual.Models
}
}
- // Excel 애플리케이션 및 워크시트 초기화
- private void InitializeExcel()
+ private bool UpdateExcelFromMappingData()
{
try
{
- var excelApp = new Excel.Application();
- excelApplication = excelApp;
- excelApplication.Visible = false; // WPF에서는 숨김 처리
- Excel.Workbook workbook = excelApp.Workbooks.Add();
- workbook1 = workbook;
-
- // Title Block Sheet 설정 (기본 Sheet1)
- titleBlockSheet = (Excel.Worksheet)workbook.Sheets[1];
- titleBlockSheet.Name = "Title Block";
- SetupTitleBlockHeaders();
-
- // Text Entities Sheet 추가
- textEntitiesSheet = (Excel.Worksheet)workbook.Sheets.Add();
- textEntitiesSheet.Name = "Text Entities";
- SetupTextEntitiesHeaders();
-
- // 매핑 데이터용 워크북 및 시트 생성
- mappingWorkbook = excelApp.Workbooks.Add();
- mappingSheet = (Excel.Worksheet)mappingWorkbook.Sheets[1];
- mappingSheet.Name = "Mapping Data";
- SetupMappingHeaders();
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"Excel 초기화 중 오류 발생: {ex.Message}");
- ReleaseExcelObjects();
- throw;
- }
- }
-
- // Title Block 시트 헤더 설정
- private void SetupTitleBlockHeaders()
- {
- titleBlockSheet.Cells[1, 1] = "Type"; // 예: AttributeReference, AttributeDefinition
- titleBlockSheet.Cells[1, 2] = "Name"; // BlockReference 이름 또는 BlockDefinition 이름
- titleBlockSheet.Cells[1, 3] = "Tag"; // Attribute Tag
- titleBlockSheet.Cells[1, 4] = "Prompt"; // Attribute Prompt
- titleBlockSheet.Cells[1, 5] = "Value"; // Attribute 값 (TextString)
- titleBlockSheet.Cells[1, 6] = "Path"; // 원본 DWG 파일 전체 경로
- titleBlockSheet.Cells[1, 7] = "FileName"; // 원본 DWG 파일 이름만
-
- // 헤더 행 스타일
- Excel.Range headerRange = titleBlockSheet.Range["A1:G1"];
- headerRange.Font.Bold = true;
- headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
- }
-
- // Text Entities 시트 헤더 설정
- private void SetupTextEntitiesHeaders()
- {
- textEntitiesSheet.Cells[1, 1] = "Type"; // DBText, MText
- textEntitiesSheet.Cells[1, 2] = "Layer"; // Layer 이름
- textEntitiesSheet.Cells[1, 3] = "Text"; // 실제 텍스트 내용
- textEntitiesSheet.Cells[1, 4] = "Path"; // 원본 DWG 파일 전체 경로
- textEntitiesSheet.Cells[1, 5] = "FileName"; // 원본 DWG 파일 이름만
-
- // 헤더 행 스타일
- Excel.Range headerRange = textEntitiesSheet.Range["A1:E1"];
- headerRange.Font.Bold = true;
- headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGreen);
- }
-
- // 매핑 데이터 시트 헤더 설정
- private void SetupMappingHeaders()
- {
- mappingSheet.Cells[1, 1] = "FileName"; // 파일 이름
- mappingSheet.Cells[1, 2] = "MapKey"; // 매핑 키
- mappingSheet.Cells[1, 3] = "AILabel"; // AI 라벨
- mappingSheet.Cells[1, 4] = "DwgTag"; // DWG Tag
- mappingSheet.Cells[1, 5] = "Att_value"; // DWG 값
- mappingSheet.Cells[1, 6] = "Pdf_value"; // PDF 값 (현재는 빈 값)
-
- // 헤더 행 스타일
- Excel.Range headerRange = mappingSheet.Range["A1:F1"];
- headerRange.Font.Bold = true;
- headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightYellow);
- }
-
- ///
- /// 단일 DWG 파일에서 AttributeReference/AttributeDefinition 데이터를 추출하여
- /// 초기화된 Excel 워크시트에 추가합니다.
- ///
- /// 처리할 DWG 파일 경로
- /// 진행 상태 보고를 위한 IProgress 객체
- /// 작업 취소를 위한 CancellationToken
- /// 성공 시 true, 실패 시 false 반환
- public bool ExportDwgToExcel(string filePath, IProgress progress = null, CancellationToken cancellationToken = default)
- {
- Debug.WriteLine($"[DEBUG] ExportDwgToExcel 시작: {filePath}");
-
- if (excelApplication == null)
- {
- Debug.WriteLine("❌ Excel이 초기화되지 않았습니다.");
- return false;
- }
-
- if (!File.Exists(filePath))
- {
- Debug.WriteLine($"❌ 파일이 존재하지 않습니다: {filePath}");
- return false;
- }
-
- try
- {
- Debug.WriteLine("[DEBUG] 진행률 0% 보고");
- progress?.Report(0);
- cancellationToken.ThrowIfCancellationRequested();
-
- Debug.WriteLine("[DEBUG] ODA Database 객체 생성 중...");
- // ODA Database 객체 생성 및 DWG 파일 읽기
- using (var database = new Database(false, true))
- {
- Debug.WriteLine($"[DEBUG] DWG 파일 읽기 시도: {filePath}");
- database.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
- Debug.WriteLine("[DEBUG] DWG 파일 읽기 성공");
-
- cancellationToken.ThrowIfCancellationRequested();
- Debug.WriteLine("[DEBUG] 진행률 10% 보고");
- progress?.Report(10);
-
- Debug.WriteLine("[DEBUG] 트랜잭션 시작 중...");
- using (var tran = database.TransactionManager.StartTransaction())
- {
- Debug.WriteLine("[DEBUG] BlockTable 접근 중...");
- var bt = tran.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
- Debug.WriteLine("[DEBUG] ModelSpace 접근 중...");
- using (var btr = tran.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord)
- {
- int totalEntities = btr.Cast().Count();
- Debug.WriteLine($"[DEBUG] 총 엔티티 수: {totalEntities}");
- int processedCount = 0;
-
- foreach (ObjectId entId in btr)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var ent = tran.GetObject(entId, OpenMode.ForRead) as Entity)
- {
- // Layer 이름 가져오기 (공통)
- string layerName = GetLayerName(ent.LayerId, tran, database);
- var fileName = Path.GetFileNameWithoutExtension(database.Filename);
-
- // 파일명 유효성 검사
- if (string.IsNullOrEmpty(fileName))
- {
- fileName = "Unknown_File";
- Debug.WriteLine($"[DEBUG] Using default filename: {fileName}");
- }
-
- // 파일별 매핑 딕셔너리 초기화
- if (!FileToMapkeyToLabelTagValuePdf.ContainsKey(fileName))
- {
- FileToMapkeyToLabelTagValuePdf[fileName] = new Dictionary();
- }
-
- // AttributeDefinition 추출
- if (ent is AttributeDefinition attDef)
- {
- titleBlockSheet.Cells[titleBlockCurrentRow, 1] = attDef.GetType().Name;
- titleBlockSheet.Cells[titleBlockCurrentRow, 2] = attDef.BlockName;
- titleBlockSheet.Cells[titleBlockCurrentRow, 3] = attDef.Tag;
- titleBlockSheet.Cells[titleBlockCurrentRow, 4] = attDef.Prompt;
- titleBlockSheet.Cells[titleBlockCurrentRow, 5] = attDef.TextString;
- titleBlockSheet.Cells[titleBlockCurrentRow, 6] = database.Filename;
- titleBlockSheet.Cells[titleBlockCurrentRow, 7] = Path.GetFileName(database.Filename);
- titleBlockCurrentRow++;
-
- var tag = attDef.Tag;
- var aiLabel = fieldMapper.ExpresswayToAilabel(tag);
- var mapKey = fieldMapper.AilabelToDocAiKey(aiLabel);
- var attValue = attDef.TextString;
- Debug.WriteLine($"[DEBUG] AttributeDefinition - Tag: {tag}, AILabel: {aiLabel}, MapKey: {mapKey}");
-
- // 매핑 데이터 저장
- if (!string.IsNullOrEmpty(aiLabel))
- {
- // mapKey가 null이면 aiLabel을 mapKey로 사용
- var finalMapKey = mapKey ?? aiLabel;
- FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (aiLabel, tag, attValue, "");
- Debug.WriteLine($"[DEBUG] Added mapping: {tag} -> {aiLabel} (MapKey: {finalMapKey})");
- }
- else
- {
- // aiLabel이 null이면 tag를 사용하여 저장
- var finalMapKey = mapKey ?? tag;
- if (!string.IsNullOrEmpty(finalMapKey))
- {
- FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (tag, tag, attValue, "");
- Debug.WriteLine($"[DEBUG] Added unmapped tag: {tag} -> {tag} (MapKey: {finalMapKey})");
- }
- else
- {
- Debug.WriteLine($"[DEBUG] Skipped empty tag for AttributeDefinition");
- }
- }
- }
- // BlockReference 및 그 안의 AttributeReference 추출
- else if (ent is BlockReference blr)
- {
- foreach (ObjectId attId in blr.AttributeCollection)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var attRef = tran.GetObject(attId, OpenMode.ForRead) as AttributeReference)
- {
- if (attRef != null && attRef.TextString.Trim() != "")
- {
- titleBlockSheet.Cells[titleBlockCurrentRow, 1] = attRef.GetType().Name;
- titleBlockSheet.Cells[titleBlockCurrentRow, 2] = blr.Name;
- titleBlockSheet.Cells[titleBlockCurrentRow, 3] = attRef.Tag;
- titleBlockSheet.Cells[titleBlockCurrentRow, 4] = GetPromptFromAttributeReference(tran, blr, attRef.Tag);
-
- titleBlockSheet.Cells[titleBlockCurrentRow, 5] = attRef.TextString;
- titleBlockSheet.Cells[titleBlockCurrentRow, 6] = database.Filename;
- titleBlockSheet.Cells[titleBlockCurrentRow, 7] = Path.GetFileName(database.Filename);
-
- titleBlockCurrentRow++;
-
- var tag = attRef.Tag;
- var aiLabel = fieldMapper.ExpresswayToAilabel(tag);
- if (aiLabel == null) continue;
- var mapKey = fieldMapper.AilabelToDocAiKey(aiLabel);
- var attValue = attRef.TextString;
-
- Debug.WriteLine($"[DEBUG] AttributeReference - Tag: {tag}, AILabel: {aiLabel}, MapKey: {mapKey}");
-
- // 매핑 데이터 저장
- if (!string.IsNullOrEmpty(aiLabel))
- {
- // mapKey가 null이면 aiLabel을 mapKey로 사용
- var finalMapKey = mapKey ?? aiLabel;
- FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (aiLabel, tag, attValue, "");
- Debug.WriteLine($"[DEBUG] Added mapping: {tag} -> {aiLabel} (MapKey: {finalMapKey})");
- }
- else
- {
- // aiLabel이 null이면 tag를 사용하여 저장
- var finalMapKey = mapKey ?? tag;
- if (!string.IsNullOrEmpty(finalMapKey))
- {
- FileToMapkeyToLabelTagValuePdf[fileName][finalMapKey] = (tag, tag, attValue, "");
- Debug.WriteLine($"[DEBUG] Added unmapped tag: {tag} -> {tag} (MapKey: {finalMapKey})");
- }
- else
- {
- Debug.WriteLine($"[DEBUG] Skipped empty tag for AttributeReference");
- }
- }
- }
- }
- }
- }
- // DBText 엔티티 추출 (별도 시트)
- else if (ent is DBText dbText)
- {
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 1] = "DBText"; // Type
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 2] = layerName; // Layer
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 3] = dbText.TextString; // Text
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 4] = database.Filename; // Path
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 5] = Path.GetFileName(database.Filename); // FileName
-
- textEntitiesCurrentRow++;
- }
- // MText 엔티티 추출 (별도 시트)
- else if (ent is MText mText)
- {
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 1] = "MText"; // Type
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 2] = layerName; // Layer
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 3] = mText.Contents; // Text
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 4] = database.Filename; // Path
- textEntitiesSheet.Cells[textEntitiesCurrentRow, 5] = Path.GetFileName(database.Filename); // FileName
-
- textEntitiesCurrentRow++;
- }
- }
-
- // write values in new excel file with FileToMapkeyToLabelTag
-
- processedCount++;
- double currentProgress = 10.0 + (double)processedCount / totalEntities * 80.0;
- progress?.Report(Math.Min(currentProgress, 90.0));
- }
- }
-
- tran.Commit();
- }
-
- Debug.WriteLine($"[DEBUG] Transaction committed. Total mapping entries collected: {FileToMapkeyToLabelTagValuePdf.Sum(f => f.Value.Count)}");
-
- // 매핑 데이터를 Excel 시트에 기록
- Debug.WriteLine("[DEBUG] 매핑 데이터를 Excel에 기록 중...");
- WriteMappingDataToExcel();
- Debug.WriteLine("[DEBUG] 매핑 데이터 Excel 기록 완료");
- }
-
- progress?.Report(100);
- return true;
- }
- catch (OperationCanceledException)
- {
- Debug.WriteLine("❌ 작업이 취소되었습니다.");
- progress?.Report(0);
- return false;
- }
- catch (Teigha.Runtime.Exception ex)
- {
- progress?.Report(0);
- Debug.WriteLine($"❌ DWG 파일 처리 중 Teigha 오류 발생:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" ErrorStatus: {ex.ErrorStatus}");
- if (ex.InnerException != null)
- {
- Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
- }
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- return false;
- }
- catch (System.Exception ex)
- {
- progress?.Report(0);
- Debug.WriteLine($"❌ 일반 오류 발생:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
- if (ex.InnerException != null)
- {
- Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
- }
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- return false;
- }
- }
-
- // 매핑 데이터를 Excel 시트에 기록
- private void WriteMappingDataToExcel()
- {
- try
- {
- int currentRow = 2; // 헤더 다음 행부터 시작
-
- Debug.WriteLine($"[DEBUG] Writing mapping data to Excel. Total files: {FileToMapkeyToLabelTagValuePdf.Count}");
- Debug.WriteLine($"[DEBUG] 시작 행: {currentRow}");
-
- foreach (var fileEntry in FileToMapkeyToLabelTagValuePdf)
- {
- string fileName = fileEntry.Key;
- var mappingData = fileEntry.Value;
-
- Debug.WriteLine($"[DEBUG] Processing file: {fileName}, entries: {mappingData.Count}");
-
- foreach (var mapEntry in mappingData)
- {
- string mapKey = mapEntry.Key;
- (string aiLabel, string dwgTag, string attValue, string pdfValue) = mapEntry.Value;
-
- // null 값 방지
- if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(mapKey))
- {
- Debug.WriteLine($"[DEBUG] Skipping entry with null/empty values: fileName={fileName}, mapKey={mapKey}");
- continue;
- }
-
- Debug.WriteLine($"[DEBUG] Writing row {currentRow}: {fileName} | {mapKey} | {aiLabel} | {dwgTag} | PDF: {pdfValue}");
-
- try
- {
- // 배치 업데이트를 위한 배열 사용
- object[,] rowData = new object[1, 6];
- rowData[0, 0] = fileName; // FileName
- rowData[0, 1] = mapKey; // MapKey
- rowData[0, 2] = aiLabel ?? ""; // AILabel
- rowData[0, 3] = dwgTag ?? ""; // DwgTag
- rowData[0, 4] = attValue ?? ""; // DwgValue (Att_value)
- rowData[0, 5] = pdfValue ?? ""; // PdfValue (Pdf_value)
-
- Excel.Range range = mappingSheet.Range[mappingSheet.Cells[currentRow, 1], mappingSheet.Cells[currentRow, 6]];
- range.Value = rowData;
-
- Debug.WriteLine($"[DEBUG] Row {currentRow} written successfully");
- }
- 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}");
- Debug.WriteLine($"[DEBUG] 최종 행 번호: {currentRow}");
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 매핑 데이터 Excel 기록 중 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
- if (ex.InnerException != null)
- {
- Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
- }
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw; // 상위 메서드로 예외 전파
- }
- }
-
- ///
- /// 기존 Excel 파일을 열어 JSON 파일의 PDF 분석 결과로 매핑 시트를 업데이트합니다.
- ///
- /// 기존 Excel 파일 경로
- /// PDF 분석 결과 JSON 파일 경로
- /// 성공 시 true, 실패 시 false
- public bool UpdateExistingExcelWithJson(string excelFilePath, string jsonFilePath)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] 기존 Excel 파일 업데이트 시작: {excelFilePath}");
-
- if (!File.Exists(excelFilePath))
- {
- Debug.WriteLine($"❌ Excel 파일이 존재하지 않습니다: {excelFilePath}");
- return false;
- }
-
- if (!File.Exists(jsonFilePath))
- {
- Debug.WriteLine($"❌ JSON 파일이 존재하지 않습니다: {jsonFilePath}");
- return false;
- }
-
- Debug.WriteLine($"[DEBUG] Excel 애플리케이션 초기화 중...");
-
- // 기존 Excel 파일 열기
- if (excelApplication == null)
- {
- excelApplication = new Excel.Application();
- excelApplication.Visible = false;
- Debug.WriteLine("[DEBUG] 새 Excel 애플리케이션 생성됨");
- }
-
- Debug.WriteLine($"[DEBUG] Excel 파일 열기 시도: {excelFilePath}");
- mappingWorkbook = excelApplication.Workbooks.Open(excelFilePath);
- Debug.WriteLine("[DEBUG] Excel 파일 열기 성공");
-
- Debug.WriteLine("[DEBUG] 'Mapping Data' 시트 찾는 중...");
- mappingSheet = (Excel.Worksheet)mappingWorkbook.Sheets["Mapping Data"];
-
- if (mappingSheet == null)
- {
- Debug.WriteLine("❌ 'Mapping Data' 시트를 찾을 수 없습니다.");
- // 사용 가능한 시트 목록 출력
- Debug.WriteLine("사용 가능한 시트:");
- foreach (Excel.Worksheet sheet in mappingWorkbook.Sheets)
- {
- Debug.WriteLine($" - {sheet.Name}");
- }
- return false;
- }
-
- Debug.WriteLine("✅ 기존 Excel 파일 열기 성공");
-
- // JSON에서 PDF 값 업데이트
- Debug.WriteLine("[DEBUG] JSON 파싱 및 업데이트 시작");
- bool result = UpdateMappingSheetFromJson(jsonFilePath);
-
- Debug.WriteLine($"[DEBUG] 업데이트 결과: {result}");
- return result;
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 기존 Excel 파일 업데이트 중 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
- if (ex.InnerException != null)
- {
- Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
- }
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- return false;
- }
- }
-
- ///
- /// JSON 파일에서 PDF 분석 결과를 읽어 Excel 매핑 시트의 Pdf_value 컬럼을 업데이트합니다.
- ///
- /// PDF 분석 결과 JSON 파일 경로
- /// 성공 시 true, 실패 시 false
- public bool UpdateMappingSheetFromJson(string jsonFilePath)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] JSON 파일에서 PDF 값 업데이트 시작: {jsonFilePath}");
-
- if (!File.Exists(jsonFilePath))
- {
- Debug.WriteLine($"❌ JSON 파일이 존재하지 않습니다: {jsonFilePath}");
- return false;
- }
-
- if (mappingSheet == null)
- {
- Debug.WriteLine("❌ 매핑 시트가 초기화되지 않았습니다.");
- return false;
- }
-
- // JSON 파일 읽기 및 정리
- string jsonContent = File.ReadAllText(jsonFilePath, System.Text.Encoding.UTF8);
- Debug.WriteLine($"[DEBUG] JSON 파일 크기: {jsonContent.Length} bytes");
-
- // JSON 내용 정리 (주석 제거 등)
- jsonContent = CleanJsonContent(jsonContent);
-
- JObject jsonData;
- try
- {
- jsonData = JObject.Parse(jsonContent);
- }
- catch (Newtonsoft.Json.JsonReaderException jsonEx)
- {
- Debug.WriteLine($"❌ JSON 파싱 오류: {jsonEx.Message}");
- Debug.WriteLine($"❌ JSON 내용 미리보기 (첫 500자):");
- Debug.WriteLine(jsonContent.Length > 500 ? jsonContent.Substring(0, 500) + "..." : jsonContent);
- throw new System.Exception($"PDF 분석 JSON 파일 파싱 실패: {jsonEx.Message}\n파일: {jsonFilePath}");
- }
-
- var results = jsonData["results"] as JArray;
- if (results == null)
- {
- Debug.WriteLine("❌ JSON에서 'results' 배열을 찾을 수 없습니다.");
- Debug.WriteLine($"❌ JSON 루트 키들: {string.Join(", ", jsonData.Properties().Select(p => p.Name))}");
- return false;
- }
-
- int updatedCount = 0;
- int totalEntries = 0;
-
- foreach (JObject result in results)
- {
- var fileInfo = result["file_info"];
- var pdfAnalysis = result["pdf_analysis"];
-
- if (fileInfo == null || pdfAnalysis == null) continue;
-
- string fileName = fileInfo["name"]?.ToString();
- if (string.IsNullOrEmpty(fileName)) continue;
-
- // 파일 확장자 제거
- string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
- Debug.WriteLine($"[DEBUG] Processing PDF file: {fileNameWithoutExt}");
-
- // PDF 분석 결과의 각 필드 처리
- foreach (var property in pdfAnalysis.Cast())
- {
- string aiLabel = property.Name; // 예: "설계공구_Station_col1"
- var valueObj = property.Value as JObject;
-
- if (valueObj == null) continue;
-
- string pdfValue = valueObj["value"]?.ToString();
- if (string.IsNullOrEmpty(pdfValue)) continue;
-
- totalEntries++;
- Debug.WriteLine($"[DEBUG] Searching for match: FileName={fileNameWithoutExt}, AILabel={aiLabel}, Value={pdfValue}");
-
- // Excel 시트에서 매칭되는 행 찾기 및 업데이트
- if (UpdateExcelRow(fileNameWithoutExt, aiLabel, pdfValue))
- {
- updatedCount++;
- Debug.WriteLine($"✅ Updated: {fileNameWithoutExt} -> {aiLabel} = {pdfValue}");
- }
- else
- {
- Debug.WriteLine($"⚠️ No match found: {fileNameWithoutExt} -> {aiLabel}");
- }
- }
- }
-
- Debug.WriteLine($"[DEBUG] PDF 값 업데이트 완료: {updatedCount}/{totalEntries} 업데이트됨");
- return true;
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ JSON에서 PDF 값 업데이트 중 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 예외 타입: {ex.GetType().Name}");
- if (ex.InnerException != null)
- {
- Debug.WriteLine($" 내부 예외: {ex.InnerException.Message}");
- }
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- return false;
- }
- }
-
- ///
- /// Excel 매핑 시트에서 FileName과 AILabel이 매칭되는 행을 찾아 Pdf_value를 업데이트합니다.
- ///
- /// 파일명 (확장자 제외)
- /// AI 라벨 (예: "설계공구_Station_col1")
- /// PDF에서 추출된 값
- /// 업데이트 성공 시 true
- private bool UpdateExcelRow(string fileName, string aiLabel, string pdfValue)
- {
- try
- {
- // Excel 시트의 마지막 사용된 행 찾기
- Excel.Range usedRange = mappingSheet.UsedRange;
- if (usedRange == null) return false;
-
- int lastRow = usedRange.Rows.Count;
-
- // 2행부터 검색 (1행은 헤더)
- for (int row = 2; row <= lastRow; row++)
- {
- // Column 1: FileName, Column 3: AILabel 확인
- var cellFileName = mappingSheet.Cells[row, 1]?.Value?.ToString() ?? "";
- var cellAiLabel = mappingSheet.Cells[row, 3]?.Value?.ToString() ?? "";
-
- Debug.WriteLine($"[DEBUG] Row {row}: FileName='{cellFileName}', AILabel='{cellAiLabel}'");
-
- // 매칭 확인 (대소문자 구분 없이)
- if (string.Equals(cellFileName.Trim(), fileName.Trim(), StringComparison.OrdinalIgnoreCase) &&
- string.Equals(cellAiLabel.Trim(), aiLabel.Trim(), StringComparison.OrdinalIgnoreCase))
- {
- // Column 6: Pdf_value에 값 기록
- mappingSheet.Cells[row, 6] = pdfValue;
- Debug.WriteLine($"[DEBUG] Updated row {row}, column 6 with value: {pdfValue}");
- return true;
- }
- }
-
- return false; // 매칭되는 행을 찾지 못함
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ Excel 행 업데이트 중 오류: {ex.Message}");
- return false;
- }
- }
-
- // Paste the helper function from above here
- public string GetPromptFromAttributeReference(Transaction tr, BlockReference blockref, string tag)
- {
-
- string prompt = null;
-
-
- BlockTableRecord blockDef = tr.GetObject(blockref.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
- if (blockDef == null) return null;
-
- foreach (ObjectId objId in blockDef)
- {
- AttributeDefinition attDef = tr.GetObject(objId, OpenMode.ForRead) as AttributeDefinition;
- if (attDef != null)
- {
- if (attDef.Tag.Equals(tag, System.StringComparison.OrdinalIgnoreCase))
- {
- prompt = attDef.Prompt;
- break;
- }
- }
- }
-
-
- return prompt;
- }
-
- ///
- /// 현재 열려있는 Excel 워크북을 저장합니다.
- ///
- /// 성공 시 true, 실패 시 false
- public bool SaveExcel()
- {
- try
- {
- if (mappingWorkbook != null)
- {
- mappingWorkbook.Save();
- Debug.WriteLine("✅ Excel 파일 저장 완료");
- return true;
- }
-
- if (workbook1 != null)
- {
- workbook1.Save();
- Debug.WriteLine("✅ Excel 파일 저장 완료");
- return true;
- }
-
- Debug.WriteLine("❌ 저장할 워크북이 없습니다.");
- return false;
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ Excel 파일 저장 중 오류: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 매핑 워크북만 저장하고 닫습니다 (완전한 매핑용).
- ///
- /// 저장할 파일 경로
- public void SaveMappingWorkbookOnly(string savePath)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] 매핑 워크북 저장 시작: {savePath}");
-
- if (mappingWorkbook == null)
- {
- Debug.WriteLine("❌ 매핑 워크북이 초기화되지 않았습니다.");
- return;
- }
-
- string directory = Path.GetDirectoryName(savePath);
- if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- // 매핑 워크북만 저장 (Excel 2007+ 형식으로)
- mappingWorkbook.SaveAs(savePath,
- FileFormat: Excel.XlFileFormat.xlOpenXMLWorkbook,
- AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
-
- Debug.WriteLine($"✅ 매핑 워크북 저장 완료: {Path.GetFileName(savePath)}");
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 매핑 워크북 저장 중 오류: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
- }
- }
-
- ///
- /// DWG 전용 매핑 워크북을 생성하고 저장합니다 (PDF 컬럼 제외).
- ///
- /// 결과 파일 저장 폴더 경로
- public void SaveDwgOnlyMappingWorkbook(string resultFolderPath)
- {
- try
- {
- string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
- string savePath = Path.Combine(resultFolderPath, $"{timestamp}_DwgOnly_Mapping.xlsx");
-
- Debug.WriteLine($"[DEBUG] DWG 전용 매핑 워크북 생성 시작: {savePath}");
-
- // Excel 애플리케이션 초기화 확인
- if (excelApplication == null)
- {
- excelApplication = new Excel.Application();
- excelApplication.Visible = false;
- Debug.WriteLine("[DEBUG] 새 Excel 애플리케이션 생성됨 (DWG 전용)");
- }
-
- // DWG 전용 워크북 생성
- var dwgOnlyWorkbook = excelApplication.Workbooks.Add();
- var dwgOnlyWorksheet = (Excel.Worksheet)dwgOnlyWorkbook.Worksheets[1];
- dwgOnlyWorksheet.Name = "DWG Mapping Data";
-
- // 헤더 생성 (PDF Value 컬럼 제외)
- dwgOnlyWorksheet.Cells[1, 1] = "파일명";
- dwgOnlyWorksheet.Cells[1, 2] = "Map Key";
- dwgOnlyWorksheet.Cells[1, 3] = "AI Label";
- dwgOnlyWorksheet.Cells[1, 4] = "DWG Tag";
- dwgOnlyWorksheet.Cells[1, 5] = "DWG Value";
-
- // 헤더 스타일 적용
- 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;
-
- // 데이터 입력 (배치 처리로 성능 향상)
- int totalRows = FileToMapkeyToLabelTagValuePdf.Sum(f => f.Value.Count);
- if (totalRows > 0)
- {
- object[,] data = new object[totalRows, 5];
- int row = 0;
-
- foreach (var fileEntry in FileToMapkeyToLabelTagValuePdf)
- {
- 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();
-
- // 파일 저장
- string directory = Path.GetDirectoryName(savePath);
- if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- dwgOnlyWorkbook.SaveAs(savePath,
- FileFormat: Excel.XlFileFormat.xlOpenXMLWorkbook,
- AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
-
- Debug.WriteLine($"✅ DWG 전용 매핑 워크북 저장 완료: {Path.GetFileName(savePath)}");
-
- // 워크북 정리
- dwgOnlyWorkbook.Close(false);
- ReleaseComObject(dwgOnlyWorksheet);
- ReleaseComObject(dwgOnlyWorkbook);
-
- // 가비지 컬렉션 강제 실행으로 메모리 해제
- GC.Collect();
- GC.WaitForPendingFinalizers();
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ DWG 전용 매핑 워크북 저장 중 오류: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
- }
- }
-
- ///
- /// 현재 Excel 워크북을 지정된 경로에 저장하고 Excel 애플리케이션을 종료합니다.
- ///
- /// Excel 파일을 저장할 전체 경로
- public void SaveAndCloseExcel(string savePath)
- {
- if (workbook1 == null) return;
-
- try
- {
- string directory = Path.GetDirectoryName(savePath);
- if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- workbook1.SaveAs(savePath, AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
-
- // 매핑 데이터 워크북도 저장
- if (mappingWorkbook != null)
- {
- string mappingPath = Path.Combine(directory, Path.GetFileNameWithoutExtension(savePath) + "_Mapping.xlsx");
- mappingWorkbook.SaveAs(mappingPath, AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
- }
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"Excel 파일 저장 중 오류 발생: {ex.Message}");
- }
- finally
- {
- CloseExcelObjects();
- }
- }
-
- ///
- /// Excel 객체들을 닫습니다 (저장하지 않음).
- ///
- public void CloseExcelObjectsWithoutSaving()
- {
- try
- {
- Debug.WriteLine("[DEBUG] Excel 객체 정리 시작");
-
- if (workbook1 != null)
- {
- try { workbook1.Close(false); }
- catch { }
- }
- if (mappingWorkbook != null)
- {
- try { mappingWorkbook.Close(false); }
- catch { }
- }
- if (excelApplication != null)
- {
- try { excelApplication.Quit(); }
- catch { }
- }
-
- ReleaseExcelObjects();
-
- Debug.WriteLine("✅ Excel 객체 정리 완료");
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ Excel 객체 정리 중 오류: {ex.Message}");
- }
- }
-
- private void CloseExcelObjects()
- {
- if (workbook1 != null)
- {
- try { workbook1.Close(false); }
- catch { }
- }
- if (mappingWorkbook != null)
- {
- try { mappingWorkbook.Close(false); }
- catch { }
- }
- if (excelApplication != null)
- {
- try { excelApplication.Quit(); }
- catch { }
- }
-
- ReleaseExcelObjects();
- }
-
- private void ReleaseExcelObjects()
- {
- ReleaseComObject(titleBlockSheet);
- ReleaseComObject(textEntitiesSheet);
- ReleaseComObject(mappingSheet);
- ReleaseComObject(workbook1);
- ReleaseComObject(mappingWorkbook);
- ReleaseComObject(excelApplication);
-
- titleBlockSheet = null;
- textEntitiesSheet = null;
- mappingSheet = null;
- workbook1 = null;
- mappingWorkbook = null;
- excelApplication = null;
- }
-
- private void ReleaseComObject(object obj)
- {
- try
- {
- if (obj != null && Marshal.IsComObject(obj))
- {
- Marshal.ReleaseComObject(obj);
- }
- }
- catch (System.Exception)
- {
- // 해제 중 오류 발생 시 무시
- }
- finally
- {
- obj = null;
- }
- }
-
- ///
- /// Layer ID로부터 Layer 이름을 가져옵니다.
- ///
- /// Layer ObjectId
- /// 현재 트랜잭션
- /// 데이터베이스 객체
- /// Layer 이름 또는 빈 문자열
- private string GetLayerName(ObjectId layerId, Transaction transaction, Database database)
- {
- try
- {
- using (var layerTableRecord = transaction.GetObject(layerId, OpenMode.ForRead) as LayerTableRecord)
- {
- return layerTableRecord?.Name ?? "";
- }
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"Layer 이름 가져오기 오류: {ex.Message}");
- return "";
- }
- }
-
- ///
- /// 매핑 딕셔너리를 JSON 파일로 저장합니다.
- ///
- /// 저장할 JSON 파일 경로
- public void SaveMappingDictionary(string filePath)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] 매핑 딕셔너리 저장 시작: {filePath}");
-
- // 딕셔너리를 직렬화 가능한 형태로 변환
- var serializableData = new Dictionary>();
-
foreach (var fileEntry in FileToMapkeyToLabelTagValuePdf)
{
- var fileData = new Dictionary();
foreach (var mapEntry in fileEntry.Value)
- {
- fileData[mapEntry.Key] = new
- {
- AILabel = mapEntry.Value.Item1,
- DwgTag = mapEntry.Value.Item2,
- DwgValue = mapEntry.Value.Item3,
- PdfValue = mapEntry.Value.Item4
- };
- }
- serializableData[fileEntry.Key] = fileData;
- }
-
- // JSON으로 직렬화
- string jsonContent = JsonConvert.SerializeObject(serializableData, Formatting.Indented);
- File.WriteAllText(filePath, jsonContent, System.Text.Encoding.UTF8);
-
- Debug.WriteLine($"✅ 매핑 딕셔너리 저장 완료: {Path.GetFileName(filePath)}");
- Debug.WriteLine($"📊 저장된 파일 수: {FileToMapkeyToLabelTagValuePdf.Count}");
-
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 매핑 딕셔너리 저장 중 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
- }
- }
-
- ///
- /// JSON 파일에서 매핑 딕셔너리를 로드합니다.
- ///
- /// 로드할 JSON 파일 경로
- public void LoadMappingDictionary(string filePath)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] 매핑 딕셔너리 로드 시작: {filePath}");
-
- if (!File.Exists(filePath))
- {
- Debug.WriteLine($"⚠️ 매핑 파일이 존재하지 않습니다: {filePath}");
- return;
- }
-
- string jsonContent = File.ReadAllText(filePath, System.Text.Encoding.UTF8);
- Debug.WriteLine($"[DEBUG] JSON 내용 길이: {jsonContent.Length}");
-
- // JSON 내용 정리 (주석 제거 등)
- jsonContent = CleanJsonContent(jsonContent);
-
- Dictionary> deserializedData;
- try
- {
- deserializedData = JsonConvert.DeserializeObject>>(jsonContent);
- }
- catch (Newtonsoft.Json.JsonReaderException jsonEx)
- {
- Debug.WriteLine($"❌ JSON 파싱 오류: {jsonEx.Message}");
- Debug.WriteLine($"❌ JSON 내용 미리보기 (첫 500자):");
- Debug.WriteLine(jsonContent.Length > 500 ? jsonContent.Substring(0, 500) + "..." : jsonContent);
- throw new System.Exception($"매핑 JSON 파일 파싱 실패: {jsonEx.Message}\n파일: {filePath}");
- }
-
- // 새로운 딕셔너리 초기화
- FileToMapkeyToLabelTagValuePdf.Clear();
- Debug.WriteLine("[DEBUG] 기존 딕셔너리 초기화됨");
-
- if (deserializedData != null)
- {
- Debug.WriteLine($"[DEBUG] 역직렬화된 파일 수: {deserializedData.Count}");
-
- foreach (var fileEntry in deserializedData)
- {
- Debug.WriteLine($"[DEBUG] 파일 처리 중: {fileEntry.Key}");
- var fileData = new Dictionary();
-
- foreach (var mapEntry in fileEntry.Value)
- {
- var valueObj = mapEntry.Value;
- string aiLabel = valueObj["AILabel"]?.ToString() ?? "";
- string dwgTag = valueObj["DwgTag"]?.ToString() ?? "";
- string dwgValue = valueObj["DwgValue"]?.ToString() ?? "";
- string pdfValue = valueObj["PdfValue"]?.ToString() ?? "";
-
- fileData[mapEntry.Key] = (aiLabel, dwgTag, dwgValue, pdfValue);
- Debug.WriteLine($"[DEBUG] 항목 로드: {mapEntry.Key} -> AI:{aiLabel}, DWG:{dwgTag}, PDF:{pdfValue}");
- }
-
- FileToMapkeyToLabelTagValuePdf[fileEntry.Key] = fileData;
- Debug.WriteLine($"[DEBUG] 파일 {fileEntry.Key}: {fileData.Count}개 항목 로드됨");
- }
- }
-
- Debug.WriteLine($"✅ 매핑 딕셔너리 로드 완료");
- Debug.WriteLine($"📊 로드된 파일 수: {FileToMapkeyToLabelTagValuePdf.Count}");
- Debug.WriteLine($"📊 총 항목 수: {FileToMapkeyToLabelTagValuePdf.Sum(f => f.Value.Count)}");
-
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 매핑 딕셔너리 로드 중 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
- }
- }
-
- ///
- /// 매핑 딕셔너리 데이터를 Excel 시트에 기록합니다 (완전한 매핑용).
- ///
- public void WriteCompleteMapping()
- {
- try
- {
- Debug.WriteLine("[DEBUG] WriteCompleteMapping 시작");
- Debug.WriteLine($"[DEBUG] 매핑 딕셔너리 항목 수: {FileToMapkeyToLabelTagValuePdf.Count}");
-
- if (FileToMapkeyToLabelTagValuePdf.Count == 0)
- {
- Debug.WriteLine("⚠️ 매핑 딕셔너리가 비어있습니다.");
- return;
- }
-
- if (mappingSheet == null)
- {
- Debug.WriteLine("❌ 매핑 시트가 초기화되지 않았습니다.");
- return;
- }
-
- Debug.WriteLine($"[DEBUG] 매핑 시트 이름: {mappingSheet.Name}");
- Debug.WriteLine($"[DEBUG] 전체 데이터 항목 수: {FileToMapkeyToLabelTagValuePdf.Sum(f => f.Value.Count)}");
-
- // 샘플 데이터 출력 (처음 3개)
- int sampleCount = 0;
- foreach (var fileEntry in FileToMapkeyToLabelTagValuePdf.Take(2))
- {
- Debug.WriteLine($"[DEBUG] 파일: {fileEntry.Key}");
- foreach (var mapEntry in fileEntry.Value.Take(3))
{
var (aiLabel, dwgTag, dwgValue, pdfValue) = mapEntry.Value;
- Debug.WriteLine($"[DEBUG] {mapEntry.Key} -> AI:{aiLabel}, DWG:{dwgTag}, DWGVAL:{dwgValue}, PDF:{pdfValue}");
- sampleCount++;
+ if (!string.IsNullOrEmpty(pdfValue))
+ {
+ excelWriter.UpdateExcelRow(fileEntry.Key, aiLabel, pdfValue);
+ }
}
}
-
- // 기존 WriteMappingDataToExcel 메서드 호출
- WriteMappingDataToExcel();
-
- Debug.WriteLine("✅ WriteCompleteMapping 완료");
+ return true;
}
catch (System.Exception ex)
{
- Debug.WriteLine($"❌ WriteCompleteMapping 중 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
+ Debug.WriteLine($"❌ Excel 업데이트 중 오류: {ex.Message}");
+ return false;
}
}
- ///
- /// PDF 분석 결과 JSON으로 매핑 딕셔너리의 PdfValue를 업데이트합니다.
- ///
- /// PDF 분석 결과 JSON 파일 경로
- public void UpdateWithPdfData(string jsonFilePath)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] PDF 데이터로 매핑 딕셔너리 업데이트 시작: {jsonFilePath}");
-
- if (!File.Exists(jsonFilePath))
- {
- Debug.WriteLine($"❌ JSON 파일이 존재하지 않습니다: {jsonFilePath}");
- return;
- }
-
- // JSON 파일 읽기 및 정리
- string jsonContent = File.ReadAllText(jsonFilePath, System.Text.Encoding.UTF8);
- Debug.WriteLine($"[DEBUG] JSON 파일 크기: {jsonContent.Length} bytes");
-
- // JSON 내용 정리 (주석 제거 등)
- jsonContent = CleanJsonContent(jsonContent);
-
- JObject jsonData;
- try
- {
- jsonData = JObject.Parse(jsonContent);
- }
- catch (Newtonsoft.Json.JsonReaderException jsonEx)
- {
- Debug.WriteLine($"❌ JSON 파싱 오류: {jsonEx.Message}");
- Debug.WriteLine($"❌ JSON 내용 미리보기 (첫 500자):");
- Debug.WriteLine(jsonContent.Length > 500 ? jsonContent.Substring(0, 500) + "..." : jsonContent);
- throw new System.Exception($"PDF 분석 JSON 파일 파싱 실패: {jsonEx.Message}\n파일: {jsonFilePath}");
- }
-
- var results = jsonData["results"] as JArray;
- if (results == null)
- {
- Debug.WriteLine("❌ JSON에서 'results' 배열을 찾을 수 없습니다.");
- Debug.WriteLine($"❌ JSON 루트 키들: {string.Join(", ", jsonData.Properties().Select(p => p.Name))}");
- return;
- }
-
- int updatedCount = 0;
- int totalEntries = 0;
-
- foreach (JObject result in results)
- {
- var fileInfo = result["file_info"];
- var pdfAnalysis = result["pdf_analysis"];
-
- if (fileInfo == null || pdfAnalysis == null) continue;
-
- string fileName = fileInfo["name"]?.ToString();
- if (string.IsNullOrEmpty(fileName)) continue;
-
- // 파일 확장자 제거
- string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
- Debug.WriteLine($"[DEBUG] Processing PDF file: {fileNameWithoutExt}");
-
- // 해당 파일의 매핑 데이터 확인
- if (!FileToMapkeyToLabelTagValuePdf.ContainsKey(fileNameWithoutExt))
- {
- Debug.WriteLine($"⚠️ 매핑 데이터에 파일이 없습니다: {fileNameWithoutExt}");
- continue;
- }
-
- // PDF 분석 결과의 각 필드 처리
- foreach (var property in pdfAnalysis.Cast())
- {
- string aiLabel = property.Name; // 예: "설계공구_Station_col1"
- var valueObj = property.Value as JObject;
-
- if (valueObj == null) continue;
-
- string pdfValue = valueObj["value"]?.ToString();
- if (string.IsNullOrEmpty(pdfValue)) continue;
-
- totalEntries++;
-
- // 매핑 딕셔너리에서 해당 항목 찾기
- var fileData = FileToMapkeyToLabelTagValuePdf[fileNameWithoutExt];
-
- // AILabel로 매칭 찾기
- var matchingEntry = fileData.FirstOrDefault(kvp =>
- string.Equals(kvp.Value.Item1.Trim(), aiLabel.Trim(), StringComparison.OrdinalIgnoreCase));
-
- if (!string.IsNullOrEmpty(matchingEntry.Key))
- {
- // 기존 값 유지하면서 PdfValue만 업데이트
- var existingValue = matchingEntry.Value;
- fileData[matchingEntry.Key] = (existingValue.Item1, existingValue.Item2, existingValue.Item3, pdfValue);
- updatedCount++;
-
- Debug.WriteLine($"✅ Updated: {fileNameWithoutExt} -> {aiLabel} = {pdfValue}");
- }
- else
- {
- Debug.WriteLine($"⚠️ No match found: {fileNameWithoutExt} -> {aiLabel}");
- }
- }
- }
-
- Debug.WriteLine($"[DEBUG] PDF 데이터 업데이트 완료: {updatedCount}/{totalEntries} 업데이트됨");
-
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ PDF 데이터 업데이트 중 오류:");
- Debug.WriteLine($" 메시지: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
- }
- }
-
- ///
- /// 폴더 간 처리를 위해 누적된 데이터를 정리합니다.
- ///
- public void ClearAccumulatedData()
- {
- try
- {
- Debug.WriteLine("[DEBUG] 누적 데이터 정리 시작");
-
- FileToMapkeyToLabelTagValuePdf.Clear();
-
- titleBlockCurrentRow = 2;
- textEntitiesCurrentRow = 2;
- mappingDataCurrentRow = 2;
-
- Debug.WriteLine("✅ 누적 데이터 정리 완료");
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 누적 데이터 정리 중 오류: {ex.Message}");
- }
- }
-
- ///
- /// JSON 내용을 정리하여 파싱 가능한 상태로 만듭니다.
- /// 주석 제거 및 기타 무효한 문자 처리
- ///
- /// 원본 JSON 내용
- /// 정리된 JSON 내용
- private string CleanJsonContent(string jsonContent)
- {
- if (string.IsNullOrEmpty(jsonContent))
- return jsonContent;
-
- try
- {
- // 줄별로 처리하여 주석 제거
- var lines = jsonContent.Split('\n');
- var cleanedLines = new List();
-
- bool inMultiLineComment = false;
-
- foreach (string line in lines)
- {
- string processedLine = line;
-
- // 멀티라인 주석 처리 (/* */)
- if (inMultiLineComment)
- {
- int endIndex = processedLine.IndexOf("*/");
- if (endIndex >= 0)
- {
- processedLine = processedLine.Substring(endIndex + 2);
- inMultiLineComment = false;
- }
- else
- {
- continue; // 전체 라인이 주석
- }
- }
-
- // 멀티라인 주석 시작 확인
- int multiLineStart = processedLine.IndexOf("/*");
- if (multiLineStart >= 0)
- {
- int multiLineEnd = processedLine.IndexOf("*/", multiLineStart + 2);
- if (multiLineEnd >= 0)
- {
- // 같은 라인에서 시작하고 끝나는 주석
- processedLine = processedLine.Substring(0, multiLineStart) +
- processedLine.Substring(multiLineEnd + 2);
- }
- else
- {
- // 멀티라인 주석 시작
- processedLine = processedLine.Substring(0, multiLineStart);
- inMultiLineComment = true;
- }
- }
-
- // 싱글라인 주석 제거 (//) - 문자열 내부의 //는 제외
- bool inString = false;
- bool escaped = false;
- int commentIndex = -1;
-
- for (int i = 0; i < processedLine.Length - 1; i++)
- {
- char current = processedLine[i];
- char next = processedLine[i + 1];
-
- if (escaped)
- {
- escaped = false;
- continue;
- }
-
- if (current == '\\')
- {
- escaped = true;
- continue;
- }
-
- if (current == '"')
- {
- inString = !inString;
- continue;
- }
-
- if (!inString && current == '/' && next == '/')
- {
- commentIndex = i;
- break;
- }
- }
-
- if (commentIndex >= 0)
- {
- processedLine = processedLine.Substring(0, commentIndex);
- }
-
- // 빈 라인이 아니면 추가
- if (!string.IsNullOrWhiteSpace(processedLine))
- {
- cleanedLines.Add(processedLine);
- }
- }
-
- string result = string.Join("\n", cleanedLines);
- Debug.WriteLine($"[DEBUG] JSON 정리 완료: {jsonContent.Length} -> {result.Length} bytes");
- return result;
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ JSON 정리 중 오류: {ex.Message}");
- // 정리 실패시 원본 반환
- return jsonContent;
- }
- }
-
- ///
- /// DWG 파일들을 처리하여 각 파일별로 시트를 생성하고 Height 순으로 정렬된 Excel 파일을 생성합니다.
- ///
- /// 처리할 DWG 파일 경로 배열
- /// 결과 파일 저장 폴더
- public void ExportDwgToExcelHeightSorted(string[] dwgFiles, string resultFolder)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] Height 정렬 Excel 생성 시작: {dwgFiles.Length}개 파일");
-
- string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
- string savePath = Path.Combine(resultFolder, $"{timestamp}_HeightSorted.xlsx");
-
- // 새로운 Excel 워크북 생성
- if (excelApplication == null)
- {
- excelApplication = new Excel.Application();
- excelApplication.Visible = false;
- }
-
- var heightSortedWorkbook = excelApplication.Workbooks.Add();
-
- Debug.WriteLine($"[DEBUG] DWG 파일 처리 시작");
-
- bool firstSheetProcessed = false;
-
- foreach (string dwgFile in dwgFiles)
- {
- if (!File.Exists(dwgFile))
- {
- Debug.WriteLine($"[DEBUG] 파일이 존재하지 않음: {dwgFile}");
- continue;
- }
-
- string fileName = Path.GetFileNameWithoutExtension(dwgFile);
- Debug.WriteLine($"[DEBUG] 처리 중인 파일: {fileName}");
-
- try
- {
- Excel.Worksheet worksheet;
- if (!firstSheetProcessed)
- {
- // 첫 번째 파일은 기본 시트 사용
- worksheet = (Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
- firstSheetProcessed = true;
- }
- else
- {
- // 이후 파일들은 새 시트 생성
- worksheet = (Excel.Worksheet)heightSortedWorkbook.Worksheets.Add();
- }
- worksheet.Name = GetValidSheetName(fileName);
-
- // 헤더 설정
- 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";
-
- // 헤더 스타일 적용
- var headerRange = worksheet.Range["A1:F1"];
- headerRange.Font.Bold = true;
- headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
-
- // DWG 파일에서 텍스트 엔티티 추출
- var textEntities = ExtractTextEntitiesWithHeight(dwgFile);
-
- // Height 순으로 내림차순 정렬
- var sortedEntities = textEntities.OrderByDescending(entity => entity.Height).ToList();
-
- Debug.WriteLine($"[DEBUG] {fileName}: {sortedEntities.Count}개 텍스트 엔티티 추출됨");
-
- // 데이터 입력
- int row = 2;
- foreach (var entity in sortedEntities)
- {
- 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();
-
- Debug.WriteLine($"[DEBUG] {fileName} 시트 완료: {sortedEntities.Count}개 행");
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ {fileName} 처리 중 오류: {ex.Message}");
- continue;
- }
- }
-
- // DWG 파일이 하나도 없었다면 기본 시트에 메시지 추가
- if (!firstSheetProcessed)
- {
- var defaultSheet = (Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
- defaultSheet.Name = "No_DWG_Files";
- defaultSheet.Cells[1, 1] = "No DWG files found in this folder";
- Debug.WriteLine("[DEBUG] DWG 파일이 없어 기본 메시지 시트 생성");
- }
-
- // 파일 저장
- string directory = Path.GetDirectoryName(savePath);
- if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- heightSortedWorkbook.SaveAs(savePath,
- FileFormat: Excel.XlFileFormat.xlOpenXMLWorkbook,
- AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
-
- Debug.WriteLine($"✅ Height 정렬 Excel 파일 저장 완료: {Path.GetFileName(savePath)}");
-
- // 워크북 정리
- heightSortedWorkbook.Close(false);
- ReleaseComObject(heightSortedWorkbook);
-
- GC.Collect();
- GC.WaitForPendingFinalizers();
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ Height 정렬 Excel 생성 중 오류: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
- }
- }
-
- ///
- /// DWG 파일에서 텍스트 엔티티들을 추출하여 Height 정보와 함께 반환합니다.
- ///
- /// DWG 파일 경로
- /// 텍스트 엔티티 정보 리스트
- private List ExtractTextEntitiesWithHeight(string filePath)
- {
- var textEntities = new List();
-
- try
- {
- using (var database = new Database(false, true))
- {
- database.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
-
- using (var tran = database.TransactionManager.StartTransaction())
- {
- var bt = tran.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
- using (var btr = tran.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord)
- {
- foreach (ObjectId entId in btr)
- {
- using (var ent = tran.GetObject(entId, OpenMode.ForRead) as Entity)
- {
- string layerName = GetLayerName(ent.LayerId, tran, database);
-
- // AttributeReference 처리
- if (ent is BlockReference blr)
- {
- foreach (ObjectId attId in blr.AttributeCollection)
- {
- using (var attRef = tran.GetObject(attId, OpenMode.ForRead) as AttributeReference)
- {
- if (attRef != null && !string.IsNullOrWhiteSpace(attRef.TextString))
- {
- textEntities.Add(new TextEntityInfo
- {
- Height = attRef.Height,
- Type = "AttRef",
- Layer = layerName,
- Tag = attRef.Tag,
- Text = attRef.TextString
- });
- }
- }
- }
- }
- // DBText 처리
- else if (ent is DBText dbText)
- {
- textEntities.Add(new TextEntityInfo
- {
- Height = dbText.Height,
- Type = "DBText",
- Layer = layerName,
- Tag = "",
- Text = dbText.TextString
- });
- }
- // MText 처리
- else if (ent is MText mText)
- {
- textEntities.Add(new TextEntityInfo
- {
- Height = mText.Height,
- Type = "MText",
- Layer = layerName,
- Tag = "",
- Text = mText.Contents
- });
- }
- }
- }
- }
-
- tran.Commit();
- }
- }
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 텍스트 엔티티 추출 중 오류 ({Path.GetFileName(filePath)}): {ex.Message}");
- }
-
- return textEntities;
- }
-
- ///
- /// 모든 DWG 파일들을 하나의 Excel 파일로 처리하여 각 파일별로 시트를 생성하고 Height 순으로 정렬합니다.
- ///
- /// 처리할 DWG 파일 리스트 (파일경로, 폴더명)
- /// 결과 Excel 파일 저장 경로
- public void ExportAllDwgToExcelHeightSorted(List<(string filePath, string folderName)> allDwgFiles, string savePath)
- {
- try
- {
- Debug.WriteLine($"[DEBUG] 단일 Excel 파일로 Height 정렬 생성 시작: {allDwgFiles.Count}개 파일");
-
- // 새로운 Excel 워크북 생성
- if (excelApplication == null)
- {
- excelApplication = new Excel.Application();
- excelApplication.Visible = false;
- }
-
- var heightSortedWorkbook = excelApplication.Workbooks.Add();
-
- Debug.WriteLine($"[DEBUG] DWG 파일 처리 시작");
-
- bool firstSheetProcessed = false;
-
- foreach (var (filePath, folderName) in allDwgFiles)
- {
- if (!File.Exists(filePath))
- {
- Debug.WriteLine($"[DEBUG] 파일이 존재하지 않음: {filePath}");
- continue;
- }
-
- string fileName = Path.GetFileNameWithoutExtension(filePath);
- Debug.WriteLine($"[DEBUG] 처리 중인 파일: {fileName} (폴더: {folderName})");
-
- try
- {
- Excel.Worksheet worksheet;
- if (!firstSheetProcessed)
- {
- // 첫 번째 파일은 기본 시트 사용
- worksheet = (Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
- firstSheetProcessed = true;
- }
- else
- {
- // 이후 파일들은 새 시트 생성
- worksheet = (Excel.Worksheet)heightSortedWorkbook.Worksheets.Add();
- }
- worksheet.Name = GetValidSheetName(fileName);
-
- // 헤더 설정
- 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";
-
- // 헤더 스타일 적용
- var headerRange = worksheet.Range["A1:F1"];
- headerRange.Font.Bold = true;
- headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue);
-
- // DWG 파일에서 텍스트 엔티티 추출
- var textEntities = ExtractTextEntitiesWithHeight(filePath);
-
- // Height 순으로 내림차순 정렬
- var sortedEntities = textEntities.OrderByDescending(entity => entity.Height).ToList();
-
- Debug.WriteLine($"[DEBUG] {fileName}: {sortedEntities.Count}개 텍스트 엔티티 추출됨");
-
- // 데이터 입력
- int row = 2;
- foreach (var entity in sortedEntities)
- {
- 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();
-
- Debug.WriteLine($"[DEBUG] {fileName} 시트 완료: {sortedEntities.Count}개 행");
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ {fileName} 처리 중 오류: {ex.Message}");
- continue;
- }
- }
-
- // DWG 파일이 하나도 없었다면 기본 시트에 메시지 추가
- if (!firstSheetProcessed)
- {
- var defaultSheet = (Excel.Worksheet)heightSortedWorkbook.Worksheets[1];
- defaultSheet.Name = "No_DWG_Files";
- defaultSheet.Cells[1, 1] = "No DWG files found in any folder";
- Debug.WriteLine("[DEBUG] DWG 파일이 없어 기본 메시지 시트 생성");
- }
-
- // 파일 저장
- string directory = Path.GetDirectoryName(savePath);
- if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- heightSortedWorkbook.SaveAs(savePath,
- FileFormat: Excel.XlFileFormat.xlOpenXMLWorkbook,
- AccessMode: Excel.XlSaveAsAccessMode.xlNoChange);
-
- Debug.WriteLine($"✅ 단일 Height 정렬 Excel 파일 저장 완료: {Path.GetFileName(savePath)}");
-
- // 워크북 정리
- heightSortedWorkbook.Close(false);
- ReleaseComObject(heightSortedWorkbook);
-
- GC.Collect();
- GC.WaitForPendingFinalizers();
- }
- catch (System.Exception ex)
- {
- Debug.WriteLine($"❌ 단일 Height 정렬 Excel 생성 중 오류: {ex.Message}");
- Debug.WriteLine($" 스택 트레이스: {ex.StackTrace}");
- throw;
- }
- }
-
- ///
- /// Excel 시트명으로 사용할 수 있는 유효한 이름을 생성합니다.
- ///
- /// 원본 파일명
- /// 유효한 시트명
- private string GetValidSheetName(string originalName)
- {
- if (string.IsNullOrEmpty(originalName))
- return "Sheet";
-
- // Excel 시트명에서 허용되지 않는 문자 제거
- string validName = originalName;
- char[] invalidChars = { '\\', '/', '?', '*', '[', ']', ':' };
-
- foreach (char c in invalidChars)
- {
- validName = validName.Replace(c, '_');
- }
-
- // 31자로 제한 (Excel 시트명 최대 길이)
- if (validName.Length > 31)
- {
- validName = validName.Substring(0, 31);
- }
-
- return validName;
- }
-
public void Dispose()
{
try
{
Debug.WriteLine("[DEBUG] ExportExcel Dispose 시작");
- if (excelApplication != null)
- {
- Debug.WriteLine("[DEBUG] Excel 객체 정리 중...");
- CloseExcelObjects();
- }
+ excelManager?.Dispose();
if (appServices != null)
{
@@ -1956,20 +419,7 @@ namespace DwgExtractorManual.Models
catch (System.Exception ex)
{
Debug.WriteLine($"[DEBUG] ExportExcel Dispose 중 전역 오류: {ex.Message}");
- // Disposal 오류는 로그만 남기고 계속 진행
}
}
}
-
- ///
- /// 텍스트 엔티티 정보를 담는 클래스
- ///
- public class TextEntityInfo
- {
- public double Height { get; set; }
- public string Type { get; set; }
- public string Layer { get; set; }
- public string Tag { get; set; }
- public string Text { get; set; }
- }
}
diff --git a/Models/JsonDataProcessor.cs b/Models/JsonDataProcessor.cs
new file mode 100644
index 0000000..65df6c3
--- /dev/null
+++ b/Models/JsonDataProcessor.cs
@@ -0,0 +1,317 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace DwgExtractorManual.Models
+{
+ ///
+ /// JSON ó ϴ Ŭ
+ ///
+ internal class JsonDataProcessor
+ {
+ ///
+ /// JSON Ͽ PDF м о Ʈ
+ ///
+ public bool UpdateMappingDataFromJson(Dictionary> mappingData, string jsonFilePath)
+ {
+ try
+ {
+ Debug.WriteLine($"[DEBUG] JSON Ͽ PDF Ʈ : {jsonFilePath}");
+
+ if (!File.Exists(jsonFilePath))
+ {
+ Debug.WriteLine($"? JSON ʽϴ: {jsonFilePath}");
+ return false;
+ }
+
+ // JSON б
+ string jsonContent = File.ReadAllText(jsonFilePath, System.Text.Encoding.UTF8);
+ jsonContent = CleanJsonContent(jsonContent);
+
+ JObject jsonData;
+ try
+ {
+ jsonData = JObject.Parse(jsonContent);
+ }
+ catch (Newtonsoft.Json.JsonReaderException jsonEx)
+ {
+ Debug.WriteLine($"? JSON Ľ : {jsonEx.Message}");
+ throw new System.Exception($"PDF м JSON Ľ : {jsonEx.Message}\n: {jsonFilePath}");
+ }
+
+ var results = jsonData["results"] as JArray;
+ if (results == null)
+ {
+ Debug.WriteLine("? JSON 'results' 迭 ã ϴ.");
+ return false;
+ }
+
+ int updatedCount = 0;
+ int totalEntries = 0;
+
+ foreach (JObject result in results)
+ {
+ var fileInfo = result["file_info"];
+ var pdfAnalysis = result["pdf_analysis"];
+
+ if (fileInfo == null || pdfAnalysis == null) continue;
+
+ string fileName = fileInfo["name"]?.ToString();
+ if (string.IsNullOrEmpty(fileName)) continue;
+
+ string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
+
+ if (!mappingData.ContainsKey(fileNameWithoutExt))
+ {
+ Debug.WriteLine($"?? Ϳ ϴ: {fileNameWithoutExt}");
+ continue;
+ }
+
+ foreach (var property in pdfAnalysis.Cast())
+ {
+ string aiLabel = property.Name;
+ var valueObj = property.Value as JObject;
+
+ if (valueObj == null) continue;
+
+ string pdfValue = valueObj["value"]?.ToString();
+ if (string.IsNullOrEmpty(pdfValue)) continue;
+
+ totalEntries++;
+
+ var fileData = mappingData[fileNameWithoutExt];
+ var matchingEntry = fileData.FirstOrDefault(kvp =>
+ string.Equals(kvp.Value.Item1.Trim(), aiLabel.Trim(), StringComparison.OrdinalIgnoreCase));
+
+ if (!string.IsNullOrEmpty(matchingEntry.Key))
+ {
+ var existingValue = matchingEntry.Value;
+ fileData[matchingEntry.Key] = (existingValue.Item1, existingValue.Item2, existingValue.Item3, pdfValue);
+ updatedCount++;
+ }
+ }
+ }
+
+ Debug.WriteLine($"[DEBUG] PDF Ʈ Ϸ: {updatedCount}/{totalEntries} Ʈ");
+ return true;
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? JSON PDF Ʈ : {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// ųʸ JSON Ϸ
+ ///
+ public void SaveMappingDictionary(Dictionary> mappingData, string filePath)
+ {
+ try
+ {
+ Debug.WriteLine($"[DEBUG] ųʸ : {filePath}");
+
+ var serializableData = new Dictionary>();
+
+ foreach (var fileEntry in mappingData)
+ {
+ var fileData = new Dictionary();
+ foreach (var mapEntry in fileEntry.Value)
+ {
+ fileData[mapEntry.Key] = new
+ {
+ AILabel = mapEntry.Value.Item1,
+ DwgTag = mapEntry.Value.Item2,
+ DwgValue = mapEntry.Value.Item3,
+ PdfValue = mapEntry.Value.Item4
+ };
+ }
+ serializableData[fileEntry.Key] = fileData;
+ }
+
+ string jsonContent = JsonConvert.SerializeObject(serializableData, Formatting.Indented);
+ File.WriteAllText(filePath, jsonContent, System.Text.Encoding.UTF8);
+
+ Debug.WriteLine($"? ųʸ Ϸ: {Path.GetFileName(filePath)}");
+ Debug.WriteLine($"?? : {mappingData.Count}");
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? ųʸ : {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// JSON Ͽ ųʸ ε
+ ///
+ public Dictionary> LoadMappingDictionary(string filePath)
+ {
+ var result = new Dictionary>();
+
+ try
+ {
+ Debug.WriteLine($"[DEBUG] ųʸ ε : {filePath}");
+
+ if (!File.Exists(filePath))
+ {
+ Debug.WriteLine($"?? ʽϴ: {filePath}");
+ return result;
+ }
+
+ string jsonContent = File.ReadAllText(filePath, System.Text.Encoding.UTF8);
+ jsonContent = CleanJsonContent(jsonContent);
+
+ Dictionary> deserializedData;
+ try
+ {
+ deserializedData = JsonConvert.DeserializeObject>>(jsonContent);
+ }
+ catch (Newtonsoft.Json.JsonReaderException jsonEx)
+ {
+ Debug.WriteLine($"? JSON Ľ : {jsonEx.Message}");
+ throw new System.Exception($" JSON Ľ : {jsonEx.Message}\n: {filePath}");
+ }
+
+ if (deserializedData != null)
+ {
+ foreach (var fileEntry in deserializedData)
+ {
+ var fileData = new Dictionary();
+
+ foreach (var mapEntry in fileEntry.Value)
+ {
+ var valueObj = mapEntry.Value;
+ string aiLabel = valueObj["AILabel"]?.ToString() ?? "";
+ string dwgTag = valueObj["DwgTag"]?.ToString() ?? "";
+ string dwgValue = valueObj["DwgValue"]?.ToString() ?? "";
+ string pdfValue = valueObj["PdfValue"]?.ToString() ?? "";
+
+ fileData[mapEntry.Key] = (aiLabel, dwgTag, dwgValue, pdfValue);
+ }
+
+ result[fileEntry.Key] = fileData;
+ }
+ }
+
+ Debug.WriteLine($"? ųʸ ε Ϸ");
+ Debug.WriteLine($"?? ε : {result.Count}");
+
+ return result;
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? ųʸ ε : {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// JSON Ͽ Ľ · ϴ.
+ ///
+ private string CleanJsonContent(string jsonContent)
+ {
+ if (string.IsNullOrEmpty(jsonContent))
+ return jsonContent;
+
+ try
+ {
+ var lines = jsonContent.Split('\n');
+ var cleanedLines = new List();
+ bool inMultiLineComment = false;
+
+ foreach (string line in lines)
+ {
+ string processedLine = line;
+
+ // Ƽ ּ ó
+ if (inMultiLineComment)
+ {
+ int endIndex = processedLine.IndexOf("*/");
+ if (endIndex >= 0)
+ {
+ processedLine = processedLine.Substring(endIndex + 2);
+ inMultiLineComment = false;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ int multiLineStart = processedLine.IndexOf("/*");
+ if (multiLineStart >= 0)
+ {
+ int multiLineEnd = processedLine.IndexOf("*/", multiLineStart + 2);
+ if (multiLineEnd >= 0)
+ {
+ processedLine = processedLine.Substring(0, multiLineStart) +
+ processedLine.Substring(multiLineEnd + 2);
+ }
+ else
+ {
+ processedLine = processedLine.Substring(0, multiLineStart);
+ inMultiLineComment = true;
+ }
+ }
+
+ // ̱۶ ּ
+ bool inString = false;
+ bool escaped = false;
+ int commentIndex = -1;
+
+ for (int i = 0; i < processedLine.Length - 1; i++)
+ {
+ char current = processedLine[i];
+ char next = processedLine[i + 1];
+
+ if (escaped)
+ {
+ escaped = false;
+ continue;
+ }
+
+ if (current == '\\')
+ {
+ escaped = true;
+ continue;
+ }
+
+ if (current == '"')
+ {
+ inString = !inString;
+ continue;
+ }
+
+ if (!inString && current == '/' && next == '/')
+ {
+ commentIndex = i;
+ break;
+ }
+ }
+
+ if (commentIndex >= 0)
+ {
+ processedLine = processedLine.Substring(0, commentIndex);
+ }
+
+ if (!string.IsNullOrWhiteSpace(processedLine))
+ {
+ cleanedLines.Add(processedLine);
+ }
+ }
+
+ return string.Join("\n", cleanedLines);
+ }
+ catch (System.Exception ex)
+ {
+ Debug.WriteLine($"? JSON : {ex.Message}");
+ return jsonContent;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Models/SettingsManager.cs b/Models/SettingsManager.cs
new file mode 100644
index 0000000..e69de29