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

PowerShellでPDFをPocketMod形式に再レイアウト (w/iTextSharp)

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

PocketMod形式については、
https://pocketmod.com
https://repocketmod.com
あたりをご参照あれ。
("PocketMod"でググる方が早いと思いますが、一応)

どこかのサイトにPDFからPocketMod形式に変換するツールがあったのですが、使用しているWindows環境では動かなくなってしまっていたので、サラッと作ってみました。

このスクリプトはPDFを8ページを1ページに割り付けている(8 in 1)だけなので、itextsharpを使ったPDFの加工例として見て貰えれば良いかと。

下準備

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

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

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

スクリプト

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

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 itextsharp.dll
    [System.Reflection.Assembly]::LoadFrom((Join-Path (Split-Path $script:MyInvocation.MyCommand.Path) "\lib\itextsharp.dll")) | Out-Null

    $pr = New-Object iTextSharp.text.pdf.PdfReader([string]$sourceDataPath)
    if($pr.IsEncrypted()){
        Write-Error("Encrypted: ", $sourceDataPath)
    }

    try {
        # Output page size
        $dst = [iTextSharp.text.PageSize]::A4
        $doc = New-Object iTextSharp.text.Document($dst)

        $fs = New-Object System.IO.FileStream([string]$destinationPath, [System.IO.FileMode]::Create)
        $pw = [iTextSharp.text.pdf.PdfWriter]::GetInstance($doc, $fs)

        $doc.Open()

        [float]$rads = (180 * [math]::PI) / 180

        for($page = 1; $page -le $pr.NumberOfPages; $page++) {

            if(($page % 8) -eq 1){
                $doc.NewPage() | Out-Null
            }

            $src = $pr.GetPageSize($page)
            if($src.Width -gt $src.Height){
                $isLandscape = $TRUE
            }
            else{
                $isLandscape = $FALSE
            }
            if($isLandscape -eq $TRUE){
                [float]$scale = $dst.Height / ($src.Height * 4)
                [float]$offset = (($dst.Width / 2) - ($scale * $src.Width)) / 2
            }
            else{
                [float]$scale = $dst.Height / ($src.Width * 4)
                [float]$offset = (($dst.Width / 2) - ($scale * $src.Height)) / 2
            }

            # For scaling and rotation
            $transRotate = New-Object iTextSharp.awt.geom.AffineTransform
            $transRotate.SetToIdentity()
            $transrotate.Scale($scale, $scale)

            # For position
            $transAdjust = New-Object iTextSharp.awt.geom.AffineTransform
            $transadjust.SetToIdentity()

            # Affine translation of PocketMod style
            [float]$px = $dst.Width / 2
            [float]$py = $dst.Height / 4

            $rot = $pr.GetPageRotation($page)

            if(($isLandscape -eq $TRUE) -or (($rot -eq 90) -or ($rot -eq 270))){
                switch($page % 8){

                    # Left side
                    # Rotate 180 degrees
                    1 {
                        $transAdjust.Translate($px - $offset, $py * 4)
                        $transRotate.Rotate(-$rads)
                    }
                    0 {
                        $transAdjust.Translate($px - $offset, $py * 3)
                        $transRotate.Rotate(-$rads)
                    }
                    7 {
                        $transAdjust.Translate($px - $offset, $py * 2)
                        $transRotate.Rotate(-$rads)
                    }
                    6 {
                        $transAdjust.Translate($px - $offset, $py)
                        $transRotate.Rotate(-$rads)
                    }

                    # Right side
                    # No rotation
                    2 {
                        $transAdjust.Translate($px + $offset, $py * 3)
                    }
                    3 {
                        $transAdjust.Translate($px + $offset, $py * 2)
                    }
                    4 {
                        $transAdjust.Translate($px + $offset, $py)
                    }
                    5 {
                        $transAdjust.Translate($px + $offset, 0)
                    }
                }
            }
            else{
                switch($page % 8){

                    # Left side
                    # Rotate counterclockwise 90 degrees
                    1 {
                        $transAdjust.Translate($px - $offset, $py * 3)
                        $transRotate.Rotate($rads)
                    }
                    0 {
                        $transAdjust.Translate($px - $offset, $py * 2)
                        $transRotate.Rotate($rads)
                    }
                    7 {
                        $transAdjust.Translate($px - $offset, $py)
                        $transRotate.Rotate($rads)
                    }
                    6 {
                        $transAdjust.Translate($px - $offset, 0)
                        $transRotate.Rotate($rads)
                    }

                    # Right side
                    # Rotate clockwise 90 degrees
                    2 {
                        $transAdjust.Translate($px + $offset, $py * 4)
                        $transRotate.Rotate(-$rads)
                    }
                    3 {
                        $transAdjust.Translate($px + $offset, $py * 3)
                        $transRotate.Rotate(-$rads)
                    }
                    4 {
                        $transAdjust.Translate($px + $offset, $py * 2)
                        $transRotate.Rotate(-$rads)
                    }
                    5 {
                        $transAdjust.Translate($px + $offset, $py)
                        $transRotate.Rotate(-$rads)
                    }
                }
            }

            $finalTrans = New-Object iTextSharp.awt.geom.AffineTransform
            $finalTrans.SetToIdentity()
            $finalTrans.Concatenate($transAdjust)
            $finalTrans.Concatenate($transRotate)

            $importedPage = $pw.GetImportedpage($pr, $page)

            $pcb = $pw.DirectContent
            $pcb.AddTemplate($importedPage, $finalTrans)

            # Draw guide line for folding
            if((($page % 8) -eq 0) -or ($page -eq $pr.NumberOfPages)){

                $pcb.SetLineWidth([float]0.01)

                #Draw outside border
                $pcb.MoveTo(0, 0)
                $pcb.LineTo($dst.Width, 0)
                $pcb.LineTo($dst.Width, $dst.Height)
                $pcb.LineTo(0, $dst.Height)
                $pcb.LineTo(0, 0)
                $pcb.Stroke()

                $pcb.MoveTo(0, $dst.Height * 3 / 4)
                $pcb.LineTo($dst.Width, $dst.Height * 3 / 4)
                $pcb.Stroke()

                $pcb.MoveTo(0, $dst.Height * 2 / 4)
                $pcb.LineTo($dst.Width, $dst.Height * 2 / 4)
                $pcb.Stroke()

                $pcb.MoveTo(0, $dst.Height * 1 / 4)
                $pcb.LineTo($dst.Width, $dst.Height * 1 / 4)
                $pcb.Stroke()

                $pcb.MoveTo($dst.Width / 2, 0)
                $pcb.LineTo($dst.Width / 2, $dst.Height * 1 / 4)
                $pcb.Stroke();

                $pcb.MoveTo($dst.Width / 2, $dst.Height * 3 / 4)
                $pcb.LineTo($dst.Width / 2, $dst.Height);
                $pcb.Stroke();

                $pcb.SetLineDash(3, 3)
                $pcb.MoveTo($dst.Width / 2, $dst.Height * 1 / 4)
                $pcb.LineTo($dst.Width / 2, $dst.Height * 3 / 4)
                $pcb.Stroke()

                $pcb.SetLineDash(0)

            }
        }

        $doc.Close()
        $pw.Close()
        $fs.Close()
        $pr.Close()
    }
    catch {
        Write-Error("Error: " + $_.Exception)
    }
}

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

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

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

使用例

PowerShellを立ち上げて、

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

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

あとがき

ホントはCubePDFあたりにPocketMod形式の出力があればいいんですけどね。

PowerShellとiTextSharpとを組み合わせると、PDFをページ単位に分割するスクリプトや右綴じに変換するスクリプトなんてのも簡単に書けそうですね。
是非トライしてみてください。

一応、拙作の例を提示しておきます。
* PowerShellでPDFをページごとに分割
* PowerShellでPDFを右綴じに変換

acknpop
にゃ?
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした