word-to-markdown 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b8d7208877325f1e568f7f91255a7cfbc2df815
4
- data.tar.gz: 4864e21eb71649017c61f677b24e611650a5aed0
3
+ metadata.gz: 65c40ca66cec250fa58be0b50f9691c49e80ba6c
4
+ data.tar.gz: 0bb6276f14bfbcbb6ff11b47f2b0fd0fbff94c10
5
5
  SHA512:
6
- metadata.gz: 63bea7811559b150d55388090f1fd79df3a4f2a562cfe767bb3675a6c2af5e398cad2c00383ffb00c4536e66aebbbdc470a378eea4e3deec9de686cfd314082f
7
- data.tar.gz: cd2c2593502aedef4fa2baddd245d671312b4521877401d1a07e60693d66d1fd15c3e7b14d5746bb7661a479daee7e00cc1c27e2a385ed73ee2846e01151a7f3
6
+ metadata.gz: f20b0252fab914e0a412fdab1b76005d5ce2daa10392bcce0daafd6fae9a7744f66cd26259fb65f1272a7ba3845b460ca0c772bb924a72a09303240bf4586dce
7
+ data.tar.gz: 84c8ab75b71bc19933cbe6860e8511c31d5dbb370898229b9d67e933aa8f754cfe8dd85a1f3220a4454199011691d2185422076a65bda7d86257a9abd6a7cf8e
data/bin/w2m ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'word-to-markdown'
4
+
5
+ if ARGV.size != 1
6
+ puts "Usage: bundle exec w2m path/to/document.docx"
7
+ exit 1
8
+ end
9
+
10
+ doc = WordToMarkdown.new ARGV[0]
11
+ puts doc.to_s
@@ -0,0 +1,22 @@
1
+ module Nokogiri
2
+ module XML
3
+ class Element
4
+
5
+ # The node's font size
6
+ # Used for guessing heading sizes
7
+ #
8
+ # Returns a float with the font-size
9
+ def font_size
10
+ styles['font-size'].to_f if styles['font-size']
11
+ end
12
+
13
+ def bold?
14
+ styles['font-weight'] && styles['font-weight'] == "bold"
15
+ end
16
+
17
+ def italic?
18
+ styles['font-style'] && styles['font-style'] == "italic"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -3,253 +3,70 @@ require 'descriptive_statistics'
3
3
  require 'premailer'
4
4
  require 'nokogiri'
5
5
  require 'nokogiri-styles'
6
+ require 'tmpdir'
7
+ require_relative 'word-to-markdown/version'
8
+ require_relative 'word-to-markdown/document'
9
+ require_relative 'word-to-markdown/converter'
10
+ require_relative 'nokogiri/xml/element'
6
11
 
7
12
  class WordToMarkdown
8
13
 
9
- HEADING_DEPTH = 6 # Number of headings to guess, e.g., h6
10
- HEADING_STEP = 100/HEADING_DEPTH
11
- MIN_HEADING_SIZE = 20
14
+ attr_reader :document, :converter
12
15
 
13
- LI_SELECTORS = %w[
14
- .MsoListParagraphCxSpFirst
15
- .MsoListParagraphCxSpMiddle
16
- .MsoListParagraphCxSpLast
17
- .MsoListParagraph
18
- li
19
- ]
20
-
21
- attr_reader :path, :doc
16
+ REVERSE_MARKDOWN_OPTIONS = {
17
+ unknown_tags: :bypass,
18
+ github_flavored: true
19
+ }
22
20
 
23
21
  # Create a new WordToMarkdown object
24
22
  #
25
23
  # input - a HTML string or path to an HTML file
26
24
  #
27
25
  # Returns the WordToMarkdown object
28
- def initialize(input)
29
- path = File.expand_path input, Dir.pwd
30
- if File.exist?(path)
31
- html = File.open(path).read
32
- @path = path
33
- else
34
- @path = String
35
- html = input.to_s
26
+ def initialize(path)
27
+ @document = WordToMarkdown::Document.new path
28
+ @converter = WordToMarkdown::Converter.new @document
29
+ converter.convert!
30
+ end
31
+
32
+ # source: https://github.com/ricn/libreconv/blob/master/lib/libreconv.rb#L48
33
+ def self.which(cmd)
34
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
35
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
36
+ exts.each do |ext|
37
+ exe = File.join(path, "#{cmd}#{ext}")
38
+ return exe if File.executable? exe
39
+ end
36
40
  end
