uniword 1.0.11 → 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 -25
- 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 +15 -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 -1097
- 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/ooxml_to_html_converter.rb +3 -5
- 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 +51 -34
- 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 +4 -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 +46 -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
|
@@ -19,22 +19,19 @@ module Uniword
|
|
|
19
19
|
Wordprocessingml::Table
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
def initialize(model = nil)
|
|
23
|
+
super
|
|
24
|
+
ensure_table_structure
|
|
25
|
+
end
|
|
26
|
+
|
|
26
27
|
def row(&block)
|
|
27
28
|
r = TableRowBuilder.new
|
|
28
29
|
yield(r) if block
|
|
29
30
|
@model.rows << r.build
|
|
31
|
+
finalize_table_structure
|
|
30
32
|
r
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
# Set table width
|
|
34
|
-
#
|
|
35
|
-
# @param value [Integer] Width in twips
|
|
36
|
-
# @param rule [String] Width rule ('auto', 'exact', 'pct')
|
|
37
|
-
# @return [self]
|
|
38
35
|
def width(value, rule: nil)
|
|
39
36
|
ensure_properties
|
|
40
37
|
@model.properties.table_width ||= Properties::TableWidth.new
|
|
@@ -43,10 +40,6 @@ module Uniword
|
|
|
43
40
|
self
|
|
44
41
|
end
|
|
45
42
|
|
|
46
|
-
# Set table indentation
|
|
47
|
-
#
|
|
48
|
-
# @param value [Integer] Indent in twips
|
|
49
|
-
# @return [self]
|
|
50
43
|
def indent(value)
|
|
51
44
|
ensure_properties
|
|
52
45
|
@model.properties.table_indent ||= Properties::TableIndent.new
|
|
@@ -62,10 +55,36 @@ module Uniword
|
|
|
62
55
|
|
|
63
56
|
private
|
|
64
57
|
|
|
58
|
+
def properties_tag
|
|
59
|
+
"tblPr"
|
|
60
|
+
end
|
|
61
|
+
|
|
65
62
|
def ensure_properties
|
|
66
63
|
@model.properties ||= Wordprocessingml::TableProperties.new
|
|
64
|
+
ensure_properties_in_order
|
|
67
65
|
@model.properties
|
|
68
66
|
end
|
|
67
|
+
|
|
68
|
+
def ensure_table_structure
|
|
69
|
+
ensure_properties
|
|
70
|
+
@model.properties.table_width ||= Properties::TableWidth.new(w: 0, type: "auto")
|
|
71
|
+
@model.properties.table_look ||= Wordprocessingml::TableDefaults.default_table_look
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def finalize_table_structure
|
|
75
|
+
col_count = @model.rows.map(&:cell_count).max || 0
|
|
76
|
+
return if col_count.zero?
|
|
77
|
+
|
|
78
|
+
@model.grid ||= Wordprocessingml::TableGrid.new
|
|
79
|
+
existing = @model.grid.columns.size
|
|
80
|
+
if existing < col_count
|
|
81
|
+
(col_count - existing).times do
|
|
82
|
+
@model.grid.columns << Wordprocessingml::GridCol.new
|
|
83
|
+
end
|
|
84
|
+
elsif existing > col_count
|
|
85
|
+
@model.grid.columns = @model.grid.columns.first(col_count)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
69
88
|
end
|
|
70
89
|
end
|
|
71
90
|
end
|
|
@@ -17,6 +17,11 @@ module Uniword
|
|
|
17
17
|
Wordprocessingml::TableCell
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def initialize(model = nil)
|
|
21
|
+
super
|
|
22
|
+
ensure_cell_structure
|
|
23
|
+
end
|
|
24
|
+
|
|
20
25
|
# Append content to the cell. Routes by type:
|
|
21
26
|
# - String -> creates a Paragraph with a Run
|
|
22
27
|
# - Paragraph -> appends to paragraphs
|
|
@@ -30,12 +35,16 @@ module Uniword
|
|
|
30
35
|
para = Wordprocessingml::Paragraph.new
|
|
31
36
|
para.runs << Wordprocessingml::Run.new(text: element)
|
|
32
37
|
@model.paragraphs << para
|
|
38
|
+
track_element_order("p")
|
|
33
39
|
when Wordprocessingml::Paragraph
|
|
34
40
|
@model.paragraphs << element
|
|
41
|
+
track_element_order("p")
|
|
35
42
|
when Wordprocessingml::Table
|
|
36
43
|
@model.tables << element
|
|
44
|
+
track_element_order("tbl")
|
|
37
45
|
when ParagraphBuilder
|
|
38
46
|
@model.paragraphs << element.build
|
|
47
|
+
track_element_order("p")
|
|
39
48
|
else
|
|
40
49
|
raise ArgumentError, "Cannot add #{element.class} to table cell"
|
|
41
50
|
end
|
|
@@ -85,10 +94,20 @@ module Uniword
|
|
|
85
94
|
|
|
86
95
|
private
|
|
87
96
|
|
|
97
|
+
def properties_tag
|
|
98
|
+
"tcPr"
|
|
99
|
+
end
|
|
100
|
+
|
|
88
101
|
def ensure_properties
|
|
89
102
|
@model.properties ||= Wordprocessingml::TableCellProperties.new
|
|
103
|
+
ensure_properties_in_order
|
|
90
104
|
@model.properties
|
|
91
105
|
end
|
|
106
|
+
|
|
107
|
+
def ensure_cell_structure
|
|
108
|
+
ensure_properties
|
|
109
|
+
@model.properties.cell_width ||= Properties::CellWidth.new(w: 0, type: "auto")
|
|
110
|
+
end
|
|
92
111
|
end
|
|
93
112
|
end
|
|
94
113
|
end
|
data/lib/uniword/builder.rb
CHANGED
|
@@ -39,6 +39,7 @@ module Uniword
|
|
|
39
39
|
autoload :SdtBuilder, "uniword/builder/sdt_builder"
|
|
40
40
|
autoload :BibliographyBuilder, "uniword/builder/bibliography_builder"
|
|
41
41
|
autoload :ChartBuilder, "uniword/builder/chart_builder"
|
|
42
|
+
autoload :DeterministicId, "uniword/builder/deterministic_id"
|
|
42
43
|
|
|
43
44
|
# Factory: creates a Run with optional formatting
|
|
44
45
|
#
|
|
@@ -216,28 +217,35 @@ module Uniword
|
|
|
216
217
|
def build_field_paragraph(instruction)
|
|
217
218
|
para = Wordprocessingml::Paragraph.new
|
|
218
219
|
|
|
219
|
-
# Begin character
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
220
|
+
# Begin character inside a run
|
|
221
|
+
run_begin = Wordprocessingml::Run.new
|
|
222
|
+
run_begin.field_char = Wordprocessingml::FieldChar.new
|
|
223
|
+
run_begin.field_char.fldCharType = "begin"
|
|
224
|
+
para.runs << run_begin
|
|
225
|
+
|
|
226
|
+
# Instruction text inside a run
|
|
227
|
+
run_instr = Wordprocessingml::Run.new
|
|
228
|
+
run_instr.instr_text = Wordprocessingml::InstrText.new
|
|
229
|
+
run_instr.instr_text.text = instruction
|
|
230
|
+
para.runs << run_instr
|
|
231
|
+
|
|
232
|
+
# Separate character inside a run
|
|
233
|
+
run_sep = Wordprocessingml::Run.new
|
|
234
|
+
run_sep.field_char = Wordprocessingml::FieldChar.new
|
|
235
|
+
run_sep.field_char.fldCharType = "separate"
|
|
236
|
+
para.runs << run_sep
|
|
237
|
+
|
|
238
|
+
# End character inside a run
|
|
239
|
+
run_end = Wordprocessingml::Run.new
|
|
240
|
+
run_end.field_char = Wordprocessingml::FieldChar.new
|
|
241
|
+
run_end.field_char.fldCharType = "end"
|
|
242
|
+
para.runs << run_end
|
|
238
243
|
|
|
239
244
|
para
|
|
240
245
|
end
|
|
241
246
|
end
|
|
247
|
+
|
|
248
|
+
# Shared run utilities
|
|
249
|
+
autoload :RunUtils, "uniword/builder/run_utils"
|
|
242
250
|
end
|
|
243
251
|
end
|
data/lib/uniword/cli/diff_cli.rb
CHANGED
data/lib/uniword/cli/main.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
4
|
require "rainbow"
|
|
5
|
-
require_relative "helpers"
|
|
6
5
|
require_relative "styleset_cli"
|
|
7
6
|
require_relative "resources_cli"
|
|
8
7
|
require_relative "theme_cli"
|
|
@@ -501,17 +500,17 @@ module Uniword
|
|
|
501
500
|
|
|
502
501
|
def apply_metadata_updates(core_properties)
|
|
503
502
|
setters = {
|
|
504
|
-
"set-title" =>
|
|
505
|
-
"set-author" =>
|
|
506
|
-
"set-subject" =>
|
|
507
|
-
"set-keywords" =>
|
|
508
|
-
"set-description" =>
|
|
503
|
+
"set-title" => ->(cp, v) { cp.title = v },
|
|
504
|
+
"set-author" => ->(cp, v) { cp.creator = v },
|
|
505
|
+
"set-subject" => ->(cp, v) { cp.subject = v },
|
|
506
|
+
"set-keywords" => ->(cp, v) { cp.keywords = v },
|
|
507
|
+
"set-description" => ->(cp, v) { cp.description = v },
|
|
509
508
|
}
|
|
510
509
|
updated = false
|
|
511
510
|
setters.each do |opt, setter|
|
|
512
511
|
next unless options[opt]
|
|
513
512
|
|
|
514
|
-
core_properties
|
|
513
|
+
setter.call(core_properties, options[opt]) if core_properties
|
|
515
514
|
updated = true
|
|
516
515
|
end
|
|
517
516
|
updated
|
data/lib/uniword/cli/toc_cli.rb
CHANGED
data/lib/uniword/cli.rb
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# CLI
|
|
4
|
-
#
|
|
3
|
+
# CLI namespace. All CLI classes are autoloaded from lib/uniword.rb.
|
|
4
|
+
# This file exists for backwards compatibility with code that
|
|
5
|
+
# requires "uniword/cli" to load the CLI namespace.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
require_relative "cli/theme_cli"
|
|
10
|
-
require_relative "cli/generate_cli"
|
|
11
|
-
require_relative "cli/review_cli"
|
|
12
|
-
require_relative "cli/diff_cli"
|
|
13
|
-
require_relative "cli/toc_cli"
|
|
14
|
-
require_relative "cli/images_cli"
|
|
15
|
-
require_relative "cli/spellcheck_cli"
|
|
16
|
-
require_relative "cli/template_cli"
|
|
17
|
-
require_relative "cli/headers_cli"
|
|
18
|
-
require_relative "cli/watermark_cli"
|
|
19
|
-
require_relative "cli/protect_cli"
|
|
20
|
-
require_relative "cli/main"
|
|
7
|
+
module Uniword
|
|
8
|
+
# All CLI autoload entries live in lib/uniword.rb.
|
|
9
|
+
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "diff_result"
|
|
4
|
-
|
|
5
3
|
module Uniword
|
|
6
4
|
module Diff
|
|
7
5
|
# Compares two DocumentRoot instances and produces a DiffResult.
|
|
@@ -442,7 +440,10 @@ module Uniword
|
|
|
442
440
|
def format_value(props, method)
|
|
443
441
|
return nil unless props
|
|
444
442
|
|
|
445
|
-
val =
|
|
443
|
+
val = case method
|
|
444
|
+
when :alignment then props.alignment
|
|
445
|
+
when :style then Array(props.style).first
|
|
446
|
+
end
|
|
446
447
|
unwrap_value(val)
|
|
447
448
|
end
|
|
448
449
|
|
|
@@ -454,7 +455,14 @@ module Uniword
|
|
|
454
455
|
def run_prop_value(props, method)
|
|
455
456
|
return nil unless props
|
|
456
457
|
|
|
457
|
-
val =
|
|
458
|
+
val = case method
|
|
459
|
+
when :bold then props.bold
|
|
460
|
+
when :italic then props.italic
|
|
461
|
+
when :underline then props.underline
|
|
462
|
+
when :fonts then props.fonts
|
|
463
|
+
when :size then props.size
|
|
464
|
+
when :color then props.color
|
|
465
|
+
end
|
|
458
466
|
return nil if val.nil?
|
|
459
467
|
|
|
460
468
|
unwrap_value(val)
|
|
@@ -468,7 +476,17 @@ module Uniword
|
|
|
468
476
|
def metadata_value(cp, field)
|
|
469
477
|
return nil unless cp
|
|
470
478
|
|
|
471
|
-
val =
|
|
479
|
+
val = case field
|
|
480
|
+
when :title then cp.title
|
|
481
|
+
when :creator then cp.creator
|
|
482
|
+
when :subject then cp.subject
|
|
483
|
+
when :keywords then cp.keywords
|
|
484
|
+
when :description then cp.description
|
|
485
|
+
when :last_modified_by then cp.last_modified_by
|
|
486
|
+
when :revision then cp.revision
|
|
487
|
+
when :created then cp.created
|
|
488
|
+
when :modified then cp.modified
|
|
489
|
+
end
|
|
472
490
|
return nil if val.nil?
|
|
473
491
|
|
|
474
492
|
unwrap_value(val).to_s
|
data/lib/uniword/diff.rb
CHANGED
|
@@ -14,6 +14,10 @@ module Uniword
|
|
|
14
14
|
autoload :DocumentDiffer, "uniword/diff/document_differ"
|
|
15
15
|
autoload :Formatter, "uniword/diff/formatter"
|
|
16
16
|
autoload :PackageDiffer, "uniword/diff/package_differ"
|
|
17
|
+
autoload :XmlChange, "uniword/diff/package_diff_result"
|
|
18
|
+
autoload :ZipMetadataChange, "uniword/diff/package_diff_result"
|
|
19
|
+
autoload :OpcIssue, "uniword/diff/package_diff_result"
|
|
20
|
+
autoload :PartChange, "uniword/diff/package_diff_result"
|
|
17
21
|
autoload :PackageDiffResult, "uniword/diff/package_diff_result"
|
|
18
22
|
end
|
|
19
23
|
end
|
|
@@ -169,27 +169,6 @@ module Uniword
|
|
|
169
169
|
detector.detect(path)
|
|
170
170
|
end
|
|
171
171
|
|
|
172
|
-
# Mapping from Docx::Package attribute names to DocumentRoot
|
|
173
|
-
# attribute names. Keys where names differ use explicit mapping.
|
|
174
|
-
PACKAGE_PART_MAPPINGS = {
|
|
175
|
-
styles: :styles_configuration,
|
|
176
|
-
numbering: :numbering_configuration,
|
|
177
|
-
settings: :settings,
|
|
178
|
-
font_table: :font_table,
|
|
179
|
-
web_settings: :web_settings,
|
|
180
|
-
theme: :theme,
|
|
181
|
-
core_properties: :core_properties,
|
|
182
|
-
app_properties: :app_properties,
|
|
183
|
-
document_rels: :document_rels,
|
|
184
|
-
theme_rels: :theme_rels,
|
|
185
|
-
package_rels: :package_rels,
|
|
186
|
-
content_types: :content_types,
|
|
187
|
-
custom_properties: :custom_properties,
|
|
188
|
-
custom_xml_items: :custom_xml_items,
|
|
189
|
-
footnotes: :footnotes,
|
|
190
|
-
endnotes: :endnotes,
|
|
191
|
-
embeddings: :embeddings,
|
|
192
|
-
}.freeze
|
|
193
172
|
|
|
194
173
|
# Copy package parts to document for round-trip preservation
|
|
195
174
|
#
|
|
@@ -199,10 +178,22 @@ module Uniword
|
|
|
199
178
|
def copy_package_parts_to_document(package, document)
|
|
200
179
|
return unless document.is_a?(Uniword::Wordprocessingml::DocumentRoot)
|
|
201
180
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
181
|
+
document.styles_configuration = package.styles if package.styles
|
|
182
|
+
document.numbering_configuration = package.numbering if package.numbering
|
|
183
|
+
document.settings = package.settings if package.settings
|
|
184
|
+
document.font_table = package.font_table if package.font_table
|
|
185
|
+
document.web_settings = package.web_settings if package.web_settings
|
|
186
|
+
document.theme = package.theme if package.theme
|
|
187
|
+
document.core_properties = package.core_properties if package.core_properties
|
|
188
|
+
document.app_properties = package.app_properties if package.app_properties
|
|
189
|
+
document.document_rels = package.document_rels if package.document_rels
|
|
190
|
+
document.theme_rels = package.theme_rels if package.theme_rels
|
|
191
|
+
document.package_rels = package.package_rels if package.package_rels
|
|
192
|
+
document.content_types = package.content_types if package.content_types
|
|
193
|
+
document.custom_properties = package.custom_properties if package.custom_properties
|
|
194
|
+
document.custom_xml_items = package.custom_xml_items if package.custom_xml_items
|
|
195
|
+
document.footnotes = package.footnotes if package.footnotes
|
|
196
|
+
document.endnotes = package.endnotes if package.endnotes
|
|
206
197
|
end
|
|
207
198
|
|
|
208
199
|
private
|
|
@@ -2,23 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Uniword
|
|
4
4
|
module Docx
|
|
5
|
-
# Calculates document statistics that appear in docProps/app.xml.
|
|
6
|
-
#
|
|
7
|
-
# Verified against Microsoft Word 2024. Rules:
|
|
8
|
-
# - Words: whitespace-separated tokens; each CJK char = 1 word
|
|
9
|
-
# - Characters: total chars minus whitespace (NO paragraph marks)
|
|
10
|
-
# - CharactersWithSpaces: total chars including spaces (NO paragraph marks)
|
|
11
|
-
# - Paragraphs: count of non-empty paragraphs (empty paras excluded)
|
|
12
|
-
# - Lines: same as non-empty paragraph count (no page layout engine)
|
|
13
|
-
# - Pages: simple approximation (no page layout engine)
|
|
14
|
-
#
|
|
15
|
-
# Known limitations:
|
|
16
|
-
# - Pages and Lines use paragraph-based approximation (no page layout engine)
|
|
17
|
-
# - Footnote/endnote text is not included (unclear if Word includes it)
|
|
18
|
-
# - Header/footer text is not included (Word likely excludes it)
|
|
19
5
|
class DocumentStatistics
|
|
20
|
-
|
|
21
|
-
CJK_REGEX = /[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u2F00-\u2FDF\u2E80-\u2EFF]/
|
|
6
|
+
CJK_REGEX = /[一-鿿㐀-䶿豈-⼀-⺀-]/
|
|
22
7
|
WHITESPACE_REGEX = /[ \t\r\n]/
|
|
23
8
|
|
|
24
9
|
def initialize(package)
|
|
@@ -32,8 +17,9 @@ module Uniword
|
|
|
32
17
|
|
|
33
18
|
text_per_paragraph = []
|
|
34
19
|
collect_text(body, text_per_paragraph)
|
|
20
|
+
collect_notes(text_per_paragraph)
|
|
21
|
+
collect_headers_footers(text_per_paragraph)
|
|
35
22
|
|
|
36
|
-
# Word only counts non-empty paragraphs
|
|
37
23
|
non_empty = text_per_paragraph.reject { |t| t.strip.empty? }
|
|
38
24
|
|
|
39
25
|
{
|
|
@@ -48,45 +34,58 @@ module Uniword
|
|
|
48
34
|
|
|
49
35
|
private
|
|
50
36
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
37
|
+
def collect_text(body, text_per_paragraph)
|
|
38
|
+
collect_paragraphs(body.paragraphs, text_per_paragraph)
|
|
39
|
+
collect_table_text(body.tables, text_per_paragraph)
|
|
40
|
+
collect_sdt_text(body.structured_document_tags, text_per_paragraph)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def collect_paragraphs(paragraphs, text_per_paragraph)
|
|
44
|
+
paragraphs.each { |para| text_per_paragraph << para.text }
|
|
45
|
+
end
|
|
58
46
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
47
|
+
def collect_table_text(tables, text_per_paragraph)
|
|
48
|
+
tables.each do |table|
|
|
49
|
+
table.rows.each do |row|
|
|
50
|
+
row.cells.each do |cell|
|
|
51
|
+
collect_paragraphs(cell.paragraphs, text_per_paragraph)
|
|
52
|
+
collect_table_text(cell.tables, text_per_paragraph)
|
|
65
53
|
end
|
|
66
54
|
end
|
|
67
55
|
end
|
|
56
|
+
end
|
|
68
57
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
58
|
+
def collect_sdt_text(sdts, text_per_paragraph)
|
|
59
|
+
sdts.each do |sdt|
|
|
60
|
+
next unless sdt.content
|
|
61
|
+
collect_paragraphs(sdt.content.paragraphs, text_per_paragraph)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def collect_notes(text_per_paragraph)
|
|
66
|
+
@package.footnotes&.footnote_entries&.each do |entry|
|
|
67
|
+
entry.paragraphs.each { |p| text_per_paragraph << p.text }
|
|
68
|
+
end
|
|
69
|
+
@package.endnotes&.endnote_entries&.each do |entry|
|
|
70
|
+
entry.paragraphs.each { |p| text_per_paragraph << p.text }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def collect_headers_footers(text_per_paragraph)
|
|
75
|
+
@package.document&.headers&.each_value do |header|
|
|
76
|
+
header.paragraphs.each { |p| text_per_paragraph << p.text }
|
|
77
|
+
end
|
|
78
|
+
@package.document&.footers&.each_value do |footer|
|
|
79
|
+
footer.paragraphs.each { |p| text_per_paragraph << p.text }
|
|
76
80
|
end
|
|
77
81
|
end
|
|
78
82
|
|
|
79
|
-
# Count words using Word's algorithm:
|
|
80
|
-
# - Whitespace-separated tokens for Latin text
|
|
81
|
-
# - Each CJK character counts as 1 word individually
|
|
82
|
-
# - Mixed CJK/Latin segments are counted appropriately
|
|
83
83
|
def count_words(text_per_paragraph)
|
|
84
84
|
total = 0
|
|
85
85
|
text_per_paragraph.each do |text|
|
|
86
86
|
next if text.strip.empty?
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
segments = text.scan(/[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u2F00-\u2FDF\u2E80-\u2EFF]+|[^ \t\r\n]+/)
|
|
88
|
+
segments = text.scan(/[一-鿿㐀-䶿豈-⼀-⺀-]+|[^ \t\r\n]+/)
|
|
90
89
|
segments.each do |seg|
|
|
91
90
|
total += if seg.match?(CJK_REGEX)
|
|
92
91
|
seg.length
|
|
@@ -98,31 +97,20 @@ module Uniword
|
|
|
98
97
|
total
|
|
99
98
|
end
|
|
100
99
|
|
|
101
|
-
# Characters (no spaces) = total chars minus whitespace.
|
|
102
|
-
#
|
|
103
|
-
# Verified against Word 2024:
|
|
104
|
-
# "Hello World" → 10 (not 11, no paragraph marks)
|
|
105
100
|
def count_characters_no_spaces(text_per_paragraph)
|
|
106
101
|
total_chars = text_per_paragraph.sum(&:length)
|
|
107
102
|
whitespace_count = text_per_paragraph.sum { |t| t.count(" \t") }
|
|
108
103
|
total_chars - whitespace_count
|
|
109
104
|
end
|
|
110
105
|
|
|
111
|
-
# Characters (with spaces) = total chars including spaces.
|
|
112
|
-
#
|
|
113
|
-
# Verified against Word 2024:
|
|
114
|
-
# "Hello World" → 11
|
|
115
106
|
def count_characters_with_spaces(text_per_paragraph)
|
|
116
107
|
text_per_paragraph.sum(&:length)
|
|
117
108
|
end
|
|
118
109
|
|
|
119
|
-
# Page estimation without page layout engine.
|
|
120
|
-
# Uses a simple heuristic: ~45 paragraphs per page.
|
|
121
110
|
def estimate_pages(paragraph_count)
|
|
122
111
|
[1, (paragraph_count / 45.0).ceil].max
|
|
123
112
|
end
|
|
124
113
|
|
|
125
|
-
# Line estimation without page layout engine.
|
|
126
114
|
def estimate_lines(paragraph_count)
|
|
127
115
|
[1, paragraph_count].max
|
|
128
116
|
end
|