tiff 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +30 -0
- data/lib/tiff.rb +8 -0
- data/lib/tiff/bindings.rb +99 -0
- data/lib/tiff/image.rb +91 -0
- data/lib/tiff/tag.rb +71 -0
- metadata +72 -0
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Tiff
|
2
|
+
====
|
3
|
+
|
4
|
+
This is a simple wrapper around libtiff using FFI. It only implements a small
|
5
|
+
subset of libffi's features -- just enough to generate a TIFF image and read
|
6
|
+
back some of its data. If you're interested in adding features, you can always
|
7
|
+
send me a pull request (please include specs!).
|
8
|
+
|
9
|
+
Usage
|
10
|
+
=====
|
11
|
+
|
12
|
+
Tiff::Image.open "filename.tif", "w" do |tiff|
|
13
|
+
|
14
|
+
tiff.set_field :compression, :CCITTFAX4
|
15
|
+
# or: tiff.set_field :compression, :CCITTFAX3
|
16
|
+
|
17
|
+
tiff.set_field :photometric, :min_is_white
|
18
|
+
# or: tiff.set_field :photometric, :min_is_black
|
19
|
+
|
20
|
+
tiff.bits_per_sample = 1
|
21
|
+
tiff.width = 200
|
22
|
+
tiff.height = 40
|
23
|
+
|
24
|
+
tiff.data = raw_data
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
image = Tiff::Image.open "filename.tif", "r"
|
29
|
+
image.get_field :width # => 200
|
30
|
+
image.get_field :height # => 40
|
data/lib/tiff.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
module Tiff
|
2
|
+
module Bindings
|
3
|
+
|
4
|
+
extend FFI::Library
|
5
|
+
|
6
|
+
ffi_lib 'libtiff'
|
7
|
+
|
8
|
+
attach_function :open, :TIFFOpen,
|
9
|
+
[:string, :string], :pointer
|
10
|
+
|
11
|
+
attach_function :close, :TIFFClose,
|
12
|
+
[:pointer], :void
|
13
|
+
|
14
|
+
attach_function :set_field, :TIFFSetField,
|
15
|
+
[:pointer, :uint, :varargs], :int
|
16
|
+
|
17
|
+
attach_function :get_field, :TIFFGetField,
|
18
|
+
[:pointer, :uint, :varargs], :int
|
19
|
+
|
20
|
+
attach_function :write_raw_strip, :TIFFWriteRawStrip,
|
21
|
+
[:pointer, :uint, :pointer, :int], :int
|
22
|
+
|
23
|
+
@@tags = Hash.new do |hash, key|
|
24
|
+
raise KeyError, "Tag #{key.inspect} is unsupported. Available tags: #{hash.keys}"
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
# Returns a hash of tag names to Tag instances. See `lib/tiff/tags.rb`
|
30
|
+
# for a list of supported tags.
|
31
|
+
def tags
|
32
|
+
@@tags
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# Initializes the tags hash with the default set of supported tags. See
|
38
|
+
# `man libtiff` and `man TIFFGetField` for details.
|
39
|
+
|
40
|
+
tags[:artist] = Tag.new(:artist, 315, :string)
|
41
|
+
tags[:bad_fax_lines] = Tag.new(:bad_fax_lines, 326, :uint)
|
42
|
+
tags[:bits_per_sample] = Tag.new(:bits_per_sample, 258, :ushort)
|
43
|
+
tags[:clean_fax_data] = Tag.new(:clean_fax_data, 327, :ushort)
|
44
|
+
tags[:compression] = Tag.new(:compression, 259, :ushort, CCITTFAX3: 3, CCITTFAX4: 4)
|
45
|
+
tags[:consecutive_bad_fax_lines] = Tag.new(:consecutive_bad_fax_lines, 328, :uint)
|
46
|
+
tags[:copyright] = Tag.new(:copyright, 33432, :string)
|
47
|
+
tags[:data_type] = Tag.new(:data_type, 32996, :ushort)
|
48
|
+
tags[:date_time] = Tag.new(:date_time, 306, :string)
|
49
|
+
tags[:document_name] = Tag.new(:document_name, 269, :string)
|
50
|
+
tags[:fill_order] = Tag.new(:fill_order, 266, :ushort)
|
51
|
+
tags[:group3_options] = Tag.new(:group3_options, 292, :uint)
|
52
|
+
tags[:group4_options] = Tag.new(:group4_options, 293, :uint)
|
53
|
+
tags[:height] = Tag.new(:height, 257, :uint)
|
54
|
+
tags[:host_computer] = Tag.new(:host_computer, 316, :string)
|
55
|
+
tags[:image_depth] = Tag.new(:image_depth, 32997, :uint)
|
56
|
+
tags[:image_description] = Tag.new(:image_description, 270, :string)
|
57
|
+
tags[:ink_names] = Tag.new(:ink_names, 333, :string)
|
58
|
+
tags[:ink_set] = Tag.new(:ink_set, 332, :ushort)
|
59
|
+
tags[:make] = Tag.new(:make, 271, :string)
|
60
|
+
tags[:matteing] = Tag.new(:matteing, 32995, :ushort)
|
61
|
+
tags[:max_sample_value] = Tag.new(:max_sample_value, 281, :ushort)
|
62
|
+
tags[:min_sample_value] = Tag.new(:min_sample_value, 280, :ushort)
|
63
|
+
tags[:model] = Tag.new(:model, 272, :string)
|
64
|
+
tags[:orientation] = Tag.new(:orientation, 274, :ushort)
|
65
|
+
tags[:page_name] = Tag.new(:page_name, 285, :string)
|
66
|
+
tags[:photometric] = Tag.new(:photometric, 262, :ushort, min_is_white: 0, min_is_black: 1)
|
67
|
+
tags[:predictor] = Tag.new(:predictor, 317, :ushort)
|
68
|
+
tags[:resolution_unit] = Tag.new(:resolution_unit, 296, :ushort, none: 1, inch: 2, centimeter: 3)
|
69
|
+
tags[:rows_per_strip] = Tag.new(:rows_per_strip, 278, :uint)
|
70
|
+
tags[:s_max_sample_value] = Tag.new(:s_max_sample_value, 341, :double)
|
71
|
+
tags[:s_min_sample_value] = Tag.new(:s_min_sample_value, 340, :double)
|
72
|
+
tags[:sample_format] = Tag.new(:sample_format, 339, :ushort)
|
73
|
+
tags[:samples_per_pixel] = Tag.new(:samples_per_pixel, 277, :ushort)
|
74
|
+
tags[:software] = Tag.new(:software, 305, :string)
|
75
|
+
tags[:sub_file_type] = Tag.new(:sub_file_type, 255, :uint)
|
76
|
+
tags[:target_printer] = Tag.new(:target_printer, 337, :string)
|
77
|
+
tags[:tile_depth] = Tag.new(:tile_depth, 32998, :uint)
|
78
|
+
tags[:tile_length] = Tag.new(:tile_length, 323, :uint)
|
79
|
+
tags[:tile_width] = Tag.new(:tile_width, 322, :uint)
|
80
|
+
tags[:width] = Tag.new(:width, 256, :uint)
|
81
|
+
tags[:x_position] = Tag.new(:x_position, 286, :float)
|
82
|
+
tags[:x_resolution] = Tag.new(:x_resolution, 282, :float)
|
83
|
+
tags[:y_cb_cr_positioning] = Tag.new(:y_cb_cr_positioning, 531, :ushort)
|
84
|
+
tags[:y_position] = Tag.new(:y_position, 286, :float)
|
85
|
+
tags[:y_resolution] = Tag.new(:y_resolution, 283, :float)
|
86
|
+
|
87
|
+
# Array types are not yet supported.
|
88
|
+
#
|
89
|
+
# tags[:reference_black_white] = Tag.new(:reference_black_white, 532, :float*)
|
90
|
+
# tags[:sto_nits] = Tag.new(:sto_nits, 37439, :double*)
|
91
|
+
# tags[:strip_byte_counts] = Tag.new(:strip_byte_counts, 279, :uint*)
|
92
|
+
# tags[:strip_offsets] = Tag.new(:strip_offsets, 273, :uint*)
|
93
|
+
# tags[:tile_byte_counts] = Tag.new(:tile_byte_counts, 324, :uint*)
|
94
|
+
# tags[:tile_offsets] = Tag.new(:tile_offsets, 324, :uint*)
|
95
|
+
# tags[:white_point] = Tag.new(:white_point, 318, :float*)
|
96
|
+
# tags[:y_cb_cr_coefficients] = Tag.new(:y_cb_cr_coefficients, 529, :float*)
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
data/lib/tiff/image.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module Tiff
|
2
|
+
|
3
|
+
class Image
|
4
|
+
|
5
|
+
attr_reader :path, :mode
|
6
|
+
|
7
|
+
# The file descriptor. This is an FFI::Pointer to the opened TIFF file.
|
8
|
+
attr_reader :fd
|
9
|
+
|
10
|
+
def initialize(path, mode)
|
11
|
+
@path = path
|
12
|
+
@mode = mode
|
13
|
+
|
14
|
+
@fd = Bindings::open path, mode
|
15
|
+
end
|
16
|
+
|
17
|
+
def close
|
18
|
+
Bindings::close fd
|
19
|
+
end
|
20
|
+
|
21
|
+
# Writes raw data to the image.
|
22
|
+
def data=(data)
|
23
|
+
Bindings::write_raw_strip fd, 0, data, data.length
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets a field on the image, using the list of tags in
|
27
|
+
# `Tiff::Bindings.tags`
|
28
|
+
def set_field(name, value)
|
29
|
+
tag = Bindings::tags[name]
|
30
|
+
Bindings::set_field fd, tag.id, tag.type, tag.serialize(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Gets a field from the image, using the list of tags in #
|
34
|
+
# `Tiff::Bindings.tags`
|
35
|
+
def get_field(name)
|
36
|
+
tag = Bindings::tags[name]
|
37
|
+
pointer = FFI::MemoryPointer.new tag.type
|
38
|
+
result = Bindings::get_field fd, tag.id, :pointer, pointer
|
39
|
+
if result == 1
|
40
|
+
tag.deserialize pointer
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sets the image's width
|
47
|
+
def width=(width)
|
48
|
+
set_field :width, width
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets the image's height
|
52
|
+
def height=(height)
|
53
|
+
set_field :height, height
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sets the image's bits_per_sample
|
57
|
+
def bits_per_sample=(bits_per_sample)
|
58
|
+
set_field :bits_per_sample, bits_per_sample
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sets the image's compression
|
62
|
+
def compression=(compresion)
|
63
|
+
set_field :compression, compresion
|
64
|
+
end
|
65
|
+
|
66
|
+
# Sets the image's photometric
|
67
|
+
def photometric=(compresion)
|
68
|
+
set_field :photometric, compresion
|
69
|
+
end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
|
73
|
+
# Initializes a new image. If a block is provided, the image is yielded
|
74
|
+
# and automatically closed.
|
75
|
+
def open(path, mode)
|
76
|
+
new(path, mode).tap do |image|
|
77
|
+
if block_given?
|
78
|
+
begin
|
79
|
+
yield image
|
80
|
+
ensure
|
81
|
+
image.close
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/lib/tiff/tag.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Tiff
|
2
|
+
class Tag
|
3
|
+
|
4
|
+
# The name of this tag
|
5
|
+
attr_accessor :name
|
6
|
+
|
7
|
+
# The value for this tag as defined by `tiff.h`
|
8
|
+
attr_accessor :id
|
9
|
+
|
10
|
+
# The FFI type for this tag. See `man TIFFSetField` for the types of each
|
11
|
+
# field, and `https://github.com/ffi/ffi/wiki/Types` for it's corresponding
|
12
|
+
# value in ruby.
|
13
|
+
attr_accessor :type
|
14
|
+
|
15
|
+
# A hash of ruby to FFI values for serialization and deserialization.
|
16
|
+
attr_accessor :map
|
17
|
+
|
18
|
+
# Creates a new tag.
|
19
|
+
#
|
20
|
+
# - name: A rubyish name for the tag. Displayed to users for exceptions.
|
21
|
+
# - id: The value for this tag as defined by `tiff.h`
|
22
|
+
# - type: The FFI type of the tag
|
23
|
+
# - map: A hash of ruby to FFI values (optional)
|
24
|
+
#
|
25
|
+
# Examples:
|
26
|
+
#
|
27
|
+
# width = Tag.new(:width, 256, :uint)
|
28
|
+
# metric = Tag.new(:photometric, 262, :ushort, {
|
29
|
+
# min_is_white: 0,
|
30
|
+
# min_is_black: 1
|
31
|
+
# })
|
32
|
+
#
|
33
|
+
def initialize(name, id, type, map = nil)
|
34
|
+
@name = name
|
35
|
+
@id = id
|
36
|
+
@type = type
|
37
|
+
@map = map
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the value for serialization. If the tag does not have a map
|
41
|
+
# defined, this simply returns the value provided.
|
42
|
+
#
|
43
|
+
# If a map was provided for the tag, it will return the value for the
|
44
|
+
# mapping. If the mapping isn't found, it will raise an exception.
|
45
|
+
def serialize(value)
|
46
|
+
return value unless map
|
47
|
+
|
48
|
+
map.fetch value do
|
49
|
+
raise KeyError, "Tag #{name.inspect} does not support value #{value.inspect}. Defined values: #{map.keys}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the deserialized value from the provided FFI::MemoryPointer.
|
54
|
+
#
|
55
|
+
# If a map was provided for the tag, it will return the ruby value if it
|
56
|
+
# exists. If the mapping isn't found, it will return the raw value.
|
57
|
+
def deserialize(pointer)
|
58
|
+
case type
|
59
|
+
when :string
|
60
|
+
string = pointer.read_pointer()
|
61
|
+
return string.null? ? nil : string.read_string()
|
62
|
+
|
63
|
+
else
|
64
|
+
value = pointer.send :"read_#{type}"
|
65
|
+
value = map.invert[value] if map && map.has_value?(value)
|
66
|
+
value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tiff
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bernerd Schaefer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-25 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ffi
|
16
|
+
requirement: &70275682402260 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70275682402260
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70275682428940 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70275682428940
|
36
|
+
description: Ruby wrapper for libtiff with FFI
|
37
|
+
email:
|
38
|
+
- bj.schaefer@gmail.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- lib/tiff/bindings.rb
|
44
|
+
- lib/tiff/image.rb
|
45
|
+
- lib/tiff/tag.rb
|
46
|
+
- lib/tiff.rb
|
47
|
+
- README.md
|
48
|
+
homepage: ''
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.8.6
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Ruby wrapper for libtiff with FFI
|
72
|
+
test_files: []
|