lm_docstache 2.0.2 → 3.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e32cdd0e7cb785ccb74285d2df67556c6ae24184a49511dd08fa6aa523e845c
4
- data.tar.gz: d5872f502d4e95d4135dfcf3014826c76fa3968db8be301e4cc52d73d04b9951
3
+ metadata.gz: b43bd6c92fe86cdde66926647f6b98a90c68d9cdf2418d7c3db07494d1872bc1
4
+ data.tar.gz: 52b819e713189acc459c8290614a46791e7dbc6e0049ba32e2fb7b204f44bd29
5
5
  SHA512:
6
- metadata.gz: 65852b8af255a23a06064bc91eee802005f31cdff3e3fc3258cbb03fbec9ba07e2f0c5cbd0af37f25324db2df774070280596a87fb49e2332d28a368514ca837
7
- data.tar.gz: 85ebb2d3d2af5a0a3c7e3787a8ba98688193e220e56aceba5b37aa3ac500a39fc3914f6d14251d4f4007a317e677d41cf6b8fd7a98e4d51960e441abac40a59a
6
+ metadata.gz: f96c3e1d1f76400286c984309db190326440aaf6c4bf93aaec2896297fa1fad4a1e44fb0c09f0e4c2aa0c8a912876e5aabdf5eadd7edd85a7345d2d819860377
7
+ data.tar.gz: fee26975013fe5e92699a5da1ef317c9bf989269f5a369e3e11ef47aa82a8047d1648563ece41d4fed6186c58a7fe7319ce9ba9b1cfab732fbc7dd77e464c981
data/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.0.1
4
+
5
+ ### Bugfix
6
+
7
+ * Fix Hide Custom Tag feature when document there is no text inside a w|r we
8
+ can't split content.
9
+
10
+ ## 3.0.0
11
+
12
+ ## Breaking Changes
13
+ * Replaced Renderer `hide_custom_tags` options to be a `Hash` instead of `Array`.
14
+ There are are edge cases which we want to replace the content on hide custom tags.
15
+ All documentations can be followed on `Renderer` and `HideCustomTag` classes.
16
+
17
+ ## 2.1.2
18
+
19
+ ### Bugfix
20
+
21
+ * Giving a document there is no *rPr* tag we should add new *rPr* tag before
22
+ *t* tag.
23
+
24
+ ## 2.1.1
25
+
26
+ ### Improvements
27
+
28
+ * Hidden custom tags now follow your document background color. If there is no background defined
29
+ we assume white font color.
30
+
31
+ ## 2.1.0
32
+
33
+ #### Improvements
34
+
35
+ * Add new feature which you're able to "hide" matching RegExp rule with white font color.
36
+ This change allows you to keep your content and don't loose locations of hidden tags.
37
+ You can use it just adding `hide_custom_tags` list of RegExp options to `Document#render_file`.
38
+ * Added `Document#render_xml` options parameter so you can call it with more documents
39
+ formatting possibilities.
40
+
3
41
  ## 2.0.2
4
42
 
5
43
  ### Improvements and bugfixes
data/lib/lm_docstache.rb CHANGED
@@ -2,6 +2,7 @@ require 'nokogiri'
2
2
  require 'zip'
3
3
  require "lm_docstache/version"
4
4
  require "lm_docstache/document"
5
+ require 'lm_docstache/hide_custom_tags'
5
6
  require "lm_docstache/parser"
6
7
  require "lm_docstache/condition"
7
8
  require "lm_docstache/conditional_block"
@@ -93,8 +93,8 @@ module LMDocstache
93
93
  buffer.sysread
94
94
  end
95
95
 
96
- def render_xml(data = {})
97
- render_documents(data)
96
+ def render_xml(data = {}, render_options = {})
97
+ render_documents(data, nil, render_options)
98
98
  end
99
99
 
100
100
  private
