uniword 1.5.1 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/lib/uniword/batch/operation/file_result.rb +39 -0
  4. data/lib/uniword/batch/operation/repair_task.rb +34 -0
  5. data/lib/uniword/batch/operation/report.rb +66 -0
  6. data/lib/uniword/batch/operation/runner.rb +30 -0
  7. data/lib/uniword/batch/operation/task.rb +40 -0
  8. data/lib/uniword/batch/operation/verify_task.rb +27 -0
  9. data/lib/uniword/batch/operation.rb +22 -0
  10. data/lib/uniword/batch.rb +1 -0
  11. data/lib/uniword/caption/caption_builder.rb +120 -0
  12. data/lib/uniword/caption/counter.rb +61 -0
  13. data/lib/uniword/caption/cross_reference.rb +44 -0
  14. data/lib/uniword/caption.rb +24 -0
  15. data/lib/uniword/cli/main.rb +134 -0
  16. data/lib/uniword/cli/review_cli.rb +34 -0
  17. data/lib/uniword/configuration.rb +17 -0
  18. data/lib/uniword/diff/semantic/change.rb +66 -0
  19. data/lib/uniword/diff/semantic/engine.rb +31 -0
  20. data/lib/uniword/diff/semantic/paragraph_comparator.rb +195 -0
  21. data/lib/uniword/diff/semantic/result.rb +53 -0
  22. data/lib/uniword/diff/semantic.rb +26 -0
  23. data/lib/uniword/diff.rb +1 -0
  24. data/lib/uniword/docx/deterministic_output.rb +47 -0
  25. data/lib/uniword/docx.rb +1 -0
  26. data/lib/uniword/find_replace/body_scope.rb +23 -0
  27. data/lib/uniword/find_replace/comment_scope.rb +23 -0
  28. data/lib/uniword/find_replace/endnote_scope.rb +23 -0
  29. data/lib/uniword/find_replace/engine.rb +84 -0
  30. data/lib/uniword/find_replace/footer_scope.rb +24 -0
  31. data/lib/uniword/find_replace/footnote_scope.rb +23 -0
  32. data/lib/uniword/find_replace/header_scope.rb +24 -0
  33. data/lib/uniword/find_replace/matcher.rb +23 -0
  34. data/lib/uniword/find_replace/paragraph_walker.rb +61 -0
  35. data/lib/uniword/find_replace/regex_matcher.rb +38 -0
  36. data/lib/uniword/find_replace/result.rb +54 -0
  37. data/lib/uniword/find_replace/scope.rb +98 -0
  38. data/lib/uniword/find_replace/string_matcher.rb +72 -0
  39. data/lib/uniword/find_replace/styles_scope.rb +38 -0
  40. data/lib/uniword/find_replace.rb +29 -0
  41. data/lib/uniword/infrastructure/zip_packager.rb +28 -1
  42. data/lib/uniword/lint/builtin_rules/banned_words.rb +35 -0
  43. data/lib/uniword/lint/builtin_rules/max_paragraph_length.rb +32 -0
  44. data/lib/uniword/lint/builtin_rules/require_body.rb +23 -0
  45. data/lib/uniword/lint/builtin_rules/required_style.rb +31 -0
  46. data/lib/uniword/lint/builtin_rules.rb +23 -0
  47. data/lib/uniword/lint/engine.rb +32 -0
  48. data/lib/uniword/lint/result.rb +59 -0
  49. data/lib/uniword/lint/rule.rb +77 -0
  50. data/lib/uniword/lint/ruleset.rb +65 -0
  51. data/lib/uniword/lint.rb +17 -0
  52. data/lib/uniword/plugin/cli_command.rb +30 -0
  53. data/lib/uniword/plugin/loader.rb +43 -0
  54. data/lib/uniword/plugin/registry.rb +83 -0
  55. data/lib/uniword/plugin/transformer.rb +50 -0
  56. data/lib/uniword/plugin/validator.rb +34 -0
  57. data/lib/uniword/plugin.rb +25 -0
  58. data/lib/uniword/redact/engine.rb +52 -0
  59. data/lib/uniword/redact/pattern.rb +23 -0
  60. data/lib/uniword/redact/pattern_library.rb +60 -0
  61. data/lib/uniword/redact/result.rb +42 -0
  62. data/lib/uniword/redact.rb +15 -0
  63. data/lib/uniword/version.rb +1 -1
  64. data/lib/uniword/wordprocessingml/document_styling.rb +131 -0
  65. data/lib/uniword/wordprocessingml/settings.rb +2 -0
  66. data/lib/uniword/wordprocessingml/track_changes.rb +19 -0
  67. data/lib/uniword/wordprocessingml.rb +1 -0
  68. data/lib/uniword.rb +16 -0
  69. metadata +56 -2
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Redact
5
+ # Redaction orchestrator. Runs each pattern through the
6
+ # FindReplace engine and aggregates per-pattern counts.
7
+ #
8
+ # Open/closed: adding a new pattern = `PatternLibrary.register`
9
+ # call. Engine unchanged.
10
+ class Engine
11
+ # @param document [Wordprocessingml::DocumentRoot]
12
+ # @param patterns [Array<Pattern>, :pii] patterns to apply
13
+ # (`:pii` selects the default library)
14
+ # @param scope [Symbol, Array<Symbol>, :all] find-replace scope
15
+ def initialize(document:, patterns: :pii, scope: :all)
16
+ @document = document
17
+ @patterns = resolve_patterns(patterns)
18
+ @scope = scope
19
+ end
20
+
21
+ # @return [Redact::Result]
22
+ def run
23
+ result = Result.new
24
+ return result if @patterns.empty?
25
+
26
+ @patterns.each do |pattern|
27
+ matcher = FindReplace::RegexMatcher.new(pattern: pattern.regex,
28
+ replacement: pattern.replacement)
29
+ fr = FindReplace::Engine.new(document: @document,
30
+ matcher: matcher,
31
+ scopes: @scope).run
32
+ result.add(pattern.name, fr.count)
33
+ end
34
+ result
35
+ end
36
+
37
+ private
38
+
39
+ def resolve_patterns(patterns)
40
+ return PatternLibrary.all if patterns == :pii
41
+ return [] if patterns.nil? || patterns.empty?
42
+
43
+ list = Array(patterns)
44
+ if list.all?(Symbol)
45
+ PatternLibrary.select(list)
46
+ else
47
+ list
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Redact
5
+ # One redaction pattern: a regex plus a label that classifies the
6
+ # kind of PII it matches.
7
+ class Pattern
8
+ attr_reader :name, :regex, :replacement, :description
9
+
10
+ # @param name [Symbol] pattern identifier (e.g. :ssn, :email)
11
+ # @param regex [Regexp] what to match
12
+ # @param replacement [String] replacement text (default "[REDACTED]")
13
+ # @param description [String] human-readable pattern description
14
+ def initialize(name:, regex:, replacement: "[REDACTED]",
15
+ description: nil)
16
+ @name = name
17
+ @regex = regex
18
+ @replacement = replacement
19
+ @description = description
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Redact
5
+ # Built-in library of common PII patterns. Open/closed: callers
6
+ # can register custom patterns via `PatternLibrary.register`.
7
+ class PatternLibrary
8
+ DEFAULT_PATTERNS = [
9
+ # US Social Security Number: AAA-GG-SSSS (no all-zeros groups)
10
+ Pattern.new(name: :ssn,
11
+ regex: /\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/,
12
+ description: "US Social Security Number"),
13
+ # Email address
14
+ Pattern.new(name: :email,
15
+ regex: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i,
16
+ description: "Email address"),
17
+ # US phone: (555) 555-5555 or 555-555-5555
18
+ Pattern.new(name: :phone,
19
+ regex: /\b\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}\b/,
20
+ description: "US phone number"),
21
+ # Credit card number: 13-19 digits, optional separators
22
+ Pattern.new(name: :credit_card,
23
+ regex: /\b(?:\d[ -]*?){13,19}\b/,
24
+ description: "Credit card number"),
25
+ # IPv4 address
26
+ Pattern.new(name: :ipv4,
27
+ regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/,
28
+ description: "IPv4 address"),
29
+ ].freeze
30
+
31
+ # All registered patterns, in registration order.
32
+ #
33
+ # @return [Array<Pattern>]
34
+ def self.all
35
+ DEFAULT_PATTERNS + @custom.to_a
36
+ end
37
+
38
+ # Select patterns by name.
39
+ #
40
+ # @param names [Array<Symbol>, :all] names to select, or :all
41
+ # @return [Array<Pattern>]
42
+ def self.select(names)
43
+ return all if names == :all
44
+
45
+ list = all
46
+ Array(names).filter_map { |n| list.find { |p| p.name == n } }
47
+ end
48
+
49
+ # Register a custom pattern. Append-only.
50
+ #
51
+ # @param pattern [Pattern]
52
+ # @return [void]
53
+ def self.register(pattern)
54
+ (@custom ||= []) << pattern
55
+ end
56
+
57
+ @custom = []
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Redact
5
+ # Aggregated redaction result.
6
+ class Result
7
+ attr_reader :by_pattern
8
+
9
+ def initialize
10
+ @by_pattern = Hash.new { |h, k| h[k] = 0 }
11
+ end
12
+
13
+ # @param name [Symbol] pattern name
14
+ # @param count [Integer] substitutions for this pattern
15
+ # @return [void]
16
+ def add(name, count)
17
+ @by_pattern[name] += count
18
+ end
19
+
20
+ # Total substitutions across all patterns.
21
+ #
22
+ # @return [Integer]
23
+ def count
24
+ @by_pattern.values.sum
25
+ end
26
+
27
+ # Patterns that matched at least once.
28
+ #
29
+ # @return [Array<Symbol>]
30
+ def patterns_matched
31
+ @by_pattern.select { |_, c| c.positive? }.keys
32
+ end
33
+
34
+ # True when zero substitutions were made.
35
+ #
36
+ # @return [Boolean]
37
+ def empty?
38
+ count.zero?
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ # Document redaction and anonymization.
5
+ #
6
+ # Built on FindReplace for content rewriting plus a metadata strip
7
+ # pass for identifying attributes (rsids, author info, revision
8
+ # metadata, hidden text). Compliance use case Word cannot serve.
9
+ module Redact
10
+ autoload :Engine, "#{__dir__}/redact/engine"
11
+ autoload :Pattern, "#{__dir__}/redact/pattern"
12
+ autoload :PatternLibrary, "#{__dir__}/redact/pattern_library"
13
+ autoload :Result, "#{__dir__}/redact/result"
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uniword
4
- VERSION = "1.5.1"
4
+ VERSION = "1.5.3"
5
5
  end
