write_xlsx 1.12.1 → 1.12.2

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/Changes +3 -0
  4. data/LICENSE.txt +1 -1
  5. data/examples/autofilter.rb +1 -2
  6. data/examples/colors.rb +4 -4
  7. data/examples/formats.rb +14 -14
  8. data/lib/write_xlsx/chart/area.rb +1 -1
  9. data/lib/write_xlsx/chart/axis.rb +4 -4
  10. data/lib/write_xlsx/chart/bar.rb +1 -1
  11. data/lib/write_xlsx/chart/caption.rb +8 -4
  12. data/lib/write_xlsx/chart/column.rb +1 -1
  13. data/lib/write_xlsx/chart/doughnut.rb +2 -2
  14. data/lib/write_xlsx/chart/line.rb +1 -1
  15. data/lib/write_xlsx/chart/pie.rb +2 -2
  16. data/lib/write_xlsx/chart/radar.rb +1 -1
  17. data/lib/write_xlsx/chart/scatter.rb +1 -1
  18. data/lib/write_xlsx/chart/series.rb +10 -20
  19. data/lib/write_xlsx/chart/stock.rb +1 -1
  20. data/lib/write_xlsx/chart.rb +14 -21
  21. data/lib/write_xlsx/chartsheet.rb +3 -3
  22. data/lib/write_xlsx/drawing.rb +108 -114
  23. data/lib/write_xlsx/format.rb +20 -24
  24. data/lib/write_xlsx/image.rb +89 -0
  25. data/lib/write_xlsx/image_property.rb +163 -0
  26. data/lib/write_xlsx/inserted_chart.rb +42 -0
  27. data/lib/write_xlsx/package/button.rb +58 -5
  28. data/lib/write_xlsx/package/conditional_format.rb +4 -4
  29. data/lib/write_xlsx/package/packager.rb +22 -27
  30. data/lib/write_xlsx/package/rich_value.rb +1 -1
  31. data/lib/write_xlsx/package/styles.rb +1 -1
  32. data/lib/write_xlsx/package/vml.rb +10 -19
  33. data/lib/write_xlsx/shape.rb +3 -2
  34. data/lib/write_xlsx/sparkline.rb +1 -1
  35. data/lib/write_xlsx/utility.rb +8 -203
  36. data/lib/write_xlsx/version.rb +1 -1
  37. data/lib/write_xlsx/workbook.rb +87 -175
  38. data/lib/write_xlsx/worksheet/data_validation.rb +1 -1
  39. data/lib/write_xlsx/worksheet/hyperlink.rb +2 -2
  40. data/lib/write_xlsx/worksheet.rb +478 -484
  41. data/lib/write_xlsx/zip_file_utils.rb +1 -1
  42. data/write_xlsx.gemspec +3 -3
  43. metadata +11 -11
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class InsertedChart
6
+ include Writexlsx::Utility
7
+
8
+ attr_reader :row, :col, :chart, :x_offset, :y_offset
9
+ attr_reader :x_scale, :y_scale
10
+ attr_reader :anchor, :description, :decorative
11
+
12
+ def initialize(
13
+ row, col, chart, x_offset, y_offset, x_scale, y_scale,
14
+ anchor, description, decorative
15
+ )
16
+ @row = row
17
+ @col = col
18
+ @chart = chart
19
+ @x_offset = x_offset
20
+ @y_offset = y_offset
21
+ @x_scale = x_scale || 0
22
+ @y_scale = y_scale || 0
23
+ @anchor = anchor
24
+ @description = description
25
+ @decorative = decorative
26
+ end
27
+
28
+ def name
29
+ chart.name
30
+ end
31
+
32
+ def scaled_width
33
+ width = chart.width if ptrue?(chart.width)
34
+ (0.5 + (width * x_scale)).to_i
35
+ end
36
+
37
+ def scaled_height
38
+ height = chart.height if ptrue?(chart.height)
39
+ (0.5 + (height * y_scale)).to_i
40
+ end
41
+ end
42
+ end
@@ -8,13 +8,66 @@ module Writexlsx
8
8
  class Button
