write_xlsx 0.78.0 → 0.79.0

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/Changes +7 -0
  3. data/README.md +1 -1
  4. data/examples/chart_pie.rb +1 -1
  5. data/lib/write_xlsx/drawing.rb +1 -1
  6. data/lib/write_xlsx/package/vml.rb +194 -7
  7. data/lib/write_xlsx/sheets.rb +38 -5
  8. data/lib/write_xlsx/version.rb +1 -1
  9. data/lib/write_xlsx/workbook.rb +150 -55
  10. data/lib/write_xlsx/worksheet/page_setup.rb +5 -2
  11. data/lib/write_xlsx/worksheet.rb +136 -11
  12. data/test/helper.rb +1 -1
  13. data/test/perl_output/demo.xlsx +0 -0
  14. data/test/regression/images/black_150.jpg +0 -0
  15. data/test/regression/images/black_150.png +0 -0
  16. data/test/regression/images/black_150e.png +0 -0
  17. data/test/regression/images/black_300.jpg +0 -0
  18. data/test/regression/images/black_300.png +0 -0
  19. data/test/regression/images/black_300e.png +0 -0
  20. data/test/regression/images/black_72.jpg +0 -0
  21. data/test/regression/images/black_72.png +0 -0
  22. data/test/regression/images/black_72e.png +0 -0
  23. data/test/regression/images/black_96.jpg +0 -0
  24. data/test/regression/images/black_96.png +0 -0
  25. data/test/regression/test_chart_scatter15.rb +44 -0
  26. data/test/regression/test_header01.rb +28 -0
  27. data/test/regression/test_header02.rb +28 -0
  28. data/test/regression/test_header03.rb +31 -0
  29. data/test/regression/test_header_image01.rb +26 -0
  30. data/test/regression/test_header_image02.rb +33 -0
  31. data/test/regression/test_header_image03.rb +34 -0
  32. data/test/regression/test_header_image04.rb +34 -0
  33. data/test/regression/test_header_image05.rb +28 -0
  34. data/test/regression/test_header_image06.rb +32 -0
  35. data/test/regression/test_header_image07.rb +29 -0
  36. data/test/regression/test_header_image08.rb +33 -0
  37. data/test/regression/test_header_image09.rb +36 -0
  38. data/test/regression/test_header_image10.rb +36 -0
  39. data/test/regression/test_header_image11.rb +28 -0
  40. data/test/regression/test_header_image12.rb +28 -0
  41. data/test/regression/test_header_image13.rb +36 -0
  42. data/test/regression/test_header_image14.rb +36 -0
  43. data/test/regression/test_image22.rb +24 -0
  44. data/test/regression/test_image23.rb +30 -0
  45. data/test/regression/test_image24.rb +24 -0
  46. data/test/regression/test_image25.rb +24 -0
  47. data/test/regression/test_image26.rb +30 -0
  48. data/test/regression/test_image27.rb +24 -0
  49. data/test/regression/xlsx_files/chart_scatter15.xlsx +0 -0
  50. data/test/regression/xlsx_files/header01.xlsx +0 -0
  51. data/test/regression/xlsx_files/header02.xlsx +0 -0
  52. data/test/regression/xlsx_files/header03.xlsx +0 -0
  53. data/test/regression/xlsx_files/header_image01.xlsx +0 -0
  54. data/test/regression/xlsx_files/header_image02.xlsx +0 -0
  55. data/test/regression/xlsx_files/header_image03.xlsx +0 -0
  56. data/test/regression/xlsx_files/header_image04.xlsx +0 -0
  57. data/test/regression/xlsx_files/header_image05.xlsx +0 -0
  58. data/test/regression/xlsx_files/header_image06.xlsx +0 -0
  59. data/test/regression/xlsx_files/header_image07.xlsx +0 -0
  60. data/test/regression/xlsx_files/header_image08.xlsx +0 -0
  61. data/test/regression/xlsx_files/header_image09.xlsx +0 -0
  62. data/test/regression/xlsx_files/header_image10.xlsx +0 -0
  63. data/test/regression/xlsx_files/header_image11.xlsx +0 -0
  64. data/test/regression/xlsx_files/header_image12.xlsx +0 -0
  65. data/test/regression/xlsx_files/header_image13.xlsx +0 -0
  66. data/test/regression/xlsx_files/header_image14.xlsx +0 -0
  67. data/test/regression/xlsx_files/image22.xlsx +0 -0
  68. data/test/regression/xlsx_files/image23.xlsx +0 -0
  69. data/test/regression/xlsx_files/image24.xlsx +0 -0
  70. data/test/regression/xlsx_files/image25.xlsx +0 -0
  71. data/test/regression/xlsx_files/image26.xlsx +0 -0
  72. data/test/regression/xlsx_files/image27.xlsx +0 -0
  73. metadata +119 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 97af755aa6f07672a642c569fa9916a0e18365d8
