├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── config └── config.exs ├── lib ├── symbol.ex └── zbar.ex ├── mix.exs ├── mix.lock ├── src ├── base64.h └── zbar_scanner.c └── test ├── test_helper.exs └── zbar_test.exs /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | .DS_Store 22 | *.o 23 | priv/zbar_scanner 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Greg Mefford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Variables to override 2 | # 3 | # CC C compiler 4 | # CROSSCOMPILE crosscompiler prefix, if any 5 | # CFLAGS compiler flags for compiling all C files 6 | # LDFLAGS linker flags for linking all binaries 7 | # MIX_COMPILE_PATH path to the build's ebin directory 8 | 9 | LDFLAGS += -lzbar -ljpeg 10 | CFLAGS += -Wall -std=gnu99 11 | CC ?= $(CROSSCOMPILE)-gcc 12 | 13 | ifeq ($(MIX_COMPILE_PATH),) 14 | $(error MIX_COMPILE_PATH should be set by elixir_make!) 15 | endif 16 | 17 | PREFIX = $(MIX_COMPILE_PATH)/../priv 18 | BUILD = $(MIX_COMPILE_PATH)/../obj 19 | 20 | SRC=src/zbar_scanner.c 21 | OBJ=$(SRC:.c=.o) 22 | 23 | DEFAULT_TARGETS ?= $(PREFIX) $(PREFIX)/zbar_scanner 24 | 25 | calling_from_make: 26 | mix compile 27 | 28 | all: $(BUILD) $(DEFAULT_TARGETS) 29 | 30 | $(BUILD)/%.o: src/%.c 31 | $(CC) $(CFLAGS) -c $< -o $@ 32 | 33 | $(BUILD): 34 | mkdir -p $@ 35 | 36 | $(PREFIX): 37 | mkdir -p $@ 38 | 39 | $(PREFIX)/zbar_scanner: $(BUILD)/zbar_scanner.o 40 | $(CC) $^ $(LDFLAGS) -o $@ 41 | 42 | clean: 43 | rm -rf $(PREFIX)/* $(BUILD)/* 44 | 45 | .PHONY: all clean calling_from_make 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zbar 2 | 3 | Scan one or more barcodes from a JPEG image. 4 | 5 | The API for this library is very simple: 6 | 7 | ```elixir 8 | iex> File.read!("QR_REF1.jpg") |> Zbar.scan() 9 | %Zbar.Symbol{ 10 | data: "REF1", 11 | points: [{40, 40}, {40, 250}, {250, 250}, {250, 40}], 12 | quality: 1, 13 | type: "QR-Code" 14 | } 15 | ``` 16 | 17 | More detailed API documentation can be found at 18 | [https://hexdocs.pm/zbar](https://hexdocs.pm/zbar). 19 | 20 | ## Installation 21 | 22 | This package is [available in Hex](https://hex.pm/packages/zbar) and can be 23 | installed by adding `:zbar` to your list of dependencies in `mix.exs`: 24 | 25 | ```elixir 26 | def deps do 27 | [{:zbar, "~> 0.2"}] 28 | end 29 | ``` 30 | 31 | You will also need to have `zbar` and `libjpeg` installed in order to compile 32 | the required native code. On OSX with Homebrew, this is as simple as: 33 | 34 | ```bash 35 | $ brew install zbar libjpeg 36 | ``` 37 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | -------------------------------------------------------------------------------- /lib/symbol.ex: -------------------------------------------------------------------------------- 1 | defmodule Zbar.Symbol do 2 | @moduledoc """ 3 | The `Zbar.Symbol` struct represents a barcode that has been detected by `zbar`. 4 | 5 | It has the following fields: 6 | 7 | * `type`: The type of barcode that has been detected, as a string. Possible 8 | values are listed in `t:type_enum/0` 9 | 10 | * `quality`: An integer metric representing barcode scan confidence. 11 | 12 | Larger values are better than smaller values, but only the ordered 13 | relationship between two values is meaningful. The values themselves 14 | are not defined and may change in future versions of the library. 15 | 16 | * `points`: The list of coordinates (encoded as `{x, y}` tuples) where a 17 | barcode was located within the source image. 18 | 19 | The structure of the points depends on the type of barcode being scanned. 20 | For example, for the `QR-Code` type, the points represent the bounding 21 | rectangle, with the first point indicating the top-left positioning pattern 22 | of the QR-Code if it had not been rotated. 23 | 24 | * `data`: The actual barcode data, as a binary string. 25 | 26 | Note that this is a string that may contain arbitrary binary data, 27 | including non-printable characters. 28 | """ 29 | 30 | defstruct type: :UNKNOWN, quality: 0, points: [], data: nil 31 | 32 | @type type_enum :: 33 | :CODE_39 34 | | :CODE_128 35 | | :EAN_8 36 | | :EAN_13 37 | | :I2_5 38 | | :ISBN_10 39 | | :ISBN_13 40 | | :PDF417 41 | | :QR_Code 42 | | :UPC_A 43 | | :UPC_E 44 | | :UNKNOWN 45 | 46 | @type point :: {non_neg_integer(), non_neg_integer()} 47 | 48 | @typedoc @moduledoc 49 | @type t :: %__MODULE__{ 50 | type: type_enum(), 51 | quality: non_neg_integer(), 52 | points: [point()], 53 | data: binary() 54 | } 55 | 56 | end 57 | -------------------------------------------------------------------------------- /lib/zbar.ex: -------------------------------------------------------------------------------- 1 | defmodule Zbar do 2 | @moduledoc """ 3 | Scan all barcodes in a JPEG image using the `zbar` library. 4 | """ 5 | 6 | alias Zbar.Symbol 7 | 8 | require Logger 9 | 10 | @doc """ 11 | Scan all barcode data in a JPEG-encoded image. 12 | 13 | * `jpeg_data` should be a binary containing JPEG-encoded image data. 14 | * `timeout` is the time in milliseconds to allow for the processing of the image 15 | (default 5000 milliseconds). 16 | 17 | Returns: 18 | * `{:ok, [%Zbar.Symbol{}]}` on success 19 | * `{:error, :timeout}` if the zbar process hung for some reason 20 | * `{:error, binary()}` if there was an error in the scanning process 21 | """ 22 | @spec scan(binary(), pos_integer()) :: 23 | {:ok, list(Zbar.Symbol.t())} 24 | | {:error, :timeout} 25 | | {:error, binary()} 26 | def scan(jpeg_data, timeout \\ 5000) do 27 | # We run this in a `Task` so that `collect_output` can use `receive` 28 | # without interfering with the caller's mailbox. 29 | Task.async(fn -> do_scan(jpeg_data, timeout) end) 30 | |> Task.await(:infinity) 31 | end 32 | 33 | @spec do_scan(binary(), pos_integer()) :: 34 | {:ok, [Zbar.Symbol.t()]} 35 | | {:error, :timeout} 36 | | {:error, binary()} 37 | defp do_scan(jpeg_data, timeout) do 38 | File.open!(temp_file(), [:write, :binary], & IO.binwrite(&1, jpeg_data)) 39 | 40 | {:spawn_executable, to_charlist(zbar_binary())} 41 | |> Port.open([ 42 | {:args, [temp_file()]}, 43 | :binary, 44 | :stream, 45 | :exit_status, 46 | :use_stdio, 47 | :stderr_to_stdout 48 | ]) 49 | |> collect_output(timeout) 50 | |> case do 51 | {:ok, data} -> 52 | symbols = 53 | data 54 | |> String.split("\n", trim: true) 55 | |> Enum.map(&parse_symbol/1) 56 | 57 | {:ok, symbols} 58 | 59 | {:error, reason} -> 60 | {:error, reason} 61 | end 62 | end 63 | 64 | @spec temp_file() :: binary() 65 | defp temp_file, do: Path.join(System.tmp_dir!(), "zbar_image.jpg") 66 | 67 | @spec zbar_binary() :: binary() 68 | defp zbar_binary, do: Path.join(:code.priv_dir(:zbar), "zbar_scanner") 69 | 70 | @spec collect_output(port(), pos_integer(), binary()) :: 71 | {:ok, binary()} 72 | | {:error, :timeout} 73 | | {:error, binary()} 74 | defp collect_output(port, timeout, buffer \\ "") do 75 | receive do 76 | {^port, {:data, "Premature end of JPEG file\n"}} -> 77 | # Handles an error condition described in https://github.com/elixir-vision/zbar-elixir/issues/1 78 | collect_output(port, timeout, buffer) 79 | {^port, {:data, data}} -> 80 | collect_output(port, timeout, buffer <> to_string(data)) 81 | {^port, {:exit_status, 0}} -> 82 | {:ok, buffer} 83 | {^port, {:exit_status, _}} -> 84 | {:error, buffer} 85 | after timeout -> 86 | Port.close(port) 87 | {:error, :timeout} 88 | end 89 | end 90 | 91 | # Accepts strings like: 92 | # type:QR-Code quality:1 points:40,40;40,250;250,250;250,40 data:UkVGMQ== 93 | # 94 | # Returns structs like: 95 | # %Zbar.Symbol{ 96 | # data: "REF1", 97 | # points: [{40, 40}, {40, 250}, {250, 250}, {250, 40}], 98 | # quality: 1, 99 | # type: "QR-Code" 100 | # } 101 | @spec parse_symbol(binary()) :: Zbar.Symbol.t() 102 | defp parse_symbol(string) do 103 | string 104 | |> String.split(" ") 105 | |> Enum.reduce(%Symbol{}, fn item, acc -> 106 | [key, value] = String.split(item, ":", parts: 2) 107 | case key do 108 | "type" -> 109 | %Symbol{acc | type: parse_type(value)} 110 | 111 | "quality" -> 112 | %Symbol{acc | quality: String.to_integer(value)} 113 | 114 | "points" -> 115 | %Symbol{acc | points: parse_points(value)} 116 | 117 | "data" -> 118 | %Symbol{acc | data: Base.decode64!(value)} 119 | end 120 | end) 121 | end 122 | 123 | @spec parse_type(binary()) :: Zbar.Symbol.type_enum() 124 | defp parse_type("CODE-39"), do: :CODE_39 125 | defp parse_type("CODE-128"), do: :CODE_128 126 | defp parse_type("EAN-8"), do: :EAN_8 127 | defp parse_type("EAN-13"), do: :EAN_13 128 | defp parse_type("I2/5"), do: :I2_5 129 | defp parse_type("ISBN-10"), do: :ISBN_10 130 | defp parse_type("ISBN-13"), do: :ISBN_13 131 | defp parse_type("PDF417"), do: :PDF417 132 | defp parse_type("QR-Code"), do: :QR_Code 133 | defp parse_type("UPC-A"), do: :UPC_A 134 | defp parse_type("UPC-E"), do: :UPC_E 135 | defp parse_type(_), do: :UNKNOWN 136 | 137 | @spec parse_points(binary()) :: [Zbar.Symbol.point()] 138 | defp parse_points(string) do 139 | string 140 | |> String.split(";") 141 | |> Enum.map(& parse_point/1) 142 | end 143 | 144 | @spec parse_point(binary()) :: Zbar.Symbol.point() 145 | defp parse_point(string) do 146 | [x, y] = String.split(string, ",", parts: 2) 147 | {String.to_integer(x), String.to_integer(y)} 148 | end 149 | end 150 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Zbar.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :zbar, 7 | version: "0.2.1", 8 | description: "Scan one or more barcodes from a JPEG image", 9 | elixir: "~> 1.4", 10 | make_targets: ["all"], 11 | make_clean: ["clean"], 12 | compilers: [:elixir_make | Mix.compilers()], 13 | build_embedded: true, 14 | start_permanent: Mix.env == :prod, 15 | compilers: [:elixir_make] ++ Mix.compilers, 16 | package: package(), 17 | deps: deps(), 18 | dialyzer: [plt_add_apps: [:mix]], 19 | docs: docs() 20 | ] 21 | end 22 | 23 | def application do 24 | [extra_applications: [:logger]] 25 | end 26 | 27 | defp deps do 28 | [ 29 | {:elixir_make, "~> 0.6", runtime: false}, 30 | {:dialyxir, "~> 1.0.0-rc.6", only: :dev, runtime: false}, 31 | {:ex_doc, "~> 0.21", only: :dev, runtime: false}, 32 | ] 33 | end 34 | 35 | defp docs do 36 | [ 37 | main: "README", 38 | extras: [ 39 | "README.md" 40 | ] 41 | ] 42 | end 43 | 44 | defp package do 45 | [ 46 | files: [ 47 | "lib", 48 | "src/*.[ch]", 49 | "Makefile", 50 | "mix.exs", 51 | "README.md", 52 | "LICENSE" 53 | ], 54 | maintainers: ["Greg Mefford"], 55 | licenses: ["MIT"], 56 | links: %{"GitHub" => "https://github.com/elixir-vision/zbar-elixir"} 57 | ] 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "dialyxir": {:hex, :dialyxir, "1.0.0-rc.6", "78e97d9c0ff1b5521dd68041193891aebebce52fc3b93463c0a6806874557d7d", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"}, 3 | "earmark": {:hex, :earmark, "1.3.5", "0db71c8290b5bc81cb0101a2a507a76dca659513984d683119ee722828b424f6", [:mix], [], "hexpm"}, 4 | "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"}, 5 | "erlex": {:hex, :erlex, "0.2.4", "23791959df45fe8f01f388c6f7eb733cc361668cbeedd801bf491c55a029917b", [:mix], [], "hexpm"}, 6 | "ex_doc": {:hex, :ex_doc, "0.21.1", "5ac36660846967cd869255f4426467a11672fec3d8db602c429425ce5b613b90", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, 8 | "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, 10 | } 11 | -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | https://github.com/superwills/NibbleAndAHalf 4 | base64.h -- Fast base64 encoding and decoding. 5 | version 1.0.0, April 17, 2013 143a 6 | 7 | Copyright (C) 2013 William Sherif 8 | 9 | This software is provided 'as-is', without any express or implied 10 | warranty. In no event will the authors be held liable for any damages 11 | arising from the use of this software. 12 | 13 | Permission is granted to anyone to use this software for any purpose, 14 | including commercial applications, and to alter it and redistribute it 15 | freely, subject to the following restrictions: 16 | 17 | 1. The origin of this software must not be misrepresented; you must not 18 | claim that you wrote the original software. If you use this software 19 | in a product, an acknowledgment in the product documentation would be 20 | appreciated but is not required. 21 | 2. Altered source versions must be plainly marked as such, and must not be 22 | misrepresented as being the original software. 23 | 3. This notice may not be removed or altered from any source distribution. 24 | 25 | William Sherif 26 | will.sherif@gmail.com 27 | 28 | YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz 29 | 30 | */ 31 | #ifndef BASE64_H 32 | #define BASE64_H 33 | 34 | #include 35 | #include 36 | 37 | const static char* b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ; 38 | 39 | // maps A=>0,B=>1.. 40 | const static unsigned char unb64[]={ 41 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10 42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20 43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30 44 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40 45 | 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, //50 46 | 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, //60 47 | 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, //70 48 | 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, //80 49 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, //90 50 | 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, //100 51 | 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, //110 52 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, //120 53 | 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, //130 54 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140 55 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150 56 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160 57 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170 58 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180 59 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190 60 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200 61 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210 62 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220 63 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230 64 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240 65 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //250 66 | 0, 0, 0, 0, 0, 0, 67 | }; // This array has 256 elements 68 | 69 | // Converts binary data of length=len to base64 characters. 70 | // Length of the resultant string is stored in flen 71 | // (you must pass pointer flen). 72 | char* base64( const void* binaryData, int len, int *flen ) 73 | { 74 | const unsigned char* bin = (const unsigned char*) binaryData ; 75 | char* res ; 76 | 77 | int rc = 0 ; // result counter 78 | int byteNo ; // I need this after the loop 79 | 80 | int modulusLen = len % 3 ; 81 | int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1) ; // 2 gives 1 and 1 gives 2, but 0 gives 0. 82 | 83 | *flen = 4*(len + pad)/3 ; 84 | res = (char*) malloc( *flen + 1 ) ; // and one for the null 85 | if( !res ) 86 | { 87 | puts( "ERROR: base64 could not allocate enough memory." ) ; 88 | puts( "I must stop because I could not get enough" ) ; 89 | return 0; 90 | } 91 | 92 | for( byteNo = 0 ; byteNo <= len-3 ; byteNo+=3 ) 93 | { 94 | unsigned char BYTE0=bin[byteNo]; 95 | unsigned char BYTE1=bin[byteNo+1]; 96 | unsigned char BYTE2=bin[byteNo+2]; 97 | res[rc++] = b64[ BYTE0 >> 2 ] ; 98 | res[rc++] = b64[ ((0x3&BYTE0)<<4) + (BYTE1 >> 4) ] ; 99 | res[rc++] = b64[ ((0x0f&BYTE1)<<2) + (BYTE2>>6) ] ; 100 | res[rc++] = b64[ 0x3f&BYTE2 ] ; 101 | } 102 | 103 | if( pad==2 ) 104 | { 105 | res[rc++] = b64[ bin[byteNo] >> 2 ] ; 106 | res[rc++] = b64[ (0x3&bin[byteNo])<<4 ] ; 107 | res[rc++] = '='; 108 | res[rc++] = '='; 109 | } 110 | else if( pad==1 ) 111 | { 112 | res[rc++] = b64[ bin[byteNo] >> 2 ] ; 113 | res[rc++] = b64[ ((0x3&bin[byteNo])<<4) + (bin[byteNo+1] >> 4) ] ; 114 | res[rc++] = b64[ (0x0f&bin[byteNo+1])<<2 ] ; 115 | res[rc++] = '='; 116 | } 117 | 118 | res[rc]=0; // NULL TERMINATOR! ;) 119 | return res ; 120 | } 121 | 122 | unsigned char* unbase64( const char* ascii, int len, int *flen ) 123 | { 124 | const unsigned char *safeAsciiPtr = (const unsigned char*)ascii ; 125 | unsigned char *bin ; 126 | int cb=0; 127 | int charNo; 128 | int pad = 0 ; 129 | 130 | if( len < 2 ) { // 2 accesses below would be OOB. 131 | // catch empty string, return NULL as result. 132 | puts( "ERROR: You passed an invalid base64 string (too short). You get NULL back." ) ; 133 | *flen=0; 134 | return 0 ; 135 | } 136 | if( safeAsciiPtr[ len-1 ]=='=' ) ++pad ; 137 | if( safeAsciiPtr[ len-2 ]=='=' ) ++pad ; 138 | 139 | *flen = 3*len/4 - pad ; 140 | bin = (unsigned char*)malloc( *flen ) ; 141 | if( !bin ) 142 | { 143 | puts( "ERROR: unbase64 could not allocate enough memory." ) ; 144 | puts( "I must stop because I could not get enough" ) ; 145 | return 0; 146 | } 147 | 148 | for( charNo=0; charNo <= len - 4 - pad ; charNo+=4 ) 149 | { 150 | int A=unb64[safeAsciiPtr[charNo]]; 151 | int B=unb64[safeAsciiPtr[charNo+1]]; 152 | int C=unb64[safeAsciiPtr[charNo+2]]; 153 | int D=unb64[safeAsciiPtr[charNo+3]]; 154 | 155 | bin[cb++] = (A<<2) | (B>>4) ; 156 | bin[cb++] = (B<<4) | (C>>2) ; 157 | bin[cb++] = (C<<6) | (D) ; 158 | } 159 | 160 | if( pad==1 ) 161 | { 162 | int A=unb64[safeAsciiPtr[charNo]]; 163 | int B=unb64[safeAsciiPtr[charNo+1]]; 164 | int C=unb64[safeAsciiPtr[charNo+2]]; 165 | 166 | bin[cb++] = (A<<2) | (B>>4) ; 167 | bin[cb++] = (B<<4) | (C>>2) ; 168 | } 169 | else if( pad==2 ) 170 | { 171 | int A=unb64[safeAsciiPtr[charNo]]; 172 | int B=unb64[safeAsciiPtr[charNo+1]]; 173 | 174 | bin[cb++] = (A<<2) | (B>>4) ; 175 | } 176 | 177 | return bin ; 178 | } 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /src/zbar_scanner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "base64.h" 11 | 12 | #define zbar_fourcc(a, b, c, d) \ 13 | ((unsigned long)(a) | \ 14 | ((unsigned long)(b) << 8) | \ 15 | ((unsigned long)(c) << 16) | \ 16 | ((unsigned long)(d) << 24)) 17 | 18 | int main (int argc, char **argv) 19 | { 20 | if (argc != 2) 21 | errx(EXIT_FAILURE, "Usage: %s filename.jpg", argv[0]); 22 | 23 | struct jpeg_decompress_struct cinfo; 24 | struct jpeg_error_mgr jerr; 25 | unsigned long bmp_size; 26 | unsigned char *bmp_buffer; 27 | unsigned char *scanline_buffer; 28 | uint16_t width, height; 29 | uint8_t pixel_size; 30 | 31 | cinfo.err = jpeg_std_error(&jerr); 32 | jpeg_create_decompress(&cinfo); 33 | 34 | FILE * file = fopen(argv[1], "rb"); 35 | if (file == NULL) 36 | errx(EXIT_FAILURE, "Could not open file %s", argv[1]); 37 | 38 | jpeg_stdio_src(&cinfo, file); 39 | 40 | if (jpeg_read_header(&cinfo, TRUE) != 1) 41 | exit(EXIT_FAILURE); 42 | 43 | jpeg_start_decompress(&cinfo); 44 | 45 | width = cinfo.output_width; 46 | height = cinfo.output_height; 47 | pixel_size = cinfo.output_components; 48 | 49 | bmp_size = width * height; 50 | bmp_buffer = (unsigned char*) malloc(bmp_size); 51 | 52 | uint32_t scanline_size = width * pixel_size; 53 | scanline_buffer = (unsigned char*) malloc(scanline_size); 54 | 55 | uint16_t row = 0, col = 0; 56 | uint8_t r, g, b, lum; 57 | while (row < height) { 58 | if (jpeg_read_scanlines(&cinfo, &scanline_buffer, 1) == 1) { 59 | for(col = 0; col < width; col++) { 60 | r = scanline_buffer[3 * col]; 61 | g = scanline_buffer[3 * col + 1]; 62 | b = scanline_buffer[3 * col + 2]; 63 | lum = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16; 64 | bmp_buffer[row * width + col] = lum; 65 | } 66 | row++; 67 | } 68 | } 69 | 70 | jpeg_finish_decompress(&cinfo); 71 | jpeg_destroy_decompress(&cinfo); 72 | fclose(file); 73 | 74 | zbar_image_t *image = zbar_image_create(); 75 | zbar_image_set_size(image, width, height); 76 | zbar_image_set_format(image, zbar_fourcc('Y', '8', '0', '0')); 77 | zbar_image_set_data(image, bmp_buffer, width * height, NULL); 78 | 79 | zbar_image_scanner_t *scanner = zbar_image_scanner_create(); 80 | zbar_image_scanner_set_config(scanner, 0, ZBAR_CFG_ENABLE, 1); 81 | zbar_scan_image(scanner, image); 82 | 83 | const zbar_symbol_t *symbol = zbar_image_first_symbol(image); 84 | for(; symbol; symbol = zbar_symbol_next(symbol)) { 85 | printf( 86 | "type:%s quality:%i points:", 87 | zbar_get_symbol_name(zbar_symbol_get_type(symbol)), 88 | zbar_symbol_get_quality(symbol) 89 | ); 90 | unsigned int point_count = zbar_symbol_get_loc_size(symbol); 91 | for(int i = 0; i < point_count; i++) { 92 | printf( 93 | "%i,%i", 94 | zbar_symbol_get_loc_x(symbol, i), 95 | zbar_symbol_get_loc_y(symbol, i) 96 | ); 97 | if (i+1 < point_count) printf(";"); 98 | } 99 | const char *data = zbar_symbol_get_data(symbol); 100 | unsigned int data_length = zbar_symbol_get_data_length(symbol); 101 | int base64_length; 102 | char * base64_data = base64(data, data_length, &base64_length); 103 | printf(" data:%s\n", base64_data); 104 | free(base64_data); 105 | } 106 | 107 | zbar_image_destroy(image); 108 | zbar_image_scanner_destroy(scanner); 109 | 110 | return(0); 111 | } 112 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /test/zbar_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ZbarTest do 2 | use ExUnit.Case 3 | doctest Zbar 4 | 5 | test "the truth" do 6 | assert 1 + 1 == 2 7 | end 8 | end 9 | --------------------------------------------------------------------------------