Open Azunyan- opened 3 years ago
There is a way to do this, which I've been thinking about implementing. You could try to first encode the url the user gives and then fill the rest up with random data, but set the data size to just the length of the URL. There are three main challenges with it: First of all, what do we do if the users URL is long? It can take up too much of the QR code then... Secondly, the library that is used now, doesn't allow this directly. Thirdly, will all QR code scanners handle this correctly? To figure this out, it'll need to be implemented and tested with lots of scanners.
However, at the moment, I don't have time to do this. But if you, or anyone else, do have time to do that, feel free to create a PR and I'm happy to answer any questions :)
Another way could be to append the character #
or ?
at the end of the URL and put all the drawing data after that
For example, these URL still works correctly: https://example.com#94N+2DEZQNRDU1ZC94RD/MRD... https://example.com?USERSDRAWINGDATA94N+2DEZQNRDU1ZC94RD......... https://bit.ly/3wh8HKG?USERS+RANDOM+DRAWING+DATA+HERE
The #
and ?
characters are not in the QR code alphanumeric set, but, according to thonky.com / wikipedia
It is possible to use multiple modes in a single QR code by including the mode indicator before each section of bytes that uses that mode
After every indicator that selects an encoding mode is a length field that tells how many characters are encoded in that mode.
So the first part of the URL could use byte
mode, and then the drawing data can use alphanumeric
.
There are other libraries that support encoding multiple modes in a single QR code.
But it's still a problem that the user's URL needs to be very short... at least this would let the user pick their favorite URL shortener.
Here is some progress on using custom URLs.
It is a very dumb genetic algorithm that starts with any QR code, and then mutates closer towards a target image.
A novel contribution here is that the target can be grayscale. The generated QR code will dither, making tradeoffs to get close to the the requested color.
This allows to paint on the part that my-qr.art lets you paint on. And it also lets you paint on the error codes. The whole area is your canvas.
// William Entriken (c) 2021
// qr-spray-and-pray.mqs // Node 17+ // npm -i pngjs qrcode
//
// Create target.png of what you want QR to look like
// Tune parameters
// Output is a QR closer and closer to what you want
//
import QRCode from "qrcode";
import * as fs from "fs";
import{ PNG } from "pngjs";
// Load target /////////////////////////////////////////////////////////////////
const targetData = fs.readFileSync("target.png");
const target = PNG.sync.read(targetData);
var targetGray255 = [];
for (var index=0; index<target.width*target.height; index+=4) {
targetGray255.push(target.data[index]); // Only keep first channel of four
}
// Create //////////////////////////////////////////////////////////////////////
const options = {
errorCorrectionLevel: "L",
version: 6,
maskPattern: 1,
margin: 0,
scale: 1
}
const prefix = "https://phor.net/#";
function loss(suffix) {
const qrcode = QRCode.create(prefix + suffix, options);
var loss = 0;
for (var index=0; index<targetGray255.length; index++) {
const moduleBit = qrcode.modules.data[index] == 1; // 1 = DARK
const target255 = targetGray255[index]; // 255 = WHITE
loss += Math.floor(moduleBit
? Math.pow(target255, 1.5)
: Math.pow(255 - target255, 1.5));
}
return loss;
}
function randomSpliceDigits(string, spliceSizes) {
spliceSizes.forEach(numberOfDigits => {
const valueToInsert = Math.floor(Math.random() * Math.pow(10, numberOfDigits));
const stringToInsert = String(valueToInsert).padStart(numberOfDigits, "0");
const locationToInsert = Math.floor(Math.random() * (string.length - numberOfDigits));
string = string.substr(0, locationToInsert) + stringToInsert + string.substr(locationToInsert + numberOfDigits);
});
return string;
}
// to start, manually set suffix to be as large as possible for version/errorcorrection by trial/getting errors
var bestSuffix = "37348811905212874935831596698511608404485249315311992159406439210629002607128961487044174248108263018999806889709719495546738427914989016968717957729315662204376571097387723144555160821753721774774216159174612135825629709926432407621787044780906828380642588961079399703815370";
var bestLoss = loss(bestSuffix);
const splicePatterns = [
[1],
[2],
[3],
[1,2,3],
[3,3],
[3,3,3],
[1,1,1,1,1,1],
[2,2,2],
];
while (true) {
const candidateSuffix = randomSpliceDigits(bestSuffix, splicePatterns[Math.floor(Math.random()*splicePatterns.length)]);
if (candidateSuffix === bestSuffix) continue;
const candidateLoss = loss(candidateSuffix);
console.log(candidateLoss, candidateSuffix);
if (candidateLoss <= bestLoss) {
bestSuffix = candidateSuffix;
bestLoss = candidateLoss;
await QRCode.toFile("best.png", prefix + candidateSuffix, options);
console.log("❤️");
}
}
If it's not clear, I have no idea what I'm doing. I'll appreciate any feedback that moves this in the right direction.
Here is one more less ridiculous approach you can use with my-qr.art:
#
as a prefix to that URL (npm install -g qrcode
and qrcode -m 1 -e l
)#
to your prefix and delete one character from the front of that URL (not your prefix) (this shifts everything over one bit)Done.
Result:
That URL is direct to my website, designed with my-qr.art, and works on iPhone.
my-qr.art is very impressive and able to make some awesome looking QR codes. Unfortunately, I can't use them in projects because the domain name is unfamiliar and doesn't add trust. I believe people will be less likely to click links to a unrelated qr code website. The link-redirection is a deal breaker.
I would like to be able to generate a qr code that links directly to the url I want without having it redirect via your site.