9
9
  include Writexlsx::Utility
10
10
 
11
- attr_accessor :font, :macro, :vertices, :description
11
+ def initialize(worksheet, row, col, params, default_row_pixels, button_number)
12
+ @worksheet = worksheet
13
+ @default_row_pixels = default_row_pixels
14
+
15
+ # Set the button caption.
16
+ caption = params[:caption] || "Button #{button_number}"
17
+
18
+ @font = { _caption: caption }
19
+
20
+ # Set the macro name.
21
+ @macro = if params[:macro]
22
+ "[0]!#{params[:macro]}"
23
+ else
24
+ "[0]!Button#{button_number}_Click"
25
+ end
26
+
27
+ # Set the alt text for the button.
28
+ @description = params[:description]
29
+
30
+ # Ensure that a width and height have been set.
31
+ default_height = @default_row_pixels
32
+ width = params[:width] || DEFAULT_COL_PIXELS
33
+ height = params[:height] || default_row_pixels
34
+
35
+ # Scale the size of the button box if required.
36
+ width *= params[:x_scale] if params[:x_scale]
37
+ height *= params[:y_scale] if params[:y_scale]
38
+
39
+ # Round the dimensions to the nearest pixel.
40
+ width = (0.5 + width).to_i
41
+ height = (0.5 + height).to_i
42
+
43
+ # Set the x/y offsets.
44
+ x_offset = params[:x_offset] || 0
45
+ y_offset = params[:y_offset] || 0
46
+
47
+ start_row = row
48
+ start_col = col
49
+
50
+ # Calculate the positions of button object.
51
+ vertices = @worksheet.position_object_pixels(
52
+ start_col,
53
+ start_row,
54
+ x_offset,
55
+ y_offset,
56
+ width,
57
+ height
58
+ )
59
+
60
+ # Add the width and height for VML.
61
+ vertices << [width, height]
62
+
63
+ @vertices = vertices
64
+ end
12
65
 
13
66
  def v_shape_attributes(id, z_index)
14
67
  attributes = v_shape_attributes_base(id)
15
- attributes << ['alt', description] if description
68
+ attributes << ['alt', @description] if @description
16
69
 
17
- attributes << ['style', (v_shape_style_base(z_index, vertices) + style_addition).join]
70
+ attributes << ['style', (v_shape_style_base(z_index, @vertices) + style_addition).join]
18
71
  attributes << ['o:button', 't']
19
72
  attributes << ['fillcolor', color]
20
73
  attributes << ['strokecolor', 'windowText [64]']
@@ -81,7 +134,7 @@ module Writexlsx
81
134
 
82
135
  @writer.tag_elements('v:textbox', attributes) do
83
136
  # Write the div element.
84
- write_div('center', font)
137
+ write_div('center', @font)
85
138
  end
86
139
  end
87
140
 
@@ -118,7 +171,7 @@ module Writexlsx
118
171
  # Write the <x:FmlaMacro> element.
119
172
  #
120
173
  def write_fmla_macro
121
- @writer.data_element('x:FmlaMacro', macro)
174
+ @writer.data_element('x:FmlaMacro', @macro)
122
175
  end
123
176
 
124
177
  #
@@ -409,7 +409,7 @@ module Writexlsx
409
409
 
410
410
  def row_col_param_for_conditional_formatting(*args)
411
411
  # Check for a cell reference in A1 notation and substitute row and column
412
- user_range = if args[0].to_s =~ (/^\D/) && (args[0] =~ /,/)
412
+ user_range = if args[0].to_s =~ /^\D/ && (args[0] =~ /,/)
413
413
  # Check for a user defined multiple range like B3:K6,B8:K11.
