Help us understand the problem. What is going on with this article?

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

arachan@github
情報システムという名の雑用係 社内のパソコンをADなしで集中管理したいために PowerShellを少し嗜む。 プログラムよりもUBCD使ってPCを修理するのが得意な逸般人 最近、Win7マシンに軒並み入れ替わったので、 修理マシンがなく、プログラムに精を出し始めたが、 大して何も作れてないマン。 Excel方眼紙とネ申FileMakerが大っ嫌い。
http://arachansan.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away