charify 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -0
- package/assets/charify.svg +12 -0
- package/bin/charify.js +205 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
High-quality image to ASCII art converter for the command line.
|
|
4
|
+
npm-ready. No dependencies on Unix tools.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
Global install:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g charify
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Run without installing:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx charify image.png
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
charify image.png
|
|
28
|
+
charify image.jpg -w 120
|
|
29
|
+
charify image.png -o output.txt
|
|
30
|
+
charify image.png --html -o output.html
|
|
31
|
+
charify --link https://example.com/image.jpg
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Options
|
|
37
|
+
|
|
38
|
+
| Option | Description |
|
|
39
|
+
| ------------ | ---------------------------------------------------------- |
|
|
40
|
+
| -w, --width | Output width (default: 100) |
|
|
41
|
+
| --ratio | Height/width ratio (default: 0.55) |
|
|
42
|
+
| --gamma | Gamma correction (default: 2.2) |
|
|
43
|
+
| --invert | Invert brightness |
|
|
44
|
+
| --html | Export as HTML (only works if output file ends with .html) |
|
|
45
|
+
| -o, --output | Output file (.txt or .html) |
|
|
46
|
+
| --link | Load image from URL |
|
|
47
|
+
| --charset | Custom ASCII charset |
|
|
48
|
+
| --preset | Use a preset: dense, light, blocks, minimal |
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Presets
|
|
53
|
+
|
|
54
|
+
| Name | Description |
|
|
55
|
+
| ------- | ---------------------- |
|
|
56
|
+
| dense | Best quality (default) |
|
|
57
|
+
| light | Softer output |
|
|
58
|
+
| blocks | Block-style ASCII |
|
|
59
|
+
| minimal | Very simple |
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
charify image.png --preset blocks
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Custom Charset
|
|
70
|
+
|
|
71
|
+
Specify your own charset to control ASCII output characters:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
charify image.png --charset " .:-=+*#%@"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## HTML Export
|
|
80
|
+
|
|
81
|
+
To export ASCII art wrapped in an HTML page, use `--html` **with** an output file ending in `.html`:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
charify image.png --html -o art.html
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If you use `--html` without specifying an output file ending with `.html`, it will be ignored and plain ASCII will be output instead.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Input from URL
|
|
92
|
+
|
|
93
|
+
Load image from the internet:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
charify --link https://example.com/image.jpg
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
You can combine with other options like `--html` and `-o`:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
charify --link https://example.com/image.jpg --html -o output.html
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Quality Tuning
|
|
108
|
+
|
|
109
|
+
Adjust gamma correction and character aspect ratio:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
charify image.png --gamma 2.0 --ratio 0.6
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Invert Brightness
|
|
118
|
+
|
|
119
|
+
Invert the brightness mapping:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
charify image.png --invert
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Local Development
|
|
128
|
+
|
|
129
|
+
Run directly without installing:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
node bin/charify.js image.png
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Donate
|
|
138
|
+
|
|
139
|
+
If you find **charify** useful and want to support development, you can donate any amount here:
|
|
140
|
+
|
|
141
|
+
[](https://paypal.me/Abdoelsayd81)
|
|
142
|
+
|
|
143
|
+
Thank you for your support!
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
+
<svg width="100%" height="100%" viewBox="0 0 512 127" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
4
|
+
<g transform="matrix(1,0,0,1,0,-6.755875)">
|
|
5
|
+
<g transform="matrix(1,-0,-0,1,-0,6.755875)">
|
|
6
|
+
<use xlink:href="#_Image1" x="0" y="0" width="512px" height="127px"/>
|
|
7
|
+
</g>
|
|
8
|
+
</g>
|
|
9
|
+
<defs>
|
|
10
|
+
<image id="_Image1" width="512px" height="127px" xlink:href="data:image/png;base64,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"/>
|
|
11
|
+
</defs>
|
|
12
|
+
</svg>
|
package/bin/charify.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import https from "https";
|
|
6
|
+
import sharp from "sharp";
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
|
|
9
|
+
/* ================= CONFIG ================= */
|
|
10
|
+
|
|
11
|
+
const PRESETS = {
|
|
12
|
+
dense: "█▓▒░@%#*+=-:. ",
|
|
13
|
+
light: "#*+=-:. ",
|
|
14
|
+
blocks: "█▓▒░ ",
|
|
15
|
+
minimal: "#.+ ",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DEFAULT_PRESET = "dense";
|
|
19
|
+
const DEFAULT_WIDTH = 100;
|
|
20
|
+
const DEFAULT_RATIO = 0.55;
|
|
21
|
+
const DEFAULT_GAMMA = 2.2;
|
|
22
|
+
|
|
23
|
+
/* ================= CLI ================= */
|
|
24
|
+
|
|
25
|
+
const program = new Command();
|
|
26
|
+
|
|
27
|
+
program
|
|
28
|
+
.name("charify")
|
|
29
|
+
.description("Convert images to ASCII art")
|
|
30
|
+
.argument("[image]", "local image path")
|
|
31
|
+
.option("--link <url>", "image URL")
|
|
32
|
+
.option("-w, --width <number>", "output width", String(DEFAULT_WIDTH))
|
|
33
|
+
.option("--ratio <number>", "height/width ratio", String(DEFAULT_RATIO))
|
|
34
|
+
.option("--gamma <number>", "gamma correction", String(DEFAULT_GAMMA))
|
|
35
|
+
.option("--charset <chars>", "custom ASCII charset")
|
|
36
|
+
.option("--preset <name>", "preset: dense | light | blocks | minimal")
|
|
37
|
+
.option("--invert", "invert brightness")
|
|
38
|
+
.option(
|
|
39
|
+
"--html",
|
|
40
|
+
"export as HTML (only works if output file ends with .html)"
|
|
41
|
+
)
|
|
42
|
+
.option("-o, --output <file>", "output file (.txt or .html)");
|
|
43
|
+
program
|
|
44
|
+
.addHelpText(
|
|
45
|
+
"before",
|
|
46
|
+
`
|
|
47
|
+
_ _ __
|
|
48
|
+
___| |__ __ _ _ __(_)/ _|_ _
|
|
49
|
+
/ __| '_ \\ / _' | '__| | |_| | | |
|
|
50
|
+
| (__| | | | (_| | | | | _| |_| |
|
|
51
|
+
\\___|_| |_|\\__,_|_| |_|_| \\__, |
|
|
52
|
+
|___/
|
|
53
|
+
`
|
|
54
|
+
)
|
|
55
|
+
.parse(process.argv);
|
|
56
|
+
|
|
57
|
+
const opts = program.opts();
|
|
58
|
+
const inputPath = program.args[0];
|
|
59
|
+
|
|
60
|
+
if (!opts.link && !inputPath) {
|
|
61
|
+
console.error("❌ Provide an image path or --link URL");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ================= HELPERS ================= */
|
|
66
|
+
|
|
67
|
+
function fetchImage(url) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
https.get(url, (res) => {
|
|
70
|
+
const chunks = [];
|
|
71
|
+
res.on("data", (d) => chunks.push(d));
|
|
72
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
73
|
+
res.on("error", reject);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function gammaCorrect(v, gamma) {
|
|
79
|
+
return Math.pow(v / 255, 1 / gamma) * 255;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeBuffer(buffer) {
|
|
83
|
+
let min = 255,
|
|
84
|
+
max = 0;
|
|
85
|
+
for (const v of buffer) {
|
|
86
|
+
if (v < min) min = v;
|
|
87
|
+
if (v > max) max = v;
|
|
88
|
+
}
|
|
89
|
+
if (max === min) return buffer;
|
|
90
|
+
|
|
91
|
+
return Buffer.from(
|
|
92
|
+
buffer.map((v) => Math.round(((v - min) / (max - min)) * 255))
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function toAscii(buffer, width, charset, invert, gamma) {
|
|
97
|
+
let out = "";
|
|
98
|
+
const len = charset.length - 1;
|
|
99
|
+
|
|
100
|
+
for (let i = 0; i < buffer.length; i += width) {
|
|
101
|
+
for (let x = 0; x < width; x++) {
|
|
102
|
+
let v = buffer[i + x];
|
|
103
|
+
v = gammaCorrect(v, gamma);
|
|
104
|
+
const idx = Math.floor((v / 255) * len);
|
|
105
|
+
out += invert ? charset[len - idx] : charset[idx];
|
|
106
|
+
}
|
|
107
|
+
out += "\n";
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function wrapHtml(ascii) {
|
|
113
|
+
return `<!doctype html>
|
|
114
|
+
<html>
|
|
115
|
+
<head>
|
|
116
|
+
<meta charset="utf-8">
|
|
117
|
+
<title>charify</title>
|
|
118
|
+
<style>
|
|
119
|
+
body {
|
|
120
|
+
background: #000;
|
|
121
|
+
color: #0f0;
|
|
122
|
+
font-family: monospace;
|
|
123
|
+
white-space: pre;
|
|
124
|
+
line-height: 1;
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
127
|
+
</head>
|
|
128
|
+
<body>${ascii}</body>
|
|
129
|
+
</html>`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* ================= MAIN ================= */
|
|
133
|
+
|
|
134
|
+
(async () => {
|
|
135
|
+
try {
|
|
136
|
+
const width = Number(opts.width);
|
|
137
|
+
const ratio = Number(opts.ratio);
|
|
138
|
+
const gamma = Number(opts.gamma);
|
|
139
|
+
|
|
140
|
+
const charset =
|
|
141
|
+
opts.charset || PRESETS[opts.preset] || PRESETS[DEFAULT_PRESET];
|
|
142
|
+
|
|
143
|
+
const inputBuffer = opts.link
|
|
144
|
+
? await fetchImage(opts.link)
|
|
145
|
+
: fs.readFileSync(path.resolve(inputPath));
|
|
146
|
+
|
|
147
|
+
let sharpInstance = sharp(inputBuffer)
|
|
148
|
+
.resize({
|
|
149
|
+
width,
|
|
150
|
+
fit: sharp.fit.inside,
|
|
151
|
+
withoutEnlargement: true,
|
|
152
|
+
})
|
|
153
|
+
.grayscale();
|
|
154
|
+
|
|
155
|
+
const metadata = await sharpInstance.metadata();
|
|
156
|
+
|
|
157
|
+
const adjustedHeight = Math.round(metadata.height * ratio);
|
|
158
|
+
|
|
159
|
+
sharpInstance = sharpInstance.resize(width, adjustedHeight);
|
|
160
|
+
|
|
161
|
+
const { data, info } = await sharpInstance
|
|
162
|
+
.raw()
|
|
163
|
+
.toBuffer({ resolveWithObject: true });
|
|
164
|
+
|
|
165
|
+
const normalizedData = normalizeBuffer(data);
|
|
166
|
+
|
|
167
|
+
const ascii = toAscii(
|
|
168
|
+
normalizedData,
|
|
169
|
+
info.width,
|
|
170
|
+
charset,
|
|
171
|
+
opts.invert,
|
|
172
|
+
gamma
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
let output = ascii;
|
|
176
|
+
const wantHtml =
|
|
177
|
+
opts.html && opts.output && opts.output.toLowerCase().endsWith(".html");
|
|
178
|
+
|
|
179
|
+
if (wantHtml) {
|
|
180
|
+
output = wrapHtml(ascii);
|
|
181
|
+
} else if (
|
|
182
|
+
opts.html &&
|
|
183
|
+
(!opts.output || !opts.output.toLowerCase().endsWith(".html"))
|
|
184
|
+
) {
|
|
185
|
+
if (!opts.output) {
|
|
186
|
+
output = ascii;
|
|
187
|
+
} else {
|
|
188
|
+
console.warn(
|
|
189
|
+
"⚠ Warning: --html ignored because output file extension is not .html"
|
|
190
|
+
);
|
|
191
|
+
output = ascii;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (opts.output) {
|
|
196
|
+
fs.writeFileSync(opts.output, output, "utf8");
|
|
197
|
+
console.log("✔ Saved:", opts.output);
|
|
198
|
+
} else {
|
|
199
|
+
process.stdout.write(output);
|
|
200
|
+
}
|
|
201
|
+
} catch (err) {
|
|
202
|
+
console.error("❌ Error:", err.message);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
})();
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "charify",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "High-quality image to ASCII art CLI",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ascii",
|
|
7
|
+
"ascii",
|
|
8
|
+
"art"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/abdodev/charify#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/abdodev/charify/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/abdodev/charify.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "AbdoDev",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "/bin/charify.js",
|
|
22
|
+
"bin": {
|
|
23
|
+
"charify": "bin/charify.js"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"commander": "^12.0.0",
|
|
30
|
+
"sharp": "^0.33.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
}
|
|
36
|
+
}
|