From 0fb187fff7d8296656f5a068f83afe5b4fdf34d2 Mon Sep 17 00:00:00 2001 From: "otoneko." Date: Wed, 28 Oct 2026 20:32:27 +0900 Subject: [PATCH] feat: add tools/resize.ps1 for Windows --- AGENTS.md | 2 +- README.md | 6 +++++ tools/resize.ps1 | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tools/resize.ps1 diff --git a/AGENTS.md b/AGENTS.md index 4723e0b..cf787af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,7 +35,7 @@ This is the main way people contribute. Add one shop end to end. sh tools/resize.sh /path/to/original.jpg content/shops/{shopId}/{shopId}-001.jpg ``` - It prints the final `pixelHeight`/`pixelWidth` — record them. Off macOS use an equivalent (ImageMagick `convert -resize 800x800 -strip`, read size with `identify -format '%w %h'`). + It prints the final `pixelHeight`/`pixelWidth` — record them. On Windows use `powershell -ExecutionPolicy Bypass -File tools/resize.ps1 ` (same arguments and output, no extra tools needed). On other systems use an equivalent (ImageMagick `convert -resize 800x800 -strip`, read size with `identify -format '%w %h'`). 4. **Prefecture** (optional but encouraged) — the 都道府県 the shop is physically in (must end with 都/道/府/県). **Verify the real location** (search the web); do not infer from the name — a "札幌ラーメン" shop can be in Kanagawa, a 家系 shop can be in Tokyo. For a nationwide chain or any shop whose branch you cannot pin down, **omit the field entirely** rather than recording a guess. diff --git a/README.md b/README.md index ff50b9e..eb8bf93 100644 --- a/README.md +++ b/README.md @@ -437,6 +437,12 @@ Remove Exif and optimize the image: jpegtran -copy none -optimize -outfile yoshimuraya-001.jpg yoshimuraya-001.jpg ``` +On Windows, `tools/resize.ps1` does all of the above with no extra tools: + +```powershell +powershell -ExecutionPolicy Bypass -File tools/resize.ps1 IMG_001.JPG ./content/shops/yoshimuraya/yoshimuraya-001.jpg +``` + ## Projects using Ramen API - [yusukebe/ramen-api-example](https://github.com/yusukebe/ramen-api-example) - An example web pages with React and Cloudflare Workers. diff --git a/tools/resize.ps1 b/tools/resize.ps1 new file mode 100644 index 0000000..105b2d5 --- /dev/null +++ b/tools/resize.ps1 @@ -0,0 +1,70 @@ +# Windows counterpart of resize.sh, using the built-in System.Drawing instead of sips and jpegtran. +$ErrorActionPreference = 'Stop' + +$cmdName = Split-Path -Leaf $PSCommandPath +if ($args.Count -ne 2) { + [Console]::Error.WriteLine("Usage: $cmdName IMG_123.JPG yoshimuraya-001.jpg") + exit 1 +} + +$orgPath = $args[0] +$newPath = $args[1] + +Write-Output "Move: $orgPath to $newPath" +Move-Item -LiteralPath $orgPath -Destination $newPath -Force + +Write-Output "Resize and Optimize: $newPath" +Add-Type -AssemblyName System.Drawing +$fullPath = (Resolve-Path -LiteralPath $newPath).Path + +# Image.FromFile keeps the file locked, so read it into memory to be able to overwrite it in place. +$stream = [System.IO.MemoryStream]::new([System.IO.File]::ReadAllBytes($fullPath)) +$image = [System.Drawing.Image]::FromStream($stream) + +# The EXIF metadata is dropped below, so bake its orientation (tag 0x0112) into the pixels. +$orientationTag = 0x0112 +if ($image.PropertyIdList -contains $orientationTag) { + # EXIF orientation value -> System.Drawing.RotateFlipType + $rotateFlipTypes = @{ + 2 = [System.Drawing.RotateFlipType]::RotateNoneFlipX + 3 = [System.Drawing.RotateFlipType]::Rotate180FlipNone + 4 = [System.Drawing.RotateFlipType]::Rotate180FlipX + 5 = [System.Drawing.RotateFlipType]::Rotate90FlipX + 6 = [System.Drawing.RotateFlipType]::Rotate90FlipNone + 7 = [System.Drawing.RotateFlipType]::Rotate270FlipX + 8 = [System.Drawing.RotateFlipType]::Rotate270FlipNone + } + $orientation = [int]$image.GetPropertyItem($orientationTag).Value[0] + if ($rotateFlipTypes.ContainsKey($orientation)) { + $image.RotateFlip($rotateFlipTypes[$orientation]) + } +} + +# Same as sips -Z 800: fit the longest side to 800px, upscaling smaller images too. +$scale = 800 / [Math]::Max($image.Width, $image.Height) +$width = [int][Math]::Round($image.Width * $scale) +$height = [int][Math]::Round($image.Height * $scale) + +# Drawing onto a new bitmap strips all metadata, like jpegtran -copy none. +# The white fill keeps transparent areas of a PNG from turning black in the JPEG. +$bitmap = [System.Drawing.Bitmap]::new($width, $height) +$graphics = [System.Drawing.Graphics]::FromImage($bitmap) +$graphics.Clear([System.Drawing.Color]::White) +$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic +$graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality +$graphics.DrawImage($image, 0, 0, $width, $height) +$graphics.Dispose() +$image.Dispose() +$stream.Dispose() + +$jpegEncoder = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | + Where-Object { $_.MimeType -eq 'image/jpeg' } +$encoderParams = [System.Drawing.Imaging.EncoderParameters]::new(1) +$encoderParams.Param[0] = [System.Drawing.Imaging.EncoderParameter]::new([System.Drawing.Imaging.Encoder]::Quality, [long]85) +$bitmap.Save($fullPath, $jpegEncoder, $encoderParams) +$bitmap.Dispose() + +# Same format as sips -g pixelHeight -g pixelWidth. +Write-Output $fullPath +Write-Output " pixelHeight: $height" +Write-Output " pixelWidth: $width"