Files
Toolkit2/Toolkit.UI.Controls.Avalonia/QrCode/Encoding/DataEncodation/ECISet.cs
T
2024-04-13 11:41:33 +01:00

256 lines
6.4 KiB
C#

using System;
using System.Collections.Generic;
namespace Gma.QrCodeNet.Encoding.DataEncodation;
public sealed class ECISet
{
/// <summary>
/// ISO/IEC 18004:2006 Chapter 6.4.2 Mode indicator = 0111 Page 23
/// </summary>
private const int ECIMode = 7;
private const int ECIIndicatorNumBits = 4;
private Dictionary<string, int>? _nameToValue;
private Dictionary<int, string>? _valueToName;
/// <summary>
/// Initialize ECI Set.
/// </summary>
/// <param name="option">AppendOption is enum under ECISet
/// Use NameToValue during Encode. ValueToName during Decode</param>
internal ECISet(AppendOption option)
{
Initialize(option);
}
public enum AppendOption
{
NameToValue,
ValueToName,
Both
}
/// <summary>
/// Length indicator for number of ECI codewords
/// </summary>
/// <remarks>ISO/IEC 18004:2006 Chapter 6.4.2 Page 24.
/// 1 codeword length = 0. Any additional codeword add 1 to front. Eg: 3 = 110</remarks>
/// <description>Bits required for each one is:
/// one = 1, two = 2, three = 3</description>
private enum ECICodewordsLength
{
One = 0,
Two = 2,
Three = 6
}
/// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks>
/// <param name="eCIValue">Range: 0 ~ 999999</param>
/// <returns>Number of Codewords(Byte) for ECI Assignment Value</returns>
private static int NumOfCodewords(int eCIValue)
{
if (eCIValue is >= 0 and <= 127)
{
return 1;
}
else if (eCIValue is > 127 and <= 16383)
{
return 2;
}
else if (eCIValue is > 16383 and <= 999999)
{
return 3;
}
else
{
throw new ArgumentOutOfRangeException($"{nameof(eCIValue)} should be in range: 0 to 999999.");
}
}
/// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks>
/// <param name="eCIValue">Range: 0 ~ 999999</param>
/// <returns>Number of bits for ECI Assignment Value</returns>
private static int NumOfAssignmentBits(int eCIValue) => NumOfCodewords(eCIValue) * 8;
private void AppendECI(string name, int value, AppendOption option)
{
switch (option)
{
case AppendOption.NameToValue:
_nameToValue?.Add(name, value);
break;
case AppendOption.ValueToName:
_valueToName?.Add(value, name);
break;
case AppendOption.Both:
_nameToValue?.Add(name, value);
_valueToName?.Add(value, name);
break;
default:
throw new InvalidOperationException($"There is no such {nameof(AppendOption)}.");
}
}
private void Initialize(AppendOption option)
{
switch (option)
{
case AppendOption.NameToValue:
_nameToValue = new Dictionary<string, int>();
break;
case AppendOption.ValueToName:
_valueToName = new Dictionary<int, string>();
break;
case AppendOption.Both:
_nameToValue = new Dictionary<string, int>();
_valueToName = new Dictionary<int, string>();
break;
default:
throw new InvalidOperationException($"There is no such {nameof(AppendOption)}.");
}
// ECI table. Source 01 URL: http://strokescribe.com/en/ECI.html
// ECI table. Source 02 URL: http://lab.must.or.kr/Extended-Channel-Interpretations-ECI-Encoding.ashx
// ToDo. Fill up remaining missing table.
AppendECI("iso-8859-1", 1, option);
AppendECI("IBM437", 2, option);
// AppendECI("iso-8859-1", 3, option); //ECI value 1 is default encoding.
AppendECI("iso-8859-2", 4, option);
AppendECI("iso-8859-3", 5, option);
AppendECI("iso-8859-4", 6, option);
AppendECI("iso-8859-5", 7, option);
AppendECI("iso-8859-6", 8, option);
AppendECI("iso-8859-7", 9, option);
AppendECI("iso-8859-8", 10, option);
AppendECI("iso-8859-9", 11, option);
AppendECI("windows-874", 13, option);
AppendECI("iso-8859-13", 15, option);
AppendECI("iso-8859-15", 17, option);
AppendECI("shift_jis", 20, option);
AppendECI("utf-8", 26, option);
}
/// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks>
/// <param name="eCIValue">Range: 0 ~ 999999</param>
/// <returns>Number of bits for ECI Header</returns>
internal static int NumOfECIHeaderBits(int eCIValue) => NumOfAssignmentBits(eCIValue) + 4;
internal int GetECIValueByName(string encodingName)
{
if (_nameToValue is null)
{
Initialize(AppendOption.NameToValue);
}
if (_nameToValue!.TryGetValue(encodingName, out int eCIValue))
{
return eCIValue;
}
else
{
throw new ArgumentOutOfRangeException($"ECI does not contain encoding: {encodingName}.");
}
}
internal string GetECINameByValue(int eCIValue)
{
if (_valueToName is null)
{
Initialize(AppendOption.ValueToName);
}
if (_valueToName!.TryGetValue(eCIValue, out var eCIName))
{
return eCIName;
}
else
{
throw new ArgumentOutOfRangeException($"ECI does not contain value: {eCIValue}.");
}
}
/// <returns>ECI table in Dictionary collection</returns>
public Dictionary<string, int>? GetECITable()
{
if (_nameToValue is null)
{
Initialize(AppendOption.NameToValue);
}
return _nameToValue;
}
public bool ContainsECIName(string encodingName)
{
if (_nameToValue is null)
{
Initialize(AppendOption.NameToValue);
}
return _nameToValue!.ContainsKey(encodingName);
}
public bool ContainsECIValue(int eciValue)
{
if (_valueToName is null)
{
Initialize(AppendOption.ValueToName);
}
return _valueToName!.ContainsKey(eciValue);
}
/// <remarks>ISO/IEC 18004:2006 Chapter 6.4.2 Page 24.</remarks>
internal BitList GetECIHeader(string encodingName)
{
int eciValue = GetECIValueByName(encodingName);
BitList dataBits = new()
{
{ ECIMode, ECIIndicatorNumBits }
};
int eciAssignmentByte = NumOfCodewords(eciValue);
// Number of bits = Num codewords indicator + codeword value = Number of codewords * 8
// Chapter 6.4.2.1 ECI Designator ISOIEC 18004:2006 Page 24
int eciAssignmentBits;
switch (eciAssignmentByte)
{
case 1:
// Indicator = 0. Page 24. Chapter 6.4.2.1
dataBits.Add((int)ECICodewordsLength.One, 1);
eciAssignmentBits = (eciAssignmentByte * 8) - 1;
break;
case 2:
// Indicator = 10. Page 24. Chapter 6.4.2.1
dataBits.Add((int)ECICodewordsLength.Two, 2);
eciAssignmentBits = (eciAssignmentByte * 8) - 2;
break;
case 3:
// Indicator = 110. Page 24. Chapter 6.4.2.1
dataBits.Add((int)ECICodewordsLength.Three, 3);
eciAssignmentBits = (eciAssignmentByte * 8) - 3;
break;
default:
throw new InvalidOperationException("Assignment Codewords should be either 1, 2 or 3.");
}
dataBits.Add(eciValue, eciAssignmentBits);
return dataBits;
}
}