Refactor public api

This commit is contained in:
Vincenzo Greco 2017-03-18 00:04:06 +01:00
parent 8fb9d0bb88
commit 0d6129737e
4 changed files with 114 additions and 397 deletions

View file

@ -11,7 +11,7 @@ var file = process.argv[3]
if (text && text.length) {
if (file && file.length) {
qr.save(file, text, function (err, data) {
qr.toFile(file, text, function (err, data) {
if (!err) {
process.stdout.write('saved qrcode to: ' + file + '\n')
} else {
@ -20,7 +20,7 @@ if (text && text.length) {
}
})
} else {
qr.drawText(text, function (error, text) {
qr.toString(text, { type: 'terminal' }, function (error, text) {
if (error) {
throw new Error(error)
}

View file

@ -9,232 +9,4 @@
*
*/
var QRCodeLib = require('./renderer/qrcode-draw')
var terminalRender = require('./renderer/terminal')
var svgRender = require('./renderer/svg')
var Canvas = require('canvas')
var fs = require('fs')
var QRCodeDraw = QRCodeLib.QRCodeDraw
// EXPORTS
//
// breaking change to 0.1 this used to be an instance. now it returns the constructor.
//
exports.QRCodeDraw = QRCodeDraw
//
// export error correct levels.
//
exports.errorCorrectLevels = QRCodeLib.QRErrorCorrectLevel
//
// export original canvas to be used with draw method, esp. Canvas.Image
//
exports.canvas = Canvas
/*
* provide an api to return the max characters allowed for given dimensions, and miniumum error correction level
* the qr code library will always use the maximum error correction level for the given numbar of chars constrained by size
*/
exports.getMaxChars = function (minErrorCorrectionLevel, width, moduleScale) {
// TODO THIS NEEDS TO WORK
console.log('this doesnt work yet. comming soon =)')
}
var parseOptions = function (options) {
var textKeys = {
'minimum': QRCodeLib.QRErrorCorrectLevel.L,
'medium': QRCodeLib.QRErrorCorrectLevel.M,
'high': QRCodeLib.QRErrorCorrectLevel.Q,
'max': QRCodeLib.QRErrorCorrectLevel.H
}
if (options.errorCorrectLevel) {
var ec = options.errorCorrectLevel
if (textKeys[ec]) {
options.errorCorrectionLevel = textKeys[ec]
}
}
return options
}
// returns Canvas Object with qr code drawn on it
/*
* String text, optional Object options, Function callback
*/
var draw = exports.draw = function (text, options, cb) {
var args = Array.prototype.slice.call(arguments)
cb = args.pop()
if (typeof cb !== 'function') {
throw new TypeError('last argument must be a function')
}
text = args.shift()
options = args.shift() || {}
options = parseOptions(options)
// NOTE the width and height are determined from within the qr code lib and are not configurable from the outside yet
var drawInstance = new QRCodeDraw()
drawInstance.draw(new Canvas(200, 200), text, options, function (error, canvas, qrWidth) {
cb(error, canvas, qrWidth)
})
}
// returns data uri for drawn qrcode png
exports.toDataURL = exports.toDataURI = function (text, options, cb) {
if (typeof options === 'function') {
cb = options
options = {}
}
draw(text, options, function (error, canvas) {
if (error) {
cb(error)
} else {
canvas.toDataURL(cb)
}
})
}
// synchronous PNGStream
exports.toPNGStream = function (text, WSpath, options, cb) {
if (typeof options === 'function') {
cb = options
options = {}
}
var out = fs.createWriteStream(WSpath)
draw(text, options, function (error, canvas) {
var stream
if (error) {
cb(error, '')
} else {
stream = canvas.createPNGStream()
}
stream.pipe(out)
stream.on('end', function () {
cb(error, '')
})
stream.pipe(out)
})
return out
}
// returns bytes written to file
exports.save = function (path, text, options, cb) {
if (typeof options === 'function') {
cb = options
options = {}
}
var fileExt = path.slice((path.lastIndexOf('.') - 1 >>> 0) + 2).toLowerCase()
if (fileExt === 'svg') {
saveSvg(path, text, options, cb)
} else {
savePng(path, text, options, cb)
}
}
function savePng (path, text, options, cb) {
draw(text, options, function (error, canvas) {
if (error) {
throw new Error(error)
}
var fd
var buf
var fdAndBuf = function () {
fs.write(fd, buf, 0, buf.length, 0, function (fsErr1, written) {
fs.close(fd, function (fsErr2) {
if (cb) cb(fsErr1 || fsErr2, written)
})
})
}
// run non dependent async calls at the same time ish
canvas.toBuffer(function (canvasErr, _buf) {
if (canvasErr) return cb(canvasErr)
buf = _buf
if (fd) fdAndBuf()
})
fs.open(path, 'w', function (fsErr, _fd) {
if (fsErr) return cb(fsErr)
fd = _fd
if (buf) fdAndBuf()
})
})
}
function saveSvg (path, text, options, cb) {
exports.drawSvg(text, options, function (error, code) {
if (!error) {
fs.writeFile(path, code, function (fsErr) {
return cb(fsErr, fsErr ? null : code)
})
}
})
}
//
// this returns an array of points that have either a 0 or 1 value representing 0 for light and 1 for dark
// these values include points in the white edge of the qrcode because that edge is actually part of the spec
//
exports.drawBitArray = function (text, options, cb) {
if (typeof options === 'function') {
cb = options
options = {}
}
options = parseOptions(options)
var drawInstance = new QRCodeDraw()
drawInstance.drawBitArray(text, options, function (error, bits, width) {
cb(error, bits, width)
})
}
//
// draw qr in your terminal!
//
exports.drawText = function (text, options, cb) {
if (typeof options === 'function') {
cb = options
options = {}
}
var drawInstance = new QRCodeDraw()
drawInstance.drawBitArray(text, options, function (error, bits, width) {
if (!error) {
var code = terminalRender.renderBits(bits, width)
cb(error, code)
} else {
cb(error, null)
}
})
}
exports.drawSvg = function (text, options, cb) {
if (typeof options === 'function') {
cb = options
options = {}
}
var drawInstance = new QRCodeDraw()
drawInstance.drawBitArray(text, options, function (error, bits, width) {
if (!error) {
var code = svgRender.renderBits(bits, width, options)
cb(error, code)
} else {
cb(error, null)
}
})
}
module.exports = require('./server')

View file

@ -1,166 +0,0 @@
/*
* copyright 2010-2012 Ryan Day
* http://github.com/soldair/node-qrcode
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* canvas example and fallback support example provided by Joshua Koo
* http://jabtunes.com/labs/qrcode.html
* "Instant QRCode Mashup by Joshua Koo!"
* as far as i can tell the page and the code on the page are public domain
*
* original table example and library provided by Kazuhiko Arase
* http://d-project.googlecode.com/svn/trunk/misc/qrcode/js/
*
*/
var QRCodeLib = require('../core/qrcode')
var ErrorCorrectionLevel = require('../core/error-correction-level')
exports.QRCodeDraw = QRCodeDraw
exports.QRErrorCorrectLevel = ErrorCorrectionLevel
exports.QRCode = QRCodeLib
function QRCodeDraw () {}
QRCodeDraw.prototype = {
scale: 4, // 4 px module size
defaultMargin: 20,
marginScaleFactor: 5,
color: {
dark: 'black',
light: 'white'
},
QRErrorCorrectLevel: ErrorCorrectionLevel,
draw: function (canvas, text, options, cb) {
var error
var args = Array.prototype.slice.call(arguments)
cb = args.pop()
canvas = args.shift()
text = args.shift()
options = args.shift() || {}
if (typeof cb !== 'function') {
// enforce callback api just in case the processing can be made async in the future
// or support proc open to libqrencode
throw new Error('callback required')
}
if (typeof options !== 'object') {
options.errorCorrectionLevel = options
}
this.scale = options.scale || this.scale
this.margin = typeof (options.margin) === 'undefined' ? this.defaultMargin : options.margin
// create qrcode!
try {
var qr = new QRCodeLib(text, options)
var scale = this.scale || 4
var ctx = canvas.getContext('2d')
var width = 0
var margin = this.marginWidth()
var currenty = margin
width = this.dataWidth(qr) + margin * 2
this.resetCanvas(canvas, ctx, width)
for (var r = 0, rl = qr.getModuleCount(); r < rl; r++) {
var currentx = margin
for (var c = 0, cl = qr.getModuleCount(); c < cl; c++) {
if (qr.isDark(r, c)) {
ctx.fillStyle = this.color.dark
ctx.fillRect(currentx, currenty, scale, scale)
} else if (this.color.light) {
// if falsy configured color
ctx.fillStyle = this.color.light
ctx.fillRect(currentx, currenty, scale, scale)
}
currentx += scale
}
currenty += scale
}
} catch (e) {
error = e
}
cb(error, canvas, width)
},
drawBitArray: function (/* text, errorCorrectLevel,options,cb */) {
var args = Array.prototype.slice.call(arguments)
var cb = args.pop()
var text = args.shift()
var options = args.shift() || {}
var error
// argument processing
if (typeof cb !== 'function') {
// enforce callback api just in case the processing can be made async in the future
// or support proc open to libqrencode
throw new Error('callback required as last argument')
}
// create qrcode!
try {
var qr = new QRCodeLib(text, options)
var width = 0
var bits
var bitc = 0
width = this.dataWidth(qr, 1)
bits = new Array(width * width)
for (var r = 0, rl = qr.getModuleCount(); r < rl; r++) {
for (var c = 0, cl = qr.getModuleCount(); c < cl; c++) {
if (qr.isDark(r, c)) {
bits[bitc] = 1
} else {
bits[bitc] = 0
}
bitc++
}
}
} catch (e) {
error = e
console.log(e.stack)
}
cb(error, bits, width)
},
marginWidth: function () {
var margin = this.margin
this.scale = this.scale || 4
// elegant white space next to code is required by spec
if ((this.scale * this.marginScaleFactor > margin) && margin > 0) {
margin = this.scale * this.marginScaleFactor
}
return margin
},
dataWidth: function (qr, scale) {
return qr.getModuleCount() * (scale || this.scale || 4)
},
resetCanvas: function (canvas, ctx, width) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
if (!canvas.style) canvas.style = {}
canvas.style.height = canvas.height = width// square!
canvas.style.width = canvas.width = width
if (this.color.light) {
ctx.fillStyle = this.color.light
ctx.fillRect(0, 0, canvas.width, canvas.height)
} else {
// support transparent backgrounds?
// not exactly to spec but i really would like someone to be able to add a background with heavily reduced luminosity for simple branding
// i could just ditch this because you could also just set #******00 as the color =P
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
}
}

111
lib/server.js Normal file
View file

@ -0,0 +1,111 @@
var QRCode = require('./core/qrcode')
var PngRenderer = require('./renderer/png')
var Utf8Renderer = require('./renderer/utf8')
var TerminalRenderer = require('./renderer/terminal')
var SvgRenderer = require('./renderer/svg')
function checkParams (text, opts, cb) {
if (typeof text === 'undefined') {
throw new Error('String required as first argument')
}
if (typeof cb === 'undefined') {
cb = opts
opts = {}
}
if (typeof cb !== 'function') {
throw new Error('Callback required as last argument')
}
return {
opts: opts,
cb: cb
}
}
function getTypeFromFilename (path) {
return path.slice((path.lastIndexOf('.') - 1 >>> 0) + 2).toLowerCase()
}
function getRendererFromType (type) {
switch (type) {
case 'svg':
return SvgRenderer
case 'txt':
case 'utf8':
return Utf8Renderer
case 'png':
case 'image/png':
default:
return PngRenderer
}
}
function getStringRendererFromType (type) {
switch (type) {
case 'svg':
return SvgRenderer
case 'terminal':
return TerminalRenderer
case 'utf8':
default:
return Utf8Renderer
}
}
function render (renderFunc, text, params) {
try {
var data = QRCode.create(text, params.opts)
return params.cb(null, renderFunc(data, params.opts))
} catch (e) {
return params.cb(e)
}
}
exports.create = QRCode.create
exports.toCanvas = require('./browser').toCanvas
exports.toString = function toString (text, opts, cb) {
var params = checkParams(text, opts, cb)
var renderer = getStringRendererFromType(params.opts.type)
return render(renderer.render, text, params)
}
exports.toDataURL = function toDataURL (text, opts, cb) {
var params = checkParams(text, opts, cb)
var renderer = getRendererFromType(params.opts.type)
return render(renderer.renderToDataURL, text, params)
}
exports.toFile = function toFile (path, text, opts, cb) {
if (arguments.length < 3) {
throw new Error('Too few arguments provided')
}
var params = checkParams(text, opts, cb)
var type = params.opts.type || getTypeFromFilename(path)
var renderer = getRendererFromType(type)
var renderToFile = renderer.renderToFile.bind(null, path)
return render(renderToFile, text, params)
}
exports.toFileStream = function toFileStream (path, text, opts, cb) {
if (arguments.length < 3) {
throw new Error('Too few arguments provided')
}
var params = checkParams(text, opts, cb)
var type = params.opts.type || getTypeFromFilename(path)
var renderer = getRendererFromType(type)
var renderToFileStream = renderer.renderToFileStream.bind(null, path)
return render(renderToFileStream, text, params)
}