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 に置きます。
# フォルダ作成
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をダウンロード 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を下記のように書き換えてしまう。
#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完成です。
# 通常はこれで読込む
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に しずかちゃんが入っているはず。
OK。
出来てるね。
Lisences
サンプルソースなのでご自由にお使い下さい。
但し、iTextSharp.dllのライセンスがAGPLv3なので商用時にはiTextにライセンス料を払う必要があるかと。