javaでエクセル出力のメモ

## はじめに
`Apache POI`を使用してExcelファイルを操作する。
そのメモ。

## 記載内容
- 1.テンプレート読み込み例
- 2.セルにフォント、罫線セット
  - 2.0 セル設定流れ
  - 2.1 １つのセル、列幅等調整なし
  - 2.2 列幅固定、行の高さ自動調整
  - 2.3 列幅固定、行の高さ自動調整、複数行
  - 2.4 列幅、高さ自動調整
  - 2.5 列幅、行の高さ自動調整、複数行
  - 2.6 行の高さ　手動調整
  - 2.7 セル設定流れ(整理)
  - 2.8 セルの結合での罫線セット
  - 2.9 セル位置ずらし
- 3.ページ設定
  - 3.1 次のページ数に合わせて印刷
  - 3.2 倍率設定
  - 3.3 改ページ、ページ複製
- 4.ページ番号
  - 4.1 フッター表示
  - 4.2 先頭ページ番号１を表示される(新規シート作成)
  - 4.3 既存シートのコピーの場合１
  - 4.4 既存シートのコピーの場合２
- 5.設定確認例
  - 5.1 配置


## 1.テンプレート読み込み例
- テンプレートファイル（`template.xlsx`）を読み込み、A1セルに文字列「こんにちは、Apache POI!」、B2セルに数値`12345`を設定。
- 出力ファイル（`output.xlsx`）として保存されます。

```java
package com.example.POIExample2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App2 {
    public static void main(String[] args) {
        // テンプレートファイルのパス
        String templateFilePath = "template.xlsx";
        // 出力ファイルのパス
        String outputFilePath = "output.xlsx";

        try (FileInputStream fis = new FileInputStream(templateFilePath);
             Workbook workbook = new XSSFWorkbook(fis)) {

            // シートを取得
            Sheet sheet = workbook.getSheetAt(0); // 0番目のシートを取得
            if (sheet == null) {
                System.out.println("指定されたシートが存在しません。");
                return;
            }

            // セルを取得し値を設定 (例: A1セル)
            Row row = sheet.getRow(0); // 1行目（インデックスは0）
            if (row == null) {
                row = sheet.createRow(0); // 行がなければ作成
            }

            Cell cell = row.getCell(0); // A列（インデックスは0）
            if (cell == null) {
                cell = row.createCell(0); // セルがなければ作成
            }

            cell.setCellValue("こんにちは、Apache POI!"); // 値を設定

            // 別のセルにも値を設定（例: B2セル）
            Row row2 = sheet.getRow(1); // 2行目
            if (row2 == null) {
                row2 = sheet.createRow(1); // 行がなければ作成
            }

            Cell cell2 = row2.getCell(1); // B列
            if (cell2 == null) {
                cell2 = row2.createCell(1); // セルがなければ作成
            }

            cell2.setCellValue(12345); // 数値を設定

            // 更新されたワークブックをファイルに書き込む
            try (FileOutputStream fos = new FileOutputStream(outputFilePath)) {
                workbook.write(fos);
            }

            System.out.println("値が設定されたファイルが保存されました: " + outputFilePath);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/22951026-7739-bd57-9bce-bb2e263bbd6d.png)



## 2.セルにフォント、罫線セット

### 2.1 １つのセル、列幅等調整なし

Apache POIを使用すると、Javaでエクセルファイルを生成および操作できます。記載された3つの対応についても可能です。それぞれの設定方法を以下に示します。

#### 1. **配置タブで縦位置を中央揃えにセットする**

セルの縦方向の配置を設定するには、`CellStyle` の `setVerticalAlignment` メソッドを使用します。

```java
import org.apache.poi.ss.usermodel.*;

Workbook workbook = WorkbookFactory.create();
Sheet sheet = workbook.createSheet("Sample Sheet");
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("中央揃え");

// セルスタイルを作成
CellStyle style = workbook.createCellStyle();
// 縦方向を中央揃えに設定
style.setVerticalAlignment(VerticalAlignment.CENTER);
cell.setCellStyle(style);

// エクセルを出力する処理を続ける
```

#### 2. **フォントタブでフォント名、スタイル、サイズ等をセットする**

フォントの設定は、`Font` オブジェクトを作成して `CellStyle` に関連付けます。

```java
Font font = workbook.createFont();
font.setFontName("Arial");  // フォント名
font.setFontHeightInPoints((short) 12);  // フォントサイズ
font.setBold(true);  // 太字に設定
font.setItalic(false);  // 斜体を無効化

// セルスタイルにフォントを適用
style.setFont(font);
```
#### 3. **罫線で線をセットする**

罫線は、セルスタイルの境界線プロパティを設定します。

```java
// 上、下、左、右の罫線を設定
style.setBorderTop(BorderStyle.THIN);  // 上の罫線
style.setBorderBottom(BorderStyle.THIN);  // 下の罫線
style.setBorderLeft(BorderStyle.THIN);  // 左の罫線
style.setBorderRight(BorderStyle.THIN);  // 右の罫線

// 罫線の色を設定
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
```

### 組み合わせた例

以下は、縦方向中央揃え、フォント設定、および罫線を同時に適用する例です。



#### 出力結果

- 縦位置が中央揃え
- フォントが `Arial`、サイズ12、太字
- セルに罫線が引かれている

このコードをベースに調整してください。








```java
package com.example.POIExample2;

