コンピューター

CODE39 描画ロジック(ざっくりと)

CODE39についての描画ロジックの備忘録記事~。 CODE39 について でまとめたメモを元にコーディングしてみたよ。例によって例のごとく C#ですが、ほかの言語でも同じように以下略。見た目は前回日記(「数値や文字列からバーコードに変換」に挑戦中。)でご紹介中ですのでよろしければそちらもどうぞ。

JAN13 と同じく、アドバイスをしてくれた ponherm 氏に感謝。

 

CODE39用ビット配列定義

まずは CODE39を描画するための基本的なビット並びの定義から。
CODE39の場合のビット並びは太さパターンが太い/細いのどちらであるかを意味する配列となります。太=1 細=0で表現しています。
塗り/抜きの繰り返しパターンで表現し、一つの文字は 塗り/抜き/塗り/抜き/塗り/抜き/塗り/抜き/塗り で表現されます。(合計9本のON/OFF)
また、文字と文字の間には、細い線1本文の空白が入ります
一文字分幅はすべての文字で同じ幅となります。

 

[ patt_f ]
塗りパターンを表しています。CODE39では、基本的に数値と英文字列については、 1~ 9 、0 の順番で繰り返し塗りパターンが出現します。

[ patt_s ]
抜きパターンを表しています。CODE39では、 1-0 / A-J / K-T / U-Z でそれぞれ共通の位置に抜きパターンが発生します。

塗り、抜きが交互に出現することで、バーコードのパターンが表現されます

特殊記号等については、上記のロジックとは異なる方法でバーの描画が決定されているので、計算値ではなく、バーパターンを即値で定義しています。
塗りパターン抜きパターンを合成するにあたって、抜きパターンを4ビットずらすことによって、int32 の範囲内に収めて表記できるようにしてあります。

private Dictionary<char, int> code39 = new Dictionary<char, int>( );

/// <summary>
/// Code39 のパターンを準備します。
/// </summary>
private void initCode39Patt( )

           // 16進数の特定の桁を立ててバーの存在を示します。 
           // 数値と英文字列

           int[] patt_f = {
                0x10001,    // 1
                0x01001,    // 2
                0x11000,    // 3
                0x00101,    // 4
                0x10100,    // 5
                0x01100,    // 6
                0x00011,    // 7
                0x10010,    // 8
                0x01010,    // 9
                0x00110     // 0
            };

            // 16進数の特定の桁を立ててスペースの存在を示します。
            // ただし、patt_f と混合して使用するため、4ビットシフトして
            // 4ビット毎に交互にバーとスペースのビットが配置されるように工夫してあります。
            int[] patt_s = {
                0x0100,    // 1-0
                0x0010,    // A-J
                0x0001,    // K-T
                0x1000,    // U-Z
            };

            /// 特殊記号群。
            /// 上の規則を予め書き崩して 16進数の 1 と 4 の混合で
            /// バーとスペースの位置をディクショナリに投入しておきます。
            code39.Add( ‘*’, 0x04110 );
            code39.Add( ‘$’, 0x04440 );
            code39.Add( ‘/’, 0x04404 );
            code39.Add( ‘+’, 0x04044 );
            code39.Add( ‘%’, 0x00444 );
            code39.Add( ‘-‘, 0x04011 );
            code39.Add( ‘.’, 0x14010 );
            code39.Add( ‘ ‘, 0x05010 );

            // 数字 (1 – 9, 0) の順となります。
            for( int i = 0; i < 10; i++ )
            {
                int index = (i + 1) % 10;
                int value = patt_f[i] | (patt_s[0] << 2);
                char c = (char)( ‘0’ + index );

                code39.Add( c, value );
            }
            // 英文字 (A – Z) の順です。
            for( int j = 0; j < 3; j++ )
            {
                for( int i = 0; i < 10; i++ )
                {
                    char c =  (char)(‘A’ + j * 10 + i);
                    int value = patt_f[i] | (patt_s[1 + j] << 2);

                    code39.Add( c, value );

                    if( c == ‘Z’ )
                    {
                        return;
                    }
                }
            }
}

上記によって、ライブラリにに 1-9、0、A-Z、*、$、/、+、%、-、.、 、 のすべての文字用のバーコードパターンが格納されました。

