tiff 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|