|
# ============================================================ |
|
# 文字コード設定 |
|
# ============================================================ |
|
|
|
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false) |
|
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) |
|
$OutputEncoding = [System.Text.UTF8Encoding]::new($false) |
|
|
|
|
|
# ============================================================ |
|
# Dropbox の PDF プレビューからページ画像を取得して PNG 保存する |
|
# |
|
# ・Dropbox のPDFプレビュー画面を Edge で読み込む |
|
# ・HTML内からページ画像URLを取得する |
|
# ・PDFの総ページ数を自動取得する |
|
# ・各ページを指定倍率でダウンロードする |
|
# ・横長画像なら見開きと判断して左右に分割する |
|
# ・ページタイトルを出力ファイル名に使う |
|
# ・PNG形式で連番保存する |
|
# ============================================================ |
|
|
|
|
|
# ------------------------------------------------------------ |
|
# 設定 |
|
# ------------------------------------------------------------ |
|
|
|
$DropboxUrl = "https://dropbox.com/scl/fi/2dz2rnch41n0ym6svx2v1/260815_.pdf?rlkey=79hr9bq43shkosbwhe93003lu" |
|
|
|
# Dropbox 側の最初のページ画像番号 |
|
$FirstPageIndex = 0 |
|
|
|
# ページ総数を取得できなかった場合のデフォルト値 |
|
$DefaultLastPageIndex = 44 |
|
|
|
# Dropbox の画像取得時の拡大率 |
|
$ScalePercent = 400 |
|
|
|
|
|
# ------------------------------------------------------------ |
|
# System.Drawing を読み込む |
|
# ------------------------------------------------------------ |
|
|
|
Add-Type -AssemblyName System.Drawing |
|
|
|
|
|
# ============================================================ |
|
# 1. Microsoft Edge の実行ファイルを取得 |
|
# ============================================================ |
|
|
|
# まず、現在起動中の Edge プロセスを探す |
|
$edgeProcess = Get-Process msedge -ErrorAction SilentlyContinue | |
|
Select-Object -First 1 |
|
|
|
if ($edgeProcess) { |
|
|
|
# 起動中の Edge が見つかった場合 |
|
$edgePath = $edgeProcess.Path |
|
} |
|
else { |
|
|
|
# 起動中の Edge が見つからなかった場合は |
|
# 標準的なインストール先を使用 |
|
$edgePath = "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe" |
|
} |
|
|
|
# Edge 本体が存在するか確認 |
|
if (-not (Test-Path $edgePath)) { |
|
throw "Microsoft Edge が見つかりませんでした: $edgePath" |
|
} |
|
|
|
Write-Host "Microsoft Edge:" |
|
Write-Host " $edgePath" |
|
|
|
|
|
# ============================================================ |
|
# 2. Edge をヘッドレス起動して Dropbox ページの DOM を取得 |
|
# ============================================================ |
|
|
|
Write-Host "" |
|
Write-Host "Dropbox ページを読み込んでいます..." |
|
|
|
$html = & $edgePath ` |
|
--headless ` |
|
--dump-dom ` |
|
$DropboxUrl | |
|
Out-String |
|
|
|
|
|
# ============================================================ |
|
# 3. PDF の総ページ数を取得 |
|
# |
|
# 優先順位: |
|
# |
|
# 1. data-testid="page-count" 周辺 |
|
# 2. HTML全文の "数字 / 数字" |
|
# 3. デフォルト値 |
|
# ============================================================ |
|
|
|
$LastPageIndex = $null |
|
$totalPages = $null |
|
|
|
|
|
# ------------------------------------------------------------ |
|
# 3-1. page-count 周辺から探す |
|
# ------------------------------------------------------------ |
|
|
|
$pageCountMatch = [regex]::Match( |
|
$html, |
|
'(?is)data-testid\s*=\s*["'']page-count["''].{0,3000}?(\d+)\s*/\s*(\d+)' |
|
) |
|
|
|
if ($pageCountMatch.Success) { |
|
|
|
$currentPage = [int]$pageCountMatch.Groups[1].Value |
|
$totalPages = [int]$pageCountMatch.Groups[2].Value |
|
|
|
$LastPageIndex = $totalPages - 1 |
|
|
|
Write-Host "" |
|
Write-Host "ページ数を page-count から取得しました。" |
|
Write-Host " 現在ページ: $currentPage" |
|
Write-Host " 総ページ数: $totalPages" |
|
} |
|
|
|
|
|
# ------------------------------------------------------------ |
|
# 3-2. page-count で見つからなければ、 |
|
# HTML全文から "数字 / 数字" を探す |
|
# ------------------------------------------------------------ |
|
|
|
if ($null -eq $LastPageIndex) { |
|
|
|
# HTMLタグを簡易的に除去してテキスト化 |
|
$pageText = $html -replace '<[^>]+>', ' ' |
|
|
|
# HTMLエンティティをデコード |
|
$pageText = [System.Net.WebUtility]::HtmlDecode( |
|
$pageText |
|
) |
|
|
|
# 例: |
|
# 1 / 45 |
|
# 1/45 |
|
# 12 / 100 |
|
# |
|
# のような表記をすべて探す |
|
$pageMatches = [regex]::Matches( |
|
$pageText, |
|
'\b(\d+)\s*/\s*(\d+)\b' |
|
) |
|
|
|
if ($pageMatches.Count -gt 0) { |
|
|
|
# 複数候補がある場合は、 |
|
# 総ページ数が最大のものを採用 |
|
$pageInfo = $pageMatches | |
|
ForEach-Object { |
|
|
|
[PSCustomObject]@{ |
|
Current = [int]$_.Groups[1].Value |
|
Total = [int]$_.Groups[2].Value |
|
} |
|
|
|
} | |
|
Sort-Object Total -Descending | |
|
Select-Object -First 1 |
|
|
|
$totalPages = $pageInfo.Total |
|
$LastPageIndex = $totalPages - 1 |
|
|
|
Write-Host "" |
|
Write-Host "ページ数をHTML全文から取得しました。" |
|
Write-Host " 現在ページ: $($pageInfo.Current)" |
|
Write-Host " 総ページ数: $totalPages" |
|
} |
|
} |
|
|
|
|
|
# ------------------------------------------------------------ |
|
# 3-3. それでも見つからなければデフォルト値を使用 |
|
# ------------------------------------------------------------ |
|
|
|
if ($null -eq $LastPageIndex) { |
|
|
|
$LastPageIndex = $DefaultLastPageIndex |
|
$totalPages = $LastPageIndex + 1 |
|
|
|
Write-Warning "PDFの総ページ数を取得できませんでした。デフォルト値を使用します。" |
|
Write-Warning "LastPageIndex = $LastPageIndex" |
|
} |
|
|
|
|
|
Write-Host "" |
|
Write-Host "取得対象:" |
|
Write-Host " Dropbox画像 $FirstPageIndex ~ $LastPageIndex" |
|
Write-Host " 想定画像数: $($LastPageIndex - $FirstPageIndex + 1)" |
|
|
|
|
|
# ============================================================ |
|
# 4. HTML の <title> から出力ファイル名を作成 |
|
# ============================================================ |
|
|
|
if ($html -match '(?is)<title>(.*?)</title>') { |
|
|
|
$documentTitle = $Matches[1] |
|
|
|
# HTMLエンティティを通常の文字に戻す |
|
$documentTitle = [System.Net.WebUtility]::HtmlDecode( |
|
$documentTitle |
|
) |
|
|
|
# 前後の空白を除去 |
|
$documentTitle = $documentTitle.Trim() |
|
|
|
# 末尾の .pdf を取り除く |
|
$documentTitle = $documentTitle -replace '(?i)\.pdf$', '' |
|
|
|
# Windows のファイル名に使えない文字を "_" に置換 |
|
$invalidChars = [System.IO.Path]::GetInvalidFileNameChars() |
|
|
|
foreach ($char in $invalidChars) { |
|
$documentTitle = $documentTitle.Replace($char, '_') |
|
} |
|
|
|
# 拡大率を末尾に付ける |
|
$OutputPrefix = "$documentTitle($ScalePercent)" |
|
} |
|
else { |
|
throw "ページの <title> を取得できませんでした。" |
|
} |
|
|
|
Write-Host "" |
|
Write-Host "出力ファイル名:" |
|
Write-Host " $OutputPrefix" |
|
|
|
|
|
# ============================================================ |
|
# 5. HTML からページ画像URLのベース部分を探す |
|
# ============================================================ |
|
|
|
# 元のワンライナーと同様に、 |
|
# "htt" から "ge=" までの最初の文字列を探す。 |
|
# |
|
# Dropbox内部で使われているページ画像URLの |
|
# page= の直前までを取得することを意図している。 |
|
|
|
if ($html -notmatch 'https:.+?page=') { |
|
throw "ページ画像のURLをHTML内から見つけられませんでした。Dropbox側の仕様が変更された可能性があります。" |
|
} |
|
|
|
$baseImageUrl = $Matches[0] |
|
|
|
# HTML中の & を通常の & に戻す |
|
$baseImageUrl = $baseImageUrl -replace 'amp;', '' |
|
|
|
Write-Host "" |
|
Write-Host "画像URLのベース部分:" |
|
Write-Host " $baseImageUrl" |
|
|
|
|
|
# ============================================================ |
|
# 6. 各ページ画像をダウンロードして処理 |
|
# ============================================================ |
|
|
|
$outputPageNumber = 0 |
|
|
|
foreach ($pageIndex in $FirstPageIndex..$LastPageIndex) { |
|
|
|
# -------------------------------------------------------- |
|
# ページ画像URLを作成 |
|
# -------------------------------------------------------- |
|
|
|
$imageUrl = |
|
$baseImageUrl + |
|
$pageIndex + |
|
"&scale_percent=$ScalePercent" |
|
|
|
Write-Host "" |
|
Write-Host "取得中: Dropbox画像 $pageIndex" |
|
Write-Host " $imageUrl" |
|
|
|
|
|
# -------------------------------------------------------- |
|
# ページ画像をダウンロード |
|
# -------------------------------------------------------- |
|
|
|
$response = Invoke-WebRequest ` |
|
-Uri $imageUrl ` |
|
-UseBasicParsing |
|
|
|
|
|
# -------------------------------------------------------- |
|
# ダウンロードした画像をメモリ上で Bitmap として読み込む |
|
# -------------------------------------------------------- |
|
|
|
$memoryStream = [System.IO.MemoryStream]::new( |
|
$response.Content |
|
) |
|
|
|
$image = [System.Drawing.Bitmap]::new( |
|
$memoryStream |
|
) |
|
|
|
|
|
try { |
|
|
|
Write-Host " 画像サイズ: $($image.Width) x $($image.Height)" |
|
|
|
|
|
# ==================================================== |
|
# 7. 単ページか見開きかを判定 |
|
# |
|
# 横長: |
|
# Width > Height |
|
# → 見開きと判断して左右に分割 |
|
# |
|
# 縦長または正方形: |
|
# Width <= Height |
|
# → 単ページとしてそのまま保存 |
|
# ==================================================== |
|
|
|
if ($image.Width -gt $image.Height) { |
|
|
|
Write-Host " 判定: 横長 → 見開き" |
|
|
|
# 画像幅が奇数でも1px欠けないようにする |
|
$leftWidth = [int][Math]::Floor( |
|
$image.Width / 2 |
|
) |
|
|
|
$rightWidth = |
|
$image.Width - $leftWidth |
|
|
|
|
|
# 左ページと右ページの切り抜き範囲 |
|
$regions = @( |
|
@{ |
|
X = 0 |
|
Width = $leftWidth |
|
}, |
|
@{ |
|
X = $leftWidth |
|
Width = $rightWidth |
|
} |
|
) |
|
} |
|
else { |
|
|
|
Write-Host " 判定: 縦長または正方形 → 単ページ" |
|
|
|
# 画像全体をそのまま使用 |
|
$regions = @( |
|
@{ |
|
X = 0 |
|
Width = $image.Width |
|
} |
|
) |
|
} |
|
|
|
|
|
# ==================================================== |
|
# 8. 指定範囲を切り抜いて PNG 保存 |
|
# ==================================================== |
|
|
|
foreach ($region in $regions) { |
|
|
|
$outputPageNumber++ |
|
|
|
|
|
# ------------------------------------------------ |
|
# 切り抜き範囲を作成 |
|
# ------------------------------------------------ |
|
|
|
$rectangle = [System.Drawing.Rectangle]::new( |
|
$region.X, |
|
0, |
|
$region.Width, |
|
$image.Height |
|
) |
|
|
|
|
|
# ------------------------------------------------ |
|
# 画像を切り抜く |
|
# ------------------------------------------------ |
|
|
|
$croppedImage = $image.Clone( |
|
$rectangle, |
|
$image.PixelFormat |
|
) |
|
|
|
|
|
try { |
|
|
|
# -------------------------------------------- |
|
# 出力ファイル名 |
|
# |
|
# 例: |
|
# 260815_超かぐや姫おつかれさま本(400)_001.png |
|
# 260815_超かぐや姫おつかれさま本(400)_002.png |
|
# -------------------------------------------- |
|
|
|
$fileName = "{0}_{1:D3}.png" -f ` |
|
$OutputPrefix, |
|
$outputPageNumber |
|
|
|
|
|
# -------------------------------------------- |
|
# PNGとして保存 |
|
# -------------------------------------------- |
|
|
|
$croppedImage.Save( |
|
$fileName, |
|
[System.Drawing.Imaging.ImageFormat]::Png |
|
) |
|
|
|
Write-Host " 保存: $fileName" |
|
} |
|
finally { |
|
|
|
# 切り抜き画像を解放 |
|
$croppedImage.Dispose() |
|
} |
|
} |
|
} |
|
finally { |
|
|
|
# 元画像とメモリストリームを確実に解放 |
|
$image.Dispose() |
|
$memoryStream.Dispose() |
|
} |
|
} |
|
|
|
|
|
# ============================================================ |
|
# 完了 |
|
# ============================================================ |
|
|
|
Write-Host "" |
|
Write-Host "完了しました。" |
|
Write-Host "Dropbox画像数: $($LastPageIndex - $FirstPageIndex + 1)" |
|
Write-Host "保存したPNGページ数: $outputPageNumber" |