pdf_outline_editor 0.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
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: []