37
- @doc = Nokogiri::HTML normalize(html)
38
- semanticize!
39
- end
40
-
41
- # Perform pre-processing normalization
42
- #
43
- # html - the raw html input from the export
44
- #
45
- # Returns the normalized html
46
- def normalize(html)
47
- encoding = encoding(html)
48
- html = html.force_encoding(encoding).encode("UTF-8", :invalid => :replace, :replace => "")
49
- html = Premailer.new(html, :with_html_string => true, :input_encoding => "UTF-8").to_inline_css
50
- html.gsub! /\<\/?o:[^>]+>/, "" # Strip everything in the office namespace
51
- html.gsub! /\<\/?w:[^>]+>/, "" # Strip everything in the word namespace
52
- html.gsub! /\n|\r/," " # Remove linebreaks
53
- html.gsub! /“|”/, '"' # Straighten curly double quotes
54
- html.gsub! /‘|’/, "'" # Straighten curly single quotes
55
- html
56
- end
57
-
58
- # Pretty print the class in console
59
- def inspect
60
- "<WordToMarkdown path=\"#{@path}\">"
61
- end
62
41
 
63
- # Returns the markdown representation of the document
64
- def to_s
65
- @markdown ||= scrub_whitespace(ReverseMarkdown.parse(html))
42
+ return nil
66
43
  end
67
44
 
