Skip to content

ishk9/stampy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stampy

Turn any photo into a perforated postage-stamp PNG, from the command line.

Stampy is a TypeScript port of the original Python stampy tool. It takes an arbitrary input image, fits it into a stamp-shaped canvas, applies an optional photo effect, draws a themed border with a caption and denomination, and punches perforation holes around the edge so the result looks like a real postage stamp.

Install

npm install -g @ishk9/stampy
# or, run without installing:
npx @ishk9/stampy

Quick start

stampy                    # interactive wizard
stampy create photo.jpg   # one-shot

If -o is omitted the output is written next to the source as <input>-stamp.png.

Usage

stampy [--verbose]
       create INPUT [-o OUTPUT] [-c "CAPTION"] [-d "$1"]
              [-t THEME] [-e EFFECT]
              [-W INNER_W] [-H INNER_H] [-b BORDER]
              [--no-perforations] [--perf-radius N] [--perf-spacing N]
       themes
       effects
       wizard

Examples

stampy
stampy create cat.jpg
stampy create cat.jpg -o cat-stamp.png -c "POSTES * PARIS" -d "$1"
stampy create cat.jpg -t midnight -e vintage --perf-spacing 38
stampy create cat.jpg --no-perforations -W 1200 -H 1200

Themes

Key Label Border Background
classic Classic Red rgb(180, 30, 50) rgb(255, 252, 240)
midnight Midnight Blue rgb(25, 55, 120) rgb(245, 248, 255)
forest Forest Green rgb(35, 95, 60) rgb(250, 252, 245)
ink Ink Black rgb(30, 30, 30) rgb(252, 250, 245)
burgundy Burgundy Gold rgb(110, 25, 50) rgb(252, 244, 220)

Run stampy themes to print this table from the live registry.

Effects

Key Description
none Pass-through; the photo's tones are preserved as-is.
sepia Warm, brown-toned colorisation.
grayscale Luminance-only conversion.
vintage Faded, low-contrast colorisation with a warm cast.

Run stampy effects to print this table.

Architecture

src/
├── cli.ts                          # CLI entrypoint (commander)
├── cli/commands/                   # `create`, `themes`, `effects`, `wizard`
├── application/
│   ├── makeStamp.ts                # MakeStampUseCase
│   └── pipelineBuilder.ts          # StampPipelineBuilder
├── domain/
│   ├── models.ts                   # Effect, StampSpec, makeStampSpec
│   ├── themes.ts                   # ThemeRegistry, defaultThemeRegistry
│   └── errors.ts                   # StampyError hierarchy
├── effects/
│   ├── base.ts                     # EffectStrategy interface
│   ├── strategies.ts               # None / Sepia / Grayscale / Vintage
│   └── registry.ts                 # EffectRegistry, defaultEffectRegistry
├── pipeline/
│   ├── base.ts                     # StampImage, StampProcessor, Pipeline
│   ├── orientation.ts              # EXIF normalisation hook
│   ├── effect.ts                   # Effect step (delegates to strategy)
│   ├── fit.ts                      # Cover-fit into innerSize
│   ├── frame.ts                    # Coloured border + concentric strokes
│   ├── caption.ts                  # Top caption text
│   ├── denomination.ts             # Bottom-corner price chip
│   └── perforation.ts              # Punch transparent holes
├── rendering/
│   ├── fonts.ts                    # FontProvider, SystemFontProvider
│   └── canvas.ts                   # @napi-rs/canvas helpers
└── infrastructure/
    └── imageRepository.ts          # SharpImageRepository (load / save)

Patterns at a glance

  • StrategyEffectStrategy lets each photo effect (NoneEffect, SepiaEffect, GrayscaleEffect, VintageEffect) implement one tonal transform with a single-method interface.
  • RegistryEffectRegistry and ThemeRegistry are the only places that know about all available strategies and themes; consumers ask by key.
  • Pipes-and-Filters — the rendering pipeline is a sequence of StampProcessor steps that pass StampImage from one to the next.
  • CompositePipeline implements StampProcessor, so a whole pipeline is interchangeable with a single step.
  • BuilderStampPipelineBuilder hides the construction order and dependencies of the default pipeline.
  • RepositoryImageRepository abstracts I/O; only SharpImageRepository knows about sharp.
  • Use CaseMakeStampUseCase is the single application entrypoint and depends only on abstractions.
  • Command — each CLI subcommand is its own module under cli/commands/.
  • Dependency Injection — the CLI wires the registries, font provider, repository, and pipeline at the composition root and passes them down through constructors.

SOLID

Principle Where it shows up
SRP Each pipeline step does one thing (FitProcessor resizes, FrameProcessor draws the border, etc.).
OCP Adding a new effect is a registry entry plus a strategy class — no other code changes.
LSP All StampProcessor implementations are interchangeable; Pipeline is one.
ISP Narrow interfaces (EffectStrategy.apply, FontProvider.fontSpec, ImageRepository.load/save).
DIP MakeStampUseCase depends on ImageRepository and StampProcessor interfaces, not on sharp or @napi-rs/canvas.

Library use

import {
  defaultEffectRegistry,
  defaultThemeRegistry,
  Effect,
  makeStampSpec,
  MakeStampUseCase,
  SharpImageRepository,
  StampPipelineBuilder,
  SystemFontProvider,
} from "@ishk9/stampy";

const themes = defaultThemeRegistry();
const effects = defaultEffectRegistry();
const fonts = new SystemFontProvider();

const repository = new SharpImageRepository();
const pipeline = new StampPipelineBuilder(effects, fonts).build();
const useCase = new MakeStampUseCase(repository, pipeline);

const spec = makeStampSpec({
  theme: themes.get("classic"),
  caption: "POSTES",
  denomination: "$1",
  effect: Effect.SEPIA,
});

const result = await useCase.execute("cat.jpg", "cat-stamp.png", spec);
console.log(`wrote ${result.outputPath} (${result.width} x ${result.height})`);

A runnable version of this snippet lives at examples/library-use.ts.

Development

npm install
npm run dev -- create cat.jpg     # via tsx
npm run build && npm start -- create cat.jpg
npm test

The test suite is vitest plus tsx-style ESM resolution, so local imports keep their .js extensions even though the sources are .ts.

About

A CLI that turns photos into postage stamps. Real perforated edges, a few themes, optional sepia/vintage filters.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors