coradoc 0.2.0 → 1.0.0

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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/Dockerfile +1 -1
  3. data/.docker/docker-compose.yml +2 -2
  4. data/.editorconfig +15 -0
  5. data/CHANGELOG.md +4 -0
  6. data/README.md +4 -0
  7. data/Rakefile +10 -0
  8. data/coradoc.gemspec +11 -2
  9. data/exe/reverse_adoc +91 -0
  10. data/exe/w2a +72 -0
  11. data/lib/coradoc/document.rb +6 -6
  12. data/lib/coradoc/element/admonition.rb +8 -6
  13. data/lib/coradoc/element/attribute.rb +2 -2
  14. data/lib/coradoc/element/attribute_list.rb +94 -15
  15. data/lib/coradoc/element/audio.rb +14 -3
  16. data/lib/coradoc/element/author.rb +18 -14
  17. data/lib/coradoc/element/base.rb +69 -8
  18. data/lib/coradoc/element/block/core.rb +10 -6
  19. data/lib/coradoc/element/block/literal.rb +1 -1
  20. data/lib/coradoc/element/block/quote.rb +1 -1
  21. data/lib/coradoc/element/block/sourcecode.rb +2 -2
  22. data/lib/coradoc/element/break.rb +1 -1
  23. data/lib/coradoc/element/document_attributes.rb +6 -6
  24. data/lib/coradoc/element/header.rb +4 -2
  25. data/lib/coradoc/element/image/block_image.rb +13 -2
  26. data/lib/coradoc/element/image/core.rb +35 -5
  27. data/lib/coradoc/element/image/inline_image.rb +2 -2
  28. data/lib/coradoc/element/image.rb +0 -1
  29. data/lib/coradoc/element/inline/anchor.rb +4 -2
  30. data/lib/coradoc/element/inline/bold.rb +10 -4
  31. data/lib/coradoc/element/inline/cross_reference.rb +4 -2
  32. data/lib/coradoc/element/inline/hard_line_break.rb +1 -1
  33. data/lib/coradoc/element/inline/highlight.rb +12 -6
  34. data/lib/coradoc/element/inline/italic.rb +10 -4
  35. data/lib/coradoc/element/inline/link.rb +26 -10
  36. data/lib/coradoc/element/inline/monospace.rb +10 -4
  37. data/lib/coradoc/element/inline/quotation.rb +4 -1
  38. data/lib/coradoc/element/inline/subscript.rb +5 -2
  39. data/lib/coradoc/element/inline/superscript.rb +5 -2
  40. data/lib/coradoc/element/inline.rb +0 -1
  41. data/lib/coradoc/element/list/core.rb +10 -8
  42. data/lib/coradoc/element/list/definition.rb +19 -0
  43. data/lib/coradoc/element/list/ordered.rb +1 -1
  44. data/lib/coradoc/element/list/unordered.rb +1 -1
  45. data/lib/coradoc/element/list.rb +1 -1
  46. data/lib/coradoc/element/list_item.rb +9 -4
  47. data/lib/coradoc/element/list_item_definition.rb +32 -0
  48. data/lib/coradoc/element/paragraph.rb +5 -3
  49. data/lib/coradoc/element/revision.rb +20 -16
  50. data/lib/coradoc/element/section.rb +21 -4
  51. data/lib/coradoc/element/table.rb +36 -19
  52. data/lib/coradoc/element/text_element.rb +63 -17
  53. data/lib/coradoc/element/title.rb +27 -7
  54. data/lib/coradoc/element/video.rb +33 -6
  55. data/lib/coradoc/generator.rb +2 -2
  56. data/lib/coradoc/legacy_parser.rb +41 -41
  57. data/lib/coradoc/oscal.rb +2 -4
  58. data/lib/coradoc/parser/asciidoc/content.rb +15 -15
  59. data/lib/coradoc/parser/asciidoc/document_attributes.rb +1 -1
  60. data/lib/coradoc/parser/asciidoc/header.rb +6 -6
  61. data/lib/coradoc/parser/asciidoc/section.rb +1 -1
  62. data/lib/coradoc/reverse_adoc/LICENSE.txt +25 -0
  63. data/lib/coradoc/reverse_adoc/README.adoc +308 -0
  64. data/lib/coradoc/reverse_adoc/cleaner.rb +125 -0
  65. data/lib/coradoc/reverse_adoc/config.rb +73 -0
  66. data/lib/coradoc/reverse_adoc/converters/a.rb +47 -0
  67. data/lib/coradoc/reverse_adoc/converters/aside.rb +12 -0
  68. data/lib/coradoc/reverse_adoc/converters/audio.rb +25 -0
  69. data/lib/coradoc/reverse_adoc/converters/base.rb +104 -0
  70. data/lib/coradoc/reverse_adoc/converters/blockquote.rb +18 -0
  71. data/lib/coradoc/reverse_adoc/converters/br.rb +11 -0
  72. data/lib/coradoc/reverse_adoc/converters/bypass.rb +77 -0
  73. data/lib/coradoc/reverse_adoc/converters/code.rb +19 -0
  74. data/lib/coradoc/reverse_adoc/converters/div.rb +14 -0
  75. data/lib/coradoc/reverse_adoc/converters/dl.rb +55 -0
  76. data/lib/coradoc/reverse_adoc/converters/drop.rb +22 -0
  77. data/lib/coradoc/reverse_adoc/converters/em.rb +17 -0
  78. data/lib/coradoc/reverse_adoc/converters/figure.rb +21 -0
  79. data/lib/coradoc/reverse_adoc/converters/h.rb +38 -0
  80. data/lib/coradoc/reverse_adoc/converters/head.rb +19 -0
  81. data/lib/coradoc/reverse_adoc/converters/hr.rb +11 -0
  82. data/lib/coradoc/reverse_adoc/converters/ignore.rb +16 -0
  83. data/lib/coradoc/reverse_adoc/converters/img.rb +98 -0
  84. data/lib/coradoc/reverse_adoc/converters/li.rb +13 -0
  85. data/lib/coradoc/reverse_adoc/converters/mark.rb +15 -0
  86. data/lib/coradoc/reverse_adoc/converters/markup.rb +27 -0
  87. data/lib/coradoc/reverse_adoc/converters/math.rb +31 -0
  88. data/lib/coradoc/reverse_adoc/converters/ol.rb +60 -0
  89. data/lib/coradoc/reverse_adoc/converters/p.rb +19 -0
  90. data/lib/coradoc/reverse_adoc/converters/pass_through.rb +13 -0
  91. data/lib/coradoc/reverse_adoc/converters/pre.rb +51 -0
  92. data/lib/coradoc/reverse_adoc/converters/q.rb +12 -0
  93. data/lib/coradoc/reverse_adoc/converters/strong.rb +16 -0
  94. data/lib/coradoc/reverse_adoc/converters/sub.rb +18 -0
  95. data/lib/coradoc/reverse_adoc/converters/sup.rb +18 -0
  96. data/lib/coradoc/reverse_adoc/converters/table.rb +280 -0
  97. data/lib/coradoc/reverse_adoc/converters/td.rb +77 -0
  98. data/lib/coradoc/reverse_adoc/converters/text.rb +28 -0
  99. data/lib/coradoc/reverse_adoc/converters/th.rb +14 -0
  100. data/lib/coradoc/reverse_adoc/converters/tr.rb +18 -0
  101. data/lib/coradoc/reverse_adoc/converters/video.rb +25 -0
  102. data/lib/coradoc/reverse_adoc/converters.rb +53 -0
  103. data/lib/coradoc/reverse_adoc/errors.rb +10 -0
  104. data/lib/coradoc/reverse_adoc/html_converter.rb +150 -0
  105. data/lib/coradoc/reverse_adoc/plugin.rb +131 -0
  106. data/lib/coradoc/reverse_adoc/plugins/plateau.rb +174 -0
  107. data/lib/coradoc/reverse_adoc/postprocessor.rb +148 -0
  108. data/lib/coradoc/reverse_adoc.rb +30 -0
  109. data/lib/coradoc/transformer.rb +24 -14
  110. data/lib/coradoc/version.rb +1 -1
  111. data/lib/reverse_adoc.rb +20 -0
  112. metadata +184 -5
  113. data/lib/coradoc/element/inline/image.rb +0 -25
