001    /*
002     * Copyright (c) 2009 The openGion Project.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013     * either express or implied. See the License for the specific language
014     * governing permissions and limitations under the License.
015     */
016    package org.opengion.fukurou.util;
017    
018    import java.awt.Component;
019    import java.awt.Graphics;
020    import java.awt.image.BufferedImage;
021    
022    import java.io.File;
023    import java.io.IOException;
024    import javax.imageio.ImageIO;
025    
026    import com.swetake.util.Qrcode;
027    
028    /**
029     * QrcodeImage ã¯ã€?¼“次å…?ƒãƒ¼ã‚³ãƒ¼ãƒ‰ã? QRコードイメージを生æˆã™ã‚?
030     * 独立ã—ãŸã‚³ãƒ³ãƒã?ãƒãƒ³ãƒˆã§ã™ã?
031     * ã“ã“ã§ã®ä½¿ã?–¹ã¯ã€å?期化時ã«ã€ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã™ã‚‹æ–‡å­—å?(120Byte以å†?ã¨ã€?
032     * 出力ファイルåã‚’æŒ?®šã—ã¦ã€Graphics ã«æç”»ã—ãŸQRコードイメージã‚?
033     * JPEG 変æ›ã—ã?æŒ?®šã?ファイルã«ä¸Šæ›¸ãä¿å­˜ã—ã¦ã?¾ã™ã?
034     * QRコード作æ?ã«ã€http://www.swetake.com/ ã® Qrcode クラスを使用ã—ã¦ã?¾ã™ã?
035     * ã“れãŒã?2004/11/7  ver.0.50beta9 ã¨ã®ã“ã¨ãªã®ã§ã€å‹•作ãƒã‚§ãƒ?‚¯ã€ãŠã‚ˆã?ã€?
036     * 製å“ã¨ã—ã¦ã®ä¿è¨¼ã¨ã?†æ„味ã§ã¯ã€ã¾ã?½¿ãˆã‚‹ãƒ¬ãƒ™ãƒ«ã§ã¯ã‚りã¾ã›ã‚“ãŒã?
037     * コード計算ã•ãˆãƒã‚°ã£ã¦ã?ªã‘れã°ä½¿ãˆã†ã‚‹ç‚ºã€è©¦é¨“的導å?致ã—ã¾ã™ã?
038     *
039     * @version  4.0
040     * @author   Kazuhiko Hasegawa
041     * @since    JDK5.0,
042     */
043    public class QrcodeImage extends Component {
044            //* ã“ã?プログラãƒ??VERSIONæ–?­—å?を設定ã—ã¾ã™ã?       {@value} */
045            private static final String VERSION = "5.7.1.1 (2013/12/13)" ;
046    
047            private static final long serialVersionUID = 571120131213L ;
048    
049            /** エラー訂正レベル ('L','M','Q','H')  H ã¨M ã®ã¿ã‚µãƒã?ãƒ?*/
050            public static enum ErrCrct {
051                                            H(0),M(1);
052                                            private int no;
053                                            public static final ErrCrct DEF = M;
054                                            ErrCrct(int no) { this.no = no; }
055                                            public int getNo() { return no; }
056                                            public char getCh() {
057                                                    final char ch ;
058                                                    switch(this) {
059                                                            case H:  ch = 'H'; break;
060                                                            case M:
061                                                            default: ch = 'M'; break;
062                                                    }
063                                                    return ch ;
064                                            }
065                                            public static ErrCrct get( final char ch ) {
066                                                    final ErrCrct rtn ;
067                                                    switch(ch) {
068                                                            case 'H': rtn = H; break;
069                                                            case 'M':
070                                                            default:  rtn = M; break;
071                                                    }
072                                                    return rtn ;
073                                            }
074            };
075    
076            /** エンコードモーãƒ?('N':数字モーãƒ?'A':英数字モードã?'B':8bit byteモーãƒ? */
077            public static enum EncMode {
078                                            N(0),A(1),B(1);
079                                            private int no;
080                                            public static final EncMode DEF = B;
081                                            EncMode(int no) { this.no = no; }
082                                            public int getNo() { return no; }
083                                            public char getCh() {
084                                                    final char ch ;
085                                                    switch(this) {
086                                                            case N:  ch = 'N'; break;
087                                                            case A:  ch = 'A'; break;
088                                                            case B:
089                                                            default: ch = 'B'; break;
090                                                    }
091                                                    return ch ;
092                                            }
093                                            public static EncMode get( final char ch ) {
094                                                    final EncMode rtn ;
095                                                    switch(ch) {
096                                                            case 'N': rtn = N; break;
097                                                            case 'A': rtn = A; break;
098                                                            case 'B':
099                                                            default:  rtn = B; break;
100                                                    }
101                                                    return rtn ;
102                                            }
103            };
104    
105            /** ãƒã?ジョン (1ã‹ã‚‰40ã®æ•´æ•°ã€?を設定ã™ã‚‹ã¨è‡ªå‹•設定ã«ãªã‚Šã¾ã™ã?) åˆæœŸå€¤:{@value}        */
106    //      public static final int VERSION = 5;
107            public static final int DEF_VERSION = 5;                // 5.7.1.1 (2013/12/13) VERSION ãƒã‚§ãƒ?‚¯ã®ãŸã‚ã«ã€IDを変更ã—ã¾ã™ã?
108    
109            /** セルã®ãƒžã?ジン åˆæœŸå€¤:{@value}        */
110            public static final int MARGIN = 4;
111    
112            /** ?‘セル辺りã?塗りã¤ã¶ã—ピクセル åˆæœŸå€¤:{@value}   */
113            public static final int PIXEL = 3;
114    
115            /** 出力イメージã®ã‚¿ã‚¤ãƒ?PNG/JPEG) åˆæœŸå€¤:{@value} */
116            public static final String IMAGE_TYPE = "PNG";
117    
118            private String qrData;
119            private String saveFile;
120            private String imgType  = IMAGE_TYPE ;
121            private ErrCrct errCo   = ErrCrct.DEF ;
122            private EncMode encMd   = EncMode.DEF ;
123    //      private int             version = VERSION ;
124            private int             version = DEF_VERSION ;                 // 5.7.1.1 (2013/12/13) VERSION ãƒã‚§ãƒ?‚¯ã®ãŸã‚ã«ã€IDを変更ã—ã¾ã™ã?
125            private int             pixel   = PIXEL ;
126    
127            private int imageSize ;
128    
129            /**
130             * åˆæœŸåŒ–メソãƒ?ƒ‰
131             *
132             * エラー訂正レベル:M , マã?ジン:4(セルåˆ? , 塗りã¤ã¶ã—ピクセル:3
133             * エンコードモーãƒ?B(ãƒã‚¤ãƒŠãƒª) ã€ãƒãƒ¼ã‚¸ãƒ§ãƒ³:5 , イメージã®ã‚¿ã‚¤ãƒ?PNG
134             * ã«åˆæœŸåŒ–ã•れã¾ã™ã?
135             *
136             * @og.rev 5.7.1.1 (2013/12/13) VERSION ãƒã‚§ãƒ?‚¯ã®ãŸã‚ã«ã€VERSION â‡?DEF_VERSION ã«å¤‰æ›´ã—ã¾ã™ã?
137             *
138             * @param   qrData  エンコードã™ã‚‹æ–‡å­—å?(120Byte 以å†?
139             * @param   saveFile 出力ファイルå?
140             */
141            public void init( final String qrData,final String saveFile ) {
142    //              init( qrData,saveFile,VERSION,EncMode.DEF,ErrCrct.DEF,IMAGE_TYPE,PIXEL );
143                    init( qrData,saveFile,DEF_VERSION,EncMode.DEF,ErrCrct.DEF,IMAGE_TYPE,PIXEL );   // 5.7.1.1 (2013/12/13)
144            }
145    
146            /**
147             * åˆæœŸåŒ–メソãƒ?ƒ‰
148             *
149             * エラー訂正レベル:M , マã?ジン:4(セルåˆ? , 塗りã¤ã¶ã—ピクセル:3
150             * イメージã®ã‚¿ã‚¤ãƒ?PNG ã«åˆæœŸåŒ–ã•れã¾ã™ã?
151             *
152             * @param   qrData   エンコードã™ã‚‹æ–‡å­—å?(120Byte 以å†?
153             * @param   saveFile 出力ファイルå?
154             * @param   version  ãƒã?ジョン (1ã‹ã‚‰40ã®æ•´æ•°ã€?を設定ã™ã‚‹ã¨è‡ªå‹•設定ã«ãªã‚Šã¾ã™ã?)
155             * @param   encMd    エンコードモーãƒ?('N':数字モーãƒ?'A':英数字モードã?'B':8bit byteモーãƒ?
156             */
157            public void init( final String qrData,final String saveFile,final int version,final EncMode encMd ) {
158                    init( qrData,saveFile,version,encMd,ErrCrct.DEF,IMAGE_TYPE,PIXEL );
159            }
160    
161            /**
162             * åˆæœŸåŒ–メソãƒ?ƒ‰
163             *
164             * エラー訂正レベル:M , マã?ジン:4(セルåˆ? , 塗りã¤ã¶ã—ピクセル:3
165             *
166             * @param   qrData   エンコードã™ã‚‹æ–‡å­—å?(120Byte 以å†?
167             * @param   saveFile 出力ファイルå?
168             * @param   version  ãƒã?ジョン (1ã‹ã‚‰40ã®æ•´æ•°ã€?を設定ã™ã‚‹ã¨è‡ªå‹•設定ã«ãªã‚Šã¾ã™ã?)
169             * @param   encMd    エンコードモーãƒ?'N':数字モーãƒ?'A':英数字モードã?'B':8bit byteモーãƒ?
170             * @param   errCo    エラー訂正レベル ('L','M','Q','H')
171             * @param   imgType  イメージファイル形�PNG/JPEG)
172             * @param   pixel    ?‘セル辺りã?塗りã¤ã¶ã—ピクセル
173             */
174            public void init( final String qrData,final String saveFile,final int version,final EncMode encMd,
175                                                    final ErrCrct errCo ,final String imgType,final int pixel ) {
176    
177                    this.qrData   = qrData;
178                    this.saveFile = saveFile;
179                    this.imgType  = imgType;
180                    this.errCo    = errCo;
181                    this.encMd    = encMd;
182                    this.version  = version;
183                    this.pixel    = pixel;
184    
185                    imageSize = ( MARGIN*2 + 17 + version*4 )*pixel;
186            }
187    
188            /**
189             * æç”»å‡¦ç?‚’行ã„ã¾ã™ã?
190             *
191             * @param   gpx  Graphicsオブジェク�
192             */
193            @Override
194            public void paint( final Graphics gpx ) {
195                    Qrcode qrc =new Qrcode();
196                    qrc.setQrcodeErrorCorrect(errCo.getCh());
197                    qrc.setQrcodeEncodeMode(encMd.getCh());
198                    qrc.setQrcodeVersion(version);
199    
200    //              byte[] dt =qrData.getBytes();
201                    byte[] dt =qrData.getBytes( StringUtil.DEFAULT_CHARSET );       // 5.5.2.6 (2012/05/25) findbugs対�
202                    boolean[][] sfg = qrc.calQrcode( dt );
203    
204                    int size = sfg.length;
205                    int mgn  = MARGIN*pixel ;
206                    for( int i=0; i<size; i++ ) {
207                            for( int j=0; j<size; j++ ) {
208                                    if( sfg[j][i] ) {
209                                            gpx.fillRect( mgn+j*pixel,mgn+i*pixel,pixel,pixel );
210                                    }
211                            }
212                    }
213            }
214    
215            /**
216             * æç”»å‡¦ç?‚’行ã„ã¾ã™ã?
217             *
218             */
219            public void saveImage() {
220                    // 出力用イメージã®ç”Ÿæ?
221                    BufferedImage image = new BufferedImage(imageSize, imageSize, BufferedImage.TYPE_INT_BGR );
222    
223                    // イメージã‹ã‚‰ã‚°ãƒ©ãƒ•ã‚£ãƒ?‚¯ã‚³ãƒ³ãƒ?‚­ã‚¹ãƒˆã‚’å–å¾?
224                    Graphics grph = image.getGraphics();
225                    grph.setColor( java.awt.Color.WHITE );
226                    grph.fillRect( 0,0,imageSize, imageSize );
227                    grph.setColor( java.awt.Color.BLACK );
228    
229                    // JEditorPane ã‚’ã‚¤ãƒ¡ãƒ¼ã‚¸ã«æ›¸ãè¾¼ã‚?
230                    // paintComponent 㯠proteced ãªã®ã§ä½¿ç”¨ã§ããªã?
231                    paint( grph );
232    
233                    // 使ã?µ‚ã‚ã£ãŸã‚°ãƒ©ãƒ•ã‚£ãƒ?‚¯ã‚³ãƒ³ãƒ?‚­ã‚¹ãƒˆã‚’開放
234                    grph.dispose();
235    
236                    try {
237                            // イメージã®å‡ºåŠ?Image I/O を使用
238                            ImageIO.write( image, imgType, new File( saveFile ) );
239                    } catch ( IOException ex ) {
240                            String errMsg = "イメージファイルã®å‡ºåŠ›ã«å¤±æ•—ã—ã¾ã—ãŸã€?
241                                            + "File=[" + saveFile + "]" ;
242                            throw new RuntimeException( errMsg,ex );
243                    }
244            }
245    
246            /**
247             * メイン処ç?§ã™ã?
248             * Usage: java org.opengion.fukurou.util.QrcodeImage Encode [SaevFile]
249             *
250             * @param  args  引数æ–?­—å?é…å?
251             */
252            public static void main( final String[] args ) {
253                    if( args.length == 0 ) {
254                            LogWriter.log( "Usage: java org.opengion.fukurou.util.QrcodeImage Encode [SaevFile]" );
255                            return ;
256                    }
257    
258                    String qrcode = args[0];
259                    String file   = (args.length > 1) ? args[1] : "img.png";
260    
261                    QrcodeImage qrImage = new QrcodeImage();
262                    qrImage.init( qrcode,file );
263                    qrImage.saveImage();
264            }
265    
266            /**
267             * å†?ƒ¨ãƒ??タを標準å?力ã¸å‡ºåŠ›ã—ã¾ã™ã?
268             *
269             */
270            public static void printQcData() {
271                    char[] strJ = new char[] { 'H','M' };
272                    char[] strK = new char[] { 'N','A','B' };
273    
274                    for( int i=0; i<QC_DATA.length; i++ ) {
275                            System.out.print( "version=[" + (i+1) + "] " );
276                            for( int j=0; j<QC_DATA[i].length; j++ ) {
277                                    char errCo = strJ[j];
278                                    for( int k=0; k<QC_DATA[i][j].length; k++ ) {
279                                            System.out.print( errCo + strK[k] + "=[" + QC_DATA[i][j][k] + "] " );
280                                    }
281                            }
282                            System.out.println();
283                    }
284            }
285    
286            /**
287             * ãƒã?ジョンæƒ??ã‚’å–å¾—ã—ã¾ã™ã?
288             *
289             * @param   errCo       エラー訂正レベル ('L','M','Q','H')
290             * @param       encMd   エンコードモーãƒ?('N':数字モーãƒ?'A':英数字モードã?'B':8bit byteモーãƒ?
291             * @param       len             対象ç¯?›²
292             *
293             * @return      ãƒã?ジョンæƒ??
294             */
295            public static int getVersion( final ErrCrct errCo, final EncMode encMd, final int len ) {
296                    int errCoInt = errCo.getNo() ;
297                    int encMdInt = encMd.getNo() ;
298    
299                    int rtn = -1;
300                    for( int i=0; i<QC_DATA.length; i++ ) {
301                            if( QC_DATA[i][errCoInt][encMdInt] >= len ) {
302                                    rtn = i;
303                                    break;
304                            }
305                    }
306    
307                    if( rtn < 0 ) {
308                            String errMsg = "ãƒ??ã‚¿é‡ãŒå¯¾è±¡ç¯?›²ã‚’è¶?ˆã¦ã?¾ã™ã?エラーレベルã‚??モードを調整ã—ã¦ãã ã•ã„ã€?
309                                                    + "ErrCo:" + errCo + " EncMd:" + encMd + " len=[" + len + "]"
310                                                    + " MaxLen=[" + QC_DATA[QC_DATA.length-1][errCoInt][encMdInt] + "]" ;
311                            throw new RuntimeException( errMsg );
312                    }
313    
314                    return rtn + 1 ;
315            }
316    
317            /**
318             * æœ?¤§ã‚µã‚¤ã‚ºã‚’å–å¾—ã—ã¾ã™ã?
319             *
320             * @param       version ãƒã?ジョンæƒ??
321             * @param   errCo       エラー訂正レベル ('L','M','Q','H')
322             * @param       encMd   エンコードモーãƒ?('N':数字モーãƒ?'A':英数字モードã?'B':8bit byteモーãƒ?
323             *
324             * @return      æœ?¤§ã‚µã‚¤ã‚º
325             */
326            public static int getMaxSize( final int version, final ErrCrct errCo, final EncMode encMd ) {
327                    int errCoInt = errCo.getNo() ;
328                    int encMdInt = encMd.getNo() ;
329    
330                    return QC_DATA[version][errCoInt][encMdInt] ;
331            }
332    
333            // version  0 ??39 => 1 ??40
334            // errCo    H,M    0:H 1:M
335            // encMd    N,A,B  0:N 1:A 2:B
336            private static final int[][][] QC_DATA = new int[][][] {
337                    {{   17 ,   10 ,   4 } , {   34 ,   20 ,    8 }} ,
338                    {{   34 ,   20 ,   8 } , {   63 ,   38 ,   16 }} ,
339                    {{   58 ,   35 ,  15 } , {  101 ,   61 ,   26 }} ,
340                    {{   82 ,   50 ,  21 } , {  149 ,   90 ,   38 }} ,
341                    {{  106 ,   64 ,  27 } , {  202 ,  122 ,   52 }} ,
342                    {{  139 ,   84 ,  36 } , {  255 ,  154 ,   65 }} ,
343                    {{  154 ,   93 ,  39 } , {  293 ,  178 ,   75 }} ,
344                    {{  202 ,  122 ,  52 } , {  365 ,  221 ,   93 }} ,
345                    {{  235 ,  143 ,  60 } , {  432 ,  262 ,  111 }} ,
346                    {{  288 ,  174 ,  74 } , {  513 ,  311 ,  131 }} ,
347                    {{  331 ,  200 ,  85 } , {  604 ,  366 ,  155 }} ,
348                    {{  374 ,  227 ,  96 } , {  691 ,  419 ,  177 }} ,
349                    {{  427 ,  259 , 109 } , {  796 ,  483 ,  204 }} ,
350                    {{  468 ,  283 , 120 } , {  871 ,  528 ,  223 }} ,
351                    {{  530 ,  321 , 136 } , {  991 ,  600 ,  254 }} ,
352                    {{  602 ,  365 , 154 } , { 1082 ,  656 ,  277 }} ,
353                    {{  674 ,  408 , 173 } , { 1212 ,  734 ,  310 }} ,
354                    {{  746 ,  452 , 191 } , { 1346 ,  816 ,  345 }} ,
355                    {{  813 ,  493 , 208 } , { 1500 ,  909 ,  384 }} ,
356                    {{  919 ,  557 , 235 } , { 1600 ,  970 ,  410 }} ,
357                    {{  969 ,  587 , 248 } , { 1708 , 1035 ,  438 }} ,
358                    {{ 1056 ,  640 , 270 } , { 1872 , 1134 ,  480 }} ,
359                    {{ 1108 ,  672 , 284 } , { 2059 , 1248 ,  528 }} ,
360                    {{ 1228 ,  744 , 315 } , { 2188 , 1326 ,  561 }} ,
361                    {{ 1286 ,  779 , 330 } , { 2395 , 1451 ,  614 }} ,
362                    {{ 1425 ,  864 , 365 } , { 2544 , 1542 ,  652 }} ,
363                    {{ 1501 ,  910 , 385 } , { 2701 , 1637 ,  692 }} ,
364                    {{ 1581 ,  958 , 405 } , { 2857 , 1732 ,  732 }} ,
365                    {{ 1677 , 1016 , 430 } , { 3035 , 1839 ,  778 }} ,
366                    {{ 1782 , 1080 , 457 } , { 3289 , 1994 ,  843 }} ,
367                    {{ 1897 , 1150 , 486 } , { 3486 , 2113 ,  894 }} ,
368                    {{ 2022 , 1226 , 518 } , { 3693 , 2238 ,  947 }} ,
369                    {{ 2157 , 1307 , 553 } , { 3909 , 2369 , 1002 }} ,
370                    {{ 2301 , 1394 , 590 } , { 4134 , 2506 , 1060 }} ,
371                    {{ 2361 , 1431 , 605 } , { 4343 , 2632 , 1113 }} ,
372                    {{ 2524 , 1530 , 647 } , { 4588 , 2780 , 1176 }} ,
373                    {{ 2625 , 1591 , 673 } , { 4775 , 2894 , 1224 }} ,
374                    {{ 2735 , 1658 , 701 } , { 5039 , 3054 , 1292 }} ,
375                    {{ 2927 , 1774 , 750 } , { 5313 , 3220 , 1362 }} ,
376                    {{ 3057 , 1852 , 784 } , { 5596 , 3391 , 1435 }} } ;
377    }