feat: update to 'micro' (quad-block) QR terminal output
authorAJ ONeal <coolaj86@gmail.com>
Sat, 25 Jun 2022 06:27:10 +0000 (00:27 -0600)
committerAJ ONeal <coolaj86@gmail.com>
Sat, 25 Jun 2022 06:55:07 +0000 (00:55 -0600)
bin/crowdnode.js
lib/qr.js

index 8b8ce396ff0be8d4a1ec0bc843052c1bf42e75e4..d8697d95594b95dfdae417e12cba0b8f909953fa 100755 (executable)
@@ -337,7 +337,7 @@ function showQr(addr, duffs = 0) {
     dashUri += `?amount=${dashAmount}`;
   }
 
-  let dashQr = Qr.ascii(dashUri, { indent: 4 });
+  let dashQr = Qr.ascii(dashUri, { indent: 4, size: "micro" });
   let addrPad = Math.ceil((qrWidth - dashUri.length) / 2);
 
   console.info(dashQr);
index 059ba550588bdfafe5adfd07690f387fcc91e7d8..632218b6e87ab9969684c9c7b75ef4f367977876 100644 (file)
--- a/lib/qr.js
+++ b/lib/qr.js
@@ -6,6 +6,22 @@ let Fs = require("fs").promises;
 
 let QrCode = require("qrcode-svg");
 
+/**
+ * @typedef QrOpts
+ * @property {String} [background]
+ * @property {String} [color]
+ * @property {String} [ecl]
+ * @property {Number} [height]
+ * @property {Number} [indent]
+ * @property {Number} [padding]
+ * @property {"mini" | "micro"} [size]
+ * @property {Number} [width]
+ */
+
+/**
+ * @param {String} data
+ * @param {QrOpts} opts
+ */
 Qr._create = function (data, opts) {
   return new QrCode({
     content: data,
@@ -18,12 +34,101 @@ Qr._create = function (data, opts) {
   });
 };
 
+/**
+ * @type {Object.<String, String>}
+ */
+let microMap = {
+  // top-left, top-right, bottom-left, bottom-right
+  0b0000: " ",
+  //
+  0b0001: "▗",
+  //
+  0b0010: "▖",
+  //
+  0b0011: "▄",
+  //
+  0b0100: "▝",
+  //
+  0b0101: "▐",
+  //
+  0b0110: "▞",
+  //
+  0b0111: "▟",
+  //
+  0b1000: "▘",
+  //
+  0b1001: "▚",
+  //
+  0b1010: "▌",
+  //
+  0b1011: "▙",
+  //
+  0b1100: "▀",
+  //
+  0b1101: "▜",
+  //
+  0b1110: "▛",
+  //
+  0b1111: "█",
+};
+
+/**
+ * @param {String} data
+ * @param {QrOpts} opts
+ */
+Qr.microAscii = function (data, opts) {
+  let qrcode = Qr._create(data, opts);
+  let indent = opts?.indent ?? 4;
+  let modules = qrcode.qrcode.modules;
+
+  let ascii = ``.padStart(indent, " ");
+  let length = modules.length;
+  for (let y = 0; y < length; y += 2) {
+    for (let x = 0; x < length; x += 2) {
+      let count = 0;
+      // qr codes can be odd numbers
+      if (x >= length) {
+        ascii += microMap[count];
+        continue;
+      }
+      if (modules[x][y]) {
+        count += 8;
+      }
+      if (modules[x][y + 1]) {
+        count += 2;
+      }
+
+      if (x + 1 >= length) {
+        ascii += microMap[count];
+        continue;
+      }
+      if (modules[x + 1][y]) {
+        count += 4;
+      }
+      if (modules[x + 1][y + 1]) {
+        count += 1;
+      }
+      ascii += microMap[count];
+    }
+    ascii += `\n`.padEnd(indent, " ");
+  }
+  return " ".padEnd(indent - 1, " ") + ascii.trim();
+};
+
+/**
+ * @param {String} data
+ * @param {QrOpts} opts
+ */
 Qr.ascii = function (data, opts) {
+  if ("micro" === opts.size) {
+    return Qr.microAscii(data, opts);
+  }
+
   let qrcode = Qr._create(data, opts);
   let indent = opts?.indent ?? 4;
-  var modules = qrcode.qrcode.modules;
+  let modules = qrcode.qrcode.modules;
 
-  let ascii = ``.padStart(indent - 1, ' ');
+  let ascii = ``.padStart(indent - 1, " ");
   let length = modules.length;
   for (let y = 0; y < length; y += 1) {
     for (let x = 0; x < length; x += 1) {
@@ -33,16 +138,25 @@ Qr.ascii = function (data, opts) {
       }
       ascii += block;
     }
-    ascii += `\n`.padEnd(indent, ' ');
+    ascii += `\n`.padEnd(indent, " ");
   }
   return ascii;
 };
 
+/**
+ * @param {String} data
+ * @param {QrOpts} opts
+ */
 Qr.svg = function (data, opts) {
   let qrcode = Qr._create(data, opts);
   return qrcode.svg();
 };
 
+/**
+ * @param {String} filepath
+ * @param {String} data
+ * @param {QrOpts} opts
+ */
 Qr.save = async function (filepath, data, opts) {
   let qrcode = Qr.svg(data, opts);
   await Fs.writeFile(filepath, qrcode, "utf8");