standard-procedure-consolidate 0.4.3 → 0.4.5
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/checksums/standard-procedure-consolidate-0.4.4.gem.sha512 +1 -0
- data/checksums/standard-procedure-consolidate-0.4.5.gem.sha512 +1 -0
- data/lib/consolidate/docx/image.rb +15 -11
- data/lib/consolidate/docx/image_reference_node_builder.rb +175 -121
- data/lib/consolidate/image.rb +2 -3
- 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: 81a63ebba28600dbc0e124ab27e8c6cf150f8dafc80942a12c5e22691bbf8b88
|
4
|
+
data.tar.gz: cbfad371ee9b13baa9eb1fd8aefa9aecf8e1979d581e9b490df7ebdf01706ec3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 856086986d7a075cefe919716d039481ac80480ee169b00d6038d9aeef751338daf4fab90889c96fbd00d183708fa610c450df3d14f704aa9c6d515f5a1a5b54
|
7
|
+
data.tar.gz: ef54556ae1258cf20b08a3a519c1744f6010d80c955e3d26469bf2710a76b2ddf70734c4fe9d76920ab06ca8fbc0d08741a7b2f2dfdf5a0f1769524dcc642b3d
|
@@ -0,0 +1 @@
|
|
1
|
+
c1a13d3d2b2c50741b839e2ca20c730b2689fac090e9c67660950a506ce83b3b9e07e31f3de205ec67dcbf4b1eff462f8fc507468fdb378e864e9a5952b9decf
|
@@ -0,0 +1 @@
|
|
1
|
+
cb04bbf111020966f8acc5dbe0b4d8917d4b6603e6dae83afc15d81e1c9c9163072d3ba7c177a2a0a127d9dc4a5d2e3a5f7177d957bb02e4411ec7301587106b
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "zip"
|
4
4
|
require "nokogiri"
|
5
|
-
|
6
5
|
module Consolidate
|
7
6
|
module Docx
|
8
7
|
class Image < SimpleDelegator
|
@@ -12,21 +11,26 @@ module Consolidate
|
|
12
11
|
# Path to use when storing this image within the docx
|
13
12
|
def storage_path = "word/#{media_path}"
|
14
13
|
|
15
|
-
# Convert width from pixels to EMU
|
16
|
-
def
|
14
|
+
# Convert width from pixels to EMU with proper DPI scaling
|
15
|
+
def width_in_emu = width * emu_per_width_pixel
|
17
16
|
|
18
|
-
# Convert height from pixels to EMU
|
19
|
-
def
|
17
|
+
# Convert height from pixels to EMU with proper DPI scaling
|
18
|
+
def height_in_emu = height * emu_per_height_pixel
|
20
19
|
|
21
|
-
|
22
|
-
def clamped_width(maximum = 7_772_400) = [width, maximum].min
|
20
|
+
def emu_per_width_pixel = EMU_PER_PIXEL * 72 / dpi[:x]
|
23
21
|
|
24
|
-
|
25
|
-
def clamped_height(maximum = 7_772_400) = (height * clamped_width(maximum).to_f / width.to_f).to_i
|
22
|
+
def emu_per_height_pixel = EMU_PER_PIXEL * 72 / dpi[:y]
|
26
23
|
|
27
|
-
|
24
|
+
# Constants
|
25
|
+
DEFAULT_PAGE_WIDTH = 12_240
|
26
|
+
TWENTIETHS_OF_A_POINT_TO_EMU = 635
|
27
|
+
DEFAULT_PAGE_WIDTH_IN_EMU = DEFAULT_PAGE_WIDTH * TWENTIETHS_OF_A_POINT_TO_EMU
|
28
|
+
EMU_PER_PIXEL = 9525
|
29
|
+
DEFAULT_PAGE_HEIGHT = DEFAULT_PAGE_WIDTH * 11 / 8.5 # Assuming US Letter size
|
30
|
+
DEFAULT_PAGE_HEIGHT_IN_EMU = DEFAULT_PAGE_HEIGHT * TWENTIETHS_OF_A_POINT_TO_EMU
|
28
31
|
|
29
|
-
|
32
|
+
# Common page margins in EMU (0.75 inches)
|
33
|
+
DEFAULT_MARGIN_IN_EMU = 685800
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -2,148 +2,202 @@
|
|
2
2
|
|
3
3
|
require "zip"
|
4
4
|
require "nokogiri"
|
5
|
-
|
5
|
+
# To test image scaling manually, from the console:
|
6
|
+
# Consolidate::Docx::Merge.open "spec/files/logo-doc.docx" do |doc|
|
7
|
+
# doc.data logo_image: Consolidate::Image.new(name: "logo.png", width: 4096, height: 1122, path: "spec/files/logo.png")
|
8
|
+
# doc.write_to "tmp/doc-with-logos.docx"
|
9
|
+
# end
|
6
10
|
module Consolidate
|
7
11
|
module Docx
|
8
12
|
class ImageReferenceNodeBuilder < Data.define(:field_name, :image, :node_id, :image_number, :document)
|
9
13
|
def call
|
10
|
-
|
11
|
-
scaled_width, scaled_height = scale_dimensions(image.
|
14
|
+
usable_width, usable_height = usable_dimensions_from(document)
|
15
|
+
scaled_width, scaled_height = scale_dimensions(image.width_in_emu, image.height_in_emu, usable_width, usable_height)
|
12
16
|
|
13
17
|
Nokogiri::XML::Node.new("w:drawing", document).tap do |drawing|
|
14
18
|
drawing["xmlns:a"] = "http://schemas.openxmlformats.org/drawingml/2006/main"
|
15
|
-
drawing <<
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
19
|
+
drawing << create_inline_node(scaled_width, scaled_height)
|
20
|
+
end
|
21
|
+
rescue => ex
|
22
|
+
puts ex.backtrace
|
23
|
+
end
|
24
|
+
|
25
|
+
private def create_inline_node(scaled_width, scaled_height)
|
26
|
+
Nokogiri::XML::Node.new("wp:inline", document).tap do |inline|
|
27
|
+
# Set the inline properties
|
28
|
+
inline["distT"] = "0"
|
29
|
+
inline["distB"] = "0"
|
30
|
+
inline["distL"] = "0"
|
31
|
+
inline["distR"] = "0"
|
32
|
+
|
33
|
+
# Size node (extent)
|
34
|
+
inline << Nokogiri::XML::Node.new("wp:extent", document).tap do |extent|
|
35
|
+
extent["cx"] = scaled_width
|
36
|
+
extent["cy"] = scaled_height
|
37
|
+
end
|
38
|
+
|
39
|
+
# Effect extent
|
40
|
+
inline << create_effect_extent_node
|
41
|
+
|
42
|
+
# Document properties
|
43
|
+
inline << create_doc_properties_node
|
44
|
+
|
45
|
+
# Non-visual properties
|
46
|
+
inline << create_non_visual_properties_node
|
47
|
+
|
48
|
+
# Graphic node
|
49
|
+
inline << create_graphic_node(scaled_width, scaled_height)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private def create_effect_extent_node
|
54
|
+
Nokogiri::XML::Node.new("wp:effectExtent", document).tap do |effect_extent|
|
55
|
+
effect_extent["l"] = "0"
|
56
|
+
effect_extent["t"] = "0"
|
57
|
+
effect_extent["r"] = "0"
|
58
|
+
effect_extent["b"] = "0"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private def create_doc_properties_node
|
63
|
+
Nokogiri::XML::Node.new("wp:docPr", document).tap do |doc_pr|
|
64
|
+
doc_pr["id"] = image_number
|
65
|
+
doc_pr["name"] = image.name
|
66
|
+
doc_pr["descr"] = image.name
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private def create_non_visual_properties_node
|
71
|
+
Nokogiri::XML::Node.new("wp:cNvGraphicFramePr", document).tap do |c_nv_graphic_frame_pr|
|
72
|
+
c_nv_graphic_frame_pr << Nokogiri::XML::Node.new("a:graphicFrameLocks", document).tap do |graphic_frame_locks|
|
73
|
+
graphic_frame_locks["noChangeAspect"] = "1"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private def create_graphic_node(scaled_width, scaled_height)
|
79
|
+
Nokogiri::XML::Node.new("a:graphic", document).tap do |graphic|
|
80
|
+
graphic["xmlns:a"] = "http://schemas.openxmlformats.org/drawingml/2006/main"
|
81
|
+
graphic << Nokogiri::XML::Node.new("a:graphicData", document).tap do |graphic_data|
|
82
|
+
graphic_data["uri"] = "http://schemas.openxmlformats.org/drawingml/2006/picture"
|
83
|
+
graphic_data << create_picture_node(scaled_width, scaled_height)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private def create_picture_node(scaled_width, scaled_height)
|
89
|
+
Nokogiri::XML::Node.new("pic:pic", document).tap do |pic|
|
90
|
+
pic["xmlns:pic"] = "http://schemas.openxmlformats.org/drawingml/2006/picture"
|
91
|
+
|
92
|
+
# Non-visual picture properties
|
93
|
+
pic << create_non_visual_picture_properties
|
94
|
+
|
95
|
+
# Blip fill (the image reference)
|
96
|
+
pic << create_blip_fill
|
97
|
+
|
98
|
+
# Shape properties
|
99
|
+
pic << create_shape_properties(scaled_width, scaled_height)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private def create_non_visual_picture_properties
|
104
|
+
Nokogiri::XML::Node.new("pic:nvPicPr", document).tap do |nv_pic_pr|
|
105
|
+
nv_pic_pr << Nokogiri::XML::Node.new("pic:cNvPr", document).tap do |c_nv_pr|
|
106
|
+
c_nv_pr["id"] = image_number
|
107
|
+
c_nv_pr["name"] = image.name
|
108
|
+
c_nv_pr["descr"] = image.name
|
109
|
+
end
|
110
|
+
nv_pic_pr << Nokogiri::XML::Node.new("pic:cNvPicPr", document).tap do |c_nv_pic_pr|
|
111
|
+
c_nv_pic_pr << Nokogiri::XML::Node.new("a:picLocks", document).tap do |pic_locks|
|
112
|
+
pic_locks["noChangeAspect"] = "1"
|
113
|
+
pic_locks["noChangeArrowheads"] = "0"
|
60
114
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private def create_blip_fill
|
120
|
+
Nokogiri::XML::Node.new("pic:blipFill", document).tap do |blip_fill|
|
121
|
+
blip_fill << Nokogiri::XML::Node.new("a:blip", document).tap do |blip|
|
122
|
+
blip["r:embed"] = node_id
|
123
|
+
blip["cstate"] = "print"
|
124
|
+
end
|
125
|
+
blip_fill << Nokogiri::XML::Node.new("a:stretch", document).tap do |stretch|
|
126
|
+
stretch << Nokogiri::XML::Node.new("a:fillRect", document)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
private def create_shape_properties(scaled_width, scaled_height)
|
132
|
+
Nokogiri::XML::Node.new("pic:spPr", document).tap do |sp_pr|
|
133
|
+
sp_pr["bwMode"] = "auto"
|
134
|
+
# Transform
|
135
|
+
sp_pr << Nokogiri::XML::Node.new("a:xfrm", document).tap do |xfrm|
|
136
|
+
xfrm << Nokogiri::XML::Node.new("a:off", document).tap do |off|
|
137
|
+
off["x"] = "0"
|
138
|
+
off["y"] = "0"
|
65
139
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
graphic_data["uri"] = "http://schemas.openxmlformats.org/drawingml/2006/picture"
|
70
|
-
graphic_data << Nokogiri::XML::Node.new("pic:pic", document).tap do |pic|
|
71
|
-
pic["xmlns:pic"] = "http://schemas.openxmlformats.org/drawingml/2006/picture"
|
72
|
-
pic << Nokogiri::XML::Node.new("pic:nvPicPr", document).tap do |nv_pic_pr|
|
73
|
-
nv_pic_pr << Nokogiri::XML::Node.new("pic:cNvPr", document).tap do |c_nv_pr|
|
74
|
-
c_nv_pr["id"] = image_number
|
75
|
-
c_nv_pr["name"] = image.name
|
76
|
-
c_nv_pr["descr"] = image.name
|
77
|
-
c_nv_pr["hidden"] = false
|
78
|
-
c_nv_pr << Nokogiri::XML::Node.new("pic:cNvPicPr", document).tap do |c_nv_pic_pr|
|
79
|
-
c_nv_pic_pr << Nokogiri::XML::Node.new("a:picLocks", document).tap do |pic_locks|
|
80
|
-
pic_locks["noChangeAspect"] = "1"
|
81
|
-
pic_locks["noChangeArrowheads"] = "1"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
pic << Nokogiri::XML::Node.new("pic:blipFill", document).tap do |blip_fill|
|
87
|
-
blip_fill << Nokogiri::XML::Node.new("a:blip", document).tap do |blip|
|
88
|
-
blip["r:embed"] = node_id
|
89
|
-
end
|
90
|
-
blip_fill << Nokogiri::XML::Node.new("a:stretch", document).tap do |stretch|
|
91
|
-
stretch << Nokogiri::XML::Node.new("a:fillRect", document)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
pic << Nokogiri::XML::Node.new("pic:spPr", document).tap do |sp_pr|
|
95
|
-
sp_pr["bwMode"] = "auto"
|
96
|
-
sp_pr << Nokogiri::XML::Node.new("a:xfrm", document).tap do |xfrm|
|
97
|
-
xfrm << Nokogiri::XML::Node.new("a:off", document).tap do |off|
|
98
|
-
off["x"] = "0"
|
99
|
-
off["y"] = "0"
|
100
|
-
end
|
101
|
-
xfrm << Nokogiri::XML::Node.new("a:ext", document).tap do |ext|
|
102
|
-
ext["cx"] = scaled_width
|
103
|
-
ext["cy"] = scaled_height
|
104
|
-
end
|
105
|
-
end
|
106
|
-
sp_pr << Nokogiri::XML::Node.new("a:prstGeom", document).tap do |prst_geom|
|
107
|
-
prst_geom["prst"] = "rect"
|
108
|
-
prst_geom << Nokogiri::XML::Node.new("a:avLst", document)
|
109
|
-
end
|
110
|
-
sp_pr << Nokogiri::XML::Node.new("a:noFill", document)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
140
|
+
xfrm << Nokogiri::XML::Node.new("a:ext", document).tap do |ext|
|
141
|
+
ext["cx"] = scaled_width
|
142
|
+
ext["cy"] = scaled_height
|
114
143
|
end
|
115
144
|
end
|
145
|
+
# Pre-set geometry
|
146
|
+
sp_pr << Nokogiri::XML::Node.new("a:prstGeom", document).tap do |prst_geom|
|
147
|
+
prst_geom["prst"] = "rect"
|
148
|
+
prst_geom << Nokogiri::XML::Node.new("a:avLst", document)
|
149
|
+
end
|
150
|
+
# No fill
|
151
|
+
sp_pr << Nokogiri::XML::Node.new("a:noFill", document)
|
116
152
|
end
|
117
153
|
end
|
118
154
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
DEFAULT_PAGE_HEIGHT = DEFAULT_PAGE_WIDTH * 11 / 8.5 # Assuming standard page ratio
|
124
|
-
DEFAULT_PAGE_HEIGHT_IN_EMU = DEFAULT_PAGE_HEIGHT * TWENTIETHS_OF_A_POINT_TO_EMU
|
155
|
+
private def usable_dimensions_from(document)
|
156
|
+
# Get page dimensions
|
157
|
+
page_width = (document.at_xpath("//w:sectPr/w:pgSz/@w:w")&.value || Image::DEFAULT_PAGE_WIDTH).to_i
|
158
|
+
page_height = (document.at_xpath("//w:sectPr/w:pgSz/@w:h")&.value || Image::DEFAULT_PAGE_HEIGHT).to_i
|
125
159
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
160
|
+
# Convert to EMU
|
161
|
+
width_emu = page_width * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
162
|
+
height_emu = page_height * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
163
|
+
|
164
|
+
# Account for margins
|
165
|
+
left_margin = (document.at_xpath("//w:sectPr/w:pgMar/@w:left")&.value || 0).to_i * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
166
|
+
right_margin = (document.at_xpath("//w:sectPr/w:pgMar/@w:right")&.value || 0).to_i * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
167
|
+
top_margin = (document.at_xpath("//w:sectPr/w:pgMar/@w:top")&.value || 0).to_i * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
168
|
+
bottom_margin = (document.at_xpath("//w:sectPr/w:pgMar/@w:bottom")&.value || 0).to_i * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
130
169
|
|
131
|
-
|
132
|
-
|
133
|
-
|
170
|
+
# If no margins found, use defaults
|
171
|
+
if left_margin == 0 && right_margin == 0
|
172
|
+
left_margin = right_margin = Image::DEFAULT_MARGIN_IN_EMU
|
173
|
+
end
|
134
174
|
|
135
|
-
|
136
|
-
|
175
|
+
if top_margin == 0 && bottom_margin == 0
|
176
|
+
top_margin = bottom_margin = Image::DEFAULT_MARGIN_IN_EMU
|
177
|
+
end
|
137
178
|
|
138
|
-
|
179
|
+
# Usable area
|
180
|
+
usable_width = width_emu - left_margin - right_margin
|
181
|
+
usable_height = height_emu - top_margin - bottom_margin
|
182
|
+
|
183
|
+
# Add a small buffer (10%) to ensure image fits
|
184
|
+
usable_width = (usable_width * 0.9).to_i
|
185
|
+
usable_height = (usable_height * 0.9).to_i
|
186
|
+
|
187
|
+
[usable_width, usable_height]
|
139
188
|
end
|
140
189
|
|
141
|
-
private def scale_dimensions(
|
142
|
-
|
143
|
-
|
144
|
-
|
190
|
+
private def scale_dimensions(width_emu, height_emu, max_width_emu, max_height_emu)
|
191
|
+
# Ensure we're working with EMU values throughout
|
192
|
+
width_ratio = max_width_emu.to_f / width_emu
|
193
|
+
height_ratio = max_height_emu.to_f / height_emu
|
194
|
+
|
195
|
+
# Take the smaller ratio to ensure image fits within boundaries
|
196
|
+
# but never scale up (keep at 1.0 if the image is smaller than available space)
|
197
|
+
scale = [width_ratio, height_ratio, 1.0].min
|
145
198
|
|
146
|
-
|
199
|
+
# Apply scaling factor and ensure integer values
|
200
|
+
[(width_emu * scale).to_i, (height_emu * scale).to_i]
|
147
201
|
end
|
148
202
|
end
|
149
203
|
end
|
data/lib/consolidate/image.rb
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
module Consolidate
|
4
4
|
class Image
|
5
5
|
attr_reader :name, :width, :height, :aspect_ratio, :dpi
|
6
|
-
|
7
|
-
def initialize name:, width:, height:, path: nil, url: nil, contents: nil
|
6
|
+
def initialize name:, width:, height:, path: nil, url: nil, contents: nil, dpix: nil, dpiy: nil
|
8
7
|
@name = name
|
9
8
|
@width = width
|
10
9
|
@height = height
|
@@ -13,7 +12,7 @@ module Consolidate
|
|
13
12
|
@contents = contents
|
14
13
|
@aspect_ratio = width.to_f / height.to_f
|
15
14
|
# TODO: Read this from the contents
|
16
|
-
@dpi = {x: 72, y: 72}
|
15
|
+
@dpi = {x: dpix || 72, y: dpiy || 72}
|
17
16
|
end
|
18
17
|
|
19
18
|
def to_s = name
|
data/lib/consolidate/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: standard-procedure-consolidate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rahoul Baruah
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-02-
|
10
|
+
date: 2025-02-25 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rubyzip
|
@@ -67,6 +67,8 @@ files:
|
|
67
67
|
- checksums/standard-procedure-consolidate-0.4.1.gem.sha512
|
68
68
|
- checksums/standard-procedure-consolidate-0.4.2.gem.sha512
|
69
69
|
- checksums/standard-procedure-consolidate-0.4.3.gem.sha512
|
70
|
+
- checksums/standard-procedure-consolidate-0.4.4.gem.sha512
|
71
|
+
- checksums/standard-procedure-consolidate-0.4.5.gem.sha512
|
70
72
|
- exe/consolidate
|
71
73
|
- exe/examine
|
72
74
|
- lib/consolidate.rb
|