Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No warnings, added tests, added viewBox option #2

Merged
merged 10 commits into from
Sep 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 35 additions & 32 deletions lib/eqrcode.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,30 @@ defmodule EQRCode do
```
"""

alias EQRCode.{Encode, ReedSolomon, Matrix}

@doc """
Encode the binary.
"""
@spec encode(binary) :: EQRCode.Matrix.t()
@spec encode(binary) :: Matrix.t()
def encode(bin) when byte_size(bin) <= 154 do
data =
EQRCode.Encode.encode(bin)
|> EQRCode.ReedSolomon.encode()
Encode.encode(bin)
|> ReedSolomon.encode()

EQRCode.Encode.version(bin)
|> EQRCode.Matrix.new()
|> EQRCode.Matrix.draw_finder_patterns()
|> EQRCode.Matrix.draw_seperators()
|> EQRCode.Matrix.draw_alignment_patterns()
|> EQRCode.Matrix.draw_timing_patterns()
|> EQRCode.Matrix.draw_dark_module()
|> EQRCode.Matrix.draw_reserved_format_areas()
|> EQRCode.Matrix.draw_reserved_version_areas()
|> EQRCode.Matrix.draw_data_with_mask(data)
|> EQRCode.Matrix.draw_format_areas()
|> EQRCode.Matrix.draw_version_areas()
|> EQRCode.Matrix.draw_quite_zone()
Encode.version(bin)
|> Matrix.new()
|> Matrix.draw_finder_patterns()
|> Matrix.draw_seperators()
|> Matrix.draw_alignment_patterns()
|> Matrix.draw_timing_patterns()
|> Matrix.draw_dark_module()
|> Matrix.draw_reserved_format_areas()
|> Matrix.draw_reserved_version_areas()
|> Matrix.draw_data_with_mask(data)
|> Matrix.draw_format_areas()
|> Matrix.draw_version_areas()
|> Matrix.draw_quite_zone()
end

def encode(bin) when is_nil(bin) do
Expand All @@ -47,22 +49,22 @@ defmodule EQRCode do
@doc """
Encode the binary with custom pattern bits. Only supports version 5.
"""
@spec encode(binary, bitstring) :: EQRCode.Matrix.t()
@spec encode(binary, bitstring) :: Matrix.t()
def encode(bin, bits) when byte_size(bin) <= 106 do
data =
EQRCode.Encode.encode(bin, bits)
|> EQRCode.ReedSolomon.encode()
Encode.encode(bin, bits)
|> ReedSolomon.encode()

EQRCode.Matrix.new(5)
|> EQRCode.Matrix.draw_finder_patterns()
|> EQRCode.Matrix.draw_seperators()
|> EQRCode.Matrix.draw_alignment_patterns()
|> EQRCode.Matrix.draw_timing_patterns()
|> EQRCode.Matrix.draw_dark_module()
|> EQRCode.Matrix.draw_reserved_format_areas()
|> EQRCode.Matrix.draw_data_with_mask0(data)
|> EQRCode.Matrix.draw_format_areas()
|> EQRCode.Matrix.draw_quite_zone()
Matrix.new(5)
|> Matrix.draw_finder_patterns()
|> Matrix.draw_seperators()
|> Matrix.draw_alignment_patterns()
|> Matrix.draw_timing_patterns()
|> Matrix.draw_dark_module()
|> Matrix.draw_reserved_format_areas()
|> Matrix.draw_data_with_mask0(data)
|> Matrix.draw_format_areas()
|> Matrix.draw_quite_zone()
end

def encode(_, _), do: IO.puts("Binary too long.")
Expand All @@ -71,16 +73,17 @@ defmodule EQRCode do
```elixir
qr_code_content
|> EQRCode.encode()
|> EQRCode.svg(%{color: "#cc6600", shape: "circle", width: 300})
|> EQRCode.svg(color: "#cc6600", shape: "circle", width: 300)
```

You can specify the following attributes of the QR code:

* `color`: In hexadecimal format. The default is `#000`
* `shape`: Only `square` or `circle`. The default is `square`
* `width`: The width of the QR code in pixel. Without the width attribute, the QR code size will be dynamically generated based on the input string.
* `viewbox`: When set to `true`, the SVG element will specify its height and width using `viewBox`, instead of explicit `height` and `width` tags.