@@ -119,6 +119,122 @@ module Uniword
119
119
  replacer.replace(self)
120
120
  end
121
121
 
122
+ # Find and replace text across document parts.
123
+ #
124
+ # Mirrors Word's Home → Replace dialog (without the GUI).
125
+ # Replaces every non-overlapping match of `pattern` with
126
+ # `replacement` across the configured scopes. Returns a
127
+ # `FindReplace::Result` with per-scope counts.
128
+ #
129
+ # v1 limitation: matches inside a single run only. Matches
130
+ # that span run boundaries are silently skipped (Word does the
131
+ # same in default mode).
132
+ #
133
+ # @param pattern [String, Regexp] literal String or Regexp
134
+ # @param replacement [String] replacement text; for regex
135
+ # patterns, may reference captures via `\1`, `\2`, ...
136
+ # @param scope [Symbol, Array<Symbol>, :all] one or more of
137
+ # `:body`, `:headers`, `:footers`, `:footnotes`, `:endnotes`,
138
+ # `:comments`, `:styles`, or `:all` (default)
139
+ # @param ignore_case [Boolean] case-insensitive match
140
+ # @return [FindReplace::Result]
141
+ def find_replace(pattern, replacement, scope: :all, ignore_case: false)
142
+ matcher = build_find_replace_matcher(pattern, replacement, ignore_case)
143
+ FindReplace::Engine.new(document: self, matcher: matcher,
144
+ scopes: scope).run
145
+ end
146
+
147
+ # Turn change tracking on. Every subsequent edit is recorded as
148
+ # a tracked change. Mirrors Word's Review → Track Changes → On.
149
+ #
150
+ # @return [self]
151
+ def track_changes_on!
152
+ ensure_settings.track_changes = Wordprocessingml::TrackChanges.new
153
+ self
154
+ end
155
+
156
+ # Turn change tracking off. Existing tracked changes remain in
157
+ # the document; new edits are applied silently. Mirrors Word's
158
+ # Review → Track Changes → Off.
159
+ #
160
+ # @return [self]
161
+ def track_changes_off!
162
+ ensure_settings.track_changes = nil
163
+ self
164
+ end
165
+
166
+ # True when change tracking is enabled.
167
+ #
168
+ # @return [Boolean]
169
+ def track_changes_enabled?
170
+ settings&.track_changes ? true : false
171
+ end
172
+
173
+ # Redact PII patterns and/or custom regex across the document.
174
+ #
175
+ # Built on FindReplace. Default pattern library matches US
176
+ # phone numbers, email, SSN, credit card numbers, IPv4
177
+ # addresses. Pass `patterns: :pii` for defaults, or an array
178
+ # of names (`[:ssn, :email]`) to select a subset.
179
+ #
180
+ # @param patterns [Symbol, Array<Symbol>, Array<Redact::Pattern>]
181
+ # `:pii` (default), or names/Pattern objects to apply
182
+ # @param scope [Symbol, Array<Symbol>, :all] find-replace scope
183
+ # @return [Redact::Result]
184
+ def redact(patterns: :pii, scope: :all)
185
+ Redact::Engine.new(document: self, patterns: patterns,
186
+ scope: scope).run
187
+ end
188
+
189
+ # Run a lint ruleset against the document.
190
+ #
191
+ # @param ruleset [Lint::Ruleset, Array<Lint::Rule>] rules to
192
+ # apply
193
+ # @return [Lint::Result]
194
+ def lint(ruleset:)
195
+ Lint::Engine.new(document: self, ruleset: ruleset).run
196
+ end
197
+
198
+ # Counter for auto-numbered captions. Persisted across the
199
+ # document's lifetime; reset on document load.
200
+ #
201
+ # @return [Caption::Counter]
202
+ def caption_counter
203
+ @caption_counter ||= Caption::Counter.new
204
+ end
205
+
206
+ # Add an auto-numbered caption paragraph to the body and
207
+ # return the bookmark name (for use in cross-references).
208
+ #
209
+ # The caption is appended as a new paragraph with style
210
+ # "Caption" and a SEQ field. The bookmark wraps the entire
211
+ # paragraph so cross-references resolve to the caption text.
212
+ #
213
+ # @param label [String] "Figure", "Table", "Equation", or any
214
+ # other category — each gets its own counter
215
+ # @param text [String] caption body
216
+ # @param separator [String] between the label/number and the
217
+ # body (default ": ")
218
+ # @return [String] bookmark name (e.g. "_Figure1")
219
+ def add_caption(label:, text:, separator: ": ")
220
+ builder = Caption::CaptionBuilder.new(caption_counter)
221
+ paragraph, bookmark_name = builder.build(label: label,
222
+ text: text,
223
+ separator: separator)
224
+ body.paragraphs << paragraph
225
+ bookmark_name
226
+ end
227
+
228
+ # Build a cross-reference run targeting a bookmark. Returns a
229
+ # Run containing a SimpleField with a REF instruction. The
230
+ # caller decides where to place it.
231
+ #
232
+ # @param bookmark_name [String] target bookmark
233
+ # @return [Wordprocessingml::SimpleField]
234
+ def cross_reference_to(bookmark_name)
235
+ Caption::CrossReference.new(bookmark_name).build
236
+ end
237
+
122
238
  # Apply uniform page setup to every section of the document
