    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.hayabusa.taglib;
    017
    018import org.opengion.hayabusa.common.HybsSystem;
    019import org.opengion.hayabusa.common.HybsSystemException;
    020import org.opengion.fukurou.util.StringUtil;
    021import org.opengion.fukurou.util.ToString;
    022import org.opengion.fukurou.util.XHTMLTag;
    023import org.opengion.fukurou.util.Attributes;
    024
    025import org.opengion.fukurou.util.QrcodeImage;
    026import static org.opengion.fukurou.util.QrcodeImage.ErrCrct;
    027//import static org.opengion.fukurou.util.QrcodeImage.EncMode;                                  // 8.4.1.0 (2023/02/10) Delete
    028import static org.opengion.fukurou.util.StringUtil.nval;
    029
    030import java.util.Locale;
    031
    032/**
    033 * QRｺｰﾄﾞに対応したｲﾒｰｼﾞﾌｧｲﾙを作成するﾀｸﾞです。
    034 *
    035 * QRｺｰﾄﾞで表示できる文字数は、ﾊﾞｰｼﾞｮﾝ、ｴﾝｺｰﾄﾞﾓｰﾄﾞ、ｴﾗｰ訂正ﾚﾍﾞﾙ に依存します。
    036 * また、ｲﾒｰｼﾞの大きさは、文字数と1ｾﾙ辺りのﾋﾟｸｾﾙに依存します。
    037 * fileURLは、通常、ｼｽﾃﾑﾘｿｰｽのFILE_URL(=filetemp)なので、fileURL="{&#064;USER.ID}" と
    038 * するのが一般的です。
    039 * ﾌｧｲﾙ名は、初期値:rqcode ですが、拡張子はimageType(初期値:PNG)を小文字化して追加します。
    040 * 拡張子が付いている場合は、そのまま使用されます。
    041 * また、同一ﾌｧｲﾙ名の場合、ﾌﾞﾗｳｻﾞｷｬｯｼｭのため、画像が更新されないことがあるため
    042 * imgﾀｸﾞのsrc属性に、ｷｬｯｼｭの無効化のための引数を追加しています。
    043 *
    044 * @og.formSample
    045 * ●形式：&lt;og:qrCode fileURL="{&#064;USER.ID}" &gt;
    046 *             ｴﾝｺｰﾄﾞする文字列
    047 *         &lt;/og:qrCode &gt;
    048 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
    049 *
    050 * または、
    051 *
    052 * ●形式：&lt;og:qrCode value="ｴﾝｺｰﾄﾞする文字列" /&gt;
    053 *
    054 * ●Tag定義：
    055 *   &lt;og:qrCode
    056 *       value              【TAG】ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)
    057 *       version            【TAG】ﾊﾞｰｼﾞｮﾝ (2から40の整数)(初期値:5)
    058 *  <del>encodeMode         【TAG】ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ 'B':8bit byteﾓｰﾄﾞ)(初期値:B)</del> 8.4.1.0 (2023/02/10) Delete
    059 *       errCorrect         【TAG】ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H')(初期値:M)
    060 *       imageType          【TAG】ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG)(初期値:PNG)
    061 *       pixel              【TAG】1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙ(初期値:3)
    062 *       fileURL            【TAG】QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘ(初期値:FILE_URL)
    063 *       filename           【TAG】QRｲﾒｰｼﾞﾌｧｲﾙ名 (初期値:rqcode)
    064 *       textEncode         【TAG】ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ(初期値:Charset.defaultCharset()) 7.2.3.0 (2020/04/10)
    065 *       caseKey            【TAG】このﾀｸﾞ自体を利用するかどうかの条件ｷｰを指定します(初期値:null)
    066 *       caseVal            【TAG】このﾀｸﾞ自体を利用するかどうかの条件値を指定します(初期値:null)
    067 *       caseNN             【TAG】指定の値が、null/ｾﾞﾛ文字列 でない場合(Not Null=NN)は、このﾀｸﾞは使用されます(初期値:判定しない)
    068 *       caseNull           【TAG】指定の値が、null/ｾﾞﾛ文字列 の場合は、このﾀｸﾞは使用されます(初期値:判定しない)
    069 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このﾀｸﾞは使用されます(初期値:判定しない)
    070 *       debug              【TAG】ﾃﾞﾊﾞｯｸﾞ情報を出力するかどうか[true/false]を指定します(初期値:false)
    071 *   &gt;   ... Body ...
    072 *   &lt;/og:qrCode&gt;
    073 *
    074 * @og.rev 7.2.1.0 (2020/03/13) 新規作成
    075 * @og.group 画面表示
    076 *
    077 * @version     7.2
    078 * @author       Kazuhiko Hasegawa
    079 * @since       JDK11.0,
    080 */
    081public class QRcodeTag extends CommonTagSupport {
    082        /** このプログラムのVERSION文字列を設定します。   {@value} */
    083        private static final String VERSION = "8.4.1.0 (2023/02/10)" ;
    084        private static final long serialVersionUID = 841020230210L ;
    085
    086        /** ｲﾒｰｼﾞに表示させる alt属性の文字数制限 */
    087        private static final int MAX_ALT_SIZE = 50;
    088
    089        /** ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述) */
    090        private String  value           ;
    091        /** ﾊﾞｰｼﾞｮﾝ (2から40の整数) 初期値:5 */
    092        private int             version         = QrcodeImage.DEF_VERSION;
    093//      private EncMode encMode         = EncMode.DEF;                                                  // ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ 'B':8bit byteﾓｰﾄﾞ)(初期値:B) 8.4.1.0 (2023/02/10) Delete
    094        /** ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H') 初期値:M */
    095        private ErrCrct errCrct         = ErrCrct.DEF;
    096        /** ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG) 初期値:PNG */
    097        private String  imgType         = QrcodeImage.IMAGE_TYPE;
    098        /** 1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙ 初期値:3 */
    099        private int             pixel           = QrcodeImage.PIXEL;
    100        /** QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘ 初期値:FILE_URL/{&#064;USER.ID} */
    101        private String  fileURL         = HybsSystem.sys( "FILE_URL" );
    102        /** QRｲﾒｰｼﾞﾌｧｲﾙ名 (初期値:rqcode) */
    103        private String  filename        = "rqcode";
    104        /** ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ 初期値:Charset.defaultCharset() */
    105        private String  textEncode      ;                                                                               // 7.2.3.0 (2020/04/10)
    106
    107        /**
    108         * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
    109         *
    110         * @og.rev 7.2.1.0 (2020/03/13) 新規作成
    111         */
    112        public QRcodeTag() { super(); }                 // これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。
    113
    114        /**
    115         * Taglibの開始ﾀｸﾞが見つかったときに処理する doStartTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
    116         *
    117         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
    118         */
    119        @Override
    120        public int doStartTag() {
    121                // caseKey、caseVal 属性対応
    122                if( useTag() ) {
    123                        if( value == null || value.length() <= 0 ) {
    124                                return EVAL_BODY_BUFFERED ;             // Body を評価する
    125                        }
    126                }
    127                return SKIP_BODY ;                                              // Body を評価しない
    128        }
    129
    130        /**
    131         * Taglibのﾀｸﾞ本体を処理する doAfterBody() を ｵｰﾊﾞｰﾗｲﾄﾞします。
    132         *
    133         * @return      後続処理の指示(SKIP_BODY)
    134         */
    135        @Override
    136        public int doAfterBody() {
    137                value = getBodyString();
    138
    139                return SKIP_BODY ;
    140        }
    141
    142        /**
    143         * Taglibの終了ﾀｸﾞが見つかったときに処理する doEndTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
    144         *
    145         * @og.rev 7.2.3.0 (2020/04/10) textEncode byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ追加
    146         * @og.rev 8.4.1.0 (2023/02/10) QRｺｰﾄﾞを swetake から ZXing への置換(encodeMode廃止)
    147         *
    148         * @return      後続処理の指示
    149         */
    150        @Override
    151        public int doEndTag() {
    152                debugPrint();
    153                // caseKey、caseVal 属性対応
    154                if( useTag() ) {
    155                        if( filename.indexOf( '.' ) < 0 ) {                             // 拡張子が存在しない
    156                                filename = filename + "." + imgType.toLowerCase(Locale.JAPAN);
    157                        }
    158
    159                        try {
    160                                final String saveFile = HybsSystem.url2dir( fileURL,filename );
    161
    162                                final QrcodeImage qrcode = new QrcodeImage();
    163//                              qrcode.init( value,saveFile,version,encMode,errCrct,imgType,pixel );
    164//                              qrcode.init( value,saveFile,version,encMode,errCrct,imgType,pixel,textEncode ); // 7.2.3.0 (2020/04/10)
    165                                qrcode.init( value,saveFile,version,errCrct,imgType,pixel,textEncode );                 // 8.4.1.0 (2023/02/10)
    166                                qrcode.saveImage();
    167                        }
    168                        catch( final Throwable th ) {
    169                                final String errMsg = "QRコードが作成できませんでした。"                                       + CR
    170                                                                + "指定のパラメータが正しいかどうかご確認ください。"            + CR
    171                                                                + "  version    = [" + version   + "] バージョン (2から40の整数)"                                         + CR
    172//                                                              + "  encodeMode = [" + encMode   + "] エンコードモード('N':数字 'A':英数字'B':byte)" + CR    // 8.4.1.0 (2023/02/10) Delete
    173                                                                + "  errCorrect = [" + errCrct   + "] エラー訂正レベル ('L','M','Q','H')"       + CR
    174                                                                + "  imageType  = [" + imgType   + "] イメージファイル形式(PNG/JPEG)"             + CR
    175                                                                + "  pixel      = [" + pixel     + "] 1セル辺りの塗りつぶしピクセル"          ;
    176                                throw new HybsSystemException( errMsg,th );
    177                        }
    178
    179                        filename = filename + "?val=" + System.currentTimeMillis() ;            // 同じﾌｧｲﾙ名の場合のｷｬｯｼｭの無効化
    180                        final String src = StringUtil.urlAppend( getContextPath() , fileURL , filename );
    181                        final String alt = value.substring( 0,Math.min( value.length() , MAX_ALT_SIZE ) );
    182
    183                        // 作成された QRｺｰﾄﾞのｲﾒｰｼﾞを表示します。
    184                        final String img = XHTMLTag.img(
    185                                        new Attributes()
    186                                                .set( "src"             , src )
    187                                                .set( "alt"             , alt )
    188                                                .set( "title"   , alt )
    189                        );
    190
    191                        jspPrint( img );
    192                }
    193                return EVAL_PAGE ;
    194        }
    195
    196        /**
    197         * ﾀｸﾞﾘﾌﾞｵﾌﾞｼﾞｪｸﾄをﾘﾘｰｽします。
    198         * ｷｬｯｼｭされて再利用されるので、ﾌｨｰﾙﾄﾞの初期設定を行います。
    199         *
    200         * @og.rev 7.2.3.0 (2020/04/10) textEncode byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ追加
    201         * @og.rev 8.4.1.0 (2023/02/10) QRｺｰﾄﾞを swetake から ZXing への置換(encodeMode廃止)
    202         */
    203        @Override
    204        protected void release2() {
    205                super.release2();
    206                value           = null;                                                                         // ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)
    207                version         = QrcodeImage.DEF_VERSION;                                      // ﾊﾞｰｼﾞｮﾝ (2から40の整数)(初期値:5)
    208//              encMode         = EncMode.DEF;                                                          // ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ 'B':8bit byteﾓｰﾄﾞ)(初期値:B)  // 8.4.1.0 (2023/02/10) Delete
    209                errCrct         = ErrCrct.DEF;                                                          // ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H')(初期値:M)
    210                imgType         = QrcodeImage.IMAGE_TYPE;                                       // ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG)(初期値:PNG)
    211                pixel           = QrcodeImage.PIXEL;                                            // 1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙ(初期値:3)
    212                fileURL         = HybsSystem.sys( "FILE_URL" );                         // QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘ(初期値:FILE_URL/{@USER.ID})
    213                filename        = "rqcode";                                                                     // QRｲﾒｰｼﾞﾌｧｲﾙ名 (初期値:rqcode)
    214                textEncode      = null;                                                                         // 7.2.3.0 (2020/04/10) byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ
    215        }
    216
    217        /**
    218         * 【TAG】ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)を指定します。
    219         *
    220         * @og.tag
    221         * ｴﾝｺｰﾄﾞする文字列のﾊﾞｲﾄ数は、ﾊﾞｰｼﾞｮﾝ、ｴﾝｺｰﾄﾞﾓｰﾄﾞ、ｴﾗｰ訂正ﾚﾍﾞﾙに依存します。
    222         * また、ｲﾒｰｼﾞの大きさは、それらﾌﾟﾗｽ1ｾﾙ辺りのﾋﾟｸｾﾙも影響します。
    223         *
    224         * @param       val     ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)
    225         */
    226        public void setValue( final String val ) {
    227                value = nval( getRequestParameter( val ),value );
    228        }
    229
    230        /**
    231         * 【TAG】ﾊﾞｰｼﾞｮﾝ (2から40の整数)を指定します(初期値:5)。
    232         *
    233         * @og.tag
    234         * ｴﾝｺｰﾄﾞする文字列のﾊﾞｲﾄ数は、ｴﾗｰ訂正ﾚﾍﾞﾙ、ﾊﾞｰｼﾞｮﾝ に依存します。
    235         * 文字列のﾊﾞｲﾄ数を増やす場合は、ﾊﾞｰｼﾞｮﾝを適切に設定します。
    236         *
    237         * @param       ver     ﾊﾞｰｼﾞｮﾝ
    238         */
    239        public void setVersion( final String ver ) {
    240                version = nval( getRequestParameter( ver ),version );
    241        }
    242
    243//      /**
    244//       * 【TAG】ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ　'B':8bit byteﾓｰﾄﾞ)を指定します(初期値:B)。
    245//       *
    246//       * @og.tag
    247//       * ｴﾝｺｰﾄﾞする文字列の種類に応じて設定します。
    248//       * 日本語等を含む場合は、'B':8bit byteﾓｰﾄﾞ にしてください。
    249//       *
    250//       * @og.rev 8.4.1.0 (2023/02/10) QRｺｰﾄﾞを swetake から ZXing への置換(encodeMode廃止)
    251//       *
    252//       * @param       mode    ｴﾝｺｰﾄﾞﾓｰﾄﾞ
    253//       */
    254//      public void setEncodeMode( final String mode ) {
    255//              final String em = nval( getRequestParameter( mode ),null );
    256//              if( em != null ) {
    257//                      encMode = EncMode.get( em.charAt(0) );
    258//              }
    259//      }
    260
    261        /**
    262         * 【TAG】ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H')を指定します(初期値:M)。
    263         *
    264         * @og.tag
    265         * ｴﾝｺｰﾄﾞする文字列のﾊﾞｲﾄ数は、ｴﾗｰ訂正ﾚﾍﾞﾙ、ﾊﾞｰｼﾞｮﾝ に依存します。
    266         * 通常、初期値のままで問題ありません。
    267         *
    268         * @param       crct    ｴﾗｰ訂正ﾚﾍﾞﾙ
    269         */
    270        public void setErrCorrect( final String crct ) {
    271                final String ec = nval( getRequestParameter( crct ),null );
    272                if( ec != null ) {
    273                        errCrct = ErrCrct.get( ec.charAt(0) );
    274                }
    275        }
    276
    277        /**
    278         * 【TAG】ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG)を指定します(初期値:PNG)。
    279         *
    280         * @og.tag
    281         * QRｺｰﾄﾞのｲﾒｰｼﾞﾌｧｲﾙの形式を指定します。
    282         * 拡張子は自動的にｲﾒｰｼﾞﾌｧｲﾙ形式(の小文字)がｾｯﾄされます。
    283         *
    284         * @param       type    ｲﾒｰｼﾞﾌｧｲﾙ形式
    285         */
    286        public void setImageType( final String type ) {
    287                imgType = nval( getRequestParameter( type ),imgType );
    288        }
    289
    290        /**
    291         * 【TAG】1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙを指定します(初期値:3)。
    292         *
    293         * @og.tag
    294         * QRｺｰﾄﾞのｲﾒｰｼﾞﾌｧｲﾙの形式を指定します。
    295         * 拡張子は自動的にｲﾒｰｼﾞﾌｧｲﾙ形式(の小文字)がｾｯﾄされます。
    296         *
    297         * @param       px      ﾋﾟｸｾﾙ数
    298         */
    299        public void setPixel( final String px ) {
    300                pixel = nval( getRequestParameter( px ),pixel );
    301        }
    302
    303        /**
    304         * 【TAG】QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘを指定します(初期値:FILE_URL)。
    305         *
    306         * @og.tag
    307         * この属性で指定されるﾃﾞｨﾚｸﾄﾘに、QRｲﾒｰｼﾞﾌｧｲﾙをｾｰﾌﾞします。
    308         * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、２文字目が、
    309         * ":" (Windows)の場合は、指定のURLそのままのﾃﾞｨﾚｸﾄﾘに、そうでない場合は、
    310         * FILE_URL 属性で指定のﾌｫﾙﾀﾞの下に、ﾌｫﾙﾀﾞを作成します。
    311         * 初期値は、FILE_URL になるため、通常は{&#064;USER.ID}を指定する必要があります。
    312         *
    313         * @param       url     保存先ﾃﾞｨﾚｸﾄﾘ名
    314         * @see         org.opengion.hayabusa.common.SystemData#FILE_URL
    315         */
    316        public void setFileURL( final String url ) {
    317                final String furl = nval( getRequestParameter( url ),null );
    318                if( furl != null ) {
    319                        fileURL = StringUtil.urlAppend( fileURL,furl );
    320                }
    321        }
    322
    323        /**
    324         * 【TAG】QRｲﾒｰｼﾞﾌｧｲﾙ名をｾｯﾄします(初期値:rqcode)。
    325         *
    326         * @og.tag
    327         * ﾌｧｲﾙを作成するときのﾌｧｲﾙ名をｾｯﾄします。
    328         * ﾌｧｲﾙ名の拡張子は、imageType属性の小文字を追加します。
    329         * 拡張子付きで指定した場合(ﾌｧｲﾙ名に、ﾋﾟﾘｵﾄﾞを含む場合)は、
    330         * そのままの値を使用します。
    331         *
    332         * @param       fname   ﾌｧｲﾙ名
    333         */
    334        public void setFilename( final String fname ) {
    335                filename = nval( getRequestParameter( fname ),filename );
    336        }
    337
    338        /**
    339         * 【TAG】ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞをｾｯﾄします(初期値:環境依存)。
    340         *
    341         * @og.tag
    342         * ﾃｷｽﾄのｴﾝｺｰﾄﾞの指定がない場合は、ﾌﾟﾗｯﾄﾌｫｰﾑ依存のﾃﾞﾌｫﾙﾄの Charset です。
    343         * java.nio.charset.Charset#defaultCharset()
    344         * QRｺｰﾄﾞで、機種依存文字(①など)は、Windows-31J を指定しても読み取り側が対応していません。
    345         * その場合は、UTF-8 を指定します。(必要なﾊﾞｲﾄ数は当然増えます)
    346         *
    347         * 通常、何も指定しないか、UTF-8 を指定するかのどちらかになります。
    348         *
    349         * @og.rev 7.2.3.0 (2020/04/10) textEncode byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ追加
    350         *
    351         * @param       txtEnc  ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ
    352         */
    353        public void setTextEncode( final String txtEnc ) {
    354                textEncode = nval( getRequestParameter( txtEnc ),textEncode );
    355        }
    356
    357        /**
    358         * このｵﾌﾞｼﾞｪｸﾄの文字列表現を返します。
    359         * 基本的にﾃﾞﾊﾞｯｸﾞ目的に使用します。
    360         *
    361         * @return      このｸﾗｽの文字列表現
    362         * @og.rtnNotNull
    363         */
    364        @Override
    365        public String toString() {
    366                return ToString.title( this.getClass().getName() )
    367                                .println( "VERSION"                     , VERSION                       )
    368                                .println( "value"                       , value                         )
    369                                .println( "version"                     , version                       )
    370//                              .println( "encMode"                     , encMode                       )       // 8.4.1.0 (2023/02/10) Delete
    371                                .println( "errCrct"                     , errCrct                       )
    372                                .println( "imgType"                     , imgType                       )
    373                                .println( "pixel"                       , pixel                         )
    374                                .println( "fileURL"                     , fileURL                       )
    375                                .println( "filename"            , filename                      )
    376                                .fixForm().toString() ;
    377        }
    378}