diff --git a/.gitignore b/.gitignore index 3c3629e..b48bcce 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +examples/tmp/* \ No newline at end of file diff --git a/README.md b/README.md index f417daf..3ab90b2 100644 --- a/README.md +++ b/README.md @@ -8,25 +8,68 @@ This is an npm package [captcha-canvas](https://npmjs.com/package/captcha-canvas [![captcha-canvas](https://nodei.co/npm/captcha-canvas.png)](https://npmjs.com/package/captcha-canvas) -#### Captcha Image: +## Captcha Image: ![captcha](examples/example.png) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FShashank3736%2Fcaptcha-canvas.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FShashank3736%2Fcaptcha-canvas?ref=badge_shield) -### Features +## Features +* Easy to use. * Highly customisable you can customise every single value use to make this package. -* 95% OCR fails to read this captcha image and throw Error. +* ChatGPT fails to read this captcha image and throw Error. * Use class constructor method so you can generate as many frames as many you want by using same values. * No bundled dependencies. You need to install them to use the package. * Support of background images is also possible. * Captcha adapt all the external options very easily. ## How to use? -* **v2:** If you are using v2 [click here](https://captcha-canvas.js.org/v2/index.html) for documentation and examples. -* **v3:** If you are using v3 [click here](https://captcha-canvas.js.org) for documentation and examples. -> Note: v3 also contain alot of functions to create instant captcha like [`createCaptcha`](https://captcha-canvas.js.org/global.html#createCaptcha). +```js +const fs = require('fs'); +const { createCanvas } = require('canvas'); +const { createCaptcha } = require('captcha-canvas'); + +const canvas = createCanvas(300, 100); +const ctx = canvas.getContext('2d'); + +const { text } = createCaptcha({ ctx }); + +console.log(text); +fs.writeFileSync('./examples/example.png', canvas.toBuffer('image/png')); +``` + +You can also customize captcha as much as you want by customizing the placement of layers. + +Captcha creation have mainly 3 steps draw trace line, draw decoy text and then draw captcha. If you want you can order them however you want by using `Captcha` class. + +```js +const fs = require('fs'); +const { createCanvas } = require('canvas'); +const { Captcha } = require('captcha-canvas'); + +const canvas = createCanvas(300, 100); +const ctx = canvas.getContext('2d'); + +const captcha = new Captcha({ ctx, characters: 8 }); + +captcha.drawCaptcha({ + color: 'deeppink', // optional + size: 60, // optional +}); +captcha.drawTrace({ + size: 5, // optional + color: 'deeppink', // optional +}); +captcha.addDecoy({ + total: 100, // optional +}); + +const text = captcha.text; +fs.writeFileSync(`./examples/tmp/${new Date().getTime()}-${text}.png`, canvas.toBuffer('image/png')); +``` + +There are many more customization you can do in this module. Check [documentation](https://captcha-canvas.js.org/) for more details. ## Need Help: Open an [issue](https://github.com/Shashank3736/captcha-canvas/issues) if you need help regarding this module or want to report any bug. diff --git a/docs/Captcha.html b/docs/Captcha.html new file mode 100644 index 0000000..f190f92 --- /dev/null +++ b/docs/Captcha.html @@ -0,0 +1,1191 @@ + + + + + + + + Captcha + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

Captcha

+
+ + + + + +
+ +
+ +

Captcha(optionopt)

+ +

Captcha Generator

+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new Captcha(optionopt) + + +

+ + + + +
+

Start captcha image creation.

+
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
option + + +ConstructorOptions + + + + + + <optional>
+ + + + + +

Size of captcha text.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + captcha.js, line 10 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + +
+

Members

+
+ +
+ +

+ # + + + context + + +

+ + + + +
+

Get canvas context.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + captcha.js, line 34 + +

+ +
+ + + + + +
+ +
+ +

+ # + + + text + + +

+ + + + +
+

Get Captcha text.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + captcha.js, line 41 + +

+ +
+ + + + + +
+ +
+
+ + + +
+

Methods

+
+ +
+ + + +

+ # + + + + addDecoy(decoyOptionopt) → {Captcha} + + +

+ + + + +
+

Add decoy on your captcha image.

+
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
decoyOption + + +SetDecoyOptions + + + + + + <optional>
+ + + + + +

Decoy option you want to customise

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + captcha.js, line 58 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ + +Captcha + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + drawCaptcha(captchaOptionopt) → {Captcha} + + +

+ + + + +
+

Draw captcha text on captcha image.

+
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
captchaOption + + +DrawCaptchaOption + + + + + + <optional>
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + captcha.js, line 99 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ + +Captcha + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + drawImage(image) → {Captcha} + + +

+ + + + +
+

Draw image on your captcha.

+
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
image + + +Image + + + +

Choose image you want to add.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + captcha.js, line 49 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ + +Captcha + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + drawTrace(traceOptionopt) → {Captcha} + + +

+ + + + +
+

Draw trace line over your captcha.

+

Note: If you want to use custom text or change size of captcha text then drawCaptcha before drawTrace.

+
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
traceOption + + +SetTraceOptions + + + + + + <optional>
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + captcha.js, line 78 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ + +Captcha + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/CaptchaGenerator.html b/docs/CaptchaGenerator.html index e4bfbc1..88ba0f0 100644 --- a/docs/CaptchaGenerator.html +++ b/docs/CaptchaGenerator.html @@ -66,7 +66,7 @@ @@ -85,9 +85,9 @@

CaptchaGenerator

-

CaptchaGenerator(options)

+

CaptchaGenerator(optionsopt)

-

Captcha Generator

+

Captcha generator class.

@@ -111,7 +111,7 @@

- new CaptchaGenerator(options) + new CaptchaGenerator(optionsopt)

@@ -145,8 +145,12 @@
Parameters:
Type + Attributes + + Default + Description @@ -171,8 +175,22 @@
Parameters:
+ + + <optional>
+ + + + + + + + + + +

Options for constructor.

@@ -195,7 +213,23 @@
Parameters:
+ + + <optional>
+ + + + + + + + + + + 100 + +

Height of captcha image.

@@ -218,7 +252,23 @@
Parameters:
+ + + <optional>
+ + + + + + + + + + + 300 + +

Width of captcha image.

@@ -272,9 +322,9 @@
Parameters:

- View Source + View Source - main.js, line 32 + CaptchaGenerator.js, line 10

@@ -398,9 +448,9 @@

- View Source + View Source - main.js, line 85 + CaptchaGenerator.js, line 33

@@ -495,9 +545,9 @@

- View Source + View Source - main.js, line 190 + CaptchaGenerator.js, line 134

@@ -549,7 +599,12 @@

Example
-
const { CaptchaGenerator } = require("captcha-canvas");
const fs = require("fs")
const captcha = new CaptchaGenerator();
const buffer = await captcha.generate() //generate image

fs.writeFileSync("image.png", buffer)
+
const { CaptchaGenerator } = require("captcha-canvas");
+const fs = require("fs")
+const captcha = new CaptchaGenerator();
+const buffer = await captcha.generate() //generate image
+
+fs.writeFileSync("image.png", buffer)
@@ -728,9 +783,9 @@
Parameters:

- View Source + View Source - main.js, line 273 + CaptchaGenerator.js, line 161

@@ -759,7 +814,14 @@
Parameters:
Example
-
const { CaptchaGenerator, resolveImage } = require("captcha-canvas");
const fs = require("fs");
const img = await resolveImage("./path/to/file");

const captcha = new CaptchaGenerator()
.generateSync({background: img});

fs.writeFileSync("image.png", captcha);
+
const { CaptchaGenerator, resolveImage } = require("captcha-canvas");
+const fs = require("fs");
+const img = await resolveImage("./path/to/file");
+
+const captcha = new CaptchaGenerator()
+.generateSync({background: img});
+
+fs.writeFileSync("image.png", captcha);
@@ -886,9 +948,9 @@
Parameters:

- View Source + View Source - main.js, line 121 + CaptchaGenerator.js, line 68

@@ -917,7 +979,13 @@
Parameters:
Example
-
const { CaptchaGenerator } = require("captcha-canvas");
const fs = require("fs")
const captcha = new CaptchaGenerator();
captcha.setBackground("./path/toFile");
const buffer = await captcha.generate() //generate image

fs.writeFileSync("image.png", buffer)
+
const { CaptchaGenerator } = require("captcha-canvas");
+const fs = require("fs")
+const captcha = new CaptchaGenerator();
+captcha.setBackground("./path/toFile");
+const buffer = await captcha.generate() //generate image
+
+fs.writeFileSync("image.png", buffer)
@@ -1044,9 +1112,9 @@
Parameters:

- View Source + View Source - main.js, line 139 + CaptchaGenerator.js, line 86

@@ -1075,7 +1143,14 @@
Parameters:
Example
-
const { CaptchaGenerator } = require("captcha-canvas");
const fs = require("fs")
const captcha = new CaptchaGenerator();
const options = {font: "Comic Sans", size: 60}
captcha.setCaptcha(options)
const buffer = await captcha.generate() //generate image

fs.writeFileSync("image.png", buffer)
+
const { CaptchaGenerator } = require("captcha-canvas");
+const fs = require("fs")
+const captcha = new CaptchaGenerator();
+const options = {font: "Comic Sans", size: 60}
+captcha.setCaptcha(options)
+const buffer = await captcha.generate() //generate image
+
+fs.writeFileSync("image.png", buffer)
@@ -1202,9 +1277,9 @@
Parameters:

- View Source + View Source - main.js, line 173 + CaptchaGenerator.js, line 117

@@ -1378,9 +1453,9 @@
Parameters:

- View Source + View Source - main.js, line 103 + CaptchaGenerator.js, line 50

@@ -1409,7 +1484,13 @@
Parameters:
Example
-
const { CaptchaGenerator } = require("captcha-canvas");
const fs = require("fs")
const captcha = new CaptchaGenerator();
captcha.setDimension(200, 600);
const buffer = await captcha.generate() //generate image

fs.writeFileSync("image.png", buffer)
+
const { CaptchaGenerator } = require("captcha-canvas");
+const fs = require("fs")
+const captcha = new CaptchaGenerator();
+captcha.setDimension(200, 600);
+const buffer = await captcha.generate() //generate image
+
+fs.writeFileSync("image.png", buffer)
@@ -1536,9 +1617,9 @@
Parameters:

- View Source + View Source - main.js, line 164 + CaptchaGenerator.js, line 108

@@ -1567,7 +1648,14 @@
Parameters:
Example
-
const { CaptchaGenerator } = require("captcha-canvas");
const fs = require("fs")
const captcha = new CaptchaGenerator();
const options = {size: 5, color: "deeppink"}
captcha.setTrace(options)
const buffer = await captcha.generate() //generate image

fs.writeFileSync("image.png", buffer)
+
const { CaptchaGenerator } = require("captcha-canvas");
+const fs = require("fs")
+const captcha = new CaptchaGenerator();
+const options = {size: 5, color: "deeppink"}
+captcha.setTrace(options)
+const buffer = await captcha.generate() //generate image
+
+fs.writeFileSync("image.png", buffer)
diff --git a/docs/CaptchaGenerator.js.html b/docs/CaptchaGenerator.js.html new file mode 100644 index 0000000..88bdeff --- /dev/null +++ b/docs/CaptchaGenerator.js.html @@ -0,0 +1,296 @@ + + + + + + + + + + CaptchaGenerator.js + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

CaptchaGenerator.js

+
+ + + + + +
+
+
"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.CaptchaGenerator = void 0;
+const _1 = require(".");
+const constants_1 = require("./constants");
+const util_1 = require("./util");
+/**
+ * Captcha generator class.
+ */
+class CaptchaGenerator {
+    /**
+     * Initatiates the creation of captcha image generation.
+     * @example const captcha = new CaptchaGenerator({height: 200, width: 600});
+     * @param {object} [options] Options for constructor.
+     * @param {integer} [options.height=100] Height of captcha image.
+     * @param {integer} [options.width=300] Width of captcha image.
+     * @since 2.0.0
+     */
+    constructor(options) {
+        this.height = options.height || 100;
+        this.width = options.width || 300;
+        this.captcha = constants_1.defaultCaptchaOption;
+        this.trace = constants_1.defaultTraceOptions;
+        this.decoy = constants_1.defaultDecoyOptions;
+        this.captcha.text = (0, util_1.randomText)(this.captcha.characters || 6);
+        this.ctx = options.ctx;
+    }
+    /**
+     * Get the text of captcha.
+     * @type {string}
+     * @since 2.0.3
+     */
+    get text() {
+        return this.captcha.text;
+    }
+    /**
+     * set dimension for your captcha image
+     * @param {integer} height Height of captcha image.
+     * @param {integer} width Width of captcha image.
+     * @example
+     * const { CaptchaGenerator } = require("captcha-canvas");
+     * const fs = require("fs")
+     * const captcha = new CaptchaGenerator();
+     * captcha.setDimension(200, 600);
+     * const buffer = await captcha.generate() //generate image
+     *
+     * fs.writeFileSync("image.png", buffer)
+     * @since 2.0.0
+     */
+    setDimension(height, width) {
+        this.height = height;
+        this.width = width;
+        return this;
+    }
+    /**
+     * Set background for captcha image.
+     * @param {buffer} image Buffer/url/path of image.
+     * @example
+     * const { CaptchaGenerator } = require("captcha-canvas");
+     * const fs = require("fs")
+     * const captcha = new CaptchaGenerator();
+     * captcha.setBackground("./path/toFile");
+     * const buffer = await captcha.generate() //generate image
+     *
+     * fs.writeFileSync("image.png", buffer)
+     * @since 2.0.0
+     */
+    setBackground(image) {
+        this.background = image;
+        return this;
+    }
+    /**
+     * Change captcha text options
+     * @param {SetCaptchaOptions} options Captcha appearance options.
+     * @example
+     * const { CaptchaGenerator } = require("captcha-canvas");
+     * const fs = require("fs")
+     * const captcha = new CaptchaGenerator();
+     * const options = {font: "Comic Sans", size: 60}
+     * captcha.setCaptcha(options)
+     * const buffer = await captcha.generate() //generate image
+     *
+     * fs.writeFileSync("image.png", buffer)
+     * @since 2.0.0
+     */
+    setCaptcha(option) {
+        this.captcha = { ...this.captcha, ...option };
+        if (option.text)
+            this.captcha.characters = option.text.length;
+        if (!option.text && option.characters)
+            this.captcha.text = (0, util_1.randomText)(option.characters);
+        return this;
+    }
+    /**
+     * Change trace creation options.
+     * @param {SetTraceOptions} options Trace Line appearance options.
+     * @example
+     * const { CaptchaGenerator } = require("captcha-canvas");
+     * const fs = require("fs")
+     * const captcha = new CaptchaGenerator();
+     * const options = {size: 5, color: "deeppink"}
+     * captcha.setTrace(options)
+     * const buffer = await captcha.generate() //generate image
+     *
+     * fs.writeFileSync("image.png", buffer)
+     * @since 2.0.0
+     */
+    setTrace(option) {
+        this.trace = { ...this.trace, ...option };
+        return this;
+    }
+    /**
+     * Change decoy options
+     * @param {SetDecoyOptions} options Decoy characters customisation options
+     * @since 2.0.0
+     */
+    setDecoy(option) {
+        this.decoy = { ...this.decoy, ...option };
+        return this;
+    }
+    /**
+     * Method which returns image buffer
+     * @async
+     * @returns {Promise<Buffer>}
+     * @example
+     * const { CaptchaGenerator } = require("captcha-canvas");
+     * const fs = require("fs")
+     * const captcha = new CaptchaGenerator();
+     * const buffer = await captcha.generate() //generate image
+     *
+     * fs.writeFileSync("image.png", buffer)
+     * @since 2.0.0
+     */
+    async generate() {
+        const captchaCanvas = new _1.Captcha({ height: this.height, width: this.width, ctx: this.ctx });
+        if (this.decoy.opacity)
+            captchaCanvas.addDecoy(this.decoy);
+        if (this.captcha.opacity)
+            captchaCanvas.drawCaptcha(this.captcha);
+        if (this.trace.opacity)
+            captchaCanvas.drawTrace(this.trace);
+        return captchaCanvas.getContext();
+    }
+    /**
+     * Non asynchronous method to generate captcha image.
+     * > Note: It do not use `setBackground` method value for background image. If you want to set background
+     * and also use generateSync method then use background option in genrateSync method.
+     * @param {object} [options] Options to add extra values
+     * @param {Image} [options.background] Add background image.
+     * @example
+     * const { CaptchaGenerator, resolveImage } = require("captcha-canvas");
+     * const fs = require("fs");
+     * const img = await resolveImage("./path/to/file");
+     *
+     * const captcha = new CaptchaGenerator()
+     * .generateSync({background: img});
+     *
+     * fs.writeFileSync("image.png", captcha);
+     * @since 2.2.0
+     */
+    generateSync(option = {}) {
+        const captchaCanvas = new _1.Captcha({
+            width: this.width,
+            height: this.height,
+            characters: this.captcha.characters,
+            ctx: this.ctx,
+        });
+        captchaCanvas.async = false;
+        if (option.background)
+            captchaCanvas.drawImage(option.background);
+        if (this.decoy.opacity)
+            captchaCanvas.addDecoy(this.decoy);
+        if (this.captcha.opacity)
+            captchaCanvas.drawCaptcha(this.captcha);
+        if (this.trace.opacity)
+            captchaCanvas.drawTrace(this.trace);
+        return captchaCanvas.getContext();
+    }
+}
+exports.CaptchaGenerator = CaptchaGenerator;
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/captcha.js.html b/docs/captcha.js.html new file mode 100644 index 0000000..3cc26d5 --- /dev/null +++ b/docs/captcha.js.html @@ -0,0 +1,255 @@ + + + + + + + + + + captcha.js + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

