pandocomatic 0.0.13 → 0.1.0.b
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 +4 -4
- data/bin/pandocomatic +2 -78
- data/lib/pandocomatic/cli.rb +183 -0
- data/lib/pandocomatic/command/command.rb +113 -0
- data/lib/pandocomatic/command/convert_dir_command.rb +155 -0
- data/lib/pandocomatic/command/convert_file_command.rb +163 -0
- data/lib/pandocomatic/command/copy_file_command.rb +49 -0
- data/lib/pandocomatic/command/create_link_command.rb +85 -0
- data/lib/pandocomatic/command/skip_command.rb +50 -0
- data/lib/pandocomatic/configuration.rb +212 -164
- data/lib/pandocomatic/error/cli_error.rb +49 -0
- data/lib/pandocomatic/error/configuration_error.rb +39 -0
- data/lib/pandocomatic/error/io_error.rb +50 -0
- data/lib/pandocomatic/error/pandoc_error.rb +30 -0
- data/lib/pandocomatic/error/pandocomatic_error.rb +48 -0
- data/lib/pandocomatic/error/processor_error.rb +33 -0
- data/lib/pandocomatic/fileinfo_preprocessor.rb +34 -13
- data/lib/pandocomatic/pandoc_metadata.rb +66 -34
- data/lib/pandocomatic/pandocomatic.rb +176 -0
- data/lib/pandocomatic/printer/command_printer.rb +28 -0
- data/lib/pandocomatic/printer/configuration_errors_printer.rb +29 -0
- data/lib/pandocomatic/printer/error_printer.rb +34 -0
- data/lib/pandocomatic/printer/finish_printer.rb +44 -0
- data/lib/pandocomatic/printer/help_printer.rb +27 -0
- data/lib/pandocomatic/printer/printer.rb +43 -0
- data/lib/pandocomatic/printer/summary_printer.rb +35 -0
- data/lib/pandocomatic/printer/version_printer.rb +30 -0
- data/lib/pandocomatic/printer/views/cli_error.txt +12 -0
- data/lib/pandocomatic/printer/views/command.txt +1 -0
- data/lib/pandocomatic/printer/views/configuration_error.txt +1 -0
- data/lib/pandocomatic/printer/views/configuration_errors.txt +10 -0
- data/lib/pandocomatic/printer/views/error.txt +5 -0
- data/lib/pandocomatic/printer/views/finish.txt +1 -0
- data/lib/pandocomatic/printer/views/help.txt +135 -0
- data/lib/pandocomatic/printer/views/io_error.txt +12 -0
- data/lib/pandocomatic/printer/views/pandoc_error.txt +1 -0
- data/lib/pandocomatic/printer/views/processor_error.txt +6 -0
- data/lib/pandocomatic/printer/views/summary.txt +1 -0
- data/lib/pandocomatic/printer/views/version.txt +7 -0
- data/lib/pandocomatic/printer/views/warning.txt +1 -0
- data/lib/pandocomatic/printer/warning_printer.rb +33 -0
- data/lib/pandocomatic/processor.rb +25 -8
- data/lib/pandocomatic/warning.rb +36 -0
- metadata +79 -17
- data/lib/pandocomatic/dir_converter.rb +0 -119
- data/lib/pandocomatic/file_converter.rb +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dffe0869e5d1d21dea4701c4a102be0965a5fb38
|
4
|
+
data.tar.gz: f0c876b6f2833288b1c70d02e64c65efee067985
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13a606257cc004eb37f775f24aa561459e0d44adf4657ea0b28dcd7c5e4991a4d7bd5f34f985eca94eef1c972e1800f53be2cf54f8b65247c4edbd5a3e072ca2
|
7
|
+
data.tar.gz: 0c8bb7e39e23858b4a123a5e213005aaed34e63ff42fd213c8c55f54a1cb3583cba51f6eded036012a989dfca15e0b379c524da8dbfb926798aa534b4659ddc5
|
data/bin/pandocomatic
CHANGED
@@ -1,79 +1,3 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
|
4
|
-
require 'fileutils'
|
5
|
-
|
6
|
-
require_relative '../lib/pandocomatic/dir_converter.rb'
|
7
|
-
require_relative '../lib/pandocomatic/file_converter.rb'
|
8
|
-
require_relative '../lib/pandocomatic/configuration.rb'
|
9
|
-
|
10
|
-
opts = Trollop::options do
|
11
|
-
version "pandocomatic 0.0.13"
|
12
|
-
banner <<-EOB
|
13
|
-
|
14
|
-
Pandocomatic automates the use of pandoc (<http://www.pandoc.org>). It can be
|
15
|
-
used to convert one file or a whole directory (tree).
|
16
|
-
|
17
|
-
Usage:
|
18
|
-
|
19
|
-
pandocomatic [--config pandocomatic.yaml] --output output input
|
20
|
-
|
21
|
-
Options:
|
22
|
-
EOB
|
23
|
-
|
24
|
-
opt :config, "Pandocomatic configuration file, default is ./pandocomatic.yaml", :type => :string
|
25
|
-
opt :output, "output file or directory", :type => :string
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
# input file/directory
|
31
|
-
Trollop::die "Expect exactly one input file or directory" if ARGV.length != 1
|
32
|
-
input = ARGV.first
|
33
|
-
|
34
|
-
# output file/directory
|
35
|
-
Trollop::die "Expect an output file or directory" if opts[:output].nil?
|
36
|
-
output = opts[:output]
|
37
|
-
|
38
|
-
# Configuration
|
39
|
-
if opts[:config].nil?
|
40
|
-
if Dir.entries(Dir.pwd).include? 'pandocomatic.yaml'
|
41
|
-
settings_file = File.join Dir.pwd, 'pandocomatic.yaml'
|
42
|
-
else
|
43
|
-
default_dir = File.join Dir.home, '.pandocomatic'
|
44
|
-
default_config = File.join default_dir, 'pandocomatic.yaml'
|
45
|
-
begin
|
46
|
-
if not Dir.exist? default_dir
|
47
|
-
Dir.mkdir default_dir
|
48
|
-
end
|
49
|
-
|
50
|
-
if not File.exist? default_config
|
51
|
-
config_template = File.absolute_path '../lib/pandocomatic/default_configuration.yaml', __dir__
|
52
|
-
FileUtils.cp config_template, default_config
|
53
|
-
end
|
54
|
-
rescue Exception => e
|
55
|
-
raise "Failed creating default pandocomatic file at #{default_config}: #{e.message}"
|
56
|
-
end
|
57
|
-
settings_file = default_config
|
58
|
-
end
|
59
|
-
else
|
60
|
-
settings_file = opts[:config]
|
61
|
-
end
|
62
|
-
|
63
|
-
if not File.exist? settings_file or not File.readable? settings_file then
|
64
|
-
Trollop::die "Cannot read configuration file #{settings_file}" if File
|
65
|
-
end
|
66
|
-
|
67
|
-
config = Pandocomatic::Configuration.new settings_file
|
68
|
-
|
69
|
-
if File.directory? input then
|
70
|
-
if File.exist? output and not File.directory? output then
|
71
|
-
Trollop::die "Expected an output directory, found a file instead"
|
72
|
-
end
|
73
|
-
|
74
|
-
Pandocomatic::DirConverter.new(input, output, config).convert
|
75
|
-
|
76
|
-
else
|
77
|
-
# input is one file
|
78
|
-
Pandocomatic::FileConverter.new.convert input, output, config
|
79
|
-
end
|
2
|
+
require 'pandocomatic/pandocomatic'
|
3
|
+
Pandocomatic::Pandocomatic.run ARGV
|
@@ -0,0 +1,183 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2017, Huub de Beer <Huub@heerdebeer.org>
|
3
|
+
#
|
4
|
+
# This file is part of pandocomatic.
|
5
|
+
#
|
6
|
+
# Pandocomatic is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by the
|
8
|
+
# Free Software Foundation, either version 3 of the License, or (at your
|
9
|
+
# option) any later version.
|
10
|
+
#
|
11
|
+
# Pandocomatic is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
13
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
14
|
+
# for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with pandocomatic. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
module Pandocomatic
|
20
|
+
require 'trollop'
|
21
|
+
|
22
|
+
require_relative './error/cli_error.rb'
|
23
|
+
|
24
|
+
##
|
25
|
+
# Command line options parser for pandocomatic using trollop.
|
26
|
+
#
|
27
|
+
class CLI
|
28
|
+
|
29
|
+
##
|
30
|
+
# Parse the arguments, returns a triplet with the global options, an
|
31
|
+
# optional subcommand, and the (optional) options for that subcommand.
|
32
|
+
#
|
33
|
+
# @param args [String, Array] A command line invocation string or a list of strings like ARGV
|
34
|
+
#
|
35
|
+
# @return [Hash] The options to pandocomatic
|
36
|
+
# the subcommand's options
|
37
|
+
#
|
38
|
+
def self.parse(args)
|
39
|
+
args = args.split if args.is_a? String
|
40
|
+
|
41
|
+
begin
|
42
|
+
options = parse_options args || {:help => true, :help_given => true}
|
43
|
+
options
|
44
|
+
rescue Trollop::CommandlineError => e
|
45
|
+
raise CLIError.new(:problematic_invocation, e, args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Parse pandocomatic's global options.
|
52
|
+
def self.parse_options(args)
|
53
|
+
parser = Trollop::Parser.new do
|
54
|
+
# General options
|
55
|
+
opt :dry_run, 'Do a dry run', :short => '-y'
|
56
|
+
opt :quiet, 'Run quietly', :short => '-q'
|
57
|
+
opt :modified_only, 'Modified files only', :short => '-m'
|
58
|
+
|
59
|
+
# Configuration of the converter
|
60
|
+
opt :data_dir, 'Data dir', :short => '-d', :type => String
|
61
|
+
opt :config, 'Configuration file', :short => '-c', :type => String
|
62
|
+
|
63
|
+
# What to convert and where to put it
|
64
|
+
opt :output, 'Output', :short => '-o', :type => String
|
65
|
+
opt :input, 'Input', :short => '-i', :type => String
|
66
|
+
|
67
|
+
# Version and help
|
68
|
+
opt :show_version, 'Version', :short => '-v', :long => 'version'
|
69
|
+
opt :show_help, 'Help', :short => '-h', :long => 'help'
|
70
|
+
end
|
71
|
+
|
72
|
+
# All options should be parsed according to the specification given in the parser
|
73
|
+
begin
|
74
|
+
options = parser.parse args
|
75
|
+
rescue Trollop::CommandlineError => e
|
76
|
+
raise CLIError.new(:problematic_invocation, e, args)
|
77
|
+
end
|
78
|
+
|
79
|
+
options = use_custom_version options
|
80
|
+
options = use_custom_help options
|
81
|
+
|
82
|
+
if options_need_to_be_validated? options
|
83
|
+
# if no input option is specified, it should follow as the last item
|
84
|
+
if not options[:input_given]
|
85
|
+
options[:input] = args.shift
|
86
|
+
options[:input_given] = true
|
87
|
+
end
|
88
|
+
|
89
|
+
# There should be no other options left.
|
90
|
+
raise CLIError.new(:too_many_options, nil, args) if not args.empty?
|
91
|
+
|
92
|
+
# There should be an input specified
|
93
|
+
raise CLIError.new(:no_input_given) if options[:input].nil? or options[:input].empty?
|
94
|
+
|
95
|
+
# The input file or directory should exist
|
96
|
+
input = File.absolute_path options[:input]
|
97
|
+
raise CLIError.new(:input_does_not_exist, nil, options[:input]) unless File.exist? input
|
98
|
+
raise CLIError.new(:input_is_not_readable, nil, input) unless File.readable? input
|
99
|
+
|
100
|
+
if options[:output_given]
|
101
|
+
output = File.absolute_path options[:output]
|
102
|
+
# Input and output should be both files or directories
|
103
|
+
match_file_types input, output
|
104
|
+
|
105
|
+
# The output, if it already exist, should be writable
|
106
|
+
raise CLIError.new(:output_is_not_writable, nil, output) unless not File.exist? output or File.writable? output
|
107
|
+
else
|
108
|
+
# If the input is a directory, an output directory should be
|
109
|
+
# specified as well. If the input is a file, the output could be
|
110
|
+
# specified in the input file, or STDOUT could be used.
|
111
|
+
raise CLIError.new(:no_output_given) if File.directory? input
|
112
|
+
end
|
113
|
+
|
114
|
+
# Data dir, if specified, should be an existing and readable directory
|
115
|
+
if options[:data_dir_given]
|
116
|
+
data_dir = File.absolute_path options[:data_dir]
|
117
|
+
|
118
|
+
raise CLIError.new(:data_dir_does_not_exist, nil, options[:data_dir]) unless File.exist? data_dir
|
119
|
+
raise CLIError.new(:data_dir_is_not_readable, nil, data_dir) unless File.readable? data_dir
|
120
|
+
raise CLIError.new(:data_dir_is_not_a_directory, nil, data_dir) unless File.directory? data_dir
|
121
|
+
end
|
122
|
+
|
123
|
+
# Config file, if specified, should be an existing and readable file
|
124
|
+
if options[:config_given]
|
125
|
+
config = File.absolute_path options[:config]
|
126
|
+
|
127
|
+
raise CLIError.new(:config_file_does_not_exist, nil, options[:config]) unless File.exist? config
|
128
|
+
raise CLIError.new(:config_file_is_not_readable, nil, config) unless File.readable? config
|
129
|
+
raise CLIError.new(:config_file_is_not_a_file, nil, config) unless File.file? config
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
options
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.options_need_to_be_validated? options
|
138
|
+
not options[:version_given] and not options[:help_given]
|
139
|
+
end
|
140
|
+
|
141
|
+
#--
|
142
|
+
#Trollop has special behavior for the version and help options. To
|
143
|
+
# overcome, "show_version" and "show_help" options are introduced. When
|
144
|
+
# set, these are put in the options as "version" and "help"
|
145
|
+
# respectively.
|
146
|
+
#++
|
147
|
+
|
148
|
+
def self.use_custom_version options
|
149
|
+
if options[:show_version]
|
150
|
+
options.delete :show_version
|
151
|
+
options.delete :show_version_given
|
152
|
+
options[:version] = true
|
153
|
+
options[:version_given] = true
|
154
|
+
end
|
155
|
+
options
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.use_custom_help options
|
159
|
+
if options[:show_help]
|
160
|
+
options.delete :show_help
|
161
|
+
options.delete :show_help_given
|
162
|
+
options[:help] = true
|
163
|
+
options[:help_given] = true
|
164
|
+
end
|
165
|
+
options
|
166
|
+
end
|
167
|
+
|
168
|
+
# If output does not exist, the output can be
|
169
|
+
# created with the same type. If output does exist, however, it should
|
170
|
+
# have the same type as the input.
|
171
|
+
def self.matching_file_types?(input, output)
|
172
|
+
not File.exist?(output) or File.ftype(input) == File.ftype(output)
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.match_file_types(input, output)
|
176
|
+
if not matching_file_types? input, output
|
177
|
+
raise CLIError.new(:output_is_not_a_file, nil, input) if File.file? input
|
178
|
+
raise CLIError.new(:output_is_not_a_directory, nil, input) if File.directory? input
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2017, Huub de Beer <Huub@heerdebeer.org>
|
3
|
+
#
|
4
|
+
# This file is part of pandocomatic.
|
5
|
+
#
|
6
|
+
# Pandocomatic is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by the
|
8
|
+
# Free Software Foundation, either version 3 of the License, or (at your
|
9
|
+
# option) any later version.
|
10
|
+
#
|
11
|
+
# Pandocomatic is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
13
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
14
|
+
# for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with pandocomatic. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
module Pandocomatic
|
20
|
+
|
21
|
+
require_relative '../printer/command_printer.rb'
|
22
|
+
|
23
|
+
class Command
|
24
|
+
|
25
|
+
attr_reader :errors, :index
|
26
|
+
|
27
|
+
@@total = 0
|
28
|
+
@@dry_run = false
|
29
|
+
@@quiet = false
|
30
|
+
@@src_root = "."
|
31
|
+
@@modified_only = false
|
32
|
+
|
33
|
+
def initialize()
|
34
|
+
@errors = []
|
35
|
+
@@total += 1
|
36
|
+
@index = @@total
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.reset(src_root = ".", dry_run = false, quiet = false, modified_only)
|
40
|
+
@@src_root = src_root
|
41
|
+
@@dry_run = dry_run
|
42
|
+
@@quiet = quiet
|
43
|
+
@@modified_only = modified_only
|
44
|
+
@@total = 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def src_root()
|
48
|
+
@@src_root
|
49
|
+
end
|
50
|
+
|
51
|
+
def dry_run?()
|
52
|
+
@@dry_run
|
53
|
+
end
|
54
|
+
|
55
|
+
def quiet?()
|
56
|
+
@@quiet
|
57
|
+
end
|
58
|
+
|
59
|
+
def modified_only?()
|
60
|
+
@@modified_only
|
61
|
+
end
|
62
|
+
|
63
|
+
def count()
|
64
|
+
1
|
65
|
+
end
|
66
|
+
|
67
|
+
def all_errors()
|
68
|
+
@errors
|
69
|
+
end
|
70
|
+
|
71
|
+
def index_to_s()
|
72
|
+
"#{@@total - @index + 1}".rjust(@@total.to_s.size)
|
73
|
+
end
|
74
|
+
|
75
|
+
def execute()
|
76
|
+
CommandPrinter.new(self).print unless quiet?
|
77
|
+
run if not dry_run? and runnable?
|
78
|
+
end
|
79
|
+
|
80
|
+
def run()
|
81
|
+
end
|
82
|
+
|
83
|
+
def runnable?()
|
84
|
+
not has_errors?
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_s()
|
88
|
+
'command'
|
89
|
+
end
|
90
|
+
|
91
|
+
def directory?()
|
92
|
+
false
|
93
|
+
end
|
94
|
+
|
95
|
+
def skip?()
|
96
|
+
false
|
97
|
+
end
|
98
|
+
|
99
|
+
def uncount()
|
100
|
+
@@total -= 1
|
101
|
+
end
|
102
|
+
|
103
|
+
def has_errors?()
|
104
|
+
not @errors.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
# src file is newer than the dstination file?
|
108
|
+
def file_modified?(src, dst)
|
109
|
+
not File.exist? dst or File.mtime(src) > File.mtime(dst)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2017, Huub de Beer <Huub@heerdebeer.org>
|
3
|
+
#
|
4
|
+
# This file is part of pandocomatic.
|
5
|
+
#
|
6
|
+
# Pandocomatic is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by the
|
8
|
+
# Free Software Foundation, either version 3 of the License, or (at your
|
9
|
+
# option) any later version.
|
10
|
+
#
|
11
|
+
# Pandocomatic is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
13
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
14
|
+
# for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with pandocomatic. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
module Pandocomatic
|
20
|
+
|
21
|
+
require_relative '../error/io_error.rb'
|
22
|
+
|
23
|
+
require_relative 'command.rb'
|
24
|
+
require_relative 'create_link_command.rb'
|
25
|
+
require_relative 'convert_file_command.rb'
|
26
|
+
require_relative 'copy_file_command.rb'
|
27
|
+
require_relative 'skip_command.rb'
|
28
|
+
|
29
|
+
class ConvertDirCommand < Command
|
30
|
+
|
31
|
+
attr_reader :config, :src_dir, :dst_dir, :subcommands
|
32
|
+
|
33
|
+
def initialize(current_config, src_dir, dst_dir)
|
34
|
+
super()
|
35
|
+
@src_dir = src_dir
|
36
|
+
@config = current_config
|
37
|
+
|
38
|
+
begin
|
39
|
+
@config = reconfigure current_config, @src_dir
|
40
|
+
rescue ConfigurationError => e
|
41
|
+
@errors.push e
|
42
|
+
end
|
43
|
+
|
44
|
+
@dst_dir = dst_dir
|
45
|
+
|
46
|
+
if Dir.exist? @dst_dir
|
47
|
+
@errors.push IOError.new(:directory_is_not_readable, nil, @dst_dir) unless File.readable? @dst_dir
|
48
|
+
@errors.push IOError.new(:directory_is_not_writable, nil, @dst_dir) unless File.writable? @dst_dir
|
49
|
+
@errors.push IOError.new(:directory_is_not_a_directory, nil, @dst_dir) unless File.directory? @dst_dir
|
50
|
+
end
|
51
|
+
|
52
|
+
@subcommands = []
|
53
|
+
|
54
|
+
Dir.foreach @src_dir do |filename|
|
55
|
+
src = File.join @src_dir, filename
|
56
|
+
|
57
|
+
next if config.skip? src
|
58
|
+
|
59
|
+
@errors.push IOError.new(:file_or_directory_does_not_exist, nil, src) unless File.exist? src
|
60
|
+
|
61
|
+
dst = File.join @dst_dir, filename
|
62
|
+
|
63
|
+
if File.symlink? src and not config.follow_links?
|
64
|
+
subcommand = CreateLinkCommand.new(src, dst)
|
65
|
+
elsif File.directory? src then
|
66
|
+
if config.recursive? then
|
67
|
+
subcommand = ConvertDirCommand.new(config, src, dst)
|
68
|
+
else
|
69
|
+
subcommand = SkipCommand.new(src, :skipping_directory)
|
70
|
+
end
|
71
|
+
elsif File.file? src
|
72
|
+
if config.convert? src then
|
73
|
+
dst = config.set_extension dst
|
74
|
+
if not modified_only? or file_modified? src, dst then
|
75
|
+
subcommand = ConvertFileCommand.new(config, src, dst)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
if not modified_only? or file_modified? src, dst then
|
79
|
+
subcommand = CopyFileCommand.new(src, dst)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
else
|
83
|
+
subcommand = SkipCommand.new(src, :unclear_what_to_do)
|
84
|
+
end
|
85
|
+
|
86
|
+
@subcommands.push subcommand unless subcommand.nil? or subcommand.skip?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Empty commands do not count to the total amount of commands to execute
|
90
|
+
uncount if skip?
|
91
|
+
end
|
92
|
+
|
93
|
+
def skip?()
|
94
|
+
@subcommands.empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
def count()
|
98
|
+
@subcommands.reduce(if skip? then 0 else 1 end) do |total, subcommand|
|
99
|
+
total += subcommand.count
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def all_errors()
|
104
|
+
@subcommands.reduce(@errors) do |total, subcommand|
|
105
|
+
total += subcommand.all_errors
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def directory?()
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s()
|
114
|
+
"convert #{@src_dir}" + '; ' + if create_directory? then 'create and ' else '' end + "enter #{@dst_dir}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def run()
|
118
|
+
begin
|
119
|
+
Dir.mkdir @dst_dir if create_directory?
|
120
|
+
rescue SystemError => e
|
121
|
+
raise IOError.new(:error_creating_directory, e, @dst_dir)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def execute()
|
126
|
+
if not @subcommands.empty?
|
127
|
+
CommandPrinter.new(self).print unless quiet?
|
128
|
+
run if not dry_run? and runnable?
|
129
|
+
|
130
|
+
@subcommands.each do |subcommand|
|
131
|
+
subcommand.execute
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def create_directory?()
|
139
|
+
not File.exist? @dst_dir or not File.directory? @dst_dir
|
140
|
+
end
|
141
|
+
|
142
|
+
# If the source directory contains a configuration file, use it to
|
143
|
+
# reconfigure the converter. Otherwise, use the current configuration
|
144
|
+
def reconfigure(current_config, src_dir)
|
145
|
+
config_file = File.join src_dir, Pandocomatic::CONFIG_FILE
|
146
|
+
if File.exist? config_file then
|
147
|
+
config = current_config.reconfigure config_file
|
148
|
+
else
|
149
|
+
config = current_config
|
150
|
+
end
|
151
|
+
config
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2017, Huub de Beer <Huub@heerdebeer.org>
|
3
|
+
#
|
4
|
+
# This file is part of pandocomatic.
|
5
|
+
#
|
6
|
+
# Pandocomatic is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by the
|
8
|
+
# Free Software Foundation, either version 3 of the License, or (at your
|
9
|
+
# option) any later version.
|
10
|
+
#
|
11
|
+
# Pandocomatic is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
13
|
+
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
14
|
+
# for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with pandocomatic. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
module Pandocomatic
|
20
|
+
|
21
|
+
require 'paru'
|
22
|
+
|
23
|
+
require_relative '../pandoc_metadata.rb'
|
24
|
+
require_relative '../processor.rb'
|
25
|
+
require_relative '../fileinfo_preprocessor'
|
26
|
+
|
27
|
+
require_relative '../error/io_error.rb'
|
28
|
+
require_relative '../error/configuration_error.rb'
|
29
|
+
require_relative '../error/processor_error.rb'
|
30
|
+
|
31
|
+
require_relative 'command.rb'
|
32
|
+
|
33
|
+
class ConvertFileCommand < Command
|
34
|
+
|
35
|
+
attr_reader :config, :src, :dst
|
36
|
+
|
37
|
+
def initialize(config, src, dst)
|
38
|
+
super()
|
39
|
+
@config = config
|
40
|
+
@src = src
|
41
|
+
@dst = dst
|
42
|
+
|
43
|
+
@errors.push IOError.new(:file_does_not_exist, nil, @src) unless File.exist? @src
|
44
|
+
@errors.push IOError.new(:file_is_not_a_file, nil, @src) unless File.file? @src
|
45
|
+
@errors.push IOError.new(:file_is_not_readable, nil, @src) unless File.readable? @src
|
46
|
+
end
|
47
|
+
|
48
|
+
def run
|
49
|
+
convert_file
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"convert #{File.basename @src} -> #{File.basename @dst}"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def convert_file
|
59
|
+
metadata = PandocMetadata.load_file @src
|
60
|
+
|
61
|
+
if metadata.has_template? then
|
62
|
+
template_name = metadata.template_name
|
63
|
+
else
|
64
|
+
template_name = @config.determine_template @src
|
65
|
+
end
|
66
|
+
|
67
|
+
raise ConfigurationError.new(:no_such_template, nil, template_name) unless @config.has_template? template_name
|
68
|
+
|
69
|
+
template = @config.get_template template_name
|
70
|
+
|
71
|
+
pandoc_options = (template['pandoc'] || {}).merge(metadata.pandoc_options || {})
|
72
|
+
|
73
|
+
input = File.read @src
|
74
|
+
input = FileInfoPreprocessor.run input, @src
|
75
|
+
input = preprocess input, template
|
76
|
+
input = pandoc input, pandoc_options, File.dirname(@src)
|
77
|
+
output = postprocess input, template
|
78
|
+
|
79
|
+
if @dst.to_s.empty? and metadata.pandoc_options.has_key? 'output'
|
80
|
+
@dst = metadata.pandoc_options['output']
|
81
|
+
end
|
82
|
+
|
83
|
+
begin
|
84
|
+
File.open(@dst, 'w') do |file|
|
85
|
+
raise IOError.new(:file_is_not_a_file, nil, @dst) unless File.file? @dst
|
86
|
+
raise IOError.new(:file_is_not_writable, nil, @dst) unless File.writable? @dst
|
87
|
+
file << output
|
88
|
+
end
|
89
|
+
rescue StandardError => e
|
90
|
+
raise IOError.new(:error_writing_file, e, @dst)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# TODO: update this list
|
95
|
+
PANDOC_OPTIONS_WITH_PATH = [
|
96
|
+
'filter',
|
97
|
+
'template',
|
98
|
+
'css',
|
99
|
+
'include-in-header',
|
100
|
+
'include-before-body',
|
101
|
+
'include-after-body',
|
102
|
+
'reference-odt',
|
103
|
+
'reference-docx',
|
104
|
+
'epub-stylesheet',
|
105
|
+
'epub-cover-image',
|
106
|
+
'epub-metadata',
|
107
|
+
'epub-embed-font',
|
108
|
+
'bibliography',
|
109
|
+
'csl'
|
110
|
+
]
|
111
|
+
|
112
|
+
def pandoc(input, options, src_dir)
|
113
|
+
converter = Paru::Pandoc.new
|
114
|
+
options.each do |option, value|
|
115
|
+
|
116
|
+
value = @config.update_path value, src_dir if
|
117
|
+
PANDOC_OPTIONS_WITH_PATH.include? option
|
118
|
+
|
119
|
+
converter.send option, value unless option == 'output'
|
120
|
+
# don't let pandoc write the output to enable postprocessing
|
121
|
+
end
|
122
|
+
|
123
|
+
begin
|
124
|
+
converter << input
|
125
|
+
rescue Paru::Error => e
|
126
|
+
raise PandocError.new(:error_running_pandoc, e, input_document)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def preprocess(input, config = {})
|
131
|
+
process input, 'preprocessors', config
|
132
|
+
end
|
133
|
+
|
134
|
+
def postprocess(input, config = {})
|
135
|
+
process input, 'postprocessors', config
|
136
|
+
end
|
137
|
+
|
138
|
+
# Run the input string through a list of filters called processors. There
|
139
|
+
# are to types: preprocessors and postprocessors
|
140
|
+
def process(input, type, config = {})
|
141
|
+
if config.has_key? type then
|
142
|
+
processors = config[type]
|
143
|
+
output = input
|
144
|
+
processors.each do |processor|
|
145
|
+
script = @config.update_path(processor, File.dirname(@src))
|
146
|
+
|
147
|
+
raise ProcessorError.new(:script_does_not_exist, nil, script) unless File.exist? script
|
148
|
+
raise ProcessorError.new(:script_is_not_executable, nil, script) unless File.executable? script
|
149
|
+
|
150
|
+
begin
|
151
|
+
output = Processor.run(script, output)
|
152
|
+
rescue StandardError => e
|
153
|
+
ProcessorError.new(:error_processing_script, e, [script, @src])
|
154
|
+
end
|
155
|
+
end
|
156
|
+
output
|
157
|
+
else
|
158
|
+
input
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|