uniword 1.5.0 → 1.5.2

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/uniword/cli/main.rb +134 -0
  4. data/lib/uniword/cli/review_cli.rb +34 -0
  5. data/lib/uniword/configuration.rb +17 -0
  6. data/lib/uniword/docx/deterministic_output.rb +47 -0
  7. data/lib/uniword/docx.rb +1 -0
  8. data/lib/uniword/find_replace/body_scope.rb +23 -0
  9. data/lib/uniword/find_replace/comment_scope.rb +23 -0
  10. data/lib/uniword/find_replace/endnote_scope.rb +23 -0
  11. data/lib/uniword/find_replace/engine.rb +84 -0
  12. data/lib/uniword/find_replace/footer_scope.rb +24 -0
  13. data/lib/uniword/find_replace/footnote_scope.rb +23 -0
  14. data/lib/uniword/find_replace/header_scope.rb +24 -0
  15. data/lib/uniword/find_replace/matcher.rb +23 -0
  16. data/lib/uniword/find_replace/paragraph_walker.rb +61 -0
  17. data/lib/uniword/find_replace/regex_matcher.rb +38 -0
  18. data/lib/uniword/find_replace/result.rb +54 -0
  19. data/lib/uniword/find_replace/scope.rb +98 -0
  20. data/lib/uniword/find_replace/string_matcher.rb +72 -0
  21. data/lib/uniword/find_replace/styles_scope.rb +38 -0
  22. data/lib/uniword/find_replace.rb +29 -0
  23. data/lib/uniword/infrastructure/zip_packager.rb +49 -3
  24. data/lib/uniword/lint/builtin_rules/banned_words.rb +35 -0
  25. data/lib/uniword/lint/builtin_rules/max_paragraph_length.rb +32 -0
  26. data/lib/uniword/lint/builtin_rules/require_body.rb +23 -0
  27. data/lib/uniword/lint/builtin_rules/required_style.rb +31 -0
  28. data/lib/uniword/lint/builtin_rules.rb +23 -0
  29. data/lib/uniword/lint/engine.rb +32 -0
  30. data/lib/uniword/lint/result.rb +59 -0
  31. data/lib/uniword/lint/rule.rb +77 -0
  32. data/lib/uniword/lint/ruleset.rb +65 -0
  33. data/lib/uniword/lint.rb +17 -0
  34. data/lib/uniword/redact/engine.rb +52 -0
  35. data/lib/uniword/redact/pattern.rb +23 -0
  36. data/lib/uniword/redact/pattern_library.rb +60 -0
  37. data/lib/uniword/redact/result.rb +42 -0
  38. data/lib/uniword/redact.rb +15 -0
  39. data/lib/uniword/version.rb +1 -1
  40. data/lib/uniword/wordprocessingml/document_styling.rb +91 -0
  41. data/lib/uniword/wordprocessingml/settings.rb +2 -0
  42. data/lib/uniword/wordprocessingml/track_changes.rb +19 -0
  43. data/lib/uniword/wordprocessingml.rb +1 -0
  44. data/lib/uniword.rb +10 -0
  45. metadata +34 -2
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ module Lint
5
+ # Abstract base class for a single lint rule. Subclasses implement
6
+ # `#check` (yields findings) and declare `name`, `severity`.
7
+ #
8
+ # Open/closed: new rule type = new subclass + (optionally) a
9
+ # YAML schema extension. Engine unchanged.
10
+ class Rule
11
+ # Registered rule subclasses by type name. Constant on `Rule`
12
+ # itself (not subclasses) so `register` calls always reach the
13
+ # same hash. `Hash.new` (rather than `{}`) avoids the
14
+ # Style/MutableConstant autocorrect that would `.freeze` it and
15
+ # break `register`.
16
+ TYPES = Hash.new
17
+
18
+ attr_reader :name, :severity, :options
19
+
20
+ # @param name [Symbol] rule identifier
21
+ # @param severity [Symbol] :error, :warning, or :info
22
+ # @param options [Hash] rule-specific options
23
+ def initialize(name:, severity: :warning, **options)
24
+ @name = name
25
+ @severity = severity
26
+ @options = options
27
+ end
28
+
29
+ # Walk the document and yield each finding.
30
+ #
31
+ # @param document [Wordprocessingml::DocumentRoot]
32
+ # @yieldparam finding [Hash] { message:, path:, position: }
33
+ # @return [void]
34
+ def check(document)
35
+ raise NotImplementedError
36
+ end
37
+
38
+ # Build a finding hash.
39
+ #
40
+ # @param message [String]
41
+ # @param path [String, nil] document path (paragraph index,
42
+ # style id, etc.)
43
+ # @return [Hash]
44
+ def finding(message:, path: nil)
45
+ { rule: name, severity: severity, message: message, path: path }
46
+ end
47
+
48
+ class << self
49
+ # Register a subclass under a type name for YAML ruleset
50
+ # lookup. Always writes to `Rule::TYPES` regardless of the
51
+ # calling subclass.
52
+ #
53
+ # @param name [Symbol]
54
+ # @param klass [Class<Rule>]
55
+ # @return [void]
56
+ def register(name, klass)
57
+ Rule::TYPES[name] = klass
58
+ end
59
+
60
+ # Lookup a registered rule subclass by type name.
61
+ #
62
+ # @param name [Symbol]
63
+ # @return [Class<Rule>, nil]
64
+ def type(name)
65
+ Rule::TYPES[name]
66
+ end
67
+
68
+ # All registered rule types.
69
+ #
70
+ # @return [Hash{Symbol => Class<Rule>}]
71
+ def types
72
+ Rule::TYPES
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Uniword
6
+ module Lint
7
+ # Loads rules from a YAML ruleset or symbol registry. Each rule
8
+ # has a `type` (which `Rule` subclass to instantiate) and
9
+ # rule-specific options.
10
+ #
11
+ # Example YAML:
12
+ #
13
+ # rules:
14
+ # - type: sentence_case_headings
15
+ # severity: warning
16
+ # - type: max_paragraph_length
17
+ # severity: warning
18
+ # max: 200
19
+ # - type: banned_words
20
+ # severity: warning
21
+ # words: [really, very, basically]
22
+ class Ruleset
23
+ # @param rules [Array<Rule>]
24
+ def initialize(rules = [])
25
+ @rules = rules
26
+ end
27
+
28
+ # @return [Array<Rule>]
29
+ attr_reader :rules
30
+
31
+ # Load a ruleset from a YAML file.
32
+ #
33
+ # @param path [String]
34
+ # @return [Ruleset]
35
+ def self.load(path)
36
+ data = YAML.safe_load_file(path)
37
+ from_hash(data || {})
38
+ end
39
+
40
+ # Build a ruleset from a parsed YAML hash.
41
+ #
42
+ # @param data [Hash] `{"rules" => [{"type" => ..., ...}, ...]}`
43
+ # @return [Ruleset]
44
+ def self.from_hash(data)
45
+ rule_list = (data["rules"] || []).map { |spec| build_rule(spec) }
46
+ new(rule_list)
47
+ end
48
+
49
+ # Build one rule from a spec hash by type name.
50
+ #
51
+ # @param spec [Hash] `{"type" => "name", "severity" => ...}`
52
+ # @return [Rule]
53
+ def self.build_rule(spec)
54
+ type = spec["type"].to_sym
55
+ klass = Rule.types[type]
56
+ raise ArgumentError, "unknown rule type: #{spec['type']}" unless klass
57
+
58
+ kwargs = spec.transform_keys(&:to_sym)
59
+ kwargs.delete(:type)
60
+ kwargs[:name] = type unless kwargs.key?(:name)
61
+ klass.new(**kwargs)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uniword
4
+ # Document lint: enforce content + structure rules beyond OPC
5
+ # validity. Backed by a YAML ruleset; rules are open for extension
6
+ # via `Lint::Rule.register`.
7
+ module Lint
8
+ autoload :Engine, "#{__dir__}/lint/engine"
9
+ autoload :Ruleset, "#{__dir__}/lint/ruleset"
10
+ autoload :Rule, "#{__dir__}/lint/rule"
11
+ autoload :Result, "#{__dir__}/lint/result"
12
+ autoload :BuiltinRules, "#{__dir__}/lint/builtin_rules"
13
+
14
+ # Force-load the builtins so their `register` calls run.
15
+ BuiltinRules.to_s
16
+ end
17
+ end
@@ -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.0"
4
+ VERSION = "1.5.2"
5
5
  end
