konjac 0.2.9.5 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|