import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("Sample Sheet");
            Row row = sheet.createRow(0);
            Cell cell = row.createCell(0);
            cell.setCellValue("フォーマット済みセル");

            // セルスタイルを作成
            CellStyle style = workbook.createCellStyle();

            // 縦方向中央揃え
            style.setVerticalAlignment(VerticalAlignment.CENTER);

            // フォント設定
            Font font = workbook.createFont();
            font.setFontName("Arial");
            font.setFontHeightInPoints((short) 12);
            font.setBold(true);
            style.setFont(font);

            // 罫線設定
            style.setBorderTop(BorderStyle.THIN);
            style.setBorderBottom(BorderStyle.THIN);
            style.setBorderLeft(BorderStyle.THIN);
            style.setBorderRight(BorderStyle.THIN);
            style.setTopBorderColor(IndexedColors.BLACK.getIndex());
            style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
            style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
            style.setRightBorderColor(IndexedColors.BLACK.getIndex());

            // スタイルをセルに適用
            cell.setCellStyle(style);

            // ファイル出力
            try (FileOutputStream fos = new FileOutputStream("formatted_excel.xlsx")) {
                workbook.write(fos);
            }

            System.out.println("エクセルファイルが生成されました。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/442d2cee-f5b1-4801-fbb0-a9ca19af07d8.png)



### 2.2 列幅固定、行の高さ自動調整
1セルのみの内容を変更し、列の幅をテンプレート（既存の設定値）のままにしながら行の高さを自動調整する場合、以下の例をご覧ください。

このコードでは、列幅の自動調整は行わず、行の高さだけを内容に基づいて自動的に調整します。

---

### 例: 列幅は固定で、行の高さを自動調整するコード

```java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;

public class SingleCellAutoHeightExample {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            // テンプレートとして既存の列幅を設定
            Sheet sheet = workbook.createSheet("Template Example");
            sheet.setColumnWidth(0, 5000); // 列幅を固定（既存設定の再現）

            // 行とセルを作成
            Row row = sheet.createRow(0); // 1行目
            Cell cell = row.createCell(0); // A1セル

            // セルにデータを設定
            cell.setCellValue("このセルには長い内容が含まれており、列の幅を変更せずに行の高さを自動調整します。");

            // スタイルを適用
            CellStyle style = workbook.createCellStyle();
            style.setWrapText(true); // 折り返し設定を有効化
            style.setVerticalAlignment(VerticalAlignment.CENTER); // 縦中央揃え
            cell.setCellStyle(style);

            // 行の高さを自動調整
            row.setHeight((short) -1); // 自動調整の特殊値

            // ファイル出力
            try (FileOutputStream fos = new FileOutputStream("single_cell_auto_height.xlsx")) {
                workbook.write(fos);
            }

            System.out.println("1セルのみの行高さ自動調整エクセルファイルが生成されました。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

---

### コードのポイント

1. **列幅をテンプレートのまま固定**:
   ```java
   sheet.setColumnWidth(0, 5000);
   ```
   この設定で列幅は変更されません。

2. **行の高さを自動調整**:
   ```java
   row.setHeight((short) -1);
   ```
   Apache POI はセル内容に基づいて行高さを自動的に計算します。

3. **折り返し設定を有効化**:
   ```java
   style.setWrapText(true);
   ```
   折り返し設定を行うことで、セル内のテキストが複数行に分割され、行の高さが適切に調整されます。

### 出力結果
- 列幅は 5000（約17.86ポイント）に固定されています。
- セル内の長い内容は自動的に折り返され、行の高さが適切に調整されます。

### 行の高さ自動調整
`row.setHeight((short) -1)` は行の高さを「自動調整」に設定するための特殊値ですが、この方法は直感的でない上、環境やシートの設定によっては意図しない動作を引き起こす可能性があります。

### **`row.setHeight((short) -1)` の問題点**
1. **特殊値で分かりにくい**  
   - `(short) -1` という値が具体的に「自動調整」を意味することがコードから直感的に分かりにくいです。メンテナンス性が下がる可能性があります。

2. **列幅に依存する**  
   - 行の高さの自動調整は、セル内の内容が列幅に収まるかどうかに大きく影響されます。`row.setHeight((short) -1)` では、列幅が正しく設定されていない場合、意図通りに自動調整されない可能性があります。

3. **シート全体の設定が影響**  
   - シートやセルのスタイル設定によっては、`row.setHeight((short) -1)` の挙動が期待通りでないことがあります。

### **`sheet.autoSizeRow(rowIndex)` の利点**
`sheet.autoSizeRow(rowIndex)` を使用する方が、以下の点でより安全かつ直感的です：

1. **明確で直感的**  
   - メソッド名からして「行の高さを自動調整する」という目的がはっきりしており、コードの可読性が向上します。

2. **依存関係の解消**  
   - 列幅やセルスタイルが影響を与える場合でも、Apache POI の内部で処理が行われるため、期待通りの動作を得られる可能性が高いです。

3. **API のサポートに依存**  
   - Apache POI のバージョンによって挙動が改善されることがあるため、公式のメソッドを利用する方が将来的に安心です。


### **例：列幅と行高さの調整**
以下のコードは列幅を設定した上で、行高さを自動調整する例です：

```java
// 列幅を設定（文字数に応じて自動調整）
sheet.autoSizeColumn(0); // 0列目

// 行高さを自動調整
sheet.autoSizeRow(rowIndex);
```

複数の行を一括で調整する場合は、ループを利用して適用できます：

```java
for (int rowIndex = 0; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
    sheet.autoSizeRow(rowIndex);
}
```




### 注意点
- 行高さの自動調整は、セルに折り返し (`setWrapText`) が設定されている場合のみ有効です。
- 固定された列幅がセル内容に比べて狭すぎる場合、折り返しが多くなり行高さが増える可能性があります。

このコードを実行すれば、列幅をテンプレートとして固定したまま、セル内容に基づいて行の高さが適切に調整されたエクセルファイルが生成されます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/1fc23582-2ce8-2a9b-b0ec-7c5f8b107c4c.png)



## 2.3 列幅固定、行の高さ自動調整、複数行
複数行のセルデータを扱い、列幅を固定したまま行の高さを自動調整する例を以下に示します。

### 複数行対応の例: 列幅固定、行高さ自動調整

```java
package com.example.POIExample2;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;

public class MultiRowAutoHeightExample {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            // シート作成
            Sheet sheet = workbook.createSheet("Multi Row Example");

            // 列幅をテンプレートのまま固定
            sheet.setColumnWidth(0, 5000); // 列幅固定（5000 = 約17.86ポイント）

            // サンプルデータ
            String[] data = {
                "短いテキスト",
                "中くらいのテキストで、少し長めの内容です。",
                "非常に長いテキスト。このセルには長い内容が含まれており、行の高さが自動的に調整されます。"
            };

            // データを挿入
            for (int rowIndex = 0; rowIndex < data.length; rowIndex++) {
                Row row = sheet.createRow(rowIndex); // 行を作成
                Cell cell = row.createCell(0); // A列にセルを作成

                // セルにデータを設定
                cell.setCellValue(data[rowIndex]);

                // セルスタイルを設定
                CellStyle style = workbook.createCellStyle();
                style.setWrapText(true); // 折り返しを有効化
                style.setVerticalAlignment(VerticalAlignment.CENTER); // 縦中央揃え
                cell.setCellStyle(style);

                // 行の高さを自動調整
                row.setHeight((short) -1); // 自動調整の特殊値
            }

            // ファイル出力
            try (FileOutputStream fos = new FileOutputStream("multi_row_auto_height.xlsx")) {
                workbook.write(fos);
            }

            System.out.println("複数行の行高さ自動調整エクセルファイルが生成されました。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

### コードのポイント

1. **列幅を固定**  
   列幅は `sheet.setColumnWidth(0, 5000)` を使用して固定します。  
   これにより、列幅がデータに関係なく一定に保たれます。

2. **行高さを自動調整**  
   各行の高さを `(short) -1` に設定することで、Apache POI がデータ内容に基づいて行の高さを計算します。

3. **セルの折り返しを有効化**  
   長いデータがセル内で複数行に分割されるように、`CellStyle.setWrapText(true)` を使用します。

4. **複数行データのループ処理**  
   配列 `data` を使用して複数行のセルデータを処理しています。

### 出力結果

| データ                                                                                         |
|-----------------------------------------------------------------------------------------------|
| 短いテキスト                                                                                 |
| 中くらいのテキストで、少し長めの内容です。                                                     |
| 非常に長いテキスト。このセルには長い内容が含まれており、行の高さが自動的に調整されます。       |

- 列幅は固定されており、内容に応じてセルが折り返されます。
- 各行の高さはデータ内容に基づいて自動的に調整されます。

---

### 注意点
1. **固定列幅が狭すぎる場合**  
   非常に長いテキストがあると折り返しが多くなり、行の高さが過度に増える可能性があります。適切な列幅を設定してください。

2. **フォントサイズの影響**  
   フォントサイズが大きい場合、行の高さも大きくなるため、フォントサイズに合わせた調整を検討してください。


このコードを実行すれば、列幅が固定されつつ、各行の高さが内容に応じて自動的に調整されたエクセルファイルが生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/b7e2cf0e-2df7-aff7-8cd4-37776fb0c4b5.png)

## 2.4 列幅、高さ自動調整、１行

```java
package com.example.POIExample2;

import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("Sample Sheet");

            // サンプルデータを挿入
            Row row = sheet.createRow(0);
            Cell cell = row.createCell(0);
            cell.setCellValue("フォーマット済みセルの内容が罫線内に収まるように設定");

            // セルスタイルを作成
            CellStyle style = workbook.createCellStyle();

            // 縦方向中央揃え
            style.setVerticalAlignment(VerticalAlignment.CENTER);
            style.setAlignment(HorizontalAlignment.CENTER);  // 水平方向中央揃え

            // フォント設定
            Font font = workbook.createFont();
            font.setFontName("Arial");
            font.setFontHeightInPoints((short) 12);
            font.setBold(true);
            style.setFont(font);

            // 罫線設定
            style.setBorderTop(BorderStyle.THIN);
            style.setBorderBottom(BorderStyle.THIN);
            style.setBorderLeft(BorderStyle.THIN);
            style.setBorderRight(BorderStyle.THIN);
            style.setTopBorderColor(IndexedColors.BLACK.getIndex());
            style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
            style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
            style.setRightBorderColor(IndexedColors.BLACK.getIndex());

            // 折り返し設定
            style.setWrapText(true);

            // スタイルをセルに適用
            cell.setCellStyle(style);

            // 行の高さを自動調整
            row.setHeight((short) -1);  // 自動調整の特殊値

            // 列の幅を自動調整
            sheet.autoSizeColumn(0);

            // ファイル出力
            try (FileOutputStream fos = new FileOutputStream("formatted_excel_with_wrapping.xlsx")) {
                workbook.write(fos);
            }

            System.out.println("エクセルファイルが生成されました。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/47c1caee-f102-fbfb-9b0a-75b09dbd1761.png)




セルの内容がはみ出さず、罫線が見えるようにするには、以下の対応を行います。

1. **列の幅を適切に調整する**  
   `Sheet.autoSizeColumn` メソッドを使うことで、セル内容に合わせて列の幅を自動調整できます。

2. **セルの折り返しを有効にする**  
   長い内容をセル内で折り返すように設定するには、`CellStyle.setWrapText(true)` を使用します。

#### 修正内容ポイント

1. **折り返し設定**  
   `style.setWrapText(true);` を追加し、セル内で内容を折り返します。

2. **行の高さ自動調整**  
   `row.setHeight((short) -1);` を設定し、折り返しに応じて行の高さを自動調整します。

3. **列の幅自動調整**  
   `sheet.autoSizeColumn(0);` を設定し、内容に合わせて列幅を調整します。


- **セル内容が折り返され、罫線内に収まります。**
- **列幅がセル内容に合わせて調整され、罫線が見やすくなります。**

これで、セル内容が罫線内に収まり、適切に表示されるはずです！





## 2.5 列幅、行の高さ自動調整、複数行
複数行に対応する場合、各行に対して適切なスタイル設定や行の高さ、列の幅を調整する必要があります。以下に、複数行のデータを含む例を示します。

---

### 複数行対応のコード例

```java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;

public class MultiRowExcelExample {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("Multi Row Example");

            // サンプルデータ
            String[][] data = {
                {"名前", "役職", "コメント"},
                {"田中 太郎", "課長", "複数行対応の例を示します。"},
                {"鈴木 一郎", "主任", "このセルには非常に長いコメントが含まれており、罫線内に収まるように折り返しが設定されています。"},
                {"佐藤 花子", "部長", "短いコメント"}
            };

            // データを挿入
            for (int rowIndex = 0; rowIndex < data.length; rowIndex++) {
                Row row = sheet.createRow(rowIndex);
                for (int colIndex = 0; colIndex < data[rowIndex].length; colIndex++) {
                    Cell cell = row.createCell(colIndex);
                    cell.setCellValue(data[rowIndex][colIndex]);

                    // スタイルを適用
                    CellStyle style = workbook.createCellStyle();

                    // 中央揃え設定
                    style.setVerticalAlignment(VerticalAlignment.CENTER);
                    style.setAlignment(HorizontalAlignment.CENTER);

                    // フォント設定
                    Font font = workbook.createFont();
                    font.setFontName("Arial");
                    font.setFontHeightInPoints((short) 12);
                    style.setFont(font);

                    // 罫線設定
                    style.setBorderTop(BorderStyle.THIN);
                    style.setBorderBottom(BorderStyle.THIN);
                    style.setBorderLeft(BorderStyle.THIN);
                    style.setBorderRight(BorderStyle.THIN);

                    // 折り返し設定
                    style.setWrapText(true);
                    cell.setCellStyle(style);
                }
            }

            // 列の幅を自動調整
            for (int colIndex = 0; colIndex < data[0].length; colIndex++) {
                sheet.autoSizeColumn(colIndex);
            }

            // ファイル出力
            try (FileOutputStream fos = new FileOutputStream("multi_row_excel.xlsx")) {
                workbook.write(fos);
            }

            System.out.println("複数行のエクセルファイルが生成されました。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

---

### このコードのポイント

1. **複数行データの処理**  
   `String[][] data` を用いて、複数行のデータを管理します。

2. **セルごとのスタイル設定**  
   各セルに対してスタイルを設定します。中央揃え、折り返し、罫線などが含まれます。

3. **列幅の自動調整**  
   `sheet.autoSizeColumn(colIndex);` を使用して、各列の幅をデータ内容に応じて自動調整します。

### 出力結果

| 名前       | 役職   | コメント                                                                 |
|------------|--------|--------------------------------------------------------------------------|
| 田中 太郎  | 課長   | 複数行対応の例を示します。                                               |
| 鈴木 一郎  | 主任   | このセルには非常に長いコメントが含まれており、罫線内に収まるように折り返しが設定されています。 |
| 佐藤 花子  | 部長   | 短いコメント                                                             |

- 長いコメントはセル内で自動的に折り返され、罫線内に収まります。
- 列幅はデータの長さに応じて調整されます。

このコードを実行すると、複数行のエクセルデータが正しく表示されるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/6ad60518-647f-bed7-becb-77b5188a44ad.png)

## 2.6 行の高さ　手動調整
自動折り返しでなく、計算で行う例。
```java
package com.example.POIExample2;

import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        // ダミーデータを作成
        List<String[]> data = fetchData();

        // ワークブックとシートを作成
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("Export Data");

        // 列幅を設定
        int columnWidth = 30 * 256; // 30文字分の幅
        sheet.setColumnWidth(0, columnWidth); // 最初の列に適用

        // データをセット
        for (int rowIndex = 0; rowIndex < data.size(); rowIndex++) {
            Row row = sheet.createRow(rowIndex);
            String[] rowData = data.get(rowIndex);

            for (int colIndex = 0; colIndex < rowData.length; colIndex++) {
                // セルにデータをセット
                Cell cell = row.createCell(colIndex);
                cell.setCellValue(rowData[colIndex]);

                // 折り返し設定を適用
                CellStyle style = workbook.createCellStyle();
                style.setWrapText(true);
                
                // 上詰めに設定
                style.setVerticalAlignment(VerticalAlignment.TOP);
                
                cell.setCellStyle(style);
            }

            // 行の高さを計算して設定
            adjustRowHeight(sheet, row, columnWidth);
        }

        // Excelファイルを出力
        try (FileOutputStream outputStream = new FileOutputStream("ExportedData.xlsx")) {
            workbook.write(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // ワークブックを閉じる
        try {
            workbook.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // ダミーデータを取得（本来はDBやAPIから取得）
    public static List<String[]> fetchData() {
        List<String[]> data = new ArrayList<>();
        data.add(new String[]{"短いデータ", "Another short data"});
        data.add(new String[]{"このデータは非常に長い内容です。この列幅では折り返しが発生します。", "More data here"});
        data.add(new String[]{"改行も\n含まれるデータ", "Another cell"});
        return data;
    }

    // 行の高さを調整
    public static void adjustRowHeight(Sheet sheet, Row row, int columnWidth) {
        int maxLineCount = 1; // 最小行数は1行

        for (Cell cell : row) {
            if (cell.getCellType() == CellType.STRING) {
                String cellValue = cell.getStringCellValue();

                // 列幅に基づく1行あたりの文字数を計算
                int charPerLine = columnWidth / 256; // 列幅から1行に収まる文字数を計算
                int wrappedLines = (int) Math.ceil((double) cellValue.length() / charPerLine); // 折り返し行数
                int explicitNewLines = cellValue.split("\n").length; // 明示的な改行数

                // 最大行数を更新
                maxLineCount = Math.max(maxLineCount, wrappedLines + explicitNewLines - 1);
            }
        }

        // 余裕を持たせるために行数に+1行を追加
        row.setHeight((short) ((maxLineCount + 1) * 20 * 20));
    }

}

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/48ac5362-a01e-9b3a-75e2-2f2c9cd8e715.png)

### 2.7 セル設定流れ(整理)
以下に、各コード部分の役割を再確認します。

コード全体の流れは以下の通りです：

1. **セルを作成**: `row.createCell(colIndex)` を使用。
2. **値を設定**: `cell.setCellValue()` を使用。
3. **スタイルを作成**: `workbook.createCellStyle()` でスタイルを定義。
4. **スタイルを適用**: `cell.setCellStyle(style)` でセルにスタイルを設定。

この理解に基づいて、セルの値やスタイルを柔軟に操作できます。

#### **1. 値のセット**

```java
Cell cell = row.createCell(colIndex);
cell.setCellValue(data[rowIndex][colIndex]);
```
- **`row.createCell(colIndex)`**: 指定した列（`colIndex`）に新しいセルを作成します。
- **`cell.setCellValue()`**: セルに値を設定します。この値は文字列、数値、日付など、様々な型をサポートします。

#### **2. スタイルの設定**

```java
CellStyle style = workbook.createCellStyle();
```
- **`workbook.createCellStyle()`**: ワークブック（Excelファイル）に新しいセルスタイルを作成します。
- 作成した `style` オブジェクトを使用して、以下のプロパティを設定できます。

#### **`style` を使用したプロパティ設定例**

1. **配置の設定**
   - **水平揃え**
     ```java
     style.setAlignment(HorizontalAlignment.CENTER); // 中央揃え
     ```
   - **垂直揃え**
     ```java
     style.setVerticalAlignment(VerticalAlignment.CENTER); // 中央揃え
     ```

2. **フォントの設定**
   ```java
   Font font = workbook.createFont();
   font.setFontName("Arial"); // フォント名
   font.setFontHeightInPoints((short) 12); // フォントサイズ
   font.setBold(true); // 太字
   style.setFont(font);
   ```

3. **罫線の設定**
   ```java
   style.setBorderTop(BorderStyle.THIN); // 上線
   style.setBorderBottom(BorderStyle.THIN); // 下線
   style.setBorderLeft(BorderStyle.THIN); // 左線
   style.setBorderRight(BorderStyle.THIN); // 右線
   style.setTopBorderColor(IndexedColors.BLACK.getIndex()); // 上線の色
   style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); // 下線の色
   style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); // 左線の色
   style.setRightBorderColor(IndexedColors.BLACK.getIndex()); // 右線の色
   ```

4. **折り返しの設定**
   ```java
   style.setWrapText(true); // テキストを折り返す
   ```

5. **背景色の設定**
   ```java
   style.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); // 背景色
   style.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 塗りつぶしパターン
   ```

#### **3. スタイルの適用**

最後に、作成したスタイルをセルに適用します。

```java
cell.setCellStyle(style);
```

#### **行の高さを自動調整**

```java
row.setHeight((short) -1);
```

##### 説明
- 行の高さを内容に基づいて自動調整します。
- セル内のテキストの長さと折り返し設定に応じて、行の高さが適切に計算されます。
- **注意点**:
  - 折り返し設定が有効 (`style.setWrapText(true)`) である必要があります。
  - 環境やフォント情報に依存するため、完全に期待通りに動作しない場合があります。

---

#### **行の折り返し**

```java
style.setWrapText(true);
```

- セル内のテキストを折り返して表示します。
- セルの幅に収まらない内容が改行され、複数行として表示されます。
- **注意点**:
  - 折り返しが有効になっていない場合、自動調整の効果が得られません。


#### **列の幅を自動調整**

```java
sheet.autoSizeColumn(列の位置);
```

- 列内の最も長い内容に基づいて列幅を自動調整します。
- 文字数やフォントサイズに応じて列幅が決まります。
- **注意点**:
  - 列幅が狭すぎる場合に手動での調整が必要になる場合があります。
  - 折り返し (`setWrapText(true)`) が有効になっているセルでは、適切に調整されないことがあります。

### 2.8 セルの結合での罫線セット

以下コードは、テンプレートExcelファイル (`template.xlsx`) を読み込み、指定されたデータを結合セルに設定し、罫線を引いてExcelファイルを出力するプログラムです。


#### **プログラムの動作フロー**
1. `template.xlsx`を読み込み、テンプレートの構造を保持。
2. データ配列に従って結合セルにデータを挿入。
3. スタイル（罫線や中央揃え）をセルに適用。
4. 出力されたExcelファイルに保存。

このプログラムのポイントは、既存のテンプレートを利用しながら、結合セルやスタイルを維持しつつデータを挿入している点です。

```java
package com.example.POIExample2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        // データを準備
        String[][] data = {
                {"Data1", "Data2"},
                {"Data10", "Data11"},
                {"Data19", "Data20"},
                {"Data28", "Data29"},
                {"Data37", "Data38"},
                {"Data46", "Data47"},
                {"Data55", "Data56"},
                {"Data64", "Data65"},
                {"Data73", "Data74"},
                {"Data82", "Data83"}
        };

        // テンプレートファイルを読み込み
        try (FileInputStream templateFile = new FileInputStream("template.xlsx");
             XSSFWorkbook workbook = new XSSFWorkbook(templateFile)) {

            XSSFSheet sheet = workbook.getSheetAt(0);

            // フォントとスタイル設定
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setBorderTop(BorderStyle.THIN);
            cellStyle.setBorderBottom(BorderStyle.THIN);
            cellStyle.setBorderLeft(BorderStyle.THIN);
            cellStyle.setBorderRight(BorderStyle.THIN);
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

            // 色を「自動」に設定（デフォルト色）
//            cellStyle.setTopBorderColor(IndexedColors.AUTOMATIC.getIndex());
//            cellStyle.setBottomBorderColor(IndexedColors.AUTOMATIC.getIndex());
//            cellStyle.setLeftBorderColor(IndexedColors.AUTOMATIC.getIndex());
//            cellStyle.setRightBorderColor(IndexedColors.AUTOMATIC.getIndex());
            cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
            cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
            cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
            cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());

            // 既存の結合セルを取得
            List<CellRangeAddress> existingMergedRegions = sheet.getMergedRegions();

            // データ挿入
            for (int i = 0; i < data.length; i++) {
                Row row;
                if (i < 5) {
                    row = sheet.getRow(i + 1);
                } else {
                    row = sheet.createRow(i + 1);
                }

                // A列からE列のデータを結合セルに設定
                CellRangeAddress rangeAtoE = new CellRangeAddress(i + 1, i + 1, 0, 4);
                if (!isRegionMerged(existingMergedRegions, rangeAtoE)) {
                    sheet.addMergedRegion(rangeAtoE);
                }
                for (int col = 0; col <= 4; col++) {
                    Cell cell = row.createCell(col);
                    cell.setCellStyle(cellStyle);
                }
                Cell cell1 = row.getCell(0);
                cell1.setCellValue(data[i][0]);

                // F列からI列のデータを結合セルに設定
                CellRangeAddress rangeFtoI = new CellRangeAddress(i + 1, i + 1, 5, 8);
                if (!isRegionMerged(existingMergedRegions, rangeFtoI)) {
                    sheet.addMergedRegion(rangeFtoI);
                }
                for (int col = 5; col <= 8; col++) {
                    Cell cell = row.createCell(col);
                    cell.setCellStyle(cellStyle);
                }
                Cell cell2 = row.getCell(5);
                cell2.setCellValue(data[i][1]);
            }

            // 列幅を自動調整
            for (int i = 0; i < 9; i++) {
                sheet.autoSizeColumn(i);
            }

            // ファイル出力
            try (FileOutputStream outputStream = new FileOutputStream("output.xlsx")) {
                workbook.write(outputStream);
            }

            System.out.println("Excelファイルを作成しました。");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 既存の結合セル範囲をチェック
    private static boolean isRegionMerged(List<CellRangeAddress> mergedRegions, CellRangeAddress newRegion) {
        for (CellRangeAddress region : mergedRegions) {
            if (region.formatAsString().equals(newRegion.formatAsString())) {
                return true;
            }
        }
        return false;
    }
}