@@ -119,6 +119,82 @@ 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
+
122
198
  # Apply uniform page setup to every section of the document
123
199
  #
124
200
  # Mirrors Word's Layout dialog: named paper sizes, orientation
@@ -230,6 +306,21 @@ module Uniword
230
306
 
231
307
  private
232
308
 
309
+ def build_find_replace_matcher(pattern, replacement, ignore_case)
310
+ if pattern.is_a?(Regexp)
311
+ FindReplace::RegexMatcher.new(pattern: pattern,
312
+ replacement: replacement)
313
+ else
314
+ FindReplace::StringMatcher.new(pattern: pattern,
315
+ replacement: replacement,
316
+ ignore_case: ignore_case)
317
+ end
318
+ end
319
+
320
+ def ensure_settings
321
+ self.settings ||= Wordprocessingml::Settings.new
322
+ end
323
+
233
324
  # Ensure the document carries a theme part, creating it from a
234
325
  # fresh parse of the bundled Office theme when absent (every
235
326
  # 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,16 @@ 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
+
133
143
  # CLI
134
144
  autoload :CLI, "uniword/cli/main"
135
145
  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.0
4
+ version: 1.5.2
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-21 00:00:00.000000000 Z
11
+ date: 2026-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logger
@@ -817,6 +817,7 @@ files:
817
817
  - lib/uniword/docx.rb