123
239
  #
124
240
  # Mirrors Word's Layout dialog: named paper sizes, orientation
@@ -230,6 +346,21 @@ module Uniword
230
346
 
231
347
  private
232
348
 
349
+ def build_find_replace_matcher(pattern, replacement, ignore_case)
350
+ if pattern.is_a?(Regexp)
351
+ FindReplace::RegexMatcher.new(pattern: pattern,
352
+ replacement: replacement)
353
+ else
354
+ FindReplace::StringMatcher.new(pattern: pattern,
355
+ replacement: replacement,
356
+ ignore_case: ignore_case)
357
+ end
358
+ end
359
+
360
+ def ensure_settings
361
+ self.settings ||= Wordprocessingml::Settings.new
362
+ end
363
+
233
364
  # Ensure the document carries a theme part, creating it from a
234
365
  # fresh parse of the bundled Office theme when absent (every
235
366
  # document gets its own copy — no shared state).
@@ -37,6 +37,7 @@ module Uniword
37
37
  attribute :hyphenation_zone, HyphenationZone
38
38
  attribute :style_pane_sort_method, StylePaneSortMethod
39
39
  attribute :doc_vars, DocVars
40
+ attribute :track_changes, TrackChanges
40
41
  attribute :w14_doc_id, W14DocId
