konjac 0.2.9.5 → 0.3
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.
- data/konjac.gemspec +1 -0
- data/lib/konjac.rb +0 -3
- data/lib/konjac/cli.rb +2 -2
- data/lib/konjac/exception.rb +3 -0
- data/lib/konjac/office.rb +64 -257
- data/lib/konjac/office/generic.rb +76 -0
- data/lib/konjac/office/mac.rb +11 -0
- data/lib/konjac/office/mac/excel.rb +73 -0
- data/lib/konjac/office/mac/power_point.rb +68 -0
- data/lib/konjac/office/mac/shared.rb +36 -0
- data/lib/konjac/office/mac/word.rb +114 -0
- data/lib/konjac/office/os.rb +53 -0
- data/lib/konjac/office/tag.rb +91 -0
- data/lib/konjac/office/windows.rb +11 -0
- data/lib/konjac/office/windows/excel.rb +0 -0
- data/lib/konjac/office/windows/power_point.rb +0 -0
- data/lib/konjac/office/windows/shared.rb +2 -0
- data/lib/konjac/office/windows/word.rb +0 -0
- data/lib/konjac/office/xml.rb +11 -0
- data/lib/konjac/office/xml/shared.rb +262 -0
- data/lib/konjac/version.rb +1 -1
- data/lib/locales/en.yml +1 -0
- data/lib/locales/ja.yml +1 -0
- data/spec/office/generic_spec.rb +72 -0
- data/spec/office_spec.rb +37 -0
- metadata +58 -39
- data/lib/applescripts/konjac_excel_export +0 -120
- data/lib/applescripts/konjac_excel_import +0 -112
- data/lib/applescripts/konjac_powerpoint_export +0 -116
- data/lib/applescripts/konjac_powerpoint_import +0 -109
- data/lib/applescripts/konjac_word_export +0 -110
- data/lib/applescripts/konjac_word_import +0 -139
- data/lib/konjac/os.rb +0 -51
- data/lib/konjac/tag.rb +0 -50
- data/lib/konjac/tag_manager.rb +0 -65
- data/spec/tag_spec.rb +0 -63
data/konjac.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_runtime_dependency "highline"
|
25
25
|
s.add_runtime_dependency "i18n"
|
26
26
|
s.add_runtime_dependency "nokogiri"
|
27
|
+
s.add_runtime_dependency "rb-appscript"
|
27
28
|
s.add_runtime_dependency "sdoc"
|
28
29
|
s.add_runtime_dependency "term-ansicolor"
|
29
30
|
s.add_runtime_dependency "trollop"
|
data/lib/konjac.rb
CHANGED
@@ -18,10 +18,7 @@ module Konjac
|
|
18
18
|
autoload :Installer, "konjac/installer"
|
19
19
|
autoload :Language, "konjac/language"
|
20
20
|
autoload :Office, "konjac/office"
|
21
|
-
autoload :OS, "konjac/os"
|
22
21
|
autoload :Suggestor, "konjac/suggestor"
|
23
|
-
autoload :Tag, "konjac/tag"
|
24
|
-
autoload :TagManager, "konjac/tag_manager"
|
25
22
|
autoload :Translator, "konjac/translator"
|
26
23
|
autoload :Utils, "konjac/utils"
|
27
24
|
|
data/lib/konjac/cli.rb
CHANGED
@@ -80,7 +80,7 @@ eos
|
|
80
80
|
:default => Config.dictionary, :multi => true
|
81
81
|
opt :help, I18n.t(:help, :scope => :opts)
|
82
82
|
end
|
83
|
-
Office.
|
83
|
+
Office.new(ARGV[0]).export
|
84
84
|
when "exportxml"
|
85
85
|
opts = Trollop::options do
|
86
86
|
banner sc_banner % I18n.t(:filenames_arg)
|
@@ -100,7 +100,7 @@ eos
|
|
100
100
|
opt :output, I18n.t(:output, :scope => :opts), :type => :string
|
101
101
|
opt :help, I18n.t(:help, :scope => :opts)
|
102
102
|
end
|
103
|
-
Office.
|
103
|
+
Office.new(ARGV[0]).import
|
104
104
|
when "importxml"
|
105
105
|
opts = Trollop::options do
|
106
106
|
banner sc_banner % I18n.t(:filenames_arg)
|
data/lib/konjac/exception.rb
CHANGED
@@ -5,4 +5,7 @@ module Konjac
|
|
5
5
|
# The user has supplied an invalid language and Konjac can't continue to
|
6
6
|
# process the request
|
7
7
|
class InvalidLanguageError < StandardError; end
|
8
|
+
|
9
|
+
# The user has requested an unimplemented virtual method
|
10
|
+
class VirtualMethodError < StandardError; end
|
8
11
|
end
|
data/lib/konjac/office.rb
CHANGED
@@ -1,283 +1,90 @@
|
|
1
1
|
module Konjac
|
2
2
|
# A singleton for dealing with extracting and importing tags from Microsoft
|
3
|
-
# Office documents
|
3
|
+
# Office documents. It also has abstract some of the lengthier calls to the
|
4
|
+
# different applications of different environments by detecting the user's
|
5
|
+
# operating system and using ghost methods. For example, the following are
|
6
|
+
# equivalent for opening the document at <tt>~/text.doc</tt> on a Mac:
|
7
|
+
#
|
8
|
+
# doc = Konjac::Office::Mac::Word.new("~/text.doc")
|
9
|
+
# doc = Konjac::Office.word("~/text.doc")
|
10
|
+
#
|
11
|
+
# In addition, if the document is already open and is the active document, the
|
12
|
+
# following will work too:
|
13
|
+
#
|
14
|
+
# doc = Konjac::Office.word
|
4
15
|
module Office
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
case File.extname(sub_file)
|
12
|
-
when ".doc", ".docx"
|
13
|
-
return if OS.not_a_mac
|
14
|
-
system File.join(File.dirname(__FILE__), "..", "applescripts", "konjac_word_import"), sub_file
|
15
|
-
when ".ppt", ".pptx"
|
16
|
-
return if OS.not_a_mac
|
17
|
-
system File.join(File.dirname(__FILE__), "..", "applescripts", "konjac_powerpoint_import"), sub_file
|
18
|
-
when ".xls", ".xlsx"
|
19
|
-
return if OS.not_a_mac
|
20
|
-
system File.join(File.dirname(__FILE__), "..", "applescripts", "konjac_excel_import"), sub_file
|
21
|
-
else
|
22
|
-
puts I18n.t(:unknown) % sub_file
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Imports the text content of a tag file into a Word 2003+, utilizing a
|
28
|
-
# cleaned-up version of the document's original XML structure
|
29
|
-
def import_xml(files, opts = {})
|
30
|
-
sub_files = Utils.parse_files(files)
|
31
|
-
return if sub_files.empty?
|
32
|
-
sub_files.each do |sub_file|
|
33
|
-
case File.extname(sub_file)
|
34
|
-
when ".docx"
|
35
|
-
# Build the list of paths we need to work with
|
36
|
-
dirname = File.dirname(sub_file)
|
37
|
-
basename = File.basename(sub_file, ".*")
|
38
|
-
orig_docx = "#{dirname}/#{basename}.docx"
|
39
|
-
new_path = "#{dirname}/#{basename}_imported.docx"
|
40
|
-
xml_path = "#{dirname}/#{basename}.xml"
|
41
|
-
tags_path = "#{dirname}/#{basename}.docx.diff"
|
42
|
-
out_path = "#{dirname}/word/document.xml"
|
43
|
-
|
44
|
-
# Open the original XML file and the updated tags
|
45
|
-
writer = Nokogiri::XML(File.read(xml_path))
|
46
|
-
nodes = writer.xpath("//w:t")
|
47
|
-
tags = TagManager.new(tags_path)
|
16
|
+
autoload :Generic, "konjac/office/generic"
|
17
|
+
autoload :Mac, "konjac/office/mac"
|
18
|
+
autoload :OS, "konjac/office/os"
|
19
|
+
autoload :Tag, "konjac/office/tag"
|
20
|
+
autoload :Windows, "konjac/office/windows"
|
21
|
+
autoload :XML, "konjac/office/windows"
|
48
22
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
# Write the modified XML to a file
|
62
|
-
File.open(out_path, "w") do |file|
|
63
|
-
file.write writer.to_xml.gsub(/\n\s*/, "").sub(/\?></, "?>\n<")
|
64
|
-
end
|
65
|
-
|
66
|
-
# Copy the original file
|
67
|
-
FileUtils.cp orig_docx, new_path
|
68
|
-
|
69
|
-
# Add the new document XML to the copied file
|
70
|
-
system "cd #{dirname} && zip -q #{new_path} word/document.xml"
|
71
|
-
else
|
72
|
-
puts I18n.t(:unknown) % sub_file
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Exports the text content of Microsoft Office document
|
78
|
-
def export_tags(files, opts = {})
|
79
|
-
# Determine whether to attempt translating
|
80
|
-
if opts[:from_given] && opts[:to_given]
|
81
|
-
from_lang = Language.find(opts[:from])
|
82
|
-
to_lang = Language.find(opts[:to])
|
83
|
-
unless from_lang.nil? || to_lang.nil?
|
84
|
-
Translator.load_dictionary from_lang, to_lang, opts
|
85
|
-
attempting_to_translate = true
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
sub_files = Utils.parse_files(files)
|
90
|
-
return if sub_files.empty?
|
91
|
-
sub_files.each do |sub_file|
|
92
|
-
case File.extname(sub_file)
|
93
|
-
when ".doc", ".docx"
|
94
|
-
return if OS.not_a_mac
|
95
|
-
break unless Utils.user_allows_overwrite?(sub_file + ".diff")
|
96
|
-
|
97
|
-
system File.join(File.dirname(__FILE__), "..", "applescripts", "konjac_word_export"), sub_file
|
98
|
-
when ".ppt", ".pptx"
|
99
|
-
return if OS.not_a_mac
|
100
|
-
break unless Utils.user_allows_overwrite?(sub_file + ".diff")
|
101
|
-
|
102
|
-
system File.join(File.dirname(__FILE__), "..", "applescripts", "konjac_powerpoint_export"), sub_file
|
103
|
-
when ".xls", ".xlsx"
|
104
|
-
return if OS.not_a_mac
|
105
|
-
break unless Utils.user_allows_overwrite?(sub_file + ".diff")
|
106
|
-
|
107
|
-
system File.join(File.dirname(__FILE__), "..", "applescripts", "konjac_excel_export"), sub_file
|
108
|
-
else
|
109
|
-
puts I18n.t(:unknown) % sub_file
|
110
|
-
end
|
23
|
+
class << self
|
24
|
+
def new(path)
|
25
|
+
env = environment
|
26
|
+
return nil if env.nil?
|
27
|
+
|
28
|
+
case File.basename(path)
|
29
|
+
when /\.docx?/
|
30
|
+
env::Word.new(path)
|
31
|
+
when /\.pptx?/
|
32
|
+
env::PowerPoint.new(path)
|
33
|
+
when /\.xlsx?/
|
34
|
+
env::Excel.new(path)
|
111
35
|
end
|
112
36
|
end
|
113
37
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
if opts[:from_given] && opts[:to_given]
|
122
|
-
from_lang = Language.find(opts[:from])
|
123
|
-
to_lang = Language.find(opts[:to])
|
124
|
-
unless from_lang.nil? || to_lang.nil?
|
125
|
-
Translator.load_dictionary from_lang, to_lang, opts
|
126
|
-
attempting_to_translate = true
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
sub_files = Utils.parse_files(files)
|
131
|
-
return if sub_files.empty?
|
132
|
-
sub_files.each do |sub_file|
|
133
|
-
case File.extname(sub_file)
|
134
|
-
when ".docx"
|
135
|
-
# Build a list of all the paths we're working with
|
136
|
-
dirname = File.dirname(sub_file)
|
137
|
-
basename = File.basename(sub_file, ".*")
|
138
|
-
orig_docx = "#{dirname}/#{basename}.docx"
|
139
|
-
xml_path = "#{dirname}/#{basename}_orig.xml"
|
140
|
-
clean_path = "#{dirname}/#{basename}.xml"
|
141
|
-
tags_path = "#{dirname}/#{basename}.docx.diff"
|
142
|
-
|
143
|
-
break unless Utils.user_allows_overwrite?(tags_path)
|
144
|
-
|
145
|
-
# Unzip the DOCX's word/document.xml file and pipe the output into
|
146
|
-
# an XML with the same base name as the DOCX
|
147
|
-
system "unzip -p #{orig_docx} word/document.xml > #{xml_path}"
|
148
|
-
|
149
|
-
# Read in the XML file and extract the content from each <w:t> tag
|
150
|
-
cleaner = Nokogiri::XML(File.read(xml_path))
|
151
|
-
File.open(tags_path, "w") do |tags_file|
|
152
|
-
# Remove all grammar and spellcheck tags
|
153
|
-
cleaner.xpath("//w:proofErr").remove
|
154
|
-
|
155
|
-
nodes = cleaner.xpath("//w:r")
|
156
|
-
prev = nil
|
157
|
-
nodes.each do |node|
|
158
|
-
unless prev.nil?
|
159
|
-
if (prev.next_sibling == node) && compare_nodes(prev, node)
|
160
|
-
begin
|
161
|
-
node.at_xpath("w:t").content = prev.at_xpath("w:t").content +
|
162
|
-
node.at_xpath("w:t").content
|
163
|
-
prev.remove
|
164
|
-
rescue
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
prev = node
|
170
|
-
end
|
171
|
-
|
172
|
-
# Write the tags file
|
173
|
-
tags_file.puts "---" + orig_docx
|
174
|
-
tags_file.puts "+++" + orig_docx
|
175
|
-
cleaner.xpath("//w:t").each_with_index do |node, index|
|
176
|
-
tags_file.puts "@@ %i @@" % [index, additional_info(node)]
|
177
|
-
tags_file.puts "-" + node.content
|
178
|
-
if attempting_to_translate
|
179
|
-
tags_file.puts "+" + Translator.translate_content(node.content)
|
180
|
-
else
|
181
|
-
tags_file.puts "+" + node.content
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Write the cleaned-up XML to a file for inspection
|
187
|
-
File.open(clean_path, "w") do |xml|
|
188
|
-
xml.puts cleaner.to_xml
|
189
|
-
end
|
190
|
-
else
|
191
|
-
puts I18n.t(:unknown) % sub_file
|
192
|
-
end
|
38
|
+
def environment
|
39
|
+
if OS.mac?
|
40
|
+
Mac
|
41
|
+
elsif OS.windows?
|
42
|
+
Windows
|
43
|
+
else
|
44
|
+
nil
|
193
45
|
end
|
194
46
|
end
|
195
47
|
|
196
48
|
private
|
197
49
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
# Converts an XML node into a Ruby hash
|
207
|
-
def xml_node_to_hash(node) # :doc:
|
208
|
-
# If we are at the root of the document, start the hash
|
209
|
-
if node.element?
|
210
|
-
result_hash = {}
|
211
|
-
if node.attributes != {}
|
212
|
-
result_hash[:attributes] = {}
|
213
|
-
node.attributes.keys.each do |key|
|
214
|
-
result_hash[:attributes][node.attributes[key].name.to_sym] = prepare(node.attributes[key].value)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
if node.children.size > 0
|
218
|
-
node.children.each do |child|
|
219
|
-
result = xml_node_to_hash(child)
|
220
|
-
|
221
|
-
if child.name == "text"
|
222
|
-
unless child.next_sibling || child.previous_sibling
|
223
|
-
return prepare(result)
|
224
|
-
end
|
225
|
-
elsif result_hash[child.name.to_sym]
|
226
|
-
if result_hash[child.name.to_sym].is_a?(Array)
|
227
|
-
result_hash[child.name.to_sym] << prepare(result)
|
228
|
-
else
|
229
|
-
result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << prepare(result)
|
230
|
-
end
|
231
|
-
else
|
232
|
-
result_hash[child.name.to_sym] = prepare(result)
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
return result_hash
|
237
|
-
else
|
238
|
-
return result_hash
|
239
|
-
end
|
50
|
+
def method_missing(name, *args, &block)
|
51
|
+
env = environment
|
52
|
+
return super if env.nil?
|
53
|
+
index = env.constants.index(camelize(name).to_sym)
|
54
|
+
if index.nil?
|
55
|
+
super
|
240
56
|
else
|
241
|
-
|
57
|
+
env.const_get(env.constants[index]).new *args
|
242
58
|
end
|
243
59
|
end
|
244
60
|
|
245
|
-
|
246
|
-
|
247
|
-
(data.class == String && data.to_i.to_s == data) ? data.to_i : data
|
248
|
-
end
|
61
|
+
def valid_environments
|
62
|
+
return @environments unless @environments.nil?
|
249
63
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
64
|
+
@environments = []
|
65
|
+
downcased = constants.map { |c| underscore(c.to_s) }
|
66
|
+
Dir.glob(File.join(File.dirname(File.expand_path(__FILE__)), "office/*/")).each do |dir|
|
67
|
+
index = downcased.index(dir.split("/").last)
|
68
|
+
@environments << constants[index] unless index.nil?
|
69
|
+
end
|
255
70
|
|
256
|
-
|
257
|
-
|
258
|
-
info = []
|
259
|
-
info << "hyperlink" if node.parent.parent.name == "hyperlink"
|
71
|
+
@environments
|
72
|
+
end
|
260
73
|
|
261
|
-
|
262
|
-
|
74
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
75
|
+
if first_letter_in_uppercase
|
76
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
263
77
|
else
|
264
|
-
|
78
|
+
lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
265
79
|
end
|
266
80
|
end
|
267
81
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
last_member = hierarchy.pop
|
275
|
-
|
276
|
-
hierarchy.each do |member|
|
277
|
-
node = node[member] unless node.nil?
|
278
|
-
end
|
279
|
-
|
280
|
-
node.delete last_member unless node.nil?
|
82
|
+
def underscore(camel_cased_word)
|
83
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
84
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
85
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
86
|
+
tr("-", "_").
|
87
|
+
downcase
|
281
88
|
end
|
282
89
|
end
|
283
90
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Konjac
|
3
|
+
module Office
|
4
|
+
# This is a basic class that contains all the methods that are universal
|
5
|
+
# across OSes, file formats, applications, etc.
|
6
|
+
class Generic
|
7
|
+
attr_reader :document, :index, :current
|
8
|
+
|
9
|
+
def initialize(location = nil)
|
10
|
+
@document = open(File.expand_path(location)) unless location.nil?
|
11
|
+
@document = active_document
|
12
|
+
@index = 0
|
13
|
+
@current = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# This only does the bare minimum, extracting arguments from
|
17
|
+
# <tt>*args</tt>, so that subclass methods have their parameters parsed
|
18
|
+
def write(text, *args)
|
19
|
+
parse_args *args
|
20
|
+
end
|
21
|
+
|
22
|
+
def read(*args)
|
23
|
+
opts = parse_args(*args)
|
24
|
+
clean find(opts), (opts.nil? ? nil : opts[:type])
|
25
|
+
end
|
26
|
+
|
27
|
+
def tags
|
28
|
+
Tag.load path
|
29
|
+
end
|
30
|
+
|
31
|
+
def export
|
32
|
+
Tag.dump data, path
|
33
|
+
end
|
34
|
+
|
35
|
+
def import
|
36
|
+
tags.each do |tag|
|
37
|
+
if tag.changed? && !tag.blank?
|
38
|
+
write tag.added.join(delimiter(tag.type)), *tag.indices,
|
39
|
+
:type => tag.type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def delimiter(type)
|
45
|
+
if type.nil?
|
46
|
+
"\v"
|
47
|
+
else
|
48
|
+
case type
|
49
|
+
when :shape
|
50
|
+
"\r"
|
51
|
+
else
|
52
|
+
"\n"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def parse_args(*args)
|
60
|
+
return nil if args.empty? || args.nil?
|
61
|
+
|
62
|
+
# Extract final argument if it's a hash
|
63
|
+
parsed = args.last.is_a?(Hash) ? args.pop : {}
|
64
|
+
|
65
|
+
# Create hash using @parse_order as keys and args as values, then merge
|
66
|
+
# that with any pre-parsed hashes. Arguments specified via the hash have
|
67
|
+
# priority
|
68
|
+
parsed = Hash[@parse_order.zip(args)].merge(parsed)
|
69
|
+
end
|
70
|
+
|
71
|
+
def clean(text, type = nil)
|
72
|
+
text.gsub(@strippable, "").split delimiter(type)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|