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.model; 017 018import java.io.File; // 6.2.0.0 (2015/02/27) 019import java.io.IOException; 020import java.io.OutputStream; 021import java.io.FileOutputStream; 022import java.io.BufferedOutputStream; 023import java.util.Locale; 024import java.util.Map; // 6.0.2.3 (2014/10/10) 画像関連 025import java.util.HashMap; // 6.0.2.3 (2014/10/10) 画像関連 026 027import org.apache.poi.ss.usermodel.Workbook; 028import org.apache.poi.ss.usermodel.Sheet; 029import org.apache.poi.ss.usermodel.Row; 030import org.apache.poi.ss.usermodel.Cell; 031import org.apache.poi.ss.usermodel.CellStyle; 032import org.apache.poi.ss.usermodel.Font; 033import org.apache.poi.ss.usermodel.IndexedColors; 034import org.apache.poi.ss.usermodel.RichTextString; 035import org.apache.poi.ss.usermodel.Hyperlink; 036import org.apache.poi.ss.usermodel.CreationHelper; 037import org.apache.poi.ss.usermodel.Drawing; // 6.0.2.3 (2014/10/10) 画像関連 038import org.apache.poi.ss.usermodel.ClientAnchor; // 6.0.2.3 (2014/10/10) 画像関連 039import org.apache.poi.ss.usermodel.Picture; // 6.0.2.3 (2014/10/10) 画像関連 040import static org.opengion.fukurou.util.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 041import static org.opengion.fukurou.util.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 042 043import org.apache.poi.ss.util.WorkbookUtil; 044 045import org.apache.poi.hssf.usermodel.HSSFWorkbook; // .xls 046import org.apache.poi.xssf.streaming.SXSSFWorkbook; 047import org.apache.poi.xssf.usermodel.XSSFWorkbook; // .xlsx 048 049import org.apache.poi.POIXMLDocumentPart; // 6.2.4.2 (2015/05/29) テキスト変換処理 050import org.apache.poi.xssf.usermodel.XSSFDrawing; // 6.2.4.2 (2015/05/29) テキスト変換処理 051import org.apache.poi.xssf.usermodel.XSSFShape; // 6.2.4.2 (2015/05/29) テキスト変換処理 052import org.apache.poi.xssf.usermodel.XSSFSimpleShape; // 6.2.4.2 (2015/05/29) テキスト変換処理 053import org.apache.poi.xssf.usermodel.XSSFTextParagraph; // 6.2.4.2 (2015/05/29) テキスト変換処理 054import org.apache.poi.xssf.usermodel.XSSFTextRun; // 6.2.4.2 (2015/05/29) テキスト変換処理 055 056import org.opengion.fukurou.util.Closer; 057import org.opengion.fukurou.util.ImageUtil; // 6.0.2.3 (2014/10/10) 画像関連 058 059/** 060 * POI による、EXCELバイナリファイルに対する、データモデルクラスです。 061 * 062 * 共通的な EXCEL処理 を集約しています。 063 * staticメソッドによる簡易的なアクセスの他に、順次処理も可能なように 064 * 現在アクセス中の、Workbook、Sheet、Row、Cell オブジェクトを内部で管理しています。 065 * 066 * 入力形式は、openXML形式にも対応しています。 067 * ファイルの内容に応じて、.xlsと.xlsxのどちらで読み取るかは、内部的に 068 * 自動判定されます。 069 * 070 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 071 * @og.group その他 072 * 073 * @version 6.0 074 * @author Kazuhiko Hasegawa 075 * @since JDK7.0, 076 */ 077public class ExcelModel { 078 //* このプログラムのVERSION文字列を設定します。 {@value} */ 079 private static final String VERSION = "6.3.1.0 (2015/06/28)" ; 080 081 private static final String DEF_SHEET_NAME = "Sheet" ; 082 083 // 6.0.2.3 (2014/10/10) ImageUtil の Suffix と、Workbook.PICTURE_TYPE_*** の関連付けをしておきます。 084 // Suffix 候補は、[bmp, gif, jpeg, jpg, png, wbmp] だが、対応する PICTURE_TYPE は一致しない。 085 private static final Map<String,Integer> PICTURE_TYPE ; 086 087 private final String inFilename ; // エラー発生時のキーとなる、EXCELファイル名 088 private final String sufix ; // 6.1.0.0 (2014/12/26) オープンしたファイル形式を記憶(ピリオドを含む) 089 090 private final Workbook wkbook ; // 現在処理中の Workbook 091 private Sheet sheet ; // 現在処理中の Sheet 092 private Row rowObj ; // 現在処理中の Row 093 094 private int refSheetIdx = -1; // 雛形シートのインデックス 095 096 private final CreationHelper createHelper ; // poi.xssf対応 097 098 private CellStyle style ; // 共通のセルスタイル 099 private CellStyle hLinkStyle ; // Hyperlink用のセルスタイル(青文字+下線) 100 101 private int maxColCount = 5 ; // 標準セル幅の5倍を最大幅とする。 102 private int dataStartRow = -1; // データ行の開始位置。未設定時は、-1 103 private boolean isAutoCellSize ; // カラム幅の自動調整を行うかどうか(true:行う/false:行わない) 104 105 private String addTitleSheet ; // Sheet一覧を先頭Sheetに作成する場合のSheet名 106 107 static { 108 PICTURE_TYPE = new HashMap() ; 109 PICTURE_TYPE.put( "png" , Integer.valueOf( Workbook.PICTURE_TYPE_PNG ) ); 110 PICTURE_TYPE.put( "jpeg" , Integer.valueOf( Workbook.PICTURE_TYPE_JPEG ) ); 111 PICTURE_TYPE.put( "jpg" , Integer.valueOf( Workbook.PICTURE_TYPE_JPEG ) ); 112 } 113 114 /** 115 * EXCELファイルのWookbookのデータ処理モデルを作成します。 116 * 117 * ここでは、既存のファイルを読み込んで、データ処理モデルを作成しますので、 118 * ファイルがオープンできなければエラーになります。 119 * 120 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 121 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 122 * 123 * @param file EXCELファイル 124 * @see #ExcelModel( File , boolean ) 125 */ 126 public ExcelModel( final File file ) { 127 this( file,true ); 128 } 129 130 /** 131 * EXCELファイルのWookbookのデータ処理モデルを作成します。 132 * 133 * isOpen条件によって、ファイルオープン(true)か、新規作成(false)が分かれます。 134 * ファイルオープンの場合は、EXCELの読み込み以外に、追記するとか、雛形参照する 135 * 場合にも、使用します。 136 * ファイルオープンの場合は、当然、ファイルがオープンできなければエラーになります。 137 * 138 * isOpen=新規作成(false) の場合は、ファイル名の拡張子で、XSSFWorkbook か HSSFWorkbook を 139 * 判定します。.xlsx の場合⇒XSSFWorkbook オブジェクトを使用します。 140 * 141 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 142 * @og.rev 6.0.2.3 (2014/10/10) POIUtil#createWorkbook( String ) を使用するように変更 143 * @og.rev 6.1.0.0 (2014/12/26) 入力ファイルの拡張子判定の対応 144 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 145 * @og.rev 6.2.2.0 (2015/03/27) マクロ付Excel(.xlsm)対応 146 * 147 * @param file EXCELファイル 148 * @param isOpen true:ファイルオープン/false:新規作成 149 * @see #ExcelModel( File ) 150 */ 151 public ExcelModel( final File file , final boolean isOpen ) { 152 inFilename = file.getName(); 153 154 final int idx = inFilename.lastIndexOf( '.' ); // 拡張子の位置 155 if( idx >= 0 ) { 156 sufix = inFilename.substring( idx ).toLowerCase( Locale.JAPAN ); // ピリオドを含む 157 } 158 else { 159 final String errMsg = "ファイルの拡張子が見当たりません。(.xls か .xlsx/.xlsm を指定下さい)" + CR 160 + " filename=[" + file + "]" + CR ; 161 throw new IllegalArgumentException( errMsg ); 162 } 163 164 if( isOpen ) { 165 wkbook = POIUtil.createWorkbook( file ); 166 } 167 else { 168 // 新規の場合、ファイル名に.xlsxで終了した場合⇒.xlsx形式ファイル作成、その他⇒.xls形式ファイル作成 169 if( ".xlsx".equals( sufix ) || ".xlsm".equals( sufix ) ) { // 6.2.2.0 (2015/03/27) 170// wkbook = new XSSFWorkbook(); 171 wkbook = new SXSSFWorkbook(); // 機能制限有:シートや行の削除や、AutoCellSize の指定ができないなど。 172 } 173 else if( ".xls".equals( sufix ) ) { 174 wkbook = new HSSFWorkbook(); 175 } 176 else { 177 final String errMsg = "ファイルの拡張子が不正です。(.xls か .xlsx/.xlsm のみ可能)" + CR 178 + " filename=[" + file + "]" + CR ; 179 throw new IllegalArgumentException( errMsg ); 180 } 181 } 182 183 createHelper = wkbook.getCreationHelper(); // poi.xssf対応 184 } 185 186 /** 187 * 内部 Workbook に、フォント名、フォントサイズを設定します。 188 * fontName(フォント名)は、"MS Pゴシック" など名称になります。 189 * fontPoint は、フォントの大きさを指定します。 190 * 内部的には、setFontHeightInPoints(short)メソッドで設定します。 191 * 192 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 193 * 194 * @param fontName フォント名 ("MS Pゴシック" など。nullの場合セットしません) 195 * @param fontPoint フォントの大きさ (0やマイナスの場合はセットしません) 196 */ 197 public void setFont( final String fontName , final short fontPoint ) { 198 if( style == null ) { style = wkbook.createCellStyle(); } 199 200 final Font font = wkbook.createFont(); 201 if( fontName != null ) { 202 font.setFontName( fontName ); // "MS Pゴシック" など 203 } 204 if( fontPoint > 0 ) { 205 font.setFontHeightInPoints( fontPoint ); 206 } 207 208 style.setFont( font ); 209 } 210 211 /** 212 * データ設定する セルに、罫線を追加します。 213 * 214 * ここで設定するのは、罫線の種類と、罫線の色ですが、内部的に固定にしています。 215 * Border=CellStyle.BORDER_THIN 216 * BorderColor=IndexedColors.BLACK 217 * 218 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 219 */ 220 public void setCellStyle() { 221 if( style == null ) { style = wkbook.createCellStyle(); } 222 223 style.setBorderBottom( CellStyle.BORDER_THIN ); 224 style.setBorderLeft( CellStyle.BORDER_THIN ); 225 style.setBorderRight( CellStyle.BORDER_THIN ); 226 style.setBorderTop( CellStyle.BORDER_THIN ); 227 228 style.setBottomBorderColor( IndexedColors.BLACK.getIndex() ); 229 style.setLeftBorderColor( IndexedColors.BLACK.getIndex() ); 230 style.setRightBorderColor( IndexedColors.BLACK.getIndex() ); 231 style.setTopBorderColor( IndexedColors.BLACK.getIndex() ); 232 233 style.setVerticalAlignment( CellStyle.VERTICAL_TOP ); // isAutoCellSize=true 文字は上寄せする。 234 // style.setWrapText( true ); // isAutoCellSize=true 折り返して表示する。 235 } 236 237 /** 238 * 全てのSheetに対して、autoSizeColumn設定を行うかどうか指定します(初期値:false)。 239 * 240 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 241 * 初期カラム幅の5倍を限度にしています。 242 * 243 * なお、autoSizeColumn設定は負荷の大きな処理なので、saveFile(String)の 244 * 中で実行されます。(セーブしなければ実行されません。) 245 * よって、指定は、いつ行っても構いません。 246 * 247 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 248 * 249 * @param flag autoSizeColumn設定を行うかどうか [true:自動カラム幅設定を行う/false:行わない] 250 * @see #useAutoCellSize( boolean,int ) 251 */ 252 public void useAutoCellSize( final boolean flag ) { 253 isAutoCellSize = flag; 254 } 255 256 /** 257 * 全てのSheetに対して、autoSizeColumn設定を行うかどうか指定します(初期値:false)。 258 * 259 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 260 * 初期カラム幅のcount倍を限度に設定します。 261 * ただし、count がマイナスの場合は、無制限になります。 262 * 263 * なお、autoSizeColumn設定は負荷の大きな処理なので、saveFile(String)の 264 * 中で実行されます。(セーブしなければ実行されません。) 265 * よって、指定は、いつ行っても構いません。 266 * 267 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 268 * 269 * @param flag autoSizeColumn設定を行うかどうか [true:自動カラム幅設定を行う/false:行わない] 270 * @param count 最大幅を標準セル幅の何倍にするかを指定。マイナスの場合は、無制限 271 * @see #useAutoCellSize( boolean ) 272 */ 273 public void useAutoCellSize( final boolean flag, final int count ) { 274 isAutoCellSize = flag; 275 maxColCount = count ; 276 } 277 278 /** 279 * データ行の書き込み開始位置の行番号を設定します。 280 * 281 * これは、autoSize設定で、自動調整するカラムを、ヘッダーではなく、 282 * データ部で計算する場合に使用します。 283 * 284 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 285 * 286 * @param st データ行の開始位置。未設定時は、-1 287 * @see #useAutoCellSize( boolean ) 288 */ 289 public void setDataStartRow( final int st ) { 290 dataStartRow = st; 291 } 292 293 /** 294 * Sheet一覧を先頭Sheetに作成する場合のSheet名を指定します。 295 * 296 * これは、Workbook に含まれる Sheet 一覧を作成する場合に、利用可能です。 297 * 298 * この処理は、#saveFile( File ) 処理時に、実行されます。 299 * 300 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 301 * 302 * @param sheetName Sheet一覧のSheet名 303 * @see #makeAddTitleSheet() 304 */ 305 public void setAddTitleSheet( final String sheetName ) { 306 addTitleSheet = sheetName; 307 } 308 309 /** 310 * 内部 Workbookの Sheet数を返します。 311 * 312 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 313 * 314 * @return シート数 315 */ 316 public int getNumberOfSheets() { 317 return wkbook.getNumberOfSheets(); 318 } 319 320 /** 321 * 内部 Workbookより、雛形Sheetをセットします。 322 * 323 * これは、雛形シートを使用する場合に、使います。このメソッドが呼ばれると、 324 * 雛形シートを使用すると判定されます。 325 * 雛形シート名が、内部 Workbook に存在しない場合は、エラーになります。 326 * ただし、null をセットした場合は、最初のシートを雛形シートとして使用すると 327 * 判定します。 328 * 329 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 330 * 331 * @param refSheetName 参照シート名(nullの場合、参照シート使用する場合は、先頭のシート) 332 */ 333 public void setRefSheetName( final String refSheetName ) { 334 // 参照シート名の指定がない場合は、最初のシート 335 refSheetIdx = ( refSheetName == null ) ? 0 : wkbook.getSheetIndex( refSheetName ); 336 337 if( refSheetIdx < 0 ) { // 参照シート名が存在しなかった。 338 final String errMsg = "指定の参照シート名は存在しませんでした。" + CR 339 + " inFilename=[" + inFilename + "] , refSheetName=[" + refSheetName + "]" + CR ; 340 throw new IllegalArgumentException( errMsg ); 341 } 342 } 343 344 /** 345 * 内部 Workbookより、新しいSheetを作ります。 346 * 347 * 先に雛形シートを指定している場合は、その雛形シートから作成します。 348 * 指定していない場合は、新しいシートを作成します。 349 * 雛形シートを参照する場合は、雛形シートそのものを返します。 350 * また、雛形シートの枚数を超える場合は、前の雛形シートをコピーします。 351 * 雛形シートが存在しない場合は、新しいシートを作成します。 352 * 353 * シート名は、重複チェックを行い、同じ名前のシートの場合は、(1),(2)が付けられます。 354 * sheetName が null の場合は、"Sheet" が割り振られます。 355 * 356 * この処理を行うと、内部の Sheet にも、ここで作成された Sheet が設定されます。 357 * 358 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 359 * @og.rev 6.2.2.3 (2015/04/10) 雛形シートにそのままデータを書き込んでいく。 360 * 361 * @param sheetName シート名 (重複する場合は、(2)、(3)のような文字列を追加 、nullの場合は、"Sheet") 362 */ 363 public void createSheet( final String sheetName ) { 364 // 参照シートを使う場合(整合性の問題で、両方ともチェックしておきます) 365 366 // 6.2.2.3 (2015/04/10) 雛形シートにそのままデータを書き込んでいく。 367 final int shtNo ; 368 if( refSheetIdx < 0 ) { // 雛形シートを使用しない。 369 sheet = wkbook.createSheet(); 370 shtNo = wkbook.getNumberOfSheets() - 1; 371 } 372 else if( refSheetIdx >= wkbook.getNumberOfSheets() ) { // シート数が雛形より超えている。 373 sheet = wkbook.cloneSheet( refSheetIdx-1 ); // 最後の雛形シートをコピーします。 374 shtNo = wkbook.getNumberOfSheets() - 1; 375 refSheetIdx++ ; 376 } 377 else { 378 sheet = wkbook.getSheetAt( refSheetIdx ); // 雛形シートをそのまま使用 379 shtNo = refSheetIdx; 380 refSheetIdx++ ; 381 } 382 383 setSheetName( shtNo , sheetName ); 384 385 } 386 387 /** 388 * 内部 Workbook の指定のシート番号の Sheet の名前を設定します。 389 * 390 * 指定のシート名が、既存のシートになければ、そのまま設定します。 391 * すでに、同じ名前のシートが存在する場合は、そのシート名の後に 392 * (1)、(2)、(3)のような文字列を追加します。 393 * sheetName が null の場合は、"Sheet" が割り振られます。 394 * 395 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 396 * @og.rev 6.2.5.1 (2015/06/12) シート名重複が自分自身の場合は、(1)等の追加は行わない。 397 * 398 * @param shNo シート番号 399 * @param sheetName シート名 (重複する場合は、(1)、(2)のような文字列を追加 、nullの場合は、"Sheet") 400 */ 401 public void setSheetName( final int shNo, final String sheetName ) { 402 String tempName = ( sheetName == null ) ? DEF_SHEET_NAME : WorkbookUtil.createSafeSheetName( sheetName ) ; 403 int cnt = 1; 404 405 // 6.2.5.1 (2015/06/12) シート名重複が自分自身の場合は、(1)等の追加は行わない。 406 // ※ EXCELのシート名は、大文字、小文字だけでなく、全角半角の区別もしない。 407 final String shtName = wkbook.getSheetName( shNo ); 408 if( tempName != null && !tempName.equals( shtName ) ) { // 全く同一の場合は、何もしない。 409 if( shNo == wkbook.getSheetIndex( tempName ) ) { // シート名判定が、自身の場合 410 wkbook.setSheetName( shNo,tempName ); 411 } 412 else { 413 while( wkbook.getSheetIndex( tempName ) >= 0 ) { // シート名が存在している場合 414 tempName = WorkbookUtil.createSafeSheetName( sheetName + "(" + cnt + ")" ); 415 if( tempName.length() >= 31 ) { // 重複時の追加文字分を減らす。 416 tempName = tempName.substring( 0,26 ) + "(" + cnt + ")" ; // cnt3桁まで可能 417 } 418 cnt++; 419 } 420 wkbook.setSheetName( shNo,tempName ); 421 } 422 } 423 } 424 425 /** 426 * 内部 Workbook の 指定のSheet番号のシート名前を返します。 427 * 428 * シートが存在しない場合は、null を返します。 429 * 430 * この処理を行うと、内部の Sheet にも、ここで見つけた Sheet が設定されます。 431 * 432 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 433 * 434 * @param shNo シート番号 435 * 436 * @return sheetName シート名 437 */ 438 public String getSheetName( final int shNo ) { 439 final int shLen = wkbook.getNumberOfSheets(); 440 441 String shName = null; 442 if( shNo < shLen ) { 443 sheet = wkbook.getSheetAt( shNo ); // 現在の sheet に設定する。 444 shName = sheet.getSheetName(); 445 } 446 447 return shName ; 448 } 449 450 /** 451 * 内部 Workbook の 指定のSheet名のシート番号を返します。 452 * 453 * シートが存在しない場合は、-1 を返します。 454 * この処理を行うと、内部の Sheet にも、ここで見つけた Sheet が設定されます。 455 * シートが存在しない場合、内部の Sheet オブジェクトも null がセットされますのでご注意ください。 456 * 457 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 458 * 459 * @param shName シート名 460 * 461 * @return シート番号(名前のシートがなければ、-1) 462 */ 463 public int getSheetNo( final String shName ) { 464 sheet = wkbook.getSheet( shName ); // シート名がマッチしなければ、null 465 466 return wkbook.getSheetIndex( shName ) ; // シート名がマッチしなければ、-1 467 } 468 469 /** 470 * Excelの指定Sheetオブジェクトを削除します。 471 * 472 * 削除するシートは、シート番号でFrom-To形式で指定します。 473 * Fromも Toも、削除するシート番号を含みます。 474 * 例えば、0,3 と指定すると、0,1,2,3 の 4シート分を削除します。 475 * 476 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 477 * 478 * @param fromNo 削除する開始シート番号(含む) 479 * @param toNo 削除する終了シート番号(含む) 480 */ 481 public void removeSheet( final int fromNo,final int toNo ) { 482 for( int shtNo=toNo; shtNo>=fromNo; shtNo-- ) { // 逆順に処理します。 483 wkbook.removeSheetAt( shtNo ); 484 } 485 } 486 487 /** 488 * 内部 Workbookの 現在Sheet の最初の行番号を返します。 489 * 490 * 行は、0 から始まります。 491 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 492 * 493 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 494 * 495 * @return 最初の行番号 496 */ 497 public int getFirstRowNum() { 498 return sheet.getFirstRowNum(); 499 } 500 501 /** 502 * 内部 Workbookの 現在Sheet の最後の行番号を返します。 503 * 504 * 最終行は、含みます。よって、行数は、getLastRowNum()+1になります。 505 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 506 * 507 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 508 * 509 * @return 最後の行番号 510 */ 511 public int getLastRowNum() { 512 return sheet.getLastRowNum(); 513 } 514 515 /** 516 * Excelの指定行のRowオブジェクトを作成します。 517 * 518 * 指定行の Row オブジェクトが存在しない場合は、新規作成します。 519 * この処理を実行すると、指定行の Rowオブジェクトが内部 Row に設定されます。 520 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 521 * 522 * この処理を行うと、内部の Rowオブジェクトが設定されます。 523 * 524 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 525 * 526 * @param rowNo 行の番号 527 */ 528 public void createRow( final int rowNo ) { 529 rowObj = sheet.getRow( rowNo ); 530 if( rowObj == null ) { rowObj = sheet.createRow( rowNo ); } 531 } 532 533 /** 534 * Excelの指定行以降の余計なRowオブジェクトを削除します。 535 * 536 * 指定行の Row オブジェクトから、getLastRowNum() までの行を、削除します。 537 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 538 * 539 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 540 * 541 * @param startRowNum 指定以降の余計な行を削除 542 */ 543 public void removeRow( final int startRowNum ) { 544 final int stR = startRowNum; 545 final int edR = sheet.getLastRowNum(); 546 547 for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) { // 逆順に処理します。 548 final Row rowObj = sheet.getRow( rowNo ); 549 if( rowObj != null ) { sheet.removeRow( rowObj ); } 550 } 551 } 552 553 /** 554 * Excelの処理中のRowオブジェクトの指定カラム以降の余計なCellオブジェクトを削除します。 555 * 556 * 指定行の Row オブジェクトから、getLastCellNum() までのカラムを、削除します。 557 * この処理は、内部Rowが作成されているか、null でない場合のみ実行できます。 558 * 559 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 560 * 561 * @param startCellNum 指定以降の余計なカラムを削除 562 */ 563 public void removeCell( final int startCellNum ) { 564 final int stC = startCellNum; 565 final int edC = rowObj.getLastCellNum(); 566 567 for( int colNo=edC; colNo>=stC; colNo-- ) { // 逆順に処理します。 568 final Cell colObj = rowObj.getCell( colNo ); 569 if( colObj != null ) { rowObj.removeCell( colObj ); } 570 } 571 } 572 573 /** 574 * row にあるセルのオブジェクト値を設定します。 575 * 576 * 行が存在しない場合、行を追加します。 577 * この処理を行うと、内部の Rowオブジェクトがなければ新規作成されます。 578 * 579 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 580 * 581 * @param vals 新しい配列値。 582 * @param rowNo 値が変更される行(無視されます) 583 */ 584 public void setValues( final String[] vals,final int rowNo ) { 585 if( rowObj == null ) { createRow( rowNo ); } 586 587 if( vals != null ) { 588 for( int colNo=0; colNo<vals.length; colNo++ ) { 589 setCellValue( vals[colNo],colNo ); 590 } 591 } 592 } 593 594 /** 595 * row にあるセルのオブジェクト値を設定します。 596 * 597 * 行が存在しない場合、行を追加します。 598 * 引数に、カラムがNUMBER型かどうかを指定することが出来ます。 599 * この処理を行うと、内部の Rowオブジェクトがなければ新規作成されます。 600 * 601 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 602 * 603 * @param vals 新しい配列値。 604 * @param rowNo 値が変更される行(無視されます) 605 * @param isNums セルが、NUMBER型の場合は、true/それ以外は、false 606 */ 607 public void setValues( final String[] vals,final int rowNo,final boolean[] isNums ) { 608 if( rowObj == null ) { createRow( rowNo ); } 609 610 if( vals != null ) { 611 for( int colNo=0; colNo<vals.length; colNo++ ) { 612 setCellValue( vals[colNo],colNo,isNums[colNo] ); 613 } 614 } 615 } 616 617 /** 618 * Excelの指定セルにデータを設定します。 619 * 620 * ここで設定する行は、現在の内部 Row です。 621 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 622 * このメソッドでは、データを文字列型として設定します。 623 * この処理は、内部Rowが作成されているか、null でない場合のみ実行できます。 624 * 625 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 626 * 627 * @param dataVal String文字列 628 * @param colNo セルの番号(0,1,2・・・・) 629 * @see #setCellValue( String,int,boolean ) 630 */ 631 public void setCellValue( final String dataVal , final int colNo ) { 632 setCellValue( dataVal,colNo,false ); 633 } 634 635 /** 636 * Excelの指定セルにデータを設定します。 637 * 638 * ここで設定する行は、現在の内部 Row です。 639 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 640 * このメソッドでは、引数のデータ型をNUMBER型の場合は、doubleに変換して、 641 * それ以外は文字列としてとして設定します。 642 * この処理は、内部Rowが作成されているか、null でない場合のみ実行できます。 643 * 644 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 645 * 646 * @param dataVal String文字列 647 * @param colNo セルの番号(0,1,2・・・・) 648 * @param isNumber セルが、NUMBER型の場合は、true/それ以外は、false 649 * @see #createRow( int ) 650 * @see #setCellValue( String,int ) 651 */ 652 public void setCellValue( final String dataVal , final int colNo , final boolean isNumber ) { 653 Cell colObj = rowObj.getCell( colNo ); 654 if( colObj == null ) { colObj = rowObj.createCell( colNo ); } 655 656 if( style != null ) { colObj.setCellStyle(style); } 657 658 // CELL_TYPE_NUMERIC 以外は、String扱いします。 659 if( isNumber ) { 660 final Double dbl = parseDouble( dataVal ); 661 if( dbl != null ) { 662 colObj.setCellValue( dbl.doubleValue() ); 663 return ; // Double 変換できた場合は、即抜けます。 664 } 665 } 666 667 final RichTextString richText = createHelper.createRichTextString( dataVal ); 668 colObj.setCellValue( richText ); 669 } 670 671 /** 672 * Excelの指定セルにHyperlinkを設定します。 673 * 674 * ここで設定する行は、現在の内部 Row です。 675 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 676 * このメソッドで設定するHyperlinkは、Sheetに対する LINK_DOCUMENT です。 677 * 先に、セルに対する値をセットしておいてください。 678 * Hyperlinkは、文字に対して、下線 と 青字 のスタイル設定を行います。 679 * 680 * Link文字列(シート名) が、null や ゼロ文字列の場合は、処理を行いません。 681 * 682 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 683 * 684 * @param linkVal Link文字列(シート名) 685 * @param colNo セルの番号(0,1,2・・・・) 686 * @see #setCellValue( String,int ) 687 */ 688 public void setCellLink( final String linkVal , final int colNo ) { 689 if( linkVal == null || linkVal.isEmpty() ) { return; } 690 691 Cell colObj = rowObj.getCell( colNo ); 692 if( colObj == null ) { colObj = rowObj.createCell( colNo ); } 693 694 if( hLinkStyle == null ) { 695 hLinkStyle = wkbook.createCellStyle(); 696 if( style != null ) { hLinkStyle.cloneStyleFrom(style); } 697 698 final Font font = wkbook.createFont(); 699 font.setColor( IndexedColors.BLUE.getIndex() ); // リンクは青文字 700 font.setUnderline( Font.U_SINGLE ); // 下線付 701 702 hLinkStyle.setFont( font ); 703 } 704 colObj.setCellStyle(hLinkStyle); 705 706 final Hyperlink hLink = createHelper.createHyperlink( Hyperlink.LINK_DOCUMENT ); 707 hLink.setAddress( "'" + linkVal + "'!A1" ); 708 colObj.setHyperlink( hLink ); 709 } 710 711 /** 712 * 現在のRow にあるセルの属性値を配列で返します。 713 * 714 * Rowオブジェクトが存在しない場合は、null を返します。 715 * また、Rowオブジェクトの中の セルオブジェクトが存在しない場合は、 716 * null がセットされます。 717 * 718 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 719 * この処理を実行すると、指定行の Rowオブジェクトが内部 Row に設定されます。 720 * 721 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 722 * 723 * @param rowNo 行の番号 724 * @return 指定されたセルの属性値。Rowがnullの場合は、nullを返します。 725 */ 726 public String[] getValues( final int rowNo ) { 727 rowObj = sheet.getRow( rowNo ); 728 if( rowObj == null ) { return null; } 729 730 final int len = rowObj.getLastCellNum(); // 含まないので、length と同じ意味になる。 731 String[] vals = new String[len]; 732 733 for( int colNo=0; colNo<len; colNo++ ) { 734 final Cell colObj = rowObj.getCell( colNo ); 735 vals[colNo] = POIUtil.getValue( colObj ); 736 } 737 738 return vals ; 739 } 740 741 /** 742 * 現在のrow にあるセルの属性値を返します。 743 * 744 * セルオブジェクトが存在しない場合は、null を返します。 745 * 746 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 747 * この処理を実行すると、指定行の Rowオブジェクトが内部 Row に設定されます。 748 * 749 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 750 * 751 * @param rowNo 値が参照される行 752 * @param colNo 値が参照される列 753 * 754 * @return 指定されたセルの値 T 755 */ 756 public String getValue( final int rowNo, final int colNo ) { 757 rowObj = sheet.getRow( rowNo ); 758 if( rowObj == null ) { return null; } 759 760 final Cell colObj = rowObj.getCell( colNo ); 761 return POIUtil.getValue( colObj ); 762 } 763 764 /** 765 * 指定のシートの行・列の箇所に、イメージファイルを挿入します。 766 * 767 * ここでは、セル範囲ではなく、指定の行列の箇所に、アンカーを設定して、画像ファイルを 768 * 挿入します。一応、リサイズして、元の大きさ近くに戻しますが、縦横比が変わってしまいます。 769 * 正確に挿入する場合は、セル範囲の指定と、マージンを指定しなければなりませんが、 770 * 微調整が必要です。 771 * 772 * この処理で使用される Sheetオブジェクトは一時的に作成されます。(キャッシュされません) 773 * 一連処理のどのタイミングで実行しても、内部の状態には影響はありません。 774 * 775 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 776 * 777 * @param imgFile 挿入するイメージファイル名 778 * @param shtNo シート番号 779 * @param rowNo 挿入する行 780 * @param colNo 挿入する列 781 */ 782 public void addImageFile( final String imgFile, final int shtNo, final int rowNo, final int colNo ) { 783 addImageFile( imgFile,shtNo,rowNo,colNo,rowNo,colNo,0,0,0,0 ); 784 } 785 786 /** 787 * 指定のシートの行・列の箇所に、イメージファイルを挿入します。 788 * 789 * ここでは、セル範囲ではなく、指定の行列の箇所に、アンカーを設定して、画像ファイルを 790 * 挿入します。一応、リサイズして、元の大きさ近くに戻しますが、縦横比が変わってしまいます。 791 * 正確に挿入する場合は、セル範囲の指定と、マージンを指定しなければなりませんが、 792 * 微調整が必要です。 793 * 794 * この処理で使用される Sheetオブジェクトは一時的に作成されます。(キャッシュされません) 795 * 一連処理のどのタイミングで実行しても、内部の状態には影響はありません。 796 * 797 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 798 * 799 * @param imgFile 挿入するイメージファイル名 800 * @param shtNo シート番号 801 * @param row1 挿入する行(開始) 802 * @param col1 挿入する列(開始) 803 * @param row2 挿入する行(終了-含まず) 804 * @param col2 挿入する列(終了-含まず) 805 * @param dx1 開始セルのX軸座標(マージン) 806 * @param dy1 開始セルのY軸座標(マージン) 807 * @param dx2 終了セルのX軸座標(マージン) 808 * @param dy2 終了セルのY軸座標(マージン) 809 */ 810 public void addImageFile( final String imgFile , final int shtNo , 811 final int row1 , final int col1 , final int row2 , final int col2 , 812 final int dx1 , final int dy1 , final int dx2 , final int dy2 ) { 813 final String suffix = ImageUtil.getSuffix( imgFile ); 814 final Integer picType = PICTURE_TYPE.get( suffix ); 815 816 // 実験した結果、bmp,gif,tif については、PICTURE_TYPE_PNG で、挿入できた。 817 final int pictureType = picType != null ? picType.intValue() : Workbook.PICTURE_TYPE_PNG ; 818 819 final byte[] imgs = ImageUtil.byteImage( imgFile ); 820 821 final int pictureIdx = wkbook.addPicture( imgs, pictureType ); 822 823 final Sheet sheet = wkbook.getSheetAt( shtNo ); 824 final Drawing patriarch = sheet.createDrawingPatriarch(); // 昔は一度しか実行できなかったようです。 825 826 final ClientAnchor anchor = patriarch.createAnchor( dx1,dy1,dx2,dy2,col1,row1,col2,row2 ); 827 // ClientAnchor anchor = createHelper.createClientAnchor(); でも作成可能。 828 829 // MOVE_AND_RESIZE, MOVE_DONT_RESIZE, DONT_MOVE_AND_RESIZE から、決め打ち。 830 anchor.setAnchorType( ClientAnchor.MOVE_DONT_RESIZE ); 831 832 final Picture pic = patriarch.createPicture( anchor, pictureIdx ); 833 // セルの範囲指定がゼロの場合、画像サイズもゼロになる為、リサイズしておく。 834 if( row1 == row2 || col1 == col2 ) { pic.resize(); } // resize すると、anchor のマージンが無視されるようです。 835 } 836 837 /** 838 * 内部 Workbook オブジェクトをファイルに書き出します。 839 * 840 * Excelの形式は、ここで指定する出力ファイルの拡張子ではなく、コンストラクタで 841 * 指定したファイルの拡張子で決まります。 842 * 異なる形式の拡張子を持つファイルを指定した場合、強制的に、オープンした 843 * Workbook の形式の拡張子を追加します。 844 * 845 * 拡張子は、Excel 2007以降の形式(.xlsx)か、Excel 2003以前の形式(.xls) が指定できます。 846 * 拡張子が未設定の場合は、オープンした Workbook の形式に合わせた拡張子を付与します。 847 * 848 * isAutoCellSize=true の場合は、ここで全Sheetに対してCell幅の自動調整が行われます。 849 * 850 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 851 * @og.rev 6.1.0.0 (2014/12/26) 入力ファイルの拡張子判定の対応 852 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 853 * 854 * @param file セーブするファイル 855 */ 856 public void saveFile( final File file ) { 857 final File saveFile ; 858 String fname = file.getName(); 859 if( fname.toLowerCase(Locale.JAPAN).endsWith( sufix ) ) { 860 saveFile = file; 861 } 862 else { 863 final int idx = fname.lastIndexOf( '.' ); 864 if( idx >= 0 ) { fname = fname.substring( 0,idx ); } 865 saveFile = new File( file.getParent() , fname + sufix ); 866 } 867 868 if( isAutoCellSize ) { POIUtil.autoCellSize( wkbook, maxColCount, dataStartRow ); } 869 870 // こちらの都合で、TitleSheet は、autoCellSize ではなく、Sheet#autoSizeColumn(int) を使用して、自動計算させる。 871 if( addTitleSheet != null ) { makeAddTitleSheet(); } 872 873 OutputStream fileOut = null ; 874 try { 875 fileOut = new BufferedOutputStream( new FileOutputStream( saveFile ) ); // 6.1.0.0 (2014/12/26) 876 wkbook.write( fileOut ); 877 } 878 catch( IOException ex ) { 879 final String errMsg = "ファイルへ書込み中にエラーが発生しました。" + CR 880 + " File=" + saveFile + CR 881 + ex.getMessage() ; 882 throw new RuntimeException( errMsg,ex ); 883 } 884 finally { 885 Closer.ioClose( fileOut ); 886 } 887 } 888 889 /** 890 * 内部 Workbook オブジェクトのSheet一覧のSheetを、先頭に追加します。 891 * 892 * これは、Workbook に含まれる Sheet 一覧を作成する場合に、利用可能です。 893 * 894 * この処理は、内部のWorkbook、Sheetオブジェクトに依存して実行されます。 895 * また、単独ではなく、#saveFile( File ) 実行時に、addTitleSheet が 896 * 設定されている場合のみ、実行されます。 897 * 898 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 899 * 900 * @see #saveFile( File ) 901 * @see #setAddTitleSheet( String ) 902 */ 903 private void makeAddTitleSheet() { 904 sheet = wkbook.createSheet(); 905 final String shtNm = sheet.getSheetName(); // Sheet名の取得 906 wkbook.setSheetOrder( shtNm,0 ); // そのSheetを先頭に移動 907 setSheetName( 0,addTitleSheet ); // そのSheet名を変更 → これが、TitleSheet 908 909 int rowNo = 0; 910 createRow( rowNo++ ); // 先頭行(インスタンス共通のRowオブジェクト)作成 911 setCellValue( "No" , 0 ); 912 setCellValue( "Sheet", 1 ); 913 914 final int shCnt = wkbook.getNumberOfSheets(); 915 for( int shNo=1; shNo<shCnt; shNo++,rowNo++ ) { 916 final String nm = wkbook.getSheetName( shNo ); 917 918 createRow( rowNo ); // 行の追加作成 919 setCellValue( String.valueOf( rowNo ),0,true ); // 行番号として、数字型で登録 920 setCellValue( nm , 1 ); // シートの値を書き込む 921 setCellLink( nm , 1 ); // シートへのリンクを作成する。 922 } 923 924 sheet.autoSizeColumn( 0 ); 925 sheet.autoSizeColumn( 1 ); 926 } 927 928 /** 929 * Workbook の全Sheetを対象に、空行を取り除き、全体をシュリンクします。 930 * 931 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 932 * 933 * ここでは、Row を逆順にスキャンし、Cellが 存在しない間は、行を削除します。 934 * 途中の空行の削除ではなく、最終行からの連続した空行の削除です。 935 * 936 * isCellDel=true を指定すると、Cellの末尾削除を行います。 937 * 有効行の最後のCellから空セルを削除していきます。 938 * 表形式などの場合は、Cellのあるなしで、レイアウトが崩れる場合がありますので 939 * 処理が不要な場合は、isCellDel=false を指定してください。 940 * 941 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 942 * 943 * @param isCellDel Cellの末尾削除を行うかどうか(true:行う/false:行わない) 944 */ 945 public void activeWorkbook( final boolean isCellDel ) { 946 POIUtil.activeWorkbook( wkbook, isCellDel ); 947 } 948 949 /** 950 * Workbook の全Sheetを対象に、テキスト変換処理を行います(XSLX限定)。 951 * 952 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 953 * #activeWorkbook( boolean ) との順番は構いません。 954 * 955 * ・シート名の一覧をピックアップします。 956 * ・セル値を、セル単位にピックアップします。 957 * ・オブジェクト文字列を、改行単位にピックアップし、結果を合成します。 958 * 959 * ここでは、内部的に、TextConverterインターフェースを作成して処理します。 960 * 961 * @og.rev 6.2.4.2 (2015/05/29) テキスト変換処理 962 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 963 * 964 * @param convMap 変換対象を管理するMapオブジェクト 965 * @see #textConverter( TextConverter ) 966 */ 967 public void textConverter( final Map<String,String> convMap ) { 968 // textConverter( 969 // ( val,cmnt ) -> convMap.get( val ) 970 // ); 971 972 textConverter( 973 new TextConverter<String,String>() { 974 /** 975 * 入力文字列を、変換します。 976 * 977 * @param val 入力文字列 978 * @param cmnt コメント 979 * @return 変換文字列(変換されない場合は、null) 980 */ 981 @Override 982 public String change( final String val , final String cmnt ) { 983 return convMap.get( val ); 984 } 985 } 986 ); 987 } 988 989 /** 990 * Workbook の全Sheetを対象に、テキスト変換処理を行います(XSLX限定)。 991 * 992 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 993 * #activeWorkbook( boolean ) との順番は構いません。 994 * 995 * ・シート名の一覧をピックアップします。 996 * ・セル値を、セル単位内の改行単位にピックアップし、結果を合成ます。 997 * ・オブジェクト文字列を、改行単位にピックアップし、結果を合成します。 998 * 999 * ここでは、シート名、セルテキスト、SimpleShapeオブジェクトのテキストを 1000 * input に、TextConverterインターフェース の change メソッドを呼び出します。 1001 * 戻り値が、null でないなら、元のデータと置き換えます。 1002 * 戻り値が、null の場合は、そのまま読み飛ばします。(なにもしません) 1003 * EXCELへの書き戻しが発生しますので、万一、ファイル破損で、開けなくなる場合を 1004 * 想定して、バックアップファイルは、各自で準備してください。 1005 * 1006 * @og.rev 6.2.4.2 (2015/05/29) テキスト変換処理 1007 * @og.rev 6.2.5.0 (2015/06/05) xsl形式のオブジェクト取得…はできなかった。 1008 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 1009 * 1010 * @param conv TextConverterインターフェース 1011 * @see #textConverter( Map ) 1012 */ 1013 public void textConverter( final TextConverter<String,String> conv ) { 1014 // if( ".xlsx".equals( sufix ) || ".xlsm".equals( sufix ) ) { 1015 final int shCnt = wkbook.getNumberOfSheets(); 1016 for( int shNo=0; shNo<shCnt; shNo++ ) { 1017 final Sheet sht = wkbook.getSheetAt( shNo ); 1018 // シート名の変換 1019// final String shtNm = conv.change( sht.getSheetName() ); 1020 final String shtNm = conv.change( sht.getSheetName() , "Sheet" ); 1021 if( shtNm != null ) { 1022 setSheetName( shNo,shtNm ); // 同一シート対策済みのメソッドを呼び出す。 1023 } 1024 1025 // セル値の変換 1026 final int stR = Math.max( sht.getFirstRowNum(),0 ); // stR が、マイナスのケースがある。 1027 final int edR = sht.getLastRowNum(); 1028 1029 for( int rowNo=stR; rowNo<=edR; rowNo++ ) { 1030 final Row rowObj = sht.getRow( rowNo ); 1031 if( rowObj != null ) { 1032 final int stC = Math.max( rowObj.getFirstCellNum(),0 ); // stC が、マイナスのケースがある。 1033 final int edC = rowObj.getLastCellNum(); 1034 for( int colNo=stC; colNo<=edC; colNo++ ) { 1035 final Cell colObj = rowObj.getCell( colNo ); 1036 if( colObj != null && colObj.getCellType() != Cell.CELL_TYPE_BLANK ) { 1037 final String cmnt= "Sht" + shNo + ":" + POIUtil.getCelKigo( rowNo,colNo ); 1038 final String val = crConv( conv, POIUtil.getValue( colObj ),cmnt ); // 改行対応 1039 if( val != null ) { 1040 colObj.setCellValue( val ); 1041 } 1042 } 1043 } 1044 } 1045 } 1046 1047 // オブジェクト文字列の変換 1048 if( sht instanceof POIXMLDocumentPart ) { 1049 for( final POIXMLDocumentPart pxdp : ((POIXMLDocumentPart)sht).getRelations() ) { 1050 if( pxdp instanceof XSSFDrawing ) { 1051 for( final XSSFShape shape : ((XSSFDrawing)pxdp).getShapes() ) { 1052 final org.apache.poi.xssf.usermodel.XSSFAnchor anc = shape.getAnchor(); 1053 final String ancSt = "XY(" + anc.getDx1() + "-" + anc.getDy1() + ")" ; 1054 int cnt = 0; 1055 if( shape instanceof XSSFSimpleShape ) { 1056 for( final XSSFTextParagraph para : ((XSSFSimpleShape)shape).getTextParagraphs() ) { 1057 for( final XSSFTextRun text : para.getTextRuns() ) { 1058 final String cmnt= "Sht" + shNo + ":" + ancSt + ":Tb(" + cnt++ + ")" ; 1059 final String val = crConv( conv,text.getText() , cmnt ); 1060 if( val != null ) { 1061 text.setText( val ); 1062 } 1063 } 1064 } 1065 } 1066 } 1067 } 1068 } 1069 } 1070 // 6.2.5.0 (2015/06/05) xsl形式のオブジェクト取得…はできなかった。 1071 // else if( sht instanceof HSSFSheet ) { 1072 // HSSFPatriarch patri = ((HSSFSheet)sht).getDrawingPatriarch(); 1073 // for( final HSSFShape shape : patri.getChildren() ) { 1074 // if( shape instanceof HSSFTextbox ) { 1075 // HSSFRichTextString rts = ((HSSFSimpleShape)shape).getString(); 1076 // if( rts != null ) { 1077 // final String val = crConv( conv,rts.getString() ); 1078 // if( val != null ) { 1079 // HSSFRichTextString rts2 = new HSSFRichTextString( val ); 1080 // ((HSSFSimpleShape)shape).setString( rts2 ); 1081 // } 1082 // } 1083 // } 1084 // } 1085 // } 1086 } 1087 // } 1088 } 1089 1090 /** 1091 * Workbook の全Sheetを対象に、テキスト変換処理を行います(XSLX限定)。 1092 * 1093 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 1094 * #activeWorkbook( boolean ) との順番は構いません。 1095 * 1096 * ・シート名の一覧をピックアップします。 1097 * ・セル値を、セル単位内の改行単位にピックアップし、結果を合成ます。 1098 * ・オブジェクト文字列を、改行単位にピックアップし、結果を合成します。 1099 * 1100 * ここでは、シート名、セルテキスト、SimpleShapeオブジェクトのテキストを 1101 * input に、TextConverterインターフェース の change メソッドを呼び出します。 1102 * 戻り値が、null でないなら、元のデータと置き換えます。 1103 * 戻り値が、null の場合は、そのまま読み飛ばします。(なにもしません) 1104 * EXCELへの書き戻しが発生しますので、万一、ファイル破損で、開けなくなる場合を 1105 * 想定して、バックアップファイルは、各自で準備してください。 1106 * 1107 * @og.rev 6.2.4.2 (2015/05/29) テキスト変換処理 1108 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 1109 * 1110 * @param conv TextConverterインターフェース 1111 * @param val 改行処理を行う元の値 1112 * @param cmnt コメント 1113 * @return 改行処理の結果の値(対象が無ければ、null) 1114 * @see #textConverter( Map ) 1115 */ 1116// private String crConv( final TextConverter conv , final String val ) { 1117 private String crConv( final TextConverter<String,String> conv , final String val , final String cmnt ) { 1118 String rtn = null; 1119 if( val != null ) { 1120 if( val.contains( "\n" ) ) { // 改行がある場合(EXCEL のセル内改行コードは、LF(0A)=\n のみ。 1121 final String[] val2 = val.split( "\\n" ); // 改行で分割する。 1122 boolean flag = false; 1123 for( int i=0; i<val2.length; i++ ) { 1124// final String val3 = conv.change( val2[i] ); 1125 final String val3 = conv.change( val2[i],cmnt ); // 6.3.1.0 (2015/06/28) 1126 if( val3 != null ) { val2[i] = val3; flag = true; } 1127 } 1128 if( flag ) { 1129 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 1130 buf.append( val2[0] ); 1131 for( int i=1; i<val2.length; i++ ) { 1132 buf.append( '\n' ).append( val2[i] ); // LF(\n)で、セパレートしているので、LF のみ追加する。 1133 } 1134 rtn = buf.toString(); 1135 } 1136 } 1137 else { // 改行がない場合 1138// rtn = conv.change( val ); 1139 rtn = conv.change( val,cmnt ); // 6.3.1.0 (2015/06/28) 1140 } 1141 } 1142 return rtn; 1143 } 1144 1145 /** 1146 * シート一覧を、内部の Workbook から取得します。 1147 * 1148 * 取得元が、Workbook なので、xls , xlsx どちらの形式でも取り出せます。 1149 * 1150 * EXCEL上のシート名を、配列で返します。 1151 * 1152 * @og.rev 6.2.6.0 (2015/06/19) 新規作成 1153 * 1154 * @return シート名の配列 1155 * @see POIUtil#getSheetNames( Workbook ) 1156 */ 1157 public String[] getSheetNames() { 1158 return POIUtil.getSheetNames( wkbook ); 1159 } 1160 1161 /** 1162 * 名前定義一覧を内部の Workbook から取得します。 1163 * 1164 * EXCEL上に定義された名前を、配列で返します。 1165 * ここでは、名前とFormulaをタブで連結した文字列を配列で返します。 1166 * Name オブジェクトを削除すると、EXCELが開かなくなったりするので、 1167 * 取りあえず一覧を作成して、手動で削除してください。 1168 * なお、名前定義には、非表示というのがありますので、ご注意ください。 1169 * 1170 * @og.rev 6.2.6.0 (2015/06/19) 新規作成 1171 * 1172 * @return 名前定義(名前+TAB+Formula)の配列 1173 * @see POIUtil#getNames( Workbook ) 1174 * @og.rtnNotNull 1175 */ 1176 public String[] getNames() { 1177 return POIUtil.getNames( wkbook ); 1178 } 1179 1180 /** 1181 * 書式のスタイル一覧を内部の Workbook から取得します。 1182 * 1183 * EXCEL上に定義された書式のスタイルを、配列で返します。 1184 * 書式のスタイルの名称は、CellStyle にメソッドが定義されていません。 1185 * 実クラスである HSSFCellStyle にキャストして使用する 1186 * 必要があります。(XSSFCellStyle にも名称を取得するメソッドがありません。) 1187 * 1188 * ※ EXCEL2010 ホームタブ→セルのスタイル は、一つづつしか削除できません。 1189 * マクロは、開発タブ→Visual Basic で、挿入→標準モジュール を開き 1190 * テキストを張り付けてください。 1191 * 実行は、開発タブ→マクロ で、マクロ名を選択して、実行します。 1192 * 最後は、削除してください。 1193 * 1194 * @og.rev 6.2.6.0 (2015/06/19) 新規作成 1195 * 1196 * @return 書式のスタイル一覧 1197 * @see POIUtil#getStyleNames( Workbook ) 1198 * @og.rtnNotNull 1199 */ 1200 public String[] getStyleNames() { 1201 return POIUtil.getStyleNames( wkbook ); 1202 } 1203 1204 /** 1205 * 文字列を Double オブジェクトに変換します。 1206 * 1207 * これは、引数の カンマ(,) を削除した文字列から、Double オブジェクトを生成します。 1208 * 処理中に、文字列が解析可能な double を含まない場合(NumberFormatException) 1209 * また、引数が、null,ゼロ文字列,'_', エラー の時には、null を返します。 1210 * 1211 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1212 * 1213 * @param value Doubleに変換する元の文字列 1214 * 1215 * @return 変換後のDoubleオブジェクト(エラー発生時や変換不可の場合は、null) 1216 */ 1217 private Double parseDouble( final String value ) { 1218 Double rtn = null ; 1219 1220 try { 1221 if( value == null || value.isEmpty() || value.equals( "_" ) ) { 1222 rtn = null; 1223 } 1224 else if( value.indexOf( ',' ) < 0 ) { 1225 rtn = Double.valueOf( value ); // 6.0.2.4 (2014/10/17) メソッドが非効率だった。 1226 } 1227 else { 1228 char[] chs = value.toCharArray() ; 1229 int j=0; 1230 for( int i=0;i<chs.length; i++ ) { 1231 if( chs[i] == ',' ) { continue; } 1232 chs[j] = chs[i]; 1233 j++; 1234 } 1235 // 6.0.2.5 (2014/10/31) refactoring 1236 rtn = Double.valueOf( String.valueOf( chs,0,j ) ); 1237 } 1238 } 1239 catch( NumberFormatException ex ) { // 文字列が解析可能な数値を含まない場合 1240 final String errMsg = "Double変換できませんでした。" + CR 1241 + ex.getMessage() + CR 1242 + " value=" + value; 1243 System.err.println( errMsg ); 1244 rtn = null; 1245 } 1246 1247 return rtn ; 1248 } 1249 1250 /** 1251 * アプリケーションのサンプルです。 1252 * 1253 * Usage: java org.opengion.fukurou.model.ExcelModel 入力ファイル名 [出力ファイル名] ・・・ 1254 * 通常は標準出力に行単位に、セルをタブ区切り出力します。 1255 * 出力ファイル名 を指定すると、EXCEL ファイルとしてセーブし直します。 1256 * その場合は、以下のパラメータも使用できます。 1257 * -CS CellStyleを 設定します。 1258 * -AS useAutoCellSizeを 設定します。 1259 * -FN=*** FontNameを 設定します。 1260 * -FP=** FontPointを 設定します。 1261 * -IMG 画像ファイルを挿入します。(-IMG 画像ファイル名 シート番号 行 列)をスペース区切りで続けます。 1262 * 1263 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1264 * 1265 * @param args コマンド引数配列 1266 */ 1267 public static void main( final String[] args ) { 1268 if( args.length == 0 ) { 1269 final String usage = "Usage: java org.opengion.fukurou.model.ExcelModel 入力ファイル名 [出力ファイル名] ・・・\n" + 1270 "\t-CS CellStyleを 設定します。 \n" + 1271 "\t-TC TextConverterを実行します。 \n" + 1272 "\t-AS useAutoCellSizeを 設定します。 \n" + 1273 "\t-FN=*** FontNameを 設定します。 \n" + 1274 "\t-FP=** FontPointを 設定します。 \n" + 1275 "\t-IMG 画像ファイルを挿入します。 \n" + 1276 "\t (-IMG ファイル名 シート番号 行 列) \n" ; 1277 System.err.println( usage ); 1278 return ; 1279 } 1280 1281 final ExcelModel excel = new ExcelModel( new File( args[0] ) , true ); 1282 1283 excel.activeWorkbook( true ); // 余計な行を削除します。 1284 1285 if( args.length > 1 ) { 1286 final File outFile = new File( args[1] ); // 6.2.0.0 (2015/02/27) 1287 boolean isCS = false; 1288 boolean isAS = false; 1289 boolean isTC = false; // 6.2.4.2 (2015/05/29) テキスト変換処理 1290 String fn = null; 1291 short fp = -1; 1292 for( int i=2; i<args.length; i++ ) { 1293 final String prm = args[i]; 1294 1295 if( prm.equalsIgnoreCase( "-CS" ) ) { isCS = true; } 1296 if( prm.equalsIgnoreCase( "-AS" ) ) { isAS = true; } 1297 if( prm.equalsIgnoreCase( "-TC" ) ) { isTC = true; } 1298 if( prm.startsWith( "-FN" ) ) { fn = prm.substring( 3 ); } 1299 if( prm.startsWith( "-FP" ) ) { fp = Short.parseShort( prm.substring( 3 ) ); } 1300 if( prm.equalsIgnoreCase( "-IMG" ) ) { 1301 final String img = args[++i]; 1302 final int shtNo = Integer.parseInt( args[++i] ); 1303 final int rowNo = Integer.parseInt( args[++i] ); 1304 final int colNo = Integer.parseInt( args[++i] ); 1305 1306 excel.addImageFile( img,shtNo,rowNo,colNo ); 1307 } 1308 } 1309 1310 if( isCS ) { excel.setCellStyle(); } 1311 excel.useAutoCellSize( isAS ); 1312 excel.setFont( fn,fp ); 1313 1314 // 6.2.4.2 (2015/05/29) テキスト変換処理 1315 if( isTC ) { 1316 excel.textConverter( 1317 new TextConverter<String,String>() { 1318 /** 1319 * 入力文字列を、変換します。 1320 * 1321 * @param val 入力文字列 1322 * @param cmnt コメント 1323 * @return 変換文字列(変換されない場合は、null) 1324 */ 1325 @Override 1326// public String change( final String val ) { 1327 public String change( final String val , final String cmnt ) { 1328 System.out.println( val ); // すべてのテキストを読み取る。 1329 return null; // 変換せず。 1330 } 1331 } 1332 ); 1333 } 1334 1335 excel.saveFile( outFile ); 1336 } 1337 else { 1338 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 1339 1340 final int shLen = excel.getNumberOfSheets(); 1341 for( int shNo=0; shNo<shLen; shNo++ ) { 1342 final String sheetName = excel.getSheetName( shNo ); 1343 1344 final int stRow = excel.getFirstRowNum(); 1345 final int edRow = excel.getLastRowNum(); 1346 for( int rowNo=stRow; rowNo<=edRow; rowNo++ ) { 1347 buf.setLength(0); // Clearの事 1348 buf.append( sheetName ).append( '\t' ).append( rowNo ); 1349 final String[] vals = excel.getValues( rowNo ); 1350 if( vals != null ) { 1351 for( int colNo=0; colNo<vals.length; colNo++ ) { 1352 final String val = vals[colNo] == null ? "" : vals[colNo]; 1353 buf.append( '\t' ).append( val ); 1354 } 1355 } 1356 System.out.println( buf ); 1357 } 1358 System.out.println(); 1359 } 1360 } 1361 } 1362}