lm_docstache 2.0.2 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
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