@@ -0,0 +1,27 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Markup < Base
4
+ def to_coradoc(node, state = {})
5
+ u_before = unconstrained_before?(node)
6
+ u_after = unconstrained_after?(node)
7
+
8
+ leading_whitespace, trailing_whitespace =
9
+ extract_leading_trailing_whitespace(node)
10
+
11
+ content = treat_children_coradoc(node, state)
12
+
13
+ if node_has_ancestor?(node, markup_ancestor_tag_names)
14
+ content
15
+ elsif node.children.empty?
16
+ leading_whitespace.to_s
17
+ else
18
+ u = (u_before && leading_whitespace.nil?) ||
19
+ (u_after && trailing_whitespace.nil?)
20
+
21
+ e = coradoc_class.new(content, unconstrained: u)
22
+ [leading_whitespace, e, trailing_whitespace]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # Unless run with Coradoc::ReverseAdoc.config.mathml2asciimath,
2
+ # this is cheating: we're injecting MathML into Asciidoctor, but
3
+ # Asciidoctor only understands AsciiMath or LaTeX
4
+
5
+ require "mathml2asciimath"
6
+
7
+ module Coradoc::ReverseAdoc
8
+ module Converters
9
+ class Math < Base
10
+ # FIXIT
11
+ def to_coradoc(node, state = {})
12
+ convert(node, state)
13
+ end
14
+
15
+ def convert(node, _state = {})
16
+ stem = node.to_s.gsub(/\n/, " ")
17
+ stem = MathML2AsciiMath.m2a(stem) if Coradoc::ReverseAdoc.config.mathml2asciimath
18
+ unless stem.nil?
19
+ stem = stem.gsub(/\[/, "\\[").gsub(/\]/, "\\]").gsub(
20
+ /\(\(([^\)]+)\)\)/, "(\\1)"
21
+ )
22
+ end
23
+
24
+ # TODO: This is to be done in Coradoc
25
+ " stem:[" << stem << "] "
26
+ end
27
+ end
28
+
29
+ register :math, Math.new
30
+ end
31
+ end
@@ -0,0 +1,60 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Ol < Base
4
+ # FIXIT
5
+ def to_coradoc(node, state = {})
6
+ # convert(node, state)
7
+ id = node["id"]
8
+ ol_count = state.fetch(:ol_count, 0) + 1
9
+ attrs = ol_attrs(node)
10
+ items = treat_children_coradoc(node, state.merge(ol_count: ol_count))
11
+
12
+ options = {}.tap do |hash|
13
+ hash[:id] = id
14
+ hash[:ol_count] = ol_count
15
+ hash[:attrs] = attrs
16
+ end
17
+
18
+ case get_list_type(node, state)
19
+ when :ordered
20
+ Coradoc::Element::List::Ordered.new(items, options)
21
+ when :unordered
22
+ Coradoc::Element::List::Unordered.new(items, options)
23
+ end
24
+ end
25
+
26
+ def get_list_type(node, _state)
27
+ case node.name
28
+ when "ol"
29
+ :ordered
30
+ when "ul"
31
+ :unordered
32
+ end
33
+ end
34
+
35
+ def number_style(node)
36
+ case node["style"]
37
+ when "1" then "arabic"
38
+ when "A" then "upperalpha"
39
+ when "a" then "loweralpha"
40
+ when "I" then "upperroman"
41
+ when "i" then "lowerroman"
42
+ end
43
+ end
44
+
45
+ def ol_attrs(node)
46
+ attrs = Coradoc::Element::AttributeList.new
47
+ style = number_style(node)
48
+ attrs.add_positional(style) if style
49
+ attrs.add_positional("%reversed") if node["reversed"]
50
+ attrs.add_named("start", node["start"]) if node["start"]
51
+ attrs.add_named("type", node["type"]) if node["type"]
52
+ attrs
53
+ end
54
+ end
55
+
56
+ register :ol, Ol.new
57
+ register :ul, Ol.new
58
+ register :dir, Ol.new
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class P < Base
4
+ def to_coradoc(node, state = {})
5
+ id = node["id"]
6
+ content = treat_children_coradoc(node, state)
7
+
8
+ options = {}.tap do |hash|
9
+ hash[:id] = id if id
10
+ hash[:tdsinglepara] = true if state[:tdsinglepara]
11
+ end
12
+
13
+ Coradoc::Element::Paragraph.new(content, options)
14
+ end
15
+ end
16
+
17
+ register :p, P.new
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class PassThrough < Base
4
+ def to_coradoc(node, _state = {})
5
+ node.to_s
6
+ end
7
+
8
+ def convert(node, state = {})
9
+ to_coradoc(node, state)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Pre < Base
4
+ def to_coradoc(node, state = {})
5
+ id = node["id"]
6
+ lang = language(node)
7
+ content = treat_children(node, state)
8
+
9
+ unless lang
10
+ return Coradoc::Element::Block::Literal.new(
11
+ nil,
12
+ lines: content,
13
+ id: id,
14
+ )
15
+ end
16
+
17
+ Coradoc::Element::Block::SourceCode.new(
18
+ nil,
19
+ lines: content,
20
+ lang: lang,
21
+ id: id,
22
+ )
23
+ end
24
+
25
+ private
26
+
27
+ def treat(node, _state)
28
+ return "\n" if node.name == "br"
29
+
30
+ prefix = postfix = "\n\n" if node.name == "p"
31
+
32
+ "#{prefix}#{node.text}#{postfix}"
33
+ end
34
+
35
+ def language(node)
36
+ lang = language_from_highlight_class(node)
37
+ lang || language_from_confluence_class(node)
38
+ end
39
+
40
+ def language_from_highlight_class(node)
41
+ node.parent["class"].to_s[/highlight-([a-zA-Z0-9]+)/, 1]
42
+ end
43
+
44
+ def language_from_confluence_class(node)
45
+ node["class"].to_s[/brush:\s?(:?.*);/, 1]
46
+ end
47
+ end
48
+
49
+ register :pre, Pre.new
50
+ end
51
+ end
@@ -0,0 +1,12 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Q < Base
4
+ def to_coradoc(node, state = {})
5
+ content = treat_children(node, state)
6
+ Coradoc::Element::Inline::Quotation.new(content)
7
+ end
8
+ end
9
+
10
+ register :q, Q.new
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Strong < Markup
4
+ def coradoc_class
5
+ Coradoc::Element::Inline::Bold
6
+ end
7
+
8
+ def markup_ancestor_tag_names
9
+ %w[strong b]
10
+ end
11
+ end
12
+
13
+ register :strong, Strong.new
14
+ register :b, Strong.new
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Sub < Base
4
+ def to_coradoc(node, state = {})
5
+ leading_whitespace, trailing_whitespace = extract_leading_trailing_whitespace(node)
6
+
7
+ content = treat_children_coradoc(node, state)
8
+
9
+ return content if Coradoc::Generator.gen_adoc(content).strip.empty?
10
+
11
+ e = Coradoc::Element::Inline::Subscript.new(content)
12
+ [leading_whitespace, e, trailing_whitespace]
13
+ end
14
+ end
15
+
16
+ register :sub, Sub.new
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Sup < Base
4
+ def to_coradoc(node, state = {})
5
+ leading_whitespace, trailing_whitespace = extract_leading_trailing_whitespace(node)
6
+
7
+ content = treat_children_coradoc(node, state)
8
+
9
+ return content if Coradoc::Generator.gen_adoc(content).strip.empty?
10
+
11
+ e = Coradoc::Element::Inline::Superscript.new(content)
12
+ [leading_whitespace, e, trailing_whitespace]
13
+ end
14
+ end
15
+
16
+ register :sup, Sup.new
17
+ end
18
+ end
@@ -0,0 +1,280 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Table < Base
4
+ def to_coradoc(node, state = {})
5
+ id = node["id"]
6
+ title = extract_title(node)
7
+ attrs = style(node)
8
+ content = treat_children_coradoc(node, state)
9
+ Coradoc::Element::Table.new(title, content, { id: id, attrs: attrs })
10
+ end
11
+
12
+ def extract_title(node)
13
+ title = node.at("./caption")
14
+ return "" if title.nil?
15
+
16
+ treat_children(title, {})
17
+ end
18
+
19
+ def frame(node)
20
+ case node["frame"]
21
+ when "void"
22
+ "none"
23
+ when "hsides"
24
+ "topbot"
25
+ when "vsides"
26
+ "sides"
27
+ when "box", "border"
28
+ "all"
29
+ end
30
+ end
31
+
32
+ def rules(node)
33
+ case node["rules"]
34
+ when "all"
35
+ "all"
36
+ when "rows"
37
+ "rows"
38
+ when "cols"
39
+ "cols"
40
+ when "none"
41
+ "none"
42
+ end
43
+ end
44
+
45
+ def style(node)
46
+ attrs = Coradoc::Element::AttributeList.new
47
+ # Width is disabled on tables for now. (From #88)
48
+ # attrs.add_named("width", node["width"]) if node["width"]
49
+
50
+ frame_attr = frame(node)
51
+ attrs.add_named("frame", frame_attr) if frame_attr
52
+
53
+ rules_attr = rules(node)
54
+ attrs.add_named("rules", rules_attr) if rules_attr
55
+
56
+ cols = ensure_row_column_integrity_and_get_column_sizes(node)
57
+ attrs.add_named("cols", cols)
58
+
59
+ # Header first rows can't span multiple riws - drop header if they do.
60
+ header = node.at_xpath(".//tr")
61
+ unless header.xpath("./td | ./th").all? { |i| [nil, "1", ""].include? i["rowspan"] }
62
+ attrs.add_named("options", ["noheader"])
63
+ end
64
+
65
+ # This line should be removed.
66
+ return "" if attrs.empty?
67
+
68
+ attrs
69
+ end
70
+
71
+ def ensure_row_column_integrity_and_get_column_sizes(node)
72
+ rows = node.xpath(".//tr")
73
+ num_rows = rows.length
74
+ computed_columns_per_row = [0] * num_rows
75
+ # Both those variables may seem the same, but they have a crucial
76
+ # difference.
77
+ #
78
+ # cell_references don't necessarily contain an image of created
79
+ # table. New cells are pushed to it as needed, so if we have a
80
+ # one cell with colspan and rowspan of 2 on the previous row,
81
+ # those will always be first in the row, regardless of their
82
+ # position. This array is only used to warrant the table
83
+ # integrity.
84
+ #
85
+ # cell_matrix, on the other hand, can't be constructed outright.
86
+ # It will be incorrect as long as the table isn't fixed. So we
87
+ # can't use it to correct colspans/rowspans/missing rows. Once
88
+ # we have it constructed, only then we can calculate the correct
89
+ # column widths.
90
+ cell_references = [nil] * num_rows
91
+ cell_matrix = [nil] * num_rows
92
+
93
+ recompute = proc do
94
+ return ensure_row_column_integrity_and_get_column_sizes(node)
95
+ end
96
+
97
+ fits_in_cell_matrix = proc do |y,x,rowspan,colspan|
98
+ rowspan.times.all? do |yy|
99
+ colspan.times.all? do |xx|
100
+ !cell_matrix.dig(y+yy, x+xx)
101
+ end
102
+ end
103
+ end
104
+
105
+ rows.each_with_index do |row, i|
106
+ columns = row.xpath("./td | ./th")
107
+ column_id = 0
108
+
109
+ columns.each do |cell|
110
+ colspan = cell["colspan"]&.to_i || 1
111
+ rowspan = cell["rowspan"]&.to_i || 1
112
+
113
+ column_id += 1 until fits_in_cell_matrix.(i,column_id,rowspan,colspan)
114
+
115
+ rowspan.times do |j|
116
+ # Let's increase the table for particularly bad documents
117
+ computed_columns_per_row[i + j] ||= 0
118
+ computed_columns_per_row[i + j] += colspan
119
+
120
+ cell_references[i + j] ||= []
121
+ cell_matrix[i + j] ||= []
122
+ colspan.times do |k|
123
+ cell_references[i + j] << [cell, k > 0]
124
+ cell_matrix[i + j][column_id] = cell
125
+ column_id += 1
126
+ end
127
+ column_id -= colspan
128
+ end
129
+ column_id += colspan
130
+ end
131
+ end
132
+
133
+ ##### Fixups
134
+ cpr = computed_columns_per_row
135
+
136
+ # Some cell has too high colspan
137
+ if cpr.length > num_rows
138
+ cell_references[num_rows].each do |cell,|
139
+ next unless cell
140
+
141
+ cell["rowspan"] = cell["rowspan"].to_i - 1
142
+ end
143
+
144
+ # Let's recompute the numbers
145
+ recompute.()
146
+ end
147
+
148
+ if [cpr.first] * num_rows != cpr
149
+ # Colspan inconsistencies
150
+ modified = false
151
+ cpr_max = cpr.max
152
+ max_rows = cell_references.sort_by(&:length).reverse
153
+ max_rows.each do |row|
154
+ break if row.length != cpr_max
155
+
156
+ cell, spanning = row.last
157
+
158
+ if spanning
159
+ modified = true
160
+ cell["colspan"] = cell["colspan"].to_i - 1
161
+ end
162
+ end
163
+
164
+ recompute.() if modified
165
+
166
+ # We are out of colspans to fix. If we are at this point, this
167
+ # means there is an inconsistent number of TH/TDs simply.
168
+ # Here, the solution is to add empty TDs.
169
+ min_rows = cell_references.sort_by(&:length)
170
+ cpr_min = cpr.min
171
+ min_rows.each do |row|
172
+ break if row.length != cpr_min
173
+
174
+ row_obj = row.last.first.parent
175
+ doc = row_obj.document
176
+ row_obj.add_child(Nokogiri::XML::Node.new("td", doc))
177
+
178
+ modified = true
179
+ end
180
+
181
+ recompute.() if modified
182
+
183
+ ### We should have a correct document now and we should never
184
+ ### end up here.
185
+ warn "**** Couldn't fix table sizes for table on line #{node.line}"
186
+ end
187
+
188
+ # For column size computation, we must have an integral cell_matrix.
189
+ # Let's verify that all columns and rows are populated.
190
+ cell_matrix_correct = cell_matrix.length.times.all? do |y|
191
+ cell_matrix[y].length.times.all? do |x|
192
+ cell_matrix[y][x]
193
+ end
194
+ end
195
+
196
+ unless cell_matrix_correct
197
+ warn <<~WARNING.gsub("\n", " ")
198
+ **** Couldn't construct a valid image of a table on line
199
+ #{node.line}. We need that to reliably compute column
200
+ widths of that table. Please report a bug to metanorma/coradoc
201
+ repository.
202
+ WARNING
203
+ end
204
+
205
+ # Compute column sizes
206
+ column_sizes = []
207
+ cell_matrix.each do |row|
208
+ row.each_with_index do |(cell,_), i|
209
+ next unless !cell || [nil, "", "1"].include?(cell["colspan"])
210
+
211
+ column_sizes[i] ||= []
212
+ column_sizes[i] << cell["width"]
213
+ end
214
+ end
215
+
216
+ document_width = Coradoc::ReverseAdoc.config.doc_width.to_r
217
+
218
+ column_sizes += [nil] * (cpr.first - column_sizes.length)
219
+
220
+ sizes = column_sizes.map do |col|
221
+ col = [] if col.nil?
222
+
223
+ max = col.map do |i|
224
+ if i.nil?
225
+ 0r
226
+ elsif i.end_with?("%")
227
+ document_width * i.to_i / 100
228
+ else
229
+ i.to_r
230
+ end
231
+ end.max
232
+
233
+ if max.nil? || max.negative?
234
+ 0r
235
+ else
236
+ max
237
+ end
238
+ end
239
+
240
+ # The table seems bigger than the document... let's scale all
241
+ # values.
242
+ while sizes.map { |i|
243
+ i.zero? ? document_width / 3 / sizes.length : i
244
+ }.sum > document_width
245
+
246
+ sizes = sizes.map { |i| i * 4 / 5 }
247
+ end
248
+
249
+ rest = document_width - sizes.sum
250
+ unset = sizes.count(&:zero?)
251
+
252
+ # Fill in zeros with remainder space
253
+ sizes = sizes.map do |i|
254
+ if i.zero?
255
+ rest / unset
256
+ else
257
+ i
258
+ end
259
+ end
260
+
261
+ # Scale to integers
262
+ lcm = sizes.map(&:denominator).inject(1) { |i,j| i.lcm(j) }
263
+ sizes = sizes.map { |i| i * lcm }.map(&:to_i)
264
+
265
+ # Scale down by gcd
266
+ gcd = sizes.inject(sizes.first) { |i,j| i.gcd(j) }
267
+ sizes = sizes.map { |i| i / gcd }
268
+
269
+ # Try to generate a shorter definition
270
+ if [sizes.first] * sizes.length == sizes
271
+ sizes.length
272
+ else
273
+ sizes.join(",")
274
+ end
275
+ end
276
+ end
277
+
278
+ register :table, Table.new
279
+ end
280
+ end
@@ -0,0 +1,77 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Td < Base
4
+ def to_coradoc(node, state = {})
5
+ # convert(node, state)
6
+ id = node["id"]
7
+ colrowattr = colrow(node["colspan"], node["rowspan"])
8
+ alignattr = alignstyle(node)
9
+ style = cellstyle(node)
10
+
11
+ singlepara = node.elements.size == 1 && node.elements.first.name == "p"
12
+ state[:tdsinglepara] = singlepara if singlepara
13
+
14
+ adoccell = node.at(".//ul | .//ol | .//pre | .//blockquote | .//br | .//hr | .//img[@src]") ||
15
+ node.at(".//p") && !singlepara
16
+
17
+ style = "a" if adoccell
18
+ content = treat_children_coradoc(node, state)
19
+ options = {}.tap do |hash|
20
+ hash[:id] = id
21
+ hash[:colrowattr] = colrowattr
22
+ hash[:alignattr] = alignattr
23
+ hash[:style] = style
24
+ hash[:content] = content
25
+ end
26
+
27
+ Coradoc::Element::Table::Cell.new(options)
28
+ end
29
+
30
+ def cellstyle(_node)
31
+ ""
32
+ end
33
+
34
+ def colrow(colspan, rowspan)
35
+ if colspan && rowspan && colspan != "1" && rowspan != "1"
36
+ "#{colspan}.#{rowspan}+"
37
+ elsif colspan && colspan != "1"
38
+ "#{colspan}+"
39
+ elsif rowspan && rowspan != "1"
40
+ ".#{rowspan}+"
41
+ else
42
+ ""
43
+ end
44
+ end
45
+
46
+ def alignstyle(node)
47
+ align = node["align"]
48
+ valign = node["valign"]
49
+ a = case align
50
+ when "left" then "<"
51
+ when "center" then "^"
52
+ when "right" then ">"
53
+ else
54
+ ""
55
+ end
56
+ v = case valign
57
+ when "top" then "<"
58
+ when "middle" then "^"
59
+ when "bottom" then ">"
60
+ else
61
+ ""
62
+ end
63
+ if align && valign
64
+ "#{a}.#{v}"
65
+ elsif align
66
+ a
67
+ elsif valign
68
+ ".#{v}"
69
+ else
70
+ ""
71
+ end
72
+ end
73
+ end
74
+
75
+ register :td, Td.new
76
+ end
77
+ end
@@ -0,0 +1,28 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Text < Base
4
+ def to_coradoc(node, state = {})
5
+ return treat_empty(node, state) if node.text.strip.empty?
6
+
7
+ Coradoc::Element::TextElement.new(node.text)
8
+ end
9
+
10
+ private
11
+
12
+ def treat_empty(node, state)
13
+ parent = node.parent.name.to_sym
14
+ if %i[ol ul].include?(parent) # Otherwise the identation is broken
15
+ ""
16
+ elsif state[:tdsinglepara]
17
+ ""
18
+ elsif node.text == " " # Regular whitespace text node
19
+ " "
20
+ else
21
+ ""
22
+ end
23
+ end
24
+ end
25
+
26
+ register :text, Text.new
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Th < Td
4
+ def cellstyle(node)
5
+ # this is the header row
6
+ return "" if node.parent.previous_element.nil?
7
+
8
+ "h"
9
+ end
10
+ end
11
+
12
+ register :th, Th.new
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Tr < Base
4
+ def to_coradoc(node, state = {})
5
+ content = treat_children_coradoc(node, state)
6
+ header = table_header_row?(node)
7
+ Coradoc::Element::Table::Row.new(content, header)
8
+ end
9
+
10
+ def table_header_row?(node)
11
+ # node.element_children.all? {|child| child.name.to_sym == :th}
12
+ node.previous_element.nil?
13
+ end
14
+ end
15
+
16
+ register :tr, Tr.new
17
+ end
18
+ end