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 { getset; }
private GR_num { getset; }
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 0x0fthis.GL_num = 0break// LS0
            case 0x0ethis.GL_num = 1break// LS1
            case 0x1b:
                byte control2 = (byte)ms.ReadByte();
                switch (control2)
                {
                    case 0x6ethis.GL_num = 2break// LS2
                    case 0x6fthis.GL_num = 3break// LS3
                    case 0x7cthis.GR_num = 3break// LS3R
                    case 0x7dthis.GR_num = 2break// LS2R
                    case 0x7ethis.GR_num = 1break// 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 0x89this.StrSize = STRSIZE.MEDIUM; break// MSZ
            case 0x8athis.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 0x28this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 0); break;
                    case 0x29this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 1); break;
                    case 0x2athis.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 2); break;
                    case 0x2bthis.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 3); break;
                }
            }
            else
            {
                switch (control2)
                {
                    case 0x29this.DesignationSetGraphic(control3, 1); break;
                    case 0x2athis.DesignationSetGraphic(control3, 2); break;
                    case 0x2bthis.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 0x28this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 0); break;
                case 0x29this.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 1); break;
                case 0x2athis.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 2); break;
                case 0x2bthis.DesignationSetDRCSGraphic((byte)ms.ReadByte(), 3); break;
            }
        }
        else
        {
            switch(control)
            {
                case 0x28this.DesignationSetGraphic(control2, 0); break;
                case 0x29this.DesignationSetGraphic(control2, 1); break;
                case 0x2athis.DesignationSetGraphic(control2, 2); break;
                case 0x2bthis.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;
    }
}

全部まとめたコードは番組表を取得できた後にこのサイトからダウンロード可能とします。

この記事が気に入ったらサポートをしてみませんか?