captcha.js

+
+ + + + + +
+
+
"use strict";
+/// <reference lib="dom" />
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Captcha = void 0;
+const constants_1 = require("./constants");
+const util_1 = require("./util");
+/**
+ * Captcha Generator
+ */
+class Captcha {
+    /**
+     * Start captcha image creation.
+     * @param {ConstructorOptions} [option] Size of captcha text.
+     * @constructor
+     */
+    constructor({ characters = constants_1.defaultCaptchaOption.characters, ctx, }) {
+        this._captcha = constants_1.defaultCaptchaOption;
+        this._captcha.characters = characters;
+        this._trace = constants_1.defaultTraceOptions;
+        this._decoy = constants_1.defaultDecoyOptions;
+        ctx.lineJoin = 'miter';
+        ctx.textBaseline = 'middle';
+        ctx.textAlign = 'center';
+        this._width = ctx.canvas.width;
+        this._height = ctx.canvas.height;
+        this._ctx = ctx;
+        this.async = true;
+        this._coordinates = [];
+    }
+    /**
+     * Get canvas context.
+     * @returns {CanvasRenderingContext2D}
+     */
+    get context() {
+        return this._ctx;
+    }
+    /**
+     * Get Captcha text.
+     * @returns {string} Get captcha text.
+     */
+    get text() {
+        return this._captcha.text || "";
+    }
+    /**
+     * Draw image on your captcha.
+     * @param {Image} image Choose image you want to add.
+     * @returns {Captcha}
+     */
+    drawImage(image) {
+        this._ctx.drawImage(image, 0, 0, this._width, this._height);
+        return this;
+    }
+    /**
+     * Add decoy on your captcha image.
+     * @param {SetDecoyOptions} [decoyOption] Decoy option you want to customise
+     * @returns {Captcha}
+     */
+    addDecoy(decoyOption = {}) {
+        const option = { ...this._decoy, ...decoyOption };
+        if (!option.total)
+            option.total = Math.floor(this._width * this._height / 10000);
+        const decoyText = (0, util_1.randomText)(option.total);
+        this._ctx.font = `${option.size}px ${option.font}`;
+        this._ctx.globalAlpha = option.opacity;
+        this._ctx.fillStyle = option.color;
+        for (const element of decoyText) {
+            this._ctx.fillText(element, (0, util_1.getRandom)(30, this._width - 30), (0, util_1.getRandom)(30, this._height - 30));
+        }
+        return this;
+    }
+    /**
+     * Draw trace line over your captcha.
+     *
+     * Note: If you want to use custom text or change size of captcha text then drawCaptcha before drawTrace.
+     * @param {SetTraceOptions} [traceOption]
+     * @returns {Captcha}
+     */
+    drawTrace(traceOption = {}) {
+        const option = { ...this._trace, ...traceOption };
+        if (!this._coordinates[0])
+            this._coordinates = (0, util_1.getRandomCoordinate)(this._height, this._width, this._captcha.characters || 6);
+        const coordinates = this._coordinates;
+        this._ctx.strokeStyle = option.color;
+        this._ctx.globalAlpha = option.opacity;
+        this._ctx.beginPath();
+        this._ctx.moveTo(coordinates[0][0], coordinates[0][1]);
+        this._ctx.lineWidth = option.size;
+        for (let i = 1; i < coordinates.length; i++) {
+            this._ctx.lineTo(coordinates[i][0], coordinates[i][1]);
+        }
+        this._ctx.stroke();
+        return this;
+    }
+    /**
+     * Draw captcha text on captcha image.
+     * @param {DrawCaptchaOption} [captchaOption]
+     * @returns {Captcha}
+     */
+    drawCaptcha(captchaOption = {}) {
+        var _a;
+        const option = { ...this._captcha, ...captchaOption };
+        if (captchaOption.text)
+            option.text = captchaOption.text;
+        if (!option.text)
+            option.text = (0, util_1.randomText)(option.characters);
+        if (option.text.length != option.characters) {
+            if (captchaOption.text) {
+                throw new Error("Size of text and no. of characters is not matching.");
+            }
+            else {
+                option.text = (0, util_1.randomText)(option.characters);
+            }
+        }
+        this._captcha = option;
+        if (!this._coordinates[0])
+            this._coordinates = (0, util_1.getRandomCoordinate)(this._height, this._width, option.characters || 6);
+        const coordinates = this._coordinates;
+        this._ctx.font = `${option.size}px ${option.font}`;
+        this._ctx.globalAlpha = option.opacity;
+        this._ctx.fillStyle = option.color;
+        for (let n = 0; n < coordinates.length; n++) {
+            this._ctx.save();
+            this._ctx.translate(coordinates[n][0], coordinates[n][1]);
+            if (option.skew) {
+                this._ctx.transform(1, Math.random(), (0, util_1.getRandom)(20) / 100, 1, 0, 0);
+            }
+            if (option.rotate && option.rotate > 0) {
+                this._ctx.rotate((0, util_1.getRandom)(-option.rotate, option.rotate) * Math.PI / 180);
+            }
+            if (option.colors && ((_a = option.colors) === null || _a === void 0 ? void 0 : _a.length) >= 2) {
+                this._ctx.fillStyle = option.colors[(0, util_1.getRandom)(option.colors.length - 1)];
+            }
+            this._ctx.fillText(option.text[n], 0, 0);
+            this._ctx.restore();
+        }
+        return this;
+    }
+}
+exports.Captcha = Captcha;
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/constants.js.html b/docs/constants.js.html index 8645ebd..3db1317 100644 --- a/docs/constants.js.html +++ b/docs/constants.js.html @@ -68,7 +68,7 @@ @@ -85,18 +85,34 @@

constants.js

-
/**
- * Customise dimension of captcha image.
- *
- * @typedef SetDimensionOption
- * @property {integer} [height=100] Height of captcha image.
- * @property {integer} [width=300] Width of captcha image.
+            
"use strict";
+/// <reference lib="dom" />
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.CreateCaptchaOptions = exports.defaultDimension = exports.defaultDecoyOptions = exports.defaultTraceOptions = exports.defaultCaptchaOption = exports.defaultDrawCaptchaOption = void 0;
+/**
+ * Captcha text options to customise text appearance and value.
+ * @typedef DrawCaptchaOption
+ * @property {string} [text="Random UpperCase string"] Text of captcha
+ * @property {hexCode} [color="#32cf7e"] Color of captcha text.
+ * @property {font} [font="Sans"] Font of captcha text.
+ * @property {boolean} [skew=true] Skew captcha text.
+ * @property {array} [colors=[]] Array of hexCode will override color property.
+ * @property {number} [rotate=5] Range of angle to rotate text.
+ * @property {number} [size=40] Size of captcha text.
+ * @property {float} [opacity=1] Opcaity of captcha text.
  */