4
- data.tar.gz: f3ca578778d1725fe6a7936e37d5586bdb834eba
3
+ metadata.gz: e671b68b1de5d627fc5e2306b17a157dd669ac0b
4
+ data.tar.gz: 9a33233b791e917331d2714bfa1212789ff7d515
5
5
  SHA512:
6
- metadata.gz: 21399bff146d040567803600ed057b6b2e7eb5d49beee2a4744aefe19ef6987958d4152e050e1d0ee7ec58ad87454ece4bc90250f316f5bbdfb50b7f0a420bbb
7
- data.tar.gz: 376f41e382cf9c8f88c35ee1dbe69a567c1189ec20e2b010d9cfa9ef39507abd5eb728893ee2aca931f6dffe5caef1aa23568ab3095fb1468e7208deeb76d0d4
6
+ metadata.gz: 2b218047dedb38eb475c41a3dda88d3eefca2d185ea34af3270f6d5ca9ebb884f1e81d3b8f893b16cbb84930fda41f28b2a62c926d2a58b9754c2c0e296204ce
7
+ data.tar.gz: d9ea75c34d0510b98d1a9a985fabbe3bcd62a73b8f31eba6b9d334313d1f94e29ffe6392dda2bc12cc84e1d4a778ace0e8ddff6afe530a5c5eb3661aeaa7ef07
data/Changes CHANGED
@@ -1,4 +1,11 @@
1
1
  Change history of write_xlsx rubygem.
2
+ 2014-11-29 v0.79.0
3
+ Added option to add images to headers and footers.
4
+ Added option to not scale heaader/footer with page.
5
+ Fixed issue where non 96dpi images weren’t scaled properly in Excel.
6
+ Fix for issue where X axis title formula was overwritten by the
7
+ Y axis title.
8
+
2
9
  2014-11-22 v0.78.0
3
10
  Added Doughnut chart with set_rotation and set_hole_size methods.
4
11
  Added set_rotation method to Pie charts.
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  gem to create a new file in the Excel 2007+ XLSX format, and you can use the
6
6
  same interface as writeexcel gem. write_xlsx is converted from Perl's module
