Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,24 @@ public static Bitmap ToBitmap(this CustomerBarCode code, int scale = 1)
if (code == null) throw new ArgumentNullException(nameof(code));

var img = new Bitmap(code.GetWidth(scale), code.GetHeight(scale));
using (var gr = Graphics.FromImage(img))
try
{
gr.Clear(Color.White);
foreach (var bar in code.GetBars(scale))
using (var gr = Graphics.FromImage(img))
{
gr.FillRectangle(Brushes.Black, bar.X, bar.Y, bar.Width, bar.Height);
gr.Clear(Color.White);
foreach (var bar in code.GetBars(scale))
{
gr.FillRectangle(Brushes.Black, bar.X, bar.Y, bar.Width, bar.Height);
}
}
return img;
}
catch
{
// 描画に失敗した場合は生成済みのイメージを破棄する
img.Dispose();
throw;
}
return img;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,25 @@ public static SKBitmap ToSKBitmap(this CustomerBarCode code, int scale = 1)
if (code == null) throw new ArgumentNullException(nameof(code));

var img = new SKBitmap(code.GetWidth(scale), code.GetHeight(scale));
using (var canvas = new SKCanvas(img))
using (var paint = new SKPaint { Color = SKColors.Black, IsAntialias = false })
try
{
canvas.Clear(SKColors.White);
foreach (var bar in code.GetBars(scale))
using (var canvas = new SKCanvas(img))
using (var paint = new SKPaint { Color = SKColors.Black, IsAntialias = false })
{
canvas.DrawRect(SKRect.Create(bar.X, bar.Y, bar.Width, bar.Height), paint);
canvas.Clear(SKColors.White);
foreach (var bar in code.GetBars(scale))
{
canvas.DrawRect(SKRect.Create(bar.X, bar.Y, bar.Width, bar.Height), paint);
}
}
return img;
}
catch
{
// 描画に失敗した場合は生成済みのイメージを破棄する
img.Dispose();
throw;
}
return img;
}

/// <summary>
Expand Down
54 changes: 53 additions & 1 deletion ZipConvertCustomerBarCode.Tests/BarCodeTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace ZipConvertCustomerBarCode.Tests
using System.Globalization;

