coradoc-docx 0.1.0 → 0.1.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/Rakefile +8 -0
- data/lib/coradoc/docx/transform/from_core_model.rb +15 -44
- data/lib/coradoc/docx/transform/frontmatter_core_properties.rb +107 -0
- data/lib/coradoc/docx/transform/numbering_resolver.rb +3 -3
- data/lib/coradoc/docx/transform/ordered_content.rb +15 -3
- data/lib/coradoc/docx/transform/rules/break_rule.rb +1 -1
- data/lib/coradoc/docx/transform/rules/heading_rule.rb +1 -2
- data/lib/coradoc/docx/transform/rules/math_rule.rb +1 -2
- data/lib/coradoc/docx/transform/rules/paragraph_rule.rb +1 -1
- data/lib/coradoc/docx/transform/style_resolver.rb +6 -1
- data/lib/coradoc/docx/transform/to_core_model.rb +2 -3
- data/lib/coradoc/docx/transform.rb +1 -0
- data/lib/coradoc/docx/version.rb +1 -1
- data/lib/coradoc/docx.rb +0 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2d3e5bd2f76d67000623cf3a2cb3889d5b49105a7420494f6f09622bac8a61b
|
|
4
|
+
data.tar.gz: 2c611f389f427ac79c73b5ce6e727e602b1d392f97f6090af837d2c6a5d05d9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: faff02134845b773db02e2e8e8f4a0f99e58548b013db9ad99212fe349f1e8be7088fc153465bb32b54c584c05030d24c8cc5a07dbf0e8365ab2cd3368ed5bc6
|
|
7
|
+
data.tar.gz: 308659e8aaee319757fb35fd19abf69296aebe00e8d31d4405b9d262454cd1e8e020aed702a6ab043664dd13e64670bf2758bcfda41ef7c80115d6c02d4f513b
|
data/Rakefile
ADDED
|
@@ -82,57 +82,26 @@ module Coradoc
|
|
|
82
82
|
private
|
|
83
83
|
|
|
84
84
|
def transform_structural_element(element)
|
|
85
|
-
|
|
86
|
-
when 'document'
|
|
87
|
-
transform_document(element)
|
|
88
|
-
when 'section'
|
|
89
|
-
transform_section(element)
|
|
90
|
-
else
|
|
91
|
-
transform_section(element)
|
|
92
|
-
end
|
|
85
|
+
transform_document(element)
|
|
93
86
|
end
|
|
94
87
|
|
|
95
88
|
def transform_document(element)
|
|
96
89
|
doc = Uniword::Builder::DocumentBuilder.new
|
|
97
90
|
doc.title(element.title) if element.title
|
|
98
91
|
|
|
92
|
+
FrontmatterCoreProperties.apply(doc, FrontmatterCoreProperties.extract(element))
|
|
93
|
+
|
|
99
94
|
transform_children(element.children, doc)
|
|
100
95
|
|
|
101
96
|
doc.build
|
|
102
97
|
end
|
|
103
98
|
|
|
104
|
-
def transform_section(element)
|
|
105
|
-
paragraphs = []
|
|
106
|
-
|
|
107
|
-
paragraphs << build_heading(element.title, level: element.heading_level) if element.title
|
|
108
|
-
|
|
109
|
-
element.children&.each do |child|
|
|
110
|
-
case child
|
|
111
|
-
when Coradoc::CoreModel::StructuralElement
|
|
112
|
-
paragraphs << transform_section(child)
|
|
113
|
-
when Coradoc::CoreModel::AnnotationBlock
|
|
114
|
-
paragraphs << transform_annotation_block(child)
|
|
115
|
-
when Coradoc::CoreModel::Block
|
|
116
|
-
paragraphs << build_ooxml_paragraph(child)
|
|
117
|
-
when Coradoc::CoreModel::ListBlock
|
|
118
|
-
paragraphs.concat(build_ooxml_list(child))
|
|
119
|
-
when Coradoc::CoreModel::Table
|
|
120
|
-
paragraphs << build_ooxml_table(child)
|
|
121
|
-
when Coradoc::CoreModel::Image
|
|
122
|
-
paragraphs << build_ooxml_image(child)
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
paragraphs
|
|
127
|
-
end
|
|
128
|
-
|
|
129
99
|
def transform_block(block)
|
|
130
|
-
|
|
131
|
-
|
|
100
|
+
semantic = block.resolve_semantic_type
|
|
101
|
+
case semantic
|
|
102
|
+
when :page_break
|
|
132
103
|
build_page_break
|
|
133
|
-
when
|
|
134
|
-
build_ooxml_paragraph(block)
|
|
135
|
-
when 'comment'
|
|
104
|
+
when :comment
|
|
136
105
|
nil
|
|
137
106
|
else
|
|
138
107
|
build_ooxml_paragraph(block)
|
|
@@ -206,8 +175,8 @@ module Coradoc
|
|
|
206
175
|
end
|
|
207
176
|
|
|
208
177
|
def add_block_to_builder(block, builder)
|
|
209
|
-
case block.
|
|
210
|
-
when
|
|
178
|
+
case block.resolve_semantic_type
|
|
179
|
+
when :page_break
|
|
211
180
|
builder.page_break
|
|
212
181
|
else
|
|
213
182
|
add_paragraph_to_builder(block, builder)
|
|
@@ -224,10 +193,12 @@ module Coradoc
|
|
|
224
193
|
def add_paragraph_to_builder(block, builder)
|
|
225
194
|
content = block.renderable_content
|
|
226
195
|
|
|
227
|
-
if content.is_a?(Array) && content.any? { |c| !c.is_a?(
|
|
196
|
+
if content.is_a?(Array) && content.any? { |c| !c.is_a?(Coradoc::CoreModel::TextContent) }
|
|
228
197
|
builder.paragraph do |p|
|
|
229
198
|
content.each do |child|
|
|
230
199
|
case child
|
|
200
|
+
when Coradoc::CoreModel::TextContent
|
|
201
|
+
p << child.text
|
|
231
202
|
when String
|
|
232
203
|
p << child
|
|
233
204
|
when Coradoc::CoreModel::InlineElement
|
|
@@ -242,7 +213,7 @@ module Coradoc
|
|
|
242
213
|
end
|
|
243
214
|
|
|
244
215
|
def add_list_to_builder(list_block, builder)
|
|
245
|
-
list_method = list_block.marker_type == '
|
|
216
|
+
list_method = list_block.marker_type == 'ordered' ? :numbered_list : :bullet_list
|
|
246
217
|
|
|
247
218
|
if list_method == :numbered_list
|
|
248
219
|
builder.numbered_list do |list|
|
|
@@ -300,9 +271,9 @@ module Coradoc
|
|
|
300
271
|
if content.is_a?(Array)
|
|
301
272
|
content.each do |child|
|
|
302
273
|
case child
|
|
303
|
-
when
|
|
274
|
+
when Coradoc::CoreModel::TextContent
|
|
304
275
|
run = Uniword::Wordprocessingml::Run.new
|
|
305
|
-
run.text = Uniword::Wordprocessingml::Text.new(content: child)
|
|
276
|
+
run.text = Uniword::Wordprocessingml::Text.new(content: child.text)
|
|
306
277
|
para.runs << run
|
|
307
278
|
when Coradoc::CoreModel::InlineElement
|
|
308
279
|
para.runs << build_ooxml_run(child)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Docx
|
|
5
|
+
module Transform
|
|
6
|
+
# Single source of truth for FrontmatterBlock -> OOXML core
|
|
7
|
+
# properties mapping. (MECE: DOCX-specific concerns live in the
|
|
8
|
+
# DOCX gem; CoreModel has no knowledge of OOXML.)
|
|
9
|
+
#
|
|
10
|
+
# Uniword's DocumentBuilder exposes setter methods (title, author,
|
|
11
|
+
# subject, keywords, description, date_field) that write the
|
|
12
|
+
# docProps/core.xml Dublin Core elements. This module reads
|
|
13
|
+
# FrontmatterBlock entries and invokes the right setters, so the
|
|
14
|
+
# mapping table lives in exactly one place.
|
|
15
|
+
#
|
|
16
|
+
# Mapping table (single source of truth):
|
|
17
|
+
#
|
|
18
|
+
# | Frontmatter key | OOXML element | Builder method |
|
|
19
|
+
# |-----------------------|----------------------|----------------|
|
|
20
|
+
# | title | dc:title | title |
|
|
21
|
+
# | author | dc:creator | author |
|
|
22
|
+
# | date | dc:date | created |
|
|
23
|
+
# | description, excerpt | dc:description | description |
|
|
24
|
+
# | subject | dc:subject | subject |
|
|
25
|
+
# | tags, categories | cp:keywords | keywords |
|
|
26
|
+
module FrontmatterCoreProperties
|
|
27
|
+
DESCRIPTION_KEYS = %w[description excerpt].freeze
|
|
28
|
+
KEYWORDS_KEYS = %w[tags categories].freeze
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# Apply FrontmatterBlock data to a Uniword DocumentBuilder.
|
|
32
|
+
# Skips nil/empty values. Does not overwrite builder values
|
|
33
|
+
# that the caller has set explicitly (caller invokes this
|
|
34
|
+
# AFTER setting document.title, for example).
|
|
35
|
+
#
|
|
36
|
+
# @param builder [Uniword::Builder::DocumentBuilder]
|
|
37
|
+
# @param block [Coradoc::CoreModel::FrontmatterBlock, nil]
|
|
38
|
+
# @return [void]
|
|
39
|
+
def apply(builder, block)
|
|
40
|
+
return unless block.is_a?(Coradoc::CoreModel::FrontmatterBlock)
|
|
41
|
+
|
|
42
|
+
data = block.data || {}
|
|
43
|
+
|
|
44
|
+
if (v = find_first_scalar(data, KEYWORDS_KEYS))
|
|
45
|
+
builder.keywords(v)
|
|
46
|
+
end
|
|
47
|
+
if (v = find_first_scalar(data, %w[author]))
|
|
48
|
+
builder.author(v)
|
|
49
|
+
end
|
|
50
|
+
if (v = find_first_scalar(data, %w[subject]))
|
|
51
|
+
builder.subject(v)
|
|
52
|
+
end
|
|
53
|
+
if (v = find_first_scalar(data, DESCRIPTION_KEYS))
|
|
54
|
+
builder.description(v)
|
|
55
|
+
end
|
|
56
|
+
if (v = find_first_scalar(data, %w[date]))
|
|
57
|
+
builder.created(v)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Locate the FrontmatterBlock in a DocumentElement's children,
|
|
62
|
+
# if any.
|
|
63
|
+
#
|
|
64
|
+
# @param document [Coradoc::CoreModel::DocumentElement]
|
|
65
|
+
# @return [Coradoc::CoreModel::FrontmatterBlock, nil]
|
|
66
|
+
def extract(document)
|
|
67
|
+
return nil unless document.is_a?(Coradoc::CoreModel::DocumentElement)
|
|
68
|
+
|
|
69
|
+
Array(document.children).find { |c| c.is_a?(Coradoc::CoreModel::FrontmatterBlock) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def find_first_scalar(data, keys)
|
|
75
|
+
keys.each do |k|
|
|
76
|
+
value = data[k.to_s]
|
|
77
|
+
next if value.nil?
|
|
78
|
+
|
|
79
|
+
str = scalar_value(value)
|
|
80
|
+
return str if str && !str.empty?
|
|
81
|
+
end
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def scalar_value(value)
|
|
86
|
+
return nil if value.nil?
|
|
87
|
+
|
|
88
|
+
case value
|
|
89
|
+
when String then value
|
|
90
|
+
when Integer, Float, TrueClass, FalseClass then value.to_s
|
|
91
|
+
when Date, Time, DateTime then value.iso8601
|
|
92
|
+
when Symbol then value.to_s
|
|
93
|
+
when Array then array_to_csv(value)
|
|
94
|
+
when Hash then nil
|
|
95
|
+
else value.to_s
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def array_to_csv(value)
|
|
100
|
+
strings = value.map { |i| scalar_value(i) }.compact
|
|
101
|
+
strings.any? ? strings.join(', ') : nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -112,13 +112,13 @@ module Coradoc
|
|
|
112
112
|
|
|
113
113
|
def marker_type_for(fmt)
|
|
114
114
|
case fmt
|
|
115
|
-
when 'decimal' then '
|
|
115
|
+
when 'decimal' then 'ordered'
|
|
116
116
|
when 'lowerLetter' then 'lower_alpha'
|
|
117
117
|
when 'upperLetter' then 'upper_alpha'
|
|
118
118
|
when 'lowerRoman' then 'lower_roman'
|
|
119
119
|
when 'upperRoman' then 'upper_roman'
|
|
120
|
-
when 'bullet' then '
|
|
121
|
-
else '
|
|
120
|
+
when 'bullet' then 'unordered'
|
|
121
|
+
else 'ordered'
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
124
|
end
|
|
@@ -21,12 +21,24 @@ module Coradoc
|
|
|
21
21
|
order = paragraph.is_a?(Uniword::Wordprocessingml::Paragraph) ? paragraph.element_order : nil
|
|
22
22
|
|
|
23
23
|
if order && !order.empty?
|
|
24
|
-
transform_ordered(paragraph, order, context)
|
|
24
|
+
wrap_text_children(transform_ordered(paragraph, order, context))
|
|
25
25
|
else
|
|
26
|
-
transform_sequential(paragraph, context)
|
|
26
|
+
wrap_text_children(transform_sequential(paragraph, context))
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# Wrap raw strings in TextContent so all children are Base instances.
|
|
31
|
+
#
|
|
32
|
+
# @param items [Array] mixed Strings and CoreModel::Base
|
|
33
|
+
# @return [Array<CoreModel::Base>]
|
|
34
|
+
def wrap_text_children(items)
|
|
35
|
+
items.map do |item|
|
|
36
|
+
next nil if item.nil?
|
|
37
|
+
|
|
38
|
+
item.is_a?(String) ? CoreModel::TextContent.new(text: item) : item
|
|
39
|
+
end.compact
|
|
40
|
+
end
|
|
41
|
+
|
|
30
42
|
# Flatten children array to plain text string.
|
|
31
43
|
#
|
|
32
44
|
# @param children [Array] mixed content (Strings, InlineElements, Blocks)
|
|
@@ -34,7 +46,7 @@ module Coradoc
|
|
|
34
46
|
def extract_plain_text(children)
|
|
35
47
|
children.map do |c|
|
|
36
48
|
case c
|
|
37
|
-
when
|
|
49
|
+
when CoreModel::TextContent then c.text
|
|
38
50
|
when CoreModel::InlineElement then c.content.to_s
|
|
39
51
|
when CoreModel::Block then c.content.to_s
|
|
40
52
|
else c.to_s
|
|
@@ -16,7 +16,7 @@ module Coradoc
|
|
|
16
16
|
|
|
17
17
|
def apply(brk, _context)
|
|
18
18
|
if brk.type == 'page'
|
|
19
|
-
Coradoc::CoreModel::Block.new(
|
|
19
|
+
Coradoc::CoreModel::Block.new(block_semantic_type: 'page_break')
|
|
20
20
|
else
|
|
21
21
|
Coradoc::CoreModel::InlineElement.new(
|
|
22
22
|
format_type: 'hard_line_break'
|
|
@@ -94,7 +94,12 @@ module Coradoc
|
|
|
94
94
|
return false unless paragraph.properties
|
|
95
95
|
|
|
96
96
|
num_id = paragraph.properties.num_id
|
|
97
|
-
|
|
97
|
+
# Uniword (via lutaml-model) returns an UninitializedClass
|
|
98
|
+
# sentinel for attributes that weren't deserialized from source.
|
|
99
|
+
# Treat anything that isn't a real Integer as "no list id".
|
|
100
|
+
return false unless num_id.is_a?(Integer)
|
|
101
|
+
|
|
102
|
+
num_id.positive?
|
|
98
103
|
end
|
|
99
104
|
|
|
100
105
|
# Check if paragraph has a specific role based on style name
|
|
@@ -56,8 +56,7 @@ module Coradoc
|
|
|
56
56
|
children.shift
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
doc = Coradoc::CoreModel::
|
|
60
|
-
element_type: 'document',
|
|
59
|
+
doc = Coradoc::CoreModel::DocumentElement.new(
|
|
61
60
|
title: doc_title,
|
|
62
61
|
children: children
|
|
63
62
|
)
|
|
@@ -147,7 +146,7 @@ module Coradoc
|
|
|
147
146
|
else
|
|
148
147
|
# Standalone section break → thematic break
|
|
149
148
|
Coradoc::CoreModel::Block.new(
|
|
150
|
-
|
|
149
|
+
block_semantic_type: 'thematic_break'
|
|
151
150
|
)
|
|
152
151
|
end
|
|
153
152
|
end
|
|
@@ -11,6 +11,7 @@ module Coradoc
|
|
|
11
11
|
autoload :StyleResolver, 'coradoc/docx/transform/style_resolver'
|
|
12
12
|
autoload :NumberingResolver, 'coradoc/docx/transform/numbering_resolver'
|
|
13
13
|
autoload :OrderedContent, 'coradoc/docx/transform/ordered_content'
|
|
14
|
+
autoload :FrontmatterCoreProperties, 'coradoc/docx/transform/frontmatter_core_properties'
|
|
14
15
|
|
|
15
16
|
# Element transform rules
|
|
16
17
|
module Rules
|
data/lib/coradoc/docx/version.rb
CHANGED
data/lib/coradoc/docx.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: coradoc-docx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -103,10 +103,12 @@ extensions: []
|
|
|
103
103
|
extra_rdoc_files: []
|
|
104
104
|
files:
|
|
105
105
|
- README.adoc
|
|
106
|
+
- Rakefile
|
|
106
107
|
- lib/coradoc/docx.rb
|
|
107
108
|
- lib/coradoc/docx/transform.rb
|
|
108
109
|
- lib/coradoc/docx/transform/context.rb
|
|
109
110
|
- lib/coradoc/docx/transform/from_core_model.rb
|
|
111
|
+
- lib/coradoc/docx/transform/frontmatter_core_properties.rb
|
|
110
112
|
- lib/coradoc/docx/transform/numbering_resolver.rb
|
|
111
113
|
- lib/coradoc/docx/transform/ordered_content.rb
|
|
112
114
|
- lib/coradoc/docx/transform/rule.rb
|
|
@@ -135,6 +137,7 @@ licenses:
|
|
|
135
137
|
metadata:
|
|
136
138
|
homepage_uri: https://github.com/lutaml/coradoc
|
|
137
139
|
source_code_uri: https://github.com/lutaml/coradoc
|
|
140
|
+
rubygems_mfa_required: 'true'
|
|
138
141
|
rdoc_options: []
|
|
139
142
|
require_paths:
|
|
140
143
|
- lib
|
|
@@ -142,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
142
145
|
requirements:
|
|
143
146
|
- - ">="
|
|
144
147
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: 3.
|
|
148
|
+
version: 3.3.0
|
|
146
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
150
|
requirements:
|
|
148
151
|
- - ">="
|