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.
- checksums.yaml +4 -4
- data/.rubocop.yml +12 -0
- data/Changes +3 -0
- data/LICENSE.txt +1 -1
- data/examples/autofilter.rb +1 -2
- data/examples/colors.rb +4 -4
- data/examples/formats.rb +14 -14
- data/lib/write_xlsx/chart/area.rb +1 -1
- data/lib/write_xlsx/chart/axis.rb +4 -4
- data/lib/write_xlsx/chart/bar.rb +1 -1
- data/lib/write_xlsx/chart/caption.rb +8 -4
- data/lib/write_xlsx/chart/column.rb +1 -1
- data/lib/write_xlsx/chart/doughnut.rb +2 -2
- data/lib/write_xlsx/chart/line.rb +1 -1
- data/lib/write_xlsx/chart/pie.rb +2 -2
- data/lib/write_xlsx/chart/radar.rb +1 -1
- data/lib/write_xlsx/chart/scatter.rb +1 -1
- data/lib/write_xlsx/chart/series.rb +10 -20
- data/lib/write_xlsx/chart/stock.rb +1 -1
- data/lib/write_xlsx/chart.rb +14 -21
- data/lib/write_xlsx/chartsheet.rb +3 -3
- data/lib/write_xlsx/drawing.rb +108 -114
- data/lib/write_xlsx/format.rb +20 -24
- data/lib/write_xlsx/image.rb +89 -0
- data/lib/write_xlsx/image_property.rb +163 -0
- data/lib/write_xlsx/inserted_chart.rb +42 -0
- data/lib/write_xlsx/package/button.rb +58 -5
- data/lib/write_xlsx/package/conditional_format.rb +4 -4
- data/lib/write_xlsx/package/packager.rb +22 -27
- data/lib/write_xlsx/package/rich_value.rb +1 -1
- data/lib/write_xlsx/package/styles.rb +1 -1
- data/lib/write_xlsx/package/vml.rb +10 -19
- data/lib/write_xlsx/shape.rb +3 -2
- data/lib/write_xlsx/sparkline.rb +1 -1
- data/lib/write_xlsx/utility.rb +8 -203
- data/lib/write_xlsx/version.rb +1 -1
- data/lib/write_xlsx/workbook.rb +87 -175
- data/lib/write_xlsx/worksheet/data_validation.rb +1 -1
- data/lib/write_xlsx/worksheet/hyperlink.rb +2 -2
- data/lib/write_xlsx/worksheet.rb +478 -484
- data/lib/write_xlsx/zip_file_utils.rb +1 -1
- data/write_xlsx.gemspec +3 -3
- metadata +11 -11
data/lib/write_xlsx/drawing.rb
CHANGED
@@ -6,8 +6,11 @@ require 'write_xlsx/utility'
|
|
6
6
|
|
7
7
|
module Writexlsx
|
8
8
|
class Drawing
|
9
|
+
include Writexlsx::Utility
|
10
|
+
|
9
11
|
attr_accessor :type, :dimensions, :width, :height, :shape, :anchor, :rel_index, :url_rel_index, :name, :description
|
10
12
|
attr_reader :tip, :decorative
|
13
|
+
attr_writer :writer, :embedded, :orientation
|
11
14
|
|
12
15
|
def initialize(type, dimensions, width, height, shape, anchor, rel_index = nil, url_rel_index = nil, tip = nil, name = nil, description = nil, decorative = nil)
|
13
16
|
@type = type
|
@@ -23,93 +26,11 @@ module Writexlsx
|
|
23
26
|
@description = description
|
24
27
|
@decorative = decorative
|
25
28
|
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class Drawings
|
29
|
-
include Writexlsx::Utility
|
30
|
-
|
31
|
-
attr_writer :embedded, :orientation
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@writer = Package::XMLWriterSimple.new
|
35
|
-
@drawings = []
|
36
|
-
@embedded = false
|
37
|
-
@orientation = false
|
38
|
-
end
|
39
|
-
|
40
|
-
def xml_str
|
41
|
-
@writer.string
|
42
|
-
end
|
43
|
-
|
44
|
-
def set_xml_writer(filename)
|
45
|
-
@writer.set_xml_writer(filename)
|
46
|
-
end
|
47
|
-
|
48
|
-
#
|
49
|
-
# Assemble and write the XML file.
|
50
|
-
#
|
51
|
-
def assemble_xml_file
|
52
|
-
write_xml_declaration do
|
53
|
-
# Write the xdr:wsDr element.
|
54
|
-
write_drawing_workspace do
|
55
|
-
if @embedded
|
56
|
-
index = 0
|
57
|
-
@drawings.each do |drawing|
|
58
|
-
# Write the xdr:twoCellAnchor element.
|
59
|
-
index += 1
|
60
|
-
write_two_cell_anchor(index, drawing)
|
61
|
-
end
|
62
|
-
else
|
63
|
-
# Write the xdr:absoluteAnchor element.
|
64
|
-
write_absolute_anchor(1)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
#
|
71
|
-
# Add a chart, image or shape sub object to the drawing.
|
72
|
-
#
|
73
|
-
def add_drawing_object(drawing)
|
74
|
-
@drawings << drawing
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
#
|
80
|
-
# Write the <xdr:wsDr> element.
|
81
|
-
#
|
82
|
-
def write_drawing_workspace(&block)
|
83
|
-
schema = 'http://schemas.openxmlformats.org/drawingml/'
|
84
|
-
attributes = [
|
85
|
-
['xmlns:xdr', "#{schema}2006/spreadsheetDrawing"],
|
86
|
-
['xmlns:a', "#{schema}2006/main"]
|
87
|
-
]
|
88
|
-
|
89
|
-
@writer.tag_elements('xdr:wsDr', attributes, &block)
|
90
|
-
end
|
91
29
|
|
92
30
|
#
|
93
31
|
# Write the <xdr:twoCellAnchor> element.
|
94
32
|
#
|
95
|
-
def write_two_cell_anchor(
|
96
|
-
index, drawing = args
|
97
|
-
|
98
|
-
type = drawing.type
|
99
|
-
width = drawing.width
|
100
|
-
height = drawing.height
|
101
|
-
shape = drawing.shape
|
102
|
-
anchor = drawing.anchor
|
103
|
-
rel_index = drawing.rel_index
|
104
|
-
url_rel_index = drawing.url_rel_index
|
105
|
-
tip = drawing.tip
|
106
|
-
name = drawing.name
|
107
|
-
description = drawing.description
|
108
|
-
decorative = drawing.decorative
|
109
|
-
|
110
|
-
col_from, row_from, col_from_offset, row_from_offset,
|
111
|
-
col_to, row_to, col_to_offset, row_to_offset, col_absolute, row_absolute = drawing.dimensions
|
112
|
-
|
33
|
+
def write_two_cell_anchor(index)
|
113
34
|
attributes = []
|
114
35
|
|
115
36
|
# Add attribute for images.
|
@@ -124,9 +45,9 @@ module Writexlsx
|
|
124
45
|
|
125
46
|
@writer.tag_elements('xdr:twoCellAnchor', attributes) do
|
126
47
|
# Write the xdr:from element.
|
127
|
-
write_from
|
48
|
+
write_from
|
128
49
|
# Write the xdr:to element.
|
129
|
-
write_to
|
50
|
+
write_to
|
130
51
|
|
131
52
|
if type == 1
|
132
53
|
# Graphic frame.
|
@@ -135,14 +56,10 @@ module Writexlsx
|
|
135
56
|
write_graphic_frame(index, rel_index, name, description, decorative)
|
136
57
|
elsif type == 2
|
137
58
|
# Write the xdr:pic element.
|
138
|
-
write_pic(
|
139
|
-
index, rel_index, col_absolute,
|
140
|
-
row_absolute, width, height,
|
141
|
-
description, url_rel_index, tip, decorative
|
142
|
-
)
|
59
|
+
write_pic(index)
|
143
60
|
else
|
144
61
|
# Write the xdr:sp element for shapes.
|
145
|
-
write_sp(index,
|
62
|
+
write_sp(index, shape)
|
146
63
|
end
|
147
64
|
|
148
65
|
# Write the xdr:clientData element.
|
@@ -182,7 +99,9 @@ module Writexlsx
|
|
182
99
|
#
|
183
100
|
# Write the <xdr:from> element.
|
184
101
|
#
|
185
|
-
def write_from
|
102
|
+
def write_from
|
103
|
+
col, row, col_offset, row_offset = dimensions
|
104
|
+
|
186
105
|
@writer.tag_elements('xdr:from') do
|
187
106
|
# Write the xdr:col element.
|
188
107
|
write_col(col)
|
@@ -198,7 +117,9 @@ module Writexlsx
|
|
198
117
|
#
|
199
118
|
# Write the <xdr:to> element.
|
200
119
|
#
|
201
|
-
def write_to
|
120
|
+
def write_to
|
121
|
+
col, row, col_offset, row_offset = dimensions[4, 4]
|
122
|
+
|
202
123
|
@writer.tag_elements('xdr:to') do
|
203
124
|
# Write the xdr:col element.
|
204
125
|
write_col(col)
|
@@ -290,7 +211,6 @@ module Writexlsx
|
|
290
211
|
def write_nv_graphic_frame_pr(
|
291
212
|
index, name = nil, description = nil, decorative = nil
|
292
213
|
)
|
293
|
-
|
294
214
|
name = "Chart #{index}" unless ptrue?(name)
|
295
215
|
|
296
216
|
@writer.tag_elements('xdr:nvGraphicFramePr') do
|
@@ -522,7 +442,7 @@ module Writexlsx
|
|
522
442
|
#
|
523
443
|
# Write the <xdr:sp> element.
|
524
444
|
#
|
525
|
-
def write_sp(index,
|
445
|
+
def write_sp(index, shape)
|
526
446
|
if shape.connect == 0
|
527
447
|
# Add attribute for shapes.
|
528
448
|
attributes = [
|
@@ -534,7 +454,7 @@ module Writexlsx
|
|
534
454
|
write_nv_sp_pr(index, shape)
|
535
455
|
|
536
456
|
# Write the xdr:spPr element.
|
537
|
-
write_xdr_sp_pr(
|
457
|
+
write_xdr_sp_pr(shape)
|
538
458
|
|
539
459
|
# Write the xdr:txBody element.
|
540
460
|
write_tx_body(shape) if shape.text != 0
|
@@ -546,7 +466,7 @@ module Writexlsx
|
|
546
466
|
write_nv_cxn_sp_pr(index, shape)
|
547
467
|
|
548
468
|
# Write the xdr:spPr element.
|
549
|
-
write_xdr_sp_pr(
|
469
|
+
write_xdr_sp_pr(shape)
|
550
470
|
end
|
551
471
|
end
|
552
472
|
end
|
@@ -601,26 +521,29 @@ module Writexlsx
|
|
601
521
|
#
|
602
522
|
# Write the <xdr:pic> element.
|
603
523
|
#
|
604
|
-
def write_pic(index
|
524
|
+
def write_pic(index)
|
525
|
+
col_absolute, row_absolute = dimensions[8, 2]
|
526
|
+
|
605
527
|
@writer.tag_elements('xdr:pic') do
|
606
528
|
# Write the xdr:nvPicPr element.
|
607
|
-
write_nv_pic_pr(index
|
529
|
+
write_nv_pic_pr(index)
|
608
530
|
# Write the xdr:blipFill element.
|
609
|
-
write_blip_fill
|
531
|
+
write_blip_fill
|
610
532
|
|
611
533
|
# Pictures are rectangle shapes by default.
|
612
|
-
|
613
|
-
|
534
|
+
pic_shape = Shape.new
|
535
|
+
pic_shape.type = 'rect'
|
614
536
|
|
615
537
|
# Write the xdr:spPr element.
|
616
|
-
write_sp_pr(
|
538
|
+
write_sp_pr(pic_shape)
|
617
539
|
end
|
618
540
|
end
|
619
541
|
|
620
542
|
#
|
621
543
|
# Write the <xdr:nvPicPr> element.
|
622
544
|
#
|
623
|
-
def write_nv_pic_pr(index, _rel_index, description, url_rel_index, tip, decorative)
|
545
|
+
# def write_nv_pic_pr(index, _rel_index, description, url_rel_index, tip, decorative)
|
546
|
+
def write_nv_pic_pr(index)
|
624
547
|
@writer.tag_elements('xdr:nvPicPr') do
|
625
548
|
# Write the xdr:cNvPr element.
|
626
549
|
write_c_nv_pr(
|
@@ -656,10 +579,10 @@ module Writexlsx
|
|
656
579
|
#
|
657
580
|
# Write the <xdr:blipFill> element.
|
658
581
|
#
|
659
|
-
def write_blip_fill
|
582
|
+
def write_blip_fill
|
660
583
|
@writer.tag_elements('xdr:blipFill') do
|
661
584
|
# Write the a:blip element.
|
662
|
-
write_a_blip(
|
585
|
+
write_a_blip(rel_index)
|
663
586
|
# Write the a:stretch element.
|
664
587
|
write_a_stretch
|
665
588
|
end
|
@@ -701,10 +624,10 @@ module Writexlsx
|
|
701
624
|
#
|
702
625
|
# Write the <xdr:spPr> element, for charts.
|
703
626
|
#
|
704
|
-
def write_sp_pr(
|
627
|
+
def write_sp_pr(shape)
|
705
628
|
@writer.tag_elements('xdr:spPr') do
|
706
629
|
# Write the a:xfrm element.
|
707
|
-
write_a_xfrm
|
630
|
+
write_a_xfrm
|
708
631
|
# Write the a:prstGeom element.
|
709
632
|
write_a_prst_geom(shape)
|
710
633
|
end
|
@@ -713,12 +636,12 @@ module Writexlsx
|
|
713
636
|
#
|
714
637
|
# Write the <xdr:spPr> element for shapes.
|
715
638
|
#
|
716
|
-
def write_xdr_sp_pr(
|
639
|
+
def write_xdr_sp_pr(shape)
|
717
640
|
attributes = [%w[bwMode auto]]
|
718
641
|
|
719
642
|
@writer.tag_elements('xdr:spPr', attributes) do
|
720
643
|
# Write the a:xfrm element.
|
721
|
-
write_a_xfrm(
|
644
|
+
write_a_xfrm(shape)
|
722
645
|
|
723
646
|
# Write the a:prstGeom element.
|
724
647
|
write_a_prst_geom(shape)
|
@@ -738,7 +661,7 @@ module Writexlsx
|
|
738
661
|
#
|
739
662
|
# Write the <a:xfrm> element.
|
740
663
|
#
|
741
|
-
def write_a_xfrm(
|
664
|
+
def write_a_xfrm(shape = nil)
|
742
665
|
attributes = []
|
743
666
|
|
744
667
|
rotation = shape ? shape.rotation : 0
|
@@ -750,7 +673,7 @@ module Writexlsx
|
|
750
673
|
|
751
674
|
@writer.tag_elements('a:xfrm', attributes) do
|
752
675
|
# Write the a:off element.
|
753
|
-
write_a_off
|
676
|
+
write_a_off
|
754
677
|
# Write the a:ext element.
|
755
678
|
write_a_ext(width, height)
|
756
679
|
end
|
@@ -759,7 +682,9 @@ module Writexlsx
|
|
759
682
|
#
|
760
683
|
# Write the <a:off> element.
|
761
684
|
#
|
762
|
-
def write_a_off
|
685
|
+
def write_a_off
|
686
|
+
x, y = dimensions[8, 2]
|
687
|
+
|
763
688
|
attributes = [
|
764
689
|
['x', x],
|
765
690
|
['y', y]
|
@@ -923,7 +848,7 @@ module Writexlsx
|
|
923
848
|
@writer.tag_elements('a:rPr', attributes) do
|
924
849
|
color = shape.format[:color]
|
925
850
|
if color
|
926
|
-
color = shape.
|
851
|
+
color = shape.palette_color_from_index(color)
|
927
852
|
color = color.sub(/^FF/, '') # Remove leading FF from rgb for shape color.
|
928
853
|
else
|
929
854
|
color = '000000'
|
@@ -944,4 +869,73 @@ module Writexlsx
|
|
944
869
|
end
|
945
870
|
end
|
946
871
|
end
|
872
|
+
|
873
|
+
class Drawings
|
874
|
+
include Writexlsx::Utility
|
875
|
+
|
876
|
+
attr_writer :embedded, :orientation
|
877
|
+
|
878
|
+
def initialize
|
879
|
+
@writer = Package::XMLWriterSimple.new
|
880
|
+
@drawings = []
|
881
|
+
@embedded = false
|
882
|
+
@orientation = false
|
883
|
+
end
|
884
|
+
|
885
|
+
def xml_str
|
886
|
+
@writer.string
|
887
|
+
end
|
888
|
+
|
889
|
+
def set_xml_writer(filename)
|
890
|
+
@writer.set_xml_writer(filename)
|
891
|
+
end
|
892
|
+
|
893
|
+
#
|
894
|
+
# Assemble and write the XML file.
|
895
|
+
#
|
896
|
+
def assemble_xml_file
|
897
|
+
write_xml_declaration do
|
898
|
+
# Write the xdr:wsDr element.
|
899
|
+
write_drawing_workspace do
|
900
|
+
if @embedded
|
901
|
+
@drawings.each_with_index do |drawing, index|
|
902
|
+
# Write the xdr:twoCellAnchor element.
|
903
|
+
drawing.writer = @writer
|
904
|
+
drawing.embedded = @embedded
|
905
|
+
drawing.write_two_cell_anchor(index + 1)
|
906
|
+
end
|
907
|
+
else
|
908
|
+
# Write the xdr:absoluteAnchor element.
|
909
|
+
drawing = Drawing.new(nil, nil, nil, nil, nil, nil)
|
910
|
+
drawing.writer = @writer
|
911
|
+
drawing.embedded = @embedded
|
912
|
+
drawing.orientation = @orientation
|
913
|
+
drawing.write_absolute_anchor(1)
|
914
|
+
end
|
915
|
+
end
|
916
|
+
end
|
917
|
+
end
|
918
|
+
|
919
|
+
#
|
920
|
+
# Add a chart, image or shape sub object to the drawing.
|
921
|
+
#
|
922
|
+
def add_drawing_object(drawing)
|
923
|
+
@drawings << drawing
|
924
|
+
end
|
925
|
+
|
926
|
+
private
|
927
|
+
|
928
|
+
#
|
929
|
+
# Write the <xdr:wsDr> element.
|
930
|
+
#
|
931
|
+
def write_drawing_workspace(&block)
|
932
|
+
schema = 'http://schemas.openxmlformats.org/drawingml/'
|
933
|
+
attributes = [
|
934
|
+
['xmlns:xdr', "#{schema}2006/spreadsheetDrawing"],
|
935
|
+
['xmlns:a', "#{schema}2006/main"]
|
936
|
+
]
|
937
|
+
|
938
|
+
@writer.tag_elements('xdr:wsDr', attributes, &block)
|
939
|
+
end
|
940
|
+
end
|
947
941
|
end
|
data/lib/write_xlsx/format.rb
CHANGED
@@ -9,10 +9,10 @@ module Writexlsx
|
|
9
9
|
|
10
10
|
attr_reader :xf_index, :dxf_index, :num_format # :nodoc:
|
11
11
|
attr_reader :underline, :font_script, :size, :theme, :font, :font_family, :hyperlink, :xf_id # :nodoc:
|
12
|
-
attr_reader :diag_type, :diag_color, :font_only, :color_indexed
|
12
|
+
attr_reader :diag_type, :diag_color, :font_only, :color_indexed # :nodoc:
|
13
13
|
attr_reader :left, :left_color, :right, :right_color, :top, :top_color, :bottom, :bottom_color # :nodoc:
|
14
14
|
attr_reader :font_scheme # :nodoc:
|
15
|
-
attr_accessor :quote_prefix, :num_format_index, :border_index, :font_index
|
15
|
+
attr_accessor :quote_prefix, :num_format_index, :border_index, :font_index # :nodoc:
|
16
16
|
attr_accessor :fill_index, :font_condense, :font_extend, :diag_border # :nodoc:
|
17
17
|
attr_accessor :bg_color, :fg_color, :pattern # :nodoc:
|
18
18
|
|
@@ -166,7 +166,7 @@ module Writexlsx
|
|
166
166
|
align << %w[horizontal right] if @text_h_align == 3
|
167
167
|
align << %w[horizontal fill] if @text_h_align == 4
|
168
168
|
align << %w[horizontal justify] if @text_h_align == 5
|
169
|
-
align << ['horizontal', continuous]
|
169
|
+
align << ['horizontal', continuous] if @text_h_align == 6
|
170
170
|
align << %w[horizontal distributed] if @text_h_align == 7
|
171
171
|
|
172
172
|
align << ['justifyLastLine', 1] if @just_distrib != 0
|
@@ -345,28 +345,24 @@ module Writexlsx
|
|
345
345
|
|
346
346
|
location = location.downcase
|
347
347
|
|
348
|
-
|
349
|
-
set_text_h_align(
|
350
|
-
|
351
|
-
set_text_h_align(3)
|
352
|
-
set_text_h_align(4)
|
353
|
-
set_text_h_align(5)
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
348
|
+
case location
|
349
|
+
when 'left' then set_text_h_align(1)
|
350
|
+
when 'centre', 'center' then set_text_h_align(2)
|
351
|
+
when 'right' then set_text_h_align(3)
|
352
|
+
when 'fill' then set_text_h_align(4)
|
353
|
+
when 'justify' then set_text_h_align(5)
|
354
|
+
when 'center_across', 'centre_across', 'merge'
|
355
|
+
set_text_h_align(6)
|
356
|
+
when 'distributed', 'equal_space', 'justify_distributed'
|
357
|
+
set_text_h_align(7)
|
358
|
+
when 'top' then set_text_v_align(1)
|
359
|
+
when 'vcentre', 'vcenter' then set_text_v_align(2)
|
360
|
+
when 'bottom' then set_text_v_align(3)
|
361
|
+
when 'vjustify' then set_text_v_align(4)
|
362
|
+
when 'vdistributed', 'vequal_space' then set_text_v_align(5)
|
363
|
+
end
|
360
364
|
|
361
365
|
@just_distrib = 1 if location == 'justify_distributed'
|
362
|
-
|
363
|
-
set_text_v_align(1) if location == 'top'
|
364
|
-
set_text_v_align(2) if location == 'vcentre'
|
365
|
-
set_text_v_align(2) if location == 'vcenter'
|
366
|
-
set_text_v_align(3) if location == 'bottom'
|
367
|
-
set_text_v_align(4) if location == 'vjustify'
|
368
|
-
set_text_v_align(5) if location == 'vdistributed'
|
369
|
-
set_text_v_align(5) if location == 'vequal_space' # S::PE.
|
370
366
|
end
|
371
367
|
|
372
368
|
#
|
@@ -419,7 +415,7 @@ module Writexlsx
|
|
419
415
|
def set_rotation(rotation)
|
420
416
|
if rotation == 270
|
421
417
|
rotation = 255
|
422
|
-
elsif rotation
|
418
|
+
elsif rotation.between?(-90, 90)
|
423
419
|
rotation = -rotation + 90 if rotation < 0
|
424
420
|
else
|
425
421
|
raise "Rotation #{rotation} outside range: -90 <= angle <= 90"
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'write_xlsx/image_property'
|
5
|
+
|
6
|
+
module Writexlsx
|
7
|
+
class Image
|
8
|
+
attr_reader :row, :col, :x_offset, :y_offset, :x_scale, :y_scale
|
9
|
+
attr_reader :url, :tip, :anchor, :description, :decorative
|
10
|
+
|
11
|
+
def initialize(
|
12
|
+
row, col, image, x_offset, y_offset, x_scale, y_scale,
|
13
|
+
url, tip, anchor, description, decorative
|
14
|
+
)
|
15
|
+
@row = row
|
16
|
+
@col = col
|
17
|
+
@image = ImageProperty.new(image)
|
18
|
+
@x_offset = x_offset
|
19
|
+
@y_offset = y_offset
|
20
|
+
@x_scale = x_scale
|
21
|
+
@y_scale = y_scale
|
22
|
+
@url = url
|
23
|
+
@tip = tip
|
24
|
+
@anchor = anchor
|
25
|
+
@description = description
|
26
|
+
@decorative = decorative
|
27
|
+
end
|
28
|
+
|
29
|
+
def scaled_width
|
30
|
+
width * x_scale * 96.0 / x_dpi
|
31
|
+
end
|
32
|
+
|
33
|
+
def scaled_height
|
34
|
+
height * y_scale * 96.0 / y_dpi
|
35
|
+
end
|
36
|
+
|
37
|
+
def width_emus
|
38
|
+
(0.5 + (scaled_width * 9_525)).to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
def height_emus
|
42
|
+
(0.5 + (scaled_height * 9_525)).to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
def image
|
46
|
+
@image.filename
|
47
|
+
end
|
48
|
+
|
49
|
+
def type
|
50
|
+
@image.type
|
51
|
+
end
|
52
|
+
|
53
|
+
def width
|
54
|
+
@image.width
|
55
|
+
end
|
56
|
+
|
57
|
+
def height
|
58
|
+
@image.height
|
59
|
+
end
|
60
|
+
|
61
|
+
def name
|
62
|
+
@image.name
|
63
|
+
end
|
64
|
+
|
65
|
+
def x_dpi
|
66
|
+
@image.x_dpi
|
67
|
+
end
|
68
|
+
|
69
|
+
def y_dpi
|
70
|
+
@image.y_dpi
|
71
|
+
end
|
72
|
+
|
73
|
+
def md5
|
74
|
+
@image.md5
|
75
|
+
end
|
76
|
+
|
77
|
+
def filename
|
78
|
+
@image.filename
|
79
|
+
end
|
80
|
+
|
81
|
+
def position
|
82
|
+
@image.position
|
83
|
+
end
|
84
|
+
|
85
|
+
def ref_id
|
86
|
+
@image.ref_id
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'digest/md5'
|
5
|
+
|
6
|
+
module Writexlsx
|
7
|
+
class ImageProperty
|
8
|
+
attr_reader :type, :width, :height, :name, :x_dpi, :y_dpi, :md5
|
9
|
+
attr_reader :filename, :description, :decorative
|
10
|
+
attr_accessor :ref_id, :body, :position
|
11
|
+
|
12
|
+
def initialize(filename, options = {})
|
13
|
+
@filename = filename
|
14
|
+
@description = options[:description]
|
15
|
+
@decorative = options[:decorative]
|
16
|
+
@position = options[:position]
|
17
|
+
@name = File.basename(filename)
|
18
|
+
|
19
|
+
# Open the image file and import the data.
|
20
|
+
data = File.binread(filename)
|
21
|
+
@md5 = Digest::MD5.hexdigest(data)
|
22
|
+
if data.unpack1('x A3') == 'PNG'
|
23
|
+
process_png(data)
|
24
|
+
elsif data.unpack1('n') == 0xFFD8
|
25
|
+
process_jpg(data)
|
26
|
+
elsif data.unpack1('A4') == 'GIF8'
|
27
|
+
process_gif(data)
|
28
|
+
elsif data.unpack1('A2') == 'BM'
|
29
|
+
process_bmp(data)
|
30
|
+
else
|
31
|
+
# TODO. Add Image::Size to support other types.
|
32
|
+
raise "Unsupported image format for file: #{filename}\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set a default dpi for images with 0 dpi.
|
36
|
+
@x_dpi = 96 if @x_dpi == 0
|
37
|
+
@y_dpi = 96 if @y_dpi == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Extract width and height information from a PNG file.
|
42
|
+
#
|
43
|
+
def process_png(data)
|
44
|
+
@type = 'png'
|
45
|
+
@width = 0
|
46
|
+
@height = 0
|
47
|
+
@x_dpi = 96
|
48
|
+
@y_dpi = 96
|
49
|
+
|
50
|
+
offset = 8
|
51
|
+
data_length = data.size
|
52
|
+
|
53
|
+
# Search through the image data to read the height and width in th the
|
54
|
+
# IHDR element. Also read the DPI in the pHYs element.
|
55
|
+
while offset < data_length
|
56
|
+
|
57
|
+
length = data[offset + 0, 4].unpack1("N")
|
58
|
+
png_type = data[offset + 4, 4].unpack1("A4")
|
59
|
+
|
60
|
+
case png_type
|
61
|
+
when "IHDR"
|
62
|
+
@width = data[offset + 8, 4].unpack1("N")
|
63
|
+
@height = data[offset + 12, 4].unpack1("N")
|
64
|
+
when "pHYs"
|
65
|
+
x_ppu = data[offset + 8, 4].unpack1("N")
|
66
|
+
y_ppu = data[offset + 12, 4].unpack1("N")
|
67
|
+
units = data[offset + 16, 1].unpack1("C")
|
68
|
+
|
69
|
+
if units == 1
|
70
|
+
@x_dpi = x_ppu * 0.0254
|
71
|
+
@y_dpi = y_ppu * 0.0254
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
offset = offset + length + 12
|
76
|
+
|
77
|
+
break if png_type == "IEND"
|
78
|
+
end
|
79
|
+
raise "#{@filename}: no size data found in png image.\n" unless @height
|
80
|
+
end
|
81
|
+
|
82
|
+
def process_jpg(data)
|
83
|
+
@type = 'jpeg'
|
84
|
+
@x_dpi = 96
|
85
|
+
@y_dpi = 96
|
86
|
+
|
87
|
+
offset = 2
|
88
|
+
data_length = data.bytesize
|
89
|
+
|
90
|
+
# Search through the image data to read the JPEG markers.
|
91
|
+
while offset < data_length
|
92
|
+
marker = data[offset + 0, 2].unpack1("n")
|
93
|
+
length = data[offset + 2, 2].unpack1("n")
|
94
|
+
|
95
|
+
# Read the height and width in the 0xFFCn elements
|
96
|
+
# (Except C4, C8 and CC which aren't SOF markers).
|
97
|
+
if (marker & 0xFFF0) == 0xFFC0 &&
|
98
|
+
marker != 0xFFC4 && marker != 0xFFCC
|
99
|
+
@height = data[offset + 5, 2].unpack1("n")
|
100
|
+
@width = data[offset + 7, 2].unpack1("n")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Read the DPI in the 0xFFE0 element.
|
104
|
+
if marker == 0xFFE0
|
105
|
+
units = data[offset + 11, 1].unpack1("C")
|
106
|
+
x_density = data[offset + 12, 2].unpack1("n")
|
107
|
+
y_density = data[offset + 14, 2].unpack1("n")
|
108
|
+
|
109
|
+
if units == 1
|
110
|
+
@x_dpi = x_density
|
111
|
+
@y_dpi = y_density
|
112
|
+
elsif units == 2
|
113
|
+
@x_dpi = x_density * 2.54
|
114
|
+
@y_dpi = y_density * 2.54
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
offset += length + 2
|
119
|
+
break if marker == 0xFFDA
|
120
|
+
end
|
121
|
+
|
122
|
+
raise "#{@filename}: no size data found in jpeg image.\n" unless @height
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Extract width and height information from a GIF file.
|
127
|
+
#
|
128
|
+
def process_gif(data)
|
129
|
+
@type = 'gif'
|
130
|
+
@x_dpi = 96
|
131
|
+
@y_dpi = 96
|
132
|
+
|
133
|
+
@width = data[6, 2].unpack1("v")
|
134
|
+
@height = data[8, 2].unpack1("v")
|
135
|
+
|
136
|
+
raise "#{@filename}: no size data found in gif image.\n" if @height.nil?
|
137
|
+
end
|
138
|
+
|
139
|
+
# Extract width and height information from a BMP file.
|
140
|
+
def process_bmp(data) # :nodoc:
|
141
|
+
@type = 'bmp'
|
142
|
+
|
143
|
+
# Check that the file is big enough to be a bitmap.
|
144
|
+
raise "#{@filename} doesn't contain enough data." if data.bytesize <= 0x36
|
145
|
+
|
146
|
+
# Read the bitmap width and height. Verify the sizes.
|
147
|
+
@width, @height = data.unpack("x18 V2")
|
148
|
+
raise "#{@filename}: largest image width #{width} supported is 65k." if width > 0xFFFF
|
149
|
+
raise "#{@filename}: largest image height supported is 65k." if @height > 0xFFFF
|
150
|
+
|
151
|
+
# Read the bitmap planes and bpp data. Verify them.
|
152
|
+
planes, bitcount = data.unpack("x26 v2")
|
153
|
+
raise "#{@filename} isn't a 24bit true color bitmap." unless bitcount == 24
|
154
|
+
raise "#{@filename}: only 1 plane supported in bitmap image." unless planes == 1
|
155
|
+
|
156
|
+
# Read the bitmap compression. Verify compression.
|
157
|
+
compression = data.unpack1("x30 V")
|
158
|
+
raise "#{@filename}: compression not supported in bitmap image." unless compression == 0
|
159
|
+
|
160
|
+
@x_dpi = @y_dpi = 96
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|