standard-procedure-consolidate 0.3.9 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/checksums/standard-procedure-consolidate-0.4.0.gem.sha512 +1 -0
- data/checksums/standard-procedure-consolidate-0.4.1.gem.sha512 +1 -0
- data/lib/consolidate/docx/image.rb +11 -3
- data/lib/consolidate/docx/image_reference_node_builder.rb +64 -62
- data/lib/consolidate/docx/merge.rb +147 -47
- data/lib/consolidate/image.rb +4 -1
- data/lib/consolidate/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ffce925315399bd4432936d929e995114acc2dea081f3835eb437fe2787f612
|
4
|
+
data.tar.gz: b64ae5181ae92d5e66be79e5861787c3355fe08870dbb4ee3ab8355ef4ce149d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e56547c32223cf66f807c804cdfff2897f63887729dcf1ff80d51c35a0615115f47b16347b582d58a6ae128c3ecad91a67ca128e0d7c89b6e55a3e88b2d5a6c9
|
7
|
+
data.tar.gz: 4b351ad3a367dd4382abc18e18fbc978095fec92aa8b258a90feb82f2e0a65ffbcca26a8177383d75fab07d711a170b10b541e48c195c4b8b7db0eea2cfd1b26
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# [0.4.1] - 2024-12-18
|
2
|
+
|
3
|
+
Replace image merge fields with blanks if the image data is not provided
|
4
|
+
|
5
|
+
# [0.4.0] - 2024-12-16
|
6
|
+
|
7
|
+
Image embedding works
|
8
|
+
|
1
9
|
# [0.3.9] - 2024-12-4
|
2
10
|
|
3
11
|
Image embedding - not fully tested but it seems to work in a few test cases
|
@@ -0,0 +1 @@
|
|
1
|
+
58d1597374e775340c60e3e49b9d65437909b482f7be3040fb9fdca5d8d4b4d3020508a9ee01e3521b2c552e1b853d0475335d1ca1c092b4172c1e9ac86df640
|
@@ -0,0 +1 @@
|
|
1
|
+
dfd2ec36154a632f86dd8f99a2b85c59ed6048dbb6b8100f84645ea1cf49e3e77ad73b3367ef89d46e055c860f1b1e82c5b0d0d37c969a19c2644a3e42ebbe21
|
@@ -13,12 +13,20 @@ module Consolidate
|
|
13
13
|
def storage_path = "word/#{media_path}"
|
14
14
|
|
15
15
|
# Convert width from pixels to EMU
|
16
|
-
def width = super *
|
16
|
+
def width = super * emu_per_width_pixel
|
17
17
|
|
18
18
|
# Convert height from pixels to EMU
|
19
|
-
def height = super *
|
19
|
+
def height = super * emu_per_height_pixel
|
20
20
|
|
21
|
-
|
21
|
+
# Get the width of this image in EMU up to a maximum page width (also in EMU)
|
22
|
+
def clamped_width(maximum = 7_772_400) = [width, maximum].min
|
23
|
+
|
24
|
+
# Get the height of this image in EMU adjusted for a maximum page width (also in EMU)
|
25
|
+
def clamped_height(maximum = 7_772_400) = (height * clamped_width(maximum).to_f / width.to_f).to_i
|
26
|
+
|
27
|
+
def emu_per_width_pixel = 914_400 / dpi[:x]
|
28
|
+
|
29
|
+
def emu_per_height_pixel = 914_400 / dpi[:y]
|
22
30
|
end
|
23
31
|
end
|
24
32
|
end
|
@@ -5,74 +5,67 @@ require "nokogiri"
|
|
5
5
|
|
6
6
|
module Consolidate
|
7
7
|
module Docx
|
8
|
-
class ImageReferenceNodeBuilder < Data.define(:field_name, :image, :node_id, :document)
|
8
|
+
class ImageReferenceNodeBuilder < Data.define(:field_name, :image, :node_id, :image_number, :document)
|
9
9
|
def call
|
10
|
-
Nokogiri::XML::Node.new("w:
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
graphic_frame_locks["noChangeAspect"] = "1"
|
31
|
-
end
|
10
|
+
Nokogiri::XML::Node.new("w:drawing", document).tap do |drawing|
|
11
|
+
drawing["xmlns:a"] = "http://schemas.openxmlformats.org/drawingml/2006/main"
|
12
|
+
drawing << Nokogiri::XML::Node.new("wp:inline", document).tap do |inline|
|
13
|
+
inline["distT"] = "0"
|
14
|
+
inline["distB"] = "0"
|
15
|
+
inline["distL"] = "0"
|
16
|
+
inline["distR"] = "0"
|
17
|
+
inline << Nokogiri::XML::Node.new("wp:extent", document).tap do |extent|
|
18
|
+
extent["cx"] = image.clamped_width(max_width_from(document))
|
19
|
+
extent["cy"] = image.clamped_height(max_width_from(document))
|
20
|
+
end
|
21
|
+
inline << Nokogiri::XML::Node.new("wp:effectExtent", document).tap do |effect_extent|
|
22
|
+
effect_extent["l"] = "0"
|
23
|
+
effect_extent["t"] = "0"
|
24
|
+
effect_extent["r"] = "0"
|
25
|
+
effect_extent["b"] = "0"
|
26
|
+
end
|
27
|
+
inline << Nokogiri::XML::Node.new("wp:cNvGraphicFramePr", document).tap do |c_nv_graphic_frame_pr|
|
28
|
+
c_nv_graphic_frame_pr << Nokogiri::XML::Node.new("a:graphicFrameLocks", document).tap do |graphic_frame_locks|
|
29
|
+
graphic_frame_locks["noChangeAspect"] = true
|
32
30
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
31
|
+
end
|
32
|
+
inline << Nokogiri::XML::Node.new("a:graphic", document).tap do |graphic|
|
33
|
+
graphic["xmlns:a"] = "http://schemas.openxmlformats.org/drawingml/2006/main"
|
34
|
+
graphic << Nokogiri::XML::Node.new("a:graphicData", document).tap do |graphic_data|
|
35
|
+
graphic_data["uri"] = "http://schemas.openxmlformats.org/drawingml/2006/picture"
|
36
|
+
graphic_data << Nokogiri::XML::Node.new("pic:pic", document).tap do |pic|
|
37
|
+
pic["xmlns:pic"] = "http://schemas.openxmlformats.org/drawingml/2006/picture"
|
38
|
+
pic << Nokogiri::XML::Node.new("pic:nvPicPr", document).tap do |nv_pic_pr|
|
39
|
+
nv_pic_pr << Nokogiri::XML::Node.new("pic:cNvPr", document).tap do |c_nv_pr|
|
40
|
+
c_nv_pr["id"] = image_number
|
41
|
+
c_nv_pr["name"] = image.name
|
42
|
+
c_nv_pr["descr"] = image.name
|
43
|
+
c_nv_pr["hidden"] = false
|
44
|
+
c_nv_pr << Nokogiri::XML::Node.new("pic:cNvPicPr", document)
|
49
45
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
blip << Nokogiri::XML::Node.new("a:extLst", document)
|
55
|
-
end
|
56
|
-
blip_fill << Nokogiri::XML::Node.new("a:stretch", document).tap do |stretch|
|
57
|
-
stretch << Nokogiri::XML::Node.new("a:fillRect", document)
|
58
|
-
end
|
46
|
+
end
|
47
|
+
pic << Nokogiri::XML::Node.new("pic:blipFill", document).tap do |blip_fill|
|
48
|
+
blip_fill << Nokogiri::XML::Node.new("a:blip", document).tap do |blip|
|
49
|
+
blip["r:embed"] = node_id
|
59
50
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
51
|
+
blip_fill << Nokogiri::XML::Node.new("a:stretch", document).tap do |stretch|
|
52
|
+
stretch << Nokogiri::XML::Node.new("a:fillRect", document)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
pic << Nokogiri::XML::Node.new("pic:spPr", document).tap do |sp_pr|
|
56
|
+
sp_pr << Nokogiri::XML::Node.new("a:xfrm", document).tap do |xfrm|
|
57
|
+
xfrm << Nokogiri::XML::Node.new("a:off", document).tap do |off|
|
58
|
+
off["x"] = "0"
|
59
|
+
off["y"] = "0"
|
70
60
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
61
|
+
xfrm << Nokogiri::XML::Node.new("a:ext", document).tap do |ext|
|
62
|
+
ext["cx"] = image.clamped_width(max_width_from(document))
|
63
|
+
ext["cy"] = image.clamped_height(max_width_from(document))
|
74
64
|
end
|
75
|
-
|
65
|
+
end
|
66
|
+
sp_pr << Nokogiri::XML::Node.new("a:prstGeom", document).tap do |prst_geom|
|
67
|
+
prst_geom["prst"] = "rect"
|
68
|
+
prst_geom << Nokogiri::XML::Node.new("a:avLst", document)
|
76
69
|
end
|
77
70
|
end
|
78
71
|
end
|
@@ -81,6 +74,15 @@ module Consolidate
|
|
81
74
|
end
|
82
75
|
end
|
83
76
|
end
|
77
|
+
|
78
|
+
DEFAULT_PAGE_WIDTH = 12_240
|
79
|
+
TWENTIETHS_OF_A_POINT_TO_EMU = 635
|
80
|
+
DEFAULT_PAGE_WIDTH_IN_EMU = DEFAULT_PAGE_WIDTH * TWENTIETHS_OF_A_POINT_TO_EMU
|
81
|
+
|
82
|
+
private def max_width_from document
|
83
|
+
page_width = (document.at_xpath("//w:sectPr/w:pgSz/@w:w")&.value || DEFAULT_PAGE_WIDTH).to_i
|
84
|
+
page_width * TWENTIETHS_OF_A_POINT_TO_EMU
|
85
|
+
end
|
84
86
|
end
|
85
87
|
end
|
86
88
|
end
|
@@ -20,13 +20,16 @@ module Consolidate
|
|
20
20
|
@zip = Zip::File.open(path)
|
21
21
|
@documents = load_documents
|
22
22
|
@relations = load_relations
|
23
|
+
@contents_xml = load_and_update_contents_xml
|
23
24
|
@output = {}
|
24
25
|
@images = {}
|
26
|
+
@mapping = {}
|
25
27
|
end
|
26
28
|
|
27
29
|
# Helper method to display the contents of the document and the merge fields from the CLI
|
28
30
|
def examine
|
29
31
|
puts "Documents: #{document_names.join(", ")}"
|
32
|
+
puts "Content documents: #{content_document_names.join(", ")}"
|
30
33
|
puts "Merge fields: #{text_field_names.join(", ")}"
|
31
34
|
puts "Image fields: #{image_field_names.join(", ")}"
|
32
35
|
end
|
@@ -38,29 +41,48 @@ module Consolidate
|
|
38
41
|
def image_field_names = @image_field_names ||= tag_nodes.collect { |tag_node| image_field_names_from tag_node }.flatten.compact.uniq
|
39
42
|
|
40
43
|
# List the documents stored within this docx
|
41
|
-
def document_names = @zip.entries.
|
44
|
+
def document_names = @zip.entries.map(&:name)
|
45
|
+
|
46
|
+
# List the content within this docx
|
47
|
+
def content_document_names = @documents.keys
|
48
|
+
|
49
|
+
# List the field names that are present in the merge data
|
50
|
+
def merge_field_names = @mapping.keys
|
42
51
|
|
43
52
|
# Set the merge data and erform the substitution - creating copies of any documents that contain merge tags and replacing the tags with the supplied data
|
44
53
|
def data mapping = {}
|
45
|
-
mapping = mapping.transform_keys(&:to_s)
|
46
|
-
|
54
|
+
@mapping = mapping.transform_keys(&:to_s)
|
55
|
+
if verbose
|
56
|
+
puts "...mapping data"
|
57
|
+
puts @mapping.keys.select { |field_name| text_field_names.include?(field_name) }.map { |field_name| "... #{field_name} => #{@mapping[field_name]}" }.join("\n")
|
58
|
+
end
|
47
59
|
|
48
|
-
@images =
|
60
|
+
@images = load_images_and_link_relations
|
49
61
|
|
50
62
|
@documents.each do |name, document|
|
51
|
-
@output[name] = substitute(document.dup,
|
63
|
+
@output[name] = substitute(document.dup, document_name: name).serialize save_with: 0
|
52
64
|
end
|
53
65
|
end
|
54
66
|
|
55
67
|
def write_to path
|
56
68
|
puts "...writing to #{path}" if verbose
|
57
69
|
Zip::File.open(path, Zip::File::CREATE) do |out|
|
70
|
+
@output[contents_xml] = @contents_xml.serialize save_with: 0
|
71
|
+
|
58
72
|
@images.each do |field_name, image|
|
59
|
-
|
73
|
+
next if image.nil?
|
74
|
+
puts "... writing image #{field_name} to #{image.storage_path}" if verbose
|
60
75
|
out.get_output_stream(image.storage_path) { |o| o.write image.contents }
|
61
76
|
end
|
62
77
|
|
63
|
-
@
|
78
|
+
@relations.each do |relation_name, relations|
|
79
|
+
puts "... writing relations #{relation_name}" if verbose
|
80
|
+
out.get_output_stream(relation_name) { |o| o.write relations }
|
81
|
+
end
|
82
|
+
|
83
|
+
@zip.reject do |entry|
|
84
|
+
@relations.key? entry.name
|
85
|
+
end.each do |entry|
|
64
86
|
puts "... writing updated document to #{entry.name}" if verbose
|
65
87
|
out.get_output_stream(entry.name) { |o| o.write(@output[entry.name] || @relations[entry.name] || @zip.read(entry.name)) }
|
66
88
|
end
|
@@ -71,25 +93,7 @@ module Consolidate
|
|
71
93
|
|
72
94
|
attr_reader :verbose
|
73
95
|
|
74
|
-
def
|
75
|
-
@zip.entries.each_with_object({}) do |entry, results|
|
76
|
-
next unless entry.name.match?(/word\/(document|header|footer|footnotes|endnotes).?\.xml/)
|
77
|
-
puts "...reading document #{entry.name}" if verbose
|
78
|
-
contents = @zip.get_input_stream entry
|
79
|
-
results[entry.name] = Nokogiri::XML(contents) { |x| x.noent }
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def load_relations
|
84
|
-
@zip.entries.each_with_object({}) do |entry, results|
|
85
|
-
next unless entry.name.match?(/word\/_rels\/.*.rels/)
|
86
|
-
puts "...reading relation #{entry.name}" if verbose
|
87
|
-
contents = @zip.get_input_stream entry
|
88
|
-
results[entry.name] = Nokogiri::XML(contents) { |x| x.noent }
|
89
|
-
end
|
90
|
-
ensure
|
91
|
-
@zip.close
|
92
|
-
end
|
96
|
+
def contents_xml = "[Content_Types].xml"
|
93
97
|
|
94
98
|
# Regex to find merge fields that contain text
|
95
99
|
def text_tag = /\{\{\s*(?!.*_image\b)(\S+)\s*\}\}/i
|
@@ -113,30 +117,61 @@ module Consolidate
|
|
113
117
|
# Extract the image field name(s) from the paragraph
|
114
118
|
def image_field_names_from(tag_node) = (matches = tag_node.content.scan(image_tag)).empty? ? nil : matches.flatten.map(&:strip)
|
115
119
|
|
120
|
+
# Unique number for each image field
|
121
|
+
def relation_number_for(field_name) = @mapping.keys.index(field_name) + 1000
|
122
|
+
|
116
123
|
# Identifier to use when linking a merge field to the actual image file contents
|
117
124
|
def relation_id_for(field_name) = "rId#{field_name}"
|
118
125
|
|
126
|
+
# Empty elations document for documents that do not already have one
|
127
|
+
def default_relations_document = %(<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>)
|
128
|
+
|
129
|
+
def load_documents
|
130
|
+
@zip.entries.each_with_object({}) do |entry, results|
|
131
|
+
next unless entry.name.match?(/word\/(document|header|footer|footnotes|endnotes).?\.xml/)
|
132
|
+
puts "...reading document #{entry.name}" if verbose
|
133
|
+
contents = @zip.get_input_stream entry
|
134
|
+
results[entry.name] = Nokogiri::XML(contents) { |x| x.noent }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def load_relations
|
139
|
+
@zip.entries.each_with_object({}) do |entry, results|
|
140
|
+
next unless entry.name.match?(/word\/(document|header|footer|footnotes|endnotes).?\.xml/)
|
141
|
+
relation_document = entry.name.gsub("word/", "word/_rels/").gsub(".xml", ".xml.rels")
|
142
|
+
puts "...reading or building relations for #{relation_document}" if verbose
|
143
|
+
contents = @zip.find_entry(relation_document) ? @zip.get_input_stream(relation_document) : default_relations_document
|
144
|
+
results[relation_document] = Nokogiri::XML(contents) { |x| x.noent }
|
145
|
+
end
|
146
|
+
ensure
|
147
|
+
@zip.close
|
148
|
+
end
|
149
|
+
|
119
150
|
# Create relation links for each image field and store the image data
|
120
|
-
def
|
121
|
-
|
151
|
+
def load_images_and_link_relations
|
152
|
+
load_images.tap do |images|
|
122
153
|
link_relations_to images
|
123
154
|
end
|
124
155
|
end
|
125
156
|
|
126
157
|
# Build a mapping of image paths to the image data so that the image data can be stored in the output docx
|
127
|
-
def
|
158
|
+
def load_images
|
128
159
|
image_field_names.each_with_object({}) do |field_name, result|
|
129
|
-
result[field_name] = Consolidate::Docx::Image.new(mapping[field_name])
|
160
|
+
result[field_name] = @mapping[field_name].nil? ? nil : Consolidate::Docx::Image.new(@mapping[field_name])
|
161
|
+
puts "... #{field_name} => #{result[field_name]&.media_path}" if verbose
|
130
162
|
end
|
131
163
|
end
|
132
164
|
|
133
165
|
# Update all relation documents to include a relationship for each image field and its stored image path
|
134
166
|
def link_relations_to images
|
135
167
|
@relations.each do |name, xml|
|
168
|
+
puts "... linking images in #{name}" if verbose
|
136
169
|
images.each do |field_name, image|
|
170
|
+
# Has an actual image file been supplied?
|
171
|
+
next if image.nil?
|
137
172
|
# Is this image already referenced in this relationship document?
|
138
|
-
next unless xml.at_xpath("//Relationship[@Target
|
139
|
-
puts "...
|
173
|
+
next unless xml.at_xpath("//Relationship[@Target=\"#{image.media_path}\"]").nil?
|
174
|
+
puts "... #{relation_id_for(field_name)} => #{image.media_path}" if verbose
|
140
175
|
xml.root << Nokogiri::XML::Node.new("Relationship", xml).tap do |relation|
|
141
176
|
relation["Id"] = relation_id_for(field_name)
|
142
177
|
relation["Type"] = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
|
@@ -146,32 +181,38 @@ module Consolidate
|
|
146
181
|
end
|
147
182
|
end
|
148
183
|
|
184
|
+
def load_and_update_contents_xml
|
185
|
+
puts "...reading and updating #{contents_xml}" if verbose
|
186
|
+
content = @zip.get_input_stream(contents_xml)
|
187
|
+
Nokogiri::XML(content) { |x| x.noent }.tap do |document|
|
188
|
+
add_content_relations_to document
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
149
192
|
# Go through the given document, replacing any merge fields with the values provided
|
150
193
|
# and storing the results in a new document
|
151
|
-
def substitute document, document_name
|
194
|
+
def substitute document, document_name:
|
195
|
+
puts "...substituting fields in #{document_name}" if verbose && tag_nodes_for(document).any?
|
196
|
+
substitute_text document, document_name: document_name
|
197
|
+
substitute_images document, document_name: document_name
|
198
|
+
end
|
199
|
+
|
200
|
+
def substitute_text document, document_name:
|
152
201
|
tag_nodes_for(document).each do |tag_node|
|
153
|
-
|
154
|
-
image_field_names = image_field_names_from(tag_node) || []
|
202
|
+
field_names = text_field_names_from(tag_node) || []
|
155
203
|
|
156
204
|
# Extract the properties (formatting) nodes if they exist
|
157
205
|
paragraph_properties = tag_node.search ".//w:pPr"
|
158
206
|
run_properties = tag_node.at_xpath ".//w:rPr"
|
159
207
|
|
160
|
-
# Get the current contents, then substitute any text fields
|
208
|
+
# Get the current contents, then substitute any text fields
|
161
209
|
text = tag_node.content
|
162
210
|
|
163
|
-
|
164
|
-
field_value = mapping[field_name].to_s
|
165
|
-
puts "...substituting #{field_name} with #{field_value}
|
211
|
+
field_names.each do |field_name|
|
212
|
+
field_value = @mapping[field_name].to_s
|
213
|
+
puts "... substituting '#{field_name}' with '#{field_value}'" if verbose
|
166
214
|
text = text.gsub(tag_for(field_name), field_value)
|
167
215
|
end
|
168
|
-
image_nodes = image_field_names.collect do |field_name|
|
169
|
-
image = @images[field_name]
|
170
|
-
puts "...substituting #{field_name} in #{document_name}" if verbose
|
171
|
-
# Remove the merge tag and create an image reference node to be added to this node
|
172
|
-
text = text.gsub(tag_for(field_name), "")
|
173
|
-
ImageReferenceNodeBuilder.new(field_name: field_name, image: image, node_id: relation_id_for(field_name), document: document).call
|
174
|
-
end
|
175
216
|
|
176
217
|
# Create a new text node with the substituted text
|
177
218
|
text_node = Nokogiri::XML::Node.new("w:t", tag_node.document)
|
@@ -182,7 +223,46 @@ module Consolidate
|
|
182
223
|
run_node << run_properties unless run_properties.nil?
|
183
224
|
run_node << text_node
|
184
225
|
# Add the paragraph properties and the run node to the tag node
|
185
|
-
tag_node.children = Nokogiri::XML::NodeSet.new(document, paragraph_properties.to_a + [run_node]
|
226
|
+
tag_node.children = Nokogiri::XML::NodeSet.new(document, paragraph_properties.to_a + [run_node])
|
227
|
+
rescue => ex
|
228
|
+
# Have to mangle the exception message otherwise it outputs the entire document
|
229
|
+
puts ex.message.to_s[0..255]
|
230
|
+
puts ex.backtrace.first
|
231
|
+
end
|
232
|
+
document
|
233
|
+
end
|
234
|
+
|
235
|
+
# Go through the given document, replacing any merge fields with the values provided
|
236
|
+
# and storing the results in a new document
|
237
|
+
def substitute_images document, document_name:
|
238
|
+
tag_nodes_for(document).each do |tag_node|
|
239
|
+
field_names = image_field_names_from(tag_node) || []
|
240
|
+
# Extract the properties (formatting) nodes if they exist
|
241
|
+
paragraph_properties = tag_node.search ".//w:pPr"
|
242
|
+
run_properties = tag_node.at_xpath ".//w:rPr"
|
243
|
+
|
244
|
+
pieces = tag_node.content.split(image_tag)
|
245
|
+
# Split the content into pieces - either text or an image merge field
|
246
|
+
# Then replace the text with text nodes or the image merge fields with drawing nodes
|
247
|
+
replacement_nodes = pieces.collect do |piece|
|
248
|
+
field_name = piece.strip
|
249
|
+
if field_names.include? field_name
|
250
|
+
image = @images[field_name]
|
251
|
+
# if no image was provided then insert blank text
|
252
|
+
# otherwise insert a w:drawing node that references the image contents
|
253
|
+
if image.nil?
|
254
|
+
puts "... substituting '#{field_name}' with blank as no image was provided" if verbose
|
255
|
+
Nokogiri::XML::Node.new("w:t", document) { |t| t.content = "" }
|
256
|
+
else
|
257
|
+
puts "... substituting '#{field_name}' with '<#{relation_id_for(field_name)}/>'" if verbose
|
258
|
+
ImageReferenceNodeBuilder.new(field_name: field_name, image: image, node_id: relation_id_for(field_name), image_number: relation_number_for(field_name), document: document).call
|
259
|
+
end
|
260
|
+
else
|
261
|
+
Nokogiri::XML::Node.new("w:t", document) { |t| t.content = piece }
|
262
|
+
end
|
263
|
+
end
|
264
|
+
run_nodes = (replacement_nodes.map { |node| Nokogiri::XML::Node.new("w:r", document) { |run_node| run_node.children = node } } + [run_properties]).compact
|
265
|
+
tag_node.children = Nokogiri::XML::NodeSet.new(document, paragraph_properties.to_a + run_nodes)
|
186
266
|
rescue => ex
|
187
267
|
# Have to mangle the exception message otherwise it outputs the entire document
|
188
268
|
puts ex.message.to_s[0..255]
|
@@ -190,6 +270,26 @@ module Consolidate
|
|
190
270
|
end
|
191
271
|
document
|
192
272
|
end
|
273
|
+
|
274
|
+
CONTENT_RELATIONS = {
|
275
|
+
jpeg: "image/jpg",
|
276
|
+
png: "image/png",
|
277
|
+
bmp: "image/bmp",
|
278
|
+
gif: "image/gif",
|
279
|
+
tif: "image/tif",
|
280
|
+
pdf: "application/pdf",
|
281
|
+
mov: "application/movie"
|
282
|
+
}.freeze
|
283
|
+
|
284
|
+
def add_content_relations_to document
|
285
|
+
CONTENT_RELATIONS.each do |file_type, content_type|
|
286
|
+
next unless document.at_xpath("//Default[@Extension=\"#{file_type}\"]").nil?
|
287
|
+
document.root << Nokogiri::XML::Node.new("Default", document).tap do |relation|
|
288
|
+
relation["Extension"] = file_type
|
289
|
+
relation["ContentType"] = content_type
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
193
293
|
end
|
194
294
|
end
|
195
295
|
end
|
data/lib/consolidate/image.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Consolidate
|
4
4
|
class Image
|
5
|
-
attr_reader :name, :width, :height
|
5
|
+
attr_reader :name, :width, :height, :aspect_ratio, :dpi
|
6
6
|
|
7
7
|
def initialize name:, width:, height:, path: nil, url: nil, contents: nil
|
8
8
|
@name = name
|
@@ -11,6 +11,9 @@ module Consolidate
|
|
11
11
|
@path = path
|
12
12
|
@url = url
|
13
13
|
@contents = contents
|
14
|
+
@aspect_ratio = width.to_f / height.to_f
|
15
|
+
# TODO: Read this from the contents
|
16
|
+
@dpi = {x: 72, y: 72}
|
14
17
|
end
|
15
18
|
|
16
19
|
def to_s = name
|
data/lib/consolidate/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: standard-procedure-consolidate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rahoul Baruah
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -64,6 +64,8 @@ files:
|
|
64
64
|
- checksums/standard-procedure-consolidate-0.3.0.gem.sha512
|
65
65
|
- checksums/standard-procedure-consolidate-0.3.1.gem.sha512
|
66
66
|
- checksums/standard-procedure-consolidate-0.3.9.gem.sha512
|
67
|
+
- checksums/standard-procedure-consolidate-0.4.0.gem.sha512
|
68
|
+
- checksums/standard-procedure-consolidate-0.4.1.gem.sha512
|
67
69
|
- exe/consolidate
|
68
70
|
- exe/examine
|
69
71
|
- lib/consolidate.rb
|