Edited at

PowerShellでPDF Fill Form

More than 1 year has passed since last update.

PDFフォームにデータを差し込むことができればどんなに楽だろうか?

上手くいけばPDFフォームが帳票テンプレートして使えるんじゃないだろうか。

Windowsでは


  • PowerShell標準装備

  • PowerShellはdllからライブラリを呼べる

iTextSharpというiTextをC#で弄れるライブラリが存在する。

PowerShellからiTextSharpを呼べばPDFフォームにデータを差し込めるんじゃないだろうか。

PowerShellでPDF Fill Formをやってみたいと思います。


見つけた!

そのまんまのを見つけてしまった。(^-^;

How To Fill In PDF Forms Using PowerShell

このScriptちと問題が…。


  • Find-iTextLibrary を実行すると常にiTextSharp.dllをダウンロード

  • Get-Fields でdll指定しないとFind-iTextLibraryを即実行

  • 既にdllがあると解凍時にエラー吐きまくる

ウザい!

使いやすいように改造することにする。


改造

もう何もかも内蔵してCmdletとして呼び出せばいいんだよ。

下記の方針で行きます。


  • Module化

  • dll内蔵


Module化

PowerShellGetに登録は却下。

だって僕のコードじゃないんだもの。

個人で使うライブラリは $env:USERPROFILE\Documents\WindowsPowerShell/Modules に置きます。


Module化

# フォルダ作成

PS> mkdir $env:USERPROFILE\Documents\WindowsPowerShell/Modules\PdfForm
# PdfForm.psm1をダウンロード
PS> Invoke-WebRequest -Uri https://raw.githubusercontent.com/adbertram/Random-PowerShell-Work/master/Random%20Stuff/PdfForm.psm1 -OutFile $env:USERPROFILE\Documents\WindowsPowerShell/Modules\PdfForm\PdfForm.psm1


iTextSharpも用意


iTextSharp.dll

# iTextSharp.dllをダウンロード GitHubにあるよ。

PS> Invoke-WebRequest -Uri 'https://github.com/itext/itextsharp/releases/download/5.5.10/itextsharp-all-5.5.10.zip' -OutFile $env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfFrom\itextsharp.zip
# 解凍
PS> cd $env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfFrom
PS> Expand-Archive $env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfFrom\itextsharp.zip
PS> Expand-Archive $env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfFrom\itextsharp\itextsharp-dll-core.zip
# このitextsharp.dllを使うようにする。
PS> ls .\itextsharp-dll-core

ディレクトリ: C:\Users\username\Documents\WindowsPowerShell\Modules\PdfFrom\itextsharp-dll-core

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2016/10/07 15:33 4059136 itextsharp.dll
-a---- 2016/10/07 15:33 3164846 iTextSharp.xml


PdfForm.psm1を下記のように書き換えてしまう。


PdfForm.psm1

#Requires -Version 4

## Find-iTextLibraryは削除

function Get-PdfFieldNames
{
[OutputType([string])]
[CmdletBinding()]
param
(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('\.pdf$')]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
[string]$FilePath,

[Parameter()]
[ValidateNotNullOrEmpty()]
[ValidatePattern('\.dll$')]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
#[string]$ITextLibraryPath = (Find-ITextSharpLibrary).FullName
[string]$ITextLibraryPath = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfFrom\itext-core-dll\itextsharp.dll"
)
begin
{
$ErrorActionPreference = 'Stop'
## Load the iTextSharp DLL to do all the heavy-lifting
[System.Reflection.Assembly]::LoadFrom($ITextLibraryPath) | Out-Null
}
process
{
try
{
$reader = New-Object iTextSharp.text.pdf.PdfReader -ArgumentList $FilePath
$reader.AcroFields.Fields.Key
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
}
}

function Save-PdfField
{
[CmdletBinding()]
param
(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[Hashtable]$Fields,

[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('\.pdf$')]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
[string]$InputPdfFilePath,

[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('\.pdf$')]
[ValidateScript({ -not (Test-Path -Path $_ -PathType Leaf) })]
[string]$OutputPdfFilePath,

[Parameter()]
[ValidateNotNullOrEmpty()]
[ValidatePattern('\.dll$')]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
#[string]$ITextSharpLibrary = (Find-ITextSharpLibrary).FullName
[string]$ITextLibraryPath = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfFrom\itext-core-dll\itextsharp.dll"
)
begin
{
$ErrorActionPreference = 'Stop'
}
process
{
try
{
$reader = New-Object iTextSharp.text.pdf.PdfReader -ArgumentList $InputPdfFilePath
$stamper = New-Object iTextSharp.text.pdf.PdfStamper($reader, [System.IO.File]::Create($OutputPdfFilePath))

## Apply all hash table elements into the PDF form
foreach ($j in $Fields.GetEnumerator())
{
$null = $stamper.AcroFields.SetField($j.Key, $j.Value)
}
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
finally
{
## Close up shop
$stamper.Close()
Get-Item -Path $OutputPdfFilePath
}
}
}


保存します。

これでModule完成です。


load

# 通常はこれで読込む

PS> Import-Module PdfForm
# ダメならフルパス指定で読込
PS> Import-Module "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfForm"

これで使える!


使用方法


フォームのフィールド名を取得

PS> Get-PdfFieldNames -FilePath .\pdfform.pdf

name
gender



フィールドにデータを埋めて出力

PS>  Save-PdfField -Fields @{'name'='オレオレ';'gender'='不明'} -InputPdfFilePath .\pdfform.pdf -OutputPdfFilePath .\fill.pdf

ディレクトリ: C:\Users\yusuke

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2018/02/21 11:18 6707 fill.pdf


AdobeReaderで開いてみましょう。

動いてますね。


複数のデータを差込


ソース

Write-Output "csvデータ読み込み"

$data=Import-Csv -Path formdata.csv -Delimiter `t
$data

Import-Module $env:USERPROFILE\Documents\WindowsPowerShell\Modules\PdfFrom

Write-Output " "
Write-Output "FieldNameを取得"
Get-PdfFieldNames -FilePath .\pdfform.pdf

Write-Output " "
Write-Output "データ差込実行"
$i=0
foreach($d in $data){
Save-PdfField -InputPdfFilePath .\pdfform.pdf -OutputPdfFilePath .\fill$i.pdf -Fields @{'name'=$d.name;'gender'=$d.gender}
$i=$i+1
}



実行結果

PS> fillforms.ps1

csvデータ読み込み

name gender
---- ------
のびた
ドラえもん
しずか

FieldNameを取得
name
gender

データ差込実行
fill0.pdf
fill1.pdf
fill2.pdf


fill2.pdfに しずかちゃんが入っているはず。

output1.png

OK。

出来てるね。


Lisences

サンプルソースなのでご自由にお使い下さい。

但し、iTextSharp.dllのライセンスがAGPLv3なので商用時にはiTextにライセンス料を払う必要があるかと。

iTextSharp Lisence AGPLv3