新增条码元素的填充选项,支持无损填满外层容器的配置。更新相关渲染逻辑和接口,提升条码显示的灵活性和用户体验。
This commit is contained in:
@@ -345,8 +345,12 @@ public static class NativePrintRenderService
|
||||
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));
|
||||
// 与后端对齐:优先读元素级 fillCell;兼容旧模板的 style.fillCell。
|
||||
// 均未配置时默认 false(保比例居中)。
|
||||
var fillCellNode = el["fillCell"] ?? el["style"]?["fillCell"];
|
||||
var fillCell = string.Equals(ReadAsString(fillCellNode, "false"), "true", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var inner = BuildBarcodeSvgInner(value, format, displayValue, textAlign, lineWidthPx, barHeightPx, barFontSize);
|
||||
var inner = BuildBarcodeSvgInner(value, format, displayValue, textAlign, lineWidthPx, barHeightPx, barFontSize, fillCell);
|
||||
|
||||
var wrapStyle = "display:flex;align-items:center;justify-content:center;overflow:hidden;";
|
||||
|
||||
@@ -426,38 +430,53 @@ public static class NativePrintRenderService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 把 ZXing 输出的 SVG 转换为"由外层 CSS 控制尺寸 + 保持原始条宽比例"的形式:
|
||||
/// 1) 若原 SVG 无 viewBox,用原 width/height 派生(让 preserveAspectRatio 生效);
|
||||
/// 2) 删除根 svg 节点的 width/height 属性(移交给 CSS);
|
||||
/// 3) 强制 preserveAspectRatio="xMidYMid meet"(与 web 端 jsbarcode 一致);
|
||||
/// 4) 重新挂上 width="100%" height="100%",确保 div 100% 铺满。
|
||||
/// 防护层:若 ZXing 输出的 SVG 已带 viewBox,直接复用;否则从 width/height 推导。
|
||||
/// 把 ZXing 输出的 SVG 转换为"由外层 CSS 控制尺寸 + 可配置铺满策略"的形式:
|
||||
/// 1) <b>强制清除</b>根 svg 节点上原有的 width / height / viewBox / preserveAspectRatio 属性
|
||||
/// (不再依赖 ZXing 自身输出格式,无论它用单引号、双引号、无引号都覆写);
|
||||
/// 2) 用调用方传入的 viewBoxWidth × viewBoxHeight 作为 viewBox,保证容器内
|
||||
/// 条码比例与 BuildBarcodeSvgInner 计算的画布比例一致;
|
||||
/// 3) preserveAspectRatio:fillCell=true → "none"(按容器拉伸铺满,矢量缩放无损);
|
||||
/// fillCell=false → "xMidYMid meet"(保比例居中,与 web jsbarcode 默认一致);
|
||||
/// 4) 重新挂上 width="100%" height="100%",确保外层 div 100% 铺满。
|
||||
/// 这里不再"尝试从原 SVG 派生 viewBox",否则 regex 不匹配 ZXing 实际输出(单引号 /
|
||||
/// 无属性 / 顺序差异)时,viewBox 会被漏掉,导致 SVG 被 CSS 双向拉伸,条码视觉变粗、变高。
|
||||
/// </summary>
|
||||
private static string? NormalizeBarcodeSvg(string svg)
|
||||
private static string? NormalizeBarcodeSvg(string svg, bool fillCell = false,
|
||||
double viewBoxWidth = 0, double viewBoxHeight = 0)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(svg)) return null;
|
||||
var targetAspect = fillCell ? "none" : "xMidYMid meet";
|
||||
return Regex.Replace(svg, @"<svg\b([^>]*)>", m =>
|
||||
{
|
||||
var attrs = m.Groups[1].Value;
|
||||
|
||||
// 取原 width/height 数值(若有),用于派生 viewBox
|
||||
var widthMatch = Regex.Match(attrs, @"\swidth\s*=\s*""([^""]+)""", RegexOptions.IgnoreCase);
|
||||
var heightMatch = Regex.Match(attrs, @"\sheight\s*=\s*""([^""]+)""", RegexOptions.IgnoreCase);
|
||||
var hasViewBox = Regex.IsMatch(attrs, @"\sviewBox\s*=", RegexOptions.IgnoreCase);
|
||||
// 同时兼容单/双引号,彻底清理可能影响最终缩放策略的属性
|
||||
attrs = Regex.Replace(attrs, @"\s(width|height|viewBox|preserveAspectRatio)\s*=\s*(""[^""]*""|'[^']*')",
|
||||
string.Empty, RegexOptions.IgnoreCase);
|
||||
|
||||
if (!hasViewBox && widthMatch.Success && heightMatch.Success
|
||||
&& double.TryParse(Regex.Replace(widthMatch.Groups[1].Value, @"[^\d.]", ""),
|
||||
NumberStyles.Any, CultureInfo.InvariantCulture, out var w) && w > 0
|
||||
&& double.TryParse(Regex.Replace(heightMatch.Groups[1].Value, @"[^\d.]", ""),
|
||||
NumberStyles.Any, CultureInfo.InvariantCulture, out var h) && h > 0)
|
||||
// 用调用方提供的稳定画布比例作为 viewBox;调用方没传时再回退去读原属性
|
||||
var vbW = viewBoxWidth;
|
||||
var vbH = viewBoxHeight;
|
||||
if (vbW <= 0 || vbH <= 0)
|
||||
{
|
||||
attrs += $" viewBox=\"0 0 {w.ToString("0.###", CultureInfo.InvariantCulture)} {h.ToString("0.###", CultureInfo.InvariantCulture)}\"";
|
||||
// 兼容回退路径:从原 svg 标签 attrs 中再读一次 width/height(无 viewBox 时)
|
||||
// 这段在调用方传入了 viewBoxWidth/Height 时不会走,保留兜底防御
|
||||
var widthMatch = Regex.Match(m.Groups[1].Value, @"\swidth\s*=\s*(""[^""]+""|'[^']+')", RegexOptions.IgnoreCase);
|
||||
var heightMatch = Regex.Match(m.Groups[1].Value, @"\sheight\s*=\s*(""[^""]+""|'[^']+')", RegexOptions.IgnoreCase);
|
||||
if (widthMatch.Success && heightMatch.Success)
|
||||
{
|
||||
var ws = widthMatch.Groups[1].Value.Trim('"', '\'');
|
||||
var hs = heightMatch.Groups[1].Value.Trim('"', '\'');
|
||||
double.TryParse(Regex.Replace(ws, @"[^\d.]", ""), NumberStyles.Any, CultureInfo.InvariantCulture, out vbW);
|
||||
double.TryParse(Regex.Replace(hs, @"[^\d.]", ""), NumberStyles.Any, CultureInfo.InvariantCulture, out vbH);
|
||||
}
|
||||
}
|
||||
// 实在拿不到时按 jsbarcode 风格的兜底比例(避免 SVG 被纯拉伸)
|
||||
if (vbW <= 0) vbW = 200;
|
||||
if (vbH <= 0) vbH = 60;
|
||||
|
||||
attrs = Regex.Replace(attrs, @"\s(width|height)\s*=\s*""[^""]*""", string.Empty, RegexOptions.IgnoreCase);
|
||||
if (!Regex.IsMatch(attrs, @"preserveAspectRatio", RegexOptions.IgnoreCase))
|
||||
attrs += " preserveAspectRatio=\"xMidYMid meet\"";
|
||||
return $"<svg width=\"100%\" height=\"100%\"{attrs}>";
|
||||
var vbStr = $"0 0 {vbW.ToString("0.###", CultureInfo.InvariantCulture)} {vbH.ToString("0.###", CultureInfo.InvariantCulture)}";
|
||||
return $"<svg width=\"100%\" height=\"100%\" viewBox=\"{vbStr}\" preserveAspectRatio=\"{targetAspect}\"{attrs}>";
|
||||
}, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
@@ -483,37 +502,47 @@ public static class NativePrintRenderService
|
||||
/// Width/Height,使生成的 SVG 内部 viewBox 比例稳定(与 web 视觉风格对齐),不会随
|
||||
/// 元素框形状(90×60 / 50×30 mm)漂移成 1.5:1。外层 div 用 100%+preserveAspectRatio
|
||||
/// ="xMidYMid meet" 自适应铺满。
|
||||
/// textAlign 控制底部文字对齐:center / left / right / justify(两端),通过 SVG
|
||||
/// 后处理修改 ZXing 输出的 <text> 节点 x/text-anchor 实现,与 web 端逻辑同源。
|
||||
/// textAlign 控制底部文字对齐:center / left / right / justify(两端),
|
||||
/// 文字通过 InjectBarcodeText 向 SVG 底部注入 <text> 元素实现,与 web 端 jsbarcode 行为对齐。
|
||||
/// 注意:ZXing.Net 的 SVG 渲染器不生成人类可读文字(PureBarcode=false 在 SvgRenderer 无效),
|
||||
/// 必须手动注入;同时只将 barHeight 传给 ZXing 渲染条形,避免条形撑满含文字区的总高度。
|
||||
/// </summary>
|
||||
private static string BuildBarcodeSvgInner(string value, BarcodeFormat format, bool displayValue,
|
||||
string? textAlign = null, int lineWidth = 2, int barHeight = 60, int barFontSize = 14)
|
||||
string? textAlign = null, int lineWidth = 2, int barHeight = 60, int barFontSize = 14, bool fillCell = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
try
|
||||
{
|
||||
// moduleCount 仅用于派生稳定的 SVG viewBox 宽度,ZXing 实际按编码后的真实模块数
|
||||
// 1:1 绘制;多给一点宽度不会让条变粗,只会让两侧留白略多。
|
||||
var moduleCount = EstimateBarcodeModuleCount(value, format);
|
||||
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 barOnlyH = Math.Max(10, barHeight); // 只把条高给 ZXing;文字由 InjectBarcodeText 注入
|
||||
|
||||
var writer = new BarcodeWriterSvg
|
||||
{
|
||||
Format = format,
|
||||
Options = new ZXing.Common.EncodingOptions
|
||||
{
|
||||
Width = widthPx,
|
||||
Height = heightPx,
|
||||
Margin = 0,
|
||||
PureBarcode = !displayValue,
|
||||
Width = widthPx,
|
||||
Height = barOnlyH,
|
||||
Margin = 0,
|
||||
PureBarcode = true, // ZXing SVG renderer 不支持文字,统一用 PureBarcode=true
|
||||
},
|
||||
};
|
||||
var svgStr = writer.Write(value)?.Content ?? string.Empty;
|
||||
var normalized = NormalizeBarcodeSvg(svgStr) ?? BuildFallbackBarcodeSvg(value);
|
||||
// 仅当显示文字时执行对齐后处理
|
||||
if (displayValue) normalized = ApplyBarcodeTextAlign(normalized, textAlign);
|
||||
var svgStr = writer.Write(value)?.Content ?? string.Empty;
|
||||
// 关键:把我们提供给 ZXing 的 widthPx × barOnlyH 作为目标 viewBox 强制写入,
|
||||
// 让桌面端 SVG 与 web jsbarcode 同样进入"viewBox + preserveAspectRatio=meet"渲染
|
||||
// 路径,避免某些 ZXing 输出顺序/引号风格让 regex 漏掉 viewBox 派生导致条码被 CSS 双向拉伸。
|
||||
//(注:底部文字区由后续 InjectBarcodeText 再把 viewBox 的高度扩展上去,
|
||||
// 因此此处只用条形高度 barOnlyH。)
|
||||
var normalized = NormalizeBarcodeSvg(svgStr, fillCell, widthPx, barOnlyH)
|
||||
?? BuildFallbackBarcodeSvg(value);
|
||||
// 裁掉 ZXing 可能保留的左右静区,让条码主体宽度更贴近 web 端 jsbarcode 观感。
|
||||
normalized = TightenBarcodeHorizontalViewBox(normalized, widthPx);
|
||||
|
||||
// 文字注入:在 viewBox 底部添加 <text>,扩展 viewBox 高度以容纳文字区
|
||||
if (displayValue)
|
||||
normalized = InjectBarcodeText(normalized, value, barFontSize, textAlign);
|
||||
|
||||
return normalized;
|
||||
}
|
||||
catch
|
||||
@@ -523,45 +552,139 @@ public static class NativePrintRenderService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后处理 ZXing 输出 SVG 中的底部文字 <text> 节点,按 textAlign 修改 x/text-anchor,
|
||||
/// justify(两端对齐)通过 textLength + lengthAdjust=spacing 让浏览器自动拉伸字符间距。
|
||||
/// 宽度从 SVG 自身的 viewBox 第三个分量读取,不依赖 ZXing 的内部缩放策略,保证
|
||||
/// "user units 坐标系内对齐 + 浏览器渲染" 一致。
|
||||
/// 向 ZXing 条码 SVG 底部注入人类可读文字并扩展 viewBox 高度,与 web 端 jsbarcode displayValue 行为对齐。
|
||||
/// textAlign 支持 center / left / right / justify(两端对齐,通过 textLength+lengthAdjust 实现)。
|
||||
/// </summary>
|
||||
private static string ApplyBarcodeTextAlign(string svg, string? textAlign)
|
||||
private static string InjectBarcodeText(string svg, string text, int fontSize, string? textAlign)
|
||||
{
|
||||
if (string.IsNullOrEmpty(svg)) return svg;
|
||||
var align = (textAlign ?? "center").Trim().ToLowerInvariant();
|
||||
if (align is not ("left" or "right" or "justify")) return svg; // center 即 ZXing 默认,不动
|
||||
var (x0, y0, vbWidth, vbHeight) = ParseViewBoxOrDefault(svg);
|
||||
var safeFs = Math.Max(8, fontSize);
|
||||
const double textMargin = 2d; // 对齐 jsbarcode 默认 textMargin
|
||||
var footerH = safeFs + textMargin + 2d; // 预留文字区 + 底部余量,避免裁切
|
||||
var newH = vbHeight + footerH;
|
||||
var wStr = vbWidth.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
var hStr = newH.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
|
||||
// 解析 viewBox 取 user units 宽度,作为对齐坐标参照
|
||||
double vbWidth = 0;
|
||||
var vbMatch = Regex.Match(svg, @"viewBox\s*=\s*""([^""]+)""", RegexOptions.IgnoreCase);
|
||||
if (vbMatch.Success)
|
||||
// 扩展 viewBox 高度(保留原始 x0/y0)
|
||||
svg = Regex.Replace(svg, @"viewBox\s*=\s*""([^""]+)""", m =>
|
||||
{
|
||||
var parts = Regex.Split(vbMatch.Groups[1].Value.Trim(), @"[\s,]+");
|
||||
return $"viewBox=\"{x0.ToString("0.###", CultureInfo.InvariantCulture)} {y0.ToString("0.###", CultureInfo.InvariantCulture)} {wStr} {hStr}\"";
|
||||
}, RegexOptions.IgnoreCase);
|
||||
|
||||
// 计算文字 y 基线(条形底边 + textMargin + fontSize),与 jsbarcode 版式一致
|
||||
var yStr = (y0 + vbHeight + textMargin + safeFs).ToString("0.###", CultureInfo.InvariantCulture);
|
||||
// 关键:文字水平对齐基于“条码墨迹真实范围”而非整块 viewBox。
|
||||
// 否则当 SVG 内部存在左右静区/内缩时,文字会看起来和条码不对齐。
|
||||
var (inkLeft, inkRight) = ResolveBarcodeInkBounds(svg, vbWidth);
|
||||
var inkWidth = Math.Max(1e-6, inkRight - inkLeft);
|
||||
var leftStr = inkLeft.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
var rightStr = inkRight.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
var widthStr = inkWidth.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
var midX = (inkLeft + inkWidth / 2d).ToString("0.###", CultureInfo.InvariantCulture);
|
||||
var align = (textAlign ?? "center").Trim().ToLowerInvariant();
|
||||
var esc = EscapeHtml(text);
|
||||
|
||||
var textElem = align switch
|
||||
{
|
||||
"left" => $"<text x=\"{leftStr}\" y=\"{yStr}\" text-anchor=\"start\" font-size=\"{safeFs}\" font-family=\"monospace\" fill=\"#000000\">{esc}</text>",
|
||||
"right" => $"<text x=\"{rightStr}\" y=\"{yStr}\" text-anchor=\"end\" font-size=\"{safeFs}\" font-family=\"monospace\" fill=\"#000000\">{esc}</text>",
|
||||
"justify" => $"<text x=\"{leftStr}\" y=\"{yStr}\" text-anchor=\"start\" textLength=\"{widthStr}\" lengthAdjust=\"spacing\" font-size=\"{safeFs}\" font-family=\"monospace\" fill=\"#000000\">{esc}</text>",
|
||||
_ => $"<text x=\"{midX}\" y=\"{yStr}\" text-anchor=\"middle\" font-size=\"{safeFs}\" font-family=\"monospace\" fill=\"#000000\">{esc}</text>",
|
||||
};
|
||||
|
||||
// 插入 </svg> 闭合标签之前
|
||||
return Regex.Replace(svg, @"</svg>", $"{textElem}</svg>", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 收紧条码水平 viewBox,裁掉左右静区;仅调整 x/width,不改 y/height。
|
||||
/// </summary>
|
||||
private static string TightenBarcodeHorizontalViewBox(string svg, double fallbackWidth)
|
||||
{
|
||||
var (x0, y0, vbW, vbH) = ParseViewBoxOrDefault(svg, fallbackWidth);
|
||||
var (inkLeft, inkRight) = ResolveBarcodeInkBounds(svg, vbW);
|
||||
var inkW = inkRight - inkLeft;
|
||||
if (inkW <= 1e-6) return svg;
|
||||
|
||||
var newX = x0 + inkLeft;
|
||||
var newViewBox = $"viewBox=\"{newX.ToString("0.###", CultureInfo.InvariantCulture)} {y0.ToString("0.###", CultureInfo.InvariantCulture)} {inkW.ToString("0.###", CultureInfo.InvariantCulture)} {vbH.ToString("0.###", CultureInfo.InvariantCulture)}\"";
|
||||
return Regex.Replace(svg, @"viewBox\s*=\s*""[^""]+""", newViewBox, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
private static (double x0, double y0, double w, double h) ParseViewBoxOrDefault(string svg, double fallbackWidth = 200, double fallbackHeight = 60)
|
||||
{
|
||||
var m = Regex.Match(svg ?? string.Empty, @"viewBox\s*=\s*""([^""]+)""", RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
var parts = Regex.Split(m.Groups[1].Value.Trim(), @"[\s,]+");
|
||||
if (parts.Length >= 4
|
||||
&& double.TryParse(parts[2], NumberStyles.Any, CultureInfo.InvariantCulture, out var w) && w > 0)
|
||||
&& double.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var x0)
|
||||
&& double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var y0)
|
||||
&& double.TryParse(parts[2], NumberStyles.Any, CultureInfo.InvariantCulture, out var w)
|
||||
&& double.TryParse(parts[3], NumberStyles.Any, CultureInfo.InvariantCulture, out var h)
|
||||
&& w > 0 && h > 0)
|
||||
{
|
||||
vbWidth = w;
|
||||
return (x0, y0, w, h);
|
||||
}
|
||||
}
|
||||
if (vbWidth <= 0) return svg; // 拿不到 viewBox 宽度时不动,保持 ZXing 默认居中
|
||||
return (0d, 0d, Math.Max(1d, fallbackWidth), Math.Max(1d, fallbackHeight));
|
||||
}
|
||||
|
||||
return Regex.Replace(svg, @"<text\b([^>]*)>", m =>
|
||||
/// <summary>
|
||||
/// 从 SVG 中提取条码“黑色墨迹”的左右边界。
|
||||
/// 优先扫描 <rect>,忽略白色/透明填充;失败时回退到整块 viewBox。
|
||||
/// </summary>
|
||||
private static (double left, double right) ResolveBarcodeInkBounds(string svg, double fallbackWidth)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(svg))
|
||||
return (0d, Math.Max(1d, fallbackWidth));
|
||||
|
||||
var rects = Regex.Matches(svg, @"<rect\b[^>]*>", RegexOptions.IgnoreCase);
|
||||
double minX = double.PositiveInfinity;
|
||||
double maxX = double.NegativeInfinity;
|
||||
|
||||
foreach (Match m in rects)
|
||||
{
|
||||
var attrs = m.Groups[1].Value;
|
||||
attrs = Regex.Replace(attrs, @"\s(x|text-anchor|textLength|lengthAdjust)\s*=\s*""[^""]*""", string.Empty, RegexOptions.IgnoreCase);
|
||||
var widthStr = vbWidth.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
var tag = m.Value;
|
||||
|
||||
return align switch
|
||||
{
|
||||
"left" => $"<text x=\"0\" text-anchor=\"start\"{attrs}>",
|
||||
"right" => $"<text x=\"{widthStr}\" text-anchor=\"end\"{attrs}>",
|
||||
"justify" => $"<text x=\"0\" text-anchor=\"start\" textLength=\"{widthStr}\" lengthAdjust=\"spacing\"{attrs}>",
|
||||
_ => m.Value,
|
||||
};
|
||||
}, RegexOptions.IgnoreCase);
|
||||
// 跳过明显的白底/透明背景 rect
|
||||
var fill = ReadSvgAttr(tag, "fill")?.Trim().ToLowerInvariant();
|
||||
if (fill is "none" or "transparent" or "#fff" or "#ffffff" or "white")
|
||||
continue;
|
||||
|
||||
var xs = ReadSvgAttr(tag, "x");
|
||||
var ws = ReadSvgAttr(tag, "width");
|
||||
if (!TryParseSvgNumber(xs, out var x) || !TryParseSvgNumber(ws, out var w) || w <= 0)
|
||||
continue;
|
||||
|
||||
if (x < minX) minX = x;
|
||||
if (x + w > maxX) maxX = x + w;
|
||||
}
|
||||
|
||||
if (double.IsInfinity(minX) || double.IsInfinity(maxX) || maxX <= minX)
|
||||
return (0d, Math.Max(1d, fallbackWidth));
|
||||
|
||||
return (minX, maxX);
|
||||
}
|
||||
|
||||
private static string? ReadSvgAttr(string tag, string attr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tag) || string.IsNullOrWhiteSpace(attr))
|
||||
return null;
|
||||
var mm = Regex.Match(tag, $@"\b{Regex.Escape(attr)}\s*=\s*(""([^""]*)""|'([^']*)')", RegexOptions.IgnoreCase);
|
||||
if (!mm.Success) return null;
|
||||
return !string.IsNullOrEmpty(mm.Groups[2].Value) ? mm.Groups[2].Value : mm.Groups[3].Value;
|
||||
}
|
||||
|
||||
private static bool TryParseSvgNumber(string? raw, out double value)
|
||||
{
|
||||
value = 0;
|
||||
if (string.IsNullOrWhiteSpace(raw)) return false;
|
||||
var s = raw.Trim();
|
||||
// 支持 "123", "123.45", "123px";百分比不参与墨迹边界计算
|
||||
if (s.EndsWith("%", StringComparison.Ordinal)) return false;
|
||||
s = Regex.Replace(s, @"[^\d\.\-+eE]", "");
|
||||
return double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1617,9 +1740,9 @@ public static class NativePrintRenderService
|
||||
}
|
||||
if (t == "barcode")
|
||||
{
|
||||
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 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>";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user