@@ -0,0 +1,90 @@
1
+ module LMDocstache
2
+ class HideCustomTags
3
+ attr_reader :document, :hide_custom_tags
4
+
5
+ # The +hide_custom_tags+ options is a +Hash+ of +Regexp+ or +String+ keys representing
6
+ # the pattern you expect to keep at the document but replacing the content to use
7
+ # font color equal to document background color or white.
8
+ # For the +Hash+ values we can have:
9
+ #
10
+ # * +false+ -> In this case we don't change the text content.
11
+ # * +Proc+ -> When a +Proc+ instance is provided, it's expected it to be
12
+ # able to receive the matched string and to return the string that will be
13
+ # used as replacement.
14
+ # * any other value that will be turned into a string -> in this case, this
15
+ # will be the value that will replace the matched string
16
+ def initialize(document:, hide_custom_tags: {})
17
+ @document = document
18
+ @hide_custom_tags = hide_custom_tags
19
+ end
20
+
21
+ # Find all run nodes matching hide custom tags +Regexp's+ options you defined, split it
22
+ # in multiple runs and replace font color to document background color or white in the matching tag run node.
23
+ # Replace content if you have defined any replacement value.
24
+ def hide_custom_tags!
25
+ hide_custom_tags.each do |full_pattern, value|
26
+ paragraphs = document.css('w|p')
27
+ while paragraph = paragraphs.shift do
28
+ next unless paragraph.text =~ full_pattern
29
+ run_nodes = paragraph.css('w|r')
30
+ while run_node = run_nodes.shift
31
+ next if run_node.text.to_s.strip.size == 0
32
+ next unless run_node.at_css('w|t')
33
+ remainder_run_node = run_node.clone
34
+ run_node.unlink
35
+ tag_contents = split_tag_content(remainder_run_node.text, full_pattern)
36
+ tag_contents[:content_list].each_with_index do |content, idx|
37
+ replace_content(remainder_run_node, content)
38
+ run_node_with_match = remainder_run_node.dup
39
+ matched_tag = tag_contents[:matched_tags][idx]
40
+ nodes_list = [remainder_run_node]
41
+ if matched_tag
42
+ replace_style(run_node_with_match)
43
+ matched_content = matched_tag
44
+ if value
45
+ matched_content = value.is_a?(Proc) ?
46
+ value.call(matched_tag) :
47
+ value.to_s
48
+ end
49
+ replace_content(run_node_with_match, matched_content)
50
+ nodes_list << run_node_with_match
51
+ end
52
+ paragraph << Nokogiri::XML::NodeSet.new(document, nodes_list)
53
+ remainder_run_node = remainder_run_node.clone
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def font_color
63
+ @font_color ||= document.at_css('w|background')&.attr('w:color') || 'FFFFFF'
64
+ end
65
+
66
+ def split_tag_content(text, full_pattern)
67
+ content_list = text.split(full_pattern)
68
+ content_list = content_list.empty? ? [''] : content_list
69
+ matched_tags = text.scan(full_pattern)
70
+ { content_list: content_list, matched_tags: matched_tags}
71
+ end
72
+
73
+ def replace_style(run_node)
74
+ style = run_node.at_css('w|rPr')
75
+ if style
76
+ w_color = style.at_css('w|color')
77
+ w_color.unlink if w_color
78
+ style << "<w:color w:val=\"#{font_color}\"/>"
79
+ else
80
+ run_node.prepend_child("<w:rPr><w:color w:val=\"#{font_color}\"/></w:rPr>")
81
+ end
82
+ end
83
+
84
+ def replace_content(run_node, content)
85
+ run_text = run_node.at_css('w|t')
86
+ run_text['xml:space'] = 'preserve'
87
+ run_text.content = content
88
+ end
89
+ end
90
+ end
@@ -17,7 +17,7 @@ module LMDocstache
17
17
  BLOCK_MATCHER = /#{BLOCK_PATTERN}/
