uniword 1.0.10 → 1.2.4
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/config/font_metadata.yml +56 -0
- data/lib/uniword/accessibility/rules/descriptive_headings_rule.rb +1 -1
- data/lib/uniword/assembly/toc.rb +4 -14
- data/lib/uniword/assembly/variable_substitutor.rb +3 -4
- data/lib/uniword/batch/stages/normalize_styles_stage.rb +4 -2
- data/lib/uniword/builder/base_builder.rb +25 -0
- data/lib/uniword/builder/chart_builder.rb +33 -14
- data/lib/uniword/builder/deterministic_id.rb +17 -0
- data/lib/uniword/builder/document_builder.rb +129 -34
- data/lib/uniword/builder/footnote_builder.rb +22 -23
- data/lib/uniword/builder/header_footer_builder.rb +10 -10
- data/lib/uniword/builder/image_builder.rb +24 -16
- data/lib/uniword/builder/list_builder.rb +2 -1
- data/lib/uniword/builder/paragraph_builder.rb +95 -20
- data/lib/uniword/builder/run_utils.rb +97 -0
- data/lib/uniword/builder/section_builder.rb +40 -67
- data/lib/uniword/builder/table_builder.rb +32 -13
- data/lib/uniword/builder/table_cell_builder.rb +19 -0
- data/lib/uniword/builder.rb +27 -19
- data/lib/uniword/cli/diff_cli.rb +0 -1
- data/lib/uniword/cli/generate_cli.rb +0 -1
- data/lib/uniword/cli/headers_cli.rb +0 -1
- data/lib/uniword/cli/images_cli.rb +0 -1
- data/lib/uniword/cli/main.rb +6 -7
- data/lib/uniword/cli/protect_cli.rb +0 -1
- data/lib/uniword/cli/resources_cli.rb +0 -1
- data/lib/uniword/cli/review_cli.rb +0 -1
- data/lib/uniword/cli/spellcheck_cli.rb +0 -1
- data/lib/uniword/cli/styleset_cli.rb +0 -1
- data/lib/uniword/cli/template_cli.rb +0 -1
- data/lib/uniword/cli/theme_cli.rb +0 -1
- data/lib/uniword/cli/toc_cli.rb +0 -1
- data/lib/uniword/cli/watermark_cli.rb +0 -1
- data/lib/uniword/cli.rb +6 -17
- data/lib/uniword/diff/document_differ.rb +23 -5
- data/lib/uniword/diff/package_differ.rb +0 -1
- data/lib/uniword/diff.rb +4 -0
- data/lib/uniword/document_factory.rb +16 -24
- data/lib/uniword/docx/document_statistics.rb +42 -54
- data/lib/uniword/docx/id_allocator.rb +143 -0
- data/lib/uniword/docx/package.rb +42 -6
- data/lib/uniword/docx/package_defaults.rb +16 -25
- data/lib/uniword/docx/package_serialization.rb +131 -127
- data/lib/uniword/docx/reconciler/body.rb +197 -0
- data/lib/uniword/docx/reconciler/fix_codes.rb +74 -0
- data/lib/uniword/docx/reconciler/helpers.rb +337 -0
- data/lib/uniword/docx/reconciler/notes.rb +301 -0
- data/lib/uniword/docx/reconciler/package_structure.rb +322 -0
- data/lib/uniword/docx/reconciler/parts.rb +484 -0
- data/lib/uniword/docx/reconciler/referential_integrity.rb +449 -0
- data/lib/uniword/docx/reconciler/tables.rb +162 -0
- data/lib/uniword/docx/reconciler/theme.rb +141 -0
- data/lib/uniword/docx/reconciler.rb +44 -1069
- data/lib/uniword/docx.rb +3 -0
- data/lib/uniword/drawingml/color_scheme.rb +38 -5
- data/lib/uniword/hyperlink.rb +27 -26
- data/lib/uniword/infrastructure/zip_packager.rb +41 -38
- data/lib/uniword/lazy_loader.rb +36 -62
- data/lib/uniword/math.rb +3 -86
- data/lib/uniword/mhtml/document.rb +1 -3
- data/lib/uniword/model_attribute_access.rb +27 -0
- data/lib/uniword/ooxml/namespaces.rb +62 -2
- data/lib/uniword/ooxml/relationships/package_relationships.rb +7 -0
- data/lib/uniword/presentationml/slide_id.rb +0 -1
- data/lib/uniword/presentationml/slide_master_id.rb +0 -1
- data/lib/uniword/properties/bold.rb +0 -2
- data/lib/uniword/properties/boolean_formatting.rb +0 -2
- data/lib/uniword/properties/borders.rb +1 -1
- data/lib/uniword/properties/italic.rb +0 -2
- data/lib/uniword/properties/numbering_properties.rb +1 -1
- data/lib/uniword/properties/style_reference.rb +13 -0
- data/lib/uniword/quality/rules/heading_hierarchy_rule.rb +1 -1
- data/lib/uniword/quality/rules/style_consistency_rule.rb +1 -1
- data/lib/uniword/review/review_manager.rb +0 -2
- data/lib/uniword/template/helpers/loop_helper.rb +2 -2
- data/lib/uniword/template/variable_resolver.rb +14 -1
- data/lib/uniword/theme/theme_xml_parser.rb +1 -1
- data/lib/uniword/toc/toc_generator.rb +3 -14
- data/lib/uniword/transformation/mhtml_element_renderer.rb +94 -37
- data/lib/uniword/transformation/mhtml_metadata_builder.rb +1 -1
- data/lib/uniword/transformation/mhtml_style_builder.rb +9 -2
- data/lib/uniword/transformation/ooxml_to_html_converter.rb +3 -5
- data/lib/uniword/transformation/ooxml_to_mhtml_converter.rb +7 -15
- data/lib/uniword/transformation/yaml_css_generator.rb +815 -0
- data/lib/uniword/transformation.rb +2 -0
- data/lib/uniword/validation/checkers/internal_link_checker.rb +2 -3
- data/lib/uniword/version.rb +1 -1
- data/lib/uniword/visitor/text_extractor.rb +2 -1
- data/lib/uniword/watermark/manager.rb +2 -2
- data/lib/uniword/wordprocessingml/allow_png.rb +14 -0
- data/lib/uniword/wordprocessingml/body.rb +53 -18
- data/lib/uniword/wordprocessingml/body_div.rb +17 -0
- data/lib/uniword/wordprocessingml/div_border.rb +23 -0
- data/lib/uniword/wordprocessingml/div_borders.rb +24 -0
- data/lib/uniword/wordprocessingml/divs_child.rb +17 -0
- data/lib/uniword/wordprocessingml/document_root.rb +6 -3
- data/lib/uniword/wordprocessingml/endnote.rb +1 -1
- data/lib/uniword/wordprocessingml/endnotes.rb +4 -0
- data/lib/uniword/wordprocessingml/footer.rb +4 -0
- data/lib/uniword/wordprocessingml/footer_reference.rb +0 -1
- data/lib/uniword/wordprocessingml/footnote.rb +1 -1
- data/lib/uniword/wordprocessingml/footnotes.rb +4 -0
- data/lib/uniword/wordprocessingml/header.rb +4 -0
- data/lib/uniword/wordprocessingml/header_reference.rb +0 -1
- data/lib/uniword/wordprocessingml/hyperlink.rb +2 -2
- data/lib/uniword/wordprocessingml/instr_text.rb +20 -5
- data/lib/uniword/wordprocessingml/level.rb +2 -0
- data/lib/uniword/wordprocessingml/mar_bottom.rb +17 -0
- data/lib/uniword/wordprocessingml/mar_left.rb +17 -0
- data/lib/uniword/wordprocessingml/mar_right.rb +17 -0
- data/lib/uniword/wordprocessingml/mar_top.rb +17 -0
- data/lib/uniword/wordprocessingml/num.rb +4 -0
- data/lib/uniword/wordprocessingml/numbering.rb +1 -22
- data/lib/uniword/wordprocessingml/optimize_for_browser.rb +14 -0
- data/lib/uniword/wordprocessingml/page_defaults.rb +39 -0
- data/lib/uniword/wordprocessingml/paragraph.rb +16 -13
- data/lib/uniword/wordprocessingml/paragraph_properties.rb +12 -8
- data/lib/uniword/wordprocessingml/run.rb +32 -36
- data/lib/uniword/wordprocessingml/run_properties/merging.rb +45 -0
- data/lib/uniword/wordprocessingml/run_properties.rb +8 -5
- data/lib/uniword/wordprocessingml/section_properties.rb +3 -3
- data/lib/uniword/wordprocessingml/separator_char.rb +21 -0
- data/lib/uniword/wordprocessingml/settings.rb +19 -17
- data/lib/uniword/wordprocessingml/styles/style_definition.rb +1 -1
- data/lib/uniword/wordprocessingml/styles/style_library.rb +16 -0
- data/lib/uniword/wordprocessingml/table_defaults.rb +44 -0
- data/lib/uniword/wordprocessingml/text.rb +8 -2
- data/lib/uniword/wordprocessingml/web_div.rb +41 -0
- data/lib/uniword/wordprocessingml/web_divs.rb +18 -0
- data/lib/uniword/wordprocessingml/web_encoding.rb +17 -0
- data/lib/uniword/wordprocessingml/web_settings.rb +5 -21
- data/lib/uniword/wordprocessingml/zoom.rb +2 -0
- data/lib/uniword/wordprocessingml.rb +23 -0
- data/lib/uniword.rb +27 -0
- metadata +47 -70
- data/lib/uniword/math/accent.rb +0 -25
- data/lib/uniword/math/accent_properties.rb +0 -25
- data/lib/uniword/math/argument_properties.rb +0 -22
- data/lib/uniword/math/bar.rb +0 -25
- data/lib/uniword/math/bar_properties.rb +0 -25
- data/lib/uniword/math/begin_char.rb +0 -22
- data/lib/uniword/math/border_box.rb +0 -25
- data/lib/uniword/math/border_box_properties.rb +0 -39
- data/lib/uniword/math/box.rb +0 -25
- data/lib/uniword/math/box_properties.rb +0 -33
- data/lib/uniword/math/char.rb +0 -22
- data/lib/uniword/math/control_properties.rb +0 -27
- data/lib/uniword/math/degree.rb +0 -26
- data/lib/uniword/math/delimiter.rb +0 -25
- data/lib/uniword/math/delimiter_properties.rb +0 -33
- data/lib/uniword/math/denominator.rb +0 -26
- data/lib/uniword/math/element.rb +0 -61
- data/lib/uniword/math/end_char.rb +0 -22
- data/lib/uniword/math/equation_array.rb +0 -25
- data/lib/uniword/math/equation_array_properties.rb +0 -33
- data/lib/uniword/math/fraction.rb +0 -27
- data/lib/uniword/math/fraction_properties.rb +0 -25
- data/lib/uniword/math/function.rb +0 -27
- data/lib/uniword/math/function_name.rb +0 -26
- data/lib/uniword/math/function_properties.rb +0 -23
- data/lib/uniword/math/group_char.rb +0 -25
- data/lib/uniword/math/group_char_properties.rb +0 -29
- data/lib/uniword/math/lim.rb +0 -26
- data/lib/uniword/math/lower_limit.rb +0 -27
- data/lib/uniword/math/lower_limit_properties.rb +0 -23
- data/lib/uniword/math/math_break.rb +0 -22
- data/lib/uniword/math/math_font.rb +0 -22
- data/lib/uniword/math/math_properties.rb +0 -53
- data/lib/uniword/math/math_run.rb +0 -42
- data/lib/uniword/math/math_run_properties.rb +0 -37
- data/lib/uniword/math/math_simple_int_val.rb +0 -24
- data/lib/uniword/math/math_simple_val.rb +0 -24
- data/lib/uniword/math/math_style.rb +0 -22
- data/lib/uniword/math/math_text.rb +0 -23
- data/lib/uniword/math/matrix.rb +0 -25
- data/lib/uniword/math/matrix_column.rb +0 -23
- data/lib/uniword/math/matrix_column_properties.rb +0 -24
- data/lib/uniword/math/matrix_columns.rb +0 -23
- data/lib/uniword/math/matrix_properties.rb +0 -37
- data/lib/uniword/math/matrix_row.rb +0 -23
- data/lib/uniword/math/nary.rb +0 -29
- data/lib/uniword/math/nary_properties.rb +0 -33
- data/lib/uniword/math/numerator.rb +0 -26
- data/lib/uniword/math/o_math.rb +0 -65
- data/lib/uniword/math/o_math_para.rb +0 -25
- data/lib/uniword/math/o_math_para_properties.rb +0 -22
- data/lib/uniword/math/phantom.rb +0 -25
- data/lib/uniword/math/phantom_properties.rb +0 -33
- data/lib/uniword/math/pre_sub_superscript.rb +0 -29
- data/lib/uniword/math/pre_sub_superscript_properties.rb +0 -23
- data/lib/uniword/math/radical.rb +0 -27
- data/lib/uniword/math/radical_properties.rb +0 -25
- data/lib/uniword/math/separator_char.rb +0 -22
- data/lib/uniword/math/sub.rb +0 -26
- data/lib/uniword/math/sub_superscript.rb +0 -29
- data/lib/uniword/math/sub_superscript_properties.rb +0 -25
- data/lib/uniword/math/subscript.rb +0 -27
- data/lib/uniword/math/subscript_properties.rb +0 -23
- data/lib/uniword/math/sup.rb +0 -26
- data/lib/uniword/math/superscript.rb +0 -27
- data/lib/uniword/math/superscript_properties.rb +0 -23
- data/lib/uniword/math/upper_limit.rb +0 -27
- data/lib/uniword/math/upper_limit_properties.rb +0 -23
|
@@ -24,6 +24,13 @@ module Uniword
|
|
|
24
24
|
# Generate package-level .rels file
|
|
25
25
|
#
|
|
26
26
|
# @return [PackageRelationships] Relationships object for _rels/.rels
|
|
27
|
+
def self.next_available_rid(relationships)
|
|
28
|
+
max = relationships.relationships.filter_map do |r|
|
|
29
|
+
r.id[/\ArId(\d+)\z/, 1]&.to_i
|
|
30
|
+
end.max || 0
|
|
31
|
+
"rId#{max + 1}"
|
|
32
|
+
end
|
|
33
|
+
|
|
27
34
|
def self.generate_package_rels
|
|
28
35
|
new(
|
|
29
36
|
relationships: [
|
|
@@ -28,8 +28,8 @@ module Uniword
|
|
|
28
28
|
namespace Ooxml::Namespaces::WordProcessingML
|
|
29
29
|
|
|
30
30
|
map_element "top", to: :top, render_nil: false
|
|
31
|
-
map_element "bottom", to: :bottom, render_nil: false
|
|
32
31
|
map_element "left", to: :left, render_nil: false
|
|
32
|
+
map_element "bottom", to: :bottom, render_nil: false
|
|
33
33
|
map_element "right", to: :right, render_nil: false
|
|
34
34
|
map_element "between", to: :between, render_nil: false
|
|
35
35
|
map_element "bar", to: :bar, render_nil: false
|
|
@@ -17,8 +17,8 @@ module Uniword
|
|
|
17
17
|
element "numPr"
|
|
18
18
|
namespace Ooxml::Namespaces::WordProcessingML
|
|
19
19
|
|
|
20
|
-
map_element "numId", to: :num_id, render_nil: false
|
|
21
20
|
map_element "ilvl", to: :ilvl, render_nil: false
|
|
21
|
+
map_element "numId", to: :num_id, render_nil: false
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -17,6 +17,19 @@ module Uniword
|
|
|
17
17
|
map_attribute "val", to: :value
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
# Cast raw style-id strings into references (mirrors Text.cast), so
|
|
21
|
+
# `properties.style = "Heading1"` stores a proper StyleReference
|
|
22
|
+
def self.cast(value)
|
|
23
|
+
case value
|
|
24
|
+
when StyleReference, nil
|
|
25
|
+
value
|
|
26
|
+
when String
|
|
27
|
+
new(value: value)
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
20
33
|
# Compare with another StyleReference or a string
|
|
21
34
|
def ==(other)
|
|
22
35
|
if other.is_a?(StyleReference)
|
|
@@ -72,7 +72,7 @@ module Uniword
|
|
|
72
72
|
# @param paragraph [Paragraph] The paragraph to check
|
|
73
73
|
# @return [Integer, nil] Heading level (1-9) or nil if not a heading
|
|
74
74
|
def extract_heading_level(paragraph)
|
|
75
|
-
style_name = paragraph.
|
|
75
|
+
style_name = paragraph.style
|
|
76
76
|
return nil unless style_name
|
|
77
77
|
|
|
78
78
|
match = style_name.to_s.match(HEADING_PATTERN)
|
|
@@ -105,12 +105,12 @@ module Uniword
|
|
|
105
105
|
# recursively process all text nodes
|
|
106
106
|
return unless element.is_a?(Uniword::Wordprocessingml::Run)
|
|
107
107
|
|
|
108
|
-
text = element.
|
|
108
|
+
text = element.text_string
|
|
109
109
|
# Simple variable replacement ({{var}})
|
|
110
110
|
text.scan(/\{\{([^@].+?)\}\}/).each do |match|
|
|
111
111
|
var_name = match[0]
|
|
112
112
|
value = resolver.resolve(var_name)
|
|
113
|
-
element.
|
|
113
|
+
element.substitute("{{#{var_name}}}", value.to_s)
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
116
|
end
|
|
@@ -175,7 +175,20 @@ module Uniword
|
|
|
175
175
|
|
|
176
176
|
return false if left_num.nil? || right_num.nil?
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
case operator
|
|
179
|
+
when :== then left_num == right_num
|
|
180
|
+
when :!= then left_num != right_num
|
|
181
|
+
when :> then left_num > right_num
|
|
182
|
+
when :>= then left_num >= right_num
|
|
183
|
+
when :< then left_num < right_num
|
|
184
|
+
when :<= then left_num <= right_num
|
|
185
|
+
when :+ then left_num + right_num
|
|
186
|
+
when :- then left_num - right_num
|
|
187
|
+
when :* then left_num * right_num
|
|
188
|
+
when :/ then left_num / right_num
|
|
189
|
+
else
|
|
190
|
+
raise ArgumentError, "Unsupported operator: #{operator.inspect}"
|
|
191
|
+
end
|
|
179
192
|
end
|
|
180
193
|
|
|
181
194
|
# Convert value to number
|
|
@@ -79,7 +79,7 @@ module Uniword
|
|
|
79
79
|
|
|
80
80
|
# Set the color using the appropriate attribute
|
|
81
81
|
attr_name = color_name == "folHlink" ? :fol_hlink : color_name.to_sym
|
|
82
|
-
scheme.
|
|
82
|
+
scheme.assign_color_by_name(attr_name, color_obj)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
# Sync hash interface
|
|
@@ -142,14 +142,7 @@ module Uniword
|
|
|
142
142
|
# @param paragraph [Wordprocessingml::Paragraph] Paragraph to inspect
|
|
143
143
|
# @return [String, nil] Style name or nil
|
|
144
144
|
def resolve_style_name(paragraph)
|
|
145
|
-
|
|
146
|
-
return nil unless style_ref
|
|
147
|
-
|
|
148
|
-
if style_ref.is_a?(Uniword::Properties::StyleReference)
|
|
149
|
-
style_ref.value
|
|
150
|
-
else
|
|
151
|
-
style_ref.to_s
|
|
152
|
-
end
|
|
145
|
+
paragraph.style&.value
|
|
153
146
|
end
|
|
154
147
|
|
|
155
148
|
# Extract text content from a paragraph.
|
|
@@ -197,9 +190,7 @@ module Uniword
|
|
|
197
190
|
def build_title_paragraph
|
|
198
191
|
para = Wordprocessingml::Paragraph.new
|
|
199
192
|
para.properties = Wordprocessingml::ParagraphProperties.new
|
|
200
|
-
para.properties.style =
|
|
201
|
-
value: "TOCHeading",
|
|
202
|
-
)
|
|
193
|
+
para.properties.style = "TOCHeading"
|
|
203
194
|
|
|
204
195
|
run = Wordprocessingml::Run.new
|
|
205
196
|
run.text = "Table of Contents"
|
|
@@ -251,9 +242,7 @@ module Uniword
|
|
|
251
242
|
def build_entry_paragraph(entry)
|
|
252
243
|
para = Wordprocessingml::Paragraph.new
|
|
253
244
|
para.properties = Wordprocessingml::ParagraphProperties.new
|
|
254
|
-
para.properties.style =
|
|
255
|
-
value: "TOC#{entry.level}",
|
|
256
|
-
)
|
|
245
|
+
para.properties.style = "TOC#{entry.level}"
|
|
257
246
|
|
|
258
247
|
run = Wordprocessingml::Run.new
|
|
259
248
|
run.text = entry.text
|
|
@@ -69,7 +69,8 @@ module Uniword
|
|
|
69
69
|
# Convert OOXML Run to HTML
|
|
70
70
|
def run_to_html(run)
|
|
71
71
|
# Handle breaks and drawings first (these never get style wrapping)
|
|
72
|
-
|
|
72
|
+
# (parsed runs hold [] for absent collections, so check emptiness)
|
|
73
|
+
return break_to_html(run.break) if Array(run.break).any?
|
|
73
74
|
if run.drawings && !run.drawings.empty?
|
|
74
75
|
return drawing_to_html(run.drawings.first)
|
|
75
76
|
end
|
|
@@ -105,7 +106,8 @@ module Uniword
|
|
|
105
106
|
# Skip wrapping for internal OOXML styles that shouldn't produce
|
|
106
107
|
# visible HTML class wrappers
|
|
107
108
|
is_internal_style = %w[stem hyperlink].include?(style_id)
|
|
108
|
-
is_stem_spacer = (style_id == "stem") &&
|
|
109
|
+
is_stem_spacer = (style_id == "stem") &&
|
|
110
|
+
run.text_string.strip.empty?
|
|
109
111
|
unless is_internal_style || is_stem_spacer
|
|
110
112
|
style_val = run_style_to_class(props.style.value)
|
|
111
113
|
content = %(<span class="#{style_val}">#{content}</span>)
|
|
@@ -125,7 +127,7 @@ module Uniword
|
|
|
125
127
|
elsif run.tab
|
|
126
128
|
tab_to_html(run)
|
|
127
129
|
else
|
|
128
|
-
text = run.
|
|
130
|
+
text = run.text_string
|
|
129
131
|
return "" if text.empty?
|
|
130
132
|
|
|
131
133
|
escape_html(text)
|
|
@@ -142,13 +144,15 @@ module Uniword
|
|
|
142
144
|
%(<span class="MsoEndnoteReference"><span style="mso-special-character:endnote"></span></span>)
|
|
143
145
|
end
|
|
144
146
|
|
|
145
|
-
# Convert OOXML
|
|
146
|
-
def break_to_html(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
# Convert OOXML Breaks to HTML, one <br> per Break element
|
|
148
|
+
def break_to_html(breaks)
|
|
149
|
+
Array(breaks).map do |brk|
|
|
150
|
+
if brk.type == "page"
|
|
151
|
+
%(<br clear="all" style="mso-special-character:line-break;page-break-before:always" />)
|
|
152
|
+
else
|
|
153
|
+
%(<br />)
|
|
154
|
+
end
|
|
155
|
+
end.join
|
|
152
156
|
end
|
|
153
157
|
|
|
154
158
|
# Convert OOXML FieldChar to HTML span
|
|
@@ -272,7 +276,9 @@ module Uniword
|
|
|
272
276
|
end
|
|
273
277
|
end
|
|
274
278
|
|
|
275
|
-
# Convert OOXML OMath to HTML (wrapped in stem span)
|
|
279
|
+
# Convert an OOXML OMath model to HTML (wrapped in stem span).
|
|
280
|
+
# Accepts Omml::Models::CTOMath/OMath/OMathPara (the typed attribute
|
|
281
|
+
# values on Paragraph) — anything responding to `to_xml` works.
|
|
276
282
|
def omath_to_html(o_math)
|
|
277
283
|
xml = o_math.to_xml
|
|
278
284
|
# Strip XML declaration and namespace prefixes for clean inline HTML
|
|
@@ -491,23 +497,87 @@ module Uniword
|
|
|
491
497
|
|
|
492
498
|
private
|
|
493
499
|
|
|
494
|
-
#
|
|
495
|
-
#
|
|
500
|
+
# Render paragraph content (runs, hyperlinks, SDTs, oMath) in the
|
|
501
|
+
# document order captured by `paragraph.element_order`.
|
|
502
|
+
#
|
|
503
|
+
# Walking element_order (rather than `build_position_map` keyed on
|
|
504
|
+
# run_position) keeps MHTML positioning in lockstep with the DOCX
|
|
505
|
+
# serializer, which is the single source of truth for interleaving.
|
|
506
|
+
# It also handles inline math, which is now an Omml::Models::CTOMath
|
|
507
|
+
# instance without a run_position attribute.
|
|
496
508
|
def paragraph_content_to_html(paragraph)
|
|
509
|
+
order = paragraph.element_order
|
|
510
|
+
return paragraph_content_to_html_legacy(paragraph) if order.nil? || order.empty?
|
|
511
|
+
|
|
512
|
+
run_idx = 0
|
|
513
|
+
hl_idx = 0
|
|
514
|
+
omath_idx = 0
|
|
515
|
+
omp_idx = 0
|
|
516
|
+
sdt_idx = 0
|
|
517
|
+
|
|
518
|
+
parts = []
|
|
519
|
+
order.each do |entry|
|
|
520
|
+
next unless entry.element?
|
|
521
|
+
|
|
522
|
+
case entry.name
|
|
523
|
+
when "r"
|
|
524
|
+
collect_run_at(paragraph, run_idx, parts)
|
|
525
|
+
run_idx += 1
|
|
526
|
+
when "hyperlink"
|
|
527
|
+
hl = paragraph.hyperlinks[hl_idx]
|
|
528
|
+
parts << hyperlink_to_html(hl) if hl
|
|
529
|
+
hl_idx += 1
|
|
530
|
+
when "oMath"
|
|
531
|
+
om = paragraph.o_maths[omath_idx]
|
|
532
|
+
parts << omath_to_html(om) if om
|
|
533
|
+
omath_idx += 1
|
|
534
|
+
when "oMathPara"
|
|
535
|
+
omp = paragraph.o_math_paras[omp_idx]
|
|
536
|
+
parts << omath_to_html(omp) if omp
|
|
537
|
+
omp_idx += 1
|
|
538
|
+
when "sdt"
|
|
539
|
+
sdt = paragraph.sdts[sdt_idx]
|
|
540
|
+
parts << sdt_to_inline_html(sdt) if sdt
|
|
541
|
+
sdt_idx += 1
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# Defensive: emit any math/SDTs not represented in element_order.
|
|
546
|
+
paragraph.o_math_paras.each_with_index do |omp, i|
|
|
547
|
+
next if i < omp_idx
|
|
548
|
+
|
|
549
|
+
parts << omath_to_html(omp)
|
|
550
|
+
end
|
|
551
|
+
paragraph.sdts.each_with_index do |sdt, i|
|
|
552
|
+
next if i < sdt_idx
|
|
553
|
+
|
|
554
|
+
parts << sdt_to_inline_html(sdt)
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
parts.join
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def collect_run_at(paragraph, idx, parts)
|
|
561
|
+
run = paragraph.runs[idx]
|
|
562
|
+
return unless run
|
|
563
|
+
|
|
564
|
+
parts << if run.is_a?(Uniword::Wordprocessingml::StructuredDocumentTag)
|
|
565
|
+
sdt_to_inline_html(run)
|
|
566
|
+
else
|
|
567
|
+
run_to_html(run)
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
# Fallback for Paragraphs constructed without element_order tracking
|
|
572
|
+
# (older code paths). Preserves the previous behavior of appending
|
|
573
|
+
# inline math/hyperlinks at the end.
|
|
574
|
+
def paragraph_content_to_html_legacy(paragraph)
|
|
497
575
|
run_count = paragraph.runs.size
|
|
498
576
|
|
|
499
|
-
# Build position-indexed maps of hyperlinks and oMaths
|
|
500
577
|
hl_by_pos = build_position_map(paragraph.hyperlinks, run_count)
|
|
501
|
-
omath_by_pos = build_position_map(paragraph.o_maths, run_count)
|
|
502
578
|
|
|
503
579
|
parts = []
|
|
504
580
|
paragraph.runs.each_with_index do |run, idx|
|
|
505
|
-
# Insert any oMaths that should appear at this position
|
|
506
|
-
if omath_by_pos.key?(idx)
|
|
507
|
-
omath_by_pos[idx].each { |om| parts << omath_to_html(om) }
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
# Insert any hyperlinks that should appear at this position
|
|
511
581
|
if hl_by_pos.key?(idx)
|
|
512
582
|
hl_by_pos[idx].each { |hl| parts << hyperlink_to_html(hl) }
|
|
513
583
|
end
|
|
@@ -519,26 +589,13 @@ module Uniword
|
|
|
519
589
|
end
|
|
520
590
|
end
|
|
521
591
|
|
|
522
|
-
# Append any remaining oMaths at the end
|
|
523
|
-
if omath_by_pos.key?(run_count)
|
|
524
|
-
omath_by_pos[run_count].each { |om| parts << omath_to_html(om) }
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
# Append any remaining hyperlinks at the end
|
|
528
592
|
if hl_by_pos.key?(run_count)
|
|
529
593
|
hl_by_pos[run_count].each { |hl| parts << hyperlink_to_html(hl) }
|
|
530
594
|
end
|
|
531
595
|
|
|
532
|
-
paragraph.
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
parts << omath_to_html(om)
|
|
536
|
-
end
|
|
537
|
-
end
|
|
538
|
-
|
|
539
|
-
paragraph.sdts.each do |sdt|
|
|
540
|
-
parts << sdt_to_inline_html(sdt)
|
|
541
|
-
end
|
|
596
|
+
paragraph.o_maths.each { |om| parts << omath_to_html(om) }
|
|
597
|
+
paragraph.o_math_paras.each { |omp| parts << omath_to_html(omp) }
|
|
598
|
+
paragraph.sdts.each { |sdt| parts << sdt_to_inline_html(sdt) }
|
|
542
599
|
|
|
543
600
|
parts.join
|
|
544
601
|
end
|
|
@@ -697,6 +697,12 @@ module Uniword
|
|
|
697
697
|
</style>
|
|
698
698
|
LSB_STYLE
|
|
699
699
|
|
|
700
|
+
class << self
|
|
701
|
+
# Custom CSS override set by callers (e.g., ISO adapter).
|
|
702
|
+
# When set, #style_block returns this instead of the default.
|
|
703
|
+
attr_accessor :custom_style_block
|
|
704
|
+
end
|
|
705
|
+
|
|
700
706
|
# Build the w:LatentStyles block.
|
|
701
707
|
# @return [String] Static latent styles XML
|
|
702
708
|
def self.latent_styles
|
|
@@ -704,9 +710,10 @@ module Uniword
|
|
|
704
710
|
end
|
|
705
711
|
|
|
706
712
|
# Build the CSS style block.
|
|
707
|
-
#
|
|
713
|
+
# Returns custom CSS if set via custom_style_block=, otherwise default.
|
|
714
|
+
# @return [String] CSS style block
|
|
708
715
|
def self.style_block
|
|
709
|
-
STYLE_BLOCK
|
|
716
|
+
custom_style_block || STYLE_BLOCK
|
|
710
717
|
end
|
|
711
718
|
end
|
|
712
719
|
end
|
|
@@ -54,7 +54,7 @@ module Uniword
|
|
|
54
54
|
# @param run [Uniword::Wordprocessingml::Run]
|
|
55
55
|
# @return [String] HTML text content with inline formatting
|
|
56
56
|
def self.run_to_html(run)
|
|
57
|
-
text = escape_html(run.
|
|
57
|
+
text = escape_html(run.text_string)
|
|
58
58
|
return text if text.empty?
|
|
59
59
|
|
|
60
60
|
props = run.properties
|
|
@@ -135,12 +135,10 @@ module Uniword
|
|
|
135
135
|
# @param paragraph [Uniword::Wordprocessingml::Paragraph]
|
|
136
136
|
# @return [String] HTML class/style attribute or empty string
|
|
137
137
|
def self.paragraph_style(paragraph)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
style = paragraph.properties.style
|
|
138
|
+
style = paragraph.style
|
|
141
139
|
return "" unless style
|
|
142
140
|
|
|
143
|
-
" class=\"#{escape_html(style)}\""
|
|
141
|
+
" class=\"#{escape_html(style.to_s)}\""
|
|
144
142
|
end
|
|
145
143
|
|
|
146
144
|
# Convert OOXML font size (half-points) to HTML font size
|
|
@@ -18,7 +18,8 @@ module Uniword
|
|
|
18
18
|
# output = Uniword::Infrastructure::MimePackager.new(mhtml_doc).build_mime_content
|
|
19
19
|
#
|
|
20
20
|
class OoxmlToMhtmlConverter
|
|
21
|
-
# Static MsoNormalTable CSS (used in wrap_html_document head)
|
|
21
|
+
# Static MsoNormalTable CSS (used in wrap_html_document head).
|
|
22
|
+
# Only used when MhtmlStyleBuilder does not provide custom CSS.
|
|
22
23
|
MSO_NORMAL_TABLE_STYLE = <<~CSS
|
|
23
24
|
<!--[if gte mso 10]>
|
|
24
25
|
<style>
|
|
@@ -30,21 +31,12 @@ module Uniword
|
|
|
30
31
|
mso-style-noshow:yes;
|
|
31
32
|
mso-style-priority:99;
|
|
32
33
|
mso-style-parent:"";
|
|
33
|
-
mso-padding-alt:
|
|
34
|
-
mso-para-margin
|
|
35
|
-
mso-para-margin-
|
|
36
|
-
mso-para-margin-bottom:8.0pt;
|
|
37
|
-
mso-para-margin-left:0in;
|
|
38
|
-
line-height:115%;
|
|
34
|
+
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
|
|
35
|
+
mso-para-margin:0cm;
|
|
36
|
+
mso-para-margin-bottom:.0001pt;
|
|
39
37
|
mso-pagination:widow-orphan;
|
|
40
|
-
font-size:
|
|
41
|
-
font-family:"
|
|
42
|
-
mso-ascii-font-family:Aptos;
|
|
43
|
-
mso-ascii-theme-font:minor-latin;
|
|
44
|
-
mso-hansi-font-family:Aptos;
|
|
45
|
-
mso-hansi-theme-font:minor-latin;
|
|
46
|
-
mso-font-kerning:1.0pt;
|
|
47
|
-
mso-ligatures:standardcontextual;}
|
|
38
|
+
font-size:10pt;
|
|
39
|
+
font-family:"Cambria",serif;}
|
|
48
40
|
</style>
|
|
49
41
|
<![endif]-->
|
|
50
42
|
CSS
|