414
414
  args[0].sub(/^=/, '').gsub(/\s*,\s*/, ' ').gsub("$", '')
415
415
  end
@@ -516,7 +516,7 @@ module Writexlsx
516
516
  end
517
517
 
518
518
  # 'Between' and 'Not between' criteria require 2 values.
519
- if param[:criteria] == 'between' || param[:criteria] == 'notBetween'
519
+ if %w[between notBetween].include?(param[:criteria])
520
520
  raise WriteXLSXOptionParameterError, "Invalid criteria : #{param[:criteria]}" unless param.has_key?(:minimum) || param.has_key?(:maximum)
521
521
  else
522
522
  param[:minimum] = nil
@@ -524,7 +524,7 @@ module Writexlsx
524
524
  end
525
525
 
526
526
  # Convert date/times value if required.
527
- raise WriteXLSXOptionParameterError if (param[:type] == 'date' || param[:type] == 'time') && !(convert_date_time_value(param, :value) || convert_date_time_value(param, :maximum))
527
+ raise WriteXLSXOptionParameterError if %w[date time].include?(param[:type]) && !(convert_date_time_value(param, :value) || convert_date_time_value(param, :maximum))
528
528
  end
529
529
 
530
530
  def convert_date_time_if_required(val)
@@ -697,7 +697,7 @@ module Writexlsx
697
697
  max_data = user_props.size
698
698
  max_data = total_icons - 1 if max_data >= total_icons
699
699
 
700
- (0..max_data - 1).each do |i|
700
+ (0..(max_data - 1)).each do |i|
701
701
  # Set the user defined 'value' property.
702
702
  props[i][:value] = user_props[i][:value].to_s.sub(/^=/, '') if user_props[i][:value]
703
703
 
@@ -203,11 +203,9 @@ module Writexlsx
203
203
  # Write the rdrichvalue(*).xml file.
204
204
  #
205
205
  def write_rich_value_files
206
- dir = @package_dir
207
-
208
206
  return unless @workbook.has_embedded_images?
209
207
 
210
- FileUtils.mkdir_p("#{dir}/xl/richData")
208
+ FileUtils.mkdir_p("#{@package_dir}/xl/richData")
211
209
 
212
210
  write_rich_value_file
213
211
  write_rich_value_structure_file
@@ -219,12 +217,11 @@ module Writexlsx
219
217
  # Write the rdrichvalue.xml file.
220
218
  #
221
219
  def write_rich_value_file
222
- dir = @package_dir
223
220
  rich_value = Package::RichValue.new
224
221
 
225
222
  rich_value.embedded_images = @workbook.embedded_images
226
223
 
227
- rich_value.set_xml_writer("#{dir}/xl/richData/rdrichvalue.xml")
224
+ rich_value.set_xml_writer("#{@package_dir}/xl/richData/rdrichvalue.xml")
228
225
  rich_value.assemble_xml_file
229
226
  end
230
227
 
@@ -232,12 +229,11 @@ module Writexlsx
232
229
  # Write the rdrichvaluestructure.xml file.
233
230
  #
234
231
  def write_rich_value_structure_file
235
- dir = @package_dir
236
232
  rich_value = Package::RichValueStructure.new
237
233
 
238
234
  rich_value.has_embedded_descriptions = @workbook.has_embedded_descriptions?
239
235
 
240
- rich_value.set_xml_writer("#{dir}/xl/richData/rdrichvaluestructure.xml")
236
+ rich_value.set_xml_writer("#{@package_dir}/xl/richData/rdrichvaluestructure.xml")
241
237
  rich_value.assemble_xml_file
242
238
  end
243
239
 
@@ -246,9 +242,8 @@ module Writexlsx
246
242
  #
247
243
  def write_rich_value_types_file
248
244
  rich_value = Package::RichValueTypes.new
249
- dir = @package_dir
250
245
 
