mirror of
https://github.com/Wyrrrd/serialize-mtg.git
synced 2026-05-29 16:42:28 +02:00
Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/input
|
||||
/output
|
||||
@@ -0,0 +1,27 @@
|
||||
# Serialize MTG
|
||||
|
||||

|
||||
|
||||
## Overview
|
||||
This script bulk overlays a box with numbers over input images to have serialized versions of them, just as WotC does it. The concept includes handling of any input image size (assuming it is roughly a scaled 63mm x 88mm MTG card design). As the box to be applied is taken from a source image, there needs to be two pixel spaces: reference space (1500px x 2100px) and image space (defined by the input image). They will be transformed via an automatically calculated scale. This scale is not x/y separate and only a best effort to not squash the box image, so it might break with input images too far removed from the MTG ratio.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Clone or download repository
|
||||
2. Install [ImageMagick](https://imagemagick.org/)
|
||||
3. Run
|
||||
|
||||
## Parameters
|
||||
|
||||
```
|
||||
.\Serialize_MTG.ps1 -TotalCount 100 [-Offset @(10,20)] [-UseColorMap] [-FontPath arial.ttf]
|
||||
```
|
||||
|
||||
* **TotalCount (required)**: describes, how many versions of each card with incrementing numbers will be created.
|
||||
* **Offset (optional)**: overrides the location of the upper left corner of the serial box on the cards. This will apply to all inputs, so if you need different offsets, run the script once for each different offset.
|
||||
* **UseColorMap (optional)**: controls, if the white numbers and box border parts should be overlayed with an image. If you want to customize, replace colormap.png in the repository root directory.
|
||||
* **FontPath (optional)**: overrides the default font for the numbers.
|
||||
|
||||
## Credits
|
||||
|
||||
The Gotham font family was designed by Tobias Frere-Jones & Jesse Ragan. It is free for personal use. If you want to use it commercially, you need to license it.
|
||||
@@ -0,0 +1,290 @@
|
||||
# Serialize_MTG.ps1
|
||||
# © Wyrrrd 30.01.2026
|
||||
|
||||
# This script bulk overlays a box with numbers over input images to have serialized versions of them, just as WotC does it.
|
||||
# The concept includes handling of any input image size (assuming it is roughly a scaled 63mm x 88mm MTG card design).
|
||||
# As the box to be applied is taken from a source image, there needs to be two pixel spaces: reference space (1500px x 2100px)
|
||||
# and image space (defined by the input image). They will be transformed via an automatically calculated scale. This scale
|
||||
# is not x/y separate and only a best effort to not squash the box image, so it might break with input images too far removed
|
||||
# from the MTG ratio.
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
param (
|
||||
# Total count of serialization, so how many versions you want of a card
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int] $TotalCount,
|
||||
|
||||
# Font of the numbers in serial box
|
||||
# BEWARE: The Gotham font family is only free for personal use. If you want to use it commercially, you have to license it.
|
||||
[string] $FontPath = "gothambold.otf",
|
||||
|
||||
# Offsets as tuples @(x,y)
|
||||
# supplied in final image pixels (not in reference space)
|
||||
[int[]] $Offset,
|
||||
|
||||
# Apply color map using colormap.png
|
||||
[switch] $UseColorMap
|
||||
)
|
||||
|
||||
|
||||
### Paths & constants
|
||||
|
||||
# Path to magick executable, or just "magick" if it's in the PATH.
|
||||
$magick = "magick"
|
||||
|
||||
# Static directories and files
|
||||
$inputDir = "input"
|
||||
$outputDir = "output"
|
||||
$backgroundImg = "background.png"
|
||||
$colormapImg = "colormap.png"
|
||||
|
||||
# Reference card resolution
|
||||
$refResolution = @{ Width=1500; Height=2100 }
|
||||
|
||||
# Hardcoded background / color map reference size and offset
|
||||
# background.png and color map.png MUST match size
|
||||
# offset will be overridden by Offset parameter later in image space, if supplied
|
||||
$backgroundBoxRef = @{ X=128; Y=1032; Width=346; Height=107 }
|
||||
|
||||
# Text boxes (in reference space, relative to background box)
|
||||
$currentBoxRef = @{ X=25; Y=24; Width=135; Height=63 }
|
||||
$totalBoxRef = @{ X=190; Y=24; Width=135; Height=63 }
|
||||
|
||||
|
||||
### Functions
|
||||
|
||||
function DisplayFileSize {
|
||||
param (
|
||||
$bytecount
|
||||
)
|
||||
|
||||
switch -Regex ([math]::truncate([math]::log($bytecount,1024))) {
|
||||
'^0' {"$bytecount Bytes"}
|
||||
'^1' {"{0:n2} KB" -f ($bytecount / 1KB)}
|
||||
'^2' {"{0:n2} MB" -f ($bytecount / 1MB)}
|
||||
'^3' {"{0:n2} GB" -f ($bytecount / 1GB)}
|
||||
'^4' {"{0:n2} TB" -f ($bytecount / 1TB)}
|
||||
Default {"{0:n2} Bytes" -f ($bytecount / 1KB)}
|
||||
}
|
||||
}
|
||||
|
||||
function ScaleBox {
|
||||
param (
|
||||
$box
|
||||
)
|
||||
|
||||
@{
|
||||
X = [int]($box.X * $scale)
|
||||
Y = [int]($box.Y * $scale)
|
||||
Width = [int]($box.Width * $scale)
|
||||
Height = [int]($box.Height * $scale)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
########
|
||||
# MAIN #
|
||||
########
|
||||
|
||||
|
||||
### Prepare input/output directory
|
||||
|
||||
if (-not (Test-Path $inputDir)) {
|
||||
New-Item -ItemType Directory -Path $inputDir | Out-Null
|
||||
}
|
||||
|
||||
$inputFiles = Get-ChildItem $inputDir -File | Where-Object {
|
||||
$_.Extension -match '\.(png|jpg|jpeg)$'
|
||||
}
|
||||
|
||||
# Wait for input files to be added
|
||||
while ($inputFiles.Count -eq 0) {
|
||||
Write-Host "Input directory is empty."
|
||||
Write-Host "Please place input images into '$inputDir' directory, then continue."
|
||||
start $inputDir
|
||||
Pause
|
||||
$inputFiles = Get-ChildItem $inputDir -File | Where-Object {
|
||||
$_.Extension -match '\.(png|jpg|jpeg)$'
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path $outputDir)) {
|
||||
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
|
||||
}
|
||||
|
||||
|
||||
### Loop over input images
|
||||
|
||||
foreach ($inputFile in $inputFiles) {
|
||||
|
||||
$baseName = [IO.Path]::GetFileNameWithoutExtension($inputFile.Name)
|
||||
$ext = $inputFile.Extension
|
||||
$outputSubDir = Join-Path $outputDir $baseName
|
||||
|
||||
if (-not (Test-Path $outputSubDir)) {
|
||||
New-Item -ItemType Directory -Force -Path $outputSubDir | Out-Null
|
||||
}
|
||||
|
||||
# Input image size & scaling
|
||||
$imageInfo = & $magick identify -format "%w %h" $inputFile.FullName
|
||||
$image = @{}
|
||||
$image.Width, $image.Height = $imageInfo -split " " | ForEach-Object { [int]$_ }
|
||||
$scaleX = $image.Width / $refResolution.Width
|
||||
$scaleY = $image.Height / $refResolution.Height
|
||||
$scale = [Math]::Min($scaleX, $scaleY)
|
||||
|
||||
|
||||
### Transform box positions and dimensions
|
||||
|
||||
$backgroundBox = ScaleBox $backgroundBoxRef
|
||||
|
||||
# Override with offset, if supplied
|
||||
if (-not $useDefaultOffset -and $Offset) {
|
||||
if ($Offset.Count -eq 2) {
|
||||
$backgroundBox.X = $Offset[0]
|
||||
$backgroundBox.Y = $Offset[1]
|
||||
} else {
|
||||
"`nThe given offset was malformed, please enter it as '@(x,y)'.`nDo you want to continue with the default offset?" | Write-Warning -WarningAction Inquire
|
||||
$useDefaultOffset = true
|
||||
}
|
||||
}
|
||||
|
||||
$currentBox = ScaleBox $currentBoxRef
|
||||
$totalBox = ScaleBox $totalBoxRef
|
||||
|
||||
### Sanity checks
|
||||
|
||||
# Total file size >500MB check
|
||||
if ($inputFile.Length * $totalCount -gt 524288000) {
|
||||
$totalFileSize = DisplayFileSize($inputFile.Length * $totalCount)
|
||||
"`nYou specified a total count of $totalCount for $inputFile.`nThis would result in new image files using up approximately $totalFileSize of your disk." | Write-Warning -WarningAction Inquire
|
||||
}
|
||||
|
||||
Write-Host "Starting to serialize $inputFile."
|
||||
|
||||
# Out of bounds check
|
||||
if ( $backgroundBox.X + $backgroundBox.Width -gt $image.Width -or $backgroundBox.Y + $backgroundBox.Height -gt $image.Height) {
|
||||
if (-not $useDefaultOffset -and $Offset) {
|
||||
Write-Host -NoNewline "The given "
|
||||
} else {
|
||||
Write-Host -NoNewline "The default "
|
||||
}
|
||||
Write-Host "offset would cause the serial box to be (partially) outside of the input image's boundaries.`nSkipping input image $inputFile."
|
||||
continue
|
||||
} else {
|
||||
Write-Host -NoNewline "Using "
|
||||
if (-not $useDefaultOffset -and $Offset) {
|
||||
Write-Host -NoNewline "given "
|
||||
} else {
|
||||
Write-Host -NoNewline "scaled default "
|
||||
}
|
||||
Write-Host -NoNewline "offset of '@($($backgroundBox.X),$($backgroundBox.Y))'"
|
||||
if ($UseColorMap) {
|
||||
Write-Host -NoNewline " with color map"
|
||||
}
|
||||
Write-Host "."
|
||||
}
|
||||
|
||||
|
||||
### Temporary files
|
||||
|
||||
$currentFile = Join-Path $outputSubDir "_current.png"
|
||||
$totalFile = Join-Path $outputSubDir "_total.png"
|
||||
$boxFile = Join-Path $outputSubDir "_box.png"
|
||||
|
||||
|
||||
### Generate total number file
|
||||
|
||||
$totalStr = $TotalCount.ToString()
|
||||
|
||||
& $magick `
|
||||
-size "$($totalBox.Width)x$($totalBox.Height)" `
|
||||
-background none `
|
||||
-fill white `
|
||||
-font $FontPath `
|
||||
-kerning 3 `
|
||||
-gravity center `
|
||||
caption:"$totalStr" `
|
||||
$totalFile
|
||||
|
||||
|
||||
### Serialization padding
|
||||
|
||||
$padLength = $totalStr.Length
|
||||
|
||||
|
||||
### Per-card generation of current number
|
||||
|
||||
for ($i = 1; $i -le $TotalCount; $i++) {
|
||||
|
||||
|
||||
### Generate current number file
|
||||
|
||||
$currentStr = $i.ToString("D$padLength")
|
||||
|
||||
& $magick `
|
||||
-size "$($currentBox.Width)x$($currentBox.Height)" `
|
||||
-background none `
|
||||
-fill white `
|
||||
-font $FontPath `
|
||||
-kerning 3 `
|
||||
-gravity center `
|
||||
caption:"$currentStr" `
|
||||
$currentFile
|
||||
|
||||
|
||||
### Output file
|
||||
|
||||
$outputFile = Join-Path $outputSubDir ("{0}_{1}{2}" -f $baseName, $currentStr, $ext)
|
||||
|
||||
|
||||
### Compose file
|
||||
|
||||
# Create box with numbers
|
||||
& $magick `
|
||||
"(" $backgroundImg -resize "$($backgroundBox.Width)x$($backgroundBox.Height)!" ")" `
|
||||
$currentFile -geometry +$($currentBox.X)+$($currentBox.Y) -composite `
|
||||
$totalFile -geometry +$($totalBox.X)+$($totalBox.Y) -composite `
|
||||
$boxFile
|
||||
|
||||
if ($UseColorMap) {
|
||||
# Convert to mask
|
||||
& $magick `
|
||||
$boxFile -background black -alpha remove -alpha off `
|
||||
$boxFile
|
||||
|
||||
# Mask color map
|
||||
& $magick `
|
||||
"(" $colormapImg -resize "$($backgroundBox.Width)x$($backgroundBox.Height)!" ")" `
|
||||
$boxFile -compose CopyOpacity -composite `
|
||||
$boxFile
|
||||
|
||||
# Re-add background
|
||||
& $magick `
|
||||
"(" $backgroundImg -resize "$($backgroundBox.Width)x$($backgroundBox.Height)!" ")" `
|
||||
$boxFile -colorspace srgb -composite `
|
||||
$boxFile
|
||||
}
|
||||
|
||||
# Compose over input image at offset location
|
||||
& $magick `
|
||||
$inputFile.FullName `
|
||||
$boxFile -geometry +$($backgroundBox.X)+$($backgroundBox.Y) -composite `
|
||||
$outputFile
|
||||
|
||||
Write-Host -NoNewline "`r($i/$totalCount)"
|
||||
}
|
||||
|
||||
|
||||
### Cleanup
|
||||
|
||||
Remove-Item $currentFile
|
||||
Remove-Item $totalFile
|
||||
Remove-Item $boxFile
|
||||
Write-Host "`n"
|
||||
}
|
||||
|
||||
Write-Host "All cards serialized successfully."
|
||||
start $outputDir
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 404 KiB |
Binary file not shown.
Reference in New Issue
Block a user