uniword 1.0.4 → 1.0.6
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 +10 -28
- 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
|
@@ -390,12 +390,7 @@ module Uniword
|
|
|
390
390
|
record_fix("R2", "Generated w15:docId in GUID format")
|
|
391
391
|
end
|
|
392
392
|
|
|
393
|
-
|
|
394
|
-
settings.mc_ignorable = Ooxml::Types::McIgnorable.new(
|
|
395
|
-
"w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du",
|
|
396
|
-
)
|
|
397
|
-
record_fix("R1", "Added mc:Ignorable to settings")
|
|
398
|
-
end
|
|
393
|
+
settings.mc_ignorable = nil
|
|
399
394
|
end
|
|
400
395
|
|
|
401
396
|
def reconcile_font_table
|
|
@@ -436,9 +431,7 @@ module Uniword
|
|
|
436
431
|
font_table.fonts << font
|
|
437
432
|
end
|
|
438
433
|
|
|
439
|
-
font_table.mc_ignorable
|
|
440
|
-
"w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du",
|
|
441
|
-
)
|
|
434
|
+
font_table.mc_ignorable = nil
|
|
442
435
|
record_fix("R13",
|
|
443
436
|
"Populated font table with profile fonts and signatures")
|
|
444
437
|
end
|
|
@@ -457,9 +450,7 @@ module Uniword
|
|
|
457
450
|
|
|
458
451
|
ensure_default_styles(styles)
|
|
459
452
|
|
|
460
|
-
styles.mc_ignorable
|
|
461
|
-
"w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du",
|
|
462
|
-
)
|
|
453
|
+
styles.mc_ignorable = nil
|
|
463
454
|
record_fix("R10",
|
|
464
455
|
"Ensured styles have docDefaults, latentStyles, and default styles")
|
|
465
456
|
end
|
|
@@ -468,18 +459,13 @@ module Uniword
|
|
|
468
459
|
return unless profile
|
|
469
460
|
return unless package.numbering
|
|
470
461
|
|
|
471
|
-
|
|
472
|
-
package.numbering.mc_ignorable = Ooxml::Types::McIgnorable.new(
|
|
473
|
-
"w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du wp14",
|
|
474
|
-
)
|
|
475
|
-
record_fix("R1", "Added mc:Ignorable to numbering")
|
|
476
|
-
end
|
|
462
|
+
package.numbering.mc_ignorable = nil
|
|
477
463
|
|
|
478
464
|
# Validate instance → definition references
|
|
479
465
|
package.numbering.instances.each do |inst|
|
|
480
466
|
next unless inst.abstract_num_id
|
|
481
467
|
|
|
482
|
-
abs_id = inst.abstract_num_id.
|
|
468
|
+
abs_id = inst.abstract_num_id.is_a?(Uniword::Wordprocessingml::AbstractNumId) ? inst.abstract_num_id.val : inst.abstract_num_id
|
|
483
469
|
defn = package.numbering.definitions.find do |d|
|
|
484
470
|
d.abstract_num_id == abs_id
|
|
485
471
|
end
|
|
@@ -499,10 +485,8 @@ module Uniword
|
|
|
499
485
|
package.web_settings
|
|
500
486
|
end
|
|
501
487
|
|
|
502
|
-
ws.mc_ignorable
|
|
503
|
-
|
|
504
|
-
)
|
|
505
|
-
record_fix("R1", "Added mc:Ignorable to webSettings")
|
|
488
|
+
ws.mc_ignorable = nil
|
|
489
|
+
record_fix("R1", "Cleared mc:Ignorable on webSettings")
|
|
506
490
|
end
|
|
507
491
|
|
|
508
492
|
def reconcile_app_properties
|
|
@@ -596,9 +580,7 @@ module Uniword
|
|
|
596
580
|
return unless package.document&.body
|
|
597
581
|
|
|
598
582
|
doc = package.document
|
|
599
|
-
doc.mc_ignorable
|
|
600
|
-
"w14 w15 w16se w16cid w16 w16cex w16sdtdh w16sdtfl w16du wp14",
|
|
601
|
-
)
|
|
583
|
+
doc.mc_ignorable = Ooxml::Types::McIgnorable.new("w14")
|
|
602
584
|
|
|
603
585
|
record_fix("R1", "Added mc:Ignorable to document body")
|
|
604
586
|
record_fix("R12", "Assigned rsid and paraId to paragraphs")
|
|
@@ -985,8 +967,8 @@ module Uniword
|
|
|
985
967
|
# 4. East Asian light font, 5. Major font (headings)
|
|
986
968
|
names << fs&.minor_font if fs&.minor_font
|
|
987
969
|
|
|
988
|
-
ea_font = loc
|
|
989
|
-
ea_light = loc
|
|
970
|
+
ea_font = loc&.east_asian_font
|
|
971
|
+
ea_light = loc&.east_asian_light_font
|
|
990
972
|
|
|
991
973
|
# Default East Asian fonts for zh-CN when locale profile omits them
|
|
992
974
|
if loc.east_asia_lang == "zh-CN"
|
|
@@ -15,7 +15,7 @@ module Uniword
|
|
|
15
15
|
# @param element_class [Class] The element class to register
|
|
16
16
|
# @return [void]
|
|
17
17
|
def register(element_class)
|
|
18
|
-
return if element_class
|
|
18
|
+
return if element_class <= Uniword::Element && element_class.abstract?
|
|
19
19
|
|
|
20
20
|
key = element_class_key(element_class)
|
|
21
21
|
return if key.nil? # Skip anonymous classes
|
data/lib/uniword/endnote.rb
CHANGED
|
@@ -50,7 +50,9 @@ module Uniword
|
|
|
50
50
|
#
|
|
51
51
|
# @return [String] The combined text of all paragraphs
|
|
52
52
|
def text
|
|
53
|
-
@content.map
|
|
53
|
+
@content.map do |p|
|
|
54
|
+
p.is_a?(Uniword::Wordprocessingml::Paragraph) ? p.text : p.to_s
|
|
55
|
+
end.join("\n")
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
# Convert to hash representation.
|
data/lib/uniword/footnote.rb
CHANGED
|
@@ -49,7 +49,9 @@ module Uniword
|
|
|
49
49
|
#
|
|
50
50
|
# @return [String] The combined text of all paragraphs
|
|
51
51
|
def text
|
|
52
|
-
@content.map
|
|
52
|
+
@content.map do |p|
|
|
53
|
+
p.is_a?(Uniword::Wordprocessingml::Paragraph) ? p.text : p.to_s
|
|
54
|
+
end.join("\n")
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
# Convert to hash representation.
|
|
@@ -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
|