251
- rich_value.set_xml_writer("#{dir}/xl/richData/rdRichValueTypes.xml")
246
+ rich_value.set_xml_writer("#{@package_dir}/xl/richData/rdRichValueTypes.xml")
252
247
  rich_value.assemble_xml_file
253
248
  end
254
249
 
@@ -256,12 +251,11 @@ module Writexlsx
256
251
  # Write the rdrichvalue.xml file.
257
252
  #
258
253
  def write_rich_value_rel
259
- dir = @package_dir
260
254
  rich_value = Package::RichValueRel.new
261
255
 
262
256
  rich_value.value_count = @workbook.embedded_images.size
263
257
 
264
- rich_value.set_xml_writer("#{dir}/xl/richData/richValueRel.xml")
258
+ rich_value.set_xml_writer("#{@package_dir}/xl/richData/richValueRel.xml")
265
259
  rich_value.assemble_xml_file
266
260
  end
267
261
 
@@ -456,22 +450,21 @@ module Writexlsx
456
450
  def write_rich_value_rels_files
457
451
  return if @workbook.embedded_images.empty?
458
452
 
459
- dir = @package_dir
460
-
461
453
  # Create the .rels dirs.
462
- FileUtils.mkdir_p("#{dir}/xl/richData/_rels")
454
+ rich_value_rels_dir = "#{@package_dir}/xl/richData/_rels"
455
+ FileUtils.mkdir_p(rich_value_rels_dir)
463
456
 
464
457
  rels = Package::Relationships.new
465
458
 
466
459
  @workbook.embedded_images.each_with_index do |image_data, index|
467
- file_type = image_data[1]
460
+ file_type = image_data.type
468
461
  image_file = "../media/image#{index + 1}.#{file_type}"
469
462
 
470
463
  rels.add_worksheet_relationship('/image', image_file)
471
464
  end
472
465
 
473
466
  # Create the .rels file.
474
- rels.set_xml_writer("#{dir}/xl/richData/_rels/richValueRel.xml.rels")
467
+ rels.set_xml_writer("#{rich_value_rels_dir}/richValueRel.xml.rels")
475
468
  rels.assemble_xml_file
476
469
  end
477
470
 
@@ -481,32 +474,34 @@ module Writexlsx
481
474
  def add_image_files
482
475
  index = 1
483
476
 
484
- dir = "#{@package_dir}/xl/media"
485
-
486
477
  @workbook.embedded_images.each do |image|
487
- FileUtils.mkdir_p(dir)
488
- FileUtils.cp(image[0], "#{dir}/image#{index}.#{image[1]}")
489
- index += 1
478
+ index = write_image_x_xml_files(image, index)
490
479
  end
491
480
 
492
481
  @workbook.images.each do |image|
493
- FileUtils.mkdir_p(dir)
494
- FileUtils.cp(image[0], "#{dir}/image#{index}.#{image[1]}")
495
- index += 1
482
+ index = write_image_x_xml_files(image, index)
496
483
  end
497
484
  end
498
485
 
486
+ def write_image_x_xml_files(image, index)
487
+ FileUtils.mkdir_p("#{@package_dir}/xl/media")
488
+ FileUtils.cp(
489
+ image.filename,
490
+ "#{@package_dir}/xl/media/image#{index}.#{image.type}"
491
+ )
492
+ index + 1
493
+ end
494
+
499
495
  #
500
496
  # Write the vbaProject.bin file.
501
497
  #
502
498
  def add_vba_project
503
- dir = @package_dir
504
499
  vba_project = @workbook.vba_project
505
500
 
506
501
  return unless vba_project
507
502
 
508
- FileUtils.mkdir_p("#{dir}/xl")
509
- FileUtils.copy(vba_project, "#{dir}/xl/vbaProject.bin")
503
+ FileUtils.mkdir_p("#{@package_dir}/xl")
504
+ FileUtils.copy(vba_project, "#{@package_dir}/xl/vbaProject.bin")
510
505
  end
