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 */
016package org.opengion.fukurou.util;
017
018import java.awt.Component;
019import java.awt.Graphics;
020import java.awt.image.BufferedImage;
021import java.io.File;
022import java.io.IOException;
023import javax.imageio.ImageIO;
024//import java.nio.charset.Charset;                                                                      // 7.2.3.0 (2020/04/10) 8.4.1.0 (2023/02/10) Delete
025//import java.nio.charset.StandardCharsets;                                                     // 7.2.3.0 (2020/04/10) 8.4.1.0 (2023/02/10) Delete
026
027import java.util.HashMap;                                                                                       // 8.4.1.0 (2023/02/10) Add
028import java.util.Map;                                                                                           // 8.4.1.0 (2023/02/10) Add
029
030//import com.swetake.util.Qrcode;                                                                       // 8.4.1.0 (2023/02/10) Delete
031import com.google.zxing.BarcodeFormat;                                                          // 8.4.1.0 (2023/02/10) Add
032import com.google.zxing.EncodeHintType;                                                         // 8.4.1.0 (2023/02/10) Add
033import com.google.zxing.WriterException;                                                        // 8.4.1.0 (2023/02/10) Add
034import com.google.zxing.client.j2se.MatrixToImageWriter;                        // 8.4.1.0 (2023/02/10) Add
035import com.google.zxing.common.BitMatrix;                                                       // 8.4.1.0 (2023/02/10) Add
036import com.google.zxing.qrcode.QRCodeWriter;                                            // 8.4.1.0 (2023/02/10) Add
037
038import org.opengion.fukurou.system.HybsConst;                                           // fukurou.util.StringUtil → fukurou.system.HybsConst に変更
039//import org.opengion.fukurou.system.LogWriter;                                         // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system 8.4.1.0 (2023/02/10) Delete
040import org.opengion.fukurou.system.OgRuntimeException;                          // 6.4.2.0 (2016/01/29)
041
042/**
043 * QrcodeImage は、2次元バーコードの QRコードイメージを生成する
044 * 独立したコンポーネントです。
045 *
046 * 8.4.1.0 (2023/02/10) swetake から ZXing への置換
047 * <del>ここでの使い方は、初期化時に、エンコードする文字列(120Byte以内)と、
048 * 出力ファイル名を指定して、Graphics に描画したQRコードイメージを
049 * JPEG 変換し、指定のファイルに上書き保存しています。
050 * QRコード作成に、http://www.swetake.com/ の Qrcode クラスを使用しています。
051 * これが、2004/11/7  ver.0.50beta9 とのことなので、動作チェック、および、
052 * 製品としての保証という意味では、まだ使えるレベルではありませんが、
053 * コード計算さえバグっていなければ使えうる為、試験的導入致します。</del>
054 *
055 * ZXing (com.google.zxing) の Qrcode クラスを使用しています。
056 * エンコードモード ('N':数字モード 'A':英数字モード 'B':8bit byteモード) は
057 * 自動判定になる為、廃止されました。
058 *
059 * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
060 *
061 * @version     4.0
062 * @author      Kazuhiko Hasegawa
063 * @since       JDK5.0,
064 */
065public class QrcodeImage extends Component {
066        /** このプログラムのVERSION文字列を設定します。   {@value} */
067        private static final String VERSION = "8.5.5.1 (2024/02/29)";
068        private static final long serialVersionUID = 855120240229L ;
069
070        // version  0 ~ 39 => 1 ~ 40
071//      // errCo    H,M    0:H 1:M
072        // errCo    L,M,Q,H                                             // 7.2.1.0 (2020/03/13)
073//      // encMd    N,A,B  0:N 1:A 2:B                  // 8.4.1.0 (2023/02/10) Delete
074
075        // 8.4.1.0 (2023/02/10) Delete
076//      private static final int[][][] QC_DATA = new int[][][] {
077//              {{   17 ,   10 ,   4 } , {   34 ,   20 ,    8 }} ,
078//              {{   34 ,   20 ,   8 } , {   63 ,   38 ,   16 }} ,
079//              {{   58 ,   35 ,  15 } , {  101 ,   61 ,   26 }} ,
080//              {{   82 ,   50 ,  21 } , {  149 ,   90 ,   38 }} ,
081//              {{  106 ,   64 ,  27 } , {  202 ,  122 ,   52 }} ,
082//              {{  139 ,   84 ,  36 } , {  255 ,  154 ,   65 }} ,
083//              {{  154 ,   93 ,  39 } , {  293 ,  178 ,   75 }} ,
084//              {{  202 ,  122 ,  52 } , {  365 ,  221 ,   93 }} ,
085//              {{  235 ,  143 ,  60 } , {  432 ,  262 ,  111 }} ,
086//              {{  288 ,  174 ,  74 } , {  513 ,  311 ,  131 }} ,
087//              {{  331 ,  200 ,  85 } , {  604 ,  366 ,  155 }} ,
088//              {{  374 ,  227 ,  96 } , {  691 ,  419 ,  177 }} ,
089//              {{  427 ,  259 , 109 } , {  796 ,  483 ,  204 }} ,
090//              {{  468 ,  283 , 120 } , {  871 ,  528 ,  223 }} ,
091//              {{  530 ,  321 , 136 } , {  991 ,  600 ,  254 }} ,
092//              {{  602 ,  365 , 154 } , { 1082 ,  656 ,  277 }} ,
093//              {{  674 ,  408 , 173 } , { 1212 ,  734 ,  310 }} ,
094//              {{  746 ,  452 , 191 } , { 1346 ,  816 ,  345 }} ,
095//              {{  813 ,  493 , 208 } , { 1500 ,  909 ,  384 }} ,
096//              {{  919 ,  557 , 235 } , { 1600 ,  970 ,  410 }} ,
097//              {{  969 ,  587 , 248 } , { 1708 , 1035 ,  438 }} ,
098//              {{ 1056 ,  640 , 270 } , { 1872 , 1134 ,  480 }} ,
099//              {{ 1108 ,  672 , 284 } , { 2059 , 1248 ,  528 }} ,
100//              {{ 1228 ,  744 , 315 } , { 2188 , 1326 ,  561 }} ,
101//              {{ 1286 ,  779 , 330 } , { 2395 , 1451 ,  614 }} ,
102//              {{ 1425 ,  864 , 365 } , { 2544 , 1542 ,  652 }} ,
103//              {{ 1501 ,  910 , 385 } , { 2701 , 1637 ,  692 }} ,
104//              {{ 1581 ,  958 , 405 } , { 2857 , 1732 ,  732 }} ,
105//              {{ 1677 , 1016 , 430 } , { 3035 , 1839 ,  778 }} ,
106//              {{ 1782 , 1080 , 457 } , { 3289 , 1994 ,  843 }} ,
107//              {{ 1897 , 1150 , 486 } , { 3486 , 2113 ,  894 }} ,
108//              {{ 2022 , 1226 , 518 } , { 3693 , 2238 ,  947 }} ,
109//              {{ 2157 , 1307 , 553 } , { 3909 , 2369 , 1002 }} ,
110//              {{ 2301 , 1394 , 590 } , { 4134 , 2506 , 1060 }} ,
111//              {{ 2361 , 1431 , 605 } , { 4343 , 2632 , 1113 }} ,
112//              {{ 2524 , 1530 , 647 } , { 4588 , 2780 , 1176 }} ,
113//              {{ 2625 , 1591 , 673 } , { 4775 , 2894 , 1224 }} ,
114//              {{ 2735 , 1658 , 701 } , { 5039 , 3054 , 1292 }} ,
115//              {{ 2927 , 1774 , 750 } , { 5313 , 3220 , 1362 }} ,
116//              {{ 3057 , 1852 , 784 } , { 5596 , 3391 , 1435 }} } ;
117
118        /**
119         * エラー訂正レベル ('L','M','Q','H') の enum 定義
120         *
121         * @og.rev 8.5.5.1 (2024/02/29) switch式の使用
122        */
123        public enum ErrCrct {
124//      public static enum ErrCrct {
125//                                      H(0),M(1);
126                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 直接修正します。
127                                        /** エラー訂正レベル */ EC_L(1),
128                                        /** エラー訂正レベル */ EC_M(0),
129                                        /** エラー訂正レベル */ EC_Q(3),
130                                        /** エラー訂正レベル */ EC_H(2);                                               // 7.2.1.0 (2020/03/13)
131                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ImmutableField 対応
132                                        private final int no;
133                                        /** 初期エラー訂正レベル */
134                                        public static final ErrCrct DEF = EC_M;
135                                        /**
136                                         * enum コンストラクター
137                                         *
138                                         * @param       no      番号
139                                         */
140                                        ErrCrct( final int no ) { this.no = no; }
141                                        /**
142                                         * 番号を取得します。
143                                         *
144                                         * @return      番号
145                                         */
146                                        public int getNo() { return no; }
147                                        /**
148                                         * チャネルを取得します。
149                                         *
150                                         * @og.rev 7.2.1.0 (2020/03/13) QrcodeImage 見直し。
151                                         *
152                                         * @return      チャネルキャラクタ
153                                         */
154                                        public char getCh() {
155                                                // 8.5.5.1 (2024/02/29) switch式の使用
156//                                              final char ch ;
157//                                              switch(this) {
158//                                                      // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 直接修正します。
159//                                                      case EC_L:  ch = 'L'; break;                    // 7.2.1.0 (2020/03/13)
160//                                                      case EC_Q:  ch = 'Q'; break;                    // 7.2.1.0 (2020/03/13)
161//                                                      case EC_H:  ch = 'H'; break;
162//                                                      case EC_M:
163//                                                      default: ch = 'M'; break;
164//                                              }
165//                                              return ch ;
166                                                return switch(this) {
167                                                        case EC_L -> 'L';                       // 7.2.1.0 (2020/03/13)
168                                                        case EC_Q -> 'Q';                       // 7.2.1.0 (2020/03/13)
169                                                        case EC_H -> 'H';
170                                                        case EC_M -> 'M';
171                                                        default   -> 'M';
172                                                };
173                                        }
174                                        /**
175                                         * チャネルに応じたエラー訂正を取得します。
176                                         *
177                                         * @og.rev 7.2.1.0 (2020/03/13) QrcodeImage 見直し。
178                                         *
179                                         * @param       ch      チャネルキャラクタ
180                                         * @return      エラー訂正
181                                         */
182                                        public static ErrCrct get( final char ch ) {
183                                                // 8.5.5.1 (2024/02/29) switch式の使用
184//                                              final ErrCrct rtn ;
185//                                              switch(ch) {
186//                                                      // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 直接修正します。
187//                                                      case 'L': rtn = EC_L; break;                    // 7.2.1.0 (2020/03/13)
188//                                                      case 'Q': rtn = EC_Q; break;                    // 7.2.1.0 (2020/03/13)
189//                                                      case 'H': rtn = EC_H; break;
190//                                                      case 'M':
191//                                                      default:  rtn = EC_M; break;
192//                                              }
193//                                              return rtn ;
194                                                return switch(ch) {
195                                                        case 'L' -> EC_L;                       // 7.2.1.0 (2020/03/13)
196                                                        case 'Q' -> EC_Q;                       // 7.2.1.0 (2020/03/13)
197                                                        case 'H' -> EC_H;
198                                                        case 'M' -> EC_M;
199                                                        default  -> EC_M;
200                                                };
201                                        }
202        }               // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessarySemicolon
203
204        // 8.4.1.0 (2023/02/10) Delete
205//      /** エンコードモード ('N':数字モード 'A':英数字モード 'B':8bit byteモード) */
206//      public enum EncMode {
207////    public static enum EncMode {
208//                                      /** エンコードモード */ N(0),
209//                                      /** エンコードモード */ A(1),
210//                                      /** エンコードモード */ B(1);
211//                                      private int no;
212//                                      /** 初期エンコードモード */
213//                                      public static final EncMode DEF = B;
214//
215//                                      /**
216//                                       * エンコードモード enum を構築するコンストラクタ
217//                                       *
218//                                       * @param       エンコードモードの番号
219//                                       */
220//                                      EncMode( final int no ) { this.no = no; }
221//
222//                                      /**
223//                                       * エンコードモードの番号を返します。
224//                                       * 'N'=0:数字モード
225//                                       * 'A'=1:英数字モード
226//                                       * 'B'=1:8bit byteモード
227//                                       *
228//                                       * @return      エンコードモードの番号
229//                                       */
230//                                      public int getNo() { return no; }
231//
232//                                      /**
233//                                       * エンコードモードを表す文字
234//                                       * 'N':数字モード
235//                                       * 'A':英数字モード
236//                                       * 'B':8bit byteモード
237//                                       *
238//                                       * @return      エンコードモードを指定する文字('N':数字モード 'A':英数字モード 'B':8bit byteモード)
239//                                       */
240//                                      public char getCh() {
241//                                              final char ch ;
242//                                              switch(this) {
243//                                                      case N:  ch = 'N'; break;
244//                                                      case A:  ch = 'A'; break;
245//                                                      case B:
246//                                                      default: ch = 'B'; break;
247//                                              }
248//                                              return ch ;
249//                                      }
250//
251//                                      /**
252//                                       * エンコードモードを指定する文字から、エンコードモード enum を作成します。
253//                                       * 'N':数字モード
254//                                       * 'A':英数字モード
255//                                       * 'B':8bit byteモード
256//                                       *
257//                                       * @param       ch      エンコードモードを指定する文字
258//                                       * @return      エンコードモード
259//                                       */
260//                                      public static EncMode get( final char ch ) {
261//                                              final EncMode rtn ;
262//                                              switch(ch) {
263//                                                      case 'N': rtn = N; break;
264//                                                      case 'A': rtn = A; break;
265//                                                      case 'B':
266//                                                      default:  rtn = B; break;
267//                                              }
268//                                              return rtn ;
269//                                      }
270//      };
271
272        /** バージョン (1から40の整数。0を設定すると自動設定になります。) 初期値:{@value} */
273        public static final int DEF_VERSION = 5;                // 5.7.1.1 (2013/12/13) VERSION チェックのために、IDを変更します。
274        /** セルのマージン 初期値:{@value} */
275        public static final int MARGIN = 4;
276        /** 1セル辺りの塗りつぶしピクセル 初期値:{@value} */
277        public static final int PIXEL = 3;
278        /** 出力イメージのタイプ(PNG/JPEG) 初期値:{@value} */
279        public static final String IMAGE_TYPE = "PNG";
280
281        /** エンコードする文字列 */
282        private String  qrData;
283        /** 出力ファイル名 */
284        private String  saveFile;
285        /** イメージファイル形式(PNG/JPEG) 初期値:PNG */
286        private String  imgType = IMAGE_TYPE;
287        /** エラー訂正レベル ('L','M','Q','H') 初期値:M */
288        private ErrCrct errCo   = ErrCrct.DEF;
289//      private EncMode encMd   = EncMode.DEF;                  // 8.4.1.0 (2023/02/10) Delete
290        /** バージョン (2から40の整数) 初期値:5 */
291        private int             version = DEF_VERSION;                  // 5.7.1.1 (2013/12/13) VERSION チェックのために、IDを変更します。
292//      private int             pixel   = PIXEL;                                // 8.4.1.0 (2023/02/10) Delete
293        /** テキスト文字エンコード */
294        private String  txtEnc;                                                 // 7.2.3.0 (2020/04/10)
295
296        /** サイズ */
297        private int             imageSize;
298
299        /**
300         * デフォルトコンストラクター
301         *
302         * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: デフォルトのコンストラクタの使用で、コメントが指定されていません
303         */
304        public QrcodeImage() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
305
306        /**
307         * 初期化メソッド
308         *
309         * エラー訂正レベル:M , マージン:4(セル分) , 塗りつぶしピクセル:3
310         * <del>エンコードモード:B(バイナリ)</del> バージョン:5 , イメージのタイプ:PNG
311         * に初期化されます。
312         *
313         * @og.rev 5.7.1.1 (2013/12/13) VERSION チェックのために、VERSION ⇒ DEF_VERSION に変更します。
314         * @og.rev 7.2.3.0 (2020/04/10) byteモード時のテキスト文字エンコード。
315         * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
316         *
317         * @param       qrData          エンコードする文字列(120Byte 以内)
318         * @param       saveFile        出力ファイル名
319         */
320        public void init( final String qrData,final String saveFile ) {
321//              init( qrData,saveFile,DEF_VERSION,EncMode.DEF,ErrCrct.DEF,IMAGE_TYPE,PIXEL );                   // 5.7.1.1 (2013/12/13)
322//              init( qrData,saveFile,DEF_VERSION,EncMode.DEF,ErrCrct.DEF,IMAGE_TYPE,PIXEL,txtEnc );    // 7.2.3.0 (2020/04/10)
323                init( qrData,saveFile,DEF_VERSION,ErrCrct.DEF,IMAGE_TYPE,PIXEL,txtEnc );                                // 8.4.1.0 (2023/02/10)
324        }
325
326        /**
327         * 初期化メソッド
328         *
329         * エラー訂正レベル:M , マージン:4(セル分) , 塗りつぶしピクセル:3
330         * イメージのタイプ:PNG に初期化されます。
331         *
332         * @og.rev 7.2.3.0 (2020/04/10) byteモード時のテキスト文字エンコード。
333         * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
334         *
335         * @param       qrData          エンコードする文字列(120Byte 以内)
336         * @param       saveFile        出力ファイル名
337         * @param       version         バージョン (1から40の整数。0を設定すると自動設定になります。)
338         * <del>@param  encMd           エンコードモード ('N':数字モード 'A':英数字モード 'B':8bit byteモード)</del>
339         */
340//      public void init( final String qrData,final String saveFile,final int version,final EncMode encMd ) {
341////            init( qrData,saveFile,version,encMd,ErrCrct.DEF,IMAGE_TYPE,PIXEL );
342//              init( qrData,saveFile,version,encMd,ErrCrct.DEF,IMAGE_TYPE,PIXEL,txtEnc );              // 7.2.3.0 (2020/04/10)
343//      }
344        // 8.4.1.0 (2023/02/10) Modify
345        public void init( final String qrData,final String saveFile,final int version ) {
346                init( qrData,saveFile,version,ErrCrct.DEF,IMAGE_TYPE,PIXEL,txtEnc );
347        }
348
349        /**
350         * 初期化メソッド。
351         *
352         * @og.rev 7.2.3.0 (2020/04/10) textEncode byteモード時のテキスト文字エンコード追加
353         * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
354         *
355         * @param       qrData          エンコードする文字列(120Byte 以内)
356         * @param       saveFile        出力ファイル名
357         * @param       version         バージョン (1から40の整数。0を設定すると自動設定になります。)
358         * <del>@param  encMd           エンコードモード('N':数字モード 'A':英数字モード 'B':8bit byteモード)</del>
359         * @param       errCo           エラー訂正レベル ('L','M','Q','H')
360         * @param       imgType         イメージファイル形式(PNG/JPEG)
361         * @param       pixel           1セル辺りの塗りつぶしピクセル
362         * @param       txtEnc          テキスト文字エンコード
363         */
364//      public void init( final String qrData,final String saveFile,final int version,final EncMode encMd,
365//                                              final ErrCrct errCo ,final String imgType,final int pixel ) {
366        public void init( final String qrData,final String saveFile,final int version,                                                  // 8.4.1.0 (2023/02/10)
367                                                final ErrCrct errCo ,final String imgType,final int pixel,final String txtEnc ) {       // 7.2.3.0 (2020/04/10)
368
369                this.qrData             = qrData;
370                this.saveFile   = saveFile;
371                this.imgType    = imgType;
372                this.errCo              = errCo;
373//              this.encMd              = encMd;                                        // 8.4.1.0 (2023/02/10) Delete
374                this.version    = version;
375//              this.pixel              = pixel;                                        // 8.4.1.0 (2023/02/10) Delete
376                this.txtEnc             = txtEnc;                                       // 7.2.3.0 (2020/04/10)
377
378                imageSize = ( MARGIN*2 + 17 + version*4 ) * pixel;
379        }
380
381        // 8.4.1.0 (2023/02/10) Delete
382//      /**
383//       * 描画処理を行います。
384//       *
385//       * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.StringUtil → fukurou.system.HybsConst に変更
386//       * @og.rev 7.2.3.0 (2020/04/10) textEncode byteモード時のテキスト文字エンコード追加
387//       * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
388//       *
389//       * @param       gpx     Graphicsオブジェクト
390//       */
391//      @Override
392//      public void paint( final Graphics gpx ) {
393//              final Qrcode qrc =new Qrcode();
394//              qrc.setQrcodeErrorCorrect(errCo.getCh());
395//              qrc.setQrcodeEncodeMode(encMd.getCh());
396//              qrc.setQrcodeVersion(version);
397//
398//              final Charset txtEncode = txtEnc == null || txtEnc.isEmpty()                    // 7.2.3.0 (2020/04/10)
399//                                                                      ? HybsConst.DEFAULT_CHARSET                                     // 6.4.2.0 (2016/01/29)
400//                                                                      : "UTF-8".equals( txtEnc )
401//                                                                              ? StandardCharsets.UTF_8
402//                                                                              : Charset.forName( txtEnc ) ;
403//
404//      //      final byte[] dt =qrData.getBytes( HybsConst.DEFAULT_CHARSET );                  // 6.4.2.0 (2016/01/29)
405//              final byte[] dt =qrData.getBytes( txtEncode );                                                  // 7.2.3.0 (2020/04/10)
406//              final boolean[][] sfg = qrc.calQrcode( dt );
407//
408//              final int size = sfg.length;
409//              final int mgn  = MARGIN*pixel ;
410//              for( int i=0; i<size; i++ ) {
411//                      for( int j=0; j<size; j++ ) {
412//                              if( sfg[j][i] ) {
413//                                      gpx.fillRect( mgn+j*pixel,mgn+i*pixel,pixel,pixel );
414//                              }
415//                      }
416//              }
417//      }
418
419        /**
420         * 描画処理を行います。
421         *
422         * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
423         * @og.rev 8.5.3.2 (2023/10/13) JDK21注意。JDK17までは、Windows-31J だったが、JDK21から、UTF-8 に変更されている。
424         * @og.rev 8.5.3.2 (2023/10/13) データなしの場合に黒四角が印字される不具合対応
425         */
426        public void saveImage() {
427//              // 出力用イメージの生成
428//              final BufferedImage image = new BufferedImage(imageSize, imageSize, BufferedImage.TYPE_INT_BGR );
429//
430//              // イメージからグラフィックコンテキストを取得
431//              final Graphics grph = image.getGraphics();
432//              grph.setColor( java.awt.Color.WHITE );
433//              grph.fillRect( 0,0,imageSize, imageSize );
434//              grph.setColor( java.awt.Color.BLACK );
435//
436//              // JEditorPane をイメージに書き込む
437//              // paintComponent は proteced なので使用できない
438//              paint( grph );
439//
440//              // 使い終わったグラフィックコンテキストを開放
441//              grph.dispose();
442
443                // 8.4.1.0 (2023/02/10) Modify
444                try {
445                        BufferedImage image;
446                        if( qrData == null || qrData.isEmpty() ){
447                                image = new BufferedImage(imageSize, imageSize, BufferedImage.TYPE_INT_BGR );
448                                // 8.5.3.2 (2023/10/13) 黒四角の印字対応
449                                final Graphics grph = image.getGraphics();
450                                grph.setColor( java.awt.Color.WHITE );                  // 白色
451                                grph.fillRect( 0,0,imageSize, imageSize );
452                        }
453                        else {
454                                final String txtEncode = txtEnc == null || txtEnc.isEmpty()
455                                                                                ? HybsConst.DEFAULT_CHARSET.displayName()       // 8.5.3.2 (2023/10/13) JDK21注意
456                                                                                : txtEnc;
457
458                                final Map<EncodeHintType, Object> hints = new HashMap<>();
459                                hints.put( EncodeHintType.QR_VERSION, version );                                        // バージョン
460                                hints.put( EncodeHintType.ERROR_CORRECTION, errCo.getCh() );            // エラー訂正レベル(L, M, Q, H)
461                                hints.put( EncodeHintType.CHARACTER_SET, txtEncode );                           // 文字エンコード
462
463                                final QRCodeWriter writer = new QRCodeWriter();
464                                final BitMatrix bitMatrix = writer.encode( qrData, BarcodeFormat.QR_CODE, imageSize, imageSize, hints );
465                                image = MatrixToImageWriter.toBufferedImage( bitMatrix );
466                        }
467
468                        // イメージの出力 Image I/O を使用
469                        ImageIO.write( image, imgType, new File( saveFile ) );
470                } catch ( final WriterException ex) {
471                        final String errMsg = "QRコードの生成に失敗しました。"
472                                        + "Data=[" + qrData + "]" ;
473                        throw new OgRuntimeException( errMsg,ex );
474                } catch( final IOException ex ) {
475                        final String errMsg = "イメージファイルの出力に失敗しました。"
476                                        + "File=[" + saveFile + "]" ;
477                        throw new OgRuntimeException( errMsg,ex );
478                }
479        }
480
481        // 8.4.1.0 (2023/02/10) Delete
482//      /**
483//       * メイン処理です。
484//       * Usage: java org.opengion.fukurou.util.QrcodeImage Encode [SaevFile]
485//       *
486//       * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
487//       *
488//       * @param       args    引数文字列配列
489//       */
490//      public static void main( final String[] args ) {
491//              if( args.length == 0 ) {
492//                      LogWriter.log( "Usage: java org.opengion.fukurou.util.QrcodeImage Encode [SaevFile]" );
493//                      return ;
494//              }
495//
496//              final String qrcode = args[0];
497//              final String file   = args.length > 1 ? args[1] : "img.png";
498//
499//              final QrcodeImage qrImage = new QrcodeImage();
500//              qrImage.init( qrcode,file );
501//              qrImage.saveImage();
502//      }
503
504        // 8.4.1.0 (2023/02/10) Delete
505//      /**
506//       * 内部データを標準出力へ出力します。
507//       *
508//       * @og.rev 7.2.1.0 (2020/03/13) QrcodeImage 見直し。
509//       * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
510//       */
511//      public static void printQcData() {
512////            final char[] strJ = new char[] { 'H','M' };
513//              final char[] strJ = new char[] { 'L','M','Q','H' };                                                     // 7.2.1.0 (2020/03/13)
514//              final char[] strK = new char[] { 'N','A','B' };
515//
516//              for( int i=0; i<QC_DATA.length; i++ ) {
517//                      System.out.print( "version=[" + (i+1) + "] " );
518//                      for( int j=0; j<QC_DATA[i].length; j++ ) {
519//                              final char errCo = strJ[j];
520//                              for( int k=0; k<QC_DATA[i][j].length; k++ ) {
521//                                      System.out.print( errCo + strK[k] + "=[" + QC_DATA[i][j][k] + "] " );
522//                              }
523//                      }
524//                      System.out.println();
525//              }
526        }
527
528        // 8.4.1.0 (2023/02/10) Delete
529//      /**
530//       * バージョン情報を取得します。
531//       *
532//       * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
533//       *
534//       * @param       errCo   エラー訂正レベル ('L','M','Q','H')
535//       * @param       encMd   エンコードモード ('N':数字モード 'A':英数字モード 'B':8bit byteモード)
536//       * @param       len             対象範囲
537//       * @return      バージョン情報
538//       */
539//      public static int getVersion( final ErrCrct errCo, final EncMode encMd, final int len ) {
540//              final int errCoInt = errCo.getNo() ;
541//              final int encMdInt = encMd.getNo() ;
542//
543//              int rtn = -1;
544//              for( int i=0; i<QC_DATA.length; i++ ) {
545//                      if( QC_DATA[i][errCoInt][encMdInt] >= len ) {
546//                              rtn = i;
547//                              break;
548//                      }
549//              }
550//
551//              if( rtn < 0 ) {
552//                      final String errMsg = "データ量が対象範囲を超えています。エラーレベルや、モードを調整してください。"
553//                                              + "ErrCo:" + errCo + " EncMd:" + encMd + " len=[" + len + "]"
554//                                              + " MaxLen=[" + QC_DATA[QC_DATA.length-1][errCoInt][encMdInt] + "]" ;
555//                      throw new OgRuntimeException( errMsg );
556//              }
557//
558//              return rtn + 1 ;
559//      }
560
561        // 8.4.1.0 (2023/02/10) Delete
562//      /**
563//       * 最大サイズを取得します。
564//       *
565//       * @og.rev 8.4.1.0 (2023/02/10) QRコードを swetake から ZXing への置換(encodeMode廃止)
566//       *
567//       * @param       version バージョン情報
568//       * @param       errCo   エラー訂正レベル ('L','M','Q','H')
569//       * @param       encMd   エンコードモード ('N':数字モード 'A':英数字モード 'B':8bit byteモード)
570//       * @return      最大サイズ
571//       */
572//      public static int getMaxSize( final int version, final ErrCrct errCo, final EncMode encMd ) {
573//              final int errCoInt = errCo.getNo() ;
574//              final int encMdInt = encMd.getNo() ;
575//
576//              return QC_DATA[version][errCoInt][encMdInt] ;
577//      }
578//}