41
42
  attribute :w15_chart_tracking_ref_based, W15ChartTrackingRefBased
42
43
  attribute :w15_doc_id, W15DocId
@@ -106,6 +107,7 @@ module Uniword
106
107
  map_element "endnotePr", to: :endnote_pr, render_nil: false
107
108
  map_element "compat", to: :compat, render_nil: false
108
109
  map_element "docVars", to: :doc_vars, render_nil: false
110
+ map_element "trackChanges", to: :track_changes, render_nil: false
109
111
  map_element "rsids", to: :rsids, render_nil: false
110
112
  map_element "mathPr", to: :math_pr, render_nil: false
111
113
  map_element "themeFontLang", to: :theme_font_lang, render_nil: false
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Uniword
6
+ module Wordprocessingml
7
+ # Empty `<w:trackChanges/>` element in `<w:settings>` — Word's
8
+ # Review → Track Changes toggle.
9
+ #
10
+ # When present, Word records every edit as a tracked change. When
11
+ # absent, edits are applied silently.
12
+ class TrackChanges < Lutaml::Model::Serializable
13
+ xml do
14
+ element "trackChanges"
15
+ namespace Uniword::Ooxml::Namespaces::WordProcessingML
16
+ end
17
+ end
18
+ end
19
+ end
@@ -88,6 +88,7 @@ module Uniword
88
88
  autoload :DefaultTabStop, "uniword/wordprocessingml/default_tab_stop"
