InDesignで様々なバーコード、特にJAN/EAN-13のような商品バーコードを扱うことがよくあります。

現代Javascriptでは色々バーコードを生成する無料ライブラリー沢山ありますが、ExtendScriptでは、その恩恵を受けていない。

 JANコード裏にある複雑なエンコード仕様を理解して作れるが、大量な時間が必要ではないかと。 一方、考え方を変えると、描画されるまで、バーコードは一本一本の黒と白のバーの並びだけで、その並び順番分かれば、InDesignの作業は単純な描画作業になります。 

バーコードデータの生成

 まず、python-barcode(pip install python-barcode)ライブラリを使って、データとなる黒白のバーのシーケンスを取得します。

from barcode import JAN
# 13桁のJANコードを指定
jan_code_str = "4901550151296"
# JANコードオブジェクトを作成
code_obj = JAN(jan_code_str)
# build()`メソッドは、バイナリ文字列を含むリストを返します。
# この長さ95の文字列が、EAN-13バーコードを構成する(左右のクワイエットゾーンを除いた)すべてのモジュールです。
# '1'と'0'で構成されるバーコードのシーケンスを取得
bar_sequence = code_obj.build()[0]
print(bar_sequence) # '10100010110100111001100101******'

バーコードを描画 

黒と白バーの順番分かれば、残りの作業は非常に簡単。appscriptを使ってInDesignで、生成したシーケンスに基づいて描画します。→効率を上げるため、黒いバーを一本ずつ描くのではなく、連続する黒いバーを一つの幅広の矩形にまとめて一度に描画します。

from barcode import JAN  # pip3 install python-barcode
from appscript import app, k
id_app = app("Adobe InDesign 2024")


class InDesignJancodeMaker():
    def __init__(self):
        self.black_color = "Black"
        self.white_color = "Paper"

    def __find_true_segments(self, a: list):
        """
        Finds the true segments in a given list.

        Args:
            a (list): The input list.

        Returns:
            list: A list of tuples representing the true segments.
                Each tuple contains the start index and length of a true segment.
        """
        true_segments = []
        start = None
        length = 0
        for i, value in enumerate(a):
            if value:
                if start is None:
                    start = i
                length += 1
            else:
                if start is not None:
                    true_segments.append((start, length))
                    start = None
                    length = 0
        # handle the case where the list ends with a True segment
        if start is not None:
            true_segments.append((start, length))
        return true_segments

    def create_jancode(self, data: str, module_width=0.33,
                       module_height=22.85,
                       guard_bar_height=25.93,
                       left_quiet_zone=11, right_quiet_zone=7):
        # https://internationalbarcodes.com/ean-13-specifications/
        # unit: mm
        # 13桁のJANコードを生成する。
        # JAN-13/EAN-13の寸法
        # 1モジュールの幅:0.33mm
        # 左右のクワイエットゾーンを除く幅:31.35mm(0.33mm x 95モジュール)
        # クワイエットゾーンを含めた幅:37.29mm(0.33mm x 113モジュール)
        # 高さ(キャラクタのパターンのバー):22.85mm
        # 左2本、右2本、中央2本のバーコードの高さ:25.93mm <--ヒゲ
        if len(data) != 13:  # TODO:JAN8の場合
            raise ValueError("JAN code must be 13 digits.", data)
        code_obj = JAN(data)  # , guardbar=True
        assert code_obj.get_fullcode() == data, "Invalid JAN code."
        bar_sequence = code_obj.build()[0]  # '10100010*******'
        # 黒バーの開始位置と長さを取得(効率よく描画する為)
        true_segments = self.__find_true_segments(int(x) for x in bar_sequence)
        assert len(true_segments) == 30, "Invalid JAN code length."
        # draw white background
        bg_rect_width = (left_quiet_zone + len(bar_sequence) + right_quiet_zone
                         ) * module_width
        doc = id_app.make(new=k.document)
        group_items = []
        rect = doc.make(new=k.rectangle,
                        with_properties={
                            k.name: "background",
                            k.stroke_weight: 0,
                            k.fill_color: self.white_color,  # 背景の矩形
                            k.geometric_bounds: [0, 0, guard_bar_height, bg_rect_width],
                        })
        group_items.append(rect)
        # draw module rectangles
        guard_bar_pos = (0, 1, 14, 15, 28, 29)
        for i, (start_pos, length) in enumerate(true_segments):
            height = guard_bar_height if i in guard_bar_pos else module_height
            rect = doc.make(new=k.rectangle,
                            with_properties={
                                k.stroke_weight: 0,
                                k.fill_color: self.black_color,  # 黒バーの背景
                                k.geometric_bounds: [0, (start_pos + left_quiet_zone) * module_width,
                                                     height, (start_pos + left_quiet_zone + length) * module_width],
                            })
            group_items.append(rect)
        # 背景とバーをグループ化
        group_obj = doc.make(new=k.group,
                             with_properties={k.group_items: group_items})
        group_obj.name.set("JANCode")
        return doc


my_code = InDesignJancodeMaker()
my_code.create_jancode("4901550151296")

全て種類のバーコード内部実は全て同じ構造:黒と白の並び、create_jancode関数を少し抽象化したら、code128、ITF等の描画も可能だ。