バーコードパターン数値をバーコードパターン表現に戻すためのメソッドは、

/// <summary>
/// パターン定義数値をパターン文字列に変換。
/// </summary>
/// <param name="value">ライブラリに登録されたint32で表現されているパターン定義数値。</param>
/// <returns>0、1の文字列として表現されたバーコードパターン。</returns>
private static string ToStringQuad( int value )
{
    char[] c = new char[9];

    for( int i = 8; i >= 0; i– )
    {
        c[i] = (value % 4 != 0) ? ‘1’ : ‘0’;
        value >>= 2;
    }

    return new string( c );
}

となります。

 

バーの描画

あとは、上記を使用して、出力したい文字を元に、ライブラリからパターン数値を取得し、そこからバーコードパターン文字列に変換し、 このパターン文字列を 0 は細い線、1は太い線として、 前から塗り/抜きの順に描画していくだけでOKです。一文字分のバーコードの描画終了時は、細い線1本文の抜きスペースを空けます。(※こうしないと前の塗りと次の塗りがつながってしまい、読み取れなくなる。)

スタートコード、ストップコード

CODE39 の場合、数値・文字の前に必ずスタートコードとストップコードを表示する必要があります。(スタートストップともに*記号です。)
なので、「123456ABC」というバーコードを作成したい場合は 「*123456ABC*」 となります。

チェックデジット

CODE39のチェックデジット付加については、任意のようですが、こちらを設定することにより読み取り制度が格段に上がるらしいです。(バーコード端末側でチェックデジットの有無を設定する必要があります。)なので、チェックデジット値を計算するコードも追加で作成しておきました。
モジュラス43なので、文字列すべてを数値化して合計した値を43で割った余りがチェックデジットとなります。

バーコードリーダーでチェックデジットを有効にした場合、文字列の最後にここで割り出された数値を追加します。(※ストップコード直前に付加。)

/// <summary>
/// チェックディジットの計算をします。
/// </summary>
/// <param name="str">バーコード化する文字列</param>
/// <returns>チェックデジット文字</returns>

private char calcCheckDigit( string str )
{
    // 数値にする。
    // すべてを合計する。
    int calc = 0;

    foreach( char c in str )
    {
        int check = (int)c;

        if( 48 <= check && check <= 57 )
        {
            // 数値範囲。
            //        0-9はそのまま    (ASCIIでは48-57)
            calc += ( check – 48 );
        }
        else if( 65 <= check && check <= 90 )
        {
            // アルファベット範囲。
            //        A-Zは10-35        (ASCIIでは65-90)

            calc += ( check – 55 );
        }
        else
        {
            // 特殊記号。
            switch( check )
            {
            case 45:    calc += 36;    break;    //    36:-           
            case 46:    calc += 37;    break;    //    37:.           
            case 32:    calc += 38;    break;    //    38: space       
            case 36:    calc += 39;    break;    //    39:$           
            case 47:    calc += 40;    break;    //    40:/           
            case 43:    calc += 41;    break;   //    41:+           
            case 37:    calc += 42;    break;    //    42:%           
            default:
                // ここには来ない!
                Debug.Fail( "never reach" );
                break;
            }
        }
    }

    // 43で割る。
    calc = calc % 43;

    // 余りの値を文字にする。
    if( 0 <= calc && calc <= 9 )
    {
        return (char)(‘0’ + calc);
    }
   else if( 10 <= calc && calc <= 35 )
    {
        return (char)(‘A’ + calc-10);
    }
    else
    {
        switch( calc )
        {
        case 36:    return ‘-‘;
        case 37:    return ‘.’;
        case 38:    return ‘ ‘;
        case 39:    return ‘$’;
        case 40:    return ‘/’;
        case 41:    return ‘+’;
        case 42:    return ‘%’;
        default:
            Debug.Fail( "never reach" );
            return ‘0’;
        }
    }
}

ちなみに、CODE39 での文字列の数値割り当て順は、ASCIIと微妙に出現順が異なるため、とくに記号関連の割り当てについては一つずつ再割り当てするという、まどろっこしい状態となってしまっています。どうにかならんかなー。

という感じで、備忘録終了。おつかれさまでした。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください