pdfinfo 1.2.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +13 -5
  2. data/lib/pdfinfo.rb +112 -72
  3. metadata +11 -39
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9c599fc8f36c8f2eaad1d072b30b702d1abc9283
4
- data.tar.gz: 886c3593bd2bd6159ecca069c748265f14ad126c
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NGI3ZTIzYzMxYzEyNTkyYmQ2ZDEwNzBjMDg2NWYxOGFkNTk3MTM2YQ==
5
+ data.tar.gz: !binary |-
6
+ ZDQ1NzRmMjczZmMyNTI4YWEzNDcwZWUyOWYxYTNhZDAyNDI4NGRlZA==
5
7
  SHA512:
6
- metadata.gz: 901d3defb09ac65a5e1086e746f8a3d242efffc8e85b99159623f538a273d8b463b49002a95658a53f38ba0e94bb6df764726030111014cc8d1418c0bae40068
7
- data.tar.gz: 6547e5457cdd88dfbcf2506208ce8dc2c501431806e5c6dc342aab0de84aef3f508dc0a71c451cb2a39313440ec8b5f4ef41fd5bc5decc65b69e310f2ef8acc9
8
+ metadata.gz: !binary |-
9
+ ZTY5YWU4M2Q3Y2VhNzIxYTg3ZDZlNWY4OGRiZjZmMmMwMzcyZTJlMjU3ODA2
10
+ ZDBiYmRkNDMwNTgwNzgyY2EwNTYzZDNkNmY5MmZjNjIwZmI4OGNmMDMwYzU2
11
+ M2I1ZTBhMjgwOGM2MzkwMjA1MDVhYWEyOTM1MDM4N2Q4MjE5YjE=
12
+ data.tar.gz: !binary |-
13
+ MzVhZjE1MmQwYTNiYjM4NjY5MjY0NTE2NzA3MjRjOTE1MDc3YTdjMzdiYjEw
14
+ NWFlODJjMzI1MTY1MzA2ZDg0MTMzZmU1NGJjZGQ2ODEzNTEwODQyMTE3NWVh
15
+ YWJiZWY5YjhkNjUzODcyMDczNTExNzgwMWFhM2FhMjAyNDgzMzQ=
data/lib/pdfinfo.rb CHANGED
@@ -1,79 +1,62 @@
1
1
  require 'open3'
2
2
  require 'shellwords'
3
+ require 'date'
3
4
  require 'time'
5
+ %w(object_to_hash errors page).each {|f| require File.expand_path("../pdfinfo/#{f}", __FILE__)}
4
6
 
5
7
  class Pdfinfo
6
- DIMENSIONS_REGEXP = /([\d\.]+) x ([\d\.]+)/
8
+ include ObjectToHash
7
9
 
8
- class PdfinfoError < ::StandardError
9
- end
10
+ attr_reader :pages, :title, :subject, :keywords, :author, :creator,
11
+ :creation_date, :modified_date, :usage_rights, :producer,
12
+ :form, :page_count, :width, :height, :file_size, :pdf_version
10
13
 
11
- class CommandNotFound < PdfinfoError
12
- def initialize(command)
13
- super("Command Not Found - '#{command}'")
14
+ class << self
15
+ def pdfinfo_command
16
+ @pdfinfo_command || 'pdfinfo'
14
17
  end
15
- end
16
18
 
19
+ def pdfinfo_command=(cmd)
20
+ @pdfinfo_command = cmd
21
+ end
17
22
 
18
- attr_reader :title,
19
- :subject,
20
- :keywords,
21
- :author,
22
- :creator,
23
- :creation_date,
24
- :usage_rights,
25
- :producer,
26
- :form,
27
- :page_count,
28
- :width,
29
- :height,
30
- :file_size,
31
- :pdf_version
32
-
33
- def self.exec(file_path, opts = {})
34
- raise CommandNotFound, 'pdfinfo' unless pdfinfo_command?
35
- flags = []
36
- flags.concat(['-enc', opts.fetch(:encoding, 'UTF-8')])
37
- flags.concat(['-opw', opts[:owner_password]]) if opts[:owner_password]
38
- flags.concat(['-upw', opts[:user_password]]) if opts[:user_password]
23
+ # @return [Boolean]
24
+ def pdfinfo_command?
25
+ system("type #{pdfinfo_command} >/dev/null 2>&1")
26
+ end
39
27
 
40
- command = Shellwords.join([pdfinfo_command, *flags, file_path.to_s])
41
- stdout, status = Open3.capture2(command)
42
- stdout.encode('UTF-8', invalid: :replace, replace: '', undef: :replace)
28
+ attr_accessor :config_path
43
29
  end
44
30
 
45
- def self.pdfinfo_command
46
- @pdfinfo_command || 'pdfinfo'
47
- end
31
+ def initialize(source_path, opts = {})
32
+ @pages = []
48
33
 
