standard-procedure-consolidate 0.4.4 → 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
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
|
+
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,22 +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
20
|
def emu_per_width_pixel = EMU_PER_PIXEL * 72 / dpi[:x]
|
22
21
|
|
23
22
|
def emu_per_height_pixel = EMU_PER_PIXEL * 72 / dpi[:y]
|
24
23
|
|
24
|
+
# Constants
|
25
25
|
DEFAULT_PAGE_WIDTH = 12_240
|
26
26
|
TWENTIETHS_OF_A_POINT_TO_EMU = 635
|
27
27
|
DEFAULT_PAGE_WIDTH_IN_EMU = DEFAULT_PAGE_WIDTH * TWENTIETHS_OF_A_POINT_TO_EMU
|
28
28
|
EMU_PER_PIXEL = 9525
|
29
29
|
DEFAULT_PAGE_HEIGHT = DEFAULT_PAGE_WIDTH * 11 / 8.5 # Assuming US Letter size
|
30
30
|
DEFAULT_PAGE_HEIGHT_IN_EMU = DEFAULT_PAGE_HEIGHT * TWENTIETHS_OF_A_POINT_TO_EMU
|
31
|
+
|
32
|
+
# Common page margins in EMU (0.75 inches)
|
33
|
+
DEFAULT_MARGIN_IN_EMU = 685800
|
31
34
|
end
|
32
35
|
end
|
33
36
|
end
|
@@ -2,141 +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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
sp_pr << Nokogiri::XML::Node.new("a:noFill", document)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
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"
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
-
private def
|
120
|
-
|
121
|
-
|
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
|
122
129
|
end
|
123
130
|
|
124
|
-
private def
|
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"
|
139
|
+
end
|
140
|
+
xfrm << Nokogiri::XML::Node.new("a:ext", document).tap do |ext|
|
141
|
+
ext["cx"] = scaled_width
|
142
|
+
ext["cy"] = scaled_height
|
143
|
+
end
|
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)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private def usable_dimensions_from(document)
|
156
|
+
# Get page dimensions
|
125
157
|
page_width = (document.at_xpath("//w:sectPr/w:pgSz/@w:w")&.value || Image::DEFAULT_PAGE_WIDTH).to_i
|
126
158
|
page_height = (document.at_xpath("//w:sectPr/w:pgSz/@w:h")&.value || Image::DEFAULT_PAGE_HEIGHT).to_i
|
127
159
|
|
160
|
+
# Convert to EMU
|
128
161
|
width_emu = page_width * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
129
162
|
height_emu = page_height * Image::TWENTIETHS_OF_A_POINT_TO_EMU
|
130
163
|
|
131
|
-
|
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
|
169
|
+
|
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
|
174
|
+
|
175
|
+
if top_margin == 0 && bottom_margin == 0
|
176
|
+
top_margin = bottom_margin = Image::DEFAULT_MARGIN_IN_EMU
|
177
|
+
end
|
178
|
+
|
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]
|
132
188
|
end
|
133
189
|
|
134
|
-
private def scale_dimensions(
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
138
198
|
|
139
|
-
|
199
|
+
# Apply scaling factor and ensure integer values
|
200
|
+
[(width_emu * scale).to_i, (height_emu * scale).to_i]
|
140
201
|
end
|
141
202
|
end
|
142
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
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- checksums/standard-procedure-consolidate-0.4.2.gem.sha512
|
69
69
|
- checksums/standard-procedure-consolidate-0.4.3.gem.sha512
|
70
70
|
- checksums/standard-procedure-consolidate-0.4.4.gem.sha512
|
71
|
+
- checksums/standard-procedure-consolidate-0.4.5.gem.sha512
|
71
72
|
- exe/consolidate
|
72
73
|
- exe/examine
|
73
74
|
- lib/consolidate.rb
|