write_xlsx 1.11.2 → 1.12.1

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