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