增强条码和自由表格元素的渲染逻辑,支持更多边框样式和参数配置,优化打印输出效果。更新相关方法以提升灵活性和用户体验。
This commit is contained in:
@@ -169,6 +169,16 @@ public static class NativePrintRenderService
|
|||||||
var bgCss = bg != null ? $"background:{bg};" : string.Empty;
|
var bgCss = bg != null ? $"background:{bg};" : string.Empty;
|
||||||
var rotCss = rotate != 0 ? $"transform:rotate({rotate}deg);transform-origin:top left;" : 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<double>() ?? 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 位置覆盖 ─
|
// ─ reportHeader/reportFooter 位置覆盖 ─
|
||||||
var isReportHeader = type == "reportHeader";
|
var isReportHeader = type == "reportHeader";
|
||||||
var isReportFooter = type == "reportFooter";
|
var isReportFooter = type == "reportFooter";
|
||||||
@@ -193,7 +203,7 @@ public static class NativePrintRenderService
|
|||||||
$"top:{renderY.ToString("0.###", CultureInfo.InvariantCulture)}mm;" +
|
$"top:{renderY.ToString("0.###", CultureInfo.InvariantCulture)}mm;" +
|
||||||
$"width:{renderW.ToString("0.###", CultureInfo.InvariantCulture)}mm;" +
|
$"width:{renderW.ToString("0.###", CultureInfo.InvariantCulture)}mm;" +
|
||||||
$"height:{h.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
|
return type switch
|
||||||
{
|
{
|
||||||
@@ -332,8 +342,11 @@ public static class NativePrintRenderService
|
|||||||
var format = ParseBarcodeFormat(ReadAsString(el["format"]));
|
var format = ParseBarcodeFormat(ReadAsString(el["format"]));
|
||||||
var displayValue = !string.Equals(ReadAsString(el["displayValue"], "true"), "false", StringComparison.OrdinalIgnoreCase);
|
var displayValue = !string.Equals(ReadAsString(el["displayValue"], "true"), "false", StringComparison.OrdinalIgnoreCase);
|
||||||
var textAlign = ReadAsString(el["textAlign"], "center")!;
|
var textAlign = ReadAsString(el["textAlign"], "center")!;
|
||||||
|
var lineWidthPx = Math.Max(1, (int)Math.Round(el["lineWidth"]?.GetValue<double>() ?? 2d));
|
||||||
|
var barHeightPx = Math.Max(10, (int)Math.Round(el["barHeight"]?.GetValue<double>() ?? 60d));
|
||||||
|
var barFontSize = Math.Max(8, (int)Math.Round(el["fontSize"]?.GetValue<double>() ?? 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;";
|
var wrapStyle = "display:flex;align-items:center;justify-content:center;overflow:hidden;";
|
||||||
|
|
||||||
@@ -461,8 +474,8 @@ public static class NativePrintRenderService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表格单元格用:包装统一的 BuildBarcodeSvgInner,保持与独立 barcode 元素一致的视觉比例。
|
/// 表格单元格用:包装统一的 BuildBarcodeSvgInner,保持与独立 barcode 元素一致的视觉比例。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static string BuildBarcodeCellSvg(string value, string? format = null, bool displayValue = true, string? textAlign = null)
|
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);
|
=> BuildBarcodeSvgInner(value ?? string.Empty, ParseBarcodeFormat(format), displayValue, textAlign, 2, 60, barFontSize);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 调用 ZXing.Net 生成条码 SVG 的统一入口:用与 web 端 jsbarcode 默认参数等价的比例
|
/// 调用 ZXing.Net 生成条码 SVG 的统一入口:用与 web 端 jsbarcode 默认参数等价的比例
|
||||||
@@ -473,7 +486,8 @@ public static class NativePrintRenderService
|
|||||||
/// textAlign 控制底部文字对齐:center / left / right / justify(两端),通过 SVG
|
/// textAlign 控制底部文字对齐:center / left / right / justify(两端),通过 SVG
|
||||||
/// 后处理修改 ZXing 输出的 <text> 节点 x/text-anchor 实现,与 web 端逻辑同源。
|
/// 后处理修改 ZXing 输出的 <text> 节点 x/text-anchor 实现,与 web 端逻辑同源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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;
|
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||||
try
|
try
|
||||||
@@ -481,11 +495,9 @@ public static class NativePrintRenderService
|
|||||||
// moduleCount 仅用于派生稳定的 SVG viewBox 宽度,ZXing 实际按编码后的真实模块数
|
// moduleCount 仅用于派生稳定的 SVG viewBox 宽度,ZXing 实际按编码后的真实模块数
|
||||||
// 1:1 绘制;多给一点宽度不会让条变粗,只会让两侧留白略多。
|
// 1:1 绘制;多给一点宽度不会让条变粗,只会让两侧留白略多。
|
||||||
var moduleCount = EstimateBarcodeModuleCount(value, format);
|
var moduleCount = EstimateBarcodeModuleCount(value, format);
|
||||||
const int lineWidth = 2;
|
var fontFooter = displayValue ? Math.Max(8, barFontSize) + 4 : 0; // fontSize + padding ≈ jsbarcode 行为
|
||||||
const int barHeight = 60;
|
|
||||||
var fontFooter = displayValue ? 18 : 0; // jsbarcode 默认 fontSize 14 + 上下 padding ≈ 18px
|
|
||||||
var widthPx = Math.Max(120, moduleCount * lineWidth);
|
var widthPx = Math.Max(120, moduleCount * lineWidth);
|
||||||
var heightPx = barHeight + fontFooter;
|
var heightPx = Math.Max(10, barHeight) + fontFooter;
|
||||||
|
|
||||||
var writer = new BarcodeWriterSvg
|
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 lineKeys = ResolveFreeTableCellLineStyleKeys(el, cell.Row, cell.Col, rs, cs, rowCount, colCount);
|
||||||
var borderCss = BorderSidesToCssFragment(sides, borderWidth, borderColor, lineKeys);
|
var borderCss = BorderSidesToCssFragment(sides, borderWidth, borderColor, lineKeys);
|
||||||
|
|
||||||
// 跨列宽度
|
// 跨列/跨行高度(用于自适应字号和内边距)
|
||||||
var spanW = 0d;
|
var spanW = 0d;
|
||||||
for (var ci = cell.Col; ci < cell.Col + cs && ci < colWidths.Length; ci++)
|
for (var ci = cell.Col; ci < cell.Col + cs && ci < colWidths.Length; ci++)
|
||||||
spanW += colWidths[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)
|
// 内容渲染(支持所有 contentType)
|
||||||
var innerHtml = RenderFreeTableCellContent(cell, data);
|
var innerHtml = RenderFreeTableCellContent(cell, data);
|
||||||
@@ -678,13 +703,13 @@ public static class NativePrintRenderService
|
|||||||
var ws = nowrap ? "nowrap" : "normal";
|
var ws = nowrap ? "nowrap" : "normal";
|
||||||
var wb = nowrap ? "normal" : "break-all";
|
var wb = nowrap ? "normal" : "break-all";
|
||||||
var ow = nowrap ? "normal" : "anywhere";
|
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 widthCss = $"width:{spanW.ToString("0.###", CultureInfo.InvariantCulture)}mm;";
|
||||||
|
|
||||||
var rsAttr = rs > 1 ? $" rowspan=\"{rs}\"" : string.Empty;
|
var rsAttr = rs > 1 ? $" rowspan=\"{rs}\"" : string.Empty;
|
||||||
var csAttr = cs > 1 ? $" colspan=\"{cs}\"" : string.Empty;
|
var csAttr = cs > 1 ? $" colspan=\"{cs}\"" : string.Empty;
|
||||||
|
|
||||||
sb.Append($"<td{rsAttr}{csAttr} style=\"box-sizing:border-box;{borderCss}{widthCss}padding:2mm;text-align:{cell.Align};vertical-align:{cell.VerticalAlign};font-size:{cell.FontSize.ToString("0.###", CultureInfo.InvariantCulture)}px;color:{cell.Color};background:{cell.BackgroundColor};white-space:{ws};word-break:{wb};overflow-wrap:{ow};line-height:{lh};\">");
|
sb.Append($"<td{rsAttr}{csAttr} style=\"box-sizing:border-box;{borderCss}{widthCss}padding:{vPadMm.ToString("0.###", CultureInfo.InvariantCulture)}mm {hPadMm.ToString("0.###", CultureInfo.InvariantCulture)}mm;text-align:{cell.Align};vertical-align:{cell.VerticalAlign};font-size:{fitFontSize.ToString("0.###", CultureInfo.InvariantCulture)}px;color:{cell.Color};background:{cell.BackgroundColor};white-space:{ws};word-break:{wb};overflow-wrap:{ow};line-height:{lh};\">");
|
||||||
sb.Append(innerHtml);
|
sb.Append(innerHtml);
|
||||||
sb.Append("</td>");
|
sb.Append("</td>");
|
||||||
}
|
}
|
||||||
@@ -720,6 +745,10 @@ public static class NativePrintRenderService
|
|||||||
public int DecimalPlaces { get; init; } = 2;
|
public int DecimalPlaces { get; init; } = 2;
|
||||||
public string? AmountType { get; init; }
|
public string? AmountType { get; init; }
|
||||||
public bool AutoWrap { get; init; } = true;
|
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)
|
private static FreeTableAnchorCell ParseFreeTableCell(JsonObject c)
|
||||||
@@ -756,6 +785,9 @@ public static class NativePrintRenderService
|
|||||||
DecimalPlaces = Math.Clamp(c["decimalPlaces"]?.GetValue<int>() ?? 2, 0, 6),
|
DecimalPlaces = Math.Clamp(c["decimalPlaces"]?.GetValue<int>() ?? 2, 0, 6),
|
||||||
AmountType = amtType,
|
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<double>() ?? 14d)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1031,7 +1063,7 @@ public static class NativePrintRenderService
|
|||||||
{
|
{
|
||||||
var ws = fillCell ? "100%" : $"{scale.ToString("0", CultureInfo.InvariantCulture)}%";
|
var ws = fillCell ? "100%" : $"{scale.ToString("0", CultureInfo.InvariantCulture)}%";
|
||||||
var hs = fillCell ? "100%" : $"{Math.Max(20d, scale * 0.6d).ToString("0", CultureInfo.InvariantCulture)}%";
|
var hs = fillCell ? "100%" : $"{Math.Max(20d, scale * 0.6d).ToString("0", CultureInfo.InvariantCulture)}%";
|
||||||
var svg = BuildBarcodeCellSvg(innerArg);
|
var svg = BuildBarcodeCellSvg(innerArg, cell.BarcodeFormat, cell.DisplayBarcodeValue, null, cell.BarcodeFontSize);
|
||||||
return $"<div style=\"display:flex;align-items:center;justify-content:center;width:{ws};height:{hs};margin:0 auto;overflow:hidden;\">{svg}</div>";
|
return $"<div style=\"display:flex;align-items:center;justify-content:center;width:{ws};height:{hs};margin:0 auto;overflow:hidden;\">{svg}</div>";
|
||||||
}
|
}
|
||||||
return EscapeHtml(displayValue);
|
return EscapeHtml(displayValue);
|
||||||
@@ -1191,7 +1223,7 @@ public static class NativePrintRenderService
|
|||||||
var autoWrap = !string.Equals(ReadAsString(col?["autoWrap"], "true"), "false", StringComparison.OrdinalIgnoreCase);
|
var autoWrap = !string.Equals(ReadAsString(col?["autoWrap"], "true"), "false", StringComparison.OrdinalIgnoreCase);
|
||||||
var raw = !string.IsNullOrWhiteSpace(field) ? ResolveField(row, field!) : null;
|
var raw = !string.IsNullOrWhiteSpace(field) ? ResolveField(row, field!) : null;
|
||||||
var text = FormatColumnValue(raw, col, contentType);
|
var text = FormatColumnValue(raw, col, contentType);
|
||||||
var html = ResolveTableCellInnerHtml(contentType, text);
|
var html = ResolveTableCellInnerHtml(contentType, text, col);
|
||||||
|
|
||||||
// 字体:fontFamily / fontColor / fontSize(含 autoFitFont 自适应)
|
// 字体:fontFamily / fontColor / fontSize(含 autoFitFont 自适应)
|
||||||
var fontFamily = ReadAsString(col?["fontFamily"], "inherit") ?? "inherit";
|
var fontFamily = ReadAsString(col?["fontFamily"], "inherit") ?? "inherit";
|
||||||
@@ -1568,7 +1600,7 @@ public static class NativePrintRenderService
|
|||||||
return new List<JsonObject>();
|
return new List<JsonObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
var t = (contentType ?? "text").Trim().ToLowerInvariant();
|
||||||
if (t == "qrcode")
|
if (t == "qrcode")
|
||||||
@@ -1585,7 +1617,10 @@ public static class NativePrintRenderService
|
|||||||
}
|
}
|
||||||
if (t == "barcode")
|
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<double>() ?? 14d));
|
||||||
|
var svg = BuildBarcodeCellSvg(value, fmt, dv, null, bFontSize);
|
||||||
return $"<div style=\"display:flex;align-items:center;justify-content:center;width:100%;height:100%;overflow:hidden;\">{svg}</div>";
|
return $"<div style=\"display:flex;align-items:center;justify-content:center;width:100%;height:100%;overflow:hidden;\">{svg}</div>";
|
||||||
}
|
}
|
||||||
if (t == "image")
|
if (t == "image")
|
||||||
|
|||||||
Reference in New Issue
Block a user