Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 70 additions & 10 deletions Demos/Reports/QR-Codes.frx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion FastReport.Base/Data/JsonConnection/JsonTableDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ internal static void InitSchema(Column table, JsonSchema schema, bool simpleStru
else if (kv.Value.Type == "array")
{
Column c = new JsonTableDataSource();
c.Name = kv.Key;
c.Name = table.Report?.Dictionary?.CreateUniqueName(kv.Key) ?? kv.Key;
c.Alias = kv.Key;
c.PropName = kv.Key;
c.DataType = kv.Value.DataType;
Expand Down
20 changes: 19 additions & 1 deletion FastReport.Base/Export/Html/HTMLExport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ public bool Layers
set { layers = value; }
}

/// <summary>
/// For internal use only.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public string PrintScriptSrc { get; set; }

/// <summary>
/// For internal use only.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool DisableInlineScript { get; set; }

/// <summary>
/// For internal use only.
/// </summary>
Expand Down Expand Up @@ -618,7 +630,13 @@ private void DoPageStart(Stream stream, string documentTitle)
private void DoPageEnd(Stream stream, bool print)
{
if (print)
ExportUtils.WriteLn(stream, PRINT_JS);
{
if (string.IsNullOrEmpty(PrintScriptSrc))
ExportUtils.WriteLn(stream, PRINT_JS);
else
ExportUtils.WriteLn(stream, $@"<script type=""module"" src=""{PrintScriptSrc}""></script>");
}

ExportUtils.WriteLn(stream, BODY_END);
ExportUtils.Write(stream, templates.PageTemplateFooter);
}
Expand Down
18 changes: 8 additions & 10 deletions FastReport.Base/Export/Html/HTMLExportLayers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ private void Layer(FastString Page, ReportComponentBase obj,
obj.AbsLeft.ToString("#0"),
obj.AbsTop.ToString("#0"));

Page.Append(" onclick=\"")
.AppendFormat(OnClickTemplate, ReportID, onclick, eventParam)
.Append("\"");
Page.Append(" " + ClickEvent(onclick, eventParam));
}

Page.Append(">");
Expand Down Expand Up @@ -148,17 +146,17 @@ private string GetHref(ReportComponentBase obj)
EncodeURL(obj.Name), // object name for security reasons
EncodeURL(obj.Hyperlink.ReportParameter),
EncodeURL(obj.Hyperlink.Value));
string onClick = String.Format(OnClickTemplate, ReportID, "detailed_report", url);
href = String.Format("<a {0} onclick=\"{1}\">", hrefStyle, onClick);
string onClick = ClickEvent("detailed_report", url);
href = String.Format("<a {0} {1}>", hrefStyle, onClick);
}
else if (obj.Hyperlink.Kind == HyperlinkKind.DetailPage)
{
url = String.Format("{0},{1},{2}",
EncodeURL(obj.Name),
EncodeURL(obj.Hyperlink.ReportParameter),
EncodeURL(obj.Hyperlink.Value));
string onClick = String.Format(OnClickTemplate, ReportID, "detailed_page", url);
href = String.Format("<a {0} onclick=\"{1}\">", hrefStyle, onClick);
string onClick = ClickEvent("detailed_page", url);
href = String.Format("<a {0} {1}>", hrefStyle, onClick);
}
else if (SinglePage)
{
Expand All @@ -171,12 +169,12 @@ private string GetHref(ReportComponentBase obj)
{
string onClick = String.Empty;
if (obj.Hyperlink.Kind == HyperlinkKind.Bookmark)
onClick = String.Format(OnClickTemplate, ReportID, "bookmark", url);
onClick = ClickEvent("bookmark", url);
else if (obj.Hyperlink.Kind == HyperlinkKind.PageNumber)
onClick = String.Format(OnClickTemplate, ReportID, "goto", url);
onClick = ClickEvent("goto", url);

if (onClick != String.Empty)
href = String.Format("<a {0} onclick=\"{1}\">", hrefStyle, onClick);
href = String.Format("<a {0} {1}>", hrefStyle, onClick);
}
}
return href;
Expand Down
7 changes: 7 additions & 0 deletions FastReport.Base/Export/Html/HTMLExportUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ private void WriteMimePart(Stream stream, string mimetype, string charset, strin
sb.Clear();
}

