生活中有不少用 Word 撰寫文件,但交件格式限定 PDF 的需求。Word 很早已內建另存 PDF 功能,所以我都是用 Word 編輯修改,最後再另存轉 PDF。

但我有個壞習慣,寫文章寫文件喜歡反覆琢磨補充潤飾,總覺得還可以改得更好。所以寫完交卷前我會存成 PDF 先擱一陣子,確保萬一想到新點子還可以改,動不動就開 Word、修改、另存 PDF 更新,保持隨時可交件的機動性;上廁所靈光乍現,想到可以加兩句,就再開 Word、修改、另存 PDF。

結果這個壞習慣產生一堆無意義的另存 PDF 動作,感覺有改進空間。

我想起之前寫過 DOCX 轉 PDF PowerShell 小工具,如果做成右鍵選單便能解決問題。平時專心編輯 Word 檔即可,交卷前一刻,在檔案按滑鼠右鍵再點兩下,馬上 Word 變 PDF,是不是很方便?

上回寫的 PowerShell 會呼叫 Word 將 docx 轉 pdf,功能已完整,現在只需在 docx 右鍵加入快捷選單即大功告成。這裡會用到之前學過的技巧,在 Registry 針對 Word.Document.12 加上自動 Command:(不是加在 .docx 副檔名)

擴充了上次的 .ps1,加入 -Register、-Unregister 兩個參數用來註冊及移除 Registry。

註冊好,在 .docx 檔案用滑鼠右鍵點開選單,會有個「轉存 PDF」選項,點下去等三四秒,熱騰騰的 PDF 檔就出現了。(Word 轉 PDF 的速度無法跟商業元件相比,但看在不用多花錢的份上,相信大家都能接受)

Talk is cheap, show me the code! 範例程式碼如下,有需要的朋友歡迎自取修改使用。

Param(
    [parameter(ValueFromRemainingArguments=$true)]
    [string]
    $docPaths, 
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [switch][bool]
    $Replace,
    [switch][bool]
    $Register, # 註冊右鍵選單
    [switch][bool]
    $Unregister # 取消註冊右鍵選單
)
$ErrorActionPreference = "STOP"
function getDocxShellKeyPath() {
    # 由 HKEY_CLASSES_ROOT\.docx 取得 Word 文件對應的機碼名稱(如:Word.Document.12)
    $docxKeyName = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey(".docx").GetValue('')
    return "SOFTWARE\Classes\$docxKeyName\shell\ToPdf"   
}
if ($Unregister) {
    $regPath = getDocxShellKeyPath
    $reg = [Microsoft.Win32.Registry]::CurrentUser
    $key = $reg.OpenSubKey($regPath, $true)
    if ($key) { $reg.DeleteSubKeyTree($regPath) }
    return
}
if ($Register) {
    $regPath = getDocxShellKeyPath
    $reg = [Microsoft.Win32.Registry]::CurrentUser
    $key = $reg.OpenSubKey($regPath, $true)
    if (-not $key) { $key = $reg.CreateSubKey($regPath) }
    $key.SetValue("", "轉存 PDF")
    $key = $reg.OpenSubKey("$regPath\command", $true)
    if (-not $key) { $key = $reg.CreateSubKey("$regPath\command") }
    $key.SetValue("", "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -Command `"$PSScriptRoot\Atk-Docx2Pdf.ps1 `"`"%1`"`"`"")
    return
}

if (!$docPaths) {
    Write-Host "請指定要轉換的 Word 檔案路徑"
    return
}

try 
{
    $doc = New-Object -ComObject Word.Application
    $doc.Visible = $true # 顯示 UI,方便使用者了解處理進度、輸入密碼等
    Get-Item $docPaths | ForEach-Object {
        $path = $_.FullName
        Write-Host "開啟 $path..."
        try {
            $doc.Documents.Open($path) | Out-Null
            $savePath = [System.IO.Path]::ChangeExtension($path, ".pdf")
            # https://docs.microsoft.com/en-us/office/vba/api/word.wdsaveformat
            # WdSaveFormat.wdFormatPDF = 17
            $doc.ActiveDocument.SaveAs2($savePath, 17)
            $doc.ActiveDocument.Close()
            if ($replace) {
                Remove-Item $path                
            }
        }
        catch {
            Write-Host "發生錯誤 - $path" -ForegroundColor Red
        }
    }
}
finally 
{
    $doc.Quit()
}

Example of PowerShell to register explorer shell command to convert .docx to .pdf with MS Word.


Comments

# by I.Z.

版主好, 感謝您寫的腳本,有個小問題 檔名包含左右圓括號時會出錯耶,在powershell中要怎麼escape掉圓括號呢?

# by Jeffrey

to I.Z. 不是很確定你說的狀況,能描述你的檔名跟操作方式嗎?

# by I.Z.

有兩個檔案: 「申請書(空白).docx」 「申請書.docx」 1. 先執行 .\Atk-Docx2Pdf.ps1 -Register 2. 在「申請書.docx」 右鍵轉存PDF,此時可正常進行 DOCX to PDF 轉換 3. 在「申請書(空白).docx」 右鍵轉存PDF,此時會報下列錯誤(我有增加 -NoExit 以利排錯),推測應該是因為圓括號導致錯誤,但小弟搜尋了一陣居然不知道怎麼 Escape 掉圓括號 >"<。 空白 : 無法辨識 '空白' 詞彙是否為 Cmdlet、函數、指令檔或可執行程式的名稱。請檢查名稱拼字是否正確,如果包含路徑的話,請 確認路徑是否正確,然後再試一次。 位於 線路:1 字元:148 + ... rs\B06065\OneDrive\桌面\CustomTools\doc2docx_pdf_converter\申請書(空白).docx + ~~ + CategoryInfo : ObjectNotFound: (空白:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException

# by Jeffrey

to I.Z. 感謝提供資訊,已重現問題。修正方法是在註冊 Registry 時, -Command `"$PSScriptRoot\Atk-Docx2Pdf.ps1 `"`"%1`"`"`"" 的 %1 前後要加上 `"`",應該就能避開問題了,你試看看。

Post a comment