w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 661 forks source link

[css-images][css-values][css-gcpm] qrcode() for generating QR code images from URLs #6544

Open hober opened 3 years ago

hober commented 3 years ago

Generating QR code images from URLs is more and more common these days. It'd be nice if you could generate QR code images from CSS.

For example, you could generate QR codes for links in a print stylesheet:

@media print {
    ul.citations > li > a::after {
        background-image: qrcode(attr(href));
        ...
    }
}

Or you could generate a QR code for the document itself in a print stylesheet, so someone could find the online version of an article in print (see also #6546):

@media print {
    body {
        background-image: qrcode(url(...));
    }
}

Thought of this while reading a twitter thread between @jyasskin @dauwhe @tabatkins: https://twitter.com/jyasskin/status/1430586358658043909

BigBlueHat commented 3 years ago

Would a pseudo protocol have wider application--and still be viable in CSS?

Maybe...

body { background-image: "qrcode:https://w3.org/" }
# though the actual text may have to be URI encoded...
body { background-image: "qrcode:https%3A%2F%2Fw3.org%2F" }
<img src="qrcode:https://w3.org/" />
<!-- or -->
<img src="qrcode:https%3A%2F%2Fw3.org%2F" />
tabatkins commented 3 years ago

If we went that way, we already have data: urls for that purpose; you'd just need to mint a qr code MIME type, where the resource data is intended to be interpreted as a qr code. Like data:image/qrcode,https://example.com

tabatkins commented 3 years ago

However, that doesn't remove the use-case for a dedicated function that just takes a string, since that allows one to send the data directly, as @hober demonstrated, and without having to worry about URL encoding.

So a MIME type can be pursued in tandem, and then perhaps this feature rebased over that later, without stopping us from defining qrcode() in CSS.

BigBlueHat commented 3 years ago

If we went that way, we already have data: urls for that purpose; you'd just need to mint a qr code MIME type, where the resource data is intended to be interpreted as a qr code. Like data:image/qrcode,https://example.com

Not sure a media type is necessary (or wise) in this case. Media types shouldn't generally describe what's inside the package--just the packaging. 😃 And qrcode's themselves are "shipped" in all kinds of image/* packages currently (and it should stay that way).

The point of this proposal (as I understand it) is to have the browser turn strings into QR codes with minimal effort. Using the data: URL wouldn't provide for that; just an alternate means of shipping an existing image, and extending it to be more than a means of conveyance would be concerning.

tabatkins commented 3 years ago

I think you misunderstood - that url wasn't a compressed example omitting a ton of binary data, it was literally what you would write. image/qrcode would be an image format, same as, say, SVG; like SVG, it would be a declarative image format, containing drawing instructions (namely, "draw the smallest QR code that embeds the following data") rather than pixel data.

But like I said, that's a separate discussion that doesn't have to impinge on any efforts to add qrcode() to CSS.

BigBlueHat commented 3 years ago

@tabatkins great explanation. 😃 I do think that browser support for either URL approach we've each proposed would obviate the need for qrcode() in CSS.

It'd be nice to have this feature regardless.

tabatkins commented 3 years ago

Like I said - it wouldn't obviate the need. @hober's example from the first post showed off something that absolutely could not be done if this was just a URL - you can't (currently...) construct a url() from multiple strings, and you definitely can't url-encode a string in CSS.


@hober do you know of a reference for the definition of qr codes? I'm wondering if we need more (optional) specifiers, like size; I know you can output the same data in multiple sizes of qr code, and I imagine a consistent size would be desirable in practice. (You can of course change the size of the generated image, but I mean the rows/cols of the grid itself.)

jyasskin commented 3 years ago

https://wicg.github.io/shape-detection-api/#dom-barcodeformat-qr_code mentions ISO 18004. @yellowdoge might know more details.

hober commented 3 years ago

ISO 18004 isn't free to access in full, grumble, but it does look like someone put the PDF on GitHub.

Malvoz commented 3 years ago

I think it's important that QR codes are exposed to accessibility APIs differently from (background-)images such that non-visual users can know there's something to scan (without relying on every author to provide proper alternative text). See the related imagecode element (QR/barcodes) proposal: https://github.com/whatwg/html/issues/5801.

faceless2 commented 3 years ago

We've implemented ISO18004 (along with a dozen other symbologies). The main controllable parameter for QR-code are:

So yes, you'd need to allow for parameters other than the actual content of the barcode. The idea of using <img> is problematic for the reasons tab said (crazy long URLs). We use object and/or embed:

<object type="bfo/barcode">
 <param name="value" value="whatever">
 <param name="ecc" value="high"> 
</object>

But until now we hadn't thought of doing this with CSS.

I agree with @Malvoz that this needs to be useful for accessibility, but if you made QR as a general image type, like linear-gradient, then they wouldn't be limited to backgrounds; you could do something like

div.qrcode {
    content: qrcode(contents);
}
<div class="qr">content here</div>

which would let accessibility APIs do the right thing without extra effort on the part of the author (note that content is allowed on non-pseudo elements but is limited to a few types, one of which includes <image> - so no issues with doing this. And we already use the contents keywords there to mean the DOM content of the node)

Two other things CSS would give us over a markup-based solution (object or a <barcode> element as proposed in https://github.com/whatwg/html/issues/5801):

One suggestion however is to use barcode() rather than qrcode(). The reasoning is that while QR is the flavour du jour, in twenty years we might all be using aztec code or whatever.

So I'd think something more like this would be better:

div.qrcode { content: barcode(contents, ecc=high, unit=1mm); }
div.azteccode { content: barcode(contents, ecc=high, unit=1mm, symbology=aztec); }

With an open list of parameters to the barcode() function and a symbology that defaults to qrcode, you're completely future proofed and can define symbologies as necessary.

For reference, PDFReactor have support for this, as does Antenna House formatter. PDFReactor uses a <barcode> element and Antenna House use a magic URL (ref)

tabatkins commented 3 years ago

There's no need to make a generic function that smuggles the details in arguments, when the syntax of what's allowed might vary based on that argument. We're not exactly pressed for function names, so we can just give functions the maximally useful name to start with, and then give them the correct specialized syntax based on that. In 20 years we can add an azteccode() function alongside qrcode(), that's fine.

Thanks for the description of what needs to be controllable for QR codes, btw - that's what I was missing to make this a proper proposal. So we're looking at a grammar like:

<qrcode()> = qrcode( <string> | <url>] 
                     [ [ low-ecc | medium-ecc | high-ecc ] 
                       || [ unit [ auto | <length> ] ]
                       || [ version [ auto | <integer [1, 40]> ] ]
                     ]
                   )

Your example would be:

div.qrcode { content: qrcode("contents" high-ecc unit 1mm); }

Some further questions:

bkardell commented 3 years ago

Just linking up related issues for notifications of interest/posterity/etc: https://github.com/whatwg/html/issues/5801 which in turn links to other related things, as well as https://discourse.wicg.io/t/proposal-element-for-qr-code-barcodes/4673

faceless2 commented 3 years ago

Sorry, just checking ISO18004 for confirmation and there are actually four ECC levels, which have the highly descriptive names L M Q and H in ISO18004:2006 (what I have here). I've no idea what Q means. quite-high-ecc?

tabatkins commented 3 years ago

"Q" is "quartile", meaning that up to 25% of the data can be recovered by the error correction - it lies between M and H in terms of recovery %.

Yes, a minimum amount of whitespace is required around the code to make it scan (the "quiet zone"). I'd suggest this should be part of the intrinsic dimensions, as otherwise a user who wants to generate qrcodes to spec is going to have to calculate it themselves, which seems bad.

Yeah looks like it should be 4 units in width. Having it default to that but be controllable (so one can have codes stack up next to each other with just the minimum quiet zone between them, rather than double the zone) seems reasonable.

LeaVerou commented 2 years ago

Agree it may make sense to make it easier to generate QR codes, but is it CSS' place to define this?

tabatkins commented 2 years ago

If it's used in content, you can specify an alt text, and we could (should!) auto-fill that from the content.

That said, for url use-cases there should be a link anyway, probably wrapping the qr code. a[href].qr { content: qrcode(attr(href) ...); } would work wonderfully, for example.

hober commented 2 years ago

That said, for url use-cases there should be a link anyway, probably wrapping the qr code. a[href].qr { content: qrcode(attr(href) ...); } would work wonderfully, for example.

Yup, exactly.

Crissov commented 2 years ago

1D barcodes and 2D matrix codes are alternative, machine readable, monochromatic graphic representations of textual content. Using image files for them is roughly like Word Art. So, ideally, it would be used something like this:

<code>text to be encoded</code>

<a href="url:to.be/encoded">label</a>
code {
  display: code; /* add magic */
}

a::after {
  display: code;
  content: attr(href);
}

:root {
  code-redundancy: high; /* ECC */
  code-unit: 1mm;
  code-type: quick-response;
}

(display and code are probably not the best choices, but hopefully get the idea across.)

PS: It would be too much of a stretch to use text-transform for this, but perhaps text-transcode.