write_xlsx 1.11.2 → 1.12.1

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.
@@ -0,0 +1,70 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'write_xlsx/package/xml_writer_simple'
5
+ require 'write_xlsx/utility'
6
+
7
+ module Writexlsx
8
+ module Package
9
+ #
10
+ # RichValueRel - A class for writing the Excel XLSX richValueRel.xml file.
11
+ #
12
+ # Used in conjunction with Excel::Writer::XLSX
13
+ #
14
+ # Copyright 2000-2024, John McNamara, jmcnamara@cpan.org
15
+ #
16
+ # Convert to Ruby by Hideo Nakamura, nakamura.hideo@gmail.com
17
+ #
18
+ class RichValueRel
19
+ include Writexlsx::Utility
20
+
21
+ attr_writer :value_count
22
+
23
+ def initialize
24
+ @writer = Package::XMLWriterSimple.new
25
+ @value_count = 0
26
+ end
27
+
28
+ def set_xml_writer(filename)
29
+ @writer.set_xml_writer(filename)
30
+ end
31
+
32
+ def assemble_xml_file
33
+ write_xml_declaration do
34
+ write_rich_value_rels
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ #
41
+ # Write the <richValueRels> element.
42
+ #
43
+ def write_rich_value_rels
44
+ xmlns = 'http://schemas.microsoft.com/office/spreadsheetml/2022/richvaluerel'
45
+ xmlns_r = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
46
+
47
+ attributes = [
48
+ ['xmlns', xmlns],
49
+ ['xmlns:r', xmlns_r]
50
+ ]
51
+
52
+ @writer.tag_elements('richValueRels', attributes) do
53
+ (0..(@value_count - 1)).each do |index|
54
+ # Write the rel element.
55
+ write_rel(index + 1)
56
+ end
57
+ end
58
+ end
59
+
60
+ #
61
+ # Write the <rel> element.
62
+ #
63
+ def write_rel(id)
64
+ attributes = [['r:id', "rId#{id}"]]
65
+
66
+ @writer.empty_tag('rel', attributes)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'write_xlsx/package/xml_writer_simple'
5
+ require 'write_xlsx/utility'
6
+
7
+ module Writexlsx
8
+ module Package
9
+ #
10
+ # RichValueStructure - A class for writing the Excel XLSX rdrichvaluestructure.xml
11
+ # file.
12
+ #
13
+ # Used in conjunction with Excel::Writer::XLSX
14
+ #
15
+ # Copyright 2000-2024, John McNamara, jmcnamara@cpan.org
16
+ #
17
+ # Convert to Ruby by Hideo NAKAMURA, nakamura.hideo@gmail.com
18
+ #
19
+ class RichValueStructure
20
+ include Writexlsx::Utility
21
+
22
+ attr_writer :has_embedded_descriptions
23
+
24
+ def initialize
25
+ @writer = Package::XMLWriterSimple.new
26
+ @has_embedded_descriptions = false
27
+ end
28
+
29
+ def set_xml_writer(filename)
30
+ @writer.set_xml_writer(filename)
31
+ end
32
+
33
+ def assemble_xml_file
34
+ write_xml_declaration do
35
+ write_rv_structures
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ #
42
+ # Write the <rvStructures> element.
43
+ #
44
+ def write_rv_structures
45
+ xmlns = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata'
46
+
47
+ attributes = [
48
+ ['xmlns', xmlns],
49
+ ['count', 1]
50
+ ]
51
+
52
+ @writer.tag_elements('rvStructures', attributes) do
53
+ write_s
54
+ end
55
+ end
56
+
57
+ #
58
+ # Write the <s> element.
59
+ #
60
+ def write_s
61
+ attributes = [%w[t _localImage]]
62
+
63
+ @writer.tag_elements('s', attributes) do
64
+ write_k('_rvRel:LocalImageIdentifier', 'i')
65
+ write_k('CalcOrigin', 'i')
66
+ write_k('Text', 's') if @has_embedded_descriptions
67
+ end
68
+ end
69
+
70
+ #
71
+ # Write the <k> element.
72
+ #
73
+ def write_k(n, t)
74
+ attributes = [
75
+ ['n', n],
76
+ ['t', t]
77
+ ]
78
+
79
+ @writer.empty_tag('k', attributes)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,103 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'write_xlsx/package/xml_writer_simple'
5
+ require 'write_xlsx/utility'
6
+
7
+ module Writexlsx
8
+ module Package
9
+ #
10
+ # RichValueTypes - A class for writing the Excel XLSX rdRichValueTypes.xml file.
11
+ #
12
+ # Used in conjunction with Excel::Writer::XLSX
13
+ #
14
+ # Copyright 2000-2024, John McNamara, jmcnamara@cpan.org
15
+ #
16
+ # Convert to Ruby by Hideo NAKAMURA, nakamura.hideo@gmail.com
17
+ #
18
+ class RichValueTypes
19
+ include Writexlsx::Utility
20
+
21
+ def initialize
22
+ @writer = Package::XMLWriterSimple.new
23
+ end
24
+
25
+ def set_xml_writer(filename)
26
+ @writer.set_xml_writer(filename)
27
+ end
28
+
29
+ def assemble_xml_file
30
+ write_xml_declaration do
31
+ write_rv_types_info
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ #
38
+ # Write the <rvTypesInfo> element.
39
+ #
40
+ def write_rv_types_info
41
+ xmlns = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata2'
42
+ xmlns_mc = 'http://schemas.openxmlformats.org/markup-compatibility/2006'
43
+ xmlns_x = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
44
+
45
+ attributes = [
46
+ ['xmlns', xmlns],
47
+ ['xmlns:mc', xmlns_mc],
48
+ ['mc:Ignorable', 'x'],
49
+ ['xmlns:x', xmlns_x]
50
+ ]
51
+
52
+ key_flags = [
53
+ ['_Self', %w[ExcludeFromFile ExcludeFromCalcComparison]],
54
+ ['_DisplayString', ['ExcludeFromCalcComparison']],
55
+ ['_Flags', ['ExcludeFromCalcComparison']],
56
+ ['_Format', ['ExcludeFromCalcComparison']],
57
+ ['_SubLabel', ['ExcludeFromCalcComparison']],
58
+ ['_Attribution', ['ExcludeFromCalcComparison']],
59
+ ['_Icon', ['ExcludeFromCalcComparison']],
60
+ ['_Display', ['ExcludeFromCalcComparison']],
61
+ ['_CanonicalPropertyNames', ['ExcludeFromCalcComparison']],
62
+ ['_ClassificationId', ['ExcludeFromCalcComparison']]
63
+ ]
64
+
65
+ @writer.tag_elements('rvTypesInfo', attributes) do
66
+ @writer.tag_elements('global') do
67
+ @writer.tag_elements('keyFlags') do
68
+ # Write the keyFlags element.
69
+ key_flags.each do |key_flag|
70
+ write_key(key_flag[0], key_flag[1])
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ #
78
+ # Write the <key> element.
79
+ #
80
+ def write_key(name, flags = [])
81
+ attributes = [['name', name]]
82
+
83
+ @writer.tag_elements('key', attributes) do
84
+ flags.each do |flag|
85
+ write_flag(flag)
86
+ end
87
+ end
88
+ end
89
+
90
+ #
91
+ # Write the <flag> element.
92
+ #
93
+ def write_flag(name)
94
+ attributes = [
95
+ ['name', name],
96
+ ['value', 1]
97
+ ]
98
+
99
+ @writer.empty_tag('flag', attributes)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -988,6 +988,178 @@ module Writexlsx
988
988
  def write_a_end_para_rpr # :nodoc:
989
989
  @writer.empty_tag('a:endParaRPr', [%w[lang en-US]])
990
990
  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
991
1163
  end
992
1164
 
993
1165
  module WriteDPtPoint
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- WriteXLSX_VERSION = "1.11.2"
3
+ WriteXLSX_VERSION = "1.12.1"