68
- # Returns the html representation of the document
69
- def html
70
- doc.to_html.gsub("</li>\n", "</li>")
71
- end
72
-
73
- # Determine the document encoding
74
- #
75
- # html - the raw html export
76
- #
77
- # Returns the encoding, defaulting to "UTF-8"
78
- def encoding(html)
79
- match = html.encode("UTF-8", :invalid => :replace, :replace => "").match(/charset=([^\"]+)/)
80
- if match
81
- match[1].sub("macintosh", "MacRoman")
45
+ def self.soffice_path
46
+ if RUBY_PLATFORM.include?("darwin")
47
+ "/Applications/LibreOffice.app/Contents/MacOS/soffice"
82
48
  else
83
- "UTF-8"
49
+ soffice_path ||= which("soffice")
50
+ soffice_path ||= which("soffice.bin")
51
+ soffice_path ||= "soffice"
84
52
  end
85
53
  end
86
54
 
87
- # Perform post-processing normalization of certain Word quirks
88
- #
89
- # string - the markdown representation of the document
90
- #
91
- # Returns the normalized markdown
92
- def scrub_whitespace(string)
93
- string.sub!(/\A[[:space:]]+/,'') # leading whitespace
94
- string.sub!(/[[:space:]]+\z/,'') # trailing whitespace
95
- string.gsub!(/\n\n \n\n/,"\n\n") # Quadruple line breaks
96
- string.gsub!(/\u00A0/, "") # Unicode non-breaking spaces, injected as tabs
97
- string
55
+ # Ideally this would be done via open3, but Travis CI can't seen to find soffice when we do
56
+ def self.run_command(*args)
57
+ `#{soffice_path} #{args.join(' ')}`
98
58
  end
99
59
 
100
- # Returns an array of Nokogiri nodes that are implicit headings
101
- def implicit_headings
102
- @implicit_headings ||= begin
103
- headings = []
104
- doc.css("[style]").each do |element|
105
- headings.push element unless element.font_size.nil? || element.font_size < MIN_HEADING_SIZE
106
- end
107
- headings
108
- end
60
+ def self.soffice_version
61
+ run_command('--version').strip.sub "LibreOffice ", ""
109
62
  end
110
63
 
111
- # Returns an array of font-sizes for implicit headings in the document
112
- def font_sizes
113
- @font_sizes ||= begin
114
- sizes = []
115
- doc.css("[style]").each do |element|
116
- sizes.push element.font_size.round(-1) unless element.font_size.nil?
117
- end
118
- sizes.uniq.sort
119
- end
120
- end
121
-
122
- # Given a Nokogiri node, guess what heading it represents, if any
123
- #
124
- # node - the nokigiri node
125
- #
126
- # retuns the heading tag (e.g., H1), or nil
127
- def guess_heading(node)
128
- return nil if node.font_size == nil
129
- [*1...HEADING_DEPTH].each do |heading|
130
- return "h#{heading}" if node.font_size >= h(heading)
131
- end
132
- nil
133
- end
134
-
135
- # Minimum font size required for a given heading
136
- # e.g., H(2) would represent the minimum font size of an implicit h2
137
- #
138
- # n - the heading number, e.g., 1, 2
139
- #
140
- # returns the minimum font size as an integer
141
- def h(n)
142
- font_sizes.percentile ((HEADING_DEPTH-1)-n) * HEADING_STEP
143
- end
144
-
145
- # CSS selector to select non-symantic lists
146
- def li_selectors
147
- LI_SELECTORS.join(",")
148
- end
149
-
150
- # Returns an array of all indented values
151
- def indents
152
- @indents ||= doc.css(li_selectors).map{ |el| el.indent }.uniq.sort
153
- end
154
-
155
- # Determine the indent level given an indent value
156
- #
157
- # level - the true indent, e.g., 2.5 (from 2.5em)
158
- #
159
- # Returns an integer representing the indent level
160
- def indent(level)
161
- indents.find_index level
162
- end
163
-
164
- # Try to make semantic markup explicit where implied by the export
165
- def semanticize!
166
-
167
- # Semanticize lists
168
- indent_level = 0
169
- doc.css(li_selectors).each do |node|
170
-
171
- # Determine if this is an implicit UL or an implicit OL list item
172
- if node.classes.include?("MsoListParagraph") || node.content.match(/^[a-zA-Z0-9]+\./)
173
- list_type = "ol"
174
- else
175
- list_type = "ul"
176
- end
177
-
178
- # calculate indent level
179
- current_indent = indent(node.indent)
180
-
181
- # Determine parent node for this li, creating it if necessary
182
- if current_indent > indent_level || indent_level == 0 && node.parent.css(".indent#{current_indent}").empty?
183
- list = Nokogiri::XML::Node.new list_type, @doc
184
- list.classes = ["list", "indent#{current_indent}"]
185
- list.parent = node.parent.css(".indent#{current_indent-1} li").last || node.parent
186
- else
187
- list = node.parent.css(".indent#{current_indent}").last
188
- end
189
-
190
- # Note our current nesting depth
191
- indent_level = current_indent
192
-
193
- # Convert list paragraphs to actual numbered and unnumbered lists
194
- node.node_name = "li"
195
- node.parent = list if list
196
-
197
- # Scrub unicode bullets
198
- span = node.css("span:first")[1]
199
- if span && span.styles["mso-list"] && span.styles["mso-list"] == "Ignore"
200
- span.content = span.content[1..-1] unless span.content.match /^\d+\./
201
- end
202
-
203
- # Convert all pseudo-numbered list items into numbered list items, e.g., ii. => 2.
204
- node.content = node.content.gsub /^[[:space:] ]+/, ""
205
- node.content = node.content.gsub /^[a-zA-Z0-9]+\.[[:space:]]+/, ""
206
-
207
- end
208
-
209
- # Try to guess heading where implicit bassed on font size
210
- implicit_headings.each do |element|
211
- heading = guess_heading element
212
- element.node_name = heading unless heading.nil?
213
- end
214
-
215
- # Removes paragraphs from tables
216
- doc.search("td p").each { |node| node.node_name = "span" }
64
+ # Pretty print the class in console
65
+ def inspect
66
+ "<WordToMarkdown path=\"#{@document.path}\">"
217
67
  end
218
- end
219
68
 
220
- module Nokogiri
221
- module XML
222
- class Element
223
-
224
- def indent
225
- if styles['mso-list']
226
- styles['mso-list'].split(" ")[1].sub("level","").to_i
227
- else
228
- (left_margin / 0.5).to_i
229
- end
230
- end
231
-
232
- # The node's left-margin
233
- # Used for parsing nested Lis
234
- #
235
- # Returns a float with the left margin
236
- def left_margin
237
- if styles['margin-left']
238
- styles['margin-left'].to_f
239
- elsif styles['margin']
240
- styles['margin'].split(" ").last.to_f
241
- else
242
- 0
243
- end
244
- end
245
-
246
- # The node's font size
247
- # Used for guessing heading sizes
248
- #
249
- # Returns a float with the font-size
250
- def font_size
251
- styles['font-size'].to_f if styles['font-size']
252
- end
253
- end
69
+ def to_s
70
+ document.to_s
254
71
  end
255
72
  end
@@ -0,0 +1,124 @@
1
+ class WordToMarkdown
2
+ class Converter
3
+
4
+ attr_reader :document
5
+
6
+ HEADING_DEPTH = 6 # Number of headings to guess, e.g., h6
7
+ HEADING_STEP = 100/HEADING_DEPTH
8
+ MIN_HEADING_SIZE = 20
9
+ UNICODE_BULLETS = ["○", "o", "●", "\uF0B7", "\u2022", "\uF0A7"]
10
+
11
+ def initialize(document)
12
+ @document = document
13
+ end
14
+
15
+ def convert!
16
+
17
+ # Fonts and headings
18
+ semanticize_font_styles!
19
+ semanticize_headings!
20
+
21
+ # Tables
22
+ remove_paragraphs_from_tables!
23
+ semanticize_table_headers!
24
+
25
+ # list items
26
+ remove_paragraphs_from_list_items!
27
+ remove_unicode_bullets_from_list_items!
28
+ remove_whitespace_from_list_items!
29
+ remove_numbering_from_list_items!
30
+ end
31
+
32
+ # Returns an array of Nokogiri nodes that are implicit headings
33
+ def implicit_headings
34
+ @implicit_headings ||= begin
35
+ headings = []
36
+ @document.tree.css("[style]").each do |element|
37
+ headings.push element unless element.font_size.nil? || element.font_size < MIN_HEADING_SIZE
38
+ end
39
+ headings
40
+ end
41
+ end
42
+
43
+ # Returns an array of font-sizes for implicit headings in the document
44
+ def font_sizes
45
+ @font_sizes ||= begin
46
+ sizes = []
47
+ @document.tree.css("[style]").each do |element|
48
+ sizes.push element.font_size.round(-1) unless element.font_size.nil?
49
+ end
50
+ sizes.uniq.sort
51
+ end
52
+ end
53
+
54
+ # Given a Nokogiri node, guess what heading it represents, if any
55
+ #
56
+ # node - the nokigiri node
57
+ #
58
+ # retuns the heading tag (e.g., H1), or nil
59
+ def guess_heading(node)
60
+ return nil if node.font_size == nil
61
+ [*1...HEADING_DEPTH].each do |heading|
62
+ return "h#{heading}" if node.font_size >= h(heading)
63
+ end
64
+ nil
65
+ end
66
+
67
+ # Minimum font size required for a given heading
68
+ # e.g., H(2) would represent the minimum font size of an implicit h2
69
+ #
70
+ # n - the heading number, e.g., 1, 2
71
+ #
72
+ # returns the minimum font size as an integer
73
+ def h(n)
74
+ font_sizes.percentile ((HEADING_DEPTH-1)-n) * HEADING_STEP
75
+ end
76
+
77
+ def semanticize_font_styles!
78
+ @document.tree.css("span").each do |node|
79
+ if node.bold?
80
+ node.node_name = "strong"
81
+ elsif node.italic?
82
+ node.node_name = "em"
83
+ end
84
+ end
85
+ end
86
+
87
+ def remove_paragraphs_from_tables!
88
+ @document.tree.search("td p").each { |node| node.node_name = "span" }
89
+ end
90
+
91
+ def remove_paragraphs_from_list_items!
92
+ @document.tree.search("li p").each { |node| node.node_name = "span" }
93
+ end
94
+
95
+ def remove_unicode_bullets_from_list_items!
96
+ @document.tree.search("li span").each do |span|
97
+ span.content = span.content.gsub /^([#{UNICODE_BULLETS.join("")}]+)/, ""
98
+ end
99
+ end
100
+
101
+ def remove_numbering_from_list_items!
102
+ @document.tree.search("li span").each do |span|
103
+ span.content = span.content.gsub /^[a-zA-Z0-9]+\./m, ""
104
+ end
105
+ end
106
+
107
+ def remove_whitespace_from_list_items!
108
+ @document.tree.search("li span").each { |span| span.content.strip! }
109
+ end
110
+
111
+ def semanticize_table_headers!
112
+ @document.tree.search("table tr:first td").each { |node| node.node_name = "th" }
113
+ end
114
+
115
+ # Try to guess heading where implicit bassed on font size
116
+ def semanticize_headings!
117
+ implicit_headings.each do |element|
118
+ heading = guess_heading element
119
+ element.node_name = heading unless heading.nil?
120
+ end
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,97 @@
1
+ class WordToMarkdown
2
+ class Document
3
+ class NotFoundError < StandardError; end
4
+
5
+ attr_reader :path, :raw_html
6
+
7
+ def initialize(path)
8
+ @path = File.expand_path path, Dir.pwd
9
+ raise NotFoundError, "File #{@path} does not exist" unless File.exist?(@path)
10
+ end
11
+
12
+ def extension
13
+ File.extname path
14
+ end
15
+
16
+ def tree
17
+ @tree ||= begin
18
+ tree = Nokogiri::HTML(normalize(raw_html))
19
+ tree.css("title").remove
20
+ tree
21
+ end
22
+ end
23
+
24
+ # Returns the html representation of the document
25
+ def html
26
+ tree.to_html.gsub("</li>\n", "</li>")
27
+ end
28
+
29
+ # Returns the markdown representation of the document
30
+ def to_s
31
+ @markdown ||= scrub_whitespace(ReverseMarkdown.convert(html, WordToMarkdown::REVERSE_MARKDOWN_OPTIONS))
32
+ end
33
+
34
+ # Determine the document encoding
35
+ #
36
+ # html - the raw html export
37
+ #
38
+ # Returns the encoding, defaulting to "UTF-8"
39
+ def encoding(html)
40
+ match = html.encode("UTF-8", :invalid => :replace, :replace => "").match(/charset=([^\"]+)/)
41
+ if match
42
+ match[1].sub("macintosh", "MacRoman")
43
+ else
44
+ "UTF-8"
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # Perform pre-processing normalization
51
+ #
52
+ # html - the raw html input from the export
53
+ #
54
+ # Returns the normalized html
55
+ def normalize(html)
56
+ encoding = encoding(html)
57
+ html = html.force_encoding(encoding).encode("UTF-8", :invalid => :replace, :replace => "")
58
+ html = Premailer.new(html, :with_html_string => true, :input_encoding => "UTF-8").to_inline_css
59
+ html.gsub! /\n|\r/," " # Remove linebreaks
60
+ html.gsub! /“|”/, '"' # Straighten curly double quotes
61
+ html.gsub! /‘|’/, "'" # Straighten curly single quotes
62
+ html.gsub! />\s+</, "><" # Remove extra whitespace between tags
63
+ html
64
+ end
65
+
66
+ # Perform post-processing normalization of certain Word quirks
67
+ #
68
+ # string - the markdown representation of the document
69
+ #
70
+ # Returns the normalized markdown
71
+ def scrub_whitespace(string)
72
+ string.sub!(/\A[[:space:]]+/,'') # leading whitespace
73
+ string.sub!(/[[:space:]]+\z/,'') # trailing whitespace
74
+ string.gsub!(/\n\n \n\n/,"\n\n") # Quadruple line breaks
75
+ string.gsub!(/\u00A0/, "") # Unicode non-breaking spaces, injected as tabs
76
+ string
77
+ end
78
+
79
+ def tmpdir
80
+ @tmpdir ||= Dir.mktmpdir
81
+ end
82
+
83
+ def dest_path
84
+ dest_filename = File.basename(path).gsub(/#{Regexp.escape(extension)}$/, ".html")
85
+ File.expand_path(dest_filename, tmpdir)
86
+ end
87
+
88
+ def raw_html
89
+ @raw_html ||= begin
90
+ WordToMarkdown::run_command '--headless', '--convert-to', 'html', path, '--outdir', tmpdir
91
+ html = File.read dest_path
92
+ File.delete dest_path
93
+ html
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,3 @@
1
+ class WordToMarkdown
2
+ VERSION = "1.0.0"
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: word-to-markdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Balter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-30 00:00:00.000000000 Z
11
+ date: 2014-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reverse_markdown
@@ -16,147 +16,167 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.4.7
19
+ version: '0.5'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.4.7
26
+ version: '0.5'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: descriptive_statistics
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.1.3
33
+ version: '1.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.1.3
40
+ version: '1.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: premailer
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '1.8'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '1.8'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: nokogiri-styles
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '0.1'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '0.1'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '10.3'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '10.3'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: shoulda
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '3.5'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '3.5'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rdoc
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '4.1'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '4.1'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: bundler
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ">="
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: '1.6'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">="
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: '1.6'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: pry
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - ">="
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '0'
131
+ version: '0.9'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - ">="
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '0'
138
+ version: '0.9'
139
139
  - !ruby/object:Gem::Dependency
140
- name: rerun
140
+ name: mocha
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - ">="
143
+ - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '0'
145
+ version: '1.0'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ">="
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: minitest
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '4.7'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
151
165
  - !ruby/object:Gem::Version
152
- version: '0'
166
+ version: '4.7'
153
167
  description: Ruby Gem to convert Word documents to markdown.
154
168
  email: ben.balter@github.com
155
- executables: []
169
+ executables:
170
+ - w2m
156
171
  extensions: []
157
172
  extra_rdoc_files: []
158
173
  files:
174
+ - bin/w2m
175
+ - lib/nokogiri/xml/element.rb
159
176
  - lib/word-to-markdown.rb
177
+ - lib/word-to-markdown/converter.rb
178
+ - lib/word-to-markdown/document.rb
179
+ - lib/word-to-markdown/version.rb
160
180
  homepage: https://github.com/benbalter/word-to-markdown
161
181
  licenses:
162
182
  - MIT