pandocomatic 0.0.13 → 0.1.0.b
Sign up to get free protection for your applications and to get access to all the features.
- 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
|