Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@acknpop

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

More than 1 year has passed since last update.

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 を入手してください。

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を右綴じに変換

1
Help us understand the problem. What is going on with this article?
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
acknpop
にゃ?

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?