using System.Globalization; using System.Text.Json; using PdfSharp.Drawing; using PdfSharp.Fonts; using PdfSharp.Pdf; // Réplica de las llamadas FPDF de pdf.php (Letter, mm, márgenes 20/10, Times). // Las fórmulas de posición son las de FPDF: baseline = y + .5*h + .3*tamañoFuente, texto a +1mm del borde de celda. sealed class Fpdf : IDisposable { const double K = 72 / 25.4; // pt por mm readonly PdfDocument doc = new(); XGraphics? g; double x, y, lasth, size = 10; bool bold; const double LMargin = 20, TMargin = 10; static Fpdf() { GlobalFontSettings.UseWindowsFontsUnderWindows = true; } public int PageNo => doc.PageCount; public void AddPage() { g?.Dispose(); var p = doc.AddPage(); p.Width = XUnit.FromMillimeter(215.9); p.Height = XUnit.FromMillimeter(279.4); g = XGraphics.FromPdfPage(p); x = LMargin; y = TMargin; } public double Y => y; public void SetFont(string style, double sz) { bold = style.Contains('B', StringComparison.OrdinalIgnoreCase); underline = style.Contains('U', StringComparison.OrdinalIgnoreCase); size = sz; } bool underline; XFont Font => new("Times New Roman", size, bold ? XFontStyleEx.Bold : XFontStyleEx.Regular); public void Cell(double w, double h, string txt, string align = "L") { txt ??= ""; var font = Font; double tw = txt.Length == 0 ? 0 : g!.MeasureString(txt, font, XStringFormats.Default).Width / K; // mm double dx = align switch { "R" => w - 1 - tw, "C" => (w - tw) / 2, _ => 1 }; double fsMm = size / K; double baseMm = y + .5 * h + .3 * fsMm; if (txt.Length > 0) { g!.DrawString(txt, font, XBrushes.Black, new XPoint((x + dx) * K, baseMm * K), XStringFormats.BaseLineLeft); if (underline) g.DrawRectangle(XBrushes.Black, (x + dx) * K, baseMm * K + 0.1 * size, tw * K, 0.05 * size); } lasth = h; x += w; } public void Ln(double? h = null) { x = LMargin; y += h ?? lasth; } public byte[] Save() { g?.Dispose(); g = null; using var ms = new MemoryStream(); doc.Save(ms, false); return ms.ToArray(); } public void Dispose() { g?.Dispose(); doc.Dispose(); } } static class PdfInforme { const double Ancho = 186; static readonly CultureInfo Inv = CultureInfo.InvariantCulture; // ---- lectura del modelo (JSON del informe) ---- static JsonElement P(JsonElement e, string n) => e.ValueKind == JsonValueKind.Object && e.TryGetProperty(n, out var v) ? v : default; static string Str(JsonElement e, string n) { var v = P(e, n); return v.ValueKind == JsonValueKind.String ? v.GetString()! : v.ValueKind is JsonValueKind.Number ? v.ToString() : ""; } static decimal Num(JsonElement e, string n) { var v = P(e, n); return v.ValueKind == JsonValueKind.Number ? v.GetDecimal() : 0; } static string Gr(decimal v) => v.ToString("0.#", Inv); static string F1(decimal v) => v.ToString("0.0", Inv); // ---- primitivas de fila (n6special, n7, n8, n20) ---- static void N(Fpdf p, string[] datos, double[]? anchos, string[]? bold, double h, double fs, string[]? aligns = null) { for (int i = 0; i < datos.Length; i++) { p.SetFont(bold != null && i < bold.Length ? bold[i] : "", fs); double w = anchos == null ? Ancho / datos.Length : (i < anchos.Length ? Ancho * anchos[i] / 100 : 0); p.Cell(w, h, datos[i], aligns != null && i < aligns.Length ? aligns[i] : "L"); } p.Ln(); } static void N7(Fpdf p, string[] d, string b) => N(p, d, null, Enumerable.Repeat(b, d.Length).ToArray(), 5, 10); static void N8(Fpdf p, string[] d, double[] a, string[] b) => N(p, d, a, b, 4, 10); static void N20(Fpdf p, string[] d, double[] a, string b) => N(p, d, a, Enumerable.Repeat(b, d.Length).ToArray(), 3, 6); static void Titulo1(Fpdf p, string t) { p.SetFont("BU", 10); p.Cell(Ancho, 5, t, "C"); p.Ln(); } static void Linea(Fpdf p) => p.Ln(5); static string[] B(string pattern) => pattern.Select(c => c switch { 'b' => "B", 'B' => "B", 'U' => "BU", _ => "" }).ToArray(); static void Cabecera(Fpdf p, JsonElement m, string titulo) { p.AddPage(); p.SetFont("", 10); N(p, new[] { "Ats Intech", "", "", "", "", " Página N° " + p.PageNo }, null, null, 5, 10, new[] { "L", "L", "L", "L", "L", "R" }); N(p, new[] { "Id Proceso :N°" + Str(m, "idCorto"), "", "", Str(m, "titulo1").Trim() }, null, null, 5, 10, new[] { "L", "L", "L", "R" }); N(p, new[] { "", "", "", Str(m, "titulo2").Trim() }, null, null, 5, 10, new[] { "L", "L", "L", "R" }); Titulo1(p, "INFORME DE FUMIGACIÓN CON BROMURO DE METILO"); Titulo1(p, Str(m, "tipo")); Linea(p); N(p, new[] { titulo }, null, new[] { "B" }, 5, 10); Linea(p); N7(p, new[] { "Proceso:", "Sector:", "F/H Inicio", "F/H Informe" }, "B"); N7(p, new[] { Str(m, "tipo"), Str(m, "camara"), Str(m, "inicio"), Str(m, "informe") }, ""); N7(p, new[] { "Solicitud:" }, "B"); Linea(p); } public static byte[] Generar(JsonElement m) { using var p = new Fpdf(); Cabecera(p, m, "1.- RECORD DE FRUTAS POR PALLET"); // Con.ini / Con.fin foreach (var (lbl, key) in new[] { ("Con.ini:", "conIni"), ("Con.fin:", "conFin") }) { var c = P(m, key); if (c.ValueKind != JsonValueKind.Object) { N8(p, new[] { lbl, "--" }, new double[] { 8, 15 }, B("")); continue; } N8(p, new[] { lbl, $"{Gr(Num(c, "dosis"))} (gr/m3)", "T.Min:", $"{F1(Num(c, "tmin"))}(°C)", "F/H:", Str(c, "fecha"), "Cant inyec:", $"{Gr(Num(c, "kg"))}(Kg)" }, new double[] { 8, 15, 7, 15, 6, 21, 13, 15 }, new[] { "", "", "", "", "", "", "B", "" }); } Linea(p); // Inyección y re-inyección var iny = P(m, "inyeccion"); string Peso(JsonElement arr, int i) { var e = arr.ValueKind == JsonValueKind.Array && i < arr.GetArrayLength() ? arr[i] : default; return e.ValueKind == JsonValueKind.Object ? F1(Num(e, "peso")) : "0"; } decimal PN(JsonElement arr, int i) { var e = arr.ValueKind == JsonValueKind.Array && i < arr.GetArrayLength() ? arr[i] : default; return e.ValueKind == JsonValueKind.Object ? Num(e, "peso") : 0; } string Dif(int i) => Gr(Math.Round(PN(P(iny, "pesoIni"), i) - PN(P(iny, "pesoFin"), i), 1)); string? Nv(string n) { var v = P(iny, n); return v.ValueKind == JsonValueKind.String ? v.GetString() : null; } var aI = new double[] { 18, 15, 17, 15, 12, 21 }; foreach (var (titulo, a, b, ini, fin, tot) in new[] { ("INYECCIÓN", 0, 1, Nv("inicioIny"), Nv("finIny"), Gr(Num(iny, "total"))), ("Re-INYECCIÓN", 2, 3, Nv("inicioRe"), Nv("finRe"), Gr(Num(iny, "totalRe"))) }) { N7(p, new[] { titulo }, "BU"); N8(p, new[] { "Peso Ini Bal. 1", Peso(P(iny, "pesoIni"), a) + " (Kg)", "Peso ini Bal.2", Peso(P(iny, "pesoIni"), b) + " (Kg)", "Inicio Iny.", ini ?? "--" }, aI, B("")); N8(p, new[] { "Peso Fin Bal. 1", Peso(P(iny, "pesoFin"), a) + " (Kg)", "Peso Fin Bal.2", Peso(P(iny, "pesoFin"), b) + " (Kg)", "Fin Iny.", fin ?? "--" }, aI, B("")); N8(p, new[] { "Inyec Bal. 1", Dif(a) + " (Kg)", "Inyec bal.2", Dif(b) + " (Kg)", "Total Iny:", tot + " (Kg)" }, aI, new[] { "", "", "", "", "B", "" }); Linea(p); } N7(p, new[] { "TOTAL INYECTADO" }, "BU"); N8(p, new[] { "Inyeccion:", Gr(Num(iny, "total")) + " (kg)", "Reinyección:", Gr(Num(iny, "totalRe")) + " (Kg)", "Total:", Gr(Num(iny, "granTotal")) + " (Kg)" }, new double[] { 15, 10, 15, 10, 15, 10 }, new[] { "B", "", "B", "", "B", "" }); Linea(p); // Temperaturas iniciales ingresadas por inspector N7(p, new[] { "TEMPERATURAS INICIALES" }, "BU"); var ti = new List { "Ingresadas por insp:" }; var rot = new[] { "Amb:", "Ppa 1:", "Ppa 2:", "Ppa 3:", "Ppa 4:", "Ppa 5:" }; int k = 0; foreach (var e in P(m, "insp").EnumerateArray()) { ti.Add(rot[k++]); var v = P(e, "valor"); ti.Add(v.ValueKind == JsonValueKind.Number ? F1(v.GetDecimal() / 10) + "(°C)" : "NR"); } N8(p, ti.ToArray(), new double[] { 18, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7 }, new[] { "B", "B", "", "B", "", "B", "", "B", "", "B", "", "B", "" }); Linea(p); // Productos var d = P(m, "datos"); N7(p, new[] { "DETALLE DE PRODUCTOS" }, "BU"); var aP = new double[] { 14, 10, 10, 24, 21, 21 }; N8(p, new[] { "Producto", "#Pallets", "#Cajas", "Procedencia", "Origen", "Destino" }, aP, B("")); Linea(p); decimal tp = 0, tc = 0; if (Str(d, "producto1").Trim() != "") { tp = Num(d, "pallets1"); tc = Num(d, "cajas1"); N8(p, new[] { Str(d, "producto1"), Gr(tp), Gr(tc), Str(d, "procedencia1"), Str(d, "origen1"), Str(d, "destino1") }, aP, B("")); } N8(p, new[] { "TOTALES:", Gr(tp), Gr(tc), "", "", "" }, aP, new[] { "B", "", "", "", "", "" }); Linea(p); // Certificados / inspector / observaciones N7(p, new[] { "CERTIFICADOS" }, "BU"); var aC = new double[] { 25, 15, 5, 15 }; N8(p, new[] { "Cert. correlativo #:", Str(d, "certificado1_ini"), "al", Str(d, "certificado1_fin") }, aC, B("")); if (Str(d, "certificado2_ini").Trim() != "") N8(p, new[] { "Cert. correlativo #:", Str(d, "certificado2_ini").Trim(), "al", Str(d, "certificado2_fin").Trim() }, aC, B("")); Linea(p); N8(p, new[] { "INSPECTOR:", Str(d, "inspector"), "OPERADOR :", Str(d, "operador") }, new double[] { 15, 25, 15, 25 }, new[] { "B", "", "B", "" }); Linea(p); N7(p, new[] { "OBSERVACIONES" }, "BU"); Titulo1(p, new string(' ', 91)); Titulo1(p, new string(' ', 91)); // ---- 2.- TEMPERATURAS Y EVENTOS ---- var aT = new double[] { 8, 8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3 }; var hdr = new[] { "Fecha", "Hora", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14", "P15", "A1", "A2", "Iny.", "Vent.", "Dum." }; var unid = new[] { "", "" }.Concat(Enumerable.Repeat("(°C)", 17)).Concat(new[] { "", "", "" }).ToArray(); int contador = 300; foreach (var l in P(m, "lecturas").EnumerateArray()) { if (++contador > 35) { Cabecera(p, m, "2.- TEMPERATURAS Y EVENTOS"); Linea(p); N20(p, hdr, aT, "B"); N20(p, unid, aT, ""); contador = 0; } var row = new List { Str(l, "f"), Str(l, "h") }; foreach (var t in P(l, "temps").EnumerateArray()) { int v = t.GetInt32(); row.Add(v == 555 ? "---" : v == -555 ? "NC" : F1(v / 10m)); } row.Add(Str(l, "valvula")); row.Add(Str(l, "ventilador")); row.Add(Str(l, "dumper")); N20(p, row.ToArray(), aT, ""); } Linea(p); N8(p, new[] { "Temperatura mínima del Proceso :", Str(m, "tminTexto") }, new double[] { 40, 60 }, new[] { "B", "" }); Linea(p); // ---- 3.- REGISTRO DE CONCENTRACIONES ---- int nMed = P(m, "nombresConc").GetArrayLength(); N7(p, new[] { "3.- REGISTRO DE CONCENTRACIONES" }, "B"); Linea(p); double[] aK; string[] hk; switch (nMed) { case 3: hk = new[] { "Fecha", "Hora", "Pta.", "Med.", "Fdo.", "Reg.", "Equ." }; aK = new double[] { 15, 15, 14, 14, 14, 14, 14 }; break; case 4: hk = new[] { "Fecha", "Hora", "Pta.", "Med.", "Fdo.", "Int.Pta.", "Reg.", "Equ." }; aK = new double[] { 15, 15, 11, 11, 11, 11, 11, 11 }; break; default: hk = new[] { "Fecha", "Hora", "Pta.", "Med.", "Fdo.", "Int.Pta.", "Int.Fdo.", "Extra", "Reg.", "Equ." }; aK = new double[] { 10, 9, 9, 9, 9, 9, 9, 9, 10, 10 }; break; } N8(p, hk, aK, Enumerable.Repeat("B", hk.Length).ToArray()); int vcols = hk.Length - 4; // columnas de valores foreach (var c in P(m, "conc").EnumerateArray()) { if (p.Y > 255) { Cabecera(p, m, "3.- REGISTRO DE CONCENTRACIONES (cont.)"); Linea(p); } var row = new List { Str(c, "f"), Str(c, "h") }; var vals = P(c, "valores").EnumerateArray().Select(v => v.GetInt32()).ToList(); for (int i = 0; i < vcols; i++) row.Add(i < vals.Count ? (vals[i] == -555 ? "---" : vals[i].ToString(Inv)) : ""); row.Add(Str(c, "responsable")); row.Add(Str(c, "equipo")); N8(p, row.ToArray(), aK, B("")); } Linea(p); Linea(p); Linea(p); Titulo1(p, new string(' ', 76)); Titulo1(p, "FIRMA SUPERVISOR"); // ---- 4.- ALARMAS EMITIDAS DURANTE EL PROCESO ---- var aA = new double[] { 15, 15, 70 }; contador = 300; foreach (var a in P(m, "alarmas").EnumerateArray()) { if (++contador > 35) { Cabecera(p, m, "4.- ALARMAS EMITIDAS DURANTE EL PROCESO"); Linea(p); N8(p, new[] { "Fecha", "Hora", "Mensaje" }, aA, B("BBB")); contador = 0; } N8(p, new[] { Str(a, "f"), Str(a, "h"), Str(a, "msg") }, aA, B("")); } Linea(p); Linea(p); Linea(p); Linea(p); Titulo1(p, new string(' ', 69)); Titulo1(p, "FIRMA INSPECTOR"); N7(p, new[] { new string(' ', 69), "", new string(' ', 69) }, "U"); N7(p, new[] { " TIMBRE VALIDACION ", "", " FIRMA Y TIMBRE SUPERVISOR " }, ""); return p.Save(); } }