Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial checkin

  • Loading branch information...
commit fae4c3f84e4c2a29cacd39d3dbaffbb2dd5b9e03 1 parent 9855e2f
@willglynn willglynn authored
View
2  LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2009 Will Glynn
+Copyright (c) 2010 Will Glynn
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
View
44 README.rdoc
@@ -1,6 +1,48 @@
= zbar
+by Will Glynn
-Description goes here.
+http://github.com/delta407/ruby-zbar
+
+== DESCRIPTION:
+
+Ruby bindings for ZBar, a barcode recognition library.
+
+== SYNOPSIS:
+
+ require 'zbar'
+
+ ZBar::Image.from_jpeg(File.read('test.jpg')).process
+ => [#<Zbar::Symbol:0x10147c668
+ @addon="",
+ @data="9876543210128",
+ @location= [...],
+ @quality=15,
+ @symbology="EAN-13">]
+
+== REQUIREMENTS:
+
+* FFI
+* ZBar, probably 0.10 or more
+
+ZBar can be obtained from http://zbar.sourceforge.net.
+
+== DOWNLOAD/INSTALL:
+
+From gemcutter:
+
+ [sudo] gem install zbar
+
+From github:
+
+ git clone git://github.com/delta407/ruby-zbar.git
+ cd ruby-zbar
+ rake install
+
+== LIMITATIONS:
+
+Doesn't expose all ZBar functionality, including:
+* No video functions
+* No low-level interfaces (scanner, decoder)
== Note on Patches/Pull Requests
View
8 Rakefile
@@ -5,13 +5,13 @@ begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "zbar"
- gem.summary = %Q{TODO: one-line summary of your gem}
- gem.description = %Q{TODO: longer description of your gem}
+ gem.summary = "Ruby bindings for ZBar, a barcode recognition library"
+ gem.description ="Ruby bindings for ZBar, a barcode recognition library. Uses FFI to interact with the underlying C library, but has no other dependencies."
gem.email = "will@willglynn.com"
- gem.homepage = "http://github.com/delta407/zbar"
+ gem.homepage = "http://github.com/delta407/ruby-zbar"
gem.authors = ["Will Glynn"]
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
+ gem.add_dependency "ffi", ">= 0"
end
Jeweler::GemcutterTasks.new
rescue LoadError
View
6 lib/zbar.rb
@@ -0,0 +1,6 @@
+require 'ffi'
+
+require 'zbar/lib'
+require 'zbar/symbol'
+require 'zbar/processor'
+require 'zbar/image'
View
102 lib/zbar/image.rb
@@ -0,0 +1,102 @@
+module ZBar
+
+ # Encapsulates a ZBar Image data structure.
+ class Image
+ # Instantiates a new Image object, either by creating an empty one,
+ # or wrapping the supplied pointer.
+ def initialize(pointer=nil)
+ @img = FFI::AutoPointer.new(
+ pointer || ZBar.zbar_image_create,
+ ZBar.method(:zbar_image_destroy)
+ )
+ end
+
+ # Instantiates an Image given JPEG data.
+ #
+ # This function uses the internal ZBar conversion function to decode the JPEG
+ # and convert it into a greyscale image suitable for further processing.
+ # This conversion may fail if ZBar was not built with <tt>--with-jpeg</tt>.
+ def self.from_jpeg(io_or_string)
+ if io_or_string.respond_to?(:read)
+ io_or_string = io_or_string.read
+ end
+
+ jpeg_image = new()
+ jpeg_image.set_data(ZBar::Format::JPEG, io_or_string)
+ return jpeg_image.convert(ZBar::Format::Y800)
+ end
+
+ # Instantiates an Image given raw PGM data.
+ #
+ # PGM is a NetPBM format, encoding width, height, and greyscale data, one byte
+ # per pixel. It is therefore ideally suited for loading into ZBar, which
+ # operates natively on Y800 pixel data--identical to the data section of a PGM
+ # file.
+ #
+ # The data is described in greater detail at
+ # http://netpbm.sourceforge.net/doc/pgm.html.
+ def self.from_pgm(io_or_string)
+ if io_or_string.respond_to?(:read)
+ io_or_string = io_or_string.read
+ end
+
+ image_data = io_or_string.gsub(/^(P5)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s/, '')
+ if $1 != 'P5'
+ raise ArgumentError, "input must be a PGM file"
+ end
+
+ width, height, max_val = $2.to_i, $3.to_i, $4.to_i
+
+ if max_val != 255
+ raise ArgumentError, "maximum value must be 255"
+ end
+
+ image = new()
+ image.set_data(ZBar::Format::Y800, image_data, width, height)
+ image
+ end
+
+ # Load arbitrary data from a string into the Image object.
+ #
+ # Format must be a ZBar::Format constant. See the ZBar documentation
+ # for what formats are supported, and how the data should be formatted.
+ #
+ # Most everyone should use one of the <tt>Image.from_*</tt> methods instead.
+ def set_data(format, data, width=nil, height=nil)
+ ZBar.zbar_image_set_format(@img, format)
+
+ # Note the @buffer jog: it's to keep Ruby GC from losing the last
+ # reference to the old @buffer before calling image_set_data.
+ new_buffer = FFI::MemoryPointer.from_string(data)
+ ZBar.zbar_image_set_data(@img, new_buffer, data.size, nil)
+ @buffer = new_buffer
+
+ if width && height
+ ZBar.zbar_image_set_size(@img, width.to_i, height.to_i)
+ end
+ end
+
+ # Ask ZBar to convert this image to a new format, returning a new Image.
+ #
+ # Not all conversions are possible: for example, if ZBar was built without
+ # JPEG support, it cannot convert JPEGs into anything else.
+ def convert(format)
+ ptr = ZBar.zbar_image_convert(@img, format)
+ if ptr.null?
+ raise ArgumentError, "conversion failed"
+ else
+ Image.new(ptr)
+ end
+ end
+
+ # Attempt to recognize barcodes in this image, using the supplied processor
+ # (if any), falling back to defaults.
+ #
+ # Returns an array of ZBar::Symbol objects.
+ def process(processor = nil)
+ processor ||= Processor.new
+ processor.process(self)
+ end
+ end
+
+end
View
44 lib/zbar/lib.rb
@@ -0,0 +1,44 @@
+module ZBar
+ extend FFI::Library
+ ffi_lib 'zbar'
+
+ attach_function :zbar_symbol_get_type, [:pointer], :int
+ attach_function :zbar_symbol_get_data, [:pointer], :string
+ attach_function :zbar_symbol_get_type, [:pointer], :int
+ attach_function :zbar_symbol_get_quality, [:pointer], :int
+ attach_function :zbar_symbol_get_loc_size, [:pointer], :uint
+ attach_function :zbar_symbol_get_loc_x, [:pointer, :uint], :int
+ attach_function :zbar_symbol_get_loc_y, [:pointer, :uint], :int
+ attach_function :zbar_symbol_next, [:pointer], :pointer
+
+ attach_function :zbar_image_create, [], :pointer
+ attach_function :zbar_image_destroy, [:pointer], :void
+ attach_function :zbar_image_first_symbol, [:pointer], :pointer
+ attach_function :zbar_image_set_format, [:pointer, :ulong], :void
+ attach_function :zbar_image_convert, [:pointer, :ulong], :pointer
+ attach_function :zbar_image_set_size, [:pointer, :uint, :uint], :void
+ attach_function :zbar_image_set_data, [:pointer, :buffer_in, :uint, :pointer], :void
+
+ attach_function :zbar_processor_create, [:int], :pointer
+ attach_function :zbar_processor_destroy, [:pointer], :void
+ attach_function :zbar_processor_init, [:pointer, :string, :int], :int
+
+ attach_function :zbar_process_image, [:pointer, :pointer], :int
+
+ attach_function :zbar_set_verbosity, [:int], :void
+ attach_function :zbar_get_symbol_name, [:int], :string
+ attach_function :zbar_get_addon_name, [:int], :string
+ attach_function :_zbar_error_spew, [:pointer, :int], :int
+
+ module Format #:nodoc:
+ %w(JPEG Y800 GREY).each { |format|
+ const_set(format.to_sym, format.unpack('V')[0])
+ }
+ end
+
+ # Sets the verbosity of the underlying ZBar library, which writes
+ # directly to stderr.
+ def self.verbosity=(v)
+ zbar_set_verbosity(v.to_i)
+ end
+end
View
37 lib/zbar/processor.rb
@@ -0,0 +1,37 @@
+module ZBar
+ class Processor
+ # Create a new processor.
+ def initialize(threads = 0)
+ @processor = FFI::AutoPointer.new(
+ ZBar.zbar_processor_create(threads),
+ ZBar.method(:zbar_processor_destroy)
+ )
+
+ if ZBar.zbar_processor_init(@processor, nil, 0) > 0
+ ZBar._zbar_error_spew(@processor, 0)
+ raise "error!"
+ end
+ end
+
+ # Attempt to recognize barcodes in this image. Raises an exception if ZBar
+ # signals an error, otherwise returns an array of ZBar::Symbol objects.
+ def process(image)
+ raise ArgumentError, "process() operates only on ZBar::Image objects" unless image.kind_of?(ZBar::Image)
+ image = image.instance_variable_get(:@img)
+
+ if ZBar.zbar_process_image(@processor, image) != 0
+ raise ArgumentError, "processing failed"
+ end
+
+ out = []
+
+ sym = ZBar.zbar_image_first_symbol(image)
+ until sym.null?
+ out << Symbol.new(sym)
+ sym = ZBar.zbar_symbol_next(sym)
+ end
+
+ out
+ end
+ end
+end
View
37 lib/zbar/symbol.rb
@@ -0,0 +1,37 @@
+module ZBar
+
+ class Symbol
+ attr_reader :symbology, :data, :addon, :quality, :location
+
+ def initialize(symbol=nil)
+ if symbol
+ type = ZBar.zbar_symbol_get_type(symbol)
+ @symbology = ZBar.zbar_get_symbol_name(type)
+ @data = ZBar.zbar_symbol_get_data(symbol)
+ @addon = ZBar.zbar_get_addon_name(type)
+ @quality = ZBar.zbar_symbol_get_quality(symbol)
+
+ @location = []
+ point_count = ZBar.zbar_symbol_get_loc_size(symbol)
+ i = 0
+ while i < point_count
+ @location << [ZBar.zbar_symbol_get_loc_x(symbol, i), ZBar.zbar_symbol_get_loc_y(symbol, i)]
+ i += 1
+ end
+ end
+ end
+
+ def ==(symbol)
+ return false unless symbol.kind_of?(Symbol)
+
+ (
+ self.symbology == symbol.symbology &&
+ self.data == symbol.data &&
+ self.addon == symbol.addon &&
+ self.quality == symbol.quality &&
+ self.location == symbol.location
+ )
+ end
+ end
+
+end
View
BIN  test/test.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  test/test.pgm
Binary file not shown
View
41 test/test_zbar.rb
@@ -1,7 +1,42 @@
require 'helper'
-class TestZbar < Test::Unit::TestCase
- should "probably rename this file and start testing for real" do
- flunk "hey buddy, you should probably rename this file and start testing for real"
+Path = File.dirname(__FILE__)
+
+class TestZBar < Test::Unit::TestCase
+ should "read the right barcode from a PGM blob" do
+ result = ZBar::Image.from_pgm(File.read("#{Path}/test.pgm")).process
+ assert_equal(result.size, 1)
+ assert_equal(result[0].data, '9876543210128')
+ assert_equal(result[0].symbology, 'EAN-13')
+ end
+
+ should "read a barcode from a PGM file" do
+ File.open("#{Path}/test.pgm") { |f|
+ result = ZBar::Image.from_pgm(f).process
+ assert_equal(result.size, 1)
+ }
+ end
+
+ should "be able to re-use a processor" do
+ processor = ZBar::Processor.new
+ pgm = File.read("#{Path}/test.pgm")
+
+ result1 = processor.process ZBar::Image.from_pgm(pgm)
+ result2 = processor.process ZBar::Image.from_pgm(pgm)
+ assert_equal(result1.size, 1)
+ assert_equal(result2.size, 1)
+ assert_equal(result1, result2)
+ end
+
+ should "read a barcode from a JPEG blob" do
+ result = ZBar::Image.from_jpeg(File.read("#{Path}/test.jpg")).process
+ assert_equal(result.size, 1)
+ end
+
+ should "read a barcode from a JPEG file" do
+ File.open("#{Path}/test.jpg") { |f|
+ result = ZBar::Image.from_jpeg(f).process
+ assert_equal(result.size, 1)
+ }
end
end
View
63 zbar.gemspec
@@ -0,0 +1,63 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{zbar}
+ s.version = "0.1.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Will Glynn"]
+ s.date = %q{2010-08-29}
+ s.description = %q{Ruby bindings for ZBar, a barcode recognition library. Uses FFI to interact with the underlying C library, but has no other dependencies.}
+ s.email = %q{will@willglynn.com}
+ s.extra_rdoc_files = [
+ "LICENSE",
+ "README.rdoc"
+ ]
+ s.files = [
+ ".document",
+ ".gitignore",
+ "LICENSE",
+ "README.rdoc",
+ "Rakefile",
+ "VERSION",
+ "lib/zbar.rb",
+ "lib/zbar/image.rb",
+ "lib/zbar/lib.rb",
+ "lib/zbar/processor.rb",
+ "lib/zbar/symbol.rb",
+ "test/helper.rb",
+ "test/test.jpg",
+ "test/test.pgm",
+ "test/test.png",
+ "test/test_zbar.rb"
+ ]
+ s.homepage = %q{http://github.com/delta407/ruby-zbar}
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.6}
+ s.summary = %q{Ruby bindings for ZBar, a barcode recognition library}
+ s.test_files = [
+ "test/helper.rb",
+ "test/test_zbar.rb"
+ ]
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
+ s.add_runtime_dependency(%q<ffi>, [">= 0"])
+ else
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
+ s.add_dependency(%q<ffi>, [">= 0"])
+ end
+ else
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
+ s.add_dependency(%q<ffi>, [">= 0"])
+ end
+end
+

0 comments on commit fae4c3f

Please sign in to comment.
Something went wrong with that request. Please try again.