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.report2; 017 018import java.io.File; 019import java.util.Map; 020import java.util.HashMap; 021import java.util.Locale; 022 023import org.opengion.fukurou.util.FileUtil; 024import org.opengion.fukurou.util.StringUtil; 025import org.opengion.hayabusa.common.HybsSystem; 026import org.opengion.hayabusa.common.HybsSystemException; 027import static org.opengion.fukurou.system.HybsConst.CR ; // 6.1.0.0 (2014/12/26) 028 029import com.sun.star.beans.PropertyValue; 030import com.sun.star.frame.XComponentLoader; 031import com.sun.star.frame.XController; 032import com.sun.star.frame.XDispatchHelper; 033import com.sun.star.frame.XDispatchProvider; 034import com.sun.star.frame.XModel; 035import com.sun.star.frame.XStorable; 036import com.sun.star.io.IOException; 037import com.sun.star.lang.EventObject; 038import com.sun.star.lang.IllegalArgumentException; 039import com.sun.star.lang.XComponent; 040import com.sun.star.uno.UnoRuntime; 041import com.sun.star.util.CloseVetoException; 042import com.sun.star.util.XCloseable; 043import com.sun.star.view.PrintJobEvent; 044import com.sun.star.view.PrintableState; 045import com.sun.star.view.XPrintJobBroadcaster; 046import com.sun.star.view.XPrintJobListener; 047import com.sun.star.view.XPrintable; 048 049/** 050 * OpenOfficeを利用して様々な形式のファイルを読み込み、出力・印刷を行うための変換クラスです。 051 * 052 * 変換を行うことのできる入出力のフォーマット以下の通りです。 053 * 054 * [対応フォーマット] 055 * 入力[Calc(ODS) ,Excel(XLS) ] ⇒ 出力[Calc(ODS) ,Excel(XLS) ,PDF] 056 * 入力[Writer(ODT) ,Word(DOC) ] ⇒ 出力[Writer(ODT) ,Word(DOC) ,PDF] 057 * 入力[Impress(ODP),PowerPoint(PPT)] ⇒ 出力[Impress(ODP),PowerPoint(PPT),PDF] 058 * 入力[ * 上記の全て ] ⇒ 印刷 059 * 060 * 変換を行うには、以下の2通りの方法があります。 061 * (1)簡易的な変換メソッドを利用する場合 062 * {@link #convert(String, String)}を利用して、変換を行います。 063 * この場合、出力形式は、出力ファイルの拡張子に従って自動的に決定されます。 064 * このため、印刷処理などを行う場合は、(2)の方法で出力して下さい。 065 * (2)段階的に書くメソッドを呼び出して変換する場合 066 * オブジェクトを生成した後、{@link #open()}、#(各種変換メソッド)、{@link #clone()}を 067 * 順番に呼び出して変換を行います。 068 * この場合、出力形式は、それに対応するメソッドを呼び出ることで決定されます。 069 * 070 * また、変換を行うための、各種メソッドは、例外としてThrowableを投げるように定義されています。 071 * このクラスを利用する場合は、このThrowableをcatchし、catch句で、必ず{@link #close( boolean )}に、 072 * "true"(エラー発生時のクローズ処理)を指定して、終了処理を行って下さい。 073 * (これを行わない場合、OpenOfficeの不要なプロセスが残ってしまう可能性があります) 074 * 075 * また、出力ファイルが既に存在する場合、出力ファイルは一旦削除された後、処理されます。 076 * なお、入力ファイルと出力ファイルが同じ場合、何も処理されません。(例外も発行されません) 077 * 078 * 入力ファイルを、CSV形式で複数指定した場合、複数の入力ファイルをマージして出力します。 079 * ※1 現状は、ファイルのマージは、入力ファイルがExcelまたはCalcの場合のみ対応しています。 080 * 081 * @og.group 帳票システム 082 * 083 * @version 4.0 084 * @author Hiroki.Nakamura 085 * @since JDK1.6 086 */ 087public class DocConverter_OOO { 088 089 private final boolean isOnline; // オンライン処理かどうか(オンライン処理の場合、プロセスはファクトリクラス経由で生成されます) 090 private final String[] mergeFile; 091 092 private String inputName; 093 private String origName; 094 095 private XComponent xComp; // 5.1.8.0 (2010/07/01) メソッドと重なる変数名の変更 096 private SOfficeProcess soffice; 097 098 // 入力、出力の拡張子とこれに対応するフィルター名 099 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 100 private static final Map<String,String> FILTER_MAP = new HashMap<>(); // 6.4.1.1 (2016/01/16) filterMap → FILTER_MAP refactoring 101 static { 102 FILTER_MAP.put( "ods_ods", "calc8" ); 103 FILTER_MAP.put( "xls_ods", "calc8" ); 104 FILTER_MAP.put( "ods_xls", "MS Excel 97" ); 105 FILTER_MAP.put( "xls_xls", "MS Excel 97" ); 106 FILTER_MAP.put( "ods_pdf", "calc_pdf_Export" ); 107 FILTER_MAP.put( "xls_pdf", "calc_pdf_Export" ); 108 FILTER_MAP.put( "odt_odt", "writer8" ); 109 FILTER_MAP.put( "doc_odt", "writer8" ); 110 FILTER_MAP.put( "odt_doc", "MS Word 97" ); 111 FILTER_MAP.put( "doc_doc", "MS Word 97" ); 112 FILTER_MAP.put( "odt_pdf", "writer_pdf_Export" ); 113 FILTER_MAP.put( "doc_pdf", "writer_pdf_Export" ); 114 FILTER_MAP.put( "odp_odp", "impress8" ); 115 FILTER_MAP.put( "ppt_odp", "impress8" ); 116 FILTER_MAP.put( "odp_ppt", "MS PowerPoint 97" ); 117 FILTER_MAP.put( "ppt_ppt", "MS PowerPoint 97" ); 118 FILTER_MAP.put( "odp_pdf", "impress_pdf_Export" ); 119 FILTER_MAP.put( "ppt_pdf", "impress_pdf_Export" ); 120 }; 121 122 /** 123 * コンストラクタです。 124 * 125 * #DocConverter(input, true)と同じです。 126 * 127 * @param input ファイル一覧(CSV形式) 128 * @see #DocConverter_OOO(String[]) 129 */ 130 public DocConverter_OOO( final String input ) { 131 this( StringUtil.csv2Array( input ), true ); 132 } 133 134 /** 135 * コンストラクタです。 136 * 137 * #DocConverter(input, true)と同じです。 138 * 139 * @param input ファイル一覧(配列) 140 * @see #DocConverter_OOO(String[], boolean) 141 */ 142 public DocConverter_OOO( final String input[] ) { 143 this( input, true ); 144 } 145 146 /** 147 * コンストラクタです。 148 * 149 * isOnline(isOl)がtrueに指定された場合、soffice.binのプロセスをファクトリークラス経由で生成し、 150 * キャッシュします。 151 * 但し、システムリソースが読み込まれないような、バッチファイルから起動した場合は、この方法は 152 * 利用できないため、isOnlineをfalseに指定する必要があります。 153 * 154 * @param input ファイル一覧(配列) 155 * @param isOl オンライン(Web環境での使用)かどうか 156 */ 157 public DocConverter_OOO( final String input[], final boolean isOl ) { 158 if( input == null || input.length == 0 || input[0].isEmpty() ) { 159 throw new HybsSystemException( "入力ファイルが指定されていません。" ); 160 } 161 final File inFile = new File( input[0] ); 162 if( !inFile.exists() ) { 163 throw new HybsSystemException( "入力ファイルが存在しません。[file=" + input[0] + "]" ); 164 } 165 isOnline = isOl; 166 inputName = input[0]; 167 origName = input[0]; 168 169 if( input.length == 1 ) { 170 mergeFile = null; 171 } 172 else { 173 if( !"xls".equals( getSuffix( input[0] ) ) && !"ods".equals( getSuffix( input[0] ) ) ) { 174 throw new HybsSystemException( "ファイルのマージを行う場合、入力ファイルは、ExcelまたはCacl形式である必要があります。" ); 175 } 176 177 mergeFile = new String[input.length-1]; 178 for( int i=0; i<mergeFile.length; i++ ) { 179 if( input[i+1].isEmpty() || !( new File( input[i+1] ) ).exists() ) { 180 throw new HybsSystemException( "マージファイルが指定されていないか、または存在しません。[file=" + input[i+1] + "]" ); 181 } 182 if( inputName.equals( input[i] + 1 ) ) { 183 throw new HybsSystemException( "マージファイルに入力ファイルと同じファイルが指定されてます。[file=" + input[i+1] + "]" ); 184 } 185 if( !"xls".equals( getSuffix( input[i+1] ) ) && !"ods".equals( getSuffix( input[i+1] ) ) ) { 186 throw new HybsSystemException( "ファイルのマージを行う場合、マージファイルは、ExcelまたはCacl形式である必要があります。" ); 187 } 188 mergeFile[i] = input[i+1]; 189 } 190 } 191 } 192 193 /** 194 * SOficeのコンポーネントを起動します。 195 * 196 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 197 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 198 * 199 * @og.rev 5.1.7.0 (2010/06/01) マージ処理対応 200 * 201 * @throws Throwable 何らかのエラーが発生した場合。 202 * @see #close() 203 * @see #close(boolean) 204 */ 205 public void open() throws Throwable { 206 // プロセスの取得 207 if( soffice == null ) { 208 if( isOnline ) { 209 soffice = ProcessFactory.newInstance(); 210 } 211 else { 212 soffice = new SOfficeProcess( "docconverter.class" ); 213 soffice.bootstrap(); 214 } 215 216 // マージする場合は、マージ対象のファイルをテンポラリにコピーする(readOnly回避のため) 217 // テンプレート(無題)として上げると、シートコピー先として特定できなくなるため 218 if( mergeFile != null ) { 219 final File origFile = new File( origName ); 220 inputName = soffice.getTempPath() + System.currentTimeMillis() + "_" + origFile.getName(); 221 FileUtil.copy( origFile, new File( inputName ) ); 222 } 223 } 224 225 // 5.1.7.0 (2010/06/01) マージ処理対応 226 xComp = getComponent( inputName, ( mergeFile == null ? true : false ), false ); 227 228 if( mergeFile != null ) { 229 for( int i=0; i<mergeFile.length; i++ ) { 230 merge( mergeFile[i] ); 231 } 232 } 233 } 234 235 /** 236 * ドキュメントコンポーネントを取得します。 237 * 238 * @param input ファイル名 239 * @param isHidden 隠し属性[true/false] 240 * @param isAsTemplate OpenOffice上のTemplate属性[true/false] 241 * 242 * @return ドキュメントコンポーネント 243 */ 244 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 245 private XComponent getComponent( final String input, final boolean isHidden, final boolean isAsTemplate ) { 246 PropertyValue[] calcProps = new PropertyValue[2]; 247 calcProps[0] = new PropertyValue(); 248 calcProps[0].Name = "Hidden"; 249 calcProps[0].Value = isHidden; 250 calcProps[1] = new PropertyValue(); 251 calcProps[1].Name = "AsTemplate"; 252 calcProps[1].Value = isAsTemplate; 253 254 final String url = "file:///" + input.replace( '\\', '/' ); 255 256 XComponent rtnDoc; 257 final XComponentLoader cloader = (XComponentLoader) UnoRuntime.queryInterface( XComponentLoader.class, soffice.getDesktop() ); 258 try { 259 rtnDoc = cloader.loadComponentFromURL( url, "_blank", 0, calcProps ); 260 } 261 catch( final IOException ex ) { 262 throw new HybsSystemException( "OpenOfficeの立ち上げ時にエラーが発生しました(入出力エラー)。", ex ); 263 } 264 catch( final IllegalArgumentException ex ) { 265 throw new HybsSystemException( "OpenOfficeの立ち上げ時にエラーが発生しました(パラメーター不正)。", ex ); 266 } 267 return rtnDoc; 268 } 269 270 /** 271 * ドキュメント(xls,ods)のマージを行います。 272 * 273 * @param mergeInputName マージ対象のファイル名 274 */ 275 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 276 private void merge( final String mergeInputName ) { 277 // マージする副ファイルは、テンプレート(無題)として上げる(readOnly回避のため) 278 final XComponent subDoc = getComponent( mergeInputName, false, true ); 279 280 final XDispatchProvider dispatchProvider 281 = (XDispatchProvider)UnoRuntime.queryInterface( XDispatchProvider.class 282 ,((XController)UnoRuntime.queryInterface( XController.class 283 ,((XModel)UnoRuntime.queryInterface( XModel.class 284 ,subDoc 285 )).getCurrentController() 286 )).getFrame() 287 ); 288 289 final XDispatchHelper xDispatchHelper = soffice.getDispatcher(); 290 xDispatchHelper.executeDispatch(dispatchProvider, ".uno:TableSelectAll", "", 0, new PropertyValue[0]); 291 292 String title = new File( inputName ).getName() ; 293 title = title.substring( 0, title.indexOf( '.' ) ); 294 295 PropertyValue[] moveProps = new PropertyValue[3]; 296 moveProps[0] = new PropertyValue(); 297 moveProps[0].Name = "DocName"; 298 moveProps[0].Value = title; 299 moveProps[1] = new PropertyValue(); 300 moveProps[1].Name = "Index"; 301 moveProps[1].Value = 32767; 302 moveProps[2] = new PropertyValue(); 303 moveProps[2].Name = "Copy"; 304 moveProps[2].Value = true; 305 xDispatchHelper.executeDispatch(dispatchProvider, ".uno:Move", "", 0, moveProps); 306 307 closeComponent( subDoc ); 308 } 309 310 /** 311 * Calcコンポーネントをクローズします。 312 * 313 * このクローズ処理は、処理が正常終了した場合に呼び出しする必要があります。 314 * 例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 315 * 316 * このメソッドは#close(false)と同じです。 317 * 318 * @throws Throwable 何らかのエラーが発生した場合。 319 * @see #close(boolean) 320 */ 321 public void close() throws Throwable { 322 close( false ); 323 } 324 325 /** 326 * Calcコンポーネントをクローズします。 327 * 328 * 引数のisErrがtrueの場合、この変換オブジェクトで生成されたプロセスは強制的に破棄されます。 329 * falseの場合は、プロセスは、ファクトリクラスを経由して、キャッシュに戻されます。 330 * (バッチ処理の場合は、いずれの場合も、プロセスは強制的に破棄されます) 331 * 332 * 起動から変換、クローズまでの書く処理で例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 333 * 334 * #close(false)は#close()と同じであるため、通常利用することはありません。 335 * 336 * @og.rev 4.2.4.1 (2008/07/07 ) 終了処理を60回で終わるように修正 337 * @og.rev 4.3.0.0 (2008/07/15 ) ↑は6秒しか待っていなかったので、60秒待つように修正 338 * 339 * @param isErr trueの場合、この変換オブジェクトで生成されたプロセスは強制的に破棄されます。 340 */ 341 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 342 public void close( final boolean isErr ) { 343 if( xComp != null ) { 344 closeComponent( xComp ); 345 } 346 347 if( soffice != null ) { 348 if( isOnline ) { 349 if( isErr ) { 350 ProcessFactory.remove( soffice ); 351 } 352 else { 353 ProcessFactory.release( soffice ); 354 } 355 } 356 else { 357 soffice.close(); 358 } 359 } 360 361 // マージした場合は、テンポラリにコピーしたファイルを削除 362 // 6.0.0.1 (2014/04/25) These nested if statements could be combined 363 if( mergeFile != null && ! ( new File( inputName ) ).delete() ) { 364 System.err.println( "テンポラリにコピーしたファイルを削除できませんでした。[" + inputName + "]" ); 365 } 366 } 367 368 /** 369 * ドキュメントコンポーネントをクローズします。 370 * 371 * @param comp ドキュメントコンポーネント 372 */ 373 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 374 private void closeComponent( final XComponent comp ) { 375 XCloseable closeable = null; 376 for( int i=0;; ++i ) { 377 try { 378 closeable = (XCloseable) UnoRuntime.queryInterface( XCloseable.class, comp ); 379 closeable.close( true ); 380 break; 381 } 382 catch( final CloseVetoException ex ) { 383 // 4.2.4.1 (2008/07/07 ) 384 // 4.3.4.4 (2009/01/01) 385 if( i == 600 ) { throw new HybsSystemException( "sofficeプロセスに接続できません。", ex ); } 386 try { 387 Thread.sleep( 100 ); 388 } 389 catch( final InterruptedException ex2 ) { 390 // throw new HybsSystemException( ex2 ); 391 } 392 } 393 } 394 } 395 396 /** 397 * 印刷を行います。 398 * 399 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 400 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 401 * 402 * @og.rev 4.3.0.0 (2008/07/16) スプールが終わるまでwaitし、さらにプリンタ発行の状況を監視し、正常終了かどうかを判断 403 * @og.rev 4.3.7.3 (2009/06/22) 存在しないプリンターを指定した場合のエラーハンドリングを追加 404 * @og.rev 5.1.2.0 (2010/01/01) CentOS等は、OS_INFOがLinux UNKNOWNとなるため、判定条件を変更 405 * 406 * @param printer プリンター名 407 * @throws Throwable 何らかのエラーが発生した場合。 408 */ 409 public void print( final String printer ) throws Throwable { 410 if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい" ); } 411 412 if( printer == null || printer.isEmpty() ) { 413 throw new HybsSystemException( "プリンターが指定されていません。" ); 414 } 415 416 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 417 final XPrintable xprintable = (XPrintable) UnoRuntime.queryInterface( XPrintable.class, xComp ); 418 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 419 final XPrintJobBroadcaster selection = (XPrintJobBroadcaster) UnoRuntime.queryInterface(XPrintJobBroadcaster.class, xprintable); 420 final MyPrintJobListener listener = new MyPrintJobListener (); 421 selection.addPrintJobListener( listener ); 422 423 PropertyValue[] tmpProps = new PropertyValue[1]; 424 tmpProps[0] = new PropertyValue(); 425 tmpProps[0].Name = "Name"; 426 // 5.1.2.0 (2010/01/01) CentOS等は、OS_INFOがLinux UNKNOWNとなるため、判定条件を変更 427 // OSがLinuxの場合は、プリンタ名称の前後に"<",">"を付加 428 tmpProps[0].Value = "LINUX".indexOf( HybsSystem.sys( "OS_INFO" ).toUpperCase( Locale.JAPAN ) ) >= 0 ? "<" + printer + ">" : printer; 429 430 // 4.3.4.4 (2009/01/01) 431 try { 432 xprintable.setPrinter( tmpProps ); 433 } 434 catch( final IllegalArgumentException ex ) { 435 throw new HybsSystemException( "印刷時にエラーが発生しました。", ex ); 436 } 437 438 // 4.3.7.3 (2009/06/22) 存在しないプリンタを指定した場合は、PropertyValueに 439 // デフォルトプリンターが入るため、引数の値と合致しているかで正しく設定されたかを確認 440 String curPrinter = null; 441 final PropertyValue[] chkProps = xprintable.getPrinter(); 442 for( int i=0; i<chkProps.length; i++ ) { 443 if( "Name".equals( chkProps[i].Name) ) { 444 curPrinter = (String)chkProps[i].Value; 445 break; 446 } 447 } 448 if( !(printer.equalsIgnoreCase( curPrinter ) ) ) { 449 final String errMsg = "プリンター[" + printer + "]を発行先に指定できませんでした。" + CR 450 + "存在しないプリンタ名が指定されている可能性があります。"; 451 throw new HybsSystemException( errMsg ); 452 } 453 454 // 4.3.0.0 (2008/07/16) 455 PropertyValue[] printProps = new PropertyValue[1]; 456 printProps[0] = new PropertyValue(); 457 printProps[0].Name = "Wait"; 458 printProps[0].Value = true; 459 460 // 4.3.4.4 (2009/01/01) 461 try { 462 xprintable.print( printProps ); 463 } 464 catch( final IllegalArgumentException ex ) { 465 throw new HybsSystemException( "印刷時にエラーが発生しました。", ex ); 466 } 467 468 // 4.3.0.0 (2008/07/16) 469 if( listener.getStatus() == null 470 || listener.getStatus() != PrintableState.JOB_COMPLETED && listener.getStatus() != PrintableState.JOB_SPOOLED ) { // 6.9.7.0 (2018/05/14) PMD Useless parentheses. 471 throw new HybsSystemException ( "Error Occured while spooling print job. Check Spooler-Service!!!"); 472 } 473 } 474 475 /** 476 * プリンタジョブの状況を監視するリスナーです。 477 * 478 * @author Hiroki.Nakamura 479 */ 480 private static final class MyPrintJobListener implements XPrintJobListener { 481 private PrintableState status ; 482 483 /** 484 * PrintJobEventのステータスを内部変数にセットします。 485 * 486 * @param event PrintJobEventオブジェクト 487 */ 488 @Override 489 public void printJobEvent( final PrintJobEvent event ) { 490 status = event.State; 491 } 492 493 /** 494 * EventObjectの処理を実施します。(ここでは何も行いません。) 495 * 496 * @param event EventObjectオブジェクト 497 */ 498 @Override 499 public void disposing( final EventObject event ) { 500 // 何もありません。(PMD エラー回避) 501 } 502 503 /** 504 * PrintableStateオブジェクトを返します。 505 * 506 * @return PrintableStateオブジェクト 507 */ 508 public PrintableState getStatus() { 509 return status; 510 } 511 } 512 513 /** 514 * PDF出力を行います。 515 * 516 * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。 517 * 518 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 519 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 520 * 521 * @param outputName 出力ファイル名 522 * @param pdfPasswd PDFパスワード 523 * @throws Throwable 何らかのエラーが発生した場合。 524 */ 525 public void pdf( final String outputName, final String pdfPasswd ) throws Throwable { 526 savePdf( outputName, getFilterName( getSuffix( inputName ), "pdf" ), pdfPasswd ); 527 } 528 529 /** 530 * Calc(ods)出力を行います。 531 * 532 * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。 533 * 534 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 535 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 536 * 537 * @param outputName 出力ファイル名 538 * @throws Throwable 何らかのエラーが発生した場合。 539 */ 540 public void ods( final String outputName ) throws Throwable { 541 saveDoc( outputName, getFilterName( getSuffix( inputName ), "ods" ) ); 542 } 543 544 /** 545 * Excel(xls)出力を行います。 546 * 547 * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。 548 * 549 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 550 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 551 * 552 * @param outputName 出力ファイル名 553 * @throws Throwable 何らかのエラーが発生した場合。 554 */ 555 public void xls( final String outputName ) throws Throwable { 556 saveDoc( outputName, getFilterName( getSuffix( inputName ), "xls" ) ); 557 } 558 559 /** 560 * Writer(ods)出力を行います。 561 * 562 * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。 563 * 564 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 565 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 566 * 567 * @param outputName 出力ファイル名 568 * @throws Throwable 何らかのエラーが発生した場合。 569 */ 570 public void odt( final String outputName ) throws Throwable { 571 saveDoc( outputName, getFilterName( getSuffix( inputName ), "odt" ) ); 572 } 573 574 /** 575 * Word(doc)出力を行います。 576 * 577 * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。 578 * 579 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 580 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 581 * 582 * @param outputName 出力ファイル名 583 * @throws Throwable 何らかのエラーが発生した場合。 584 */ 585 public void doc( final String outputName ) throws Throwable { 586 saveDoc( outputName, getFilterName( getSuffix( inputName ), "doc" ) ); 587 } 588 589 /** 590 * Impress(odp)出力を行います。 591 * 592 * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。 593 * 594 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 595 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 596 * 597 * @param outputName 出力ファイル名 598 * @throws Throwable 何らかのエラーが発生した場合。 599 */ 600 public void odp( final String outputName ) throws Throwable { 601 saveDoc( outputName, getFilterName( getSuffix( inputName ), "odp" ) ); 602 } 603 604 /** 605 * PowerPoint(ppt)出力を行います。 606 * 607 * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。 608 * 609 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 610 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 611 * 612 * @param outputName 出力ファイル名 613 * @throws Throwable 何らかのエラーが発生した場合。 614 */ 615 public void ppt( final String outputName ) throws Throwable { 616 saveDoc( outputName, getFilterName( getSuffix( inputName ), "ppt" ) ); 617 } 618 619 /** 620 * 出力ファイルから出力形式を自動判別し、変換を行います。 621 * 622 * 入出力形式で未対応の場合(形式は入出力ファイルの拡張子で判別)、例外が発行されます。 623 * 624 * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。 625 * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。 626 * 627 * @param outputName 出力ファイル名 628 * @throws Throwable 何らかのエラーが発生した場合。 629 */ 630 public void auto( final String outputName ) throws Throwable { 631 final String outSuffix = getSuffix( outputName ); 632 if( "pdf".equalsIgnoreCase( outSuffix ) ) { 633 savePdf( outputName, getFilterName( getSuffix( inputName ), outSuffix ), null ); 634 } 635 else { 636 saveDoc( outputName, getFilterName( getSuffix( inputName ), outSuffix ) ); 637 } 638 } 639 640 /** 641 * フィルター名を指定して、各種ファイル形式に出力を行います。 642 * 643 * @param outputName 出力ファイル名 644 * @param filter フィルター名 645 */ 646 private void saveDoc( final String outputName, final String filter ) { 647 if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい" ); } 648 if( !checkOutput( outputName ) ){ return; } 649 650 PropertyValue[] storeProps = new PropertyValue[1]; 651 storeProps[0] = new PropertyValue(); 652 storeProps[0].Name = "FilterName"; 653 storeProps[0].Value = filter; 654 655 final String url = "file:///" + outputName.replace( '\\', '/' ); 656 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 657 final XStorable xstorable = (XStorable) UnoRuntime.queryInterface( XStorable.class, xComp ); 658 try { 659 xstorable.storeAsURL( url, storeProps ); 660 } 661 catch( final Throwable th ) { 662 throw new HybsSystemException( "ファイルへの変換時にエラーが発生しました。[filter=" + filter + "]", th ); 663 } 664 } 665 666 /** 667 * フィルターを指定してPDF出力を行います。 668 * 669 * @param outputName 出力ファイル名 670 * @param filter フィルター名 671 * @param pdfPasswd PDFパスワード 672 */ 673 private void savePdf( final String outputName, final String filter, final String pdfPasswd ) { 674 if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい" ); } 675 if( !checkOutput( outputName ) ){ return; } 676 677 PropertyValue[] storeProps; 678 if( pdfPasswd == null || pdfPasswd.isEmpty() ) { 679 storeProps = new PropertyValue[1]; 680 storeProps[0] = new PropertyValue(); 681 storeProps[0].Name = "FilterName"; 682 storeProps[0].Value = filter; 683 } 684 // 帳票要求テーブルでPDFパスワードが設定されている場合 685 else { 686 PropertyValue[] filterProps = new PropertyValue[2]; 687 filterProps[0] = new PropertyValue(); 688 filterProps[0].Name = "EncryptFile"; 689 filterProps[0].Value = true; 690 filterProps[1] = new PropertyValue(); 691 filterProps[1].Name = "DocumentOpenPassword"; 692 filterProps[1].Value = pdfPasswd; 693 694 storeProps = new PropertyValue[2]; 695 storeProps[0] = new PropertyValue(); 696 storeProps[0].Name = "FilterName"; 697 storeProps[0].Value = "calc_pdf_Export"; 698 storeProps[1] = new PropertyValue(); 699 storeProps[1].Name = "FilterData"; 700 storeProps[1].Value = filterProps; 701 } 702 703 final String url = "file:///" + outputName.replace( '\\', '/' ); 704 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 705 final XStorable xstorable = (XStorable) UnoRuntime.queryInterface( XStorable.class, xComp ); 706 try { 707 xstorable.storeToURL( url, storeProps ); 708 } 709 catch( final Throwable th ) { 710 throw new HybsSystemException( "PDFファイルへの変換時にエラーが発生しました。[filter=" + filter + "]", th ); 711 } 712 } 713 714 /** 715 * 出力ファイルのチェックを行います。 716 * 717 * @param outputName 出力ファイル名 718 * 719 * @return 処理対象かどうか(入力ファイルと出力ファイルが同じ場合は、falseが返ります) 720 */ 721 private boolean checkOutput( final String outputName ) { 722 if( outputName == null || outputName.isEmpty() ) { 723 throw new HybsSystemException( "出力ファイルが指定されていません。" ); 724 } 725 726 final File inFile = new File( inputName ); 727 final File outFile = new File( outputName ); 728 729 if( outFile.exists() ) { 730 if( inFile.getAbsoluteFile().equals( outFile.getAbsoluteFile() ) ) { 731 // 入力と出力が同じファイルの場合な何もしない 732 return false; 733 } 734 else if( !outFile.delete() ) { 735 throw new HybsSystemException( "出力先の既存ファイルが削除できません。[file=" + outputName + "]" ); 736 } 737 } 738 return true; 739 } 740 741 /** 742 * 入出力の形式(拡張子)からフィルター名を取得します。 743 * 744 * @param inSuffix 入力拡張子 745 * @param outSuffix 出力拡張子 746 * 747 * @return フィルター名 748 */ 749 private static String getFilterName( final String inSuffix, final String outSuffix ) { 750 final String filterName = FILTER_MAP.get( inSuffix + "_" + outSuffix ); 751 if( filterName == null ) { 752 final String errMsg = "入力形式、出力形式は、以下の対応表に基づき、設定して下さい。" + CR 753 + "入力[Calc(ods) ,Excel(xls) ] ⇒ 出力[Calc(ods) ,Excel(xls) ,PDF]" + CR 754 + "入力[Writer(odt) ,Word(doc) ] ⇒ 出力[Writer(odt) ,Word(doc) ,PDF]" + CR 755 + "入力[Impress(odp),PowerPoint(ppt)] ⇒ 出力[Impress(odp),PowerPoint(ppt),PDF]" + CR; 756 throw new HybsSystemException( errMsg ); 757 } 758 return filterName; 759 } 760 761 /** 762 * ファイル名から拡張子(小文字)を求めます。 763 * 764 * @param fileName ファイル名 765 * 766 * @return 拡張子(小文字) 767 */ 768 private static String getSuffix( final String fileName ) { 769 String suffix = null; 770 if( fileName != null ) { 771 final int sufIdx = fileName.lastIndexOf( '.' ); 772 if( sufIdx >= 0 ) { 773 suffix = fileName.substring( sufIdx + 1 ).toLowerCase( Locale.JAPAN ); 774 } 775 } 776 return suffix; 777 } 778 779 /** 780 * ドキュメントの変換を行うための簡易メソッドです。 781 * 782 * 変換方法は、出力ファイルの拡張子により自動的に決定されます。 783 * 784 * @param inputFile 入力ファイル名 785 * @param outputFile 出力ファイル名 786 * @see #convert(String[], String, boolean) 787 */ 788 public static final void convert( final String inputFile, final String outputFile ) { 789 convert( StringUtil.csv2Array( inputFile ), outputFile ); 790 } 791 792 /** 793 * ドキュメントの変換を行うための簡易メソッドです。 794 * 795 * 変換方法は、出力ファイルの拡張子により自動的に決定されます。 796 * 797 * @param inputFile 入力ファイル名配列 798 * @param outputFile 出力ファイル名 799 * @see #convert(String[], String, boolean) 800 */ 801 public static final void convert( final String[] inputFile, final String outputFile ) { 802 convert( inputFile, outputFile, true ); 803 } 804 805 /** 806 * ドキュメントの変換を行うための簡易メソッドです。 807 * 808 * 変換方法は、出力ファイルの拡張子により自動的に決定されます。 809 * 810 * isOnlineがtrueに指定された場合、soffice.binのプロセスをファクトリークラス経由で生成し、 811 * キャッシュします。 812 * 但し、システムリソースが読み込まれないような、バッチファイルから起動した場合は、この方法は 813 * 利用できないため、isOnlineをfalseに指定する必要があります。 814 * 815 * @param inputFile 入力ファイル名配列 816 * @param outputFile 出力ファイル名 817 * @param isOnline オンライン(Web環境での使用)かどうか 818 */ 819 public static final void convert( final String inputFile[], final String outputFile, final boolean isOnline ) { 820 final DocConverter_OOO dc = new DocConverter_OOO( inputFile, isOnline ); 821 try { 822 dc.open(); 823 dc.auto( outputFile ); 824 dc.close(); 825 } 826 catch( final Throwable th ) { 827 dc.close( true ); 828 throw new HybsSystemException( th ); 829 } 830 } 831 832 /** 833 * ドキュメントの変換を行います。 834 * 835 * 変換方法は、出力ファイルの拡張子により自動的に決定されます。 836 * 837 * @og.rev 4.3.1.1 (2008/08/23) mkdirs の戻り値判定 838 * @og.rev 6.3.9.1 (2015/11/27) A method/constructor shouldnt explicitly throw java.lang.Exception(PMD)。 839 * 840 * @param args コマンド引数配列 841 */ 842 public static void main( final String[] args ) { 843 if( args.length < 2 ) { 844 System.out.println( "usage : OdsConverter [inputFile or inputDir] [outputDir]" ); 845 return; 846 } 847 848 final File input = new File( args[0] ); 849 final File output = new File( args[1] ); 850 851 // 4.3.1.1 (2008/08/23) mkdirs の戻り値判定 852 if( output.mkdirs() ) { 853 System.err.println( args[1] + " の ディレクトリ作成に失敗しました。" ); 854 } 855 856 if( input.isDirectory() ) { 857 final File[] inputFiles = input.listFiles(); 858 // 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値を利用している(findbugs) 859 if( inputFiles != null ) { 860 for( final File file : inputFiles ) { 861 final String inputFile = file.getAbsolutePath(); 862 final String outputFile = output.getAbsolutePath() + File.separator + file.getName().replace( ".xls", ".ods" ); 863 convert( StringUtil.csv2Array( inputFile ), outputFile, false ); 864 } 865 } 866 } 867 else { 868 final String inputFile = input.getAbsolutePath(); 869 final String outputFile = output.getAbsolutePath() + File.separator + input.getName().replace( ".xls", ".ods" ); 870 convert( StringUtil.csv2Array( inputFile ), outputFile, false ); 871 } 872 } 873}