PowerShellでPDFをPocketMod形式に再レイアウト (PDFSharp版)

PowerShellからPDFSharpを呼び出してPDFをPocketMod形式にレイアウトし直します。

オリジナル（拙作）は
https://qiita.com/acknpop/items/dd03f9e9df18eb05b18c
ですが、iTextSharpの利用が信条的に受け入れがたい人のために。

# 下準備

まず始めに pdfsharp.dll を入手してください。

https://www.nuget.org/packages/PDFSharp/

nugetするのが本筋ですが、下記はスクリプトを置くフォルダにlibというサブフォルダを作って、その下に pdfsharp.dll を入れる方式です。

普通はInstall-Moduleして、Get-Moduleで使うはず。

# スクリプト

以下を ConvertTo-PocketMod.ps1 として保存。

```powershell:ConvertTo-PocketMod.ps1
# Convert PDF To PocketMod style
param(
    [parameter(mandatory=$true)][string]$sourceDataPath,
    [parameter(mandatory=$true)][string]$destinationPath
)

function ConvertTo-PocketMod($sourceDataPath, $destinationPath)
{
    # path of pdfsharp.dll
    [System.Reflection.Assembly]::LoadFrom((Join-Path (Split-Path $script:MyInvocation.MyCommand.Path) "\lib\PdfSharp.dll")) | Out-Null

    [PdfSharp.Drawing.XPdfForm]$form = [PdfSharp.Drawing.XPdfForm]::FromFile([string]$sourceDataPath)

    $pen = New-Object PdfSharp.Drawing.XPen([PdfSharp.Drawing.XColors]::Black, 0.01)
    $dash = New-Object PdfSharp.Drawing.XPen([PdfSharp.Drawing.XColors]::Black, 0.01)
    $dash.DashPattern = @(300, 300)

    try {
        # Output page size
        $pagesize = [PdfSharp.PageSize]::A4
        $doc = New-Object PdfSharp.Pdf.PdfDocument

        for($i = 0; $i -lt $form.PageCount; $i++) {

            if(($i % 8) -eq 0){
                $dst = $doc.AddPage()
                $dst.Size = $pagesize
                [float]$dw = $dst.Width.Value
                [float]$dh = $dst.Height.Value
            }
            [PdfSharp.Drawing.XGraphics]$gfx = [PdfSharp.Drawing.XGraphics]::FromPdfPage($dst)

            $form.PageNumber = $i + 1

            [PdfSharp.Pdf.PdfPage]$src = $form.Page
            [float]$sw = $src.Width.Value
            [float]$sh = $src.Height.Value

            if($sw -gt $sh){
                $isLandscape = $TRUE
            }
            else{
                $isLandscape = $FALSE
            }
            
            if($isLandscape -eq $TRUE){
                [float]$scale = $dh / ($sh * 4)
                [float]$offset = (($dw / 2) - ($scale * $sw)) / 2
            }
            else{
                [float]$scale = $dh / ( $sw * 4)
                [float]$offset = (($dw / 2) - ($scale * $sh)) / 2
            }

            # Affine translation of PocketMod style
            [float]$px = $dw / 2
            [float]$py = $dh / 4
            [float]$pw = $sw * $scale
            [float]$ph = $sh * $scale

            $rot = $src.Rotate

            if(($isLandscape -eq $TRUE) -or (($rot -eq 90) -or ($rot -eq 270))){
                switch($i % 8){
                    # Left side
                    # Rotate 180 degrees
                    0 {
                        $box = New-Object PdfSharp.Drawing.XRect(-($px - $offset), -($py * 1), $pw, $ph)
                        $gfx.RotateTransform(-180)
                    }
                    7 {
                        $box = New-Object PdfSharp.Drawing.XRect(-($px - $offset), -($py * 2), $pw, $ph)
                        $gfx.RotateTransform(-180)
                    }
                    6 {
                        $box = New-Object PdfSharp.Drawing.XRect(-($px - $offset), -($py * 3), $pw, $ph)
                        $gfx.RotateTransform(-180)
                    }
                    5 {
                        $box = New-Object PdfSharp.Drawing.XRect(-($px - $offset), -($py * 4), $pw, $ph)
                        $gfx.RotateTransform(-180)
                    }

                    # Right side
                    # No rotation
                    1 {
                        $box = New-Object PdfSharp.Drawing.XRect(($px + $offset), ($py * 0), $pw, $ph)
                    }
                    2 {
                        $box = New-Object PdfSharp.Drawing.XRect(($px + $offset), ($py * 1), $pw, $ph)
                    }
                    3 {
                        $box = New-Object PdfSharp.Drawing.XRect(($px + $offset), ($py * 2), $pw, $ph)
                    }
                    4 {
                        $box = New-Object PdfSharp.Drawing.XRect(($px + $offset), ($py * 3), $pw, $ph)
                    }
                }
            }
            else{
                switch($i % 8){
                    # Left side
                    # Rotate counterclockwise 90 degrees
                    0 {
                        $box = New-Object PdfSharp.Drawing.XRect((($py * 0) - $offset), -$px, $pw, $ph)
                        $gfx.RotateTransform(90)
                    }
                    7 {
                        $box = New-Object PdfSharp.Drawing.XRect((($py * 1) - $offset), -$px, $pw, $ph)
                        $gfx.RotateTransform(90)
                    }
                    6 {
                        $box = New-Object PdfSharp.Drawing.XRect((($py * 2) - $offset), -$px, $pw, $ph)
                        $gfx.RotateTransform(90)
                    }
                    5 {
                        $box = New-Object PdfSharp.Drawing.XRect((($py * 3) - $offset), -$px, $pw, $ph)
                        $gfx.RotateTransform(90)
                    }

                    # Right side
                    # Rotate clockwise 90 degrees
                    1 {
                        $box = New-Object PdfSharp.Drawing.XRect(-(($py * 1) + $offset), $px, $pw, $ph)
                        $gfx.RotateTransform(-90)
                    }
                    2 {
                        $box = New-Object PdfSharp.Drawing.XRect(-(($py * 2) + $offset), $px, $pw, $ph)
                        $gfx.RotateTransform(-90)
                    }
                    3 {
                        $box = New-Object PdfSharp.Drawing.XRect(-(($py * 3) + $offset), $px, $pw, $ph)
                        $gfx.RotateTransform(-90)
                    }
                    4 {
                        $box = New-Object PdfSharp.Drawing.XRect(-(($py * 4) + $offset), $px, $pw, $ph)
                        $gfx.RotateTransform(-90)
                    }
                }
            }
            $gfx.DrawImage($form, $box)
            $gfx.Dispose()

            # Draw guide line for folding
            if((($i % 8) -eq 7) -or ($i -eq ($form.PageCount - 1))){

                [PdfSharp.Drawing.XGraphics]$gfx = [PdfSharp.Drawing.XGraphics]::FromPdfPage($dst)

                # draw outside border
                $gfx.DrawRectangle($pen, 0, 0, $dw, $dh)

                # horizontal lines
                $gfx.DrawLine($pen, 0, $dh * 3 / 4, $dw, $dh * 3 / 4)
                $gfx.DrawLine($pen, 0, $dh * 2 / 4, $dw, $dh * 2 / 4)
                $gfx.DrawLine($pen, 0, $dh * 1 / 4, $dw, $dh * 1 / 4)

                # vertical lines
                $gfx.DrawLine($pen, $dw / 2, 0, $dw / 2, $dh * 1 / 4)
                $gfx.DrawLine($pen, $dw / 2, $dh * 3 / 4, $dw / 2, $dh)

                # dashed vertical line
                $gfx.DrawLine($dash, $dw / 2, $dh * 1 / 4, $dw / 2, $dh * 3 / 4)

                $gfx.Dispose()
            }
        }

        $doc.Save($destinationPath)
    }
    catch {
        Write-Error("Error: " + $_.Exception)
    }
}

ConvertTo-PocketMod (Convert-Path $sourceDataPath) $destinationPath
```

pdfsharp.dllを置いた場所によっては、LoadFromのくだりを書き換える必要があります。

あと、出力ページサイズをA4にハードコーディングしているので、こちらも必要に応じて書き換えてください。

# 使用例

PowerShellを立ち上げて、

```powershell
PS> ConvertTo-PocketMod.ps1 元PDFファイル (Join-Path $PWD 出力先PDFファイル)
```

みたいに使います。
※PowerShellは基本絶対パス指定なので、相対パスを使いたい時はConvert-PathやJoin-Pathを組み合わせるようです
※Convert-Pathは指定したフォルダやファイルが存在しないとエラーになるので注意

# あとがき

http://www.pdfsharp.net/
にC#で書かれたSampleがあるので、参考にできるかと。

あと、通常PDFの世界では左下が原点(0,0)なのですが、PDFSharpでは左上が原点になるので要注意。
描画位置合わせは強引にやってるので、あまり参考にはならないかも。