In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: JANコードリーダー + Javascript

JANコードリーダー + Javascript
// forked from knd's JANコードリーダー + Yahoo! shopping API
// JANコードリーダー + Javascript
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.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.utils.ByteArray;
    import flash.external.*;
    
    
    [SWF(width="400",height="150")]    
    /**
     * webカメラからJANコード(書籍ならISBNコード)を読み取って
     * Javascriptへ渡します
     * ...
     * @author @chibiegg
     */
    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.8;
            info.graphics.beginFill(0);
            info.graphics.drawRect(0, 0, _w, 0.2 * _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.4;
            blendMode = BlendMode.LAYER;
            var camera:Camera = Camera.getCamera();
            camera.setMode(640, 120, 30);
            var video:Video = new Video(640, 120);
            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, 0x0000ff, 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 = 7; //ライン本数
            bc.threshold = 6; //色が変わったと見なす最小変化量
            var jan:JANcoder = new JANcoder();
            jan.toloerance = 0.4; //許容誤差 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, 100, 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
        {
                    infoText.text = "スキャン成功(" + code + ")\nクリックで新しいスキャン";
                    infoText.setTextFormat(format);
                    infoText.x = 0.5 * (info.width - infoText.textWidth);
                    infoText.y = 0.5 * (info.height - infoText.textHeight);
                    ExternalInterface.call("getBarcode",code);
        }
        private var buttonFunction:Function = null;
   
        
    }
    
}
    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;
        }
    }