pdf_outline_editor 0.1.0-java

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f4919cc4df58a6af4c74185a140a8845176b9afef34bed77558171361fbb299e
4
+ data.tar.gz: 3dd0368ac59022b051083b41b48dc48a21944e1d224d570574651d95298697a5
5
+ SHA512:
6
+ metadata.gz: 9e523afaa754c0b56534e34dd65688416d6e6b109770890f8bc19b0a7fff8ae586c828e9fe35a2245ea0164582ccb7e96d590a2afe3d8b260158cc27bfd5ad5e
7
+ data.tar.gz: 77fbe6d6d9d8afaf5bd4eb78aa987a857ef1690e99e6a72379eb487820526aa5e47779a25f30f3f3cd09ca63a3c1697517f0330e9053a79cd0505b0a3a445b84
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # `pdf_outline_editor`
2
+
3
+ [![Build Status](https://travis-ci.org/healthypackrat/pdf_outline_editor.svg?branch=master)](https://travis-ci.org/healthypackrat/pdf_outline_editor)
4
+
5
+ This gem provides a command to get/set PDF outlines from a JSON/YAML definition file.
6
+
7
+ ## Requirements
8
+
9
+ - JRuby
10
+
11
+ ## Installation
12
+
13
+ ```
14
+ $ jgem install pdf_outline_editor
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ First, generate outlines definition file by running `init` command:
20
+
21
+ ```
22
+ $ pdf_outline_editor init -f json > toc.json
23
+ ```
24
+
25
+ or, by running `dump` command with an existing pdf file:
26
+
27
+ ```
28
+ $ pdf_outline_editor dump -f json input.pdf > toc.json
29
+ ```
30
+
31
+ (Edit `toc.json` as you like...)
32
+
33
+ Then run `load` command to set outlines:
34
+
35
+ ```
36
+ $ pdf_outline_editor load input.pdf toc.json output.pdf
37
+ ```
38
+
39
+ ## Development
40
+
41
+ Run `bin/jruby-ng-server` in a new terminal window for faster loading.
42
+
43
+ Then run `bin/rspec` to run the specs.
44
+
45
+ ## Contributing
46
+
47
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/healthypackrat/pdf_outline_editor>.
48
+
49
+ ## License
50
+
51
+ The gem is available as open source under the terms of the [Apache License, Version 2.0](https://opensource.org/licenses/Apache-2.0).
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ require "pdf_outline_editor"
4
+
5
+ begin
6
+ runner = PdfOutlineEditor::Runner.new(ARGV)
7
+ runner.run
8
+ rescue PdfOutlineEditor::Error => error
9
+ warn error.message
10
+ exit 1
11
+ end
@@ -0,0 +1,17 @@
1
+ require 'java'
2
+
3
+ require_relative '../vendor/pdfbox/pdfbox-app-2.0.13.jar'
4
+
5
+ require 'pdf_outline_editor/version'
6
+
7
+ require 'pdf_outline_editor/dumper'
8
+ require 'pdf_outline_editor/loader'
9
+
10
+ require 'pdf_outline_editor/runner'
11
+ require 'pdf_outline_editor/commands/dump_command'
12
+ require 'pdf_outline_editor/commands/init_command'
13
+ require 'pdf_outline_editor/commands/load_command'
14
+
15
+ module PdfOutlineEditor
16
+ class Error < StandardError; end
17
+ end
@@ -0,0 +1,68 @@
1
+ require 'optparse'
2
+
3
+ module PdfOutlineEditor
4
+ module Commands
5
+ class DumpCommand
6
+ attr_reader :output_format, :input_pdf_path
7
+
8
+ def initialize(argv)
9
+ @parser = nil
10
+
11
+ @output_formats = [:json, :yaml]
12
+ @output_format = :json
13
+
14
+ @input_pdf_path = nil
15
+
16
+ parser.parse!(argv)
17
+
18
+ handle_args(argv)
19
+ end
20
+
21
+ def run
22
+ Dumper.open(@input_pdf_path) do |dumper|
23
+ outlines = dumper.dump
24
+
25
+ if outlines
26
+ puts send("convert_to_#{@output_format}", outlines)
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def parser
34
+ return @parser if @parser
35
+
36
+ @parser = OptionParser.new
37
+
38
+ @parser.banner = "Usage: #{File.basename($0)} dump [options] <input-pdf-path>"
39
+
40
+ desc = "Output format (default: #{@output_format}; one of #{@output_formats.join(', ')})"
41
+ @parser.on('-f', '--format=FORMAT', @output_formats, desc) do |value|
42
+ @output_format = value.to_sym
43
+ end
44
+
45
+ @parser
46
+ end
47
+
48
+ def handle_args(argv)
49
+ @input_pdf_path = argv.shift
50
+ raise Error, 'missing input pdf path' unless @input_pdf_path
51
+ end
52
+
53
+ def convert_to_json(outlines)
54
+ require 'json'
55
+
56
+ JSON.pretty_generate(outlines)
57
+ end
58
+
59
+ def convert_to_yaml(outlines)
60
+ require 'yaml'
61
+
62
+ YAML.dump(outlines)
63
+ end
64
+
65
+ Runner.register_command(:dump, self)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,70 @@
1
+ require 'optparse'
2
+
3
+ module PdfOutlineEditor
4
+ module Commands
5
+ class InitCommand
6
+ attr_reader :output_format
7
+
8
+ def initialize(argv)
9
+ @parser = nil
10
+
11
+ @output_formats = [:json, :yaml]
12
+ @output_format = :json
13
+
14
+ parser.parse!(argv)
15
+ end
16
+
17
+ def run
18
+ sample_outlines = [
19
+ {
20
+ 'title' => 'Chapter 1',
21
+ 'page' => 1,
22
+ 'children' => [
23
+ {
24
+ 'title' => 'Section 1.1',
25
+ 'page' => 2
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ 'title' => 'Chapter 2',
31
+ 'page' => 3
32
+ }
33
+ ]
34
+
35
+ puts send("convert_to_#{@output_format}", sample_outlines)
36
+ end
37
+
38
+ private
39
+
40
+ def parser
41
+ return @parser if @parser
42
+
43
+ @parser = OptionParser.new
44
+
45
+ @parser.banner = "Usage: #{File.basename($0)} init [options]"
46
+
47
+ desc = "Output format (default: #{@output_format}; one of #{@output_formats.join(', ')})"
48
+ @parser.on('-f', '--format=FORMAT', @output_formats, desc) do |value|
49
+ @output_format = value.to_sym
50
+ end
51
+
52
+ @parser
53
+ end
54
+
55
+ def convert_to_json(outlines)
56
+ require 'json'
57
+
58
+ JSON.pretty_generate(outlines)
59
+ end
60
+
61
+ def convert_to_yaml(outlines)
62
+ require 'yaml'
63
+
64
+ YAML.dump(outlines)
65
+ end
66
+
67
+ Runner.register_command(:init, self)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,80 @@
1
+ require 'optparse'
2
+
3
+ module PdfOutlineEditor
4
+ module Commands
5
+ class LoadCommand
6
+ def initialize(argv)
7
+ @parser = nil
8
+
9
+ @input_pdf_path = nil
10
+ @outlines = nil
11
+ @output_pdf_path = nil
12
+
13
+ parser.parse!(argv)
14
+
15
+ handle_args(argv)
16
+ end
17
+
18
+ def run
19
+ Loader.open(@input_pdf_path) do |loader|
20
+ loader.load(@outlines)
21
+ loader.save(@output_pdf_path)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def parser
28
+ return @parser if @parser
29
+
30
+ @parser = OptionParser.new
31
+
32
+ @parser.banner = "Usage: #{File.basename($0)} load <input-pdf-path> <input-outlines-path> <output-pdf-path>"
33
+
34
+ @parser
35
+ end
36
+
37
+ def handle_args(argv)
38
+ @input_pdf_path = argv.shift
39
+ raise Error, 'missing input pdf path' unless @input_pdf_path
40
+
41
+ input_outlines_path = argv.shift
42
+ raise Error, 'missing input outlines path' unless input_outlines_path
43
+ @outlines = parse_outlines(input_outlines_path)
44
+
45
+ @output_pdf_path = argv.shift
46
+ raise Error, 'missing output pdf path' unless @output_pdf_path
47
+ end
48
+
49
+ def parse_outlines(path)
50
+ ext = File.extname(path)[1..-1]
51
+
52
+ method_name = "parse_#{ext}_outlines"
53
+
54
+ if respond_to?(method_name, true)
55
+ send(method_name, path)
56
+ else
57
+ raise Error, "unknown extension: #{ext}"
58
+ end
59
+ end
60
+
61
+ def parse_json_outlines(path)
62
+ require 'json'
63
+
64
+ JSON.parse(File.read(path))
65
+ end
66
+
67
+ def parse_yaml_outlines(path)
68
+ require 'yaml'
69
+
70
+ YAML.load_file(path)
71
+ end
72
+
73
+ def parse_yml_outlines(path)
74
+ parse_yaml_outlines(path)
75
+ end
76
+
77
+ Runner.register_command(:load, self)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,74 @@
1
+ module PdfOutlineEditor
2
+ class Dumper
3
+ java_import org.apache.pdfbox.pdmodel.PDDocument
4
+
5
+ JavaFile = java.io.File
6
+
7
+ def self.open(input_pdf_path)
8
+ dumper = new(input_pdf_path)
9
+
10
+ begin
11
+ yield dumper
12
+ ensure
13
+ dumper.close
14
+ end
15
+ end
16
+
17
+ attr_reader :closed
18
+
19
+ def initialize(input_pdf_path)
20
+ begin
21
+ @doc = PDDocument.load(JavaFile.new(input_pdf_path))
22
+ rescue
23
+ raise Error, $!.message
24
+ end
25
+
26
+ @pages = @doc.pages
27
+
28
+ @closed = false
29
+ end
30
+
31
+ def dump
32
+ root_outline = @doc.document_catalog.document_outline
33
+
34
+ if root_outline
35
+ traverse(root_outline)
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def close
42
+ unless @closed
43
+ @doc.close
44
+ @closed = true
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def traverse(outline)
51
+ outline_items = []
52
+
53
+ current = outline.first_child
54
+
55
+ while current
56
+ outline_item = {}
57
+
58
+ outline_item['title'] = current.title
59
+
60
+ page = current.find_destination_page(@doc)
61
+ outline_item['page'] = @pages.index_of(page) + 1
62
+
63
+ children = traverse(current)
64
+ outline_item['children'] = children unless children.empty?
65
+
66
+ outline_items << outline_item
67
+
68
+ current = current.next_sibling
69
+ end
70
+
71
+ outline_items
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,85 @@
1
+ module PdfOutlineEditor
2
+ class Loader
3
+ java_import org.apache.pdfbox.pdmodel.PDDocument
4
+ java_import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination
5
+ java_import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline
6
+ java_import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem
7
+
8
+ JavaFile = java.io.File
9
+
10
+ def self.open(input_pdf_path)
11
+ loader = new(input_pdf_path)
12
+
13
+ begin
14
+ yield loader
15
+ ensure
16
+ loader.close
17
+ end
18
+ end
19
+
20
+ attr_reader :closed
21
+
22
+ def initialize(input_pdf_path)
23
+ begin
24
+ @doc = PDDocument.load(JavaFile.new(input_pdf_path))
25
+ rescue
26
+ raise Error, $!.message
27
+ end
28
+
29
+ @pages = @doc.pages.to_a
30
+
31
+ @closed = false
32
+ end
33
+
34
+ def load(entries)
35
+ root_outline = PDDocumentOutline.new
36
+
37
+ @doc.document_catalog.document_outline = root_outline
38
+
39
+ entries.each do |entry|
40
+ set_outline(root_outline, entry)
41
+ end
42
+ end
43
+
44
+ def save(output_pdf_path)
45
+ @doc.save(output_pdf_path)
46
+ end
47
+
48
+ def close
49
+ unless @closed
50
+ @doc.close
51
+ @closed = true
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def set_outline(parent_outline, entry)
58
+ page_number = entry.fetch('page')
59
+
60
+ max_page_number = @pages.size
61
+
62
+ unless (1..max_page_number).include?(page_number)
63
+ raise Error, "page number must be between 1 and #{max_page_number}: got #{page_number}"
64
+ end
65
+
66
+ page = @pages[page_number - 1]
67
+
68
+ dest = PDPageXYZDestination.new
69
+ dest.page = page
70
+ dest.top = page.bbox.upper_right_y
71
+ dest.left = 0
72
+ dest.zoom = -1
73
+
74
+ outline = PDOutlineItem.new
75
+ outline.destination = dest
76
+ outline.title = entry.fetch('title')
77
+
78
+ entry.fetch('children', []).each do |child|
79
+ set_outline(outline, child)
80
+ end
81
+
82
+ parent_outline.add_last(outline)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,56 @@
1
+ require 'optparse'
2
+
3
+ module PdfOutlineEditor
4
+ class Runner
5
+ class << self
6
+ _known_commands = {}
7
+
8
+ define_method :known_commands do
9
+ _known_commands
10
+ end
11
+
12
+ define_method :register_command do |key, klass|
13
+ _known_commands[key] = klass
14
+ end
15
+ end
16
+
17
+ def initialize(argv)
18
+ @argv = argv
19
+
20
+ @parser = nil
21
+
22
+ parser.order!(@argv)
23
+ end
24
+
25
+ def run
26
+ if @argv.empty?
27
+ raise Error, "missing a command: available commands are #{Runner.known_commands.keys.sort.join(', ')}"
28
+ end
29
+
30
+ command_name = @argv.shift
31
+
32
+ command_class = Runner.known_commands[command_name.to_sym]
33
+
34
+ if command_class
35
+ command = command_class.new(@argv)
36
+ command.run
37
+ else
38
+ raise Error, "unknown command: #{command_name}"
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def parser
45
+ return @parser if @parser
46
+
47
+ @parser = OptionParser.new
48
+
49
+ @parser.banner = "Usage: #{File.basename($0)} <command> <args>"
50
+
51
+ @parser.version = VERSION
52
+
53
+ @parser
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module PdfOutlineEditor
2
+ VERSION = "0.1.0"
3
+ end
Binary file
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pdf_outline_editor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: java
6
+ authors:
7
+ - healthypackrat
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ name: bundler
20
+ prerelease: false
21
+ type: :development
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '10.0'
33
+ name: rake
34
+ prerelease: false
35
+ type: :development
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ name: rspec
48
+ prerelease: false
49
+ type: :development
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description:
56
+ email:
57
+ - healthypackrat@gmail.com
58
+ executables:
59
+ - pdf_outline_editor
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - README.md
64
+ - exe/pdf_outline_editor
65
+ - lib/pdf_outline_editor.rb
66
+ - lib/pdf_outline_editor/commands/dump_command.rb
67
+ - lib/pdf_outline_editor/commands/init_command.rb
68
+ - lib/pdf_outline_editor/commands/load_command.rb
69
+ - lib/pdf_outline_editor/dumper.rb
70
+ - lib/pdf_outline_editor/loader.rb
71
+ - lib/pdf_outline_editor/runner.rb
72
+ - lib/pdf_outline_editor/version.rb
73
+ - vendor/pdfbox/pdfbox-app-2.0.13.jar
74
+ homepage: https://github.com/healthypackrat/pdf_outline_editor
75
+ licenses:
76
+ - Apache-2.0
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.7.6
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Set PDF outlines from a JSON/YAML definition file
98
+ test_files: []