49
- def self.pdfinfo_command=(cmd)
50
- @pdfinfo_command = cmd
51
- end
34
+ info_hash = parse_shell_response(exec(source_path, opts))
52
35
 
53
- def self.pdfinfo_command?
54
- system("type #{pdfinfo_command} >/dev/null 2>&1")
55
- end
36
+ info_hash.delete_if do |key, value|
37
+ @pages << Page.from_string(value) if key.match(/Page\s+\d+\ssize/)
38
+ end
56
39
 
57
- def initialize(source_path, opts = {})
58
- info_hash = parse_shell_response(Pdfinfo.exec(source_path, opts))
59
-
60
- @title = presence(info_hash['Title'])
61
- @subject = presence(info_hash['Subject'])
62
- @author = presence(info_hash['Author'])
63
- @creator = presence(info_hash['Creator'])
64
- @producer = presence(info_hash['Producer'])
65
- @tagged = !!(info_hash['Tagged'] =~ /yes/)
66
- @encrypted = !!(info_hash['Encrypted'] =~ /yes/)
67
- @page_count = info_hash['Pages'].to_i
68
- @file_size = info_hash['File size'].to_i
69
- @form = info_hash['Form']
70
- @pdf_version = info_hash['PDF version']
71
-
72
- @keywords = (info_hash['Keywords'] || '').split(/\s/)
73
- @creation_date = parse_time(info_hash['CreationDate'])
74
-
75
- raw_usage_rights = Hash[info_hash['Encrypted'].scan(/(\w+):(\w+)/)]
76
- booleanize_usage_right = lambda {|val| !(raw_usage_rights[val] == 'no') }
40
+ encrypted_val = info_hash.delete('Encrypted')
41
+
42
+ @title = presence(info_hash.delete('Title'))
43
+ @subject = presence(info_hash.delete('Subject'))
44
+ @author = presence(info_hash.delete('Author'))
45
+ @creator = presence(info_hash.delete('Creator'))
46
+ @producer = presence(info_hash.delete('Producer'))
47
+ @tagged = !!(info_hash.delete('Tagged') =~ /yes/)
48
+ @encrypted = !!(encrypted_val =~ /yes/)
49
+ @page_count = info_hash.delete('Pages').to_i
50
+ @file_size = info_hash.delete('File size').to_i
51
+ @form = info_hash.delete('Form')
52
+ @pdf_version = info_hash.delete('PDF version')
53
+ @optimized = !!(info_hash.delete('Optimized') =~ /yes/)
54
+ @keywords = (info_hash.delete('Keywords') || '').split(/\s/)
55
+ @creation_date = parse_time(info_hash.delete('CreationDate'))
56
+ @modified_date = parse_time(info_hash.delete('ModDate'))
57
+
58
+ raw_usage_rights = Hash[encrypted_val.scan(/(\w+):(\w+)/)]
59
+ booleanize_usage_right = lambda {|val| raw_usage_rights[val] != 'no' }
77
60
 
78
61
  @usage_rights = {}.tap do |ur|
79
62
  ur[:print] = booleanize_usage_right.call('print')
@@ -82,47 +65,104 @@ class Pdfinfo
82
65
  ur[:add_notes] = booleanize_usage_right.call('addNotes')
83
66
  end
84
67
 
85
- @width, @height = extract_page_dimensions(info_hash['Page size'])
68
+ # temporarily continue setting #width and #height on Pdfinfo object
69
+ # to maintain legacy behavior
70
+ @width = @pages[0].width
71
+ @height = @pages[0].height
86
72
  end
87
73
 
74
+ # @return [Boolean]
88
75
  def tagged?
89
76
  @tagged
90
77
  end
91
78
 
79
+ # @return [Boolean]
92
80
  def encrypted?
93
81
  @encrypted
94
82
  end
95
83
 
84
+ # @return [Boolean]
85
+ def optimized?
86
+ @optimized
87
+ end
88
+
96
89
  %w(print copy change).each do |ur|
90
+ # @return [Boolean]
97
91
  define_method("#{ur}able?") { @usage_rights[ur.to_sym] }
98
92
  end
99
93
  alias modifiable? changeable?
100
94
 
95
+ # @return [Boolean]
101
96
  def annotatable?
102
97
  @usage_rights[:add_notes]
103
98
  end
104
99
 
105
- def to_hash
106
- instance_variables.inject({}) {|h, var| h[var[1..-1].to_sym] = instance_variable_get(var); h }
107
- end
108
100
  private
