Find file
Fetching contributors…
Cannot retrieve contributors at this time
324 lines (292 sloc) 10.4 KB
//
// JapanPostBarcode.m
// BarcodePod
//
// Created by 佐藤 昭 on Mon Jun 30 2003.
// Copyright (c) 2003 SatoAkira. All rights reserved.
//
#import "JapanPostBarcode.h"
@implementation JapanPostBarcode
#define MILLIMETERPERPOINT 0.3527777777777777 // base72.0 //
#define JP_DESCENDER_BOTTOM 0.0 // 最下段基準線(mm) //
#define JP_TRACK_BOTTOM 1.2 // 中段下基準線(mm) //
#define JP_TRACK_TOP 2.4 // 中段上基準線(mm) //
#define JP_ASCENDER_TOP 3.6 // 最上段基準線(mm) //
#define JP_ASCENDER_MASK (0x222)
#define JP_DESCENDER_MASK (0x111)
// -----------------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------------
// Each hex constant represents a 4-bar code of the barcode corresponding to a single number or letter. The digits represent bars in this fashion: //
// 0 - タイミングバー (JP_TRACK_BOTTOM to JP_TRACK_TOP) //
// 1 - 下セミロングバー (JP_DESCENDER_BOTTOM to JP_TRACK_TOP) //
// 2 - 上セミロングバー (JP_TRACK_BOTTOM to JP_ASCENDER_TOP) //
// 3 - ロングバー (JP_DESCENDER_BOTTOM to JP_ASCENDER_TOP) //
unsigned int openBracketLength = 2;
unsigned int openBracket = 0x31; // スタートコード //
unsigned int closeBracketLength = 2;
unsigned int closeBracket = 0x13; // ストップコード //
NSString *barFormat = @"101010101010101010101010";
unsigned int numberLength = 3;
unsigned int numberCodesOfJapan[] = {
0x030, // - //
0x003, // CC7 . //
0x333, // CC8 / //
0x300, // 0 //
0x330, // 1 //
0x312, // 2 //
0x132, // 3 //
0x321, // 4 //
0x303, // 5 //
0x123, // 6 //
0x231, // 7 //
0x213, // 8 //
0x033, // 9 //
0x120, // CC1 : //
0x102, // CC2 ; //
0x210, // CC3 < //
0x012, // CC4 = //
0x201, // CC5 > //
0x021 // CC6 ? //
};
double japanpost_barTop(unsigned int hexDigit,double size) { // mm単位で返す。 //
return (hexDigit & JP_ASCENDER_MASK) ? JP_ASCENDER_TOP * size / 10.0 : JP_TRACK_TOP * size / 10.0;
}
double japanpost_barBottom(unsigned int hexDigit,double size) { // mm単位で返す。 //
return (hexDigit & JP_DESCENDER_MASK) ? JP_DESCENDER_BOTTOM * size / 10.0 : JP_TRACK_BOTTOM * size / 10.0;
}
unsigned int japanpost_characterDescriptor(unichar character) {
return numberCodesOfJapan[character - '-'];
}
unsigned int japanpost_barDescriptor(unsigned int descriptor,unsigned int bar,unsigned int bars)
{
unsigned int shift = (bars - 1 - (bar / 2)) * 4; // 4ビットずつで1つのバーを表す。bar / 2 番目 //
unsigned int mask = 0xF << shift; // 0xfは4ビット分 //
return (descriptor & mask) >> shift;
}
- (unsigned int)_barDescriptor:(unsigned int)index
// barcode is [self initiator][self barcode][self terminator] //
// 2 * numberLength がほぼ定数に近い働きをしており、これを変数にすることが難しい。よって'A'~'Z'は2文字の扱いをした。 //
{
unsigned int bar;
unsigned int hexDigit;
unsigned int descriptor;
unsigned int contentLength = [japanpostContents length];
if ([[self initiator] length] > index) {
bar = index % [[self initiator] length];
hexDigit = openBracket;
descriptor = japanpost_barDescriptor(hexDigit,bar,openBracketLength);
}
else {
if (index >= (contentLength + 1) * (2 * numberLength) + [[self initiator] length]) {
bar = (index - [[self initiator] length] - (contentLength + 1) * (2 * numberLength)) % [[self terminator] length];
hexDigit = closeBracket;
descriptor = japanpost_barDescriptor(hexDigit,bar,closeBracketLength);
}
else {
unsigned int digit = (index - [[self initiator] length]) / (2 * numberLength); // 何文字目か //
bar = (index - [[self initiator] length]) % (2 * numberLength); // 1文字の中の何番目のバーになるか //
if (digit != contentLength) {
hexDigit = japanpost_characterDescriptor( [[self japanpostContents] characterAtIndex:digit] );
descriptor = japanpost_barDescriptor( hexDigit, bar,numberLength);
}
else { // last digit is checksum
hexDigit = japanpost_characterDescriptor( [self checkDigit] );
descriptor = japanpost_barDescriptor( hexDigit, bar,numberLength);
}
}
}
return descriptor;
}
- (NSString *)japanpostContents {
return japanpostContents;
}
// override //
- (id)initWithContent:(NSString *)inContent printsCaption:(BOOL)inPrints andBarWidth:(float)inBarWidth andHeight:(float)inHeight andFontSize:(float)inFontSize andCheckDigit:(char)inDigit
// inPrints,inBarWidth,inFontSize,inDigitは意味を持たない。 //
{
if ((nil != inContent) && (nil != (self = [super init]))) {
[self setContent:inContent];
if ((8.0 <= inHeight) && (11.5 >= inHeight))
[self setHeight:inHeight];
else
[self setHeight:10.0];
}
return self;
}
- (void)setHeight:(float)inHeight
// inHeight:ポイント単位 //
{
if (8.5 > inHeight)
height = 8.5;
else {
if (11.5 < inHeight)
height = 11.5;
else
height = inHeight;
}
barWidth = (float)((double)height / (double)6.0);
[self calculateWidth];
}
- (void)setBarWidth:(float)inBarWidth
// inBarWidth:point単位 //
{
if (1.3333333333333333 > inBarWidth) // 8.0 / 6.0 //
barWidth = 1.3333333333333333;
else {
if (1.9166666666667 < inBarWidth) // 11.5 / 6.0 //
barWidth = 1.9166666666667;
else
barWidth = inBarWidth;
}
height = (float)((double)6.0 * (double)barWidth);
[self calculateWidth];
}
- (void)generateChecksum
{
unsigned int i,cd;
unichar uChar,character;
unsigned int checkValue = 0;
for (i = 0; i < [japanpostContents length]; i++) {
uChar = [japanpostContents characterAtIndex:i];
if ('-' == uChar)
checkValue += 10; // 10 //
else {
if ((',' == uChar) || ('/' == uChar))
checkValue += (uChar - '-') + 16; // 17 or 18 //
else {
if (('0' <= uChar) && ('9' >= uChar))
checkValue += uChar - '0'; // 0 ~ 9 //
else {
checkValue += uChar - ':' + 11; // 11 ~ 16 //
}
}
}
}
cd = 19 * (checkValue / 19 + 1) - checkValue;
if (10 > cd)
character = '0' + cd;
else {
if (10 == cd)
character = '-';
else {
if ((10 < cd) && (16 >= cd))
character = ':' + cd - 11;
else
character = '-' + cd - 16;
}
}
[self setCheckDigit:character];
}
- (void)setContent:(NSString *)inContent
// 大文字化する。20文字に揃える。使用できない文字を排除する。郵便番号の整合性はチェックしない。 //
{
unichar uChar;
unsigned int i;
NSString *uppercaseString = [inContent uppercaseString];
NSMutableString *tempStr = [NSMutableString string];
NSMutableString *tempContents = [NSMutableString string];
NSString *tenStr = @"0123456789";
unsigned int maxLength = 20; // 郵便番号7文字+住居表示番号13文字 //
for (i = 0; i < [uppercaseString length]; i++) {
uChar = [uppercaseString characterAtIndex:i];
if (('A' <= uChar) && ('Z' >= uChar)) {
if ('K' > uChar) {
[tempStr appendString:@":"]; // CC1 //
[tempStr appendString:[tenStr substringWithRange:NSMakeRange((unsigned int)(uChar - 'A'),1)]];
[tempContents appendString:[NSString stringWithCharacters:&uChar length:1]];
}
else {
if ('U' > uChar) {
[tempStr appendString:@";"]; // CC2 //
[tempStr appendString:[tenStr substringWithRange:NSMakeRange((unsigned int)(uChar - 'K'),1)]];
[tempContents appendString:[NSString stringWithCharacters:&uChar length:1]];
}
else { // ('Z' >= uChar) //
[tempStr appendString:@"<"]; // CC3 //
[tempStr appendString:[tenStr substringWithRange:NSMakeRange((unsigned int)(uChar - 'U'),1)]];
[tempContents appendString:[NSString stringWithCharacters:&uChar length:1]];
}
}
}
else {
if (('-' == uChar) || (('0' <= uChar) && ('9' >= uChar))) {
[tempStr appendString:[NSString stringWithCharacters:&uChar length:1]];
[tempContents appendString:[NSString stringWithCharacters:&uChar length:1]];
} // 使用できない文字を排除する。 //
}
}
if (maxLength > [tempStr length]) {
for (i = [tempStr length]; i < maxLength; i++)
[tempStr appendString:@"="]; // CC4 //
}
else {
if (maxLength < [tempStr length]) {
unsigned int validCount;
unsigned int tempL = [tempStr length];
NSString *tempCStr = [NSString stringWithString:tempContents];
[tempStr deleteCharactersInRange:NSMakeRange(maxLength,tempL - maxLength)];
tempL = [tempStr length];
validCount = 0;
for (i = 0; i < [tempStr length]; i++) {
uChar = [tempStr characterAtIndex:i];
if (('-' == uChar) || (('0' <= uChar) && ('9' >= uChar))) // 'A'~'Z'も制御文字+数字文字になっているのでカウントできる。 //
validCount++;
}
tempContents = [NSMutableString stringWithString:[tempCStr substringWithRange:NSMakeRange(0,validCount)]];
}
}
[japanpostContents release];
japanpostContents = [[NSString allocWithZone:[self zone]] initWithString:tempStr];
[content release];
content = [[NSString allocWithZone:[self zone]] initWithString:tempContents];
[self generateChecksum];
}
- (BOOL)isContentValid
{
unsigned int i;
unichar uChar;
BOOL result = YES;
for (i = 0; i < 7; i++) {
uChar = [content characterAtIndex:i];
if (('0' > uChar) || ('9' < uChar)) {
result = NO;
break;
}
}
return result;
}
- (NSString *)barcode
// japanpostContentsを参照するところがNKDBarcodeと異なる。 //
{
unsigned int i;
NSMutableString *theReturn = [NSMutableString string];
for (i = 0; i < [japanpostContents length]; i++)
[theReturn appendString:[self _encodeChar:[japanpostContents characterAtIndex:i]]];
if (checkDigit != -1)
[theReturn appendString:[self _encodeChar:checkDigit]];
return theReturn;
}
- (BOOL)printsCaption {
return NO;
}
- (NSString *)initiator {
return [barFormat substringToIndex:2 * openBracketLength];
}
- (NSString *)terminator {
return [barFormat substringToIndex:2 * closeBracketLength - 1]; // 最後の"0"は不要 //
}
- (float)barTop:(int)index {
return (float)(japanpost_barTop([self _barDescriptor:(unsigned int)index],(double)height) / (double)MILLIMETERPERPOINT);
}
- (float)barBottom:(int)index {
return (float)(japanpost_barBottom([self _barDescriptor:(unsigned int)index],(double)height) / (double)MILLIMETERPERPOINT);
}
- (NSString *)_encodeChar:(char)inChar {
return [barFormat substringToIndex:2 * numberLength];
}
- (void)calculateWidth
{
[self setWidth:(float)((double)[[self completeBarcode] length] * (double)[self barWidth])];
}
@end