- Products -


Software


NEWS


Special



- Knowledge -


Category × Tag



- Like -

公開
作成日:2021/11/19
更新日:2021/11/24

【C#/.NetCore】決定版!PDFを印刷する方法。ユーザー定義用紙にも対応。

・プログラムからPDFを印刷する。といった処理を実装した時に、参考になる情報があまり無かったので、以下にまとめる。

・一般的な用紙サイズ(A4やB5といったもの)であれば、簡単に実装できる。

・切手判や美濃版といった一般的では無い用紙サイズ(ユーザー定義用紙に分類されるもの)に印刷するには工夫が必要となる。

結論


・前提:印刷したいPDFファイルがある。

・実装手順は以下の通り。

①プログラムでPDFファイルを開き、用紙サイズを取得する。(言語:C#、ライブラリ:PDFSharp)

②取得した用紙サイズでPDFを画像ファイルに変換する。(ツール:GhostScript)

③画像ファイルを描画しプリンタに送信し印刷する。(言語:C#、ライブラリ:.NetFramework標準機能)


この結論に行きつくまでの試行錯誤



はじまり


・PDFファイルの自動印刷を実装したい。言語や手段は問わない。

・WEBで検索。

・これだ!という手順はあまり確立されていないようだ、いくつかの案がある。

・Adobe Acrobat Reader:
コマンドラインで実行し、引数で諸々設定するのが基本のよう。ただし、印刷スピードが遅い・いちいち画面が立ち上がってしまうという体験談を多く見かける。
https://qiita.com/takiru/items/f38e94e666e802fa9a25

・PDF-XChange Viewer:
こちらでAdobe Acrobat Readerの課題は解消するようだが、端末にPDF-XChange Viewerをインストールしなければならない点が良くない。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13201105318

・Ghostscript
PDFの印刷ができる。評判も良い。(その他画像への変換等もできて色々使えそう!という印象)
https://ghostscript.com/

・C#/.NetCore
PDFを印刷するには、一度画像にしなければならない。ひと手間掛かるので、Ghostscriptでいこう!と思う。
https://www.fenet.jp/dotnet/column/language/9667/

https://qiita.com/okataku/items/a1842ec4774c58ba853e

GhostScriptでの実装


・WEB上で探せば、色々な事例が出てくる。
https://qiita.com/Masahiro-Ito/items/ffea4838f57bb3de5232

・試しに以下の設定で印刷を実行してみる。
 ・デバイス(-sDEVICE=):mswinpr2(MSプリンタ)
 ・プリンタ(-sOutputFile=):プリンタ名を指定(物理プリンタ以外にPDFやXPSといった仮想プリンタも利用可能)
 ・用紙サイズ:一般的な用紙サイズ指定、Pointでのサイズ指定が可能。

 ・PDFのサイズはmmで設定されているため、ここでinchやPointといった単位。解像度について学ぶ。
 ・GhostScriptのサンプルでよく「-r300」という引数が出てくるが、これは解像度300という指定である。解像度300は一般的な印刷として十分な解像度と言われている値であり、これ以上解像度を上げても人の目では分からない・見えない。という基準値であった。

※単位については以下のページで詳しく説明している。
{{ search_title("https://dwd.kiminoquest.com/article/139.php") }}

・PDFを良い感じにコマンドラインから印刷することができた。

GhostScriptで印刷する際の課題


・A4やB5といったサイズでの印刷に成功したため、これを本実装に採用しようと思っていた。

・しかし、プリンタに登録されている用紙サイズ以外のものを印刷しようとすると、登録されている用紙サイズにフィットするように印刷するか、切り取られることを許容するか、という二択しか選択できないことが分かった。
 この時起きたことは、どんなサイズのPDFをどんなオプションで印刷しようとしても、A4の範囲で印刷されてしまう・・・というものであった。
 これでは、いちいちプリンタの設定でユーザー定義用紙を作成しなければならない。

・以下のマニュアルを日本語訳して読み漁ったが、求める解は見つからなかった。(これは別の目的で役に立ちそうだと学びになった)
https://ghostscript.com/doc/9.55.0/Use.htm 

※Ghostscriptの使い方については、以下のページで解説している。
{{ search_title("https://dwd.kiminoquest.com/article/135.php") }}

C#/.NetCoreでの印刷実装を試す


・C#のヘルプで、任意の用紙サイズを指定する方法が紹介されていた。

・PaperSize Class:これを使うことで、カスタム用紙サイズを設定できるというサンプルコードが紹介されていた。(公式)
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.papersize?view=windowsdesktop-5.0

・また、単位はinchで、intで設定可能。1/100インチで指定できるよ。という解説であった。(公式)
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.papersize.width?view=windowsdesktop-5.0

・このヘルプの希望を感じ、試してみることにした。

・とりあえず、サンプルコードを見ながら、画像を用意し印刷してみた。
 ・その結果は、、、大きい用紙に小さく印刷される。というものであった。
 ・縦横設定がおかしいのではないか?と思い、印刷方向を設定するコードを設定した。

// 用紙縦設定
pd.DefaultPageSettings.Landscape = true;
pd.PrinterSettings.DefaultPageSettings.Landscape = true;

https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.pagesettings.landscape?view=dotnet-plat-ext-6.0

 ・この結果、、、向きは変わったが大きさが変わらない・・・というものであった。

 ・横長のPDFであり、用紙は縦で差し込んでいるので、プリンタからみたら縦と横が逆なんじゃないか?というところを疑い、試しに画像の縦横サイズを用紙設定の際に逆にして設定してみた。

 ・成功!!

 ・期待通りに、登録されていない用紙サイズにもピッタリ印刷することができた。=ユーザー定義用紙サイズに対してプログラムから印刷することができた!

・若干、上下左右の余白のズレが気になったため、以下の設定を行った。余白を0にし、描画を余白の終点から行うという設定である。

// 余白設定
pd.OriginAtMargins = true;
pd.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);

https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.printdocument.originatmargins?view=windowsdesktop-5.0#System_Drawing_Printing_PrintDocument_OriginAtMargins

https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.pagesettings.margins?view=windowsdesktop-5.0

・この結果、ピッタリと綺麗にPDFを用紙に印刷することができた!!

※印刷の検証には、XPSプリンターを使うのがオススメ。ユーザー定義用紙への印刷をシミュレートすることができる。使い方は以下のページで解説している。
{{ search_title("https://dwd.kiminoquest.com/article/154.php") }}

※試しに、フチなし印刷にも挑戦してみたが、プリンタ固有の余白を埋めることはできなかった。この値は以下のプロパティを見て確認することができる。(XPSやPDF仮想プリンタは0で余白無し)
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.pagesettings.hardmarginx?view=windowsdesktop-5.0#System_Drawing_Printing_PageSettings_HardMarginX

本実装


・一つ一つ設定変更しながら試行することで、目的のプログラムが組めそうなことは分かった。

・これらの要素を繋ぎ合わせていく段階に入り、以下のコードを書いた。

①PDFを読み、ページサイズを取得するコード。これは外部ライブラリであるがPDFSharpを利用して実現できた。Nugetで取得可能。(PDFSharpCoreを利用)
http://www.pdfsharp.net/

PdfDocument document = PdfReader.Open(対象のファイルパス);
var width = document.Pages[0].Width.Point;
var height = document.Pages[0].Height.Point;


②Ghostscriptを使って、PDFを画像に変換する処理を実装。(GhostscriptのラッパーもNugetにあるが、こちらだと実行時エラーでたため、使用をやめた)
 オプションの内容は、プロンプト出さない等の無操作実行と、インプットアウトプットのファイルパス、デバイスをpng設定とし、解像度を350デフォルトとした。(ここの解像度が最終的な印刷の綺麗さに影響するため、調整できるようにした)
 オプションの第一引数は読まれないため、-emptyなどダミーで挿入する慣習になっているようだ。

// 第1引数がコマンド、第2引数がコマンドの引数
ProcessStartInfo processStartInfo = new ProcessStartInfo(gswin64c.exeのファイルパス, Ghostscriptの各種オプション文字列);

// コマンド実行
Process process = Process.Start(processStartInfo);

// コマンド終了の待ち合わせ
process.WaitForExit();
process.Close();


③C#の実装で書いた部分で出来上がっていたコードを持ってきて、Ghostscriptが出力した画像をプリンタへ送信するように実装をした。
・使用した主なクラスやプロパティは以下の通り。

・PrintDocument
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.printdocument?view=dotnet-plat-ext-6.0

・PrintDocument.PageSettings
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.pagesettings?view=dotnet-plat-ext-6.0

・PrintDocument.PrinterSettings
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.printing.printdocument.printersettings?view=dotnet-plat-ext-6.0#System_Drawing_Printing_PrintDocument_PrinterSettings

補足・所感


・PDF印刷を自動化する。=プログラムからPDFを印刷する、かつ、ユーザー定義用紙にも対応する。という点においてはこの方法がベストだと感じる。

・exeは発行で単一ファイルにすることで諸々のインストールやら煩わしい作業が不要になる。

・Ghostscriptはbinフォルダ内の4ファイルだけあればコマンドラインで動くため、それをパッケージに加えて内部利用すれば良い。

・最終的に、面倒なセットアップ不要で、アプリフォルダをデスクトップに置き、印刷できるプリンタに接続さえしてあれば、すぐにこの仕組みを導入できるようになった。

以上


Category



Tag




関連記事


{{tmp.name}}

{{article.category}} {{article.title}}