Default options are `%{color: "#000", shape: "square"}`.
Default options are `[color: "#000", shape: "square"]`.
"""
defdelegate svg(matrix, options \\ %{}), to: EQRCode.Svg
defdelegate svg(matrix, options \\ []), to: EQRCode.SVG
end
37 changes: 22 additions & 15 deletions lib/eqrcode/galois_field.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,41 @@ defmodule EQRCode.GaloisField do

import Bitwise

@integers %{}
@alphas %{}

Stream.iterate(1, fn e ->
n = e <<< 1
if n >= 256, do: n ^^^ 0b100011101, else: n
end)
|> Stream.take(256)
|> Stream.with_index()
|> Enum.each(fn {e, i} ->
Module.put_attribute(__MODULE__, :alphas, Map.put(@alphas, e, i))
Module.put_attribute(__MODULE__, :integers, Map.put(@integers, i, e))
end)

@doc """
Given alpha exponent returns integer.

Example:
iex> QRCode.GaloisField.to_i(1)
iex> EQRCode.GaloisField.to_i(1)
2
"""
@spec to_i(integer) :: integer
def to_i(alpha)
def to_i(alpha) do
@integers[alpha]
end

@doc """
Given integer returns alpha exponent.

Example:
iex> QRCode.GaloisField.to_a(2)
iex> EQRCode.GaloisField.to_a(2)
1
"""
@spec to_a(integer) :: integer
def to_a(integer)

Stream.iterate(1, fn e ->
n = e <<< 1
if n >= 256, do: n ^^^ 0b100011101, else: n
end)
|> Stream.take(256)
|> Stream.with_index()
|> Enum.each(fn {e, i} ->
def to_i(unquote(i)), do: unquote(e)
def to_a(unquote(e)), do: unquote(i)
end)
def to_a(integer) do
@alphas[integer]
end
end
26 changes: 19 additions & 7 deletions lib/eqrcode/svg.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
defmodule EQRCode.Svg do
defmodule EQRCode.SVG do
@moduledoc """
Render the QR Code matrix in SVG format

```elixir
qr_code_content
|> EQRCode.encode()
|> EQRCode.svg(%{color: "#cc6600", shape: "circle", width: 300})
|> EQRCode.svg(color: "#cc6600", shape: "circle", width: 300)
```

You can specify the following attributes of the QR code:
Expand All @@ -18,20 +18,32 @@ defmodule EQRCode.Svg do

"""

alias EQRCode.Matrix

@doc """
Return the SVG format of the QR Code
"""
def svg(%EQRCode.Matrix{matrix: matrix}, options) do
@spec svg(Matrix.t(), map() | Keyword.t()) :: String.t()
def svg(%Matrix{matrix: matrix}, options \\ []) do
options = options |> Enum.map(& &1)
matrix_size = matrix |> Tuple.to_list() |> Enum.count()
svg_options = options |> set_svg_options(matrix_size)
svg_options = options |> Map.new() |> set_svg_options(matrix_size)
dimension = matrix_size * svg_options[:module_size]

xml_tag = ~s(<?xml version="1.0" standalone="yes"?>)

dimension_attrs =
if Keyword.get(options, :viewbox, false) do
~s(viewBox="0 0 #{dimension} #{dimension}")
else
~s(width="#{dimension}" height="#{dimension}")
end

open_tag =
~s(<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" width="#{
dimension
}" height="#{dimension}" shape-rendering="crispEdges">)
~s(<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" #{
dimension_attrs
}
shape-rendering="crispEdges">)

close_tag = ~s(</svg>)

Expand Down
1 change: 0 additions & 1 deletion test/eqrcode_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
defmodule EQRCodeTest do
use ExUnit.Case
doctest EQRCode

describe "encoder" do
@sample_matrix {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
Expand Down
4 changes: 4 additions & 0 deletions test/galois_field_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defmodule EQRCode.GaloisFieldTest do
use ExUnit.Case
doctest EQRCode.GaloisField
end
4 changes: 4 additions & 0 deletions test/matrix_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defmodule EQRCode.MatrixTest do
use ExUnit.Case
doctest EQRCode.Matrix
end
4 changes: 4 additions & 0 deletions test/reed_solomon_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defmodule EQRCode.ReedSolomonTest do
use ExUnit.Case
doctest EQRCode.ReedSolomon
end