511
506
  end
512
507
  end
@@ -38,7 +38,7 @@ module Writexlsx
38
38
 
39
39
  @writer.tag_elements('rvData', attributes) do
40
40
  @embedded_images.each_with_index do |image, index|
41
- write_rv(index, image[2], image[3])
41
+ write_rv(index, image.description, image.decorative)
42
42
  end
43
43
  end
44
44
  end
@@ -62,7 +62,7 @@ module Writexlsx
62
62
  elsif index == 0x40
63
63
  "Automatic"
64
64
  else
65
- "FF#{super(index)}"
65
+ "FF#{palette_color_from_index(index)}"
66
66
  end
67
67
  end
68
68
 
@@ -265,21 +265,10 @@ module Writexlsx
265
265
  #
266
266
  # Write the <v:shape> element.
267
267
  #
268
- def write_image_shape(id, index, image_data)
269
- type = '#_x0000_t75'
270
-
271
- # Get the image parameters
272
- width = image_data[0]
273
- height = image_data[1]
274
- name = image_data[2]
275
- position = image_data[3]
276
- x_dpi = image_data[4]
277
- y_dpi = image_data[5]
278
- ref_id = image_data[6]
279
-
268
+ def write_image_shape(id, index, image_property)
280
269
  # Scale the height/width by the resolution, relative to 72dpi.
281
- width = width * 72.0 / x_dpi
282
- height = height * 72.0 / y_dpi
270
+ width = image_property.width * 72.0 / image_property.x_dpi
271
+ height = image_property.height * 72.0 / image_property.y_dpi
283
272
 
284
273
  # Excel uses a rounding based around 72 and 96 dpi.
285
274
  width = 72 / 96.0 * ((width * 96 / 72.0) + 0.25).to_i
@@ -288,13 +277,15 @@ module Writexlsx
288
277
  width = width.to_i if (width - width.to_i).abs < 0.1
289
278
  height = height.to_i if (height - height.to_i).abs < 0.1
290
279
 
280
+ type = '#_x0000_t75'
281
+
291
282
  style = [
292
283
  "position:absolute", "margin-left:0", "margin-top:0",
293
284
  "width:#{width}pt", "height:#{height}pt",
294
285
  "z-index:#{index}"
295
286
  ].join(';')
296
287
  attributes = [
297
- ['id', position],
288
+ ['id', image_property.position],
298
289
  ['o:spid', "_x0000_s#{id}"],
299
290
  ['type', type],
300
291
  ['style', style]
@@ -302,7 +293,7 @@ module Writexlsx
302
293
 
303
294
  @writer.tag_elements('v:shape', attributes) do
304
295
  # Write the v:imagedata element.
305
- write_imagedata(ref_id, name)
296
+ write_imagedata(image_property)
306
297
 
307
298
  # Write the o:lock element.
308
299
  write_rotation_lock
@@ -312,10 +303,10 @@ module Writexlsx
312
303
  #
313
304
  # Write the <v:imagedata> element.
314
305
  #
315
- def write_imagedata(index, o_title)
306
+ def write_imagedata(image_property)
316
307
  attributes = [
317
- ['o:relid', "rId#{index}"],
318
- ['o:title', o_title]
308
+ ['o:relid', "rId#{image_property.ref_id}"],
309
+ ['o:title', image_property.body]
319
310
  ]
320
311
 
321
312
  @writer.empty_tag('v:imagedata', attributes)
@@ -150,8 +150,9 @@ module Writexlsx
150
150
  # Calculate the vertices that define the position of a shape object within
151
151
  # the worksheet in EMUs. Save the vertices with the object.
152
152
  #
153
- # The vertices are expressed as English Metric Units (EMUs). There are 12,700
154
- # EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel.
153
+ # The vertices are expressed as English Metric Units (EMUs).
154
+ # There are 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525
155
+ # EMUs per pixel.
155
156
  #
156
157
  def calc_position_emus(worksheet)
157
158
  c_start, r_start,
@@ -246,7 +246,7 @@ module Writexlsx
246
246
  def write_sparklines # :nodoc:
247
247
  # Write the sparkline elements.
248
248
  @writer.tag_elements('x14:sparklines') do
249
- (0..count - 1).each do |i|
249
+ (0..(count - 1)).each do |i|
250
250
  range = @ranges[i]
251
251
  location = @locations[i]
252
252
 
@@ -27,6 +27,9 @@ module Writexlsx
27
27
  't' => 5, 'u' => 8, 'v' => 7, 'w' => 11, 'x' => 7, 'y' => 7,
28
28
  'z' => 6, '{' => 5, '|' => 7, '}' => 5, '~' => 7
29
29
  }.freeze
