Files
AiQ_GUI/PDF.cs

402 lines
18 KiB
C#
Raw Normal View History

2025-09-02 15:32:24 +01:00
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
using Image = MigraDoc.DocumentObjectModel.Shapes.Image;
namespace AiQ_GUI
{
internal class PDF
{
public const string TestRecordDir = "G:\\Shared drives\\MAV Production Test Records\\AiQ\\";
public const string ImageDir = $"{GoogleAPI.DrivePath}AiQ\\GUI's\\";
public static void CreateFinalTestReport(Camera CamOnTest, string UserName, string fulltestvalues, DateTime PCTime)
{
// Create a new PDF document
Document document = new();
Section section = document.AddSection();
// Create a table with two columns
Table latable = section.AddTable();
latable.Borders.Visible = false; // No visible borders
latable.AddColumn(Unit.FromCentimeter(13)); // Left column for MAV logo
latable.AddColumn(Unit.FromCentimeter(3)); // Right column for AiQ logo
// Add a row for the logos
Row larow = latable.AddRow();
Cell cellMAV = larow.Cells[0];
Cell cellAiQ = larow.Cells[1];
// Add MAV logo to the left cell
Image imageMAV = cellMAV.AddImage($"{ImageDir}MAV-Logo.png");
imageMAV.LockAspectRatio = true;
imageMAV.Height = Unit.FromCentimeter(1); // Set height to 1 cm
// Add AiQ logo to the right cell
Image imageAiQ = cellAiQ.AddImage($"{ImageDir}AiQ-Logo.png");
imageAiQ.LockAspectRatio = true;
imageAiQ.Height = Unit.FromCentimeter(1.25); // Set height to 1 cm
// Add spacing below the table
Paragraph tableParagraph = section.AddParagraph();
tableParagraph.Format.SpaceAfter = "0.5cm";
// Add Title
Paragraph title = section.AddParagraph("Certificate of Conformity");
title.Format.Font.Size = 20;
title.Format.Font.Bold = true;
title.Format.Alignment = ParagraphAlignment.Center;
title.Format.SpaceAfter = "0.15cm";
// Add SubTitle
string Subtitle = $"AiQ {CamOnTest.Model} {CamOnTest.Serial}" + (CamOnTest.RMANum != 0 ? $" (RMA {CamOnTest.RMANum})" : "");
Paragraph subtitle = section.AddParagraph(Subtitle);
subtitle.Format.Font.Size = 14;
subtitle.Format.Alignment = ParagraphAlignment.Center;
subtitle.Format.SpaceAfter = "0.5cm";
// Add Date
Paragraph date = section.AddParagraph($"Date: {PCTime}{Environment.NewLine}Engineer: {UserName}{Environment.NewLine}Tested in accordance with MAV.146.060.010.01");
date.Format.Font.Size = 10;
date.Format.SpaceAfter = "0.5cm";
// Add Test Values header
Paragraph valuesHeader = section.AddParagraph("Test Values:");
valuesHeader.Format.Font.Size = 14;
valuesHeader.Format.Font.Bold = true;
valuesHeader.Format.SpaceAfter = "0.25cm";
// Add Test Values
Paragraph FTV = section.AddParagraph(fulltestvalues);
FTV.Format.Font.Size = 10;
FTV.Format.SpaceAfter = "0.5cm";
// Add Images
Paragraph imagesHeader = section.AddParagraph("Images:");
imagesHeader.Format.Font.Size = 14;
imagesHeader.Format.Font.Bold = true;
imagesHeader.Format.SpaceAfter = "0.25cm";
// Create a table with two columns
Table table = section.AddTable();
table.Borders.Width = 0; // No border
table.AddColumn(Unit.FromCentimeter(6)); // Column 1 width
table.AddColumn(Unit.FromCentimeter(6)); // Column 2 width
table.AddColumn(Unit.FromCentimeter(6)); // Column 3 width
table.Rows.Alignment = RowAlignment.Center;
Row row = table.AddRow();
// Add IR F16 to the first column
Cell cell1 = row.Cells[0];
cell1.Format.Alignment = ParagraphAlignment.Center; // Center horizontally
cell1.AddParagraph("Infrared - F16.0");
Image imageIR = cell1.AddImage(LDS.MAVPath + LDS.IRTightsavePath);
imageIR.LockAspectRatio = true; // Maintain aspect ratio
imageIR.Width = Unit.FromCentimeter(6); // Scale as needed
// Add IR F2 to the second column
Cell cell2 = row.Cells[1];
cell2.Format.Alignment = ParagraphAlignment.Center; // Center horizontally
cell2.AddParagraph("Infrared - F2.0");
Image imageIR17 = cell2.AddImage(LDS.MAVPath + LDS.IROpensavePath);
imageIR17.LockAspectRatio = true; // Maintain aspect ratio
imageIR17.Width = Unit.FromCentimeter(6); // Scale as needed
// Add Image OV to the third column
Cell cell3 = row.Cells[2];
cell3.Format.Alignment = ParagraphAlignment.Center; // Center horizontally
cell3.AddParagraph("Overview");
Image imageOV = cell3.AddImage(LDS.MAVPath + LDS.OVsavePath);
imageOV.LockAspectRatio = true; // Maintain aspect ratio
imageOV.Width = Unit.FromCentimeter(6); // Scale as needed
section.AddParagraph().Format.SpaceAfter = "0.5cm";
// Add signiture
Paragraph Signiture = section.AddParagraph("This unit is approved for shipment by:");
Signiture.Format.Font.Size = 14;
Signiture.Format.Alignment = ParagraphAlignment.Right;
Paragraph paragraph = section.AddParagraph();
Image image = paragraph.AddImage($"{ImageDir}RP-Sig.jpg");
image.Height = Unit.FromCentimeter(2);
image.LockAspectRatio = true;
paragraph.Format.Alignment = ParagraphAlignment.Right;
Paragraph PostSig = section.AddParagraph($"Richard Porter{Environment.NewLine}Head of Engineering{Environment.NewLine}MAV Systems Ltd");
PostSig.Format.Font.Size = 14;
PostSig.Format.Alignment = ParagraphAlignment.Right;
try
{
// Render PDF
PdfDocumentRenderer renderer = new()
{
Document = document
};
renderer.RenderDocument();
renderer.PdfDocument.Options.Layout = PdfWriterLayout.Compact;
// Adds RMA number to file namme if there is one
string saveLoc = $"{TestRecordDir}{CamOnTest.Model}\\FinalTestReport_{CamOnTest.Model}_{CamOnTest.Serial}_{PCTime:dd-MM-yyyy_HH-mm-ss}" +
(CamOnTest.RMANum != 0 ? $" RMA{CamOnTest.RMANum}" : "") + ".pdf";
if (!Directory.Exists($"{TestRecordDir}{CamOnTest.Model}\\")) // Does the model directory exist?
{
Directory.CreateDirectory($"{TestRecordDir}{CamOnTest.Model}\\"); // if not then create the directory now
}
renderer.PdfDocument.Save(saveLoc);
Logging.LogMessage("Final Test PDF saved to " + saveLoc);
}
catch (Exception ex)
{
// Show a message box to inform the user that PDF creation failed, displaying the exception message.
MainForm.Instance.AddToActionsList($"Failed to create PDF:\n{ex.Message}");
}
}
public static string CreateSoakTestReport(Camera CamSoak, string userName, DateTime pcTime, string logFilePath)
{
if (!File.Exists(logFilePath))
{
MainForm.Instance.AddToActionsList("Soak log file not found. Cannot create Soak Test Report.");
return null;
}
List<string> logLines = File.Exists(logFilePath) ? File.ReadAllLines(logFilePath).ToList() : new List<string>();
List<string> errorLines = logLines.Where(l => l.Contains("[ERROR]")).ToList();
List<string> warningLines = logLines.Where(l => l.Contains("[WARNING]")).ToList();
// Create PDF document
Document document = new();
Section section = document.AddSection();
// Header table with logos
Table logoTable = section.AddTable();
logoTable.Borders.Visible = false;
logoTable.AddColumn(Unit.FromCentimeter(13)); // Left column
logoTable.AddColumn(Unit.FromCentimeter(3)); // Right column
Row logoRow = logoTable.AddRow();
Cell cellMAV = logoRow.Cells[0];
Cell cellAiQ = logoRow.Cells[1];
Image mavLogo = cellMAV.AddImage($"{ImageDir}MAV-Logo.png");
mavLogo.LockAspectRatio = true;
mavLogo.Height = Unit.FromCentimeter(1);
Image aiqLogo = cellAiQ.AddImage($"{ImageDir}AiQ-Logo.png");
aiqLogo.LockAspectRatio = true;
aiqLogo.Height = Unit.FromCentimeter(1.25);
section.AddParagraph().Format.SpaceAfter = "0.5cm";
// Title
Paragraph title = section.AddParagraph("Soak Test Certificate");
title.Format.Font.Size = 20;
title.Format.Font.Bold = true;
title.Format.Alignment = ParagraphAlignment.Center;
title.Format.SpaceAfter = "0.15cm";
// Subtitle with RMA number if available
string subtitleText = $"AiQ {CamSoak.Model} {CamSoak.Serial}" + (CamSoak.RMANum != 0 ? $" (RMA {CamSoak.RMANum})" : "");
Paragraph subtitle = section.AddParagraph(subtitleText);
subtitle.Format.Font.Size = 14;
subtitle.Format.Alignment = ParagraphAlignment.Center;
subtitle.Format.SpaceAfter = "0.5cm";
// Date and engineer info
Paragraph datePara = section.AddParagraph($"Date: {pcTime:dd/MM/yyyy HH:mm:ss}{Environment.NewLine}Engineer: {userName}{Environment.NewLine}Tested in accordance with MAV.146.060.020.01");
datePara.Format.Font.Size = 10;
datePara.Format.SpaceAfter = "0.5cm";
// === Add warning/error summary with counts and color ===
if (warningLines.Count > 0 || errorLines.Count > 0)
{
Paragraph logSummary = section.AddParagraph("Log Summary:");
logSummary.Format.Font.Size = 12;
logSummary.Format.Font.Bold = true;
logSummary.Format.SpaceAfter = "0.2cm";
Paragraph totalCounts = section.AddParagraph();
totalCounts.Format.Font.Size = 10;
totalCounts.AddText("Total Errors: ");
FormattedText errorCountText = totalCounts.AddFormattedText(errorLines.Count.ToString());
errorCountText.Font.Color = Colors.Red;
totalCounts.AddText("\nTotal Warnings: ");
FormattedText warningCountText = totalCounts.AddFormattedText(warningLines.Count.ToString());
warningCountText.Font.Color = Colors.Orange;
totalCounts.Format.SpaceAfter = "0.2cm";
Dictionary<string, int> errorCounts = [];
Dictionary<string, int> warningCounts = [];
foreach (string line in errorLines)
{
string message = ExtractLogMessageContent(line);
if (errorCounts.TryGetValue(message, out int value))
errorCounts[message] = ++value;
else
errorCounts[message] = 1;
}
foreach (string line in warningLines)
{
string message = ExtractLogMessageContent(line);
if (warningCounts.TryGetValue(message, out int value))
warningCounts[message] = ++value;
else
warningCounts[message] = 1;
}
foreach (KeyValuePair<string, int> ErrorCounter in errorCounts) // Itterates through the dictionary and Adds Errors and how many times that warning has appeared
{
Paragraph errorPara = section.AddParagraph();
errorPara.Format.Font.Size = 9;
errorPara.AddFormattedText("[", TextFormat.NotBold);
FormattedText redText = errorPara.AddFormattedText("ERROR", TextFormat.NotBold);
redText.Font.Color = Colors.Red;
errorPara.AddFormattedText($"] {ErrorCounter.Key} (x{ErrorCounter.Value})");
}
foreach (KeyValuePair<string, int> WarningCounter in warningCounts) // Itterates through the dictionary and Adds warning and how many times that warning has appeared
{
Paragraph warnPara = section.AddParagraph();
warnPara.Format.Font.Size = 9;
warnPara.AddFormattedText("[", TextFormat.NotBold);
FormattedText orangeText = warnPara.AddFormattedText("WARNING", TextFormat.NotBold);
orangeText.Font.Color = Colors.Orange;
warnPara.AddFormattedText($"] {WarningCounter.Key} (x{WarningCounter.Value})");
}
section.AddParagraph().Format.SpaceAfter = "0.5cm";
}
// Signature
if (errorLines.Count == 0)
{
Paragraph approval = section.AddParagraph("This unit is approved for shipment by:");
approval.Format.Font.Size = 14;
approval.Format.Alignment = ParagraphAlignment.Right;
Paragraph sigPara = section.AddParagraph();
Image sigImage = sigPara.AddImage($"{ImageDir}RP-Sig.jpg");
sigImage.Height = Unit.FromCentimeter(2);
sigImage.LockAspectRatio = true;
sigPara.Format.Alignment = ParagraphAlignment.Right;
Paragraph signoff = section.AddParagraph($"Richard Porter{Environment.NewLine}Head of Engineering{Environment.NewLine}MAV Systems Ltd");
signoff.Format.Font.Size = 14;
signoff.Format.Alignment = ParagraphAlignment.Right;
}
// New page with full log
Section logSection = document.AddSection();
Paragraph fullLogTitle = logSection.AddParagraph("Full Log Output:");
fullLogTitle.Format.Font.Size = 12;
fullLogTitle.Format.Font.Bold = true;
fullLogTitle.Format.SpaceAfter = "0.2cm";
foreach (string line in logLines)
{
Paragraph logLine = logSection.AddParagraph();
logLine.Format.Font.Size = 8;
if (line.Contains("[ERROR]"))
{
int index = line.IndexOf("[ERROR]"); // You get rid of error only to add it back a few lines later? // this is because its difficukt to change the colour of text your importing its easier to remove and then make red
logLine.AddText(line[..index]);
logLine.AddFormattedText("ERROR", TextFormat.NotBold).Font.Color = Colors.Red;
logLine.AddText(line[(index + "[ERROR]".Length)..]);
}
else if (line.Contains("[WARNING]"))
{
int index = line.IndexOf("[WARNING]"); // You get rid of warning only to add it back a few lines later? // Same here
logLine.AddText(line[..index]);
logLine.AddFormattedText("WARNING", TextFormat.NotBold).Font.Color = Colors.Orange;
logLine.AddText(line[(index + "[WARNING]".Length)..]);
}
else
{
logLine.AddText(line);
}
}
try
{
// Render PDF
PdfDocumentRenderer renderer = new()
{
Document = document
};
renderer.RenderDocument();
renderer.PdfDocument.Options.Layout = PdfWriterLayout.Compact;
// Construct save path
string fullPath = TestRecordDir + CamSoak.Model + $"\\SoakTestReport_{CamSoak.Model}_{CamSoak.Serial}_{pcTime:dd-MM-yyyy_HH-mm-ss}" + (CamSoak.RMANum != 0 ? $" RMA{CamSoak.RMANum}" : "") + ".pdf";
renderer.PdfDocument.Save(fullPath);
Logging.LogMessage("Soak Test PDF saved to " + fullPath);
return fullPath;
}
catch (Exception ex)
{
// Show a message box to inform the user that PDF creation failed, displaying the exception message.
MainForm.Instance.AddToActionsList($"Failed to create PDF:\n{ex.Message}");
return null;
}
}
private static string ExtractLogMessageContent(string line)
{
int tagEnd = line.IndexOf("] ");
if (tagEnd != -1)
{
return line[(tagEnd + 2)..].Trim();
}
return line.Trim();
}
public static bool LinkPDFs(string pdf1Path, string pdf2Path, string outputPath)
{
bool hasError = false;
if (!File.Exists(pdf1Path))
{
MainForm.Instance.AddToActionsList($"{pdf1Path} does not exist.");
hasError = true;
}
if (!File.Exists(pdf2Path))
{
MainForm.Instance.AddToActionsList($"{pdf2Path} does not exist.");
hasError = true;
}
if (hasError)
return false;
using PdfDocument outputDocument = new(); // Create a new PDF document
AppendPdf(outputDocument, pdf1Path);
AppendPdf(outputDocument, pdf2Path);
outputDocument.Save(outputPath); // Save the output document
return true;
}
public static void AppendPdf(PdfDocument target, string sourcePath)
{
using PdfDocument inputDocument = PdfReader.Open(sourcePath, PdfDocumentOpenMode.Import);
for (int idx = 0; idx < inputDocument.PageCount; idx++) // Iterate through all pages and add them to the output
{
PdfPage page = inputDocument.Pages[idx];
target.AddPage(page);
}
}
}
}