TSパケットからはじめる(5) - 文字コード2
初期処理
前回からの続きとなりますが、実際に文字コードをデコードする処理を書いていきます。図形表をGCodeという名称にして列挙型としました。
public enum GCode
{
UNKNOWN,
KANJI,
ALPHANUMERIC,
HIRAGANA,
KATAKANA,
MOSAIC_A,
MOSAIC_B,
MOSAIC_C,
MOSAIC_D,
PROP_ALPHANUMERIC,
PROP_HIRAGANA,
PROP_KATAKANA,
JIS_X0201_KATAKANA,
JIS_KANJI_PLANE_1,
JIS_KANJI_PLANE_2,
ADDITIONAL_SYMBOLS,
DRCS_0,
DRCS_1,
DRCS_2,
DRCS_3,
DRCS_4,
DRCS_5,
DRCS_6,
DRCS_7,
DRCS_8,
DRCS_9,
DRCS_10,
DRCS_11,
DRCS_12,
DRCS_13,
DRCS_14,
DRCS_15,
MACRO
}
G0、G1、G2、G3はGPagesという名称にしてGPages[0]、Gpages[1]という形にします。
private List<GCode> GPages = new List<GCode>();
...
this.GPages.Add(GCode.KANJI);
this.GPages.Add(GCode.ALPHANUMERIC);
this.GPages.Add(GCode.HIRAGANA);
this.GPages.Add(GCode.KATAKANA);
GL、GRについてはG0、G1、G2、G3のどの番号を見ているか?という点で着目して、GL_numとGR_numという値を使用して図形表を取得します。
private GL_num { get; set; }
private GR_num { get; set; }
private GCode GL { get { return this.GPages[GL_num]; }}
private GCode GR { get { return this.GPages[GR_num]; }}
最後に文字サイズとなります。
public enum STRSIZE
{
NORMAL,
MEDIUM
}
全体の流れ
要は1文字ずつ処理をしていくことになります。DestStringに変換した文字列を追加していきます。
public string AtriToString(byte[] rows)
{
if (rows.Length == 0) { return string.Empty; }
this.DestString = new StringBuilder(rows.Length);
this.GPages = new List<GCode>();
// デフォルト値指定
this.GPages.Add(GCode.KANJI);
this.GPages.Add(GCode.ALPHANUMERIC);
this.GPages.Add(GCode.HIRAGANA);
this.GPages.Add(GCode.KATAKANA);
this.GL_num = 0;
this.GR_num = 2;
this.StrSize = STRSIZE.NORMAL;
using (MemoryStream ms = new MemoryStream(rows))
{
int b;
while ((b = ms.ReadByte()) != -1)
{
this.AribParse((byte)b, ms);
}
}
return DestString.ToString();
}
1文字受け取り側はコード範囲から処理を決めます。
private void AribParse(byte row, MemoryStream ms)
{
// GL領域コード範囲
if (row >= 0x21 && row <= 0x7e)
{
// GL側の処理
}
// GR領域コード範囲
else if (row >= 0xa1 && row <= 0xfe)
{
// GR側の処理
}
else
{
// GL/GRコード範囲外
this.InvocationSetGraphic(row, ms);
}
}
GL/GRコード範囲外は制御コードとなるので、
private void InvocationSetGraphic(byte control, MemoryStream ms)
{
// C0制御コード
if (control >= 0x00 && control <= 0x20)
{
switch (control)
{
case 0x0f: this.GL_num = 0; break; // LS0
case 0x0e: this.GL_num = 1; break; // LS1
case 0x1b:
byte control2 = (byte)ms.ReadByte();
switch (control2)
{
case 0x6e: this.GL_num = 2; break; // LS2
case 0x6f: this.GL_num = 3; break; // LS3
case 0x7c: this.GR_num = 3; break; // LS3R
case 0x7d: this.GR_num = 2; break; // LS2R
case 0x7e: this.GR_num = 1; break; // LS1R
// ESC
default:
this.EscapeControl(control2, ms);
break;
}
break;
// SS2
case 0x19:
int SS2 = this.GL_num;
this.GL_num = 2;
this.AribParse((byte)ms.ReadByte(), ms);
this.GL_num = SS2;
break;
// SS3
case 0x1d:
int SS3 = this.GL_num;
this.GL_num = 3;
this.AribParse((byte)ms.ReadByte(), ms);
this.GL_num = SS3;
break;
// SPACE
case 0x20:
if (this.StrSize == STRSIZE.MEDIUM)
{
this.DestString.Append(" ");
}
else if (this.StrSize == STRSIZE.NORMAL)
{
this.DestString.Append(" ");
}
break;
case 0x0d:
this.DestString.Append("\n");
break;
default:
break;
}
}
// C1制御コード
else
{
switch (control)
{
case 0x89: this.StrSize = STRSIZE.MEDIUM; break; // MSZ
case 0x8a: this.StrSize = STRSIZE.NORMAL; break; // NSZ
default:
break;
}
}
}
ESC処理の処理を外だししているので、
private void EscapeControl(byte control, MemoryStream ms)
{
byte control2 = 0;
byte control3 = 0;
if (control == 0x24)
{
control2 = (byte)ms.ReadByte();
if (control2 >= 0x28 && control2 <= 0x2b)
{
control3 = (byte)ms.ReadByte();
if (control3 == 0x20)
{
switch (control2)
{
case 0x28: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 0); break;
case 0x29: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 1); break;
case 0x2a: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 2); break;
case 0x2b: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 3); break;
}
}
else
{
switch (control2)
{
case 0x29: this.DesignationSetGraphic(control3, 1); break;
case 0x2a: this.DesignationSetGraphic(control3, 2); break;
case 0x2b: this.DesignationSetGraphic(control3, 3); break;
}
}
}
else
{
this.DesignationSetGraphic(control2, 0);
}
}
else if(control >= 0x28 && control <= 0x2b)
{
control2 = (byte)ms.ReadByte();
if (control2 == 0x20)
{
switch(control)
{
case 0x28: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 0); break;
case 0x29: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 1); break;
case 0x2a: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 2); break;
case 0x2b: this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 3); break;
}
}
else
{
switch(control)
{
case 0x28: this.DesignationSetGraphic(control2, 0); break;
case 0x29: this.DesignationSetGraphic(control2, 1); break;
case 0x2a: this.DesignationSetGraphic(control2, 2); break;
case 0x2b: this.DesignationSetGraphic(control2, 3); break;
}
}
}
}
いちいちswichで分岐させてますが、0x28を引いた値を指定した方がシンプルではあります。例えば、以下のようにすれば一行で処理出来ます。
this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), control2 - 0x28);
図形表をG0からG3に割り当てする処理部となります。
private void DesignationSetDRCSGraphic(int TableId, int G_num)
{
GCode code = GCode.UNKNOWN;
switch (TableId)
{
case 0x41: code = GCode.DRCS_0; break;
default:
code = GCode.UNKNOWN;
break;
}
this.GPages[G_num] = code;
}
private void DesignationSetGraphic(int TableId, int G_num)
{
GCode code = GCode.UNKNOWN;
switch (TableId)
{
case 0x30: code = GCode.HIRAGANA; break;
case 0x31: code = GCode.KATAKANA; break;
case 0x32: code = GCode.MOSAIC_A; break;
case 0x33: code = GCode.MOSAIC_B; break;
case 0x34: code = GCode.MOSAIC_C; break;
case 0x35: code = GCode.MOSAIC_D; break;
case 0x36: code = GCode.PROP_ALPHANUMERIC; break;
case 0x37: code = GCode.PROP_HIRAGANA; break;
case 0x38: code = GCode.PROP_KATAKANA; break;
case 0x39: code = GCode.JIS_KANJI_PLANE_1; break;
case 0x3A: code = GCode.JIS_KANJI_PLANE_2; break;
case 0x3b: code = GCode.ADDITIONAL_SYMBOLS; break;
case 0x42: code = GCode.KANJI; break;
case 0x49: code = GCode.JIS_X0201_KATAKANA; break;
case 0x4a: code = GCode.ALPHANUMERIC; break;
case 0x70: code = GCode.MACRO; break;
}
this.GPages[G_num] = code;
}
基本的に前回の内容をそのままコードにした形です。最初のGL、GRコード範囲の処理を追加すれば文字コードがデコードできます。
文字コードデコード処理部
GLコード範囲の処理部は読み込んだコードをデコード処理に回します。
if (stdb24.IsMultiByte[this.GL])
{
// 2バイト処理
this.MultiByte(this.GL, (byte)row, (byte)ms.ReadByte());
}
else
{
// 1バイト処理
this.SingleByte(this.GL, row);
}
GRコード範囲の場合は先頭ビットを抜かす必要がありますので0x7Fの&取得して渡します。
if (stdb24.IsMultiByte[this.GR])
{
// 2バイト処理
this.MultiByte(this.GR, (byte)(row & 0x7f), (byte)(ms.ReadByte() & 0x7f));
}
else
{
// 1バイト処理
this.SingleByte(this.GR, (byte)(row & 0x7f));
}
1バイト処理側は事前に用意してある図形表から取り出すだけです。
private void SingleByte(GCode code, byte c)
{
switch(code)
{
case GCode.ALPHANUMERIC:
case GCode.PROP_ALPHANUMERIC:
if (this.StrSize == STRSIZE.MEDIUM)
{
this.DestString.Append(AribTable.jisx0201Table[c]);
}
else if (this.StrSize == STRSIZE.NORMAL)
{
this.DestString.Append(AribTable.jisx0201FullwidthTable[c]);
}
break;
case GCode.HIRAGANA:
case GCode.PROP_HIRAGANA:
this.DestString.Append(AribTable.hiraganaTable[c]);
break;
case GCode.KATAKANA:
case GCode.PROP_KATAKANA:
this.DestString.Append(AribTable.katakanaTable[c]);
break;
case GCode.MACRO:
using (MemoryStream cmd = new MemoryStream(AribTable.macroTable[c]))
{
this.InvocationSetGraphic((byte)cmd.ReadByte(), cmd);
}
break;
default:
break;
}
}
次に2バイト処理側は漢字については基本的にEUCJPと互換なので標準機能を使用して変換します。
private void MultiByte(GCode code, byte c1, byte c2)
{
switch(code)
{
case GCode.KANJI:
case GCode.JIS_KANJI_PLANE_1:
case GCode.JIS_KANJI_PLANE_2:
byte[] code = new byte[8];
code[0] = 0x1B;
code[1] = 0x24;
code[2] = 0x40;
code[3] = c1;
code[4] = c2;
code[5] = 0x1B;
code[6] = 0x28;
code[7] = 0x4A;
string msg = Encoding.GetEncoding(50220).GetString(code).TrimEnd('\0');
DestString.Append(msg);
break;
case GCode.ADDITIONAL_SYMBOLS:
this.DestString.Append(AribTable.additionalSymbolTable[c1 << 8 | c2]);
break;
case GCode.DRCS_0:
break;
}
}
全部まとめたコードは番組表を取得できた後にこのサイトからダウンロード可能とします。
この記事が気に入ったらサポートをしてみませんか?