18
18
  VARIABLE_MATCHER = /{{([^#\^\/].*?)}}/
19
19
 
20
- attr_reader :document, :data, :blocks, :special_variable_replacements
20
+ attr_reader :document, :data, :blocks, :special_variable_replacements, :hide_custom_tags
21
21
 
22
22
  # The +special_variable_replacements+ option is a +Hash+ where the key is
23
23
  # expected to be either a +Regexp+ or a +String+ representing the pattern
@@ -34,13 +34,35 @@ module LMDocstache
34
34
  # * any other value that will be turned into a string -> in this case, this
35
35
  # will be the value that will replace the matched string
36
36
  #
37
+ # The +hide_custom_tags+ options is a +Hash+ of +Regexp+ or +String+ keys representing
38
+ # the pattern you expect to keep at the document but replacing the content to use
39
+ # font color equal to document background color or white.
40
+ # For the +Hash+ values we can have:
41
+ #
42
+ # * +false+ -> In this case we don't change the text content.
43
+ # * +Proc+ -> When a +Proc+ instance is provided, it's expected it to be
44
+ # able to receive the matched string and to return the string that will be
45
+ # used as replacement.
46
+ # * any other value that will be turned into a string -> in this case, this
47
+ # will be the value that will replace the matched string
37
48
  def initialize(document, data, options = {})
38
49
  @document = document
39
50
  @data = data.transform_keys(&:to_s)
40
- @special_variable_replacements = options.fetch(:special_variable_replacements, {})
51
+ @special_variable_replacements = add_blocks_to_regexp(options.fetch(:special_variable_replacements, {}))
52
+ @hide_custom_tags = add_blocks_to_regexp(options.fetch(:hide_custom_tags, {}))
53
+ end
54
+
55
+ # Replace +Regepx+ or +String+ keys to have the enclosing blocks
56
+ def add_blocks_to_regexp(options)
57
+ options.inject({}) do |x, (regexp_str, value)|
58
+ key = regexp_str.is_a?(String) ? Regexp.new("{{#{regexp_str}}}") : /{{#{regexp_str.source}}/
59
+ x[key] = value
60
+ x
61
+ end
41
62
  end
42
63
 
43
64
  def parse_and_update_document!
65
+ hide_custom_tags!
44
66
  find_blocks
45
67
  replace_conditional_blocks_in_document!
46
68
  replace_variables_in_document!
@@ -81,6 +103,11 @@ module LMDocstache
81
103
  @blocks
82
104
  end
83
105
 
106
+ def hide_custom_tags!
107
+ custom_tags = HideCustomTags.new(document: document, hide_custom_tags: hide_custom_tags)
108
+ custom_tags.hide_custom_tags!
109
+ end
110
+
84
111
  # Evaluates all conditional blocks inside the given XML document and keep or
85
112
  # remove their content inside the document, depending on the truthiness of
86
113
  # the condition on each given conditional block.
@@ -109,14 +136,13 @@ module LMDocstache
109
136
  variable_replacement.call($1) :
110
137
  variable_replacement.to_s
111
138
  end
112
-
113
139
  text_node.content = text
114
140
  end
115
141
  end
116
142
 
117
143
  def has_skippable_variable?(text)
144
+ return true if hide_custom_tags.find { |(pattern, value)| text =~ pattern }
118
145
  !!special_variable_replacements.find do |(pattern, value)|
119
- pattern = pattern.is_a?(String) ? /{{#{pattern}}}/ : /{{#{pattern.source}}}/
120
146
  text =~ pattern && value == false
121
147
  end
122
148
  end
@@ -124,7 +150,6 @@ module LMDocstache
124
150
  def special_variable_replacement(text)
125
151
  Array(
126
152
  special_variable_replacements.find do |(pattern, value)|
127
- pattern = pattern.is_a?(String) ? /{{#{pattern}}}/ : /{{#{pattern.source}}}/
128
153
  text =~ pattern && !!value
129
154
  end
130
155
  ).last
@@ -6,7 +6,8 @@ module LMDocstache
6
6
 
7
7
  def initialize(xml, data, options = {})
8
8
  @content = xml
9
- @parser = Parser.new(xml, data, options.slice(:special_variable_replacements))
9
+ option_types = [:special_variable_replacements, :hide_custom_tags]
10
+ @parser = Parser.new(xml, data, options.slice(*option_types))
10
11
  end
11
12
 
12
13
  def render
@@ -1,3 +1,3 @@
1
1
  module LMDocstache
2
- VERSION = "2.0.2"
2
+ VERSION = "3.0.1"
3
3
  end
@@ -24,9 +24,7 @@ describe LMDocstache::Renderer do
24
24
  def render_docx(doc_text)
25
25
  # create doc from blank
26
26
  blank_doc.render_replace(temp_file, doc_text)
27
-
28
- doc = LMDocstache::Document.new(temp_file).render_file(result_file, data)
29
-
27
+ LMDocstache::Document.new(temp_file).render_file(result_file, data)
30
28
  result_doc = LMDocstache::Document.new(result_file).render_xml(data)
31
29
  result_doc["word/document.xml"].text
32
30
  end
Binary file
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe LMDocstache::HideCustomTags do
4
+ context '#example' do
5
+ let(:output_dir) { "#{base_path}/tmp/" }
6
+ let(:output_file) { File.new("#{output_dir}/BlankTestOutput.docx", 'w') }
7
+
8
+ before do
9
+ FileUtils.rm_rf(output_dir) if File.exist?(output_dir)
10
+ Dir.mkdir(output_dir)
11
+ end
12
+
13
+ after do
14
+ File.delete(output_file.path)
15
+ end
16
+
17
+ let(:base_path) { SPEC_BASE_PATH.join('example_input') }
18
+ let(:document) {
19
+ doc = LMDocstache::Document.new(input_file)
20
+ doc.fix_errors
21
+ doc.instance_variable_get(:@document)
22
+ }
23
+ let(:regexp_tag) { /(?:sig|sigfirm|date|text|initial)\|(?:req|noreq)\|.+?/ }
24
+ let(:regexp_for_replacement) { /(?:check)\|(?:req|noreq)\|.+?/ }
25
+ let(:hide_custom_tags) {
26
+ LMDocstache::HideCustomTags.new(document: document, hide_custom_tags: {
27
+ /#{regexp_tag}/ => false,
28
+ /#{regexp_for_replacement}/ => 'replaced_content'
29
+ })
30
+ }
31
+
32
+ context "giving a document with blue background" do
33
+ let(:input_file) { "#{base_path}/sample-signature-blue.docx" }
34
+
35
+ it 'expect to have a white color on all hide custom tags matching and have first child node equal rPr tag' do
36
+ hide_custom_tags.hide_custom_tags!
37
+ d = hide_custom_tags.document
38
+ run_nodes = d.css('w|p w|r')
39
+ while run_node = run_nodes.shift
40
+ next unless run_node.text =~ regexp_tag
41
+ expect(run_node.at_css('w|rPr w|color').first[1]).to eq('4472C4')
42
+ expect(run_node.children.first.name).to eq('rPr')
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'giving a document with white background' do
48
+ let(:input_file) { "#{base_path}/sample-signature.docx" }
49
+
50
+ it 'expect to have a white color on all hide custom tags matching and have first child node equal rPr tag' do
51
+ hide_custom_tags.hide_custom_tags!
52
+ d = hide_custom_tags.document
53
+ run_nodes = d.css('w|p w|r')
54
+ while run_node = run_nodes.shift
55
+ next unless run_node.text =~ regexp_tag
56
+ expect(run_node.at_css('w|rPr w|color').first[1]).to eq('FFFFFF')
57
+ expect(run_node.children.first.name).to eq('rPr')
58
+ end
59
+ end
60
+ end
61
+ context 'giving a document without rpr and block tags on the left' do
62
+ let(:input_file) { "#{base_path}/docx-no-rpr.docx" }
63
+
64
+ it 'expect to have a white color on all hide custom tags matching and have first child node equal rPr tag' do
65
+ hide_custom_tags.hide_custom_tags!
66
+ d = hide_custom_tags.document
67
+ run_nodes = d.css('w|p w|r')
68
+ while run_node = run_nodes.shift
69
+ next unless run_node.text =~ regexp_tag
70
+ expect(run_node.at_css('w|rPr w|color').first[1]).to eq('FFFFFF')
71
+ expect(run_node.children.first.name).to eq('rPr')
72
+ end
73
+ end
74
+ it 'expect to have a white color on all replacement tags and content following replacement' do
75
+ hide_custom_tags.hide_custom_tags!
76
+ d = hide_custom_tags.document
77
+ run_nodes = d.css('w|p w|r')
78
+ total_replacement = 0
79
+ while run_node = run_nodes.shift
80
+ next unless run_node.text =~ /replaced_content/
81
+ total_replacement+=1
82
+ expect(run_node.at_css('w|rPr w|color').first[1]).to eq('FFFFFF')
83
+ expect(run_node.children.first.name).to eq('rPr')
84
+ end
85
+ expect(total_replacement).to eq(2)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -33,18 +33,20 @@ module LMDocstache
33
33
  end
34
34
 
35
35
  describe 'integration test', integration: true do
36
- let(:data) { LMDocstache::TestData::DATA }
37
36
  let(:base_path) { SPEC_BASE_PATH.join('example_input') }
38
- let(:input_file) { "#{base_path}/ExampleTemplate.docx" }
39
37
  let(:output_dir) { "#{base_path}/tmp" }
40
- let(:output_file) { "#{output_dir}/IntegrationTestOutput.docx" }
41
- let(:document) { LMDocstache::Document.new(input_file) }
42
- before do
43
- FileUtils.rm_rf(output_dir) if File.exist?(output_dir)
44
- Dir.mkdir(output_dir)
45
- end
46
38
 
47
39
  context 'should process that incoming docx' do
40
+ let(:data) { LMDocstache::TestData::DATA }
41
+ let(:input_file) { "#{base_path}/ExampleTemplate.docx" }
42
+ let(:output_file) { "#{output_dir}/IntegrationTestOutput.docx" }
43
+ let(:document) { LMDocstache::Document.new(input_file) }
44
+
45
+ before do
46
+ FileUtils.rm_rf(output_dir) if File.exist?(output_dir)
47
+ Dir.mkdir(output_dir)
48
+ end
49
+
48
50
  it 'loads the input file' do
49
51
  expect(document).to_not be_nil
50
52
  end
@@ -84,4 +86,58 @@ describe 'integration test', integration: true do
84
86
  document.render_file(output_file, data)
85
87
  end
86
88
  end
89
+ context "testing hide custom tags" do
90
+ before do
91
+ FileUtils.rm_rf(output_dir) if File.exist?(output_dir)
92
+ Dir.mkdir(output_dir)
93
+ end
94
+
95
+ let(:render_options) {
96
+ {
97
+ hide_custom_tags: ['(?:sig|sigfirm|date|check|text|initial)\|(?:req|noreq)\|.+?']
98
+ }
99
+ }
100
+ let(:document) { LMDocstache::Document.new(input_file) }
101
+
102
+ context "witth document with blue background" do
103
+ let(:input_file) { "#{base_path}/sample-signature-blue.docx" }
104
+
105
+ it 'should have content replacement aligned with hide custom tags' do
106
+ doc = document
107
+ doc.fix_errors
108
+ noko = doc.render_xml({}, render_options)
109
+ output = noko['word/document.xml'].to_xml
110
+ expect(output).to include('<w:r>
111
+ <w:rPr>
112
+ <w:rFonts w:cstheme="minorHAnsi"/>
113
+ <w:lang w:val="en-US"/>
114
+ <w:color w:val="4472C4"/>
115
+ </w:rPr>
116
+ <w:t xml:space="preserve">{{sig|req|client}}</w:t>
117
+ </w:r>')
118
+ expect(output).to include('<w:t xml:space="preserve">Test Multiple text in the same line </w:t>')
119
+ end
120
+ end
121
+
122
+ context "with document without backgorund" do
123
+ let(:input_file) { "#{base_path}/sample-signature.docx" }
124
+ let(:document) { LMDocstache::Document.new(input_file) }
125
+
126
+ it 'should have content replacement aligned with hide custom tags' do
127
+ doc = document
128
+ doc.fix_errors
129
+ noko = doc.render_xml({}, render_options)
130
+ output = noko['word/document.xml'].to_xml
131
+ expect(output).to include('<w:r>
132
+ <w:rPr>
133
+ <w:rFonts w:cstheme="minorHAnsi"/>
134
+ <w:lang w:val="en-US"/>
135
+ <w:color w:val="FFFFFF"/>
136
+ </w:rPr>
137
+ <w:t xml:space="preserve">{{sig|req|client}}</w:t>
138
+ </w:r>')
139
+ expect(output).to include('<w:t xml:space="preserve">Test Multiple text in the same line </w:t>')
140
+ end
141
+ end
142
+ end
87
143
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lm_docstache
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roey Chasman
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2021-03-09 00:00:00.000000000 Z
15
+ date: 2021-03-26 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: nokogiri
@@ -113,6 +113,7 @@ files:
113
113
  - lib/lm_docstache/condition.rb
114
114
  - lib/lm_docstache/conditional_block.rb
115
115
  - lib/lm_docstache/document.rb
116
+ - lib/lm_docstache/hide_custom_tags.rb
116
117
  - lib/lm_docstache/parser.rb
117
118
  - lib/lm_docstache/renderer.rb
118
119
  - lib/lm_docstache/version.rb
@@ -120,6 +121,10 @@ files:
120
121
  - spec/conditional_block_spec.rb
121
122
  - spec/example_input/ExampleTemplate.docx
122
123
  - spec/example_input/blank.docx
124
+ - spec/example_input/docx-no-rpr.docx
125
+ - spec/example_input/sample-signature-blue.docx
126
+ - spec/example_input/sample-signature.docx
127
+ - spec/hide_custom_tags_spec.rb
123
128
  - spec/integration_spec.rb
124
129
  - spec/spec_helper.rb
125
130
  - spec/template_processor_spec.rb
@@ -150,6 +155,10 @@ test_files:
150
155
  - spec/conditional_block_spec.rb
151
156
  - spec/example_input/ExampleTemplate.docx
152
157
  - spec/example_input/blank.docx
158
+ - spec/example_input/docx-no-rpr.docx
159
+ - spec/example_input/sample-signature-blue.docx
160
+ - spec/example_input/sample-signature.docx
161
+ - spec/hide_custom_tags_spec.rb
153
162
  - spec/integration_spec.rb
154
163
  - spec/spec_helper.rb
155
164
  - spec/template_processor_spec.rb