JAN コード/EANコードのバーコードをPython を使って作成する

20年前に作ったフォントが少し手直しが必要なため新たに作るようにしました

https://www.vector.co.jp/soft/data/writing/se475905.html

ソースコードを公開しています。数字なしのバーコードやバーコードの高さを変更できますので必要な場合はご自身で変更してください

JANコードの仕様書に基づいて、JANコードの構成と符号化のプロセスを簡単に説明します。

JANコードの仕様

JANコードの基本構造

JANコードは通常13桁で構成され、短縮バージョンは8桁です。13桁の場合、次のように分かれます。

  • 最初の1桁: プレフィックス・キャラクタ(国や地域を示す)
  • 次の6桁: メーカーコード
  • その次の5桁: 商品コード
  • 最後の1桁: チェックデジット(エラーチェック用)

現在は JAN コードの枯渇のため仕様が変更されています

レフトカードバーからライトガードバーまで(標準は幅31.35mm、高さ22.86mm)

例として「4901234567894」を使います。

4 | 901234 | 567894

パリティの概要

JANコードでは、バーコードの左側と右側で数字の表現が異なります。各数字は奇数パリティまたは偶数パリティによって表現されます。具体的には、奇数パリティでは黒いバー(1)の数が奇数、偶数パリティでは黒いバーの数が偶数になります。

  • 左側(L側): 奇数パリティまたは偶数パリティを使用。
  • 右側(R側): すべて偶数パリティを使用(L側の奇数パリティのビット反転)。

各数字の符号化パターンは以下の通りです。

数字ごとのパリティパターン(0~9)

JANコードの各数字は、7ビットのモジュールで表され、パリティに応じて異なるビットパターンになります。表を使って説明します。

 1文字は7モジュールから構成され、1つの数字は幅の違う2本のバー(黒)と
 2本のスペース(白)でできています。
 例)□□□■■□■

数字左側奇数パリティ (L-奇数)左側偶数パリティ (L-偶数)右側偶数パリティ (R-偶数)
0000110101001111110010
1001100101100111100110
2001001100110111101100
3011110101000011000010
4010001100111011011100
5011000101110011001110
6010111100001011010000
7011101100100011000100
8011011100010011001000
9000101100101111110100

左側と右側の違い

  • 左側奇数パリティ(L側奇数)は、ビット列の中で黒いバー(1)の数が奇数となるようなパターンです。
  • 左側偶数パリティ(L側偶数)は、黒いバーの数が偶数になるパターンです。
  • 右側偶数パリティ(R側偶数)は、左側奇数パリティのビットを反転させたものです。

これにより、数字0から9の符号化が異なるパリティを使って表現され、バーコードの形式が決まります。

プリフィックス・キャラクタによるパリティの決定

JANコードの先頭1桁(プリフィックス・キャラクタ)は、左側の6桁の数字が奇数パリティか偶数パリティかを決定します。下記の表に従い、プリフィックスの数字に応じたパリティの組み合わせが適用されます。

1桁目2桁目3桁目4桁目5桁目6桁目7桁目
0
1
2
3
4
5
6
7
8
9

例:プリフィックスが「4」の場合

「4」はパリティの組み合わせが「奇・偶・奇・奇・偶・偶」となるため、左側の6桁「901234」に適用すると、次のような符号化になります。

  • 9: 奇数パリティ 0001011
  • 0: 偶数パリティ 0100111
  • 1: 奇数パリティ 0011001
  • 2: 奇数パリティ 0010011
  • 3: 偶数パリティ 0100001
  • 4: 偶数パリティ 0011101

バーコードの完全な構造

JANコードには、レフトマージン、ガードバー、データバー、センターバー、ライトデータバー、チェックデジットバー、ライトガードバー、ライトマージンが含まれます。各部位のモジュール数は次の通りです。

  • レフトマージン: 11モジュール以上
  • レフトガードバー: 3モジュール(101)
  • レフトデータバー: 42モジュール(6キャラクタ分)
  • センターバー: 5モジュール(01010)
  • ライトデータバー: 42モジュール(6キャラクタ分)
  • チェックデジットバー: 7モジュール
  • ライトガードバー: 3モジュール(101)
  • ライトマージン: 7モジュール以上

符号化の例

例えば、JANコード「4901234567894」を符号化すると、次のようになります。