```

template.xlsx
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/6b69b652-68e7-1170-68eb-9adaf0b3131b.png)


output.xlsx
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/945e2b45-1392-0ba9-9651-80af74ca5463.png)



以下に、主な部分を説明します。

#### 1. **データの準備**
```java
String[][] data = {
    {"Data1", "Data2"},
    {"Data10", "Data11"},
    ...
};
```
- この配列は、Excelに挿入するデータを定義しています。
- `data[i][0]`がA列からE列の結合セルに、`data[i][1]`がF列からI列の結合セルに配置されます。

---

#### 2. **テンプレートファイルの読み込み**
```java
try (FileInputStream templateFile = new FileInputStream("template.xlsx");
     XSSFWorkbook workbook = new XSSFWorkbook(templateFile)) {
```
- `template.xlsx`を読み込み、既存のフォーマット（結合セルや罫線など）を使用します。
- `XSSFWorkbook`はApache POIライブラリのクラスで、Excelの操作を可能にします。


#### 3. **スタイル設定**
```java
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setAlignment(HorizontalAlignment.CENTER);
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
```
- 罫線やテキストの配置（中央揃え）を指定します。
- このスタイルは、すべてのセルに適用されます。


#### 4. **既存の結合セルのチェック**
```java
List<CellRangeAddress> existingMergedRegions = sheet.getMergedRegions();
```
- テンプレートにすでに存在する結合セルを取得します。
- `isRegionMerged`メソッドで、新しく追加する結合セルが既存のセルと重複しないか確認します。

```java
private static boolean isRegionMerged(List<CellRangeAddress> mergedRegions, CellRangeAddress newRegion) {
    for (CellRangeAddress region : mergedRegions) {
        if (region.formatAsString().equals(newRegion.formatAsString())) {
            return true;
        }
    }
    return false;
}
```

#### 5. **データの挿入と結合セルの設定**
```java
CellRangeAddress rangeAtoE = new CellRangeAddress(i + 1, i + 1, 0, 4);
if (!isRegionMerged(existingMergedRegions, rangeAtoE)) {
    sheet.addMergedRegion(rangeAtoE);
}
for (int col = 0; col <= 4; col++) {
    Cell cell = row.createCell(col);
    cell.setCellStyle(cellStyle);
}
Cell cell1 = row.getCell(0);
cell1.setCellValue(data[i][0]);
```
- A列からE列を結合し、データを挿入します。
- 同様に、F列からI列も結合してデータを挿入します。
- 結合セル内のすべてのセルにスタイルを適用しています。


#### 6. **列幅の自動調整**
```java
for (int i = 0; i < 9; i++) {
    sheet.autoSizeColumn(i);
}
```
- 各列の幅を自動調整します。
- データが長い場合でも、列幅が適切に調整されて見やすくなります。

---

#### 7. **Excelファイルの出力**
```java
try (FileOutputStream outputStream = new FileOutputStream("output.xlsx")) {
    workbook.write(outputStream);
}
```
- `output.xlsx`という名前でExcelファイルを保存します。

## 2.9 セル位置ずらし
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/27c16bc1-1380-033f-8fd5-d7f230ab96a9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/838a8612-6966-b393-e73a-439b3b48ea50.png)

```java
package com.example.POIExample2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        String templatePath = "template.xlsx"; // テンプレートファイルのパス
        String outputPath = "output.xlsx"; // 出力ファイルのパス

        try (FileInputStream fis = new FileInputStream(templatePath);
             Workbook workbook = new XSSFWorkbook(fis)) {

            // シートを取得
            Sheet sheet = workbook.getSheetAt(0);

            // C20のセル結合（C列からE列までを結合）
            int firstRow = 19; // 20行目 (0ベース)
            int firstCol = 2;  // C列 (0ベース)
            int lastCol = 4;   // E列 (0ベース)

            // 結合セルを追加
            CellRangeAddress newMergedRegion = new CellRangeAddress(firstRow, firstRow, firstCol, lastCol);
            sheet.addMergedRegion(newMergedRegion);

            // C20セル（結合セルの左上）を取得
            Row row20 = sheet.getRow(firstRow);
            if (row20 == null) {
                row20 = sheet.createRow(firstRow);
            }
            Cell cellC20 = row20.getCell(firstCol);
            if (cellC20 == null) {
                cellC20 = row20.createCell(firstCol);
            }

            // C15のスタイルをコピーして新しいスタイルを作成
            Row row15 = sheet.getRow(14); // 15行目
            Cell cellC15 = row15.getCell(2); // C列
            CellStyle originalStyleC15 = cellC15.getCellStyle(); // スタイルを取得

            // 新しいスタイルを作成
            CellStyle newStyle = workbook.createCellStyle();
            newStyle.cloneStyleFrom(originalStyleC15); // 元のスタイルをコピー
            newStyle.setAlignment(HorizontalAlignment.CENTER); // 水平中央揃え
            newStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直中央揃え

            // C20セルにスタイルを適用
            cellC20.setCellStyle(newStyle);

            // C20セルにC15の値を設定
            cellC20.setCellValue(cellC15.getStringCellValue());

            // 結合セル内の他のセルにもスタイルを適用（罫線のため）
            for (int col = firstCol + 1; col <= lastCol; col++) {
                Cell cell = row20.getCell(col);
                if (cell == null) {
                    cell = row20.createCell(col);
                }
                cell.setCellStyle(newStyle); // スタイルを適用
            }

            // ファイルに保存
            try (FileOutputStream fos = new FileOutputStream(outputPath)) {
                workbook.write(fos);
            }

            System.out.println("セル結合と値設定が完了しました: " + outputPath);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

```



## 3 ページ設定
JavaでApache POIを使用してExcelの印刷設定を行う際に、縮小/拡大設定（`setZoom`）と「次のページ数に合わせて印刷」（`setFitToPage`）は排他的であり、同時に使用することはできません。

### 理由
- `setZoom` はズーム倍率（拡大/縮小）を設定するためのもので、具体的な倍率（例: 80%）を指定できます。
- `setFitToPage` は印刷時に特定のページ数に収まるようにスケーリングする設定で、ズーム倍率を動的に計算するため、手動設定したズーム倍率（`setZoom`）が無効になります。

### 要件を満たす方法
もし「次のページ数に合わせて印刷」で「横: 1、縦: 空欄（未設定）」を実現したい場合は、以下の手順で設定できます。

### 3.1 次のページ数に合わせて印刷
#### サンプルコード
```java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;

public class ExcelPrintSetup {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("Sample Sheet");

            // 印刷設定を取得
            PrintSetup printSetup = sheet.getPrintSetup();

            // 次のページ数に合わせて印刷を設定（横: 1ページに収める）
            printSetup.setFitWidth((short) 1); // 横: 1ページ
            printSetup.setFitHeight((short) 0); // 縦: 空欄（制限なし）

            // ズーム倍率は設定しない（排他的）
            sheet.setZoom(100); // 任意の倍率を設定しても無効になる

            // サンプルデータ
            for (int i = 0; i < 100; i++) {
                Row row = sheet.createRow(i);
                for (int j = 0; j < 10; j++) {
                    Cell cell = row.createCell(j);
                    cell.setCellValue("Sample Data " + (i + 1) + "," + (j + 1));
                }
            }

            // Excelファイルを書き出し
            try (FileOutputStream fileOut = new FileOutputStream("output.xlsx")) {
                workbook.write(fileOut);
            }
            System.out.println("Excelファイルが作成されました！");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

### 重要なポイント
- **`setZoom`と`setFitToPage`は同時に有効にできない**: 「次のページ数に合わせて印刷」を設定するとズーム設定は無効になります。
- **`setFitWidth`と`setFitHeight`を使用**: `setFitWidth`で横方向のページ数を指定し、`setFitHeight`を0にすると縦方向に制限がかからない設定になります。
  
この設定で、指定通りのページ分割が可能になりますが、印刷時の見た目の調整はExcel上で確認する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/45ad9f97-59a2-c8db-0589-0e27a7d23045.png)

## 3.2 倍率設定
`sheet.setZoom(80)`はExcelの表示倍率（ズーム倍率）を設定するもので、印刷設定の拡大縮小（スケーリング）とは異なる機能です。このため、印刷設定で倍率を変更するには、`PrintSetup`を使用して適切なスケーリングを設定する必要があります。

以下に修正したサンプルコードを示します。

---

### 修正版サンプルコード
```java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;

public class ExcelPrintSettings {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("Sample Sheet");

            // 印刷設定を取得
            PrintSetup printSetup = sheet.getPrintSetup();

            // 印刷の向きを横に設定
            printSetup.setLandscape(true);

            // 拡大縮小印刷（倍率80%）を設定
            printSetup.setScale((short) 80);

            // 用紙サイズをA4に設定
            printSetup.setPaperSize(PrintSetup.A4_PAPERSIZE);

            // サンプルデータを作成
            for (int i = 0; i < 50; i++) {
                Row row = sheet.createRow(i);
                for (int j = 0; j < 10; j++) {
                    Cell cell = row.createCell(j);
                    cell.setCellValue("Data " + (i + 1) + "," + (j + 1));
                }
            }

            // Excelファイルを書き出し
            try (FileOutputStream fileOut = new FileOutputStream("print_settings_corrected_output.xlsx")) {
                workbook.write(fileOut);
            }
            System.out.println("Excelファイルが作成されました！");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

---

### 変更点
1. **印刷設定での拡大縮小倍率（`setScale`）を設定**:
   ```java
   printSetup.setScale((short) 80);
   ```
   このコードは、印刷時のスケーリング倍率を設定します。80%に設定することで、印刷時の拡大縮小が適用されます。

2. **`sheet.setZoom`**:
   `setZoom`はExcelの画面表示倍率を設定するものであり、印刷倍率とは無関係です。このコードでは削除しました。

---

### 出力結果
実行後に生成されるExcelファイルを確認すると、以下の設定が反映されます。
- **印刷の向き**: 横
- **印刷時の拡大縮小倍率**: 80%
- **用紙サイズ**: A4

もし反映されない場合やその他の問題があれば教えてください！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/942abf54-ac20-ca51-8176-6aecb90e50ba.png)



## 3.3 改ページ、ページ複製
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/90ef45a8-e3a6-6add-68cd-00467763261d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/86966ddf-de5e-3077-b17d-510829eff4f1.png)
```java
package com.example.POIExample2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        String templatePath = "template2.xlsx"; // テンプレートファイルのパス
        String outputPath = "ExcelWithModifiedPages.xlsx"; // 出力ファイルのパス

        try (FileInputStream fileInputStream = new FileInputStream(templatePath);
             XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream)) {

            XSSFSheet sheet = workbook.getSheetAt(0);

            int rowsPerPage = 26; // 1ページあたりの行数
            int totalPages = 3;   // 作成するページ数
            int startRowToModify = 7; // データを変更する開始行（8行目のインデックス）
            int endRowToModify = 15;  // データを変更する終了行（16行目のインデックス）

            // 新しいデータセット（各ページの8-16行目を置き換えるデータ）
            String[][] page2NewData = {
                    {"Page 2 Data 1-1", "Page 2 Data 1-2", "Page 2 Data 1-3", "Page 2 Data 1-4", "Page 2 Data 1-5"},
                    {"Page 2 Data 2-1", "Page 2 Data 2-2", "Page 2 Data 2-3", "Page 2 Data 2-4", "Page 2 Data 2-5"},
                    {"Page 2 Data 3-1", "Page 2 Data 3-2", "Page 2 Data 3-3", "Page 2 Data 3-4", "Page 2 Data 3-5"},
                    {"Page 2 Data 4-1", "Page 2 Data 4-2", "Page 2 Data 4-3", "Page 2 Data 4-4", "Page 2 Data 4-5"},
                    {"Page 2 Data 5-1", "Page 2 Data 5-2", "Page 2 Data 5-3", "Page 2 Data 5-4", "Page 2 Data 5-5"},
                    {"Page 2 Data 6-1", "Page 2 Data 6-2", "Page 2 Data 6-3", "Page 2 Data 6-4", "Page 2 Data 6-5"},
                    {"Page 2 Data 7-1", "Page 2 Data 7-2", "Page 2 Data 7-3", "Page 2 Data 7-4", "Page 2 Data 7-5"},
                    {"Page 2 Data 8-1", "Page 2 Data 8-2", "Page 2 Data 8-3", "Page 2 Data 8-4", "Page 2 Data 8-5"},
                    {"Page 2 Data 9-1", "Page 2 Data 9-2", "Page 2 Data 9-3", "Page 2 Data 9-4", "Page 2 Data 9-5"}
            };

            String[][] page3NewData = {
                    {"Page 3 Data 1-1", "Page 3 Data 1-2", "Page 3 Data 1-3", "Page 3 Data 1-4", "Page 3 Data 1-5"},
                    {"Page 3 Data 2-1", "Page 3 Data 2-2", "Page 3 Data 2-3", "Page 3 Data 2-4", "Page 3 Data 2-5"},
                    {"Page 3 Data 3-1", "Page 3 Data 3-2", "Page 3 Data 3-3", "Page 3 Data 3-4", "Page 3 Data 3-5"},
                    {"Page 3 Data 4-1", "Page 3 Data 4-2", "Page 3 Data 4-3", "Page 3 Data 4-4", "Page 3 Data 4-5"},
                    {"Page 3 Data 5-1", "Page 3 Data 5-2", "Page 3 Data 5-3", "Page 3 Data 5-4", "Page 3 Data 5-5"},
                    {"Page 3 Data 6-1", "Page 3 Data 6-2", "Page 3 Data 6-3", "Page 3 Data 6-4", "Page 3 Data 6-5"},
                    {"Page 3 Data 7-1", "Page 3 Data 7-2", "Page 3 Data 7-3", "Page 3 Data 7-4", "Page 3 Data 7-5"},
                    {"Page 3 Data 8-1", "Page 3 Data 8-2", "Page 3 Data 8-3", "Page 3 Data 8-4", "Page 3 Data 8-5"},
                    {"Page 3 Data 9-1", "Page 3 Data 9-2", "Page 3 Data 9-3", "Page 3 Data 9-4", "Page 3 Data 9-5"}
            };

            // 各ページごとのデータリスト
            String[][][] newDataList = {null, page2NewData, page3NewData}; // 1ページ目は新規データ不要

            // 各ページを生成
            for (int page = 1; page < totalPages; page++) {
                int offset = page * rowsPerPage; // ページごとの開始行オフセット

                // 改ページを挿入（複製した行の手前）
                sheet.setRowBreak(offset - 1);
                
                // 1ページ目をコピー
                for (int rowNum = 0; rowNum < rowsPerPage; rowNum++) {
                    Row sourceRow = sheet.getRow(rowNum);
                    Row targetRow = sheet.createRow(offset + rowNum);

                    if (sourceRow != null) {
                        copyRow(sourceRow, targetRow,sheet);
                    }
                }

                // 8-16行目を新規データに置き換え
                String[][] currentPageNewData = newDataList[page];
                if (currentPageNewData != null) {
                    for (int row = startRowToModify; row <= endRowToModify; row++) {
                        Row targetRow = sheet.getRow(offset + row);
                        for (int col = 0; col < currentPageNewData[row - startRowToModify].length; col++) {
                            Cell cell = targetRow.getCell(col);
                            if (cell == null) {
                                cell = targetRow.createCell(col);
                            }
                            cell.setCellValue(currentPageNewData[row - startRowToModify][col]);
                        }
                    }
                }

                // 改ページを挿入
                sheet.setRowBreak(offset + rowsPerPage - 1);
            }

            // ファイル出力
            try (FileOutputStream fileOutputStream = new FileOutputStream(outputPath)) {
                workbook.write(fileOutputStream);
            }

            System.out.println("Excelファイルを生成しました: " + outputPath);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 行をコピーするメソッド
    private static void copyRow(Row sourceRow, Row targetRow, Sheet sheet) {
        // 行の高さをコピー
        targetRow.setHeight(sourceRow.getHeight());

        for (int colIndex = 0; colIndex < sourceRow.getLastCellNum(); colIndex++) {
            Cell sourceCell = sourceRow.getCell(colIndex);
            Cell targetCell = targetRow.createCell(colIndex);

            if (sourceCell != null) {
                // セルの値をコピー
                switch (sourceCell.getCellType()) {
                    case STRING:
                        targetCell.setCellValue(sourceCell.getStringCellValue());
                        break;
                    case NUMERIC:
                        targetCell.setCellValue(sourceCell.getNumericCellValue());
                        break;
                    case BOOLEAN:
                        targetCell.setCellValue(sourceCell.getBooleanCellValue());
                        break;
                    case FORMULA:
                        targetCell.setCellFormula(sourceCell.getCellFormula());
                        break;
                    default:
                        break;
                }

                // セルのスタイルをコピー
                CellStyle newCellStyle = sourceCell.getSheet().getWorkbook().createCellStyle();
                newCellStyle.cloneStyleFrom(sourceCell.getCellStyle());
                targetCell.setCellStyle(newCellStyle);
            }
        }

        // 列幅をコピー
        for (int colIndex = 0; colIndex < sourceRow.getLastCellNum(); colIndex++) {
            sheet.setColumnWidth(colIndex, sheet.getColumnWidth(colIndex));
        }
    }
}

```


## 4.ページ番号
### 4.1 フッター表示
ページ番号を表示するためには、`&P`（現在のページ番号）および`&N`（総ページ数）というプレースホルダーを使用する必要があります。以下に修正版のコードを示します。

```java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;
import java.io.IOException;

public class ExcelPageNumberExample {
    public static void main(String[] args) {
        // ワークブックを作成
        Workbook workbook = new XSSFWorkbook();

        // シートを作成
        Sheet sheet = workbook.createSheet("Sample Sheet");

        // データを追加（オプション）
        for (int i = 0; i < 100; i++) {
            Row row = sheet.createRow(i);
            Cell cell = row.createCell(0);
            cell.setCellValue("Row " + (i + 1));
        }

        // ページ設定: フッターにページ番号を設定
        Footer footer = sheet.getFooter();
        footer.setCenter("Page &P of &N"); // &P: 現在のページ番号, &N: 総ページ数

        // ファイルに出力
        try (FileOutputStream fileOut = new FileOutputStream("ExcelWithPageNumbers.xlsx")) {
            workbook.write(fileOut);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // ワークブックを閉じる
        try {
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Excelファイルが作成されました。");
    }
}
```
#### 主なポイント
1. **`&P` と `&N` の使用**
   - `&P` は現在のページ番号を意味します。
   - `&N` は総ページ数を意味します。
   - これらはExcelでの動的なプレースホルダーであり、Apache POIを通じてフッターやヘッダーに設定可能です。

2. **`getFooter()` を使用**
   - `Sheet`オブジェクトの`getFooter()`または`getHeader()`を呼び出して設定します。

#### 結果
作成されたExcelファイルを開くと、フッターに「Page 1 of X」という形式でページ番号が表示されます（Excelが自動的に計算します）。これでエラーが解消されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/7c1d6292-b66a-c42d-431a-47205d850929.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/c14f58bd-8374-cd48-2b33-1d421e2ba3f8.png)


### 4.2先頭ページ番号１を表示される(新規シート作成)

```java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageSetup;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;

import java.io.FileOutputStream;
import java.io.IOException;

public class ExcelPageNumberExample {
    public static void main(String[] args) {
        // ワークブックを作成
        XSSFWorkbook workbook = new XSSFWorkbook();

        // シートを作成
        XSSFSheet sheet = workbook.createSheet("Sample Sheet");

        // データを追加
        for (int i = 0; i < 100; i++) {
            Row row = sheet.createRow(i);
            Cell cell = row.createCell(0);
            cell.setCellValue("Row " + (i + 1));
        }

        // フッターにページ番号を設定
        Footer footer = sheet.getFooter();
        footer.setCenter("Page &P of &N"); // &P: 現在のページ番号, &N: 総ページ数

        // ページ設定の準備
        CTWorksheet ctWorksheet = sheet.getCTWorksheet();
        CTPageSetup pageSetup = ctWorksheet.isSetPageSetup() ? ctWorksheet.getPageSetup() : ctWorksheet.addNewPageSetup();
        pageSetup.setFirstPageNumber(1); // 先頭ページ番号を1に設定
        pageSetup.setUseFirstPageNumber(true); // 先頭ページ番号を使用

        // 印刷設定
        PrintSetup printSetup = sheet.getPrintSetup();
        printSetup.setPaperSize(PrintSetup.A4_PAPERSIZE); // A4用紙
        printSetup.setLandscape(false); // 縦向き
        sheet.setAutobreaks(true); // 自動改ページを有効化

        // ファイルに出力
        try (FileOutputStream fileOut = new FileOutputStream("ExcelWithPageNumbers.xlsx")) {
            workbook.write(fileOut);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // ワークブックを閉じる
        try {
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Excelファイルが作成されました。");
    }
}
```

1. **`CTPageSetup`の明示的な作成**:
   - `ctWorksheet.addNewPageSetup()`を使用して、`CTPageSetup`を新たに作成します。
   - 既に存在している場合は`getPageSetup()`を使用します。

2. **`setUseFirstPageNumber(true)`の追加**:
   - `setFirstPageNumber()`を適用するために、このプロパティを有効化します。

3. **`Footer`のページ番号設定**:
   - ページ番号がフッターに正しく表示されるように、`Footer`に`Page &P of &N`を設定しました。

#### 実行結果

このコードを実行すると、次のような成果物が得られます：

1. **ページ設定**:
   - 「先頭ページ番号」が「1」に設定されます。

2. **ページプレビュー**:
   - フッターに「Page 1 of X」という形式でページ番号が表示されます。

3. **ファイル名**:
   - 出力ファイルは`ExcelWithPageNumbers.xlsx`となります。

---


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/c6861676-1396-3f58-cc1e-5400e45c798e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/3ef769eb-b8f0-d962-0536-59acc10a9802.png)

## 4.3 既存シートのコピーの場合１
シートコピーで引き継がれなかった。
```java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class ExcelTemplateExample {
    public static void main(String[] args) {
        String templatePath = "template.xlsx"; // テンプレートファイルのパス
        String outputPath = "output.xlsx";     // 出力ファイルのパス

        try (FileInputStream fis = new FileInputStream(templatePath);
             Workbook workbook = new XSSFWorkbook(fis)) {

            // テンプレートから最初のシートを取得
            Sheet templateSheet = workbook.getSheetAt(0);

            // テンプレートの既存シート名を変更（重複を回避）
            workbook.setSheetName(0, "TemplateSheet");

            // ユニークな名前のシートを作成
            for (int i = 1; i <= 3; i++) {
                String sheetName = "Sheet" + i;

                // シート名の重複を確認
                if (workbook.getSheet(sheetName) != null) {
                    System.out.println("Sheet with name " + sheetName + " already exists!");
                    continue;
                }

                // 新しいシートを作成してテンプレート設定を引き継ぐ
                Sheet newSheet = workbook.createSheet(sheetName);
                copySheetSettings(templateSheet, newSheet);

                // 新しいシートにデータを追加
                Row row = newSheet.createRow(0);
                Cell cell = row.createCell(0);
                cell.setCellValue("This is sheet " + i);
            }

            // 出力ファイルに保存
            try (FileOutputStream fos = new FileOutputStream(outputPath)) {
                workbook.write(fos);
            }

            System.out.println("Excel file created successfully!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // シート設定をコピーするメソッド
    private static void copySheetSettings(Sheet sourceSheet, Sheet targetSheet) {
        // ページ設定をコピー
        PrintSetup sourcePrintSetup = sourceSheet.getPrintSetup();
        PrintSetup targetPrintSetup = targetSheet.getPrintSetup();

        targetPrintSetup.setPaperSize(sourcePrintSetup.getPaperSize());
        targetPrintSetup.setLandscape(sourcePrintSetup.getLandscape());
        targetPrintSetup.setHeaderMargin(sourcePrintSetup.getHeaderMargin());
        targetPrintSetup.setFooterMargin(sourcePrintSetup.getFooterMargin());
        targetPrintSetup.setPageStart(sourcePrintSetup.getPageStart()); // 先頭ページ番号をコピー

        // マージンのコピー
        targetSheet.setMargin(Sheet.TopMargin, sourceSheet.getMargin(Sheet.TopMargin));
        targetSheet.setMargin(Sheet.BottomMargin, sourceSheet.getMargin(Sheet.BottomMargin));
        targetSheet.setMargin(Sheet.LeftMargin, sourceSheet.getMargin(Sheet.LeftMargin));
        targetSheet.setMargin(Sheet.RightMargin, sourceSheet.getMargin(Sheet.RightMargin));

        // ヘッダー/フッターのコピー
        Header sourceHeader = sourceSheet.getHeader();
        Header targetHeader = targetSheet.getHeader();
        targetHeader.setCenter(sourceHeader.getCenter());
        targetHeader.setLeft(sourceHeader.getLeft());
        targetHeader.setRight(sourceHeader.getRight());

        Footer sourceFooter = sourceSheet.getFooter();
        Footer targetFooter = targetSheet.getFooter();
        targetFooter.setCenter(sourceFooter.getCenter());
        targetFooter.setLeft(sourceFooter.getLeft());
        targetFooter.setRight(sourceFooter.getRight());
    }
}
```
1. **先頭ページ番号のコピー**:
   `PrintSetup#setPageStart` メソッドを使用して、コピー元のシートと同じ先頭ページ番号を設定しています。

2. **その他の設定引き継ぎ**:
   ページサイズ、向き、マージン、ヘッダー、フッターなどの設定も引き継いでいます。

3. **エラーハンドリング**:
   シート名の重複を回避するロジックを追加しています。

元のシート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/d4a41217-d852-b22e-0638-44bfc0e68aeb.png)

シート複製シート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/c28922b4-65aa-3ad4-ac68-888a987a514d.png)

## 4.4 既存シートのコピーの場合２
先頭ページは引き継がれない テストで番号は出ていることを印刷プレビューで確認
以下は、フッターにページ番号を設定して1ページから表示される場合のテスト例です。


```java
package com.example.POIExample2;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.PrintSetup;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        String templatePath = "template.xlsx"; // テンプレートファイルのパス
        String outputPath = "output.xlsx";     // 出力ファイルのパス

        try (FileInputStream fis = new FileInputStream(templatePath);
             Workbook workbook = new XSSFWorkbook(fis)) {

            // テンプレートから最初のシートを取得
            Sheet templateSheet = workbook.getSheetAt(0);

            // テンプレートの既存シート名を変更（重複を回避）
            workbook.setSheetName(0, "TemplateSheet");

            // ユニークな名前のシートを作成
            for (int i = 1; i <= 3; i++) {
                String sheetName = "Sheet" + i;

                // シート名の重複を確認
                if (workbook.getSheet(sheetName) != null) {
                    System.out.println("Sheet with name " + sheetName + " already exists!");
                    continue;
                }

                // 新しいシートを作成してテンプレート設定を引き継ぐ
                Sheet newSheet = workbook.createSheet(sheetName);
                copySheetSettings(templateSheet, newSheet);

             // フッターにページ番号を設定
                Footer footer = newSheet.getFooter();
                footer.setCenter("Page &P of &N");
                // テストデータを追加
                addTestData(newSheet, i);
            }

            // 出力ファイルに保存
            try (FileOutputStream fos = new FileOutputStream(outputPath)) {
                workbook.write(fos);
            }

            System.out.println("Excel file created successfully! Please check the page numbers in Print Preview.");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // シート設定をコピーするメソッド
    private static void copySheetSettings(Sheet sourceSheet, Sheet targetSheet) {
        // ページ設定をコピー
        PrintSetup sourcePrintSetup = sourceSheet.getPrintSetup();
        PrintSetup targetPrintSetup = targetSheet.getPrintSetup();

        targetPrintSetup.setPaperSize(sourcePrintSetup.getPaperSize());
        targetPrintSetup.setLandscape(sourcePrintSetup.getLandscape());
        targetPrintSetup.setHeaderMargin(sourcePrintSetup.getHeaderMargin());
        targetPrintSetup.setFooterMargin(sourcePrintSetup.getFooterMargin());

        // マージンのコピー
        targetSheet.setMargin(Sheet.TopMargin, sourceSheet.getMargin(Sheet.TopMargin));
        targetSheet.setMargin(Sheet.BottomMargin, sourceSheet.getMargin(Sheet.BottomMargin));
        targetSheet.setMargin(Sheet.LeftMargin, sourceSheet.getMargin(Sheet.LeftMargin));
        targetSheet.setMargin(Sheet.RightMargin, sourceSheet.getMargin(Sheet.RightMargin));
    }

    // テストデータを追加するメソッド
    private static void addTestData(Sheet sheet, int sheetIndex) {
        for (int rowIndex = 0; rowIndex < 100; rowIndex++) { // 100行データを追加
            Row row = sheet.createRow(rowIndex);
            for (int colIndex = 0; colIndex < 5; colIndex++) { // 5列データを追加
                Cell cell = row.createCell(colIndex);
                cell.setCellValue("Sheet " + sheetIndex + " - Row " + (rowIndex + 1) + ", Col " + (colIndex + 1));
            }
        }
    }
}

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/891bcd3a-aa5b-4ff6-7da2-5856c466b35e.png)

## 5.設定確認例
### 5.1 配置
```java
package com.example.POIExample2;

import java.io.FileInputStream;
import java.io.IOException;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class App {
    public static void main(String[] args) {
        String filePath = "sample.xlsx"; // 読み込むExcelファイルのパス

        try (FileInputStream fis = new FileInputStream(filePath);
             Workbook workbook = new XSSFWorkbook(fis)) {

            // シートを取得
            Sheet sheet = workbook.getSheetAt(0);

            // 1行目、1列目のセルを取得 (インデックスは0ベース)
            Row row = sheet.getRow(0);
            if (row != null) {
                Cell cell = row.getCell(0);
                if (cell != null) {
                    // セルスタイルを取得
                    CellStyle cellStyle = cell.getCellStyle();

                    // 水平配置を確認
                    HorizontalAlignment horizontalAlignment = cellStyle.getAlignment();
                    System.out.println("水平配置: " + horizontalAlignment);

                    // 垂直配置を確認
                    VerticalAlignment verticalAlignment = cellStyle.getVerticalAlignment();
                    System.out.println("垂直配置: " + verticalAlignment);
                } else {
                    System.out.println("セルが見つかりません");
                }
            } else {
                System.out.println("行が見つかりません");
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

```
(結果例)
水平配置: GENERAL
垂直配置: BOTTOM