30
+ MAX_DIGIT_WIDTH = 7 # For Calabri 11. # :nodoc:
31
+ PADDING = 5 # :nodoc:
32
+ DEFAULT_COL_PIXELS = 64
30
33
 
31
34
  #
32
35
  # xl_rowcol_to_cell($row, col, row_absolute, col_absolute)
@@ -169,7 +172,7 @@ module Writexlsx
169
172
  if time =~ /^(\d\d):(\d\d)(:(\d\d(\.\d+)?))?/
170
173
  hour = ::Regexp.last_match(1).to_i
171
174
  min = ::Regexp.last_match(2).to_i
172
- sec = ::Regexp.last_match(4).to_f || 0
175
+ sec = ::Regexp.last_match(4).to_f
173
176
  else
174
177
  return nil # Not a valid time format.
175
178
  end
@@ -226,7 +229,7 @@ module Writexlsx
226
229
 
227
230
  # Accumulate the number of days since the epoch.
228
231
  days = day # Add days for current month
229
- (0..month - 2).each do |m|
232
+ (0..(month - 2)).each do |m|
230
233
  days += mdays[m] # Add days for past months
231
234
  end
232
235
  days += range * 365 # Add days for past years
@@ -552,7 +555,7 @@ module Writexlsx
552
555
  # Write the <x:Anchor> element.
553
556
  #
554
557
  def write_anchor
555
- col_start, row_start, x1, y1, col_end, row_end, x2, y2 = vertices
558
+ col_start, row_start, x1, y1, col_end, row_end, x2, y2 = @vertices
556
559
  data = [col_start, x1, row_start, y1, col_end, x2, row_end, y2].join(', ')
557
560
 
558
561
  @writer.data_element('x:Anchor', data)
@@ -749,7 +752,7 @@ module Writexlsx
749
752
  hash[key.to_sym]
750
753
  end
751
754
 
752
- def palette_color(index)
755
+ def palette_color_from_index(index)
753
756
  # Adjust the colour index.
754
757
  idx = index - 8
755
758
 
@@ -757,32 +760,6 @@ module Writexlsx
757
760
  sprintf("%02X%02X%02X", r, g, b)
758
761
  end
759
762
 
760
- #
761
- # Workbook の生成時のオプションハッシュを解析する
762
- #
763
- def process_workbook_options(*params)
764
- case params.size
765
- when 0
766
- [{}, {}]
767
- when 1 # one hash
768
- options_keys = %i[tempdir date_1904 optimization excel2003_style strings_to_urls]
769
-
770
- hash = params.first
771
- options = hash.reject { |k, _v| !options_keys.include?(k) }
772
-
773
- default_format_properties =
774
- hash[:default_format_properties] ||
775
- hash.reject { |k, _v| options_keys.include?(k) }
776
-
777
- [options, default_format_properties.dup]
778
- when 2 # array which includes options and default_format_properties
779
- options, default_format_properties = params
780
- default_format_properties ||= {}
781
-
782
- [options.dup, default_format_properties.dup]
783
- end
784
- end
785
-
786
763
  #