101
+ # executes pdfinfo command with supplied options
102
+ # @param [String,Pathname] file_path
103
+ # @param [Hash] opts
104
+ # @return [String] output
105
+ def exec(file_path, opts = {})
106
+ validate_pdfinfo_command!
107
+
108
+ flags = build_options(opts)
109
+
110
+ command = [self.class.pdfinfo_command, *flags, file_path.to_s].shelljoin
111
+
112
+ stdout, status = Open3.capture2(command)
113
+ force_utf8_encoding(stdout)
114
+ end
115
+
116
+ # prepares array of flags to pass as command line options
117
+ # ---
118
+ # @todo: add option builder class
119
+ # +++
120
+ # @param [Hash] opts of options
121
+ # @option opts [String] :encoding ('UTF-8')
122
+ # @option opts [String] :owner_password
123
+ # @option opts [String] :user_password
124
+ # @return [Array<String>] array of flags
125
+ def build_options(opts = {})
126
+ flags = []
127
+ xpdfrc_path = opts.fetch(:config_path, self.class.config_path)
128
+
129
+ # these flag will always be part of the cli options. Values have defaults and can be overridden
130
+ flags.concat(['-f', opts.fetch(:first_page, 0)])
131
+ flags.concat(['-l', opts.fetch(:last_page, -1)])
132
+ flags.concat(['-enc', opts.fetch(:encoding, Encoding::UTF_8)])
133
+ # optional flags. if no value, the flag wont be added
134
+ flags.concat(['-cfg', xpdfrc_path]) if xpdfrc_path
135
+ flags.concat(['-opw', opts[:owner_password]]) if opts[:owner_password]
136
+ flags.concat(['-upw', opts[:user_password]]) if opts[:user_password]
137
+ flags.map(&:to_s)
138
+ end
139
+
140
+ # @param [String] str
141
+ # @return [String] UTF-8 encoded string
142
+ def force_utf8_encoding(str)
143
+ return str if str.valid_encoding?
144
+ str = str.encode(Encoding::UTF_16, invalid: :replace, undef: :replace, replace: '')
145
+ str.encode!(Encoding::UTF_8)
146
+ end
147
+
109
148
  def presence(val)
110
- (val.nil? || val.empty?) ? nil : val
149
+ (val.nil? || val.empty? ) ? nil : val
111
150
  end
112
151
 
113
152
  def parse_shell_response(response_str)
114
153
  Hash[response_str.split(/\n/).map {|kv| kv.split(/:/, 2).map(&:strip) }]
115
154
  end
116
155
 
117
- def extract_page_dimensions(str)
118
- return unless str
119
- str.match(DIMENSIONS_REGEXP).captures.map(&:to_f)
120
- end
121
-
122
156
  def parse_time(str)
123
- presence(str) ? Time.parse(str) : nil
157
+ return unless presence(str)
158
+ DateTime.strptime(str, '%a %b %e %H:%M:%S %Y')
124
159
  rescue ArgumentError => e
125
- warn(e.message)
126
160
  nil
127
161
  end
162
+
163
+ def validate_pdfinfo_command!
164
+ unless self.class.pdfinfo_command?
165
+ raise CommandNotFound, self.class.pdfinfo_command
166
+ end
167
+ end
128
168
  end
metadata CHANGED
@@ -1,83 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdfinfo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Venegas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-22 00:00:00.000000000 Z
11
+ date: 2014-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.6'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
- - !ruby/object:Gem::Dependency
28
- name: prawn
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
- - - "~>"
31
+ - - ~>
46
32
  - !ruby/object:Gem::Version
47
33
  version: 3.1.0
48
34
  type: :development
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
- - - "~>"
38
+ - - ~>
53
39
  - !ruby/object:Gem::Version
54
40
  version: 3.1.0
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: rake
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: coveralls
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
45
+ - - ! '>='
74
46
  - !ruby/object:Gem::Version
75
47
  version: '0'
76
48
  type: :development
77
49
  prerelease: false
78
50
  version_requirements: !ruby/object:Gem::Requirement
79
51
  requirements:
80
- - - ">="
52
+ - - ! '>='
81
53
  - !ruby/object:Gem::Version
82
54
  version: '0'
83
55
  description: Simple ruby wrapper around the pdfinfo executable
@@ -97,17 +69,17 @@ require_paths:
97
69
  - lib
98
70
  required_ruby_version: !ruby/object:Gem::Requirement
99
71
  requirements:
100
- - - ">="
72
+ - - ! '>='
101
73
  - !ruby/object:Gem::Version
102
74
  version: 1.9.3
103
75
  required_rubygems_version: !ruby/object:Gem::Requirement
104
76
  requirements:
105
- - - ">="
77
+ - - ! '>='
106
78
  - !ruby/object:Gem::Version
107
79
  version: '0'
108
80
  requirements: []
109
81
  rubyforge_project:
110
- rubygems_version: 2.4.4
82
+ rubygems_version: 2.2.2
111
83
  signing_key:
112
84
  specification_version: 4
113
85
  summary: Simple ruby wrapper around the pdfinfo executable