namespace ZipConvertCustomerBarCode.Tests
{
public class BarCodeTests
{
Expand All @@ -11,11 +13,29 @@ public void シンボル列が1x版と一致する(string zipCode, string addres
Assert.Equal(expected.Split(' '), code.Symbols);
}

// 住所表示番号は13けたまで。13けた目がアルファベットの場合は英字用制御コードのみ残す
// http://www.post.japanpost.jp/zipcode/zipmanual/p20.html
[Theory]
// マニュアル p20 の例
[InlineData("9100067", "福井県福井市新田塚3丁目80-25 J1ビル2-B", "STC 9 1 0 0 0 6 7 3 - 8 0 - 2 5 CC1 9 1 - 2 CC1 9 SPC")]
// アルファベットで13けたを超える
[InlineData("1234567", "A1B2C3D4E5F6G7", "STC 1 2 3 4 5 6 7 CC1 0 1 CC1 1 2 CC1 2 3 CC1 3 4 CC1 CC5 SPC")]
[InlineData("1234567", "123456789012A", "STC 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 9 0 1 2 CC1 8 SPC")]
// アルファベットが12~13けた目にちょうど収まる
[InlineData("1234567", "12345678901A", "STC 1 2 3 4 5 6 7 1 2 3 4 5 6 7 8 9 0 1 CC1 0 - SPC")]
public void 住所表示番号は13けたで切り捨てる(string zipCode, string address, string expected)
{
var code = BarCode.Create(zipCode, address);

Assert.Equal(expected.Split(' '), code.Symbols);
}

[Theory]
[InlineData("0140113")]
[InlineData("014-0113")]
[InlineData("0140113")]
[InlineData("014‐0113")]
[InlineData("014-0113")]
public void 郵便番号は全角とハイフンを受け付ける(string zipCode)
{
var code = BarCode.Create(zipCode, "添60-1");
Expand All @@ -29,11 +49,43 @@ public void 郵便番号は全角とハイフンを受け付ける(string zipCod
[InlineData("01401130")]
[InlineData("014011A")]
[InlineData("014 0113")]
[InlineData("0140113\n")]
// アラビア・インド数字
[InlineData("٠١٤٠١١٣")]
public void 郵便番号が7桁の数値でなければ例外(string zipCode)
{
Assert.Throws<ArgumentException>(() => BarCode.Create(zipCode, "添60-1"));
}

[Fact]
public void 住所の大文字変換は実行環境のカルチャによらない()
{
var original = CultureInfo.CurrentCulture;
try
{
// tr-TR では "i".ToUpper() が "İ" になる
CultureInfo.CurrentCulture = new CultureInfo("tr-TR");

var code = BarCode.Create("1234567", "1-2i");

Assert.Equal(new[] { "1", "-", "2", "CC1", "8" }, code.Symbols.Skip(8).Take(5));
}
finally
{
CultureInfo.CurrentCulture = original;
}
}

[Fact]
public void シンボル列は書き換えられない()
{
var code = BarCode.Create("0140113", "添60-1");

Assert.False(code.Symbols is string[]);
var list = Assert.IsAssignableFrom<IList<string>>(code.Symbols);
Assert.Throws<NotSupportedException>(() => list[1] = "X");
}

[Fact]
public void 引数がnullなら例外()
{
Expand Down
39 changes: 15 additions & 24 deletions ZipConvertCustomerBarCode/BarCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static class BarCode
{'p','p'},{'q','q'},{'r','r'},{'s','s'},{'t','t'},
{'u','u'},{'v','v'},{'w','w'},{'x','x'},{'y','y'},
{'z','z'},
{' ',' '},{'‐','-'}
{' ',' '},{'‐','-'},{'-','-'}
};

// 文字→バーコード辞書
Expand Down Expand Up @@ -75,7 +75,7 @@ public static CustomerBarCode Create(string zipCode, string address)
if (address == null) throw new ArgumentNullException(nameof(address));

var code = ConvertCustomerBarCode(address, CheckPostCode(zipCode));
return new CustomerBarCode(code.ToArray());
return new CustomerBarCode(code);
}

/// <summary>
Expand All @@ -92,7 +92,8 @@ private static IEnumerable<string> CheckPostCode(string zipCode)
// ハイフン除去
zipCode = zipCode.Replace("-", "");

if (!Regex.IsMatch(zipCode, @"\A\d{7}\Z"))
// \d は全角以外の Unicode 数字にも、\Z は末尾の改行にもマッチするため使わない
if (!Regex.IsMatch(zipCode, @"\A[0-9]{7}\z"))
{
throw new ArgumentException("郵便番号は7桁の数値で入力してください。");
}
Expand All @@ -118,7 +119,8 @@ private static IEnumerable<string> ConvertCustomerBarCode(string address, IEnume

// http://www.post.japanpost.jp/zipcode/zipmanual/p19.html
// 1. まず、データ内にあるアルファベットの小文字は大文字に置き換えます。
address = address.ToUpper();
// 実行環境のカルチャによらず変換する(tr-TR では i が İ になるため)
address = address.ToUpperInvariant();

// 2. 同様に、データ内にある"&"等の下記の文字は取り除き、後ろのデータを詰めます。
//「&」(アンパサンド)、「/」(スラッシュ)、「・」(中グロ)、「.」(ピリオド)
Expand Down Expand Up @@ -173,26 +175,15 @@ private static string TrimHyphen(string target)
/// <returns>カスタマバーコード</returns>
private static string[] ConvertCCode(string address)
{
int idx = 0;
var customerCode = Enumerable.Repeat("CC4", 13).ToArray();
foreach (var c in address.Select(x => x.ToString()).Take(13))
{
var cc = BarcodeCharDic[c];
if (cc.Length == 1)
{
// 数値、ハイフン
customerCode[idx] = cc[0];
}
else
{
// アルファベット
customerCode[idx] = cc[0];
customerCode[idx + 1] = cc[1];
idx++;
}
idx++;
}
return customerCode;
// 数値、ハイフンは1けた、アルファベットは英字用制御コードと数字の2けたに変換し、
// 13けたに満たない場合は CC4 で埋める
// http://www.post.japanpost.jp/zipcode/zipmanual/p20.html
// 合計けた数が20けた(住所表示番号は13けた)を超えた場合、以降の文字については切り捨てます。
// その際、20~21けた目がアルファベット文字となった場合、20けた目の英字用制御(CC1、CC2、CC3)は残して以降は切り捨てます。
return address.SelectMany(c => BarcodeCharDic[c.ToString()])
.Concat(Enumerable.Repeat("CC4", 13))
.Take(13)
.ToArray();
}

/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions ZipConvertCustomerBarCode/CustomerBarCode.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -31,9 +32,10 @@ public sealed class CustomerBarCode
{"STC"," LD"},{"SPC","DL "}
};

internal CustomerBarCode(IReadOnlyList<string> symbols)
internal CustomerBarCode(IEnumerable<string> symbols)
{
Symbols = symbols;
// 呼び出し側から書き換えられないよう、コピーを読み取り専用で保持する
Symbols = new ReadOnlyCollection<string>(symbols.ToArray());
}

/// <summary>
Expand Down