diff --git a/README.md b/README.md
index 8d77f87e..510e6b9f 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,7 @@ Renderer options:
-q, --qzone Quiet zone size [number]
-l, --lightcolor Light RGBA hex color
-d, --darkcolor Dark RGBA hex color
+ --small Output smaller QR code to terminal [boolean]
Options:
-o, --output Output file
@@ -725,6 +726,12 @@ See [Options](#options).
Scale factor. A value of `1` means 1px per modules (black dots).
+##### `small`
+ Type: `Boolean`
+ Default: `false`
+
+ Relevant only for terminal renderer. Outputs smaller QR code.
+
##### `width`
Type: `Number`
diff --git a/bin/qrcode b/bin/qrcode
index 7f1bca11..dd990b63 100755
--- a/bin/qrcode
+++ b/bin/qrcode
@@ -30,6 +30,8 @@ function parseOptions (args) {
version: args.qversion,
errorCorrectionLevel: args.error,
type: args.type,
+ small: !!args.small,
+ inverse: !!args.inverse,
maskPattern: args.mask,
margin: args.qzone,
width: args.width,
@@ -82,6 +84,12 @@ var argv = yargs
implies: 'output',
group: 'Renderer options:'
})
+ .option('i', {
+ alias: 'inverse',
+ type: 'boolean',
+ description: 'Invert colors',
+ group: 'Renderer options:'
+ })
.option('w', {
alias: 'width',
description: 'Image width (px)',
@@ -112,6 +120,12 @@ var argv = yargs
description: 'Dark RGBA hex color',
group: 'Renderer options:'
})
+ .option('small', {
+ type: 'boolean',
+ description: 'Output smaller QR code to terminal',
+ conflicts: 'type',
+ group: 'Renderer options:'
+ })
.option('o', {
alias: 'output',
description: 'Output file'
diff --git a/lib/renderer/terminal.js b/lib/renderer/terminal.js
index f15610b5..36ddfde7 100644
--- a/lib/renderer/terminal.js
+++ b/lib/renderer/terminal.js
@@ -1,49 +1,9 @@
-// let Utils = require('./utils')
+const big = require('./terminal/terminal')
+const small = require('./terminal/terminal-small')
exports.render = function (qrData, options, cb) {
- const size = qrData.modules.size
- const data = qrData.modules.data
-
- // let opts = Utils.getOptions(options)
-
- // use same scheme as https://github.com/gtanner/qrcode-terminal because it actually works! =)
- const black = '\x1b[40m \x1b[0m'
- const white = '\x1b[47m \x1b[0m'
-
- let output = ''
- const hMargin = Array(size + 3).join(white)
- const vMargin = Array(2).join(white)
-
- output += hMargin + '\n'
- for (let i = 0; i < size; ++i) {
- output += white
- for (let j = 0; j < size; j++) {
- // let topModule = data[i * size + j]
- // let bottomModule = data[(i + 1) * size + j]
-
- output += data[i * size + j] ? black : white// getBlockChar(topModule, bottomModule)
- }
- // output += white+'\n'
- output += vMargin + '\n'
- }
-
- output += hMargin + '\n'
-
- if (typeof cb === 'function') {
- cb(null, output)
- }
-
- return output
-}
-/*
-exports.renderToFile = function renderToFile (path, qrData, options, cb) {
- if (typeof cb === 'undefined') {
- cb = options
- options = undefined
+ if (options && options.small) {
+ return small.render(qrData, options, cb)
}
-
- let fs = require('fs')
- let utf8 = exports.render(qrData, options)
- fs.writeFile(path, utf8, cb)
+ return big.render(qrData, options, cb)
}
-*/
diff --git a/lib/renderer/terminal/terminal-small.js b/lib/renderer/terminal/terminal-small.js
new file mode 100644
index 00000000..9810d4e1
--- /dev/null
+++ b/lib/renderer/terminal/terminal-small.js
@@ -0,0 +1,85 @@
+const backgroundWhite = '\x1b[47m'
+const backgroundBlack = '\x1b[40m'
+const foregroundWhite = '\x1b[37m'
+const foregroundBlack = '\x1b[30m'
+const reset = '\x1b[0m'
+const lineSetupNormal = backgroundWhite + foregroundBlack // setup colors
+const lineSetupInverse = backgroundBlack + foregroundWhite // setup colors
+
+const createPalette = function (lineSetup, foregroundWhite, foregroundBlack) {
+ return {
+ // 1 ... white, 2 ... black, 0 ... transparent (default)
+
+ '00': reset + ' ' + lineSetup,
+ '01': reset + foregroundWhite + '▄' + lineSetup,
+ '02': reset + foregroundBlack + '▄' + lineSetup,
+ 10: reset + foregroundWhite + '▀' + lineSetup,
+ 11: ' ',
+ 12: '▄',
+ 20: reset + foregroundBlack + '▀' + lineSetup,
+ 21: '▀',
+ 22: '█'
+ }
+}
+
+/**
+ * Returns code for QR pixel
+ * @param {boolean[][]} modules
+ * @param {number} size
+ * @param {number} x
+ * @param {number} y
+ * @return {'0' | '1' | '2'}
+ */
+const mkCodePixel = function (modules, size, x, y) {
+ const sizePlus = size + 1
+ if ((x >= sizePlus) || (y >= sizePlus) || (y < -1) || (x < -1)) return '0'
+ if ((x >= size) || (y >= size) || (y < 0) || (x < 0)) return '1'
+ const idx = (y * size) + x
+ return modules[idx] ? '2' : '1'
+}
+
+/**
+ * Returns code for four QR pixels. Suitable as key in palette.
+ * @param {boolean[][]} modules
+ * @param {number} size
+ * @param {number} x
+ * @param {number} y
+ * @return {keyof palette}
+ */
+const mkCode = function (modules, size, x, y) {
+ return (
+ mkCodePixel(modules, size, x, y) +
+ mkCodePixel(modules, size, x, y + 1)
+ )
+}
+
+exports.render = function (qrData, options, cb) {
+ const size = qrData.modules.size
+ const data = qrData.modules.data
+
+ const inverse = !!(options && options.inverse)
+ const lineSetup = options && options.inverse ? lineSetupInverse : lineSetupNormal
+ const white = inverse ? foregroundBlack : foregroundWhite
+ const black = inverse ? foregroundWhite : foregroundBlack
+
+ const palette = createPalette(lineSetup, white, black)
+ const newLine = reset + '\n' + lineSetup
+
+ let output = lineSetup // setup colors
+
+ for (let y = -1; y < size + 1; y += 2) {
+ for (let x = -1; x < size; x++) {
+ output += palette[mkCode(data, size, x, y)]
+ }
+
+ output += palette[mkCode(data, size, size, y)] + newLine
+ }
+
+ output += reset
+
+ if (typeof cb === 'function') {
+ cb(null, output)
+ }
+
+ return output
+}
diff --git a/lib/renderer/terminal/terminal.js b/lib/renderer/terminal/terminal.js
new file mode 100644
index 00000000..f15610b5
--- /dev/null
+++ b/lib/renderer/terminal/terminal.js
@@ -0,0 +1,49 @@
+// let Utils = require('./utils')
+
+exports.render = function (qrData, options, cb) {
+ const size = qrData.modules.size
+ const data = qrData.modules.data
+
+ // let opts = Utils.getOptions(options)
+
+ // use same scheme as https://github.com/gtanner/qrcode-terminal because it actually works! =)
+ const black = '\x1b[40m \x1b[0m'
+ const white = '\x1b[47m \x1b[0m'
+
+ let output = ''
+ const hMargin = Array(size + 3).join(white)
+ const vMargin = Array(2).join(white)
+
+ output += hMargin + '\n'
+ for (let i = 0; i < size; ++i) {
+ output += white
+ for (let j = 0; j < size; j++) {
+ // let topModule = data[i * size + j]
+ // let bottomModule = data[(i + 1) * size + j]
+
+ output += data[i * size + j] ? black : white// getBlockChar(topModule, bottomModule)
+ }
+ // output += white+'\n'
+ output += vMargin + '\n'
+ }
+
+ output += hMargin + '\n'
+
+ if (typeof cb === 'function') {
+ cb(null, output)
+ }
+
+ return output
+}
+/*
+exports.renderToFile = function renderToFile (path, qrData, options, cb) {
+ if (typeof cb === 'undefined') {
+ cb = options
+ options = undefined
+ }
+
+ let fs = require('fs')
+ let utf8 = exports.render(qrData, options)
+ fs.writeFile(path, utf8, cb)
+}
+*/
diff --git a/test/unit/renderer/terminal.test.js b/test/unit/renderer/terminal.test.js
index b0ada4d0..a1a23caa 100644
--- a/test/unit/renderer/terminal.test.js
+++ b/test/unit/renderer/terminal.test.js
@@ -9,7 +9,7 @@ test('TerminalRenderer interface', function (t) {
t.end()
})
-test('TerminalRenderer render', function (t) {
+test('TerminalRenderer render big', function (t) {
const sampleQrData = QRCode.create('sample text', { version: 2 })
let str
@@ -35,3 +35,45 @@ test('TerminalRenderer render', function (t) {
t.end()
})
+
+test('TerminalRenderer render small', function (t) {
+ const sampleQrData = QRCode.create('sample text', { version: 2 })
+ let str
+ let calledCallback = false
+ const callback = function () { calledCallback = true }
+
+ t.notThrow(function () { str = TerminalRenderer.render(sampleQrData) },
+ 'Should not throw with only qrData param')
+
+ t.notThrow(function () {
+ str = TerminalRenderer.render(sampleQrData, {
+ margin: 10,
+ scale: 1,
+ small: true
+ })
+ }, 'Should not throw with options param and without callback')
+
+ t.notThrow(function () {
+ str = TerminalRenderer.render(sampleQrData, {
+ margin: 10,
+ scale: 1,
+ small: true
+ },
+ callback)
+ }, 'Should not throw with options param and callback')
+
+ t.type(str, 'string',
+ 'Should return a string')
+
+ t.equal(calledCallback, true, 'string',
+ 'Should call a callback')
+
+ t.notThrow(function () {
+ str = TerminalRenderer.render(sampleQrData, { small: true, inverse: true })
+ }, 'Should not throw with inverse options')
+
+ t.type(str, 'string',
+ 'Should return a string if inverse option is set')
+
+ t.end()
+})