787
764
  # Convert user defined font values into private hash values.
788
765
  #
@@ -960,7 +937,7 @@ module Writexlsx
960
937
  index = Format.color(color_code)
961
938
  raise "Unknown color '#{color_code}' used in chart formatting." unless index
962
939
 
963
- palette_color(index)
940
+ palette_color_from_index(index)
964
941
  end
965
942
  end
966
943
 
@@ -988,178 +965,6 @@ module Writexlsx
988
965
  def write_a_end_para_rpr # :nodoc:
989
966
  @writer.empty_tag('a:endParaRPr', [%w[lang en-US]])
990
967
  end
991
-
992
- #
993
- # Extract information from the image file such as dimension, type, filename,
994
- # and extension. Also keep track of previously seen images to optimise out
995
- # any duplicates.
996
- #
997
- def get_image_properties(filename)
998
- # Note the image_id, and previous_images mechanism isn't currently used.
999
- x_dpi = 96
1000
- y_dpi = 96
1001
-
1002
- workbook = @workbook || self
1003
-
1004
- # Open the image file and import the data.
1005
- data = File.binread(filename)
1006
- md5 = Digest::MD5.hexdigest(data)
1007
- if data.unpack1('x A3') == 'PNG'
1008
- # Test for PNGs.
1009
- type, width, height, x_dpi, y_dpi = process_png(data)
1010
- workbook.image_types[:png] = 1
1011
- elsif data.unpack1('n') == 0xFFD8
1012
- # Test for JPEG files.
1013
- type, width, height, x_dpi, y_dpi = process_jpg(data, filename)
1014
- workbook.image_types[:jpeg] = 1
1015
- elsif data.unpack1('A4') == 'GIF8'
1016
- # Test for GIFs.
1017
- type, width, height, x_dpi, y_dpi = process_gif(data, filename)
1018
- workbook.image_types[:gif] = 1
1019
- elsif data.unpack1('A2') == 'BM'
1020
- # Test for BMPs.
1021
- type, width, height = process_bmp(data, filename)
1022
- workbook.image_types[:bmp] = 1
1023
- else
1024
- # TODO. Add Image::Size to support other types.
1025
- raise "Unsupported image format for file: #{filename}\n"
1026
- end
1027
-
1028
- # Set a default dpi for images with 0 dpi.
1029
- x_dpi = 96 if x_dpi == 0
1030
- y_dpi = 96 if y_dpi == 0
1031
-
1032
- [type, width, height, File.basename(filename), x_dpi, y_dpi, md5]
1033
- end
1034
-
1035
- #
1036
- # Extract width and height information from a PNG file.
1037
- #
1038
- def process_png(data)
1039
- type = 'png'
1040
- width = 0
1041
- height = 0
1042
- x_dpi = 96
1043
- y_dpi = 96
1044
-
1045
- offset = 8
1046
- data_length = data.size
1047
-
1048
- # Search through the image data to read the height and width in th the
1049
- # IHDR element. Also read the DPI in the pHYs element.
1050
- while offset < data_length
1051
-
1052
- length = data[offset + 0, 4].unpack1("N")
1053
- png_type = data[offset + 4, 4].unpack1("A4")
1054
-
1055
- case png_type
1056
- when "IHDR"
1057
- width = data[offset + 8, 4].unpack1("N")
1058
- height = data[offset + 12, 4].unpack1("N")
1059
- when "pHYs"
1060
- x_ppu = data[offset + 8, 4].unpack1("N")
1061
- y_ppu = data[offset + 12, 4].unpack1("N")
1062
- units = data[offset + 16, 1].unpack1("C")
1063
-
1064
- if units == 1
1065
- x_dpi = x_ppu * 0.0254
1066
- y_dpi = y_ppu * 0.0254
1067
- end
1068
- end
1069
-
1070
- offset = offset + length + 12
1071
-
1072
- break if png_type == "IEND"
1073
- end
1074
- raise "#{filename}: no size data found in png image.\n" unless height
1075
-
1076
- [type, width, height, x_dpi, y_dpi]
1077
- end
1078
-
1079
- def process_jpg(data, filename)
1080
- type = 'jpeg'
1081
- x_dpi = 96
1082
- y_dpi = 96
1083
-
1084
- offset = 2
1085
- data_length = data.bytesize
1086
-
1087
- # Search through the image data to read the JPEG markers.
1088
- while offset < data_length
1089
- marker = data[offset + 0, 2].unpack1("n")
1090
- length = data[offset + 2, 2].unpack1("n")
1091
-
1092
- # Read the height and width in the 0xFFCn elements
1093
- # (Except C4, C8 and CC which aren't SOF markers).
1094
- if (marker & 0xFFF0) == 0xFFC0 &&
1095
- marker != 0xFFC4 && marker != 0xFFCC
1096
- height = data[offset + 5, 2].unpack1("n")
1097
- width = data[offset + 7, 2].unpack1("n")
1098
- end
1099
-
1100
- # Read the DPI in the 0xFFE0 element.
1101
- if marker == 0xFFE0
1102
- units = data[offset + 11, 1].unpack1("C")
1103
- x_density = data[offset + 12, 2].unpack1("n")
1104
- y_density = data[offset + 14, 2].unpack1("n")
1105
-
1106
- if units == 1
1107
- x_dpi = x_density
1108
- y_dpi = y_density
1109
- elsif units == 2
1110
- x_dpi = x_density * 2.54
1111
- y_dpi = y_density * 2.54
1112
- end
1113
- end
1114
-
1115
- offset += length + 2
1116
- break if marker == 0xFFDA
1117
- end
1118
-
1119
- raise "#{filename}: no size data found in jpeg image.\n" unless height
1120
-
1121
- [type, width, height, x_dpi, y_dpi]
1122
- end
1123
-
1124
- #
1125
- # Extract width and height information from a GIF file.
1126
- #
1127
- def process_gif(data, filename)
1128
- type = 'gif'
1129
- x_dpi = 96
1130
- y_dpi = 96
1131
-
1132
- width = data[6, 2].unpack1("v")
1133
- height = data[8, 2].unpack1("v")
1134
-
1135
- raise "#{filename}: no size data found in gif image.\n" if height.nil?
1136
-
1137
- [type, width, height, x_dpi, y_dpi]
1138
- end
1139
-
1140
- # Extract width and height information from a BMP file.
1141
- def process_bmp(data, filename) # :nodoc:
1142
- type = 'bmp'
1143
-
1144
- # Check that the file is big enough to be a bitmap.
1145
- raise "#{filename} doesn't contain enough data." if data.bytesize <= 0x36
1146
-
1147
- # Read the bitmap width and height. Verify the sizes.
1148
- width, height = data.unpack("x18 V2")
1149
- raise "#{filename}: largest image width #{width} supported is 65k." if width > 0xFFFF
1150
- raise "#{filename}: largest image height supported is 65k." if height > 0xFFFF
1151
-
1152
- # Read the bitmap planes and bpp data. Verify them.
1153
- planes, bitcount = data.unpack("x26 v2")
1154
- raise "#{filename} isn't a 24bit true color bitmap." unless bitcount == 24
1155
- raise "#{filename}: only 1 plane supported in bitmap image." unless planes == 1
1156
-
1157
- # Read the bitmap compression. Verify compression.
1158
- compression = data.unpack1("x30 V")
1159
- raise "#{filename}: compression not supported in bitmap image." unless compression == 0
1160
-
1161
- [type, width, height]
1162
- end
1163
968
  end
1164
969
 
1165
970
  module WriteDPtPoint
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- WriteXLSX_VERSION = "1.12.1"
3
+ WriteXLSX_VERSION = "1.12.2"