89
89
  autoload :CharacterSpacingControl, "uniword/wordprocessingml/character_spacing_control"
90
90
  autoload :DoNotDisplayPageBoundaries, "uniword/wordprocessingml/do_not_display_page_boundaries"
91
+ autoload :TrackChanges, "uniword/wordprocessingml/track_changes"
91
92
  autoload :Rsids, "uniword/wordprocessingml/rsids"
92
93
  autoload :RsidRoot, "uniword/wordprocessingml/rsid_root"
93
94
  autoload :Rsid, "uniword/wordprocessingml/rsid"
data/lib/uniword.rb CHANGED
@@ -130,6 +130,22 @@ module Uniword
130
130
  # Builder API (construction layer for OOXML models)
131
131
  autoload :Builder, "uniword/builder"
132
132
 
133
+ # Find & replace over document parts (body, headers, footers,
134
+ # footnotes, endnotes, comments, styles).
135
+ autoload :FindReplace, "uniword/find_replace"
136
+
137
+ # Document redaction (PII patterns, custom regex).
138
+ autoload :Redact, "uniword/redact"
139
+
140
+ # Document lint (style-guide enforcement).
141
+ autoload :Lint, "uniword/lint"
142
+
143
+ # Auto-numbered captions + cross-references.
144
+ autoload :Caption, "uniword/caption"
145
+
146
+ # Plugin system (validators, transformers, CLI commands).
147
+ autoload :Plugin, "uniword/plugin"
148
+
133
149
  # CLI
