PowerShellでzipファイルをepubファイルに変換するスクリプト

epub コンピュータ

書籍をスキャンした画像ファイルを書籍ごとにzip形式で保存しているのですが、そのzipファイルを電子書籍フォーマットのepub形式に変換するPowerShellスクリプトを作りました。

実行するにあたり、圧縮しないzipファイルの作り方がわからなかったので、7-zipを使っています。7-zipを適時インストールしてください。

<#
.SYNOPSIS
 zipファイルをepubに変換
.DESCRIPTION
 7z.exeが必要です。
.EXAMPLE
 PS>.\epub.ps1 <変換元zipファイル> <変換先epubファイル>
#>
param(
    $inFileName = "H:\powershell\epub\sample.zip",
    $outFileName = "H:\powershell\epub\sample_out.epub"
)

Add-Type -AssemblyName System.IO.Compression.FileSystem

$zipPath = "H:\tools\7-ZipPortable\App\7-Zip64\7z.exe"


function Convert-ZipToEpub
{
    param(
        $inFileName,
        $outFileName
    )


    $tmpDir = (Join-Path (Resolve-Path ".").Path "epub")
    if ((Test-Path $tmpDir) -eq $false)
    {
        mkdir $tmpDir | Out-Null
    }

    echo "application/epub+zip" | Set-Content (Join-Path $tmpDir "mimetype") -Encoding UTF8

    if (Test-Path $outFileName)
    {
        rm $outFileName
    }

    & $zipPath a -mm=Copy -tzip $outFileName (Join-Path $tmpDir "mimetype")

    rm (Join-Path $tmpDir "mimetype")

    $metaInfDir = (Join-Path $tmpDir "META-INF")
    if ((Test-Path $metaInfDir) -eq $false)
    {
        mkdir $metaInfDir | Out-Null
    }

    $textDir = (Join-Path $tmpDir "text")
    if ((Test-Path $textDir) -eq $false)
    {
        mkdir $textDir | Out-Null
    }

    $imageDir = (Join-Path $tmpDir "images")
    if ((Test-Path $imageDir) -eq $false)
    {
        mkdir $imageDir | Out-Null
    }

    # 目次
    $navStr = @"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
    <head>
    <title>nav</title>
    </head>
    <body>
    <nav epub:type="toc">
        <ol>
"@

    # パッケージ

    $title = (Get-Item $inFileName).BaseName
    $guid = (New-Guid).Guid
    $date = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"

    $bookStr = @"
<?xml version="1.0" encoding="UTF-8"?>
<package unique-identifier="pub-id" version="3.0" xmlns="http://www.idpf.org/2007/opf">
    <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
        <dc:identifier id="pub-id">urn:uuid:${guid}</dc:identifier>
        <dc:title>${title}</dc:title>
        <dc:language>ja-JP</dc:language>
        <meta property="dcterms:modified">${date}</meta>
        <meta name="cover" content="cover"/>
    </metadata>

    <manifest>
        <item id="nav" href="./text/nav.xhtml" properties="nav" media-type="application/xhtml+xml" />
"@

    $spineStr = ""

    $zip = [System.IO.Compression.ZipFile]::OpenRead($inFileName)

    $zip.Entries | % -Begin{ $i = 0 } -Process {
    
        $i++

        echo $_.fullName

        $name = $_.Name
        $fullName = $_.fullName

        # ページ画像ファイル
        $fs = $zip.GetEntry($fullName).Open()
        $img = [System.Drawing.Image]::FromStream($fs)
        $fs.Close()

        $imagePath = (Join-Path $imageDir $name)
        $img.Save($imagePath)
        $img.Dispose()

        $baseName = (Get-ChildItem $imagePath).BaseName


            # ページHTML
            $html = [XML]@"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>${baseName}</title>
</head>
<body>
<img src="../images/${name}"/>
</body>
</html>
"@

            $htmlPath = Join-Path $textDir ($baseName + ".xhtml")
            $html.Save($htmlPath)

            # 目次
            $navStr += @"
`n        <li><a href="${baseName}.xhtml">${baseName}</a></li>
"@

            # パッケージ

            $mime = switch ((Get-ChildItem $imagePath).Extension.ToUpper())
            {
                ".PNG" {"image/png"}
                ".JPEG" {"image/jpeg"}
                ".JPG" {"image/jpeg"}
                ".BMP" {"image/bmp"}
                ".TIFF" {"image/tiff"}
            }

            $imgID = "img_${baseName}"
            if ($i -eq 1)
            {
                $imgID = "cover"
            }


            $bookStr += @"
`n        <item id="${imgID}" href="./images/${name}" media-type="${mime}" />
    <item id="${baseName}" href="./text/${baseName}.xhtml" media-type="application/xhtml+xml" />
"@

            $spineStr += @"
`n        <itemref idref="${baseName}" />
"@

    }

    $zip.Dispose()

    # 目次
    $navStr += @"
`n      </ol>
    </nav>
    </body>
</html>
"@

    $nav = [XML]$navStr
    $navPath = Join-Path $textDir "nav.xhtml"
    $nav.Save($navPath)

    # パッケージ
        $bookStr += @"
`n    </manifest>

    <spine page-progression-direction="rtl">
"@

        $bookStr += $spineStr
        $bookStr += @"
`n    </spine>
</package>
"@

    $book = [XML]$bookStr
    $bookPath = Join-Path $tmpDir "book.opf"
    $book.Save($bookPath)


    # コンテナ
    $container = [XML]@"
<?xml version="1.0" encoding="UTF-8"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
    <rootfiles>
        <rootfile full-path="book.opf" media-type="application/oebps-package+xml" />
    </rootfiles>
</container>
"@

    $containerPath = Join-Path $metaInfDir "container.xml"
    $container.Save($containerPath)

    & $zipPath u -mm=Copy -tzip -r $outFileName ($tmpDir+"\*")


    # テンポラリディレクトリの削除
    rmdir -Force -Recurse $tmpDir | Out-Null


}

Convert-ZipToEpub $inFileName $outFileName

epubの仕様書を読もうと努力しましたが、早々に挫折した人間が作成しているので、スクリプトの精度はご察しください。
とりあえず出来上がったepubファイルがCalibreで読み込めることを確認しています。

コメント