write_xlsx 0.78.0 → 0.79.0

Sign up to get free protection for your applications and to get access to all the features.
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