    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 org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import java.awt.color.ColorSpace;
    020import java.awt.color.ICC_ColorSpace;
    021import java.awt.color.ICC_Profile;
    022import java.awt.Color;                                                          // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係
    023import java.awt.Font;                                                           // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係
    024import java.awt.Graphics2D;                                                     // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係
    025import java.awt.FontMetrics;                                            // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係
    026import java.awt.image.BufferedImage;
    027import java.awt.image.ColorConvertOp;
    028import java.io.File;
    029import java.io.IOException;
    030import java.io.InputStream;
    031import java.io.ByteArrayOutputStream;
    032import java.util.Locale;
    033import java.util.Arrays;
    034import javax.media.jai.JAI;
    035
    036import javax.imageio.ImageIO;
    037import javax.imageio.IIOException;
    038
    039import com.sun.media.jai.codec.FileSeekableStream;
    040import com.sun.media.jai.util.SimpleCMYKColorSpace;
    041
    042import org.opengion.fukurou.system.Closer;                                                      // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
    043import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
    044
    045/**
    046 * ImageUtil は、画像ファイル関連の処理を集めたユーティリティクラスです。
    047 *
    048 * ここでは、イメージファイルを BufferedImage にして取り扱います。
    049 * また、ImageResizer で処理していた static メソッドや、関連処理、
    050 * org.opengion.hayabusa.servlet.MakeImage の主要な処理もこちらに持ってきます。
    051 *
    052 * @version  6.0.2.3 (2014/10/10)
    053 * @author   Hiroki Nakamura
    054 * @since    JDK6.0,
    055 */
    056public final class ImageUtil {
    057
    058        private static final String ICC_PROFILE = "ISOcoated_v2_eci.icc";               // 5.5.3.4 (2012/06/19)
    059
    060        // 6.0.2.3 (2014/10/10) テキスト合成で指定できる設定値
    061        /** X軸に対して、テキストを画像の左寄せで表示します。 **/
    062        public static final int LEFT    = -1 ;
    063        /** X軸に対して、テキストを画像の中央揃えで表示します。 **/
    064        public static final int CENTER  = -2 ;
    065        /** X軸に対して、テキストを画像の右寄せで表示します。 **/
    066        public static final int RIGHT   = -3 ;
    067
    068        /** Y軸に対して、テキストを画像の上揃えで表示します。 **/
    069        public static final int TOP             = -4 ;
    070        /** Y軸に対して、テキストを画像の中央揃えで表示します。 **/
    071        public static final int MIDDLE  = -5 ;
    072        /** Y軸に対して、テキストを画像の下揃えで表示します。 **/
    073        public static final int BOTTOM  = -6 ;
    074
    075        public static final String READER_SUFFIXES ;    // 5.6.5.3 (2013/06/28) 入力画像の形式 [bmp, gif, jpeg, jpg, png, wbmp]
    076        public static final String WRITER_SUFFIXES ;    // 5.6.5.3 (2013/06/28) 出力画像の形式 [bmp, gif, jpeg, jpg, png, wbmp]
    077        // 5.6.5.3 (2013/06/28) 入力画像,出力画像の形式 を ImageIO から取り出します。
    078        static {
    079                final String[] rfn = ImageIO.getReaderFileSuffixes();
    080                Arrays.sort( rfn );
    081                READER_SUFFIXES = Arrays.toString( rfn );
    082
    083                final String[] wfn = ImageIO.getWriterFileSuffixes();
    084                Arrays.sort( wfn );
    085                WRITER_SUFFIXES = Arrays.toString( wfn );
    086        }
    087
    088        /**
    089         * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。
    090         *
    091         */
    092        private ImageUtil() {}
    093
    094        /**
    095         * 入力ファイル名を指定し、画像オブジェクトを作成します。
    096         *
    097         * @og.rev 5.4.3.5 (2012/01/17) CMYK対応
    098         * @og.rev 5.4.3.7 (2012/01/20) FAIでのファイル取得方法変更
    099         * @og.rev 5.4.3.8 (2012/01/24) エラーメッセージ追加
    100         * @og.rev 5.6.5.3 (2013/06/28) 入力画像の形式 を ImageIO から取り出します。
    101         * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。
    102         *
    103         * @param fin 入力ファイル名
    104         * @return 読み込まれた画像オブジェクト(BufferedImage)
    105         */
    106        public static BufferedImage readFile( final String fin ) {
    107                // 5.6.5.3 (2013/06/28) 入力画像の形式 を ImageIO から取り出します。
    108                if( !ImageUtil.isReaderSuffix( fin ) ) {
    109                        final String errMsg = "入力ファイルは" + READER_SUFFIXES + "のいずれかの形式のみ指定可能です。"
    110                                                        + "File=[" + fin + "]";
    111                        throw new OgRuntimeException( errMsg );
    112                }
    113
    114                final File inFile = new File( fin );
    115                BufferedImage bi = null;
    116                try {
    117                        bi = ImageIO.read( inFile );
    118                }
    119                catch( final IIOException ex ) { // 5.4.3.5 (2012/01/17) 決めうち
    120                        // API的には、IllegalArgumentException と IOException しか記述されていない。
    121                        // 何もせずに、下の処理に任せます。
    122                        // 6.0.2.5 (2014/10/31) refactoring:Avoid empty catch blocks 警告対応
    123                        final String errMsg = "cmykToSRGB 処理が必要です。" + ex.getMessage();
    124                        System.err.println( errMsg );
    125                }
    126                catch( final IOException ex ) {
    127                        final String errMsg = "イメージファイルの読込に失敗しました。" + "File=[" + fin + "]";
    128                        throw new OgRuntimeException( errMsg,ex );
    129                }
    130
    131                // 6.0.0.1 (2014/04/25) IIOException の catch ブロックからの例外出力を外に出します。
    132                // bi == null は、結果のストリームを読み込みできないような場合、または、IO例外が発生した場合。
    133                if( bi == null ) {
    134                        FileSeekableStream fsstream = null;
    135                        try{
    136                                // 5.4.3.7 (2012/01/20) ファイルの開放がGC依存なので、streamで取得するように変更
    137                                // bi = cmykToSRGB(JAI.create("FileLoad",inFile.toString()).getAsBufferedImage(null,null));
    138                                fsstream = new FileSeekableStream(inFile.getAbsolutePath());
    139                                bi = cmykToSRGB(JAI.create("stream",fsstream).getAsBufferedImage(null,null));
    140                        }
    141                        catch( final IOException ex ){
    142                                final String errMsg = "イメージファイルの読込(JAI)に失敗しました。" + "File=[" + fin + "]";
    143                                throw new OgRuntimeException( errMsg,ex );
    144                        }
    145                        catch( final RuntimeException ex ) {            // 5.4.3.8 (2012/01/23) その他エラーの場合追加
    146                                final String errMsg = "イメージファイルの読込(JAI)に失敗しました。ファイルが壊れている可能性があります。" + "File=[" + fin + "]";
    147                                throw new OgRuntimeException( errMsg,ex );
    148                        }
    149                        finally{
    150                                Closer.ioClose(fsstream);
    151                        }
    152                }
    153
    154                return bi;
    155        }
    156
    157        /**
    158         * 画像オブジェクト と、出力ファイル名を指定し、ファイルに書き込みます。
    159         *
    160         * ImageIO に指定する formatName(ファイル形式)は、出力ファイル名の拡張子から取得します。
    161         * [bmp, gif, jpeg, jpg, png, wbmp] 位がサポートされています。
    162         *
    163         * @og.rev 6.0.2.3 (2014/10/10) 新規作成
    164         *
    165         * @param image 出力する画像オブジェクト(BufferedImage)
    166         * @param fout 出力ファイル名
    167         */
    168        public static void saveFile( final BufferedImage image , final String fout ) {
    169                final File outFile = new File( fout );
    170                try {
    171                        final String outSuffix = ImageUtil.getSuffix( fout );
    172                        ImageIO.write( image, outSuffix, outFile );
    173                }
    174                catch( final IOException ex ) {
    175                        final String errMsg = "イメージファイルの書き込みに失敗しました。" + "File=[" + fout + "]";
    176                        throw new OgRuntimeException( errMsg,ex );
    177                }
    178        }
    179
    180        /**
    181         * 入力ファイル名を指定し、画像ファイルの byte配列を作成します。
    182         *
    183         * ImageIO に指定する formatName(ファイル形式)は、出力ファイル名の拡張子から取得します。
    184         * [bmp, gif, jpeg, jpg, png, wbmp] 位がサポートされています。
    185         *
    186         * @og.rev 6.0.2.3 (2014/10/10) 新規作成
    187         *
    188         * @param fin 入力ファイル名
    189         * @return 読み込まれた画像ファイルの byte配列
    190         * @og.rtnNotNull
    191         */
    192        public static byte[] byteImage( final String fin ) {
    193                final ByteArrayOutputStream baOut = new ByteArrayOutputStream();
    194
    195                final BufferedImage img = ImageUtil.readFile( fin );
    196                try {
    197                        final String suffix = ImageUtil.getSuffix( fin );
    198                        ImageIO.write( img, suffix, baOut );
    199                }
    200                catch( final IOException ex ) {
    201                        final String errMsg = "イメージファイルの読み込みに失敗しました。" + "File=[" + fin + "]";
    202                        throw new OgRuntimeException( errMsg,ex );
    203                }
    204                finally {
    205                        Closer.ioClose( baOut );                // ByteArrayOutputStreamを閉じても、何の影響もありません。
    206                }
    207
    208                return baOut.toByteArray();
    209        }
    210
    211        /**
    212         * ファイル名から拡張子(小文字)を求めます。
    213         * 拡張子 が存在しない場合は、null を返します。
    214         *
    215         * @og.rev 5.6.5.3 (2013/06/28) private ⇒ public へ変更
    216         * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。
    217         *
    218         * @param fileName ファイル名
    219         *
    220         * @return 拡張子(小文字)。なければ、null
    221         */
    222        public static String getSuffix( final String fileName ) {
    223                String suffix = null;
    224                if( fileName != null ) {
    225                        final int sufIdx = fileName.lastIndexOf( '.' );
    226                        if( sufIdx >= 0 ) {
    227                                suffix = fileName.substring( sufIdx + 1 ).toLowerCase( Locale.JAPAN );
    228                        }
    229                }
    230                return suffix;
    231        }
    232
    233        /**
    234         * ファイル名から入力画像になりうるかどうかを判定します。
    235         * コンストラクターの引数（入力画像）や、実際の処理の中（出力画像）で
    236         * 、変換対象となるかどうかをチェックしていますが、それを事前に確認できるようにします。
    237         *
    238         * @og.rev 5.6.5.3 (2013/06/28) 新規追加
    239         * @og.rev 5.6.6.1 (2013/07/12) getSuffix が null を返すケースへの対応
    240         * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。
    241         *
    242         * @param fileName ファイル名
    243         *
    244         * @return 入力画像として使用できるかどうか。できる場合は、true
    245         */
    246        public static boolean isReaderSuffix( final String fileName ) {
    247                final String suffix = getSuffix( fileName );
    248
    249                return suffix != null && READER_SUFFIXES.indexOf( suffix ) >= 0 ;
    250        }
    251
    252        /**
    253         * ファイル名から出力画像になりうるかどうかを判定します。
    254         * コンストラクターの引数（入力画像）や、実際の処理の中（出力画像）で
    255         * 、変換対象となるかどうかをチェックしていますが、それを事前に確認できるようにします。
    256         *
    257         * @og.rev 5.6.5.3 (2013/06/28) 新規追加
    258         * @og.rev 5.6.6.1 (2013/07/12) getSuffix が null を返すケースへの対応
    259         * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。
    260         *
    261         * @param fileName ファイル名
    262         *
    263         * @return 出力画像として使用できるかどうか。できる場合は、true
    264         */
    265        public static boolean isWriterSuffix( final String fileName ) {
    266                final String suffix = getSuffix( fileName );
    267
    268                return suffix != null && WRITER_SUFFIXES.indexOf( suffix ) >= 0 ;
    269        }
    270
    271        /**
    272         * ファイル名から入力画像になりうるかどうかを判定します。
    273         * コンストラクターの引数（入力画像）や、実際の処理の中（出力画像）で、
    274         * 変換対象となるかどうかをチェックしていますが、それを事前に確認できるようにします。
    275         *
    276         * @og.rev 6.0.2.3 (2014/10/10) 新規追加
    277         *
    278         * @param img 変換対象のBufferedImage
    279         * @param fCol 変換対象の色
    280         * @param tCol 変換後の色
    281         */
    282        public static void changeColor( final BufferedImage img , final Color fCol , final Color tCol ) {
    283                final int wd = img.getWidth();
    284                final int ht = img.getHeight();
    285                final int fc = fCol.getRGB();           // 変換元のRGB値。
    286                final int tc = tCol.getRGB();           // 変換後のRGB値。例：new Color( 255,255,255,0 ) なら、透明
    287
    288                for( int y=0; y<ht; y++ ) {
    289                        for( int x=0; x<wd; x++ ) {
    290                                if( img.getRGB( x,y ) == fc ) {
    291                                        img.setRGB( x,y,tc );
    292                                }
    293                        }
    294                }
    295        }
    296
    297        /**
    298         * BufferedImageをISOCoatedのICCプロファイルで読み込み、RGBにした結果を返します。
    299         * (CMYKからRBGへの変換、ビット反転)
    300         * なお、ここでは、外部の ICC_PROFILE(ISOcoated_v2_eci.icc) を利用して、処理速度アップを図りますが、
    301         * 存在しない場合、標準の、com.sun.media.jai.util.SimpleCMYKColorSpace を利用しますので、エラーは出ません。
    302         * ただし、ものすごく遅いため、実用的ではありません。
    303         * ISOcoated_v2_eci.icc ファイルは、zip圧縮して、拡張子をjar に変更後、(ISOcoated_v2_eci.jar)
    304         * javaエクステンション((JAVA_HOME\)jre\lib\ext) にコピーするか、実行時に、CLASSPATHに設定します。
    305         *
    306         * @og.rev 5.4.3.5 (2012/01/17)
    307         * @og.rev 5.5.3.4 (2012/06/19) ICC_PROFILE の取得先を、ISOcoated_v2_eci.icc に変更
    308         * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。(static にして)
    309         *
    310         * @param readImage BufferedImageオブジェクト
    311         *
    312         * @return 変換後のBufferedImage
    313         * @throws IOException 入出力エラーが発生したとき
    314         */
    315        public static BufferedImage cmykToSRGB( final BufferedImage readImage ) throws IOException {
    316                final ClassLoader loader = Thread.currentThread().getContextClassLoader();
    317                final InputStream icc_stream = loader.getResourceAsStream( ICC_PROFILE );
    318
    319                // 5.5.3.4 (2012/06/19) ICC_PROFILE が存在しない場合は、標準のSimpleCMYKColorSpace を使用。
    320                ColorSpace cmykCS = null;
    321                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    322                if( icc_stream == null ) {
    323                        // 遅いので標準のスペースは使えない
    324                        final String errMsg = ICC_PROFILE + " が見つかりません。" + CR
    325                                                        + " CLASSPATHの設定されている場所に配備してください。"      +       CR
    326                                                        + " 標準のSimpleCMYKColorSpaceを使用しますのでエラーにはなりませんが、非常に遅いです。" ;
    327                        System.out.println( errMsg );
    328                        cmykCS = SimpleCMYKColorSpace.getInstance();
    329                }
    330                else {
    331                        final ICC_Profile prof = ICC_Profile.getInstance(icc_stream);   //変換プロファイル
    332                        cmykCS = new ICC_ColorSpace(prof);
    333                }
    334
    335                final BufferedImage rgbImage = new BufferedImage(readImage.getWidth(),readImage.getHeight(), BufferedImage.TYPE_INT_RGB);
    336                final ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace();
    337                final ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null);
    338                cmykToRgb.filter(readImage, rgbImage);
    339
    340                final int width  = rgbImage.getWidth();
    341                final int height = rgbImage.getHeight();
    342                // 反転が必要
    343                for( int i=0;i<width;i++ ) {
    344                        for( int j=0;j<height;j++ ) {
    345                                int rgb = rgbImage.getRGB(i, j);
    346                                final int rr = (rgb & 0xff0000) >> 16;
    347                                final int gg = (rgb & 0x00ff00) >> 8;
    348                                final int bb =  rgb & 0x0000ff ;
    349                                rgb = (Math.abs(rr - 255) << 16) + (Math.abs(gg - 255) << 8) + (Math.abs(bb - 255));
    350                                rgbImage.setRGB(i, j, rgb);
    351                        }
    352                }
    353
    354                return rgbImage;
    355        }
    356
    357        /**
    358         * 画像イメージに、文字列を動的に合成作成して返します。
    359         *
    360         * 描画指定の位置(x,y)は、テキストの左下の位置を、画像イメージの、左上を起点(0,0)とした
    361         * 位置になります。
    362         * maxW , maxH を指定すると、テキストのフォントサイズをその範囲に収まるように自動調整します。
    363         *
    364         * @og.rev 6.0.2.3 (2014/10/10) 新規追加
    365         *
    366         * @param image 合成する元の画像オブジェクト
    367         * @param text  描画される文字列
    368         * @param xAxis テキストが描画される位置のx座標。または、{@link #LEFT LEFT},{@link #CENTER CENTER},{@link #RIGHT RIGHT} 指定で、自動計算する。
    369         * @param yAxis テキストが描画される位置のy座標。または、{@link #TOP TOP},{@link #MIDDLE MIDDLE},{@link #BOTTOM BOTTOM} 指定で、自動計算する。
    370         * @param maxW  テキストの最大幅(imageの幅と比較して小さい方の値。0以下の場合は、imageの幅)
    371         * @param maxH  テキストの最大高さ(imageの高さと比較して小さい方の値。0以下の場合は、imageの高さ)
    372         * @param font  描画されるテキストのフォント。null の場合は、初期値(Dialog.plain,12px)が使われる
    373         * @param color 描画されるテキストの色(Color)。null の場合は、Color.BLACK が使われる
    374         *
    375         * @return 合成された画像オブジェクト(BufferedImage)
    376         * @og.rtnNotNull
    377         * @see         #mixImage( BufferedImage, String, int, int, Font, Color )
    378         */
    379        public static BufferedImage mixImage( final BufferedImage image,
    380                                                                                        final String text, final int xAxis, final int yAxis, final int maxW, final int maxH,
    381                                                                                        final Font font, final Color color ) {
    382
    383                final int imgWidth  = image.getWidth();                                 // 画像の幅
    384                final int imgHeight = image.getHeight();                                        // 画像の高さ
    385
    386                final int maxWidth  = maxW <= 0 ? imgWidth  : Math.min( maxW,imgWidth );
    387                final int maxHeight = maxH <= 0 ? imgHeight : Math.min( maxH,imgHeight );
    388
    389                final Graphics2D gph = image.createGraphics();
    390                if( font != null ) { gph.setFont(  font  ); }           // new Font("Serif", Font.BOLD, 14)
    391
    392                float size = 5.0f;              // 小さすぎると見えないので、開始はこれくらいから行う。
    393                final float step = 0.5f;                // 刻み幅
    394                while( true ) {
    395                        final Font tmpFont = gph.getFont().deriveFont( size );
    396                        gph.setFont( tmpFont );
    397
    398                        final FontMetrics fm = gph.getFontMetrics();
    399                        final int txtWidth  = fm.stringWidth( text );
    400                        final int txtHeight = fm.getAscent();
    401
    402                        if( maxWidth < txtWidth || maxHeight < txtHeight ) {
    403                                size -= step;   // 一つ戻しておく。場合によっては、step分戻して、stepを小さくして続ける方法もある。
    404                                break;
    405                        }
    406                        size += step;
    407                }
    408                final Font newFont = gph.getFont().deriveFont( size );
    409
    410                return mixImage( image, text, xAxis, yAxis, newFont, color );
    411        }
    412
    413        /**
    414         * 画像イメージに、文字列を動的に合成作成して返します。
    415         *
    416         * 描画指定の位置(x,y)は、テキストの左下の位置を、画像イメージの、左上を起点(0,0)とした
    417         * 位置になります。
    418         *
    419         * @og.rev 6.0.2.3 (2014/10/10) org.opengion.hayabusa.servlet.MakeImage から、移植しました。
    420         *
    421         * @param image 合成する元の画像オブジェクト
    422         * @param text  描画される文字列
    423         * @param xAxis テキストが描画される位置のx座標。または、{@link #LEFT LEFT},{@link #CENTER CENTER},{@link #RIGHT RIGHT} 指定で、自動計算する。
    424         * @param yAxis テキストが描画される位置のy座標。または、{@link #TOP TOP},{@link #MIDDLE MIDDLE},{@link #BOTTOM BOTTOM} 指定で、自動計算する。
    425         * @param font  描画されるテキストのフォント。null の場合は、初期値(Dialog.plain,12px)が使われる
    426         * @param color 描画されるテキストの色(Color)。null の場合は、Color.BLACK が使われる
    427         *
    428         * @return 合成された画像オブジェクト(BufferedImage)
    429         * @og.rtnNotNull
    430         * @see         #mixImage( BufferedImage, String, int, int, int, int, Font, Color )
    431         */
    432        public static BufferedImage mixImage( final BufferedImage image,
    433                                                                                        final String text, final int xAxis, final int yAxis,
    434                                                                                        final Font font, final Color color ) {
    435
    436                final Graphics2D gph = image.createGraphics();
    437
    438        //      gph.setRenderingHint( java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
    439
    440                if( font  != null ) { gph.setFont(  font  ); }                  // new Font("Serif", Font.BOLD, 14)
    441                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    442                if( color == null ) { gph.setColor( Color.BLACK ); }    // new Color(0,0,255) など
    443                else {                            gph.setColor( color ); }
    444
    445                // 実際の位置ではなく、X軸が、LEFT,CENTER,RIGHT 等の指定
    446                int x1 = xAxis ;
    447                if( x1 < 0 ) {
    448                        final int imgWidth = image.getWidth();                                  // 画像の幅
    449                        final FontMetrics fm = gph.getFontMetrics();
    450                        final int txtWidth = fm.stringWidth( text );                            // テキストの長さ
    451
    452                        switch( x1 ) {
    453                                case LEFT   : x1 = 0;                                                   // 左寄せなので、0
    454                                                                break;
    455                                case CENTER : x1 = imgWidth/2 - txtWidth/2;             // 画像の中心から、テキストの中心を引き算
    456                                                                break;
    457                                case RIGHT  : x1 = imgWidth - txtWidth;                 // 右寄せは、画像の右端からテキスト分を引き算
    458                                                                break;
    459                                default :
    460                                        final String errMsg = "X軸 で範囲外のデータが指定されました。" + "text=[" + text + "]"
    461                                                                                + " (x,y)=[" + xAxis + "," + yAxis + "]" ;
    462                                        throw new OgRuntimeException( errMsg );
    463                                //      break;          制御は移りません。
    464                        }
    465                }
    466
    467                // 実際の位置ではなく、Y軸が、TOP,MIDDLE,BOTTOM 等の指定
    468                final int Ydef = 2 ;    // 良く判らないが、位置合わせに必要。
    469                int y1 = yAxis ;
    470                if( y1 < 0 ) {
    471                        final int imgHeight = image.getHeight() -Ydef;                  // 画像の高さ
    472                        final FontMetrics fm = gph.getFontMetrics();
    473                        final int txtHeight = fm.getAscent() -Ydef;                             // テキストの幅(=Ascent)
    474
    475                        switch( y1 ) {
    476                                case TOP    : y1 = txtHeight;                                   // 上寄せは、テキストの幅分だけ下げる
    477                                                                break;
    478                                case MIDDLE : y1 = (imgHeight)/2 + (txtHeight)/2 ;      // 画像の中心から、テキストの中心分下げる(加算)
    479                                                                break;
    480                                case BOTTOM : y1 = imgHeight;                                   // 下寄せは、画像の高さ分-2
    481                                                                break;
    482                                default :
    483                                        final String errMsg = "Y軸 で範囲外のデータが指定されました。" + "text=[" + text + "]"
    484                                                                                + " (x,y)=[" + xAxis + "," + yAxis + "]" ;
    485                                        throw new OgRuntimeException( errMsg );
    486                                //      break;          制御は移りません。
    487                        }
    488                }
    489
    490                gph.drawString( text, x1, y1 );
    491                gph.dispose();          // グラフィックス・コンテキストを破棄
    492
    493                return image;
    494        }
    495
    496        /**
    497         * アプリケーションのサンプルです。
    498         *
    499         * 入力イメージファイルを読み取って、テキストを合成して、出力イメージファイル に書き込みます。
    500         * テキストの挿入位置を、X軸、Y軸で指定します。
    501         * X軸とY軸には、特別な記号があり、左寄せ、右寄せ等の指示が可能です。
    502         *
    503         * サンプルでは、new Font("Serif", Font.PLAIN, 14); と、new Color(0,0,255);（青色）を固定で渡しています。
    504         *
    505         * Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル
    506         *                                                     -mix テキスト X軸 Y軸 [-fname=フォント名 -fstyle=スタイル -fsize=サイズ -color=カラー]
    507         *   X軸 指定(正の値は実際の位置)
    508         *    -1 ･･･ LEFT    左寄せ
    509         *    -2 ･･･ CENTER  中央揃え
    510         *    -3 ･･･ RIGHT   右寄せ
    511         *
    512         *   Y軸 指定(正の値は実際の位置)
    513         *    -4 ･･･ TOP     上揃え
    514         *    -5 ･･･ MIDDLE  中央揃え
    515         *    -6 ･･･ BOTTOM  下揃え
    516         *
    517         *   -fname=フォント名(初期値:Serif)
    518         *    Serif , SansSerif , Monospaced , Dialog , DialogInput
    519         *
    520         *   -fstyle=スタイル(初期値:0:PLAIN)を、数字で選びます。
    521         *    0:PLAIN ,  1:BOLD , 2:ITALIC
    522         *
    523         *   -fsize=サイズ(初期値:14)
    524         *    フォントサイズを整数で指定します。
    525         *
    526         *   -color=カラー
    527         *    色を表す文字列(BLUE,GREEN か、#808000 などの16bitRGB表記)
    528         *
    529         * Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル
    530         *                                                     -trans [-color=カラー -alpha=透過率(0-100%)]
    531         *   -color=カラー(初期値:WHITE)
    532         *    透明色にする色を指定(BLUE,GREEN か、#808000 などの16bitRGB表記)
    533         *
    534         *   -alpha=透過率(0-100%)(初期値:0)
    535         *    透過率は、0:透明から100不透明まで指定します。
    536         *
    537         * @og.rev 6.4.5.1 (2016/04/28) mainメソッドの起動方法を変更します。
    538         *
    539         * @param  args  引数文字列配列 入力ファイル、出力ファイル、縦横最大サイズ
    540         */
    541        public static void main( final String[] args ) {
    542                if( args.length < 3 ) {
    543                        final String usage = "Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル\n" +
    544                                                        "               -mix テキスト X軸 Y軸 [-fname=フォント名 -fstyle=スタイル -fsize=サイズ -color=カラー]\n" +
    545                                                        "\tX軸とY軸には、特別な記号があり、左寄せ、右寄せ等の指示が可能です。\n" +
    546                                                        "\t   X軸 指定(正の値は実際の位置)\n"       +
    547                                                        "\t    -1 ･･･ LEFT    左寄せ\n"                    +
    548                                                        "\t    -2 ･･･ CENTER  中央揃え\n"           +
    549                                                        "\t    -3 ･･･ RIGHT   右寄せ\n"                    +
    550                                                        "\t\n"                                                                  +
    551                                                        "\t   Y軸 指定(正の値は実際の位置)\n"       +
    552                                                        "\t    -4 ･･･ TOP     上揃え\n"                    +
    553                                                        "\t    -5 ･･･ MIDDLE  中央揃え\n"           +
    554                                                        "\t    -6 ･･･ BOTTOM  下揃え\n"                    +
    555                                                        "\t\n"                                                                  +
    556                                                        "\t   -fname=フォント名(初期値:Serif)\n"        +
    557                                                        "\t    Serif , SansSerif , Monospaced , Dialog , DialogInput\n" +
    558                                                        "\t\n"                                                                  +
    559                                                        "\t   -fstyle=スタイル(初期値:0:PLAIN)\n"      +
    560                                                        "\t    0:PLAIN ,  1:BOLD , 2:ITALIC\n"  +
    561                                                        "\t\n"                                                                  +
    562                                                        "\t   -fsize=サイズ(初期値:14)\n"             +
    563                                                        "\t    フォントサイズを整数で指定\n" +
    564                                                        "\t\n"                                                                  +
    565                                                        "\t   -color=カラー\n"                                     +
    566                                                        "\t    色を表す文字列(BLUE,GREEN か、#808000 などの16bitRGB表記)\n"   +
    567                                                        "\t\n"                                                                  +
    568                                                        "Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル\n" +
    569                                                        "               -trans [-color=カラー -alpha=透過率(0-100%)]\n"               +
    570                                                        "\t   -color=カラー\n"                                     +
    571                                                        "\t    透明色にする色を指定(BLUE,GREEN か、#808000 などの16bitRGB表記)"  +
    572                                                        "\t   -alpha=透過率(0-100%)\n"                                     +
    573                                                        "\t    透過率は、0:透明から100不透明まで指定します。\n" ;
    574                        System.out.println( usage );
    575                        return ;
    576                }
    577
    578                final String inImg  = args[0];
    579                final String outImg = args[1];
    580                final String imgType= args[2];
    581
    582//              final boolean isMix = imgType.equals( "-mix" );                 // 文字列合成
    583//              final boolean isTrn = imgType.equals( "-trans" );               // 透過色指定
    584
    585                final BufferedImage image = ImageUtil.readFile( inImg );
    586
    587                final boolean isMix = imgType.equals( "-mix" );                 // 文字列合成
    588                if( isMix ) {
    589                        final String text   = args[3];
    590                        final int x = Integer.parseInt( args[4] );
    591                        final int y = Integer.parseInt( args[5] );
    592
    593                        String  fname  = "Serif";
    594                        int             fstyle = Font.PLAIN;            // =0;
    595                        int             fsize  = 14;
    596                        Color   color  = Color.BLUE;
    597
    598                        for( int i=6; i<args.length; i++ ) {
    599                                if( args[i].startsWith( "-fname="       ) ) { fname             = args[i].substring( 7 ); }                                                                             // 7 = "-fname=".length()
    600                                if( args[i].startsWith( "-fstyle="      ) ) { fstyle    = Integer.parseInt(                      args[i].substring( 8 ) ); }            // 8 = "-fstyle=".length()
    601                                if( args[i].startsWith( "-fsize="       ) ) { fsize             = Integer.parseInt(                      args[i].substring( 7 ) ); }            // 7 = "-fsize=".length()
    602                                if( args[i].startsWith( "-color="       ) ) { color             = ColorMap.getColorInstance( args[i].substring( 7 ) ); }                // 7 = "-color=".length()
    603                        }
    604
    605                        // 6.9.8.0 (2018/05/28) FindBugs:条件は効果がない
    606//                      if( isMix ) {
    607                                final Font font = new Font( fname, fstyle, fsize );
    608                                ImageUtil.mixImage( image , text , x , y , font , color );
    609//                      }
    610                }
    611
    612                final boolean isTrn = imgType.equals( "-trans" );               // 透過色指定
    613                if( isTrn ) {
    614                        Color   fColor = Color.WHITE;
    615                        int             alpha  = 0;
    616
    617                        for( int i=3; i<args.length; i++ ) {
    618                                if( args[i].startsWith( "-color="       ) ) { fColor = ColorMap.getColorInstance( args[i].substring( 7 ) ); }                   // 7 = "-color=".length()
    619                                if( args[i].startsWith( "-alpha="       ) ) { alpha      = 255 / 100 * Integer.parseInt( args[i].substring( 7 ) ); }            // 7 = "-alpha=".length()
    620                        }
    621
    622                        final Color tColor = new Color( fColor.getRed() , fColor.getGreen() , fColor.getBlue() , alpha );
    623
    624                        ImageUtil.changeColor( image , fColor , tColor );
    625                }
    626
    627                ImageUtil.saveFile( image , outImg );
    628        }
    629}