7
- [Excel::Writer::XLSX-0.78](https://github.com/jmcnamara/excel-writer-xlsx) .
7
+ [Excel::Writer::XLSX-0.79](https://github.com/jmcnamara/excel-writer-xlsx) .
8
8
 
9
9
  ## Description
10
10
 
@@ -60,7 +60,7 @@ worksheet.insert_chart('C2', chart1, 25, 10)
60
60
  # Create an example Pie chart like above.
61
61
  chart2 = workbook.add_chart(:type => 'pie', :embedded => 1)
62
62
 
63
- # Configure the series and add user defined segment colours.
63
+ # Configure the series.
64
64
  chart2.add_series(
65
65
  :name => 'Pie sales data',
66
66
  :categories => '=Sheet1!$A$2:$A$4',
@@ -86,7 +86,7 @@ module Writexlsx
86
86
  @writer.tag_elements('xdr:twoCellAnchor', attributes) do
87
87
  # Write the xdr:from element.
88
88
  write_from(col_from, row_from, col_from_offset, row_from_offset)
89
- # Write the xdr:from element.
89
+ # Write the xdr:to element.
90
90
  write_to(col_to, row_to, col_to_offset, row_to_offset)
91
91
 
92
92
  if type == 1
@@ -16,30 +16,42 @@ module Writexlsx
16
16
  @writer.set_xml_writer(filename)
17
17
  end
18
18
 
19
- def assemble_xml_file(worksheet)
19
+ def assemble_xml_file(
20
+ data_id, vml_shape_id, comments_data,
21
+ buttons_data, header_images_data = []
22
+ )
20
23
  return unless @writer
21
24
 
22
25
  write_xml_namespace do
23
26
  # Write the o:shapelayout element.
24
- write_shapelayout(worksheet.vml_data_id)
27
+ write_shapelayout(data_id)
25
28
 
26
29
  z_index = 1
27
- vml_shape_id = worksheet.vml_shape_id
28
- unless worksheet.buttons_data.empty?
30
+ unless buttons_data.empty?
29
31
  vml_shape_id, z_index =
30
32
  write_shape_type_and_shape(
31
- worksheet.buttons_data,
33
+ buttons_data,
32
34
  vml_shape_id, z_index) do
33
35
  write_button_shapetype
34
36
  end
35
37
  end
36
- unless worksheet.sorted_comments.empty?
38
+ unless comments_data.empty?
37
39
  write_shape_type_and_shape(
38
- worksheet.sorted_comments,
40
+ comments_data,
39
41
  vml_shape_id, z_index) do
40
42
  write_comment_shapetype
41
43
  end
42
44
  end
45
+ unless header_images_data.empty?
46
+ write_image_shapetype
47
+ index = 1
48
+ header_images_data.each do |image|
49
+ # Write the v:shape element.
50
+ vml_shape_id += 1
51
+ write_image_shape(vml_shape_id, index, image)
52
+ index += 1
53
+ end
54
+ end
43
55
  end
44
56
  @writer.crlf
45
57
  @writer.close
@@ -144,6 +156,43 @@ module Writexlsx
144
156
  end
145
157
  end
146
158
 
159
+ #
160
+ # Write the <v:shapetype> element.
161
+ #
162
+ def write_image_shapetype
163
+ id = '_x0000_t75'
164
+ coordsize = '21600,21600'
165
+ spt = 75
166
+ o_preferrelative = 't'
167
+ path = 'm@4@5l@4@11@9@11@9@5xe'
168
+ filled = 'f'
169
+ stroked = 'f'
170
+
171
+ attributes = [
172
+ ['id', id],
173
+ ['coordsize', coordsize],
174
+ ['o:spt', spt],
175
+ ['o:preferrelative', o_preferrelative],
176
+ ['path', path],
177
+ ['filled', filled],
178
+ ['stroked', stroked]
179
+ ]
180
+
181
+ @writer.tag_elements('v:shapetype', attributes) do
182
+ # Write the v:stroke element.
183
+ write_stroke
184
+
185
+ # Write the v:formulas element.
186
+ write_formulas
187
+
188
+ # Write the v:path element.
189
+ write_image_path
190
+
191
+ # Write the o:lock element.
192
+ write_aspect_ratio_lock
193
+ end
194
+ end
195
+
147
196
  #
148
197
  # Write the <v:path> element.
149
198
  #
@@ -158,6 +207,23 @@ module Writexlsx
158
207
  @writer.empty_tag('v:path', attributes)
159
208
  end
160
209
 
210
+ #
211
+ # Write the <v:path> element.
212
+ #
213
+ def write_image_path
214
+ extrusionok = 'f'
215
+ gradientshapeok = 't'
216
+ connecttype = 'rect'
217
+
218
+ attributes = [
219
+ ['o:extrusionok', extrusionok],
220
+ ['gradientshapeok', gradientshapeok],
221
+ ['o:connecttype', connecttype]
222
+ ]
223
+
224
+ @writer.empty_tag('v:path', attributes)
225
+ end
226
+
161
227
  #
162
228
  # Write the <o:lock> element.
163
229
  #
@@ -168,6 +234,127 @@ module Writexlsx
168
234
  ]
169
235
  @writer.empty_tag('o:lock', attributes)
170
236
  end
237
+
238
+ #
239
+ # Write the <o:lock> element.
240
+ #
241
+ def write_rotation_lock
242
+ attributes = [
243
+ ['v:ext', 'edit'],
244
+ ['rotation', 't']
245
+ ]
246
+ @writer.empty_tag('o:lock', attributes)
247
+ end
248
+
249
+ #
250
+ # Write the <o:lock> element.
251
+ #
252
+ def write_aspect_ratio_lock
253
+ ext = 'edit'
254
+ aspectratio = 't'
255
+
256
+ attributes = [
257
+ ['v:ext', ext],
258
+ ['aspectratio', aspectratio]
259
+ ]
260
+
261
+ @writer.empty_tag('o:lock', attributes)
262
+ end
263
+
264
+ #
265
+ # Write the <v:shape> element.
266
+ #
267
+ def write_image_shape(id, index, image_data)
268
+ type = '#_x0000_t75'
269
+
270
+ # Set the shape index.
271
+ shape_index = "_x0000_s#{id}"
272
+
273
+ # Get the image parameters
274
+ width = image_data[0]
275
+ height = image_data[1]
276
+ name = image_data[2]
277
+ position = image_data[3]
278
+ x_dpi = image_data[4]
279
+ y_dpi = image_data[5]
280
+
281
+ # Scale the height/width by the resolution, relative to 72dpi.
282
+ width = width * 72.0 / x_dpi
283
+ height = height * 72.0 / y_dpi
284
+
285
+ # Excel uses a rounding based around 72 and 96 dpi.
286
+ width = 72/96.0 * (width * 96/72.0 + 0.25).to_i
287
+ height = 72/96.0 * (height * 96/72.0 + 0.25).to_i
288
+
289
+ if (width - width.to_i).abs < 0.1
290
+ width = width.to_i
291
+ end
292
+ if (height - height.to_i).abs < 0.1
293
+ height = height.to_i
294
+ end
295
+
296
+ style = [
297
+ "position:absolute", "margin-left:0", "margin-top:0",
298
+ "width:#{width}pt", "height:#{height}pt",
299
+ "z-index:#{index}"
300
+ ].join(';')
301
+ attributes = [
302
+ ['id', position],
303
+ ['o:spid', "_x0000_s#{id}"],
304
+ ['type', type],
305
+ ['style', style]
306
+ ]
307
+
308
+ @writer.tag_elements('v:shape', attributes) do
309
+ # Write the v:imagedata element.
310
+ write_imagedata(index, name)
311
+
312
+ # Write the o:lock element.
313
+ write_rotation_lock
314
+ end
315
+ end
316
+
317
+ #
318
+ # Write the <v:imagedata> element.
319
+ #
320
+ def write_imagedata(index, o_title)
321
+ attributes = [
322
+ ['o:relid', "rId#{index}"],
323
+ ['o:title', o_title]
324
+ ]
325
+
326
+ @writer.empty_tag('v:imagedata', attributes)
327
+ end
328
+
329
+ #
330
+ # Write the <v:formulas> element.
331
+ #
332
+ def write_formulas
333
+ @writer.tag_elements('v:formulas') do
334
+ # Write the v:f elements.
335
+ write_f('if lineDrawn pixelLineWidth 0')
336
+ write_f('sum @0 1 0')
337
+ write_f('sum 0 0 @1')
338
+ write_f('prod @2 1 2')
339
+ write_f('prod @3 21600 pixelWidth')
340
+ write_f('prod @3 21600 pixelHeight')
341
+ write_f('sum @0 0 1')
342
+ write_f('prod @6 1 2')
343
+ write_f('prod @7 21600 pixelWidth')
344
+ write_f('sum @8 21600 0')
345
+ write_f('prod @7 21600 pixelHeight')
346
+ write_f('sum @10 21600 0')
347
+ end
348
+ end
349
+
350
+ #
351
+ # Write the <v:f> element.
352
+ #
353
+ def write_f(eqn)
354
+ attributes = [ ['eqn', eqn] ]
355
+
356
+ @writer.empty_tag('v:f', attributes)
357
+ end
171
358
  end
172
359
  end
173
360
  end
@@ -58,13 +58,30 @@ module Writexlsx
58
58
 
59
59
  def write_vml_files(package_dir)
60
60
  dir = "#{package_dir}/xl/drawings"
61
- self.select { |sheet| sheet.has_vml? }.
62
- each_with_index do |sheet, index|
61
+ index = 1
62
+ self.each do |sheet|
63
+ next if !sheet.has_vml? and !sheet.has_header_vml?
63
64
  FileUtils.mkdir_p(dir)
64
65
 
65
- vml = Package::Vml.new
66
- vml.set_xml_writer("#{dir}/vmlDrawing#{index+1}.vml")
67
- vml.assemble_xml_file(sheet)
66
+ if sheet.has_vml?
67
+ vml = Package::Vml.new
68
+ vml.set_xml_writer("#{dir}/vmlDrawing#{index}.vml")
69
+ vml.assemble_xml_file(
70
+ sheet.vml_data_id, sheet.vml_shape_id,
71
+ sheet.sorted_comments, sheet.buttons_data
72
+ )
73
+ index += 1
74
+ end
75
+ if sheet.has_header_vml?
76
+ vml = Package::Vml.new
77
+ vml.set_xml_writer("#{dir}/vmlDrawing#{index}.vml")
78
+ vml.assemble_xml_file(
79
+ sheet.vml_header_id, sheet.vml_header_id * 1024,
80
+ [], [], sheet.header_images_data
81
+ )
82
+ write_vml_drawing_rels_files(package_dir, sheet, index)
83
+ index += 1
84
+ end
68
85
  end
69
86
  end
70
87
 
@@ -125,6 +142,22 @@ module Writexlsx
125
142
  end
126
143
  end
127
144
 
145
+ def write_vml_drawing_rels_files(package_dir, worksheet, index)
146
+ # Create the drawing .rels dir.
147
+ dir = "#{package_dir}/xl/drawings/_rels"
148
+ FileUtils.mkdir_p(dir)
149
+
150
+ rels = Package::Relationships.new
151
+
152
+ worksheet.vml_drawing_links.each do |drawing_data|
153
+ rels.add_document_relationship(*drawing_data)
154
+ end
155
+
156
+ # Create the .rels file such as /xl/drawings/_rels/vmlDrawing1.vml.rels.
157
+ rels.set_xml_writer("#{dir}/vmlDrawing#{index}.vml.rels")
158
+ rels.assemble_xml_file
159
+ end
160
+
128
161
  def write_worksheet_rels_files(package_dir)
129
162
  write_sheet_rels_files_base(worksheets, "#{package_dir}/xl/worksheets/_rels",
130
163
  'sheet')
@@ -1,5 +1,5 @@
1
1
  require 'write_xlsx/workbook'
2
2
 
3
3
  class WriteXLSX < Writexlsx::Workbook
4
- VERSION = "0.78.0"
4
+ VERSION = "0.79.0"
5
5
  end
@@ -985,7 +985,7 @@ module Writexlsx
985
985
  end
986
986
 
987
987
  def num_vml_files
988
- @worksheets.select { |sheet| sheet.has_vml? }.count
988
+ @worksheets.select { |sheet| sheet.has_vml? || sheet.has_header_vml? }.count
989
989
  end
990
990
 
991
991
  def num_comment_files
@@ -1534,18 +1534,32 @@ module Writexlsx
1534
1534
  comment_id = 0
1535
1535
  vml_drawing_id = 0
1536
1536
  vml_data_id = 1
1537
+ vml_header_id = 0
1537
1538
  vml_shape_id = 1024
1539
+ comment_files = 0
1538
1540
 
1539
- @worksheets.select { |sheet| sheet.has_vml? }.each do |sheet|
1540
- comment_id += 1 if sheet.has_comments?
1541
- vml_drawing_id += 1
1541
+ @worksheets.each do |sheet|
1542
+ next if !sheet.has_vml? && !sheet.has_header_vml?
1543
+ if sheet.has_vml?
1544
+ if sheet.has_comments?
1545
+ comment_files += 1
1546
+ comment_id += 1
1547
+ end
1548
+ vml_drawing_id += 1
1542
1549
 
1543
- sheet.prepare_vml_objects(vml_data_id, vml_shape_id,
1544
- vml_drawing_id, comment_id)
1550
+ sheet.prepare_vml_objects(vml_data_id, vml_shape_id,
1551
+ vml_drawing_id, comment_id)
1545
1552
 
1546
- # Each VML file should start with a shape id incremented by 1024.
1547
- vml_data_id += 1 * ( 1 + sheet.num_comments_block )
1548
- vml_shape_id += 1024 * ( 1 + sheet.num_comments_block )
1553
+ # Each VML file should start with a shape id incremented by 1024.
1554
+ vml_data_id += 1 * ( 1 + sheet.num_comments_block )
1555
+ vml_shape_id += 1024 * ( 1 + sheet.num_comments_block )
1556
+ end
1557
+
1558
+ if sheet.has_header_vml?
1559
+ vml_header_id += 1
1560
+ vml_drawing_id += 1
1561
+ sheet.prepare_header_vml_objects(vml_header_id, vml_drawing_id)
1562
+ end
1549
1563
  end
1550
1564
 
1551
1565
  add_font_format_for_cell_comments if num_comment_files > 0
@@ -1730,27 +1744,69 @@ module Writexlsx
1730
1744
  chart_count = sheet.charts.size
1731
1745
  image_count = sheet.images.size
1732
1746
  shape_count = sheet.shapes.size
1733
- next if chart_count + image_count + shape_count == 0
1747
+ header_image_count = sheet.header_images.size
1748
+ footer_image_count = sheet.footer_images.size
1749
+ has_drawing = false
1734
1750
 
1735
- drawing_id += 1
1751
+ # Check that some image or drawing needs to be processed.
1752
+ next if chart_count + image_count + shape_count + header_image_count + footer_image_count == 0
1736
1753
 
1754
+ # Don't increase the drawing_id header/footer images.
1755
+ if chart_count + image_count + shape_count > 0
1756
+ drawing_id += 1
1757
+ has_drawing = true
1758
+ end
1759
+
1760
+ # Prepare the worksheet charts.
1737
1761
  sheet.charts.each_with_index do |chart, index|
1738
1762
  chart_ref_id += 1
1739
1763
  sheet.prepare_chart(index, chart_ref_id, drawing_id)
1740
1764
  end
1741
1765
 
1766
+ # Prepare the worksheet images.
1742
1767
  sheet.images.each_with_index do |image, index|
1743
- image_id, type, width, height, name = get_image_properties(image[2])
1768
+ type, width, height, name, x_dpi, y_dpi = get_image_properties(image[2])
1744
1769
  image_ref_id += 1
1745
- sheet.prepare_image(index, image_ref_id, drawing_id, width, height, name, type)
1770
+ sheet.prepare_image(index, image_ref_id, drawing_id, width, height, name, type, x_dpi, y_dpi)
1746
1771
  end
1747
1772
 
1773
+ # Prepare the worksheet shapes.
1748
1774
  sheet.shapes.each_with_index do |shape, index|
1749
1775
  sheet.prepare_shape(index, drawing_id)
1750
1776
  end
1751
1777
 
1752
- drawing = sheet.drawing
1753
- @drawings << drawing
1778
+ # Prepare the header images.
1779
+ header_image_count.times do |index|
1780
+ filename = sheet.header_images[index][0]
1781
+ position = sheet.header_images[index][1]
1782
+
1783
+ type, width, height, name, x_dpi, y_dpi =
1784
+ get_image_properties(filename)
1785
+
1786
+ image_ref_id += 1
1787
+
1788
+ sheet.prepare_header_image(image_ref_id, width, height,
1789
+ name, type, position, x_dpi, y_dpi)
1790
+ end
1791
+
1792
+ # Prepare the footer images.
1793
+ footer_image_count.times do |index|
1794
+ filename = sheet.footer_images[index][0]
1795
+ position = sheet.footer_images[index][1]
1796
+
1797
+ type, width, height, name, x_dpi, y_dpi =
1798
+ get_image_properties(filename)
1799
+
1800
+ image_ref_id += 1
1801
+
1802
+ sheet.prepare_header_image(image_ref_id, width, height,
1803
+ name, type, position, x_dpi, y_dpi)
1804
+ end
1805
+
1806
+ if has_drawing
1807
+ drawing = sheet.drawing
1808
+ @drawings << drawing
1809
+ end
1754
1810
  end
1755
1811
 
1756
1812
  # Sort the workbook charts references into the order that the were
@@ -1766,41 +1822,32 @@ module Writexlsx
1766
1822
  # any duplicates.
1767
1823
  #
1768
1824
  def get_image_properties(filename)
1769
- previous_images = []
1770
- images_seen = {}
1771
- image_id = 1;
1772
- if images_seen[filename]
1773
- # We've processed this file already.
1774
- index = images_seen[filename] - 1
1825
+ # Note the image_id, and previous_images mechanism isn't currently used.
1826
+ x_dpi = 96
1827
+ y_dpi = 96
1828
+
1829
+ # Open the image file and import the data.
1830
+ data = File.binread(filename)
1831
+ if data.unpack('x A3')[0] == 'PNG'
1832
+ # Test for PNGs.
1833
+ type, width, height, x_dpi, y_dpi = process_png(data)
1834
+ @image_types[:png] = 1
1835
+ elsif data.unpack('n')[0] == 0xFFD8
1836
+ # Test for JPEG files.
1837
+ type, width, height, x_dpi, y_dpi = process_jpg(data, filename)
1838
+ @image_types[:jpeg] = 1
1839
+ elsif data.unpack('A2')[0] == 'BM'
1840
+ # Test for BMPs.
1841
+ type, width, height = process_bmp(data, filename)
1842
+ @image_types[:bmp] = 1
1775
1843
  else
1776
- # Open the image file and import the data.
1777
- data = File.binread(filename)
1778
- if data.unpack('x A3')[0] == 'PNG'
1779
- # Test for PNGs.
1780
- type, width, height = process_png(data)
1781
- @image_types[:png] = 1
1782
- elsif data.unpack('n')[0] == 0xFFD8
1783
- # Test for JPEG files.
1784
- type, width, height = process_jpg(data, filename)
1785
- @image_types[:jpeg] = 1
1786
- elsif data.unpack('A2')[0] == 'BM'
1787
- # Test for BMPs.
1788
- type, width, height = process_bmp(data, filename)
1789
- @image_types[:bmp] = 1
1790
- else
1791
- # TODO. Add Image::Size to support other types.
1792
- raise "Unsupported image format for file: #{filename}\n"
1793
- end
1794
-
1795
- @images << [filename, type]
1796
-
1797
- # Also store new data for use in duplicate images.
1798
- previous_images << [image_id, type, width, height]
1799
- images_seen[filename] = image_id
1800
- image_id += 1
1844
+ # TODO. Add Image::Size to support other types.
1845
+ raise "Unsupported image format for file: #{filename}\n"
1801
1846
  end
1802
1847
 
1803
- [image_id, type, width, height, File.basename(filename)]
1848
+ @images << [filename, type]
1849
+
1850
+ [type, width, height, File.basename(filename), x_dpi, y_dpi]
1804
1851
  end
1805
1852
 
1806
1853
  #
@@ -1808,27 +1855,75 @@ module Writexlsx
1808
1855
  #
1809
1856
  def process_png(data)
1810
1857
  type = 'png'
1811
- width = data[16, 4].unpack("N")[0]
1812
- height = data[20, 4].unpack("N")[0]
1858
+ width = 0
1859
+ height = 0
1860
+ x_dip = 96
1861
+ y_dpi = 96
1813
1862
 
1814
- [type, width, height]
1863
+ offset = 8
1864
+ data_length = data.size
1865
+
1866
+ # Search through the image data to read the height and width in th the
1867
+ # IHDR element. Also read the DPI in the pHYs element.
1868
+ while offset < data_length
1869
+
1870
+ length = data[offset + 0, 4].unpack("N")[0]
1871
+ png_type = data[offset + 4, 4].unpack("A4")[0]
1872
+
1873
+ case png_type
1874
+ when "IHDR"
1875
+ width = data[offset + 8, 4].unpack("N")[0]
1876
+ height = data[offset + 12, 4].unpack("N")[0]
1877
+ when "pHYs"
1878
+ x_ppu = data[offset + 8, 4].unpack("N")[0]
1879
+ y_ppu = data[offset + 12, 4].unpack("N")[0]
1880
+ units = data[offset + 16, 1].unpack("C")[0]
1881
+
1882
+ if units == 1
1883
+ x_dpi = x_ppu * 0.0254
1884
+ y_dpi = y_ppu * 0.0254
1885
+ end
1886
+ end
1887
+
1888
+ offset = offset + length + 12
1889
+
1890
+ break if png_type == "IEND"
1891
+ end
1892
+ raise "#{filename}: no size data found in png image.\n" unless height
1893
+
1894
+ [type, width, height, x_dpi, y_dpi]
1815
1895
  end
1816
1896
 
1817
1897
  def process_jpg(data, filename)
1818
1898
  type = 'jpeg'
1899
+ x_dpi = 96
1900
+ y_dpi = 96
1901
+
1819
1902
  offset = 2
1820
1903
  data_length = data.bytesize
1821
1904
 
1822
- # Search through the image data to find the 0xFFC0 marker. The height and
1823
- # width are contained in the data for that sub element.
1905
+ # Search through the image data to read the height and width in the
1906
+ # 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element.
1824
1907
  while offset < data_length
1825
- marker = data[offset, 2].unpack("n")[0]
1908
+ marker = data[offset+0, 2].unpack("n")[0]
1826
1909
  length = data[offset+2, 2].unpack("n")[0]
1827
1910
 
1828
1911
  if marker == 0xFFC0 || marker == 0xFFC2
1829
1912
  height = data[offset+5, 2].unpack("n")[0]
1830
1913
  width = data[offset+7, 2].unpack("n")[0]
1831
- break
1914
+ end
1915
+ if marker == 0xFFE0
1916
+ units = data[offset + 11, 1].unpack("C")[0]
1917
+ x_density = data[offset + 12, 2].unpack("n")[0]
1918
+ y_density = data[offset + 14, 2].unpack("n")[0]
1919
+
1920
+ if units == 1
1921
+ x_dpi = x_density
1922
+ y_dpi = y_density
1923
+ elsif units == 2
1924
+ x_dpi = x_density * 2.54
1925
+ y_dpi = y_density * 2.54
1926
+ end
1832
1927
  end
1833
1928
 
1834
1929
  offset += length + 2
@@ -1836,7 +1931,7 @@ module Writexlsx
1836
1931
  end
1837
1932
 
1838
1933
  raise "#{filename}: no size data found in jpeg image.\n" unless height
1839
- [type, width, height]
1934
+ [type, width, height, x_dpi, y_dpi]
1840
1935
  end
1841
1936
 
1842
1937
  # Extract width and height information from a BMP file.
@@ -12,7 +12,7 @@ module Writexlsx
12
12
  attr_accessor :fit_page, :fit_width, :fit_height, :page_setup_changed # :nodoc:
13
13
  attr_writer :across # :nodoc:
14
14
  attr_accessor :orientation, :print_options_changed # :nodoc:
15
- attr_accessor :header, :footer, :header_footer_changed, :header_footer_aligns
15
+ attr_accessor :header, :footer, :header_footer_changed, :header_footer_aligns, :header_footer_scales
16
16
  attr_writer :page_start
17
17
 
18
18
  def initialize # :nodoc:
@@ -34,6 +34,8 @@ module Writexlsx
34
34
  @page_setup_changed = false
35
35
  @across = false
36
36
  @orientation = true
37
+ @header_footer_aligns = true
38
+ @header_footer_scales = true
37
39
  end
38
40
 
39
41
  def paper=(paper_size)
@@ -139,7 +141,8 @@ module Writexlsx
139
141
  def write_header_footer(writer, excel2003_style) #:nodoc:
140
142
  tag = 'headerFooter'
141
143
  attributes = []
142
- attributes << ['alignWithMargins', 0] if @header_footer_aligns
144
+ attributes << ['scaleWithDoc', 0] unless ptrue?(@header_footer_scales)
145
+ attributes << ['alignWithMargins', 0] unless ptrue?(@header_footer_aligns)
143
146
 
144
147
  if @header_footer_changed
145
148
  writer.tag_elements(tag, attributes) do