@miconvert/image-watermark
v1.0.1
Published
Add text or logo watermark to images in the browser. Client-side, fast, beautiful — no server needed. Supports tiled patterns, 9-point positioning, responsive scaling, and full opacity/rotation control.
Downloads
27
Maintainers
Readme
@miconvert/image-watermark
Add text or logo watermark to images right in the browser. Fast, beautiful, zero server dependency.
✨ Features
- Text Watermark — Custom font, size, color, stroke outline & drop shadow
- Image/Logo Watermark — Supports URL, File, Blob, or HTMLImageElement
- 9-Point Positioning —
top-left,top-center,top-right,center-left,center,center-right,bottom-left,bottom-center,bottom-right - Tiled Pattern Mode — Repeat watermark across entire image (anti-crop protection, like Shutterstock)
- Responsive Scaling — Logo/text auto-scales based on image resolution (no more tiny watermarks on 4K images)
- Rotation & Opacity — Full control over angle and transparency
- 🌍 Multilingual Font Auto-Loading — Auto-detects script (CJK, Arabic, Thai, Vietnamese, Cyrillic, Devanagari...) and loads the correct Google Noto font. No more □□□ tofu boxes!
- 100% Client-Side — Uses HTML5 Canvas, no server required
- TypeScript — Full type definitions included
📦 Installation
npm install @miconvert/image-watermark🚀 Quick Start
Text Watermark
import { addWatermark } from '@miconvert/image-watermark';
const watermarked = await addWatermark(imageFile, {
type: 'text',
text: '© Miconvert Property',
position: 'bottom-right',
fontSize: 48,
color: 'rgba(255, 255, 255, 0.5)',
strokeColor: 'black',
strokeWidth: 2,
padding: 20,
});
// Display result
const url = URL.createObjectURL(watermarked);
document.getElementById('preview').src = url;Logo Watermark
// Logo auto-scales to 15% of image width
const watermarked = await addWatermark(imageFile, {
type: 'image',
source: './logo.png', // URL string
// imageElement: logoBlob, // or File/Blob/HTMLImageElement
position: 'bottom-right',
width: 0.15, // 15% of image width
opacity: 0.7,
padding: 20,
});Tiled Pattern (Anti-Crop Protection)
const watermarked = await addWatermark(imageFile, {
type: 'text',
text: 'Bản quyền thuộc Miconvert',
mode: 'tiled',
opacity: 0.3,
rotate: -45,
fontSize: 36,
color: 'rgba(255, 255, 255, 0.4)',
});📖 API Reference
addWatermark(file, options): Promise<Blob>
| Parameter | Type | Description |
|-----------|------|-------------|
| file | File \| Blob | Source image |
| options | WatermarkOptions | Configuration object |
Common Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| type | 'text' \| 'image' | — | Required. Watermark type |
| position | WatermarkPosition | 'bottom-right' | Anchor position (9 presets) |
| mode | 'single' \| 'tiled' | 'single' | Single stamp or tiled pattern |
| opacity | number | 1.0 | Transparency (0.0–1.0) |
| rotate | number | 0 | Rotation in degrees |
| padding | number | 20 | Distance from image edge (px) |
| offsetX | number | 0 | Extra horizontal offset (px) |
| offsetY | number | 0 | Extra vertical offset (px) |
| scale | number | — | Responsive scale factor |
| tileSpacingX | number | 100 | Horizontal tile gap (px) |
| tileSpacingY | number | 80 | Vertical tile gap (px) |
Text-Specific Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| text | string | — | Required. Text content |
| fontFamily | string | 'Arial, Helvetica, sans-serif' | CSS font family |
| fontSize | number | 48 | Font size in pixels |
| fontWeight | string | 'bold' | 'normal', 'bold', etc. |
| fontStyle | string | 'normal' | 'normal', 'italic' |
| color | string | 'rgba(255,255,255,0.5)' | Fill color (any CSS color) |
| strokeColor | string | — | Outline color |
| strokeWidth | number | 2 | Outline width (px) |
| shadowColor | string | 'rgba(0,0,0,0.5)' | Drop shadow color |
| shadowBlur | number | 4 | Shadow blur radius |
Image-Specific Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| source | string | — | Logo URL |
| imageElement | File \| Blob \| HTMLImageElement | — | Logo as File/Blob/Element |
| width | number | 0.15 | Logo width as fraction of image width (0.0–1.0) |
🎯 Position Map
┌─────────────┬─────────────┬─────────────┐
│ top-left │ top-center │ top-right │
├─────────────┼─────────────┼─────────────┤
│ center-left │ center │center-right │
├─────────────┼─────────────┼─────────────┤
│ bottom-left │bottom-center│bottom-right │
└─────────────┴─────────────┴─────────────┘🌍 Multilingual Font Support
Text watermarks automatically detect the language/script of your text and load the correct Google Noto font via the FontFace API. No manual font configuration needed!
| Script | Languages | Auto-loaded Font | |--------|-----------|-----------------| | CJK (Simplified Chinese) | Chinese | Noto Sans SC | | Japanese | Japanese | Noto Sans JP | | Korean | Korean | Noto Sans KR | | Arabic | Arabic, Persian, Urdu | Noto Sans Arabic | | Devanagari | Hindi, Sanskrit, Marathi | Noto Sans Devanagari | | Bengali | Bengali, Assamese | Noto Sans Bengali | | Thai | Thai | Noto Sans Thai | | Vietnamese | Vietnamese | Noto Sans | | Cyrillic | Russian, Ukrainian | Noto Sans | | Hebrew | Hebrew | Noto Sans Hebrew | | Tamil | Tamil | Noto Sans Tamil | | Latin | English, European | Noto Sans |
Custom Font Loading
import { loadCustomFont, addWatermark } from '@miconvert/image-watermark';
// Load a custom font from URL
await loadCustomFont('MyBrand', 'https://example.com/fonts/mybrand.woff2');
const result = await addWatermark(imageFile, {
type: 'text',
text: 'My Brand™',
fontFamily: 'MyBrand',
// ...other options
});⚠️ Important Notes
- EXIF metadata is lost — Canvas processing strips EXIF data (GPS, camera info, orientation). Modern browsers auto-apply EXIF orientation before drawing, so images will look correct, but the metadata is not preserved in the output.
- iOS Safari limit — Images larger than ~16.7 megapixels are automatically downscaled to prevent Safari crashes. A console warning is logged when this happens.
- SSR / Node.js — This library requires a browser environment (DOM + Canvas). In Next.js/Nuxt, use
dynamic import()or checktypeof window !== 'undefined'before importing. - Multiline text — Use
\nin your text string for multi-line watermarks. Example:text: '© Company\nAll Rights Reserved' - Animated GIF — Only the first frame is watermarked. Canvas API does not support multi-frame GIF rendering.
- AVIF output — When using
outputType: 'image/avif', color quality depends on your browser's AVIF encoder. Chrome 121+ and Safari 17+ produce good results.
🌐 Browser Support
Works in all modern browsers that support HTMLCanvasElement.toBlob() and FontFace API:
Chrome 50+, Firefox 19+, Safari 11+, Edge 79+
🤝 Support
For bug reports and feature requests, please visit miconvert.com/en/contact.