private string ClickEvent(string func, string parametrs)
{
if (DisableInlineScript)
return OnClickTemplate.Replace("{0}", func).Replace("{1}", parametrs);
return $"onclick=\"{String.Format(OnClickTemplate, ReportID, func, parametrs)}\"";
}

private void WriteMHTHeader(Stream Stream, string FileName)
{
FastString sb = new FastString(256);
Expand Down
274 changes: 274 additions & 0 deletions FastReport.Base/Functions/NumToWordsCn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
using System.Text;
using System.Collections.Generic;

namespace FastReport.Functions
{
/// <summary>
/// Converts numbers to Chinese financial notation (大写) for currencies CNY, USD, EUR.
/// </summary>
internal class NumToWordsCn : NumToWordsBase
{
private static Dictionary<string, CurrencyInfo> currencyList;

private static readonly string Zero = "零";

private static readonly string[] FixedWords =
{
Zero, "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"
};

private static readonly string Ten = "拾"; // 10
private static readonly string Hundred = "佰"; // 100
private static readonly string Thousand = "仟"; // 1 000
private static readonly string TenThousand = "万"; // 10 000
private static readonly string HundredMillion = "亿"; // 100 000 000

private static readonly string Yuan = "元"; // Chinese Yuan (CNY)
private static readonly string Jiao = "角"; // 0.1 Yuan (1/10 of Yuan)
private static readonly string Fen = "分"; // 0.01 Yuan (1/100 of Yuan)
private static readonly string USD = "美元"; // US Dollar
private static readonly string USCent = "美分"; // US Cents
private static readonly string EUR = "欧元"; // Euro
private static readonly string EuroCent = "欧分"; // Euro Cents

private static readonly string WholeAmount = "整"; // "Exact amount" (used for whole numbers without fractional part)
private static readonly string Minus = "负";

private static readonly WordInfo unused = new WordInfo("");

private bool _hasNonZeroIntegerPart = false;

protected override void Str(long value, WordInfo senior, StringBuilder result)
{
if (IsFractionalUnit(senior))
{
HandleFractionalPart(value, senior, result);
}
else
{
HandleIntegerPart(value, senior, result);
}
}

private void HandleFractionalPart(long value, WordInfo senior, StringBuilder result)
{
if (value == 0)
{
result.Append(WholeAmount);
return;
}

if (senior.one == Fen)
{
HandleCnyFractional(value, result);
}
else
{
HandleForeignCurrencyFractional(value, senior, result);
}
}

/// <summary>
/// Handles USD/EUR fractional part as cent amounts.
/// Adds "零" before single-digit cents when integer part exists.
/// </summary>
private void HandleForeignCurrencyFractional(long value, WordInfo senior, StringBuilder result)
{
string fractionalPart = ConvertInteger(value);
// Add "零" before single-digit cents if integer part is non-zero
if (_hasNonZeroIntegerPart && value < 10)
{
result.Append(Zero);
}
result.Append(fractionalPart);

if (value != 0)
{
if (senior.one == USCent) result.Append(USCent);
else if (senior.one == EuroCent) result.Append(EuroCent);

}
}

/// <summary>
/// Handles CNY fractional part with 角(jiao)/分(fen) structure.
/// Adds "零" (zero) between integer part and 分 when 角 is missing.
/// </summary>
private void HandleCnyFractional(long value, StringBuilder result)
{
int jiao = (int)(value / 10);
int fen = (int)(value % 10);

if (jiao > 0)
result.Append(FixedWords[jiao]).Append(Jiao);

if (fen > 0)
{
// Add "零" only if there's no 角 but integer part exists
if (jiao == 0 && _hasNonZeroIntegerPart)
result.Append(Zero);
result.Append(FixedWords[fen]).Append(Fen);
}
}

private void HandleIntegerPart(long value, WordInfo senior, StringBuilder result)
{
_hasNonZeroIntegerPart = (value != 0);

string integerPart = ConvertInteger(value);
if (value == 0 && string.IsNullOrEmpty(integerPart))
{
integerPart = Zero;
}
result.Append(integerPart);

// Add currency symbol
if (senior.one == Yuan) result.Append(Yuan);
else if (senior.one == USD) result.Append(USD);
else if (senior.one == EUR) result.Append(EUR);
}

private bool IsFractionalUnit(WordInfo unit)
{
return (unit != null) &&
(unit.one == Fen || unit.one == USCent || unit.one == EuroCent);
}

/// <summary>
/// Converts integer number to Chinese financial notation using 亿/万 blocks.
/// </summary>
private string ConvertInteger(long num)
{
if (num == 0) return "";

long units = num % 10000;
long tenThousand = (num / 10000) % 10000;
long hundredMillion = num / 100000000;

StringBuilder sb = new StringBuilder();

// 亿
if (hundredMillion > 0)
{
sb.Append(ConvertFourDigits((int)hundredMillion));
sb.Append(HundredMillion);
}

// 万
if (tenThousand > 0)
{
// Missing digits between 亿 and 万 blocks
if (hundredMillion > 0 && units > 0 && tenThousand < 1000)
{
if (!sb.ToString().EndsWith(Zero))
sb.Append(Zero);
}
sb.Append(ConvertFourDigits((int)tenThousand));
sb.Append(TenThousand);
}
else if (hundredMillion > 0 && units > 0)
{
sb.Append(Zero);
}

if (units > 0)
{
string unitStr = ConvertFourDigits((int)units);
// Missing thousands after 万 block
if (tenThousand > 0 && units < 1000 && !unitStr.StartsWith(Zero))
{
unitStr = Zero + unitStr;
}
sb.Append(unitStr);
}

return sb.ToString();
}

/// <summary>
/// Converts 4-digit number to Chinese financial notation.
/// </summary>
private string ConvertFourDigits(int num)
{
if (num == 0) return "";
StringBuilder sb = new StringBuilder();

bool hasThousands = false;
bool hasHundreds = false;

if (num >= 1000)
{
sb.Append(FixedWords[num / 1000]).Append(Thousand);
num %= 1000;
hasThousands = true;
}

if (num >= 100)
{
sb.Append(FixedWords[num / 100]).Append(Hundred);
num %= 100;
hasHundreds = true;
}
else if ((hasThousands) && num > 0)
{
// Missing hundreds between thousands and lower digits
sb.Append(Zero);
}

if (num >= 10)
{
sb.Append(FixedWords[num / 10]).Append(Ten);
num %= 10;
}
else if ((hasThousands || hasHundreds) && num > 0)
{
// Missing tens between higher digits and units
if (!sb.ToString().EndsWith(Zero))
sb.Append(Zero);
}

if (num > 0)
{
sb.Append(FixedWords[num]);
}

return sb.ToString();
}

protected override int GetFixedWordsCount() => 10;
protected override string GetFixedWords(bool male, long value) =>
(value >= 0 && value < FixedWords.Length) ? FixedWords[value] : string.Empty;
protected override string GetTen(bool male, long value) => string.Empty;
protected override string GetHund(bool male, long value) => string.Empty;

protected override WordInfo GetThousands() => unused;
protected override WordInfo GetMillions() => unused;
protected override WordInfo GetMilliards() => unused;
protected override WordInfo GetTrillions() => unused;

protected override CurrencyInfo GetCurrency(string currencyName)
{
currencyName = currencyName.ToUpper();
if (currencyList.TryGetValue(currencyName, out var info))
return info;
return currencyList["CNY"];
}

protected override string GetZero() => Zero;
protected override string GetMinus() => Minus;
protected override string GetDecimalSeparator() => string.Empty;
protected override string Get10_1Separator() => string.Empty;
protected override string Get100_10Separator() => string.Empty;
protected override string Case(long value, WordInfo info) => info.one;

static NumToWordsCn()
{
currencyList = new Dictionary<string, CurrencyInfo>
{
["CNY"] = new CurrencyInfo(new WordInfo(Yuan), new WordInfo(Fen)),
["USD"] = new CurrencyInfo(new WordInfo(USD), new WordInfo(USCent)),
["EUR"] = new CurrencyInfo(new WordInfo(EUR), new WordInfo(EuroCent))
};
}
}
}
Loading