From b6e468abfa8a2f57d19cb910321effdc9f033083 Mon Sep 17 00:00:00 2001 From: geht <2947093423@qq.com> Date: Wed, 13 May 2026 13:04:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E6=9D=A1=E7=A0=81=E5=92=8C?= =?UTF-8?q?=E8=87=AA=E7=94=B1=E8=A1=A8=E6=A0=BC=E5=85=83=E7=B4=A0=E7=9A=84?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E9=80=BB=E8=BE=91=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E8=BE=B9=E6=A1=86=E6=A0=B7=E5=BC=8F=E5=92=8C?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E8=BE=93=E5=87=BA=E6=95=88=E6=9E=9C=E3=80=82?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E6=96=B9=E6=B3=95=E4=BB=A5?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E7=81=B5=E6=B4=BB=E6=80=A7=E5=92=8C=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/Print/NativePrintRenderService.cs | 87 +++++++++++++------ 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/yy-admin-master/YY.Admin.Services/Service/Print/NativePrintRenderService.cs b/yy-admin-master/YY.Admin.Services/Service/Print/NativePrintRenderService.cs index 8a1b652..99dffbc 100644 --- a/yy-admin-master/YY.Admin.Services/Service/Print/NativePrintRenderService.cs +++ b/yy-admin-master/YY.Admin.Services/Service/Print/NativePrintRenderService.cs @@ -169,6 +169,16 @@ public static class NativePrintRenderService var bgCss = bg != null ? $"background:{bg};" : string.Empty; var rotCss = rotate != 0 ? $"transform:rotate({rotate}deg);transform-origin:top left;" : string.Empty; + // ─ 元素级边框(title/subtitle/text/date/pageNo/reportHeader/reportFooter 均支持)─ + var bWidth = Math.Max(0d, style?["borderWidth"]?.GetValue() ?? 0d); + var bColor = ReadAsString(style?["borderColor"], "#222") ?? "#222"; + string ElemBorderSide(string hideKey) => + bWidth > 0 && !ReadBoolDefault(style?[hideKey], false) + ? $"{(int)Math.Round(bWidth)}px solid {bColor}" : "none"; + var elemBorderCss = bWidth > 0 + ? $"border-top:{ElemBorderSide("hideBorderTop")};border-right:{ElemBorderSide("hideBorderRight")};border-bottom:{ElemBorderSide("hideBorderBottom")};border-left:{ElemBorderSide("hideBorderLeft")};" + : string.Empty; + // ─ reportHeader/reportFooter 位置覆盖 ─ var isReportHeader = type == "reportHeader"; var isReportFooter = type == "reportFooter"; @@ -193,7 +203,7 @@ public static class NativePrintRenderService $"top:{renderY.ToString("0.###", CultureInfo.InvariantCulture)}mm;" + $"width:{renderW.ToString("0.###", CultureInfo.InvariantCulture)}mm;" + $"height:{h.ToString("0.###", CultureInfo.InvariantCulture)}mm;" + - $"z-index:{zIndex};{bgCss}{rotCss}"; + $"z-index:{zIndex};{bgCss}{rotCss}{elemBorderCss}"; return type switch { @@ -332,8 +342,11 @@ public static class NativePrintRenderService var format = ParseBarcodeFormat(ReadAsString(el["format"])); var displayValue = !string.Equals(ReadAsString(el["displayValue"], "true"), "false", StringComparison.OrdinalIgnoreCase); var textAlign = ReadAsString(el["textAlign"], "center")!; + var lineWidthPx = Math.Max(1, (int)Math.Round(el["lineWidth"]?.GetValue() ?? 2d)); + var barHeightPx = Math.Max(10, (int)Math.Round(el["barHeight"]?.GetValue() ?? 60d)); + var barFontSize = Math.Max(8, (int)Math.Round(el["fontSize"]?.GetValue() ?? 14d)); - var inner = BuildBarcodeSvgInner(value, format, displayValue, textAlign); + var inner = BuildBarcodeSvgInner(value, format, displayValue, textAlign, lineWidthPx, barHeightPx, barFontSize); var wrapStyle = "display:flex;align-items:center;justify-content:center;overflow:hidden;"; @@ -461,8 +474,8 @@ public static class NativePrintRenderService /// /// 表格单元格用:包装统一的 BuildBarcodeSvgInner,保持与独立 barcode 元素一致的视觉比例。 /// - private static string BuildBarcodeCellSvg(string value, string? format = null, bool displayValue = true, string? textAlign = null) - => BuildBarcodeSvgInner(value ?? string.Empty, ParseBarcodeFormat(format), displayValue, textAlign); + private static string BuildBarcodeCellSvg(string value, string? format = null, bool displayValue = true, string? textAlign = null, int barFontSize = 14) + => BuildBarcodeSvgInner(value ?? string.Empty, ParseBarcodeFormat(format), displayValue, textAlign, 2, 60, barFontSize); /// /// 调用 ZXing.Net 生成条码 SVG 的统一入口:用与 web 端 jsbarcode 默认参数等价的比例 @@ -473,7 +486,8 @@ public static class NativePrintRenderService /// textAlign 控制底部文字对齐:center / left / right / justify(两端),通过 SVG /// 后处理修改 ZXing 输出的 <text> 节点 x/text-anchor 实现,与 web 端逻辑同源。 /// - private static string BuildBarcodeSvgInner(string value, BarcodeFormat format, bool displayValue, string? textAlign = null) + private static string BuildBarcodeSvgInner(string value, BarcodeFormat format, bool displayValue, + string? textAlign = null, int lineWidth = 2, int barHeight = 60, int barFontSize = 14) { if (string.IsNullOrWhiteSpace(value)) return string.Empty; try @@ -481,11 +495,9 @@ public static class NativePrintRenderService // moduleCount 仅用于派生稳定的 SVG viewBox 宽度,ZXing 实际按编码后的真实模块数 // 1:1 绘制;多给一点宽度不会让条变粗,只会让两侧留白略多。 var moduleCount = EstimateBarcodeModuleCount(value, format); - const int lineWidth = 2; - const int barHeight = 60; - var fontFooter = displayValue ? 18 : 0; // jsbarcode 默认 fontSize 14 + 上下 padding ≈ 18px - var widthPx = Math.Max(120, moduleCount * lineWidth); - var heightPx = barHeight + fontFooter; + var fontFooter = displayValue ? Math.Max(8, barFontSize) + 4 : 0; // fontSize + padding ≈ jsbarcode 行为 + var widthPx = Math.Max(120, moduleCount * lineWidth); + var heightPx = Math.Max(10, barHeight) + fontFooter; var writer = new BarcodeWriterSvg { @@ -665,10 +677,23 @@ public static class NativePrintRenderService var lineKeys = ResolveFreeTableCellLineStyleKeys(el, cell.Row, cell.Col, rs, cs, rowCount, colCount); var borderCss = BorderSidesToCssFragment(sides, borderWidth, borderColor, lineKeys); - // 跨列宽度 + // 跨列/跨行高度(用于自适应字号和内边距) var spanW = 0d; for (var ci = cell.Col; ci < cell.Col + cs && ci < colWidths.Length; ci++) spanW += colWidths[ci]; + var spanH = 0d; + for (var ri = cell.Row; ri < cell.Row + rs && ri < rowHeights.Length; ri++) + spanH += rowHeights[ri]; + + // 随行高自适应内边距(行越密 padding 越小,防止把行撑高导致底部被裁切) + const double pxPerMm96 = 96d / 25.4d; + var vPadMm = Math.Max(0.15d, Math.Min(0.8d, spanH * 0.08d)); + var hPadMm = Math.Max(0.3d, Math.Min(1.2d, vPadMm * 1.6d)); + + // 随行高自动收缩字号(与前端 renderFreeTable fitFontSize 逻辑一致) + var innerHmm = Math.Max(0.1d, spanH - vPadMm * 2d); + var innerHpx = innerHmm * pxPerMm96; + var fitFontSize = Math.Max(1d, Math.Min(cell.FontSize, Math.Floor(innerHpx * 0.82d))); // 内容渲染(支持所有 contentType) var innerHtml = RenderFreeTableCellContent(cell, data); @@ -678,13 +703,13 @@ public static class NativePrintRenderService var ws = nowrap ? "nowrap" : "normal"; var wb = nowrap ? "normal" : "break-all"; var ow = nowrap ? "normal" : "anywhere"; - var lh = nowrap ? $"{rh.ToString("0.###", CultureInfo.InvariantCulture)}mm" : "1.3"; + var lh = nowrap ? $"{innerHmm.ToString("0.###", CultureInfo.InvariantCulture)}mm" : "1.15"; var widthCss = $"width:{spanW.ToString("0.###", CultureInfo.InvariantCulture)}mm;"; var rsAttr = rs > 1 ? $" rowspan=\"{rs}\"" : string.Empty; var csAttr = cs > 1 ? $" colspan=\"{cs}\"" : string.Empty; - sb.Append($""); + sb.Append($""); sb.Append(innerHtml); sb.Append(""); } @@ -714,12 +739,16 @@ public static class NativePrintRenderService public bool HideBorderRight { get; init; } public bool HideBorderBottom { get; init; } public bool HideBorderLeft { get; init; } - public bool? FillCell { get; init; } // null → true for media content - public double ContentScale { get; init; } = 100d; - public string? ImageFit { get; init; } - public int DecimalPlaces { get; init; } = 2; - public string? AmountType { get; init; } - public bool AutoWrap { get; init; } = true; + public bool? FillCell { get; init; } // null → true for media content + public double ContentScale { get; init; } = 100d; + public string? ImageFit { get; init; } + public int DecimalPlaces { get; init; } = 2; + public string? AmountType { get; init; } + public bool AutoWrap { get; init; } = true; + // 条码专属 + public string? BarcodeFormat { get; init; } + public bool DisplayBarcodeValue { get; init; } = true; + public int BarcodeFontSize { get; init; } = 14; } private static FreeTableAnchorCell ParseFreeTableCell(JsonObject c) @@ -755,7 +784,10 @@ public static class NativePrintRenderService ImageFit = imgFit, DecimalPlaces = Math.Clamp(c["decimalPlaces"]?.GetValue() ?? 2, 0, 6), AmountType = amtType, - AutoWrap = !string.Equals(ReadAsString(c["autoWrap"]), "false", StringComparison.OrdinalIgnoreCase), + AutoWrap = !string.Equals(ReadAsString(c["autoWrap"]), "false", StringComparison.OrdinalIgnoreCase), + BarcodeFormat = ReadAsString(c["barcodeFormat"]), + DisplayBarcodeValue = !string.Equals(ReadAsString(c["displayValue"]), "false", StringComparison.OrdinalIgnoreCase), + BarcodeFontSize = Math.Max(8, (int)Math.Round(c["barcodeFontSize"]?.GetValue() ?? 14d)), }; } @@ -1029,9 +1061,9 @@ public static class NativePrintRenderService } if (ct == "barcode") { - var ws = fillCell ? "100%" : $"{scale.ToString("0", CultureInfo.InvariantCulture)}%"; - var hs = fillCell ? "100%" : $"{Math.Max(20d, scale * 0.6d).ToString("0", CultureInfo.InvariantCulture)}%"; - var svg = BuildBarcodeCellSvg(innerArg); + var ws = fillCell ? "100%" : $"{scale.ToString("0", CultureInfo.InvariantCulture)}%"; + var hs = fillCell ? "100%" : $"{Math.Max(20d, scale * 0.6d).ToString("0", CultureInfo.InvariantCulture)}%"; + var svg = BuildBarcodeCellSvg(innerArg, cell.BarcodeFormat, cell.DisplayBarcodeValue, null, cell.BarcodeFontSize); return $"
{svg}
"; } return EscapeHtml(displayValue); @@ -1191,7 +1223,7 @@ public static class NativePrintRenderService var autoWrap = !string.Equals(ReadAsString(col?["autoWrap"], "true"), "false", StringComparison.OrdinalIgnoreCase); var raw = !string.IsNullOrWhiteSpace(field) ? ResolveField(row, field!) : null; var text = FormatColumnValue(raw, col, contentType); - var html = ResolveTableCellInnerHtml(contentType, text); + var html = ResolveTableCellInnerHtml(contentType, text, col); // 字体:fontFamily / fontColor / fontSize(含 autoFitFont 自适应) var fontFamily = ReadAsString(col?["fontFamily"], "inherit") ?? "inherit"; @@ -1568,7 +1600,7 @@ public static class NativePrintRenderService return new List(); } - private static string ResolveTableCellInnerHtml(string? contentType, string value) + private static string ResolveTableCellInnerHtml(string? contentType, string value, JsonNode? col = null) { var t = (contentType ?? "text").Trim().ToLowerInvariant(); if (t == "qrcode") @@ -1585,7 +1617,10 @@ public static class NativePrintRenderService } if (t == "barcode") { - var svg = BuildBarcodeCellSvg(value); + var fmt = ReadAsString(col?["barcodeFormat"]); + var dv = !string.Equals(ReadAsString(col?["displayValue"]), "false", StringComparison.OrdinalIgnoreCase); + var bFontSize = Math.Max(8, (int)Math.Round(col?["barcodeFontSize"]?.GetValue() ?? 14d)); + var svg = BuildBarcodeCellSvg(value, fmt, dv, null, bFontSize); return $"
{svg}
"; } if (t == "image")