134
150
  autoload :CLI, "uniword/cli/main"
135
151
  autoload :GenerateCLI, "uniword/cli/generate_cli"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uniword
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-27 00:00:00.000000000 Z
11
+ date: 2026-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logger
@@ -592,6 +592,13 @@ files:
592
592
  - lib/uniword/batch.rb
593
593
  - lib/uniword/batch/batch_result.rb
594
594
  - lib/uniword/batch/document_processor.rb
595
+ - lib/uniword/batch/operation.rb
596
+ - lib/uniword/batch/operation/file_result.rb
597
+ - lib/uniword/batch/operation/repair_task.rb
598
+ - lib/uniword/batch/operation/report.rb
599
+ - lib/uniword/batch/operation/runner.rb
600
+ - lib/uniword/batch/operation/task.rb
601
+ - lib/uniword/batch/operation/verify_task.rb
595
602
  - lib/uniword/batch/processing_stage.rb
596
603
  - lib/uniword/batch/stages/compress_images_stage.rb
597
604
  - lib/uniword/batch/stages/convert_format_stage.rb
@@ -656,6 +663,10 @@ files:
656
663
  - lib/uniword/builder/theme_builder.rb
657
664
  - lib/uniword/builder/toc_builder.rb
658
665
  - lib/uniword/builder/watermark_builder.rb
666
+ - lib/uniword/caption.rb
667
+ - lib/uniword/caption/caption_builder.rb
668
+ - lib/uniword/caption/counter.rb
669
+ - lib/uniword/caption/cross_reference.rb
659
670
  - lib/uniword/chart.rb
660
671
  - lib/uniword/chart/area3_d_chart.rb
661
672
  - lib/uniword/chart/area_chart.rb
@@ -800,6 +811,11 @@ files:
800
811
  - lib/uniword/diff/formatter.rb
801
812
  - lib/uniword/diff/package_diff_result.rb
802
813
  - lib/uniword/diff/package_differ.rb
814
+ - lib/uniword/diff/semantic.rb
815
+ - lib/uniword/diff/semantic/change.rb
816
+ - lib/uniword/diff/semantic/engine.rb
817
+ - lib/uniword/diff/semantic/paragraph_comparator.rb
818
+ - lib/uniword/diff/semantic/result.rb
803
819
  - lib/uniword/document_factory.rb
804
820
  - lib/uniword/document_input.rb
805
821
  - lib/uniword/document_variables.rb
@@ -817,6 +833,7 @@ files:
817
833
  - lib/uniword/docx.rb
818
834
  - lib/uniword/docx/chart_part.rb
819
835
  - lib/uniword/docx/custom_xml_item.rb
836
+ - lib/uniword/docx/deterministic_output.rb
820
837
  - lib/uniword/docx/document_statistics.rb
821
838
  - lib/uniword/docx/header_footer_part.rb
822
839
  - lib/uniword/docx/header_footer_part_collection.rb
@@ -1001,6 +1018,21 @@ files:
1001
1018
  - lib/uniword/endnote.rb
1002
1019
  - lib/uniword/errors.rb
1003
1020
  - lib/uniword/field.rb
