uniword 1.0.4 → 1.0.5
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/lib/uniword/accessibility/rules/descriptive_headings_rule.rb +7 -9
- data/lib/uniword/accessibility/rules/document_title_rule.rb +2 -2
- data/lib/uniword/accessibility/rules/heading_structure_rule.rb +4 -3
- data/lib/uniword/accessibility/rules/language_specification_rule.rb +2 -2
- data/lib/uniword/accessibility/rules/table_headers_rule.rb +1 -1
- data/lib/uniword/assembly/cross_reference_resolver.rb +6 -59
- data/lib/uniword/assembly/toc.rb +2 -2
- data/lib/uniword/assembly/variable_substitutor.rb +0 -24
- data/lib/uniword/batch/stages/compress_images_stage.rb +2 -2
- data/lib/uniword/batch/stages/normalize_styles_stage.rb +12 -12
- data/lib/uniword/batch/stages/update_metadata_stage.rb +26 -11
- data/lib/uniword/batch/stages/validate_links_stage.rb +1 -1
- data/lib/uniword/builder/bibliography_builder.rb +1 -1
- data/lib/uniword/builder/chart_builder.rb +2 -2
- data/lib/uniword/builder/has_borders.rb +1 -1
- data/lib/uniword/builder/image_builder.rb +1 -1
- data/lib/uniword/cli/main.rb +4 -4
- data/lib/uniword/diff/document_differ.rb +11 -7
- data/lib/uniword/document_factory.rb +3 -3
- data/lib/uniword/docx/document_statistics.rb +3 -3
- data/lib/uniword/docx/package_defaults.rb +2 -2
- data/lib/uniword/docx/reconciler.rb +3 -3
- data/lib/uniword/element_registry.rb +1 -1
- data/lib/uniword/endnote.rb +3 -1
- data/lib/uniword/footnote.rb +3 -1
- data/lib/uniword/format_converter.rb +7 -7
- data/lib/uniword/format_detector.rb +1 -1
- data/lib/uniword/generation/document_generator.rb +2 -2
- data/lib/uniword/infrastructure/zip_extractor.rb +1 -1
- data/lib/uniword/math_equation.rb +3 -1
- data/lib/uniword/mhtml/math_converter.rb +1 -1
- data/lib/uniword/mhtml/word_css.rb +14 -14
- data/lib/uniword/ooxml/schema/element_serializer.rb +14 -14
- data/lib/uniword/quality/document_checker.rb +2 -2
- data/lib/uniword/quality/rules/link_validation_rule.rb +4 -4
- data/lib/uniword/quality/rules/style_consistency_rule.rb +1 -1
- data/lib/uniword/quality/rules/table_header_rule.rb +1 -1
- data/lib/uniword/resource/color_transformer.rb +1 -1
- data/lib/uniword/resource/resource_resolver.rb +1 -1
- data/lib/uniword/resource/theme_processor.rb +1 -1
- data/lib/uniword/resource/theme_transition.rb +1 -1
- data/lib/uniword/review/interactive_review.rb +1 -1
- data/lib/uniword/spellcheck/spell_checker.rb +6 -6
- data/lib/uniword/styleset.rb +1 -1
- data/lib/uniword/template/helpers/conditional_helper.rb +3 -4
- data/lib/uniword/template/helpers/filter_helper.rb +1 -1
- data/lib/uniword/template/helpers/loop_helper.rb +1 -1
- data/lib/uniword/template/helpers/variable_helper.rb +1 -1
- data/lib/uniword/template/template_parser.rb +9 -7
- data/lib/uniword/template/template_renderer.rb +3 -3
- data/lib/uniword/template/variable_resolver.rb +5 -6
- data/lib/uniword/themes/theme_transformation.rb +1 -1
- data/lib/uniword/toc/toc_generator.rb +1 -1
- data/lib/uniword/transformation/mhtml_element_renderer.rb +5 -7
- data/lib/uniword/transformation/mhtml_metadata_builder.rb +17 -18
- data/lib/uniword/transformation/ooxml_to_html_converter.rb +1 -1
- data/lib/uniword/transformation/transformer.rb +6 -6
- data/lib/uniword/validation/checkers/external_link_checker.rb +1 -1
- data/lib/uniword/validation/checkers/file_reference_checker.rb +5 -5
- data/lib/uniword/validation/checkers/footnote_reference_checker.rb +12 -12
- data/lib/uniword/validation/checkers/internal_link_checker.rb +7 -9
- data/lib/uniword/validation/link_validator.rb +17 -23
- data/lib/uniword/validation/validation_report.rb +5 -3
- data/lib/uniword/validation/validation_result.rb +4 -4
- data/lib/uniword/validators/element_validator.rb +1 -7
- data/lib/uniword/version.rb +1 -1
- data/lib/uniword/visitor/text_extractor.rb +3 -3
- data/lib/uniword/warnings/warning_collector.rb +1 -1
- data/lib/uniword/watermark/manager.rb +1 -1
- data/lib/uniword/wordprocessingml/numbering_configuration.rb +1 -1
- data/lib/uniword/wordprocessingml/run.rb +4 -4
- data/lib/uniword/wordprocessingml/styles/style_definition.rb +1 -1
- metadata +2 -2
|
@@ -278,18 +278,18 @@ module Uniword
|
|
|
278
278
|
# @return [Hash] Statistics hash
|
|
279
279
|
def document_stats(document)
|
|
280
280
|
# Handle OOXML document
|
|
281
|
-
if document.
|
|
281
|
+
if document.is_a?(Uniword::Wordprocessingml::DocumentRoot) || document.is_a?(Uniword::Wordprocessingml::Body)
|
|
282
282
|
return {
|
|
283
283
|
paragraphs: document.paragraphs.count,
|
|
284
|
-
tables: document.
|
|
285
|
-
images: document.
|
|
284
|
+
tables: document.is_a?(Uniword::Wordprocessingml::DocumentRoot) ? document.tables.count : 0,
|
|
285
|
+
images: document.is_a?(Uniword::Wordprocessingml::DocumentRoot) ? document.images.count : 0
|
|
286
286
|
}
|
|
287
287
|
end
|
|
288
288
|
|
|
289
289
|
# Handle MHTML document - estimate from HTML content
|
|
290
|
-
html = if document.
|
|
290
|
+
html = if document.is_a?(Mhtml::Document) && document.raw_html
|
|
291
291
|
document.raw_html
|
|
292
|
-
elsif document.
|
|
292
|
+
elsif document.is_a?(Mhtml::Document) && document.html_content
|
|
293
293
|
document.html_content
|
|
294
294
|
end
|
|
295
295
|
if html
|
|
@@ -339,8 +339,8 @@ module Uniword
|
|
|
339
339
|
def log_conversion_start(source, source_format, target, target_format)
|
|
340
340
|
return unless @logger
|
|
341
341
|
|
|
342
|
-
source_name = source.
|
|
343
|
-
target_name = target.
|
|
342
|
+
source_name = source.is_a?(IO) ? source.path : source.to_s
|
|
343
|
+
target_name = target.is_a?(IO) ? target.path : target.to_s
|
|
344
344
|
|
|
345
345
|
@logger.info(
|
|
346
346
|
"Converting #{source_name} (#{source_format}) → #{target_name} (#{target_format})"
|
|
@@ -67,7 +67,7 @@ module Uniword
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def import_styles(builder, source_doc)
|
|
70
|
-
return unless source_doc.
|
|
70
|
+
return unless source_doc.is_a?(Uniword::Wordprocessingml::DocumentRoot)
|
|
71
71
|
return unless source_doc.styles_configuration
|
|
72
72
|
|
|
73
73
|
source_doc.styles_configuration.styles.each do |style|
|
|
@@ -79,7 +79,7 @@ module Uniword
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def import_theme(builder, source_doc)
|
|
82
|
-
return unless source_doc.
|
|
82
|
+
return unless source_doc.is_a?(Uniword::Wordprocessingml::DocumentRoot)
|
|
83
83
|
return unless source_doc.theme
|
|
84
84
|
|
|
85
85
|
builder.model.theme = source_doc.theme
|
|
@@ -126,7 +126,7 @@ module Uniword
|
|
|
126
126
|
return if path.is_a?(IO) || path.is_a?(StringIO)
|
|
127
127
|
|
|
128
128
|
# For strings, validate as file path
|
|
129
|
-
if path.
|
|
129
|
+
if path.is_a?(String) && path.empty?
|
|
130
130
|
raise ArgumentError,
|
|
131
131
|
"Path cannot be empty"
|
|
132
132
|
end
|
|
@@ -152,7 +152,9 @@ module Uniword
|
|
|
152
152
|
# If present, it should be a Plurimath formula
|
|
153
153
|
return true if formula.nil?
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
return false unless defined?(Plurimath::Formula)
|
|
156
|
+
|
|
157
|
+
formula.is_a?(Plurimath::Formula)
|
|
156
158
|
end
|
|
157
159
|
end
|
|
158
160
|
end
|
|
@@ -117,7 +117,7 @@ module Uniword
|
|
|
117
117
|
# @example Check if element is math
|
|
118
118
|
# is_math = MathConverter.math_element?(element)
|
|
119
119
|
def self.math_element?(element)
|
|
120
|
-
return false unless element.
|
|
120
|
+
return false unless element.is_a?(Nokogiri::XML::Element)
|
|
121
121
|
|
|
122
122
|
math_tags = %w[math mml:math m:oMath m:oMathPara]
|
|
123
123
|
return true if math_tags.include?(element.name)
|
|
@@ -78,14 +78,14 @@ module Uniword
|
|
|
78
78
|
# @param section_name [String] The section name
|
|
79
79
|
# @return [String, nil] The CSS @page rule
|
|
80
80
|
def self.build_page_rule(section, section_name)
|
|
81
|
-
properties = section
|
|
81
|
+
properties = section&.properties
|
|
82
82
|
return nil unless properties
|
|
83
83
|
|
|
84
84
|
rules = []
|
|
85
85
|
|
|
86
86
|
# Page size
|
|
87
|
-
width = properties.
|
|
88
|
-
height = properties.
|
|
87
|
+
width = properties.page_width
|
|
88
|
+
height = properties.page_height
|
|
89
89
|
if width && height
|
|
90
90
|
# Convert from twips to inches (1 inch = 1440 twips)
|
|
91
91
|
w_in = CssNumberFormatter.twips_to_in(width, precision: 1)
|
|
@@ -97,14 +97,14 @@ module Uniword
|
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
# Margins
|
|
100
|
-
rules << "margin-top: #{CssNumberFormatter.twips_to_in(properties.margin_top)}" if properties.
|
|
101
|
-
rules << "margin-bottom: #{CssNumberFormatter.twips_to_in(properties.margin_bottom)}" if properties.
|
|
102
|
-
rules << "margin-left: #{CssNumberFormatter.twips_to_in(properties.margin_left)}" if properties.
|
|
103
|
-
rules << "margin-right: #{CssNumberFormatter.twips_to_in(properties.margin_right)}" if properties.
|
|
100
|
+
rules << "margin-top: #{CssNumberFormatter.twips_to_in(properties.margin_top)}" if properties.margin_top
|
|
101
|
+
rules << "margin-bottom: #{CssNumberFormatter.twips_to_in(properties.margin_bottom)}" if properties.margin_bottom
|
|
102
|
+
rules << "margin-left: #{CssNumberFormatter.twips_to_in(properties.margin_left)}" if properties.margin_left
|
|
103
|
+
rules << "margin-right: #{CssNumberFormatter.twips_to_in(properties.margin_right)}" if properties.margin_right
|
|
104
104
|
|
|
105
105
|
# Header/Footer margins
|
|
106
|
-
rules << "mso-header-margin: #{CssNumberFormatter.twips_to_in(properties.header_margin)}" if properties.
|
|
107
|
-
rules << "mso-footer-margin: #{CssNumberFormatter.twips_to_in(properties.footer_margin)}" if properties.
|
|
106
|
+
rules << "mso-header-margin: #{CssNumberFormatter.twips_to_in(properties.header_margin)}" if properties.header_margin
|
|
107
|
+
rules << "mso-footer-margin: #{CssNumberFormatter.twips_to_in(properties.footer_margin)}" if properties.footer_margin
|
|
108
108
|
|
|
109
109
|
"@page #{section_name} {\n #{rules.join(";\n ")};\n}"
|
|
110
110
|
end
|
|
@@ -127,16 +127,16 @@ module Uniword
|
|
|
127
127
|
properties = []
|
|
128
128
|
|
|
129
129
|
# Font properties
|
|
130
|
-
properties << "font-family: '#{style.font}'" if style.
|
|
131
|
-
if style.
|
|
130
|
+
properties << "font-family: '#{style.font}'" if style.font
|
|
131
|
+
if style.font_size
|
|
132
132
|
properties << "font-size: #{CssNumberFormatter.format(style.font_size, 'pt',
|
|
133
133
|
precision: 1)}"
|
|
134
134
|
end
|
|
135
|
-
properties << "font-weight: bold" if style.
|
|
136
|
-
properties << "font-style: italic" if style.
|
|
135
|
+
properties << "font-weight: bold" if style.bold
|
|
136
|
+
properties << "font-style: italic" if style.italic
|
|
137
137
|
|
|
138
138
|
# Paragraph properties
|
|
139
|
-
properties << "text-align: #{style.alignment}" if style.
|
|
139
|
+
properties << "text-align: #{style.alignment}" if style.alignment
|
|
140
140
|
|
|
141
141
|
return nil if properties.empty?
|
|
142
142
|
|
|
@@ -47,7 +47,7 @@ module Uniword
|
|
|
47
47
|
|
|
48
48
|
# If element has its own to_xml method, use it directly
|
|
49
49
|
# This ensures proper serialization with all attributes and content
|
|
50
|
-
if element.
|
|
50
|
+
if element.is_a?(Lutaml::Model::Serializable) && !options[:use_schema]
|
|
51
51
|
xml_str = element.to_xml(pretty: options[:pretty])
|
|
52
52
|
|
|
53
53
|
# Remove XML declaration unless standalone
|
|
@@ -169,7 +169,7 @@ module Uniword
|
|
|
169
169
|
|
|
170
170
|
# Skip if no children and optional
|
|
171
171
|
next if children.nil? && child_def.optional?
|
|
172
|
-
next if children.
|
|
172
|
+
next if children.is_a?(Array) && children.empty? && child_def.optional?
|
|
173
173
|
|
|
174
174
|
# Serialize child elements
|
|
175
175
|
if child_def.multiple?
|
|
@@ -215,9 +215,9 @@ module Uniword
|
|
|
215
215
|
namespace_uri)
|
|
216
216
|
node.content = child
|
|
217
217
|
node
|
|
218
|
-
elsif child.
|
|
218
|
+
elsif child.is_a?(Uniword::Element)
|
|
219
219
|
# Check if this is a TextElement that needs special handling
|
|
220
|
-
if child.instance_of?(::Uniword::TextElement) && child.
|
|
220
|
+
if child.instance_of?(::Uniword::TextElement) && child.is_a?(Uniword::TextElement)
|
|
221
221
|
# TextElement: serialize as element with text content
|
|
222
222
|
node = Nokogiri::XML::Node.new(local_name, doc)
|
|
223
223
|
node.namespace = node.add_namespace_definition(prefix,
|
|
@@ -276,7 +276,7 @@ module Uniword
|
|
|
276
276
|
namespace_uri)
|
|
277
277
|
node.content = child
|
|
278
278
|
node
|
|
279
|
-
elsif child.
|
|
279
|
+
elsif child.is_a?(Uniword::TextElement)
|
|
280
280
|
# TextElement or similar - get the content
|
|
281
281
|
node = Nokogiri::XML::Node.new(local_name, doc)
|
|
282
282
|
node.namespace = node.add_namespace_definition(prefix,
|
|
@@ -302,10 +302,10 @@ module Uniword
|
|
|
302
302
|
property_name = attr_def.property_name
|
|
303
303
|
|
|
304
304
|
# Try to get value using property name
|
|
305
|
-
if element.
|
|
306
|
-
element.
|
|
307
|
-
elsif element.
|
|
308
|
-
element
|
|
305
|
+
if element.is_a?(Lutaml::Model::Serializable) && element.class.attributes.key?(property_name)
|
|
306
|
+
element.public_send(property_name)
|
|
307
|
+
elsif element.is_a?(Hash) && element.key?(property_name)
|
|
308
|
+
element[property_name]
|
|
309
309
|
end
|
|
310
310
|
end
|
|
311
311
|
|
|
@@ -318,10 +318,10 @@ module Uniword
|
|
|
318
318
|
property_name = child_def.property_name
|
|
319
319
|
|
|
320
320
|
# Try to get children using property name
|
|
321
|
-
if element.
|
|
322
|
-
element.
|
|
323
|
-
elsif element.
|
|
324
|
-
element
|
|
321
|
+
if element.is_a?(Lutaml::Model::Serializable) && element.class.attributes.key?(property_name)
|
|
322
|
+
element.public_send(property_name)
|
|
323
|
+
elsif element.is_a?(Hash) && element.key?(property_name)
|
|
324
|
+
element[property_name]
|
|
325
325
|
end
|
|
326
326
|
end
|
|
327
327
|
|
|
@@ -330,7 +330,7 @@ module Uniword
|
|
|
330
330
|
# @param element [Object] Object to validate
|
|
331
331
|
# @raise [ArgumentError] if not a valid element
|
|
332
332
|
def validate_element(element)
|
|
333
|
-
return if element.
|
|
333
|
+
return if element.is_a?(Lutaml::Model::Serializable)
|
|
334
334
|
|
|
335
335
|
raise ArgumentError,
|
|
336
336
|
"Element must respond to #to_xml, got #{element.class}"
|
|
@@ -133,10 +133,10 @@ module Uniword
|
|
|
133
133
|
# @param document [Object] The document to validate
|
|
134
134
|
# @raise [ArgumentError] if document is invalid
|
|
135
135
|
def validate_document!(document)
|
|
136
|
-
return if document.
|
|
136
|
+
return if document.is_a?(Uniword::Wordprocessingml::DocumentRoot)
|
|
137
137
|
|
|
138
138
|
raise ArgumentError,
|
|
139
|
-
"Document must
|
|
139
|
+
"Document must be a Uniword::Wordprocessingml::DocumentRoot"
|
|
140
140
|
end
|
|
141
141
|
end
|
|
142
142
|
|
|
@@ -71,7 +71,7 @@ module Uniword
|
|
|
71
71
|
# @param link [Hyperlink] The link to check
|
|
72
72
|
# @return [Boolean] true if internal link
|
|
73
73
|
def internal_link?(link)
|
|
74
|
-
link.
|
|
74
|
+
link.is_a?(Lutaml::Model::Serializable) && link.class.attributes.key?(:anchor) && !link.anchor.nil? && !link.anchor.empty?
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# Check if link is external (URL)
|
|
@@ -83,10 +83,10 @@ module Uniword
|
|
|
83
83
|
return false unless link
|
|
84
84
|
|
|
85
85
|
# Check for id attribute (relationship-based external link)
|
|
86
|
-
return true if link.
|
|
86
|
+
return true if link.is_a?(Lutaml::Model::Serializable) && link.class.attributes.key?(:id) && !link.id.nil? && !link.id.empty? && !internal_link?(link)
|
|
87
87
|
|
|
88
88
|
# Check for target that looks like a URL
|
|
89
|
-
return !link.target.start_with?("#") if link.
|
|
89
|
+
return !link.target.start_with?("#") if link.is_a?(Lutaml::Model::Serializable) && link.class.attributes.key?(:target) && !link.target.nil? && !link.target.empty?
|
|
90
90
|
|
|
91
91
|
false
|
|
92
92
|
end
|
|
@@ -114,7 +114,7 @@ module Uniword
|
|
|
114
114
|
# @return [String, nil] The URL or nil
|
|
115
115
|
def link_url(link)
|
|
116
116
|
# External links use id attribute (relationship ID) or target
|
|
117
|
-
link.
|
|
117
|
+
link.is_a?(Lutaml::Model::Serializable) && link.class.attributes.key?(:target) ? link.target : link.id
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
# Validate URL format
|
|
@@ -51,7 +51,7 @@ module Uniword
|
|
|
51
51
|
next if @allow_direct_formatting
|
|
52
52
|
|
|
53
53
|
para.runs.each_with_index do |run, run_index|
|
|
54
|
-
next unless run.
|
|
54
|
+
next unless run.is_a?(Uniword::Wordprocessingml::Run)
|
|
55
55
|
next unless run.properties
|
|
56
56
|
|
|
57
57
|
next unless has_direct_run_formatting?(run)
|
|
@@ -56,7 +56,7 @@ module Uniword
|
|
|
56
56
|
return false if table.rows.empty?
|
|
57
57
|
|
|
58
58
|
first_row = table.rows.first
|
|
59
|
-
return false unless first_row.
|
|
59
|
+
return false unless first_row.is_a?(Uniword::Wordprocessingml::TableRow)
|
|
60
60
|
|
|
61
61
|
# Check if first row cells have header properties
|
|
62
62
|
# In OOXML, header rows typically have specific table cell properties
|
|
@@ -42,7 +42,7 @@ module Uniword
|
|
|
42
42
|
color_scheme.dup.tap do |scheme|
|
|
43
43
|
# Transform each color in the scheme
|
|
44
44
|
scheme.colors&.each_value do |color|
|
|
45
|
-
next unless color.
|
|
45
|
+
next unless color.is_a?(Uniword::Drawingml::SrgbColor) && color.val&.match?(/^[0-9A-Fa-f]{6}$/)
|
|
46
46
|
|
|
47
47
|
color.val = shift_color(
|
|
48
48
|
color.val,
|
|
@@ -93,7 +93,7 @@ module Uniword
|
|
|
93
93
|
|
|
94
94
|
def bundled_path(subdir)
|
|
95
95
|
# Use Gem.datadir if available, otherwise fall back to data/ directory
|
|
96
|
-
data_path = (Gem.datadir("uniword") if defined?(Gem) && Gem.
|
|
96
|
+
data_path = (Gem.datadir("uniword") if defined?(Gem) && Gem.is_a?(Module))
|
|
97
97
|
data_path ||= File.expand_path("../../../data", __dir__)
|
|
98
98
|
File.join(data_path, subdir)
|
|
99
99
|
end
|
|
@@ -66,7 +66,7 @@ module Uniword
|
|
|
66
66
|
uniword_slug = detect_ms_theme(word_theme)
|
|
67
67
|
|
|
68
68
|
# Fall back to detection by theme name
|
|
69
|
-
uniword_slug = from_ms_name(word_theme.name) if uniword_slug.nil? && word_theme.
|
|
69
|
+
uniword_slug = from_ms_name(word_theme.name) if uniword_slug.nil? && word_theme.is_a?(Uniword::Drawingml::Theme) && word_theme.name
|
|
70
70
|
|
|
71
71
|
return nil unless uniword_slug
|
|
72
72
|
|
|
@@ -58,16 +58,16 @@ module Uniword
|
|
|
58
58
|
def extract_text(document)
|
|
59
59
|
parts = []
|
|
60
60
|
|
|
61
|
-
paragraphs = if document.
|
|
61
|
+
paragraphs = if document.is_a?(Uniword::Wordprocessingml::DocumentRoot) || document.is_a?(Uniword::Wordprocessingml::Body)
|
|
62
62
|
document.paragraphs
|
|
63
63
|
else
|
|
64
64
|
[]
|
|
65
65
|
end
|
|
66
66
|
paragraphs.each do |para|
|
|
67
|
-
parts << para.text if para.
|
|
67
|
+
parts << para.text if para.is_a?(Uniword::Wordprocessingml::Paragraph)
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
tables = if document.
|
|
70
|
+
tables = if document.is_a?(Uniword::Wordprocessingml::DocumentRoot)
|
|
71
71
|
document.tables
|
|
72
72
|
else
|
|
73
73
|
[]
|
|
@@ -85,13 +85,13 @@ module Uniword
|
|
|
85
85
|
# @param parts [Array<String>] Accumulator for text parts
|
|
86
86
|
# @return [void]
|
|
87
87
|
def extract_table_text(table, parts)
|
|
88
|
-
return unless table.
|
|
88
|
+
return unless table.is_a?(Uniword::Wordprocessingml::Table)
|
|
89
89
|
|
|
90
90
|
table.rows.each do |row|
|
|
91
|
-
next unless row.
|
|
91
|
+
next unless row.is_a?(Uniword::Wordprocessingml::TableRow)
|
|
92
92
|
|
|
93
93
|
row.cells.each do |cell|
|
|
94
|
-
parts << cell.text if cell.
|
|
94
|
+
parts << cell.text if cell.is_a?(Uniword::Wordprocessingml::TableCell)
|
|
95
95
|
end
|
|
96
96
|
end
|
|
97
97
|
end
|
data/lib/uniword/styleset.rb
CHANGED
|
@@ -140,7 +140,7 @@ module Uniword
|
|
|
140
140
|
# - :rename - Keep both, rename imported styles
|
|
141
141
|
# @return [void]
|
|
142
142
|
def apply_to(document, strategy: :keep_existing)
|
|
143
|
-
return unless document.
|
|
143
|
+
return unless document.is_a?(Uniword::Wordprocessingml::DocumentRoot)
|
|
144
144
|
|
|
145
145
|
styles.each do |style|
|
|
146
146
|
case strategy
|
|
@@ -97,10 +97,9 @@ module Uniword
|
|
|
97
97
|
# @return [void]
|
|
98
98
|
def remove_element(element, document)
|
|
99
99
|
# Remove from paragraphs
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
document.clear_element_cache if document.respond_to?(:clear_element_cache)
|
|
100
|
+
if document.body.is_a?(Uniword::Wordprocessingml::Body)
|
|
101
|
+
document.body.paragraphs.delete(element)
|
|
102
|
+
end
|
|
104
103
|
end
|
|
105
104
|
end
|
|
106
105
|
end
|
|
@@ -65,7 +65,7 @@ module Uniword
|
|
|
65
65
|
# @param format_string [String] Format string
|
|
66
66
|
# @return [String] Formatted value
|
|
67
67
|
def apply_format(value, format_string)
|
|
68
|
-
if value.
|
|
68
|
+
if value.is_a?(Date) || value.is_a?(Time)
|
|
69
69
|
value.strftime(format_string)
|
|
70
70
|
else
|
|
71
71
|
value.to_s
|
|
@@ -103,7 +103,7 @@ module Uniword
|
|
|
103
103
|
# Find and replace variables in element
|
|
104
104
|
# This is simplified - full implementation would
|
|
105
105
|
# recursively process all text nodes
|
|
106
|
-
return unless element.
|
|
106
|
+
return unless element.is_a?(Uniword::Wordprocessingml::Run)
|
|
107
107
|
|
|
108
108
|
text = element.text
|
|
109
109
|
# Simple variable replacement ({{var}})
|
|
@@ -33,7 +33,7 @@ module Uniword
|
|
|
33
33
|
replace_cell(element, text)
|
|
34
34
|
else
|
|
35
35
|
# Try to treat as paragraph-like
|
|
36
|
-
replace_paragraph(element, text) if element.
|
|
36
|
+
replace_paragraph(element, text) if element.is_a?(Uniword::Wordprocessingml::Paragraph)
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -62,7 +62,7 @@ module Uniword
|
|
|
62
62
|
def parse_paragraphs(paragraphs)
|
|
63
63
|
paragraphs.each_with_index do |para, index|
|
|
64
64
|
# Check paragraph's own comments
|
|
65
|
-
if para.
|
|
65
|
+
if para.is_a?(Uniword::CommentsPart) && para.comments
|
|
66
66
|
para.comments.each do |comment|
|
|
67
67
|
marker = parse_comment_text(comment.text, para, index)
|
|
68
68
|
@markers << marker if marker
|
|
@@ -70,10 +70,10 @@ module Uniword
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
# Check run comments
|
|
73
|
-
next unless para.
|
|
73
|
+
next unless para.is_a?(Uniword::Wordprocessingml::Paragraph)
|
|
74
74
|
|
|
75
75
|
para.runs.each do |run|
|
|
76
|
-
next unless run.
|
|
76
|
+
next unless run.is_a?(Uniword::CommentsPart) && run.comments
|
|
77
77
|
|
|
78
78
|
run.comments.each do |comment|
|
|
79
79
|
marker = parse_comment_text(comment.text, run, index)
|
|
@@ -93,13 +93,13 @@ module Uniword
|
|
|
93
93
|
base_position = @document.paragraphs.size + (table_index * 100)
|
|
94
94
|
|
|
95
95
|
# Parse table rows
|
|
96
|
-
next unless table.
|
|
96
|
+
next unless table.is_a?(Uniword::Wordprocessingml::Table)
|
|
97
97
|
|
|
98
98
|
table.rows.each_with_index do |row, row_index|
|
|
99
99
|
row_position = base_position + (row_index * 10)
|
|
100
100
|
|
|
101
101
|
# Check row comments
|
|
102
|
-
if row.
|
|
102
|
+
if row.is_a?(Uniword::CommentsPart) && row.comments
|
|
103
103
|
row.comments.each do |comment|
|
|
104
104
|
marker = parse_comment_text(comment.text, row, row_position)
|
|
105
105
|
@markers << marker if marker
|
|
@@ -107,11 +107,13 @@ module Uniword
|
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
# Parse cells
|
|
110
|
-
next unless row.
|
|
110
|
+
next unless row.is_a?(Uniword::Wordprocessingml::TableRow)
|
|
111
111
|
|
|
112
112
|
row.cells.each_with_index do |cell, _cell_index|
|
|
113
113
|
# Parse cell paragraphs
|
|
114
|
-
|
|
114
|
+
if cell.is_a?(Uniword::Wordprocessingml::TableCell)
|
|
115
|
+
parse_paragraphs(cell.paragraphs)
|
|
116
|
+
end
|
|
115
117
|
end
|
|
116
118
|
end
|
|
117
119
|
end
|
|
@@ -178,10 +178,10 @@ module Uniword
|
|
|
178
178
|
def remove_template_comments(document)
|
|
179
179
|
# Remove comments from paragraphs
|
|
180
180
|
document.paragraphs.each do |para|
|
|
181
|
-
next unless para.
|
|
181
|
+
next unless para.is_a?(Uniword::CommentsPart)
|
|
182
182
|
|
|
183
183
|
# Filter out template comments
|
|
184
|
-
next unless para.
|
|
184
|
+
next unless para.is_a?(Uniword::CommentsPart)
|
|
185
185
|
|
|
186
186
|
para.attached_comments.reject! do |c|
|
|
187
187
|
template_comment?(c)
|
|
@@ -194,7 +194,7 @@ module Uniword
|
|
|
194
194
|
# @param comment [Comment] Comment to check
|
|
195
195
|
# @return [Boolean] true if template comment
|
|
196
196
|
def template_comment?(comment)
|
|
197
|
-
return false unless comment.
|
|
197
|
+
return false unless comment.is_a?(Uniword::Comment)
|
|
198
198
|
|
|
199
199
|
text = comment.text
|
|
200
200
|
text.match?(/^\{\{.+\}\}$/)
|
|
@@ -108,9 +108,8 @@ module Uniword
|
|
|
108
108
|
# For hashes, use key access
|
|
109
109
|
if object.is_a?(Hash)
|
|
110
110
|
object[property.to_sym] || object[property]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
object.send(property.to_sym)
|
|
111
|
+
else
|
|
112
|
+
object.public_send(property.to_sym)
|
|
114
113
|
end
|
|
115
114
|
end
|
|
116
115
|
|
|
@@ -176,7 +175,7 @@ module Uniword
|
|
|
176
175
|
|
|
177
176
|
return false if left_num.nil? || right_num.nil?
|
|
178
177
|
|
|
179
|
-
left_num.
|
|
178
|
+
left_num.public_send(operator, right_num)
|
|
180
179
|
end
|
|
181
180
|
|
|
182
181
|
# Convert value to number
|
|
@@ -185,8 +184,8 @@ module Uniword
|
|
|
185
184
|
# @return [Numeric, nil] Numeric value or nil
|
|
186
185
|
def to_number(value)
|
|
187
186
|
return value if value.is_a?(Numeric)
|
|
188
|
-
return value.to_i if value.
|
|
189
|
-
return value.to_f if value.
|
|
187
|
+
return value.to_i if value.to_s.match?(/^\d+$/)
|
|
188
|
+
return value.to_f if value.to_s.match?(/^\d+\.\d+$/)
|
|
190
189
|
|
|
191
190
|
nil
|
|
192
191
|
end
|
|
@@ -239,7 +239,7 @@ module Uniword
|
|
|
239
239
|
result = %(<span style="font-size:#{size_pt}pt">#{result}</span>)
|
|
240
240
|
end
|
|
241
241
|
|
|
242
|
-
if props.font.
|
|
242
|
+
if props.font.is_a?(Uniword::Properties::RunFonts) && props.font.ascii
|
|
243
243
|
result = %(<span style="font-family:'#{props.font.ascii}'">#{result}</span>)
|
|
244
244
|
elsif props.font.is_a?(String) && !props.font.empty?
|
|
245
245
|
result = %(<span style="font-family:'#{props.font}'">#{result}</span>)
|
|
@@ -582,7 +582,7 @@ module Uniword
|
|
|
582
582
|
|
|
583
583
|
attrs = []
|
|
584
584
|
|
|
585
|
-
attrs << %(w:id="#{props.id.value}") if props.id
|
|
585
|
+
attrs << %(w:id="#{props.id.value}") if props.id&.value
|
|
586
586
|
|
|
587
587
|
attrs << 'w:showingPlcHdr="t"' if props.showing_placeholder_header
|
|
588
588
|
|
|
@@ -590,14 +590,12 @@ module Uniword
|
|
|
590
590
|
|
|
591
591
|
if props.placeholder&.doc_part
|
|
592
592
|
doc_part = props.placeholder.doc_part
|
|
593
|
-
attrs << %(w:docPart="#{doc_part.value}") if doc_part
|
|
593
|
+
attrs << %(w:docPart="#{doc_part.value}") if doc_part&.value
|
|
594
594
|
end
|
|
595
595
|
|
|
596
|
-
attrs << %(w:
|
|
596
|
+
attrs << %(w:tag="#{escape_xml(props.tag.value)}") if props.tag&.value
|
|
597
597
|
|
|
598
|
-
attrs << %(w:
|
|
599
|
-
|
|
600
|
-
attrs << %(w:alias="#{escape_xml(props.alias_name.value)}") if props.alias_name.respond_to?(:value) && props.alias_name.value
|
|
598
|
+
attrs << %(w:alias="#{escape_xml(props.alias_name.value)}") if props.alias_name&.value
|
|
601
599
|
|
|
602
600
|
attrs.empty? ? "" : " #{attrs.join(' ')}"
|
|
603
601
|
end
|