pdfinfo 1.2.0 → 1.3.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.
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