ruby_pptx 0.1.0 → 0.2.0
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/CHANGELOG.md +33 -0
- data/README.md +83 -2
- data/lib/ruby_pptx/action.rb +12 -4
- data/lib/ruby_pptx/autoshape_spec.rb +6 -2
- data/lib/ruby_pptx/builder.rb +3 -1
- data/lib/ruby_pptx/chart/categories.rb +45 -15
- data/lib/ruby_pptx/chart/chart.rb +77 -27
- data/lib/ruby_pptx/chart/combo.rb +9 -3
- data/lib/ruby_pptx/chart/data.rb +45 -15
- data/lib/ruby_pptx/chart/format.rb +229 -76
- data/lib/ruby_pptx/chart/workbook_writer.rb +9 -3
- data/lib/ruby_pptx/chart/xml_writer.rb +54 -18
- data/lib/ruby_pptx/chart/xy_data.rb +68 -22
- data/lib/ruby_pptx/dml/color.rb +27 -9
- data/lib/ruby_pptx/dml/effect.rb +6 -2
- data/lib/ruby_pptx/dml/fill.rb +42 -14
- data/lib/ruby_pptx/element_proxy.rb +12 -4
- data/lib/ruby_pptx/enum/base.rb +30 -10
- data/lib/ruby_pptx/enum/prog_id.rb +3 -1
- data/lib/ruby_pptx/geometry.rb +32 -10
- data/lib/ruby_pptx/image.rb +27 -9
- data/lib/ruby_pptx/length.rb +94 -27
- data/lib/ruby_pptx/media.rb +12 -4
- data/lib/ruby_pptx/numeric_lengths.rb +34 -9
- data/lib/ruby_pptx/opc/oxml.rb +7 -5
- data/lib/ruby_pptx/opc/pack_uri.rb +15 -5
- data/lib/ruby_pptx/opc/package.rb +97 -31
- data/lib/ruby_pptx/opc/serialized.rb +15 -5
- data/lib/ruby_pptx/oxml/backend.rb +585 -0
- data/lib/ruby_pptx/oxml/chart.rb +112 -42
- data/lib/ruby_pptx/oxml/content_model.rb +6 -2
- data/lib/ruby_pptx/oxml/dml/color.rb +6 -2
- data/lib/ruby_pptx/oxml/dml/fill.rb +6 -2
- data/lib/ruby_pptx/oxml/element.rb +138 -101
- data/lib/ruby_pptx/oxml/ns.rb +7 -7
- data/lib/ruby_pptx/oxml/presentation.rb +9 -3
- data/lib/ruby_pptx/oxml/section.rb +3 -1
- data/lib/ruby_pptx/oxml/shapes/autoshape.rb +39 -13
- data/lib/ruby_pptx/oxml/shapes/groupshape.rb +15 -7
- data/lib/ruby_pptx/oxml/shapes/other.rb +47 -16
- data/lib/ruby_pptx/oxml/shapes/shared.rb +96 -30
- data/lib/ruby_pptx/oxml/simple_types.rb +169 -52
- data/lib/ruby_pptx/oxml/slide.rb +24 -8
- data/lib/ruby_pptx/oxml/table.rb +39 -13
- data/lib/ruby_pptx/oxml/text.rb +37 -15
- data/lib/ruby_pptx/oxml/theme.rb +13 -4
- data/lib/ruby_pptx/package.rb +9 -3
- data/lib/ruby_pptx/parts/chart.rb +6 -2
- data/lib/ruby_pptx/parts/image.rb +12 -4
- data/lib/ruby_pptx/parts/media.rb +3 -1
- data/lib/ruby_pptx/parts/presentation.rb +15 -5
- data/lib/ruby_pptx/parts/slide.rb +33 -11
- data/lib/ruby_pptx/parts/theme.rb +3 -1
- data/lib/ruby_pptx/pattern_matching.rb +6 -2
- data/lib/ruby_pptx/presentation.rb +24 -8
- data/lib/ruby_pptx/section.rb +30 -10
- data/lib/ruby_pptx/shapes/adjustments.rb +9 -3
- data/lib/ruby_pptx/shapes/authoring.rb +3 -1
- data/lib/ruby_pptx/shapes/base.rb +74 -24
- data/lib/ruby_pptx/shapes/freeform.rb +36 -12
- data/lib/ruby_pptx/shapes/placeholder.rb +44 -15
- data/lib/ruby_pptx/shapes/shape.rb +91 -30
- data/lib/ruby_pptx/shapes/shape_tree.rb +66 -22
- data/lib/ruby_pptx/slide.rb +123 -41
- data/lib/ruby_pptx/table.rb +105 -35
- data/lib/ruby_pptx/text/font_metrics.rb +30 -9
- data/lib/ruby_pptx/text/text.rb +108 -36
- data/lib/ruby_pptx/theme.rb +30 -10
- data/lib/ruby_pptx/version.rb +1 -1
- data/lib/ruby_pptx.rb +8 -1
- metadata +22 -1
data/lib/ruby_pptx/image.rb
CHANGED
|
@@ -50,7 +50,9 @@ module Pptx
|
|
|
50
50
|
attr_reader :blob, :filename
|
|
51
51
|
|
|
52
52
|
class << self
|
|
53
|
-
def from_blob(blob, filename = nil)
|
|
53
|
+
def from_blob(blob, filename = nil)
|
|
54
|
+
new(blob, filename)
|
|
55
|
+
end
|
|
54
56
|
|
|
55
57
|
# Load from a path or an IO stream.
|
|
56
58
|
def from_file(image_file)
|
|
@@ -69,18 +71,28 @@ module Pptx
|
|
|
69
71
|
end
|
|
70
72
|
|
|
71
73
|
# @return [Symbol] :PNG, :JPEG, :GIF, :BMP, :TIFF or :WMF
|
|
72
|
-
def format
|
|
74
|
+
def format
|
|
75
|
+
header.fetch(:format)
|
|
76
|
+
end
|
|
73
77
|
|
|
74
78
|
# True for a vector image, which has no pixel size of its own and needs a
|
|
75
79
|
# raster fallback to be placed in a slide.
|
|
76
|
-
def vector?
|
|
80
|
+
def vector?
|
|
81
|
+
format == :SVG
|
|
82
|
+
end
|
|
77
83
|
|
|
78
84
|
# @return [Array(Integer, Integer)] width and height in pixels
|
|
79
|
-
def size
|
|
85
|
+
def size
|
|
86
|
+
[header.fetch(:width), header.fetch(:height)]
|
|
87
|
+
end
|
|
80
88
|
|
|
81
|
-
def width_px
|
|
89
|
+
def width_px
|
|
90
|
+
header.fetch(:width)
|
|
91
|
+
end
|
|
82
92
|
|
|
83
|
-
def height_px
|
|
93
|
+
def height_px
|
|
94
|
+
header.fetch(:height)
|
|
95
|
+
end
|
|
84
96
|
|
|
85
97
|
# @return [Array(Integer, Integer)] horizontal and vertical dots per inch
|
|
86
98
|
def dpi
|
|
@@ -94,9 +106,13 @@ module Pptx
|
|
|
94
106
|
end
|
|
95
107
|
end
|
|
96
108
|
|
|
97
|
-
def content_type
|
|
109
|
+
def content_type
|
|
110
|
+
CONTENT_TYPE_FOR_EXT.fetch(ext)
|
|
111
|
+
end
|
|
98
112
|
|
|
99
|
-
def sha1
|
|
113
|
+
def sha1
|
|
114
|
+
@sha1 ||= Digest::SHA1.hexdigest(@blob)
|
|
115
|
+
end
|
|
100
116
|
|
|
101
117
|
# Native size in EMU, derived from the pixel size and resolution.
|
|
102
118
|
#
|
|
@@ -109,7 +125,9 @@ module Pptx
|
|
|
109
125
|
Length.emu((EMU_PER_INCH * height_px / vert_dpi).to_i)]
|
|
110
126
|
end
|
|
111
127
|
|
|
112
|
-
def inspect
|
|
128
|
+
def inspect
|
|
129
|
+
"#<Pptx::Image #{format} #{width_px}x#{height_px} #{dpi.join("x")}dpi>"
|
|
130
|
+
end
|
|
113
131
|
|
|
114
132
|
private
|
|
115
133
|
|
data/lib/ruby_pptx/length.rb
CHANGED
|
@@ -30,16 +30,31 @@ module Pptx
|
|
|
30
30
|
|
|
31
31
|
class << self
|
|
32
32
|
# @param emu [Integer] distance in English Metric Units
|
|
33
|
-
def emu(emu)
|
|
33
|
+
def emu(emu)
|
|
34
|
+
new(emu)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def inches(inches)
|
|
38
|
+
new((inches * EMUS_PER_INCH).to_i)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cm(cm)
|
|
42
|
+
new((cm * EMUS_PER_CM).to_i)
|
|
43
|
+
end
|
|
34
44
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
def mm(mm)
|
|
46
|
+
new((mm * EMUS_PER_MM).to_i)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def pt(points)
|
|
50
|
+
new((points * EMUS_PER_PT).to_i)
|
|
51
|
+
end
|
|
39
52
|
|
|
40
53
|
# Hundredths of a point (1/7200 inch); the unit PowerPoint stores font
|
|
41
54
|
# sizes in.
|
|
42
|
-
def centipoints(centipoints)
|
|
55
|
+
def centipoints(centipoints)
|
|
56
|
+
new((centipoints * EMUS_PER_CENTIPOINT).to_i)
|
|
57
|
+
end
|
|
43
58
|
|
|
44
59
|
# Coerce +value+ to a Length, treating a bare Integer as EMU.
|
|
45
60
|
#
|
|
@@ -63,23 +78,48 @@ module Pptx
|
|
|
63
78
|
freeze
|
|
64
79
|
end
|
|
65
80
|
|
|
66
|
-
def inches
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
def inches
|
|
82
|
+
@emu / EMUS_PER_INCH.to_f
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def cm
|
|
86
|
+
@emu / EMUS_PER_CM.to_f
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def mm
|
|
90
|
+
@emu / EMUS_PER_MM.to_f
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def pt
|
|
94
|
+
@emu / EMUS_PER_PT.to_f
|
|
95
|
+
end
|
|
70
96
|
|
|
71
97
|
# @return [Integer] whole hundredths of a point, rounded toward negative infinity
|
|
72
|
-
def centipoints
|
|
98
|
+
def centipoints
|
|
99
|
+
@emu / EMUS_PER_CENTIPOINT
|
|
100
|
+
end
|
|
73
101
|
|
|
74
|
-
def to_i
|
|
102
|
+
def to_i
|
|
103
|
+
@emu
|
|
104
|
+
end
|
|
75
105
|
alias to_int to_i
|
|
76
106
|
|
|
77
|
-
def +(other)
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
def +(other)
|
|
108
|
+
Length.new(@emu + Length.coerce(other).emu)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def -(other)
|
|
112
|
+
Length.new(@emu - Length.coerce(other).emu)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def -@
|
|
116
|
+
Length.new(-@emu)
|
|
117
|
+
end
|
|
80
118
|
|
|
81
119
|
# Scaling by a plain number; `length * 2` is twice as long.
|
|
82
|
-
def *(factor)
|
|
120
|
+
def *(factor)
|
|
121
|
+
Length.new((@emu * factor).to_i)
|
|
122
|
+
end
|
|
83
123
|
|
|
84
124
|
# Deliberately no #coerce, so `2 * length` raises rather than working.
|
|
85
125
|
#
|
|
@@ -96,25 +136,52 @@ module Pptx
|
|
|
96
136
|
@emu <=> other.emu
|
|
97
137
|
end
|
|
98
138
|
|
|
99
|
-
def ==(other)
|
|
139
|
+
def ==(other)
|
|
140
|
+
(self <=> other)&.zero? || false
|
|
141
|
+
end
|
|
100
142
|
alias eql? ==
|
|
101
143
|
|
|
102
|
-
def hash
|
|
144
|
+
def hash
|
|
145
|
+
@emu.hash
|
|
146
|
+
end
|
|
103
147
|
|
|
104
|
-
def zero?
|
|
148
|
+
def zero?
|
|
149
|
+
@emu.zero?
|
|
150
|
+
end
|
|
105
151
|
|
|
106
|
-
def to_s
|
|
152
|
+
def to_s
|
|
153
|
+
"#{@emu}emu"
|
|
154
|
+
end
|
|
107
155
|
|
|
108
|
-
def inspect
|
|
156
|
+
def inspect
|
|
157
|
+
"#<Pptx::Length #{@emu}emu (#{format("%g", inches)}in)>"
|
|
158
|
+
end
|
|
109
159
|
end
|
|
110
160
|
|
|
111
161
|
class << self
|
|
112
162
|
# Convenience constructors, e.g. `Pptx.inches(1.5)`.
|
|
113
|
-
def emu(v)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def
|
|
118
|
-
|
|
163
|
+
def emu(v)
|
|
164
|
+
Length.emu(v)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def inches(v)
|
|
168
|
+
Length.inches(v)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def cm(v)
|
|
172
|
+
Length.cm(v)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def mm(v)
|
|
176
|
+
Length.mm(v)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def pt(v)
|
|
180
|
+
Length.pt(v)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def centipoints(v)
|
|
184
|
+
Length.centipoints(v)
|
|
185
|
+
end
|
|
119
186
|
end
|
|
120
187
|
end
|
data/lib/ruby_pptx/media.rb
CHANGED
|
@@ -28,7 +28,9 @@ module Pptx
|
|
|
28
28
|
|
|
29
29
|
attr_reader :blob, :content_type
|
|
30
30
|
|
|
31
|
-
def self.from_blob(blob, content_type, filename = nil)
|
|
31
|
+
def self.from_blob(blob, content_type, filename = nil)
|
|
32
|
+
new(blob, content_type, filename)
|
|
33
|
+
end
|
|
32
34
|
|
|
33
35
|
# Load from a path or an IO stream.
|
|
34
36
|
def self.from_file(movie_file, content_type)
|
|
@@ -55,10 +57,16 @@ module Pptx
|
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
# The name PowerPoint shows for the shape.
|
|
58
|
-
def filename
|
|
60
|
+
def filename
|
|
61
|
+
@source_filename || "movie.#{ext}"
|
|
62
|
+
end
|
|
59
63
|
|
|
60
|
-
def sha1
|
|
64
|
+
def sha1
|
|
65
|
+
@sha1 ||= Digest::SHA1.hexdigest(@blob)
|
|
66
|
+
end
|
|
61
67
|
|
|
62
|
-
def inspect
|
|
68
|
+
def inspect
|
|
69
|
+
"#<Pptx::Video #{filename} #{@content_type}>"
|
|
70
|
+
end
|
|
63
71
|
end
|
|
64
72
|
end
|
|
@@ -14,17 +14,42 @@ module Pptx
|
|
|
14
14
|
# Prefer the refinement. It is scoped to the file that asks for it, so it
|
|
15
15
|
# cannot surprise another library that happens to share the process.
|
|
16
16
|
module NumericLengths
|
|
17
|
-
def emu
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def
|
|
22
|
-
|
|
17
|
+
def emu
|
|
18
|
+
Pptx::Length.emu(self)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def inches
|
|
22
|
+
Pptx::Length.inches(self)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def cm
|
|
26
|
+
Pptx::Length.cm(self)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def mm
|
|
30
|
+
Pptx::Length.mm(self)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def pt
|
|
34
|
+
Pptx::Length.pt(self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def centipoints
|
|
38
|
+
Pptx::Length.centipoints(self)
|
|
39
|
+
end
|
|
23
40
|
|
|
24
41
|
# Spelled out rather than aliased: `import_methods`, which the refinement
|
|
25
42
|
# uses to share these, cannot carry an alias across.
|
|
26
|
-
def inch
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
def inch
|
|
44
|
+
inches
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def points
|
|
48
|
+
pt
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def point
|
|
52
|
+
pt
|
|
53
|
+
end
|
|
29
54
|
end
|
|
30
55
|
end
|
data/lib/ruby_pptx/opc/oxml.rb
CHANGED
|
@@ -20,9 +20,7 @@ module Pptx
|
|
|
20
20
|
# to match what python-pptx (and lxml) produce, so packages compare
|
|
21
21
|
# byte-for-byte.
|
|
22
22
|
def self.serialize_part_xml(element)
|
|
23
|
-
|
|
24
|
-
body = node.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML |
|
|
25
|
-
Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
|
|
23
|
+
body = Element.wrap(element).to_xml
|
|
26
24
|
%(<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n#{body})
|
|
27
25
|
end
|
|
28
26
|
|
|
@@ -70,7 +68,9 @@ module Pptx
|
|
|
70
68
|
optional_attr "TargetMode", type: ST::ST_TargetMode,
|
|
71
69
|
default: RELATIONSHIP_TARGET_MODE::INTERNAL, as: :targetMode
|
|
72
70
|
|
|
73
|
-
def external?
|
|
71
|
+
def external?
|
|
72
|
+
targetMode == RELATIONSHIP_TARGET_MODE::EXTERNAL
|
|
73
|
+
end
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# `<Relationships>`, the root of a `.rels` item.
|
|
@@ -92,7 +92,9 @@ module Pptx
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
# Bytes for a `.rels` item, with the XML declaration.
|
|
95
|
-
def xml_file_bytes
|
|
95
|
+
def xml_file_bytes
|
|
96
|
+
Oxml.serialize_part_xml(self)
|
|
97
|
+
end
|
|
96
98
|
end
|
|
97
99
|
end
|
|
98
100
|
end
|
|
@@ -99,7 +99,9 @@ module Pptx
|
|
|
99
99
|
|
|
100
100
|
# The part name without its leading slash -- the form used as the Zip
|
|
101
101
|
# entry name.
|
|
102
|
-
def member_name
|
|
102
|
+
def member_name
|
|
103
|
+
@to_s[1..]
|
|
104
|
+
end
|
|
103
105
|
|
|
104
106
|
# A reference to this part relative to +base_uri+, as written into a
|
|
105
107
|
# .rels part.
|
|
@@ -130,14 +132,22 @@ module Pptx
|
|
|
130
132
|
@to_s <=> other.to_s
|
|
131
133
|
end
|
|
132
134
|
|
|
133
|
-
def ==(other)
|
|
135
|
+
def ==(other)
|
|
136
|
+
(self <=> other)&.zero? || false
|
|
137
|
+
end
|
|
134
138
|
alias eql? ==
|
|
135
139
|
|
|
136
|
-
def hash
|
|
140
|
+
def hash
|
|
141
|
+
@to_s.hash
|
|
142
|
+
end
|
|
137
143
|
|
|
138
|
-
def to_str
|
|
144
|
+
def to_str
|
|
145
|
+
@to_s
|
|
146
|
+
end
|
|
139
147
|
|
|
140
|
-
def inspect
|
|
148
|
+
def inspect
|
|
149
|
+
"#<Pptx::Opc::PackURI #{@to_s.inspect}>"
|
|
150
|
+
end
|
|
141
151
|
|
|
142
152
|
PACKAGE = new("/")
|
|
143
153
|
CONTENT_TYPES = new("/[Content_Types].xml")
|
|
@@ -15,7 +15,9 @@ module Pptx
|
|
|
15
15
|
#
|
|
16
16
|
# @raise [NotFoundError] when there is no such relationship
|
|
17
17
|
# @raise [Error] when there is more than one
|
|
18
|
-
def part_related_by(reltype)
|
|
18
|
+
def part_related_by(reltype)
|
|
19
|
+
rels.part_with_reltype(reltype)
|
|
20
|
+
end
|
|
19
21
|
|
|
20
22
|
# Relate this part to +target+, returning the rId. An existing matching
|
|
21
23
|
# relationship is reused rather than duplicated.
|
|
@@ -29,12 +31,18 @@ module Pptx
|
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
def related_part(r_id)
|
|
34
|
+
def related_part(r_id)
|
|
35
|
+
rels.fetch(r_id).target_part
|
|
36
|
+
end
|
|
33
37
|
|
|
34
|
-
def target_ref(r_id)
|
|
38
|
+
def target_ref(r_id)
|
|
39
|
+
rels.fetch(r_id).target_ref
|
|
40
|
+
end
|
|
35
41
|
|
|
36
42
|
# @return [Relationships]
|
|
37
|
-
def rels
|
|
43
|
+
def rels
|
|
44
|
+
raise(NotImplementedError, "#{self.class} must implement #rels")
|
|
45
|
+
end
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
# An OPC package: the object graph behind a .pptx file.
|
|
@@ -43,18 +51,26 @@ module Pptx
|
|
|
43
51
|
|
|
44
52
|
# Open a package from a path, an IO stream, or a directory holding an
|
|
45
53
|
# unzipped package.
|
|
46
|
-
def self.open(pkg_file)
|
|
54
|
+
def self.open(pkg_file)
|
|
55
|
+
new(pkg_file).load
|
|
56
|
+
end
|
|
47
57
|
|
|
48
58
|
def initialize(pkg_file = nil)
|
|
49
59
|
@pkg_file = pkg_file
|
|
50
60
|
end
|
|
51
61
|
|
|
52
|
-
def rels
|
|
62
|
+
def rels
|
|
63
|
+
@rels ||= Relationships.new(PackURI::PACKAGE.base_uri)
|
|
64
|
+
end
|
|
53
65
|
|
|
54
|
-
def drop_rel(r_id)
|
|
66
|
+
def drop_rel(r_id)
|
|
67
|
+
rels.delete(r_id)
|
|
68
|
+
end
|
|
55
69
|
|
|
56
70
|
# The presentation part, which is the package's main document part.
|
|
57
|
-
def main_document_part
|
|
71
|
+
def main_document_part
|
|
72
|
+
part_related_by(RELATIONSHIP_TYPE::OFFICE_DOCUMENT)
|
|
73
|
+
end
|
|
58
74
|
|
|
59
75
|
# Every part in the package, each yielded once.
|
|
60
76
|
def parts
|
|
@@ -125,7 +141,9 @@ module Pptx
|
|
|
125
141
|
# Loads a package from its serialized form.
|
|
126
142
|
class PackageLoader
|
|
127
143
|
# @return [Array(Oxml::CT_Relationships, Hash{String=>Part})]
|
|
128
|
-
def self.load(pkg_file, package)
|
|
144
|
+
def self.load(pkg_file, package)
|
|
145
|
+
new(pkg_file, package).load
|
|
146
|
+
end
|
|
129
147
|
|
|
130
148
|
def initialize(pkg_file, package)
|
|
131
149
|
@pkg_file = pkg_file
|
|
@@ -140,7 +158,9 @@ module Pptx
|
|
|
140
158
|
|
|
141
159
|
private
|
|
142
160
|
|
|
143
|
-
def reader
|
|
161
|
+
def reader
|
|
162
|
+
@reader ||= PackageReader.new(@pkg_file)
|
|
163
|
+
end
|
|
144
164
|
|
|
145
165
|
def content_types
|
|
146
166
|
@content_types ||= ContentTypeMap.from_xml(reader[PackURI::CONTENT_TYPES])
|
|
@@ -199,7 +219,9 @@ module Pptx
|
|
|
199
219
|
|
|
200
220
|
attr_reader :partname, :content_type, :package
|
|
201
221
|
|
|
202
|
-
def self.load(partname, content_type, package, blob)
|
|
222
|
+
def self.load(partname, content_type, package, blob)
|
|
223
|
+
new(partname, content_type, package, blob)
|
|
224
|
+
end
|
|
203
225
|
|
|
204
226
|
def initialize(partname, content_type, package, blob = nil)
|
|
205
227
|
@partname = partname
|
|
@@ -215,19 +237,27 @@ module Pptx
|
|
|
215
237
|
end
|
|
216
238
|
|
|
217
239
|
# @return [String] the bytes this part serializes to
|
|
218
|
-
def blob
|
|
240
|
+
def blob
|
|
241
|
+
@blob || ""
|
|
242
|
+
end
|
|
219
243
|
|
|
220
244
|
attr_writer :blob
|
|
221
245
|
|
|
222
|
-
def rels
|
|
246
|
+
def rels
|
|
247
|
+
@rels ||= Relationships.new(@partname.base_uri)
|
|
248
|
+
end
|
|
223
249
|
|
|
224
250
|
def load_rels_from_xml(xml_rels, parts)
|
|
225
251
|
rels.load_from_xml(@partname.base_uri, xml_rels, parts)
|
|
226
252
|
end
|
|
227
253
|
|
|
228
|
-
def drop_rel(r_id)
|
|
254
|
+
def drop_rel(r_id)
|
|
255
|
+
rels.delete(r_id)
|
|
256
|
+
end
|
|
229
257
|
|
|
230
|
-
def inspect
|
|
258
|
+
def inspect
|
|
259
|
+
"#<#{self.class.name} #{@partname}>"
|
|
260
|
+
end
|
|
231
261
|
end
|
|
232
262
|
|
|
233
263
|
# A part whose payload is XML, which is most of them.
|
|
@@ -243,11 +273,15 @@ module Pptx
|
|
|
243
273
|
@element = element
|
|
244
274
|
end
|
|
245
275
|
|
|
246
|
-
def blob
|
|
276
|
+
def blob
|
|
277
|
+
Oxml.serialize_part_xml(@element)
|
|
278
|
+
end
|
|
247
279
|
|
|
248
280
|
# This part. Objects further down the tree delegate up to find the part
|
|
249
281
|
# they live in; the chain ends here.
|
|
250
|
-
def part
|
|
282
|
+
def part
|
|
283
|
+
self
|
|
284
|
+
end
|
|
251
285
|
|
|
252
286
|
# Drop a relationship unless the XML still refers to it more than once.
|
|
253
287
|
#
|
|
@@ -274,12 +308,18 @@ module Pptx
|
|
|
274
308
|
@part_type_for[content_type] = part_class
|
|
275
309
|
end
|
|
276
310
|
|
|
277
|
-
def part_class_for(content_type)
|
|
311
|
+
def part_class_for(content_type)
|
|
312
|
+
@part_type_for.fetch(content_type, Part)
|
|
313
|
+
end
|
|
278
314
|
|
|
279
|
-
def registered
|
|
315
|
+
def registered
|
|
316
|
+
@part_type_for.dup
|
|
317
|
+
end
|
|
280
318
|
|
|
281
319
|
# Test seam: restore a previous registration table.
|
|
282
|
-
def reset!(to)
|
|
320
|
+
def reset!(to)
|
|
321
|
+
(@part_type_for = to)
|
|
322
|
+
end
|
|
283
323
|
|
|
284
324
|
def build(partname, content_type, package, blob)
|
|
285
325
|
part_class_for(content_type).load(partname, content_type, package, blob)
|
|
@@ -325,21 +365,41 @@ module Pptx
|
|
|
325
365
|
@rels = {}
|
|
326
366
|
end
|
|
327
367
|
|
|
328
|
-
def each(&)
|
|
368
|
+
def each(&)
|
|
369
|
+
@rels.each_value(&)
|
|
370
|
+
end
|
|
329
371
|
|
|
330
|
-
def each_value(&)
|
|
372
|
+
def each_value(&)
|
|
373
|
+
@rels.each_value(&)
|
|
374
|
+
end
|
|
331
375
|
|
|
332
|
-
def [](r_id)
|
|
376
|
+
def [](r_id)
|
|
377
|
+
@rels[r_id]
|
|
378
|
+
end
|
|
333
379
|
|
|
334
380
|
def fetch(r_id)
|
|
335
381
|
@rels.fetch(r_id) { raise NotFoundError, "no relationship with key #{r_id.inspect}" }
|
|
336
382
|
end
|
|
337
383
|
|
|
338
|
-
def key?(r_id)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
def
|
|
384
|
+
def key?(r_id)
|
|
385
|
+
@rels.key?(r_id)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def keys
|
|
389
|
+
@rels.keys
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def size
|
|
393
|
+
@rels.size
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def empty?
|
|
397
|
+
@rels.empty?
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def delete(r_id)
|
|
401
|
+
@rels.delete(r_id)
|
|
402
|
+
end
|
|
343
403
|
|
|
344
404
|
# The rId of a relationship of +reltype+ to +target_part+, adding one if
|
|
345
405
|
# no match exists.
|
|
@@ -458,7 +518,9 @@ module Pptx
|
|
|
458
518
|
end
|
|
459
519
|
|
|
460
520
|
# True when the target is outside the package, such as a URL.
|
|
461
|
-
def external?
|
|
521
|
+
def external?
|
|
522
|
+
@target_mode == RELATIONSHIP_TARGET_MODE::EXTERNAL
|
|
523
|
+
end
|
|
462
524
|
|
|
463
525
|
def target_part
|
|
464
526
|
raise Error, "#target_part is undefined for an external relationship" if external?
|
|
@@ -474,9 +536,13 @@ module Pptx
|
|
|
474
536
|
|
|
475
537
|
# The reference as written into the `.rels` item: a relative partname
|
|
476
538
|
# internally, or the URL for an external relationship.
|
|
477
|
-
def target_ref
|
|
539
|
+
def target_ref
|
|
540
|
+
external? ? @target : target_partname.relative_ref(@base_uri)
|
|
541
|
+
end
|
|
478
542
|
|
|
479
|
-
def inspect
|
|
543
|
+
def inspect
|
|
544
|
+
"#<Pptx::Opc::Relationship #{@r_id} #{@reltype}>"
|
|
545
|
+
end
|
|
480
546
|
end
|
|
481
547
|
end
|
|
482
548
|
end
|
|
@@ -20,11 +20,15 @@ module Pptx
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# @return [Boolean] whether the package holds an item at +pack_uri+
|
|
23
|
-
def key?(pack_uri)
|
|
23
|
+
def key?(pack_uri)
|
|
24
|
+
@reader.key?(pack_uri)
|
|
25
|
+
end
|
|
24
26
|
|
|
25
27
|
# @return [String] binary contents of the item at +pack_uri+
|
|
26
28
|
# @raise [KeyError] when there is no such item
|
|
27
|
-
def [](pack_uri)
|
|
29
|
+
def [](pack_uri)
|
|
30
|
+
@reader[pack_uri]
|
|
31
|
+
end
|
|
28
32
|
|
|
29
33
|
# The XML of the `.rels` item belonging to +partname+, or nil when that
|
|
30
34
|
# part has no relationships.
|
|
@@ -86,7 +90,9 @@ module Pptx
|
|
|
86
90
|
@path = File.expand_path(path)
|
|
87
91
|
end
|
|
88
92
|
|
|
89
|
-
def key?(pack_uri)
|
|
93
|
+
def key?(pack_uri)
|
|
94
|
+
File.file?(path_for(pack_uri))
|
|
95
|
+
end
|
|
90
96
|
|
|
91
97
|
def [](pack_uri)
|
|
92
98
|
File.binread(path_for(pack_uri))
|
|
@@ -115,7 +121,9 @@ module Pptx
|
|
|
115
121
|
@pkg_file = pkg_file
|
|
116
122
|
end
|
|
117
123
|
|
|
118
|
-
def key?(pack_uri)
|
|
124
|
+
def key?(pack_uri)
|
|
125
|
+
blobs.key?(pack_uri.to_s)
|
|
126
|
+
end
|
|
119
127
|
|
|
120
128
|
def [](pack_uri)
|
|
121
129
|
blobs.fetch(pack_uri.to_s) { raise KeyError, "no member #{pack_uri} in package" }
|
|
@@ -132,9 +140,11 @@ module Pptx
|
|
|
132
140
|
def each_entry(&block)
|
|
133
141
|
return enum_for(:each_entry) unless block
|
|
134
142
|
|
|
143
|
+
# get_input_stream without a block opens a file handle per member and
|
|
144
|
+
# leaves it for GC; on Windows that keeps the .pptx locked after open.
|
|
135
145
|
open_zip do |zip|
|
|
136
146
|
zip.each do |entry|
|
|
137
|
-
block.call(entry.name, entry.get_input_stream
|
|
147
|
+
block.call(entry.name, entry.get_input_stream(&:read)) if entry.file?
|
|
138
148
|
end
|
|
139
149
|
end
|
|
140
150
|
end
|