レフトデータバー

  • 9: 奇数パリティ 0001011
  • 0: 偶数パリティ 0100111
  • 1: 奇数パリティ 0011001
  • 2: 奇数パリティ 0010011
  • 3: 偶数パリティ 0100001
  • 4: 偶数パリティ 0011101

ライトデータバー

  • 5: 偶数パリティ 1001110
  • 6: 偶数パリティ 1101100
  • 7: 偶数パリティ 1100110
  • 8: 偶数パリティ 1011100
  • 9: 偶数パリティ 1110100
  • 4: 偶数パリティ 1011100

ガードバー、センターバー、マージン

  • レフトガードバー: 101
  • センターバー: 01010
  • ライトガードバー: 101

最終出力

00000000000_101_000101101001110110111000110101100110110011_01010_100111011011001100110101110011100101000010_101_0000000

このフォントとバーコードの関係

各数字のパリティ符号化

以下の表は、0から9までの数字が奇数パリティ、偶数パリティ、および右側の偶数パリティでどのように符号化されるかを示しています。

数字左側奇数パリティ左側偶数パリティ右側偶数パリティ
00001101 (0)0100111 (@)1110010 (P)
10011001 (1)0110011 (A)1100110 (Q)
20010011 (2)0011011 (B)1101100 (R)
30111101 (3)0100001 (C)1000010 (S)
40100011 (4)0011101 (D)1011100 (T)
50110001 (5)0111001 (E)1001110 (U)
60101111 (6)0000101 (F)1010000 (V)
70111011 (7)0010001 (G)1000100 (W)
80110111 (8)0001001 (H)1001000 (X)
90001011 (9)0010111 (I)1110100 (Y)
  • 左側の奇数パリティは、黒いバーの数が奇数。
  • 左側の偶数パリティは、黒いバーの数が偶数。
  • 右側の偶数パリティは、左側奇数パリティのビット反転です。

例: プリフィックス「4」の場合

「4」は「奇偶奇奇偶偶」のパリティで符号化されます。例えば、左側の6桁が「901234」の場合、次のように符号化されます。

この符号化パターンを適用すると、901234は次のようになります。

901234 -> 9@12CD

ここで、「@」は0の偶数パリティ、「C」は3の偶数パリティ、「D」は4の偶数パリティです。

バーコードの全体構造

JANコードのバーコードにはスタートコード、センターコード、ストップコードが含まれます。符号化されたデータは次のように表されます。

  • スタートコード: {
  • 左側データ: 9@12CD
  • センターコード: |
  • 右側データ: UVWXYT
  • ストップコード: }

これにより、バーコード表記は次のようになります。

{9@12CD|UVWXYT}

このフォントでは次のようにしています

レフトガードバー: 00000000000101
ライトガードバー: 0000000101

数字付きバーコードの表現

バーコードの先頭にあるスタートコードは1桁目のプリフィックス・キャラクタによって決まります。各プリフィックスに対応するコードは以下の通りです。

  • 0 → p
  • 1 → q
  • 2 → r
  • 3 → s
  • 4 → t
  • 5 → u
  • 6 → v
  • 7 → w
  • 8 → x
  • 9 → y

これにより、最終的なバーコード表記は次のようになります。

t9@12CD|UVWXYT}

プログラム

パターンファイルでバーコードを作るプログラム

このプログラムは1モジュール12ポイントで作っています

フォントの高さは1000ポイントです

import csv
import os

# 文字列を16進数に変換し、"0x" をつける
def string_to_hex(s):
    hex_value = ''.join([format(ord(c), 'x') for c in s])  # 文字列をUTF-8バイト列の16進数に変換
    return f"0x{hex_value}"  # "0x"を先頭につけて返す

# CSVからデータを読み込み、指定のフォルダにSVGファイルを生成
def generate_svg_from_csv(csv_file_path, output_folder):
    # フォルダが存在しない場合は作成する
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
    
    # CSVファイルを読み込み
    with open(csv_file_path, mode='r') as file:
        reader = csv.reader(file)
        for row in reader:
            binary_string = row[0]  # 1列目の文字列を取得
            hex_value = string_to_hex(row[1])  # 2列目の文字列を16進数に変換
            file_name = f"{hex_value}.svg"  # 変換した16進数をファイル名にする
            
            # SVGデータを生成
            svg_data = generate_svg_from_string(binary_string)
            
            # 出力ファイルのパスを作成
            file_path = os.path.join(output_folder, file_name)
            
            # SVGファイルとして保存
            with open(file_path, 'w') as svg_file:
                svg_file.write(svg_data)