-exports.SetDimensionOption = {};
-
+exports.defaultDrawCaptchaOption = {
+    size: 40,
+    font: 'Sans',
+    skew: true,
+    colors: [],
+    rotate: 5,
+    color: '#32cf7e',
+    opacity: 0.8,
+    text: '',
+};
 /**
  * Captcha text options to customise text appearance and value.
- *
  * @typedef SetCaptchaOptions
  * @property {integer} [characters=6] Length of captcha text.
  * @property {string} [text="Random UpperCase string"] Text of captcha
@@ -108,60 +124,62 @@ 

constants.js

* @property {number} [size=40] Size of captcha text. * @property {float} [opacity=1] Opcaity of captcha text. */ -exports.SetCaptchaOptions = {}; - +exports.defaultCaptchaOption = { + characters: 6, + size: 40, + font: 'Sans', + skew: true, + colors: [], + rotate: 5, + color: '#32cf7e', + opacity: 0.8, + text: '', +}; /** * @typedef SetTraceOptions * @property {hexCode} [color="#32cf7e"] Color of trace line. * @property {number} [size=3] Width of trace line. * @property {float} [opacity=1] Opacoty of trace line. */ -exports.SetTraceOptions = {}; - +exports.defaultTraceOptions = { + size: 3, + color: '#32cf7e', + opacity: 1, +}; /** * @typedef SetDecoyOptions * @property {hexCode} [color="#646566"] Color of decoy characters. * @property {font} [font="Sans"] Font of decoy characters. * @property {number} [size=20] Size of decoy characters. * @property {float} [opacity=0.8] Opacity of decoy characters. + * @property {number} [total] Total count of decoy characters. */ -exports.SetDecoyOptions = {}; - -/** - * Default captcha customisation options. - * @type {SetCaptchaOptions} - */ -exports.defaultCaptchaOptions = { - characters: 6, - size: 40, - font: 'Sans', - skew: true, - colors: [], - rotate: 5, - color: '#32cf7e', - opacity: 0.8, +exports.defaultDecoyOptions = { + color: '#646566', + font: 'Sans', + size: 20, + opacity: 0.8, }; - /** - * Default trace line options. - * @type {SetTraceOptions} + * Customise dimension of captcha image. + * @typedef SetDimensionOption + * @property {integer} [height=100] Height of captcha image. + * @property {integer} [width=300] Width of captcha image. */ -exports.defaultTraceOptions = { - size: 3, - color: '#32cf7e', - opacity: 1, +exports.defaultDimension = { + height: 100, + width: 300 }; - /** - * Default Decoy Options - * @type {SetDecoyOptions} + * Create captcha options in functions. + * @typedef CreateCaptchaOptions + * @property {SetCaptchaOptions} [captcha] Captcha text options to customise text appearance and value. + * @property {SetDecoyOptions} [decoy] + * @property {SetTraceOptions} [trace] + * @property {Image} [background] */ -exports.defaultDecoyOptions = { - color: '#646566', - font: 'Sans', - size: 20, - opacity: 0.8, -};
+exports.CreateCaptchaOptions = {}; +
diff --git a/docs/extra.js.html b/docs/extra.js.html new file mode 100644 index 0000000..4b741ab --- /dev/null +++ b/docs/extra.js.html @@ -0,0 +1,141 @@ + + + + + + + + + + extra.js + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

extra.js

+
+ + + + + +
+
+
"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.createCaptcha = void 0;
+const _1 = require(".");
+/**
+ * Create custom captcha from scratch.
+ * @param {CreateCaptchaOptions} [option] Captcha text.
+ * @returns { text: string }
+ */
+function createCaptcha(option) {
+    const captcha = new _1.Captcha({ ctx: option.ctx });
+    const width = option.ctx.canvas.width;
+    const height = option.ctx.canvas.height;
+    const decoyCount = Math.floor(width * height / 2500);
+    if (!option.decoy)
+        option.decoy = {};
+    if (!option.decoy.total)
+        option.decoy.total = decoyCount;
+    captcha.addDecoy(option.decoy);
+    captcha.drawCaptcha(option.captcha);
+    captcha.drawTrace(option.trace);
+    captcha.addDecoy({ opacity: 1 });
+    return { text: captcha.text };
+}
+exports.createCaptcha = createCaptcha;
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/global.html b/docs/global.html index 2ad880a..bde03e0 100644 --- a/docs/global.html +++ b/docs/global.html @@ -66,7 +66,7 @@ @@ -150,34 +150,103 @@

+ +
-

Members

+

Methods

- - + + +

+ # -SetCaptchaOptions + + + createCaptcha(optionopt) + + +

+ + + +
+

Create custom captcha from scratch.

+
+ -
-

- # - - - defaultCaptchaOptions - + + + + + + + +

Parameters:
- +
+ + + + + + + + + + + -
-

Default captcha customisation options.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
option + + +CreateCaptchaOptions + + + + + + <optional>
+ + + + + +

Captcha text.

@@ -217,9 +286,9 @@

- View Source + View Source - constants.js, line 47 + extra.js, line 10

@@ -229,23 +298,55 @@

-

+ + + + + + + + + + + + + +
+
+
-
+ - - - -SetDecoyOptions +
+ + +
+ +
+
- -

- # + + +

+ +
+
+ + + +
+

Type Definitions

+
+ +
+ +

+ # - defaultDecoyOptions + CreateCaptchaOptions

@@ -254,13 +355,168 @@

-

Default Decoy Options

+

Create captcha options in functions.

+

Properties:
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
captcha + + +SetCaptchaOptions + + + + + + <optional>
+ + + +

Captcha text options to customise text appearance and value.

decoy + + +SetDecoyOptions + + + + + + <optional>
+ + + +
trace + + +SetTraceOptions + + + + + + <optional>
+ + + +
background + + +Image + + + + + + <optional>
+ + + +
+
+ + +
@@ -296,7 +552,7 @@

View Source - constants.js, line 72 + constants.js, line 86

@@ -310,19 +566,11 @@

- - - -SetTraceOptions - - - - -

- # +

+ # - defaultTraceOptions + DrawCaptchaOption

@@ -331,169 +579,334 @@

-

Default trace line options.

+

Captcha text options to customise text appearance and value.

-
+
Properties:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + - + - + + + + + + + - - - - + -
-

Methods

-
- -
- -

- # +

+ + + + + + - -
-

random number in range

-
+ - - - - - - - - -
Parameters:
+
NameTypeAttributesDefaultDescription
text + + +string + + + + + + <optional>
+ + + +
+ + "Random UpperCase string" + +

Text of captcha

color + + +hexCode + + + + + + <optional>
+ + + +
+ + "#32cf7e" + +

Color of captcha text.

font + + +font + + + + + + <optional>
+ + + +
+ + "Sans" + +

Font of captcha text.

skew + + +boolean + + + + + + <optional>
+ + + +
+ + true + +

Skew captcha text.

colors + +array - + + + <optional>
+ + +
+ + [] + +

Array of hexCode will override color property.

rotate + +number - -

- View Source - - constants.js, line 62 - -

- - - - - - +
+ + <optional>
+ + +
- - - getRandom(start, end) - - - + 5 + +

Range of angle to rotate text.

size -
- - - - - - - - - - - - - - - - - - +number - - - - - - - - - + + + - + + + - + - - - - - + + + - + + - + + + - - + + + + + @@ -502,8 +915,6 @@
Parameters:
- -
@@ -537,9 +948,9 @@
Parameters:

- View Source + View Source - main.js, line 24 + constants.js, line 5

@@ -549,50 +960,8 @@
Parameters:
- - - - - - - - - - - - - -
-
-
- - - -
- -

random number between start and end

- - -
- - -
-
- - - - - - - - - -
-

Type Definitions

-
-

@@ -1016,7 +1385,7 @@

Properties:

View Source - constants.js, line 10 + constants.js, line 27

@@ -1221,6 +1590,41 @@
Properties:
+ +
+ + + + + + + + + + + + + + + + + +
NameTypeDescription
start - - -Number + + + + <optional>
+ - + +
+ + 40 + +

range start num

Size of captcha text.

endopacity - - -Number + + + +float - - + + <optional>
+ - + +

range end num

+ + 1 + +

Opcaity of captcha text.

total + + +number + + + + + + <optional>
+ + + +
+ +

Total count of decoy characters.

@@ -1262,7 +1666,7 @@
Properties:

View Source - constants.js, line 34 + constants.js, line 62

@@ -1438,7 +1842,7 @@
Properties:

View Source - constants.js, line 1 + constants.js, line 76

@@ -1647,7 +2051,7 @@
Properties:

View Source - constants.js, line 26 + constants.js, line 51

diff --git a/docs/index.html b/docs/index.html index c4aadb1..b190f22 100644 --- a/docs/index.html +++ b/docs/index.html @@ -66,7 +66,7 @@ @@ -101,9 +101,9 @@

-

Note: for v3 (skia-canvas) doumentation click here.

-
-

Getting Started

+

Note: for v3 (skia-canvas) doumentation click here.

+ +

Getting Started

To start using this npm module. We first need to install it from npm by using command npm install captcha-canvas.

Now require this package in your file where you want to create captcha.

const { CaptchaGenerator } = require("captcha-canvas");
diff --git a/examples/all.png b/examples/all.png
deleted file mode 100644
index 8420d6c..0000000
Binary files a/examples/all.png and /dev/null differ
diff --git a/examples/captcha.png b/examples/captcha.png
deleted file mode 100644
index 21d274e..0000000
Binary files a/examples/captcha.png and /dev/null differ
diff --git a/examples/default.png b/examples/default.png
deleted file mode 100644
index 67228cc..0000000
Binary files a/examples/default.png and /dev/null differ
diff --git a/examples/dimension.png b/examples/dimension.png
deleted file mode 100644
index bfac300..0000000
Binary files a/examples/dimension.png and /dev/null differ
diff --git a/examples/example.js b/examples/example.js
index 1630e0a..98eb200 100644
--- a/examples/example.js
+++ b/examples/example.js
@@ -1,40 +1,40 @@
 const fs = require('fs');
-const { CaptchaGenerator, Captcha } = require('../js-script');
-
-(async () => {
-	const captcha = new CaptchaGenerator();
-	fs.writeFileSync('./examples/default.png', captcha.generateSync());
-	/* Set dimension method example*/
-	captcha.setDimension(200, 400);
-	fs.writeFileSync('./examples/dimension.png', captcha.generateSync());
-	/* Set Captcha Method example*/
-	captcha.setCaptcha({
+const { Captcha, createCaptcha } = require('../js-script');
+const { createCanvas } = require('canvas');
+
+function test1() {
+	const canvas = createCanvas(300, 100);
+	const ctx = canvas.getContext('2d');
+
+	const { text } = createCaptcha({
+		ctx,
+	});
+
+	console.log(text);
+	fs.writeFileSync('./examples/example.png', canvas.toBuffer('image/png'));
+}
+
+async function test2() {
+	const canvas = createCanvas(600, 400);
+	const ctx = canvas.getContext('2d');
+
+	const captcha = new Captcha({ ctx, characters: 8 });
+
+	captcha.drawCaptcha({
 		color: 'deeppink',
-		font: 'Candara',
-		size: '60',
-		characters: 8,
-	})
-		.setDimension(150, 450);
-	fs.writeFileSync('./examples/captcha.png', captcha.generateSync());
-	console.log(captcha.text);
-	/* All methods at once*/
-	captcha
-		.setCaptcha({ color: 'deeppink', size: 60, text: 'CUSTOM05' })
-		.setDecoy({ opacity: 0.5 })
-		.setTrace({ color: 'deeppink', size: 5 });
-	fs.writeFileSync('./examples/all.png', await captcha.generate());
-	// console text of captcha
-	console.log(captcha.text);
-	// example captcha
-	const exmCaptcha = new Captcha(600, 200, 8);
-	exmCaptcha.addDecoy({ total: 20, size: 40 });
-	exmCaptcha.drawCaptcha({ size: 40 });
-	exmCaptcha.addDecoy();
-	exmCaptcha.drawTrace();
-
-	exmCaptcha.async = false;
-	console.log('Example captcha text: ' + exmCaptcha.text);
-
-	fs.writeFileSync('./examples/example.png', exmCaptcha.png);
-})();
+		size: 60,
+	});
+	captcha.drawTrace({
+		size: 5,
+		color: 'deeppink',
+	});
+	captcha.addDecoy({
+		total: 100,
+	});
+
+	const text = captcha.text;
+	fs.writeFileSync(`./examples/tmp/${new Date().getTime()}-${text}.png`, canvas.toBuffer('image/png'));
+}
 
+test1();
+test2();
\ No newline at end of file
diff --git a/examples/example.png b/examples/example.png
index 4381562..c89d076 100644
Binary files a/examples/example.png and b/examples/example.png differ
diff --git a/js-script/CaptchaGenerator.d.ts b/js-script/CaptchaGenerator.d.ts
index 7f17fe2..a51cab5 100644
--- a/js-script/CaptchaGenerator.d.ts
+++ b/js-script/CaptchaGenerator.d.ts
@@ -1,5 +1,4 @@
 /// 
-import { Image } from "skia-canvas";
 import { SetCaptchaOption, SetDecoyOption, SetTraceOption } from "./constants";
 /**
  * Captcha generator class.
@@ -11,6 +10,7 @@ export declare class CaptchaGenerator {
     private trace;
     private decoy;
     private background?;
+    private ctx;
     /**
      * Initatiates the creation of captcha image generation.
      * @example const captcha = new CaptchaGenerator({height: 200, width: 600});
@@ -19,9 +19,10 @@ export declare class CaptchaGenerator {
      * @param {integer} [options.width=300] Width of captcha image.
      * @since 2.0.0
      */
-    constructor(options?: {
-        height: number;
-        width: number;
+    constructor(options: {
+        height?: number;
+        width?: number;
+        ctx: CanvasRenderingContext2D;
     });
     /**
      * Get the text of captcha.
@@ -107,7 +108,7 @@ export declare class CaptchaGenerator {
      * fs.writeFileSync("image.png", buffer)
      * @since 2.0.0
      */
-    generate(): Promise;
+    generate(): Promise;
     /**
      * Non asynchronous method to generate captcha image.
      * > Note: It do not use `setBackground` method value for background image. If you want to set background
@@ -126,6 +127,6 @@ export declare class CaptchaGenerator {
      * @since 2.2.0
      */
     generateSync(option?: {
-        background?: Image;
-    }): Buffer;
+        background?: CanvasImageSource;
+    }): CanvasRenderingContext2D;
 }
diff --git a/js-script/CaptchaGenerator.js b/js-script/CaptchaGenerator.js
index 02470c1..125dcfc 100644
--- a/js-script/CaptchaGenerator.js
+++ b/js-script/CaptchaGenerator.js
@@ -1,7 +1,6 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.CaptchaGenerator = void 0;
-const skia_canvas_1 = require("skia-canvas");
 const _1 = require(".");
 const constants_1 = require("./constants");
 const util_1 = require("./util");
@@ -17,13 +16,14 @@ class CaptchaGenerator {
      * @param {integer} [options.width=300] Width of captcha image.
      * @since 2.0.0
      */
-    constructor(options = { height: 100, width: 300 }) {
-        this.height = options.height;
-        this.width = options.width;
+    constructor(options) {
+        this.height = options.height || 100;
+        this.width = options.width || 300;
         this.captcha = constants_1.defaultCaptchaOption;
         this.trace = constants_1.defaultTraceOptions;
         this.decoy = constants_1.defaultDecoyOptions;
         this.captcha.text = (0, util_1.randomText)(this.captcha.characters || 6);
+        this.ctx = options.ctx;
     }
     /**
      * Get the text of captcha.
@@ -132,16 +132,14 @@ class CaptchaGenerator {
      * @since 2.0.0
      */
     async generate() {
-        const captchaCanvas = new _1.Captcha(this.width, this.height);
-        if (this.background)
-            captchaCanvas.drawImage(await (0, skia_canvas_1.loadImage)(this.background));
+        const captchaCanvas = new _1.Captcha({ height: this.height, width: this.width, ctx: this.ctx });
         if (this.decoy.opacity)
             captchaCanvas.addDecoy(this.decoy);
         if (this.captcha.opacity)
             captchaCanvas.drawCaptcha(this.captcha);
         if (this.trace.opacity)
             captchaCanvas.drawTrace(this.trace);
-        return captchaCanvas.png;
+        return captchaCanvas.getContext();
     }
     /**
      * Non asynchronous method to generate captcha image.
@@ -161,7 +159,12 @@ class CaptchaGenerator {
      * @since 2.2.0
      */
     generateSync(option = {}) {
-        const captchaCanvas = new _1.Captcha(this.width, this.height, this.captcha.characters);
+        const captchaCanvas = new _1.Captcha({
+            width: this.width,
+            height: this.height,
+            characters: this.captcha.characters,
+            ctx: this.ctx,
+        });
         captchaCanvas.async = false;
         if (option.background)
             captchaCanvas.drawImage(option.background);
@@ -171,7 +174,7 @@ class CaptchaGenerator {
             captchaCanvas.drawCaptcha(this.captcha);
         if (this.trace.opacity)
             captchaCanvas.drawTrace(this.trace);
-        return captchaCanvas.png;
+        return captchaCanvas.getContext();
     }
 }
 exports.CaptchaGenerator = CaptchaGenerator;
diff --git a/js-script/captcha.d.ts b/js-script/captcha.d.ts
index 739e90b..ccaa743 100644
--- a/js-script/captcha.d.ts
+++ b/js-script/captcha.d.ts
@@ -1,43 +1,49 @@
-/// 
-import { Canvas, CanvasRenderingContext2D, Image } from "skia-canvas";
-import { SetCaptchaOption, SetDecoyOption, SetTraceOption, DrawCaptchaOption } from "./constants";
+/// 
+import { defaultCaptchaOption, defaultDecoyOptions, defaultTraceOptions, SetDecoyOption, SetTraceOption, DrawCaptchaOption } from "./constants";
+/**
+ * Constructor options
+ * @typedef ConstructorOptions
+ * @property {number} [characters=6] Length of captcha text.
+ * @property {CanvasRenderingContext2D} ctx
+ */
+export declare type ConstructorOptions = {
+    characters?: number;
+    ctx: CanvasRenderingContext2D;
+};
 /**
  * Captcha Generator
  */
 export declare class Captcha {
     protected _height: number;
     protected _width: number;
-    protected _captcha: SetCaptchaOption;
-    protected _trace: SetTraceOption;
-    protected _decoy: SetDecoyOption;
-    protected _canvas: Canvas;
+    protected _captcha: typeof defaultCaptchaOption;
+    protected _trace: typeof defaultTraceOptions;
+    protected _decoy: typeof defaultDecoyOptions;
     protected _ctx: CanvasRenderingContext2D;
     protected _coordinates: number[][];
     async: boolean;
     /**
      * Start captcha image creation.
-     * @param {number} [width] Width of captcha image.
-     * @param {number} [height] Height of captcha image.
-     * @param {number} [characters] Size of captcha text.
+     * @param {ConstructorOptions} [option] Size of captcha text.
      * @constructor
      */
-    constructor(width?: number, height?: number, characters?: number);
+    constructor({ characters, ctx, }: ConstructorOptions);
+    /**
+     * Get canvas context.
+     * @returns {CanvasRenderingContext2D}
+     */
+    get context(): CanvasRenderingContext2D;
     /**
      * Get Captcha text.
      * @returns {string} Get captcha text.
      */
     get text(): string;
-    /**
-     * Get png image of captcha.
-     * @returns {Buffer | Promise} Get png image of captcha created.
-     */
-    get png(): Buffer | Promise;
     /**
      * Draw image on your captcha.
      * @param {Image} image Choose image you want to add.
      * @returns {Captcha}
      */
-    drawImage(image: Image): Captcha;
+    drawImage(image: CanvasImageSource): Captcha;
     /**
      * Add decoy on your captcha image.
      * @param {SetDecoyOptions} [decoyOption] Decoy option you want to customise
diff --git a/js-script/captcha.js b/js-script/captcha.js
index 65afcf1..3def88a 100644
--- a/js-script/captcha.js
+++ b/js-script/captcha.js
@@ -1,7 +1,7 @@
 "use strict";
+/// 
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.Captcha = void 0;
-const skia_canvas_1 = require("skia-canvas");
 const constants_1 = require("./constants");
 const util_1 = require("./util");
 /**
@@ -10,28 +10,29 @@ const util_1 = require("./util");
 class Captcha {
     /**
      * Start captcha image creation.
-     * @param {number} [width] Width of captcha image.
-     * @param {number} [height] Height of captcha image.
-     * @param {number} [characters] Size of captcha text.
+     * @param {ConstructorOptions} [option] Size of captcha text.
      * @constructor
      */
-    constructor(width = constants_1.defaultDimension.width, height = constants_1.defaultDimension.height, characters = constants_1.defaultCaptchaOption.characters) {
-        this._height = height;
-        this._width = width;
+    constructor({ characters = constants_1.defaultCaptchaOption.characters, ctx, }) {
         this._captcha = constants_1.defaultCaptchaOption;
         this._captcha.characters = characters;
         this._trace = constants_1.defaultTraceOptions;
         this._decoy = constants_1.defaultDecoyOptions;
-        const canvas = new skia_canvas_1.Canvas(width, height);
-        const ctx = canvas.getContext('2d');
         ctx.lineJoin = 'miter';
         ctx.textBaseline = 'middle';
         ctx.textAlign = 'center';
-        this._canvas = canvas;
+        this._width = ctx.canvas.width;
+        this._height = ctx.canvas.height;
         this._ctx = ctx;
         this.async = true;
         this._coordinates = [];
-        this._canvas.gpu = false;
+    }
+    /**
+     * Get canvas context.
+     * @returns {CanvasRenderingContext2D}
+     */
+    get context() {
+        return this._ctx;
     }
     /**
      * Get Captcha text.
@@ -40,18 +41,6 @@ class Captcha {
     get text() {
         return this._captcha.text || "";
     }
-    /**
-     * Get png image of captcha.
-     * @returns {Buffer | Promise} Get png image of captcha created.
-     */
-    get png() {
-        if (this.async) {
-            return this._canvas.toBuffer('png');
-        }
-        else {
-            return this._canvas.toBufferSync('png');
-        }
-    }
     /**
      * Draw image on your captcha.
      * @param {Image} image Choose image you want to add.
diff --git a/js-script/constants.d.ts b/js-script/constants.d.ts
index dacccc1..38eaf00 100644
--- a/js-script/constants.d.ts
+++ b/js-script/constants.d.ts
@@ -1,4 +1,4 @@
-import { Image } from "skia-canvas";
+/// 
 export interface SetDimensionOption {
     height: number;
     width: number;
@@ -40,7 +40,9 @@ export interface CreateCaptchaOptions {
     captcha?: SetCaptchaOption;
     trace?: SetTraceOption;
     decoy?: SetDecoyOption;
-    background?: Image;
+    background?: CanvasImageSource;
+    dimension?: SetDimensionOption;
+    ctx: CanvasRenderingContext2D;
 }
 /**
  * Captcha text options to customise text appearance and value.
@@ -54,7 +56,16 @@ export interface CreateCaptchaOptions {
  * @property {number} [size=40] Size of captcha text.
  * @property {float} [opacity=1] Opcaity of captcha text.
  */
-export declare const defaultDrawCaptchaOption: DrawCaptchaOption;
+export declare const defaultDrawCaptchaOption: {
+    size: number;
+    font: string;
+    skew: boolean;
+    colors: never[];
+    rotate: number;
+    color: string;
+    opacity: number;
+    text: string;
+};
 /**
  * Captcha text options to customise text appearance and value.
  * @typedef SetCaptchaOptions
@@ -68,14 +79,28 @@ export declare const defaultDrawCaptchaOption: DrawCaptchaOption;
  * @property {number} [size=40] Size of captcha text.
  * @property {float} [opacity=1] Opcaity of captcha text.
  */
-export declare const defaultCaptchaOption: SetCaptchaOption;
+export declare const defaultCaptchaOption: {
+    characters: number;
+    size: number;
+    font: string;
+    skew: boolean;
+    colors: string[] | [];
+    rotate: number;
+    color: string;
+    opacity: number;
+    text: string;
+};
 /**
  * @typedef SetTraceOptions
  * @property {hexCode} [color="#32cf7e"] Color of trace line.
  * @property {number} [size=3] Width of trace line.
  * @property {float} [opacity=1] Opacoty of trace line.
  */
-export declare const defaultTraceOptions: SetTraceOption;
+export declare const defaultTraceOptions: {
+    size: number;
+    color: string;
+    opacity: number;
+};
 /**
  * @typedef SetDecoyOptions
  * @property {hexCode} [color="#646566"] Color of decoy characters.
@@ -84,14 +109,22 @@ export declare const defaultTraceOptions: SetTraceOption;
  * @property {float} [opacity=0.8] Opacity of decoy characters.
  * @property {number} [total] Total count of decoy characters.
  */
-export declare const defaultDecoyOptions: SetDecoyOption;
+export declare const defaultDecoyOptions: {
+    color: string;
+    font: string;
+    size: number;
+    opacity: number;
+};
 /**
  * Customise dimension of captcha image.
  * @typedef SetDimensionOption
  * @property {integer} [height=100] Height of captcha image.
  * @property {integer} [width=300] Width of captcha image.
  */
-export declare const defaultDimension: SetDimensionOption;
+export declare const defaultDimension: {
+    height: number;
+    width: number;
+};
 /**
  * Create captcha options in functions.
  * @typedef CreateCaptchaOptions
diff --git a/js-script/constants.js b/js-script/constants.js
index 215b7f9..b6d94a9 100644
--- a/js-script/constants.js
+++ b/js-script/constants.js
@@ -1,4 +1,5 @@
 "use strict";
+/// 
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.CreateCaptchaOptions = exports.defaultDimension = exports.defaultDecoyOptions = exports.defaultTraceOptions = exports.defaultCaptchaOption = exports.defaultDrawCaptchaOption = void 0;
 /**
@@ -21,6 +22,7 @@ exports.defaultDrawCaptchaOption = {
     rotate: 5,
     color: '#32cf7e',
     opacity: 0.8,
+    text: '',
 };
 /**
  * Captcha text options to customise text appearance and value.
@@ -39,11 +41,12 @@ exports.defaultCaptchaOption = {
     characters: 6,
     size: 40,
     font: 'Sans',
-    skew: true,
+    skew: false,
     colors: [],
-    rotate: 5,
+    rotate: 0,
     color: '#32cf7e',
     opacity: 0.8,
+    text: '',
 };
 /**
  * @typedef SetTraceOptions
diff --git a/js-script/extra.d.ts b/js-script/extra.d.ts
index 71d7367..45ec961 100644
--- a/js-script/extra.d.ts
+++ b/js-script/extra.d.ts
@@ -1,28 +1,9 @@
-/// 
 import { CreateCaptchaOptions } from "./constants";
-interface captchaValueSync {
-    image: Buffer;
-    text: string;
-}
-interface captchaValue {
-    image: Promise;
-    text: string;
-}
 /**
  * Create custom captcha from scratch.
- * @async
- * @param {number} width Width of captcha image.
- * @param {number} height Height of captcha image.
- * @param {CreateCaptchaOptions} [option] Captcha text.
- * @returns
- */
-export declare function createCaptcha(width: number, height: number, option?: CreateCaptchaOptions): captchaValue;
-/**
- * Create captcha in sync mode.
- * @param {number} width captcha image width.
- * @param {number} height captcha image height.
  * @param {CreateCaptchaOptions} [option] Captcha text.
- * @returns
+ * @returns { text: string }
  */
-export declare function createCaptchaSync(width: number, height: number, option?: CreateCaptchaOptions): captchaValueSync;
-export {};
+export declare function createCaptcha(option: CreateCaptchaOptions): {
+    text: string;
+};
diff --git a/js-script/extra.js b/js-script/extra.js
index 108296d..03edc40 100644
--- a/js-script/extra.js
+++ b/js-script/extra.js
@@ -1,48 +1,83 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.createCaptchaSync = exports.createCaptcha = void 0;
-const _1 = require(".");
+exports.createCaptcha = void 0;
+const constants_1 = require("./constants");
+const util_1 = require("./util");
+function drawDecoy(ctx, decoyOption = {}) {
+    const option = { ...constants_1.defaultDecoyOptions, ...decoyOption };
+    if (!option.total)
+        option.total = Math.floor(ctx.canvas.width * ctx.canvas.height / 10000);
+    const decoyText = (0, util_1.randomText)(option.total);
+    ctx.font = `${option.size}px ${option.font}`;
+    ctx.globalAlpha = option.opacity;
+    ctx.fillStyle = option.color;
+    for (const element of decoyText) {
+        ctx.fillText(element, (0, util_1.getRandom)(30, ctx.canvas.width - 30), (0, util_1.getRandom)(30, ctx.canvas.height - 30));
+    }
+}
+function drawTrace(ctx, coordinates, traceOption = {}) {
+    const option = { ...constants_1.defaultTraceOptions, ...traceOption };
+    ctx.strokeStyle = option.color;
+    ctx.globalAlpha = option.opacity;
+    ctx.beginPath();
+    ctx.moveTo(coordinates[0][0], coordinates[0][1]);
+    ctx.lineWidth = option.size;
+    for (let i = 1; i < coordinates.length; i++) {
+        ctx.lineTo(coordinates[i][0], coordinates[i][1]);
+    }
+    ctx.stroke();
+}
+function drawCaptcha(ctx, coordinates, captchaOption = {}) {
+    var _a;
+    const option = { ...constants_1.defaultCaptchaOption, ...captchaOption };
+    if (captchaOption.text)
+        option.text = captchaOption.text;
+    if (!option.text || option.text == "")
+        option.text = (0, util_1.randomText)(option.characters);
+    if (option.text.length != option.characters) {
+        if (captchaOption.text) {
+            throw new Error("Size of text and no. of characters is not matching.");
+        }
+        else {
+            option.text = (0, util_1.randomText)(option.characters);
+        }
+    }
+    ctx.font = `${option.size}px ${option.font}`;
+    ctx.globalAlpha = option.opacity;
+    ctx.fillStyle = option.color;
+    for (let n = 0; n < coordinates.length; n++) {
+        if (option.skew) {
+            ctx.transform(1, Math.random(), (0, util_1.getRandom)(20) / 100, 1, 0, 0);
+        }
+        if (option.rotate && option.rotate > 0) {
+            ctx.rotate((0, util_1.getRandom)(-option.rotate, option.rotate) * Math.PI / 180);
+        }
+        if (option.colors && ((_a = option.colors) === null || _a === void 0 ? void 0 : _a.length) >= 2) {
+            ctx.fillStyle = option.colors[(0, util_1.getRandom)(option.colors.length - 1)];
+        }
+        ctx.fillText(option.text[n], coordinates[n][0], coordinates[n][1]);
+    }
+    return { text: option.text };
+}
 /**
  * Create custom captcha from scratch.
- * @async
- * @param {number} width Width of captcha image.
- * @param {number} height Height of captcha image.
  * @param {CreateCaptchaOptions} [option] Captcha text.
- * @returns
+ * @returns { text: string }
  */
-function createCaptcha(width, height, option = {}) {
-    const captcha = new _1.Captcha(width, height);
+function createCaptcha(option) {
+    var _a;
+    const width = option.ctx.canvas.width;
+    const height = option.ctx.canvas.height;
     const decoyCount = Math.floor(width * height / 2500);
+    const coordinates = (0, util_1.getRandomCoordinate)(height, width, ((_a = option.captcha) === null || _a === void 0 ? void 0 : _a.characters) || constants_1.defaultCaptchaOption.characters);
     if (!option.decoy)
         option.decoy = {};
     if (!option.decoy.total)
         option.decoy.total = decoyCount;
-    captcha.addDecoy(option.decoy);
-    captcha.drawCaptcha(option.captcha);
-    captcha.drawTrace(option.trace);
-    captcha.addDecoy({ opacity: 1 });
-    return { image: captcha.png, text: captcha.text };
+    drawDecoy(option.ctx, option.decoy);
+    const { text } = drawCaptcha(option.ctx, coordinates, option.captcha);
+    drawTrace(option.ctx, coordinates, option.trace);
+    drawDecoy(option.ctx, { opacity: 1 });
+    return { text };
 }
 exports.createCaptcha = createCaptcha;
-/**
- * Create captcha in sync mode.
- * @param {number} width captcha image width.
- * @param {number} height captcha image height.
- * @param {CreateCaptchaOptions} [option] Captcha text.
- * @returns
- */
-function createCaptchaSync(width, height, option = {}) {
-    const captcha = new _1.Captcha(width, height);
-    const decoyCount = Math.floor(width * height / 2500);
-    captcha.async = false;
-    if (!option.decoy)
-        option.decoy = {};
-    if (!option.decoy.total)
-        option.decoy.total = decoyCount;
-    captcha.addDecoy(option.decoy);
-    captcha.drawCaptcha(option.captcha);
-    captcha.drawTrace(option.trace);
-    captcha.addDecoy({ opacity: 1 });
-    return { image: captcha.png, text: captcha.text };
-}
-exports.createCaptchaSync = createCaptchaSync;
diff --git a/js-script/index.d.ts b/js-script/index.d.ts
index 0724cbf..45d7adf 100644
--- a/js-script/index.d.ts
+++ b/js-script/index.d.ts
@@ -1,4 +1,2 @@
-export { createCaptcha, createCaptchaSync } from "./extra";
-export { loadImage as resolveImage } from "skia-canvas";
+export { createCaptcha } from "./extra";
 export { Captcha } from "./captcha";
-export { CaptchaGenerator } from "./CaptchaGenerator";
diff --git a/js-script/index.js b/js-script/index.js
index ddc28cb..07cd28d 100644
--- a/js-script/index.js
+++ b/js-script/index.js
@@ -1,12 +1,7 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.CaptchaGenerator = exports.Captcha = exports.resolveImage = exports.createCaptchaSync = exports.createCaptcha = void 0;
+exports.Captcha = exports.createCaptcha = void 0;
 var extra_1 = require("./extra");
 Object.defineProperty(exports, "createCaptcha", { enumerable: true, get: function () { return extra_1.createCaptcha; } });
-Object.defineProperty(exports, "createCaptchaSync", { enumerable: true, get: function () { return extra_1.createCaptchaSync; } });
-var skia_canvas_1 = require("skia-canvas");
-Object.defineProperty(exports, "resolveImage", { enumerable: true, get: function () { return skia_canvas_1.loadImage; } });
 var captcha_1 = require("./captcha");
 Object.defineProperty(exports, "Captcha", { enumerable: true, get: function () { return captcha_1.Captcha; } });
-var CaptchaGenerator_1 = require("./CaptchaGenerator");
-Object.defineProperty(exports, "CaptchaGenerator", { enumerable: true, get: function () { return CaptchaGenerator_1.CaptchaGenerator; } });
diff --git a/package-lock.json b/package-lock.json
index fbbd2fb..734ceee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,14 +8,11 @@
       "name": "captcha-canvas",
       "version": "3.2.3",
       "license": "Apache-2.0",
-      "dependencies": {
-        "skia-canvas": "^1.0.2"
-      },
       "devDependencies": {
         "@types/node": "^14.11.8",
         "@types/skia-canvas": "^0.9.28",
         "better-docs": "^2.3.2",
-        "canvas": "^2.8.0",
+        "canvas": "^2.11.2",
         "eslint": "^7.27.0",
         "jsdoc": "^3.6.7",
         "typescript": "^4.0.2"
@@ -754,6 +751,7 @@
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz",
       "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==",
+      "dev": true,
       "dependencies": {
         "detect-libc": "^2.0.0",
         "https-proxy-agent": "^5.0.0",
@@ -809,7 +807,8 @@
     "node_modules/abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true
     },
     "node_modules/acorn": {
       "version": "7.4.1",
@@ -857,6 +856,7 @@
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
       "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dev": true,
       "dependencies": {
         "debug": "4"
       },
@@ -907,6 +907,7 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -929,12 +930,14 @@
     "node_modules/aproba": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+      "dev": true
     },
     "node_modules/are-we-there-yet": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
       "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+      "dev": true,
       "dependencies": {
         "delegates": "^1.0.0",
         "readable-stream": "^3.6.0"
@@ -1028,7 +1031,8 @@
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
     },
     "node_modules/better-docs": {
       "version": "2.3.2",
@@ -1080,6 +1084,7 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -1176,14 +1181,15 @@
       }
     },
     "node_modules/canvas": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz",
-      "integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==",
+      "version": "2.11.2",
+      "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
+      "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
       "dev": true,
       "hasInstallScript": true,
+      "license": "MIT",
       "dependencies": {
         "@mapbox/node-pre-gyp": "^1.0.0",
-        "nan": "^2.14.0",
+        "nan": "^2.17.0",
         "simple-get": "^3.0.3"
       },
       "engines": {
@@ -1229,6 +1235,7 @@
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz",
       "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==",
+      "dev": true,
       "bin": {
         "cargo-cp-artifact": "bin/cargo-cp-artifact.js"
       }
@@ -1287,6 +1294,7 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
       "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+      "dev": true,
       "engines": {
         "node": ">=10"
       }
@@ -1345,6 +1353,7 @@
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
       "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+      "dev": true,
       "bin": {
         "color-support": "bin.js"
       }
@@ -1358,12 +1367,14 @@
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
     },
     "node_modules/console-control-strings": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+      "dev": true
     },
     "node_modules/constantinople": {
       "version": "3.1.2",
@@ -1418,6 +1429,7 @@
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
       "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+      "dev": true,
       "dependencies": {
         "ms": "2.1.2"
       },
@@ -1443,6 +1455,7 @@
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
       "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "dev": true,
       "dependencies": {
         "mimic-response": "^3.1.0"
       },
@@ -1462,12 +1475,14 @@
     "node_modules/delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+      "dev": true
     },
     "node_modules/detect-libc": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
       "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
+      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -1505,7 +1520,8 @@
     "node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
     },
     "node_modules/enquirer": {
       "version": "2.3.6",
@@ -1841,6 +1857,7 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
       "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dev": true,
       "dependencies": {
         "minipass": "^3.0.0"
       },
@@ -1851,7 +1868,8 @@
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
     },
     "node_modules/function-bind": {
       "version": "1.1.1",
@@ -1869,6 +1887,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
       "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+      "dev": true,
       "dependencies": {
         "aproba": "^1.0.3 || ^2.0.0",
         "color-support": "^1.1.2",
@@ -1920,6 +1939,7 @@
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
       "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+      "dev": true,
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -2019,7 +2039,8 @@
     "node_modules/has-unicode": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+      "dev": true
     },
     "node_modules/hash-sum": {
       "version": "1.0.2",
@@ -2046,6 +2067,7 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
       "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+      "dev": true,
       "dependencies": {
         "agent-base": "6",
         "debug": "4"
@@ -2092,6 +2114,7 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
       "dependencies": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -2100,7 +2123,8 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
     },
     "node_modules/is-buffer": {
       "version": "1.1.6",
@@ -2155,6 +2179,7 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
       "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -2480,6 +2505,7 @@
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
       "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
       "dependencies": {
         "yallist": "^4.0.0"
       },
@@ -2491,6 +2517,7 @@
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
       "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "dev": true,
       "dependencies": {
         "semver": "^6.0.0"
       },
@@ -2505,6 +2532,7 @@
       "version": "6.3.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
       "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
       "bin": {
         "semver": "bin/semver.js"
       }
@@ -2556,6 +2584,7 @@
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
       "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "dev": true,
       "engines": {
         "node": ">=10"
       },
@@ -2576,6 +2605,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -2587,6 +2617,7 @@
       "version": "3.1.5",
       "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz",
       "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==",
+      "dev": true,
       "dependencies": {
         "yallist": "^4.0.0"
       },
@@ -2598,6 +2629,7 @@
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
       "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dev": true,
       "dependencies": {
         "minipass": "^3.0.0",
         "yallist": "^4.0.0"
@@ -2610,6 +2642,7 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
       "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true,
       "bin": {
         "mkdirp": "bin/cmd.js"
       },
@@ -2620,13 +2653,15 @@
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
     },
     "node_modules/nan": {
-      "version": "2.15.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
-      "dev": true
+      "version": "2.20.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
+      "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/nanocolors": {
       "version": "0.2.12",
@@ -2662,6 +2697,7 @@
       "version": "2.6.7",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
       "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "dev": true,
       "dependencies": {
         "whatwg-url": "^5.0.0"
       },
@@ -2687,6 +2723,7 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
       "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+      "dev": true,
       "dependencies": {
         "abbrev": "1"
       },
@@ -2701,6 +2738,7 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
       "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+      "dev": true,
       "dependencies": {
         "are-we-there-yet": "^2.0.0",
         "console-control-strings": "^1.1.0",
@@ -2712,6 +2750,7 @@
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
       "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -2720,6 +2759,7 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
       "dependencies": {
         "wrappy": "1"
       }
@@ -2786,12 +2826,14 @@
     "node_modules/parenthesis": {
       "version": "3.1.8",
       "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz",
-      "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw=="
+      "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==",
+      "dev": true
     },
     "node_modules/path-browserify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
-      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+      "dev": true
     },
     "node_modules/path-exists": {
       "version": "4.0.0",
@@ -2806,6 +2848,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -3107,6 +3150,7 @@
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
       "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+      "dev": true,
       "dependencies": {
         "inherits": "^2.0.3",
         "string_decoder": "^1.1.1",
@@ -3241,6 +3285,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
       "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
       "dependencies": {
         "glob": "^7.1.3"
       },
@@ -3272,6 +3317,7 @@
       "version": "7.5.4",
       "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
       "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dev": true,
       "dependencies": {
         "lru-cache": "^6.0.0"
       },
@@ -3285,7 +3331,8 @@
     "node_modules/set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+      "dev": true
     },
     "node_modules/shebang-command": {
       "version": "2.0.0",
@@ -3311,12 +3358,14 @@
     "node_modules/signal-exit": {
       "version": "3.0.5",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz",
-      "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ=="
+      "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==",
+      "dev": true
     },
     "node_modules/simple-concat": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
       "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -3336,6 +3385,7 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
       "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -3360,6 +3410,7 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/skia-canvas/-/skia-canvas-1.0.2.tgz",
       "integrity": "sha512-9IjebOdBf6ir5Ktq0aM0wPshZ61oNX+T+2/ZBNJQZ63Z3SsSAd7i6YaYbqqtP6X8bK9mqB8QO1Y5DUZXNXuYHw==",
+      "dev": true,
       "hasInstallScript": true,
       "dependencies": {
         "@mapbox/node-pre-gyp": "^1.0.9",
@@ -3374,6 +3425,7 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
       "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
       "dependencies": {
         "balanced-match": "^1.0.0"
       }
@@ -3382,6 +3434,7 @@
       "version": "8.0.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
       "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+      "dev": true,
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -3400,6 +3453,7 @@
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
       "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+      "dev": true,
       "dependencies": {
         "brace-expansion": "^2.0.1"
       },
@@ -3443,6 +3497,7 @@
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
       "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dev": true,
       "dependencies": {
         "safe-buffer": "~5.2.0"
       }
@@ -3451,6 +3506,7 @@
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -3470,6 +3526,7 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz",
       "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
+      "dev": true,
       "dependencies": {
         "parenthesis": "^3.1.5"
       }
@@ -3478,6 +3535,7 @@
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
       "dependencies": {
         "emoji-regex": "^8.0.0",
         "is-fullwidth-code-point": "^3.0.0",
@@ -3491,6 +3549,7 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
       "dependencies": {
         "ansi-regex": "^5.0.1"
       },
@@ -3583,6 +3642,7 @@
       "version": "6.2.1",
       "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
       "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+      "dev": true,
       "dependencies": {
         "chownr": "^2.0.0",
         "fs-minipass": "^2.0.0",
@@ -3599,6 +3659,7 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
       "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -3641,7 +3702,8 @@
     "node_modules/tr46": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
+      "dev": true
     },
     "node_modules/ts-map": {
       "version": "1.0.3",
@@ -3765,7 +3827,8 @@
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "dev": true
     },
     "node_modules/v8-compile-cache": {
       "version": "2.3.0",
@@ -3883,12 +3946,14 @@
     "node_modules/webidl-conversions": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
+      "dev": true
     },
     "node_modules/whatwg-url": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
       "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+      "dev": true,
       "dependencies": {
         "tr46": "~0.0.3",
         "webidl-conversions": "^3.0.0"
@@ -3913,6 +3978,7 @@
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
       "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+      "dev": true,
       "dependencies": {
         "string-width": "^1.0.2 || 2 || 3 || 4"
       }
@@ -3986,7 +4052,8 @@
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
     },
     "node_modules/xmlcreate": {
       "version": "2.0.3",
@@ -4006,7 +4073,8 @@
     "node_modules/yallist": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
     },
     "node_modules/yargs": {
       "version": "16.2.0",
@@ -4628,6 +4696,7 @@
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz",
       "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==",
+      "dev": true,
       "requires": {
         "detect-libc": "^2.0.0",
         "https-proxy-agent": "^5.0.0",
@@ -4679,7 +4748,8 @@
     "abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true
     },
     "acorn": {
       "version": "7.4.1",
@@ -4715,6 +4785,7 @@
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
       "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dev": true,
       "requires": {
         "debug": "4"
       }
@@ -4751,7 +4822,8 @@
     "ansi-regex": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true
     },
     "ansi-styles": {
       "version": "4.3.0",
@@ -4765,12 +4837,14 @@
     "aproba": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+      "dev": true
     },
     "are-we-there-yet": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
       "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+      "dev": true,
       "requires": {
         "delegates": "^1.0.0",
         "readable-stream": "^3.6.0"
@@ -4853,7 +4927,8 @@
     "balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
     },
     "better-docs": {
       "version": "2.3.2",
@@ -4895,6 +4970,7 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -4962,13 +5038,13 @@
       "dev": true
     },
     "canvas": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz",
-      "integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==",
+      "version": "2.11.2",
+      "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
+      "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
       "dev": true,
       "requires": {
         "@mapbox/node-pre-gyp": "^1.0.0",
-        "nan": "^2.14.0",
+        "nan": "^2.17.0",
         "simple-get": "^3.0.3"
       },
       "dependencies": {
@@ -5003,7 +5079,8 @@
     "cargo-cp-artifact": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz",
-      "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg=="
+      "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==",
+      "dev": true
     },
     "catharsis": {
       "version": "0.9.0",
@@ -5046,7 +5123,8 @@
     "chownr": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+      "dev": true
     },
     "clean-css": {
       "version": "4.2.3",
@@ -5094,7 +5172,8 @@
     "color-support": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
-      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
+      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+      "dev": true
     },
     "commander": {
       "version": "2.20.3",
@@ -5105,12 +5184,14 @@
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
     },
     "console-control-strings": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+      "dev": true
     },
     "constantinople": {
       "version": "3.1.2",
@@ -5160,6 +5241,7 @@
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
       "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+      "dev": true,
       "requires": {
         "ms": "2.1.2"
       }
@@ -5174,6 +5256,7 @@
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
       "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "dev": true,
       "requires": {
         "mimic-response": "^3.1.0"
       }
@@ -5187,12 +5270,14 @@
     "delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+      "dev": true
     },
     "detect-libc": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
-      "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
+      "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
+      "dev": true
     },
     "diff-match-patch": {
       "version": "1.0.5",
@@ -5224,7 +5309,8 @@
     "emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
     },
     "enquirer": {
       "version": "2.3.6",
@@ -5483,6 +5569,7 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
       "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dev": true,
       "requires": {
         "minipass": "^3.0.0"
       }
@@ -5490,7 +5577,8 @@
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
     },
     "function-bind": {
       "version": "1.1.1",
@@ -5508,6 +5596,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
       "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+      "dev": true,
       "requires": {
         "aproba": "^1.0.3 || ^2.0.0",
         "color-support": "^1.1.2",
@@ -5547,6 +5636,7 @@
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
       "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+      "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -5613,7 +5703,8 @@
     "has-unicode": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+      "dev": true
     },
     "hash-sum": {
       "version": "1.0.2",
@@ -5637,6 +5728,7 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
       "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+      "dev": true,
       "requires": {
         "agent-base": "6",
         "debug": "4"
@@ -5668,6 +5760,7 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -5676,7 +5769,8 @@
     "inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
     },
     "is-buffer": {
       "version": "1.1.6",
@@ -5720,7 +5814,8 @@
     "is-fullwidth-code-point": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true
     },
     "is-glob": {
       "version": "4.0.3",
@@ -5982,6 +6077,7 @@
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
       "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
       "requires": {
         "yallist": "^4.0.0"
       }
@@ -5990,6 +6086,7 @@
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
       "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "dev": true,
       "requires": {
         "semver": "^6.0.0"
       },
@@ -5997,7 +6094,8 @@
         "semver": {
           "version": "6.3.1",
           "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-          "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+          "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+          "dev": true
         }
       }
     },
@@ -6036,7 +6134,8 @@
     "mimic-response": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
-      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "dev": true
     },
     "min-indent": {
       "version": "1.0.1",
@@ -6048,6 +6147,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
       "requires": {
         "brace-expansion": "^1.1.7"
       }
@@ -6056,6 +6156,7 @@
       "version": "3.1.5",
       "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz",
       "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==",
+      "dev": true,
       "requires": {
         "yallist": "^4.0.0"
       }
@@ -6064,6 +6165,7 @@
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
       "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dev": true,
       "requires": {
         "minipass": "^3.0.0",
         "yallist": "^4.0.0"
@@ -6072,17 +6174,19 @@
     "mkdirp": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true
     },
     "ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
     },
     "nan": {
-      "version": "2.15.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
+      "version": "2.20.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
+      "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
       "dev": true
     },
     "nanocolors": {
@@ -6116,6 +6220,7 @@
       "version": "2.6.7",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
       "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "dev": true,
       "requires": {
         "whatwg-url": "^5.0.0"
       }
@@ -6130,6 +6235,7 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
       "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+      "dev": true,
       "requires": {
         "abbrev": "1"
       }
@@ -6138,6 +6244,7 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
       "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+      "dev": true,
       "requires": {
         "are-we-there-yet": "^2.0.0",
         "console-control-strings": "^1.1.0",
@@ -6148,12 +6255,14 @@
     "object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+      "dev": true
     },
     "once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
       "requires": {
         "wrappy": "1"
       }
@@ -6202,12 +6311,14 @@
     "parenthesis": {
       "version": "3.1.8",
       "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz",
-      "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw=="
+      "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==",
+      "dev": true
     },
     "path-browserify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
-      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+      "dev": true
     },
     "path-exists": {
       "version": "4.0.0",
@@ -6218,7 +6329,8 @@
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
     },
     "path-key": {
       "version": "3.1.1",
@@ -6482,6 +6594,7 @@
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
       "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+      "dev": true,
       "requires": {
         "inherits": "^2.0.3",
         "string_decoder": "^1.1.1",
@@ -6582,6 +6695,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
       "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
       "requires": {
         "glob": "^7.1.3"
       }
@@ -6607,6 +6721,7 @@
       "version": "7.5.4",
       "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
       "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dev": true,
       "requires": {
         "lru-cache": "^6.0.0"
       }
@@ -6614,7 +6729,8 @@
     "set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+      "dev": true
     },
     "shebang-command": {
       "version": "2.0.0",
@@ -6634,17 +6750,20 @@
     "signal-exit": {
       "version": "3.0.5",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz",
-      "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ=="
+      "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==",
+      "dev": true
     },
     "simple-concat": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
-      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
+      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+      "dev": true
     },
     "simple-get": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
       "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+      "dev": true,
       "requires": {
         "decompress-response": "^6.0.0",
         "once": "^1.3.1",
@@ -6655,6 +6774,7 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/skia-canvas/-/skia-canvas-1.0.2.tgz",
       "integrity": "sha512-9IjebOdBf6ir5Ktq0aM0wPshZ61oNX+T+2/ZBNJQZ63Z3SsSAd7i6YaYbqqtP6X8bK9mqB8QO1Y5DUZXNXuYHw==",
+      "dev": true,
       "requires": {
         "@mapbox/node-pre-gyp": "^1.0.9",
         "cargo-cp-artifact": "^0.1",
@@ -6668,6 +6788,7 @@
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
           "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+          "dev": true,
           "requires": {
             "balanced-match": "^1.0.0"
           }
@@ -6676,6 +6797,7 @@
           "version": "8.0.3",
           "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
           "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+          "dev": true,
           "requires": {
             "fs.realpath": "^1.0.0",
             "inflight": "^1.0.4",
@@ -6688,6 +6810,7 @@
           "version": "5.1.0",
           "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
           "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+          "dev": true,
           "requires": {
             "brace-expansion": "^2.0.1"
           }
@@ -6721,6 +6844,7 @@
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
       "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dev": true,
       "requires": {
         "safe-buffer": "~5.2.0"
       },
@@ -6728,7 +6852,8 @@
         "safe-buffer": {
           "version": "5.2.1",
           "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
         }
       }
     },
@@ -6736,6 +6861,7 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz",
       "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
+      "dev": true,
       "requires": {
         "parenthesis": "^3.1.5"
       }
@@ -6744,6 +6870,7 @@
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
       "requires": {
         "emoji-regex": "^8.0.0",
         "is-fullwidth-code-point": "^3.0.0",
@@ -6754,6 +6881,7 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
       "requires": {
         "ansi-regex": "^5.0.1"
       }
@@ -6826,6 +6954,7 @@
       "version": "6.2.1",
       "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
       "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+      "dev": true,
       "requires": {
         "chownr": "^2.0.0",
         "fs-minipass": "^2.0.0",
@@ -6838,7 +6967,8 @@
         "minipass": {
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
-          "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="
+          "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+          "dev": true
         }
       }
     },
@@ -6874,7 +7004,8 @@
     "tr46": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
+      "dev": true
     },
     "ts-map": {
       "version": "1.0.3",
@@ -6976,7 +7107,8 @@
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "dev": true
     },
     "v8-compile-cache": {
       "version": "2.3.0",
@@ -7079,12 +7211,14 @@
     "webidl-conversions": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
+      "dev": true
     },
     "whatwg-url": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
       "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+      "dev": true,
       "requires": {
         "tr46": "~0.0.3",
         "webidl-conversions": "^3.0.0"
@@ -7103,6 +7237,7 @@
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
       "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+      "dev": true,
       "requires": {
         "string-width": "^1.0.2 || 2 || 3 || 4"
       }
@@ -7157,7 +7292,8 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
     },
     "xmlcreate": {
       "version": "2.0.3",
@@ -7174,7 +7310,8 @@
     "yallist": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
     },
     "yargs": {
       "version": "16.2.0",
diff --git a/package.json b/package.json
index 09c2fdb..fba843d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "captcha-canvas",
-  "version": "3.2.3",
+  "version": "4.1.0",
   "description": "A captcha generator by using skia-canvas module.",
   "main": "./js-script/index.js",
   "files": [
@@ -18,10 +18,11 @@
   },
   "keywords": [
     "captcha",
+    "canvas",
+    "captcha-canvas",
     "skia-canvas",
     "image",
-    "generator",
-    "wick"
+    "generator"
   ],
   "author": "Shashank3736",
   "license": "Apache-2.0",
@@ -33,7 +34,7 @@
     "@types/node": "^14.11.8",
     "@types/skia-canvas": "^0.9.28",
     "better-docs": "^2.3.2",
-    "canvas": "^2.8.0",
+    "canvas": "^2.11.2",
     "eslint": "^7.27.0",
     "jsdoc": "^3.6.7",
     "typescript": "^4.0.2"
@@ -41,8 +42,5 @@
   "directories": {
     "doc": "docs",
     "example": "examples"
-  },
-  "dependencies": {
-    "skia-canvas": "^1.0.2"
   }
 }
diff --git a/ts-script/CaptchaGenerator.ts b/ts-script/CaptchaGenerator.ts
deleted file mode 100644
index 50afc04..0000000
--- a/ts-script/CaptchaGenerator.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import { Image, loadImage } from "skia-canvas";
-import { Captcha } from ".";
-import { defaultCaptchaOption, defaultDecoyOptions, defaultTraceOptions, SetCaptchaOption, SetDecoyOption, SetTraceOption } from "./constants";
-import { randomText } from "./util";
-/**
- * Captcha generator class.
- */
-export class CaptchaGenerator {
-    private height: number;
-    private width: number;
-    private captcha: SetCaptchaOption;
-    private trace: SetTraceOption;
-    private decoy: SetDecoyOption;
-    private background?: string | Buffer;
-    /**
-     * Initatiates the creation of captcha image generation.
-     * @example const captcha = new CaptchaGenerator({height: 200, width: 600});
-     * @param {object} [options] Options for constructor.
-     * @param {integer} [options.height=100] Height of captcha image.
-     * @param {integer} [options.width=300] Width of captcha image.
-     * @since 2.0.0
-     */
-    constructor(options = { height: 100, width: 300 }) {
-        this.height = options.height;
-        this.width = options.width;
-        this.captcha = defaultCaptchaOption;
-        this.trace = defaultTraceOptions;
-        this.decoy = defaultDecoyOptions;
-        this.captcha.text = randomText(this.captcha.characters || 6);
-    }
-    /**
-     * Get the text of captcha.
-     * @type {string}
-     * @since 2.0.3
-     */
-    get text(): string | undefined {
-        return this.captcha.text;
-    }
-    /**
-     * set dimension for your captcha image
-     * @param {integer} height Height of captcha image.
-     * @param {integer} width Width of captcha image.
-     * @example
-     * const { CaptchaGenerator } = require("captcha-canvas");
-     * const fs = require("fs")
-     * const captcha = new CaptchaGenerator();
-     * captcha.setDimension(200, 600);
-     * const buffer = await captcha.generate() //generate image
-     *
-     * fs.writeFileSync("image.png", buffer)
-     * @since 2.0.0
-     */
-    setDimension(height: number, width: number): CaptchaGenerator {
-        this.height = height;
-        this.width = width;
-        return this;
-    }
-    /**
-     * Set background for captcha image.
-     * @param {buffer} image Buffer/url/path of image.
-     * @example
-     * const { CaptchaGenerator } = require("captcha-canvas");
-     * const fs = require("fs")
-     * const captcha = new CaptchaGenerator();
-     * captcha.setBackground("./path/toFile");
-     * const buffer = await captcha.generate() //generate image
-     *
-     * fs.writeFileSync("image.png", buffer)
-     * @since 2.0.0
-     */
-    setBackground(image: string | Buffer) {
-        this.background = image;
-        return this;
-    }
-    /**
-     * Change captcha text options
-     * @param {SetCaptchaOptions} options Captcha appearance options.
-     * @example
-     * const { CaptchaGenerator } = require("captcha-canvas");
-     * const fs = require("fs")
-     * const captcha = new CaptchaGenerator();
-     * const options = {font: "Comic Sans", size: 60}
-     * captcha.setCaptcha(options)
-     * const buffer = await captcha.generate() //generate image
-     *
-     * fs.writeFileSync("image.png", buffer)
-     * @since 2.0.0
-     */
-    setCaptcha(option: SetCaptchaOption) {
-        this.captcha = { ...this.captcha, ...option };
-        if(option.text) this.captcha.characters = option.text.length;
-        if(!option.text && option.characters) this.captcha.text = randomText(option.characters);
-        return this;
-    }
-    /**
-     * Change trace creation options.
-     * @param {SetTraceOptions} options Trace Line appearance options.
-     * @example
-     * const { CaptchaGenerator } = require("captcha-canvas");
-     * const fs = require("fs")
-     * const captcha = new CaptchaGenerator();
-     * const options = {size: 5, color: "deeppink"}
-     * captcha.setTrace(options)
-     * const buffer = await captcha.generate() //generate image
-     *
-     * fs.writeFileSync("image.png", buffer)
-     * @since 2.0.0
-     */
-    setTrace(option: SetTraceOption) {
-        this.trace = { ...this.trace, ...option };
-        return this;
-    }
-    /**
-     * Change decoy options
-     * @param {SetDecoyOptions} options Decoy characters customisation options
-     * @since 2.0.0
-     */
-    setDecoy(option: SetDecoyOption) {
-        this.decoy = { ...this.decoy, ...option };
-        return this;
-    }
-    /**
-     * Method which returns image buffer
-     * @async
-     * @returns {Promise}
-     * @example
-     * const { CaptchaGenerator } = require("captcha-canvas");
-     * const fs = require("fs")
-     * const captcha = new CaptchaGenerator();
-     * const buffer = await captcha.generate() //generate image
-     *
-     * fs.writeFileSync("image.png", buffer)
-     * @since 2.0.0
-     */
-    async generate(): Promise {
-        const captchaCanvas = new Captcha(this.width, this.height);
-
-        if(this.background) captchaCanvas.drawImage(await loadImage(this.background));
-        if(this.decoy.opacity) captchaCanvas.addDecoy(this.decoy);
-        if(this.captcha.opacity) captchaCanvas.drawCaptcha(this.captcha);
-        if(this.trace.opacity) captchaCanvas.drawTrace(this.trace);
-        return captchaCanvas.png;
-    }
-    /**
-     * Non asynchronous method to generate captcha image.
-     * > Note: It do not use `setBackground` method value for background image. If you want to set background
-     * and also use generateSync method then use background option in genrateSync method.
-     * @param {object} [options] Options to add extra values
-     * @param {Image} [options.background] Add background image.
-     * @example
-     * const { CaptchaGenerator, resolveImage } = require("captcha-canvas");
-     * const fs = require("fs");
-     * const img = await resolveImage("./path/to/file");
-     *
-     * const captcha = new CaptchaGenerator()
-     * .generateSync({background: img});
-     *
-     * fs.writeFileSync("image.png", captcha);
-     * @since 2.2.0
-     */
-    generateSync(option: { background?: Image } = {}): Buffer {
-        const captchaCanvas = new Captcha(this.width, this.height, this.captcha.characters);
-        captchaCanvas.async = false;
-
-        if(option.background) captchaCanvas.drawImage(option.background);
-        if(this.decoy.opacity) captchaCanvas.addDecoy(this.decoy);
-        if(this.captcha.opacity) captchaCanvas.drawCaptcha(this.captcha);
-        if(this.trace.opacity) captchaCanvas.drawTrace(this.trace);
-
-        return captchaCanvas.png;
-    }
-}
\ No newline at end of file
diff --git a/ts-script/captcha.ts b/ts-script/captcha.ts
index ba5d97a..6e8c838 100644
--- a/ts-script/captcha.ts
+++ b/ts-script/captcha.ts
@@ -1,51 +1,64 @@
-import { Canvas, CanvasRenderingContext2D, Image } from "skia-canvas";
-import { defaultCaptchaOption, 
-    defaultDecoyOptions, 
-    defaultDimension, 
-    defaultTraceOptions, 
-    SetCaptchaOption, 
+/// 
+
+import { 
+    defaultCaptchaOption, 
+    defaultDecoyOptions,
+    defaultTraceOptions,
     SetDecoyOption, 
     SetTraceOption, 
-    DrawCaptchaOption } from "./constants";
+    DrawCaptchaOption 
+} from "./constants";
+
 import { getRandom, getRandomCoordinate, randomText } from "./util";
 
+/**
+ * Constructor options
+ * @typedef ConstructorOptions
+ * @property {number} [characters=6] Length of captcha text.
+ * @property {CanvasRenderingContext2D} ctx
+ */
+export type ConstructorOptions = { characters?: number, ctx: CanvasRenderingContext2D };
+
 /**
  * Captcha Generator
  */
 export class Captcha {
     protected _height: number;
     protected _width: number;
-    protected _captcha: SetCaptchaOption;
-    protected _trace: SetTraceOption;
-    protected _decoy: SetDecoyOption;
-    protected _canvas: Canvas;
+    protected _captcha: typeof defaultCaptchaOption;
+    protected _trace: typeof defaultTraceOptions;
+    protected _decoy: typeof defaultDecoyOptions;
     protected _ctx: CanvasRenderingContext2D;
     protected _coordinates: number[][];
     public async: boolean;
     /**
      * Start captcha image creation.
-     * @param {number} [width] Width of captcha image.
-     * @param {number} [height] Height of captcha image.
-     * @param {number} [characters] Size of captcha text.
+     * @param {ConstructorOptions} [option] Size of captcha text.
      * @constructor
      */
-    constructor(width: number = defaultDimension.width, height: number = defaultDimension.height, characters: number = defaultCaptchaOption.characters) {
-        this._height = height;
-        this._width = width;
+    constructor({ 
+        characters = defaultCaptchaOption.characters,
+        ctx,
+    }: ConstructorOptions) {
         this._captcha = defaultCaptchaOption;
         this._captcha.characters = characters;
         this._trace = defaultTraceOptions;
         this._decoy = defaultDecoyOptions;
-        const canvas = new Canvas(width, height);
-        const ctx = canvas.getContext('2d');
         ctx.lineJoin = 'miter';
 		ctx.textBaseline = 'middle';
         ctx.textAlign = 'center';
-        this._canvas = canvas;
+        this._width = ctx.canvas.width
+        this._height = ctx.canvas.height
         this._ctx = ctx;
         this.async = true;
         this._coordinates = [];
-        this._canvas.gpu = false;
+    }
+    /**
+     * Get canvas context.
+     * @returns {CanvasRenderingContext2D}
+     */
+    get context(): CanvasRenderingContext2D {
+        return this._ctx;
     }
     /**
      * Get Captcha text.
@@ -54,24 +67,12 @@ export class Captcha {
     get text(): string {
         return this._captcha.text || "";
     }
-    /**
-     * Get png image of captcha.
-     * @returns {Buffer | Promise} Get png image of captcha created.
-     */
-    get png(): Buffer | Promise {
-        if(this.async) {
-            return this._canvas.toBuffer('png');
-        }
-        else {
-            return this._canvas.toBufferSync('png');
-        }
-    }
     /**
      * Draw image on your captcha.
      * @param {Image} image Choose image you want to add.
      * @returns {Captcha}
      */
-    drawImage(image: Image): Captcha {
+    drawImage(image: CanvasImageSource): Captcha {
         this._ctx.drawImage(image, 0, 0, this._width, this._height);
         return this;
     }
diff --git a/ts-script/constants.ts b/ts-script/constants.ts
index 1e6def9..c2077c7 100644
--- a/ts-script/constants.ts
+++ b/ts-script/constants.ts
@@ -1,4 +1,4 @@
-import { Image } from "skia-canvas";
+/// 
 
 export interface SetDimensionOption {
     height: number;
@@ -46,7 +46,9 @@ export interface CreateCaptchaOptions {
     captcha?: SetCaptchaOption;
     trace?: SetTraceOption;
     decoy?: SetDecoyOption;
-    background?: Image;
+    background?: CanvasImageSource;
+    dimension?: SetDimensionOption;
+    ctx: CanvasRenderingContext2D;
 }
 
 /**
@@ -61,7 +63,7 @@ export interface CreateCaptchaOptions {
  * @property {number} [size=40] Size of captcha text.
  * @property {float} [opacity=1] Opcaity of captcha text.
  */
- export const defaultDrawCaptchaOption: DrawCaptchaOption = {
+ export const defaultDrawCaptchaOption = {
 	size: 40,
 	font: 'Sans',
 	skew: true,
@@ -69,6 +71,7 @@ export interface CreateCaptchaOptions {
 	rotate: 5,
 	color: '#32cf7e',
 	opacity: 0.8,
+    text: '',
 };
 /**
  * Captcha text options to customise text appearance and value.
@@ -83,15 +86,26 @@ export interface CreateCaptchaOptions {
  * @property {number} [size=40] Size of captcha text.
  * @property {float} [opacity=1] Opcaity of captcha text.
  */
-export const defaultCaptchaOption: SetCaptchaOption = {
+export const defaultCaptchaOption: {
+    characters: number,
+    size: number,
+    font: string,
+    skew: boolean,
+    colors: string[] | [],
+    rotate: number,
+    color: string,
+    opacity: number,
+    text: string
+} = {
     characters: 6,
 	size: 40,
 	font: 'Sans',
-	skew: true,
+	skew: false,
 	colors: [],
-	rotate: 5,
+	rotate: 0,
 	color: '#32cf7e',
 	opacity: 0.8,
+    text: '',
 };
 /**
  * @typedef SetTraceOptions
@@ -99,7 +113,7 @@ export const defaultCaptchaOption: SetCaptchaOption = {
  * @property {number} [size=3] Width of trace line.
  * @property {float} [opacity=1] Opacoty of trace line.
  */
-export const defaultTraceOptions: SetTraceOption = {
+export const defaultTraceOptions = {
 	size: 3,
 	color: '#32cf7e',
 	opacity: 1,
@@ -112,7 +126,7 @@ export const defaultTraceOptions: SetTraceOption = {
  * @property {float} [opacity=0.8] Opacity of decoy characters.
  * @property {number} [total] Total count of decoy characters.
  */
-export const defaultDecoyOptions: SetDecoyOption = {
+export const defaultDecoyOptions = {
 	color: '#646566',
 	font: 'Sans',
 	size: 20,
@@ -124,7 +138,7 @@ export const defaultDecoyOptions: SetDecoyOption = {
  * @property {integer} [height=100] Height of captcha image.
  * @property {integer} [width=300] Width of captcha image.
  */
-export const defaultDimension: SetDimensionOption = {
+export const defaultDimension = {
     height: 100,
     width: 300
 };
diff --git a/ts-script/extra.ts b/ts-script/extra.ts
index c2fe5e1..584a908 100644
--- a/ts-script/extra.ts
+++ b/ts-script/extra.ts
@@ -1,65 +1,82 @@
 import { Captcha } from ".";
-import { CreateCaptchaOptions } from "./constants";
+import { CreateCaptchaOptions, defaultCaptchaOption, defaultDecoyOptions, defaultTraceOptions, DrawCaptchaOption, SetDecoyOption, SetTraceOption } from "./constants";
+import { getRandom, getRandomCoordinate, randomText } from "./util";
 
-interface captchaValueSync {
-    image: Buffer,
-    text: string
-}
-
-interface captchaValue {
-    image: Promise,
-    text: string
+function drawDecoy(ctx: CanvasRenderingContext2D, decoyOption: SetDecoyOption = {}) {
+    const option = { ...defaultDecoyOptions, ...decoyOption };
+    if(!option.total) option.total = Math.floor(ctx.canvas.width * ctx.canvas.height/10000);
+    
+    const decoyText = randomText(option.total);
+    ctx.font = `${option.size}px ${option.font}`;
+    ctx.globalAlpha = option.opacity;
+    ctx.fillStyle = option.color;
+    for(const element of decoyText) {
+        ctx.fillText(element, getRandom(30, ctx.canvas.width - 30), getRandom(30, ctx.canvas.height - 30));
+    }
 }
 
-/**
- * Create custom captcha from scratch.
- * @async
- * @param {number} width Width of captcha image.
- * @param {number} height Height of captcha image.
- * @param {CreateCaptchaOptions} [option] Captcha text.
- * @returns 
- */
-export function createCaptcha(width: number, height: number, option: CreateCaptchaOptions = {}): captchaValue {
-    const captcha = new Captcha(width, height);
-    const decoyCount = Math.floor(width*height/2500);
+function drawTrace(ctx: CanvasRenderingContext2D, coordinates: number[][], traceOption: SetTraceOption = {}) {
+    const option = { ...defaultTraceOptions, ...traceOption };
 
-    if(!option.decoy) option.decoy = {};
-    if(!option.decoy.total) option.decoy.total = decoyCount;
+    ctx.strokeStyle = option.color;
+    ctx.globalAlpha = option.opacity;
 
-    captcha.addDecoy(option.decoy);
+    ctx.beginPath();
+    ctx.moveTo(coordinates[0][0], coordinates[0][1]);
+    ctx.lineWidth = option.size;
+    for(let i = 1; i < coordinates.length; i++) {
+        ctx.lineTo(coordinates[i][0], coordinates[i][1]);
+    }
+    ctx.stroke();
+}
 
+function drawCaptcha(ctx: CanvasRenderingContext2D, coordinates: number[][], captchaOption: DrawCaptchaOption = {}) {
+    const option = { ...defaultCaptchaOption, ...captchaOption };
+    if(captchaOption.text) option.text = captchaOption.text;
+    if(!option.text || option.text == "") option.text = randomText(option.characters);
+    if(option.text.length != option.characters) {
+        if(captchaOption.text) {
+            throw new Error("Size of text and no. of characters is not matching.");
+        }
+        else {
+            option.text = randomText(option.characters);
+        }
+    }
     
-    captcha.drawCaptcha(option.captcha);
+    ctx.font = `${option.size}px ${option.font}`;
+    ctx.globalAlpha = option.opacity;
+    ctx.fillStyle = option.color;
 
-    captcha.drawTrace(option.trace);
+    for(let n = 0; n < coordinates.length; n++) {
+        if (option.skew) {ctx.transform(1, Math.random(), getRandom(20) / 100, 1, 0, 0);}
+        if (option.rotate && option.rotate > 0) {ctx.rotate(getRandom(-option.rotate, option.rotate) * Math.PI / 180);}
+        if (option.colors && option.colors?.length >= 2) {ctx.fillStyle = option.colors[getRandom(option.colors.length - 1)];}
+        ctx.fillText(option.text[n], coordinates[n][0], coordinates[n][1]);
+    }
 
-    captcha.addDecoy({ opacity: 1 });
-
-    return { image: captcha.png, text: captcha.text };
+    return { text: option.text };
 }
 /**
- * Create captcha in sync mode.
- * @param {number} width captcha image width.
- * @param {number} height captcha image height.
+ * Create custom captcha from scratch.
  * @param {CreateCaptchaOptions} [option] Captcha text.
- * @returns
+ * @returns { text: string }
  */
-export function createCaptchaSync(width: number, height: number, option: CreateCaptchaOptions = {}): captchaValueSync {
-    const captcha = new Captcha(width, height);
+export function createCaptcha(option: CreateCaptchaOptions) {
+    const width = option.ctx.canvas.width;
+    const height = option.ctx.canvas.height;
     const decoyCount = Math.floor(width*height/2500);
-    captcha.async = false;
+    const coordinates = getRandomCoordinate(height, width, option.captcha?.characters || defaultCaptchaOption.characters);
 
     if(!option.decoy) option.decoy = {};
     if(!option.decoy.total) option.decoy.total = decoyCount;
 
-    captcha.addDecoy(option.decoy);
+    drawDecoy(option.ctx, option.decoy);
 
-    
-    captcha.drawCaptcha(option.captcha);
+    const { text } = drawCaptcha(option.ctx, coordinates, option.captcha);
 
-    captcha.drawTrace(option.trace);
+    drawTrace(option.ctx, coordinates, option.trace);
 
-    captcha.addDecoy({ opacity: 1 });
+    drawDecoy(option.ctx, { opacity: 1 });
 
-    return { image: captcha.png, text: captcha.text };
+    return { text };
 }
diff --git a/ts-script/index.ts b/ts-script/index.ts
index bd4bc42..45d7adf 100644
--- a/ts-script/index.ts
+++ b/ts-script/index.ts
@@ -1,6 +1,2 @@
-export { createCaptcha, createCaptchaSync } from "./extra";
-
-export { loadImage as resolveImage } from "skia-canvas";
-
+export { createCaptcha } from "./extra";
 export { Captcha } from "./captcha";
-export { CaptchaGenerator } from "./CaptchaGenerator";