JANコードリーダー + Yahoo! shopping API
車輪の再発明ってことでバーコードリーダー作ってみました。
webカメラからJANコード(書籍ならISBNコード)を読み取って
Yahoo!ショッピングへのリンクを表示するよ。
「JANコード以外にも、このバーコードが読めるようになると面白いことが出来るよ!」
っていうのがあったら是非教えて下さい。
...
@author @kndys
package
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Graphics;
import flash.display.Loader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.Video;
import flash.net.navigateToURL;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.ByteArray;
[SWF(width="465",height="465")]
/**
* 車輪の再発明ってことでバーコードリーダー作ってみました。
*
* webカメラからJANコード(書籍ならISBNコード)を読み取って
* Yahoo!ショッピングへのリンクを表示するよ。
*
* 「JANコード以外にも、このバーコードが読めるようになると面白いことが出来るよ!」
* っていうのがあったら是非教えて下さい。
* ...
* @author @kndys
*/
public class BarcodeReader extends Sprite
{
private var _w:Number = stage.stageWidth;
private var _h:Number = stage.stageHeight;
private var info:Sprite;
private var button:Sprite;
private var imageLoader:Loader;
private var infoText:TextField;
private var format:TextFormat;
public function BarcodeReader()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
//Wonderfl.disable_capture( );
Wonderfl.capture_delay(30);
//画面の部品もろもろ
info = new Sprite();
info.y = _h * 0.5;
info.graphics.beginFill(0);
info.graphics.drawRect(0, 0, _w, 0.5 * _h);
info.graphics.endFill();
addChild(info);
button = new Sprite();
button.buttonMode = true;
imageLoader = new Loader();
infoText = new TextField();
infoText.autoSize = TextFieldAutoSize.LEFT;
format = new TextFormat(null, 11, 0xffffff);
button.addChild(imageLoader);
info.addChild(button);
info.addChild(infoText);
//カメラのセッティング
var center:int = _h * 0.25;
blendMode = BlendMode.LAYER;
var camera:Camera = Camera.getCamera();
camera.setMode(640, 240, 30);
var video:Video = new Video(640, 240);
video.attachCamera(camera);
var bmd:BitmapData = new BitmapData(video.width, video.height);
var bitmap:Bitmap = new Bitmap(bmd);
addChild(bitmap);
//バーコードリーダーらしい只の演出
var redLineLayer:Shape = new Shape();
redLineLayer.filters = [new BlurFilter(0, 8)];
var redLine:Graphics = redLineLayer.graphics;
redLine.lineStyle(4, 0xff0000, 0.99);
redLine.moveTo(0, center);
redLine.lineTo(_w, center);
redLineLayer.blendMode = BlendMode.ADD;
addChild(redLineLayer);
var ranges:Vector.<uint>;
var rect:Rectangle = new Rectangle(0, center-2, _w, 5);
var bc:Barcode = new Barcode(); //その実態は只の2値化データ
bc.lines = 5; //ライン本数。
bc.threshold = 8; //色が変わったと見なす最小変化量
var jan:JANcoder = new JANcoder();
jan.toloerance = 0.35; //許容誤差。0.5超えると明らかに誤読が増える。
var str:String;
stage.addEventListener(MouseEvent.CLICK, function(mouse:MouseEvent):void {
if (!hasEventListener(Event.ENTER_FRAME))
{
addEventListener(Event.ENTER_FRAME, function(event:Event):void {
bmd.lock();
bmd.draw(video);
bc.pixels = bmd.getPixels(rect);
bmd.setPixels(rect, bc.pixels);
bmd.unlock();
str = jan.decode(bc);
if (str)
{
var barcodeRect:Rectangle = new Rectangle( 10, 10, 220, 60);
var barcodePixels:ByteArray = new ByteArray();
//strをエンコードして高さ1幅任意のビットマップデータを作る
var barcodeLine:ByteArray = jan.encode(str, barcodeRect.width).pixels;
for (var n:int = 0; n< barcodeRect.height; n+=1)
{
barcodeLine.position = 0;
barcodePixels.writeBytes(barcodeLine);
}
barcodePixels.position = 0;
bmd.setPixels(barcodeRect, barcodePixels);
event.target.removeEventListener(Event.ENTER_FRAME, arguments.callee);
onDetectCode(str);
}
});
}
});
stage.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}
private function onDetectCode(code:String):void
{
var appId:String = "S8Xwcuixg64G.378PP9nnSwer3_ijeXhTfUcOCnDyIZVSxgCFivokE5SIhJ00QXuC4uoXk69";
var url:String = "http://shopping.yahooapis.jp/ShoppingWebService/V1/itemSearch?appid=" +
appId + "&jan=" + code;
var textLoader:URLLoader = new URLLoader();
textLoader.addEventListener(Event.COMPLETE, function(event:Event):void {
var yahoo :ItemSearchHits = new ItemSearchHits(XML(textLoader.data));
var lowest:Object = yahoo.getLowestPrice();
if (lowest)
{
if(lowest.imageMediumURL || lowest.imageSmallURL)
{
loadImage(lowest);
}
}
else
{
infoText.text = "ヒットしませんでした"
infoText.setTextFormat(format);
infoText.x = 0.5 * (info.width - infoText.textWidth);
infoText.y = 0.5 * (info.height - infoText.textHeight);
}
});
textLoader.load(new URLRequest(url));
}
private var buttonFunction:Function = null;
private function loadImage(hitObject:Object):void
{
if (buttonFunction != null) button.removeEventListener(MouseEvent.CLICK, buttonFunction);
buttonFunction = function(evt:MouseEvent):void {
navigateToURL(new URLRequest(hitObject.url), "_blank");
}
button.addEventListener(MouseEvent.CLICK, buttonFunction);
var container:Sprite = info;
imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(evt:Event):void {
button.scaleX = button.scaleY = 200 / imageLoader.height;
button.x = (0.5 * _w - button.width - 10);
button.y = 0.5 * (0.5 * _h - button.height);
infoText.text = String(hitObject["name"]) + "\n\n価格: " + String(hitObject.price) + "円\n\n code: " +
String(hitObject.code) + "\n\n" +
(Boolean(hitObject.isAvailable)? "購入できます": "購入できません");
infoText.setTextFormat(format);
infoText.x = button.x + button.width + 20;
infoText.y = button.y + 10;
evt.target.removeEventListener(Event.COMPLETE, arguments.callee);
});
imageLoader.load(new URLRequest(
hitObject.imageMediumURL? hitObject.imageMediumURL: hitObject.imageSmallURL
));
}
}
}
import flash.utils.ByteArray;
import flash.utils.Dictionary;
class Barcode
{
private var _pixels:ByteArray;
private var _data:ByteArray;
private var _lines:uint = 1;
private var _threshold:uint = 0;
private var redVals:Vector.<int>;
private var redGrads:Vector.<int>;
public function Barcode()
{
_pixels = new ByteArray();
_data = new ByteArray();
redVals = new Vector.<int>();
redGrads = new Vector.<int>();
}
public function set pixels(value:ByteArray):void
{
_pixels.clear();
_data.clear();
redVals.splice(0, redVals.length);
redGrads.splice(0, redGrads.length);
value.position = 0;
_pixels.writeBytes(value);
if (_pixels.length < 7)
{
return ;
}
var length:uint = _pixels.length >>> 2;
var lineLength:uint = length / _lines;
_pixels.position = 0;
var i:int;
var argb:uint;
for (i=0; i < length; i += 1)
{
argb = _pixels.readUnsignedInt();
//赤チャネルと緑チャネルを2:1で混合してる。比率はほぼ無根拠。
redVals.push(((argb >>> 15) & 0x1fe ) + ((argb>>>8) & 0xff));
}
redGrads.push(0); //1lineあたりの要素数を合わせる
redGrads.push(0);
for (i = 4; i < length; i += 1)
{
redGrads.push(-redVals[i - 4] - (redVals[i - 3] << 1) + (redVals[i - 1] << 1) + redVals[i]);
}
redGrads.push(0);
redGrads.push(0); //1lineあたりの要素数を合わせる
var maxGrad0:int = 0;
var minGrad0:int = 0;
var maxGrad:int = 0;
var minGrad:int = 0;
var lstGrad:int = 0;
var curGrad:int = 0;
var nexGrad:int = 0;
var state:int = 1; //現在の線の明暗を示す。黒なら+1、白なら-1。
//まず中央から左に向かってスキャンする
for (i = lineLength >> 1 ; i > 0; i -= 1)
{
nexGrad = curGrad;
curGrad = lstGrad;
lstGrad = redGrads[i];
//極大候補発見
if (curGrad > lstGrad && curGrad >= nexGrad)
{
//極大基準値より大きかったら
if (curGrad > maxGrad0)
{
//極大値を更新する
maxGrad0 = (curGrad >> 2) + _threshold;
minGrad0 = -maxGrad0;
}
}
//極小候補発見
else if (curGrad < lstGrad && curGrad <= nexGrad)
{
//極小基準値より小さかったら
if (curGrad < minGrad0)
{
//極小値を更新
minGrad0 = (curGrad >> 2) - _threshold;
maxGrad0 = minGrad0;
}
}
}
maxGrad = maxGrad0;
minGrad = minGrad0;
//次に左端よりスキャン
var lastWidth:Number = 0.0;
var barWidth:Number = 1.0;//左端の1ピクセル
var peak:Number;
curGrad = 0;
nexGrad = 0;
for (i = 2; i < length; i += 1 )
{
if (i % lineLength == 0)
{
_data.writeDouble(lastWidth);
barWidth += 1.0;//右端の1ピクセル
_data.writeDouble(barWidth);
if (state < 0)
{
state = 1;
_data.writeDouble(0.0);
}
lastWidth = 0.0;
barWidth = 1.0;//左端の1ピクセル
curGrad = 0;
nexGrad = 0;
maxGrad = maxGrad0;
minGrad = minGrad0;
i += 2;
}
lstGrad = curGrad;
curGrad = nexGrad;
nexGrad = redGrads[i];
//極大基準値より大きかったら
if (curGrad > lstGrad && curGrad >= nexGrad && curGrad > maxGrad)
{
//極大値を更新する
maxGrad = (curGrad >> 2) + _threshold;
minGrad = -maxGrad;
peak = calPeak(lstGrad, curGrad, nexGrad);
barWidth += (0.5 + peak);
//sum += barWidth;
if (state < 0)
{
//もし白線に切り替わった後だったら、前回の結果に足す
lastWidth += barWidth;
}
else
{
_data.writeDouble(lastWidth);
lastWidth = barWidth;
state = -1;
}
barWidth = 0.5 - peak;
}
//極小基準値より小さかったら
else if (curGrad < lstGrad && curGrad <= nexGrad && curGrad < minGrad)
{
//極小値を更新
minGrad = (curGrad >> 2) - _threshold;
maxGrad = -minGrad;
peak = calPeak(lstGrad, curGrad, nexGrad);
barWidth += (0.5 + peak);
//sum += barWidth;
if (state > 0)
{
//もし黒線に切り替わった後だったら、前回の結果に足す
lastWidth += barWidth;
}
else
{
_data.writeDouble(lastWidth);
lastWidth = barWidth;
state = 1;
}
barWidth = 0.5 - peak;
}
else
{
barWidth += 1.0;
}
}
_data.writeDouble(lastWidth);
barWidth += 1.0;//右端の1ピクセル
_data.writeDouble(barWidth);
}
private function calPeak(yDec:Number, y:Number, yInc:Number):Number
{
var _2b:Number = yInc - yDec ;
var _2a:Number = yInc + yDec - 2 * y;
return -0.5 * _2b / _2a;
}
public function get pixels():ByteArray
{
var value:ByteArray = new ByteArray();
var binColor:uint = 0xff000000;
var grayScale:uint;
var lim:uint;
var dec:Number;
var rem:Number = 0;
var i :uint ;
var length:uint = _data.length >>> 3;
var barWidth:Number;
_data.position = 0;
for (i = 0; i < length; i+=1 )
{
if (dec > 0.000001)//丸め誤差対策
{
rem = 1.0 - dec;
grayScale = 0xff & ((Number(binColor & 0xff) * dec + Number( (~binColor) & 0xff) * rem + 0.5) >> 0);
grayScale = 0xff000000 | (grayScale << 16) | (grayScale << 8) | grayScale;
value.writeUnsignedInt(grayScale);
}
else
{
rem = 0.0;
}
barWidth = _data.readDouble();
barWidth -= rem;
lim = barWidth >> 0; //バー幅の整数部
dec = barWidth - lim; //小数部
binColor ^= 0x00ffffff; //RGB反転
for (; lim > 0;lim-=1 )
{
value.writeUnsignedInt(binColor);
}
}
if (dec > 0.5)
{
value.writeUnsignedInt(binColor);
}
value.position = 0;
return value;
}
public function get data():ByteArray
{
_data.position = 0;
var value:ByteArray = new ByteArray();
value.writeBytes(_data);
value.position = 0;
return value;
}
public function set data(value:ByteArray):void
{
_data.clear();
value.position = 0;
_data.writeBytes(value);
_data.position = 0;
}
public function get lines():uint { return _lines; }
public function set lines(value:uint):void
{
_lines = value;
}
public function get threshold():uint { return _threshold >> 3; }
public function set threshold(value:uint):void
{
_threshold = value << 3;
}
}
class JANcoder
{
private var _toloerance:Number = 0.2;
private var toleranceMax:Number = 1.2;
private var toleranceMin:Number = 0.8;
private var dic:Dictionary;
private const UPSIDE_DOWN:String = "!";
private const leftOddPatterns:Vector.<uint> = new Vector.<uint>();
private const leftEvenPatterns:Vector.<uint> = new Vector.<uint>();
//private const rightOddPatterns:Vector.<uint> = new Vector.<uint>();
private const rightEvenPatterns:Vector.<uint> = new Vector.<uint>();
private const firstCharPatterns:Vector.<uint> = new Vector.<uint>();
public function JANcoder()
{
dic = new Dictionary();
//↓クリップボードに入れて使ったスニペット。便利!
//dic[$$(pat)] = new Code($$(pat), "$$(char)", true, true);
//左側奇数パリティパターン
dic[0x0d] = new Code(0x0d, "0", true, true);
dic[0x19] = new Code(0x19, "1", true, true);
dic[0x13] = new Code(0x13, "2", true, true);
dic[0x3d] = new Code(0x3d, "3", true, true);
dic[0x23] = new Code(0x23, "4", true, true);
dic[0x31] = new Code(0x31, "5", true, true);
dic[0x2f] = new Code(0x2f, "6", true, true);
dic[0x3b] = new Code(0x3b, "7", true, true);
dic[0x37] = new Code(0x37, "8", true, true);
dic[0x0b] = new Code(0x0b, "9", true, true);
leftOddPatterns.push(0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b);
//左側偶数パリティパターン
dic[0x27] = new Code(0x27, "0", true, false);
dic[0x33] = new Code(0x33, "1", true, false);
dic[0x1b] = new Code(0x1b, "2", true, false);
dic[0x21] = new Code(0x21, "3", true, false);
dic[0x1d] = new Code(0x1d, "4", true, false);
dic[0x39] = new Code(0x39, "5", true, false);
dic[0x05] = new Code(0x05, "6", true, false);
dic[0x11] = new Code(0x11, "7", true, false);
dic[0x09] = new Code(0x09, "8", true, false);
dic[0x17] = new Code(0x17, "9", true, false);
leftEvenPatterns.push(0x27, 0x33, 0x1b, 0x21, 0x1d, 0x39, 0x05, 0x11, 0x09, 0x17);
//右側偶数パリティパターン
dic[0x72] = new Code(0x72, "0", false, false);
dic[0x66] = new Code(0x66, "1", false, false);
dic[0x6c] = new Code(0x6c, "2", false, false);
dic[0x42] = new Code(0x42, "3", false, false);
dic[0x5c] = new Code(0x5c, "4", false, false);
dic[0x4e] = new Code(0x4e, "5", false, false);
dic[0x50] = new Code(0x50, "6", false, false);
dic[0x44] = new Code(0x44, "7", false, false);
dic[0x48] = new Code(0x48, "8", false, false);
dic[0x74] = new Code(0x74, "9", false, false);
rightEvenPatterns.push(0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74);
//右側奇数パリティパターン
//本来右側に奇数パリティが現れることは無いので
//左側が全て偶数パリティならバーコードは逆さま
dic[0x58] = new Code(0x58, "0", false, true);
dic[0x4c] = new Code(0x4c, "1", false, true);
dic[0x64] = new Code(0x64, "2", false, true);
dic[0x5e] = new Code(0x5e, "3", false, true);
dic[0x62] = new Code(0x62, "4", false, true);
dic[0x46] = new Code(0x46, "5", false, true);
dic[0x7a] = new Code(0x7a, "6", false, true);
dic[0x6e] = new Code(0x6e, "7", false, true);
dic[0x76] = new Code(0x76, "8", false, true);
dic[0x68] = new Code(0x68, "9", false, true);
//左側6文字から最初の文字を決定する
//(isOdd?1:0)として
dic[0xbf] = new Code(0x3f, "0", true, true);
dic[0xb4] = new Code(0x34, "1", true, true);
dic[0xb2] = new Code(0x32, "2", true, true);
dic[0xb1] = new Code(0x31, "3", true, true);
dic[0xac] = new Code(0x2c, "4", true, true);
dic[0xa6] = new Code(0x26, "5", true, true);
dic[0xa3] = new Code(0x23, "6", true, true);
dic[0xaa] = new Code(0x2a, "7", true, true);
dic[0xa9] = new Code(0x29, "8", true, true);
dic[0xa5] = new Code(0x25, "9", true, true);
dic[0x80] = new Code(0x80, UPSIDE_DOWN, true, true);
firstCharPatterns.push(0x3f, 0x34, 0x32, 0x31, 0x2c, 0x26, 0x23, 0x2a, 0x29, 0x25, 0x80);
}
public function get toloerance():Number { return _toloerance; }
public function set toloerance(value:Number):void
{
_toloerance = value;
toleranceMax = 1 + _toloerance;
toleranceMin = 1 - _toloerance;
}
private function nearlyEqual(x:Number, y:Number):Boolean
{
var ratio:Number = y / x;
return (ratio > toleranceMin && ratio < toleranceMax);
}
private function generatePattern(isLefty:Boolean,
w1:Number, w2:Number, w3:Number, w4:Number):uint
{
var sum:Number = w1 + w2 + w3 + w4;
var wid:uint = 7;
var k:Number = wid / sum;
var pos:Vector.<uint> = new Vector.<uint>();
pos.push((k * w1 + 0.5) >> 0);
sum -= w1;
wid -= pos[0];
k = wid / sum;
pos.push((k * w2 + 0.5) >> 0);
sum -= w2;
wid -= pos[1];
k = wid / sum;
pos.push((k * w3 + 0.5) >> 0);
sum -= w3;
wid -= pos[2];
k = wid / sum;
pos.push((k * w4 + 0.5) >> 0);
var pattern:uint = 0;
var bit:uint = 0;
if (!isLefty)
{
bit ^= 1;
}
for (var i:int = 0; i< 4; i+=1)
{
for (var j:int = 0; j < pos[i]; j+=1 )
{
pattern <<= 1;
pattern |= bit;
}
bit ^= 1;
}
return pattern;
}
private function isLeftBars(space1:Number, bar1:Number, space2:Number, bar2:Number):Boolean
{
var min:Number = Math.min(bar1, space2, bar2);
var max:Number = Math.max(bar1, space2, bar2);
return max < 0.2 * space1 && nearlyEqual(min , max);
}
private function isCenterBars(
space1:Number, bar1:Number, space2:Number, bar2:Number, space3:Number):Boolean
{
var min:Number = Math.min(space1, bar1, space2, bar2, space3);
var max:Number = Math.max(space1, bar1, space2, bar2, space3);
return nearlyEqual(min, max);
}
private function isRightBars(bar1:Number, space1:Number, bar2:Number, space2:Number):Boolean
{
var min:Number = Math.min(bar1, space1, bar2);
var max:Number = Math.max(bar1, space1, bar2);
return max < 0.2 * space2 && nearlyEqual(min , max);
}
private function checkDigits(codes:Vector.<Code>):Boolean
{
if (codes.length != 13) return false;
var sum:int;
for (var i:int = 0; i< 12; i+=1)
{
sum += (1 + 2 * (i % 2)) * parseInt(codes[i].character, 10);
}
return parseInt(codes[12].character, 10) == (10 - sum % 10) % 10;
}
public function decode(barcode:Barcode):String
{
var data:ByteArray = barcode.data;
var length:uint = data.length;
if (length < 45) //JANコードを表現出来るだけの線の数が無ければ即終了
{
return null;
}
var codes:Vector.<Code>;
var space1:Number;
var bar1:Number;
var space2:Number;
var bar2:Number;
var space3:Number;
var code:Code;
var str:String;
var parities:uint;
var readingPosition:uint;
var i:uint;
data.position = 0;
//JANコードが見つかるまでこのループからは出られない。
MAIN_LOOP:
while(true)
{
space1 = data.readDouble();
if (data.position == length) { return null; }
bar1 = data.readDouble();
readingPosition = data.position; //読み取れなかったらここに戻る
if (data.position == length) { return null; }
space2 = data.readDouble();
if (data.position == length) { return null; }
bar2 = data.readDouble();
if (data.position == length) { return null; }
if (isLeftBars(space1, bar1, space2, bar2))
{
//Vectorの生成コストを抑えるために、必要になってから生成。
if (!(codes)) { codes = new Vector.<Code>(); }
//Vectorが存在していたら空にする。
else { codes.splice(0, codes.length); }
LEFT:
while (true)
{
space1 = data.readDouble();
if (data.position == length) { return null; }
bar1 = data.readDouble();
if (data.position == length) { return null; }
space2 = data.readDouble();
if (data.position == length) { return null; }
bar2 = data.readDouble();
if (data.position == length) { return null; }
space3 = data.readDouble();
if (data.position == length) { return null; }
if (isCenterBars(space1, bar1, space2, bar2, space3)) break LEFT;
code = dic[generatePattern(true, space1, bar1, space2, bar2)];
if (!code || !(code.isLefty)) //もしパターンが辞書にないか、右側のパターンだったら
{
// 若干黒線が細かったとしたら
code = dic[generatePattern(true, space1 * toleranceMin, bar1 * toleranceMax,
space2 * toleranceMin, bar2 * toleranceMax)];
if (!code || !(code.isLefty))
{
//若干黒線が太かったとしたら
code = dic[generatePattern(true, space1 * toleranceMax, bar1 * toleranceMin,
space2 * toleranceMax, bar2 * toleranceMin)];
if (!code || !(code.isLefty))
{
data.position = readingPosition;
continue MAIN_LOOP;
}
}
}
codes.push(code);
data.position -= 8;
} // LEFT
RIGHT:
while (true)
{
bar1 = data.readDouble();
if (data.position == length) { return null; }
space1 = data.readDouble();
if (data.position == length) { return null; }
bar2 = data.readDouble();
if (data.position == length) { return null; }
space2 = data.readDouble();
if (data.position == length) { return null; }
if (isRightBars(bar1, space1, bar2, space2)) break RIGHT;
code = dic[generatePattern(false, bar1, space1, bar2, space2)];
if (!code || code.isLefty) //もしパターンが辞書にないか、左側のパターンだったら
{
code = dic[generatePattern(false, bar1 * toleranceMax, space1 * toleranceMin,
bar2 * toleranceMax, space2 * toleranceMin)];
if (!code || code.isLefty)
{
code = dic[generatePattern(false, bar1 * toleranceMin, space1 * toleranceMax,
bar2 * toleranceMin, space2 * toleranceMax)];
if (!code || code.isLefty)
{
data.position = readingPosition;
continue MAIN_LOOP;
}
}
}
codes.push(code);
} // RIGHT
parities = 0;
for (i = 0; i < 6; i++)
{
if (!(codes[i].isLefty))
{
//初めの6文字の中に右側のコードが混ざったらやり直し
continue MAIN_LOOP;
}
parities <<= 1;
if (codes[i].isOdd)
{
parities |= 1;
}
}
if (parities == 0) //パリティが全て偶数ならバーコードは逆さま
{
codes.reverse();
parities = 0;
for (i = 0; i < 6; i++)
{
if ((codes[i].isLefty))
{
//初めの6文字の中に左側のコードが混ざったらやり直し
continue MAIN_LOOP;
}
parities <<= 1;
if (codes[i].isOdd)
{
parities |= 1;
}
}
}
code = dic[parities | 0x80];
if (code)
{
codes.unshift(code);
}
if (checkDigits(codes))
{
break MAIN_LOOP;
}
} // if(isLeftBar)
//JANコードが見つからなかった場合ここを通ってMAIN_LOOPに戻る
data.position = readingPosition;
} // MAIN_LOOP
//JANコード発見&ループ脱出
str = "";
for each(code in codes)
{
str += code.character;
}
//trace(str);
return str;
}
public function encode(codeNumbers:String, lineWidth:uint):Barcode
{
//dataが数字x13の繰り返しでなければnull返す。
if (!(/^\D*(?:(\d)\D*){13}$/.test(codeNumbers))) { return null; }
codeNumbers = codeNumbers.replace(/\D/g, "");
var i:int;
var num:uint = parseInt(codeNumbers.charAt(i));
var parities:Vector.<Boolean> = new Vector.<Boolean>();
var msk:uint = 0x20;
var pat:uint = firstCharPatterns[num];
for (i = 0; i < 6; i += 1)
{
parities.push((pat & msk) != 0);
msk >>>= 1;
}
var patterns:Vector.<uint> = new Vector.<uint>();
for (i = 1; i< 7; i+=1)
{
num = parseInt(codeNumbers.charAt(i));
if (parities[i-1]) { patterns.push(leftOddPatterns[num]); }
else { patterns.push(leftEvenPatterns[num]); }
}
for (i = 7; i < 13; i += 1)
{
num = parseInt(codeNumbers.charAt(i));
patterns.push(rightEvenPatterns[num]);
}
var unit:Number = lineWidth / 115;// 115 = 13 + 7*6 + 5 + 7*6 + 13
var data:ByteArray = new ByteArray();
data.clear();
// write left guard bars
data.writeDouble(unit * 10);
data.writeDouble(unit);
data.writeDouble(unit);
data.writeDouble(unit);
var j:int;
var state:Boolean = false;
var wid:Number;
for (i = 0; i < 12; i += 1)
{
if (i == 6) // write center bars
{
data.writeDouble(unit);
data.writeDouble(unit);
data.writeDouble(unit);
data.writeDouble(unit);
data.writeDouble(unit);
state = !state;
}
msk = 0x40;
pat = patterns[i];
wid = 0.0;
for (j = 0; j < 7; j += 1)
{
if (((msk & pat) != 0) == state)
{
wid += unit;
}
else
{
data.writeDouble(wid);
state = !state;
wid = unit;
}
msk >>>= 1;
}
data.writeDouble(wid);
state = !state;
}
// write right guard bars
data.writeDouble(unit);
data.writeDouble(unit);
data.writeDouble(unit);
data.writeDouble(unit * 10);
data.position = 0;
var barcode:Barcode = new Barcode();
barcode.data = data;
return barcode;
}
}
class Code
{
public var pettern:uint;
public var character:String;
public var isLefty:Boolean;
public var isOdd:Boolean;
public function Code(pettern:uint, char:String,isLefty:Boolean, isOdd:Boolean)
{
this.pettern = pettern;
this.character = char;
this.isLefty = isLefty;
this.isOdd = isOdd;
}
}
class ItemSearchHits
{
private var list:XMLList;
private var hits:Vector.<Object>;
public function ItemSearchHits(resultList:XML)
{
var ns:Namespace = resultList.namespace();
default xml namespace = ns;
if (resultList.@totalResultsReturned == "0") return;
list = resultList..Hit;
hits = new Vector.<Object>();
var obj:Object;
for each (var hit:XML in list)
{
obj = {
"index" : hit.@index.toString(),
"name" : hit.Name.toString(),
"url" : hit.Url.toString(),
"isAvailable" : hit.Availability.toString() == "instock",
"imageSmallURL" : hit.Image.Small.toString(),
"imageMediumURL" : hit.Image.Medium.toString(),
"price" : parseInt(hit.Price.toString()),
"code" : (hit.JanCode?hit.JanCode.toString():hit.IsbnCode.toString())
};
hits.push(obj);
}
}
public function toString():String
{
var str:String = "";
for each(var obj:Object in hits)
{
str += "{"
for (var prop:String in obj)
{
str += prop + ": " + obj[prop] + ", ";
}
str += "}\n";
}
return str;
}
public function getLowestPrice(onlyAvailable:Boolean = false):Object
{
var price:int = int.MAX_VALUE;
var result:Object;
for each( var obj:Object in hits)
{
if ((!onlyAvailable || obj.isAvailable) && obj.price < price)
{
result = obj;
price = obj.price;
}
}
return result;
}
}