# SVGデータを生成する関数
def generate_svg_from_string(binary_string, width=12, height=832):
    if  len(binary_string) != 7:
        height+=24
    # SVGのヘッダー部分
    svg_header = '<?xml version="1.0" standalone="no"?>\n'
    svg_open = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 {} {}">\n'.format(len(binary_string) * width, 1024)
    svg_close = '</svg>'
    svg_path_open = '   <path fill="black"\n'
    svg_path_d = 'd="'
    
    # 文字列をループして「1」に対する黒の四角を作成
    for index, char in enumerate(binary_string):
        if char == '1':
            x_position = index * width
            svg_path_d += 'M{} 0v{}h{}v-{}h-{}z'.format(x_position, height, width, height, width)
    
    # pathタグを閉じる
    svg_path_d += '" />\n'
    
    # すべてのパーツを結合してSVGデータを完成
    svg_content = svg_header + svg_open + svg_path_open + svg_path_d + svg_close
    return svg_content

# 実行例: CSVファイルを読み込み、指定フォルダにSVGファイルを生成
csv_file_path = 'input.csv'  # CSVファイルのパス
output_folder = 'output_svgs'  # SVGファイルを保存するフォルダ
generate_svg_from_csv(csv_file_path, output_folder)

パターンファイル

0001101,0
0100111,@
1110010,P
0011001,1
0110011,A
1100110,Q
0010011,2
0011011,B
1101100,R
0111101,3
0100001,C
1000010,S
0100011,4
0011101,D
1011100,T
0110001,5
0111001,E
1001110,U
0101111,6
0000101,F
1010000,V
0111011,7
0010001,G
1000100,W
0110111,8
0001001,H
1001000,X
0001011,9
0010111,I
1110100,Y
00000000000101,{
1010000000,}
01010,|
00000000000101,p
00000000000101,q
00000000000101,r
00000000000101,s
00000000000101,t
00000000000101,u
00000000000101,v
00000000000101,w
00000000000101,x
00000000000101,y

バーコード と数字の SVG ファイルを合体させる バッチファイル

echo e %*
::@pwsh -NoProfile -Command  "& (Invoke-Expression -Command ('{' + (Get-Content %~f0 -Encoding Shift-JIS | Where-Object {$_.readcount -gt 9}| Out-String )+ '}'))" %*
::
@powershell -Command  "& (Invoke-Expression -Command ('{' + (Get-Content %~f0 | Where-Object {$_.readcount -gt 9}| Out-String )+ '}'))" %*
timeout /t 5&goto:eof




# フォルダパスの設定
$sourceFolder1 = "D:\WinPython\notebooks\output_svgs"
$sourceFolder2 = "D:\data2\input_s"
$outputFolder = "Y:\input_svg"

# ソースフォルダ1のテキストファイルを取得
$files = Get-ChildItem -Path $sourceFolder1 -Filter "*.*"

# 各ファイルを処理
foreach ($file in $files) {
    # ファイル名の取得
    $fileName = $file.Name

    # ソース1のファイルを読み込み、最後の行を削除
    $filePath1 = Join-Path $sourceFolder1 $fileName
    $content0 = Get-Content $filePath1
    $content1 = Get-Content $filePath1
    $content1 = $content1[0..($content1.Count - 2)]  # 最後の行を削除

    # ソース2の対応するファイルパスを設定
    $filePath2 = Join-Path $sourceFolder2 $fileName

    # ソース2のファイルが存在するか確認
    if (Test-Path $filePath2) {
        # ソース2のファイルを読み込み、5行目から最後まで取得
        $content2 = Get-Content $filePath2
        $content2 = $content2[4..($content2.Count - 1)]  # 5行目から最後まで

        # 両方のファイルの内容を結合
        $combinedContent = $content1 + $content2
    } else {
        # ソース2が存在しない場合はソース1のみ使用
        Write-Host "$fileName のソース2が見つかりませんでした。ソース1のみを使用します。"
        $combinedContent = $content0
    }

    # 出力フォルダに新しいファイルを保存
    $outputFilePath = Join-Path $outputFolder $fileName
    $combinedContent | Set-Content $outputFilePath
}

Write-Host "処理が完了しました。ファイルは $outputFolder に保存されました。"

フォントを作成するプログラム

フォントのダウンロード

font,Python

Posted by eightban