Conv-ZipToPDF.ps1にOCR機能を追加してみた。

コンピュータ
以前、ZIPファイル内にある画像ファイルを連結してPDFファイルを作成するスクリプトを作りました。
PowershellでZIPファイルをPDFファイルに変換する
はじめに このスクリプトは画像ファイル(.jpg.png)を含むZIPファイルをPDFファイルに変換します。 PowerShellスクリプトを初めて実行する場合 PowerShellスクリプトの実行を許可してください。 iTextSharp...

PDFに文字を載せる方法と、
PowerShellスクリプトでテキスト文書のPDFファイルを新規に作成する
要itextsharp.dll スクリプト # # 新規にPDFファイルの文書を作成するサンプル # # ファイル名:Create-NewPDF.ps1 # $ErrorActionPreference = "stop" ::LoadFro...

オープンソースのOCRソフトを見つけましたので、
オープンソースなOCRソフトTesseractを試してみた。
以下のサイトからソフトをダウンロード 私の環境は64bitなので、 tesseract-ocr-w64-setup-v4.1.0.20190314 (rc1) (64 bit) resp. をクリックしダウンロード。 日本語の辞書データ?が...

画像をPDFにする際、OCRで文字認識をし、その文字(テキスト)をPDFに透明な文字として貼りつけるようスクリプトを変更してみました。

スクリプト

#
# 画像ファイルをPDFファイルに変換する
#
# ファイル名:Conv-ZipToPDF.ps1
# 
# 引数:
# SourcePath...変換元のZIPファイルまたはZIPファイルを含むディレクトリのパスを指定
# DestinationPath...変換先のディレクトリを指定
# Recurse...-Recurseを選択すると$DestinationPathディレクトリを再帰的に検索します
#
# 変更:
# 20190220 初出
# 20190222 各種コマンドレットのオプションを-Pathから-LiteralPathに変更
# 20190518 画像をOCRで文字認識し透明文字として貼り付け


param(
    [string]$SourcePath,
    [string]$DestinationPath="h:\script\",
    [switch]$Recurse
)

$ErrorActionPreference = "stop"

[void][System.Reflection.Assembly]::LoadFrom((Join-Path $env:USERPROFILE "/Documents/Modules/pdf/itextsharp.dll"));

$tesseract_exe = 'C:\Program Files\Tesseract-OCR\tesseract.exe' # tesseract.exeのパス

$scriptPath = $MyInvocation.MyCommand.Path

# OCRの実行
function Exec-OCR
{
    param (
        [string]$in
    )

    # テンポラリファイル
    $TmpFile = New-TemporaryFile
    $out = $TmpFile.FullName

    $sp = (Get-Item -LiteralPath $scriptPath)
    # 標準出力のパス
    $log_path = Join-Path $sp.DirectoryName ($sp.BaseName + ".log")
    # 標準エラーのパス
    $err_path = Join-Path $sp.DirectoryName ($sp.BaseName + ".err")

    

    # Start-Processのオプション
    # -FilePath 実行ファイルのパス
    # -ArgumentList 実行ファイルの引数
    # -Wait コマンドの終了をまつ。
    # -PassThru 結果をオブジェクトで返す。
    # -RedirectStandardOutput 標準出力
    # -RedirectStandardError 標準エラー
    # -NoNewWindow ウィンドウを新たに開かない
    $proc = Start-Process -FilePath $tesseract_exe "${in} ${out} -l jpn" -Wait -PassThru -RedirectStandardOutput $log_path -RedirectStandardError $err_path -NoNewWindow

    $result = ""
    switch ($proc.ExitCode) {
        0 {
            # echo "成功"
            $result = Get-Content -Encoding UTF8 ($out+".txt")
        }
        default {
            # echo "失敗"
        }
    }


    # テンポラリファイルを削除
    rm $TmpFile
    rm ($out+".txt")

    return $result
}


# 画像変換
function Conv-ImageToPDF
{
    param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [string]
        $SrcX,
        [string]
        $DstX
    );
    begin
    {
        $stream = [System.IO.FileStream]::new($DstX, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite + [System.IO.FileShare]::Delete);
    }
    process
    {
        
        # OCRを実行
        $text = Exec-OCR $SrcX

        $image = [iTextSharp.text.Image]::GetInstance($SrcX)
        $pagesize = New-Object iTextSharp.text.Rectangle($image.Width, $image.Height);
        
        $doc = New-Object iTextSharp.text.Document($pagesize);
        $writer = [iTextSharp.text.pdf.PdfWriter]::GetInstance($doc, $stream);


        $doc.Open();
        $image.SetAbsolutePosition(0, 0);
        [void]$doc.Add($image);

        #############################################################################################
        # テキストを貼り付けてみる。

        # フォント
        $BaseFont = [iTextSharp.text.pdf.BaseFont]::CreateFont("C:\windows\fonts\msmincho.ttc,0",[iTextSharp.text.pdf.BaseFont]::"IDENTITY_H",$True)

        $pdfContentByte = $writer.DirectContent
        $pdfContentByte.SetFontAndSize($BaseFont, 20)
    
        # 文字を透明にする。
        $pdfContentByte.SetTextRenderingMode([iTextSharp.text.pdf.PdfContentByte]::TEXT_RENDER_MODE_INVISIBLE) 

        # 文字列を表示する領域を確保
        $columntext = New-Object iTextSharp.text.pdf.ColumnText($pdfContentByte)
        $columntext.SetSimpleColumn(0, 0, $pagesize.width, $pagesize.height)
    

        # 文字列をセット
        $paragraph = New-Object iTextSharp.text.Paragraph($text, $BaseFont)
        
        $pdfContentByte.BeginText()

        # 
        # PDFの編集ルーチンをここに記述
        # 

        $columntext.AddElement($paragraph)
        $result = $columntext.Go()


        $pdfContentByte.EndText()

        #############################################################################################
        $doc.Close();
    }
    end
    {
        $stream.Close();
    }
}

