diff --git a/ReleaseFiles/App.config b/ReleaseFiles/App.config
index 7465d52..7123378 100644
--- a/ReleaseFiles/App.config
+++ b/ReleaseFiles/App.config
@@ -7,7 +7,10 @@
-
+
+
+
+
@@ -122,7 +125,6 @@
-
@@ -148,7 +150,7 @@
-
+
@@ -161,6 +163,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ReleaseFiles/BouncyCastle.Cryptography.dll b/ReleaseFiles/BouncyCastle.Cryptography.dll
new file mode 100644
index 0000000..637242d
Binary files /dev/null and b/ReleaseFiles/BouncyCastle.Cryptography.dll differ
diff --git a/ReleaseFiles/Microsoft.DotNet.PlatformAbstractions.dll b/ReleaseFiles/Microsoft.DotNet.PlatformAbstractions.dll
new file mode 100644
index 0000000..ae549ea
Binary files /dev/null and b/ReleaseFiles/Microsoft.DotNet.PlatformAbstractions.dll differ
diff --git a/ReleaseFiles/Microsoft.Extensions.DependencyInjection.dll b/ReleaseFiles/Microsoft.Extensions.DependencyInjection.dll
index 3ebfa64..1034ee6 100644
Binary files a/ReleaseFiles/Microsoft.Extensions.DependencyInjection.dll and b/ReleaseFiles/Microsoft.Extensions.DependencyInjection.dll differ
diff --git a/ReleaseFiles/Microsoft.Extensions.Logging.Abstractions.dll b/ReleaseFiles/Microsoft.Extensions.Logging.Abstractions.dll
index 5bc5d39..2c87f79 100644
Binary files a/ReleaseFiles/Microsoft.Extensions.Logging.Abstractions.dll and b/ReleaseFiles/Microsoft.Extensions.Logging.Abstractions.dll differ
diff --git a/ReleaseFiles/Microsoft.Extensions.Logging.dll b/ReleaseFiles/Microsoft.Extensions.Logging.dll
index bda07c2..6df35e1 100644
Binary files a/ReleaseFiles/Microsoft.Extensions.Logging.dll and b/ReleaseFiles/Microsoft.Extensions.Logging.dll differ
diff --git a/ReleaseFiles/Microsoft.Extensions.Options.dll b/ReleaseFiles/Microsoft.Extensions.Options.dll
index 088358a..8a2a8c8 100644
Binary files a/ReleaseFiles/Microsoft.Extensions.Options.dll and b/ReleaseFiles/Microsoft.Extensions.Options.dll differ
diff --git a/ReleaseFiles/Microsoft.Win32.SystemEvents.dll b/ReleaseFiles/Microsoft.Win32.SystemEvents.dll
index 279431a..cd1f3d1 100644
Binary files a/ReleaseFiles/Microsoft.Win32.SystemEvents.dll and b/ReleaseFiles/Microsoft.Win32.SystemEvents.dll differ
diff --git a/ReleaseFiles/PlcAddrDfn.xml b/ReleaseFiles/PlcAddrDfn.xml
new file mode 100644
index 0000000..ed5dbb3
--- /dev/null
+++ b/ReleaseFiles/PlcAddrDfn.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ReleaseFiles/System.Data.OleDb.dll b/ReleaseFiles/System.Data.OleDb.dll
index f7840cd..95d3e71 100644
Binary files a/ReleaseFiles/System.Data.OleDb.dll and b/ReleaseFiles/System.Data.OleDb.dll differ
diff --git a/ReleaseFiles/System.Data.SqlClient.dll b/ReleaseFiles/System.Data.SqlClient.dll
index c9675bc..9d91846 100644
Binary files a/ReleaseFiles/System.Data.SqlClient.dll and b/ReleaseFiles/System.Data.SqlClient.dll differ
diff --git a/ReleaseFiles/Templates/Logo.png b/ReleaseFiles/Templates/Logo.png
new file mode 100644
index 0000000..5a4dc2c
Binary files /dev/null and b/ReleaseFiles/Templates/Logo.png differ
diff --git a/ReleaseFiles/Templates/Logo2.png b/ReleaseFiles/Templates/Logo2.png
new file mode 100644
index 0000000..9674cea
Binary files /dev/null and b/ReleaseFiles/Templates/Logo2.png differ
diff --git a/ReleaseFiles/Templates/StandardReportTemplate.json b/ReleaseFiles/Templates/StandardReportTemplate.json
new file mode 100644
index 0000000..64fb759
--- /dev/null
+++ b/ReleaseFiles/Templates/StandardReportTemplate.json
@@ -0,0 +1,222 @@
+{
+ "document": {
+ "pageSize": "A4",
+ "orientation": "Portrait",
+ "margins": { "top": 40, "bottom": 20, "left": 20, "right": 20 },
+ "header": {
+ "enabled": true,
+ "left": [
+ "${loc:Report_Title}",
+ "${loc:Report_Id}:${metadata.reportId} | ${loc:Report_Date}:${formatDate(metadata.inspectionDate)} | ${loc:Report_Sample}:${metadata.sampleName}"
+ ],
+ "rightImageKey": "companyLogo",
+ "fontSize": 7,
+ "color": "#666666",
+ "showLine": true
+ },
+ "footer": {
+ "enabled": true,
+ "left": ["${CompanyName}"],
+ "right": ["{currentPage} / {totalPages}"],
+ "fontSize": 8,
+ "color": "#666666",
+ "showLine": true
+ }
+ },
+ "pages": [
+ {
+ "type": "homepage",
+ "elements": [
+ {
+ "type": "row", "size": [170, 40], "positioning": "flow",
+ "children": [
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "image", "dataKey": "companyLogo", "size": [50, 30], "align": "left" },
+ { "type": "text", "content": "${CompanyName}", "style": "companyName", "align": "left" }
+ ]
+ },
+ {
+ "type": "column", "align": "right",
+ "children": [
+ { "type": "image", "dataKey": "softwareLogo", "size": [15, 20], "align": "right" },
+ { "type": "text", "content": "${SoftwareName}", "style": "companyName", "align": "right" }
+ ]
+ }
+ ]
+ },
+ { "type": "spacer", "size": [170, 15], "positioning": "flow" },
+ { "type": "text", "content": "${loc:Report_Title}", "style": "title", "positioning": "flow" },
+ { "type": "spacer", "size": [170, 10], "positioning": "flow" },
+ { "type": "text", "content": "${loc:Report_Id}:${metadata.reportId}", "style": "homepageInfo", "positioning": "flow" },
+ { "type": "text", "content": "${loc:Report_Sample}:${metadata.sampleName}", "style": "homepageInfo", "positioning": "flow" },
+ { "type": "text", "content": "${loc:Report_Description}:${metadata.description}", "style": "homepageInfo", "positioning": "flow" },
+ { "type": "image", "dataKey": "workpieceImage", "size": [160, 110], "border": true, "align": "center", "style": "imageDefault", "positioning": "flow" },
+ { "type": "text", "content": "${loc:Report_Operator}:${metadata.operatorName}", "style": "homepageFooter", "positioning": "flow" },
+ { "type": "text", "content": "${loc:Report_Date}:${formatDate(metadata.inspectionDate)}", "style": "homepageFooter", "positioning": "flow" }
+ ]
+ },
+ {
+ "type": "summary",
+ "elements": [
+ { "type": "text", "content": "${loc:Report_Summary}", "style": "homepageHeading", "positioning": "flow" },
+ {
+ "type": "table", "dataKey": "summaryTable", "positioning": "flow", "size": [170, 0], "style": "tableDefault",
+ "columns": [
+ { "header": "${loc:Field_InspectionType}", "field": "inspectionType", "width": 50, "align": "left" },
+ { "header": "${loc:Field_Result}", "field": "classification", "width": 30, "align": "center", "colorRules": { "Pass": "#008000", "Fail": "#FF0000" } },
+ { "header": "${loc:Field_Status}", "field": "status", "width": 30, "align": "center", "colorRules": { "合格": "#008000", "PASS": "#008000", "不合格": "#FF0000", "FAIL": "#FF0000" } }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "metricData",
+ "elements": [
+ { "type": "text", "content": "${loc:Page_MetricData}", "style": "heading", "positioning": "flow" },
+ {
+ "type": "row", "size": [170, 100], "widths": [6, 4], "positioning": "flow",
+ "children": [
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "image", "dataKey": "lineMeasurementImage", "size": [110, 90], "border": true, "align": "left" }
+ ]
+ },
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "text", "content": "${loc:Measurement_Type}:${measurementType}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Field_Point1}:${point1}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Field_Point2}:${point2}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Measurement_Distance}:${actualDistance} ${unit}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Measurement_Angle}:${angle}°", "style": "body", "align": "left" }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "bgaInspection",
+ "elements": [
+ { "type": "text", "content": "${loc:Page_BgaInspection}", "style": "heading", "positioning": "flow" },
+ {
+ "type": "row", "size": [170, 100], "widths": [6, 4], "positioning": "flow",
+ "children": [
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "image", "dataKey": "bgaInspectionImage", "size": [110, 90], "border": true, "align": "left" }
+ ]
+ },
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "text", "content": "${loc:Bga_Count}:${bgaCount}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Measurement_VoidRate}:${formatPercent(voidRate)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Measurement_FillRate}:${formatPercent(fillRate)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Bga_TotalArea}:${formatNumber(totalBgaArea, 2)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Bga_TotalVoidArea}:${formatNumber(totalVoidArea, 2)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Bga_VoidLimit}:${formatPercent(voidLimit)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Table_Classification}:${classification}", "style": "conclusion", "align": "left", "colorRules": { "Pass": "#008000", "Fail": "#FF0000" } }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "table", "dataKey": "bgaBallsTable", "positioning": "flow", "size": [170, 0], "style": "tableDefault",
+ "columns": [
+ { "header": "${loc:Bga_BallIndex}", "field": "index", "width": 25, "align": "center" },
+ { "header": "${loc:Table_VoidRate}", "field": "voidRate", "width": 40, "align": "center" },
+ { "header": "${loc:Table_Area}", "field": "area", "width": 40, "align": "center" },
+ { "header": "${loc:Table_Classification}", "field": "classification", "width": 35, "align": "center", "colorRules": { "Pass": "#008000", "Fail": "#FF0000" } }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "voidInspection",
+ "elements": [
+ { "type": "text", "content": "${loc:Page_VoidInspection}", "style": "heading", "positioning": "flow" },
+ {
+ "type": "row", "size": [170, 100], "widths": [6, 4], "positioning": "flow",
+ "children": [
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "image", "dataKey": "voidInspectionImage", "size": [110, 90], "border": true, "align": "left" }
+ ]
+ },
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "text", "content": "${loc:Void_RoiArea}:${formatNumber(roiArea, 2)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Void_TotalArea}:${formatNumber(totalVoidArea, 2)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Measurement_VoidRate}:${formatPercent(voidRate)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Void_Limit}:${formatPercent(voidLimit)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Void_Count}:${voidCount}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Void_MaxArea}:${formatNumber(maxVoidArea, 2)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Table_Classification}:${classification}", "style": "conclusion", "align": "left", "colorRules": { "Pass": "#008000", "Fail": "#FF0000" } }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "table", "dataKey": "voidsTable", "positioning": "flow", "size": [170, 0], "style": "tableDefault",
+ "columns": [
+ { "header": "${loc:Table_Index}", "field": "index", "width": 20, "align": "center" },
+ { "header": "${loc:Table_Area}", "field": "area", "width": 35, "align": "center" },
+ { "header": "${loc:Table_AreaPercent}", "field": "areaPercent", "width": 35, "align": "center" },
+ { "header": "${loc:Table_CenterX}", "field": "centerX", "width": 30, "align": "center" },
+ { "header": "${loc:Table_CenterY}", "field": "centerY", "width": 30, "align": "center" }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "viaFillInspection",
+ "elements": [
+ { "type": "text", "content": "${loc:Page_ViaFillInspection}", "style": "heading", "positioning": "flow" },
+ {
+ "type": "row", "size": [170, 100], "widths": [6, 4], "positioning": "flow",
+ "children": [
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "image", "dataKey": "viaFillImage", "size": [110, 90], "border": true, "align": "left" }
+ ]
+ },
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "text", "content": "${loc:Fill_Rate}:${formatPercent(fillRate)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Measurement_VoidRate}:${formatPercent(voidRate)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Fill_FullDistance}:${formatNumber(fullDistance, 2)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Fill_FillDistance}:${formatNumber(fillDistance, 2)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Fill_THTLimit}:${formatPercent(thtLimit)}", "style": "body", "align": "left" },
+ { "type": "text", "content": "${loc:Table_Classification}:${classification}", "style": "conclusion", "align": "left", "colorRules": { "Pass": "#008000", "Fail": "#FF0000" } }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "styles": {
+ "title": { "font": "auto", "size": 28, "bold": true, "italic": false, "color": "#1a1a1a", "align": "center" },
+ "companyName": { "font": "auto", "size": 10, "bold": false, "italic": false, "color": "#333333", "align": "center" },
+ "homepageInfo": { "font": "auto", "size": 12, "bold": false, "italic": false, "color": "#333333", "align": "left", "paddingLeft": 10 },
+ "homepageFooter": { "font": "auto", "size": 12, "bold": false, "italic": false, "color": "#333333", "align": "center" },
+ "homepageHeading": { "font": "auto", "size": 14, "bold": true, "italic": false, "color": "#333333", "align": "left" },
+ "heading": { "font": "auto", "size": 16, "bold": true, "italic": false, "color": "#333333", "align": "left", "marginBottom": 3 },
+ "body": { "font": "auto", "size": 11, "bold": false, "italic": false, "color": "#333333", "align": "left" },
+ "bodyBold": { "font": "auto", "size": 11, "bold": true, "italic": false, "color": "#1a1a1a", "align": "left" },
+ "conclusion": { "font": "auto", "size": 12, "bold": true, "italic": false, "color": "#333333", "align": "left", "marginTop": 3 },
+ "imageDefault": { "font": "auto", "size": 12, "bold": false, "italic": false, "color": "#333333", "align": "center", "marginTop": 5, "marginBottom": 5 },
+ "tableHeader": { "font": "auto", "size": 10, "bold": true, "italic": false, "color": "#ffffff", "align": "center", "backgroundColor": "#4472C4" },
+ "tableDefault": { "font": "auto", "size": 10, "bold": false, "italic": false, "color": "#333333", "align": "center", "marginTop": 5 },
+ "tableCell": { "font": "auto", "size": 10, "bold": false, "italic": false, "color": "#333333", "align": "center" }
+ }
+}
diff --git a/ReleaseFiles/UserManual.pdf b/ReleaseFiles/UserManual.pdf
new file mode 100644
index 0000000..2e0b69a
Binary files /dev/null and b/ReleaseFiles/UserManual.pdf differ
diff --git a/ReleaseFiles/XP.Camera.dll b/ReleaseFiles/XP.Camera.dll
index 5a53c26..c927ba4 100644
Binary files a/ReleaseFiles/XP.Camera.dll and b/ReleaseFiles/XP.Camera.dll differ
diff --git a/ReleaseFiles/XP.Camera.pdb b/ReleaseFiles/XP.Camera.pdb
index 6112b4e..728c2e3 100644
Binary files a/ReleaseFiles/XP.Camera.pdb and b/ReleaseFiles/XP.Camera.pdb differ
diff --git a/ReleaseFiles/XP.Common.dll b/ReleaseFiles/XP.Common.dll
index 7bc5736..a8833d8 100644
Binary files a/ReleaseFiles/XP.Common.dll and b/ReleaseFiles/XP.Common.dll differ
diff --git a/ReleaseFiles/XP.Common.pdb b/ReleaseFiles/XP.Common.pdb
index 85e8f2e..c0fc2a9 100644
Binary files a/ReleaseFiles/XP.Common.pdb and b/ReleaseFiles/XP.Common.pdb differ
diff --git a/ReleaseFiles/XP.Hardware.Detector.dll b/ReleaseFiles/XP.Hardware.Detector.dll
index f02c874..9d8a578 100644
Binary files a/ReleaseFiles/XP.Hardware.Detector.dll and b/ReleaseFiles/XP.Hardware.Detector.dll differ
diff --git a/ReleaseFiles/XP.Hardware.Detector.pdb b/ReleaseFiles/XP.Hardware.Detector.pdb
index 436b074..6b4aa4e 100644
Binary files a/ReleaseFiles/XP.Hardware.Detector.pdb and b/ReleaseFiles/XP.Hardware.Detector.pdb differ
diff --git a/ReleaseFiles/XP.Hardware.MotionControl.dll b/ReleaseFiles/XP.Hardware.MotionControl.dll
index 2985310..d44058d 100644
Binary files a/ReleaseFiles/XP.Hardware.MotionControl.dll and b/ReleaseFiles/XP.Hardware.MotionControl.dll differ
diff --git a/ReleaseFiles/XP.Hardware.MotionControl.pdb b/ReleaseFiles/XP.Hardware.MotionControl.pdb
index 104cb14..8f0de88 100644
Binary files a/ReleaseFiles/XP.Hardware.MotionControl.pdb and b/ReleaseFiles/XP.Hardware.MotionControl.pdb differ
diff --git a/ReleaseFiles/XP.Hardware.PLC.dll b/ReleaseFiles/XP.Hardware.PLC.dll
index 9d3bdcc..6f3d513 100644
Binary files a/ReleaseFiles/XP.Hardware.PLC.dll and b/ReleaseFiles/XP.Hardware.PLC.dll differ
diff --git a/ReleaseFiles/XP.Hardware.PLC.pdb b/ReleaseFiles/XP.Hardware.PLC.pdb
index 80e5165..4c2e8ae 100644
Binary files a/ReleaseFiles/XP.Hardware.PLC.pdb and b/ReleaseFiles/XP.Hardware.PLC.pdb differ
diff --git a/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.dll b/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.dll
index e542b84..24b0107 100644
Binary files a/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.dll and b/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.dll differ
diff --git a/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.pdb b/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.pdb
index f393a13..7fe6daf 100644
Binary files a/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.pdb and b/ReleaseFiles/XP.Hardware.RaySource.Comet.Messages.pdb differ
diff --git a/ReleaseFiles/XP.Hardware.RaySource.dll b/ReleaseFiles/XP.Hardware.RaySource.dll
index 40c575b..21e3295 100644
Binary files a/ReleaseFiles/XP.Hardware.RaySource.dll and b/ReleaseFiles/XP.Hardware.RaySource.dll differ
diff --git a/ReleaseFiles/XP.Hardware.RaySource.pdb b/ReleaseFiles/XP.Hardware.RaySource.pdb
index d1976de..6176218 100644
Binary files a/ReleaseFiles/XP.Hardware.RaySource.pdb and b/ReleaseFiles/XP.Hardware.RaySource.pdb differ
diff --git a/ReleaseFiles/XP.ImageProcessing.CfgControl.dll b/ReleaseFiles/XP.ImageProcessing.CfgControl.dll
index 5668ff5..5924248 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.CfgControl.dll and b/ReleaseFiles/XP.ImageProcessing.CfgControl.dll differ
diff --git a/ReleaseFiles/XP.ImageProcessing.CfgControl.pdb b/ReleaseFiles/XP.ImageProcessing.CfgControl.pdb
index 2a6bfa6..9e87d84 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.CfgControl.pdb and b/ReleaseFiles/XP.ImageProcessing.CfgControl.pdb differ
diff --git a/ReleaseFiles/XP.ImageProcessing.Core.dll b/ReleaseFiles/XP.ImageProcessing.Core.dll
index 80646aa..0af1c02 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.Core.dll and b/ReleaseFiles/XP.ImageProcessing.Core.dll differ
diff --git a/ReleaseFiles/XP.ImageProcessing.Core.pdb b/ReleaseFiles/XP.ImageProcessing.Core.pdb
index 190e2af..3690f67 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.Core.pdb and b/ReleaseFiles/XP.ImageProcessing.Core.pdb differ
diff --git a/ReleaseFiles/XP.ImageProcessing.Processors.dll b/ReleaseFiles/XP.ImageProcessing.Processors.dll
index c776aef..645297e 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.Processors.dll and b/ReleaseFiles/XP.ImageProcessing.Processors.dll differ
diff --git a/ReleaseFiles/XP.ImageProcessing.Processors.pdb b/ReleaseFiles/XP.ImageProcessing.Processors.pdb
index 12ee5d7..7b55029 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.Processors.pdb and b/ReleaseFiles/XP.ImageProcessing.Processors.pdb differ
diff --git a/ReleaseFiles/XP.ImageProcessing.RoiControl.dll b/ReleaseFiles/XP.ImageProcessing.RoiControl.dll
index ad057b3..d478d53 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.RoiControl.dll and b/ReleaseFiles/XP.ImageProcessing.RoiControl.dll differ
diff --git a/ReleaseFiles/XP.ImageProcessing.RoiControl.pdb b/ReleaseFiles/XP.ImageProcessing.RoiControl.pdb
index aaf84d2..5441b82 100644
Binary files a/ReleaseFiles/XP.ImageProcessing.RoiControl.pdb and b/ReleaseFiles/XP.ImageProcessing.RoiControl.pdb differ
diff --git a/ReleaseFiles/XP.ReportEngine.dll b/ReleaseFiles/XP.ReportEngine.dll
new file mode 100644
index 0000000..c3d733d
Binary files /dev/null and b/ReleaseFiles/XP.ReportEngine.dll differ
diff --git a/ReleaseFiles/XP.ReportEngine.pdb b/ReleaseFiles/XP.ReportEngine.pdb
new file mode 100644
index 0000000..ffd6093
Binary files /dev/null and b/ReleaseFiles/XP.ReportEngine.pdb differ
diff --git a/ReleaseFiles/XplorePlane.deps.json b/ReleaseFiles/XplorePlane.deps.json
index 6d5820f..7583502 100644
--- a/ReleaseFiles/XplorePlane.deps.json
+++ b/ReleaseFiles/XplorePlane.deps.json
@@ -1,11 +1,12 @@
{
"runtimeTarget": {
- "name": ".NETCoreApp,Version=v8.0",
+ "name": ".NETCoreApp,Version=v8.0/win-x64",
"signature": ""
},
"compilationOptions": {},
"targets": {
- ".NETCoreApp,Version=v8.0": {
+ ".NETCoreApp,Version=v8.0": {},
+ ".NETCoreApp,Version=v8.0/win-x64": {
"XplorePlane/1.0.0": {
"dependencies": {
"Emgu.CV": "4.10.0.5680",
@@ -29,12 +30,21 @@
"XP.ImageProcessing.Core": "1.0.0",
"XP.ImageProcessing.Processors": "1.0.0",
"XP.ImageProcessing.RoiControl": "1.0.0",
+ "XP.ReportEngine": "1.0.0",
"BR.AN.PviServices": "1.1.0.0"
},
"runtime": {
"XplorePlane.dll": {}
}
},
+ "BouncyCastle.Cryptography/2.2.1": {
+ "runtime": {
+ "lib/net6.0/BouncyCastle.Cryptography.dll": {
+ "assemblyVersion": "2.0.0.0",
+ "fileVersion": "2.2.1.47552"
+ }
+ }
+ },
"ControlzEx/5.0.1": {
"dependencies": {
"Microsoft.Xaml.Behaviors.Wpf": "1.1.122",
@@ -88,171 +98,48 @@
"Emgu.runtime.windows.msvc.rt.x64": "19.42.34435",
"Emgu.runtime.windows.msvc.rt.x86": "19.42.34435"
},
- "runtimeTargets": {
- "runtimes/win-arm64/native/cvextern.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "4.10.0.5680"
- },
+ "native": {
"runtimes/win-x64/native/cvextern.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "4.10.0.5680"
},
"runtimes/win-x64/native/libusb-1.0.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "0.0.0.0"
},
"runtimes/win-x64/native/opencv_videoio_ffmpeg4100_64.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "2024.5.0.0"
- },
- "runtimes/win-x86/native/cvextern.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "4.10.0.5680"
- },
- "runtimes/win-x86/native/libusb-1.0.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-x86/native/opencv_videoio_ffmpeg4100.dll": {
- "rid": "win-x86",
- "assetType": "native",
"fileVersion": "2024.5.0.0"
}
}
},
- "Emgu.runtime.windows.msvc.rt.arm64/19.42.34435": {
- "runtimeTargets": {
- "runtimes/win-arm64/native/concrt140.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-arm64/native/msvcp140.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-arm64/native/msvcp140_1.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-arm64/native/msvcp140_2.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-arm64/native/msvcp140_atomic_wait.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-arm64/native/msvcp140_codecvt_ids.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-arm64/native/vcruntime140.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-arm64/native/vcruntime140_1.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- }
- }
- },
+ "Emgu.runtime.windows.msvc.rt.arm64/19.42.34435": {},
"Emgu.runtime.windows.msvc.rt.x64/19.42.34435": {
- "runtimeTargets": {
+ "native": {
"runtimes/win-x64/native/concrt140.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
},
"runtimes/win-x64/native/msvcp140.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
},
"runtimes/win-x64/native/msvcp140_1.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
},
"runtimes/win-x64/native/msvcp140_2.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
},
"runtimes/win-x64/native/msvcp140_atomic_wait.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
},
"runtimes/win-x64/native/msvcp140_codecvt_ids.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
},
"runtimes/win-x64/native/vcruntime140.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
},
"runtimes/win-x64/native/vcruntime140_1.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- }
- }
- },
- "Emgu.runtime.windows.msvc.rt.x86/19.42.34435": {
- "runtimeTargets": {
- "runtimes/win-x86/native/concrt140.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-x86/native/msvcp140.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-x86/native/msvcp140_1.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-x86/native/msvcp140_2.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-x86/native/msvcp140_atomic_wait.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-x86/native/msvcp140_codecvt_ids.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "14.42.34433.0"
- },
- "runtimes/win-x86/native/vcruntime140.dll": {
- "rid": "win-x86",
- "assetType": "native",
"fileVersion": "14.42.34433.0"
}
}
},
+ "Emgu.runtime.windows.msvc.rt.x86/19.42.34435": {},
"EntityFramework/6.4.4": {
"dependencies": {
"Microsoft.CSharp": "4.7.0",
@@ -283,546 +170,103 @@
}
}
},
- "MahApps.Metro/2.4.11": {
+ "itext/8.0.5": {
"dependencies": {
- "ControlzEx": "5.0.1"
+ "Microsoft.DotNet.PlatformAbstractions": "1.1.0",
+ "Microsoft.Extensions.DependencyModel": "10.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "System.Collections.NonGeneric": "4.3.0",
+ "System.Diagnostics.Process": "4.3.0",
+ "System.Globalization.Extensions": "4.3.0",
+ "System.Runtime.Loader": "4.3.0",
+ "System.Runtime.Serialization.Formatters": "4.3.0",
+ "System.Security.Cryptography.Csp": "4.3.0",
+ "System.Text.Encoding.CodePages": "4.3.0",
+ "System.Text.RegularExpressions": "4.3.1",
+ "System.Threading.Thread": "4.3.0",
+ "System.Threading.ThreadPool": "4.3.0",
+ "System.Xml.XmlDocument": "4.3.0",
+ "itext.commons": "8.0.5"
},
"runtime": {
- "lib/netcoreapp3.1/MahApps.Metro.dll": {
- "assemblyVersion": "2.0.0.0",
- "fileVersion": "2.4.11.0"
- }
- },
- "resources": {
- "lib/netcoreapp3.1/de/MahApps.Metro.resources.dll": {
- "locale": "de"
+ "lib/netstandard2.0/itext.barcodes.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.bouncy-castle-connector.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.forms.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.io.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.kernel.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.layout.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.pdfa.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.pdfua.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.sign.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.styledxmlparser.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
+ },
+ "lib/netstandard2.0/itext.svg.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
}
}
},
- "MahApps.Metro.IconPacks/6.2.1": {
+ "itext.bouncy-castle-adapter/8.0.5": {
"dependencies": {
- "MahApps.Metro.IconPacks.BootstrapIcons": "6.2.1",
- "MahApps.Metro.IconPacks.BoxIcons": "6.2.1",
- "MahApps.Metro.IconPacks.BoxIcons2": "6.2.1",
- "MahApps.Metro.IconPacks.CircumIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Codicons": "6.2.1",
- "MahApps.Metro.IconPacks.Coolicons": "6.2.1",
- "MahApps.Metro.IconPacks.Core": "6.2.1",
- "MahApps.Metro.IconPacks.Entypo": "6.2.1",
- "MahApps.Metro.IconPacks.EvaIcons": "6.2.1",
- "MahApps.Metro.IconPacks.FeatherIcons": "6.2.1",
- "MahApps.Metro.IconPacks.FileIcons": "6.2.1",
- "MahApps.Metro.IconPacks.FontAwesome": "6.2.1",
- "MahApps.Metro.IconPacks.FontAwesome5": "6.2.1",
- "MahApps.Metro.IconPacks.FontAwesome6": "6.2.1",
- "MahApps.Metro.IconPacks.Fontaudio": "6.2.1",
- "MahApps.Metro.IconPacks.Fontisto": "6.2.1",
- "MahApps.Metro.IconPacks.ForkAwesome": "6.2.1",
- "MahApps.Metro.IconPacks.GameIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Ionicons": "6.2.1",
- "MahApps.Metro.IconPacks.JamIcons": "6.2.1",
- "MahApps.Metro.IconPacks.KeyruneIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Lucide": "6.2.1",
- "MahApps.Metro.IconPacks.Material": "6.2.1",
- "MahApps.Metro.IconPacks.MaterialDesign": "6.2.1",
- "MahApps.Metro.IconPacks.MaterialLight": "6.2.1",
- "MahApps.Metro.IconPacks.MemoryIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Microns": "6.2.1",
- "MahApps.Metro.IconPacks.MingCuteIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Modern": "6.2.1",
- "MahApps.Metro.IconPacks.MynaUIIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Octicons": "6.2.1",
- "MahApps.Metro.IconPacks.PhosphorIcons": "6.2.1",
- "MahApps.Metro.IconPacks.PicolIcons": "6.2.1",
- "MahApps.Metro.IconPacks.PixelartIcons": "6.2.1",
- "MahApps.Metro.IconPacks.RPGAwesome": "6.2.1",
- "MahApps.Metro.IconPacks.RadixIcons": "6.2.1",
- "MahApps.Metro.IconPacks.RemixIcon": "6.2.1",
- "MahApps.Metro.IconPacks.SimpleIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Typicons": "6.2.1",
- "MahApps.Metro.IconPacks.Unicons": "6.2.1",
- "MahApps.Metro.IconPacks.VaadinIcons": "6.2.1",
- "MahApps.Metro.IconPacks.WeatherIcons": "6.2.1",
- "MahApps.Metro.IconPacks.Zondicons": "6.2.1"
+ "BouncyCastle.Cryptography": "2.2.1",
+ "itext.commons": "8.0.5"
},
"runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
+ "lib/netstandard2.0/itext.bouncy-castle-adapter.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
}
}
},
- "MahApps.Metro.IconPacks.BootstrapIcons/6.2.1": {
+ "itext.commons/8.0.5": {
"dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Newtonsoft.Json": "13.0.3"
},
"runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.BootstrapIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
+ "lib/netstandard2.0/itext.commons.dll": {
+ "assemblyVersion": "8.0.5.0",
+ "fileVersion": "8.0.5.0"
}
}
},
- "MahApps.Metro.IconPacks.BoxIcons/6.2.1": {
+ "itext7/8.0.5": {
"dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.BoxIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
+ "itext": "8.0.5"
}
},
- "MahApps.Metro.IconPacks.BoxIcons2/6.2.1": {
+ "itext7.bouncy-castle-adapter/8.0.5": {
"dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.BoxIcons2.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.CircumIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.CircumIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Codicons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Codicons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Coolicons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Coolicons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Core/6.2.1": {
- "dependencies": {
- "System.Text.Json": "10.0.0"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Core.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Entypo/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Entypo.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.EvaIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.EvaIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.FeatherIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.FeatherIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.FileIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.FileIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Fontaudio/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Fontaudio.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.FontAwesome/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.FontAwesome.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.FontAwesome5/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.FontAwesome5.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.FontAwesome6/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.FontAwesome6.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Fontisto/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Fontisto.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.ForkAwesome/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.ForkAwesome.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.GameIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.GameIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Ionicons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Ionicons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.JamIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.JamIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.KeyruneIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.KeyruneIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Lucide/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Lucide.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Material/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Material.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.MaterialDesign/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.MaterialDesign.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.MaterialLight/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.MaterialLight.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.MemoryIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.MemoryIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Microns/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Microns.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.MingCuteIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.MingCuteIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Modern/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Modern.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.MynaUIIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.MynaUIIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Octicons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Octicons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.PhosphorIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.PhosphorIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.PicolIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.PicolIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.PixelartIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.PixelartIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.RadixIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.RadixIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.RemixIcon/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.RemixIcon.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.RPGAwesome/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.RPGAwesome.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.SimpleIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.SimpleIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Typicons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Typicons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Unicons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Unicons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.VaadinIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.VaadinIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.WeatherIcons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.WeatherIcons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
- }
- },
- "MahApps.Metro.IconPacks.Zondicons/6.2.1": {
- "dependencies": {
- "MahApps.Metro.IconPacks.Core": "6.2.1"
- },
- "runtime": {
- "lib/net8.0-windows7.0/MahApps.Metro.IconPacks.Zondicons.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.2.1.0"
- }
+ "itext.bouncy-castle-adapter": "8.0.5"
}
},
"Microsoft.Bcl.AsyncInterfaces/1.1.1": {
@@ -860,6 +304,24 @@
}
}
},
+ "Microsoft.DotNet.PlatformAbstractions/1.1.0": {
+ "dependencies": {
+ "System.AppContext": "4.3.0",
+ "System.Collections": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.IO.FileSystem": "4.3.0",
+ "System.Reflection.TypeExtensions": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Runtime.InteropServices.RuntimeInformation": "4.3.0"
+ },
+ "runtime": {
+ "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": {
+ "assemblyVersion": "1.1.0.0",
+ "fileVersion": "1.1.0.0"
+ }
+ }
+ },
"Microsoft.EntityFrameworkCore/3.1.5": {
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
@@ -867,8 +329,8 @@
"Microsoft.EntityFrameworkCore.Abstractions": "3.1.5",
"Microsoft.EntityFrameworkCore.Analyzers": "3.1.5",
"Microsoft.Extensions.Caching.Memory": "3.1.5",
- "Microsoft.Extensions.DependencyInjection": "3.1.5",
- "Microsoft.Extensions.Logging": "3.1.5",
+ "Microsoft.Extensions.DependencyInjection": "5.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
"System.Collections.Immutable": "1.7.1",
"System.ComponentModel.Annotations": "4.7.0",
"System.Diagnostics.DiagnosticSource": "4.7.1"
@@ -904,8 +366,8 @@
"dependencies": {
"Microsoft.Extensions.Caching.Abstractions": "3.1.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1",
- "Microsoft.Extensions.Logging.Abstractions": "3.1.5",
- "Microsoft.Extensions.Options": "3.1.5"
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.Extensions.Caching.Memory.dll": {
@@ -949,14 +411,14 @@
}
}
},
- "Microsoft.Extensions.DependencyInjection/3.1.5": {
+ "Microsoft.Extensions.DependencyInjection/5.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1"
},
"runtime": {
- "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.dll": {
- "assemblyVersion": "3.1.5.0",
- "fileVersion": "3.100.520.27009"
+ "lib/net5.0/Microsoft.Extensions.DependencyInjection.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
}
}
},
@@ -980,37 +442,37 @@
}
}
},
- "Microsoft.Extensions.Logging/3.1.5": {
+ "Microsoft.Extensions.Logging/5.0.0": {
"dependencies": {
- "Microsoft.Extensions.Configuration.Binder": "10.0.0",
- "Microsoft.Extensions.DependencyInjection": "3.1.5",
- "Microsoft.Extensions.Logging.Abstractions": "3.1.5",
- "Microsoft.Extensions.Options": "3.1.5"
+ "Microsoft.Extensions.DependencyInjection": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0"
},
"runtime": {
- "lib/netcoreapp3.1/Microsoft.Extensions.Logging.dll": {
- "assemblyVersion": "3.1.5.0",
- "fileVersion": "3.100.520.27009"
+ "lib/netstandard2.1/Microsoft.Extensions.Logging.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
}
}
},
- "Microsoft.Extensions.Logging.Abstractions/3.1.5": {
+ "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {
- "assemblyVersion": "3.1.5.0",
- "fileVersion": "3.100.520.27009"
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
}
}
},
- "Microsoft.Extensions.Options/3.1.5": {
+ "Microsoft.Extensions.Options/5.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1",
"Microsoft.Extensions.Primitives": "10.0.0"
},
"runtime": {
- "lib/netcoreapp3.1/Microsoft.Extensions.Options.dll": {
- "assemblyVersion": "3.1.5.0",
- "fileVersion": "3.100.520.27009"
+ "lib/net5.0/Microsoft.Extensions.Options.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
}
}
},
@@ -1032,75 +494,11 @@
"Microsoft.ML.OnnxRuntime.Gpu.Linux/1.20.1": {
"dependencies": {
"Microsoft.ML.OnnxRuntime.Managed": "1.20.1"
- },
- "runtimeTargets": {
- "runtimes/linux-x64/native/libonnxruntime.so": {
- "rid": "linux-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-x64/native/libonnxruntime_providers_cuda.so": {
- "rid": "linux-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-x64/native/libonnxruntime_providers_shared.so": {
- "rid": "linux-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-x64/native/libonnxruntime_providers_tensorrt.so": {
- "rid": "linux-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- }
}
},
"Microsoft.ML.OnnxRuntime.Gpu.Windows/1.20.1": {
"dependencies": {
"Microsoft.ML.OnnxRuntime.Managed": "1.20.1"
- },
- "runtimeTargets": {
- "runtimes/win-x64/native/onnxruntime.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "1.20.24.1119"
- },
- "runtimes/win-x64/native/onnxruntime.lib": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-x64/native/onnxruntime_providers_cuda.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-x64/native/onnxruntime_providers_cuda.lib": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-x64/native/onnxruntime_providers_shared.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "1.20.24.1119"
- },
- "runtimes/win-x64/native/onnxruntime_providers_shared.lib": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-x64/native/onnxruntime_providers_tensorrt.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-x64/native/onnxruntime_providers_tensorrt.lib": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- }
}
},
"Microsoft.ML.OnnxRuntime.Managed/1.20.1": {
@@ -1162,7 +560,8 @@
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.win.Microsoft.Win32.Primitives": "4.3.0"
}
},
"Microsoft.Win32.Registry/4.7.0": {
@@ -1173,15 +572,7 @@
},
"Microsoft.Win32.SystemEvents/9.0.0": {
"runtime": {
- "lib/net8.0/Microsoft.Win32.SystemEvents.dll": {
- "assemblyVersion": "9.0.0.0",
- "fileVersion": "9.0.24.52809"
- }
- },
- "runtimeTargets": {
"runtimes/win/lib/net8.0/Microsoft.Win32.SystemEvents.dll": {
- "rid": "win",
- "assetType": "runtime",
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52809"
}
@@ -1235,7 +626,7 @@
"System.Security.Cryptography.X509Certificates": "4.3.0",
"System.Text.Encoding": "4.3.0",
"System.Text.Encoding.Extensions": "4.3.0",
- "System.Text.RegularExpressions": "4.3.0",
+ "System.Text.RegularExpressions": "4.3.1",
"System.Threading": "4.3.0",
"System.Threading.Tasks": "4.3.0",
"System.Threading.Timer": "4.3.0",
@@ -1318,6 +709,31 @@
}
}
},
+ "runtime.any.System.Collections/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.1"
+ }
+ },
+ "runtime.any.System.Diagnostics.Tools/4.3.0": {},
+ "runtime.any.System.Diagnostics.Tracing/4.3.0": {},
+ "runtime.any.System.Globalization/4.3.0": {},
+ "runtime.any.System.Globalization.Calendars/4.3.0": {},
+ "runtime.any.System.IO/4.3.0": {},
+ "runtime.any.System.Reflection/4.3.0": {},
+ "runtime.any.System.Reflection.Extensions/4.3.0": {},
+ "runtime.any.System.Reflection.Primitives/4.3.0": {},
+ "runtime.any.System.Resources.ResourceManager/4.3.0": {},
+ "runtime.any.System.Runtime/4.3.0": {
+ "dependencies": {
+ "System.Private.Uri": "4.3.0"
+ }
+ },
+ "runtime.any.System.Runtime.Handles/4.3.0": {},
+ "runtime.any.System.Runtime.InteropServices/4.3.0": {},
+ "runtime.any.System.Text.Encoding/4.3.0": {},
+ "runtime.any.System.Text.Encoding.Extensions/4.3.0": {},
+ "runtime.any.System.Threading.Tasks/4.3.0": {},
+ "runtime.any.System.Threading.Timer/4.3.0": {},
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {},
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {},
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {},
@@ -1373,31 +789,93 @@
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {},
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {},
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {},
- "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": {
- "runtimeTargets": {
- "runtimes/win-arm64/native/sni.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "4.6.25512.1"
- }
- }
- },
+ "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": {},
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni/4.4.0": {
- "runtimeTargets": {
+ "native": {
"runtimes/win-x64/native/sni.dll": {
- "rid": "win-x64",
- "assetType": "native",
"fileVersion": "4.6.25512.1"
}
}
},
- "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": {
- "runtimeTargets": {
- "runtimes/win-x86/native/sni.dll": {
- "rid": "win-x86",
- "assetType": "native",
- "fileVersion": "4.6.25512.1"
- }
+ "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": {},
+ "runtime.win.Microsoft.Win32.Primitives/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.1",
+ "System.Runtime.InteropServices": "4.3.0"
+ }
+ },
+ "runtime.win.System.Console/4.3.1": {
+ "dependencies": {
+ "System.IO": "4.3.0",
+ "System.IO.FileSystem.Primitives": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Text.Encoding.Extensions": "4.3.0",
+ "System.Threading": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ }
+ },
+ "runtime.win.System.Diagnostics.Debug/4.3.0": {},
+ "runtime.win.System.IO.FileSystem/4.3.0": {
+ "dependencies": {
+ "System.Buffers": "4.3.0",
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.IO.FileSystem.Primitives": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Text.Encoding.Extensions": "4.3.0",
+ "System.Threading": "4.3.0",
+ "System.Threading.Overlapped": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ }
+ },
+ "runtime.win.System.Net.Primitives/4.3.0": {
+ "dependencies": {
+ "Microsoft.Win32.Primitives": "4.3.0",
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Tracing": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Threading": "4.3.0"
+ }
+ },
+ "runtime.win.System.Net.Sockets/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Diagnostics.Tracing": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.IO.FileSystem": "4.3.0",
+ "System.IO.FileSystem.Primitives": "4.3.0",
+ "System.Net.NameResolution": "4.3.0",
+ "System.Net.Primitives": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Security.Principal.Windows": "4.7.0",
+ "System.Threading": "4.3.0",
+ "System.Threading.Overlapped": "4.3.0",
+ "System.Threading.Tasks": "4.3.0"
+ }
+ },
+ "runtime.win.System.Runtime.Extensions/4.3.0": {
+ "dependencies": {
+ "System.Private.Uri": "4.3.0"
}
},
"Serilog/4.3.1": {
@@ -1564,120 +1042,8 @@
}
},
"SQLitePCLRaw.lib.e_sqlite3/2.1.11": {
- "runtimeTargets": {
- "runtimes/browser-wasm/nativeassets/net8.0/e_sqlite3.a": {
- "rid": "browser-wasm",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-arm/native/libe_sqlite3.so": {
- "rid": "linux-arm",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-arm64/native/libe_sqlite3.so": {
- "rid": "linux-arm64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-armel/native/libe_sqlite3.so": {
- "rid": "linux-armel",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-mips64/native/libe_sqlite3.so": {
- "rid": "linux-mips64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-musl-arm/native/libe_sqlite3.so": {
- "rid": "linux-musl-arm",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-musl-arm64/native/libe_sqlite3.so": {
- "rid": "linux-musl-arm64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-musl-riscv64/native/libe_sqlite3.so": {
- "rid": "linux-musl-riscv64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-musl-s390x/native/libe_sqlite3.so": {
- "rid": "linux-musl-s390x",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-musl-x64/native/libe_sqlite3.so": {
- "rid": "linux-musl-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-ppc64le/native/libe_sqlite3.so": {
- "rid": "linux-ppc64le",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-riscv64/native/libe_sqlite3.so": {
- "rid": "linux-riscv64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-s390x/native/libe_sqlite3.so": {
- "rid": "linux-s390x",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-x64/native/libe_sqlite3.so": {
- "rid": "linux-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/linux-x86/native/libe_sqlite3.so": {
- "rid": "linux-x86",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/maccatalyst-arm64/native/libe_sqlite3.dylib": {
- "rid": "maccatalyst-arm64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/maccatalyst-x64/native/libe_sqlite3.dylib": {
- "rid": "maccatalyst-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/osx-arm64/native/libe_sqlite3.dylib": {
- "rid": "osx-arm64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/osx-x64/native/libe_sqlite3.dylib": {
- "rid": "osx-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-arm/native/e_sqlite3.dll": {
- "rid": "win-arm",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-arm64/native/e_sqlite3.dll": {
- "rid": "win-arm64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
+ "native": {
"runtimes/win-x64/native/e_sqlite3.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/win-x86/native/e_sqlite3.dll": {
- "rid": "win-x86",
- "assetType": "native",
"fileVersion": "0.0.0.0"
}
}
@@ -1700,25 +1066,8 @@
"fileVersion": "1.0.118.0"
}
},
- "runtimeTargets": {
- "runtimes/linux-x64/native/SQLite.Interop.dll": {
- "rid": "linux-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
- "runtimes/osx-x64/native/SQLite.Interop.dll": {
- "rid": "osx-x64",
- "assetType": "native",
- "fileVersion": "0.0.0.0"
- },
+ "native": {
"runtimes/win-x64/native/SQLite.Interop.dll": {
- "rid": "win-x64",
- "assetType": "native",
- "fileVersion": "1.0.118.0"
- },
- "runtimes/win-x86/native/SQLite.Interop.dll": {
- "rid": "win-x86",
- "assetType": "native",
"fileVersion": "1.0.118.0"
}
}
@@ -1742,7 +1091,8 @@
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Collections": "4.3.0"
}
},
"System.Collections.Concurrent/4.3.0": {
@@ -1760,17 +1110,21 @@
}
},
"System.Collections.Immutable/1.7.1": {},
+ "System.Collections.NonGeneric/4.3.0": {
+ "dependencies": {
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Threading": "4.3.0"
+ }
+ },
"System.ComponentModel.Annotations/4.7.0": {},
"System.Configuration.ConfigurationManager/8.0.0": {
"dependencies": {
"System.Diagnostics.EventLog": "8.0.0",
"System.Security.Cryptography.ProtectedData": "8.0.0"
- },
- "runtime": {
- "lib/net8.0/System.Configuration.ConfigurationManager.dll": {
- "assemblyVersion": "8.0.0.0",
- "fileVersion": "8.0.23.53103"
- }
}
},
"System.Console/4.3.0": {
@@ -1779,7 +1133,8 @@
"Microsoft.NETCore.Targets": "1.1.3",
"System.IO": "4.3.0",
"System.Runtime": "4.3.1",
- "System.Text.Encoding": "4.3.0"
+ "System.Text.Encoding": "4.3.0",
+ "runtime.win.System.Console": "4.3.1"
}
},
"System.Data.OleDb/6.0.0": {
@@ -1788,15 +1143,7 @@
"System.Diagnostics.PerformanceCounter": "6.0.0"
},
"runtime": {
- "lib/net6.0/System.Data.OleDb.dll": {
- "assemblyVersion": "6.0.0.0",
- "fileVersion": "6.0.21.52210"
- }
- },
- "runtimeTargets": {
"runtimes/win/lib/net6.0/System.Data.OleDb.dll": {
- "rid": "win",
- "assetType": "runtime",
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
@@ -1809,21 +1156,7 @@
"runtime.native.System.Data.SqlClient.sni": "4.7.0"
},
"runtime": {
- "lib/netcoreapp2.1/System.Data.SqlClient.dll": {
- "assemblyVersion": "4.6.1.1",
- "fileVersion": "4.700.20.6702"
- }
- },
- "runtimeTargets": {
- "runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": {
- "rid": "unix",
- "assetType": "runtime",
- "assemblyVersion": "4.6.1.1",
- "fileVersion": "4.700.20.6702"
- },
"runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": {
- "rid": "win",
- "assetType": "runtime",
"assemblyVersion": "4.6.1.1",
"fileVersion": "4.700.20.6702"
}
@@ -1855,43 +1188,56 @@
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.win.System.Diagnostics.Debug": "4.3.0"
}
},
"System.Diagnostics.DiagnosticSource/4.7.1": {},
- "System.Diagnostics.EventLog/8.0.0": {
- "runtime": {
- "lib/net8.0/System.Diagnostics.EventLog.dll": {
- "assemblyVersion": "8.0.0.0",
- "fileVersion": "8.0.23.53103"
- }
- },
- "runtimeTargets": {
- "runtimes/win/lib/net8.0/System.Diagnostics.EventLog.dll": {
- "rid": "win",
- "assetType": "runtime",
- "assemblyVersion": "8.0.0.0",
- "fileVersion": "8.0.23.53103"
- }
- }
- },
+ "System.Diagnostics.EventLog/8.0.0": {},
"System.Diagnostics.PerformanceCounter/6.0.0": {
"dependencies": {
"System.Configuration.ConfigurationManager": "8.0.0"
}
},
+ "System.Diagnostics.Process/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "Microsoft.Win32.Primitives": "4.3.0",
+ "Microsoft.Win32.Registry": "4.7.0",
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.IO.FileSystem": "4.3.0",
+ "System.IO.FileSystem.Primitives": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Text.Encoding.Extensions": "4.3.0",
+ "System.Threading": "4.3.0",
+ "System.Threading.Tasks": "4.3.0",
+ "System.Threading.Thread": "4.3.0",
+ "System.Threading.ThreadPool": "4.3.0",
+ "runtime.native.System": "4.3.0"
+ }
+ },
"System.Diagnostics.Tools/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Diagnostics.Tools": "4.3.0"
}
},
"System.Diagnostics.Tracing/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Diagnostics.Tracing": "4.3.0"
}
},
"System.Drawing.Common/9.0.0": {
@@ -1919,7 +1265,8 @@
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Globalization": "4.3.0"
}
},
"System.Globalization.Calendars/4.3.0": {
@@ -1927,7 +1274,8 @@
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
"System.Globalization": "4.3.0",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Globalization.Calendars": "4.3.0"
}
},
"System.Globalization.Extensions/4.3.0": {
@@ -1946,7 +1294,8 @@
"Microsoft.NETCore.Targets": "1.1.3",
"System.Runtime": "4.3.1",
"System.Text.Encoding": "4.3.0",
- "System.Threading.Tasks": "4.3.0"
+ "System.Threading.Tasks": "4.3.0",
+ "runtime.any.System.IO": "4.3.0"
}
},
"System.IO.Compression/4.3.0": {
@@ -1990,7 +1339,8 @@
"System.Runtime": "4.3.1",
"System.Runtime.Handles": "4.3.0",
"System.Text.Encoding": "4.3.0",
- "System.Threading.Tasks": "4.3.0"
+ "System.Threading.Tasks": "4.3.0",
+ "runtime.win.System.IO.FileSystem": "4.3.0"
}
},
"System.IO.FileSystem.Primitives/4.3.0": {
@@ -2067,12 +1417,31 @@
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
}
},
+ "System.Net.NameResolution/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Tracing": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.Net.Primitives": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Security.Principal.Windows": "4.7.0",
+ "System.Threading": "4.3.0",
+ "System.Threading.Tasks": "4.3.0",
+ "runtime.native.System": "4.3.0"
+ }
+ },
"System.Net.Primitives/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
"System.Runtime": "4.3.1",
- "System.Runtime.Handles": "4.3.0"
+ "System.Runtime.Handles": "4.3.0",
+ "runtime.win.System.Net.Primitives": "4.3.0"
}
},
"System.Net.Sockets/4.3.0": {
@@ -2082,7 +1451,8 @@
"System.IO": "4.3.0",
"System.Net.Primitives": "4.3.0",
"System.Runtime": "4.3.1",
- "System.Threading.Tasks": "4.3.0"
+ "System.Threading.Tasks": "4.3.0",
+ "runtime.win.System.Net.Sockets": "4.3.0"
}
},
"System.ObjectModel/4.3.0": {
@@ -2107,13 +1477,20 @@
}
}
},
+ "System.Private.Uri/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "Microsoft.NETCore.Targets": "1.1.3"
+ }
+ },
"System.Reflection/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
"System.IO": "4.3.0",
"System.Reflection.Primitives": "4.3.0",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Reflection": "4.3.0"
}
},
"System.Reflection.DispatchProxy/4.5.0": {},
@@ -2146,14 +1523,16 @@
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
"System.Reflection": "4.3.0",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Reflection.Extensions": "4.3.0"
}
},
"System.Reflection.Primitives/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Reflection.Primitives": "4.3.0"
}
},
"System.Reflection.TypeExtensions/4.3.0": {
@@ -2168,27 +1547,31 @@
"Microsoft.NETCore.Targets": "1.1.3",
"System.Globalization": "4.3.0",
"System.Reflection": "4.3.0",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Resources.ResourceManager": "4.3.0"
}
},
"System.Runtime/4.3.1": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
- "Microsoft.NETCore.Targets": "1.1.3"
+ "Microsoft.NETCore.Targets": "1.1.3",
+ "runtime.any.System.Runtime": "4.3.0"
}
},
"System.Runtime.Extensions/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.win.System.Runtime.Extensions": "4.3.0"
}
},
"System.Runtime.Handles/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Runtime.Handles": "4.3.0"
}
},
"System.Runtime.InteropServices/4.3.0": {
@@ -2198,7 +1581,8 @@
"System.Reflection": "4.3.0",
"System.Reflection.Primitives": "4.3.0",
"System.Runtime": "4.3.1",
- "System.Runtime.Handles": "4.3.0"
+ "System.Runtime.Handles": "4.3.0",
+ "runtime.any.System.Runtime.InteropServices": "4.3.0"
}
},
"System.Runtime.InteropServices.RuntimeInformation/4.3.0": {
@@ -2212,6 +1596,13 @@
"runtime.native.System": "4.3.0"
}
},
+ "System.Runtime.Loader/4.3.0": {
+ "dependencies": {
+ "System.IO": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Runtime": "4.3.1"
+ }
+ },
"System.Runtime.Numerics/4.3.0": {
"dependencies": {
"System.Globalization": "4.3.0",
@@ -2220,6 +1611,21 @@
"System.Runtime.Extensions": "4.3.0"
}
},
+ "System.Runtime.Serialization.Formatters/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Serialization.Primitives": "4.3.0"
+ }
+ },
+ "System.Runtime.Serialization.Primitives/4.3.0": {
+ "dependencies": {
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1"
+ }
+ },
"System.Security.AccessControl/4.7.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
@@ -2311,14 +1717,7 @@
"System.Threading.Tasks": "4.3.0"
}
},
- "System.Security.Cryptography.ProtectedData/8.0.0": {
- "runtime": {
- "lib/net8.0/System.Security.Cryptography.ProtectedData.dll": {
- "assemblyVersion": "8.0.0.0",
- "fileVersion": "8.0.23.53103"
- }
- }
- },
+ "System.Security.Cryptography.ProtectedData/8.0.0": {},
"System.Security.Cryptography.X509Certificates/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
@@ -2391,7 +1790,24 @@
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Text.Encoding": "4.3.0"
+ }
+ },
+ "System.Text.Encoding.CodePages/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "System.Collections": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Runtime.Handles": "4.3.0",
+ "System.Runtime.InteropServices": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Threading": "4.3.0"
}
},
"System.Text.Encoding.Extensions/4.3.0": {
@@ -2399,7 +1815,8 @@
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
"System.Runtime": "4.3.1",
- "System.Text.Encoding": "4.3.0"
+ "System.Text.Encoding": "4.3.0",
+ "runtime.any.System.Text.Encoding.Extensions": "4.3.0"
}
},
"System.Text.Encodings.Web/10.0.0": {
@@ -2408,14 +1825,6 @@
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.25.52411"
}
- },
- "runtimeTargets": {
- "runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll": {
- "rid": "browser",
- "assetType": "runtime",
- "assemblyVersion": "10.0.0.0",
- "fileVersion": "10.0.25.52411"
- }
}
},
"System.Text.Json/10.0.0": {
@@ -2430,7 +1839,7 @@
}
}
},
- "System.Text.RegularExpressions/4.3.0": {
+ "System.Text.RegularExpressions/4.3.1": {
"dependencies": {
"System.Runtime": "4.3.1"
}
@@ -2441,11 +1850,20 @@
"System.Threading.Tasks": "4.3.0"
}
},
+ "System.Threading.Overlapped/4.3.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "3.1.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Handles": "4.3.0"
+ }
+ },
"System.Threading.Tasks/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Threading.Tasks": "4.3.0"
}
},
"System.Threading.Tasks.Extensions/4.3.0": {
@@ -2455,11 +1873,23 @@
"System.Threading.Tasks": "4.3.0"
}
},
+ "System.Threading.Thread/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.1"
+ }
+ },
+ "System.Threading.ThreadPool/4.3.0": {
+ "dependencies": {
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Handles": "4.3.0"
+ }
+ },
"System.Threading.Timer/4.3.0": {
"dependencies": {
"Microsoft.NETCore.Platforms": "3.1.0",
"Microsoft.NETCore.Targets": "1.1.3",
- "System.Runtime": "4.3.1"
+ "System.Runtime": "4.3.1",
+ "runtime.any.System.Threading.Timer": "4.3.0"
}
},
"System.Xml.ReaderWriter/4.3.0": {
@@ -2476,7 +1906,7 @@
"System.Runtime.InteropServices": "4.3.0",
"System.Text.Encoding": "4.3.0",
"System.Text.Encoding.Extensions": "4.3.0",
- "System.Text.RegularExpressions": "4.3.0",
+ "System.Text.RegularExpressions": "4.3.1",
"System.Threading.Tasks": "4.3.0",
"System.Threading.Tasks.Extensions": "4.3.0"
}
@@ -2497,6 +1927,20 @@
"System.Xml.ReaderWriter": "4.3.0"
}
},
+ "System.Xml.XmlDocument/4.3.0": {
+ "dependencies": {
+ "System.Collections": "4.3.0",
+ "System.Diagnostics.Debug": "4.3.0",
+ "System.Globalization": "4.3.0",
+ "System.IO": "4.3.0",
+ "System.Resources.ResourceManager": "4.3.0",
+ "System.Runtime": "4.3.1",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Text.Encoding": "4.3.0",
+ "System.Threading": "4.3.0",
+ "System.Xml.ReaderWriter": "4.3.0"
+ }
+ },
"Telerik.UI.for.Wpf.NetCore.Xaml/2024.1.408": {
"dependencies": {
"Microsoft.EntityFrameworkCore": "3.1.5",
@@ -2786,15 +2230,16 @@
"dependencies": {
"Emgu.CV": "4.10.0.5680",
"Emgu.CV.Bitmap": "4.10.0.5680",
- "Emgu.CV.runtime.windows": "4.10.0.5680",
- "MahApps.Metro.IconPacks": "6.2.1",
"Prism.DryIoc": "9.0.537",
"Prism.Wpf": "9.0.537",
"Serilog": "4.3.1",
"XP.Common": "1.0.0"
},
"runtime": {
- "XP.Camera.dll": {}
+ "XP.Camera.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
},
"resources": {
"en-US/XP.Camera.resources.dll": {
@@ -2805,7 +2250,6 @@
"XP.Common/1.0.0": {
"dependencies": {
"Emgu.CV": "4.10.0.5680",
- "Emgu.CV.runtime.windows": "4.10.0.5680",
"Microsoft.Data.Sqlite": "10.0.3",
"Prism.Wpf": "9.0.537",
"Serilog": "4.3.1",
@@ -2815,7 +2259,10 @@
"Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408"
},
"runtime": {
- "XP.Common.dll": {}
+ "XP.Common.dll": {
+ "assemblyVersion": "1.4.16.1",
+ "fileVersion": "1.4.16.1"
+ }
},
"resources": {
"en-US/XP.Common.resources.dll": {
@@ -2836,7 +2283,10 @@
"XP.Common": "1.0.0"
},
"runtime": {
- "XP.Hardware.Detector.dll": {}
+ "XP.Hardware.Detector.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
},
"resources": {
"en-US/XP.Hardware.Detector.resources.dll": {
@@ -2859,7 +2309,10 @@
"XP.Hardware.PLC": "1.0.0"
},
"runtime": {
- "XP.Hardware.MotionControl.dll": {}
+ "XP.Hardware.MotionControl.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
},
"resources": {
"en-US/XP.Hardware.MotionControl.resources.dll": {
@@ -2880,7 +2333,10 @@
"XP.Common": "1.0.0"
},
"runtime": {
- "XP.Hardware.PLC.dll": {}
+ "XP.Hardware.PLC.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
},
"resources": {
"en-US/XP.Hardware.PLC.resources.dll": {
@@ -2903,7 +2359,10 @@
"XP.Hardware.RaySource.Comet.Messages": "1.0.0"
},
"runtime": {
- "XP.Hardware.RaySource.dll": {}
+ "XP.Hardware.RaySource.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
},
"resources": {
"en-US/XP.Hardware.RaySource.resources.dll": {
@@ -2922,17 +2381,21 @@
"Newtonsoft.Json": "13.0.3"
},
"runtime": {
- "XP.Hardware.RaySource.Comet.Messages.dll": {}
+ "XP.Hardware.RaySource.Comet.Messages.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
}
},
"XP.ImageProcessing.CfgControl/1.0.0": {
"dependencies": {
- "MahApps.Metro": "2.4.11",
- "MahApps.Metro.IconPacks": "6.2.1",
"XP.ImageProcessing.Core": "1.0.0"
},
"runtime": {
- "XP.ImageProcessing.CfgControl.dll": {}
+ "XP.ImageProcessing.CfgControl.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
},
"resources": {
"zh-CN/XP.ImageProcessing.CfgControl.resources.dll": {
@@ -2942,18 +2405,19 @@
},
"XP.ImageProcessing.Core/1.0.0": {
"dependencies": {
- "Emgu.CV": "4.10.0.5680",
- "Emgu.CV.runtime.windows": "4.10.0.5680"
+ "Emgu.CV": "4.10.0.5680"
},
"runtime": {
- "XP.ImageProcessing.Core.dll": {}
+ "XP.ImageProcessing.Core.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
}
},
"XP.ImageProcessing.Processors/1.0.0": {
"dependencies": {
"Emgu.CV": "4.10.0.5680",
"Emgu.CV.Bitmap": "4.10.0.5680",
- "Emgu.CV.runtime.windows": "4.10.0.5680",
"Microsoft.ML.OnnxRuntime.Gpu": "1.20.1",
"Serilog": "4.3.1",
"Serilog.Sinks.Console": "6.1.1",
@@ -2961,7 +2425,10 @@
"XP.ImageProcessing.Core": "1.0.0"
},
"runtime": {
- "XP.ImageProcessing.Processors.dll": {}
+ "XP.ImageProcessing.Processors.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
},
"resources": {
"zh-CN/XP.ImageProcessing.Processors.resources.dll": {
@@ -2974,7 +2441,37 @@
"Microsoft.Xaml.Behaviors.Wpf": "1.1.122"
},
"runtime": {
- "XP.ImageProcessing.RoiControl.dll": {}
+ "XP.ImageProcessing.RoiControl.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
+ }
+ },
+ "XP.ReportEngine/1.0.0": {
+ "dependencies": {
+ "Newtonsoft.Json": "13.0.3",
+ "Prism.Wpf": "9.0.537",
+ "Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408",
+ "XP.Common": "1.0.0",
+ "itext7": "8.0.5",
+ "itext7.bouncy-castle-adapter": "8.0.5"
+ },
+ "runtime": {
+ "XP.ReportEngine.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
+ },
+ "resources": {
+ "en-US/XP.ReportEngine.resources.dll": {
+ "locale": "en-US"
+ },
+ "zh-CN/XP.ReportEngine.resources.dll": {
+ "locale": "zh-CN"
+ },
+ "zh-TW/XP.ReportEngine.resources.dll": {
+ "locale": "zh-TW"
+ }
}
},
"BR.AN.PviServices/1.1.0.0": {
@@ -3009,6 +2506,13 @@
"serviceable": false,
"sha512": ""
},
+ "BouncyCastle.Cryptography/2.2.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-A6Zr52zVqJKt18ZBsTnX0qhG0kwIQftVAjLmszmkiR/trSp8H+xj1gUOzk7XHwaKgyREMSV1v9XaKrBUeIOdvQ==",
+ "path": "bouncycastle.cryptography/2.2.1",
+ "hashPath": "bouncycastle.cryptography.2.2.1.nupkg.sha512"
+ },
"ControlzEx/5.0.1": {
"type": "package",
"serviceable": true,
@@ -3079,320 +2583,40 @@
"path": "fluent.ribbon/9.0.0",
"hashPath": "fluent.ribbon.9.0.0.nupkg.sha512"
},
- "MahApps.Metro/2.4.11": {
+ "itext/8.0.5": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-pvk757N/ViZy+zlbB56AakryuZkts328M6pX2mMn/Z7r5ifkScDrS300zSBjLd+eGOPeHgUjH4f7cZbRiMyvQg==",
- "path": "mahapps.metro/2.4.11",
- "hashPath": "mahapps.metro.2.4.11.nupkg.sha512"
+ "sha512": "sha512-AptrL1Kvm4SrrkZSSfvQF9heRHekr9R91gR/3hEFR0uxJlBdVMmVnPQiTS+AhxJwalNtGNytKeFMWRoEUpWuTg==",
+ "path": "itext/8.0.5",
+ "hashPath": "itext.8.0.5.nupkg.sha512"
},
- "MahApps.Metro.IconPacks/6.2.1": {
+ "itext.bouncy-castle-adapter/8.0.5": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-D4iq02tGW42jn0go7M+mBI8uMkfAmUrPwQl9gaV3WqiIssjABwP9vAtU05NTWUx9lB0xl+YlfcnVfFoKki0Ylw==",
- "path": "mahapps.metro.iconpacks/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.6.2.1.nupkg.sha512"
+ "sha512": "sha512-mam0CtYkKmVwdX7wU1Zlg9oVW0B9efMASfAEIm+fQGovc9HcplsmP3MylS+VmQHbYlCNSH68DQlVhDz6Cu9WxQ==",
+ "path": "itext.bouncy-castle-adapter/8.0.5",
+ "hashPath": "itext.bouncy-castle-adapter.8.0.5.nupkg.sha512"
},
- "MahApps.Metro.IconPacks.BootstrapIcons/6.2.1": {
+ "itext.commons/8.0.5": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-dTd5CaF63Ke2mWKlpagKEoU7sQmAtpoTyIMrZulPtI8N6OEkA6my3ekTPEt0OHu68zjiiXzZA5HLH2dlHUX+9w==",
- "path": "mahapps.metro.iconpacks.bootstrapicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.bootstrapicons.6.2.1.nupkg.sha512"
+ "sha512": "sha512-exnLqeWOnn0Ee393bHf3zWTmcYuQfYuj7iIbMluG49XFczMWMMXrN9UvMmt5iYOKq0mGZsYb1eRoVATGuDiXGQ==",
+ "path": "itext.commons/8.0.5",
+ "hashPath": "itext.commons.8.0.5.nupkg.sha512"
},
- "MahApps.Metro.IconPacks.BoxIcons/6.2.1": {
+ "itext7/8.0.5": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-fvxelxwj1Fr+osvmJnvlSthN34ZpwWMDacCWSGpylw8Pz+bFWcPIA3DxDMbxshvUhZ5dYzigCOxHZKG/BHPEdw==",
- "path": "mahapps.metro.iconpacks.boxicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.boxicons.6.2.1.nupkg.sha512"
+ "sha512": "sha512-/1btEFYY+ncIR7j/g+pr26PX8Nbly8pbdDWfSHQq9E/rbxhz0ylkOLVpXNEvoaV545q508P8hociHaRiRG7bwg==",
+ "path": "itext7/8.0.5",
+ "hashPath": "itext7.8.0.5.nupkg.sha512"
},
- "MahApps.Metro.IconPacks.BoxIcons2/6.2.1": {
+ "itext7.bouncy-castle-adapter/8.0.5": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-TkTvGg1L/w3kCGB7D7Hqd3EhYevijeknuh/VGVsaF47J4eOwf42Y2zXV7rpCXAY13zfrGWCq8RQQdacwFStQow==",
- "path": "mahapps.metro.iconpacks.boxicons2/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.boxicons2.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.CircumIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-6L+lBg3Cj8Tq1TK0FHIBvS4t9iDa9V966dGZrzNAryZD/jQ2ncvz4rIDsZpSnsrLIhxMqChLvNogg3sDEuchtA==",
- "path": "mahapps.metro.iconpacks.circumicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.circumicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Codicons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-+pQeVS29X7pG7qrJ4y0ZPSRUQVaWidwwM/tdoIABo40MQpC0Izg8t1BXnXprP0C10Od9Zzsi2Qt/GWgfEW/aSw==",
- "path": "mahapps.metro.iconpacks.codicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.codicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Coolicons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-YdAqLEsLON2ripNNXkzrVX3iHQgEVXvfcvChx/2UsFyefV63RMVRu1rXCtLrOGRm/NAl7okTu2d6IeUVj+bCFQ==",
- "path": "mahapps.metro.iconpacks.coolicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.coolicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Core/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-ySAyax37C045Ef1ufN33yQjn3qPM1MuQLgKdxZKYUAl2uZaIrn9wCeBNvfG46QPNRvBFLR6gVKTs6O3o81LNhQ==",
- "path": "mahapps.metro.iconpacks.core/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.core.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Entypo/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-H1xzNFcF1gPgkrMFT3RgVc+whTZ88+g1hFUzaRkZYjH7k4JGGxsb6dqVjHyMQCbSygCUiWNuzy0yzNrv1krQOg==",
- "path": "mahapps.metro.iconpacks.entypo/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.entypo.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.EvaIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-bUQfSeAb0uphQrhks3Mhj5kk+WsqL22dPF5b9DSVrRi7bHgWt2+ciMZZp85Q+zFKtHM3PkdGC2nUbl0bBu6AyQ==",
- "path": "mahapps.metro.iconpacks.evaicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.evaicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.FeatherIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-a5sxUHp39ejJu6p2wbbJT71tYY4zYuS+N4Vb9VxcnsQqaeMktuHtiy1ySR43h4dkdzhAohbyxvb9j80AkDXZLA==",
- "path": "mahapps.metro.iconpacks.feathericons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.feathericons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.FileIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-Rir1rKL8LNhxhWNmVdWDbSRjoJg+y0t4kqDdD5zLH6T7uFm6yjPvIEK4pvA30NIFPgvl4bkN2TKQLv+7P4FNDw==",
- "path": "mahapps.metro.iconpacks.fileicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.fileicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Fontaudio/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-AsYb9vBKtkYBPV/3OEGTijH+wrlxxPcMFRXVbbRZLz1E9JIoGxkRcwnFryWs2bhDIMiLVzj2DgYClqvJSovmEw==",
- "path": "mahapps.metro.iconpacks.fontaudio/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.fontaudio.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.FontAwesome/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-Su6jzajwH8rP7H5OGhXvyC2SlsoyH0pNwNCkDFSP5zyVQlMtmUZGOY8X0Qcj1QFn+t4TmEnMjwGMB6RuzozgEg==",
- "path": "mahapps.metro.iconpacks.fontawesome/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.fontawesome.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.FontAwesome5/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-PNTiFAT/+ro1SitUj8k+gGWDq7mC/eYD1cy8QfF84ZyCp+goLh5BApj0IGvv+eisLMw7hFIIzI8vIHQ1GbyExw==",
- "path": "mahapps.metro.iconpacks.fontawesome5/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.fontawesome5.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.FontAwesome6/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-IB2AdqnGAdM2RK8Ekmf8U2VBw7cAnRQHQaVJ1Kv/0Ek+VavsmUqlyotmSg28iL4Sq9BBC2SQrRbvuIGfz9seWA==",
- "path": "mahapps.metro.iconpacks.fontawesome6/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.fontawesome6.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Fontisto/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-+JDR5+YpYq/iBdj7DZiT5XA4U5+I2kxqxGK6usqOi077iemIEXKWxhrEiWyruHfRQt4VrD/WGy27PrJzU/h6cA==",
- "path": "mahapps.metro.iconpacks.fontisto/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.fontisto.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.ForkAwesome/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-luVbeZF3hH7DQl+QCT6NV9vGJ8a1CtOJd0W2AShOms8YuAU4RnsHMm9jrrONpCBeVqQI8mpFRTzd0bYit8gilg==",
- "path": "mahapps.metro.iconpacks.forkawesome/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.forkawesome.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.GameIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-oOVAHye1k+cjfXaGfBP6nu5AtZND65WhsolAqmS8wMtM5wPN5q0k8jUq3lxm5/MRMh+rCT99oAcZKmyDaRlcVA==",
- "path": "mahapps.metro.iconpacks.gameicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.gameicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Ionicons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-WBHtfiUfEGUPiLaJkQ2ZXipo01eRbW6PM6qI7S4LLPGs5FM9YIy6PiaeahN8FcYS/HnYx8E2HSJ99R+U+M2nBA==",
- "path": "mahapps.metro.iconpacks.ionicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.ionicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.JamIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-9Jc2IzyBa0vASPCup7woKHTSwG31rtBvZsdpA+Jot++eWkZpvCpgWn8BKdcNcIh3Hjnve9qLzPWb11WobPO2CQ==",
- "path": "mahapps.metro.iconpacks.jamicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.jamicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.KeyruneIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-9Cs7gIl1WCTsZHPgX0Ri32r8vTPi32Zxf/z/3CxLpfksY7qsTodCWWWlIgjEqxriWE/AGoUzdh1o73j5qtQ/RQ==",
- "path": "mahapps.metro.iconpacks.keyruneicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.keyruneicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Lucide/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-xehG2J/Ljfa6NxlKCqvDxnxUMrApR0xSLGaoaFSVN531jF+WNZNAaUvE9qkxOHKZ+jpH4gUnb2sC3qNcpAGVkA==",
- "path": "mahapps.metro.iconpacks.lucide/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.lucide.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Material/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-DcNQIsJLAmuzlpXdD2hQtMV8IzLRHsxFBn9+nHDLAsYs8duTBTycpNpQitkzuNYH0o+sgIbefCSp3jDUu9hIiQ==",
- "path": "mahapps.metro.iconpacks.material/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.material.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.MaterialDesign/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-qMQHCZzHoBZy3bcU9CBf9GusPMpbxy0DPxiJrfbhM7IHh/hK5xWREoP2TOevqAVTEOk3YszWbiJBkYo/oeEliw==",
- "path": "mahapps.metro.iconpacks.materialdesign/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.materialdesign.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.MaterialLight/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-8ypOe4HwSGnoQl/JIpC5hb6VnK+xFtSbvhlTmw+cnJVNluiwWdpJv+m9jpU3/9+SRkJRHVgHvEMwEXQx/XmS4A==",
- "path": "mahapps.metro.iconpacks.materiallight/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.materiallight.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.MemoryIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-TIU3n0ZXhKgpSaSlqg3EEjPBp+E89UyV6nOhK4DCP9yUbnZ/CuLmkwrrd2rtt9au+VD+TB1lWbA6+3NjpH7G9Q==",
- "path": "mahapps.metro.iconpacks.memoryicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.memoryicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Microns/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-stnuTZXy+k7BmKGvI4oP0NlspElhernJ4yFXUlEkA3X/38qlqq+YltM5KPPKgQo1BLHj58lOOn+U03grfdPI5A==",
- "path": "mahapps.metro.iconpacks.microns/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.microns.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.MingCuteIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-AfMV+SbqWabsuQdS6wt7SpinS5cah+7Pkdj+bfhHBBizdMsL+nY8+tiwmKF1up+wfZm8lCvnrCQWTkJ2mMSjUA==",
- "path": "mahapps.metro.iconpacks.mingcuteicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.mingcuteicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Modern/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-xCDNREDX37HZE1YIAwQ9YOwwhQLHRby1ec1FwLKRU6MWX0OJuceqk3HY3532xG7KpGGaOIV/dpB5vFq3oo+wug==",
- "path": "mahapps.metro.iconpacks.modern/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.modern.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.MynaUIIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-StYsKBuK2kKfUdkKq6ZDa5xtlWk4gyI59ip9mS30niYxn4K0yAZIMEyx1EX4efpGanlLF4oPGZ/YUvWGfl98NA==",
- "path": "mahapps.metro.iconpacks.mynauiicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.mynauiicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Octicons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-1WGEPYjdcOMgXNspZHhYXooNJbylZyqLAkzXiCvlu1pW6HinBzaJI+vSkPtoZaF9QaMkSxwT2ZxG4mMkbhUowA==",
- "path": "mahapps.metro.iconpacks.octicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.octicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.PhosphorIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-PTfhQXTe3INWoELJy0dCfm92/faS6hF3F/C4kZyTiVe9Pgf21cd5v7kIBeT2m/eNHyEBFZX2ifmk8Q3xK1dr7g==",
- "path": "mahapps.metro.iconpacks.phosphoricons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.phosphoricons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.PicolIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-6lEuXdF77v9Ip4QoDEJm7+mrfVRz7EV3LJGxDzVfPsSPwA4VxIsqOU0xHKDQ/5Ekv+gPXW/PQtecfjlQ+UrWGw==",
- "path": "mahapps.metro.iconpacks.picolicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.picolicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.PixelartIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-KJwxtRvqpMHRkghru0H7AZcDgUDpgNq4bQTclivcQyUTChedfZh64REfJ1B5QCfKdCKmvVMR66JpJnGoM6ekdA==",
- "path": "mahapps.metro.iconpacks.pixelarticons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.pixelarticons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.RadixIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-1COtOqMAtgOydnx8cU/ukwNn4KRAzaFp5+SCucYgjyq0vdm1+1hi8inNDEZvIJqAhCSdCU13afFhsL/1KYLbBQ==",
- "path": "mahapps.metro.iconpacks.radixicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.radixicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.RemixIcon/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-ROcfFFN2S45IV7dqU6Alf8i5gMIG+4iArm8Mzb+KXjiyLOHpTZ/QHGIy9DtqIG4NuCO9pwf+fNTGCsDlXIG3TQ==",
- "path": "mahapps.metro.iconpacks.remixicon/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.remixicon.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.RPGAwesome/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-6zHlSIU5z5meUIjDpwX872Z9Zd1ZhBRuohWiy0a+AUevdOpPw9or7rGpiMAdAzEvmab4sy9Un787A4YC5LQr7w==",
- "path": "mahapps.metro.iconpacks.rpgawesome/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.rpgawesome.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.SimpleIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-EbK+/s00A72ATU+Qpk72ZxQsLvVGGRD8UA/jsGDqV0Hlym0I//dtQjYwWxzfwmTp2Ay3Sh6RmLu49qqv3Ey2/Q==",
- "path": "mahapps.metro.iconpacks.simpleicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.simpleicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Typicons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-o6kNl3wKxsO1MkgRg8V/wY7un1ueNBbqy5Ut3Y46mC2vVzEwxcejss5+BtuO/oFud3GQtet2Ng53z+70dNjG4Q==",
- "path": "mahapps.metro.iconpacks.typicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.typicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Unicons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-JdTZD7CtLjcf+Ne0jN6EsyvG/Dl+v4w08OQRSC9XTknuVW/zlJyHcxbJy+NvkBdcO+Jup/TwNslV5g9xVheavQ==",
- "path": "mahapps.metro.iconpacks.unicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.unicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.VaadinIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-6GOuzyvLNojG+NLZDULsWSfQ+RAh2v8W6n2iPWc+c1pDcmgbuKCPiazQXb0YpPuldGEOpK1ur6WJOXXsrTRR6A==",
- "path": "mahapps.metro.iconpacks.vaadinicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.vaadinicons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.WeatherIcons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-ChU6387+6rDPQZRS/L7wT4YFMmSvv0AdeCBAPVZTeW3SdeFt49tOWQvz2Xpb+N55Qn4tGjb+/lE2dg/NAYYU2w==",
- "path": "mahapps.metro.iconpacks.weathericons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.weathericons.6.2.1.nupkg.sha512"
- },
- "MahApps.Metro.IconPacks.Zondicons/6.2.1": {
- "type": "package",
- "serviceable": true,
- "sha512": "sha512-6+IMX7VptiD05ctXsjhEcvBHx0Gbj4DH1j13Ko5gk9yO7m6cCYaWqsKlt/GMH5CJEfa7Obz9+1pRstOzxZ03+A==",
- "path": "mahapps.metro.iconpacks.zondicons/6.2.1",
- "hashPath": "mahapps.metro.iconpacks.zondicons.6.2.1.nupkg.sha512"
+ "sha512": "sha512-9jpalaOyxbcuhbl9yJJQGXdseKUFrKlUjjcjIvReb3Qz4Aw0F+WNXKOo0sax87TGGNb66fyHsfJib3JTnfzRYw==",
+ "path": "itext7.bouncy-castle-adapter/8.0.5",
+ "hashPath": "itext7.bouncy-castle-adapter.8.0.5.nupkg.sha512"
},
"Microsoft.Bcl.AsyncInterfaces/1.1.1": {
"type": "package",
@@ -3429,6 +2653,13 @@
"path": "microsoft.data.sqlite.core/10.0.3",
"hashPath": "microsoft.data.sqlite.core.10.0.3.nupkg.sha512"
},
+ "Microsoft.DotNet.PlatformAbstractions/1.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Bl6KYfbFSIW3QIRHAp931iR5h01qHjKghdpAtncwbzNUs0+IUZ+XfwkIU0sQsR33ufGvi3u4dZMIYYFysjpHAA==",
+ "path": "microsoft.dotnet.platformabstractions/1.1.0",
+ "hashPath": "microsoft.dotnet.platformabstractions.1.1.0.nupkg.sha512"
+ },
"Microsoft.EntityFrameworkCore/3.1.5": {
"type": "package",
"serviceable": true,
@@ -3485,12 +2716,12 @@
"path": "microsoft.extensions.configuration.binder/10.0.0",
"hashPath": "microsoft.extensions.configuration.binder.10.0.0.nupkg.sha512"
},
- "Microsoft.Extensions.DependencyInjection/3.1.5": {
+ "Microsoft.Extensions.DependencyInjection/5.0.0": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-I+RTJQi7TtenIHZqL2zr6523PYXfL88Ruu4UIVmspIxdw14GHd8zZ+2dGLSdwX7fn41Hth4d42S1e1iHWVOJyQ==",
- "path": "microsoft.extensions.dependencyinjection/3.1.5",
- "hashPath": "microsoft.extensions.dependencyinjection.3.1.5.nupkg.sha512"
+ "sha512": "sha512-Rc2kb/p3Ze6cP6rhFC3PJRdWGbLvSHZc0ev7YlyeU6FmHciDMLrhoVoTUEzKPhN5ZjFgKF1Cf5fOz8mCMIkvpA==",
+ "path": "microsoft.extensions.dependencyinjection/5.0.0",
+ "hashPath": "microsoft.extensions.dependencyinjection.5.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.1": {
"type": "package",
@@ -3506,26 +2737,26 @@
"path": "microsoft.extensions.dependencymodel/10.0.0",
"hashPath": "microsoft.extensions.dependencymodel.10.0.0.nupkg.sha512"
},
- "Microsoft.Extensions.Logging/3.1.5": {
+ "Microsoft.Extensions.Logging/5.0.0": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-C85NYDym6xy03o70vxX+VQ4ZEjj5Eg5t5QoGW0t100vG5MmPL6+G3XXcQjIIn1WRQrjzGWzQwuKf38fxXEWIWA==",
- "path": "microsoft.extensions.logging/3.1.5",
- "hashPath": "microsoft.extensions.logging.3.1.5.nupkg.sha512"
+ "sha512": "sha512-MgOwK6tPzB6YNH21wssJcw/2MKwee8b2gI7SllYfn6rvTpIrVvVS5HAjSU2vqSku1fwqRvWP0MdIi14qjd93Aw==",
+ "path": "microsoft.extensions.logging/5.0.0",
+ "hashPath": "microsoft.extensions.logging.5.0.0.nupkg.sha512"
},
- "Microsoft.Extensions.Logging.Abstractions/3.1.5": {
+ "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-ZvwowjRSWXewdPI+whPFXgwF4Qme6Q9KV9SCPEITSGiqHLArct7q5hTBtTzj3GPsVLjTqehvTg6Bd/EQk9JS0A==",
- "path": "microsoft.extensions.logging.abstractions/3.1.5",
- "hashPath": "microsoft.extensions.logging.abstractions.3.1.5.nupkg.sha512"
+ "sha512": "sha512-NxP6ahFcBnnSfwNBi2KH2Oz8Xl5Sm2krjId/jRR3I7teFphwiUoUeZPwTNA21EX+5PtjqmyAvKaOeBXcJjcH/w==",
+ "path": "microsoft.extensions.logging.abstractions/5.0.0",
+ "hashPath": "microsoft.extensions.logging.abstractions.5.0.0.nupkg.sha512"
},
- "Microsoft.Extensions.Options/3.1.5": {
+ "Microsoft.Extensions.Options/5.0.0": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-f+JT/7lkKBMp/Ak2tVjO+TD7o+UoCfjnExkZNn0PZIso8kIXrqNy6x42Lrxf4Q0pW3JMf9ExmL2EQlvk2XnFAg==",
- "path": "microsoft.extensions.options/3.1.5",
- "hashPath": "microsoft.extensions.options.3.1.5.nupkg.sha512"
+ "sha512": "sha512-CBvR92TCJ5uBIdd9/HzDSrxYak+0W/3+yxrNg8Qm6Bmrkh5L+nu6m3WeazQehcZ5q1/6dDA7J5YdQjim0165zg==",
+ "path": "microsoft.extensions.options/5.0.0",
+ "hashPath": "microsoft.extensions.options.5.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/10.0.0": {
"type": "package",
@@ -3688,6 +2919,125 @@
"path": "prism.wpf/9.0.537",
"hashPath": "prism.wpf.9.0.537.nupkg.sha512"
},
+ "runtime.any.System.Collections/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==",
+ "path": "runtime.any.system.collections/4.3.0",
+ "hashPath": "runtime.any.system.collections.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Diagnostics.Tools/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-S/GPBmfPBB48ZghLxdDR7kDAJVAqgAuThyDJho3OLP5OS4tWD2ydyL8LKm8lhiBxce10OKe9X2zZ6DUjAqEbPg==",
+ "path": "runtime.any.system.diagnostics.tools/4.3.0",
+ "hashPath": "runtime.any.system.diagnostics.tools.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Diagnostics.Tracing/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-1lpifymjGDzoYIaam6/Hyqf8GhBI3xXYLK2TgEvTtuZMorG3Kb9QnMTIKhLjJYXIiu1JvxjngHvtVFQQlpQ3HQ==",
+ "path": "runtime.any.system.diagnostics.tracing/4.3.0",
+ "hashPath": "runtime.any.system.diagnostics.tracing.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Globalization/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==",
+ "path": "runtime.any.system.globalization/4.3.0",
+ "hashPath": "runtime.any.system.globalization.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Globalization.Calendars/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-M1r+760j1CNA6M/ZaW6KX8gOS8nxPRqloqDcJYVidRG566Ykwcs29AweZs2JF+nMOCgWDiMfPSTMfvwOI9F77w==",
+ "path": "runtime.any.system.globalization.calendars/4.3.0",
+ "hashPath": "runtime.any.system.globalization.calendars.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.IO/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==",
+ "path": "runtime.any.system.io/4.3.0",
+ "hashPath": "runtime.any.system.io.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==",
+ "path": "runtime.any.system.reflection/4.3.0",
+ "hashPath": "runtime.any.system.reflection.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==",
+ "path": "runtime.any.system.reflection.extensions/4.3.0",
+ "hashPath": "runtime.any.system.reflection.extensions.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Reflection.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==",
+ "path": "runtime.any.system.reflection.primitives/4.3.0",
+ "hashPath": "runtime.any.system.reflection.primitives.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Resources.ResourceManager/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==",
+ "path": "runtime.any.system.resources.resourcemanager/4.3.0",
+ "hashPath": "runtime.any.system.resources.resourcemanager.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==",
+ "path": "runtime.any.system.runtime/4.3.0",
+ "hashPath": "runtime.any.system.runtime.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime.Handles/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==",
+ "path": "runtime.any.system.runtime.handles/4.3.0",
+ "hashPath": "runtime.any.system.runtime.handles.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Runtime.InteropServices/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==",
+ "path": "runtime.any.system.runtime.interopservices/4.3.0",
+ "hashPath": "runtime.any.system.runtime.interopservices.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Text.Encoding/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==",
+ "path": "runtime.any.system.text.encoding/4.3.0",
+ "hashPath": "runtime.any.system.text.encoding.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Text.Encoding.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-NLrxmLsfRrOuVqPWG+2lrQZnE53MLVeo+w9c54EV+TUo4c8rILpsDXfY8pPiOy9kHpUHHP07ugKmtsU3vVW5Jg==",
+ "path": "runtime.any.system.text.encoding.extensions/4.3.0",
+ "hashPath": "runtime.any.system.text.encoding.extensions.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Threading.Tasks/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==",
+ "path": "runtime.any.system.threading.tasks/4.3.0",
+ "hashPath": "runtime.any.system.threading.tasks.4.3.0.nupkg.sha512"
+ },
+ "runtime.any.System.Threading.Timer/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-w4ehZJ+AwXYmGwYu+rMvym6RvMaRiUEQR1u6dwcyuKHxz8Heu/mO9AG1MquEgTyucnhv3M43X0iKpDOoN17C0w==",
+ "path": "runtime.any.system.threading.timer/4.3.0",
+ "hashPath": "runtime.any.system.threading.timer.4.3.0.nupkg.sha512"
+ },
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {
"type": "package",
"serviceable": true,
@@ -3828,6 +3178,55 @@
"path": "runtime.win-x86.runtime.native.system.data.sqlclient.sni/4.4.0",
"hashPath": "runtime.win-x86.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512"
},
+ "runtime.win.Microsoft.Win32.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-NU51SEt/ZaD2MF48sJ17BIqx7rjeNNLXUevfMOjqQIetdndXwYjZfZsT6jD+rSWp/FYxjesdK4xUSl4OTEI0jw==",
+ "path": "runtime.win.microsoft.win32.primitives/4.3.0",
+ "hashPath": "runtime.win.microsoft.win32.primitives.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Console/4.3.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-vHPXC3B18dxhyipVce8xQT1MQv1o5srYZqBlCNu9p9MNjhgGOntdQh/Xh2X4o7M2F839YUcQiGwu8Q498FyDjg==",
+ "path": "runtime.win.system.console/4.3.1",
+ "hashPath": "runtime.win.system.console.4.3.1.nupkg.sha512"
+ },
+ "runtime.win.System.Diagnostics.Debug/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hHHP0WCStene2jjeYcuDkETozUYF/3sHVRHAEOgS3L15hlip24ssqCTnJC28Z03Wpo078oMcJd0H4egD2aJI8g==",
+ "path": "runtime.win.system.diagnostics.debug/4.3.0",
+ "hashPath": "runtime.win.system.diagnostics.debug.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.IO.FileSystem/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Z37zcSCpXuGCYtFbqYO0TwOVXxS2d+BXgSoDFZmRg8BC4Cuy54edjyIvhhcfCrDQA9nl+EPFTgHN54dRAK7mNA==",
+ "path": "runtime.win.system.io.filesystem/4.3.0",
+ "hashPath": "runtime.win.system.io.filesystem.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Net.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-lkXXykakvXUU+Zq2j0pC6EO20lEhijjqMc01XXpp1CJN+DeCwl3nsj4t5Xbpz3kA7yQyTqw6d9SyIzsyLsV3zA==",
+ "path": "runtime.win.system.net.primitives/4.3.0",
+ "hashPath": "runtime.win.system.net.primitives.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Net.Sockets/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-FK/2gX6MmuLIKNCGsV59Fe4IYrLrI5n9pQ1jh477wiivEM/NCXDT2dRetH5FSfY0bQ+VgTLcS3zcmjQ8my3nxQ==",
+ "path": "runtime.win.system.net.sockets/4.3.0",
+ "hashPath": "runtime.win.system.net.sockets.4.3.0.nupkg.sha512"
+ },
+ "runtime.win.System.Runtime.Extensions/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-RkgHVhUPvzZxuUubiZe8yr/6CypRVXj0VBzaR8hsqQ8f+rUo7e4PWrHTLOCjd8fBMGWCrY//fi7Ku3qXD7oHRw==",
+ "path": "runtime.win.system.runtime.extensions/4.3.0",
+ "hashPath": "runtime.win.system.runtime.extensions.4.3.0.nupkg.sha512"
+ },
"Serilog/4.3.1": {
"type": "package",
"serviceable": true,
@@ -3989,6 +3388,13 @@
"path": "system.collections.immutable/1.7.1",
"hashPath": "system.collections.immutable.1.7.1.nupkg.sha512"
},
+ "System.Collections.NonGeneric/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==",
+ "path": "system.collections.nongeneric/4.3.0",
+ "hashPath": "system.collections.nongeneric.4.3.0.nupkg.sha512"
+ },
"System.ComponentModel.Annotations/4.7.0": {
"type": "package",
"serviceable": true,
@@ -4073,6 +3479,13 @@
"path": "system.diagnostics.performancecounter/6.0.0",
"hashPath": "system.diagnostics.performancecounter.6.0.0.nupkg.sha512"
},
+ "System.Diagnostics.Process/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-J0wOX07+QASQblsfxmIMFc9Iq7KTXYL3zs2G/Xc704Ylv3NpuVdo6gij6V3PGiptTxqsK0K7CdXenRvKUnkA2g==",
+ "path": "system.diagnostics.process/4.3.0",
+ "hashPath": "system.diagnostics.process.4.3.0.nupkg.sha512"
+ },
"System.Diagnostics.Tools/4.3.0": {
"type": "package",
"serviceable": true,
@@ -4192,6 +3605,13 @@
"path": "system.net.http/4.3.0",
"hashPath": "system.net.http.4.3.0.nupkg.sha512"
},
+ "System.Net.NameResolution/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-AFYl08R7MrsrEjqpQWTZWBadqXyTzNDaWpMqyxhb0d6sGhV6xMDKueuBXlLL30gz+DIRY6MpdgnHWlCh5wmq9w==",
+ "path": "system.net.nameresolution/4.3.0",
+ "hashPath": "system.net.nameresolution.4.3.0.nupkg.sha512"
+ },
"System.Net.Primitives/4.3.0": {
"type": "package",
"serviceable": true,
@@ -4220,6 +3640,13 @@
"path": "system.private.servicemodel/4.7.0",
"hashPath": "system.private.servicemodel.4.7.0.nupkg.sha512"
},
+ "System.Private.Uri/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==",
+ "path": "system.private.uri/4.3.0",
+ "hashPath": "system.private.uri.4.3.0.nupkg.sha512"
+ },
"System.Reflection/4.3.0": {
"type": "package",
"serviceable": true,
@@ -4318,6 +3745,13 @@
"path": "system.runtime.interopservices.runtimeinformation/4.3.0",
"hashPath": "system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512"
},
+ "System.Runtime.Loader/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-DHMaRn8D8YCK2GG2pw+UzNxn/OHVfaWx7OTLBD/hPegHZZgcZh3H6seWegrC4BYwsfuGrywIuT+MQs+rPqRLTQ==",
+ "path": "system.runtime.loader/4.3.0",
+ "hashPath": "system.runtime.loader.4.3.0.nupkg.sha512"
+ },
"System.Runtime.Numerics/4.3.0": {
"type": "package",
"serviceable": true,
@@ -4325,6 +3759,20 @@
"path": "system.runtime.numerics/4.3.0",
"hashPath": "system.runtime.numerics.4.3.0.nupkg.sha512"
},
+ "System.Runtime.Serialization.Formatters/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-KT591AkTNFOTbhZlaeMVvfax3RqhH1EJlcwF50Wm7sfnBLuHiOeZRRKrr1ns3NESkM20KPZ5Ol/ueMq5vg4QoQ==",
+ "path": "system.runtime.serialization.formatters/4.3.0",
+ "hashPath": "system.runtime.serialization.formatters.4.3.0.nupkg.sha512"
+ },
+ "System.Runtime.Serialization.Primitives/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
+ "path": "system.runtime.serialization.primitives/4.3.0",
+ "hashPath": "system.runtime.serialization.primitives.4.3.0.nupkg.sha512"
+ },
"System.Security.AccessControl/4.7.0": {
"type": "package",
"serviceable": true,
@@ -4437,6 +3885,13 @@
"path": "system.text.encoding/4.3.0",
"hashPath": "system.text.encoding.4.3.0.nupkg.sha512"
},
+ "System.Text.Encoding.CodePages/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-IRiEFUa5b/Gs5Egg8oqBVoywhtOeaO2KOx3j0RfcYY/raxqBuEK7NXRDgOwtYM8qbi+7S4RPXUbNt+ZxyY0/NQ==",
+ "path": "system.text.encoding.codepages/4.3.0",
+ "hashPath": "system.text.encoding.codepages.4.3.0.nupkg.sha512"
+ },
"System.Text.Encoding.Extensions/4.3.0": {
"type": "package",
"serviceable": true,
@@ -4458,12 +3913,12 @@
"path": "system.text.json/10.0.0",
"hashPath": "system.text.json.10.0.0.nupkg.sha512"
},
- "System.Text.RegularExpressions/4.3.0": {
+ "System.Text.RegularExpressions/4.3.1": {
"type": "package",
"serviceable": true,
- "sha512": "sha512-RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==",
- "path": "system.text.regularexpressions/4.3.0",
- "hashPath": "system.text.regularexpressions.4.3.0.nupkg.sha512"
+ "sha512": "sha512-N0kNRrWe4+nXOWlpLT4LAY5brb8caNFlUuIRpraCVMDLYutKkol1aV079rQjLuSxKMJT2SpBQsYX9xbcTMmzwg==",
+ "path": "system.text.regularexpressions/4.3.1",
+ "hashPath": "system.text.regularexpressions.4.3.1.nupkg.sha512"
},
"System.Threading/4.3.0": {
"type": "package",
@@ -4472,6 +3927,13 @@
"path": "system.threading/4.3.0",
"hashPath": "system.threading.4.3.0.nupkg.sha512"
},
+ "System.Threading.Overlapped/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-m3HQ2dPiX/DSTpf+yJt8B0c+SRvzfqAJKx+QDWi+VLhz8svLT23MVjEOHPF/KiSLeArKU/iHescrbLd3yVgyNg==",
+ "path": "system.threading.overlapped/4.3.0",
+ "hashPath": "system.threading.overlapped.4.3.0.nupkg.sha512"
+ },
"System.Threading.Tasks/4.3.0": {
"type": "package",
"serviceable": true,
@@ -4486,6 +3948,20 @@
"path": "system.threading.tasks.extensions/4.3.0",
"hashPath": "system.threading.tasks.extensions.4.3.0.nupkg.sha512"
},
+ "System.Threading.Thread/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-OHmbT+Zz065NKII/ZHcH9XO1dEuLGI1L2k7uYss+9C1jLxTC9kTZZuzUOyXHayRk+dft9CiDf3I/QZ0t8JKyBQ==",
+ "path": "system.threading.thread/4.3.0",
+ "hashPath": "system.threading.thread.4.3.0.nupkg.sha512"
+ },
+ "System.Threading.ThreadPool/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-k/+g4b7vjdd4aix83sTgC9VG6oXYKAktSfNIJUNGxPEj7ryEOfzHHhfnmsZvjxawwcD9HyWXKCXmPjX8U4zeSw==",
+ "path": "system.threading.threadpool/4.3.0",
+ "hashPath": "system.threading.threadpool.4.3.0.nupkg.sha512"
+ },
"System.Threading.Timer/4.3.0": {
"type": "package",
"serviceable": true,
@@ -4507,6 +3983,13 @@
"path": "system.xml.xdocument/4.3.0",
"hashPath": "system.xml.xdocument.4.3.0.nupkg.sha512"
},
+ "System.Xml.XmlDocument/4.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==",
+ "path": "system.xml.xmldocument/4.3.0",
+ "hashPath": "system.xml.xmldocument.4.3.0.nupkg.sha512"
+ },
"Telerik.UI.for.Wpf.NetCore.Xaml/2024.1.408": {
"type": "package",
"serviceable": true,
@@ -4569,6 +4052,11 @@
"serviceable": false,
"sha512": ""
},
+ "XP.ReportEngine/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
"BR.AN.PviServices/1.1.0.0": {
"type": "reference",
"serviceable": false,
diff --git a/ReleaseFiles/XplorePlane.dll b/ReleaseFiles/XplorePlane.dll
index 25a23cf..8f80c5a 100644
Binary files a/ReleaseFiles/XplorePlane.dll and b/ReleaseFiles/XplorePlane.dll differ
diff --git a/ReleaseFiles/XplorePlane.dll.config b/ReleaseFiles/XplorePlane.dll.config
index 7465d52..7123378 100644
--- a/ReleaseFiles/XplorePlane.dll.config
+++ b/ReleaseFiles/XplorePlane.dll.config
@@ -7,7 +7,10 @@
-
+
+
+
+
@@ -122,7 +125,6 @@
-
@@ -148,7 +150,7 @@
-
+
@@ -161,6 +163,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ReleaseFiles/XplorePlane.exe b/ReleaseFiles/XplorePlane.exe
index c6c35c7..c60cd80 100644
Binary files a/ReleaseFiles/XplorePlane.exe and b/ReleaseFiles/XplorePlane.exe differ
diff --git a/ReleaseFiles/XplorePlane.pdb b/ReleaseFiles/XplorePlane.pdb
index e5dbb6b..05fa658 100644
Binary files a/ReleaseFiles/XplorePlane.pdb and b/ReleaseFiles/XplorePlane.pdb differ
diff --git a/ReleaseFiles/XplorePlane.runtimeconfig.json b/ReleaseFiles/XplorePlane.runtimeconfig.json
index 1dc0145..b2dedf3 100644
--- a/ReleaseFiles/XplorePlane.runtimeconfig.json
+++ b/ReleaseFiles/XplorePlane.runtimeconfig.json
@@ -12,7 +12,8 @@
}
],
"configProperties": {
- "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": true
+ "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": true,
+ "CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": false
}
}
}
\ No newline at end of file
diff --git a/ReleaseFiles/Y.Tracing.Configuration.txt b/ReleaseFiles/Y.Tracing.Configuration.txt
new file mode 100644
index 0000000..6b53ce9
--- /dev/null
+++ b/ReleaseFiles/Y.Tracing.Configuration.txt
@@ -0,0 +1,4 @@
+// Flag, ob die Trace-Meldungen asynchron zu protokolliern sind: 1 fr asynchron, 0 fr synchron.
+1
+// Kategorie Konfigurationen:
+FXDriver 0
diff --git a/ReleaseFiles/en-US/XP.Camera.resources.dll b/ReleaseFiles/en-US/XP.Camera.resources.dll
index 6b54249..0467459 100644
Binary files a/ReleaseFiles/en-US/XP.Camera.resources.dll and b/ReleaseFiles/en-US/XP.Camera.resources.dll differ
diff --git a/ReleaseFiles/en-US/XP.Common.resources.dll b/ReleaseFiles/en-US/XP.Common.resources.dll
index d7a9c9d..5b2d299 100644
Binary files a/ReleaseFiles/en-US/XP.Common.resources.dll and b/ReleaseFiles/en-US/XP.Common.resources.dll differ
diff --git a/ReleaseFiles/en-US/XP.Hardware.Detector.resources.dll b/ReleaseFiles/en-US/XP.Hardware.Detector.resources.dll
index bf52a80..27b97b8 100644
Binary files a/ReleaseFiles/en-US/XP.Hardware.Detector.resources.dll and b/ReleaseFiles/en-US/XP.Hardware.Detector.resources.dll differ
diff --git a/ReleaseFiles/en-US/XP.Hardware.MotionControl.resources.dll b/ReleaseFiles/en-US/XP.Hardware.MotionControl.resources.dll
index 6cb548f..85e1fd4 100644
Binary files a/ReleaseFiles/en-US/XP.Hardware.MotionControl.resources.dll and b/ReleaseFiles/en-US/XP.Hardware.MotionControl.resources.dll differ
diff --git a/ReleaseFiles/en-US/XP.Hardware.PLC.resources.dll b/ReleaseFiles/en-US/XP.Hardware.PLC.resources.dll
index f84b5a5..facd652 100644
Binary files a/ReleaseFiles/en-US/XP.Hardware.PLC.resources.dll and b/ReleaseFiles/en-US/XP.Hardware.PLC.resources.dll differ
diff --git a/ReleaseFiles/en-US/XP.Hardware.RaySource.resources.dll b/ReleaseFiles/en-US/XP.Hardware.RaySource.resources.dll
index 8fb508c..78bd8c2 100644
Binary files a/ReleaseFiles/en-US/XP.Hardware.RaySource.resources.dll and b/ReleaseFiles/en-US/XP.Hardware.RaySource.resources.dll differ
diff --git a/ReleaseFiles/en-US/XP.ReportEngine.resources.dll b/ReleaseFiles/en-US/XP.ReportEngine.resources.dll
new file mode 100644
index 0000000..a525426
Binary files /dev/null and b/ReleaseFiles/en-US/XP.ReportEngine.resources.dll differ
diff --git a/ReleaseFiles/itext.barcodes.dll b/ReleaseFiles/itext.barcodes.dll
new file mode 100644
index 0000000..00eafd0
Binary files /dev/null and b/ReleaseFiles/itext.barcodes.dll differ
diff --git a/ReleaseFiles/itext.bouncy-castle-adapter.dll b/ReleaseFiles/itext.bouncy-castle-adapter.dll
new file mode 100644
index 0000000..c665389
Binary files /dev/null and b/ReleaseFiles/itext.bouncy-castle-adapter.dll differ
diff --git a/ReleaseFiles/itext.bouncy-castle-connector.dll b/ReleaseFiles/itext.bouncy-castle-connector.dll
new file mode 100644
index 0000000..cb4618a
Binary files /dev/null and b/ReleaseFiles/itext.bouncy-castle-connector.dll differ
diff --git a/ReleaseFiles/itext.commons.dll b/ReleaseFiles/itext.commons.dll
new file mode 100644
index 0000000..7e1ac5a
Binary files /dev/null and b/ReleaseFiles/itext.commons.dll differ
diff --git a/ReleaseFiles/itext.forms.dll b/ReleaseFiles/itext.forms.dll
new file mode 100644
index 0000000..6cca33a
Binary files /dev/null and b/ReleaseFiles/itext.forms.dll differ
diff --git a/ReleaseFiles/itext.io.dll b/ReleaseFiles/itext.io.dll
new file mode 100644
index 0000000..816b041
Binary files /dev/null and b/ReleaseFiles/itext.io.dll differ
diff --git a/ReleaseFiles/itext.kernel.dll b/ReleaseFiles/itext.kernel.dll
new file mode 100644
index 0000000..3cb7dc3
Binary files /dev/null and b/ReleaseFiles/itext.kernel.dll differ
diff --git a/ReleaseFiles/itext.layout.dll b/ReleaseFiles/itext.layout.dll
new file mode 100644
index 0000000..1e4b6c9
Binary files /dev/null and b/ReleaseFiles/itext.layout.dll differ
diff --git a/ReleaseFiles/itext.pdfa.dll b/ReleaseFiles/itext.pdfa.dll
new file mode 100644
index 0000000..80d49b2
Binary files /dev/null and b/ReleaseFiles/itext.pdfa.dll differ
diff --git a/ReleaseFiles/itext.pdfua.dll b/ReleaseFiles/itext.pdfua.dll
new file mode 100644
index 0000000..3fedc4f
Binary files /dev/null and b/ReleaseFiles/itext.pdfua.dll differ
diff --git a/ReleaseFiles/itext.sign.dll b/ReleaseFiles/itext.sign.dll
new file mode 100644
index 0000000..0c7e1cf
Binary files /dev/null and b/ReleaseFiles/itext.sign.dll differ
diff --git a/ReleaseFiles/itext.styledxmlparser.dll b/ReleaseFiles/itext.styledxmlparser.dll
new file mode 100644
index 0000000..7d2a96c
Binary files /dev/null and b/ReleaseFiles/itext.styledxmlparser.dll differ
diff --git a/ReleaseFiles/itext.svg.dll b/ReleaseFiles/itext.svg.dll
new file mode 100644
index 0000000..9ddd289
Binary files /dev/null and b/ReleaseFiles/itext.svg.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.Common.resources.dll b/ReleaseFiles/zh-CN/XP.Common.resources.dll
index 3a415f1..374d52e 100644
Binary files a/ReleaseFiles/zh-CN/XP.Common.resources.dll and b/ReleaseFiles/zh-CN/XP.Common.resources.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.Hardware.Detector.resources.dll b/ReleaseFiles/zh-CN/XP.Hardware.Detector.resources.dll
index 773b80e..d714c9c 100644
Binary files a/ReleaseFiles/zh-CN/XP.Hardware.Detector.resources.dll and b/ReleaseFiles/zh-CN/XP.Hardware.Detector.resources.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.Hardware.MotionControl.resources.dll b/ReleaseFiles/zh-CN/XP.Hardware.MotionControl.resources.dll
index 7219b8b..c18ff74 100644
Binary files a/ReleaseFiles/zh-CN/XP.Hardware.MotionControl.resources.dll and b/ReleaseFiles/zh-CN/XP.Hardware.MotionControl.resources.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.Hardware.PLC.resources.dll b/ReleaseFiles/zh-CN/XP.Hardware.PLC.resources.dll
index 9c19a09..7f90aa5 100644
Binary files a/ReleaseFiles/zh-CN/XP.Hardware.PLC.resources.dll and b/ReleaseFiles/zh-CN/XP.Hardware.PLC.resources.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.Hardware.RaySource.resources.dll b/ReleaseFiles/zh-CN/XP.Hardware.RaySource.resources.dll
index 50753d8..648d5a6 100644
Binary files a/ReleaseFiles/zh-CN/XP.Hardware.RaySource.resources.dll and b/ReleaseFiles/zh-CN/XP.Hardware.RaySource.resources.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.ImageProcessing.CfgControl.resources.dll b/ReleaseFiles/zh-CN/XP.ImageProcessing.CfgControl.resources.dll
index a2bf31a..b7786dd 100644
Binary files a/ReleaseFiles/zh-CN/XP.ImageProcessing.CfgControl.resources.dll and b/ReleaseFiles/zh-CN/XP.ImageProcessing.CfgControl.resources.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.ImageProcessing.Processors.resources.dll b/ReleaseFiles/zh-CN/XP.ImageProcessing.Processors.resources.dll
index 2321969..6441b5e 100644
Binary files a/ReleaseFiles/zh-CN/XP.ImageProcessing.Processors.resources.dll and b/ReleaseFiles/zh-CN/XP.ImageProcessing.Processors.resources.dll differ
diff --git a/ReleaseFiles/zh-CN/XP.ReportEngine.resources.dll b/ReleaseFiles/zh-CN/XP.ReportEngine.resources.dll
new file mode 100644
index 0000000..0bfffa6
Binary files /dev/null and b/ReleaseFiles/zh-CN/XP.ReportEngine.resources.dll differ
diff --git a/ReleaseFiles/zh-TW/XP.Common.resources.dll b/ReleaseFiles/zh-TW/XP.Common.resources.dll
index 3c36a0e..e984f03 100644
Binary files a/ReleaseFiles/zh-TW/XP.Common.resources.dll and b/ReleaseFiles/zh-TW/XP.Common.resources.dll differ
diff --git a/ReleaseFiles/zh-TW/XP.Hardware.Detector.resources.dll b/ReleaseFiles/zh-TW/XP.Hardware.Detector.resources.dll
index c914527..2e81501 100644
Binary files a/ReleaseFiles/zh-TW/XP.Hardware.Detector.resources.dll and b/ReleaseFiles/zh-TW/XP.Hardware.Detector.resources.dll differ
diff --git a/ReleaseFiles/zh-TW/XP.Hardware.MotionControl.resources.dll b/ReleaseFiles/zh-TW/XP.Hardware.MotionControl.resources.dll
index 993cae2..a74c4a4 100644
Binary files a/ReleaseFiles/zh-TW/XP.Hardware.MotionControl.resources.dll and b/ReleaseFiles/zh-TW/XP.Hardware.MotionControl.resources.dll differ
diff --git a/ReleaseFiles/zh-TW/XP.Hardware.PLC.resources.dll b/ReleaseFiles/zh-TW/XP.Hardware.PLC.resources.dll
index 73a659b..1bdde25 100644
Binary files a/ReleaseFiles/zh-TW/XP.Hardware.PLC.resources.dll and b/ReleaseFiles/zh-TW/XP.Hardware.PLC.resources.dll differ
diff --git a/ReleaseFiles/zh-TW/XP.Hardware.RaySource.resources.dll b/ReleaseFiles/zh-TW/XP.Hardware.RaySource.resources.dll
index ccf0a59..d280028 100644
Binary files a/ReleaseFiles/zh-TW/XP.Hardware.RaySource.resources.dll and b/ReleaseFiles/zh-TW/XP.Hardware.RaySource.resources.dll differ
diff --git a/ReleaseFiles/zh-TW/XP.ReportEngine.resources.dll b/ReleaseFiles/zh-TW/XP.ReportEngine.resources.dll
new file mode 100644
index 0000000..73fad13
Binary files /dev/null and b/ReleaseFiles/zh-TW/XP.ReportEngine.resources.dll differ
diff --git a/XP.ReportEngine/Configs/ConfigLoader.cs b/XP.ReportEngine/Configs/ConfigLoader.cs
new file mode 100644
index 0000000..d2516cd
--- /dev/null
+++ b/XP.ReportEngine/Configs/ConfigLoader.cs
@@ -0,0 +1,221 @@
+using System;
+using System.Configuration;
+using System.IO;
+using XP.Common.Logging.Interfaces;
+
+namespace XP.ReportEngine.Configs
+{
+ ///
+ /// 报告引擎配置加载器(读取 App.config)| Report engine configuration loader (reads from App.config)
+ ///
+ public class ConfigLoader
+ {
+ private readonly ILoggerService _logger;
+
+ ///
+ /// 构造函数 | Constructor
+ ///
+ /// 日志服务 | Logger service
+ public ConfigLoader(ILoggerService logger)
+ {
+ _logger = logger.ForModule();
+ }
+
+ ///
+ /// 从 App.config 加载报告引擎配置 | Load report engine configuration from App.config
+ ///
+ /// 配置前缀,默认为 "Report" | Configuration prefix, default is "Report"
+ /// 报告配置对象 | Report configuration object
+ public ReportConfig LoadReportConfig(string prefix = "Report")
+ {
+ try
+ {
+ _logger.Info("开始从 App.config 加载报告引擎配置,前缀: {Prefix} | Loading report config from App.config, prefix: {Prefix}", prefix);
+
+ var config = new ReportConfig();
+
+ // 读取输出目录 | Read output directory
+ var outputDirectory = ConfigurationManager.AppSettings[$"{prefix}:OutputDirectory"];
+ if (!string.IsNullOrEmpty(outputDirectory))
+ {
+ config.OutputDirectory = outputDirectory;
+ }
+
+ // 读取模板路径 | Read template path
+ var templatePath = ConfigurationManager.AppSettings[$"{prefix}:TemplatePath"];
+ if (!string.IsNullOrEmpty(templatePath))
+ {
+ config.TemplatePath = templatePath;
+ }
+
+ // 读取文件名模式 | Read file name pattern
+ var fileNamePattern = ConfigurationManager.AppSettings[$"{prefix}:FileNamePattern"];
+ if (!string.IsNullOrEmpty(fileNamePattern))
+ {
+ config.FileNamePattern = fileNamePattern;
+ }
+
+ // 读取重复文件名自动累加设置 | Read auto-increment on duplicate setting
+ var autoIncrement = ConfigurationManager.AppSettings[$"{prefix}:AutoIncrementOnDuplicate"];
+ if (bool.TryParse(autoIncrement, out var autoIncrementValue))
+ {
+ config.AutoIncrementOnDuplicate = autoIncrementValue;
+ }
+
+ // 读取自动打开设置 | Read auto-open setting
+ var autoOpen = ConfigurationManager.AppSettings[$"{prefix}:AutoOpenAfterGenerate"];
+ if (bool.TryParse(autoOpen, out var autoOpenValue))
+ {
+ config.AutoOpenAfterGenerate = autoOpenValue;
+ }
+
+ // 读取页面尺寸 | Read page size
+ var pageSize = ConfigurationManager.AppSettings[$"{prefix}:DefaultPageSize"];
+ if (!string.IsNullOrEmpty(pageSize))
+ {
+ config.DefaultPageSize = pageSize;
+ }
+
+ // 读取页面方向 | Read page orientation
+ var orientation = ConfigurationManager.AppSettings[$"{prefix}:DefaultOrientation"];
+ if (!string.IsNullOrEmpty(orientation))
+ {
+ config.DefaultOrientation = orientation;
+ }
+
+ // 读取边距配置 | Read margin configuration
+ var marginTop = ConfigurationManager.AppSettings[$"{prefix}:MarginTop"];
+ if (float.TryParse(marginTop, out var marginTopValue))
+ {
+ config.MarginTop = marginTopValue;
+ }
+
+ var marginBottom = ConfigurationManager.AppSettings[$"{prefix}:MarginBottom"];
+ if (float.TryParse(marginBottom, out var marginBottomValue))
+ {
+ config.MarginBottom = marginBottomValue;
+ }
+
+ var marginLeft = ConfigurationManager.AppSettings[$"{prefix}:MarginLeft"];
+ if (float.TryParse(marginLeft, out var marginLeftValue))
+ {
+ config.MarginLeft = marginLeftValue;
+ }
+
+ var marginRight = ConfigurationManager.AppSettings[$"{prefix}:MarginRight"];
+ if (float.TryParse(marginRight, out var marginRightValue))
+ {
+ config.MarginRight = marginRightValue;
+ }
+
+ // 读取公司名称 | Read company name
+ var companyName = ConfigurationManager.AppSettings[$"{prefix}:CompanyName"];
+ if (!string.IsNullOrEmpty(companyName))
+ {
+ config.CompanyName = companyName;
+ }
+
+ // 读取公司 Logo 路径 | Read company logo path
+ var companyLogo = ConfigurationManager.AppSettings[$"{prefix}:CompanyLogo"];
+ if (!string.IsNullOrEmpty(companyLogo))
+ {
+ config.CompanyLogo = companyLogo;
+ }
+
+ // 读取软件名称 | Read software name
+ var softwareName = ConfigurationManager.AppSettings[$"{prefix}:SoftwareName"];
+ if (!string.IsNullOrEmpty(softwareName))
+ {
+ config.SoftwareName = softwareName;
+ }
+
+ // 读取软件 Logo 路径 | Read software logo path
+ var softwareLogo = ConfigurationManager.AppSettings[$"{prefix}:SoftwareLogo"];
+ if (!string.IsNullOrEmpty(softwareLogo))
+ {
+ config.SoftwareLogo = softwareLogo;
+ }
+
+ // 验证配置 | Validate configuration
+ ValidateConfig(config);
+
+ _logger.Info("报告引擎配置加载成功:输出目录={OutputDir}, 模板={Template} | Report config loaded: OutputDir={OutputDir}, Template={Template}",
+ config.OutputDirectory, config.TemplatePath);
+
+ return config;
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("加载报告引擎配置失败,使用默认配置 | Failed to load report config, using defaults: {Message}", ex.Message);
+ return new ReportConfig();
+ }
+ }
+
+ ///
+ /// 验证配置参数 | Validate configuration parameters
+ ///
+ /// 报告配置对象 | Report configuration object
+ private void ValidateConfig(ReportConfig config)
+ {
+ // 验证输出目录(不存在则尝试创建)| Validate output directory (create if not exists)
+ if (string.IsNullOrWhiteSpace(config.OutputDirectory))
+ {
+ _logger.Warn("OutputDirectory 为空,使用默认值 | OutputDirectory is empty, using default");
+ config.OutputDirectory = new ReportConfig().OutputDirectory;
+ }
+
+ // 验证模板路径 | Validate template path
+ if (string.IsNullOrWhiteSpace(config.TemplatePath))
+ {
+ _logger.Warn("TemplatePath 为空,使用默认值 | TemplatePath is empty, using default");
+ config.TemplatePath = @"Templates\StandardReportTemplate.json";
+ }
+
+ // 验证文件名模式 | Validate file name pattern
+ if (string.IsNullOrWhiteSpace(config.FileNamePattern))
+ {
+ _logger.Warn("FileNamePattern 为空,使用默认值 | FileNamePattern is empty, using default");
+ config.FileNamePattern = "{ReportId}";
+ }
+
+ // 验证页面方向 | Validate page orientation
+ if (config.DefaultOrientation != "Portrait" && config.DefaultOrientation != "Landscape")
+ {
+ _logger.Warn("DefaultOrientation 无效: {Value},使用默认值 Portrait | DefaultOrientation invalid: {Value}, using default Portrait", config.DefaultOrientation);
+ config.DefaultOrientation = "Portrait";
+ }
+
+ // 验证边距范围(0-100mm)| Validate margin range (0-100mm)
+ config.MarginTop = ClampMargin(config.MarginTop, "MarginTop");
+ config.MarginBottom = ClampMargin(config.MarginBottom, "MarginBottom");
+ config.MarginLeft = ClampMargin(config.MarginLeft, "MarginLeft");
+ config.MarginRight = ClampMargin(config.MarginRight, "MarginRight");
+
+ // 验证 Logo 路径(如果配置了则检查文件是否存在)| Validate logo path (check file exists if configured)
+ if (!string.IsNullOrEmpty(config.CompanyLogo))
+ {
+ var logoPath = Path.IsPathRooted(config.CompanyLogo)
+ ? config.CompanyLogo
+ : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.CompanyLogo);
+
+ if (!File.Exists(logoPath))
+ {
+ _logger.Warn("公司 Logo 文件不存在: {Path},将不显示 Logo | Company logo file not found: {Path}, logo will not be displayed", logoPath);
+ }
+ }
+ }
+
+ ///
+ /// 将边距值限制在有效范围内 | Clamp margin value to valid range
+ ///
+ private float ClampMargin(float value, string name)
+ {
+ if (value < 0 || value > 100)
+ {
+ _logger.Warn("{Name} 超出有效范围 [0, 100]: {Value},使用默认值 20 | {Name} out of valid range [0, 100]: {Value}, using default 20", name, value);
+ return 20f;
+ }
+ return value;
+ }
+ }
+}
diff --git a/XP.ReportEngine/Configs/ReportConfig.cs b/XP.ReportEngine/Configs/ReportConfig.cs
new file mode 100644
index 0000000..24e5365
--- /dev/null
+++ b/XP.ReportEngine/Configs/ReportConfig.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace XP.ReportEngine.Configs
+{
+ ///
+ /// 报告引擎配置模型 | Report engine configuration model
+ ///
+ public class ReportConfig
+ {
+ ///
+ /// 报告输出文件夹路径 | Report output directory path
+ ///
+ public string OutputDirectory { get; set; } = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
+ "XplorePlane", "Reports");
+
+ ///
+ /// 报告模板文件路径(相对或绝对)| Report template file path (relative or absolute)
+ ///
+ public string TemplatePath { get; set; } = @"Templates\StandardReportTemplate.json";
+
+ ///
+ /// 输出文件名模式,支持占位符 | Output file name pattern, supports placeholders
+ /// 支持的占位符 | Supported placeholders:
+ /// {ReportId} - 报告编号(如 RPT-20250512-001)
+ /// {CncProgram} - CNC 程序名称
+ /// {ProductName} - 产品名称
+ /// {ProductCode} - 产品类型码
+ /// {WorkpieceSN} - 工件 SN 码
+ /// {DeviceId} - 检测设备编号(本机)
+ /// {MachineId} - 生产机台号
+ /// {Date} - 日期(yyyyMMdd)
+ /// {Time} - 时间(HHmmss)
+ /// {Result} - 综合检测结论(Pass/Fail)
+ ///
+ public string FileNamePattern { get; set; } = "{ReportId}";
+
+ ///
+ /// 文件名重复时是否自动累加序号 | Whether to auto-increment suffix when file name duplicates
+ /// true: 重复时生成 filename(1).pdf, filename(2).pdf ...
+ /// false: 直接覆盖同名文件
+ ///
+ public bool AutoIncrementOnDuplicate { get; set; } = true;
+
+ ///
+ /// 生成后是否自动打开 PDF 阅读器 | Whether to auto-open PDF viewer after generation
+ ///
+ public bool AutoOpenAfterGenerate { get; set; } = false;
+
+ ///
+ /// 默认页面尺寸 | Default page size
+ ///
+ public string DefaultPageSize { get; set; } = "A4";
+
+ ///
+ /// 默认页面方向(Portrait / Landscape)| Default page orientation
+ ///
+ public string DefaultOrientation { get; set; } = "Portrait";
+
+ ///
+ /// 默认上边距(mm)| Default top margin (mm)
+ ///
+ public float MarginTop { get; set; } = 20f;
+
+ ///
+ /// 默认下边距(mm)| Default bottom margin (mm)
+ ///
+ public float MarginBottom { get; set; } = 20f;
+
+ ///
+ /// 默认左边距(mm)| Default left margin (mm)
+ ///
+ public float MarginLeft { get; set; } = 20f;
+
+ ///
+ /// 默认右边距(mm)| Default right margin (mm)
+ ///
+ public float MarginRight { get; set; } = 20f;
+
+ ///
+ /// 报告中显示的公司名称 | Company name displayed in report
+ ///
+ public string CompanyName { get; set; } = "海克斯康制造智能技术(青岛)有限公司";
+
+ ///
+ /// 公司 Logo 图片路径(可选,为空则不显示)| Company logo image path (optional, empty means no logo)
+ ///
+ public string CompanyLogo { get; set; } = string.Empty;
+
+ ///
+ /// 报告中显示的软件名称 | Software name displayed in report
+ ///
+ public string SoftwareName { get; set; } = "XplorePlane";
+
+ ///
+ /// 软件 Logo 图片路径(可选,为空则不显示)| Software logo image path (optional, empty means no logo)
+ ///
+ public string SoftwareLogo { get; set; } = string.Empty;
+
+ ///
+ /// 获取解析后的模板绝对路径 | Get resolved absolute template path
+ /// 如果 TemplatePath 是相对路径,则基于应用程序目录解析
+ /// If TemplatePath is relative, resolves based on application directory
+ ///
+ public string GetResolvedTemplatePath()
+ {
+ if (Path.IsPathRooted(TemplatePath))
+ {
+ return TemplatePath;
+ }
+ return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TemplatePath);
+ }
+
+ ///
+ /// 根据文件名模式和上下文参数生成实际文件名 | Generate actual file name based on pattern and context parameters
+ ///
+ /// 占位符参数字典 | Placeholder parameter dictionary
+ /// 生成的文件名(不含扩展名)| Generated file name (without extension)
+ public string ResolveFileName(Dictionary parameters)
+ {
+ var fileName = FileNamePattern;
+
+ // 替换所有已知占位符 | Replace all known placeholders
+ if (parameters != null)
+ {
+ foreach (var kvp in parameters)
+ {
+ fileName = fileName.Replace($"{{{kvp.Key}}}", SanitizeFileName(kvp.Value ?? ""));
+ }
+ }
+
+ // 替换日期和时间(始终可用)| Replace date and time (always available)
+ fileName = fileName.Replace("{Date}", DateTime.Now.ToString("yyyyMMdd"));
+ fileName = fileName.Replace("{Time}", DateTime.Now.ToString("HHmmss"));
+
+ // 清理未被替换的占位符(替换为空)| Clean up unreplaced placeholders
+ fileName = System.Text.RegularExpressions.Regex.Replace(fileName, @"\{[^}]+\}", "");
+
+ // 移除连续的分隔符 | Remove consecutive separators
+ fileName = System.Text.RegularExpressions.Regex.Replace(fileName, @"[_\-]{2,}", "_");
+ fileName = fileName.Trim('_', '-');
+
+ return string.IsNullOrWhiteSpace(fileName) ? "Report" : fileName;
+ }
+
+ ///
+ /// 解析最终输出文件完整路径(含重复累加逻辑)| Resolve final output file full path (with duplicate increment logic)
+ ///
+ /// 占位符参数字典 | Placeholder parameter dictionary
+ /// 文件扩展名(含点号,如 ".pdf")| File extension (with dot, e.g. ".pdf")
+ /// 最终输出文件完整路径 | Final output file full path
+ public string ResolveOutputFilePath(Dictionary parameters, string extension = ".pdf")
+ {
+ var baseName = ResolveFileName(parameters);
+ var outputDir = OutputDirectory;
+
+ // 确保输出目录存在 | Ensure output directory exists
+ if (!Directory.Exists(outputDir))
+ {
+ Directory.CreateDirectory(outputDir);
+ }
+
+ var filePath = Path.Combine(outputDir, baseName + extension);
+
+ // 重复累加逻辑 | Duplicate increment logic
+ if (AutoIncrementOnDuplicate && File.Exists(filePath))
+ {
+ int counter = 1;
+ string newPath;
+ do
+ {
+ newPath = Path.Combine(outputDir, $"{baseName}({counter}){extension}");
+ counter++;
+ } while (File.Exists(newPath));
+
+ filePath = newPath;
+ }
+
+ return filePath;
+ }
+
+ ///
+ /// 清理文件名中的非法字符 | Sanitize illegal characters in file name
+ ///
+ private static string SanitizeFileName(string name)
+ {
+ var invalidChars = Path.GetInvalidFileNameChars();
+ foreach (var c in invalidChars)
+ {
+ name = name.Replace(c, '_');
+ }
+ return name;
+ }
+ }
+}
diff --git a/XP.ReportEngine/Documents/App.config.example b/XP.ReportEngine/Documents/App.config.example
new file mode 100644
index 0000000..b48ec6e
--- /dev/null
+++ b/XP.ReportEngine/Documents/App.config.example
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XP.ReportEngine/Documents/FontFilesGuidance.md b/XP.ReportEngine/Documents/FontFilesGuidance.md
new file mode 100644
index 0000000..63d4ff5
--- /dev/null
+++ b/XP.ReportEngine/Documents/FontFilesGuidance.md
@@ -0,0 +1,73 @@
+# 字体方案说明 | Font Strategy
+
+## 当前方案 | Current Approach
+
+XP.ReportEngine 使用 **Windows 系统自带字体** 生成 PDF,无需额外下载或嵌入字体文件。
+
+| 语言 | 字体 | 文件 | 说明 |
+|------|------|------|------|
+| 简体中文 / 繁体中文 | 微软雅黑 | `C:\Windows\Fonts\msyh.ttc` | Windows 10+ 自带,支持简繁体 |
+| 西文(英文等) | Arial | `C:\Windows\Fonts\arial.ttf` | Windows 自带 |
+
+## 为什么使用系统字体 | Why System Fonts
+
+1. **Telerik RadPdfViewer 兼容性** — 系统字体生成的 PDF 在 Telerik RadPdfViewer 中显示正常,不会出现中文乱码
+2. **无需额外部署** — 不需要随应用程序分发字体文件,减小安装包体积
+3. **跨阅读器兼容** — 字体以子集嵌入 PDF 中(`PREFER_EMBEDDED`),在任何 PDF 阅读器中都能正确显示
+
+## 后备机制 | Fallback Mechanism
+
+`ITextPdfRenderer` 按以下顺序加载字体:
+
+```
+微软雅黑 (msyh.ttc)
+ ↓ 加载失败
+宋体 (simsun.ttc)
+ ↓ 加载失败
+_cjkFont = null
+
+Arial (arial.ttf)
+ ↓ 加载失败
+_westernFont = null
+
+最终后备: iText 内置 Helvetica(不支持中文字符)
+```
+
+语言选择逻辑:
+- zh-CN / zh-TW → 优先使用微软雅黑
+- en-US → 优先使用 Arial,后备微软雅黑(微软雅黑也支持西文)
+
+## 字体子集化 | Font Subsetting
+
+iText7 在 `document.Close()` 时执行字体子集化:
+- 分析文档中实际使用的字符
+- 从完整字体文件(微软雅黑约 15MB)中提取用到的字形子集
+- 仅嵌入子集到 PDF 中
+
+这使得最终 PDF 文件大小合理(通常 200KB-2MB),但 `Close()` 操作需要约 1-1.5 秒。
+
+## 性能说明 | Performance Notes
+
+| 阶段 | 首次耗时 | 后续耗时 | 说明 |
+|------|---------|---------|------|
+| 字体加载 | ~5-6s | 0ms | 首次需从磁盘读取 TTC 文件并解析 |
+| 字体子集化 | ~1.2s | ~1.2s | 每次生成都需要,与字符数量相关 |
+
+模块通过 `WarmUpAsync()` 预热机制在应用启动时后台完成首次字体加载,用户首次生成报告时不会感受到延迟。
+
+## 系统要求 | System Requirements
+
+- Windows 10 或更高版本(微软雅黑为系统预装字体)
+- 如果在精简版 Windows 上运行,需确保系统已安装微软雅黑字体
+
+## 自定义字体扩展 | Custom Font Extension
+
+如需添加自定义字体:
+
+1. 将字体文件(.ttf / .ttc / .otf)放入项目 `Fonts/` 目录
+2. 在 `.csproj` 中取消注释嵌入资源配置:
+ ```xml
+
+
+ ```
+3. 在 `ITextPdfRenderer.InitializeFonts()` 中扩展加载逻辑
diff --git a/XP.ReportEngine/Documents/Guidance.md b/XP.ReportEngine/Documents/Guidance.md
new file mode 100644
index 0000000..6eea6f2
--- /dev/null
+++ b/XP.ReportEngine/Documents/Guidance.md
@@ -0,0 +1,269 @@
+# XP.ReportEngine 使用指南 | Usage Guidance
+
+## 1. 概述
+
+本文档说明如何在 XplorePlane 项目中使用 `XP.ReportEngine` 模块生成 PDF 检测报告。
+
+模块提供两种调用方式:
+- **推荐**:通过 `IReportService` 门面接口(一行调用,自动处理所有细节)
+- **高级**:直接使用底层管线接口(`IReportGenerator`、`IReportDataAdapter` 等)
+
+## 2. 前置条件
+
+### 2.1 项目引用
+
+在调用方项目的 `.csproj` 中添加项目引用:
+
+```xml
+
+```
+
+### 2.2 模块注册
+
+确保 `ReportEngineModule` 已在 `XP.App` 的模块目录中注册。模块初始化时会自动:
+- 注册所有服务到 DI 容器
+- 注册多语言资源到 Fallback Chain
+- 后台执行引擎预热(字体加载 + JIT 编译)
+
+### 2.3 配置文件
+
+在 `App.config` 中添加报告引擎配置项,参见 `Documents/App.config.example`。
+
+## 3. 通过 IReportService 生成报告(推荐)
+
+### 3.1 基本用法
+
+```csharp
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+public class InspectionService
+{
+ private readonly IReportService _reportService;
+
+ public InspectionService(IReportService reportService)
+ {
+ _reportService = reportService;
+ }
+
+ public async Task GenerateInspectionReport(
+ List processorOutputs,
+ string productName,
+ string operatorName)
+ {
+ var request = new ReportRequest
+ {
+ // 必填:处理器输出数据
+ ProcessorOutputs = processorOutputs,
+
+ // 必填:报告元数据
+ Metadata = new ReportMetadata
+ {
+ SampleName = productName,
+ OperatorName = operatorName,
+ InspectionDate = DateTime.Now,
+ Description = "BGA 焊球气泡率检测"
+ },
+
+ // 可选:文件名占位符参数(用于 FileNamePattern)
+ FileNameParameters = new Dictionary
+ {
+ ["ProductName"] = productName,
+ ["ProductCode"] = "PCBA-X100",
+ ["WorkpieceSN"] = "SN20250001",
+ ["DeviceId"] = "XP-CT-001",
+ ["MachineId"] = "MC01",
+ ["Result"] = "Pass"
+ }
+ };
+
+ var result = await _reportService.GenerateAsync(request);
+
+ if (result.IsSuccess)
+ {
+ return result.OutputFilePath; // PDF 文件路径
+ }
+ else
+ {
+ throw new Exception($"报告生成失败: {result.ErrorMessage}");
+ }
+ }
+}
+```
+
+### 3.2 指定输出路径
+
+```csharp
+var request = new ReportRequest
+{
+ ProcessorOutputs = outputs,
+ Metadata = metadata,
+ // 指定输出路径后,不再使用 ReportConfig 中的 OutputDirectory 和 FileNamePattern
+ OutputFilePath = @"D:\CustomPath\MyReport.pdf"
+};
+```
+
+### 3.3 注入额外图像
+
+```csharp
+var request = new ReportRequest
+{
+ ProcessorOutputs = outputs,
+ Metadata = metadata,
+ AdditionalImages = new Dictionary
+ {
+ // 工件整体图(首页显示)
+ ["workpieceImage"] = new ImageData
+ {
+ SourceType = ImageSourceType.FilePath,
+ FilePath = @"D:\Images\workpiece.png"
+ },
+ // 自定义图像
+ ["customImage"] = new ImageData
+ {
+ SourceType = ImageSourceType.Bytes,
+ Bytes = imageBytes
+ }
+ }
+};
+```
+
+### 3.4 注入自定义属性
+
+```csharp
+var request = new ReportRequest
+{
+ ProcessorOutputs = outputs,
+ Metadata = metadata,
+ // 自定义属性会合并到 ReportContext.Properties,可在模板中通过 ${key} 绑定
+ CustomProperties = new Dictionary
+ {
+ ["batchNumber"] = "BATCH-2025-001",
+ ["inspectionStation"] = "Station-A"
+ }
+};
+```
+
+## 4. ReportRequest 完整字段说明
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| `ProcessorOutputs` | `List` | 是 | 处理器输出数据列表 |
+| `Metadata` | `ReportMetadata` | 是 | 报告元数据 |
+| `OutputFilePath` | `string` | 否 | 输出路径,为空时自动生成 |
+| `FileNameParameters` | `Dictionary` | 否 | 文件名占位符参数 |
+| `AdditionalImages` | `Dictionary` | 否 | 额外图像数据 |
+| `CustomProperties` | `Dictionary` | 否 | 自定义属性 |
+
+### ReportMetadata 字段
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `ReportId` | `string` | 报告编号(为空时自动生成 RPT-yyyyMMdd-NNN) |
+| `InspectionDate` | `DateTime` | 检测日期 |
+| `SampleName` | `string` | 样品/产品名称 |
+| `OperatorName` | `string` | 操作员 |
+| `Description` | `string` | 描述信息 |
+
+### ProcessorOutput 字段
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `ProcessorType` | `string` | 处理器类型标识 |
+| `OutputData` | `Dictionary` | 输出数据字典 |
+| `AnnotatedImage` | `ImageData` | 关联的已标注图像 |
+
+支持的 ProcessorType:
+- `LineMeasurementProcessor` — 线测量
+- `BgaVoidRateProcessor` — BGA 气泡率
+- `VoidMeasurementProcessor` — 空隙测量
+- `FillRateProcessor` — 通孔填锡率
+
+## 5. ReportServiceResult 结果说明
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `IsSuccess` | `bool` | 是否成功 |
+| `OutputFilePath` | `string` | 输出文件路径(成功时有值) |
+| `ReportId` | `string` | 报告编号 |
+| `ErrorMessage` | `string` | 错误信息(失败时有值) |
+| `Exception` | `Exception` | 异常对象(失败时有值) |
+
+## 6. 在 ViewModel 中使用(带 UI 交互)
+
+如果需要进度条、文件对话框等 UI 交互,参考 `ReportDemoViewModel.cs` 的实现模式:
+
+```csharp
+// 在后台线程执行,避免阻塞 UI
+var genResult = await Task.Run(() => _reportService.GenerateAsync(request));
+```
+
+关键点:
+- `IReportService.GenerateAsync` 内部是 CPU 密集型操作(PDF 渲染)
+- 必须用 `Task.Run` 推到线程池,否则会阻塞 UI 线程
+- 进度条、对话框等 UI 逻辑留在 ViewModel 中
+
+## 7. 高级用法:直接使用底层管线
+
+适用于需要自定义管线某个阶段的场景:
+
+```csharp
+// 1. 数据适配
+var adapter = container.Resolve();
+var context = adapter.Adapt(processorOutputs, metadata);
+
+// 2. 手动修改上下文
+context.Properties["customKey"] = "customValue";
+context.Images["myImage"] = new ImageData { ... };
+
+// 3. 调用管线生成
+var generator = container.Resolve();
+var options = new ReportGenerationOptions
+{
+ TemplatePath = "Templates/StandardReportTemplate.json",
+ OutputFilePath = @"D:\output.pdf",
+ Format = ReportOutputFormat.Pdf
+};
+
+var result = await generator.GenerateAsync(context, options);
+```
+
+## 8. 预热机制
+
+模块启动时自动在后台执行预热,无需手动调用。如果需要在特定时机手动预热:
+
+```csharp
+var reportService = container.Resolve();
+await reportService.WarmUpAsync();
+```
+
+预热内容:
+- iText7 程序集加载
+- BouncyCastle 加密提供程序注册
+- 微软雅黑字体文件读取和解析(约 5-6 秒)
+- JSON 模板反序列化 JIT 编译
+- 管线各阶段代码 JIT 编译
+
+预热完成后,首次正式生成报告不会有额外延迟。
+
+## 9. 线程安全
+
+`IReportService` 内部使用 `SemaphoreSlim(1, 1)` 互斥锁,确保:
+- 预热与正式生成不会并发执行
+- 多个并发的 `GenerateAsync` 调用会串行执行
+
+这是因为 `ITextPdfRenderer` 的字体字段在每次渲染时重置,并发渲染会导致 iText7 的 `"belongs to other PDF document"` 错误。
+
+## 10. 常见问题
+
+### Q: 首次生成报告很慢?
+A: 正常现象。首次需要加载字体(~5s)+ 字体子集化(~1.2s)。预热机制会在应用启动时后台完成字体加载,用户首次操作时只需等待子集化时间。
+
+### Q: PDF 中图像显示为"无图像"占位矩形?
+A: 检查 `ProcessorOutput.AnnotatedImage` 或 `ReportRequest.AdditionalImages` 中的图像路径是否正确、文件是否存在。
+
+### Q: 报告中某些字段为空?
+A: 检查 `ProcessorOutput.OutputData` 中的键名是否与 `ProcessorDataAdapter` 中的映射一致(区分大小写)。
+
+### Q: 如何自定义报告模板?
+A: 修改 `Templates/StandardReportTemplate.json`,参考 `Documents/XP.ReportEngineModelDefine.md` 中的模板结构定义。
diff --git a/XP.ReportEngine/Documents/README.md b/XP.ReportEngine/Documents/README.md
new file mode 100644
index 0000000..a53dc39
--- /dev/null
+++ b/XP.ReportEngine/Documents/README.md
@@ -0,0 +1,217 @@
+# XP.ReportEngine — PDF 报告生成引擎
+
+## 概述
+
+XP.ReportEngine 是 XplorePlane 平面 CT 检测系统的 PDF 报告生成模块。负责将检测分析结果(距离测量、BGA 气泡率、空隙测量、通孔填锡率)转换为结构化的 PDF 检测报告。
+
+模块采用管线式架构(Pipeline),将报告生成过程分解为五个独立阶段:
+**模板加载 → 数据适配 → 数据绑定 → 排版计算 → PDF 渲染**
+
+对外提供 `IReportService` 门面接口,外部模块只需构建 `ReportRequest` 即可一行调用完成报告生成。
+
+## 技术栈
+
+| 依赖 | 版本 | 用途 |
+|------|------|------|
+| .NET 8.0 | net8.0-windows7.0 | 运行时 |
+| iText7 | 8.0.5 | PDF 文档生成核心库 |
+| itext7.bouncy-castle-adapter | 8.0.5 | iText7 加密支持 |
+| Newtonsoft.Json | 13.0.3 | JSON 模板反序列化 |
+| Prism.Wpf | 9.0.537 | 模块化框架与 DI |
+| Telerik UI for WPF | 2024.1.408 | UI 控件(演示窗口) |
+| XP.Common | — | 日志、本地化、通用窗体、PDF 阅读器 |
+
+## 项目结构
+
+```
+XP.ReportEngine/
+├── Configs/ # 配置加载
+│ ├── ConfigLoader.cs
+│ └── ReportConfig.cs
+├── Interfaces/ # 核心接口定义
+│ ├── IReportService.cs ★ 门面接口(外部调用入口)
+│ ├── IReportGenerator.cs
+│ ├── IReportGeneratorFactory.cs
+│ ├── ITemplateEngine.cs
+│ ├── IDataBinder.cs
+│ ├── ILayoutEngine.cs
+│ ├── IPdfRenderer.cs
+│ └── IReportDataAdapter.cs
+├── Models/ # 数据模型
+│ ├── ReportRequest.cs ★ 报告生成请求
+│ ├── ReportServiceResult.cs ★ 报告服务结果
+│ ├── ReportContext.cs
+│ ├── ReportMetadata.cs
+│ ├── ImageData.cs
+│ ├── ReportTemplate.cs
+│ ├── TemplateElement.cs
+│ ├── LayoutPage.cs
+│ ├── ReportResult.cs
+│ ├── ReportGenerationOptions.cs
+│ ├── ProcessorOutput.cs
+│ └── TemplateValidationResult.cs
+├── Services/ # 服务实现
+│ ├── ReportService.cs ★ 门面服务实现
+│ ├── PdfReportGenerator.cs # 管线协调器
+│ ├── ReportGeneratorFactory.cs # 工厂
+│ ├── JsonTemplateEngine.cs # JSON 模板加载与验证
+│ ├── ExpressionDataBinder.cs # ${} 表达式数据绑定
+│ ├── PageLayoutEngine.cs # 分页与排版
+│ ├── ITextPdfRenderer.cs # iText7 PDF 渲染
+│ ├── ProcessorDataAdapter.cs # 处理器数据适配
+│ └── ReportIdGenerator.cs # 报告编号生成(RPT-yyyyMMdd-NNN)
+├── Templates/ # JSON 报告模板
+│ └── StandardReportTemplate.json
+├── Resources/ # 多语言资源文件
+│ ├── Resources.resx
+│ ├── Resources.zh-CN.resx
+│ ├── Resources.zh-TW.resx
+│ └── Resources.en-US.resx
+├── ViewModels/ # 演示窗口 ViewModel
+│ └── ReportDemoViewModel.cs
+├── Views/ # 演示窗口 View
+│ ├── ReportDemoWindow.xaml
+│ └── ReportDemoWindow.xaml.cs
+├── Documents/ # 项目文档
+│ ├── README.md ← 本文件
+│ ├── Guidance.md # 使用指南
+│ ├── App.config.example # 配置示例
+│ ├── FontFilesGuidance.md # 字体方案说明
+│ ├── XP.ReportEngineDesign.md # 架构设计文档
+│ └── XP.ReportEngineModelDefine.md # 模型定义文档
+├── ReportEngineModule.cs # Prism 模块入口
+└── XP.ReportEngine.csproj
+```
+
+## 核心接口
+
+| 接口 | 实现类 | 职责 |
+|------|--------|------|
+| **`IReportService`** | **`ReportService`** | **门面接口,外部模块调用入口** |
+| `IReportGenerator` | `PdfReportGenerator` | 协调管线各阶段,生成 PDF |
+| `IReportGeneratorFactory` | `ReportGeneratorFactory` | 根据格式创建对应生成器 |
+| `ITemplateEngine` | `JsonTemplateEngine` | JSON 模板加载、反序列化、验证 |
+| `IDataBinder` | `ExpressionDataBinder` | `${}` 表达式解析与数据绑定 |
+| `ILayoutEngine` | `PageLayoutEngine` | 分页、元素定位、表格跨页 |
+| `IPdfRenderer` | `ITextPdfRenderer` | 使用 iText7 渲染 PDF |
+| `IReportDataAdapter` | `ProcessorDataAdapter` | ProcessorOutput → ReportContext 转换 |
+
+## 架构分层
+
+```
+┌─────────────────────────────────────────────────────┐
+│ 外部调用方(XP.App、其他模块) │
+│ 注入 IReportService,传入 ReportRequest │
+└──────────────────────┬──────────────────────────────┘
+ │
+┌──────────────────────▼──────────────────────────────┐
+│ ReportService(门面层) │
+│ 生成报告ID → 解析输出路径 → 数据适配 → 注入配置数据 │
+└──────────────────────┬──────────────────────────────┘
+ │
+┌──────────────────────▼──────────────────────────────┐
+│ PdfReportGenerator(管线协调层) │
+│ 模板加载 → 数据绑定 → 排版计算 → PDF 渲染 → 保存 │
+└─────────────────────────────────────────────────────┘
+```
+
+## 快速开始
+
+详细使用指南请参阅 `Documents/Guidance.md`。
+
+### 最简调用(通过 IReportService)
+
+```csharp
+public class MyService
+{
+ private readonly IReportService _reportService;
+
+ public MyService(IReportService reportService)
+ {
+ _reportService = reportService;
+ }
+
+ public async Task GenerateReport(List outputs)
+ {
+ var request = new ReportRequest
+ {
+ ProcessorOutputs = outputs,
+ Metadata = new ReportMetadata
+ {
+ SampleName = "PCB-001",
+ OperatorName = "张三",
+ InspectionDate = DateTime.Now
+ }
+ };
+
+ var result = await _reportService.GenerateAsync(request);
+
+ if (result.IsSuccess)
+ {
+ // result.OutputFilePath — 生成的 PDF 路径
+ // result.ReportId — 报告编号
+ }
+ }
+}
+```
+
+## 预热机制
+
+模块在 Prism 初始化时自动在后台执行一次预热(`WarmUpAsync`),触发 iText7 初始化、字体加载、JIT 编译等一次性开销,避免用户首次生成报告时卡顿。
+
+预热与正式生成通过 `SemaphoreSlim` 互斥锁串行执行,不会出现并发冲突。
+
+## 数据绑定表达式
+
+模板中支持以下绑定语法:
+
+| 语法 | 说明 | 示例 |
+|------|------|------|
+| `${propertyName}` | 简单属性绑定 | `${sampleName}` |
+| `${object.property}` | 嵌套属性路径 | `${metadata.reportId}` |
+| `${list[index]}` | 列表索引访问 | `${bgaBalls[0].voidRate}` |
+| `${functionName(param)}` | 格式化函数 | `${formatDate(inspectionDate)}` |
+| `${loc:ResourceKey}` | 本地化键解析 | `${loc:Report_Title}` |
+
+## 多语言支持
+
+支持三种语言:简体中文(zh-CN)、繁体中文(zh-TW)、英文(en-US)。
+
+通过 `ILocalizationService` 自动解析 `${loc:Key}` 表达式为当前语言文本。模块在初始化时注册资源源到 Fallback Chain。
+
+## 模板页面类型
+
+标准模板 `StandardReportTemplate.json` 包含以下页面类型:
+
+| 页面类型 | 说明 |
+|---------|------|
+| `homepage` | 报告首页(公司信息 + 元数据 + 汇总表格) |
+| `summary` | 检测结果汇总页 |
+| `metricData` | 距离测量数据页 |
+| `bgaInspection` | BGA 焊球检测页(含数据表格) |
+| `voidInspection` | 空隙检测页(含数据表格) |
+| `viaFillInspection` | 通孔填锡检测页 |
+
+## 错误处理
+
+模块采用结果对象模式(Result Pattern):
+- `ReportServiceResult.Success(path, reportId)` — 成功
+- `ReportServiceResult.Failure(message, ex)` — 失败
+
+非致命性问题(缺失属性、图像缺失)会记录警告日志并继续执行,不会中断报告生成。
+
+## 构建
+
+```bash
+cd XplorePlane
+dotnet build XP.ReportEngine/XP.ReportEngine.csproj
+```
+
+## 相关文档
+
+- `Documents/Guidance.md` — 详细使用指南(IReportService 调用方式)
+- `Documents/TemplateDevelopment.md` — 模板开发指南(新增/自定义模板)
+- `Documents/App.config.example` — 配置文件示例
+- `Documents/FontFilesGuidance.md` — 字体方案说明
+- `Documents/XP.ReportEngineDesign.md` — 架构设计文档
+- `Documents/XP.ReportEngineModelDefine.md` — 模型定义文档
diff --git a/XP.ReportEngine/Documents/TemplateDevelopment.md b/XP.ReportEngine/Documents/TemplateDevelopment.md
new file mode 100644
index 0000000..cdc3b6d
--- /dev/null
+++ b/XP.ReportEngine/Documents/TemplateDevelopment.md
@@ -0,0 +1,403 @@
+# 报告模板开发指南 | Template Development Guide
+
+## 1. 概述
+
+XP.ReportEngine 使用 JSON 格式定义报告模板。模板描述了 PDF 报告的页面结构、元素布局、数据绑定和样式定义。
+
+模板文件存放在 `XP.ReportEngine/Templates/` 目录下,通过 `App.config` 中的 `Report:TemplatePath` 配置项指定使用哪个模板。
+
+## 2. 新增模板步骤
+
+### 2.1 创建模板文件
+
+1. 在 `Templates/` 目录下创建新的 JSON 文件,如 `CustomReportTemplate.json`
+2. 在 `.csproj` 中确认模板文件会被复制到输出目录(已有通配规则):
+ ```xml
+
+ PreserveNewest
+
+ ```
+3. 修改 `App.config` 中的 `Report:TemplatePath` 指向新模板:
+ ```xml
+
+ ```
+
+### 2.2 模板验证规则
+
+模板加载后会自动验证,必须满足以下条件:
+- 包含 `document` 顶层字段(页面设置)
+- 包含 `pages` 顶层字段(至少一个页面定义)
+- 包含 `styles` 顶层字段(样式字典)
+
+验证失败时报告生成会返回错误,不会生成 PDF。
+
+## 3. 模板 JSON 结构
+
+```json
+{
+ "document": { ... }, // 必需:文档级设置(页面尺寸、边距、页眉页脚)
+ "pages": [ ... ], // 必需:页面定义数组
+ "styles": { ... } // 必需:样式定义字典
+}
+```
+
+## 4. document 配置
+
+```json
+{
+ "document": {
+ "pageSize": "A4",
+ "orientation": "Portrait",
+ "margins": { "top": 40, "bottom": 20, "left": 20, "right": 20 },
+ "header": {
+ "enabled": true,
+ "left": ["标题文本", "第二行文本"],
+ "right": ["右侧文本"],
+ "rightImageKey": "companyLogo",
+ "leftImageKey": "softwareLogo",
+ "fontSize": 7,
+ "color": "#666666",
+ "showLine": true
+ },
+ "footer": {
+ "enabled": true,
+ "left": ["公司名称"],
+ "right": ["{currentPage} / {totalPages}"],
+ "fontSize": 8,
+ "color": "#666666",
+ "showLine": true
+ }
+ }
+}
+```
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `pageSize` | string | `"A4"` | 页面尺寸 |
+| `orientation` | string | `"Portrait"` | 方向:Portrait / Landscape |
+| `margins` | object | `{top:20, bottom:20, left:20, right:20}` | 边距(mm) |
+| `header` | object | null | 页眉配置(首页不显示) |
+| `footer` | object | null | 页脚配置(首页不显示) |
+
+### 页眉/页脚字段
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `enabled` | bool | 是否启用 |
+| `left` | string[] | 左侧文本行(支持 `${}` 绑定) |
+| `right` | string[] | 右侧文本行(支持 `${}` 绑定) |
+| `leftImageKey` | string | 左侧图像的 dataKey |
+| `rightImageKey` | string | 右侧图像的 dataKey |
+| `fontSize` | float | 字体大小 |
+| `color` | string | 字体颜色(十六进制) |
+| `showLine` | bool | 是否显示分隔线 |
+
+页脚特殊占位符:
+- `{currentPage}` — 当前页码
+- `{totalPages}` — 总页数
+
+## 5. pages 页面定义
+
+```json
+{
+ "pages": [
+ {
+ "type": "homepage",
+ "elements": [ ... ]
+ },
+ {
+ "type": "customPage",
+ "elements": [ ... ]
+ }
+ ]
+}
+```
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `type` | string | 页面类型标识(`homepage` 类型不显示页眉页脚) |
+| `elements` | array | 页面内的元素列表 |
+
+### 内置页面类型
+
+| 类型 | 说明 | 特殊行为 |
+|------|------|---------|
+| `homepage` | 首页 | 不显示页眉页脚 |
+| `summary` | 汇总页 | 无 |
+| `metricData` | 距离测量页 | 无 |
+| `bgaInspection` | BGA 检测页 | 无 |
+| `voidInspection` | 空隙检测页 | 无 |
+| `viaFillInspection` | 通孔填锡页 | 无 |
+
+你可以自定义任意 `type` 名称,只有 `homepage` 有特殊行为(不显示页眉页脚)。
+
+## 6. 元素类型
+
+### 6.1 text — 文本元素
+
+```json
+{
+ "type": "text",
+ "content": "${loc:Report_Title}",
+ "style": "heading",
+ "positioning": "flow",
+ "align": "center",
+ "colorRules": { "Pass": "#008000", "Fail": "#FF0000" }
+}
+```
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `content` | string | 文本内容,支持 `${}` 绑定表达式 |
+| `style` | string | 引用 `styles` 中定义的样式名 |
+| `align` | string | 对齐:left / center / right |
+| `colorRules` | object | 条件颜色规则(内容包含关键词时变色) |
+
+### 6.2 image — 图像元素
+
+```json
+{
+ "type": "image",
+ "dataKey": "workpieceImage",
+ "size": [160, 110],
+ "border": true,
+ "align": "center",
+ "style": "imageDefault",
+ "positioning": "flow"
+}
+```
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `dataKey` | string | 图像数据键(对应 `ReportContext.Images` 中的键) |
+| `size` | float[2] | [宽, 高](mm),图像会等比缩放适应 |
+| `border` | bool | 是否显示边框 |
+| `align` | string | 对齐:left / center / right |
+
+图像缺失时会渲染灰色占位矩形,不会中断报告生成。
+
+### 6.3 table — 表格元素
+
+```json
+{
+ "type": "table",
+ "dataKey": "bgaBallsTable",
+ "positioning": "flow",
+ "size": [170, 0],
+ "style": "tableDefault",
+ "columns": [
+ { "header": "序号", "field": "index", "width": 25, "align": "center" },
+ { "header": "气泡率", "field": "voidRate", "width": 40, "align": "center" },
+ { "header": "面积", "field": "area", "width": 40, "align": "center" },
+ {
+ "header": "分类",
+ "field": "classification",
+ "width": 35,
+ "align": "center",
+ "colorRules": { "Pass": "#008000", "Fail": "#FF0000" }
+ }
+ ]
+}
+```
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `dataKey` | string | 表格数据键(对应 `ReportContext.Properties` 中的 `List>` 数据) |
+| `size` | float[2] | [宽, 高](mm),高度为 0 表示自动 |
+| `columns` | array | 列定义数组 |
+
+#### 列定义(ColumnDefinition)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `header` | string | 表头文本(支持 `${}` 绑定) |
+| `field` | string | 数据字段名(对应行数据字典中的键) |
+| `width` | float | 列宽(mm) |
+| `align` | string | 对齐:left / center / right |
+| `colorRules` | object | 条件颜色规则(单元格值匹配时变色) |
+
+### 6.4 row — 水平布局容器
+
+```json
+{
+ "type": "row",
+ "size": [170, 100],
+ "widths": [6, 4],
+ "positioning": "flow",
+ "children": [
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "image", "dataKey": "myImage", "size": [110, 90] }
+ ]
+ },
+ {
+ "type": "column", "align": "left",
+ "children": [
+ { "type": "text", "content": "文本内容", "style": "body" }
+ ]
+ }
+ ]
+}
+```
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `size` | float[2] | [总宽, 总高](mm) |
+| `widths` | float[] | 列宽比例数组(如 `[6, 4]` 表示 60%:40%) |
+| `children` | array | 子列元素(`type: "column"`) |
+
+### 6.5 spacer — 间距元素
+
+```json
+{ "type": "spacer", "size": [170, 10], "positioning": "flow" }
+```
+
+用于在元素之间添加垂直间距。`size[1]` 为间距高度(mm)。
+
+### 6.6 divider — 分隔线
+
+```json
+{ "type": "divider", "positioning": "flow" }
+```
+
+渲染一条水平分隔线。
+
+### 6.7 pagebreak — 强制分页
+
+```json
+{ "type": "pagebreak" }
+```
+
+在当前位置强制插入分页符。
+
+## 7. 样式定义
+
+```json
+{
+ "styles": {
+ "heading": {
+ "font": "auto",
+ "size": 16,
+ "bold": true,
+ "italic": false,
+ "color": "#333333",
+ "align": "left",
+ "marginTop": 0,
+ "marginBottom": 3,
+ "paddingLeft": 0,
+ "lineHeight": 0,
+ "backgroundColor": ""
+ }
+ }
+}
+```
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `font` | string | `"auto"` | 字体(`auto` 根据语言自动选择) |
+| `size` | float | 12 | 字体大小(pt) |
+| `bold` | bool | false | 粗体 |
+| `italic` | bool | false | 斜体 |
+| `color` | string | `"#000000"` | 字体颜色(十六进制) |
+| `align` | string | `"left"` | 对齐:left / center / right |
+| `backgroundColor` | string | — | 背景色(十六进制) |
+| `marginTop` | float | 0 | 上边距(mm) |
+| `marginBottom` | float | 0 | 下边距(mm) |
+| `paddingLeft` | float | 0 | 左缩进(mm) |
+| `lineHeight` | float | 0 | 行高倍数(0 = 默认) |
+
+元素引用未定义的样式名时,会使用默认样式(12pt、黑色、左对齐),不会报错。
+
+## 8. 数据绑定表达式
+
+模板中的 `content`、`header` 等文本字段支持 `${}` 绑定表达式:
+
+| 语法 | 说明 | 示例 |
+|------|------|------|
+| `${key}` | 从 Properties 中取值 | `${sampleName}` |
+| `${metadata.field}` | 从 Metadata 中取值 | `${metadata.reportId}` |
+| `${loc:Key}` | 多语言资源键 | `${loc:Report_Title}` |
+| `${formatDate(value)}` | 格式化日期 | `${formatDate(metadata.inspectionDate)}` |
+| `${formatNumber(value, decimals)}` | 格式化数字 | `${formatNumber(totalArea, 2)}` |
+| `${formatPercent(value)}` | 格式化百分比 | `${formatPercent(voidRate)}` |
+
+### 数据来源
+
+绑定表达式从 `ReportContext` 中查找数据:
+- `Properties` 字典 — 扁平化的键值对(由 `ProcessorDataAdapter` 从处理器输出转换而来)
+- `Metadata` — 报告元数据对象
+- `Images` 字典 — 图像数据(通过 `dataKey` 引用)
+
+### ProcessorDataAdapter 输出的键名
+
+| 处理器类型 | Properties 中的键 | Images 中的键 | 表格 dataKey |
+|-----------|------------------|--------------|-------------|
+| LineMeasurementProcessor | measurementType, point1, point2, pixelDistance, actualDistance, unit, angle | lineMeasurementImage | — |
+| BgaVoidRateProcessor | bgaCount, voidRate, fillRate, totalBgaArea, totalVoidArea, voidLimit, classification | bgaInspectionImage | bgaBallsTable |
+| VoidMeasurementProcessor | roiArea, totalVoidArea, voidRate, voidLimit, voidCount, maxVoidArea, classification | voidInspectionImage | voidsTable |
+| FillRateProcessor | fillRate, voidRate, fullDistance, fillDistance, thtLimit, classification, e1-e4 | viaFillImage | — |
+
+### ReportService 自动注入的键
+
+| 键 | 来源 | 说明 |
+|----|------|------|
+| `CompanyName` | ReportConfig | 公司名称 |
+| `SoftwareName` | ReportConfig | 软件名称 |
+| `companyLogo` | ReportConfig.CompanyLogo | 公司 Logo(Images) |
+| `softwareLogo` | ReportConfig.SoftwareLogo | 软件 Logo(Images) |
+| `summaryTable` | 自动生成 | 首页汇总表数据 |
+
+## 9. 定位方式
+
+| 值 | 说明 |
+|----|------|
+| `"flow"` | 流式布局,元素按顺序从上到下排列(推荐) |
+| `"absolute"` | 绝对定位,使用 `position` 坐标(不推荐,兼容性差) |
+
+建议所有元素使用 `"positioning": "flow"`。
+
+## 10. 完整模板示例(最小化)
+
+```json
+{
+ "document": {
+ "pageSize": "A4",
+ "orientation": "Portrait",
+ "margins": { "top": 20, "bottom": 20, "left": 20, "right": 20 }
+ },
+ "pages": [
+ {
+ "type": "homepage",
+ "elements": [
+ { "type": "text", "content": "检测报告", "style": "title", "positioning": "flow" },
+ { "type": "text", "content": "报告编号:${metadata.reportId}", "style": "body", "positioning": "flow" },
+ { "type": "text", "content": "检测日期:${formatDate(metadata.inspectionDate)}", "style": "body", "positioning": "flow" }
+ ]
+ },
+ {
+ "type": "dataPage",
+ "elements": [
+ { "type": "text", "content": "检测数据", "style": "heading", "positioning": "flow" },
+ { "type": "image", "dataKey": "inspectionImage", "size": [150, 100], "positioning": "flow" }
+ ]
+ }
+ ],
+ "styles": {
+ "title": { "size": 24, "bold": true, "align": "center" },
+ "heading": { "size": 16, "bold": true },
+ "body": { "size": 12 }
+ }
+}
+```
+
+## 11. 注意事项
+
+1. **尺寸单位**:所有尺寸(size、margins、width)单位为 **mm**(毫米)
+2. **颜色格式**:使用十六进制格式 `#RRGGBB`(如 `#FF0000` 为红色)
+3. **表格自动跨页**:当表格数据行超出当前页面剩余空间时,排版引擎会自动分页
+4. **图像缺失容错**:图像 dataKey 对应的数据不存在时,渲染占位矩形,不中断生成
+5. **样式缺失容错**:引用未定义的样式名时使用默认样式,不中断生成
+6. **绑定表达式缺失**:`${}` 表达式对应的数据不存在时,替换为空字符串
+7. **首页特殊处理**:`type: "homepage"` 的页面不显示页眉页脚
+8. **JSON 编码**:模板文件必须使用 **UTF-8** 编码保存
diff --git a/XP.ReportEngine/Documents/XP.ReportEngineDesign.md b/XP.ReportEngine/Documents/XP.ReportEngineDesign.md
new file mode 100644
index 0000000..490c1a8
--- /dev/null
+++ b/XP.ReportEngine/Documents/XP.ReportEngineDesign.md
@@ -0,0 +1,422 @@
+# XP.ReportEngine 项目规划
+
+## 一、项目结构优化
+
+**1. 完整目录结构**
+
+```
+XP.ReportEngine/
+├── Interfaces/
+│ ├── IReportGenerator.cs // 报告生成器核心接口
+│ ├── ITemplateEngine.cs // 模板引擎接口
+│ └── IDataBinder.cs // 数据绑定接口
+├── Models/
+│ ├── ReportContext.cs // 报告上下文(包含测量数据、图片列表)
+│ ├── ReportTemplate.cs // 报告模板定义
+│ ├── TemplateElement.cs // 模板元素定义
+│ └── LayoutSettings.cs // 布局配置参数
+├── Services/
+│ ├── MeasurementReportBuilder.cs // 测量报告构建器
+│ ├── ImageLayoutService.cs // 图片排版服务(计算坐标、缩放)
+│ ├── TemplateEngine.cs // 模板引擎实现
+│ ├── DataBinder.cs // 数据绑定实现
+│ └── PdfGenerationService.cs // PDF生成核心服务
+├── Templates/
+│ └── StandardReportTemplate.json // 标准报告模板配置
+└── Extensions/
+ └── ImageExtensions.cs // 图像处理扩展方法
+```
+
+## 二、核心模块设计
+
+### 1. 报告模板引擎
+
+**1.1 模板定义规范**
+
+- **JSON模板结构**:
+
+ ```json
+ {
+ "document": {
+ "pageSize": "A4",
+ "margins": { "top": 20, "bottom": 20, "left": 20, "right": 20 }
+ },
+ "pages": [
+ {
+ "type": "title",
+ "elements": [
+ { "type": "text", "content": "工业CT检测报告", "style": "title", "position": [10, 10] },
+ { "type": "text", "content": "报告日期: ${reportDate}", "style": "subtitle", "position": [10, 30] }
+ ]
+ },
+ {
+ "type": "measurement",
+ "elements": [
+ { "type": "table", "dataKey": "measurements", "position": [10, 10], "size": [190, 100] },
+ { "type": "image", "dataKey": "overviewImage", "position": [10, 120], "size": [190, 100] }
+ ]
+ },
+ {
+ "type": "defectDetail",
+ "elements": [
+ { "type": "image", "dataKey": "defectImage", "position": [10, 10], "size": [90, 90] },
+ { "type": "text", "content": "缺陷类型: ${defectType}", "style": "normal", "position": [10, 100] }
+ ]
+ }
+ ],
+ "styles": {
+ "title": { "font": "Arial", "size": 24, "bold": true, "color": "#000000" },
+ "subtitle": { "font": "Arial", "size": 16, "italic": true, "color": "#666666" },
+ "normal": { "font": "Arial", "size": 12, "color": "#000000" }
+ }
+ }
+ ```
+
+**1.2 模板引擎实现**
+
+- **模板加载与解析**:
+
+ ```csharp
+ public class TemplateEngine : ITemplateEngine
+ {
+ public ReportTemplate LoadTemplate(string templatePath)
+ {
+ var json = File.ReadAllText(templatePath);
+ return JsonConvert.DeserializeObject(json);
+ }
+
+ public List ParseTemplate(ReportTemplate template, ReportContext context)
+ {
+ var elements = new List();
+
+ foreach (var page in template.Pages)
+ {
+ foreach (var element in page.Elements)
+ {
+ // 处理数据绑定
+ var boundElement = BindData(element, context);
+ elements.Add(boundElement);
+ }
+ }
+
+ return elements;
+ }
+
+ private PageElement BindData(PageElement element, ReportContext context)
+ {
+ // 实现数据绑定逻辑
+ if (element.Type == "text" && element.Content.Contains("${"))
+ {
+ element.Content = DataBinder.Bind(element.Content, context);
+ }
+ return element;
+ }
+ }
+ ```
+
+### 2. 数据绑定系统
+
+**2.1 数据绑定机制**
+
+- **支持的绑定语法**:
+ - `${propertyName}` - 基本属性绑定
+ - `${object.property}` - 对象属性绑定
+ - `${list[index]}` - 列表索引访问
+ - `${function(param)}` - 函数调用(如日期格式化)
+
+**2.2 数据绑定实现**
+
+```csharp
+public class DataBinder : IDataBinder
+{
+ public string Bind(string template, ReportContext context)
+ {
+ // 使用正则表达式匹配绑定表达式
+ var pattern = @"\$\{([^\}]+)\}";
+ return Regex.Replace(template, pattern, match =>
+ {
+ var expression = match.Groups[1].Value;
+ return EvaluateExpression(expression, context);
+ });
+ }
+
+ private string EvaluateExpression(string expression, ReportContext context)
+ {
+ // 解析表达式并获取值
+ if (expression.Contains("."))
+ {
+ // 处理对象属性
+ var parts = expression.Split('.');
+ var obj = context.GetType().GetProperty(parts[0])?.GetValue(context);
+ return obj?.GetType().GetProperty(parts[1])?.GetValue(obj)?.ToString() ?? string.Empty;
+ }
+ else if (expression.Contains("["))
+ {
+ // 处理列表索引
+ var index = int.Parse(expression.Split('[')[1].TrimEnd(']'));
+ return context.Images.Count > index ? "Image" : string.Empty;
+ }
+ else
+ {
+ // 处理基本属性
+ return context.GetType().GetProperty(expression)?.GetValue(context)?.ToString() ?? string.Empty;
+ }
+ }
+}
+```
+
+### 3. 自动排版功能
+
+**3.1 布局引擎设计**
+
+- **核心功能**:
+ - **页面分隔**: 当内容超出一页时自动创建新页
+ - **元素定位**: 根据模板定义计算元素在页面上的精确位置
+ - **尺寸计算**: 自动调整元素大小以适应内容
+ - **响应式布局**: 根据页面尺寸动态调整元素位置
+
+**3.2 排版服务实现**
+
+```csharp
+public class ImageLayoutService
+{
+ public LayoutResult CalculateLayout(ReportTemplate template, ReportContext context)
+ {
+ var result = new LayoutResult();
+ var currentPage = 0;
+ var currentY = template.Document.Margins.Top;
+
+ foreach (var page in template.Pages)
+ {
+ foreach (var element in page.Elements)
+ {
+ // 计算元素位置
+ var position = new Point(
+ template.Document.Margins.Left + element.Position[0],
+ currentY + element.Position[1]
+ );
+
+ // 计算元素尺寸
+ var size = new Size(element.Size[0], element.Size[1]);
+
+ // 检查是否需要换页
+ if (position.Y + size.Height > PageSize.A4.Height - template.Document.Margins.Bottom)
+ {
+ currentPage++;
+ currentY = template.Document.Margins.Top;
+ position = new Point(
+ template.Document.Margins.Left + element.Position[0],
+ currentY + element.Position[1]
+ );
+ }
+
+ // 添加到布局结果
+ result.Elements.Add(new LayoutElement
+ {
+ Page = currentPage,
+ Element = element,
+ Position = position,
+ Size = size
+ });
+
+ // 更新当前Y位置
+ currentY = position.Y + size.Height;
+ }
+ }
+
+ return result;
+ }
+}
+```
+
+### 4. 图表嵌入实现
+
+**4.1 WPF图表转PDF图像**
+
+- **实现方案**:
+ - 使用`RenderTargetBitmap`将WPF控件渲染为位图
+ - 支持高质量图像输出(300 DPI)
+ - 提供图像压缩选项,平衡质量与文件大小
+
+**4.2 图像转换服务**
+
+```csharp
+public static class ImageExtensions
+{
+ public static byte[] ToPdfImage(this ImageSource image, double width, double height)
+ {
+ var renderTarget = new RenderTargetBitmap(
+ (int)width, (int)height,
+ 96, 96, PixelFormats.Pbgra32);
+
+ renderTarget.Render(image);
+
+ using (var stream = new MemoryStream())
+ {
+ var encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(renderTarget));
+ encoder.Save(stream);
+ return stream.ToArray();
+ }
+ }
+
+ public static byte[] ToPdfImage(this FrameworkElement element, double width, double height)
+ {
+ var renderTarget = new RenderTargetBitmap(
+ (int)width, (int)height,
+ 96, 96, PixelFormats.Pbgra32);
+
+ renderTarget.Render(element);
+
+ using (var stream = new MemoryStream())
+ {
+ var encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(renderTarget));
+ encoder.Save(stream);
+ return stream.ToArray();
+ }
+ }
+}
+```
+
+## 三、PDF生成核心流程
+
+### 1. 生成步骤
+
+1. **加载模板**: 从Templates目录加载JSON模板
+2. **解析上下文**: 将MeasurementData和图片列表转换为ReportContext
+3. **数据绑定**: 将上下文数据绑定到模板中的占位符
+4. **计算布局**: 确定每个元素在PDF页面上的精确位置
+5. **生成PDF**: 使用iTextSharp绘制内容到PDF文档
+6. **输出结果**: 返回MemoryStream或保存为文件
+
+### 2. 核心生成服务
+
+```csharp
+public class PdfGenerationService
+{
+ public MemoryStream GeneratePdf(ReportContext context, string templatePath)
+ {
+ // 1. 加载模板
+ var templateEngine = new TemplateEngine();
+ var template = templateEngine.LoadTemplate(templatePath);
+
+ // 2. 解析模板元素
+ var elements = templateEngine.ParseTemplate(template, context);
+
+ // 3. 计算布局
+ var layoutService = new ImageLayoutService();
+ var layout = layoutService.CalculateLayout(template, context);
+
+ // 4. 创建PDF文档
+ var document = new Document(PageSize.A4,
+ template.Document.Margins.Left,
+ template.Document.Margins.Right,
+ template.Document.Margins.Top,
+ template.Document.Margins.Bottom);
+
+ var stream = new MemoryStream();
+ var writer = PdfWriter.GetInstance(document, stream);
+ document.Open();
+
+ // 5. 绘制内容
+ DrawContent(document, layout, context);
+
+ // 6. 关闭文档
+ document.Close();
+
+ return stream;
+ }
+
+ private void DrawContent(Document document, LayoutResult layout, ReportContext context)
+ {
+ // 实现内容绘制逻辑
+ foreach (var element in layout.Elements)
+ {
+ switch (element.Element.Type)
+ {
+ case "text":
+ DrawText(document, element);
+ break;
+ case "image":
+ DrawImage(document, element, context);
+ break;
+ case "table":
+ DrawTable(document, element, context);
+ break;
+ }
+ }
+ }
+
+ private void DrawText(Document document, LayoutElement element)
+ {
+ var paragraph = new Paragraph(element.Element.Content);
+ paragraph.SetLocation(element.Position.X, document.PageSize.Height - element.Position.Y);
+ document.Add(paragraph);
+ }
+
+ private void DrawImage(Document document, LayoutElement element, ReportContext context)
+ {
+ // 获取图像数据
+ var imageData = context.Images.FirstOrDefault();
+ if (imageData == null) return;
+
+ // 创建iTextSharp图像对象
+ var image = Image.GetInstance(imageData);
+ image.SetAbsolutePosition(element.Position.X, document.PageSize.Height - element.Position.Y - element.Size.Height);
+ image.ScaleToFit(element.Size.Width, element.Size.Height);
+
+ document.Add(image);
+ }
+
+ private void DrawTable(Document document, LayoutElement element, ReportContext context)
+ {
+ // 创建表格
+ var table = new PdfPTable(3); // 假设3列
+ table.WidthPercentage = 100;
+
+ // 添加表头
+ table.AddCell(new PdfPCell(new Phrase("参数", new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD))));
+ table.AddCell(new PdfPCell(new Phrase("测量值", new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD))));
+ table.AddCell(new PdfPCell(new Phrase("标准值", new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD))));
+
+ // 添加数据行
+ foreach (var measurement in context.Measurements)
+ {
+ table.AddCell(new PdfPCell(new Phrase(measurement.Parameter)));
+ table.AddCell(new PdfPCell(new Phrase(measurement.Value.ToString())));
+ table.AddCell(new PdfPCell(new Phrase(measurement.StandardValue.ToString())));
+ }
+
+ document.Add(table);
+ }
+}
+```
+
+## 四、最佳实践与建议
+
+### 1. 性能优化
+
+- **图像处理**: 对大尺寸图像进行**压缩和缩放**,避免PDF文件过大
+- **内存管理**: 使用`using`语句确保资源及时释放
+- **批量处理**: 对大量数据采用**分批处理**策略,避免UI冻结
+
+### 2. 错误处理
+
+- 实现**完善的异常捕获**机制,特别是图像转换和PDF生成环节
+- 提供**友好的错误提示**,便于问题排查
+- 添加**日志记录**功能,跟踪报告生成过程
+
+### 3. 样式管理
+
+- 创建**样式库**,统一字体、颜色、间距等
+- 支持**主题切换**,适应不同客户的品牌要求
+- 确保**打印友好**,考虑黑白打印时的可读性
+
+### 4. 扩展性考虑
+
+- **插件架构**: 设计可扩展的插件系统,便于添加新功能
+- **多格式输出**: 基于相同架构,扩展支持Word、Excel等格式输出
+- **云服务集成**: 考虑与云存储集成,自动上传生成的报告
+
+通过以上设计,XP.ReportEngine将成为一个**灵活、高效、可维护**的PDF报告生成系统,满足工业CT检测报告的专业需求,同时保持系统的扩展性和易用性。
diff --git a/XP.ReportEngine/Documents/XP.ReportEngineModelDefine.md b/XP.ReportEngine/Documents/XP.ReportEngineModelDefine.md
new file mode 100644
index 0000000..a5d96a6
--- /dev/null
+++ b/XP.ReportEngine/Documents/XP.ReportEngineModelDefine.md
@@ -0,0 +1,328 @@
+# XP.ReportEngine 模板定义规范
+
+## **一、模板结构总览**
+
+模板使用JSON格式定义,分为以下几个核心部分:
+
+1.
+
+`document`:文档基础配置(页面尺寸、边距等)。
+
+2.
+
+`pages`:页面列表,每个页面包含元素列表。
+
+3.
+
+`elements`:页面中的具体元素(文本、表格、图片等)。
+
+4.
+
+`styles`:预定义的样式,可复用。
+
+5.
+
+`dataKey`:数据绑定字段,关联业务数据。
+
+## **二、首页布局规范**
+
+**模板路径**:`Templates/Homepage.json`
+
+代码图标/24_new/复制
+
+```
+{
+ "document": {
+ "pageSize": "A4",
+ "orientation": "Portrait",
+ "margins": { "top": 30, "right": 20, "bottom": 20, "left": 20 }
+ },
+ "pages": [
+ {
+ "type": "homepage",
+ "elements": [
+ { "type": "text", "content": "平面CT检测报告", "style": "title", "position": [10, 10] },
+ { "type": "text", "content": "报告编号:${reportId}", "style": "subtitle", "position": [10, 40] },
+ { "type": "text", "content": "检测日期:${inspectionDate}", "style": "subtitle", "position": [10, 60] },
+ { "type": "text", "content": "样品名称:${sampleName}", "style": "subtitle", "position": [10, 80] },
+ { "type": "divider", "position": [10, 100], "width": 190 },
+ { "type": "table", "dataKey": "summaryData", "columns": [...], "position": [10, 120], "size": [190, 60] }
+ ]
+ }
+ ],
+ "styles": {
+ // ... 样式定义(见下文)
+ }
+}
+```
+
+**关键要素说明**:
+
+1.
+
+**标题与元数据**:顶部显示报告标题、编号、日期和样品名称。
+
+2.
+
+**摘要表格**:显示关键统计信息(如缺陷数量、合格率等),通过`summaryData`绑定数据。
+
+3.
+
+**分隔线**:视觉分隔标题与主体内容。
+
+## **三、测量数据布局规范**
+
+**模板路径**:`Templates/MetricData.json`
+
+代码图标/24_new/复制
+
+```
+{
+ "pages": [
+ {
+ "type": "metricData",
+ "elements": [
+ { "type": "text", "content": "测量数据详情", "style": "sectionTitle", "position": [10, 10] },
+ { "type": "table", "dataKey": "measurements", "columns": [
+ { "header": "参数名称", "field": "parameter" },
+ { "header": "测量值", "field": "value" },
+ { "header": "单位", "field": "unit" }
+ ], "position": [10, 30], "size": [190, 100] }
+ ]
+ }
+ ],
+ "styles": {
+ // ... 样式定义(见下文)
+ }
+}
+```
+
+**关键要素说明**:
+
+●
+
+表格绑定`measurements`数据,列通过`field`属性绑定数据字段。
+
+●
+
+支持动态行数,自动调整表格高度。
+
+## **四、缺陷检测布局规范**
+
+**模板路径**:`Templates/DefectDetails.json`
+
+代码图标/24_new/复制
+
+```
+{
+ "pages": [
+ {
+ "type": "defectDetails",
+ "elements": [
+ { "type": "text", "content": "缺陷检测结果", "style": "sectionTitle", "position": [10, 10] },
+ {
+ "type": "grid", "dataKey": "defects",
+ "columns": [
+ { "type": "image", "dataKey": "imagePath", "size": [90, 90], "border": true },
+ { "type": "text", "content": "缺陷类型:${type}", "style": "gridText", "width": 100 },
+ { "type": "text", "content": "位置:X=${x}, Y=${y}", "style": "gridText", "width": 100 }
+ ],
+ "position": [10, 30], "colWidth": 100, "rowHeight": 100
+ }
+ ]
+ }
+ ],
+ "styles": {
+ "gridText": { "font": "Arial", "size": 10, "align": "left" }
+ }
+}
+```
+
+**关键要素说明**:
+
+1.
+
+**网格布局**:每行显示缺陷图片、类型和位置信息。
+
+2.
+
+使用`grid`元素自动排版多缺陷数据,支持水平滚动或分页。
+
+3.
+
+图片路径通过`imagePath`字段绑定,文本使用数据绑定语法(如`X=${x}`)。
+
+## **五、BGA检测布局规范**
+
+**模板路径**:`Templates/BGAInspection.json`
+
+代码图标/24_new/复制
+
+```
+{
+ "pages": [
+ {
+ "type": "bgaDetails",
+ "elements": [
+ { "type": "text", "content": "BGA焊点检测结果", "style": "sectionTitle", "position": [10, 10] },
+ {
+ "type": "image", "dataKey": "bgaTopViewImage", "position": [10, 30], "size": [190, 150], "border": true,
+ "annotations": [
+ { "type": "point", "x": ${defectX}, "y": ${defectY}, "color": "red", "size": 5 }
+ ]
+ },
+ {
+ "type": "table", "dataKey": "bgaMetrics",
+ "columns": [ "引脚编号", "焊锡高度", "偏移量" ],
+ "position": [10, 190], "size": [190, 80]
+ }
+ ]
+ }
+ ]
+}
+```
+
+**关键要素说明**:
+
+1.
+
+**图片标注**:在BGA俯视图上标记缺陷位置(通过`annotations`)。
+
+2.
+
+**表格显示参数**:焊锡高度、偏移量等数据。
+
+3.
+
+支持动态标注点,绑定缺陷坐标数据。
+
+## **六、气泡检测布局规范**
+
+**模板路径**:`Templates/VoidInspection.json`
+
+代码图标/24_new/复制
+
+```
+{
+ "pages": [
+ {
+ "type": "voidDetails",
+ "elements": [
+ { "type": "text", "content": "气泡检测结果", "style": "sectionTitle", "position": [10, 10] },
+ {
+ "type": "image", "dataKey": "voidImage", "position": [10, 30], "size": [190, 150], "border": true,
+ "annotations": [
+ { "type": "circle", "x": ${bubbleX}, "y": ${bubbleY}, "radius": ${bubbleRadius}, "color": "blue" }
+ ]
+ },
+ { "type": "text", "content": "总气泡数:${voidCount}", "style": "resultText", "position": [10, 190] },
+ { "type": "text", "content": "最大气泡体积:${maxVoidVolume} mm³", "style": "resultText", "position": [10, 210] }
+ ]
+ }
+ ],
+ "styles": {
+ "resultText": { "font": "Arial", "size": 14, "bold": true }
+ }
+}
+```
+
+**关键要素说明**:
+
+1.
+
+**气泡可视化**:在图像上用圆形标注气泡位置、半径。
+
+2.
+
+**统计结果**:显示总数量、最大体积等关键指标。
+
+3.
+
+支持多气泡标注,数据绑定气泡坐标和尺寸。
+
+## **七、通孔填锡率检测布局规范**
+
+**模板路径**:`Templates/ViaFillInspection.json`
+
+代码图标/24_new/复制
+
+```
+{
+ "pages": [
+ {
+ "type": "viaFillDetails",
+ "elements": [
+ { "type": "text", "content": "通孔填锡率检测结果", "style": "sectionTitle", "position": [10, 10] },
+ {
+ "type": "image", "dataKey": "viaImage", "position": [10, 30], "size": [190, 150], "border": true,
+ "annotations": [
+ { "type": "rectangle", "x": ${viaX}, "y": ${viaY}, "width": ${viaWidth}, "height": ${viaHeight}, "color": "green" }
+ ]
+ },
+ { "type": "text", "content": "平均填锡率:${avgFillRate}%", "style": "resultText", "position": [10, 190] },
+ { "type": "progressBar", "value": ${avgFillRate}, "position": [10, 220], "size": [190, 20], "color": "${fillRateColor(avgFillRate)}" }
+ ]
+ }
+ ],
+ "functions": {
+ "fillRateColor(value)": "return value >= 90 ? 'green' : value >= 70 ? 'orange' : 'red';"
+ }
+}
+```
+
+**关键要素说明**:
+
+1.
+
+**填锡率图示**:用矩形标注通孔位置,进度条显示填锡率。
+
+2.
+
+**颜色条件绑定**:通过函数`fillRateColor`根据数值动态设置颜色。
+
+3.
+
+支持多通孔标注,绑定位置和尺寸数据。
+
+## **八、样式定义示例**
+
+代码图标/24_new/复制
+
+```
+"styles": {
+ "title": { "font": "Arial", "size": 24, "bold": true, "color": "#000000" },
+ "subtitle": { "font": "Arial", "size": 16, "italic": true, "color": "#666666" },
+ "gridText": { "font": "Arial", "size": 10, "align": "left" },
+ "resultText": { "font": "Arial", "size": 14, "bold": true }
+}
+```
+
+**关键要素说明**:
+
+●
+
+样式可复用,通过`style`属性引用。
+
+●
+
+支持字体、大小、颜色、对齐等属性配置。
+
+**# 总结**
+
+●
+
+**模块化设计**:各检测模块独立为单独模板文件,可灵活组合。
+
+●
+
+**数据绑定**:通过`${}`语法绑定业务数据(如`measurements`、`defects`等对象)。
+
+●
+
+**动态布局**:支持表格自适应高度、图片标注、条件样式等高级功能。
+
+●
+
+**可扩展性**:新增检测类型只需定义对应模板文件即可。
+
+**备注**:实际使用时,需根据具体业务数据模型调整`dataKey`和字段名称,确保模板与输入数据匹配。
diff --git a/XP.ReportEngine/Interfaces/IDataBinder.cs b/XP.ReportEngine/Interfaces/IDataBinder.cs
new file mode 100644
index 0000000..7063ff8
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/IDataBinder.cs
@@ -0,0 +1,18 @@
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// 数据绑定器接口 | Data binder interface
+ ///
+ public interface IDataBinder
+ {
+ ///
+ /// 将上下文数据绑定到模板 | Bind context data to template
+ ///
+ /// 报告模板 | Report template
+ /// 报告上下文 | Report context
+ /// 绑定后的模板(元素内容已替换)| Bound template with resolved content
+ ReportTemplate Bind(ReportTemplate template, ReportContext context);
+ }
+}
diff --git a/XP.ReportEngine/Interfaces/ILayoutEngine.cs b/XP.ReportEngine/Interfaces/ILayoutEngine.cs
new file mode 100644
index 0000000..732ff3e
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/ILayoutEngine.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// 排版引擎接口 | Layout engine interface
+ ///
+ public interface ILayoutEngine
+ {
+ ///
+ /// 计算页面布局 | Calculate page layout
+ ///
+ /// 绑定后的模板 | Bound template
+ /// 生成选项 | Generation options
+ /// 排版后的页面列表 | List of laid-out pages
+ List CalculateLayout(ReportTemplate template, ReportGenerationOptions options);
+ }
+}
diff --git a/XP.ReportEngine/Interfaces/IPdfRenderer.cs b/XP.ReportEngine/Interfaces/IPdfRenderer.cs
new file mode 100644
index 0000000..3679c0a
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/IPdfRenderer.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using System.IO;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// PDF 渲染器接口 | PDF renderer interface
+ ///
+ public interface IPdfRenderer
+ {
+ ///
+ /// 将排版结果渲染为 PDF | Render layout result to PDF
+ ///
+ /// 排版后的页面列表 | Laid-out pages
+ /// 生成选项 | Generation options
+ /// 绑定后的模板(用于页眉页脚配置)| Bound template (for header/footer config)
+ /// PDF 内存流 | PDF memory stream
+ MemoryStream Render(List pages, ReportGenerationOptions options, ReportTemplate template = null);
+ }
+}
diff --git a/XP.ReportEngine/Interfaces/IReportDataAdapter.cs b/XP.ReportEngine/Interfaces/IReportDataAdapter.cs
new file mode 100644
index 0000000..264ab7c
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/IReportDataAdapter.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// 报告数据适配器接口 | Report data adapter interface
+ /// 将 XP.ImageProcessing 的 OutputData 转换为 ReportContext
+ ///
+ public interface IReportDataAdapter
+ {
+ ///
+ /// 将处理器输出数据适配为报告上下文 | Adapt processor output data to report context
+ ///
+ /// 处理器输出字典列表 | List of processor output dictionaries
+ /// 报告元数据 | Report metadata
+ /// 报告上下文 | Report context
+ ReportContext Adapt(List processorOutputs, ReportMetadata metadata);
+ }
+}
diff --git a/XP.ReportEngine/Interfaces/IReportGenerator.cs b/XP.ReportEngine/Interfaces/IReportGenerator.cs
new file mode 100644
index 0000000..4d2e15d
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/IReportGenerator.cs
@@ -0,0 +1,19 @@
+using System.Threading.Tasks;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// 报告生成器接口(格式无关)| Report generator interface (format-agnostic)
+ ///
+ public interface IReportGenerator
+ {
+ ///
+ /// 异步生成报告 | Generate report asynchronously
+ ///
+ /// 报告上下文数据 | Report context data
+ /// 生成选项 | Generation options
+ /// 生成结果 | Generation result
+ Task GenerateAsync(ReportContext context, ReportGenerationOptions options);
+ }
+}
diff --git a/XP.ReportEngine/Interfaces/IReportGeneratorFactory.cs b/XP.ReportEngine/Interfaces/IReportGeneratorFactory.cs
new file mode 100644
index 0000000..56f1d72
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/IReportGeneratorFactory.cs
@@ -0,0 +1,15 @@
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// 报告生成器工厂接口 | Report generator factory interface
+ ///
+ public interface IReportGeneratorFactory
+ {
+ ///
+ /// 根据输出格式创建生成器 | Create generator by output format
+ ///
+ IReportGenerator Create(ReportOutputFormat format);
+ }
+}
diff --git a/XP.ReportEngine/Interfaces/IReportService.cs b/XP.ReportEngine/Interfaces/IReportService.cs
new file mode 100644
index 0000000..8b8d63f
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/IReportService.cs
@@ -0,0 +1,45 @@
+using System.Threading.Tasks;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// 报告服务接口(门面)| Report service interface (Facade)
+ /// 提供完整的报告生成流程,外部模块通过此接口调用报告功能
+ /// Provides complete report generation workflow for external modules
+ ///
+ ///
+ /// 使用示例 | Usage example:
+ ///
+ /// var request = new ReportRequest
+ /// {
+ /// ProcessorOutputs = processorOutputs,
+ /// Metadata = new ReportMetadata
+ /// {
+ /// SampleName = "PCB-001",
+ /// OperatorName = "Operator",
+ /// InspectionDate = DateTime.Now
+ /// }
+ /// };
+ /// var result = await _reportService.GenerateAsync(request);
+ ///
+ ///
+ public interface IReportService
+ {
+ ///
+ /// 生成报告 | Generate report
+ /// 执行完整流程:生成报告ID → 数据适配 → 上下文组装 → 管线生成 → 文件保存
+ /// Executes full workflow: generate report ID → data adaptation → context assembly → pipeline generation → file saving
+ ///
+ /// 报告生成请求 | Report generation request
+ /// 报告生成结果 | Report generation result
+ Task GenerateAsync(ReportRequest request);
+
+ ///
+ /// 预热报告引擎(建议在应用启动后台调用)| Warm up report engine (recommended to call in background on app startup)
+ /// 触发 iText7 初始化、字体加载、JIT 编译等一次性开销,避免首次生成报告时卡顿
+ /// Triggers iText7 initialization, font loading, JIT compilation to avoid first-run latency
+ ///
+ Task WarmUpAsync();
+ }
+}
diff --git a/XP.ReportEngine/Interfaces/ITemplateEngine.cs b/XP.ReportEngine/Interfaces/ITemplateEngine.cs
new file mode 100644
index 0000000..7596ab4
--- /dev/null
+++ b/XP.ReportEngine/Interfaces/ITemplateEngine.cs
@@ -0,0 +1,22 @@
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Interfaces
+{
+ ///
+ /// 模板引擎接口 | Template engine interface
+ ///
+ public interface ITemplateEngine
+ {
+ ///
+ /// 加载并验证模板 | Load and validate template
+ ///
+ /// 模板文件路径 | Template file path
+ /// 解析后的模板对象 | Parsed template object
+ ReportTemplate LoadTemplate(string templatePath);
+
+ ///
+ /// 验证模板结构完整性 | Validate template structure integrity
+ ///
+ TemplateValidationResult Validate(ReportTemplate template);
+ }
+}
diff --git a/XP.ReportEngine/Models/ImageData.cs b/XP.ReportEngine/Models/ImageData.cs
new file mode 100644
index 0000000..b1aa0ed
--- /dev/null
+++ b/XP.ReportEngine/Models/ImageData.cs
@@ -0,0 +1,35 @@
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 图像数据封装 | Image data wrapper
+ ///
+ public class ImageData
+ {
+ ///
+ /// 图像来源类型 | Image source type
+ ///
+ public ImageSourceType SourceType { get; set; }
+
+ ///
+ /// 字节数组数据(当 SourceType 为 Bytes 时)| Byte array data
+ ///
+ public byte[] Bytes { get; set; }
+
+ ///
+ /// 文件路径(当 SourceType 为 FilePath 时)| File path
+ ///
+ public string FilePath { get; set; }
+
+ ///
+ /// BitmapSource 引用(当 SourceType 为 BitmapSource 时)| BitmapSource reference
+ ///
+ public object BitmapSource { get; set; }
+ }
+
+ public enum ImageSourceType
+ {
+ Bytes,
+ FilePath,
+ BitmapSource
+ }
+}
diff --git a/XP.ReportEngine/Models/LayoutPage.cs b/XP.ReportEngine/Models/LayoutPage.cs
new file mode 100644
index 0000000..9abcac2
--- /dev/null
+++ b/XP.ReportEngine/Models/LayoutPage.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 排版后的页面 | Laid-out page
+ ///
+ public class LayoutPage
+ {
+ public int PageNumber { get; set; }
+
+ ///
+ /// 页面类型(来自模板定义:homepage / metricData / bgaInspection 等)
+ /// Page type from template definition
+ ///
+ public string PageType { get; set; }
+
+ public List Elements { get; set; } = new();
+ }
+
+ ///
+ /// 排版后的元素(含计算后的绝对坐标)| Laid-out element with computed absolute coordinates
+ ///
+ public class LayoutElement
+ {
+ public TemplateElement Source { get; set; }
+ public float X { get; set; }
+ public float Y { get; set; }
+ public float Width { get; set; }
+ public float Height { get; set; }
+ public StyleDefinition ResolvedStyle { get; set; }
+ public string ResolvedContent { get; set; }
+ public List> ResolvedTableData { get; set; }
+ public ImageData ResolvedImage { get; set; }
+ }
+}
diff --git a/XP.ReportEngine/Models/ProcessorOutput.cs b/XP.ReportEngine/Models/ProcessorOutput.cs
new file mode 100644
index 0000000..3594ad7
--- /dev/null
+++ b/XP.ReportEngine/Models/ProcessorOutput.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 处理器输出数据封装 | Processor output data wrapper
+ ///
+ public class ProcessorOutput
+ {
+ ///
+ /// 处理器类型名称 | Processor type name
+ ///
+ public string ProcessorType { get; set; }
+
+ ///
+ /// 输出数据字典 | Output data dictionary
+ ///
+ public Dictionary OutputData { get; set; } = new();
+
+ ///
+ /// 关联的已标注图像 | Associated annotated image
+ ///
+ public ImageData AnnotatedImage { get; set; }
+ }
+}
diff --git a/XP.ReportEngine/Models/ReportContext.cs b/XP.ReportEngine/Models/ReportContext.cs
new file mode 100644
index 0000000..6b08c73
--- /dev/null
+++ b/XP.ReportEngine/Models/ReportContext.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 报告上下文,包含生成报告所需的全部数据 | Report context containing all data needed for report generation
+ ///
+ public class ReportContext
+ {
+ ///
+ /// 报告元数据 | Report metadata
+ ///
+ public ReportMetadata Metadata { get; set; }
+
+ ///
+ /// 检测结果数据集合(按处理器类型分组)| Inspection result data grouped by processor type
+ ///
+ public List ResultGroups { get; set; } = new();
+
+ ///
+ /// 图像数据字典(键为 dataKey,值为图像数据)| Image data dictionary (key=dataKey, value=image data)
+ ///
+ public Dictionary Images { get; set; } = new();
+
+ ///
+ /// 自定义属性字典(用于数据绑定的扁平化键值对)| Custom properties for data binding
+ ///
+ public Dictionary Properties { get; set; } = new();
+ }
+
+ ///
+ /// 报告元数据 | Report metadata
+ ///
+ public class ReportMetadata
+ {
+ public string ReportId { get; set; }
+ public DateTime InspectionDate { get; set; }
+ public string SampleName { get; set; }
+ public string OperatorName { get; set; }
+ public string Description { get; set; }
+ }
+
+ ///
+ /// 检测结果分组 | Inspection result group
+ ///
+ public class InspectionResultGroup
+ {
+ ///
+ /// 处理器类型标识 | Processor type identifier
+ ///
+ public string ProcessorType { get; set; }
+
+ ///
+ /// 数据来源标识 | Data source identifier
+ ///
+ public string SourceId { get; set; }
+
+ ///
+ /// 分类结果(Pass/Fail)| Classification result
+ ///
+ public string Classification { get; set; }
+
+ ///
+ /// 结果键值对 | Result key-value pairs
+ ///
+ public Dictionary Data { get; set; } = new();
+
+ ///
+ /// 表格数据行(用于表格渲染)| Table data rows for table rendering
+ ///
+ public List> TableRows { get; set; } = new();
+ }
+}
diff --git a/XP.ReportEngine/Models/ReportGenerationOptions.cs b/XP.ReportEngine/Models/ReportGenerationOptions.cs
new file mode 100644
index 0000000..a111849
--- /dev/null
+++ b/XP.ReportEngine/Models/ReportGenerationOptions.cs
@@ -0,0 +1,30 @@
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 报告生成选项 | Report generation options
+ ///
+ public class ReportGenerationOptions
+ {
+ ///
+ /// 模板文件路径 | Template file path
+ ///
+ public string TemplatePath { get; set; }
+
+ ///
+ /// 输出文件路径(可选,为 null 时仅返回 MemoryStream)| Output file path (optional)
+ ///
+ public string OutputFilePath { get; set; }
+
+ ///
+ /// 输出格式 | Output format
+ ///
+ public ReportOutputFormat Format { get; set; } = ReportOutputFormat.Pdf;
+ }
+
+ public enum ReportOutputFormat
+ {
+ Pdf,
+ Excel,
+ Csv
+ }
+}
diff --git a/XP.ReportEngine/Models/ReportRequest.cs b/XP.ReportEngine/Models/ReportRequest.cs
new file mode 100644
index 0000000..0c3f50a
--- /dev/null
+++ b/XP.ReportEngine/Models/ReportRequest.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 报告生成请求 | Report generation request
+ /// 封装外部调用方生成报告所需的全部输入参数
+ /// Encapsulates all input parameters needed by external callers to generate a report
+ ///
+ public class ReportRequest
+ {
+ ///
+ /// 处理器输出数据列表 | List of processor output data
+ ///
+ public List ProcessorOutputs { get; set; } = new();
+
+ ///
+ /// 报告元数据(产品名、操作员、描述等)| Report metadata (product name, operator, description, etc.)
+ ///
+ public ReportMetadata Metadata { get; set; }
+
+ ///
+ /// 额外图像数据(如工件整体图、自定义图像)| Additional image data (e.g., workpiece overview, custom images)
+ /// 键为 dataKey,值为图像数据
+ /// Key is dataKey, value is image data
+ ///
+ public Dictionary AdditionalImages { get; set; } = new();
+
+ ///
+ /// 输出文件路径(可选)| Output file path (optional)
+ /// 为空时根据 ReportConfig 和 FileNameParameters 自动生成
+ /// When empty, auto-generated based on ReportConfig and FileNameParameters
+ ///
+ public string OutputFilePath { get; set; }
+
+ ///
+ /// 文件名占位符参数(可选)| File name placeholder parameters (optional)
+ /// 用于 ReportConfig.FileNamePattern 中的占位符替换
+ /// Used for placeholder replacement in ReportConfig.FileNamePattern
+ ///
+ public Dictionary FileNameParameters { get; set; } = new();
+
+ ///
+ /// 自定义属性(可选)| Custom properties (optional)
+ /// 额外的键值对数据,会合并到 ReportContext.Properties 中
+ /// Additional key-value data merged into ReportContext.Properties
+ ///
+ public Dictionary CustomProperties { get; set; } = new();
+ }
+}
diff --git a/XP.ReportEngine/Models/ReportResult.cs b/XP.ReportEngine/Models/ReportResult.cs
new file mode 100644
index 0000000..c95753c
--- /dev/null
+++ b/XP.ReportEngine/Models/ReportResult.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 报告生成结果 | Report generation result
+ ///
+ public class ReportResult
+ {
+ public bool IsSuccess { get; set; }
+ public MemoryStream PdfStream { get; set; }
+ public string ErrorMessage { get; set; }
+ public Exception Exception { get; set; }
+
+ public static ReportResult Success(MemoryStream stream)
+ => new() { IsSuccess = true, PdfStream = stream };
+
+ public static ReportResult Failure(string message, Exception ex = null)
+ => new() { IsSuccess = false, ErrorMessage = message, Exception = ex };
+ }
+}
diff --git a/XP.ReportEngine/Models/ReportServiceResult.cs b/XP.ReportEngine/Models/ReportServiceResult.cs
new file mode 100644
index 0000000..622a412
--- /dev/null
+++ b/XP.ReportEngine/Models/ReportServiceResult.cs
@@ -0,0 +1,49 @@
+using System;
+
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 报告服务结果 | Report service result
+ /// 封装报告生成的最终结果信息
+ /// Encapsulates the final result of report generation
+ ///
+ public class ReportServiceResult
+ {
+ ///
+ /// 是否成功 | Whether successful
+ ///
+ public bool IsSuccess { get; set; }
+
+ ///
+ /// 输出文件路径(成功时有值)| Output file path (has value when successful)
+ ///
+ public string OutputFilePath { get; set; }
+
+ ///
+ /// 报告编号 | Report ID
+ ///
+ public string ReportId { get; set; }
+
+ ///
+ /// 错误信息(失败时有值)| Error message (has value when failed)
+ ///
+ public string ErrorMessage { get; set; }
+
+ ///
+ /// 异常对象(失败时有值)| Exception object (has value when failed)
+ ///
+ public Exception Exception { get; set; }
+
+ ///
+ /// 创建成功结果 | Create success result
+ ///
+ public static ReportServiceResult Success(string outputFilePath, string reportId)
+ => new() { IsSuccess = true, OutputFilePath = outputFilePath, ReportId = reportId };
+
+ ///
+ /// 创建失败结果 | Create failure result
+ ///
+ public static ReportServiceResult Failure(string message, Exception ex = null)
+ => new() { IsSuccess = false, ErrorMessage = message, Exception = ex };
+ }
+}
diff --git a/XP.ReportEngine/Models/ReportTemplate.cs b/XP.ReportEngine/Models/ReportTemplate.cs
new file mode 100644
index 0000000..e2a2218
--- /dev/null
+++ b/XP.ReportEngine/Models/ReportTemplate.cs
@@ -0,0 +1,94 @@
+using System.Collections.Generic;
+
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 报告模板定义 | Report template definition
+ ///
+ public class ReportTemplate
+ {
+ public DocumentSettings Document { get; set; }
+ public List Pages { get; set; } = new();
+ public Dictionary Styles { get; set; } = new();
+ }
+
+ public class DocumentSettings
+ {
+ public string PageSize { get; set; } = "A4";
+ public string Orientation { get; set; } = "Portrait";
+ public MarginSettings Margins { get; set; } = new();
+
+ ///
+ /// 页眉配置(仅内容页显示,首页不显示)| Header config (content pages only, not homepage)
+ ///
+ public HeaderFooterSettings Header { get; set; }
+
+ ///
+ /// 页脚配置(仅内容页显示,首页不显示)| Footer config (content pages only, not homepage)
+ ///
+ public HeaderFooterSettings Footer { get; set; }
+ }
+
+ public class MarginSettings
+ {
+ public float Top { get; set; } = 20f;
+ public float Bottom { get; set; } = 20f;
+ public float Left { get; set; } = 20f;
+ public float Right { get; set; } = 20f;
+ }
+
+ ///
+ /// 页眉/页脚配置 | Header/Footer settings
+ ///
+ public class HeaderFooterSettings
+ {
+ ///
+ /// 是否启用 | Whether enabled
+ ///
+ public bool Enabled { get; set; } = true;
+
+ ///
+ /// 左侧文本行(支持 ${} 绑定表达式)| Left-side text lines with binding expressions
+ ///
+ public List Left { get; set; } = new();
+
+ ///
+ /// 右侧文本行(支持 ${} 绑定表达式)| Right-side text lines with binding expressions
+ ///
+ public List Right { get; set; } = new();
+
+ ///
+ /// 右侧图像 dataKey(如 logo)| Right-side image dataKey (e.g. logo)
+ ///
+ public string RightImageKey { get; set; }
+
+ ///
+ /// 左侧图像 dataKey | Left-side image dataKey
+ ///
+ public string LeftImageKey { get; set; }
+
+ ///
+ /// 字体大小 | Font size
+ ///
+ public float FontSize { get; set; } = 8f;
+
+ ///
+ /// 字体颜色 | Font color
+ ///
+ public string Color { get; set; } = "#666666";
+
+ ///
+ /// 是否显示分隔线 | Whether to show separator line
+ ///
+ public bool ShowLine { get; set; } = true;
+ }
+
+ public class TemplatePage
+ {
+ ///
+ /// 页面类型:homepage / metricData / defectDetails / bgaInspection / voidInspection / viaFillInspection
+ ///
+ public string Type { get; set; }
+ public List Elements { get; set; } = new();
+ }
+}
diff --git a/XP.ReportEngine/Models/TemplateElement.cs b/XP.ReportEngine/Models/TemplateElement.cs
new file mode 100644
index 0000000..9b3dd27
--- /dev/null
+++ b/XP.ReportEngine/Models/TemplateElement.cs
@@ -0,0 +1,137 @@
+using System.Collections.Generic;
+
+namespace XP.ReportEngine.Models
+{
+ public class TemplateElement
+ {
+ ///
+ /// 元素类型:text / image / table / divider / spacer / row / grid
+ ///
+ public string Type { get; set; }
+
+ ///
+ /// 文本内容(可含 ${} 绑定表达式)| Text content with binding expressions
+ ///
+ public string Content { get; set; }
+
+ ///
+ /// 样式名称引用 | Style name reference
+ ///
+ public string Style { get; set; }
+
+ ///
+ /// 数据绑定键 | Data binding key
+ ///
+ public string DataKey { get; set; }
+
+ ///
+ /// 位置坐标 [x, y](mm)| Position coordinates in mm
+ ///
+ public float[] Position { get; set; }
+
+ ///
+ /// 尺寸 [width, height](mm)| Size in mm
+ ///
+ public float[] Size { get; set; }
+
+ ///
+ /// 表格列定义 | Table column definitions
+ ///
+ public List Columns { get; set; }
+
+ ///
+ /// 是否显示边框 | Whether to show border
+ ///
+ public bool Border { get; set; }
+
+ ///
+ /// 定位方式:absolute / flow | Positioning mode
+ ///
+ public string Positioning { get; set; } = "absolute";
+
+ ///
+ /// 子元素列表(用于 row 类型的水平布局容器)
+ /// Child elements (for row type horizontal layout container)
+ ///
+ public List Children { get; set; }
+
+ ///
+ /// 水平对齐方式(用于 row 子元素):left / center / right
+ /// Horizontal alignment for row children
+ ///
+ public string Align { get; set; }
+
+ ///
+ /// 列宽比例数组(用于 row 类型,如 [7, 3] 表示 7:3 比例)
+ /// Column width ratios for row type (e.g. [7, 3] means 7:3 ratio)
+ ///
+ public float[] Widths { get; set; }
+
+ ///
+ /// 条件颜色规则(用于 text 元素,根据内容中包含的关键词匹配颜色)
+ /// Conditional color rules for text elements (match keywords in content to color)
+ /// 格式:{ "Pass": "#008000", "Fail": "#FF0000" }
+ ///
+ public Dictionary ColorRules { get; set; }
+
+ ///
+ /// 表格数据行(数据绑定阶段填充,用于排版计算和渲染)
+ /// Table data rows (populated during data binding phase, used for layout calculation and rendering)
+ ///
+ [Newtonsoft.Json.JsonIgnore]
+ public List> TableData { get; set; }
+
+ ///
+ /// 图像数据(数据绑定阶段填充)| Image data (populated during data binding phase)
+ ///
+ [Newtonsoft.Json.JsonIgnore]
+ public ImageData ImageData { get; set; }
+ }
+
+ public class ColumnDefinition
+ {
+ public string Header { get; set; }
+ public string Field { get; set; }
+ public float Width { get; set; }
+ public string Align { get; set; } = "left";
+
+ ///
+ /// 条件颜色规则(根据单元格值匹配颜色)
+ /// Conditional color rules (match cell value to color)
+ /// 格式:{ "Pass": "#008000", "Fail": "#FF0000" }
+ ///
+ public Dictionary ColorRules { get; set; }
+ }
+
+ public class StyleDefinition
+ {
+ public string Font { get; set; }
+ public float Size { get; set; } = 12f;
+ public bool Bold { get; set; }
+ public bool Italic { get; set; }
+ public string Color { get; set; } = "#000000";
+ public string Align { get; set; } = "left";
+ public string BackgroundColor { get; set; }
+
+ ///
+ /// 上边距(mm)| Top margin in mm
+ ///
+ public float MarginTop { get; set; }
+
+ ///
+ /// 下边距(mm)| Bottom margin in mm
+ ///
+ public float MarginBottom { get; set; }
+
+ ///
+ /// 左缩进(mm)| Left indent/padding in mm
+ ///
+ public float PaddingLeft { get; set; }
+
+ ///
+ /// 行高倍数(1.0 = 单倍行距,1.5 = 1.5倍行距,0 表示使用默认)
+ /// Line height multiplier (1.0 = single spacing, 1.5 = 1.5x spacing, 0 = use default)
+ ///
+ public float LineHeight { get; set; }
+ }
+}
diff --git a/XP.ReportEngine/Models/TemplateValidationResult.cs b/XP.ReportEngine/Models/TemplateValidationResult.cs
new file mode 100644
index 0000000..6e2191a
--- /dev/null
+++ b/XP.ReportEngine/Models/TemplateValidationResult.cs
@@ -0,0 +1,17 @@
+namespace XP.ReportEngine.Models
+{
+ ///
+ /// 模板验证结果 | Template validation result
+ ///
+ public class TemplateValidationResult
+ {
+ public bool IsValid { get; set; }
+ public string ErrorMessage { get; set; }
+
+ public static TemplateValidationResult Valid()
+ => new() { IsValid = true };
+
+ public static TemplateValidationResult Invalid(string message)
+ => new() { IsValid = false, ErrorMessage = message };
+ }
+}
diff --git a/XP.ReportEngine/ReportEngineModule.cs b/XP.ReportEngine/ReportEngineModule.cs
new file mode 100644
index 0000000..a6cd48a
--- /dev/null
+++ b/XP.ReportEngine/ReportEngineModule.cs
@@ -0,0 +1,98 @@
+using Prism.Ioc;
+using Prism.Modularity;
+using System.Resources;
+using XP.Common.Localization;
+using XP.Common.Localization.Interfaces;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Configs;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Services;
+using XP.ReportEngine.ViewModels;
+using XP.ReportEngine.Views;
+
+namespace XP.ReportEngine
+{
+ ///
+ /// 报告引擎模块 | Report Engine Module
+ /// Prism 模块入口,注册报告生成相关服务到 DI 容器
+ /// Prism module entry, registers report generation services to DI container
+ ///
+ [Module(ModuleName = "ReportEngineModule")]
+ public class ReportEngineModule : IModule
+ {
+ ///
+ /// 模块初始化 | Module initialization
+ /// 注册模块级多语言资源源 | Register module-level localization resource source
+ ///
+ public void OnInitialized(IContainerProvider containerProvider)
+ {
+ // 注册模块级多语言资源到 Fallback Chain | Register module-level localization resources to Fallback Chain
+ var localizationService = containerProvider.Resolve();
+ var resourceManager = new ResourceManager(
+ "XP.ReportEngine.Resources.Resources",
+ typeof(ReportEngineModule).Assembly);
+ localizationService.RegisterResourceSource("XP.ReportEngine", resourceManager);
+
+ // 初始化 LocalizationHelper,使其通过 ILocalizationService 获取字符串(支持 Fallback Chain)
+ // Initialize LocalizationHelper to use ILocalizationService for string lookup (supports Fallback Chain)
+ LocalizationHelper.Initialize(localizationService);
+
+ // 后台预热报告引擎(触发 iText7 初始化、字体加载、JIT 编译,避免首次生成卡顿)
+ // Background warm-up report engine (triggers iText7 init, font loading, JIT to avoid first-run latency)
+ var reportService = containerProvider.Resolve();
+ _ = System.Threading.Tasks.Task.Run(() => reportService.WarmUpAsync());
+
+ System.Console.WriteLine("[ReportEngineModule] 模块已初始化 | Module initialized");
+ }
+
+ ///
+ /// 注册类型到 DI 容器 | Register types to DI container
+ ///
+ public void RegisterTypes(IContainerRegistry containerRegistry)
+ {
+ // 注册配置加载器(瞬态)| Register config loader (transient)
+ containerRegistry.Register();
+
+ // 加载并注册配置为单例 | Load and register config as singleton
+ containerRegistry.RegisterSingleton(container =>
+ {
+ var logger = container.Resolve();
+ var loader = new Configs.ConfigLoader(logger);
+ return loader.LoadReportConfig();
+ });
+
+ // 注册报告生成器(瞬态)| Register report generator (transient)
+ containerRegistry.Register();
+
+ // 注册报告生成器工厂(单例)| Register report generator factory (singleton)
+ containerRegistry.RegisterSingleton();
+
+ // 注册报告服务门面(单例)| Register report service facade (singleton)
+ containerRegistry.RegisterSingleton();
+
+ // 注册模板引擎(瞬态)| Register template engine (transient)
+ containerRegistry.Register();
+
+ // 注册数据绑定器(瞬态)| Register data binder (transient)
+ containerRegistry.Register();
+
+ // 注册排版引擎(瞬态)| Register layout engine (transient)
+ containerRegistry.Register();
+
+ // 注册 PDF 渲染器(瞬态)| Register PDF renderer (transient)
+ containerRegistry.Register();
+
+ // 注册数据适配器(瞬态)| Register data adapter (transient)
+ containerRegistry.Register();
+
+ // 注册报告编号生成器(单例,维护每日计数器状态)| Register report ID generator (singleton, maintains daily counter state)
+ containerRegistry.RegisterSingleton();
+
+ // 注册演示窗口 ViewModel 和 View | Register demo window ViewModel and View
+ containerRegistry.Register();
+ containerRegistry.Register();
+
+ System.Console.WriteLine("[ReportEngineModule] 类型注册完成 | Type registration completed");
+ }
+ }
+}
diff --git a/XP.ReportEngine/Resources/Resources.en-US.resx b/XP.ReportEngine/Resources/Resources.en-US.resx
new file mode 100644
index 0000000..9fe3fc9
--- /dev/null
+++ b/XP.ReportEngine/Resources/Resources.en-US.resx
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Inspection Report
+
+
+ Inspection Time
+
+
+ Product Name
+
+
+ Operator
+
+
+ Inspection Summary
+
+
+ Report ID
+
+
+ Description
+
+
+ PASS
+
+
+ FAIL
+
+
+ Line Measurement
+
+
+ BGA Void Rate
+
+
+ Void Measurement
+
+
+ Via Fill Rate
+
+
+ No.
+
+
+ Void Rate
+
+
+ Classification
+
+
+ Area
+
+
+ Area %
+
+
+ Center X
+
+
+ Center Y
+
+
+ Measurement Type
+
+
+ Distance
+
+
+ Unit
+
+
+ Angle
+
+
+ Fill Rate
+
+
+ Void Rate
+
+
+ Limit
+
+
+ Total Defects
+
+
+ Pass Count
+
+
+ Fail Count
+
+
+ Overall Result
+
+
+ No Image
+
+
+ Inspection Report
+
+
+ Measurement Data
+
+
+ BGA Solder Ball Inspection
+
+
+ Void Inspection
+
+
+ Via Fill Rate Inspection
+
+
+ Ball Count
+
+
+ Total Ball Area
+
+
+ Total Void Area
+
+
+ Void Rate Limit
+
+
+ Ball No.
+
+
+ ROI Area
+
+
+ Total Void Area
+
+
+ Void Count
+
+
+ Max Void Area
+
+
+ Void Rate Limit
+
+
+ Fill Rate
+
+
+ Full Distance
+
+
+ Fill Distance
+
+
+ THT Limit
+
+
+ Point 1
+
+
+ Point 2
+
+
+ Result
+
+
+ Inspection Type
+
+
+ Status
+
+
\ No newline at end of file
diff --git a/XP.ReportEngine/Resources/Resources.resx b/XP.ReportEngine/Resources/Resources.resx
new file mode 100644
index 0000000..db0c10a
--- /dev/null
+++ b/XP.ReportEngine/Resources/Resources.resx
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 检测报告
+
+
+ 检测时间
+
+
+ 产品名称
+
+
+ 操作员
+
+
+ 检测摘要
+
+
+ 报告编号
+
+
+ 描述
+
+
+ 通过
+
+
+ 不通过
+
+
+ 距离测量
+
+
+ BGA 气泡率
+
+
+ 空隙测量
+
+
+ 通孔填锡率
+
+
+ 序号
+
+
+ 气泡率
+
+
+ 分类结果
+
+
+ 面积
+
+
+ 面积百分比
+
+
+ 中心 X
+
+
+ 中心 Y
+
+
+ 测量类型
+
+
+ 距离
+
+
+ 单位
+
+
+ 角度
+
+
+ 填锡率
+
+
+ 气泡率
+
+
+ 限值
+
+
+ 总缺陷数
+
+
+ 通过数量
+
+
+ 不通过数量
+
+
+ 总体结果
+
+
+ 无图像
+
+
+ 检测报告首页
+
+
+ 测量数据
+
+
+ BGA 焊球检测
+
+
+ 空隙检测
+
+
+ 通孔填锡检测
+
+
+ 焊球数量
+
+
+ 焊球总面积
+
+
+ 气泡总面积
+
+
+ 气泡率限值
+
+
+ 焊球序号
+
+
+ ROI 面积
+
+
+ 空隙总面积
+
+
+ 空隙数量
+
+
+ 最大空隙面积
+
+
+ 空隙率限值
+
+
+ 填锡率
+
+
+ 满填距离
+
+
+ 填充距离
+
+
+ THT 限值
+
+
+ 起点
+
+
+ 终点
+
+
+ 结果
+
+
+ 检测类型
+
+
+ 状态
+
+
\ No newline at end of file
diff --git a/XP.ReportEngine/Resources/Resources.zh-CN.resx b/XP.ReportEngine/Resources/Resources.zh-CN.resx
new file mode 100644
index 0000000..db0c10a
--- /dev/null
+++ b/XP.ReportEngine/Resources/Resources.zh-CN.resx
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 检测报告
+
+
+ 检测时间
+
+
+ 产品名称
+
+
+ 操作员
+
+
+ 检测摘要
+
+
+ 报告编号
+
+
+ 描述
+
+
+ 通过
+
+
+ 不通过
+
+
+ 距离测量
+
+
+ BGA 气泡率
+
+
+ 空隙测量
+
+
+ 通孔填锡率
+
+
+ 序号
+
+
+ 气泡率
+
+
+ 分类结果
+
+
+ 面积
+
+
+ 面积百分比
+
+
+ 中心 X
+
+
+ 中心 Y
+
+
+ 测量类型
+
+
+ 距离
+
+
+ 单位
+
+
+ 角度
+
+
+ 填锡率
+
+
+ 气泡率
+
+
+ 限值
+
+
+ 总缺陷数
+
+
+ 通过数量
+
+
+ 不通过数量
+
+
+ 总体结果
+
+
+ 无图像
+
+
+ 检测报告首页
+
+
+ 测量数据
+
+
+ BGA 焊球检测
+
+
+ 空隙检测
+
+
+ 通孔填锡检测
+
+
+ 焊球数量
+
+
+ 焊球总面积
+
+
+ 气泡总面积
+
+
+ 气泡率限值
+
+
+ 焊球序号
+
+
+ ROI 面积
+
+
+ 空隙总面积
+
+
+ 空隙数量
+
+
+ 最大空隙面积
+
+
+ 空隙率限值
+
+
+ 填锡率
+
+
+ 满填距离
+
+
+ 填充距离
+
+
+ THT 限值
+
+
+ 起点
+
+
+ 终点
+
+
+ 结果
+
+
+ 检测类型
+
+
+ 状态
+
+
\ No newline at end of file
diff --git a/XP.ReportEngine/Resources/Resources.zh-TW.resx b/XP.ReportEngine/Resources/Resources.zh-TW.resx
new file mode 100644
index 0000000..9107ec1
--- /dev/null
+++ b/XP.ReportEngine/Resources/Resources.zh-TW.resx
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ 檢測報告
+
+
+ 檢測時間
+
+
+ 樣品名稱
+
+
+ 操作員
+
+
+ 檢測摘要
+
+
+ 報告編號
+
+
+ 描述
+
+
+
+ 通過
+
+
+ 不通過
+
+
+
+ 距離測量
+
+
+ BGA 氣泡率
+
+
+ 空隙測量
+
+
+ 通孔填錫率
+
+
+
+ 序號
+
+
+ 氣泡率
+
+
+ 分類結果
+
+
+ 面積
+
+
+ 面積百分比
+
+
+ 中心 X
+
+
+ 中心 Y
+
+
+
+ 測量類型
+
+
+ 距離
+
+
+ 單位
+
+
+ 角度
+
+
+ 填錫率
+
+
+ 氣泡率
+
+
+ 限值
+
+
+
+ 總缺陷數
+
+
+ 通過數量
+
+
+ 不通過數量
+
+
+ 總體結果
+
+
+
+ 無圖像
+
+
+
+ 檢測報告首頁
+
+
+ 測量數據
+
+
+ BGA 焊球檢測
+
+
+ 空隙檢測
+
+
+ 通孔填錫檢測
+
+
+
+ 焊球數量
+
+
+ 焊球總面積
+
+
+ 氣泡總面積
+
+
+ 氣泡率限值
+
+
+ 焊球序號
+
+
+
+ ROI 面積
+
+
+ 空隙總面積
+
+
+ 空隙數量
+
+
+ 最大空隙面積
+
+
+ 空隙率限值
+
+
+
+ 填錫率
+
+
+ 滿填距離
+
+
+ 填充距離
+
+
+ THT 限值
+
+
+
+ 起點
+
+
+ 終點
+
+
+ 結果
+
+
+ 檢測類型
+
+
+ 狀態
+
+
diff --git a/XP.ReportEngine/Services/ExpressionDataBinder.cs b/XP.ReportEngine/Services/ExpressionDataBinder.cs
new file mode 100644
index 0000000..a3ce3a8
--- /dev/null
+++ b/XP.ReportEngine/Services/ExpressionDataBinder.cs
@@ -0,0 +1,460 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Newtonsoft.Json;
+using XP.Common.Localization.Enums;
+using XP.Common.Localization.Interfaces;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Services
+{
+ ///
+ /// 表达式数据绑定器实现 | Expression data binder implementation
+ /// 支持 ${} 语法的数据绑定、格式化函数和本地化键解析
+ /// Supports ${} syntax data binding, format functions and localization key resolution
+ ///
+ public class ExpressionDataBinder : IDataBinder
+ {
+ private readonly ILoggerService _logger;
+ private readonly ILocalizationService _localizationService;
+
+ private static readonly Regex ExpressionPattern = new(@"\$\{([^}]+)\}", RegexOptions.Compiled);
+ private static readonly Regex LocalizationPattern = new(@"^loc:(.+)$", RegexOptions.Compiled);
+ private static readonly Regex FunctionPattern = new(@"^(\w+)\((.+)\)$", RegexOptions.Compiled);
+ private static readonly Regex IndexPattern = new(@"^([^\[]+)\[(\d+)\]$", RegexOptions.Compiled);
+
+ public ExpressionDataBinder(ILoggerService logger, ILocalizationService localizationService)
+ {
+ _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
+ _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
+ }
+
+ public ReportTemplate Bind(ReportTemplate template, ReportContext context)
+ {
+ if (template == null) throw new ArgumentNullException(nameof(template));
+ if (context == null) throw new ArgumentNullException(nameof(context));
+
+ _logger.Info("开始数据绑定 | Starting data binding");
+ var clonedTemplate = DeepClone(template);
+
+ if (clonedTemplate.Pages != null)
+ {
+ foreach (var page in clonedTemplate.Pages)
+ {
+ if (page.Elements == null) continue;
+ foreach (var element in page.Elements)
+ {
+ BindElement(element, context);
+ }
+ }
+ }
+
+ // 绑定页眉页脚中的表达式 | Bind expressions in header/footer
+ BindHeaderFooter(clonedTemplate, context);
+
+ _logger.Info("数据绑定完成 | Data binding completed");
+ return clonedTemplate;
+ }
+
+
+ private void BindElement(TemplateElement element, ReportContext context)
+ {
+ // 文本内容绑定 | Text content binding
+ if (!string.IsNullOrEmpty(element.Content))
+ {
+ element.Content = ResolveAllExpressions(element.Content, context);
+ }
+
+ // 图像元素绑定:通过 DataKey 从 ReportContext.Images 获取 ImageData
+ // Image element binding: get ImageData from ReportContext.Images via DataKey
+ if (string.Equals(element.Type, "image", StringComparison.OrdinalIgnoreCase)
+ && !string.IsNullOrEmpty(element.DataKey)
+ && context.Images != null
+ && context.Images.TryGetValue(element.DataKey, out var imageData))
+ {
+ element.ImageData = imageData;
+ }
+
+ // 表格数据绑定:通过 DataKey 从 ReportContext.Properties 获取表格行数据
+ // Table data binding: get table row data from ReportContext.Properties via DataKey
+ if (string.Equals(element.Type, "table", StringComparison.OrdinalIgnoreCase)
+ && !string.IsNullOrEmpty(element.DataKey)
+ && context.Properties != null
+ && context.Properties.TryGetValue(element.DataKey, out var tableValue)
+ && tableValue is List> tableRows)
+ {
+ element.TableData = tableRows;
+ }
+
+ // 表格列头绑定 | Table column header binding
+ if (element.Columns != null)
+ {
+ foreach (var column in element.Columns)
+ {
+ if (!string.IsNullOrEmpty(column.Header))
+ {
+ column.Header = ResolveAllExpressions(column.Header, context);
+ }
+ }
+ }
+
+ // Row 子元素递归绑定 | Recursively bind row child elements
+ if (string.Equals(element.Type, "row", StringComparison.OrdinalIgnoreCase)
+ && element.Children != null)
+ {
+ foreach (var child in element.Children)
+ {
+ BindElement(child, context);
+ // Column 子元素递归绑定 | Recursively bind column child elements
+ if (string.Equals(child.Type, "column", StringComparison.OrdinalIgnoreCase)
+ && child.Children != null)
+ {
+ foreach (var subChild in child.Children)
+ {
+ BindElement(subChild, context);
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// 绑定页眉页脚中的 ${} 表达式 | Bind ${} expressions in header/footer
+ ///
+ private void BindHeaderFooter(ReportTemplate template, ReportContext context)
+ {
+ if (template?.Document == null) return;
+
+ // 绑定页眉文本 | Bind header text
+ if (template.Document.Header != null && template.Document.Header.Enabled)
+ {
+ BindStringList(template.Document.Header.Left, context);
+ BindStringList(template.Document.Header.Right, context);
+ }
+
+ // 绑定页脚文本 | Bind footer text
+ if (template.Document.Footer != null && template.Document.Footer.Enabled)
+ {
+ BindStringList(template.Document.Footer.Left, context);
+ BindStringList(template.Document.Footer.Right, context);
+ }
+ }
+
+ ///
+ /// 绑定字符串列表中的表达式 | Bind expressions in string list
+ ///
+ private void BindStringList(List lines, ReportContext context)
+ {
+ if (lines == null) return;
+ for (int i = 0; i < lines.Count; i++)
+ {
+ if (!string.IsNullOrEmpty(lines[i]))
+ {
+ lines[i] = ResolveAllExpressions(lines[i], context);
+ }
+ }
+ }
+
+ private string ResolveAllExpressions(string input, ReportContext context)
+ {
+ return ExpressionPattern.Replace(input, match =>
+ {
+ var expression = match.Groups[1].Value.Trim();
+ return ResolveExpression(expression, context);
+ });
+ }
+
+ private string ResolveExpression(string expression, ReportContext context)
+ {
+ // 1. 本地化键 loc:ResourceKey | Localization key
+ var locMatch = LocalizationPattern.Match(expression);
+ if (locMatch.Success)
+ {
+ var resourceKey = locMatch.Groups[1].Value.Trim();
+ return ResolveLocalizationKey(resourceKey);
+ }
+
+ // 2. 格式化函数 functionName(params) | Format function
+ var funcMatch = FunctionPattern.Match(expression);
+ if (funcMatch.Success)
+ {
+ var functionName = funcMatch.Groups[1].Value;
+ var paramExpression = funcMatch.Groups[2].Value.Trim();
+ return ResolveFormatFunction(functionName, paramExpression, context);
+ }
+
+ // 3. 属性路径 | Property path
+ return ResolvePropertyPath(expression, context);
+ }
+
+ private string ResolveLocalizationKey(string resourceKey)
+ {
+ try
+ {
+ var value = _localizationService.GetString(resourceKey);
+ if (value == null)
+ {
+ _logger.Warn("本地化键未找到: {Key} | Localization key not found: {Key}", resourceKey);
+ return string.Empty;
+ }
+ return value;
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("解析本地化键失败: {Key}, 错误: {Message} | Failed to resolve localization key: {Key}, error: {Message}", resourceKey, ex.Message);
+ return string.Empty;
+ }
+ }
+
+
+ private string ResolveFormatFunction(string functionName, string paramExpression, ReportContext context)
+ {
+ switch (functionName.ToLowerInvariant())
+ {
+ case "formatdate":
+ {
+ var value = ResolvePropertyValue(paramExpression, context);
+ return FormatDate(value);
+ }
+ case "formatnumber":
+ {
+ var parts = paramExpression.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ var value = ResolvePropertyValue(parts[0].Trim(), context);
+ var decimals = parts.Length > 1 && int.TryParse(parts[1].Trim(), out var d) ? d : 2;
+ return FormatNumber(value, decimals);
+ }
+ case "formatpercent":
+ {
+ var value = ResolvePropertyValue(paramExpression, context);
+ return FormatPercent(value);
+ }
+ default:
+ {
+ _logger.Warn("未知的格式化函数: {FunctionName} | Unknown format function: {FunctionName}", functionName);
+ return string.Empty;
+ }
+ }
+ }
+
+ private string ResolvePropertyPath(string path, ReportContext context)
+ {
+ var value = ResolvePropertyValue(path, context);
+ if (value == null)
+ {
+ _logger.Warn("绑定属性未找到: {Path},替换为空字符串 | Binding property not found: {Path}, replacing with empty string", path);
+ return string.Empty;
+ }
+ return ConvertToString(value);
+ }
+
+ private object ResolvePropertyValue(string path, ReportContext context)
+ {
+ if (string.IsNullOrWhiteSpace(path)) return null;
+ path = path.Trim();
+
+ // 优先从 Properties 字典查找 | First look up in Properties dictionary
+ if (context.Properties != null && context.Properties.TryGetValue(path, out var directValue))
+ {
+ return directValue;
+ }
+
+ // 尝试从 context 对象解析嵌套路径 | Try nested path from context object
+ var resolved = ResolveNestedPath(path, context);
+ return resolved;
+ }
+
+
+ private object ResolveNestedPath(string path, object root)
+ {
+ if (root == null || string.IsNullOrWhiteSpace(path)) return null;
+
+ var segments = path.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
+ var current = root;
+
+ foreach (var segment in segments)
+ {
+ if (current == null) return null;
+
+ var indexMatch = IndexPattern.Match(segment);
+ if (indexMatch.Success)
+ {
+ var propertyName = indexMatch.Groups[1].Value;
+ var index = int.Parse(indexMatch.Groups[2].Value);
+ current = GetPropertyValue(current, propertyName);
+ if (current == null) return null;
+ current = GetIndexedValue(current, index);
+ }
+ else
+ {
+ current = GetPropertyValue(current, segment);
+ }
+ }
+
+ return current;
+ }
+
+ private object GetPropertyValue(object obj, string propertyName)
+ {
+ if (obj == null || string.IsNullOrWhiteSpace(propertyName)) return null;
+
+ // 字典访问 | Dictionary access
+ if (obj is IDictionary dict)
+ {
+ if (dict.TryGetValue(propertyName, out var dictValue))
+ return dictValue;
+ foreach (var kvp in dict)
+ {
+ if (string.Equals(kvp.Key, propertyName, StringComparison.OrdinalIgnoreCase))
+ return kvp.Value;
+ }
+ return null;
+ }
+
+ // 反射获取属性 | Reflection property access
+ var type = obj.GetType();
+ var propInfo = type.GetProperty(propertyName,
+ BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
+ if (propInfo != null)
+ {
+ try { return propInfo.GetValue(obj); }
+ catch { return null; }
+ }
+
+ return null;
+ }
+
+ private object GetIndexedValue(object obj, int index)
+ {
+ if (obj == null || index < 0) return null;
+
+ if (obj is IList list)
+ {
+ return index < list.Count ? list[index] : null;
+ }
+
+ if (obj is IEnumerable enumerable)
+ {
+ var i = 0;
+ foreach (var item in enumerable)
+ {
+ if (i == index) return item;
+ i++;
+ }
+ }
+
+ return null;
+ }
+
+
+ private string FormatDate(object value)
+ {
+ if (value == null) return string.Empty;
+
+ DateTime dateTime;
+ if (value is DateTime dt)
+ {
+ dateTime = dt;
+ }
+ else if (DateTime.TryParse(value.ToString(), out var parsed))
+ {
+ dateTime = parsed;
+ }
+ else
+ {
+ _logger.Warn("无法将值转换为日期: {Value} | Cannot convert value to date: {Value}", value);
+ return value.ToString();
+ }
+
+ var format = _localizationService.CurrentLanguage switch
+ {
+ SupportedLanguage.ZhCN => "yyyy年MM月dd日 HH:mm:ss",
+ SupportedLanguage.ZhTW => "yyyy年MM月dd日 HH:mm:ss",
+ SupportedLanguage.EnUS => "MM/dd/yyyy HH:mm:ss",
+ _ => "yyyy-MM-dd HH:mm:ss"
+ };
+
+ return dateTime.ToString(format);
+ }
+
+ private string FormatNumber(object value, int decimals)
+ {
+ if (value == null) return string.Empty;
+
+ if (!TryConvertToDouble(value, out var number))
+ {
+ _logger.Warn("无法将值转换为数字: {Value} | Cannot convert value to number: {Value}", value);
+ return value.ToString();
+ }
+
+ var culture = GetCultureInfo();
+ return number.ToString($"N{decimals}", culture);
+ }
+
+ private string FormatPercent(object value)
+ {
+ if (value == null) return string.Empty;
+
+ if (!TryConvertToDouble(value, out var number))
+ {
+ _logger.Warn("无法将值转换为百分比: {Value} | Cannot convert value to percentage: {Value}", value);
+ return value.ToString();
+ }
+
+ // 值在 0-1 范围内视为小数百分比 | Values in 0-1 range treated as decimal percentage
+ if (number >= 0 && number <= 1)
+ {
+ number *= 100;
+ }
+
+ var culture = GetCultureInfo();
+ return number.ToString("F2", culture) + "%";
+ }
+
+
+ private bool TryConvertToDouble(object value, out double result)
+ {
+ result = 0;
+ if (value == null) return false;
+
+ switch (value)
+ {
+ case double d: result = d; return true;
+ case float f: result = f; return true;
+ case int i: result = i; return true;
+ case long l: result = l; return true;
+ case decimal dec: result = (double)dec; return true;
+ default:
+ return double.TryParse(value.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
+ }
+ }
+
+ private CultureInfo GetCultureInfo()
+ {
+ return _localizationService.CurrentLanguage switch
+ {
+ SupportedLanguage.ZhCN => new CultureInfo("zh-CN"),
+ SupportedLanguage.ZhTW => new CultureInfo("zh-TW"),
+ SupportedLanguage.EnUS => new CultureInfo("en-US"),
+ _ => CultureInfo.InvariantCulture
+ };
+ }
+
+ private string ConvertToString(object value)
+ {
+ if (value == null) return string.Empty;
+ if (value is DateTime dt) return FormatDate(dt);
+ return value.ToString();
+ }
+
+ private ReportTemplate DeepClone(ReportTemplate template)
+ {
+ var json = JsonConvert.SerializeObject(template);
+ return JsonConvert.DeserializeObject(json);
+ }
+ }
+}
diff --git a/XP.ReportEngine/Services/ITextPdfRenderer.cs b/XP.ReportEngine/Services/ITextPdfRenderer.cs
new file mode 100644
index 0000000..f97f21a
--- /dev/null
+++ b/XP.ReportEngine/Services/ITextPdfRenderer.cs
@@ -0,0 +1,1481 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Windows.Media.Imaging;
+using iText.IO.Font;
+using iText.Kernel.Colors;
+using iText.Kernel.Font;
+using iText.Kernel.Geom;
+using iText.Kernel.Pdf;
+using iText.Kernel.Pdf.Canvas.Draw;
+using iText.Layout;
+using iText.Layout.Borders;
+using iText.Layout.Element;
+using iText.Layout.Properties;
+using XP.Common.Localization.Enums;
+using XP.Common.Localization.Interfaces;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Services
+{
+ ///
+ /// iText 7 PDF 渲染器实现 | iText 7 PDF renderer implementation
+ ///
+ public class ITextPdfRenderer : IPdfRenderer
+ {
+ private readonly ILoggerService _logger;
+ private readonly ILocalizationService _localizationService;
+
+ ///
+ /// mm 到 points 的转换系数 | mm to points conversion factor
+ ///
+ private const float MmToPoints = 2.83465f;
+
+ ///
+ /// A4 页面宽度(mm)| A4 page width in mm
+ ///
+ private const float A4WidthMm = 210f;
+
+ ///
+ /// A4 页面高度(mm)| A4 page height in mm
+ ///
+ private const float A4HeightMm = 297f;
+
+ ///
+ /// 表头背景色 | Table header background color
+ ///
+ private const string HeaderBackgroundColor = "#E0E0E0";
+
+ ///
+ /// 表格奇数行背景色 | Table odd row background color
+ ///
+ private const string OddRowBackgroundColor = "#FFFFFF";
+
+ ///
+ /// 表格偶数行背景色 | Table even row background color
+ ///
+ private const string EvenRowBackgroundColor = "#F5F5F5";
+
+ private PdfFont _cjkFont;
+ private PdfFont _westernFont;
+ private bool _fontsInitialized;
+ private readonly object _fontLock = new();
+ private ReportTemplate _currentTemplate;
+
+ public ITextPdfRenderer(ILoggerService logger, ILocalizationService localizationService)
+ {
+ _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
+ _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
+ // 字体延迟加载,不在构造函数中阻塞 | Fonts loaded lazily, not blocking in constructor
+ }
+
+ #region 7.1 基础 PDF 文档创建 | Basic PDF document creation
+
+ ///
+ /// 将排版结果渲染为 PDF 内存流 | Render layout result to PDF memory stream
+ ///
+ /// 排版后的页面列表 | Laid-out pages
+ /// 生成选项 | Generation options
+ /// 绑定后的模板(用于页眉页脚配置)| Bound template (for header/footer config)
+ /// PDF 内存流 | PDF memory stream
+ public MemoryStream Render(List pages, ReportGenerationOptions options, ReportTemplate template = null)
+ {
+ _logger.Info("开始 PDF 渲染,共 {PageCount} 页 | Starting PDF rendering, {PageCount} pages", pages?.Count ?? 0);
+ _currentTemplate = template;
+
+ // 每次渲染重置字体,避免跨 PdfDocument 复用导致 "belongs to other PDF document" 错误
+ // Reset fonts on each render to avoid cross-PdfDocument reuse error
+ _fontsInitialized = false;
+ _cjkFont = null;
+ _westernFont = null;
+
+ var memoryStream = new MemoryStream();
+
+ try
+ {
+ var writer = new PdfWriter(memoryStream, new WriterProperties().SetFullCompressionMode(true));
+ // 防止 PdfWriter 关闭时关闭底层流 | Prevent PdfWriter from closing the underlying stream
+ writer.SetCloseStream(false);
+
+ var pdfDocument = new PdfDocument(writer);
+ // 设置 A4 页面尺寸 | Set A4 page size
+ pdfDocument.SetDefaultPageSize(PageSize.A4);
+
+ var document = new Document(pdfDocument);
+
+ // 设置默认边距(使用默认 20mm)| Set default margins (20mm default)
+ float marginTop = 20f * MmToPoints;
+ float marginBottom = 20f * MmToPoints;
+ float marginLeft = 20f * MmToPoints;
+ float marginRight = 20f * MmToPoints;
+
+ // 如果有页眉页脚配置,为内容页增加边距空间 | Increase margins for header/footer on content pages
+ var headerConfig = template?.Document?.Header;
+ var footerConfig = template?.Document?.Footer;
+ bool hasHeader = headerConfig != null && headerConfig.Enabled;
+ bool hasFooter = footerConfig != null && footerConfig.Enabled;
+
+ // 页眉页脚占用的额外空间(mm → points)| Extra space for header/footer
+ float headerAreaHeight = hasHeader ? 15f * MmToPoints : 0f;
+ float footerAreaHeight = hasFooter ? 12f * MmToPoints : 0f;
+
+ document.SetMargins(marginTop, marginRight, marginBottom, marginLeft);
+
+ // 注册页眉页脚事件处理器 | Register header/footer event handler
+ HeaderFooterEventHandler headerFooterHandler = null;
+ if (hasHeader || hasFooter)
+ {
+ headerFooterHandler = new HeaderFooterEventHandler(
+ this, template, pages, _logger);
+ pdfDocument.AddEventHandler(iText.Kernel.Events.PdfDocumentEvent.END_PAGE, headerFooterHandler);
+ }
+
+ if (pages != null && pages.Count > 0)
+ {
+ for (int i = 0; i < pages.Count; i++)
+ {
+ var pageStopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+ if (i > 0)
+ {
+ // 添加新页面 | Add new page
+ document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
+ }
+
+ // 非首页增加页眉页脚边距 | Add header/footer margins for non-homepage
+ bool isHomepage = string.Equals(pages[i].PageType, "homepage", StringComparison.OrdinalIgnoreCase);
+ if (!isHomepage)
+ {
+ // 通过添加顶部间距为页眉留出空间 | Add top spacing for header area
+ if (hasHeader)
+ {
+ document.Add(new Paragraph("").SetMarginBottom(headerAreaHeight).SetFontSize(1));
+ }
+ }
+
+ RenderPage(document, pages[i]);
+
+ pageStopwatch.Stop();
+ _logger.Info("第 {PageIndex}/{TotalPages} 页渲染完成,类型: {PageType},元素数: {ElementCount},耗时: {ElapsedMs}ms | Page {PageIndex}/{TotalPages} rendered, type: {PageType}, elements: {ElementCount}, elapsed: {ElapsedMs}ms",
+ i + 1, pages.Count, pages[i].PageType ?? "unknown", pages[i].Elements?.Count ?? 0, pageStopwatch.ElapsedMilliseconds);
+ }
+ }
+
+ // 文档关闭前回填总页数占位符 | Fill total page count placeholder before closing
+ if (headerFooterHandler != null)
+ {
+ headerFooterHandler.WriteTotal(pdfDocument);
+ }
+
+ // 关闭文档(触发字体子集化嵌入 + PDF 交叉引用表写入 + 流压缩)
+ // Close document (triggers font subsetting + PDF cross-reference table writing + stream compression)
+ _logger.Info("开始关闭文档(字体嵌入 + 压缩)| Starting document close (font embedding + compression)");
+ var closeStopwatch = System.Diagnostics.Stopwatch.StartNew();
+ document.Close();
+ closeStopwatch.Stop();
+ _logger.Info("文档关闭完成,耗时: {ElapsedMs}ms | Document close completed, elapsed: {ElapsedMs}ms", closeStopwatch.ElapsedMilliseconds);
+
+ // 重置流位置以便后续读取 | Reset stream position for subsequent reading
+ memoryStream.Position = 0;
+ _logger.Info("PDF 渲染完成 | PDF rendering completed");
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "PDF 渲染过程中发生错误 | Error occurred during PDF rendering: {Message}", ex.Message);
+ throw;
+ }
+
+ return memoryStream;
+ }
+
+ ///
+ /// 渲染单个页面 | Render a single page
+ ///
+ private void RenderPage(Document document, LayoutPage page)
+ {
+ if (page?.Elements == null) return;
+
+ foreach (var element in page.Elements)
+ {
+ try
+ {
+ RenderElement(document, element);
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("渲染元素失败,跳过该元素 | Failed to render element, skipping: {Message}", ex.Message);
+ }
+ }
+ }
+
+ ///
+ /// 根据元素类型分发渲染 | Dispatch rendering based on element type
+ ///
+ private void RenderElement(Document document, LayoutElement element)
+ {
+ if (element?.Source == null) return;
+
+ var elementType = element.Source.Type?.ToLowerInvariant();
+
+ switch (elementType)
+ {
+ case "text":
+ RenderTextElement(document, element);
+ break;
+ case "image":
+ RenderImageElement(document, element);
+ break;
+ case "table":
+ RenderTableElement(document, element);
+ break;
+ case "divider":
+ RenderDividerElement(document, element);
+ break;
+ case "spacer":
+ RenderSpacerElement(document, element);
+ break;
+ case "row":
+ RenderRowElement(document, element);
+ break;
+ case "pagebreak":
+ RenderPageBreakElement(document);
+ break;
+ default:
+ _logger.Warn("未知的元素类型:{Type},跳过渲染 | Unknown element type: {Type}, skipping", elementType);
+ break;
+ }
+ }
+
+ #endregion
+
+ #region 7.2 文本元素渲染 | Text element rendering
+
+ ///
+ /// 渲染文本元素 | Render text element
+ ///
+ private void RenderTextElement(Document document, LayoutElement element)
+ {
+ var content = element.ResolvedContent ?? string.Empty;
+ var style = element.ResolvedStyle ?? new StyleDefinition();
+
+ var paragraph = new Paragraph(content);
+
+ // 设置紧凑的默认段落间距 | Set compact default paragraph spacing
+ paragraph.SetMarginTop(0);
+ paragraph.SetMarginBottom(2f);
+
+ // 应用字体 | Apply font
+ var font = GetFontForCurrentLanguage();
+ paragraph.SetFont(font);
+
+ // 应用字体大小 | Apply font size
+ paragraph.SetFontSize(style.Size);
+
+ // 应用粗体 | Apply bold
+ if (style.Bold)
+ {
+ paragraph.SetBold();
+ }
+
+ // 应用斜体 | Apply italic
+ if (style.Italic)
+ {
+ paragraph.SetItalic();
+ }
+
+ // 应用字体颜色 | Apply font color
+ var color = ParseColor(style.Color);
+ if (color != null)
+ {
+ paragraph.SetFontColor(color);
+ }
+
+ // 应用条件颜色规则(根据内容关键词覆盖颜色)| Apply conditional color rules (override color by content keywords)
+ if (element.Source?.ColorRules != null && !string.IsNullOrEmpty(content))
+ {
+ foreach (var rule in element.Source.ColorRules)
+ {
+ if (content.Contains(rule.Key, StringComparison.OrdinalIgnoreCase))
+ {
+ var ruleColor = ParseColor(rule.Value);
+ if (ruleColor != null)
+ {
+ paragraph.SetFontColor(ruleColor);
+ paragraph.SetBold();
+ }
+ break;
+ }
+ }
+ }
+
+ // 应用对齐方式 | Apply text alignment
+ paragraph.SetTextAlignment(ParseTextAlignment(style.Align));
+
+ // 应用背景色 | Apply background color
+ if (!string.IsNullOrEmpty(style.BackgroundColor))
+ {
+ var bgColor = ParseColor(style.BackgroundColor);
+ if (bgColor != null)
+ {
+ paragraph.SetBackgroundColor(bgColor);
+ }
+ }
+
+ // 设置固定位置(如果有坐标信息)| Set fixed position if coordinates available
+ if (element.Width > 0)
+ {
+ paragraph.SetWidth(element.Width * MmToPoints);
+ }
+
+ // 应用边距和缩进 | Apply margins and indent
+ if (style.MarginTop > 0)
+ paragraph.SetMarginTop(style.MarginTop * MmToPoints);
+ if (style.MarginBottom > 0)
+ paragraph.SetMarginBottom(style.MarginBottom * MmToPoints);
+ if (style.PaddingLeft > 0)
+ paragraph.SetPaddingLeft(style.PaddingLeft * MmToPoints);
+ if (style.LineHeight > 0)
+ paragraph.SetMultipliedLeading(style.LineHeight);
+
+ document.Add(paragraph);
+ }
+
+ #endregion
+
+ #region 7.3 字体管理 | Font management
+
+ ///
+ /// 确保字体已初始化(线程安全的延迟加载)| Ensure fonts are initialized (thread-safe lazy loading)
+ ///
+ private void EnsureFontsInitialized()
+ {
+ if (_fontsInitialized) return;
+ lock (_fontLock)
+ {
+ if (_fontsInitialized) return;
+ InitializeFonts();
+ _fontsInitialized = true;
+ }
+ }
+
+ ///
+ /// 初始化字体(从系统字体目录加载)| Initialize fonts (load from system fonts directory)
+ /// 使用 Windows 系统自带字体,确保 Telerik RadPdfViewer 兼容性
+ /// Uses Windows built-in fonts to ensure Telerik RadPdfViewer compatibility
+ ///
+ private void InitializeFonts()
+ {
+ var fontsDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts");
+
+ // 加载微软雅黑(支持简体中文、繁体中文)| Load Microsoft YaHei (supports Simplified & Traditional Chinese)
+ try
+ {
+ var msyhPath = System.IO.Path.Combine(fontsDir, "msyh.ttc");
+ _cjkFont = PdfFontFactory.CreateFont(msyhPath + ",0", PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
+ _logger.Info("中文字体加载成功(微软雅黑)| Chinese font loaded successfully (Microsoft YaHei)");
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("微软雅黑加载失败,尝试后备字体 | Microsoft YaHei load failed, trying fallback: {Message}", ex.Message);
+ try
+ {
+ // 后备:宋体 | Fallback: SimSun
+ var simsunPath = System.IO.Path.Combine(fontsDir, "simsun.ttc");
+ _cjkFont = PdfFontFactory.CreateFont(simsunPath + ",0", PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
+ _logger.Info("中文后备字体加载成功(宋体)| Chinese fallback font loaded successfully (SimSun)");
+ }
+ catch (Exception ex2)
+ {
+ _logger.Warn("宋体加载失败 | SimSun load failed: {Message}", ex2.Message);
+ _cjkFont = null;
+ }
+ }
+
+ // 加载 Arial(西文字体)| Load Arial (Western font)
+ try
+ {
+ var arialPath = System.IO.Path.Combine(fontsDir, "arial.ttf");
+ _westernFont = PdfFontFactory.CreateFont(arialPath, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
+ _logger.Info("西文字体加载成功(Arial)| Western font loaded successfully (Arial)");
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("Arial 加载失败,使用 Helvetica 后备 | Arial load failed, using Helvetica fallback: {Message}", ex.Message);
+ _westernFont = null;
+ }
+
+ // 如果系统字体都不可用,使用 iText 内置 Helvetica | If system fonts unavailable, use built-in Helvetica
+ if (_cjkFont == null && _westernFont == null)
+ {
+ _logger.Warn("所有系统字体不可用,使用 Helvetica 后备字体 | All system fonts unavailable, using Helvetica fallback");
+ }
+ }
+
+ ///
+ /// 根据当前语言获取合适的字体 | Get appropriate font based on current language
+ /// zh-CN / zh-TW → 微软雅黑(支持简繁体);en-US → Arial
+ ///
+ /// PDF 字体 | PDF font
+ private PdfFont GetFontForCurrentLanguage()
+ {
+ // 延迟初始化字体 | Lazy initialize fonts
+ EnsureFontsInitialized();
+
+ var language = _localizationService.CurrentLanguage;
+
+ PdfFont selectedFont;
+ switch (language)
+ {
+ case SupportedLanguage.ZhCN:
+ case SupportedLanguage.ZhTW:
+ selectedFont = _cjkFont;
+ break;
+ case SupportedLanguage.EnUS:
+ default:
+ selectedFont = _westernFont ?? _cjkFont; // 西文优先,中文后备(微软雅黑也支持西文)| Western preferred, CJK fallback (YaHei supports Western too)
+ break;
+ }
+
+ // 后备字体逻辑 | Fallback font logic
+ if (selectedFont != null)
+ {
+ return selectedFont;
+ }
+
+ // 尝试使用其他字体 | Try other fonts
+ if (_cjkFont != null) return _cjkFont;
+ if (_westernFont != null) return _westernFont;
+
+ // 最终后备:使用 iText 内置 Helvetica | Final fallback: use built-in Helvetica
+ return PdfFontFactory.CreateFont(iText.IO.Font.Constants.StandardFonts.HELVETICA);
+ }
+
+ #endregion
+
+ #region 7.4 图像嵌入渲染 | Image embedding rendering
+
+ ///
+ /// 渲染图像元素 | Render image element
+ ///
+ private void RenderImageElement(Document document, LayoutElement element)
+ {
+ var imageData = element.ResolvedImage;
+
+ // 如果图像数据缺失,渲染占位矩形 | If image data is missing, render placeholder
+ if (imageData == null)
+ {
+ _logger.Warn("图像数据为空,渲染占位矩形 | Image data is null, rendering placeholder");
+ RenderImagePlaceholder(document, element);
+ return;
+ }
+
+ try
+ {
+ byte[] imageBytes = GetImageBytes(imageData);
+
+ if (imageBytes == null || imageBytes.Length == 0)
+ {
+ _logger.Warn("图像字节数据为空,渲染占位矩形 | Image byte data is empty, rendering placeholder");
+ RenderImagePlaceholder(document, element);
+ return;
+ }
+
+ // 创建 iText 图像对象 | Create iText image object
+ var iTextImageData = iText.IO.Image.ImageDataFactory.Create(imageBytes);
+ var image = new Image(iTextImageData);
+
+ // 计算目标区域尺寸(mm → points)| Calculate target area size (mm → points)
+ float targetWidthPt = element.Width * MmToPoints;
+ float targetHeightPt = element.Height * MmToPoints;
+
+ // 等比缩放以适应目标区域 | Scale proportionally to fit target area
+ if (targetWidthPt > 0 && targetHeightPt > 0)
+ {
+ float imageWidth = image.GetImageWidth();
+ float imageHeight = image.GetImageHeight();
+
+ float scaleX = targetWidthPt / imageWidth;
+ float scaleY = targetHeightPt / imageHeight;
+ float scale = Math.Min(scaleX, scaleY);
+
+ image.SetWidth(imageWidth * scale);
+ image.SetHeight(imageHeight * scale);
+ }
+ else if (targetWidthPt > 0)
+ {
+ image.SetWidth(targetWidthPt);
+ image.ScaleToFit(targetWidthPt, float.MaxValue);
+ }
+
+ // 应用边框 | Apply border
+ if (element.Source?.Border == true)
+ {
+ image.SetBorder(new SolidBorder(ColorConstants.BLACK, 1f));
+ }
+
+ // 应用对齐方式 | Apply alignment
+ var align = element.Source?.Align?.ToLowerInvariant();
+ if (align == "center")
+ {
+ image.SetHorizontalAlignment(HorizontalAlignment.CENTER);
+ }
+ else if (align == "right")
+ {
+ image.SetHorizontalAlignment(HorizontalAlignment.RIGHT);
+ }
+
+ // 应用样式中的边距 | Apply margins from style
+ var style = element.ResolvedStyle;
+ if (style != null)
+ {
+ if (style.MarginTop > 0)
+ image.SetMarginTop(style.MarginTop * MmToPoints);
+ if (style.MarginBottom > 0)
+ image.SetMarginBottom(style.MarginBottom * MmToPoints);
+ }
+
+ document.Add(image);
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("图像渲染失败,渲染占位矩形 | Image rendering failed, rendering placeholder: {Message}", ex.Message);
+ RenderImagePlaceholder(document, element);
+ }
+ }
+
+ ///
+ /// 从 ImageData 获取字节数组 | Get byte array from ImageData
+ ///
+ private byte[] GetImageBytes(ImageData imageData)
+ {
+ switch (imageData.SourceType)
+ {
+ case ImageSourceType.Bytes:
+ return imageData.Bytes;
+
+ case ImageSourceType.FilePath:
+ if (!string.IsNullOrEmpty(imageData.FilePath) && File.Exists(imageData.FilePath))
+ {
+ return File.ReadAllBytes(imageData.FilePath);
+ }
+ _logger.Warn("图像文件不存在:{Path} | Image file not found: {Path}", imageData.FilePath);
+ return null;
+
+ case ImageSourceType.BitmapSource:
+ if (imageData.BitmapSource is BitmapSource bitmapSource)
+ {
+ return ConvertBitmapSourceToBytes(bitmapSource);
+ }
+ _logger.Warn("BitmapSource 对象无效 | BitmapSource object is invalid");
+ return null;
+
+ default:
+ _logger.Warn("未知的图像来源类型:{Type} | Unknown image source type: {Type}", imageData.SourceType);
+ return null;
+ }
+ }
+
+ #endregion
+
+ #region 7.5 BitmapSource 转 byte[] | BitmapSource to byte[] conversion
+
+ ///
+ /// 将 BitmapSource 转换为 PNG 编码的字节数组 | Convert BitmapSource to PNG-encoded byte array
+ ///
+ /// WPF BitmapSource 对象 | WPF BitmapSource object
+ /// PNG 编码的字节数组 | PNG-encoded byte array
+ public static byte[] ConvertBitmapSourceToBytes(BitmapSource bitmapSource)
+ {
+ if (bitmapSource == null)
+ {
+ return null;
+ }
+
+ using (var memoryStream = new MemoryStream())
+ {
+ var encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
+ encoder.Save(memoryStream);
+ return memoryStream.ToArray();
+ }
+ }
+
+ #endregion
+
+ #region 7.6 图像占位矩形渲染 | Image placeholder rendering
+
+ ///
+ /// 渲染图像缺失时的占位矩形(带"无图像 | No Image"文本标签)
+ /// Render placeholder rectangle when image is missing (with "无图像 | No Image" text label)
+ ///
+ private void RenderImagePlaceholder(Document document, LayoutElement element)
+ {
+ float widthPt = element.Width > 0 ? element.Width * MmToPoints : 100f * MmToPoints;
+ float heightPt = element.Height > 0 ? element.Height * MmToPoints : 60f * MmToPoints;
+
+ // 使用表格模拟占位矩形(带边框和居中文本)| Use table to simulate placeholder rectangle
+ var table = new Table(1);
+ table.SetWidth(widthPt);
+
+ var cell = new Cell();
+ cell.SetHeight(heightPt);
+ cell.SetBorder(new SolidBorder(ColorConstants.GRAY, 1f));
+ cell.SetBackgroundColor(new DeviceRgb(245, 245, 245));
+
+ // 居中显示"无图像 | No Image"文本 | Center "无图像 | No Image" text
+ var placeholderText = new Paragraph("无图像 | No Image");
+ var font = GetFontForCurrentLanguage();
+ placeholderText.SetFont(font);
+ placeholderText.SetFontSize(10f);
+ placeholderText.SetFontColor(ColorConstants.GRAY);
+ placeholderText.SetTextAlignment(TextAlignment.CENTER);
+
+ cell.SetVerticalAlignment(VerticalAlignment.MIDDLE);
+ cell.Add(placeholderText);
+ table.AddCell(cell);
+
+ document.Add(table);
+ }
+
+ #endregion
+
+ #region 7.7 表格渲染 | Table rendering
+
+ ///
+ /// 渲染表格元素 | Render table element
+ ///
+ private void RenderTableElement(Document document, LayoutElement element)
+ {
+ var columns = element.Source?.Columns;
+ var tableData = element.ResolvedTableData;
+
+ if (columns == null || columns.Count == 0)
+ {
+ _logger.Warn("表格列定义为空,跳过渲染 | Table column definitions are empty, skipping");
+ return;
+ }
+
+ // 计算列宽(mm → points)| Calculate column widths (mm → points)
+ var columnWidths = new float[columns.Count];
+ for (int i = 0; i < columns.Count; i++)
+ {
+ columnWidths[i] = columns[i].Width > 0 ? columns[i].Width * MmToPoints : 30f * MmToPoints;
+ }
+
+ var table = new Table(columnWidths);
+ table.SetWidth(UnitValue.CreatePercentValue(100));
+
+ var font = GetFontForCurrentLanguage();
+
+ // 渲染表头行 | Render header row
+ var headerBgColor = ParseColor(HeaderBackgroundColor);
+ foreach (var column in columns)
+ {
+ var headerCell = new Cell();
+ var headerParagraph = new Paragraph(column.Header ?? string.Empty);
+ headerParagraph.SetFont(font);
+ headerParagraph.SetFontSize(10f);
+ headerParagraph.SetBold();
+ headerParagraph.SetTextAlignment(ParseTextAlignment(column.Align));
+
+ headerCell.Add(headerParagraph);
+ headerCell.SetBackgroundColor(headerBgColor);
+ headerCell.SetBorder(new SolidBorder(ColorConstants.LIGHT_GRAY, 0.5f));
+
+ table.AddHeaderCell(headerCell);
+ }
+
+ // 渲染数据行(交替背景色)| Render data rows (alternating background colors)
+ if (tableData != null)
+ {
+ for (int rowIndex = 0; rowIndex < tableData.Count; rowIndex++)
+ {
+ var rowData = tableData[rowIndex];
+ var rowBgColor = rowIndex % 2 == 0
+ ? ParseColor(OddRowBackgroundColor)
+ : ParseColor(EvenRowBackgroundColor);
+
+ foreach (var column in columns)
+ {
+ var dataCell = new Cell();
+
+ // 从行数据中获取字段值 | Get field value from row data
+ string cellValue = string.Empty;
+ if (rowData != null && !string.IsNullOrEmpty(column.Field) && rowData.ContainsKey(column.Field))
+ {
+ cellValue = rowData[column.Field]?.ToString() ?? string.Empty;
+ }
+
+ var cellParagraph = new Paragraph(cellValue);
+ cellParagraph.SetFont(font);
+ cellParagraph.SetFontSize(9f);
+ cellParagraph.SetTextAlignment(ParseTextAlignment(column.Align));
+
+ // 应用条件颜色规则 | Apply conditional color rules
+ if (column.ColorRules != null && !string.IsNullOrEmpty(cellValue))
+ {
+ foreach (var rule in column.ColorRules)
+ {
+ if (string.Equals(cellValue, rule.Key, StringComparison.OrdinalIgnoreCase)
+ || cellValue.Contains(rule.Key, StringComparison.OrdinalIgnoreCase))
+ {
+ var ruleColor = ParseColor(rule.Value);
+ if (ruleColor != null)
+ {
+ cellParagraph.SetFontColor(ruleColor);
+ cellParagraph.SetBold();
+ }
+ break;
+ }
+ }
+ }
+
+ dataCell.Add(cellParagraph);
+ dataCell.SetBackgroundColor(rowBgColor);
+ dataCell.SetBorder(new SolidBorder(ColorConstants.LIGHT_GRAY, 0.5f));
+
+ table.AddCell(dataCell);
+ }
+ }
+ }
+
+ // 应用样式中的边距 | Apply margins from style
+ var style = element.ResolvedStyle;
+ if (style != null)
+ {
+ if (style.MarginTop > 0)
+ table.SetMarginTop(style.MarginTop * MmToPoints);
+ if (style.MarginBottom > 0)
+ table.SetMarginBottom(style.MarginBottom * MmToPoints);
+ }
+
+ document.Add(table);
+ }
+
+ #endregion
+
+ #region 7.8 分隔线渲染 | Divider rendering
+
+ ///
+ /// 渲染分隔线元素 | Render divider element
+ ///
+ private void RenderDividerElement(Document document, LayoutElement element)
+ {
+ var style = element.ResolvedStyle;
+
+ // 确定分隔线颜色(从样式或默认灰色)| Determine divider color (from style or default gray)
+ Color lineColor = ColorConstants.GRAY;
+ if (style != null && !string.IsNullOrEmpty(style.Color))
+ {
+ var parsedColor = ParseColor(style.Color);
+ if (parsedColor != null)
+ {
+ lineColor = parsedColor;
+ }
+ }
+
+ // 使用 LineSeparator 渲染水平分隔线 | Use LineSeparator to render horizontal divider
+ var lineSeparator = new LineSeparator(new SolidLine(1f));
+ lineSeparator.SetStrokeColor(lineColor);
+
+ // 设置宽度为可用区域全宽 | Set width to full available area
+ if (element.Width > 0)
+ {
+ lineSeparator.SetWidth(element.Width * MmToPoints);
+ }
+
+ // 添加上下间距 | Add vertical spacing
+ lineSeparator.SetMarginTop(5f);
+ lineSeparator.SetMarginBottom(5f);
+
+ document.Add(lineSeparator);
+ }
+
+ ///
+ /// 渲染空白间距元素 | Render spacer element
+ /// 通过 Size[1](高度,mm)控制垂直空白大小
+ /// Controls vertical whitespace via Size[1] (height in mm)
+ ///
+ private void RenderSpacerElement(Document document, LayoutElement element)
+ {
+ // 从 Size[1] 获取高度,默认 10mm | Get height from Size[1], default 10mm
+ float heightMm = 10f;
+ if (element.Source?.Size is { Length: >= 2 })
+ {
+ heightMm = element.Source.Size[1];
+ }
+
+ // 使用空段落撑出指定高度的空白 | Use empty paragraph to create specified height whitespace
+ var spacer = new Paragraph("")
+ .SetFontSize(1)
+ .SetMarginTop(0)
+ .SetMarginBottom(heightMm * MmToPoints);
+
+ document.Add(spacer);
+ }
+
+ ///
+ /// 渲染强制分页元素 | Render forced page break element
+ ///
+ private void RenderPageBreakElement(Document document)
+ {
+ document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
+ }
+
+ ///
+ /// 渲染行容器元素(水平布局)| Render row container element (horizontal layout)
+ /// 使用无边框表格实现子元素的水平排列,支持 left/center/right 对齐
+ /// Uses borderless table to arrange child elements horizontally, supports left/center/right alignment
+ ///
+ private void RenderRowElement(Document document, LayoutElement element)
+ {
+ var children = element.Source?.Children;
+ if (children == null || children.Count == 0) return;
+
+ // 创建表格,支持自定义列宽比例 | Create table with custom column width ratios
+ var columnCount = children.Count;
+ Table table;
+
+ if (element.Source.Widths != null && element.Source.Widths.Length == columnCount)
+ {
+ // 使用指定的列宽比例 | Use specified column width ratios
+ var totalRatio = 0f;
+ foreach (var w in element.Source.Widths) totalRatio += w;
+
+ var columnWidths = new float[columnCount];
+ for (int i = 0; i < columnCount; i++)
+ {
+ columnWidths[i] = element.Source.Widths[i] / totalRatio;
+ }
+ table = new Table(UnitValue.CreatePercentArray(columnWidths));
+ }
+ else
+ {
+ // 均分列宽 | Equal column widths
+ table = new Table(columnCount);
+ }
+
+ table.UseAllAvailableWidth();
+ table.SetBorder(iText.Layout.Borders.Border.NO_BORDER);
+
+ var font = GetFontForCurrentLanguage();
+
+ foreach (var child in children)
+ {
+ var cell = new Cell();
+ cell.SetBorder(iText.Layout.Borders.Border.NO_BORDER);
+ cell.SetPadding(0);
+
+ // 确定子元素对齐方式 | Determine child element alignment
+ var align = child.Align?.ToLowerInvariant() ?? "left";
+ cell.SetTextAlignment(ParseTextAlignment(align));
+
+ var childType = child.Type?.ToLowerInvariant();
+
+ if (childType == "column")
+ {
+ // 渲染列容器子元素(垂直堆叠多个元素在同一单元格内)
+ // Render column container child (stack multiple elements vertically in same cell)
+ if (child.Children != null)
+ {
+ foreach (var subChild in child.Children)
+ {
+ RenderRowChildIntoCell(cell, subChild, align, font);
+ }
+ }
+ }
+ else
+ {
+ RenderRowChildIntoCell(cell, child, align, font);
+ }
+
+ table.AddCell(cell);
+ }
+
+ document.Add(table);
+ }
+
+ ///
+ /// 将单个子元素渲染到单元格中 | Render a single child element into a cell
+ ///
+ private void RenderRowChildIntoCell(Cell cell, TemplateElement child, string align, PdfFont font)
+ {
+ var childType = child.Type?.ToLowerInvariant();
+ // 子元素可以覆盖父级对齐 | Child can override parent alignment
+ var childAlign = child.Align?.ToLowerInvariant() ?? align;
+
+ if (childType == "image")
+ {
+ // 渲染图像子元素 | Render image child element
+ var imageData = child.ImageData;
+ if (imageData != null)
+ {
+ try
+ {
+ byte[] imageBytes = GetImageBytes(imageData);
+ if (imageBytes != null && imageBytes.Length > 0)
+ {
+ var iTextImageData = iText.IO.Image.ImageDataFactory.Create(imageBytes);
+ var image = new Image(iTextImageData);
+
+ // 应用尺寸 | Apply size
+ float targetWidthPt = child.Size != null && child.Size.Length > 0 ? child.Size[0] * MmToPoints : 0;
+ float targetHeightPt = child.Size != null && child.Size.Length > 1 ? child.Size[1] * MmToPoints : 0;
+
+ if (targetWidthPt > 0 && targetHeightPt > 0)
+ {
+ float imageWidth = image.GetImageWidth();
+ float imageHeight = image.GetImageHeight();
+ float scaleX = targetWidthPt / imageWidth;
+ float scaleY = targetHeightPt / imageHeight;
+ float scale = Math.Min(scaleX, scaleY);
+ image.SetWidth(imageWidth * scale);
+ image.SetHeight(imageHeight * scale);
+ }
+
+ // 设置图像水平对齐 | Set image horizontal alignment
+ if (childAlign == "right")
+ image.SetHorizontalAlignment(HorizontalAlignment.RIGHT);
+ else if (childAlign == "center")
+ image.SetHorizontalAlignment(HorizontalAlignment.CENTER);
+ else
+ image.SetHorizontalAlignment(HorizontalAlignment.LEFT);
+
+ cell.Add(image);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("Row 子元素图像渲染失败 | Row child image rendering failed: {Message}", ex.Message);
+ }
+ }
+ }
+ else if (childType == "text")
+ {
+ // 渲染文本子元素 | Render text child element
+ var content = child.Content ?? string.Empty;
+ var style = ResolveStyleFromTemplate(child.Style);
+
+ var paragraph = new Paragraph(content);
+ paragraph.SetFont(font);
+ paragraph.SetFontSize(style.Size);
+ if (style.Bold) paragraph.SetBold();
+ if (style.Italic) paragraph.SetItalic();
+
+ var color = ParseColor(style.Color);
+ if (color != null) paragraph.SetFontColor(color);
+
+ // 应用条件颜色规则 | Apply conditional color rules
+ if (child.ColorRules != null && !string.IsNullOrEmpty(content))
+ {
+ foreach (var rule in child.ColorRules)
+ {
+ if (content.Contains(rule.Key, StringComparison.OrdinalIgnoreCase))
+ {
+ var ruleColor = ParseColor(rule.Value);
+ if (ruleColor != null)
+ {
+ paragraph.SetFontColor(ruleColor);
+ paragraph.SetBold();
+ }
+ break;
+ }
+ }
+ }
+
+ paragraph.SetTextAlignment(ParseTextAlignment(childAlign));
+ paragraph.SetMargin(0);
+
+ cell.Add(paragraph);
+ }
+ }
+
+ ///
+ /// 从当前模板中解析样式定义 | Resolve style definition from current template
+ ///
+ private StyleDefinition ResolveStyleFromTemplate(string styleName)
+ {
+ if (string.IsNullOrWhiteSpace(styleName))
+ return new StyleDefinition();
+
+ if (_currentTemplate?.Styles != null
+ && _currentTemplate.Styles.TryGetValue(styleName, out var style))
+ {
+ return style;
+ }
+
+ return new StyleDefinition();
+ }
+
+ #endregion
+
+ #region 7.85 页眉页脚渲染(事件驱动)| Header/Footer rendering (event-driven)
+
+ ///
+ /// 页眉页脚事件处理器 | Header/Footer event handler
+ /// 在 END_PAGE 事件中绘制页眉页脚,使用 PdfFormXObject 占位符实现总页数回填
+ /// Draws header/footer in END_PAGE event, uses PdfFormXObject placeholder for total page count
+ ///
+ private class HeaderFooterEventHandler : iText.Kernel.Events.IEventHandler
+ {
+ private readonly ITextPdfRenderer _renderer;
+ private readonly ReportTemplate _template;
+ private readonly List _pages;
+ private readonly ILoggerService _logger;
+ private readonly HeaderFooterSettings _headerConfig;
+ private readonly HeaderFooterSettings _footerConfig;
+ private readonly MarginSettings _margins;
+ private readonly PdfFont _font;
+
+ // 首页数量 | Homepage count
+ private readonly int _homepageCount;
+
+ // 总页数占位符模板(用于回填)| Total page count placeholder template (for backfill)
+ private readonly iText.Kernel.Pdf.Xobject.PdfFormXObject _totalPagePlaceholder;
+ private readonly List<(iText.Kernel.Pdf.Canvas.PdfCanvas canvas, float x, float y)> _totalPagePositions = new();
+
+ // 当前页面索引(从 0 开始)| Current page index (0-based)
+ private int _currentPageIndex = -1;
+
+ public HeaderFooterEventHandler(
+ ITextPdfRenderer renderer,
+ ReportTemplate template,
+ List pages,
+ ILoggerService logger)
+ {
+ _renderer = renderer;
+ _template = template;
+ _pages = pages;
+ _logger = logger;
+ _headerConfig = template?.Document?.Header;
+ _footerConfig = template?.Document?.Footer;
+ _margins = template?.Document?.Margins ?? new MarginSettings();
+ _font = renderer.GetFontForCurrentLanguage();
+
+ // 计算首页数量 | Calculate homepage count
+ _homepageCount = 0;
+ if (pages != null)
+ {
+ for (int i = 0; i < pages.Count; i++)
+ {
+ if (string.Equals(pages[i].PageType, "homepage", StringComparison.OrdinalIgnoreCase))
+ _homepageCount++;
+ else
+ break;
+ }
+ }
+
+ // 创建总页数占位符(固定宽度区域)| Create total page count placeholder (fixed width area)
+ _totalPagePlaceholder = new iText.Kernel.Pdf.Xobject.PdfFormXObject(new Rectangle(0, 0, 30, 12));
+ }
+
+ public void HandleEvent(iText.Kernel.Events.Event @event)
+ {
+ if (@event is not iText.Kernel.Events.PdfDocumentEvent docEvent) return;
+
+ _currentPageIndex++;
+ var pdfDoc = docEvent.GetDocument();
+ var pdfPage = docEvent.GetPage();
+ var pageSize = pdfPage.GetPageSize();
+
+ // 跳过首页 | Skip homepage
+ if (_currentPageIndex < _homepageCount) return;
+
+ int currentContentPageNum = _currentPageIndex - _homepageCount + 1;
+
+ try
+ {
+ var canvas = new iText.Kernel.Pdf.Canvas.PdfCanvas(pdfPage.NewContentStreamBefore(), pdfPage.GetResources(), pdfDoc);
+
+ // 绘制页眉 | Draw header
+ if (_headerConfig != null && _headerConfig.Enabled)
+ {
+ DrawHeader(canvas, pageSize);
+ }
+
+ // 绘制页脚 | Draw footer
+ if (_footerConfig != null && _footerConfig.Enabled)
+ {
+ DrawFooter(canvas, pageSize, pdfDoc, currentContentPageNum);
+ }
+
+ canvas.Release();
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("页眉页脚绘制异常 | Header/footer drawing exception: {Message}", ex.Message);
+ }
+ }
+
+ ///
+ /// 绘制页眉 | Draw header
+ ///
+ private void DrawHeader(iText.Kernel.Pdf.Canvas.PdfCanvas canvas, Rectangle pageSize)
+ {
+ float leftX = _margins.Left * MmToPoints;
+ float rightX = pageSize.GetWidth() - _margins.Right * MmToPoints;
+ float topY = pageSize.GetHeight() - (_margins.Top * MmToPoints * 0.3f);
+
+ float fontSize = _headerConfig.FontSize > 0 ? _headerConfig.FontSize : 8f;
+ var fontColor = _renderer.ParseColor(_headerConfig.Color ?? "#666666");
+
+ // 绘制左侧文本行 | Draw left-side text lines
+ if (_headerConfig.Left != null && _headerConfig.Left.Count > 0)
+ {
+ float lineY = topY;
+ float lineSpacing = (fontSize + 2f) * 1.2f;
+
+ foreach (var line in _headerConfig.Left)
+ {
+ if (string.IsNullOrEmpty(line)) continue;
+
+ canvas.BeginText()
+ .SetFontAndSize(_font, fontSize)
+ .MoveText(leftX, lineY)
+ .ShowText(line)
+ .EndText();
+
+ lineY -= lineSpacing;
+ }
+ }
+
+ // 绘制右上角 Logo | Draw right-side logo
+ if (!string.IsNullOrEmpty(_headerConfig.RightImageKey))
+ {
+ try
+ {
+ ImageData logoImageData = _renderer.FindBoundImage(_template, _headerConfig.RightImageKey);
+ if (logoImageData != null)
+ {
+ byte[] imageBytes = _renderer.GetImageBytes(logoImageData);
+ if (imageBytes != null && imageBytes.Length > 0)
+ {
+ var iTextImageData = iText.IO.Image.ImageDataFactory.Create(imageBytes);
+ float logoHeight = 10f * MmToPoints;
+ float logoWidth = logoHeight * (iTextImageData.GetWidth() / iTextImageData.GetHeight());
+
+ float logoX = rightX - logoWidth;
+ float logoY = topY - logoHeight + fontSize;
+
+ canvas.AddImageFittedIntoRectangle(iTextImageData,
+ new Rectangle(logoX, logoY, logoWidth, logoHeight), false);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("页眉 Logo 渲染失败 | Header logo rendering failed: {Message}", ex.Message);
+ }
+ }
+
+ // 绘制页眉分隔线 | Draw header separator line
+ if (_headerConfig.ShowLine)
+ {
+ float lineY = topY - (_headerConfig.Left?.Count ?? 1) * ((fontSize + 2f) * 1.2f) - 3f;
+ canvas.SetStrokeColor(fontColor)
+ .SetLineWidth(0.5f)
+ .MoveTo(leftX, lineY)
+ .LineTo(rightX, lineY)
+ .Stroke();
+ }
+ }
+
+ ///
+ /// 绘制页脚 | Draw footer
+ ///
+ private void DrawFooter(iText.Kernel.Pdf.Canvas.PdfCanvas canvas, Rectangle pageSize, PdfDocument pdfDoc, int currentPage)
+ {
+ float leftX = _margins.Left * MmToPoints;
+ float rightX = pageSize.GetWidth() - _margins.Right * MmToPoints;
+ float bottomY = _margins.Bottom * MmToPoints * 0.5f;
+
+ float fontSize = _footerConfig.FontSize > 0 ? _footerConfig.FontSize : 8f;
+ var fontColor = _renderer.ParseColor(_footerConfig.Color ?? "#666666");
+
+ // 绘制页脚分隔线 | Draw footer separator line
+ if (_footerConfig.ShowLine)
+ {
+ float lineY = bottomY + fontSize + 5f;
+ canvas.SetStrokeColor(fontColor)
+ .SetLineWidth(0.5f)
+ .MoveTo(leftX, lineY)
+ .LineTo(rightX, lineY)
+ .Stroke();
+ }
+
+ // 绘制左侧文本(公司名称)| Draw left-side text (company name)
+ if (_footerConfig.Left != null && _footerConfig.Left.Count > 0)
+ {
+ var leftText = _footerConfig.Left[0] ?? string.Empty;
+ canvas.BeginText()
+ .SetFontAndSize(_font, fontSize)
+ .MoveText(leftX, bottomY)
+ .ShowText(leftText)
+ .EndText();
+ }
+
+ // 绘制右侧页码(当前页 / 总页数占位符)| Draw right-side page number (current / total placeholder)
+ if (_footerConfig.Right != null && _footerConfig.Right.Count > 0)
+ {
+ var pageNumTemplate = _footerConfig.Right[0] ?? string.Empty;
+ // 先写当前页码部分 | Write current page number part
+ var currentPageText = pageNumTemplate.Replace("{currentPage}", currentPage.ToString()).Replace("{totalPages}", "");
+ // 分离出 totalPages 前后的文本 | Separate text around totalPages
+ var parts = pageNumTemplate.Split(new[] { "{totalPages}" }, StringSplitOptions.None);
+
+ if (parts.Length == 2)
+ {
+ // 有总页数占位符:写前缀 + 当前页码 + 占位符 XObject + 后缀
+ var prefix = parts[0].Replace("{currentPage}", currentPage.ToString());
+ var suffix = parts[1];
+
+ float prefixWidth = _font.GetWidth(prefix, fontSize);
+ float suffixWidth = _font.GetWidth(suffix, fontSize);
+ float placeholderWidth = 15f; // 预留总页数宽度 | Reserve width for total pages
+
+ float totalWidth = prefixWidth + placeholderWidth + suffixWidth;
+ float startX = rightX - totalWidth;
+
+ // 写前缀文本 | Write prefix text
+ canvas.BeginText()
+ .SetFontAndSize(_font, fontSize)
+ .MoveText(startX, bottomY)
+ .ShowText(prefix)
+ .EndText();
+
+ // 添加总页数占位符 XObject | Add total page count placeholder XObject
+ float placeholderX = startX + prefixWidth;
+ canvas.AddXObjectAt(_totalPagePlaceholder, placeholderX, bottomY - 2f);
+ _totalPagePositions.Add((canvas, placeholderX, bottomY));
+
+ // 写后缀文本 | Write suffix text
+ if (!string.IsNullOrEmpty(suffix))
+ {
+ canvas.BeginText()
+ .SetFontAndSize(_font, fontSize)
+ .MoveText(placeholderX + placeholderWidth, bottomY)
+ .ShowText(suffix)
+ .EndText();
+ }
+ }
+ else
+ {
+ // 无总页数占位符,直接写文本 | No total pages placeholder, write text directly
+ var text = pageNumTemplate.Replace("{currentPage}", currentPage.ToString());
+ float textWidth = _font.GetWidth(text, fontSize);
+ float textX = rightX - textWidth;
+
+ canvas.BeginText()
+ .SetFontAndSize(_font, fontSize)
+ .MoveText(textX, bottomY)
+ .ShowText(text)
+ .EndText();
+ }
+ }
+ }
+
+ ///
+ /// 文档关闭前回填总页数到所有占位符 | Write total page count to all placeholders before document close
+ ///
+ public void WriteTotal(PdfDocument pdfDoc)
+ {
+ int totalContentPages = pdfDoc.GetNumberOfPages() - _homepageCount;
+ var totalText = totalContentPages.ToString();
+
+ // 在占位符 XObject 上绘制总页数 | Draw total page count on placeholder XObject
+ var canvas = new iText.Kernel.Pdf.Canvas.PdfCanvas(_totalPagePlaceholder, pdfDoc);
+ canvas.BeginText()
+ .SetFontAndSize(_font, _footerConfig?.FontSize > 0 ? _footerConfig.FontSize : 8f)
+ .MoveText(0, 2f)
+ .ShowText(totalText)
+ .EndText();
+ canvas.Release();
+ }
+ }
+
+ ///
+ /// 从模板中查找已绑定的图像数据 | Find bound image data from template
+ ///
+ internal ImageData FindBoundImage(ReportTemplate template, string dataKey)
+ {
+ if (template?.Pages == null || string.IsNullOrEmpty(dataKey)) return null;
+
+ foreach (var page in template.Pages)
+ {
+ if (page.Elements == null) continue;
+ foreach (var element in page.Elements)
+ {
+ var found = FindImageInElement(element, dataKey);
+ if (found != null) return found;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// 递归搜索元素及其子元素中的图像数据 | Recursively search for image data in element and its children
+ ///
+ private ImageData FindImageInElement(TemplateElement element, string dataKey)
+ {
+ if (element == null) return null;
+
+ // 检查当前元素 | Check current element
+ if (string.Equals(element.Type, "image", StringComparison.OrdinalIgnoreCase)
+ && string.Equals(element.DataKey, dataKey, StringComparison.OrdinalIgnoreCase)
+ && element.ImageData != null)
+ {
+ return element.ImageData;
+ }
+
+ // 递归搜索子元素 | Recursively search children
+ if (element.Children != null)
+ {
+ foreach (var child in element.Children)
+ {
+ var found = FindImageInElement(child, dataKey);
+ if (found != null) return found;
+ }
+ }
+
+ return null;
+ }
+
+ #endregion
+
+ #region 7.9 PDF 保存到文件 | PDF save to file
+
+ ///
+ /// 将 PDF 内存流保存到文件 | Save PDF memory stream to file
+ ///
+ /// PDF 内存流 | PDF memory stream
+ /// 输出文件路径 | Output file path
+ /// 保存结果(成功/失败)| Save result (success/failure)
+ public ReportResult SaveToFile(MemoryStream pdfStream, string filePath)
+ {
+ if (pdfStream == null)
+ {
+ return ReportResult.Failure("PDF 流为空,无法保存 | PDF stream is null, cannot save");
+ }
+
+ if (string.IsNullOrWhiteSpace(filePath))
+ {
+ return ReportResult.Failure("输出文件路径为空 | Output file path is empty");
+ }
+
+ try
+ {
+ _logger.Info("开始保存 PDF 到文件:{FilePath} | Saving PDF to file: {FilePath}", filePath);
+
+ // 确保目标目录存在 | Ensure target directory exists
+ var directory = System.IO.Path.GetDirectoryName(filePath);
+ if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ // 保存流位置并重置 | Save stream position and reset
+ long originalPosition = pdfStream.Position;
+ pdfStream.Position = 0;
+
+ using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
+ {
+ pdfStream.CopyTo(fileStream);
+ }
+
+ // 恢复流位置 | Restore stream position
+ pdfStream.Position = originalPosition;
+
+ _logger.Info("PDF 文件保存成功:{FilePath} | PDF file saved successfully: {FilePath}", filePath);
+ return ReportResult.Success(pdfStream);
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ _logger.Error(ex, "PDF 保存失败:无写入权限 | PDF save failed: no write permission: {Path}", filePath);
+ return ReportResult.Failure($"无法写入文件,权限不足:{filePath} | Cannot write file, insufficient permissions: {filePath}", ex);
+ }
+ catch (DirectoryNotFoundException ex)
+ {
+ _logger.Error(ex, "PDF 保存失败:目录不存在 | PDF save failed: directory not found: {Path}", filePath);
+ return ReportResult.Failure($"目标目录不存在:{filePath} | Target directory not found: {filePath}", ex);
+ }
+ catch (IOException ex)
+ {
+ _logger.Error(ex, "PDF 保存失败:IO 错误 | PDF save failed: IO error: {Path}", filePath);
+ return ReportResult.Failure($"文件保存 IO 错误:{ex.Message} | File save IO error: {ex.Message}", ex);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "PDF 保存失败:未知错误 | PDF save failed: unknown error: {Path}", filePath);
+ return ReportResult.Failure($"文件保存过程中发生错误:{ex.Message} | Error occurred during file save: {ex.Message}", ex);
+ }
+ }
+
+ #endregion
+
+ #region 辅助方法 | Helper methods
+
+ ///
+ /// 解析十六进制颜色字符串为 iText Color 对象 | Parse hex color string to iText Color object
+ /// 支持格式:#RRGGBB 或 #RGB | Supports formats: #RRGGBB or #RGB
+ ///
+ /// 十六进制颜色字符串 | Hex color string
+ /// iText Color 对象,解析失败返回黑色 | iText Color object, returns black on failure
+ private Color ParseColor(string hexColor)
+ {
+ if (string.IsNullOrEmpty(hexColor))
+ {
+ return ColorConstants.BLACK;
+ }
+
+ try
+ {
+ var hex = hexColor.TrimStart('#');
+
+ if (hex.Length == 3)
+ {
+ // 扩展 #RGB 为 #RRGGBB | Expand #RGB to #RRGGBB
+ hex = $"{hex[0]}{hex[0]}{hex[1]}{hex[1]}{hex[2]}{hex[2]}";
+ }
+
+ if (hex.Length == 6)
+ {
+ int r = Convert.ToInt32(hex.Substring(0, 2), 16);
+ int g = Convert.ToInt32(hex.Substring(2, 2), 16);
+ int b = Convert.ToInt32(hex.Substring(4, 2), 16);
+ return new DeviceRgb(r, g, b);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Warn("颜色解析失败:{Color},使用默认黑色 | Color parsing failed: {Color}, using default black: {Message}", hexColor, ex.Message);
+ }
+
+ return ColorConstants.BLACK;
+ }
+
+ ///
+ /// 解析对齐方式字符串为 iText TextAlignment | Parse alignment string to iText TextAlignment
+ ///
+ /// 对齐方式字符串(left/center/right)| Alignment string
+ /// iText TextAlignment 枚举值 | iText TextAlignment enum value
+ private TextAlignment ParseTextAlignment(string align)
+ {
+ switch (align?.ToLowerInvariant())
+ {
+ case "center":
+ return TextAlignment.CENTER;
+ case "right":
+ return TextAlignment.RIGHT;
+ case "justify":
+ return TextAlignment.JUSTIFIED;
+ case "left":
+ default:
+ return TextAlignment.LEFT;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/XP.ReportEngine/Services/JsonTemplateEngine.cs b/XP.ReportEngine/Services/JsonTemplateEngine.cs
new file mode 100644
index 0000000..d7f9036
--- /dev/null
+++ b/XP.ReportEngine/Services/JsonTemplateEngine.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Newtonsoft.Json;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Services
+{
+ ///
+ /// JSON 模板引擎实现 | JSON template engine implementation
+ /// 负责加载、反序列化和验证 JSON 格式的报告模板
+ /// Responsible for loading, deserializing and validating JSON report templates
+ ///
+ public class JsonTemplateEngine : ITemplateEngine
+ {
+ private readonly ILoggerService _logger;
+
+ ///
+ /// 默认样式定义(当模板引用未定义的样式名称时使用)
+ /// Default style definition (used when template references undefined style name)
+ ///
+ public static readonly StyleDefinition DefaultStyle = new()
+ {
+ Font = null,
+ Size = 12f,
+ Bold = false,
+ Italic = false,
+ Color = "#000000",
+ Align = "left",
+ BackgroundColor = null
+ };
+
+ ///
+ /// 构造函数 | Constructor
+ ///
+ /// 日志服务 | Logger service
+ public JsonTemplateEngine(ILoggerService logger)
+ {
+ _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ ///
+ /// 加载并反序列化 JSON 模板文件 | Load and deserialize JSON template file
+ ///
+ /// 模板文件路径 | Template file path
+ /// 解析后的模板对象,文件不存在时返回 null | Parsed template object, null if file not found
+ public ReportTemplate LoadTemplate(string templatePath)
+ {
+ if (string.IsNullOrWhiteSpace(templatePath))
+ {
+ _logger.Warn("模板文件路径为空 | Template file path is null or empty");
+ return null;
+ }
+
+ if (!File.Exists(templatePath))
+ {
+ _logger.Warn("模板文件未找到: {Path} | Template file not found: {Path}", templatePath);
+ return null;
+ }
+
+ _logger.Info("开始加载模板文件: {Path} | Loading template file: {Path}", templatePath);
+
+ try
+ {
+ var json = File.ReadAllText(templatePath);
+ var template = JsonConvert.DeserializeObject(json);
+
+ _logger.Info("模板文件加载成功 | Template file loaded successfully");
+ return template;
+ }
+ catch (JsonReaderException ex)
+ {
+ // JSON 语法错误,包含错误位置信息 | JSON syntax error with position info
+ var message = $"模板 JSON 语法错误,行 {ex.LineNumber},位置 {ex.LinePosition}: {ex.Message} | " +
+ $"Template JSON syntax error at line {ex.LineNumber}, position {ex.LinePosition}: {ex.Message}";
+ _logger.Error(ex, message);
+ throw new InvalidOperationException(message, ex);
+ }
+ catch (JsonSerializationException ex)
+ {
+ // JSON 反序列化错误 | JSON deserialization error
+ var message = $"模板 JSON 反序列化失败: {ex.Message} | Template JSON deserialization failed: {ex.Message}";
+ _logger.Error(ex, message);
+ throw new InvalidOperationException(message, ex);
+ }
+ }
+
+ ///
+ /// 验证模板结构完整性 | Validate template structure integrity
+ /// 检查 document、pages、styles 必需字段是否存在
+ /// Checks if required fields (document, pages, styles) are present
+ ///
+ /// 待验证的模板 | Template to validate
+ /// 验证结果 | Validation result
+ public TemplateValidationResult Validate(ReportTemplate template)
+ {
+ if (template == null)
+ {
+ return TemplateValidationResult.Invalid("模板对象为 null | Template object is null");
+ }
+
+ var missingFields = new List();
+
+ if (template.Document == null)
+ {
+ missingFields.Add("document");
+ }
+
+ if (template.Pages == null || template.Pages.Count == 0)
+ {
+ missingFields.Add("pages");
+ }
+
+ if (template.Styles == null)
+ {
+ missingFields.Add("styles");
+ }
+
+ if (missingFields.Count > 0)
+ {
+ var fieldList = string.Join(", ", missingFields);
+ var message = $"模板缺少必需字段: {fieldList} | Template missing required fields: {fieldList}";
+ _logger.Warn(message);
+ return TemplateValidationResult.Invalid(message);
+ }
+
+ _logger.Info("模板验证通过 | Template validation passed");
+ return TemplateValidationResult.Valid();
+ }
+
+ ///
+ /// 解析样式名称,未定义时回退为默认样式 | Resolve style name, fallback to default if undefined
+ ///
+ /// 报告模板 | Report template
+ /// 样式名称 | Style name
+ /// 解析后的样式定义 | Resolved style definition
+ public StyleDefinition ResolveStyle(ReportTemplate template, string styleName)
+ {
+ // 样式名称为空时直接返回默认样式 | Return default style if style name is empty
+ if (string.IsNullOrWhiteSpace(styleName))
+ {
+ return DefaultStyle;
+ }
+
+ // 模板或样式字典为空时返回默认样式 | Return default style if template or styles dictionary is null
+ if (template?.Styles == null)
+ {
+ _logger.Warn("模板样式字典为空,使用默认样式: {StyleName} | Template styles dictionary is null, using default style: {StyleName}", styleName);
+ return DefaultStyle;
+ }
+
+ // 查找样式,找到则返回,否则回退为默认样式并记录警告
+ // Look up style, return if found, otherwise fallback to default and log warning
+ if (template.Styles.TryGetValue(styleName, out var style))
+ {
+ return style;
+ }
+
+ _logger.Warn("未定义的样式名称 '{StyleName}',使用默认样式 | Undefined style name '{StyleName}', using default style", styleName);
+ return DefaultStyle;
+ }
+ }
+}
diff --git a/XP.ReportEngine/Services/PageLayoutEngine.cs b/XP.ReportEngine/Services/PageLayoutEngine.cs
new file mode 100644
index 0000000..5bf2d19
--- /dev/null
+++ b/XP.ReportEngine/Services/PageLayoutEngine.cs
@@ -0,0 +1,522 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Services
+{
+ ///
+ /// 页面排版引擎实现 | Page layout engine implementation
+ /// 负责计算页面元素位置、处理分页和自适应布局
+ /// Responsible for calculating element positions, handling pagination and adaptive layout
+ ///
+ public class PageLayoutEngine : ILayoutEngine
+ {
+ private readonly ILoggerService _logger;
+ private readonly JsonTemplateEngine _templateEngine;
+
+ // A4 页面尺寸(mm)| A4 page dimensions (mm)
+ private const float A4Width = 210f;
+ private const float A4Height = 297f;
+
+ // 默认行高估算(mm)| Default row height estimate (mm)
+ private const float DefaultRowHeight = 8f;
+
+ // 默认文本元素高度(mm)| Default text element height (mm)
+ private const float DefaultTextHeight = 10f;
+
+ // 默认分隔线高度(mm)| Default divider height (mm)
+ private const float DefaultDividerHeight = 2f;
+
+ ///
+ /// 构造函数 | Constructor
+ ///
+ /// 日志服务 | Logger service
+ /// 模板引擎(用于样式解析)| Template engine (for style resolution)
+ public PageLayoutEngine(ILoggerService logger, JsonTemplateEngine templateEngine)
+ {
+ _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
+ _templateEngine = templateEngine ?? throw new ArgumentNullException(nameof(templateEngine));
+ }
+
+ ///
+ /// 计算页面布局 | Calculate page layout
+ /// 遍历模板中所有页面和元素,根据定位方式计算最终坐标,处理分页和表格跨页
+ /// Iterates through all pages and elements in template, calculates final coordinates based on positioning mode,
+ /// handles pagination and table page-splitting
+ ///
+ /// 绑定后的模板 | Bound template
+ /// 生成选项 | Generation options
+ /// 排版后的页面列表 | List of laid-out pages
+ public List CalculateLayout(ReportTemplate template, ReportGenerationOptions options)
+ {
+ if (template == null) throw new ArgumentNullException(nameof(template));
+
+ _logger.Info("开始排版计算 | Starting layout calculation");
+
+ var margins = template.Document?.Margins ?? new MarginSettings();
+ var availableWidth = A4Width - margins.Left - margins.Right;
+ var availableHeight = A4Height - margins.Top - margins.Bottom;
+
+ var pages = new List();
+ var currentPageNumber = 1;
+
+ if (template.Pages == null || template.Pages.Count == 0)
+ {
+ _logger.Warn("模板无页面定义 | Template has no page definitions");
+ return pages;
+ }
+
+ foreach (var templatePage in template.Pages)
+ {
+ if (templatePage.Elements == null || templatePage.Elements.Count == 0)
+ {
+ continue;
+ }
+
+ // 分离绝对定位和流式定位元素 | Separate absolute and flow positioned elements
+ var absoluteElements = templatePage.Elements
+ .Where(e => string.Equals(e.Positioning, "absolute", StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ var flowElements = templatePage.Elements
+ .Where(e => string.Equals(e.Positioning, "flow", StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ // 创建当前页面 | Create current page
+ var currentPage = new LayoutPage
+ {
+ PageNumber = currentPageNumber,
+ PageType = templatePage.Type,
+ Elements = new List()
+ };
+ pages.Add(currentPage);
+
+ // 处理绝对定位元素(不参与分页)| Process absolute positioned elements (no pagination)
+ foreach (var element in absoluteElements)
+ {
+ var layoutElement = ProcessAbsoluteElement(element, template, margins);
+ currentPage.Elements.Add(layoutElement);
+ }
+
+ // 处理流式定位元素(参与分页)| Process flow positioned elements (with pagination)
+ var currentY = margins.Top;
+
+ foreach (var element in flowElements)
+ {
+ var elementHeight = CalculateElementHeight(element);
+ var elementWidth = CalculateElementWidth(element, availableWidth);
+
+ // 强制分页元素 | Forced page break element
+ if (string.Equals(element.Type, "pagebreak", StringComparison.OrdinalIgnoreCase))
+ {
+ currentPageNumber++;
+ currentPage = new LayoutPage
+ {
+ PageNumber = currentPageNumber,
+ PageType = templatePage.Type,
+ Elements = new List()
+ };
+ pages.Add(currentPage);
+ currentY = margins.Top;
+ continue;
+ }
+
+ // 检查是否需要分页 | Check if pagination is needed
+ if (element.Type == "table" && element.Columns != null)
+ {
+ // 表格跨页拆分逻辑 | Table page-split logic
+ currentY = ProcessTableWithPageSplit(
+ element, template, margins, availableHeight, availableWidth,
+ currentY, pages, ref currentPage, ref currentPageNumber, templatePage.Type);
+ }
+ else
+ {
+ // 普通元素分页检查 | Normal element pagination check
+ if (currentY + elementHeight > margins.Top + availableHeight)
+ {
+ // 创建新页面 | Create new page
+ currentPageNumber++;
+ currentPage = new LayoutPage
+ {
+ PageNumber = currentPageNumber,
+ PageType = templatePage.Type,
+ Elements = new List()
+ };
+ pages.Add(currentPage);
+ currentY = margins.Top;
+ }
+
+ var layoutElement = CreateFlowLayoutElement(
+ element, template, margins, currentY, elementWidth, elementHeight);
+ currentPage.Elements.Add(layoutElement);
+
+ // 累计 Y 坐标 | Accumulate Y coordinate
+ currentY += elementHeight;
+ }
+ }
+
+ currentPageNumber++;
+ }
+
+ _logger.Info("排版计算完成,共 {PageCount} 页 | Layout calculation completed, {PageCount} pages total", pages.Count);
+ return pages;
+ }
+
+ ///
+ /// 处理绝对定位元素 | Process absolute positioned element
+ /// 元素坐标 = Position + Margins 偏移
+ /// Element coordinates = Position + Margins offset
+ ///
+ private LayoutElement ProcessAbsoluteElement(TemplateElement element, ReportTemplate template, MarginSettings margins)
+ {
+ var x = margins.Left + (element.Position != null && element.Position.Length > 0 ? element.Position[0] : 0f);
+ var y = margins.Top + (element.Position != null && element.Position.Length > 1 ? element.Position[1] : 0f);
+ var width = element.Size != null && element.Size.Length > 0 ? element.Size[0] : 0f;
+ var height = element.Size != null && element.Size.Length > 1 ? element.Size[1] : 0f;
+
+ // 图像等比缩放 | Image proportional scaling
+ if (element.Type == "image" && width > 0 && height > 0)
+ {
+ var scaled = CalculateScaledImageDimensions(width, height, width, height);
+ width = scaled.Width;
+ height = scaled.Height;
+ }
+
+ var resolvedStyle = _templateEngine.ResolveStyle(template, element.Style);
+
+ return new LayoutElement
+ {
+ Source = element,
+ X = x,
+ Y = y,
+ Width = width,
+ Height = height,
+ ResolvedStyle = resolvedStyle,
+ ResolvedContent = element.Content,
+ ResolvedTableData = element.TableData,
+ ResolvedImage = element.ImageData
+ };
+ }
+
+ ///
+ /// 创建流式定位的布局元素 | Create flow positioned layout element
+ ///
+ private LayoutElement CreateFlowLayoutElement(
+ TemplateElement element, ReportTemplate template, MarginSettings margins,
+ float currentY, float width, float height)
+ {
+ var x = margins.Left;
+
+ // 如果元素有 Position 定义,使用 X 偏移 | If element has Position defined, use X offset
+ if (element.Position != null && element.Position.Length > 0)
+ {
+ x = margins.Left + element.Position[0];
+ }
+
+ // 图像等比缩放 | Image proportional scaling
+ if (element.Type == "image")
+ {
+ var targetWidth = width;
+ var targetHeight = height;
+ var imageWidth = element.Size != null && element.Size.Length > 0 ? element.Size[0] : width;
+ var imageHeight = element.Size != null && element.Size.Length > 1 ? element.Size[1] : height;
+
+ if (imageWidth > 0 && imageHeight > 0 && targetWidth > 0 && targetHeight > 0)
+ {
+ var scaled = CalculateScaledImageDimensions(imageWidth, imageHeight, targetWidth, targetHeight);
+ width = scaled.Width;
+ height = scaled.Height;
+ }
+ }
+
+ var resolvedStyle = _templateEngine.ResolveStyle(template, element.Style);
+
+ return new LayoutElement
+ {
+ Source = element,
+ X = x,
+ Y = currentY,
+ Width = width,
+ Height = height,
+ ResolvedStyle = resolvedStyle,
+ ResolvedContent = element.Content,
+ ResolvedTableData = element.TableData,
+ ResolvedImage = element.ImageData
+ };
+ }
+
+ ///
+ /// 处理表格跨页拆分 | Process table with page-split
+ /// 按行高计算剩余空间,超出时拆分到新页面,续页重复表头行
+ /// Calculate remaining space by row height, split to new page when exceeded, repeat header on continuation pages
+ ///
+ /// 处理后的当前 Y 坐标 | Current Y coordinate after processing
+ private float ProcessTableWithPageSplit(
+ TemplateElement element, ReportTemplate template, MarginSettings margins,
+ float availableHeight, float availableWidth,
+ float currentY, List pages,
+ ref LayoutPage currentPage, ref int currentPageNumber, string pageType)
+ {
+ var resolvedStyle = _templateEngine.ResolveStyle(template, element.Style);
+ var tableWidth = element.Size != null && element.Size.Length > 0 ? element.Size[0] : availableWidth;
+
+ // 计算表头高度(1 行)| Calculate header height (1 row)
+ var headerHeight = DefaultRowHeight;
+
+ // 获取表格数据行数 | Get table data row count
+ var tableData = GetTableDataFromElement(element);
+ var totalDataRows = tableData?.Count ?? 0;
+
+ if (totalDataRows == 0)
+ {
+ // 空表格,仅渲染表头 | Empty table, render header only
+ var emptyTableHeight = headerHeight;
+ if (currentY + emptyTableHeight > margins.Top + availableHeight)
+ {
+ currentPageNumber++;
+ currentPage = new LayoutPage
+ {
+ PageNumber = currentPageNumber,
+ PageType = pageType,
+ Elements = new List()
+ };
+ pages.Add(currentPage);
+ currentY = margins.Top;
+ }
+
+ var emptyTableElement = new LayoutElement
+ {
+ Source = element,
+ X = margins.Left,
+ Y = currentY,
+ Width = tableWidth,
+ Height = emptyTableHeight,
+ ResolvedStyle = resolvedStyle,
+ ResolvedContent = element.Content,
+ ResolvedTableData = tableData
+ };
+ currentPage.Elements.Add(emptyTableElement);
+ currentY += emptyTableHeight;
+ return currentY;
+ }
+
+ // 计算当前页面剩余空间 | Calculate remaining space on current page
+ var remainingHeight = (margins.Top + availableHeight) - currentY;
+ var totalTableHeight = headerHeight + (totalDataRows * DefaultRowHeight);
+
+ // 如果整个表格能放下,直接放置 | If entire table fits, place directly
+ if (totalTableHeight <= remainingHeight)
+ {
+ var tableElement = new LayoutElement
+ {
+ Source = element,
+ X = margins.Left,
+ Y = currentY,
+ Width = tableWidth,
+ Height = totalTableHeight,
+ ResolvedStyle = resolvedStyle,
+ ResolvedContent = element.Content,
+ ResolvedTableData = tableData
+ };
+ currentPage.Elements.Add(tableElement);
+ currentY += totalTableHeight;
+ return currentY;
+ }
+
+ // 需要跨页拆分 | Need to split across pages
+ var currentRowIndex = 0;
+
+ while (currentRowIndex < totalDataRows)
+ {
+ // 计算当前页面可容纳的数据行数(需预留表头空间)| Calculate rows that fit on current page (reserve header space)
+ var currentRemainingHeight = (margins.Top + availableHeight) - currentY;
+ var rowsOnCurrentPage = (int)Math.Floor((currentRemainingHeight - headerHeight) / DefaultRowHeight);
+
+ if (rowsOnCurrentPage <= 0)
+ {
+ // 当前页面空间不足以放置表头+至少一行数据,创建新页面
+ // Current page doesn't have space for header + at least one data row, create new page
+ currentPageNumber++;
+ currentPage = new LayoutPage
+ {
+ PageNumber = currentPageNumber,
+ PageType = pageType,
+ Elements = new List()
+ };
+ pages.Add(currentPage);
+ currentY = margins.Top;
+ currentRemainingHeight = availableHeight;
+ rowsOnCurrentPage = (int)Math.Floor((currentRemainingHeight - headerHeight) / DefaultRowHeight);
+ }
+
+ // 确定本页实际放置的行数 | Determine actual rows to place on this page
+ var rowsToPlace = Math.Min(rowsOnCurrentPage, totalDataRows - currentRowIndex);
+ var splitData = tableData.Skip(currentRowIndex).Take(rowsToPlace).ToList();
+ var splitHeight = headerHeight + (rowsToPlace * DefaultRowHeight);
+
+ var splitElement = new LayoutElement
+ {
+ Source = element,
+ X = margins.Left,
+ Y = currentY,
+ Width = tableWidth,
+ Height = splitHeight,
+ ResolvedStyle = resolvedStyle,
+ ResolvedContent = element.Content,
+ ResolvedTableData = splitData
+ };
+ currentPage.Elements.Add(splitElement);
+ currentY += splitHeight;
+ currentRowIndex += rowsToPlace;
+
+ // 如果还有剩余行,创建新页面继续 | If there are remaining rows, create new page to continue
+ if (currentRowIndex < totalDataRows)
+ {
+ currentPageNumber++;
+ currentPage = new LayoutPage
+ {
+ PageNumber = currentPageNumber,
+ PageType = pageType,
+ Elements = new List()
+ };
+ pages.Add(currentPage);
+ currentY = margins.Top;
+ }
+ }
+
+ return currentY;
+ }
+
+ ///
+ /// 计算图像等比缩放尺寸 | Calculate proportionally scaled image dimensions
+ /// 保持宽高比,确保缩放后的宽度和高度均不超过目标区域
+ /// Maintain aspect ratio, ensure scaled width and height don't exceed target area
+ ///
+ /// 原始图像宽度 | Original image width
+ /// 原始图像高度 | Original image height
+ /// 目标区域宽度 | Target area width
+ /// 目标区域高度 | Target area height
+ /// 缩放后的尺寸 | Scaled dimensions
+ public (float Width, float Height) CalculateScaledImageDimensions(
+ float imageWidth, float imageHeight, float targetWidth, float targetHeight)
+ {
+ if (imageWidth <= 0 || imageHeight <= 0 || targetWidth <= 0 || targetHeight <= 0)
+ {
+ return (0f, 0f);
+ }
+
+ // 如果图像已经在目标区域内,无需缩放 | If image already fits, no scaling needed
+ if (imageWidth <= targetWidth && imageHeight <= targetHeight)
+ {
+ return (imageWidth, imageHeight);
+ }
+
+ // 计算宽度和高度的缩放比例,取较小值以确保两个维度都不超出
+ // Calculate scale ratios for width and height, use the smaller one to ensure both dimensions fit
+ var widthRatio = targetWidth / imageWidth;
+ var heightRatio = targetHeight / imageHeight;
+ var scale = Math.Min(widthRatio, heightRatio);
+
+ var scaledWidth = imageWidth * scale;
+ var scaledHeight = imageHeight * scale;
+
+ return (scaledWidth, scaledHeight);
+ }
+
+ ///
+ /// 计算元素高度 | Calculate element height
+ /// 根据元素类型和 Size 定义确定高度
+ /// Determine height based on element type and Size definition
+ ///
+ private float CalculateElementHeight(TemplateElement element)
+ {
+ // 如果有明确的 Size 定义,使用 Size[1] 作为高度 | If Size is defined, use Size[1] as height
+ if (element.Size != null && element.Size.Length > 1 && element.Size[1] > 0)
+ {
+ return element.Size[1];
+ }
+
+ // 根据元素类型使用默认高度 | Use default height based on element type
+ return element.Type?.ToLowerInvariant() switch
+ {
+ "text" => DefaultTextHeight,
+ "divider" => DefaultDividerHeight,
+ "spacer" => element.Size is { Length: >= 2 } ? element.Size[1] : DefaultTextHeight,
+ "row" => CalculateRowHeight(element),
+ "pagebreak" => 0f,
+ "image" => DefaultTextHeight,
+ "table" => CalculateTableHeight(element),
+ _ => DefaultTextHeight
+ };
+ }
+
+ ///
+ /// 计算表格高度 | Calculate table height
+ /// 表头行 + 数据行数 × 默认行高
+ /// Header row + data row count × default row height
+ ///
+ private float CalculateTableHeight(TemplateElement element)
+ {
+ var tableData = GetTableDataFromElement(element);
+ var dataRowCount = tableData?.Count ?? 0;
+ // 表头 1 行 + 数据行 | 1 header row + data rows
+ return DefaultRowHeight + (dataRowCount * DefaultRowHeight);
+ }
+
+ ///
+ /// 计算 Row 容器高度 | Calculate row container height
+ /// 取子元素中最大高度,如果有 Size 定义则优先使用
+ /// Uses max child height, or Size definition if available
+ ///
+ private float CalculateRowHeight(TemplateElement element)
+ {
+ // 如果 row 本身有 Size[1] 定义,直接使用 | If row has Size[1], use it directly
+ if (element.Size != null && element.Size.Length > 1 && element.Size[1] > 0)
+ {
+ return element.Size[1];
+ }
+
+ // 否则取子元素中最大高度 | Otherwise use max child height
+ if (element.Children == null || element.Children.Count == 0)
+ return DefaultTextHeight;
+
+ float maxHeight = 0;
+ foreach (var child in element.Children)
+ {
+ float childHeight = DefaultTextHeight;
+ if (child.Size != null && child.Size.Length > 1 && child.Size[1] > 0)
+ {
+ childHeight = child.Size[1];
+ }
+ if (childHeight > maxHeight) maxHeight = childHeight;
+ }
+ return maxHeight > 0 ? maxHeight : DefaultTextHeight;
+ }
+
+ ///
+ /// 计算元素宽度 | Calculate element width
+ ///
+ private float CalculateElementWidth(TemplateElement element, float availableWidth)
+ {
+ if (element.Size != null && element.Size.Length > 0 && element.Size[0] > 0)
+ {
+ return element.Size[0];
+ }
+ return availableWidth;
+ }
+
+ ///
+ /// 从元素获取表格数据 | Get table data from element
+ /// 表格数据在数据绑定阶段通过 TableData 属性填充
+ /// Table data is populated during data binding phase via TableData property
+ ///
+ private List> GetTableDataFromElement(TemplateElement element)
+ {
+ // 表格数据在数据绑定阶段已填充到 TemplateElement.TableData
+ // Table data is populated during data binding phase into TemplateElement.TableData
+ return element.TableData;
+ }
+ }
+}
diff --git a/XP.ReportEngine/Services/PdfReportGenerator.cs b/XP.ReportEngine/Services/PdfReportGenerator.cs
new file mode 100644
index 0000000..81c31ce
--- /dev/null
+++ b/XP.ReportEngine/Services/PdfReportGenerator.cs
@@ -0,0 +1,134 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Services
+{
+ ///
+ /// PDF 报告生成器实现 | PDF report generator implementation
+ /// 协调管线各阶段:模板加载 → 数据绑定 → 排版 → 渲染 → 保存
+ /// Orchestrates pipeline phases: template loading → data binding → layout → rendering → saving
+ ///
+ public class PdfReportGenerator : IReportGenerator
+ {
+ private readonly ILoggerService _logger;
+ private readonly ITemplateEngine _templateEngine;
+ private readonly IDataBinder _dataBinder;
+ private readonly ILayoutEngine _layoutEngine;
+ private readonly IPdfRenderer _pdfRenderer;
+
+ ///
+ /// 构造函数 | Constructor
+ ///
+ /// 日志服务 | Logger service
+ /// 模板引擎 | Template engine
+ /// 数据绑定器 | Data binder
+ /// 排版引擎 | Layout engine
+ /// PDF 渲染器 | PDF renderer
+ public PdfReportGenerator(
+ ILoggerService logger,
+ ITemplateEngine templateEngine,
+ IDataBinder dataBinder,
+ ILayoutEngine layoutEngine,
+ IPdfRenderer pdfRenderer)
+ {
+ _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
+ _templateEngine = templateEngine ?? throw new ArgumentNullException(nameof(templateEngine));
+ _dataBinder = dataBinder ?? throw new ArgumentNullException(nameof(dataBinder));
+ _layoutEngine = layoutEngine ?? throw new ArgumentNullException(nameof(layoutEngine));
+ _pdfRenderer = pdfRenderer ?? throw new ArgumentNullException(nameof(pdfRenderer));
+ }
+
+ ///
+ /// 异步生成 PDF 报告 | Generate PDF report asynchronously
+ /// 执行完整管线:模板加载 → 验证 → 数据绑定 → 排版计算 → PDF 渲染 → 文件保存(可选)
+ /// Executes full pipeline: template load → validate → data bind → layout → PDF render → save (optional)
+ ///
+ /// 报告上下文数据 | Report context data
+ /// 生成选项 | Generation options
+ /// 生成结果 | Generation result
+ public async Task GenerateAsync(ReportContext context, ReportGenerationOptions options)
+ {
+ try
+ {
+ _logger.Info("报告生成管线开始 | Report generation pipeline started");
+
+ // 阶段 1:加载模板 | Phase 1: Load template
+ _logger.Info("阶段 1:加载模板 | Phase 1: Loading template");
+ var template = _templateEngine.LoadTemplate(options.TemplatePath);
+ if (template == null)
+ {
+ var errorMsg = $"模板文件未找到: {options.TemplatePath}";
+ _logger.Error(null, "模板加载失败: {Path} | Template loading failed: {Path}", options.TemplatePath);
+ return ReportResult.Failure(errorMsg);
+ }
+
+ var validation = _templateEngine.Validate(template);
+ if (!validation.IsValid)
+ {
+ var errorMsg = $"模板验证失败: {validation.ErrorMessage}";
+ _logger.Error(null, "模板验证失败: {Message} | Template validation failed: {Message}", validation.ErrorMessage);
+ return ReportResult.Failure(errorMsg);
+ }
+ _logger.Info("阶段 1 完成:模板加载成功 | Phase 1 completed: Template loaded successfully");
+
+ // 阶段 2:数据绑定 | Phase 2: Data binding
+ _logger.Info("阶段 2:数据绑定 | Phase 2: Data binding");
+ var boundTemplate = _dataBinder.Bind(template, context);
+ _logger.Info("阶段 2 完成:数据绑定成功 | Phase 2 completed: Data binding successful");
+
+ // 阶段 3:排版计算 | Phase 3: Layout calculation
+ _logger.Info("阶段 3:排版计算 | Phase 3: Layout calculation");
+ var pages = _layoutEngine.CalculateLayout(boundTemplate, options);
+ _logger.Info("阶段 3 完成:排版计算成功,共 {PageCount} 页 | Phase 3 completed: Layout calculated, {PageCount} pages", pages.Count);
+
+ // 阶段 4:PDF 渲染 | Phase 4: PDF rendering
+ _logger.Info("阶段 4:PDF 渲染 | Phase 4: PDF rendering");
+ var stream = _pdfRenderer.Render(pages, options, boundTemplate);
+ _logger.Info("阶段 4 完成:PDF 渲染成功 | Phase 4 completed: PDF rendering successful");
+
+ // 阶段 5:保存文件(可选)| Phase 5: Save file (optional)
+ if (!string.IsNullOrEmpty(options.OutputFilePath))
+ {
+ _logger.Info("阶段 5:保存文件 | Phase 5: Saving file");
+ await SaveToFileAsync(stream, options.OutputFilePath);
+ _logger.Info("阶段 5 完成:文件保存成功 {Path} | Phase 5 completed: File saved successfully {Path}", options.OutputFilePath);
+ }
+
+ _logger.Info("报告生成管线完成 | Report generation pipeline completed");
+ return ReportResult.Success(stream);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "报告生成失败 | Report generation failed: {Message}", ex.Message);
+ return ReportResult.Failure($"报告生成过程中发生错误: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// 将 MemoryStream 保存到文件 | Save MemoryStream to file
+ ///
+ /// PDF 内存流 | PDF memory stream
+ /// 输出文件路径 | Output file path
+ private async Task SaveToFileAsync(MemoryStream stream, string filePath)
+ {
+ // 确保输出目录存在 | Ensure output directory exists
+ var directory = Path.GetDirectoryName(filePath);
+ if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ // 重置流位置后写入文件 | Reset stream position before writing to file
+ stream.Position = 0;
+ using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
+ await stream.CopyToAsync(fileStream);
+
+ // 重置流位置以便后续使用 | Reset stream position for subsequent use
+ stream.Position = 0;
+ }
+ }
+}
diff --git a/XP.ReportEngine/Services/ProcessorDataAdapter.cs b/XP.ReportEngine/Services/ProcessorDataAdapter.cs
new file mode 100644
index 0000000..5e794e5
--- /dev/null
+++ b/XP.ReportEngine/Services/ProcessorDataAdapter.cs
@@ -0,0 +1,533 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Services
+{
+ ///
+ /// 处理器数据适配器实现 | Processor data adapter implementation
+ /// 将 XP.ImageProcessing 的 ProcessorOutput 转换为 ReportContext
+ /// Converts XP.ImageProcessing ProcessorOutput to ReportContext
+ ///
+ public class ProcessorDataAdapter : IReportDataAdapter
+ {
+ private readonly ILoggerService _logger;
+
+ // 处理器类型常量 | Processor type constants
+ private const string LineMeasurementProcessor = "LineMeasurementProcessor";
+ private const string BgaVoidRateProcessor = "BgaVoidRateProcessor";
+ private const string VoidMeasurementProcessor = "VoidMeasurementProcessor";
+ private const string FillRateProcessor = "FillRateProcessor";
+
+ public ProcessorDataAdapter(ILoggerService logger)
+ {
+ _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ ///
+ /// 将处理器输出数据适配为报告上下文 | Adapt processor output data to report context
+ ///
+ public ReportContext Adapt(List processorOutputs, ReportMetadata metadata)
+ {
+ if (processorOutputs == null) throw new ArgumentNullException(nameof(processorOutputs));
+ if (metadata == null) throw new ArgumentNullException(nameof(metadata));
+
+ _logger.Info("开始数据适配,处理器数量: {Count} | Starting data adaptation, processor count: {Count}", processorOutputs.Count);
+
+ var context = new ReportContext
+ {
+ Metadata = metadata,
+ ResultGroups = new List(),
+ Images = new Dictionary(),
+ Properties = new Dictionary()
+ };
+
+ // 多处理器输出聚合逻辑:每个 ProcessorOutput 生成一个 InspectionResultGroup | Aggregation: each ProcessorOutput becomes one InspectionResultGroup
+ for (var i = 0; i < processorOutputs.Count; i++)
+ {
+ var output = processorOutputs[i];
+ if (output == null)
+ {
+ _logger.Warn("处理器输出为 null,索引: {Index},已跳过 | Processor output is null at index: {Index}, skipped", i);
+ continue;
+ }
+
+ var group = AdaptProcessorOutput(output, i);
+ context.ResultGroups.Add(group);
+
+ // 将结果数据扁平化到 Properties(供模板 ${key} 表达式绑定)
+ // Flatten result data to Properties (for template ${key} expression binding)
+ if (group.Data != null)
+ {
+ foreach (var kvp in group.Data)
+ {
+ context.Properties[kvp.Key] = kvp.Value;
+ }
+ }
+
+ // 将 Classification 也放入 Properties | Also put Classification into Properties
+ if (!string.IsNullOrEmpty(group.Classification))
+ {
+ context.Properties["classification"] = group.Classification;
+ }
+
+ // 将表格数据以模板期望的 dataKey 存入 Properties
+ // Store table data with template-expected dataKey into Properties
+ if (group.TableRows != null && group.TableRows.Count > 0)
+ {
+ var tableKey = GetTableDataKey(output.ProcessorType);
+ if (!string.IsNullOrEmpty(tableKey))
+ {
+ context.Properties[tableKey] = group.TableRows;
+ }
+ }
+
+ // 关联标注图像(使用模板期望的 dataKey)
+ // Associate annotated image (using template-expected dataKey)
+ if (output.AnnotatedImage != null)
+ {
+ var imageKey = GetImageDataKey(output.ProcessorType);
+ context.Images[imageKey] = output.AnnotatedImage;
+
+ // 同时保留原始键名以兼容其他调用方 | Also keep original key for other callers
+ var originalKey = $"{output.ProcessorType}_{i}_annotated";
+ context.Images[originalKey] = output.AnnotatedImage;
+ }
+ }
+
+ _logger.Info("数据适配完成,生成 {Count} 个结果分组 | Data adaptation completed, generated {Count} result groups", context.ResultGroups.Count);
+ return context;
+ }
+
+ ///
+ /// 根据处理器类型分发适配逻辑 | Dispatch adaptation logic by processor type
+ ///
+ private InspectionResultGroup AdaptProcessorOutput(ProcessorOutput output, int index)
+ {
+ var sourceId = $"{output.ProcessorType}_{index}";
+
+ return output.ProcessorType switch
+ {
+ LineMeasurementProcessor => AdaptLineMeasurement(output, sourceId),
+ BgaVoidRateProcessor => AdaptBgaVoidRate(output, sourceId),
+ VoidMeasurementProcessor => AdaptVoidMeasurement(output, sourceId),
+ FillRateProcessor => AdaptFillRate(output, sourceId),
+ _ => AdaptGeneric(output, sourceId)
+ };
+ }
+
+ #region LineMeasurementProcessor 适配 | LineMeasurementProcessor Adaptation
+
+ ///
+ /// 适配线测量处理器输出 | Adapt line measurement processor output
+ /// 提取 MeasurementType、Point1、Point2、PixelDistance、ActualDistance、Unit、Angle
+ ///
+ private InspectionResultGroup AdaptLineMeasurement(ProcessorOutput output, string sourceId)
+ {
+ _logger.Debug("适配 LineMeasurementProcessor 输出,SourceId: {SourceId} | Adapting LineMeasurementProcessor output, SourceId: {SourceId}", sourceId);
+
+ var data = output.OutputData ?? new Dictionary();
+ var group = new InspectionResultGroup
+ {
+ ProcessorType = LineMeasurementProcessor,
+ SourceId = sourceId,
+ Classification = string.Empty,
+ Data = new Dictionary
+ {
+ ["measurementType"] = GetValueOrDefault(data, "MeasurementType", string.Empty),
+ ["point1"] = GetValueOrDefault