【Unity】System.Drawing.dll で実現するプリンター 印刷制御ガイド 入門 

# はじめに
本記事では、.NET Framework に含まれる **System.Drawing.dll** を活用して、プリンター
で印刷を行う方法を解説します。今回の記事を通して、簡単なテキストや画像を印刷出来るようになります。

:::note warn
本記事で紹介する方法はWindows環境でのみ動作します。macOS、Linux、iOS、Androidなどの他のプラットフォームでは動作しませんのでご注意ください。これはSystem.Drawing.dllがWindows特有のライブラリであるためです。
:::

# 対象読者
- Unityアプリを介して、プリンターを印刷制御をしたい方
- System.Drawing.dll の役割を理解したい方

# 環境
- Windows 10
- Unity 2022.3.36f1
- EPSON TM-T88VII (レシートプリンター）

# プリンターの設定準備
前提として、Windows PCとプリンターが接続済みで、プリンター側が印刷ジョブを受けた時、印刷を問題なく出来ることとします。

忘れずに、``コントロールパネル > デバイスとプリンター`` で使用するプリンタ-を**通常使うプリンターに設定**してください。また、**プリンターのプロパティからテストページの印刷**を行い、印刷されるか確認してください。

これらの設定と印刷が正しくなければ、Unity側の実装でプリントリクエストをしても**印刷されません**。

（印刷出来ない場合は、PCとプリンターの接続を確認することや、プリンタードライバーをアンインストールしてから再度インストールするなど、各対応をしてください。）

参考までに、今回の環境では以下の画像のような設定と印刷テストが完了している状態です。

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786602/2c86f267-8d4a-4be6-a811-69583635ad49.png">

これらの準備が出来たら、プリンタ側の準備は完了です。

Unityの実装について紹介する前に、今回使用するSystem.Drawing.dll プラグインの役割と制御フローについて説明します。

# System.Drawing.dll プラグインの役割
System.Drawing.dll は、Windows環境でグラフィック処理と印刷機能を提供する重要なライブラリです。このプラグインの主な機能は以下の通りです。
- グラフィックオブジェクトの作成と操作
- テキスト、図形、画像の描画
- Windows印刷システムへのアクセス
- 印刷ジョブの設定と制御 

今回の印刷フローは以下のようになります。

```mermaid
graph LR
    A[Unity アプリケーション] --> B[System.Drawing.dll]
    B --> C[Windows 印刷 API]
    C --> D[プリンタードライバー]
    D --> E[物理プリンター]
```
Unityアプリケーションは、System.Drawing.dllを介してWindows印刷APIにアクセスし、プリンタードライバーを通じて物理プリンターへ印刷命令を送ります。このプロセスによって、Unity内で作成したコンテンツを実際の印刷物として出力することが可能になります。

これらのことを念頭に置いて、実際に実装していきます。

# Unity側の実装手順
1. System.Drawing.dll プラグインを取得
2. System.Drawing.dll プラグインをプロダクトに追加 
3. Unity Project Settings の設定
4. プリンターで印刷するためのクラス実装


## 1. System.Drawing.dll プラグインを取得

Unityの環境とバージョンとの互換性が保証されているものを取得するために、下記のアドレスにGoogleChromeなどのブラウザで検索してアクセスしてください。
(``2022.3.36f1``の箇所は、使用しているUnityのバージョンに書き換えてください。)
~~~
C:\Program Files\Unity\Hub\Editor\2022.3.36f1\Editor\Data\MonoBleedingEdge\lib\mono\4.5
~~~
アクセスすると、ダウンロード可能な``.dll``が表示されます。一覧の中から、``System.Drawing.dll``をクリックしてダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786602/bf9b84bb-cb91-4827-b04e-426adb596e99.png)

もしくは接尾に、``\System.Drawing.dll``を追加してダウンロードして頂いても大丈夫です。
~~~
C:\Program Files\Unity\Hub\Editor\2022.3.36f1\Editor\Data\MonoBleedingEdge\lib\mono\4.5\System.Drawing.dll
~~~

## 2. System.Drawing.dll プラグインをプロダクトに追加
``Assets``フォルダ直下に``Plugins``フォルダを作成して、取得したプラグインをプロダクトに追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786602/f196d75d-b53d-4dda-8486-5198131b0342.png)

## 3. Unity Project Settings の設定

``Project Settings > Player > Other Settings > Configuration > Scripting Backend`` を「**Mono**」に設定。``Api Compatibility Level``を「**.NET Framework**」 に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786602/e0bd9bda-0bc3-45b9-b265-298a610b9b5b.png)

## 4. プリンターで印刷するためのクラス実装
今回の実装で使用する、主要なクラスについて記載します。
### [PrintDocument](https://learn.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.printdocument?view=windowsdesktop-9.0&viewFallbackFrom=dotnet-plat-ext-8.0)
> Windows フォーム アプリケーションからの印刷時にプリンターに出力を送信する再利用可能なオブジェクトを定義します。 

PrintDocumentインスタンスを作成し、PrinterSettingsのプロパティであるDocumentNameの設定などを行います。そして、Printメソッド呼び出して印刷プロセスを開始します。

### [PrintPageEventHandler](https://learn.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.printpageeventhandler?view=windowsdesktop-9.0)
>PrintPage の PrintDocument イベントを処理するメソッドを表します。

