docx_manipulator 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/docx_manipulator.gemspec +1 -0
- data/lib/docx_manipulator/content.rb +27 -0
- data/lib/docx_manipulator/relationships.rb +62 -0
- data/lib/docx_manipulator/version.rb +1 -1
- data/lib/docx_manipulator.rb +15 -60
- data/spec/docx_manipulator_spec.rb +60 -55
- metadata +15 -2
data/docx_manipulator.gemspec
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
class DocxManipulator
|
4
|
+
class Content
|
5
|
+
def set(new_content, options = {})
|
6
|
+
@new_content = if new_content.kind_of?(File)
|
7
|
+
new_content.read
|
8
|
+
else
|
9
|
+
new_content
|
10
|
+
end
|
11
|
+
if options.include?(:xslt)
|
12
|
+
xslt = Nokogiri::XSLT.parse(options[:xslt])
|
13
|
+
data = Nokogiri::XML.parse(@new_content)
|
14
|
+
@new_content = xslt.transform(data).to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def writes_to_files
|
19
|
+
['word/document.xml']
|
20
|
+
end
|
21
|
+
|
22
|
+
def process(output)
|
23
|
+
output.put_next_entry 'word/document.xml'
|
24
|
+
output.write @new_content
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
class DocxManipulator
|
5
|
+
class Relationships
|
6
|
+
def initialize(source)
|
7
|
+
@relationships = read_relationships(source)
|
8
|
+
@images = []
|
9
|
+
@binary_images = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def read_relationships(path)
|
13
|
+
content = ''
|
14
|
+
Zip::ZipFile.open(path) do |file|
|
15
|
+
content = file.read('word/_rels/document.xml.rels')
|
16
|
+
end
|
17
|
+
Nokogiri::XML.parse(content)
|
18
|
+
end
|
19
|
+
private :read_relationships
|
20
|
+
|
21
|
+
def add_image(id, path)
|
22
|
+
@images << path
|
23
|
+
add_node(id, I18n.transliterate(File.basename(path)))
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_binary_image(id, name, data)
|
27
|
+
name = I18n.transliterate(name)
|
28
|
+
@binary_images[name] = data
|
29
|
+
add_node(id, name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_node(id, name)
|
33
|
+
image_node = Nokogiri::XML::Node.new('Relationship', @relationships)
|
34
|
+
image_node['Id'] = id
|
35
|
+
image_node['Type'] = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image'
|
36
|
+
image_node['Target'] = "media/#{name}"
|
37
|
+
@relationships.root << image_node
|
38
|
+
end
|
39
|
+
private :add_node
|
40
|
+
|
41
|
+
def writes_to_files
|
42
|
+
['word/_rels/document.xml.rels']
|
43
|
+
end
|
44
|
+
|
45
|
+
def process(output)
|
46
|
+
output.put_next_entry 'word/_rels/document.xml.rels'
|
47
|
+
output.write @relationships.to_s
|
48
|
+
|
49
|
+
@images.each do |path|
|
50
|
+
output.put_next_entry "word/media/#{I18n.transliterate(File.basename(path))}"
|
51
|
+
File.open(path) do |file|
|
52
|
+
IO.copy_stream file, output
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@binary_images.each do |name, data|
|
57
|
+
output.put_next_entry "word/media/#{I18n.transliterate(name)}"
|
58
|
+
output.write data
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/docx_manipulator.rb
CHANGED
@@ -1,91 +1,46 @@
|
|
1
1
|
require 'docx_manipulator/version'
|
2
|
-
require '
|
2
|
+
require 'docx_manipulator/content'
|
3
|
+
require 'docx_manipulator/relationships'
|
4
|
+
|
3
5
|
require 'zip/zip'
|
4
6
|
|
5
7
|
class DocxManipulator
|
6
8
|
|
7
|
-
attr_reader :source, :target
|
9
|
+
attr_reader :source, :target
|
8
10
|
|
9
11
|
def initialize(source, target)
|
10
12
|
@source = source
|
11
13
|
@target = target
|
12
|
-
@new_relationships = source_relationships
|
13
|
-
@images = {}
|
14
|
-
@binary_images = {}
|
15
|
-
end
|
16
|
-
|
17
|
-
def source_content
|
18
|
-
content = ''
|
19
|
-
Zip::ZipFile.open(source) do |file|
|
20
|
-
content = file.read('word/document.xml')
|
21
|
-
end
|
22
|
-
content
|
23
|
-
end
|
24
14
|
|
25
|
-
|
26
|
-
|
27
|
-
Zip::ZipFile.open(source) do |file|
|
28
|
-
content = file.read('word/_rels/document.xml.rels')
|
29
|
-
end
|
30
|
-
Nokogiri::XML.parse(content)
|
15
|
+
@content = Content.new
|
16
|
+
@relationships = Relationships.new(source)
|
31
17
|
end
|
32
18
|
|
33
19
|
def content(new_content, options = {})
|
34
|
-
|
35
|
-
when File then new_content.read
|
36
|
-
else new_content
|
37
|
-
end
|
38
|
-
if options.include?(:xslt)
|
39
|
-
xslt = Nokogiri::XSLT.parse(options[:xslt])
|
40
|
-
data = Nokogiri::XML.parse(new_content_string)
|
41
|
-
@new_content = xslt.transform(data).to_s
|
42
|
-
else
|
43
|
-
@new_content = new_content_string
|
44
|
-
end
|
20
|
+
@content.set(new_content, options)
|
45
21
|
end
|
46
22
|
|
47
23
|
def add_image(id, path)
|
48
|
-
@
|
49
|
-
image_node = Nokogiri::XML::Node.new('Relationship', new_relationships)
|
50
|
-
image_node['Id'] = id
|
51
|
-
image_node['Type'] = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image'
|
52
|
-
image_node['Target'] = "media/#{File.basename(path)}"
|
53
|
-
new_relationships.root << image_node
|
24
|
+
@relationships.add_image(id, path)
|
54
25
|
end
|
55
26
|
|
56
27
|
def add_binary_image(id, name, data)
|
57
|
-
@
|
58
|
-
image_node = Nokogiri::XML::Node.new('Relationship', new_relationships)
|
59
|
-
image_node['Id'] = id
|
60
|
-
image_node['Type'] = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image'
|
61
|
-
image_node['Target'] = "media/#{name}"
|
62
|
-
new_relationships.root << image_node
|
28
|
+
@relationships.add_binary_image(id, name, data)
|
63
29
|
end
|
64
30
|
|
65
31
|
def process
|
32
|
+
files_to_be_written = @content.writes_to_files + @relationships.writes_to_files
|
33
|
+
|
66
34
|
Zip::ZipOutputStream.open(target) do |os|
|
67
35
|
Zip::ZipFile.foreach(source) do |entry|
|
68
|
-
|
69
|
-
|
70
|
-
os.write @new_content
|
71
|
-
elsif entry.name == 'word/_rels/document.xml.rels'
|
72
|
-
os.write new_relationships.to_s
|
73
|
-
elsif entry.file?
|
36
|
+
unless files_to_be_written.include?(entry.name)
|
37
|
+
os.put_next_entry entry.name
|
74
38
|
os.write entry.get_input_stream.read
|
75
39
|
end
|
76
40
|
end
|
77
41
|
|
78
|
-
@
|
79
|
-
|
80
|
-
File.open(path) do |file|
|
81
|
-
IO.copy_stream file, os
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
@binary_images.each do |name, data|
|
86
|
-
os.put_next_entry "word/media/#{name}"
|
87
|
-
os.write data
|
88
|
-
end
|
42
|
+
@content.process(os)
|
43
|
+
@relationships.process(os)
|
89
44
|
end
|
90
45
|
end
|
91
46
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
require 'docx_manipulator'
|
2
3
|
require 'zip/zip'
|
3
4
|
|
@@ -5,13 +6,24 @@ describe DocxManipulator do
|
|
5
6
|
|
6
7
|
subject { DocxManipulator.new('spec/files/movies.docx', 'spec/files/result.docx') }
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
after :each do
|
10
|
+
File.delete('spec/files/result.docx') if File.exist?('spec/files/result.docx')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "generates the resulting document" do
|
14
|
+
subject.process
|
15
|
+
File.should exist('spec/files/result.docx')
|
12
16
|
end
|
13
17
|
|
14
|
-
|
18
|
+
context 'content' do
|
19
|
+
it "accepts a string" do
|
20
|
+
subject.content 'bla'
|
21
|
+
subject.process
|
22
|
+
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
23
|
+
file.get_input_stream('word/document.xml').read.should == 'bla'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
15
27
|
let(:xml_string) { <<-EOF
|
16
28
|
<?xml version="1.0" encoding="UTF-8"?>
|
17
29
|
<Movies>
|
@@ -31,82 +43,75 @@ EOF
|
|
31
43
|
|
32
44
|
it "transforms the data file with an xslt file" do
|
33
45
|
subject.content File.new('spec/files/data.xml'), :xslt => File.new('spec/files/document.xslt')
|
34
|
-
subject.
|
35
|
-
|
46
|
+
subject.process
|
47
|
+
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
48
|
+
data = file.get_input_stream('word/document.xml').read
|
49
|
+
data.should =~ /<w:t>The Departed<\/w:t>/
|
50
|
+
data.should =~ /<w:t>The Pursuit of Happyness<\/w:t>/
|
51
|
+
end
|
36
52
|
end
|
37
53
|
|
38
54
|
it "transforms a string with an xslt file" do
|
39
55
|
subject.content xml_string, :xslt => File.new('spec/files/document.xslt')
|
40
|
-
subject.
|
41
|
-
|
56
|
+
subject.process
|
57
|
+
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
58
|
+
data = file.get_input_stream('word/document.xml').read
|
59
|
+
data.should =~ /<w:t>The Departed<\/w:t>/
|
60
|
+
data.should =~ /<w:t>The Pursuit of Happyness<\/w:t>/
|
61
|
+
end
|
42
62
|
end
|
43
63
|
|
44
64
|
it "accepts a string" do
|
45
65
|
subject.content 'the new content'
|
46
|
-
subject.
|
66
|
+
subject.process
|
67
|
+
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
68
|
+
data = file.get_input_stream('word/document.xml').read
|
69
|
+
data.should == 'the new content'
|
70
|
+
end
|
47
71
|
end
|
48
72
|
|
49
73
|
it "accepts a file as input" do
|
50
74
|
subject.content File.new('spec/files/content.txt')
|
51
|
-
subject.new_content.should == 'this is the new content of the document'
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe '#add_image' do
|
56
|
-
it 'adds an image to the relations file' do
|
57
|
-
subject.add_image 'rId9', 'path/to/image.jpg'
|
58
|
-
subject.new_relationships.to_s.should =~ /<Relationship Id="rId9" Type="http:\/\/schemas.openxmlformats.org\/officeDocument\/2006\/relationships\/image" Target="media\/image.jpg"\/>/
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe '#add_binary_image' do
|
63
|
-
let(:image) { File.new(File.join(File.dirname(__FILE__), 'files', 'duck.jpeg'), 'rb') }
|
64
|
-
it 'adds an image in binary format to the docx' do
|
65
|
-
data = image.read
|
66
|
-
subject.add_binary_image 'rId18', 'duck.jpeg', data
|
67
|
-
subject.new_relationships.to_s.should =~ /<Relationship Id="rId18" Type="http:\/\/schemas.openxmlformats.org\/officeDocument\/2006\/relationships\/image" Target="media\/duck.jpeg"\/>/
|
68
75
|
subject.process
|
69
|
-
|
70
76
|
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
71
|
-
file.
|
77
|
+
data = file.get_input_stream('word/document.xml').read
|
78
|
+
data.should == 'this is the new content of the document'
|
72
79
|
end
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
it "generates the resulting document" do
|
83
|
+
context 'with an image' do
|
84
|
+
it 'adds an image' do
|
85
|
+
subject.add_image 'rId19', 'spec/files/duck.jpeg'
|
82
86
|
subject.process
|
83
|
-
|
87
|
+
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
88
|
+
content = Nokogiri::XML.parse(file.get_input_stream('word/_rels/document.xml.rels').read)
|
89
|
+
content.xpath('//r:Relationship[@Id="rId19"]', 'r' => 'http://schemas.openxmlformats.org/package/2006/relationships').first['Target'].should == 'media/duck.jpeg'
|
90
|
+
file.find_entry('word/media/duck.jpeg').should_not be_nil
|
91
|
+
end
|
84
92
|
end
|
85
93
|
|
86
|
-
it
|
87
|
-
|
94
|
+
it 'adds an image as binary data' do
|
95
|
+
file = File.new(File.join(File.dirname(__FILE__), 'files', 'duck.jpeg'), 'rb')
|
96
|
+
data = file.read
|
97
|
+
subject.add_binary_image 'rId19', 'duck.jpeg', data
|
88
98
|
subject.process
|
89
99
|
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
90
|
-
file.get_input_stream('word/document.xml').read
|
100
|
+
content = Nokogiri::XML.parse(file.get_input_stream('word/_rels/document.xml.rels').read)
|
101
|
+
content.xpath('//r:Relationship[@Id="rId19"]', 'r' => 'http://schemas.openxmlformats.org/package/2006/relationships').first['Target'].should == 'media/duck.jpeg'
|
102
|
+
file.read('word/media/duck.jpeg').should == data
|
91
103
|
end
|
92
104
|
end
|
93
105
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
it 'adds the image to the resulting file' do
|
105
|
-
subject.add_image 'rId19', 'spec/files/duck.jpeg'
|
106
|
-
subject.process
|
107
|
-
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
108
|
-
file.find_entry('word/media/duck.jpeg').should_not be_nil
|
109
|
-
end
|
106
|
+
it 'transliterates the image name' do
|
107
|
+
file = File.new(File.join(File.dirname(__FILE__), 'files', 'duck.jpeg'), 'rb')
|
108
|
+
data = file.read
|
109
|
+
subject.add_binary_image 'rId19', 'dück.jpeg', data
|
110
|
+
subject.process
|
111
|
+
Zip::ZipFile.open('spec/files/result.docx') do |file|
|
112
|
+
content = Nokogiri::XML.parse(file.get_input_stream('word/_rels/document.xml.rels').read)
|
113
|
+
content.xpath('//r:Relationship[@Id="rId19"]', 'r' => 'http://schemas.openxmlformats.org/package/2006/relationships').first['Target'].should == 'media/duck.jpeg'
|
114
|
+
file.find_entry('word/media/duck.jpeg').should_not be_nil
|
110
115
|
end
|
111
116
|
end
|
112
117
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: docx_manipulator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- "Michael St\xC3\xA4mpfli"
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-04-
|
13
|
+
date: 2012-04-19 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -45,6 +45,17 @@ dependencies:
|
|
45
45
|
version: "0"
|
46
46
|
type: :runtime
|
47
47
|
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: i18n
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :runtime
|
58
|
+
version_requirements: *id004
|
48
59
|
description: This Gem enables you to modify the contents of docx files.
|
49
60
|
email:
|
50
61
|
- michael.staempfli@gmail.com
|
@@ -62,6 +73,8 @@ files:
|
|
62
73
|
- Rakefile
|
63
74
|
- docx_manipulator.gemspec
|
64
75
|
- lib/docx_manipulator.rb
|
76
|
+
- lib/docx_manipulator/content.rb
|
77
|
+
- lib/docx_manipulator/relationships.rb
|
65
78
|
- lib/docx_manipulator/version.rb
|
66
79
|
- spec/docx_manipulator_spec.rb
|
67
80
|
- spec/files/content.txt
|