mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-07-08 18:16:08 +00:00
CJK, kana, hangul, fullwidth forms and emoji render at roughly twice the digit width (15.25px regular / 15.75px bold, measured against Excel), so the ASCII fallback under-sized such columns by half. Runs over runes so surrogate pairs count as one glyph.
107 lines
3.9 KiB
C#
107 lines
3.9 KiB
C#
namespace Radzen.Documents.Spreadsheet;
|
|
|
|
#nullable enable
|
|
|
|
// Per-character advance widths of Aptos Narrow 11 (the Normal font XlsxWriter emits) at 96dpi
|
|
// for ASCII 32..126, measured against Excel. Used to size auto fitted columns on export so
|
|
// their content renders without clipping in Excel regardless of the browser font metrics.
|
|
internal static class ExcelTextMetrics
|
|
{
|
|
private static readonly double[] Regular =
|
|
[
|
|
2.54, 4.57, 5.08, 7.11, 7.62, 11.18, 8.63, 3.04, 4.57, 4.57, 7.11, 7.62,
|
|
4.06, 4.57, 4.06, 4.57, 7.62, 7.62, 7.62, 7.62, 7.62, 7.62, 7.62, 7.62,
|
|
7.62, 7.62, 4.06, 4.06, 7.62, 7.62, 7.62, 6.60, 11.69, 7.62, 8.12, 9.14,
|
|
9.14, 7.62, 7.11, 9.65, 9.65, 3.55, 4.57, 7.62, 6.60, 10.67, 9.65, 9.65,
|
|
7.62, 9.65, 8.12, 7.62, 6.60, 9.14, 7.62, 12.20, 7.62, 7.11, 7.11, 4.06,
|
|
4.57, 4.06, 7.62, 6.09, 7.11, 7.11, 7.62, 7.11, 7.62, 7.11, 4.06, 6.60,
|
|
7.62, 3.55, 3.55, 6.60, 3.55, 11.69, 7.62, 7.62, 7.62, 7.62, 4.57, 6.60,
|
|
4.57, 7.62, 6.09, 9.65, 6.09, 6.09, 6.09, 4.06, 6.60, 4.06, 7.62
|
|
];
|
|
|
|
private static readonly double[] Bold =
|
|
[
|
|
2.54, 4.57, 5.58, 7.11, 7.62, 11.18, 9.14, 3.04, 4.57, 4.57, 7.11, 7.62,
|
|
4.06, 4.57, 4.06, 5.08, 7.62, 7.62, 7.62, 7.62, 7.62, 7.62, 7.62, 7.62,
|
|
7.62, 7.62, 4.06, 4.06, 7.62, 7.62, 7.62, 6.60, 11.18, 8.12, 8.12, 9.14,
|
|
9.14, 7.62, 7.11, 9.65, 9.65, 4.06, 5.08, 8.12, 6.60, 10.67, 9.65, 9.65,
|
|
8.12, 9.65, 8.63, 8.12, 6.60, 9.14, 8.12, 12.20, 8.12, 7.62, 7.11, 4.57,
|
|
5.08, 4.57, 7.62, 6.09, 7.11, 7.11, 7.62, 7.11, 7.62, 7.62, 4.57, 6.60,
|
|
7.62, 3.55, 3.55, 7.11, 4.06, 11.69, 7.62, 7.62, 7.62, 7.62, 5.08, 6.60,
|
|
4.57, 7.62, 6.60, 10.16, 6.60, 6.60, 6.09, 4.57, 7.11, 4.57, 7.62
|
|
];
|
|
|
|
// East Asian wide and fullwidth glyphs, measured the same way.
|
|
private const double WideRegular = 15.25;
|
|
private const double WideBold = 15.75;
|
|
|
|
public static double EstimateWidth(string text, bool bold, double? fontSize)
|
|
{
|
|
var widths = bold ? Bold : Regular;
|
|
var total = 0d;
|
|
|
|
foreach (var rune in text.EnumerateRunes())
|
|
{
|
|
if (rune.Value is >= ' ' and <= '~')
|
|
{
|
|
total += widths[rune.Value - ' '];
|
|
}
|
|
else if (IsWide(rune.Value))
|
|
{
|
|
total += bold ? WideBold : WideRegular;
|
|
}
|
|
else
|
|
{
|
|
total += widths['0' - ' '];
|
|
}
|
|
}
|
|
|
|
return total * (fontSize ?? 11) / 11;
|
|
}
|
|
|
|
// Unicode East Asian Width wide/fullwidth blocks (CJK, kana, hangul, fullwidth forms, emoji).
|
|
private static bool IsWide(int codePoint) => codePoint is
|
|
(>= 0x1100 and <= 0x115F) or
|
|
(>= 0x2E80 and <= 0x303E) or
|
|
(>= 0x3041 and <= 0x33FF) or
|
|
(>= 0x3400 and <= 0x4DBF) or
|
|
(>= 0x4E00 and <= 0x9FFF) or
|
|
(>= 0xA000 and <= 0xA4CF) or
|
|
(>= 0xAC00 and <= 0xD7A3) or
|
|
(>= 0xF900 and <= 0xFAFF) or
|
|
(>= 0xFE30 and <= 0xFE4F) or
|
|
(>= 0xFF00 and <= 0xFF60) or
|
|
(>= 0xFFE0 and <= 0xFFE6) or
|
|
(>= 0x1F300 and <= 0x1FAFF) or
|
|
(>= 0x20000 and <= 0x3FFFD);
|
|
|
|
// The single line that determines a cell's width: wrapped cells render hard line breaks so
|
|
// the longest line governs; non-wrapped cells render with white-space: nowrap which collapses them.
|
|
public static string DisplayLine(string text, bool wrapText)
|
|
{
|
|
if (!text.Contains('\n', System.StringComparison.Ordinal) && !text.Contains('\r', System.StringComparison.Ordinal))
|
|
{
|
|
return text;
|
|
}
|
|
|
|
text = text.Replace("\r", "", System.StringComparison.Ordinal);
|
|
|
|
if (!wrapText)
|
|
{
|
|
return text.Replace('\n', ' ');
|
|
}
|
|
|
|
var longest = "";
|
|
|
|
foreach (var line in text.Split('\n'))
|
|
{
|
|
if (line.Length > longest.Length)
|
|
{
|
|
longest = line;
|
|
}
|
|
}
|
|
|
|
return longest;
|
|
}
|
|
}
|