coradoc 2.0.1 → 2.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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +77 -146
- data/coradoc-adoc/lib/coradoc/asciidoc/model/base.rb +4 -3
- data/coradoc-adoc/lib/coradoc/asciidoc/model/document.rb +1 -1
- data/coradoc-adoc/lib/coradoc/asciidoc/model/include.rb +1 -1
- data/coradoc-adoc/lib/coradoc/asciidoc/model/resolver.rb +2 -2
- data/coradoc-adoc/lib/coradoc/asciidoc/model/serialization/asciidoc_transform.rb +3 -3
- data/coradoc-adoc/lib/coradoc/asciidoc/model/table_row.rb +1 -1
- data/coradoc-adoc/lib/coradoc/asciidoc/model/text_element.rb +4 -8
- data/coradoc-adoc/lib/coradoc/asciidoc/parse_error.rb +6 -6
- data/coradoc-adoc/lib/coradoc/asciidoc/serializer/adoc_serializer.rb +5 -10
- data/coradoc-adoc/lib/coradoc/asciidoc/serializer/formatter.rb +4 -3
- data/coradoc-adoc/lib/coradoc/asciidoc/serializer/serializers/base.rb +8 -20
- data/coradoc-adoc/lib/coradoc/asciidoc/serializer/serializers/block/core.rb +1 -1
- data/coradoc-adoc/lib/coradoc/asciidoc/serializer/serializers/document.rb +3 -6
- data/coradoc-adoc/lib/coradoc/asciidoc/serializer/serializers/inline/strikethrough.rb +1 -1
- data/coradoc-adoc/lib/coradoc/asciidoc/serializer/serializers/list/item.rb +5 -9
- data/coradoc-adoc/lib/coradoc/asciidoc/transform/from_core_model.rb +26 -34
- data/coradoc-adoc/lib/coradoc/asciidoc/transform/from_core_model_registrations.rb +18 -18
- data/coradoc-adoc/lib/coradoc/asciidoc/transform/to_core_model.rb +96 -123
- data/coradoc-adoc/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +10 -6
- data/coradoc-adoc/lib/coradoc/asciidoc/transformer/header_rules.rb +5 -5
- data/coradoc-adoc/lib/coradoc/asciidoc/transformer/list_rules.rb +2 -2
- data/coradoc-adoc/lib/coradoc/asciidoc/transformer/structural_rules.rb +1 -1
- data/coradoc-adoc/lib/coradoc/asciidoc/transformer.rb +5 -5
- data/coradoc-adoc/lib/coradoc/asciidoc.rb +1 -1
- data/coradoc-adoc/lib/coradoc/util/asciidoc.rb +4 -3
- data/coradoc-adoc/spec/coradoc/asciidoc/transform/from_core_model_spec.rb +4 -2
- data/coradoc-docx/lib/coradoc/docx/transform/context.rb +1 -1
- data/coradoc-docx/lib/coradoc/docx/transform/from_core_model.rb +13 -6
- data/coradoc-docx/lib/coradoc/docx/transform/numbering_resolver.rb +9 -7
- data/coradoc-docx/lib/coradoc/docx/transform/ordered_content.rb +2 -2
- data/coradoc-docx/lib/coradoc/docx/transform/rules/image_rule.rb +4 -1
- data/coradoc-docx/lib/coradoc/docx/transform/rules/math_rule.rb +2 -1
- data/coradoc-docx/lib/coradoc/docx/transform/rules/run_rule.rb +20 -30
- data/coradoc-docx/lib/coradoc/docx/transform/rules/simple_field_rule.rb +7 -5
- data/coradoc-docx/lib/coradoc/docx/transform/rules/table_rule.rb +3 -5
- data/coradoc-docx/lib/coradoc/docx/transform/style_resolver.rb +19 -24
- data/coradoc-docx/lib/coradoc/docx/transform/to_core_model.rb +18 -11
- data/coradoc-docx/lib/coradoc/docx.rb +6 -4
- data/coradoc-docx/spec/coradoc/docx/transform/from_core_model_spec.rb +5 -2
- data/coradoc-docx/spec/coradoc/docx/transform/rules/rule_unit_spec.rb +27 -7
- data/coradoc-docx/spec/coradoc/docx/transform/to_core_model_spec.rb +6 -2
- data/coradoc-html/lib/coradoc/html/base.rb +3 -7
- data/coradoc-html/lib/coradoc/html/converter_base.rb +5 -15
- data/coradoc-html/lib/coradoc/html/converters/base.rb +22 -28
- data/coradoc-html/lib/coradoc/html/converters/comment_line.rb +2 -2
- data/coradoc-html/lib/coradoc/html/converters/link.rb +1 -1
- data/coradoc-html/lib/coradoc/html/converters/list_item.rb +3 -3
- data/coradoc-html/lib/coradoc/html/converters/ordered.rb +1 -1
- data/coradoc-html/lib/coradoc/html/converters/span.rb +2 -2
- data/coradoc-html/lib/coradoc/html/converters/table.rb +3 -3
- data/coradoc-html/lib/coradoc/html/converters/table_cell.rb +14 -28
- data/coradoc-html/lib/coradoc/html/converters/table_row.rb +2 -2
- data/coradoc-html/lib/coradoc/html/converters/text_element.rb +1 -5
- data/coradoc-html/lib/coradoc/html/input/converters/a.rb +2 -2
- data/coradoc-html/lib/coradoc/html/input/converters/dl.rb +1 -1
- data/coradoc-html/lib/coradoc/html/input/converters/figure.rb +2 -2
- data/coradoc-html/lib/coradoc/html/input/converters/markup.rb +3 -3
- data/coradoc-html/lib/coradoc/html/input/converters/p.rb +1 -1
- data/coradoc-html/lib/coradoc/html/input/converters/td.rb +1 -1
- data/coradoc-html/lib/coradoc/html/input/converters.rb +1 -2
- data/coradoc-html/lib/coradoc/html/input/html_converter.rb +3 -3
- data/coradoc-html/lib/coradoc/html/input/plugin.rb +2 -2
- data/coradoc-html/lib/coradoc/html/renderer.rb +4 -6
- data/coradoc-html/lib/coradoc/html/theme/base.rb +2 -3
- data/coradoc-html/lib/coradoc/html/theme/classic_renderer.rb +9 -12
- data/coradoc-html/lib/coradoc/html/theme/modern/serializers/document_serializer.rb +3 -3
- data/coradoc-html/lib/coradoc/html.rb +1 -1
- data/coradoc-markdown/lib/coradoc/markdown/model/base.rb +6 -5
- data/coradoc-markdown/lib/coradoc/markdown/serializer.rb +2 -2
- data/coradoc-markdown/lib/coradoc/markdown/toc_generator.rb +4 -5
- data/coradoc-markdown/lib/coradoc/markdown/transform/from_core_model.rb +2 -2
- data/coradoc-markdown/lib/coradoc/markdown/transformer.rb +5 -3
- data/coradoc-markdown/lib/coradoc/markdown.rb +1 -1
- data/lib/coradoc/configurable.rb +6 -2
- data/lib/coradoc/coradoc.rb +18 -16
- data/lib/coradoc/core_model/base.rb +3 -3
- data/lib/coradoc/core_model/list_item.rb +3 -3
- data/lib/coradoc/core_model/toc_generator.rb +1 -1
- data/lib/coradoc/document_manipulator.rb +9 -13
- data/lib/coradoc/format_module.rb +16 -4
- data/lib/coradoc/input.rb +1 -1
- data/lib/coradoc/output.rb +1 -1
- data/lib/coradoc/query.rb +38 -186
- data/lib/coradoc/registry.rb +5 -7
- data/lib/coradoc/serializer/registry.rb +3 -5
- data/lib/coradoc/validation.rb +40 -21
- data/lib/coradoc/version.rb +1 -1
- metadata +1 -1
|
@@ -45,10 +45,8 @@ module Coradoc
|
|
|
45
45
|
private
|
|
46
46
|
|
|
47
47
|
def effective_props(run)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return ep if ep
|
|
51
|
-
end
|
|
48
|
+
ep = run.effective_run_properties
|
|
49
|
+
return ep if ep
|
|
52
50
|
|
|
53
51
|
run.properties
|
|
54
52
|
end
|
|
@@ -66,15 +64,12 @@ module Coradoc
|
|
|
66
64
|
result << context.transform(drawing)
|
|
67
65
|
end
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
result << "\t" if run.respond_to?(:tab) && run.tab
|
|
67
|
+
result << "\t" if run.tab
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
result << context.transform(run.o_math) if run.respond_to?(:o_math) && run.o_math
|
|
69
|
+
result << context.transform(run.o_math) if run.class.attributes.key?(:o_math) && run.o_math
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
text = run.del_text.respond_to?(:content) ? run.del_text.content.to_s : run.del_text.to_s
|
|
71
|
+
if run.del_text
|
|
72
|
+
text = run.del_text.is_a?(Uniword::Wordprocessingml::DeletedText) ? run.del_text.content.to_s : run.del_text.to_s
|
|
78
73
|
unless text.empty?
|
|
79
74
|
result << CoreModel::InlineElement.new(
|
|
80
75
|
format_type: 'strikethrough',
|
|
@@ -83,42 +78,37 @@ module Coradoc
|
|
|
83
78
|
end
|
|
84
79
|
end
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
sym = run.sym
|
|
89
|
-
char = sym.respond_to?(:char) ? sym.char : nil
|
|
81
|
+
if run.sym
|
|
82
|
+
char = run.sym.char
|
|
90
83
|
result << char.to_s if char && !char.empty?
|
|
91
84
|
end
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
result << "\u2011" if run.respond_to?(:no_break_hyphen) && run.no_break_hyphen
|
|
86
|
+
result << "\u2011" if run.no_break_hyphen
|
|
95
87
|
|
|
96
|
-
|
|
97
|
-
result << "\u00AD" if run.respond_to?(:soft_hyphen) && run.soft_hyphen
|
|
88
|
+
result << "\u00AD" if run.class.attributes.key?(:soft_hyphen) && run.soft_hyphen
|
|
98
89
|
|
|
99
|
-
|
|
100
|
-
result << CoreModel::InlineElement.new(format_type: 'hard_line_break') if run.respond_to?(:carriage_return) && run.carriage_return
|
|
90
|
+
result << CoreModel::InlineElement.new(format_type: 'hard_line_break') if run.class.attributes.key?(:carriage_return) && run.carriage_return
|
|
101
91
|
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
if run.alternate_content
|
|
93
|
+
result << extract_alternate_content(run.alternate_content,
|
|
94
|
+
context)
|
|
95
|
+
end
|
|
104
96
|
|
|
105
97
|
result.compact
|
|
106
98
|
end
|
|
107
99
|
|
|
108
100
|
def extract_alternate_content(ac, context)
|
|
109
|
-
|
|
110
|
-
content = if ac.respond_to?(:fallback) && ac.fallback
|
|
101
|
+
content = if ac.fallback
|
|
111
102
|
ac.fallback
|
|
112
|
-
elsif ac.
|
|
103
|
+
elsif ac.choice
|
|
113
104
|
ac.choice
|
|
114
105
|
end
|
|
115
106
|
|
|
116
107
|
return nil unless content
|
|
117
108
|
|
|
118
|
-
|
|
119
|
-
if content.respond_to?(:runs)
|
|
109
|
+
if content.is_a?(Uniword::Wordprocessingml::Run)
|
|
120
110
|
content.runs&.each { |r| context.transform(r) }
|
|
121
|
-
elsif content.
|
|
111
|
+
elsif content.is_a?(Uniword::Wordprocessingml::Paragraph)
|
|
122
112
|
content.paragraphs&.flat_map { |p| context.transform(p) }
|
|
123
113
|
end
|
|
124
114
|
end
|
|
@@ -144,7 +134,7 @@ module Coradoc
|
|
|
144
134
|
return nil unless props
|
|
145
135
|
|
|
146
136
|
# Check rStyle for semantic role
|
|
147
|
-
if context.style_resolver.
|
|
137
|
+
if context.style_resolver.is_a?(Coradoc::Docx::Transform::StyleResolver)
|
|
148
138
|
role = context.style_resolver.run_semantic_role(run)
|
|
149
139
|
case role
|
|
150
140
|
when :monospace then return 'monospace'
|
|
@@ -62,15 +62,17 @@ module Coradoc
|
|
|
62
62
|
private
|
|
63
63
|
|
|
64
64
|
def field_text(field)
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
if field.runs && !field.runs.empty?
|
|
66
|
+
return field.runs.map do |r|
|
|
67
|
+
r.text&.content.to_s
|
|
68
|
+
end.join
|
|
69
|
+
end
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
field.respond_to?(:text) ? field.text.to_s : nil
|
|
71
|
+
nil
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
def field_instruction(field)
|
|
73
|
-
instr = field.
|
|
75
|
+
instr = field.instr
|
|
74
76
|
instr.to_s
|
|
75
77
|
end
|
|
76
78
|
|
|
@@ -32,7 +32,7 @@ module Coradoc
|
|
|
32
32
|
def transform_row(row, context)
|
|
33
33
|
CoreModel::TableRow.new(
|
|
34
34
|
cells: row.cells.map { |c| transform_cell(c, context) },
|
|
35
|
-
header: row.
|
|
35
|
+
header: row.header?
|
|
36
36
|
)
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -69,17 +69,15 @@ module Coradoc
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def cell_paragraphs(cell)
|
|
72
|
-
cell.
|
|
72
|
+
cell.paragraphs || []
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def header_cell?(cell)
|
|
76
76
|
return false unless cell.properties
|
|
77
|
-
return false unless cell.properties.respond_to?(:v_merge)
|
|
78
77
|
|
|
79
78
|
vm = cell.properties.v_merge
|
|
80
|
-
vm
|
|
79
|
+
vm&.value.to_s == 'restart'
|
|
81
80
|
end
|
|
82
|
-
|
|
83
81
|
end
|
|
84
82
|
end
|
|
85
83
|
end
|
|
@@ -51,18 +51,16 @@ module Coradoc
|
|
|
51
51
|
style_name = resolve_style_name(paragraph)
|
|
52
52
|
return true if style_name && HEADING_PATTERN.match?(style_name)
|
|
53
53
|
|
|
54
|
-
# Check outline_level on paragraph properties
|
|
55
54
|
ol = paragraph.properties.outline_level
|
|
56
55
|
if ol
|
|
57
|
-
ol_level = ol.
|
|
56
|
+
ol_level = ol.is_a?(Uniword::Wordprocessingml::OutlineLevel) ? ol.value.to_i : ol.to_i
|
|
58
57
|
return true if ol_level.positive?
|
|
59
58
|
end
|
|
60
59
|
|
|
61
|
-
# Check outline_level from style definition
|
|
62
60
|
style = find_style_for_paragraph(paragraph)
|
|
63
|
-
if style
|
|
61
|
+
if style&.outline_level
|
|
64
62
|
ol_val = style.outline_level
|
|
65
|
-
ol_val = ol_val.
|
|
63
|
+
ol_val = ol_val.is_a?(Uniword::Wordprocessingml::OutlineLevel) ? ol_val.value.to_i : ol_val.to_i
|
|
66
64
|
return true if ol_val.positive?
|
|
67
65
|
end
|
|
68
66
|
|
|
@@ -82,7 +80,7 @@ module Coradoc
|
|
|
82
80
|
# Check outline_level on paragraph properties
|
|
83
81
|
ol = paragraph.properties&.outline_level
|
|
84
82
|
if ol
|
|
85
|
-
level = ol.
|
|
83
|
+
level = ol.is_a?(Uniword::Wordprocessingml::OutlineLevel) ? ol.value.to_i : ol.to_i
|
|
86
84
|
return level if level.positive?
|
|
87
85
|
end
|
|
88
86
|
|
|
@@ -138,10 +136,9 @@ module Coradoc
|
|
|
138
136
|
style_ref = paragraph.properties&.style
|
|
139
137
|
return nil unless style_ref
|
|
140
138
|
|
|
141
|
-
value = style_ref.
|
|
139
|
+
value = style_ref.is_a?(Uniword::Wordprocessingml::PStyle) ? style_ref.val : style_ref.to_s
|
|
142
140
|
return nil unless value
|
|
143
141
|
|
|
144
|
-
# Check local style map first (for custom style aliases)
|
|
145
142
|
mapped = @style_map[value]
|
|
146
143
|
return mapped if mapped
|
|
147
144
|
|
|
@@ -152,7 +149,7 @@ module Coradoc
|
|
|
152
149
|
style_ref = run.properties.style
|
|
153
150
|
return nil unless style_ref
|
|
154
151
|
|
|
155
|
-
value = style_ref.
|
|
152
|
+
value = style_ref.is_a?(Uniword::Wordprocessingml::PStyle) ? style_ref.val : style_ref.to_s
|
|
156
153
|
return nil unless value
|
|
157
154
|
|
|
158
155
|
mapped = @style_map[value]
|
|
@@ -165,23 +162,21 @@ module Coradoc
|
|
|
165
162
|
style_id = style_id_from_paragraph(paragraph)
|
|
166
163
|
return nil unless style_id
|
|
167
164
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
@config.styles.find { |s| s.styleId == style_id }
|
|
172
|
-
end
|
|
165
|
+
return unless @config.is_a?(Uniword::Wordprocessingml::StylesConfiguration)
|
|
166
|
+
|
|
167
|
+
@config.style_by_id(style_id)
|
|
173
168
|
end
|
|
174
169
|
|
|
175
170
|
def style_id_from_paragraph(paragraph)
|
|
176
171
|
style_ref = paragraph.properties&.style
|
|
177
172
|
return nil unless style_ref
|
|
178
173
|
|
|
179
|
-
style_ref.
|
|
174
|
+
style_ref.is_a?(Uniword::Wordprocessingml::PStyle) ? style_ref.val : style_ref.to_s
|
|
180
175
|
end
|
|
181
176
|
|
|
182
177
|
def build_style_map(config)
|
|
183
178
|
return {} unless config
|
|
184
|
-
return {} unless config.
|
|
179
|
+
return {} unless config.is_a?(Uniword::Wordprocessingml::StylesConfiguration)
|
|
185
180
|
|
|
186
181
|
map = {}
|
|
187
182
|
config.styles.each do |style|
|
|
@@ -191,7 +186,6 @@ module Coradoc
|
|
|
191
186
|
|
|
192
187
|
map[id] = name
|
|
193
188
|
|
|
194
|
-
# Detect custom heading styles by basedOn chain
|
|
195
189
|
if heading_by_based_on?(config, style)
|
|
196
190
|
level = heading_level_from_chain(config, style)
|
|
197
191
|
map[id] = "Heading#{level}"
|
|
@@ -202,26 +196,27 @@ module Coradoc
|
|
|
202
196
|
end
|
|
203
197
|
|
|
204
198
|
def extract_style_name(style)
|
|
205
|
-
|
|
199
|
+
sn = style.style_name
|
|
200
|
+
return sn if sn
|
|
206
201
|
|
|
207
202
|
name = style.name
|
|
208
203
|
return nil unless name
|
|
209
204
|
|
|
210
|
-
name.
|
|
205
|
+
name.is_a?(Uniword::Wordprocessingml::StyleName) ? name.val.to_s : name.to_s
|
|
211
206
|
end
|
|
212
207
|
|
|
213
208
|
def heading_by_based_on?(config, style)
|
|
214
|
-
based_on = style.
|
|
209
|
+
based_on = style.based_on
|
|
215
210
|
return false unless based_on
|
|
216
211
|
|
|
217
212
|
visited = Set.new
|
|
218
213
|
current = style
|
|
219
214
|
while current && !visited.include?(current.styleId)
|
|
220
215
|
visited << current.styleId
|
|
221
|
-
parent_id = current.
|
|
216
|
+
parent_id = current.based_on
|
|
222
217
|
return true if parent_id && HEADING_PATTERN.match?(parent_id)
|
|
223
218
|
|
|
224
|
-
break unless parent_id
|
|
219
|
+
break unless parent_id
|
|
225
220
|
|
|
226
221
|
current = config.styles.find { |s| s.styleId == parent_id }
|
|
227
222
|
end
|
|
@@ -240,8 +235,8 @@ module Coradoc
|
|
|
240
235
|
return match[2].to_i if match
|
|
241
236
|
end
|
|
242
237
|
|
|
243
|
-
parent_id = current.
|
|
244
|
-
break unless parent_id
|
|
238
|
+
parent_id = current.based_on
|
|
239
|
+
break unless parent_id
|
|
245
240
|
|
|
246
241
|
current = config.styles.find { |s| s.styleId == parent_id }
|
|
247
242
|
end
|
|
@@ -129,14 +129,13 @@ module Coradoc
|
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
def section_break?(paragraph)
|
|
132
|
-
return false unless paragraph.
|
|
132
|
+
return false unless paragraph.is_a?(Uniword::Wordprocessingml::Paragraph)
|
|
133
133
|
return false unless paragraph.properties
|
|
134
134
|
|
|
135
135
|
sect_pr = paragraph.properties.section_properties
|
|
136
136
|
return false unless sect_pr
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
sect_pr.respond_to?(:type) && sect_pr.type
|
|
138
|
+
sect_pr.type ? true : false
|
|
140
139
|
end
|
|
141
140
|
|
|
142
141
|
def section_break_element(paragraph, context)
|
|
@@ -182,12 +181,12 @@ module Coradoc
|
|
|
182
181
|
end
|
|
183
182
|
|
|
184
183
|
def body_ordered_elements(body)
|
|
185
|
-
order = body.
|
|
184
|
+
order = body.is_a?(Uniword::Wordprocessingml::Body) ? body.element_order : nil
|
|
186
185
|
return body.elements if order.nil? || order.empty?
|
|
187
186
|
|
|
188
187
|
p_idx = tbl_idx = sdt_idx = 0
|
|
189
188
|
order.filter_map do |entry|
|
|
190
|
-
name = entry.
|
|
189
|
+
name = entry.is_a?(String) ? entry : entry.name
|
|
191
190
|
case name
|
|
192
191
|
when 'p'
|
|
193
192
|
para = body.paragraphs[p_idx]
|
|
@@ -216,7 +215,7 @@ module Coradoc
|
|
|
216
215
|
doc_footnotes = document.footnotes
|
|
217
216
|
if doc_footnotes.is_a?(Hash)
|
|
218
217
|
doc_footnotes.each do |id, fn|
|
|
219
|
-
paragraphs = fn.
|
|
218
|
+
paragraphs = fn.is_a?(Uniword::Wordprocessingml::Footnote) ? fn.paragraphs : fn[:content]
|
|
220
219
|
footnotes[id.to_s] = Array(paragraphs)
|
|
221
220
|
end
|
|
222
221
|
end
|
|
@@ -280,7 +279,10 @@ module Coradoc
|
|
|
280
279
|
end
|
|
281
280
|
|
|
282
281
|
def extract_from_parts(document, method, prefix, core_doc)
|
|
283
|
-
parts =
|
|
282
|
+
parts = case method
|
|
283
|
+
when :headers then document.headers
|
|
284
|
+
when :footers then document.footers
|
|
285
|
+
end
|
|
284
286
|
return unless parts
|
|
285
287
|
|
|
286
288
|
Array(parts).each_with_index do |part, idx|
|
|
@@ -288,13 +290,18 @@ module Coradoc
|
|
|
288
290
|
next if text.nil? || text.strip.empty?
|
|
289
291
|
next if layout_only_text?(text)
|
|
290
292
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
part_type = if part.is_a?(Uniword::Wordprocessingml::Header) ||
|
|
294
|
+
part.is_a?(Uniword::Wordprocessingml::Footer)
|
|
295
|
+
part.type
|
|
296
|
+
else
|
|
297
|
+
idx
|
|
298
|
+
end
|
|
299
|
+
core_doc.set_metadata("docx.#{prefix}.#{part_type}", text.strip)
|
|
293
300
|
end
|
|
294
301
|
end
|
|
295
302
|
|
|
296
303
|
def extract_part_text(part)
|
|
297
|
-
paragraphs = part.
|
|
304
|
+
paragraphs = part.paragraphs || []
|
|
298
305
|
return nil unless paragraphs
|
|
299
306
|
|
|
300
307
|
paragraphs.map do |para|
|
|
@@ -303,7 +310,7 @@ module Coradoc
|
|
|
303
310
|
end
|
|
304
311
|
|
|
305
312
|
def extract_paragraph_text_content(para)
|
|
306
|
-
runs = para.
|
|
313
|
+
runs = para.runs || []
|
|
307
314
|
return nil unless runs
|
|
308
315
|
|
|
309
316
|
runs.map { |r| r.text&.content.to_s }.join
|
|
@@ -91,7 +91,9 @@ module Coradoc
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
# Auto-register :docx format with Coradoc when both gems are loaded
|
|
94
|
-
Coradoc.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
unless Coradoc.registered_formats.include?(:docx)
|
|
95
|
+
Coradoc.register_format(:docx, Coradoc::Docx,
|
|
96
|
+
aliases: %w[docx],
|
|
97
|
+
extensions: %w[.docx],
|
|
98
|
+
binary: true)
|
|
99
|
+
end
|
|
@@ -120,7 +120,9 @@ RSpec.describe Coradoc::Docx::Transform::FromCoreModel do
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
context 'with Abbreviation' do
|
|
123
|
-
let(:core_model)
|
|
123
|
+
let(:core_model) do
|
|
124
|
+
Coradoc::CoreModel::Abbreviation.new(term: 'API', definition: 'Application Programming Interface')
|
|
125
|
+
end
|
|
124
126
|
|
|
125
127
|
it 'produces a Paragraph with term and definition' do
|
|
126
128
|
expect(result).to be_a(Uniword::Wordprocessingml::Paragraph)
|
|
@@ -245,7 +247,8 @@ RSpec.describe Coradoc::Docx::Transform::FromCoreModel do
|
|
|
245
247
|
@tmpdir = Dir.mktmpdir
|
|
246
248
|
img_path = File.join(@tmpdir, 'test.png')
|
|
247
249
|
# Minimal 1x1 PNG
|
|
248
|
-
File.binwrite(img_path,
|
|
250
|
+
File.binwrite(img_path,
|
|
251
|
+
"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\x0f\x00\x00\x01\x01\x00\x05\x18\xd8N\x00\x00\x00\x00IEND\xAEB`\x82".b)
|
|
249
252
|
Coradoc::CoreModel::Image.new(src: img_path, alt: 'Test image')
|
|
250
253
|
end
|
|
251
254
|
|
|
@@ -148,7 +148,10 @@ RSpec.describe Coradoc::Docx::Transform::Rules::ParagraphRule do
|
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
describe '#apply' do
|
|
151
|
-
let(:context)
|
|
151
|
+
let(:context) do
|
|
152
|
+
build_context(registry: build_registry_with(described_class.new, Coradoc::Docx::Transform::Rules::RunRule.new,
|
|
153
|
+
Coradoc::Docx::Transform::Rules::TextRule.new))
|
|
154
|
+
end
|
|
152
155
|
|
|
153
156
|
it 'produces a Block paragraph with text content' do
|
|
154
157
|
para = build_paragraph('Hello')
|
|
@@ -188,7 +191,10 @@ RSpec.describe Coradoc::Docx::Transform::Rules::TableRule do
|
|
|
188
191
|
end
|
|
189
192
|
|
|
190
193
|
describe '#apply' do
|
|
191
|
-
let(:context)
|
|
194
|
+
let(:context) do
|
|
195
|
+
build_context(registry: build_registry_with(described_class.new, Coradoc::Docx::Transform::Rules::ParagraphRule.new,
|
|
196
|
+
Coradoc::Docx::Transform::Rules::RunRule.new, Coradoc::Docx::Transform::Rules::TextRule.new))
|
|
197
|
+
end
|
|
192
198
|
|
|
193
199
|
it 'produces a CoreModel::Table with rows and cells' do
|
|
194
200
|
table = build_table([%w[A B], %w[C D]])
|
|
@@ -213,7 +219,10 @@ RSpec.describe Coradoc::Docx::Transform::Rules::ListItemRule do
|
|
|
213
219
|
end
|
|
214
220
|
|
|
215
221
|
describe '#apply' do
|
|
216
|
-
let(:context)
|
|
222
|
+
let(:context) do
|
|
223
|
+
build_context(registry: build_registry_with(Coradoc::Docx::Transform::Rules::RunRule.new,
|
|
224
|
+
Coradoc::Docx::Transform::Rules::TextRule.new))
|
|
225
|
+
end
|
|
217
226
|
|
|
218
227
|
it 'produces a ListItem with content' do
|
|
219
228
|
para = build_list_paragraph('Item 1', num_id: 1)
|
|
@@ -242,7 +251,10 @@ RSpec.describe Coradoc::Docx::Transform::Rules::HeadingRule do
|
|
|
242
251
|
end
|
|
243
252
|
|
|
244
253
|
describe '#apply' do
|
|
245
|
-
let(:context)
|
|
254
|
+
let(:context) do
|
|
255
|
+
build_context(registry: build_registry_with(Coradoc::Docx::Transform::Rules::RunRule.new,
|
|
256
|
+
Coradoc::Docx::Transform::Rules::TextRule.new))
|
|
257
|
+
end
|
|
246
258
|
|
|
247
259
|
it 'produces a StructuralElement section with title and level' do
|
|
248
260
|
para = build_heading('My Title', level: 2)
|
|
@@ -363,7 +375,9 @@ RSpec.describe Coradoc::Docx::Transform::Rules::HyperlinkRule do
|
|
|
363
375
|
|
|
364
376
|
describe '#apply' do
|
|
365
377
|
let(:run_rule) { Coradoc::Docx::Transform::Rules::RunRule.new }
|
|
366
|
-
let(:context)
|
|
378
|
+
let(:context) do
|
|
379
|
+
build_context(registry: build_registry_with(run_rule, Coradoc::Docx::Transform::Rules::TextRule.new))
|
|
380
|
+
end
|
|
367
381
|
|
|
368
382
|
it 'produces InlineElement link with external URL' do
|
|
369
383
|
hl = build_hyperlink(
|
|
@@ -419,7 +433,10 @@ RSpec.describe Coradoc::Docx::Transform::Rules::SimpleFieldRule do
|
|
|
419
433
|
end
|
|
420
434
|
|
|
421
435
|
describe '#apply' do
|
|
422
|
-
let(:context)
|
|
436
|
+
let(:context) do
|
|
437
|
+
build_context(registry: build_registry_with(Coradoc::Docx::Transform::Rules::RunRule.new,
|
|
438
|
+
Coradoc::Docx::Transform::Rules::TextRule.new))
|
|
439
|
+
end
|
|
423
440
|
|
|
424
441
|
it 'returns nil for PAGE field (print layout)' do
|
|
425
442
|
field = Uniword::Wordprocessingml::SimpleField.new
|
|
@@ -449,7 +466,10 @@ RSpec.describe Coradoc::Docx::Transform::Rules::StructuredDocumentTagRule do
|
|
|
449
466
|
end
|
|
450
467
|
|
|
451
468
|
describe '#apply' do
|
|
452
|
-
let(:context)
|
|
469
|
+
let(:context) do
|
|
470
|
+
build_context(registry: build_registry_with(Coradoc::Docx::Transform::Rules::ParagraphRule.new,
|
|
471
|
+
Coradoc::Docx::Transform::Rules::RunRule.new, Coradoc::Docx::Transform::Rules::TextRule.new))
|
|
472
|
+
end
|
|
453
473
|
|
|
454
474
|
it 'unwraps SDT content and delegates to paragraph rules' do
|
|
455
475
|
sdt = Uniword::Wordprocessingml::StructuredDocumentTag.new
|
|
@@ -221,7 +221,9 @@ RSpec.describe Coradoc::Docx::Transform::ToCoreModel do
|
|
|
221
221
|
core = transform_to_core(doc)
|
|
222
222
|
|
|
223
223
|
blocks = core.children.select { |c| c.is_a?(Coradoc::CoreModel::Block) }
|
|
224
|
-
inline = blocks.first.children.find
|
|
224
|
+
inline = blocks.first.children.find do |c|
|
|
225
|
+
c.is_a?(Coradoc::CoreModel::InlineElement) && c.format_type == 'subscript'
|
|
226
|
+
end
|
|
225
227
|
expect(inline).not_to be_nil
|
|
226
228
|
expect(inline.content).to eq('2')
|
|
227
229
|
end
|
|
@@ -241,7 +243,9 @@ RSpec.describe Coradoc::Docx::Transform::ToCoreModel do
|
|
|
241
243
|
core = transform_to_core(doc)
|
|
242
244
|
|
|
243
245
|
blocks = core.children.select { |c| c.is_a?(Coradoc::CoreModel::Block) }
|
|
244
|
-
inline = blocks.first.children.find
|
|
246
|
+
inline = blocks.first.children.find do |c|
|
|
247
|
+
c.is_a?(Coradoc::CoreModel::InlineElement) && c.format_type == 'superscript'
|
|
248
|
+
end
|
|
245
249
|
expect(inline).not_to be_nil
|
|
246
250
|
expect(inline.content).to eq('2')
|
|
247
251
|
end
|
|
@@ -125,14 +125,10 @@ module Coradoc
|
|
|
125
125
|
def extract_attributes(model)
|
|
126
126
|
attrs = {}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
attrs[:
|
|
128
|
+
attrs[:id] = model.id if model.id
|
|
129
|
+
attrs[:title] = model.title if model.title
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
attrs[:title] = model.title if model.respond_to?(:title) && model.title
|
|
133
|
-
|
|
134
|
-
# Extract class/role if available
|
|
135
|
-
if model.respond_to?(:metadata) && model.metadata
|
|
131
|
+
if model.is_a?(Coradoc::CoreModel::StructuralElement) && model.metadata
|
|
136
132
|
attrs[:class] = model.metadata[:class] || model.metadata[:role]
|
|
137
133
|
attrs.merge!(model.metadata.except(:class, :role))
|
|
138
134
|
end
|
|
@@ -118,25 +118,19 @@ module Coradoc
|
|
|
118
118
|
# @param config [Hash, Object] Configuration options or object
|
|
119
119
|
# @return [Object] Built configuration object
|
|
120
120
|
def build_config(config)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
config.validate! if config.respond_to?(:validate!)
|
|
121
|
+
if config.public_methods.include?(:validate!)
|
|
122
|
+
config.validate!
|
|
124
123
|
return config
|
|
125
124
|
end
|
|
126
125
|
|
|
127
|
-
# Otherwise, build from hash (subclasses should override this)
|
|
128
126
|
config
|
|
129
127
|
end
|
|
130
128
|
|
|
131
|
-
# Extract document title
|
|
132
|
-
#
|
|
133
|
-
# @return [String] Document title
|
|
134
129
|
def extract_document_title
|
|
135
|
-
|
|
136
|
-
if @document.respond_to?(:title) && @document.title
|
|
130
|
+
if @document.is_a?(Coradoc::CoreModel::StructuralElement) && @document.title
|
|
137
131
|
title = @document.title
|
|
138
132
|
return title if title.is_a?(String)
|
|
139
|
-
return title.text if title.
|
|
133
|
+
return title.text if title.is_a?(Coradoc::CoreModel::Base) && title.text
|
|
140
134
|
|
|
141
135
|
return title.to_s
|
|
142
136
|
end
|
|
@@ -144,10 +138,6 @@ module Coradoc
|
|
|
144
138
|
'Untitled Document'
|
|
145
139
|
end
|
|
146
140
|
|
|
147
|
-
# Extract text from content (array of inline elements)
|
|
148
|
-
#
|
|
149
|
-
# @param content [Array] Content elements
|
|
150
|
-
# @return [String] Extracted text
|
|
151
141
|
def extract_text_from_content(content)
|
|
152
142
|
case content
|
|
153
143
|
when Array
|
|
@@ -157,7 +147,7 @@ module Coradoc
|
|
|
157
147
|
when Coradoc::CoreModel::InlineElement
|
|
158
148
|
content.content.to_s
|
|
159
149
|
when Coradoc::CoreModel::Base
|
|
160
|
-
if content.
|
|
150
|
+
if content.content
|
|
161
151
|
extract_text_from_content(content.content)
|
|
162
152
|
else
|
|
163
153
|
content.to_s
|
|
@@ -96,7 +96,10 @@ module Coradoc
|
|
|
96
96
|
|
|
97
97
|
return render_core_bibliography(content, state) if content.is_a?(Coradoc::CoreModel::Bibliography)
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
if content.is_a?(Coradoc::CoreModel::BibliographyEntry)
|
|
100
|
+
return render_core_bibliography_entry(content,
|
|
101
|
+
state)
|
|
102
|
+
end
|
|
100
103
|
|
|
101
104
|
# Handle unknown types gracefully
|
|
102
105
|
handle_unknown_content(content, state)
|
|
@@ -431,37 +434,28 @@ module Coradoc
|
|
|
431
434
|
|
|
432
435
|
# Extract text from unknown model types as a fallback
|
|
433
436
|
def extract_text_fallback(content)
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
return text_val.to_s if text_val.respond_to?(:to_s)
|
|
439
|
-
end
|
|
437
|
+
if content.is_a?(Coradoc::CoreModel::Base)
|
|
438
|
+
if content.class.attributes.key?(:text) && content.text
|
|
439
|
+
text_val = content.text
|
|
440
|
+
return text_val if text_val.is_a?(String)
|
|
440
441
|
|
|
441
|
-
|
|
442
|
-
if content.respond_to?(:content) && content.content
|
|
443
|
-
content_val = content.content
|
|
444
|
-
if content_val.is_a?(String)
|
|
445
|
-
return content_val
|
|
446
|
-
elsif content_val.is_a?(Array)
|
|
447
|
-
return content_val.map { |item| convert_content_to_html(item, {}) }.join
|
|
442
|
+
return text_val.to_s
|
|
448
443
|
end
|
|
449
|
-
end
|
|
450
444
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
# Try name attribute
|
|
461
|
-
return content.name.to_s if content.respond_to?(:name) && content.name
|
|
445
|
+
if content.class.attributes.key?(:content) && content.content
|
|
446
|
+
content_val = content.content
|
|
447
|
+
if content_val.is_a?(String)
|
|
448
|
+
return content_val
|
|
449
|
+
elsif content_val.is_a?(Array)
|
|
450
|
+
return content_val.map { |item| convert_content_to_html(item, {}) }.join
|
|
451
|
+
end
|
|
452
|
+
end
|
|
462
453
|
|
|
463
|
-
|
|
464
|
-
|
|
454
|
+
return content.href.to_s if content.class.attributes.key?(:href) && content.href
|
|
455
|
+
return content.term.to_s if content.class.attributes.key?(:term) && content.term
|
|
456
|
+
return content.id.to_s if content.class.attributes.key?(:id) && content.id
|
|
457
|
+
return content.name.to_s if content.class.attributes.key?(:name) && content.name
|
|
458
|
+
end
|
|
465
459
|
|
|
466
460
|
''
|
|
467
461
|
end
|
|
@@ -12,9 +12,9 @@ module Coradoc
|
|
|
12
12
|
return '' unless options[:preserve_comments]
|
|
13
13
|
|
|
14
14
|
# Get comment text - check for content or text attribute
|
|
15
|
-
text = if comment.
|
|
15
|
+
text = if comment.content
|
|
16
16
|
comment.content
|
|
17
|
-
elsif comment.
|
|
17
|
+
elsif comment.text
|
|
18
18
|
comment.text
|
|
19
19
|
else
|
|
20
20
|
''
|