comicbook 0.2.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0e1f16bc54c8073078765c5edd09bf2e886ee042a859e6f6ec7c601876fc010
4
- data.tar.gz: c7fbaa136dc9705ef955dd272c83a22864fc541200b9b435a34c850afedea34b
3
+ metadata.gz: 8e9032e12765aa91a64364150391122a0324ee9f89b9987391f4708d1db7b0d6
4
+ data.tar.gz: de1dc67b4d78eb8ecf739b02381d78cf2a39518a6b12dd6bbef521dd77e1b8dd
5
5
  SHA512:
6
- metadata.gz: 3ad1f9e9e8d24c143e6dfb22a293e62d758ce6a865e19f7bf324e908e339f7557d303e873d0cc5448378e190214ce9e8c18bed3743480203fa708e9f313fb3a9
7
- data.tar.gz: 8f7e8f708e4ff746e132d94a6a6fec8406196cabe246c2769ae1600684a2dfb7c2fe4d7dab32cd5f9c70e22967e35d942b2a649cc1784bea2734702c04036ac6
6
+ metadata.gz: 81a45ca97a476926a1d8216043069840e27f42c461175ebf0326cd0698e4cc0ea78f8627b3b932e23266ccc56081786eaec210cbbc07d1e5e452b11bcef94f4e
7
+ data.tar.gz: f073563d4dcc6253d140e6818f2c9e273dfdc8e6ce053a729b6cddddb115ef436d6afa80d3e4357cf2ff187112bb69f444032674a5a96db841a7df0664ff0f7e
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 4.0.0
1
+ 4.0.2
data/Brewfile ADDED
@@ -0,0 +1 @@
1
+ brew 'vips'
data/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.2.0] - 2025-01-17
3
+ ## [0.3.0] - 2026-04-14
4
+
5
+ ### Added
6
+
7
+ - PDF to comic book conversion (PDF → .cb → .cbz/.cb7/.cbt) with configurable DPI (default 300)
8
+ - PDF adapter with extract and pages support using libvips
9
+ - `ruby-vips` gem dependency (lazy-loaded, only required for PDF features)
10
+ - `ComicBook#info` method to read ComicInfo.xml metadata from archives and folders
11
+ - `comicinfo` gem dependency for ComicInfo.xml parsing
12
+ - `ComicBook::Info` alias for `ComicInfo::Issue`
13
+ - `comicbook info` CLI subcommand with `--format` (verbose, terse, json, yaml), `--only`, and `--except` options
14
+ - `--dpi` CLI option for PDF extraction
15
+ - `--version`/`-v` CLI flag
16
+ - Install `libvips-dev` in CI workflow
17
+
18
+ ## [0.2.0] - 2025-11-17
4
19
 
5
20
  ### Added
6
21
 
data/README.md CHANGED
@@ -7,6 +7,8 @@ A Ruby library and CLI tool for managing comic books archives.
7
7
 
8
8
  **`archive`** — to create a `.cb*` file (default: `.cbz`).
9
9
 
10
+ **`info`** — to read ComicInfo.xml metadata from a `.cb*` file.
11
+
10
12
  Currently supported formats for `archive` and `extract`:
11
13
  - CB7 — [7zip](https://en.wikipedia.org/wiki/7-Zip)
12
14
  - CBT — [Tar](https://en.wikipedia.org/wiki/Tar_(computing))
@@ -14,6 +16,7 @@ Currently supported formats for `archive` and `extract`:
14
16
 
15
17
  Currently supported formats for `extract` only:
16
18
  - **CBR** — [RAR](https://en.wikipedia.org/wiki/WinRAR) is proprietary without an open source implementation license. Extracting support is provided using vendored [`unar`](https://theunarchiver.com/command-line) binaries because a large number of comic books are archived in .cbr/.rar format. No support for creating `.cbr` files will ever be added until RAR is open source (or reverse engineered).
19
+ - **PDF** — Extract PDF pages as images using [libvips](https://www.libvips.org/). Requires libvips installed on the system.
17
20
 
18
21
  Planned formats for `extract` only:
19
22
  - **CBA** — [ACE](https://en.wikipedia.org/wiki/WinAce) is both proprietary and very old/outdated/unsupported. ACE extracting support is provided for historical posterity and completeness.
@@ -46,12 +49,29 @@ comicbook extract path/to/archive.cbz --to path/to/output
46
49
  # Extract only image files (exclude metadata like ComicInfo.xml)
47
50
  comicbook extract path/to/archive.cbz --images-only
48
51
 
52
+ # Extract PDF at custom DPI (default: 300)
53
+ comicbook extract path/to/comic.pdf --dpi 600
54
+
49
55
  # Create a comic book archive from a folder
50
56
  comicbook archive path/to/folder
51
57
 
52
58
  # Create archive at a specific destination
53
59
  comicbook archive path/to/folder --to path/to/output.cbz
54
60
 
61
+ # Show ComicInfo.xml metadata
62
+ comicbook info path/to/archive.cbz
63
+
64
+ # Show info in different formats
65
+ comicbook info path/to/archive.cbz --format json
66
+ comicbook info path/to/archive.cbz --format yaml
67
+ comicbook info path/to/archive.cbz --format terse
68
+
69
+ # Show only specific fields
70
+ comicbook info path/to/archive.cbz --only title,writer,publisher
71
+
72
+ # Show all fields except specific ones
73
+ comicbook info path/to/archive.cbz --except summary,review
74
+
55
75
  # Show help
56
76
  comicbook --help
57
77
  ```
@@ -77,10 +97,23 @@ ComicBook.new('path/to/folder').archive to: 'path/to/output.cbz'
77
97
  # Get pages from an archive
78
98
  comic = ComicBook.new 'path/to/archive.cbz'
79
99
  comic.pages # => [#<ComicBook::Page>, ...]
100
+
101
+ # Read ComicInfo.xml metadata
102
+ comic = ComicBook.new 'path/to/archive.cbz'
103
+ comic.info # => #<ComicInfo::Issue> or nil
104
+ comic.info.title # => "The Amazing Spider-Man"
105
+ comic.info.writer # => "Dan Slott, Christos Gage"
80
106
  ```
81
107
 
82
108
  ## Development
83
109
 
110
+ PDF support requires [libvips](https://www.libvips.org/). Install it with `brew bundle` (uses the included `Brewfile`) or manually:
111
+
112
+ ```sh
113
+ brew install vips # macOS
114
+ sudo apt install libvips-dev # Linux
115
+ ```
116
+
84
117
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
85
118
 
86
119
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
@@ -19,6 +19,10 @@ class ComicBook
19
19
  raise NotImplementedError, "#{self.class} must implement #pages"
20
20
  end
21
21
 
22
+ def info
23
+ raise NotImplementedError, "#{self.class} must implement #info"
24
+ end
25
+
22
26
  private
23
27
 
24
28
  attr_reader :path
data/lib/comic_book/cb.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'comicinfo'
1
2
  require_relative 'adapter'
2
3
  require_relative 'cb/archiver'
3
4
  require_relative 'cb/extractor'
@@ -8,6 +9,13 @@ class ComicBook
8
9
  Archiver.new(path).archive options
9
10
  end
10
11
 
12
+ def info
13
+ xml_path = File.join path, 'ComicInfo.xml'
14
+ return nil unless File.exist? xml_path
15
+
16
+ ComicInfo.load xml_path
17
+ end
18
+
11
19
  def extract _options = {}
12
20
  Extractor.new(path).extract
13
21
  end
@@ -1,4 +1,5 @@
1
1
  require 'seven_zip_ruby'
2
+ require 'comicinfo'
2
3
  require_relative 'adapter'
3
4
  require_relative 'cb7/archiver'
4
5
  require_relative 'cb7/extractor'
@@ -13,6 +14,21 @@ class ComicBook
13
14
  Extractor.new(path).extract options
14
15
  end
15
16
 
17
+ def info
18
+ xml = nil
19
+
20
+ File.open(path, 'rb') do |file|
21
+ SevenZipRuby::Reader.open(file) do |reader|
22
+ entry = reader.entries.find { it.path == 'ComicInfo.xml' }
23
+ xml = reader.extract_data(entry.index) if entry
24
+ end
25
+ end
26
+
27
+ return nil unless xml
28
+
29
+ ComicInfo.load xml
30
+ end
31
+
16
32
  def pages
17
33
  entries = []
18
34
 
@@ -10,6 +10,10 @@ class ComicBook
10
10
  raise Error, 'CBA extraction not yet implemented'
11
11
  end
12
12
 
13
+ def info
14
+ raise Error, 'CBA info not yet implemented'
15
+ end
16
+
13
17
  def pages
14
18
  raise Error, 'CBA page listing not yet implemented'
15
19
  end
@@ -1,4 +1,5 @@
1
1
  require 'shellwords'
2
+ require 'comicinfo'
2
3
  require_relative 'adapter'
3
4
  require_relative 'cbr/extractor'
4
5
 
@@ -12,6 +13,19 @@ class ComicBook
12
13
  Extractor.new(path).extract options
13
14
  end
14
15
 
16
+ def info
17
+ entries = CLIHelpers.lsar_list path
18
+ return nil unless entries.include? 'ComicInfo.xml'
19
+
20
+ Dir.mktmpdir do |temp_dir|
21
+ CLIHelpers.unar_extract path, temp_dir
22
+ xml_path = File.join temp_dir, 'ComicInfo.xml'
23
+ return nil unless File.exist? xml_path
24
+
25
+ ComicInfo.load xml_path
26
+ end
27
+ end
28
+
15
29
  def pages
16
30
  CLIHelpers.lsar_list(path)
17
31
  .select { image_file? it }
@@ -1,4 +1,5 @@
1
1
  require 'rubygems/package'
2
+ require 'comicinfo'
2
3
  require_relative 'adapter'
3
4
  require_relative 'cbt/archiver'
4
5
  require_relative 'cbt/extractor'
@@ -13,6 +14,25 @@ class ComicBook
13
14
  Extractor.new(path).extract options
14
15
  end
15
16
 
17
+ def info
18
+ xml = nil
19
+
20
+ File.open(path, 'rb') do |file|
21
+ Gem::Package::TarReader.new(file) do |reader|
22
+ reader.each do |entry|
23
+ next unless entry.full_name == 'ComicInfo.xml'
24
+
25
+ xml = entry.read
26
+ break
27
+ end
28
+ end
29
+ end
30
+
31
+ return nil unless xml
32
+
33
+ ComicInfo.load xml
34
+ end
35
+
16
36
  def pages
17
37
  entries = []
18
38
 
@@ -1,4 +1,5 @@
1
1
  require 'zip'
2
+ require 'comicinfo'
2
3
  require_relative 'adapter'
3
4
  require_relative 'cbz/archiver'
4
5
  require_relative 'cbz/extractor'
@@ -13,6 +14,19 @@ class ComicBook
13
14
  Extractor.new(path).extract options
14
15
  end
15
16
 
17
+ def info
18
+ xml = nil
19
+
20
+ Zip::File.open(path) do |zipfile|
21
+ entry = zipfile.find_entry('ComicInfo.xml')
22
+ xml = entry&.get_input_stream&.read
23
+ end
24
+
25
+ return nil unless xml
26
+
27
+ ComicInfo.load xml
28
+ end
29
+
16
30
  def pages
17
31
  entries = []
18
32
 
@@ -1,11 +1,25 @@
1
1
  require 'optparse'
2
+ require 'json'
3
+ require 'yaml'
2
4
 
3
5
  class ComicBook
4
6
  class CLI
5
- EXTRACT_FORMATS = %w[.cb .cb7 .cbr .cbt .cbz].freeze
7
+ EXTRACT_FORMATS = %w[.cb .cb7 .cbr .cbt .cbz .pdf].freeze
6
8
  ARCHIVE_FORMATS = %w[.cb .cb7 .cbt .cbz].freeze
9
+ INFO_FORMATS = %w[.cb .cb7 .cbr .cbt .cbz].freeze
7
10
  UNSUPPORTED_FORMATS = %w[.cba].freeze
8
11
 
12
+ # Fields that duplicate others in a less useful form
13
+ REDUNDANT_FIELDS = %i[
14
+ genre genres_raw_data
15
+ characters_raw_data
16
+ teams_raw_data
17
+ locations_raw_data
18
+ story_arc story_arcs_raw_data
19
+ story_arc_number story_arc_numbers_raw_data
20
+ web_urls
21
+ ].freeze
22
+
9
23
  def self.start argv
10
24
  new.start Array(argv)
11
25
  end
@@ -13,6 +27,11 @@ class ComicBook
13
27
  def start argv
14
28
  argv = Array argv
15
29
 
30
+ if argv.include?('-v') || argv.include?('--version')
31
+ puts ComicBook::VERSION
32
+ return
33
+ end
34
+
16
35
  if argv.empty? || argv.include?('-h') || argv.include?('--help')
17
36
  show_help
18
37
  return
@@ -22,6 +41,7 @@ class ComicBook
22
41
 
23
42
  when 'extract' then extract(argv)
24
43
  when 'archive' then archive(argv)
44
+ when 'info' then info(argv)
25
45
  else
26
46
  puts "Unknown command: #{command}"
27
47
  show_help
@@ -36,20 +56,24 @@ class ComicBook
36
56
 
37
57
  def show_help
38
58
  puts <<~HELP
39
- ComicBook CLI for .cb, .cb7, .cbt, .cbz, .cbr files
59
+ ComicBook CLI for .cb, .cb7, .cbt, .cbz, .cbr, .pdf files
60
+ Version #{ComicBook::VERSION}
40
61
 
41
62
  Usage:
42
63
  comicbook extract <file> [options]
43
64
  comicbook archive <folder> [options]
44
- comicbook -h, --help
65
+ comicbook info <file> [options]
66
+ comicbook --help
45
67
 
46
68
  Commands:
47
- extract Extract comic book archive (.cb7, .cbr, .cbt, .cbz, .cb)
69
+ extract Extract comic book archive (.cb7, .cbr, .cbt, .cbz, .cb, .pdf)
48
70
  archive Create comic book archive (.cb7, .cbt, .cbz, .cb)
71
+ info Show ComicInfo.xml metadata (.cb7, .cbr, .cbt, .cbz, .cb)
49
72
 
50
73
  Extract Options:
51
74
  --from Source file path (optional, first arg is default)
52
75
  --to Destination path
76
+ --dpi DPI PDF render resolution (default: 300)
53
77
  --images-only Extract only image files (exclude metadata, text, etc.)
54
78
  --delete-original Delete source archive after extraction
55
79
 
@@ -58,20 +82,29 @@ class ComicBook
58
82
  --to Destination path (extension determines format, default .cbz)
59
83
  --delete-original Delete source folder after archiving
60
84
 
85
+ Info Options:
86
+ --from Source file path (optional, first arg is default)
87
+ --format FORMAT Output format: verbose (default), terse, json, yaml
88
+ --only FIELDS Only show these fields (comma-separated)
89
+ --except FIELDS Show all fields except these (comma-separated)
90
+
61
91
  General Options:
62
- --help, -h Show this help
92
+ --help, -h Show this help
93
+ --version, -v Show version
63
94
  HELP
64
95
  end
65
96
 
66
97
  def extract argv
67
98
  from_path = nil
68
99
  to_path = nil
100
+ dpi = nil
69
101
  images_only = false
70
102
  delete_original = false
71
103
 
72
104
  parser = OptionParser.new do |opts|
73
105
  opts.on('--from PATH', 'Source file path') { from_path = it }
74
106
  opts.on('--to PATH', 'Destination path') { to_path = it }
107
+ opts.on('--dpi DPI', Integer, 'PDF render DPI') { dpi = it }
75
108
  opts.on('--images-only', 'Extract only images') { images_only = true }
76
109
  opts.on('--delete-original', 'Delete source after extract') { delete_original = true }
77
110
  end
@@ -85,6 +118,7 @@ class ComicBook
85
118
 
86
119
  options.delete(:images_only) unless images_only
87
120
  options.delete(:delete_original) unless delete_original
121
+ options[:dpi] = dpi if dpi
88
122
 
89
123
  ComicBook.extract from_path, options
90
124
 
@@ -118,6 +152,88 @@ class ComicBook
118
152
  puts "Archived #{from_path}#{" to #{to_path}" if to_path}"
119
153
  end
120
154
 
155
+ def info argv
156
+ from_path = nil
157
+ output_format = 'verbose'
158
+ only_fields = nil
159
+ except_fields = nil
160
+
161
+ parser = OptionParser.new do |opts|
162
+ opts.on('--from PATH', 'Source file path') { from_path = it }
163
+ opts.on('--format FORMAT', 'Output format') { output_format = it }
164
+ opts.on('--only FIELDS', 'Only these fields') { only_fields = it.split(',').map(&:strip) }
165
+ opts.on('--except FIELDS', 'Exclude these fields') { except_fields = it.split(',').map(&:strip) }
166
+ end
167
+
168
+ remaining = parser.parse argv
169
+ from_path ||= remaining.first
170
+
171
+ validate_info_args! from_path, output_format
172
+
173
+ comic_info = ComicBook.new(from_path).info
174
+
175
+ raise ComicBook::Error, "No ComicInfo.xml found in #{from_path}" unless comic_info
176
+
177
+ data = comic_info.to_h
178
+ data = filter_fields data, only_fields, except_fields
179
+
180
+ puts format_info(data, output_format)
181
+ end
182
+
183
+ def validate_info_args! from_path, output_format
184
+ raise ComicBook::Error, 'Source file required' unless from_path
185
+ raise ComicBook::Error, "Source file not found: #{from_path}" unless File.exist?(from_path)
186
+
187
+ valid_formats = %w[verbose terse json yaml]
188
+ return if valid_formats.include? output_format
189
+
190
+ raise ComicBook::Error, "Invalid format: #{output_format} (valid: #{valid_formats.join(', ')})"
191
+ end
192
+
193
+ def filter_fields data, only_fields, except_fields
194
+ if only_fields
195
+ data.slice(*only_fields.map(&:to_sym))
196
+ elsif except_fields
197
+ data.except(*except_fields.map(&:to_sym))
198
+ else
199
+ data
200
+ end
201
+ end
202
+
203
+ def format_info data, output_format
204
+ data = data.compact
205
+
206
+ {
207
+ 'json' => format_json(data),
208
+ 'yaml' => format_yaml(data),
209
+ 'terse' => format_terse(data),
210
+ 'verbose' => format_verbose(data)
211
+ }[output_format]
212
+ end
213
+
214
+ def format_json data
215
+ JSON.generate(data.transform_keys(&:to_s))
216
+ end
217
+
218
+ def format_yaml data
219
+ data.transform_keys(&:to_s).to_yaml.delete_prefix("---\n")
220
+ end
221
+
222
+ def clean_for_display data
223
+ data.except(*REDUNDANT_FIELDS)
224
+ end
225
+
226
+ def format_terse data
227
+ clean_for_display(data).map { |key, value| "#{key}=#{value}" }.join(' | ')
228
+ end
229
+
230
+ def format_verbose data
231
+ data = clean_for_display(data)
232
+ max_key_length = data.keys.map { it.to_s.length }.max || 0
233
+
234
+ data.map { |key, value| "#{key.to_s.ljust max_key_length} #{value}" }.join("\n")
235
+ end
236
+
121
237
  def validate_extract_args! from_path, to_path
122
238
  # from
123
239
  raise ComicBook::Error, 'Source file required' unless from_path
@@ -21,7 +21,7 @@ class ComicBook
21
21
  def check_linux_dependency! name
22
22
  return if system("which #{name} > /dev/null 2>&1")
23
23
 
24
- raise Error, "#{name} is not installed. Install it with: sudo apt-get install unar"
24
+ raise Error, "#{name} is not installed. Install with: sudo apt-get install unar (Ubuntu/Debian)"
25
25
  end
26
26
 
27
27
  def lsar_list archive_path
@@ -0,0 +1,75 @@
1
+ class ComicBook
2
+ class PDF < Adapter
3
+ class Extractor
4
+ DEFAULT_DPI = 300
5
+
6
+ def initialize pdf_path
7
+ @pdf_path = File.expand_path pdf_path
8
+ end
9
+
10
+ def extract options = {}
11
+ extension = options.fetch :extension, :cb
12
+ delete_original = options.fetch :delete_original, false
13
+ @dpi = options.fetch :dpi, DEFAULT_DPI
14
+
15
+ destination = options[:to] || determine_extract_path(extension)
16
+ create_destination_directory destination
17
+ render_pages destination
18
+ cleanup_pdf_file if delete_original
19
+
20
+ destination
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :pdf_path, :dpi
26
+
27
+ def determine_extract_path extension
28
+ base_name = File.basename pdf_path, '.*'
29
+ dir_name = File.dirname pdf_path
30
+ pdf_name = base_name
31
+
32
+ pdf_name << ".#{extension}" if extension
33
+
34
+ full_path = File.join dir_name, pdf_name
35
+ File.expand_path full_path
36
+ end
37
+
38
+ def create_destination_directory destination
39
+ FileUtils.mkdir_p destination
40
+ end
41
+
42
+ def page_count
43
+ require 'vips'
44
+ image = Vips::Image.new_from_file pdf_path
45
+ image.get 'n-pages'
46
+ rescue StandardError => e
47
+ raise unless e.class.name == 'Vips::Error' # rubocop:disable Style/ClassEqualityComparison
48
+
49
+ 0
50
+ end
51
+
52
+ def render_pages destination
53
+ count = page_count
54
+ return if count.zero?
55
+
56
+ count.times do |page_number|
57
+ render_page destination, page_number
58
+ end
59
+ end
60
+
61
+ def render_page destination, page_number
62
+ require 'vips'
63
+ image = Vips::Image.new_from_file pdf_path, page: page_number, dpi: dpi
64
+ file_name = format('page_%03d.jpg', page_number + 1)
65
+ file_path = File.join destination, file_name
66
+
67
+ image.jpegsave file_path
68
+ end
69
+
70
+ def cleanup_pdf_file
71
+ File.delete pdf_path
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'adapter'
2
+ require_relative 'pdf/extractor'
3
+
4
+ class ComicBook
5
+ class PDF < Adapter
6
+ def info
7
+ nil
8
+ end
9
+
10
+ def archive _options = {}
11
+ raise Error, 'PDF archiving not supported (use extract to convert PDF pages to images)'
12
+ end
13
+
14
+ def extract options = {}
15
+ require_vips!
16
+ Extractor.new(path).extract options
17
+ end
18
+
19
+ def pages
20
+ require_vips!
21
+ image = Vips::Image.new_from_file path
22
+ count = image.get 'n-pages'
23
+
24
+ (1..count).map do |page_number|
25
+ name = format('page_%03d.jpg', page_number)
26
+
27
+ ComicBook::Page.new name, name
28
+ end
29
+ rescue StandardError => e
30
+ raise unless e.class.name == 'Vips::Error' # rubocop:disable Style/ClassEqualityComparison
31
+
32
+ []
33
+ end
34
+
35
+ private
36
+
37
+ def require_vips!
38
+ require 'vips'
39
+ rescue LoadError
40
+ raise ComicBook::Error,
41
+ 'PDF support requires libvips. ' \
42
+ 'Install with: brew install vips (macOS) or apt install libvips-dev (Linux)'
43
+ end
44
+ end
45
+ end
@@ -1,3 +1,3 @@
1
1
  class ComicBook
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
data/lib/comicbook.rb CHANGED
@@ -6,11 +6,14 @@ require_relative 'comic_book/cba'
6
6
  require_relative 'comic_book/cbr'
7
7
  require_relative 'comic_book/cbt'
8
8
  require_relative 'comic_book/cbz'
9
+ require_relative 'comic_book/pdf'
9
10
  require_relative 'comic_book/cli_helpers'
10
11
 
11
12
  class ComicBook
12
13
  class Error < StandardError; end
13
14
 
15
+ Info = ComicInfo::Issue
16
+
14
17
  IMAGE_EXTENSIONS = %w[.jpg .jpeg .png .gif .bmp .webp].freeze
15
18
  IMAGE_GLOB_PATTERN = '*.{jpg,jpeg,png,gif,bmp,webp}'.freeze
16
19
 
@@ -41,6 +44,12 @@ class ComicBook
41
44
  end
42
45
  end
43
46
 
47
+ def info
48
+ return CB.new(path).info if type == :folder
49
+
50
+ adapter.info
51
+ end
52
+
44
53
  def archive options = {}
45
54
  raise Error, 'Cannot archive a file' unless %i[folder cb].include?(type)
46
55
 
@@ -77,6 +86,7 @@ class ComicBook
77
86
  when '.cbt' then :cbt
78
87
  when '.cbr' then :cbr
79
88
  when '.cba' then :cba
89
+ when '.pdf' then :pdf
80
90
  else
81
91
  raise Error, "Unsupported file type: #{File.extname(path)}"
82
92
  end
@@ -111,6 +121,7 @@ class ComicBook
111
121
  when :cbr then CBR.new path
112
122
  when :cbt then CBT.new path
113
123
  when :cbz then CBZ.new path
124
+ when :pdf then PDF.new path
114
125
  else
115
126
  raise Error, "No adapter available for type: #{type}"
116
127
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: comicbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Becker
@@ -9,6 +9,34 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: comicinfo
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby-vips
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.3'
12
40
  - !ruby/object:Gem::Dependency
13
41
  name: rubyzip
14
42
  requirement: !ruby/object:Gem::Requirement
@@ -49,6 +77,7 @@ extra_rdoc_files: []
49
77
  files:
50
78
  - ".ruby-version"
51
79
  - AGENT.md
80
+ - Brewfile
52
81
  - CHANGELOG.md
53
82
  - CODE_OF_CONDUCT.md
54
83
  - LICENSE.md
@@ -75,6 +104,8 @@ files:
75
104
  - lib/comic_book/cli.rb
76
105
  - lib/comic_book/cli_helpers.rb
77
106
  - lib/comic_book/page.rb
107
+ - lib/comic_book/pdf.rb
108
+ - lib/comic_book/pdf/extractor.rb
78
109
  - lib/comic_book/vendor/linux/MPL-License-for-lsar-and-unar.txt
79
110
  - lib/comic_book/vendor/macos/MPL-License-for-lsar-and-unar.txt
80
111
  - lib/comic_book/vendor/macos/lsar
@@ -95,6 +126,10 @@ metadata:
95
126
  source_code_uri: https://github.com/veganstraightedge/comicbook
96
127
  changelog_uri: https://github.com/veganstraightedge/comicbook/blob/main/CHANGELOG.md
97
128
  rubygems_mfa_required: 'true'
129
+ post_install_message: |
130
+ PDF support requires libvips:
131
+ macOS: brew install vips
132
+ Linux: sudo apt install libvips-dev
98
133
  rdoc_options: []
99
134
  require_paths:
100
135
  - lib
@@ -109,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
144
  - !ruby/object:Gem::Version
110
145
  version: '0'
111
146
  requirements: []
112
- rubygems_version: 4.0.3
147
+ rubygems_version: 4.0.6
113
148
  specification_version: 4
114
149
  summary: Ruby interface for working with comic book archive files
115
150
  test_files: []