C#
カスタマバーコード
郵便物を発送するときに使うカスタマバーコードというのがある。1000通以上を料金後納郵便または料金別納郵便で送る場合に、カスタマバーコードを使うと割り引きを受けられるそうだ。普通の人には用がないと思うが、割り引きを受けられなくても書いておけば郵便局の人は助かるのかな。
ソースコード
public static void DrawCustomerBarCode(Graphics g, float x, float y, string ZipNo, string AddrNo)
{
const int StartIndex = 0;
const int StartLength = 2 * 1;
const int ZipNoIndex = StartIndex + StartLength;
const int ZipNoLength = 3 * 7;
const int AddrNoIndex = ZipNoIndex + ZipNoLength;
const int AddrNoLength = 3 * 13;
const int CheckDigitIndex = AddrNoIndex + AddrNoLength;
const int CheckDigitLength = 3 * 1;
const int StopIndex = CheckDigitIndex + CheckDigitLength;
const int StopLength = 2 * 1;
const int TotalLength = StopIndex + StopLength;
const float BarWidth = 0.6f;
const float BarPitch = 1.2f;
string[] Codes = {
"144",
"114",
"132",
"312",
"123",
"141",
"321",
"231",
"231",
"411",
"414",
"324",
"342",
"234",
"432",
"243",
"423",
"441",
"111"
};
char[] Bars = new char[TotalLength];
int BarIndex = 0;
int CheckSum = 0;
Action<string> AppendCode = (string Code) =>
{
foreach (char c in Code)
{
Bars[BarIndex++] = c;
}
};
Action<int> AppendByIndex1 = (int CodeIndex) =>
{
AppendCode(Codes[CodeIndex]);
};
Action<int> AppendByIndex2 = (int CodeIndex) =>
{
AppendByIndex1(CodeIndex);
CheckSum += CodeIndex;
};
Action<int> AppendByIndex3 = (int CodeIndex) =>
{
if (BarIndex < CheckDigitIndex)
{
AppendByIndex2(CodeIndex);
}
};
Action<string> AppendText = (string s) =>
{
foreach (char c in s)
{
if (c == '-')
{
AppendByIndex3(10);
}
else if ((c >= '0') && (c <= '9'))
{
AppendByIndex3((int)c - '0');
}
else if ((c >= 'A') && (c <= 'Z'))
{
int n = (int)c - 'A';
AppendByIndex3(11 + (n / 10));
AppendByIndex3(n % 10);
}
else
{
throw new ArgumentException("不正な文字があります。カスタマバーコードに変換できません。");
}
}
};
AppendCode("13");
AppendText(ZipNo.Replace("-", string.Empty));
AppendText(AddrNo);
while (BarIndex < CheckDigitIndex)
{
AppendByIndex2(14);
}
int CheckDigit = (19 - (CheckSum % 19)) % 19;
AppendByIndex1(CheckDigit);
AppendCode("31");
float Y1 = y;
float Y2 = y + 1.2f;
float BarHeight = 0.0f;
foreach (char c in Bars)
{
switch (c)
{
case '1':
y = Y1;
BarHeight = 3.6f;
break;
case '2':
y = Y1;
BarHeight = 2.4f;
break;
case '3':
y = Y2;
BarHeight = 2.4f;
break;
case '4':
y = Y2;
BarHeight = 1.2f;
break;
}
g.FillRectangle(Brushes.Black, x, y, BarWidth, BarHeight);
x += BarPitch;
}
}
一度に1000回以上実行することを考えると、少し心配なところもある。ラムダを使うよりクラスにまとめたほうが、効率が良いような気がするけど、どのみちプリンタの印刷速度よりは速いだろう。
座標をミリメーターでしか指定できないのが多少もどかしい。フォントのサイズや線の太さにはポイントを使うのが普通なので、印刷では全体的にポイントで行うほうが都合が良いと思う。ポイントでやりたいときはGraphics.ScaleTransform()を使う。
const float _Scale = 72.0f / 25.4f;
Matrix _Saved = g.Transfrom;
g.ScaleTransform(_Scale, _Scale);
DrawCustomerBarCode(e.Graphics, x / _Scale, y / _Scale, ZipNo, AddrNo);
g.Transform = _Saved;
サンプルコード
private void PrintDocument_BeginPrint(object sender, PrintEventArgs e)
{
PrintDocument Document = (PrintDocument)sender;
int PaperWidth = (int)(Math.Round(100.0 * 120.0 / 25.4, 0));
int PaperHeight = (int)(Math.Round(100.0 * 235.0 / 25.4, 0));
Document.DefaultPageSettings.PaperSize = new PaperSize("長型3号", PaperWidth, PaperHeight);
Document.DefaultPageSettings.Landscape = true;
}
private void PrintDocument_PrintPage(object sender, PrintPageEventArgs e)
{
string ZipNo = "000-0000";
string Addr = "東京都東京市0-0-0 東京ビル101";
string Cmpn = "株式会社 悪徳商事";
string Name = "悪徳 太郎 様";
string AddrNo = "0-0-0-101";
using (StringFormat _Format = new StringFormat(StringFormat.GenericTypographic))
{
e.Graphics.PageUnit = GraphicsUnit.Millimeter;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
RectangleF _Rect = new RectangleF(
25.4f * e.MarginBounds.Left / 100.0f,
25.4f * e.MarginBounds.Top / 100.0f,
25.4f * e.MarginBounds.Width / 100.0f,
25.4f * e.MarginBounds.Height / 100.0f);
using (Font _Font = new Font("MS 明朝", 12.0f))
{
float _FontHeight = 25.4f * _Font.SizeInPoints / 72.0f;
float _LineFeed = 2.0f * _FontHeight;
float _Indent = 3.0f * _FontHeight;
e.Graphics.DrawString("〒" + ZipNo, _Font, Brushes.Black, _Rect, _Format);
_Rect.Y += _LineFeed;
_Rect.X += _Indent;
_Rect.Width -= _Indent;
e.Graphics.DrawString(Addr, _Font, Brushes.Black, _Rect, _Format);
_Rect.Y += _LineFeed;
e.Graphics.DrawString(Cmpn, _Font, Brushes.Black, _Rect, _Format);
_Rect.Y += _LineFeed;
_Rect.X -= _Indent;
_Rect.Width += _Indent;
}
using (Font _Font = new Font("MS 明朝", 24.0f))
{
float _FontHeight = 25.4f * _Font.SizeInPoints / 72.0f;
_Format.Alignment = StringAlignment.Center;
_Rect.Y += 0.5f * _FontHeight;
e.Graphics.DrawString(Name, _Font, Brushes.Black, _Rect, _Format);
_Rect.Y += _FontHeight;
}
_Rect.Y += 5.0f;
DrawCustomerBarCode(e.Graphics, (_Rect.Left + _Rect.Right - 79.8f) / 2.0f, _Rect.Y, ZipNo, AddrNo);
}
}
備考
- カスタマバーコードを使うには、事前に品質の検査を受ける必要があって、印刷サンプルを三枚提出するそうだ。大量に印刷する前に確認しておいたほうが良いとのこと。
- 住所表示番号が13桁に収まらない場合、14桁目以降はカスタマバーコードに記載されない。最後に配達員が目視で住所を確認して配達するので、それで大丈夫なのだろう。
- 住所表示番号を調べるのは、結構面倒くさい。一応、方法は郵便局のサイトで説明されている。でも、郵便番号のリストが必要になるし、アルゴリズム的にプログラムで解決できるかどうかわからない。たぶん、データベースか何かに住所を登録するときに、人の手で住所表示番号を調べて一緒に登録しておくのが妥当だろう。
- 大口事業所や私書箱の個別番号では、住所表示番号は必要ない。
ところで、割り引きの適用のない、品質検査を受けていないカスタマバーコードを印刷して発送することは、問題ないだろうか。
(2021/07/15 初稿)
(2021/08/13 更新)