001 /* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016 package org.opengion.fukurou.util; 017 018 import java.awt.Rectangle; 019 import java.awt.Robot; 020 import java.awt.image.BufferedImage; 021 import java.awt.AWTException; 022 import javax.imageio.ImageIO; 023 024 import java.awt.Toolkit; 025 import java.awt.datatransfer.Clipboard; 026 import java.awt.datatransfer.DataFlavor; 027 import java.awt.datatransfer.StringSelection; 028 import java.awt.datatransfer.FlavorListener; 029 import java.awt.datatransfer.FlavorEvent; 030 import java.awt.datatransfer.UnsupportedFlavorException; 031 import java.io.IOException; 032 import java.io.File; 033 034 /** 035 * DisplayCapture.java は、画面イメージをキャプチャして、ファイルに書き?すため?クラスです? 036 * 037 * 基本?使?は、main メソ?から立ち上げて、クリ??ボ?ド?状態を監視します? 038 * クリ??ボ?ドに?GUI:画面ID xxxxx.jsp" 形式??が書き込まれると、flavorsChanged イベントが 039 * 発生して、画面を?ファイルに書き?す??実行されます? 040 * エンジンの機?と連動すれ?、画面ID?ァイル名をクリ??ボ?ド経由でこ?アプリケーションに 041 * 渡すことで、画面操作を行う?で、?動的に画面キャプチャを行うことが可能です? 042 * 043 * エンジンでは、jsp/indexc.jsp に、この機?が?込まれて?ため、gf\BAT\displayCapture の 044 * DisplayCapture.bat を起動後?アプリケーションを?jsp/indexc.jsp で呼び出せ?、?動的に 045 * 画面のキャプチャを開始できます? 046 * キャプチャは、?画面のみです?で、IEを最大に?て操作してください? 047 * また??Prnt Scrn?ボタンにも対応して?す?で、操作中????ア??等?非?動化の 048 * 画面キャプチャも?手動で取得できます? 049 * 050 * 起動時の引数に応じて、??制御することが可能です? 051 * 052 * 書き?すフォル??、BASE_DIR で?します? 053 * すべてのキャプチャ画像?、?ースフォル?に??て保存します? 054 * キャプチャ画像???は、?力されるファイル名に反映されます?ファイル名?形式も?種類あり? 055 * キャプチャ?、画面ID??できます? 056 * 初期値は、Java起動時のフォル?なります? 057 * 058 * 書き?すファイル名?初期形式?、firstID の設定により異なります? 059 * firstID ?gui に設定した?? 060 * ベ?スフォル?フォル?作?し?そ?中に、画面ID_JSPファイル名_連番.画像形?ファイルを作?します? 061 * 画面ID 単位に、画面のキャプチャを整??使用した??合に便利です? 062 * ファイルのタイ?タン?作?時刻)で並び替えを行えば、キャプチャ?並び替えできます? 063 * firstID ?seq に設定した?? 064 * ベ?スフォル?フォル?作?し?そ?中に??番_画面ID_JSPファイル?画像形?ファイルを作?します? 065 * ファイルは、キャプチャされた?番に、画面IDも混在して作?されます?つまり?ファイル名??に 066 * 再生すれば、リンク??シス?との連携などで、画面が行き来しても?作業の?にキャプチャできて 067 * ?事になります? 068 * 069 * こ?クラスは、これらを実現するために利用して?、static メソ?をいくつか持って?す? 070 * BufferedImage doCapture() 071 * 画面イメージをキャプチャします?これは、?画面です? 072 * void saveImage( File saveFile, BufferedImage img, String imgType ) 073 * ??ファイルに、画面イメージを書き?します? 074 * imgType に、画像?種?png|gif|jpg)を指定しま?初期値:png)? 075 * String getClipboard() 076 * 現在のクリ??ボ?ド?値を取り?します?ここでは、文字?のみ取り出すことが可能です? 077 * こ?メソ?の特徴?ところは、PrintScreenなどの??以外?値をクリ??ボ?ドにセ? 078 * した場合に?GUI:PRINT SCREEN.img" と???を返すところです?つまり?そ?場合?? 079 * 全画面のキャプチャが行われると?事です? 080 * void setClipboard( String txt ) 081 * クリ??ボ?ドに、文字?をセ?します? 082 * 083 * こ?クラスが実?て? FlavorListener は、クリ??ボ?ド?"値"の更新には追従して?せん? 084 * ?の Transferable オブジェクトが変更された?合に、flavorsChanged メソ?が呼び出されます? 085 * つまり??セ?された文字型??タは、取り?した後?別のTransferable オブジェクトに変更して 086 * おかな?、次の??の変更が拾えなくなります?また?こ?別のTransferableオブジェクト? 087 * 設定で、?び、イベントが発生する?で、そのままでは、無限ループになってしま?す? 088 * そこで、少し、トリ?ーなのですが、setClipboard( String ) すると、?びイベントが呼び出され 089 * な??、取得した文字?の先?が?"GUI:" で始まる?合?み、?設定するよ?して?す? 090 * 091 * Usage: java org.opengion.fukurou.util.DisplayCapture 092 * [BASE_DIR] [firstID(seq|gui)] [imageFormat(png|gif|jpg)] [startCnt(100)] 093 * 094 * args[0] BASE_DIR : キャプチャファイルをセーブする?ースとなるディレクトリ(初期値:起動フォル? 095 * args[1] firstID : キャプチャ画像をセーブするファイル方式を?しま?初期値:seq) 096 * gui (画面ID_JSPファイル名_連番.画像形? 097 * seq (連番_画面ID_JSPファイル?画像形? 098 * args[2] imageFormat : 作?するイメージの形式?png|gif|jpg のどれか(初期値:png) 099 * args[3] startCnt : セーブファイル名をユニ?クにするためのカウン?初期値:100) 100 * 101 * こ?実??同期化されません? 102 * 103 * @og.rev 5.1.7.0 (2010/06/01) 新規追? 104 * @og.rev 5.2.1.0 (2010/10/01) 実用性を重視した改修 105 * 106 * @version 5.0 107 * @author Kazuhiko Hasegawa 108 * @since JDK6.0, 109 */ 110 public final class DisplayCapture implements FlavorListener { 111 private static final Clipboard CLIP_BOARD = Toolkit.getDefaultToolkit().getSystemClipboard(); 112 private static final Rectangle SCRN_SIZE = new Rectangle( Toolkit.getDefaultToolkit().getScreenSize() ); 113 114 private File baseDir = new File("."); // セーブする?ース?レクトリ 115 private String firstID = "seq"; // 保存時のファイル名?形?seq|gui) 116 private String imgType = "png" ; // 画像形?png|gif|jpg) 117 private int cnt = 100; // ユニ?ク番号(セーブファイル名に付? 118 119 /** 120 * キャプチャファイルをセーブする?ースとなるディレクトリを設定しま?初期値:java実行フォル?? 121 * 122 * クラスの??は、Java の実行フォル? new File(".") ) が?期?です? 123 * 124 * @param bsDir セーブする?ース?レクトリ 125 * @throws RuntimeException セーブフォル?作?できなかった?? 126 */ 127 public void setBaseDir( final String bsDir ) { 128 if( bsDir != null && bsDir.length() > 0 ) { 129 baseDir = new File( bsDir ); 130 131 // ベ?スフォル??作?。?期?は起動フォル?ので、?存在する(は? 132 if( !baseDir.exists() && !baseDir.mkdirs() ) { 133 String errMsg = "ERROR:セーブフォル?作?できませんでした? + baseDir.getAbsolutePath() ; 134 throw new RuntimeException( errMsg ); 135 } 136 } 137 } 138 139 /** 140 * キャプチャ画像をセーブするファイル方式を?しま?初期値:seq)? 141 * 142 * seq (連番_画面ID_JSPファイル?画像形? 143 * gui (画面ID_JSPファイル名_連番.画像形? 144 * 145 * 初期値は、seq です? 146 * 147 * @param firstID セーブするファイル方?seq|gui) 148 * @throws RuntimeException ファイル方式??が間違って?場? 149 */ 150 public void setFirstID( final String firstID ) { 151 if( firstID != null ) { 152 if( firstID.matches( "seq|gui" ) ) { 153 this.firstID = firstID; 154 } 155 else { 156 String errMsg = "ERROR:firstID 属?は?seq|gui)でお願いします?firstID=[" + firstID + "]" ; 157 throw new RuntimeException( errMsg ); 158 } 159 } 160 } 161 162 /** 163 * キャプチャ画像をセーブする画像形式を?しま?初期値:png)? 164 * 165 * キャプチャされたイメージをセーブするとき?画像形式を?できます? 166 * ここでは、png , gif ,jpg を指定できます? 167 * 168 * 初期値は、png 形式です? 169 * 170 * @param imgType セーブする画像形?png|gif|jpg) 171 */ 172 public void setImageType( final String imgType ) { 173 if( imgType != null && imgType.matches( "png|gif|jpg" ) ) { 174 this.imgType = imgType; 175 } 176 } 177 178 /** 179 * キャプチャ画像をセーブするファイル名?先?に付ける?番の開始数(初期値:100)? 180 * 181 * キャプチャされたイメージをセーブするとき?画面IDとJSPファイル名だけでは、前回?? 182 * 上書きしてしま?め?ファイル名?先?に連番を付与して?す? 183 * ここでは、その連番の開始番号を指定できます? 184 * 185 * 初期値は?00 です? 186 * 187 * @param startCnt 連番の開始数(初期値:100) 188 */ 189 public void setStartCnt( final String startCnt ) { 190 if( startCnt != null && startCnt.length() > 0 ) { 191 cnt = Integer.parseInt( startCnt ); 192 } 193 } 194 195 /** 196 * 全画面の画像イメージ(キャプチャ画?を取得します? 197 * 198 * java.awt.Toolkit で、?画面のスクリーンサイズを取得し、java.awt.Robot の 199 * createScreenCapture( Rectangle ) メソ?で、BufferedImage を取得して?す? 200 * 201 * @return 全画面の画像イメージ 202 * @throws RuntimeException AWTException が発生した?? 203 */ 204 public static BufferedImage doCapture() { 205 BufferedImage img = null; 206 try{ 207 Robot robo = new Robot(); 208 img = robo.createScreenCapture( SCRN_SIZE ); 209 } 210 catch( AWTException ex ) { 211 // ex.printStackTrace(); 212 String errMsg = "ERROR:画像イメージ(キャプチャ画?が取得できませんでした? ; 213 setClipboard( errMsg ); 214 throw new RuntimeException( errMsg,ex ); 215 } 216 catch( Throwable th ) { 217 String errMsg = "ERROR:" + th.getLocalizedMessage() ; 218 setClipboard( errMsg ); 219 throw new RuntimeException( errMsg,th ); 220 } 221 222 return img; 223 } 224 225 /** 226 * キャプチャ画像をファイルにセーブします? 227 * 228 * ここでは、単純に、引数そ?ままで、ImageIO.write( BufferedImage,String,File ) して?す? 229 * saveFile の?レクトリ存在チェ???ファイル名?拡張?png,gif,jpgなど)の修正? 230 * imgType の形式チェ?などは、行って?せん? 231 * それら?処??、事前に、調整しておいてください? 232 * 233 * @param img セーブする画像イメージ 234 * @param imgType セーブする画像形?png|gif|jpg) 235 * @param saveFile セーブする画像ファイルオブジェク? 236 * @throws RuntimeException IOException が発生した?? 237 * @see javax.imageio.ImageIO#write( java.awt.image.RenderedImage , String , java.io.File ) 238 */ 239 public static void saveImage( final BufferedImage img , final String imgType , final File saveFile ) { 240 try{ 241 ImageIO.write( img,imgType,saveFile ); 242 } 243 catch( IOException ex ) { 244 // ex.printStackTrace(); 245 String errMsg = "ERROR:キャプチャ画像をファイルにセーブできませんでした? + saveFile.getAbsolutePath() ; 246 setClipboard( errMsg ); 247 throw new RuntimeException( errMsg,ex ); 248 } 249 catch( Throwable th ) { 250 String errMsg = "ERROR:" + th.getLocalizedMessage() ; 251 setClipboard( errMsg ); 252 throw new RuntimeException( errMsg,th ); 253 } 254 } 255 256 /** 257 * シス?のクリ??ボ?ド???を取得します? 258 * 259 * Toolkit.getDefaultToolkit().getSystemClipboard() で取得された Clipboard オブジェクトか? 260 * ????(DataFlavor.stringFlavor)を取得します? 261 * ????が取得できな??合?(UnsupportedFlavorException が発生した?? 例えば? 262 * PrntScrn ボタンが押された?合などは、文字?として?GUI:PRINT SCREEN.img" を返します? 263 * これは、文字?が返せな??合でも?クリ??ボ?ドに書き込まれたイベントで、?画面のキャプチャ? 264 * 取得するため?、特殊なコマンドに相当します? 265 * 266 * @return クリ??ボ?ド??? 267 * @throws RuntimeException IOException が発生した?? 268 * @see java.awt.datatransfer.Clipboard#getData( DataFlavor ) 269 */ 270 public static String getClipboard() { 271 String strClip = null; 272 273 // 方法として、Transferable を取得後?getTransferData する事もできる? 274 // Transferable data = CLIP_BOARD.getContents(null); 275 276 try { 277 // クリ??ボ?ド?値を取? 278 // strClip = (String)data.getTransferData(DataFlavor.stringFlavor); 279 strClip = (String)CLIP_BOARD.getData( DataFlavor.stringFlavor ); 280 } 281 catch( UnsupportedFlavorException ex ) { 282 // PrintScreen が押された?合? 283 strClip = "GUI:PRINT SCREEN.img" ; // 形式をGUI:画面ID xxxxx.jsp 形式に合わす為 284 } 285 catch( IOException ex ) { 286 // ex.printStackTrace(); 287 String errMsg = "ERROR:クリ??ボ?ド?値を取得できませんでした? ; 288 setClipboard( errMsg ); 289 throw new RuntimeException( errMsg,ex ); 290 } 291 catch( Throwable th ) { 292 String errMsg = "ERROR:" + th.getLocalizedMessage() ; 293 setClipboard( errMsg ); 294 throw new RuntimeException( errMsg,th ); 295 } 296 297 return strClip; 298 } 299 300 /** 301 * シス?のクリ??ボ?ドに??を書き込みます? 302 * 303 * シス?の Clipboard オブジェクトに、StringSelection ?セ?します? 304 * 通常であれば、単純に、クリ??ボ?ド経由で??タの?取りをするだけ?機構ですが? 305 * FlavorListener を実?て?関係上?flavorsChanged が発生します? 306 * こ?イベントにつ?は?flavorsChanged( FlavorEvent ) を参照ください? 307 * 308 * @param txt クリ??ボ?ドに書き込?字? 309 * @see java.awt.datatransfer.StringSelection 310 * @see java.awt.datatransfer.Clipboard#setContents( Transferable , ClipboardOwner ) 311 */ 312 public static void setClipboard( final String txt ) { 313 StringSelection strSel = new StringSelection( txt ); 314 CLIP_BOARD.setContents( strSel, null ); 315 } 316 317 /** 318 * リスナ?対象の Clipboard で使用可能な DataFlavor が変更されたときに呼び出されます? 319 * 320 * これは、FlavorListener の イベント?実?す? 321 * DataFlavor が変更されたときであり、その??タの?が書き換えられた場合には、イベントが 322 * 発生しません? 323 * そ?ため、データを取り?したあとで、Transferable を?セ?する処?行って?す? 324 * 325 * クリ??ボ?ドで使用可能な??の DataFlavors の変更によるも?でな??余?な通知もあります? 326 * さらに、イベントを発生させるために、Transferable をセ?する処? #setClipboard(String) )? 327 * 実行しても?同様にイベントが発生します? 328 * 329 * ここでは、取得したクリ??ボ?ド???が?"GUI:" の場合?み処?て?す? 330 * これにより、取得後? Transferable の再セ?時???は?GUI:" を削除して?す? 331 * 332 * こ?メソ?では、画面キャプチャを取得し、クリ??ボ?ド???から、画面ID とJSPファイル名を 333 * 抜き出し?セーブする??の処?行って?す? 334 * 335 * @param fe イベントソース 336 * @see java.awt.datatransfer.FlavorListener#flavorsChanged( FlavorEvent ) 337 */ 338 @Override 339 public void flavorsChanged( final FlavorEvent fe ) { 340 String txt = getClipboard(); 341 342 // クリ??ボ?ド?値をクリアしたとき?イベント?、拾わな?め? 343 if( txt != null && txt.length() > 0 && txt.startsWith( "GUI:" ) ) { 344 System.out.println( cnt + ":? + txt + "? ); 345 BufferedImage img = doCapture(); 346 347 File saveFile = makeSaveFile( txt ); 348 349 saveImage( img,imgType,saveFile ); 350 351 // クリ??ボ?ド?Flavorを置換します?(Windwosからセ?された時にイベントを発生させるため? 352 // 先?の GUI: を取り除く?イベント?無限ループを防ぐ意味? 353 setClipboard( txt.substring( 4 ) ); 354 } 355 } 356 357 /** 358 * キャプチャ画像を書き?すファイルオブジェクトを作?します? 359 * 360 * 引数は?GUI:画面ID xxxxx.jsp" 形式を想定した文字?です? 361 * 362 * ファイル名には?種類あります? 363 * firstID ?seq に設定した?? 364 * ベ?スフォル?フォル?作?し?そ?中に??番_画面ID_JSPファイル?画像形?ファイルを作?します? 365 * ファイルは、キャプチャされた?番に、画面IDも混在して作?されます?つまり?ファイル名??に 366 * 再生すれば、リンク??シス?との連携などで、画面が行き来しても?作業の?にキャプチャできて 367 * ?事になります? 368 * firstID ?gui に設定した?? 369 * ベ?スフォル?フォル?作?し?そ?中に、画面ID_JSPファイル名_連番.画像形?ファイルを作?します? 370 * 画面ID 単位に、画面のキャプチャを整??使用した??合に便利です? 371 * ファイルのタイ?タン?作?時刻)で並び替えを行えば、キャプチャ?並び替えできます? 372 * 373 * こ?メソ?で、フォル??存在チェ?、およ?、無ければ作?(mkdirs)も行います? 374 * 375 * @param txt ファイル名??なる文字?("GUI:画面ID xxxxx.jsp" 形? 376 * 377 * @return 書き?すファイルオブジェク? 378 */ 379 private File makeSaveFile( final String txt ) { 380 int spc = txt.indexOf( ' ' ); // "GUI:画面ID xxxxx.jsp" をスペ?スで? 381 String gui = txt.substring( 4,spc ); // 画面ID の部??み?出す? 382 String jsp = txt.substring( spc+1,txt.length()-4 ); // xxxxx の部??み?出す? 383 384 String saveFile = null; 385 if( "seq".equalsIgnoreCase( firstID ) ) { 386 saveFile = cnt++ + "_" + gui + "_" + jsp + "." + imgType ; 387 } 388 else if( "gui".equalsIgnoreCase( firstID ) ) { 389 saveFile = gui + "_" + jsp + "_" + cnt++ + "." + imgType ; 390 } 391 else { // 5.5.2.6 (2012/05/25) findbugs対? 392 saveFile = cnt++ + "_" + gui + "_" + jsp + "." + imgType ; // seqかguiしかな?、経路として、?期?(seq)を設定しておく? 393 } 394 395 File svf = new File( baseDir,saveFile ); 396 // セーブフォル??作?? 397 File parent = svf.getParentFile(); 398 if( !parent.exists() && !parent.mkdirs() ) { 399 String errMsg = "ERROR:セーブフォル?作?できませんでした? + parent.getAbsolutePath() ; 400 setClipboard( errMsg ); 401 throw new RuntimeException( errMsg ); 402 } 403 404 // セーブファイルの存在チェ?。上書き禁止にしておきます? 405 if( svf.exists() ) { 406 String errMsg = "ERROR:セーブファイルがすでに作?されて?す?" + svf.getAbsolutePath() ; 407 setClipboard( errMsg ); 408 throw new RuntimeException( errMsg ); 409 } 410 411 return svf; 412 } 413 414 /** 415 * DisplayCapture.java は、画面イメージをキャプチャする、メインメソ?です? 416 * 417 * Javaアプリケーションとして実行すると、無限??入ります? 418 * ??は、flavorsChanged イベン?によるクリ??ボ?ド?監視を行います? 419 * クリ??ボ?ドに?GUI:画面ID xxxxx.jsp" 形式??が書き込まれると、画面キャプチャを? 420 * ファイルに書き?す??実行されます? 421 * 書き?すファイル名?初期形式?、firstID の設定により異なります? 422 * firstID ?seq に設定した?? 423 * ベ?スフォル?フォル?作?し?そ?中に??番_画面ID_JSPファイル?画像形?ファイルを作?します? 424 * ファイルは、キャプチャされた?番に、画面IDも混在して作?されます?つまり?ファイル名??に 425 * 再生すれば、リンク??シス?との連携などで、画面が行き来しても?作業の?にキャプチャできて 426 * ?事になります? 427 * firstID ?gui に設定した?? 428 * ベ?スフォル?フォル?作?し?そ?中に、画面ID_JSPファイル名_連番.画像形?ファイルを作?します? 429 * 画面ID 単位に、画面のキャプチャを整??使用した??合に便利です? 430 * ファイルのタイ?タン?作?時刻)で並び替えを行えば、キャプチャ?並び替えできます? 431 * 432 * Usage: java org.opengion.fukurou.util.DisplayCapture 433 * [BASE_DIR] [firstID(seq|gui)] [imageFormat(png|gif|jpg)] [startCnt(100)] 434 * 435 * args[0] BASE_DIR : キャプチャファイルをセーブする?ースとなるディレクトリ(初期値:起動フォル? 436 * args[1] firstID : キャプチャ画像をセーブするファイル方式を?しま?初期値:seq) 437 * seq (連番_画面ID_JSPファイル?画像形? 438 * gui (画面ID_JSPファイル名_連番.画像形? 439 * args[2] imageFormat : 作?するイメージの形式?png|gif|jpg のどれか(初期値:png) 440 * args[3] startCnt : セーブファイル名をユニ?クにするためのカウン?初期値:100) 441 * 442 * @param args 引数 [BASE_DIR] [firstID(seq|gui)] [imageFormat(png|gif|jpg)] [startCnt(100)] 443 */ 444 public static void main( final String[] args ) { 445 System.out.println( "DisplayCapture を起動しました? ); 446 447 DisplayCapture dispCap = new DisplayCapture(); 448 449 if( args.length > 0 ) { dispCap.setBaseDir( args[0] ); } 450 if( args.length > 1 ) { dispCap.setFirstID( args[1] ); } 451 if( args.length > 2 ) { dispCap.setImageType( args[2] ); } 452 if( args.length > 3 ) { dispCap.setStartCnt( args[3] ); } 453 454 // クリ??ボ?ド?値をクリア(FlavorEvent を起こさせるため) 455 DisplayCapture.setClipboard( null ); 456 457 // FlavorListener を登録します?(自??身のオブジェク? 458 CLIP_BOARD.addFlavorListener( dispCap ); 459 460 // FlavorEvent で処?せるので、ずっとスレ?をSleepさせておけばよい? 461 while( true ) { 462 try { 463 Thread.sleep( 100000 ); 464 } 465 catch( InterruptedException ex ) {} 466 } 467 } 468 }