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