[PHP] calcurate UPC-E check digit

最近バーコード絡みの案件があるのでちょくちょくバーコードネタが出てきます
今日はUPC-E(8桁短縮タイプ)のチェックデジットを計算する関数を作ってみました

そんなライブラリどこかに転がってるだろ、と思っていたのですが、これが意外に見つからない・・・
UPC-A対応ならあるのですが、8桁短縮コードは考慮されていない事が多かったです
探し方が悪かったのかな?
とにかく、見つけられなかった以上、自作します

基本的にはJAN(=EAN)であろうとUPCであろうとModulus10/Weight3と呼ばれる方法でチェックデジットは計算されています
これはざっくり言うと、奇数桁の合計×3 + 偶数桁の合計 を10で割った余りを10から引いたもの、です

日本語で書くよりソースコードのほうがわかりやすそうです

function calc_checkdigit($code)
{
    if( !ctype_digit($code) ) return false;
    $code = (string)$code;
    $odd_total  = 0;
    $even_total = 0;
    
    for($i=0; $i<strlen($code); $i++)
    {
        if((($i+1)%2) == 0) {
            // 偶数行合計
            $even_total += $code[$i];
        } else {
            // 奇数行合計
            $odd_total += $code[$i];
        }
    }
 
    // 奇数桁の合計×3 + 偶数桁の合計
    $sum = (3*$odd_total) + $even_total;
    // 10で割った余りを求める
    $remainder = $sum%10;
    // 求められた余りを10から引いた値がチェックデジット(10の場合は0)
    return( (10-$remainder)%10 );
}

で、問題はここから

UPC-Eの場合は一度UPC-A(12桁の通常タイプ)に変換してから計算するという仕様なのですが、この変換がちょっとややこしい
UPC-Eの8桁のうち先頭の1桁は0で固定されており、また末尾1桁はチェックデジットなので、意味のある文字列は2桁目から7桁目までの6文字になるのですが、その末尾(全体の7桁目)によって変換の方法が変わるという仕様です

まとめた資料がcanonさんにありました
UPC短縮バーコードチェックデジット計算方法

よく見るとそこまで複雑な話ではないのですが、ぱっと見じゃ理解できないですよね…

PHPで処理を書くとこうなります

function upce2upca($code)
{
    if( !ctype_digit($code) ) return false;
    $code = (string)$code;
    $len = strlen($code);
    if( $len != 7) return false;
    
    switch($code[6])
    {
        case '0':
        case '1':
        case '2':
            $ret = substr($code,0,3).$code[6].'0000'.substr($code,3,3);
            break;
        case '3':
            $ret = substr($code,0,4).'00000'.substr($code,4,2);
            break;
        case '4':
            $ret = substr($code,0,5).'00000'.$code[5];
            break;
        default:
            $ret = substr($code,0,6).'0000'.$code[6];
            break;
    }
    return $ret;
}

さきほどのModulus10/Weight3の計算と合わせて使うことでUPC-Eのチェックデジット計算ができます

// チェックデジット計算前の7桁
$raw_upce = '0123456'; 

// UPC-A(チェックデジット加える前の11桁)に変換
$raw_upca = upce2upca($raw_upce );

// チェックデジットを計算
$cd = calc_checkdigit($raw_upca); // 5

// 有効な8桁UPC-Eを作る
$valid_upce = $raw_upce.$cd; // 01234565