diff --git a/PLibs/PLibs.sln b/PLibs/PLibs.sln
index f9fe5ad..ce68421 100644
--- a/PLibs/PLibs.sln
+++ b/PLibs/PLibs.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelHelper", "ExcelHelper\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelHelperCore", "ExcelHelperCore\ExcelHelperCore.csproj", "{0DC75403-4084-462B-936B-FA86EA90811B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfHelper", "PdfHelper\PdfHelper.csproj", "{4F62138C-7B92-4FF8-8CB3-F37980AE99F2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{0DC75403-4084-462B-936B-FA86EA90811B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DC75403-4084-462B-936B-FA86EA90811B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DC75403-4084-462B-936B-FA86EA90811B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F62138C-7B92-4FF8-8CB3-F37980AE99F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F62138C-7B92-4FF8-8CB3-F37980AE99F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F62138C-7B92-4FF8-8CB3-F37980AE99F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F62138C-7B92-4FF8-8CB3-F37980AE99F2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/PLibs/PdfHelper/PdfHelper.cs b/PLibs/PdfHelper/PdfHelper.cs
new file mode 100644
index 0000000..8828ca0
--- /dev/null
+++ b/PLibs/PdfHelper/PdfHelper.cs
@@ -0,0 +1,138 @@
+using PdfSharp.Drawing;
+using PdfSharp.Pdf;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+
+namespace PdfHelper
+{
+ public class PdfHelper
+ {
+ readonly DataTable _table;
+ readonly int _timestampColIdx = 0;
+
+ // Page structure
+ readonly int PAGE_HEIGHT = 842; // A4
+ readonly int PAGE_WIDTH = 595; // A4
+
+ readonly string FONT_NAME = "Malgun Gothic";
+ readonly double FONT_SIZE = 6d;
+
+ readonly int MARGIN_LEFT = 20;
+ readonly int MARGIN_TOP = 35;
+
+ // Elements
+ readonly double TIMESTAMP_CELL_WIDTH = 70;
+
+ public readonly double MAX_CELL_COLUMN_COUNT = 10;
+ readonly double ALL_CELL_WIDTH = 485;
+ readonly double CELL_WIDTH = 48.5;
+ readonly int CELL_HEIGHT = 15;
+
+ // Design
+ readonly XSolidBrush HEADER_RECT_STYLE = new XSolidBrush(XColor.FromArgb(210, 51, 52));
+ readonly XPen RECT_EDGE_PEN = new XPen(XColor.FromArgb(34, 30, 32), 0.5d);
+ readonly XBrush HEADER_STRING_COLOR = new XSolidBrush(XColors.White);
+ readonly XBrush STRING_COLOR = new XSolidBrush(XColor.FromArgb(34, 30, 32));
+
+ readonly int ROW_COUNT_PER_PAGE = 50;
+
+ PdfDocument _document;
+ XFont _font;
+ XFont _boldFont;
+ XStringFormat _format;
+
+ public PdfHelper(DataTable table, int timestampColIdx = 0)
+ {
+ if (table.Columns.Count - 1 > MAX_CELL_COLUMN_COUNT)
+ throw new ArgumentException($"The number of data should be less than {MAX_CELL_COLUMN_COUNT}");
+
+ _table = table;
+ _timestampColIdx = timestampColIdx;
+
+ // Check cell width
+ CELL_WIDTH = ALL_CELL_WIDTH / (table.Columns.Count - 1);
+
+ _document = new PdfDocument();
+ _document.Info.Title = "NS Report";
+ _document.Info.Author = "Particle Measuring Systems Korea";
+
+ _font = new XFont(FONT_NAME, FONT_SIZE, XFontStyle.Regular);
+ _boldFont = new XFont(FONT_NAME, FONT_SIZE, XFontStyle.Bold);
+ _format = new XStringFormat();
+ _format.LineAlignment = XLineAlignment.Center;
+ _format.Alignment = XStringAlignment.Center;
+ }
+
+ public void Save(string targetPath)
+ {
+ for (int startRow = 0; startRow < _table.Rows.Count; startRow += ROW_COUNT_PER_PAGE)
+ {
+ PdfPage page = _document.AddPage();
+ page.Size = PdfSharp.PageSize.A4;
+ XGraphics gf = XGraphics.FromPdfPage(page);
+
+ // Header
+ double x = MARGIN_LEFT;
+ double y = MARGIN_TOP;
+ for (int col = 0; col < _table.Columns.Count; col++)
+ {
+ double increment = 0;
+ XRect rect;
+ if (col == _timestampColIdx)
+ {
+ rect = new XRect(x, y, TIMESTAMP_CELL_WIDTH, CELL_HEIGHT);
+ increment = TIMESTAMP_CELL_WIDTH;
+ }
+ else
+ {
+ rect = new XRect(x, y, CELL_WIDTH, CELL_HEIGHT);
+ increment = CELL_WIDTH;
+ }
+
+ gf.DrawRectangle(HEADER_RECT_STYLE, rect);
+ gf.DrawRectangle(RECT_EDGE_PEN, rect);
+ gf.DrawString(_table.Columns[col].ColumnName, _boldFont, HEADER_STRING_COLOR, rect, _format);
+
+ x += increment;
+ }
+
+ // Row
+ int endRow = Math.Min(startRow + ROW_COUNT_PER_PAGE, _table.Rows.Count);
+ for (int row = startRow; row < endRow; row++)
+ {
+ x = MARGIN_LEFT;
+ y += CELL_HEIGHT;
+ for (int col = 0; col < _table.Columns.Count; col++)
+ {
+ double increment = 0;
+ XRect rect;
+ string value;
+ if (col == _timestampColIdx)
+ {
+ rect = new XRect(x, y, TIMESTAMP_CELL_WIDTH, CELL_HEIGHT);
+ DateTime date = (DateTime)_table.Rows[row][col];
+ value = date.ToString("yyyy-MM-dd HH:mm:ss");
+ increment = TIMESTAMP_CELL_WIDTH;
+ }
+ else
+ {
+ rect = new XRect(x, y, CELL_WIDTH, CELL_HEIGHT);
+ value = _table.Rows[row][col].ToString();
+ increment = CELL_WIDTH;
+ }
+
+ gf.DrawRectangle(RECT_EDGE_PEN, rect);
+ gf.DrawString(value, _font, STRING_COLOR, rect, _format);
+
+ x += increment;
+ }
+ }
+ }
+
+ _document.Save(targetPath);
+ }
+ }
+}
diff --git a/PLibs/PdfHelper/PdfHelper.csproj b/PLibs/PdfHelper/PdfHelper.csproj
new file mode 100644
index 0000000..7a8b740
--- /dev/null
+++ b/PLibs/PdfHelper/PdfHelper.csproj
@@ -0,0 +1,57 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {4F62138C-7B92-4FF8-8CB3-F37980AE99F2}
+ Library
+ Properties
+ PdfHelper
+ PdfHelper
+ v4.0
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\PDFsharp.1.50.5147\lib\net20\PdfSharp.dll
+
+
+ ..\packages\PDFsharp.1.50.5147\lib\net20\PdfSharp.Charting.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PLibs/PdfHelper/Properties/AssemblyInfo.cs b/PLibs/PdfHelper/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..e1b7b49
--- /dev/null
+++ b/PLibs/PdfHelper/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
+// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
+// 이러한 특성 값을 변경하세요.
+[assembly: AssemblyTitle("PdfHelper")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PdfHelper")]
+[assembly: AssemblyCopyright("Copyright © 2024")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
+// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
+// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
+[assembly: ComVisible(false)]
+
+// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
+[assembly: Guid("4f62138c-7b92-4ff8-8cb3-f37980ae99f2")]
+
+// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
+//
+// 주 버전
+// 부 버전
+// 빌드 번호
+// 수정 버전
+//
+// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
+// 기본값으로 할 수 있습니다.
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/PLibs/PdfHelper/packages.config b/PLibs/PdfHelper/packages.config
new file mode 100644
index 0000000..841ada4
--- /dev/null
+++ b/PLibs/PdfHelper/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file