# PDF連結
function Join-PDF
{
    param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [string]
        $SrcDir,
        [string]
        $DstPDF
    );
    begin
    {
        echo $DstPDF;
        $stream = [System.IO.FileStream]::new($DstPDF, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite + [System.IO.FileShare]::Delete);
        $doc = New-Object iTextSharp.text.Document;
        $writer = New-Object iTextSharp.text.pdf.PdfCopy($doc, $stream);
        $doc.Open();
    }
    process
    {
        Get-ChildItem -LiteralPath $SrcDir -Recurse | where {
            $_.Extension -eq ".pdf"
        } | foreach {
            #echo $_.FullName;
            $reader = New-Object iTextSharp.text.pdf.PdfReader($_.FullName);
            $page = $writer.GetImportedPage($reader, 1);
            $writer.AddPage($page);
            $reader.Close();
        }
    }
    end
    {
        $writer.Close();
        $doc.Close();
        $stream.Close();
    }
}

# 重複しないファイル名を生成
function Create-UniquePath
{
    param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [string]$BasePath
    )
    begin
    {
    }
    process
    {
        if (!(Test-Path -LiteralPath $BasePath)) {
            return $BasePath;
        }
        $f = Get-Item -LiteralPath $BasePath;
        $Result = $BasePath;
        for($i=1; (Test-Path -LiteralPath $Result); $i++) {
            $Result = $f.Directory.FullName + "\" + $f.BaseName + "(" + $i + ")" + $f.Extension;
        }
        return $Result;
    }
    end
    {
    }
}

# ZIPファイル内の画像ファイルをPDFに変換する
function Conv-ZipToPDF
{
    param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [string]
        $Src,
        [string]
        $Dst
    )
    begin
    {
        $TmpFolder = New-TemporaryFile | %{ rm $_; mkdir $_ }
    }
    process
    {
        Expand-Archive -LiteralPath $Src -DestinationPath $TmpFolder
        Get-ChildItem -LiteralPath $TmpFolder -Recurse | where {
            $_.Extension -eq '.jpg' -Or $_.Extension -eq '.png'
        } | foreach {
            $s = $_.FullName
            $d = Join-Path $_.Directory.FullName ($_.BaseName + '.pdf')
            Conv-ImageToPDF -Src $s -Dst $d
        }
        Join-PDF -SrcDir $TmpFolder.FullName -DstPDF $Dst
    }
    end
    {
        #Get-ChildItem -LiteralPath $TmpFolder -Recurse | ForEach-Object {Remove-Item -LiteralPath $_.FullName};
        Remove-Item -LiteralPath $TmpFolder -Recurse -Force;
    }
}

if (!($SourcePath) -Or !(Test-Path -LiteralPath $SourcePath)) {
    echo ($SourcePath+"ファイルが存在しない。");
    exit;
}
$s = Get-Item -LiteralPath $SourcePath;

if (!($DestinationPath) -Or !(Test-Path -LiteralPath $DestinationPath)) {$DestinationPath = "."}

echo $s.Attributes

if ($s.Attributes -match "Directory") {
    # ディレクトリモード
    
    if ($Recurse) {
        echo "Directory Recurse Mode"

        Get-ChildItem -LiteralPath $SourcePath -Recurse | where  {
            $_.Extension -eq ".zip"
        } | foreach {
            $ss = $_.FullName
            $dd = Create-UniquePath (Join-Path $DestinationPath ($_.BaseName + '.pdf'))
            Conv-ZipToPDF -Src $ss -Dst $dd
        }
    } else {
        echo "Directory Mode"

        Get-ChildItem -LiteralPath $SourcePath | where  {
            $_.Extension -eq ".zip"
        } | foreach {
            $ss = $_.FullName
            $dd = Create-UniquePath (Join-Path $DestinationPath ($_.BaseName + '.pdf'))
            Conv-ZipToPDF -Src $ss -Dst $dd
        }
    }
} else {
    # ファイルモード
    echo "File Mode"
    $dd = Create-UniquePath (Join-Path $DestinationPath ($s.BaseName + '.pdf'))
    Conv-ZipToPDF -Src $SourcePath -Dst $dd
}

使い方

オプションは以前の記事を参照ください。
PowershellでZIPファイルをPDFファイルに変換する
はじめに このスクリプトは画像ファイル(.jpg.png)を含むZIPファイルをPDFファイルに変換します。 PowerShellスクリプトを初めて実行する場合 PowerShellスクリプトの実行を許可してください。 iTextSharp...

最後に

OCRやPDFのサンプルスクリプトをコピー&ペーストしたところとりあえず動きましたので、そのまま公開しております。上手く文字認識されるたPDFファイルをAcrobatなどで開くと、各ページの上部に認識された透明な文字列が貼り付けられているはずです。
透明な文字列はページの上部をマウスでドラックすると選択コピーすることができます。画像上の文字認識位置に透明文字列を貼り付けられるようにすると市販のOCRソフトっぽくなりますが、難しいので対応予定はありません。

コメント