QRCode - Implement QR Code in Elixir

Only 650 LOC, wrote for fun :slight_smile:

7 Likes

Hey thatā€™s quite nice!
Does it only do ANSI output or is there any chance youā€™d implement images? Perhaps by taking https://gitlab.com/Pacodastre/qrcode as a backend?

1 Like

Thanks!!

Donā€™t have plan to include png encoder. Render to terminal is just one way to represent the qr code, though one can pipe the data to any other png library.

1 Like

@sbs: No need to implement png encoder from scratch. Just draw QR code in canvas and add simple option to export it. :smiley:

For example you can draw a canvas and change it to image really easy in JavaScript:

var canvas = document.querySelector("canvas#selector.here");
var data = canvas.toDataURL("image/png");
document.write('<img src="' + data + '"/>');

Itā€™s much more simpler and allows to simple export in much more formats without any library for this!

2 Likes

How is that supposed to work? We have QRCode.encode("some data) with ā€œcanvasā€?

How?

1 Like

@Raddi_On: Just few steps:

  1. Of course you need somehow send these data to JavaScript (letā€™s say in json format)
  2. You need a simple function that draws qrcode - itā€™s just matrix of 0 (white) and 1 (black) squares. Drawing squares in canvas is one of the easiest things to do.
  3. You need to use export as I already show

I will show you how it could look in Elixir:

defmodule Example do
  # @canvas ...

  def draw_image(matrix) do
    # firstly we need to have enumarable - not tuples, so:
    matrix
    |> Tuple.to_list()
    |> Enum.map(&Tuple.to_list/1)
    # then we need to have a row numbers like:
    |> Enum.with_index()
    # then for all rows:
    |> Enum.map(&draw_row/1)
  end

  defp draw_row({row, index}) do
    row
    # we need to know also column index:
    |> Enum.with_index()
    # then for all cells in row:
    |> Enum.map(&draw_cell(&1, index))
  end

  defp draw_cell({0, column}, row) do
    do_draw_cell(row, column, {255, 255, 255})
  end
  defp draw_cell({1, column}, row) do
    do_draw_cell(row, column, {0, 0, 0})
  end

  defp do_draw_cell(row, column, color) do
    # you have row, column and color to draw
    # simply calculate `x` and `y` like:
    square_size = 5
    x = column * square_size
    y = row * square_size
    # now you can call real draw function
    # with x, y, width (square_size), height (square_size) 
    # and finally fill it with color
    Canvas.draw(@canvas, x, y, square_size, square_size, color)
    # use canvas HTML5 API to export image
    # for example on download button click
  end
end

As you can see square coordinates are noting special. You just need to know in which row and column you are and simply multiply them by number of pixels that you wish to have for each cell.

3 Likes

Thanks.

Whatā€™s Canvas? It doesnā€™t exist in Elixir.

My goal is to something pass qr_code binary data into a Phoenix template where I can draw it with this:

  var b64imgData = btoa(<%= qr_code_data %>);
  var img = document.getElementById("img_qrcode");
  img.src = "data:image/jpeg;base64," + b64imgData;

How can I do this?

1 Like

@Raddi_On: Of course not. Itā€™s only example, because Elixir is easier than JavaScript, so I could write it faster (also donā€™t used JavaScript for some time :smiley:).
I just show you how to collect required info that you will need in Canvas.
Canvas is drawn by web browser.


1 Like

I know. But my question remains: how to pass qr code binary data to a Phoenix template? Or rather: what exactly should I pass given that I have QRCode.encode("some data)?

1 Like

@Raddi_On:
You can use for example request for json.
For example:

defmodule MyAppWeb.Controllers.ExampleController do
  def action_name(conn, %{param_name: param_value}) do
    matrix = QRCode.encode(param_value).matrix
    data = matrix |> Tuple.to_list() |> Enum.map(&Tuple.to_list/1)
    json(conn, data)
  end
end

You can also use probably Drab:

4 Likes

Thatā€™s an array of arrays. Doesnā€™t it have to be converted an image format before itā€™s passed to a phoenix template?

1 Like

@Raddi_On: There are no image data. Only qrcode matrix - i.e. info about rows, columns and cells. You need to use these info (like I do in Elixir example) to get x, y, width and height and color and pass them to Canvas draw and fill functions.

You will export canvas to image - not create canvas from image.
Canvas will allow you to export to more formats than simple png.

Of course nothing stops you from hiding canvas and get base64 string from it after you will done drawing. Then you can add image tag with that base64 string.

1 Like

Or instead of using js, becaouseā€¦ You knowā€¦ Not everything resolves about web pages :wink: you can just use existing Erlang libraries like i.e. https://github.com/yuce/png/blob/master/README.md

2 Likes

Or if anyone want to use Svg (instead of Canvas), you can use this a library.

You really should properly organizer your code into modules and submodules. The current flat hirarchy will possibilly clash with other libraries from hex somewhen.

Thx, itā€™s fixed now.

Hi everyone.
Iā€™m now a little bit confused. In our a project we used this Sunboshanā€™s library, e.g this way:

QRCode.encode("http://localhost:4040/events//Users/smita/projects/elixir/fotobouda/server/photo/tmps/2018-07-28_07-44-15-809348_original-ocwo.jpg")

And as you can see in terminal output, the QRCode.Matrix contains a couple of nils. Is that correctly behaviour?

If it QRCode.render/1s fine, Iā€™d think so.

Thx for your fast an answer. QRCode.render gives me, see picture below

qrcode-nil

So I donā€™t know if this is fine. Weā€™re working with this matrix further in project (we render the matrix to svg). Can I assume if this turquoise color weā€™ll change to white the functionality of QR would be still okay?

Iā€™m not sure, perhaps you should ask the original author in the repo?

Iā€™d not use the matrix directly, as it is badly documented, and especially the matrix field is allowed to contain anything.

Another reason not to use the library at all is, that itā€™s not on hex and hasnā€™t been touched on the repo for about a year.