PrintPageイベントを処理するデリゲートです。印刷するページの内容を定義するために使用します。このイベントハンドラーは、ページが印刷される直前に呼び出され、ページの描画内容を指定できます。

### [PrintPageEventArgs](https://learn.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.printpageeventargs?view=windowsdesktop-9.0)
> PrintPage イベントのデータを提供します。

PrintPageイベントのデータを提供するクラスです。このクラスからGraphicsオブジェクトを取得し、そのGraphicsオブジェクトを使用してページに描画します。また、HasMorePagesプロパティを設定することで、複数ページの印刷を制御することもできます。

### [Graphics](https://learn.microsoft.com/ja-jp/dotnet/api/system.drawing.graphics?view=windowsdesktop-9.0)
Graphicsクラスは描画操作を表し、テキストや図形、画像などをプリンターや画面に描画するためのメソッドを提供します。PrintPageEventArgsから取得したGraphicsオブジェクトを使用して、印刷内容を定義します。


### 実装例
今回の例では、簡単なテキストと画像を印刷機能を Priter.cs に実装します。

~~~C#:Printer.cs
using UnityEngine;
using System.Drawing;
using System.Drawing.Printing;

public class Printer : MonoBehaviour
{
    void Start()
    {
        Print();
    }

    public void Print()
    {
        try
        {
            PrintDocument printDocument = new PrintDocument();
            printDocument.DocumentName = "My Print Document";
            printDocument.PrintPage += new PrintPageEventHandler(PrintTextPage);
            printDocument.Print();
        }
        catch (System.Drawing.Printing.InvalidPrinterException ex)
        {
            Debug.LogError("プリンターが見つからないか、アクセスできません: " + ex.Message);
        }
        catch (System.Exception ex)
        {
            Debug.LogError("印刷中にエラーが発生しました: " + ex.Message);
        }
    }

    /// <summary>
    /// PrintDocumentのPrintPageイベントを処理するメソッド
    /// </summary>
    /// <param name="sender">イベントのソース</param>
    /// <param name="e">PrintPage イベントのデータ（印刷に必要な情報を含む）</param>
    public void PrintTextPage(object sender, PrintPageEventArgs e)
    {
        // ページの描画に使用される Graphics を取得
        System.Drawing.Graphics graphics = e.Graphics;
        // フォントを設定（Arial, 12ポイント）
        System.Drawing.Font font = new System.Drawing.Font("Arial", 12);
        // Graphics.DrawString
        // - 第1引数: 描画するテキスト
        // - 第2引数: 使用するフォント
        // - 第3引数: テキストの色（Brushesクラスから選択）
        // - 第4引数: X座標（左からの距離）
        // - 第5引数: Y座標（上からの距離）
        graphics.DrawString("HELLO WORLD", font, Brushes.Black, 10, 10);
        // HasMorePagesプロパティがtrueの場合、次のページも印刷される
        // デフォルトはfalseで、1ページのみ印刷
        e.HasMorePages = false;
    }


    // 画像を印刷したい場合の例
    public void PrintImagePage(object sender, PrintPageEventArgs e)
    {
        System.Drawing.Graphics graphics = e.Graphics;
        
        // 画像取得
        string imagePath = Application.streamingAssetsPath + "/SampleImage.png";
        Image image = Image.FromFile(imagePath);
        // 画像描画
        float drawImageWidth = 200f;
        float drawImageHeight = 500f;
        graphics.DrawImage(image, 0, 0, drawImageWidth, drawImageHeight);
    }
}

~~~

#### 印刷処理の流れ
1. `Print()`メソッドが呼び出されると、`PrintDocument`インスタンスが作成され、印刷ジョブ名が設定されます。
2. `PrintPage`イベントに`PrintTextPage`ハンドラーが登録されます。
3. `Print()`メソッド呼び出しで印刷プロセスが開始されます。
4. 印刷が開始されると、登録された`PrintTextPage`メソッドが自動的に呼び出されます。
5. `PrintTextPage`内では、`Graphics`オブジェクトを使用してテキストや画像を描画し、それが印刷されます。

#### 印刷結果と印刷ジョブの確認
Secen上の適当なオブジェクトにPrinterクラスをアタッチして実行すると、レシートプリンターから印刷されることを確認できました。

<img width = 200 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786602/8f17b298-1998-484c-9047-a80bc41692db.jpeg">

また、印刷ジョブの表示から実際にドキュメント名が「My Print Document」に変更されていることも確認できます。

<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786602/a903a63a-b92a-4d21-9075-7735b213219d.png">

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786602/6fc79477-3e60-43c8-87a0-002a86c3cd8f.png)


## まとめ
本記事では、UnityからSystem.Drawing.dllを使用してWindows上のプリンタを制御する方法を解説しました。より詳細なエラーハンドリング実装なども確認したいところですが、基礎的なことを押さえることができたと思います。

また、簡単なテキストや画像を印刷する方法のみの紹介でしたが、ドット絵などの描画を表現して印刷できたりもします。

もっと色々な表現ができそうなので探求したいです！
楽しいUnity開発をはじめましょう！

# 参考文献
https://docs.unity3d.com/ja/current/Manual/Plugins.html

https://learn.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.printdocument?view=windowsdesktop-9.0&viewFallbackFrom=dotnet-plat-ext-8.0

https://learn.microsoft.com/ja-jp/dotnet/api/system.drawing.graphics?view=windowsdesktop-9.0

https://qiita.com/kumi0708/items/bea239baedd31f2eabd5