1021
+ - lib/uniword/find_replace.rb
1022
+ - lib/uniword/find_replace/body_scope.rb
1023
+ - lib/uniword/find_replace/comment_scope.rb
1024
+ - lib/uniword/find_replace/endnote_scope.rb
1025
+ - lib/uniword/find_replace/engine.rb
1026
+ - lib/uniword/find_replace/footer_scope.rb
1027
+ - lib/uniword/find_replace/footnote_scope.rb
1028
+ - lib/uniword/find_replace/header_scope.rb
1029
+ - lib/uniword/find_replace/matcher.rb
1030
+ - lib/uniword/find_replace/paragraph_walker.rb
1031
+ - lib/uniword/find_replace/regex_matcher.rb
1032
+ - lib/uniword/find_replace/result.rb
1033
+ - lib/uniword/find_replace/scope.rb
1034
+ - lib/uniword/find_replace/string_matcher.rb
1035
+ - lib/uniword/find_replace/styles_scope.rb
1004
1036
  - lib/uniword/footer.rb
1005
1037
  - lib/uniword/footnote.rb
1006
1038
  - lib/uniword/format_converter.rb
@@ -1048,6 +1080,16 @@ files:
1048
1080
  - lib/uniword/infrastructure/zip_packager.rb
1049
1081
  - lib/uniword/lazy_loader.rb
1050
1082
  - lib/uniword/line_numbering.rb
1083
+ - lib/uniword/lint.rb
1084
+ - lib/uniword/lint/builtin_rules.rb
1085
+ - lib/uniword/lint/builtin_rules/banned_words.rb
1086
+ - lib/uniword/lint/builtin_rules/max_paragraph_length.rb
1087
+ - lib/uniword/lint/builtin_rules/require_body.rb
1088
+ - lib/uniword/lint/builtin_rules/required_style.rb
1089
+ - lib/uniword/lint/engine.rb
1090
+ - lib/uniword/lint/result.rb
1091
+ - lib/uniword/lint/rule.rb
1092
+ - lib/uniword/lint/ruleset.rb
1051
1093
  - lib/uniword/loggable.rb
1052
1094
  - lib/uniword/logger.rb
1053
1095
  - lib/uniword/math.rb
@@ -1179,6 +1221,12 @@ files:
1179
1221
  - lib/uniword/picture/picture_source_rect.rb
1180
1222
  - lib/uniword/picture/picture_stretch.rb
1181
1223
  - lib/uniword/picture/tile.rb
1224
+ - lib/uniword/plugin.rb
1225
+ - lib/uniword/plugin/cli_command.rb
1226
+ - lib/uniword/plugin/loader.rb
1227
+ - lib/uniword/plugin/registry.rb
1228
+ - lib/uniword/plugin/transformer.rb
1229
+ - lib/uniword/plugin/validator.rb
1182
1230
  - lib/uniword/presentationml.rb
1183
1231
  - lib/uniword/presentationml/audio.rb
1184
1232
  - lib/uniword/presentationml/body_properties.rb
@@ -1294,6 +1342,11 @@ files:
1294
1342
  - lib/uniword/quality/rules/paragraph_length_rule.rb
1295
1343
  - lib/uniword/quality/rules/style_consistency_rule.rb
1296
1344
  - lib/uniword/quality/rules/table_header_rule.rb
1345
+ - lib/uniword/redact.rb
1346
+ - lib/uniword/redact/engine.rb
1347
+ - lib/uniword/redact/pattern.rb
1348
+ - lib/uniword/redact/pattern_library.rb
1349
+ - lib/uniword/redact/result.rb
1297
1350
  - lib/uniword/resource.rb
1298
1351
  - lib/uniword/resource/cache.rb
1299
1352
  - lib/uniword/resource/cache_paths.rb
@@ -1849,6 +1902,7 @@ files:
1849
1902
  - lib/uniword/wordprocessingml/theme_font_lang.rb
1850
1903
  - lib/uniword/wordprocessingml/title_pg.rb
1851
1904
  - lib/uniword/wordprocessingml/tr_height.rb
1905
+ - lib/uniword/wordprocessingml/track_changes.rb
1852
1906
  - lib/uniword/wordprocessingml/ui_priority.rb
1853
1907
  - lib/uniword/wordprocessingml/update_fields.rb
1854
1908
  - lib/uniword/wordprocessingml/val_int.rb