A JavaScript/TypeScript implementation of text blind watermarking using zero-width Unicode characters
npm install text-blind-watermark-js


中文文档 | English
A JavaScript/TypeScript implementation of text blind watermarking, inspired by the Python text_blind_watermark library. This library allows you to embed invisible watermarks into text using zero-width Unicode characters.
- 🔒 Invisible Watermarks: Embed completely invisible watermarks using zero-width Unicode characters
- 🛡️ Password Protection: Secure your watermarks with password-based encryption
- 🌐 Cross-Platform: Works in browsers, Node.js, and various platforms (WeChat, DingTalk, etc.)
- 📱 Unicode Support: Full support for international text and emojis
- 🎯 TypeScript: Written in TypeScript with full type definitions
- 🔄 Python Compatible: API compatible with the original Python library
- 📦 Multiple Formats: Available as ES modules, CommonJS, and UMD builds
``bash`
npm install text-blind-watermark-js
Or using yarn:
`bash`
yarn add text-blind-watermark-js
`javascript
import { TextBlindWatermark } from 'text-blind-watermark-js'
// Create watermark instance
const watermark = new TextBlindWatermark({ password: 'your-secret-password' })
// Add watermark to text
const originalText = 'This is a sample text for watermarking.'
const secretMessage = 'Hidden watermark'
const textWithWatermark = watermark.addWatermarkRandom(originalText, secretMessage)
console.log('Text with watermark:', textWithWatermark)
// Output looks identical to original text!
// Extract watermark
const extractedMessage = watermark.extractAsString(textWithWatermark)
console.log('Extracted watermark:', extractedMessage)
// Output: "Hidden watermark"
`
For compatibility with the original Python library:
`javascript
import { TextBlindWatermark } from 'text-blind-watermark-js'
const watermark = new TextBlindWatermark({ password: 'test_password' })
// Using Python-style method names
const textWithWatermark = watermark.add_wm_rnd(originalText, secretMessage)
const extractedBytes = watermark.extract(textWithWatermark)
`
`html`
`typescript`
new TextBlindWatermark(options?: WatermarkOptions)
Options:
- password?: string | Uint8Array - Password for encryption (default: 'default_password')seed?: number
- - Random seed for reproducible watermark placementencoding?: 'hex' | 'binary'
- - Encoding scheme. Use 'binary' for mobile-safe zero-width characters
#### addWatermarkRandom(text: string, watermark: string | Uint8Array): string
Embeds a watermark into the text at random positions.
#### add_wm_rnd(text: string, wm: string | Uint8Array): string
Python-compatible alias for addWatermarkRandom.
#### extract(textWithWatermark: string): Uint8Array
Extracts the watermark as raw bytes.
#### extractAsString(textWithWatermark: string): string
Extracts the watermark as a UTF-8 string.
#### hasWatermark(text: string): boolean
Checks if the text contains a watermark.
#### removeWatermark(textWithWatermark: string): string
Removes the watermark from the text, returning clean text.
`javascript
import { TextBlindWatermark, stringToBytes, bytesToString } from 'text-blind-watermark-js'
const watermark = new TextBlindWatermark({ password: 'binary_password' })
// Embed binary data
const binaryData = stringToBytes('Secret binary message')
const textWithWatermark = watermark.addWatermarkRandom(originalText, binaryData)
// Extract binary data
const extractedBytes = watermark.extract(textWithWatermark)
const extractedString = bytesToString(extractedBytes)
`
`javascript
const watermark = new TextBlindWatermark({
password: 'secret',
seed: 12345 // Fixed seed for reproducible results
})
const result1 = watermark.addWatermarkRandom(text, message)
const result2 = watermark.addWatermarkRandom(text, message)
// result1 === result2 (same positions every time)
`
`javascript`
try {
const extracted = watermark.extractAsString(suspiciousText)
console.log('Watermark found:', extracted)
} catch (error) {
if (error.message.includes('No watermark found')) {
console.log('Text is clean')
} else {
console.error('Extraction failed:', error.message)
}
}
1. Password Strength: Use strong, unique passwords for each application
2. Password Storage: Never hardcode passwords in client-side code
3. Watermark Size: Longer watermarks require longer original text
4. Platform Testing: Test on target platforms (WeChat, email clients, etc.)
The library includes comprehensive tests covering:
- Basic watermark embedding and extraction
- Unicode and emoji support
- Password-based security
- Error handling
- Python library compatibility
`bash`
npm test
npm run test:coverage
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
1. Fork the repository
2. Create your feature branch (git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature'
3. Commit your changes ()git push origin feature/AmazingFeature
4. Push to the branch ()
5. Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Original Python implementation: text_blind_watermark by guofei9987
- Inspired by research in text steganography and zero-width character techniques
- text_blind_watermark - Original Python implementation
- blind_watermark - Image watermarking library
- HideInfo - Information hiding toolkit
- npm package
- GitHub repository
- Documentation
- Issues
Some mobile apps and fonts render certain zero-width or combining marks as visible dotted circles. If you see circles or dots in messages, switch to the mobile-safe encoding:
`javascript
import { TextBlindWatermark } from 'text-blind-watermark-js'
const wm = new TextBlindWatermark({ password: 'secret', encoding: 'binary' })
const withWm = wm.addWatermarkRandom('Hello', 'Hidden')
const extracted = wm.extractAsString(withWm)
`
The binary mode uses only U+200B (ZWSP) and U+200C` (ZWNJ), which are the most robust invisible characters across mobile platforms.