@shba007/unascii 0.3.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ <p align="center">
2
+ <img src="./public/logo.png" lt="Logo" width="128" />
3
+ <p>
4
+
5
+ # unascii
6
+
7
+ <!-- automd:badges color=blue -->
8
+
9
+ [![npm version](https://img.shields.io/npm/v/unascii?color=blue)](https://npmjs.com/package/unascii)
10
+ [![npm downloads](https://img.shields.io/npm/dm/unascii?color=blue)](https://npmjs.com/package/unascii)
11
+ [![License](https://img.shields.io/npm/l/unascii?color=blue)](https://github.com/shba007/unascii?tab=MIT-1-ov-file)
12
+
13
+ <!-- /automd -->
14
+
15
+ > Print any image in ascii anywhere (browser/cli)
16
+
17
+ ## Usage (CLI)
18
+
19
+ Globally run unascii with `npx`:
20
+
21
+ ```sh
22
+ npx unascii@latest ./file/path.jpg
23
+ ```
24
+
25
+ or
26
+
27
+ ```sh
28
+ npx unascii@latest ./file/path.jpg --width=50
29
+ ```
30
+
31
+ Options:
32
+ --width Width of the image
33
+ --widthSkew Width Skew of the image
34
+ --output <console|file> Output as file or console
35
+ --characters <minimalist|normal|normal2|alphabetic|alphanumeric|numerical|extended|math|arrow|grayscale|max|codepage437|blockelement> Output Character Set
36
+ --grayscale <true|false> Output as grayscale or color only works with console
37
+
38
+ Use `npx unascii --help` for more usage info.
39
+
40
+ ## Usage (API)
41
+
42
+ Install package:
43
+
44
+ <!-- automd:pm-install -->
45
+
46
+ ```sh
47
+ # ✨ Auto-detect
48
+ npx nypm install unascii
49
+
50
+ # npm
51
+ npm install unascii
52
+
53
+ # yarn
54
+ yarn add unascii
55
+
56
+ # pnpm
57
+ pnpm install unascii
58
+
59
+ # bun
60
+ bun install unascii
61
+ ```
62
+
63
+ <!-- /automd -->
64
+
65
+ Import:
66
+
67
+ <!-- automd:jsimport cjs cdn name="pkg" -->
68
+
69
+ **ESM** (Node.js, Bun)
70
+
71
+ ```js
72
+ import {} from 'unascii'
73
+ ```
74
+
75
+ **CommonJS** (Legacy Node.js)
76
+
77
+ ```js
78
+ const {} = require('unascii')
79
+ ```
80
+
81
+ **CDN** (Deno, Bun and Browsers)
82
+
83
+ ```js
84
+ import {} from 'https://esm.sh/unascii'
85
+ ```
86
+
87
+ <!-- /automd -->
88
+
89
+ ## Development
90
+
91
+ <details>
92
+
93
+ <summary>local development</summary>
94
+
95
+ - Clone this repository
96
+ - Install latest LTS version of [Node.js](https://nodejs.org/en/)
97
+ - Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
98
+ - Install dependencies using `pnpm install`
99
+ - Run interactive tests using `pnpm dev`
100
+
101
+ </details>
102
+
103
+ ## License
104
+
105
+ <!-- automd:contributors license=MIT -->
106
+
107
+ Published under the [MIT](https://github.com/shba007/unascii/blob/main/LICENSE) license.
108
+ Made by [community](https://github.com/shba007/unascii/graphs/contributors) 💛
109
+ <br><br>
110
+ <a href="https://github.com/shba007/unascii/graphs/contributors">
111
+ <img src="https://contrib.rocks/image?repo=shba007/unascii" />
112
+ </a>
113
+
114
+ <!-- /automd -->
115
+
116
+ <!-- automd:with-automd -->
117
+
118
+ ---
119
+
120
+ _🤖 auto updated with [automd](https://automd.unjs.io)_
121
+
122
+ <!-- /automd -->
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runMain } from '../dist/cli.mjs'
4
+
5
+ runMain()
package/dist/cli.cjs ADDED
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ const citty = require('citty');
4
+ const consola = require('consola');
5
+ const pathe = require('pathe');
6
+ const unstorage = require('unstorage');
7
+ const fsDriver = require('unstorage/drivers/fs');
8
+ const index = require('./shared/unascii.BVDz6TJY.cjs');
9
+
10
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
11
+
12
+ const consola__default = /*#__PURE__*/_interopDefaultCompat(consola);
13
+ const pathe__default = /*#__PURE__*/_interopDefaultCompat(pathe);
14
+ const fsDriver__default = /*#__PURE__*/_interopDefaultCompat(fsDriver);
15
+
16
+ const name = "@shba007/unascii";
17
+ const version = "0.3.1";
18
+ const description = "Print any image in ascii anywhere (browser/cli)";
19
+
20
+ const storage = unstorage.createStorage({
21
+ driver: fsDriver__default({ base: "." })
22
+ });
23
+ const main = citty.defineCommand({
24
+ meta: {
25
+ name,
26
+ description,
27
+ version
28
+ },
29
+ args: {
30
+ path: {
31
+ type: "positional",
32
+ description: "Path of the image",
33
+ required: true
34
+ },
35
+ width: {
36
+ type: "string",
37
+ description: "Width of the image",
38
+ default: "32"
39
+ },
40
+ widthSkew: {
41
+ type: "string",
42
+ description: "Width Skew of the image",
43
+ default: "1.75"
44
+ },
45
+ output: {
46
+ type: "string",
47
+ description: "Output as file or console",
48
+ valueHint: "console|file",
49
+ default: "console"
50
+ },
51
+ characters: {
52
+ type: "string",
53
+ description: "Output Character Set",
54
+ valueHint: `minimalist|normal|normal2|alphabetic|alphanumeric|numerical|extended|math|arrow|grayscale|max|codepage437|blockelement`
55
+ },
56
+ grayscale: {
57
+ type: "boolean",
58
+ description: "Output as grayscale or color only works with console",
59
+ valueHint: "true|false"
60
+ },
61
+ verbose: {
62
+ type: "boolean",
63
+ description: "Verbose Output",
64
+ valueHint: "true|false",
65
+ default: false
66
+ }
67
+ },
68
+ async run({ args }) {
69
+ if (args.verbose) {
70
+ process.env.DEBUG = process.env.DEBUG || "true";
71
+ }
72
+ const print = await index.asciiPrint(args.path, {
73
+ width: args.width ? Number.parseInt(args.width) ?? void 0 : void 0,
74
+ widthSkew: args.widthSkew ? Number.parseFloat(args.widthSkew) ?? void 0 : void 0,
75
+ output: args.output,
76
+ characters: args.characters,
77
+ grayscale: args.grayscale
78
+ });
79
+ const image = await print.getImage();
80
+ if (args.output === "console") {
81
+ consola__default.info("\n" + image);
82
+ } else if (args.output === "file") {
83
+ const outputPath = pathe__default.format({ name: index.isURL(args.path) ? args.path.split("/").at(-1)?.split(".")[0] : pathe__default.parse(args.path).name, root: "/", ext: ".txt" });
84
+ await storage.setItem(outputPath, image);
85
+ }
86
+ if (!print) {
87
+ consola__default.error("Print not started.");
88
+ process.exit(1);
89
+ }
90
+ }
91
+ });
92
+ const runMain = () => citty.runMain(main);
93
+
94
+ exports.main = main;
95
+ exports.runMain = runMain;
package/dist/cli.d.cts ADDED
@@ -0,0 +1,44 @@
1
+ import * as citty from 'citty';
2
+
3
+ declare const main: citty.CommandDef<{
4
+ path: {
5
+ type: "positional";
6
+ description: string;
7
+ required: true;
8
+ };
9
+ width: {
10
+ type: "string";
11
+ description: string;
12
+ default: string;
13
+ };
14
+ widthSkew: {
15
+ type: "string";
16
+ description: string;
17
+ default: string;
18
+ };
19
+ output: {
20
+ type: "string";
21
+ description: string;
22
+ valueHint: string;
23
+ default: string;
24
+ };
25
+ characters: {
26
+ type: "string";
27
+ description: string;
28
+ valueHint: string;
29
+ };
30
+ grayscale: {
31
+ type: "boolean";
32
+ description: string;
33
+ valueHint: string;
34
+ };
35
+ verbose: {
36
+ type: "boolean";
37
+ description: string;
38
+ valueHint: string;
39
+ default: false;
40
+ };
41
+ }>;
42
+ declare const runMain: () => Promise<void>;
43
+
44
+ export { main, runMain };
package/dist/cli.d.mts ADDED
@@ -0,0 +1,44 @@
1
+ import * as citty from 'citty';
2
+
3
+ declare const main: citty.CommandDef<{
4
+ path: {
5
+ type: "positional";
6
+ description: string;
7
+ required: true;
8
+ };
9
+ width: {
10
+ type: "string";
11
+ description: string;
12
+ default: string;
13
+ };
14
+ widthSkew: {
15
+ type: "string";
16
+ description: string;
17
+ default: string;
18
+ };
19
+ output: {
20
+ type: "string";
21
+ description: string;
22
+ valueHint: string;
23
+ default: string;
24
+ };
25
+ characters: {
26
+ type: "string";
27
+ description: string;
28
+ valueHint: string;
29
+ };
30
+ grayscale: {
31
+ type: "boolean";
32
+ description: string;
33
+ valueHint: string;
34
+ };
35
+ verbose: {
36
+ type: "boolean";
37
+ description: string;
38
+ valueHint: string;
39
+ default: false;
40
+ };
41
+ }>;
42
+ declare const runMain: () => Promise<void>;
43
+
44
+ export { main, runMain };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ import * as citty from 'citty';
2
+
3
+ declare const main: citty.CommandDef<{
4
+ path: {
5
+ type: "positional";
6
+ description: string;
7
+ required: true;
8
+ };
9
+ width: {
10
+ type: "string";
11
+ description: string;
12
+ default: string;
13
+ };
14
+ widthSkew: {
15
+ type: "string";
16
+ description: string;
17
+ default: string;
18
+ };
19
+ output: {
20
+ type: "string";
21
+ description: string;
22
+ valueHint: string;
23
+ default: string;
24
+ };
25
+ characters: {
26
+ type: "string";
27
+ description: string;
28
+ valueHint: string;
29
+ };
30
+ grayscale: {
31
+ type: "boolean";
32
+ description: string;
33
+ valueHint: string;
34
+ };
35
+ verbose: {
36
+ type: "boolean";
37
+ description: string;
38
+ valueHint: string;
39
+ default: false;
40
+ };
41
+ }>;
42
+ declare const runMain: () => Promise<void>;
43
+
44
+ export { main, runMain };
package/dist/cli.mjs ADDED
@@ -0,0 +1,86 @@
1
+ import { defineCommand, runMain as runMain$1 } from 'citty';
2
+ import consola from 'consola';
3
+ import pathe from 'pathe';
4
+ import { createStorage } from 'unstorage';
5
+ import fsDriver from 'unstorage/drivers/fs';
6
+ import { a as asciiPrint, i as isURL } from './shared/unascii.C-iPcHtB.mjs';
7
+
8
+ const name = "@shba007/unascii";
9
+ const version = "0.3.1";
10
+ const description = "Print any image in ascii anywhere (browser/cli)";
11
+
12
+ const storage = createStorage({
13
+ driver: fsDriver({ base: "." })
14
+ });
15
+ const main = defineCommand({
16
+ meta: {
17
+ name,
18
+ description,
19
+ version
20
+ },
21
+ args: {
22
+ path: {
23
+ type: "positional",
24
+ description: "Path of the image",
25
+ required: true
26
+ },
27
+ width: {
28
+ type: "string",
29
+ description: "Width of the image",
30
+ default: "32"
31
+ },
32
+ widthSkew: {
33
+ type: "string",
34
+ description: "Width Skew of the image",
35
+ default: "1.75"
36
+ },
37
+ output: {
38
+ type: "string",
39
+ description: "Output as file or console",
40
+ valueHint: "console|file",
41
+ default: "console"
42
+ },
43
+ characters: {
44
+ type: "string",
45
+ description: "Output Character Set",
46
+ valueHint: `minimalist|normal|normal2|alphabetic|alphanumeric|numerical|extended|math|arrow|grayscale|max|codepage437|blockelement`
47
+ },
48
+ grayscale: {
49
+ type: "boolean",
50
+ description: "Output as grayscale or color only works with console",
51
+ valueHint: "true|false"
52
+ },
53
+ verbose: {
54
+ type: "boolean",
55
+ description: "Verbose Output",
56
+ valueHint: "true|false",
57
+ default: false
58
+ }
59
+ },
60
+ async run({ args }) {
61
+ if (args.verbose) {
62
+ process.env.DEBUG = process.env.DEBUG || "true";
63
+ }
64
+ const print = await asciiPrint(args.path, {
65
+ width: args.width ? Number.parseInt(args.width) ?? void 0 : void 0,
66
+ widthSkew: args.widthSkew ? Number.parseFloat(args.widthSkew) ?? void 0 : void 0,
67
+ output: args.output,
68
+ characters: args.characters,
69
+ grayscale: args.grayscale
70
+ });
71
+ const image = await print.getImage();
72
+ if (args.output === "console") {
73
+ consola.info("\n" + image);
74
+ } else if (args.output === "file") {
75
+ const outputPath = pathe.format({ name: isURL(args.path) ? args.path.split("/").at(-1)?.split(".")[0] : pathe.parse(args.path).name, root: "/", ext: ".txt" });
76
+ await storage.setItem(outputPath, image);
77
+ }
78
+ if (!print) {
79
+ consola.error("Print not started.");
80
+ process.exit(1);
81
+ }
82
+ }
83
+ });
84
+ const runMain = () => runMain$1(main);
85
+
86
+ export { main, runMain };
package/dist/index.cjs ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const index = require('./shared/unascii.BVDz6TJY.cjs');
4
+
5
+
6
+
7
+ exports.asciiPrint = index.asciiPrint;
@@ -0,0 +1,33 @@
1
+ type ASCIICharacterSet = keyof typeof asciiCharacterSet;
2
+ declare const asciiCharacterSet: {
3
+ minimalist: string;
4
+ normal: string;
5
+ normal2: string;
6
+ alphabetic: string;
7
+ alphanumeric: string;
8
+ numerical: string;
9
+ extended: string;
10
+ math: string;
11
+ arrow: string;
12
+ grayscale: string;
13
+ max: string;
14
+ codepage437: string;
15
+ blockelement: string;
16
+ };
17
+
18
+ type OutputType = 'console' | 'file' | 'dom';
19
+ interface PrintOptions {
20
+ width?: number;
21
+ widthSkew?: number;
22
+ widthScale?: number;
23
+ output?: OutputType;
24
+ characters?: ASCIICharacterSet;
25
+ grayscale?: boolean;
26
+ }
27
+ interface Print {
28
+ getImage: () => Promise<string>;
29
+ }
30
+
31
+ declare function asciiPrint(imagePath: string, opts?: PrintOptions): Promise<Print>;
32
+
33
+ export { type OutputType, asciiPrint };
@@ -0,0 +1,33 @@
1
+ type ASCIICharacterSet = keyof typeof asciiCharacterSet;
2
+ declare const asciiCharacterSet: {
3
+ minimalist: string;
4
+ normal: string;
5
+ normal2: string;
6
+ alphabetic: string;
7
+ alphanumeric: string;
8
+ numerical: string;
9
+ extended: string;
10
+ math: string;
11
+ arrow: string;
12
+ grayscale: string;
13
+ max: string;
14
+ codepage437: string;
15
+ blockelement: string;
16
+ };
17
+
18
+ type OutputType = 'console' | 'file' | 'dom';
19
+ interface PrintOptions {
20
+ width?: number;
21
+ widthSkew?: number;
22
+ widthScale?: number;
23
+ output?: OutputType;
24
+ characters?: ASCIICharacterSet;
25
+ grayscale?: boolean;
26
+ }
27
+ interface Print {
28
+ getImage: () => Promise<string>;
29
+ }
30
+
31
+ declare function asciiPrint(imagePath: string, opts?: PrintOptions): Promise<Print>;
32
+
33
+ export { type OutputType, asciiPrint };
@@ -0,0 +1,33 @@
1
+ type ASCIICharacterSet = keyof typeof asciiCharacterSet;
2
+ declare const asciiCharacterSet: {
3
+ minimalist: string;
4
+ normal: string;
5
+ normal2: string;
6
+ alphabetic: string;
7
+ alphanumeric: string;
8
+ numerical: string;
9
+ extended: string;
10
+ math: string;
11
+ arrow: string;
12
+ grayscale: string;
13
+ max: string;
14
+ codepage437: string;
15
+ blockelement: string;
16
+ };
17
+
18
+ type OutputType = 'console' | 'file' | 'dom';
19
+ interface PrintOptions {
20
+ width?: number;
21
+ widthSkew?: number;
22
+ widthScale?: number;
23
+ output?: OutputType;
24
+ characters?: ASCIICharacterSet;
25
+ grayscale?: boolean;
26
+ }
27
+ interface Print {
28
+ getImage: () => Promise<string>;
29
+ }
30
+
31
+ declare function asciiPrint(imagePath: string, opts?: PrintOptions): Promise<Print>;
32
+
33
+ export { type OutputType, asciiPrint };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ export { a as asciiPrint } from './shared/unascii.C-iPcHtB.mjs';
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ const asciiCharacterSet = {
4
+ minimalist: "#+-.",
5
+ normal: "@%#*+=-:.",
6
+ normal2: "&$Xx+;:.",
7
+ alphabetic: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
8
+ alphanumeric: "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz",
9
+ numerical: "0896452317",
10
+ extended: "@%#{}[]()<>^*+=~-:.",
11
+ math: "+-\xD7\xF7=\u2260\u2248\u221E\u221A\u03C0",
12
+ arrow: "\u2191\u2197\u2192\u2198\u2193\u2199\u2190\u2196",
13
+ grayscale: `@$BWM#*oahkbdpwmZO0QCJYXzcvnxrjft/|()1{}[]-_+~<>i!lI;:,"^'.`,
14
+ max: `\xC6\xD1\xCA\u0152\xD8M\xC9\xCB\xC8\xC3\xC2WQB\xC5\xE6#N\xC1\xFEE\xC4\xC0HKR\u017D\u0153Xg\xD0\xEAq\xDB\u0160\xD5\xD4A\u20AC\xDFpm\xE3\xE2G\xB5\xF8\xF0\xE98\xDA\xDC$\xEBd\xD9\xFD\xE8\xD3\xDE\xD6\xE5\xFF\xD2b\xAAFD\xF1\xE1ZP\xE4\u0161\xC7\xE0h\xFB\u0178\xDDk\u0178\xAES9\u017EUTe\xB5uOyx\xCE\xBEf4\xF55\xF4\xFA&a\xFC\u21222\xF9\xE7w\xA9Y\xA30V\xC7Lr\xCC\xB33\xCF\xEC\xD3C@n\xC4\xF2s\xA2u\u2030\xBD\xBC\u2021zJ\u0123%\xA4Itoc\xABrjv1l\xAD=\xEF\xEC<>i7\u2020[\xBF?\xD7}*{+()/\xBB\xAB\u2022\xAC|!\xA1\xF7\xA6\xAF\u2014^\xAA\u201E""~\xB3\xBA\xB2\u2013\xB0\xAD\xB9\u2039\u203A;:''\u201A'\u02DC\u02C6\xB8\u2026\xB7\xA8\xB4`,
15
+ codepage437: "\u2588\u2593\u2592\u2591",
16
+ blockelement: "\u2588"
17
+ };
18
+ function rgbToHex({ r, g, b }) {
19
+ r = Math.max(0, Math.min(255, Math.round(r)));
20
+ g = Math.max(0, Math.min(255, Math.round(g)));
21
+ b = Math.max(0, Math.min(255, Math.round(b)));
22
+ const redHex = r.toString(16).padStart(2, "0");
23
+ const greenHex = g.toString(16).padStart(2, "0");
24
+ const blueHex = b.toString(16).padStart(2, "0");
25
+ return `#${redHex}${greenHex}${blueHex}`;
26
+ }
27
+ function isURL(str) {
28
+ const urlPattern = /^(https?:\/\/)?([\d.a-z-]+)\.([.a-z]{2,6})([\w ./-]*)*\/?$/;
29
+ return urlPattern.test(str);
30
+ }
31
+
32
+ let createCanvas;
33
+ let loadImage;
34
+ let colorizer;
35
+ async function loadFunctions() {
36
+ if (process.env.BROWSER) {
37
+ let createCanvasBrowser = function(width, height) {
38
+ const canvas = document.createElement("canvas");
39
+ canvas.width = width;
40
+ canvas.height = height;
41
+ return canvas;
42
+ };
43
+ async function loadImageBrowser(url) {
44
+ return new Promise((resolve) => {
45
+ const image = new Image();
46
+ image.crossOrigin = "Anonymous";
47
+ image.addEventListener("load", () => resolve(image));
48
+ image.src = url;
49
+ });
50
+ }
51
+ createCanvas = createCanvasBrowser;
52
+ loadImage = loadImageBrowser;
53
+ colorizer = (color, char, output) => output === "console" ? char : `<span style="color: ${color}">${char}</span>`;
54
+ } else {
55
+ try {
56
+ const { createCanvas: createCanvasNode, loadImage: loadImageNode } = await import('canvas');
57
+ const { Chalk } = await import('chalk');
58
+ createCanvas = createCanvasNode;
59
+ loadImage = loadImageNode;
60
+ const chalk = new Chalk();
61
+ colorizer = (color, char, output) => output === "console" ? chalk.hex(color)(char) : char;
62
+ } catch {
63
+ throw new Error("Unable to import canvas/chalk modules");
64
+ }
65
+ }
66
+ }
67
+ function getAsciiChar(grayscale, widthScale, characterSet) {
68
+ const chars = asciiCharacterSet[characterSet] + " ".repeat(widthScale);
69
+ const index = Math.floor(grayscale * (chars.length - 1) / 255);
70
+ return chars[index];
71
+ }
72
+ function imageDataToASCII(imageData, widthScale, characterSet, isGrayscale, outputType) {
73
+ let ascii = "";
74
+ const { width, height, data } = imageData;
75
+ for (let y = 0; y < height; y++) {
76
+ for (let x = 0; x < width; x++) {
77
+ const i = (y * width + x) * 4;
78
+ const r = data[i];
79
+ const g = data[i + 1];
80
+ const b = data[i + 2];
81
+ const a = data[i + 3];
82
+ if (a < 16) {
83
+ ascii += " ";
84
+ continue;
85
+ }
86
+ const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
87
+ let char = getAsciiChar(brightness, widthScale, characterSet);
88
+ if (!isGrayscale) {
89
+ const hexColor = rgbToHex({ r, g, b });
90
+ char = colorizer(hexColor, char, outputType);
91
+ }
92
+ ascii += char;
93
+ }
94
+ ascii += "\n";
95
+ }
96
+ return ascii;
97
+ }
98
+ async function imagePathToASCII(imagePath, width, widthSkew, widthScale, characterSet, isGrayscale, outputType) {
99
+ if (process.env.DEBUG) console.time("loadImage");
100
+ const image = await loadImage(imagePath);
101
+ if (process.env.DEBUG) console.timeEnd("loadImage");
102
+ const aspectRatio = image.width / image.height;
103
+ const canvas = createCanvas(width * widthSkew, Math.floor(width / aspectRatio));
104
+ const ctx = canvas.getContext("2d");
105
+ if (!ctx)
106
+ throw new Error("Canvas Context Undefined");
107
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
108
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
109
+ if (process.env.DEBUG) console.time("imageDataToASCII");
110
+ const data = imageDataToASCII(imageData, widthScale, characterSet, isGrayscale, outputType);
111
+ if (process.env.DEBUG) console.timeEnd("imageDataToASCII");
112
+ return data;
113
+ }
114
+ async function asciiPrint(imagePath, opts) {
115
+ const { width = 32, widthSkew = 1.75, widthScale = 1, output = "console", characters = "alphanumeric", grayscale = false } = opts ?? {};
116
+ await loadFunctions();
117
+ if (process.env.DEBUG) console.time("imagePathToASCII");
118
+ const image = imagePathToASCII(imagePath, width, widthSkew, widthScale, characters, grayscale, output);
119
+ if (process.env.DEBUG) console.timeEnd("imagePathToASCII");
120
+ return {
121
+ getImage: async () => image
122
+ };
123
+ }
124
+
125
+ exports.asciiPrint = asciiPrint;
126
+ exports.isURL = isURL;
@@ -0,0 +1,123 @@
1
+ const asciiCharacterSet = {
2
+ minimalist: "#+-.",
3
+ normal: "@%#*+=-:.",
4
+ normal2: "&$Xx+;:.",
5
+ alphabetic: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
6
+ alphanumeric: "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz",
7
+ numerical: "0896452317",
8
+ extended: "@%#{}[]()<>^*+=~-:.",
9
+ math: "+-\xD7\xF7=\u2260\u2248\u221E\u221A\u03C0",
10
+ arrow: "\u2191\u2197\u2192\u2198\u2193\u2199\u2190\u2196",
11
+ grayscale: `@$BWM#*oahkbdpwmZO0QCJYXzcvnxrjft/|()1{}[]-_+~<>i!lI;:,"^'.`,
12
+ max: `\xC6\xD1\xCA\u0152\xD8M\xC9\xCB\xC8\xC3\xC2WQB\xC5\xE6#N\xC1\xFEE\xC4\xC0HKR\u017D\u0153Xg\xD0\xEAq\xDB\u0160\xD5\xD4A\u20AC\xDFpm\xE3\xE2G\xB5\xF8\xF0\xE98\xDA\xDC$\xEBd\xD9\xFD\xE8\xD3\xDE\xD6\xE5\xFF\xD2b\xAAFD\xF1\xE1ZP\xE4\u0161\xC7\xE0h\xFB\u0178\xDDk\u0178\xAES9\u017EUTe\xB5uOyx\xCE\xBEf4\xF55\xF4\xFA&a\xFC\u21222\xF9\xE7w\xA9Y\xA30V\xC7Lr\xCC\xB33\xCF\xEC\xD3C@n\xC4\xF2s\xA2u\u2030\xBD\xBC\u2021zJ\u0123%\xA4Itoc\xABrjv1l\xAD=\xEF\xEC<>i7\u2020[\xBF?\xD7}*{+()/\xBB\xAB\u2022\xAC|!\xA1\xF7\xA6\xAF\u2014^\xAA\u201E""~\xB3\xBA\xB2\u2013\xB0\xAD\xB9\u2039\u203A;:''\u201A'\u02DC\u02C6\xB8\u2026\xB7\xA8\xB4`,
13
+ codepage437: "\u2588\u2593\u2592\u2591",
14
+ blockelement: "\u2588"
15
+ };
16
+ function rgbToHex({ r, g, b }) {
17
+ r = Math.max(0, Math.min(255, Math.round(r)));
18
+ g = Math.max(0, Math.min(255, Math.round(g)));
19
+ b = Math.max(0, Math.min(255, Math.round(b)));
20
+ const redHex = r.toString(16).padStart(2, "0");
21
+ const greenHex = g.toString(16).padStart(2, "0");
22
+ const blueHex = b.toString(16).padStart(2, "0");
23
+ return `#${redHex}${greenHex}${blueHex}`;
24
+ }
25
+ function isURL(str) {
26
+ const urlPattern = /^(https?:\/\/)?([\d.a-z-]+)\.([.a-z]{2,6})([\w ./-]*)*\/?$/;
27
+ return urlPattern.test(str);
28
+ }
29
+
30
+ let createCanvas;
31
+ let loadImage;
32
+ let colorizer;
33
+ async function loadFunctions() {
34
+ if (process.env.BROWSER) {
35
+ let createCanvasBrowser = function(width, height) {
36
+ const canvas = document.createElement("canvas");
37
+ canvas.width = width;
38
+ canvas.height = height;
39
+ return canvas;
40
+ };
41
+ async function loadImageBrowser(url) {
42
+ return new Promise((resolve) => {
43
+ const image = new Image();
44
+ image.crossOrigin = "Anonymous";
45
+ image.addEventListener("load", () => resolve(image));
46
+ image.src = url;
47
+ });
48
+ }
49
+ createCanvas = createCanvasBrowser;
50
+ loadImage = loadImageBrowser;
51
+ colorizer = (color, char, output) => output === "console" ? char : `<span style="color: ${color}">${char}</span>`;
52
+ } else {
53
+ try {
54
+ const { createCanvas: createCanvasNode, loadImage: loadImageNode } = await import('canvas');
55
+ const { Chalk } = await import('chalk');
56
+ createCanvas = createCanvasNode;
57
+ loadImage = loadImageNode;
58
+ const chalk = new Chalk();
59
+ colorizer = (color, char, output) => output === "console" ? chalk.hex(color)(char) : char;
60
+ } catch {
61
+ throw new Error("Unable to import canvas/chalk modules");
62
+ }
63
+ }
64
+ }
65
+ function getAsciiChar(grayscale, widthScale, characterSet) {
66
+ const chars = asciiCharacterSet[characterSet] + " ".repeat(widthScale);
67
+ const index = Math.floor(grayscale * (chars.length - 1) / 255);
68
+ return chars[index];
69
+ }
70
+ function imageDataToASCII(imageData, widthScale, characterSet, isGrayscale, outputType) {
71
+ let ascii = "";
72
+ const { width, height, data } = imageData;
73
+ for (let y = 0; y < height; y++) {
74
+ for (let x = 0; x < width; x++) {
75
+ const i = (y * width + x) * 4;
76
+ const r = data[i];
77
+ const g = data[i + 1];
78
+ const b = data[i + 2];
79
+ const a = data[i + 3];
80
+ if (a < 16) {
81
+ ascii += " ";
82
+ continue;
83
+ }
84
+ const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
85
+ let char = getAsciiChar(brightness, widthScale, characterSet);
86
+ if (!isGrayscale) {
87
+ const hexColor = rgbToHex({ r, g, b });
88
+ char = colorizer(hexColor, char, outputType);
89
+ }
90
+ ascii += char;
91
+ }
92
+ ascii += "\n";
93
+ }
94
+ return ascii;
95
+ }
96
+ async function imagePathToASCII(imagePath, width, widthSkew, widthScale, characterSet, isGrayscale, outputType) {
97
+ if (process.env.DEBUG) console.time("loadImage");
98
+ const image = await loadImage(imagePath);
99
+ if (process.env.DEBUG) console.timeEnd("loadImage");
100
+ const aspectRatio = image.width / image.height;
101
+ const canvas = createCanvas(width * widthSkew, Math.floor(width / aspectRatio));
102
+ const ctx = canvas.getContext("2d");
103
+ if (!ctx)
104
+ throw new Error("Canvas Context Undefined");
105
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
106
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
107
+ if (process.env.DEBUG) console.time("imageDataToASCII");
108
+ const data = imageDataToASCII(imageData, widthScale, characterSet, isGrayscale, outputType);
109
+ if (process.env.DEBUG) console.timeEnd("imageDataToASCII");
110
+ return data;
111
+ }
112
+ async function asciiPrint(imagePath, opts) {
113
+ const { width = 32, widthSkew = 1.75, widthScale = 1, output = "console", characters = "alphanumeric", grayscale = false } = opts ?? {};
114
+ await loadFunctions();
115
+ if (process.env.DEBUG) console.time("imagePathToASCII");
116
+ const image = imagePathToASCII(imagePath, width, widthSkew, widthScale, characters, grayscale, output);
117
+ if (process.env.DEBUG) console.timeEnd("imagePathToASCII");
118
+ return {
119
+ getImage: async () => image
120
+ };
121
+ }
122
+
123
+ export { asciiPrint as a, isURL as i };
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@shba007/unascii",
3
+ "version": "0.3.1",
4
+ "description": "Print any image in ascii anywhere (browser/cli)",
5
+ "author": "Shirsendu Bairagi <shirsendu2001@gmail.com>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/shba007/unascii.git"
10
+ },
11
+ "keywords": [],
12
+ "sideEffects": false,
13
+ "type": "module",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.mjs",
18
+ "require": "./dist/index.cjs"
19
+ },
20
+ "./cli": {
21
+ "types": "./dist/cli.d.ts",
22
+ "import": "./dist/cli.mjs",
23
+ "require": "./dist/cli.cjs"
24
+ }
25
+ },
26
+ "main": "./dist/index.cjs",
27
+ "module": "./dist/index.mjs",
28
+ "types": "./dist/index.d.ts",
29
+ "bin": {
30
+ "unascii": "./bin/unascii.mjs"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "bin"
35
+ ],
36
+ "volta": {
37
+ "node": "20.18.1"
38
+ },
39
+ "engines": {
40
+ "node": "^20.15.0",
41
+ "pnpm": "^9.15.0"
42
+ },
43
+ "dependencies": {
44
+ "canvas": "^2.11.2",
45
+ "chalk": "^5.3.0",
46
+ "citty": "^0.1.6",
47
+ "consola": "^3.2.3",
48
+ "pathe": "^1.1.2",
49
+ "unstorage": "^1.13.1"
50
+ },
51
+ "devDependencies": {
52
+ "@changesets/cli": "^2.27.10",
53
+ "@dotenvx/dotenvx": "^1.30.1",
54
+ "@types/node": "^22.10.2",
55
+ "@vitest/coverage-v8": "^2.1.8",
56
+ "changelogen": "^0.5.7",
57
+ "eslint": "^9.17.0",
58
+ "eslint-config-unjs": "^0.4.2",
59
+ "jiti": "^2.4.1",
60
+ "prettier": "^3.4.2",
61
+ "typescript": "^5.7.2",
62
+ "unbuild": "3.0.1",
63
+ "vitest": "^2.1.8"
64
+ },
65
+ "scripts": {
66
+ "dev": "vitest dev",
67
+ "lint": "eslint . --fix",
68
+ "format": "prettier . --write",
69
+ "build": "unbuild",
70
+ "play": "jiti playground/cli",
71
+ "release": "pnpm test && changelogen --release && pnpm publish && git push --follow-tags",
72
+ "test": "pnpm lint && pnpm test:types && vitest run --coverage",
73
+ "test:types": "tsc --noEmit --skipLibCheck",
74
+ "unascii": "jiti playground/cli"
75
+ }
76
+ }