docx-cloner 0.0.1 → 0.1.0
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 +8 -8
- data/.gitignore +68 -0
- data/.rspec +2 -0
- data/Gemfile +4 -1
- data/docx-examples/read-single-tags-body.xml +1383 -0
- data/docx-examples/read-single-tags.docx +0 -0
- data/docx-examples/source.docx +0 -0
- data/docx-examples/wp.xml +52 -0
- data/features/read.feature +46 -0
- data/features/replace.feature +64 -0
- data/features/steps_define/steps.rb +92 -0
- data/lib/docx/cloner.rb +251 -1
- data/lib/docx/cloner/version.rb +1 -1
- data/spec/cloner_spec.rb +101 -0
- data/spec/spec_helper.rb +3 -0
- metadata +18 -3
Binary file
|
Binary file
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<w:p w14:paraId="4E9418BB" w14:textId="23E8C963" w:rsidR="00342A6D" w:rsidRDefault="00342A6D" w:rsidP="006A0A53">
|
2
|
+
<w:pPr>
|
3
|
+
<w:pStyle w:val="ab"/>
|
4
|
+
<w:spacing w:after="120"/>
|
5
|
+
<w:rPr>
|
6
|
+
<w:rFonts w:hint="eastAsia"/>
|
7
|
+
<w:lang w:eastAsia="zh-CN"/>
|
8
|
+
</w:rPr>
|
9
|
+
</w:pPr>
|
10
|
+
<w:r>
|
11
|
+
<w:rPr>
|
12
|
+
<w:rFonts w:hint="eastAsia"/>
|
13
|
+
<w:lang w:eastAsia="zh-CN"/>
|
14
|
+
</w:rPr>
|
15
|
+
<w:t>这是一个单词</w:t>
|
16
|
+
</w:r>
|
17
|
+
<w:r>
|
18
|
+
<w:rPr>
|
19
|
+
<w:rFonts w:hint="eastAsia"/>
|
20
|
+
<w:lang w:eastAsia="zh-CN"/>
|
21
|
+
</w:rPr>
|
22
|
+
<w:t xml:space="preserve"> </w:t>
|
23
|
+
</w:r>
|
24
|
+
<w:r w:rsidR="000F595B">
|
25
|
+
<w:rPr>
|
26
|
+
<w:rFonts w:hint="eastAsia"/>
|
27
|
+
<w:lang w:eastAsia="zh-CN"/>
|
28
|
+
</w:rPr>
|
29
|
+
<w:t>{</w:t>
|
30
|
+
</w:r>
|
31
|
+
<w:r>
|
32
|
+
<w:rPr>
|
33
|
+
<w:rFonts w:hint="eastAsia"/>
|
34
|
+
<w:lang w:eastAsia="zh-CN"/>
|
35
|
+
</w:rPr>
|
36
|
+
<w:t>n</w:t>
|
37
|
+
</w:r>
|
38
|
+
<w:r w:rsidR="000F595B">
|
39
|
+
<w:rPr>
|
40
|
+
<w:rFonts w:hint="eastAsia"/>
|
41
|
+
<w:lang w:eastAsia="zh-CN"/>
|
42
|
+
</w:rPr>
|
43
|
+
<w:t>ame}</w:t>
|
44
|
+
</w:r>
|
45
|
+
<w:r>
|
46
|
+
<w:rPr>
|
47
|
+
<w:rFonts w:hint="eastAsia"/>
|
48
|
+
<w:lang w:eastAsia="zh-CN"/>
|
49
|
+
</w:rPr>
|
50
|
+
<w:t>测试</w:t>
|
51
|
+
</w:r>
|
52
|
+
</w:p>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#language: zh-CN
|
2
|
+
|
3
|
+
功能: 读Docx内标签定义
|
4
|
+
这里要确认标签读取的正确性,然后再进入替换阶段
|
5
|
+
1、主要解决的问题包括:将docx文件拆包、找到对应的文件位置
|
6
|
+
2、xml标记可能是散开的,例如"{name}"在docx文件内部表示中,"{"、"name"、"}"是各自独立的xml标记
|
7
|
+
3、替换逻辑,希望使用DSL在程序中指定,因此不应该限定到底使用"{name}"还是"$name$"做标签标识
|
8
|
+
|
9
|
+
背景: 可读的示例文件列举
|
10
|
+
假如"docx-examples"示例文件夹中存在一个"read-single-tags.docx"的文件
|
11
|
+
|
12
|
+
场景大纲: 简单地读取词语替换标签
|
13
|
+
这是最简单的情形,例如将标签{name},替换为真正的姓名。
|
14
|
+
|
15
|
+
那么程序应该能读到"<tagname>"这个标签词
|
16
|
+
|
17
|
+
例子: 读取标签的例子
|
18
|
+
"{}"可作为默认的正则表达式设计,在DSL中无需指定
|
19
|
+
程序应该支持中文(以及其它UTF8字符)
|
20
|
+
|
21
|
+
| tagname |
|
22
|
+
| name |
|
23
|
+
| {name} |
|
24
|
+
| {Name} |
|
25
|
+
| {NAME} |
|
26
|
+
| {{名字}} |
|
27
|
+
| $名字$ |
|
28
|
+
|
29
|
+
@wip
|
30
|
+
场景大纲: 读取表格行替换标签
|
31
|
+
这通常是在表格上追加行所使用的
|
32
|
+
|
33
|
+
那么程序应该能读到"<tagname>"这个标签词
|
34
|
+
|
35
|
+
例子:
|
36
|
+
| tagname |
|
37
|
+
| {名称1} |
|
38
|
+
| {名称2} |
|
39
|
+
| {00.01} |
|
40
|
+
| {00.02} |
|
41
|
+
|
42
|
+
场景: 读取文档信息标签
|
43
|
+
包括标题、摘要、作者、邮件等设置信息
|
44
|
+
|
45
|
+
场景: 读取图像标签
|
46
|
+
这是做图像替换时使用的
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#language: zh-CN
|
2
|
+
|
3
|
+
功能: 替换Docx内标签
|
4
|
+
将docx文档中的标签替换为指定的内容。
|
5
|
+
替换的情形有很多,大致包括:
|
6
|
+
1、单个标签替换,如"{name}"替换为"周大福"
|
7
|
+
2、多个标签同时替换
|
8
|
+
3、列表标签替换,如表格中包含一行定义,每行包括"{价格}"和"{数量}",而要替换的数据是不确定的,如有5行,也可能是50行
|
9
|
+
但所替换的数据都使用标签所在的行样式
|
10
|
+
4、表格中可能包含一些复杂的情况,例如行样式包括按奇数行、偶数行的不同样式
|
11
|
+
5、docx文件也可能对List列表作为整行的样式复制
|
12
|
+
6、更复杂的情况是图表、图片等情况
|
13
|
+
7、还有页眉、页脚中的内容替换
|
14
|
+
|
15
|
+
背景: 被替换的源文件
|
16
|
+
假如"docx-examples"示例文件夹中存在一个"source.docx"的文件
|
17
|
+
而且"docx-examples/dest.docx"这个目标文件已经被清除
|
18
|
+
|
19
|
+
场景大纲: 1、简单地读取词语替换标签
|
20
|
+
这是最简单的情形,例如将标签{name},替换为真正的姓名。
|
21
|
+
|
22
|
+
假如程序将目标文件中的"<tagname>"替换为"<value>"
|
23
|
+
那么应该生成目标文件
|
24
|
+
而且被目标文件中应该包含"<value>"这个标签词
|
25
|
+
|
26
|
+
例子: 替换单个标签的几种情况
|
27
|
+
|
28
|
+
| tagname | value |
|
29
|
+
| {name} | 周大福 |
|
30
|
+
| {Name} | 周大福 |
|
31
|
+
| {NAME} | 周大福 |
|
32
|
+
| {{名字}} | 周大福 |
|
33
|
+
| $名字$ | 周大福 |
|
34
|
+
|
35
|
+
场景: 2、设置多个标签的情形
|
36
|
+
如果同时替换5个标签的,也要能正确运行
|
37
|
+
|
38
|
+
假如有这样一组数据:
|
39
|
+
| {name} | 周大福 |
|
40
|
+
| {Name} | 周二福 |
|
41
|
+
| {NAME} | 周三福 |
|
42
|
+
| {{名字}} | 周四福 |
|
43
|
+
| $名字$ | 周五福 |
|
44
|
+
|
45
|
+
当程序将源文件的第1列中标签替换为第2列数据
|
46
|
+
那么应该生成目标文件
|
47
|
+
而且被目标文件中应该包含被替换的第2列数据
|
48
|
+
|
49
|
+
@wip
|
50
|
+
场景: 3、替换表格行数据
|
51
|
+
按行数据替换表格内容是常见的应用
|
52
|
+
|
53
|
+
假如有这样一组数据:
|
54
|
+
| {名称1} | {00.01} |
|
55
|
+
| 自行车 | 256.00 |
|
56
|
+
| 小汽车 | 125600 |
|
57
|
+
| 大卡车 | 256000.00 |
|
58
|
+
| 电视机 | 6999.00 |
|
59
|
+
| 洗衣机 | 3488.00 |
|
60
|
+
|
61
|
+
当程序将表中第1行作为标签名,第2行以后作为行数据替换
|
62
|
+
那么应该生成目标文件
|
63
|
+
而且被目标文件中应该包含被替换的第2行以后的数据
|
64
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
lib = File.expand_path('../../../lib', __FILE__)
|
3
|
+
require "#{lib}/docx/cloner"
|
4
|
+
#require 'fileutils'
|
5
|
+
|
6
|
+
假如(/^"(.*?)"示例文件夹中存在一个"(.*?)"的文件$/) do |folder, file|
|
7
|
+
@source_filename = File.expand_path "#{folder}/#{file}"
|
8
|
+
File.exists?(@source_filename).should be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
那么(/^程序应该能读到"(.*?)"这个标签词$/) do |tag_name|
|
13
|
+
docx = Docx::Cloner::DocxTool.new @source_filename
|
14
|
+
result = docx.include_single_tag? tag_name
|
15
|
+
docx.release
|
16
|
+
result.should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
假如(/^"(.*?)"这个目标文件已经被清除$/) do |dest|
|
21
|
+
@dest_filename = dest
|
22
|
+
File.delete @dest_filename if File.exist?(dest)
|
23
|
+
File.exist?(dest).should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
假如(/^程序将目标文件中的"(.*?)"替换为"(.*?)"$/) do |tag, value|
|
27
|
+
docx = Docx::Cloner::DocxTool.new @source_filename
|
28
|
+
result = docx.set_single_tag tag, value
|
29
|
+
docx.save @dest_filename
|
30
|
+
docx.release
|
31
|
+
result.should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
那么(/^应该生成目标文件$/) do
|
35
|
+
File.exist?(@dest_filename).should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
而且(/^被目标文件中应该包含"(.*?)"这个标签词$/) do |value|
|
39
|
+
docx = Docx::Cloner::DocxTool.new @dest_filename
|
40
|
+
result = docx.include_single_tag? value
|
41
|
+
docx.release
|
42
|
+
result.should be_true
|
43
|
+
end
|
44
|
+
|
45
|
+
假如(/^有这样一组数据:$/) do |table|
|
46
|
+
@data = table.raw
|
47
|
+
end
|
48
|
+
|
49
|
+
当(/^程序将源文件的第1列中标签替换为第2列数据$/) do
|
50
|
+
result = true
|
51
|
+
docx = Docx::Cloner::DocxTool.new @source_filename
|
52
|
+
@data.each do |row|
|
53
|
+
result &= docx.set_single_tag row[0], row[1]
|
54
|
+
end
|
55
|
+
docx.save @dest_filename
|
56
|
+
docx.release
|
57
|
+
result.should be_true
|
58
|
+
end
|
59
|
+
|
60
|
+
那么(/^被目标文件中应该包含被替换的第2列数据$/) do
|
61
|
+
result = true
|
62
|
+
docx = Docx::Cloner::DocxTool.new @dest_filename
|
63
|
+
@data.each do |row|
|
64
|
+
result &= docx.include_single_tag? row[1]
|
65
|
+
end
|
66
|
+
docx.release
|
67
|
+
result.should be_true
|
68
|
+
end
|
69
|
+
|
70
|
+
当(/^程序将表中第1行作为标签名,第2行以后作为行数据替换$/) do
|
71
|
+
docx = Docx::Cloner::DocxTool.new @source_filename
|
72
|
+
|
73
|
+
#先设置行标签的复制范围和类型
|
74
|
+
#再逐行克隆表数据
|
75
|
+
#yield块结束后清除标签
|
76
|
+
result = docx.set_row_tags @data.first, @data[1..-1], 'tr'
|
77
|
+
docx.save @dest_filename
|
78
|
+
docx.release
|
79
|
+
result.should be_true
|
80
|
+
end
|
81
|
+
|
82
|
+
那么(/^被目标文件中应该包含被替换的第2行以后的数据$/) do
|
83
|
+
result = true
|
84
|
+
docx = Docx::Cloner::DocxTool.new @dest_filename
|
85
|
+
@data[1..-1].each do |row|
|
86
|
+
row.each do |value|
|
87
|
+
result &= docx.include_single_tag? value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
docx.release
|
91
|
+
result.should be_true
|
92
|
+
end
|
data/lib/docx/cloner.rb
CHANGED
@@ -1,7 +1,257 @@
|
|
1
|
+
#encoding: utf-8
|
1
2
|
require "docx/cloner/version"
|
3
|
+
require 'zip/zip' #rubyzip gem
|
4
|
+
require 'nokogiri'
|
2
5
|
|
3
6
|
module Docx
|
4
7
|
module Cloner
|
5
|
-
|
8
|
+
class WordXmlFile
|
9
|
+
def self.open(path, &block)
|
10
|
+
self.new(path, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(path, &block)
|
14
|
+
@replace = {}
|
15
|
+
if block_given?
|
16
|
+
@zip = Zip::ZipFile.open(path)
|
17
|
+
yield self
|
18
|
+
@zip.close
|
19
|
+
else
|
20
|
+
@zip = Zip::ZipFile.open(path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge(rec)
|
25
|
+
_xml = @zip.read("word/document.xml")
|
26
|
+
doc = Nokogiri::XML(_xml)
|
27
|
+
tags = doc.root.xpath("//w:t[contains(., '_Name')]")
|
28
|
+
tags.each do |field|
|
29
|
+
new_field = field
|
30
|
+
if field.content == 'First_Name'
|
31
|
+
field.inner_html = 'Adi'
|
32
|
+
new_field.inner_html = 'My Adi'
|
33
|
+
field.add_next_sibling(new_field.to_html)
|
34
|
+
elsif field.content == 'Last_Name'
|
35
|
+
field.inner_html = 'Zhou'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@replace["word/document.xml"] = doc.serialize :save_with => 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def save(path)
|
42
|
+
Zip::ZipFile.open(path, Zip::ZipFile::CREATE) do |out|
|
43
|
+
@zip.each do |entry|
|
44
|
+
out.get_output_stream(entry.name) do |o|
|
45
|
+
if @replace[entry.name]
|
46
|
+
o.write(@replace[entry.name])
|
47
|
+
else
|
48
|
+
o.write(@zip.read(entry.name))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@zip.close
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class DocxTool
|
58
|
+
|
59
|
+
'加载docx文件,将段落存储到@paragraph,用@paragraph[:text_content]检索,再从段落内检索xml标签位置'
|
60
|
+
def initialize(file)
|
61
|
+
@zip = Zip::ZipFile.open(file)
|
62
|
+
_xml = @zip.read("word/document.xml")
|
63
|
+
@doc = Nokogiri::XML(_xml)
|
64
|
+
@global_paragraph = generate_paragraph @doc
|
65
|
+
|
66
|
+
@replace = {}
|
67
|
+
|
68
|
+
#puts @paragraph
|
69
|
+
end
|
70
|
+
|
71
|
+
def release
|
72
|
+
@zip.close
|
73
|
+
end
|
74
|
+
|
75
|
+
def save(path)
|
76
|
+
@replace["word/document.xml"] = @doc.serialize :save_with => 0
|
77
|
+
|
78
|
+
Zip::ZipFile.open(path, Zip::ZipFile::CREATE) do |out|
|
79
|
+
@zip.each do |entry|
|
80
|
+
out.get_output_stream(entry.name) do |o|
|
81
|
+
if @replace[entry.name]
|
82
|
+
o.write(@replace[entry.name])
|
83
|
+
else
|
84
|
+
o.write(@zip.read(entry.name))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def include_single_tag?(tag)
|
93
|
+
@global_paragraph.each do |p|
|
94
|
+
if p[:text_content].include? tag
|
95
|
+
return true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
|
101
|
+
def read_single_tag_xml(tag)
|
102
|
+
@global_paragraph.each do |p|
|
103
|
+
if p[:text_content].include? tag
|
104
|
+
from = p[:text_content].index tag
|
105
|
+
to = from + tag.size - 1
|
106
|
+
#puts "from:#{from}, to:#{to}"
|
107
|
+
pos = 0
|
108
|
+
dest = ""
|
109
|
+
p[:text_run].each do |wt|
|
110
|
+
#puts "pos:#{pos}"
|
111
|
+
if pos >= from && pos < to
|
112
|
+
dest << wt.parent.to_xml << "\n"
|
113
|
+
end
|
114
|
+
if pos >= to
|
115
|
+
return dest
|
116
|
+
end
|
117
|
+
pos += wt.content.size
|
118
|
+
end
|
119
|
+
return dest
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
return ''
|
124
|
+
end
|
125
|
+
|
126
|
+
#替换单个标签为指定值
|
127
|
+
def set_single_tag tag, value
|
128
|
+
replace_tag tag, value
|
129
|
+
end
|
130
|
+
|
131
|
+
#获取标签所在的范围,例如表格的行
|
132
|
+
#简单的考虑,则tags中第一个标签位置即可确定为scope位置
|
133
|
+
#复杂的考虑,则可根据tags中所有标签的共同根(如<w:tr>)确定scope位置,这种情况将允许标签名拥有自己的作用域
|
134
|
+
#这里仅做简单的考虑
|
135
|
+
def get_tag_scope tag, type
|
136
|
+
@global_paragraph.each do |p|
|
137
|
+
if p[:text_content].include? tag #这里是简单的考虑,即使行内标签也必须全局唯一
|
138
|
+
node = p[:text_run].first
|
139
|
+
while true
|
140
|
+
return unless node #查找父节点失败
|
141
|
+
return node if node.node_name == type #查找到匹配的父节点
|
142
|
+
node = node.parent
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
|
149
|
+
def generate_paragraph node
|
150
|
+
paragraphs = []
|
151
|
+
puts "查找范围:#{node.path}"
|
152
|
+
wp_set = node.xpath(".//w:p")
|
153
|
+
#puts "#{wp_set.size}'s wp"
|
154
|
+
wp_set.each do |wp|
|
155
|
+
p = {text_content: '', text_run: []}
|
156
|
+
wp.xpath(".//w:t").each do |t|
|
157
|
+
p[:text_content] << t.content
|
158
|
+
p[:text_run] << t
|
159
|
+
#puts "node name: #{t.node_name}" if t.content.size > 0
|
160
|
+
#puts t.path
|
161
|
+
end
|
162
|
+
paragraphs << p
|
163
|
+
#puts p[:text_content].include? '$名字$'
|
164
|
+
end
|
165
|
+
return paragraphs
|
166
|
+
end
|
167
|
+
|
168
|
+
#在指定的范围内替换标签
|
169
|
+
def replace_tag tag, value, node=nil
|
170
|
+
paragraphs = node ? generate_paragraph(node) : @global_paragraph
|
171
|
+
#puts paragraphs
|
172
|
+
paragraphs.each do |p|
|
173
|
+
#puts p[:text_content]
|
174
|
+
if p[:text_content].include? tag
|
175
|
+
from = p[:text_content].index tag
|
176
|
+
to = from + tag.size - 1
|
177
|
+
#puts "tag:#{tag} | from:#{from}, to:#{to} >> #{p[:text_content]}"
|
178
|
+
pos = 0
|
179
|
+
dest = []
|
180
|
+
#puts p[:text_run]
|
181
|
+
p[:text_run].each do |wt|
|
182
|
+
#puts "pos:#{pos}"
|
183
|
+
#通常情况下,msword会把标签拆分成多个xml标签,如'{name}'被拆分成'<wt>{</wt>'和'<wt>name}</wt>'
|
184
|
+
#这可能跟编辑器有关,在处理中文时,这是一种常见的情形
|
185
|
+
if pos+1 >= from && pos <= to #通过pos+1修正临界点问题
|
186
|
+
dest << wt
|
187
|
+
end
|
188
|
+
if pos > to
|
189
|
+
break
|
190
|
+
end
|
191
|
+
pos += wt.content.size
|
192
|
+
|
193
|
+
#这里要处理一下标签没有被拆分的情形,而是作为纯文本被包含在某个标签中
|
194
|
+
#例如'{name}'包含在'<wt>my {name}</wt>'中
|
195
|
+
#puts "pos:#{pos}, to:#{to}, dest.size:#{dest.size}"
|
196
|
+
#puts wt
|
197
|
+
if pos >= to && dest.size == 0
|
198
|
+
#puts "simple_type | pos:#{pos}, to:#{to} >> #{wt.content}"
|
199
|
+
wt.inner_html = wt.content.sub(tag, value)
|
200
|
+
return true #如果是这种简单情形,就不再需要后续处理了
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
if dest.size > 0
|
205
|
+
puts "被替换节点:#{dest.first.path}"
|
206
|
+
dest.first.content = value
|
207
|
+
dest[1..-1].each do |node|
|
208
|
+
#puts node
|
209
|
+
node.remove
|
210
|
+
end
|
211
|
+
#puts "\n"
|
212
|
+
return true
|
213
|
+
else
|
214
|
+
return false
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
return false
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
#clone标签所在的范围,例如表格的行
|
224
|
+
#返回一组新的行对象集合
|
225
|
+
def clone_tag_scope node, times
|
226
|
+
#puts "clone #{node.node_name} #{times} times"
|
227
|
+
nodes = Array.new times
|
228
|
+
puts "被克隆节点:#{node.path}"
|
229
|
+
times.downto(1).each do |_i|
|
230
|
+
i = _i.to_i - 1
|
231
|
+
nodes[i] = node.dup
|
232
|
+
node.add_next_sibling nodes[i]
|
233
|
+
puts "第#{i+1}个节点克隆:#{nodes[i].path}"
|
234
|
+
end
|
235
|
+
return nodes
|
236
|
+
end
|
237
|
+
|
238
|
+
#根据行标签设置,替换成多行数据,这里考虑表格的一般情况
|
239
|
+
def set_row_tags tags, values, type
|
240
|
+
puts "tags:#{tags}, values:#{values}, type:#{type}"
|
241
|
+
#找到标签所在行的父节点
|
242
|
+
tag_scope_node = get_tag_scope tags.first, type
|
243
|
+
value_scope_nodes = clone_tag_scope tag_scope_node, values.size
|
244
|
+
value_scope_nodes.each_with_index do |node, r|
|
245
|
+
puts "查找范围:#{node.path}"
|
246
|
+
tags.each_with_index do |tag, c|
|
247
|
+
replace_tag tag, values[r][c], node
|
248
|
+
end
|
249
|
+
end
|
250
|
+
#清除标签
|
251
|
+
tag_scope_node.remove
|
252
|
+
return true
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
6
256
|
end
|
7
257
|
end
|