818
818
  - lib/uniword/docx/chart_part.rb
819
819
  - lib/uniword/docx/custom_xml_item.rb
820
+ - lib/uniword/docx/deterministic_output.rb
820
821
  - lib/uniword/docx/document_statistics.rb
821
822
  - lib/uniword/docx/header_footer_part.rb
822
823
  - lib/uniword/docx/header_footer_part_collection.rb
@@ -1001,6 +1002,21 @@ files:
1001
1002
  - lib/uniword/endnote.rb
1002
1003
  - lib/uniword/errors.rb
1003
1004
  - lib/uniword/field.rb
1005
+ - lib/uniword/find_replace.rb
1006
+ - lib/uniword/find_replace/body_scope.rb
1007
+ - lib/uniword/find_replace/comment_scope.rb
1008
+ - lib/uniword/find_replace/endnote_scope.rb
1009
+ - lib/uniword/find_replace/engine.rb
1010
+ - lib/uniword/find_replace/footer_scope.rb
1011
+ - lib/uniword/find_replace/footnote_scope.rb
1012
+ - lib/uniword/find_replace/header_scope.rb
1013
+ - lib/uniword/find_replace/matcher.rb
1014
+ - lib/uniword/find_replace/paragraph_walker.rb
1015
+ - lib/uniword/find_replace/regex_matcher.rb
1016
+ - lib/uniword/find_replace/result.rb
1017
+ - lib/uniword/find_replace/scope.rb
1018
+ - lib/uniword/find_replace/string_matcher.rb
1019
+ - lib/uniword/find_replace/styles_scope.rb
1004
1020
  - lib/uniword/footer.rb
1005
1021
  - lib/uniword/footnote.rb
1006
1022
  - lib/uniword/format_converter.rb
@@ -1048,6 +1064,16 @@ files:
1048
1064
  - lib/uniword/infrastructure/zip_packager.rb
1049
1065
  - lib/uniword/lazy_loader.rb
1050
1066
  - lib/uniword/line_numbering.rb
1067
+ - lib/uniword/lint.rb
1068
+ - lib/uniword/lint/builtin_rules.rb
1069
+ - lib/uniword/lint/builtin_rules/banned_words.rb
1070
+ - lib/uniword/lint/builtin_rules/max_paragraph_length.rb
1071
+ - lib/uniword/lint/builtin_rules/require_body.rb
1072
+ - lib/uniword/lint/builtin_rules/required_style.rb
1073
+ - lib/uniword/lint/engine.rb
1074
+ - lib/uniword/lint/result.rb
1075
+ - lib/uniword/lint/rule.rb
1076
+ - lib/uniword/lint/ruleset.rb
1051
1077
  - lib/uniword/loggable.rb
1052
1078
  - lib/uniword/logger.rb
1053
1079
  - lib/uniword/math.rb
@@ -1294,6 +1320,11 @@ files:
1294
1320
  - lib/uniword/quality/rules/paragraph_length_rule.rb
1295
1321
  - lib/uniword/quality/rules/style_consistency_rule.rb
1296
1322
  - lib/uniword/quality/rules/table_header_rule.rb
1323
+ - lib/uniword/redact.rb
1324
+ - lib/uniword/redact/engine.rb
1325
+ - lib/uniword/redact/pattern.rb
1326
+ - lib/uniword/redact/pattern_library.rb
1327
+ - lib/uniword/redact/result.rb
1297
1328
  - lib/uniword/resource.rb
1298
1329
  - lib/uniword/resource/cache.rb
1299
1330
  - lib/uniword/resource/cache_paths.rb
@@ -1849,6 +1880,7 @@ files:
1849
1880
  - lib/uniword/wordprocessingml/theme_font_lang.rb
1850
1881
  - lib/uniword/wordprocessingml/title_pg.rb
1851
1882
  - lib/uniword/wordprocessingml/tr_height.rb
1883
+ - lib/uniword/wordprocessingml/track_changes.rb
1852
1884
  - lib/uniword/wordprocessingml/ui_priority.rb
1853
1885
  - lib/uniword/wordprocessingml/update_fields.rb
1854
1886
  - lib/uniword/wordprocessingml/val_int.rb