docx_report 0.1.0 → 0.2.3
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 +4 -4
- data/lib/docx_report.rb +2 -0
- data/lib/docx_report/block_value.rb +11 -0
- data/lib/docx_report/content_file.rb +0 -0
- data/lib/docx_report/data_item.rb +1 -1
- data/lib/docx_report/document.rb +45 -13
- data/lib/docx_report/field.rb +6 -6
- data/lib/docx_report/hyperlink.rb +0 -0
- data/lib/docx_report/image.rb +88 -0
- data/lib/docx_report/parser.rb +27 -4
- data/lib/docx_report/record.rb +1 -0
- data/lib/docx_report/report.rb +2 -2
- data/lib/docx_report/table.rb +24 -6
- metadata +21 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fba6128858dc1881724b7334e94d2fa527979e2e
|
4
|
+
data.tar.gz: 2191d4a90ec26b96769b8d3b7870024d6200c81b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e4be5bff6c1c47b28913bd71028e25fd938eb82dbcb1d59daa6d3f53aaf09179d3b59c9583243930befad0851a6187cc1fcf645eadb0ea09b95f01067eff911
|
7
|
+
data.tar.gz: 84c3319c0f168a716213bdd11d88adef5f6654e926f85b87301babdc306e2d376cacafa9df91a4c626659a4a724ecb79f64267b07f92e7f750fbcb7d79f74321
|
data/lib/docx_report.rb
CHANGED
@@ -6,6 +6,8 @@ require 'docx_report/record'
|
|
6
6
|
require 'docx_report/field'
|
7
7
|
require 'docx_report/content_file'
|
8
8
|
require 'docx_report/hyperlink'
|
9
|
+
require 'docx_report/image'
|
10
|
+
require 'docx_report/block_value'
|
9
11
|
|
10
12
|
module DocxReport
|
11
13
|
def self.create_docx_report(template_path)
|
File without changes
|
data/lib/docx_report/document.rb
CHANGED
@@ -3,33 +3,32 @@ require 'nokogiri'
|
|
3
3
|
|
4
4
|
module DocxReport
|
5
5
|
class Document
|
6
|
-
attr_accessor :template_path, :files
|
6
|
+
attr_accessor :template_path, :files, :image_ids, :images, :image_types
|
7
7
|
|
8
8
|
def initialize(template_path)
|
9
|
+
@images = []
|
10
|
+
@image_types = []
|
9
11
|
@template_path = template_path
|
10
12
|
zip = Zip::File.open(template_path)
|
11
13
|
load_files zip
|
14
|
+
@image_ids = images_ids zip
|
12
15
|
zip.close
|
13
16
|
end
|
14
17
|
|
15
18
|
def save_to_memory
|
16
19
|
Zip::OutputStream.write_buffer do |output|
|
20
|
+
add_images output
|
17
21
|
add_files output
|
18
22
|
end.string
|
19
23
|
end
|
20
24
|
|
21
25
|
def save_to_file(output_path)
|
22
26
|
Zip::OutputStream.open(output_path) do |output|
|
27
|
+
add_images output
|
23
28
|
add_files output
|
24
29
|
end
|
25
30
|
end
|
26
31
|
|
27
|
-
def new_uniqe_id(type)
|
28
|
-
(@files.map do |file|
|
29
|
-
file.xml.xpath('//*[@id]').map { |e| e[:id].to_i if e.name == type }
|
30
|
-
end.flatten.compact.max || 0) + 1
|
31
|
-
end
|
32
|
-
|
33
32
|
private
|
34
33
|
|
35
34
|
def add_files(output)
|
@@ -47,11 +46,12 @@ module DocxReport
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def write_files(name, template, output)
|
50
|
-
if @files.
|
51
|
-
add_data name,
|
52
|
-
elsif @files.
|
53
|
-
file = @files.detect { |f| f.rels_name == name }
|
49
|
+
if file = @files.detect { |f| f.name == name }
|
50
|
+
add_data name, file.xml.to_xml, output
|
51
|
+
elsif file = @files.detect { |f| f.rels_name == name }
|
54
52
|
add_data name, file.rels_xml.to_xml, output if file.rels_has_items?
|
53
|
+
elsif name == CONTENT_TYPE_NAME
|
54
|
+
add_data name, @content_types.to_xml, output
|
55
55
|
else
|
56
56
|
add_data name, template.read(name), output
|
57
57
|
end
|
@@ -62,17 +62,49 @@ module DocxReport
|
|
62
62
|
output.write data
|
63
63
|
end
|
64
64
|
|
65
|
+
def add_images(output)
|
66
|
+
images.each do |image|
|
67
|
+
image.save(output)
|
68
|
+
image.new_rels.each do |rels|
|
69
|
+
rels[:Target] = format(rels[:Target], image.type)
|
70
|
+
end
|
71
|
+
add_content_type image.type
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
65
75
|
def content_types_xpath
|
66
76
|
"//*[@ContentType = '#{CONTENT_TYPES.join("' or @ContentType='")}']"
|
67
77
|
end
|
68
78
|
|
69
79
|
def load_files(zip)
|
70
80
|
@files = []
|
71
|
-
|
72
|
-
|
81
|
+
@content_types = Nokogiri::XML zip.read(CONTENT_TYPE_NAME)
|
82
|
+
@content_types.xpath(content_types_xpath).each do |e|
|
73
83
|
filename = e['PartName'][1..-1]
|
74
84
|
@files << ContentFile.new(filename, zip)
|
75
85
|
end
|
86
|
+
find_image_types.each { |type| @image_types << type[:Extension] }
|
87
|
+
end
|
88
|
+
|
89
|
+
def images_ids(zip)
|
90
|
+
zip.entries.map do |e|
|
91
|
+
if e.name.start_with?('word/media/image')
|
92
|
+
(File.basename(e.name, '.*')[5..-1]).to_i
|
93
|
+
end
|
94
|
+
end.compact
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_image_types
|
98
|
+
@content_types.xpath('//*[starts-with(@ContentType, "image")]')
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_content_type(type)
|
102
|
+
unless @image_types.include? type
|
103
|
+
@content_types.children.first << Nokogiri::XML(
|
104
|
+
format('<Default Extension="%s" ContentType="image/%s"/>',
|
105
|
+
type, type)).children.first
|
106
|
+
@image_types << type
|
107
|
+
end
|
76
108
|
end
|
77
109
|
|
78
110
|
CONTENT_TYPE_NAME = '[Content_Types].xml'.freeze
|
data/lib/docx_report/field.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
|
2
|
+
require 'docx_report/block_value'
|
3
|
+
|
1
4
|
module DocxReport
|
2
5
|
class Field
|
6
|
+
include BlockValue
|
3
7
|
attr_reader :name, :value, :type
|
4
8
|
|
5
9
|
def initialize(name, value = nil, type = :text, &block)
|
@@ -12,12 +16,8 @@ module DocxReport
|
|
12
16
|
@value = value || block
|
13
17
|
end
|
14
18
|
|
15
|
-
def
|
16
|
-
Field.new(name[1..-2],
|
17
|
-
@value.call(item)
|
18
|
-
else
|
19
|
-
item.is_a?(Hash) ? item[@value] : item.send(@value)
|
20
|
-
end, type)
|
19
|
+
def load_field(item)
|
20
|
+
Field.new(name[1..-2], load_value(item), type)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
File without changes
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
|
3
|
+
module DocxReport
|
4
|
+
class Image
|
5
|
+
attr_accessor :path, :id, :nodes, :files, :new_rels, :type
|
6
|
+
|
7
|
+
def initialize(path, document)
|
8
|
+
@path = path
|
9
|
+
@files = {}
|
10
|
+
@nodes = []
|
11
|
+
@new_rels = []
|
12
|
+
global_id(document)
|
13
|
+
end
|
14
|
+
|
15
|
+
def global_id(document)
|
16
|
+
@id = document.image_ids.max + 1
|
17
|
+
document.image_ids << @id
|
18
|
+
document.images << self
|
19
|
+
end
|
20
|
+
|
21
|
+
def file_image_id(file)
|
22
|
+
new_image_id(file) if files[file.name].nil?
|
23
|
+
files[file.name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def image_ref(id)
|
27
|
+
rels = Nokogiri::XML(
|
28
|
+
format('<Relationship Id="%s" Type="http://schemas.openxmlformats.org/'\
|
29
|
+
'officeDocument/2006/relationships/image" Target="media/image%s.%s"/>',
|
30
|
+
id, @id, '%s')).children.first
|
31
|
+
@new_rels << rels
|
32
|
+
rels
|
33
|
+
end
|
34
|
+
|
35
|
+
def save(output)
|
36
|
+
img = MiniMagick::Image.open(@path)
|
37
|
+
@type = img.type.downcase
|
38
|
+
fix_rels
|
39
|
+
output.put_next_entry "word/media/image#{@id}.#{@type}"
|
40
|
+
img.write output
|
41
|
+
set_dimentions img.width, img.height, img.resolution
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def fix_rels
|
47
|
+
new_rels.each { |rels| format(rels, @id, @type) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def new_image_id(file)
|
51
|
+
files[file.name] = "rId#{file.new_uniqe_id}"
|
52
|
+
file.rels_xml.children.first << image_ref(files[file.name])
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_dimentions(width, height, resolution)
|
56
|
+
@width = width.to_f / resolution[0] * EMUS_PER_INCH
|
57
|
+
@height = height.to_f / vert_dpi(resolution) * EMUS_PER_INCH
|
58
|
+
fit_in_page
|
59
|
+
@nodes.each do |node|
|
60
|
+
node.xpath('.//*[@cx and @cy]').each do |ext|
|
61
|
+
ext[:cx] = @width.to_i
|
62
|
+
ext[:cy] = @height.to_i
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def vert_dpi(resolution)
|
68
|
+
resolution[resolution.length == 2 ? 1 : 2]
|
69
|
+
end
|
70
|
+
|
71
|
+
def fit_in_page
|
72
|
+
if @width > MAX_WIDTH_EMUS
|
73
|
+
ratio = @height / @width
|
74
|
+
@width = MAX_WIDTH_EMUS
|
75
|
+
@height = MAX_WIDTH_EMUS * ratio
|
76
|
+
end
|
77
|
+
if @height > MAX_HEIGHT_EMUS
|
78
|
+
ratio = @width / @height
|
79
|
+
@height = MAX_HEIGHT_EMUS
|
80
|
+
@width = MAX_HEIGHT_EMUS * ratio
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
EMUS_PER_INCH = 914400
|
85
|
+
MAX_WIDTH_EMUS = 5742360
|
86
|
+
MAX_HEIGHT_EMUS = 8229600
|
87
|
+
end
|
88
|
+
end
|
data/lib/docx_report/parser.rb
CHANGED
@@ -10,6 +10,7 @@ module DocxReport
|
|
10
10
|
@document.files.each do |file|
|
11
11
|
replace_node_fields(fields, file.xml)
|
12
12
|
replace_node_hyperlinks(fields, file.xml, file)
|
13
|
+
replace_node_images(fields, file.xml, file)
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -27,10 +28,15 @@ module DocxReport
|
|
27
28
|
|
28
29
|
def find_hyperlink_nodes(name, parent_node, file)
|
29
30
|
links = file.rels_xml.xpath "//*[@Target='#{name}']"
|
30
|
-
parent_node.xpath(".//w:hyperlink[@r:id='#{
|
31
|
+
parent_node.xpath(".//w:hyperlink[@r:id='#{find_by_id(links)}']")
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
+
def find_image_nodes(name, parent_node)
|
35
|
+
parent_node.xpath(
|
36
|
+
".//w:drawing[.//wp:docPr[@title='#{name}']]")
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_by_id(links)
|
34
40
|
links.map { |link| link[:Id] }.join("' or @r:id='")
|
35
41
|
end
|
36
42
|
|
@@ -52,9 +58,24 @@ module DocxReport
|
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
61
|
+
def replace_node_images(fields, parent_node, file)
|
62
|
+
fields.select { |f| f.type == :image }.each do |field|
|
63
|
+
image = document_image(field.value)
|
64
|
+
find_image_nodes(field.name, parent_node).each do |node|
|
65
|
+
node.xpath('.//*[@r:embed]').first['r:embed'] = image
|
66
|
+
.file_image_id(file)
|
67
|
+
image.nodes << node
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def document_image(path)
|
73
|
+
@document.images.detect { |img| img.path == path } ||
|
74
|
+
Image.new(path, @document)
|
75
|
+
end
|
76
|
+
|
55
77
|
def find_table(name, parent_node)
|
56
|
-
parent_node.xpath(".//w:tbl[//w:tblCaption[@w:val='#{name}']][1]")
|
57
|
-
.first
|
78
|
+
parent_node.xpath(".//w:tbl[//w:tblCaption[@w:val='#{name}']][1]").first
|
58
79
|
end
|
59
80
|
|
60
81
|
def find_row(table, table_node)
|
@@ -75,8 +96,10 @@ module DocxReport
|
|
75
96
|
table.records.each do |record|
|
76
97
|
new_row = row_node.dup
|
77
98
|
row_node.add_previous_sibling new_row
|
99
|
+
fill_tables record.tables, new_row, file
|
78
100
|
replace_node_fields record.fields, new_row
|
79
101
|
replace_node_hyperlinks record.fields, new_row, file, true
|
102
|
+
replace_node_images record.fields, new_row, file
|
80
103
|
end
|
81
104
|
row_node.remove
|
82
105
|
end
|
data/lib/docx_report/record.rb
CHANGED
data/lib/docx_report/report.rb
CHANGED
@@ -3,7 +3,7 @@ require 'docx_report/data_item'
|
|
3
3
|
module DocxReport
|
4
4
|
class Report
|
5
5
|
include DataItem
|
6
|
-
attr_reader :fields
|
6
|
+
attr_reader :fields
|
7
7
|
|
8
8
|
def initialize(template_path)
|
9
9
|
@template_path = template_path
|
@@ -33,8 +33,8 @@ module DocxReport
|
|
33
33
|
|
34
34
|
def apply_changes(document)
|
35
35
|
parser = Parser.new document
|
36
|
-
parser.replace_all_fields @fields
|
37
36
|
parser.fill_all_tables @tables
|
37
|
+
parser.replace_all_fields @fields
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/lib/docx_report/table.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
|
+
require 'docx_report/block_value'
|
2
|
+
|
1
3
|
module DocxReport
|
2
4
|
class Table
|
5
|
+
include BlockValue
|
3
6
|
attr_reader :name, :has_header, :records
|
4
7
|
|
5
|
-
def initialize(name, has_header = false
|
8
|
+
def initialize(name, has_header = false, collection = nil, fields = [],
|
9
|
+
tables = [])
|
6
10
|
@name = name
|
7
11
|
@has_header = has_header
|
12
|
+
@value = collection
|
8
13
|
@records = []
|
9
|
-
@fields =
|
14
|
+
@fields = fields
|
15
|
+
@tables = tables
|
10
16
|
end
|
11
17
|
|
12
18
|
def new_record
|
13
19
|
new_record = Record.new
|
14
|
-
records << new_record
|
20
|
+
@records << new_record
|
15
21
|
new_record
|
16
22
|
end
|
17
23
|
|
@@ -23,12 +29,24 @@ module DocxReport
|
|
23
29
|
@fields << field
|
24
30
|
end
|
25
31
|
|
32
|
+
def add_table(name, collection = nil, has_header = false, &block)
|
33
|
+
raise 'duplicate table name' if @tables.any? { |t| t.name == name }
|
34
|
+
table = Table.new name, has_header, collection || block
|
35
|
+
@tables << table
|
36
|
+
table
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_table(item)
|
40
|
+
table = Table.new(name, has_header, nil, @fields, @tables)
|
41
|
+
table.load_records(load_value(item))
|
42
|
+
table
|
43
|
+
end
|
44
|
+
|
26
45
|
def load_records(collection)
|
27
46
|
collection.each do |item|
|
28
47
|
record = new_record
|
29
|
-
@
|
30
|
-
|
31
|
-
end
|
48
|
+
@tables.each { |table| record.tables << table.load_table(item) }
|
49
|
+
@fields.each { |field| record.fields << field.load_field(item) }
|
32
50
|
end
|
33
51
|
end
|
34
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docx_report
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ahmed Abudaqqa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -38,21 +38,37 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mini_magick
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.5'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.5'
|
41
55
|
description: |-
|
42
|
-
docx_report is a
|
43
|
-
|
44
|
-
|
56
|
+
docx_report is a gem that generates docx files by replacing
|
57
|
+
strings and inserting images on previously created .docx
|
58
|
+
template file
|
45
59
|
email: ahmed@abudaqqa.com
|
46
60
|
executables: []
|
47
61
|
extensions: []
|
48
62
|
extra_rdoc_files: []
|
49
63
|
files:
|
50
64
|
- lib/docx_report.rb
|
65
|
+
- lib/docx_report/block_value.rb
|
51
66
|
- lib/docx_report/content_file.rb
|
52
67
|
- lib/docx_report/data_item.rb
|
53
68
|
- lib/docx_report/document.rb
|
54
69
|
- lib/docx_report/field.rb
|
55
70
|
- lib/docx_report/hyperlink.rb
|
71
|
+
- lib/docx_report/image.rb
|
56
72
|
- lib/docx_report/parser.rb
|
57
73
|
- lib/docx_report/record.rb
|
58
74
|
- lib/docx_report/report.rb
|