rbpptx 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 +48 -0
- data/README.md +95 -9
- data/lib/rbpptx/package.rb +274 -0
- data/lib/rbpptx/presentation.rb +201 -75
- data/lib/rbpptx/shape.rb +31 -75
- data/lib/rbpptx/shape_container.rb +161 -0
- data/lib/rbpptx/slide.rb +131 -38
- data/lib/rbpptx/table.rb +106 -0
- data/lib/rbpptx/text_body.rb +92 -0
- data/lib/rbpptx/version.rb +1 -1
- data/lib/rbpptx.rb +4 -0
- data/sig/rbpptx.rbs +148 -10
- metadata +13 -3
data/lib/rbpptx/slide.rb
CHANGED
|
@@ -1,78 +1,171 @@
|
|
|
1
1
|
module Rbpptx
|
|
2
|
-
#
|
|
2
|
+
# Common behavior for an XML part that holds a shape tree (+<p:cSld>/
|
|
3
|
+
# <p:spTree>+): slides and notes slides.
|
|
3
4
|
#
|
|
4
|
-
# Parsing
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
class
|
|
8
|
-
|
|
5
|
+
# Parsing is deferred until the shape tree is first accessed and then
|
|
6
|
+
# cached, so simply enumerating slides does not pay the per-slide parse
|
|
7
|
+
# cost.
|
|
8
|
+
class SlidePart
|
|
9
|
+
include ShapeContainer
|
|
9
10
|
|
|
10
|
-
# @return [String] the part name
|
|
11
|
+
# @return [String] the part name inside the package, e.g.
|
|
11
12
|
# +"ppt/slides/slide1.xml"+
|
|
12
13
|
attr_reader :part_name
|
|
13
14
|
|
|
14
|
-
# @param
|
|
15
|
+
# @param package [Package] the open package
|
|
15
16
|
# @param part_name [String] absolute part name (no leading slash)
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
# @api private
|
|
18
|
+
def initialize(package:, part_name:)
|
|
19
|
+
@package = package
|
|
18
20
|
@part_name = part_name
|
|
19
21
|
@dirty = false
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
# @return [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# @return [Array<Shape>] +<p:sp>+ children of the slide's shape tree, in
|
|
28
|
-
# document order
|
|
29
|
-
def shapes
|
|
30
|
-
@shapes ||= build_shapes
|
|
24
|
+
# @return [Placeholders] placeholder shapes on this part, addressable as
|
|
25
|
+
# +placeholders[:body]+ (by type) or +placeholders[1]+ (by +idx+)
|
|
26
|
+
def placeholders
|
|
27
|
+
Placeholders.new(shapes)
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
# @return [Boolean] whether any shape on this
|
|
34
|
-
# load
|
|
30
|
+
# @return [Boolean] whether any shape on this part has been mutated since
|
|
31
|
+
# load or the last save
|
|
35
32
|
def dirty?
|
|
36
33
|
@dirty
|
|
37
34
|
end
|
|
38
35
|
|
|
39
|
-
# Marks the
|
|
40
|
-
# public API.
|
|
36
|
+
# Marks the part as dirty. Called by {Shape} and {Run} setters; not part
|
|
37
|
+
# of the public API.
|
|
41
38
|
#
|
|
42
39
|
# @api private
|
|
43
40
|
def mark_dirty!
|
|
41
|
+
@package.touch(@part_name)
|
|
44
42
|
@dirty = true
|
|
45
43
|
end
|
|
46
44
|
|
|
47
|
-
# @return [String] the slide's XML, reflecting any in-memory mutations.
|
|
48
|
-
# The XML declaration is preserved.
|
|
49
|
-
def to_xml
|
|
50
|
-
doc.to_xml
|
|
51
|
-
end
|
|
52
|
-
|
|
53
45
|
# @api private
|
|
54
46
|
def clear_dirty!
|
|
55
47
|
@dirty = false
|
|
56
48
|
end
|
|
57
49
|
|
|
50
|
+
# @return [String] the part's XML, reflecting any in-memory mutations.
|
|
51
|
+
# The XML declaration is preserved.
|
|
52
|
+
def to_xml
|
|
53
|
+
doc.to_xml(save_with: Package::SAVE_OPTIONS)
|
|
54
|
+
end
|
|
55
|
+
|
|
58
56
|
private
|
|
59
57
|
|
|
60
58
|
def doc
|
|
61
|
-
@doc ||=
|
|
59
|
+
@doc ||= @package.xml(@part_name, error_class: SlideFormatError)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def shape_tree_node
|
|
63
|
+
doc.at_xpath("/*/p:cSld/p:spTree", "p" => P_NS)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def owner_part
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# A single slide.
|
|
72
|
+
class Slide < SlidePart
|
|
73
|
+
NOTES_SLIDE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide".freeze
|
|
74
|
+
|
|
75
|
+
# @param package [Package] the open package
|
|
76
|
+
# @param part_name [String] absolute part name (no leading slash)
|
|
77
|
+
# @param presentation [Presentation, nil] owner, needed to create a notes
|
|
78
|
+
# slide on demand
|
|
79
|
+
# @api private
|
|
80
|
+
def initialize(package:, part_name:, presentation: nil)
|
|
81
|
+
super(package: package, part_name: part_name)
|
|
82
|
+
@presentation = presentation
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @return [String] the slide's basename without extension, e.g. +"slide1"+
|
|
86
|
+
def name
|
|
87
|
+
File.basename(@part_name, ".xml")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @return [Array<Shape>] +<p:sp>+ children of the slide's shape tree, in
|
|
91
|
+
# document order. Shapes inside groups are not included; see
|
|
92
|
+
# {#all_shapes} and {#groups}.
|
|
93
|
+
def shapes
|
|
94
|
+
@shapes ||= super
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @return [Shape, nil] the title placeholder (+:title+ or +:ctrTitle+),
|
|
98
|
+
# or +nil+ when the slide has none
|
|
99
|
+
def title
|
|
100
|
+
shapes.find { |s| %i[title ctrTitle].include?(s.placeholder_type) }
|
|
62
101
|
end
|
|
63
102
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
# @return [NotesSlide, nil] the slide's notes page, or +nil+ when the
|
|
104
|
+
# slide has none. Does not create one; see {#notes=}.
|
|
105
|
+
def notes_slide
|
|
106
|
+
return @notes_slide if defined?(@notes_slide)
|
|
107
|
+
|
|
108
|
+
part = @package.related(@part_name, NOTES_SLIDE_REL_TYPE).first
|
|
109
|
+
@notes_slide = part && NotesSlide.new(package: @package, part_name: part)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @return [String] speaker notes text, or +""+ when the slide has no
|
|
113
|
+
# notes page
|
|
114
|
+
def notes
|
|
115
|
+
notes_slide&.text || ""
|
|
116
|
+
end
|
|
67
117
|
|
|
68
|
-
|
|
69
|
-
|
|
118
|
+
# Replaces the speaker notes. If the slide has no notes page yet, one is
|
|
119
|
+
# created from the presentation's notes master.
|
|
120
|
+
#
|
|
121
|
+
# @param new_text [String]
|
|
122
|
+
# @return [String]
|
|
123
|
+
# @raise [Rbpptx::Error] if a notes page must be created but the
|
|
124
|
+
# presentation has no notes master, or the notes page has no body
|
|
125
|
+
# placeholder
|
|
126
|
+
def notes=(new_text)
|
|
127
|
+
ns = notes_slide
|
|
128
|
+
unless ns
|
|
129
|
+
raise Error, "#{@part_name}: cannot create a notes slide without a presentation" unless @presentation
|
|
130
|
+
|
|
131
|
+
ns = @presentation.create_notes_slide_for(self)
|
|
132
|
+
@notes_slide = ns
|
|
133
|
+
end
|
|
134
|
+
ns.text = new_text
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @return [Boolean] whether this slide or its notes page has been mutated
|
|
138
|
+
# since load or the last save
|
|
139
|
+
def dirty?
|
|
140
|
+
super || (defined?(@notes_slide) && @notes_slide&.dirty?) || false
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @api private
|
|
144
|
+
def clear_dirty!
|
|
145
|
+
@notes_slide&.clear_dirty! if defined?(@notes_slide)
|
|
146
|
+
super
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# The notes page (+<p:notes>+) attached to a {Slide}.
|
|
151
|
+
class NotesSlide < SlidePart
|
|
152
|
+
# @return [Shape, nil] the body placeholder that holds the speaker notes
|
|
153
|
+
def notes_placeholder
|
|
154
|
+
shapes.find { |s| s.placeholder_type == :body }
|
|
155
|
+
end
|
|
70
156
|
|
|
71
|
-
|
|
157
|
+
# @return [String] the speaker notes text (+""+ if there is no body
|
|
158
|
+
# placeholder)
|
|
159
|
+
def text
|
|
160
|
+
notes_placeholder&.text || ""
|
|
72
161
|
end
|
|
73
162
|
|
|
74
|
-
|
|
75
|
-
|
|
163
|
+
# @param new_text [String]
|
|
164
|
+
# @return [String]
|
|
165
|
+
# @raise [Rbpptx::Error] if the notes page has no body placeholder
|
|
166
|
+
def text=(new_text)
|
|
167
|
+
ph = notes_placeholder or raise Error, "#{@part_name}: notes slide has no body placeholder"
|
|
168
|
+
ph.text = new_text
|
|
76
169
|
end
|
|
77
170
|
end
|
|
78
171
|
end
|
data/lib/rbpptx/table.rb
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module Rbpptx
|
|
2
|
+
# A table: a +<p:graphicFrame>+ whose graphic data is an +<a:tbl>+.
|
|
3
|
+
#
|
|
4
|
+
# Only cell text is exposed. Grid geometry, table styles, borders and fills
|
|
5
|
+
# are left untouched; adding or removing rows and columns is out of scope.
|
|
6
|
+
#
|
|
7
|
+
# table = slide.tables.first
|
|
8
|
+
# table.cell(0, 1).text # => "Q2"
|
|
9
|
+
# table.cell(1, 1).text = "42"
|
|
10
|
+
# table.rows.map { |r| r.cells.map(&:text) }
|
|
11
|
+
class Table
|
|
12
|
+
P_NS = "http://schemas.openxmlformats.org/presentationml/2006/main".freeze
|
|
13
|
+
A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main".freeze
|
|
14
|
+
|
|
15
|
+
# @param node [Nokogiri::XML::Element] the +<p:graphicFrame>+ element
|
|
16
|
+
# @param slide [Slide, NotesSlide, nil] owning part
|
|
17
|
+
def initialize(node:, slide: nil)
|
|
18
|
+
@node = node
|
|
19
|
+
@slide = slide
|
|
20
|
+
@tbl = node.at_xpath("./a:graphic/a:graphicData/a:tbl", "a" => A_NS)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String, nil] value of +<p:nvGraphicFramePr>/<p:cNvPr name>+
|
|
24
|
+
def name
|
|
25
|
+
@node.at_xpath("./p:nvGraphicFramePr/p:cNvPr/@name", "p" => P_NS)&.value
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Array<TableRow>]
|
|
29
|
+
def rows
|
|
30
|
+
@tbl.xpath("./a:tr", "a" => A_NS).map { |tr| TableRow.new(node: tr, slide: @slide) }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Integer]
|
|
34
|
+
def row_count
|
|
35
|
+
@tbl.xpath("./a:tr", "a" => A_NS).size
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Integer] number of grid columns (+<a:tblGrid>/<a:gridCol>+)
|
|
39
|
+
def column_count
|
|
40
|
+
@tbl.xpath("./a:tblGrid/a:gridCol", "a" => A_NS).size
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param row [Integer] zero-based row index
|
|
44
|
+
# @param col [Integer] zero-based column index
|
|
45
|
+
# @return [TableCell, nil] +nil+ when out of range
|
|
46
|
+
def cell(row, col)
|
|
47
|
+
rows[row]&.cells&.[](col)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# A table row (+<a:tr>+).
|
|
52
|
+
class TableRow
|
|
53
|
+
A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main".freeze
|
|
54
|
+
|
|
55
|
+
def initialize(node:, slide: nil)
|
|
56
|
+
@node = node
|
|
57
|
+
@slide = slide
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @return [Array<TableCell>] one per grid column, including cells that
|
|
61
|
+
# are covered by a horizontal or vertical merge (see
|
|
62
|
+
# {TableCell#spanned?})
|
|
63
|
+
def cells
|
|
64
|
+
@node.xpath("./a:tc", "a" => A_NS).map { |tc| TableCell.new(node: tc, slide: @slide) }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# A table cell (+<a:tc>+). Text access is identical to {Shape}; see
|
|
69
|
+
# {TextBody}.
|
|
70
|
+
class TableCell
|
|
71
|
+
include TextBody
|
|
72
|
+
|
|
73
|
+
A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main".freeze
|
|
74
|
+
|
|
75
|
+
def initialize(node:, slide: nil)
|
|
76
|
+
@node = node
|
|
77
|
+
@slide = slide
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @return [Boolean] whether this cell is the top-left origin of a merged
|
|
81
|
+
# range (+gridSpan+ or +rowSpan+ greater than 1)
|
|
82
|
+
def merge_origin?
|
|
83
|
+
@node["gridSpan"].to_i > 1 || @node["rowSpan"].to_i > 1
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Boolean] whether this cell is hidden under a merged range
|
|
87
|
+
# (+hMerge+ or +vMerge+ set). PowerPoint does not render its text.
|
|
88
|
+
def spanned?
|
|
89
|
+
truthy?(@node["hMerge"]) || truthy?(@node["vMerge"])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def truthy?(value)
|
|
95
|
+
value == "1" || value == "true"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def text_body_node
|
|
99
|
+
@node.at_xpath("./a:txBody", "a" => A_NS)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def text_body_label
|
|
103
|
+
"table cell has no <a:txBody>"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require "cgi"
|
|
2
|
+
|
|
3
|
+
module Rbpptx
|
|
4
|
+
# Text access shared by everything that owns a DrawingML text body:
|
|
5
|
+
# {Shape} (+<p:txBody>+) and {TableCell} (+<a:txBody>+).
|
|
6
|
+
#
|
|
7
|
+
# Including classes provide a private +text_body_node+ returning the text
|
|
8
|
+
# body element (or +nil+), a private +text_body_label+ used in error
|
|
9
|
+
# messages, and an +@slide+ owner that responds to +mark_dirty!+.
|
|
10
|
+
module TextBody
|
|
11
|
+
A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main".freeze
|
|
12
|
+
|
|
13
|
+
# @return [Array<Paragraph>] paragraphs in the text body, in document
|
|
14
|
+
# order. Returns +[]+ when there is no text body.
|
|
15
|
+
def paragraphs
|
|
16
|
+
txbody = text_body_node
|
|
17
|
+
return [] unless txbody
|
|
18
|
+
|
|
19
|
+
txbody.xpath("./a:p", "a" => A_NS).map { |p| Paragraph.new(node: p, slide: @slide) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [Array<Run>] all runs in the text body, flattened across
|
|
23
|
+
# paragraphs in document order
|
|
24
|
+
def runs
|
|
25
|
+
paragraphs.flat_map(&:runs)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Concatenated text of the text body. Paragraphs are joined with
|
|
29
|
+
# +"\n"+; soft line breaks (+<a:br>+) inside a paragraph are also
|
|
30
|
+
# rendered as +"\n"+.
|
|
31
|
+
#
|
|
32
|
+
# @return [String]
|
|
33
|
+
def text
|
|
34
|
+
paragraphs.map(&:text).join("\n")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Replaces the text body. Lines (separated by +"\n"+) become individual
|
|
38
|
+
# +<a:p>+ paragraphs. Template formatting carries through:
|
|
39
|
+
#
|
|
40
|
+
# * run properties from the first existing +<a:rPr>+ (font, size, color,
|
|
41
|
+
# language) are cloned onto each new run. When the body has no runs —
|
|
42
|
+
# an empty form field or table cell — the first +<a:endParaRPr>+ is
|
|
43
|
+
# used instead, since that is where PowerPoint keeps the formatting
|
|
44
|
+
# of an empty paragraph;
|
|
45
|
+
# * the first paragraph's +<a:pPr>+ (alignment, indent, bullets) and
|
|
46
|
+
# +<a:endParaRPr>+ are cloned onto each new paragraph.
|
|
47
|
+
#
|
|
48
|
+
# This is the bulldozer-level setter; to preserve individual run
|
|
49
|
+
# formatting use {#runs} and {Run#text=} instead.
|
|
50
|
+
#
|
|
51
|
+
# @param new_text [String]
|
|
52
|
+
# @return [String] the value that was set
|
|
53
|
+
# @raise [Rbpptx::Error] if there is no text body
|
|
54
|
+
def text=(new_text)
|
|
55
|
+
txbody = text_body_node
|
|
56
|
+
raise Error, "#{text_body_label}; cannot set text" unless txbody
|
|
57
|
+
|
|
58
|
+
template_rpr = txbody.at_xpath(".//a:r/a:rPr", "a" => A_NS)
|
|
59
|
+
first_p = txbody.at_xpath("./a:p", "a" => A_NS)
|
|
60
|
+
template_ppr = first_p&.at_xpath("./a:pPr", "a" => A_NS)
|
|
61
|
+
template_end = first_p&.at_xpath("./a:endParaRPr", "a" => A_NS)
|
|
62
|
+
template_rpr ||= txbody.at_xpath(".//a:p/a:endParaRPr", "a" => A_NS)&.dup&.tap { |n| n.name = "rPr" }
|
|
63
|
+
txbody.xpath("./a:p", "a" => A_NS).each(&:remove)
|
|
64
|
+
|
|
65
|
+
lines = new_text.to_s.empty? ? [""] : new_text.to_s.split("\n", -1)
|
|
66
|
+
txbody.add_child(build_paragraphs_xml(lines))
|
|
67
|
+
|
|
68
|
+
txbody.xpath("./a:p", "a" => A_NS).each do |para|
|
|
69
|
+
para.prepend_child(template_ppr.dup) if template_ppr
|
|
70
|
+
para.add_child(template_end.dup) if template_end
|
|
71
|
+
next unless template_rpr
|
|
72
|
+
|
|
73
|
+
para.xpath("./a:r", "a" => A_NS).each { |run| run.prepend_child(template_rpr.dup) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
@slide&.mark_dirty!
|
|
77
|
+
new_text
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def build_paragraphs_xml(lines)
|
|
83
|
+
lines.map do |line|
|
|
84
|
+
if line.empty?
|
|
85
|
+
"<a:p/>"
|
|
86
|
+
else
|
|
87
|
+
"<a:p><a:r><a:t>#{CGI.escapeHTML(line)}</a:t></a:r></a:p>"
|
|
88
|
+
end
|
|
89
|
+
end.join
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/rbpptx/version.rb
CHANGED
data/lib/rbpptx.rb
CHANGED
|
@@ -2,9 +2,13 @@ require "nokogiri"
|
|
|
2
2
|
require "zip"
|
|
3
3
|
|
|
4
4
|
require_relative "rbpptx/errors"
|
|
5
|
+
require_relative "rbpptx/package"
|
|
6
|
+
require_relative "rbpptx/text_body"
|
|
5
7
|
require_relative "rbpptx/run"
|
|
6
8
|
require_relative "rbpptx/paragraph"
|
|
9
|
+
require_relative "rbpptx/shape_container"
|
|
7
10
|
require_relative "rbpptx/shape"
|
|
11
|
+
require_relative "rbpptx/table"
|
|
8
12
|
require_relative "rbpptx/slide"
|
|
9
13
|
require_relative "rbpptx/presentation"
|
|
10
14
|
require_relative "rbpptx/version"
|
data/sig/rbpptx.rbs
CHANGED
|
@@ -29,51 +29,189 @@ module Rbpptx
|
|
|
29
29
|
P_NS: String
|
|
30
30
|
A_NS: String
|
|
31
31
|
R_NS: String
|
|
32
|
+
P14_NS: String
|
|
32
33
|
PKG_REL_NS: String
|
|
33
34
|
OFFICE_DOC_REL_TYPE: String
|
|
34
35
|
SLIDE_REL_TYPE: String
|
|
36
|
+
NOTES_SLIDE_REL_TYPE: String
|
|
37
|
+
NOTES_MASTER_REL_TYPE: String
|
|
38
|
+
NOTES_SLIDE_CONTENT_TYPE: String
|
|
39
|
+
LEGACY_PPT_SIGNATURE: String
|
|
40
|
+
ZIP_SIGNATURE: String
|
|
35
41
|
|
|
36
42
|
attr_reader path: String
|
|
37
43
|
|
|
38
44
|
def self.open: (pathish path) -> Presentation
|
|
39
45
|
def initialize: (pathish path) -> void
|
|
40
46
|
def slides: () -> Array[Slide]
|
|
47
|
+
def slide: (Integer index) -> Slide
|
|
48
|
+
def move_slide: (Slide | Integer slide_or_index, Integer new_index) -> Array[Slide]
|
|
49
|
+
def delete_slide: (Slide | Integer slide_or_index, ?unlink: bool) -> Slide
|
|
41
50
|
def save: (pathish path) -> String
|
|
42
51
|
def close: () -> bool
|
|
43
52
|
def closed?: () -> bool
|
|
53
|
+
def create_notes_slide_for: (Slide slide) -> NotesSlide
|
|
44
54
|
end
|
|
45
55
|
|
|
46
|
-
|
|
56
|
+
# @api private
|
|
57
|
+
class Package
|
|
58
|
+
PKG_REL_NS: String
|
|
59
|
+
CT_NS: String
|
|
60
|
+
CONTENT_TYPES_PART: String
|
|
61
|
+
SAVE_OPTIONS: Integer
|
|
62
|
+
|
|
63
|
+
class Relationship < Struct[untyped]
|
|
64
|
+
attr_accessor id: String
|
|
65
|
+
attr_accessor type: String
|
|
66
|
+
attr_accessor target: String
|
|
67
|
+
attr_accessor part: String?
|
|
68
|
+
attr_accessor external: bool
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
attr_reader path: String
|
|
72
|
+
|
|
73
|
+
def initialize: (String path) -> void
|
|
74
|
+
def close: () -> void
|
|
75
|
+
def part?: (String name) -> bool
|
|
76
|
+
def part_names: () -> Array[String]
|
|
77
|
+
def xml: (String name, ?error_class: singleton(Error)) -> untyped
|
|
78
|
+
def touch: (String name) -> true
|
|
79
|
+
def add: (String name, untyped doc) -> untyped
|
|
80
|
+
def delete: (String name) -> void
|
|
81
|
+
def rels_part_for: (String part) -> String
|
|
82
|
+
def relationships: (String part) -> Array[Relationship]
|
|
83
|
+
def related: (String part, String rel_type) -> Array[String]
|
|
84
|
+
def referrers_of: (String target_part) -> Array[String]
|
|
85
|
+
def add_relationship: (String source, String rel_type, String target_part) -> String
|
|
86
|
+
def remove_relationship: (String source, String rid) -> void
|
|
87
|
+
def resolve: (String source, String target) -> String
|
|
88
|
+
def relative_target: (String source, String target_part) -> String
|
|
89
|
+
def add_content_type_override: (String part, String content_type) -> void
|
|
90
|
+
def remove_content_type_override: (String part) -> void
|
|
91
|
+
def save: (String out_path) -> String
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
interface _DirtyTracking
|
|
95
|
+
def mark_dirty!: () -> bool
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
module TextBody
|
|
99
|
+
A_NS: String
|
|
100
|
+
|
|
101
|
+
def paragraphs: () -> Array[Paragraph]
|
|
102
|
+
def runs: () -> Array[Run]
|
|
103
|
+
def text: () -> String
|
|
104
|
+
def text=: (String new_text) -> String
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
module ShapeContainer
|
|
47
108
|
P_NS: String
|
|
109
|
+
A_NS: String
|
|
110
|
+
|
|
111
|
+
def shapes: () -> Array[Shape]
|
|
112
|
+
def groups: () -> Array[GroupShape]
|
|
113
|
+
def tables: () -> Array[Table]
|
|
114
|
+
def all_shapes: () -> Array[Shape]
|
|
115
|
+
def all_tables: () -> Array[Table]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class SlidePart
|
|
119
|
+
include ShapeContainer
|
|
48
120
|
|
|
49
121
|
attr_reader part_name: String
|
|
50
122
|
|
|
51
|
-
def initialize: (
|
|
52
|
-
def
|
|
53
|
-
def shapes: () -> Array[Shape]
|
|
123
|
+
def initialize: (package: Package, part_name: String) -> void
|
|
124
|
+
def placeholders: () -> Placeholders
|
|
54
125
|
def dirty?: () -> bool
|
|
55
126
|
def mark_dirty!: () -> bool
|
|
56
127
|
def clear_dirty!: () -> bool
|
|
57
128
|
def to_xml: () -> String
|
|
58
129
|
end
|
|
59
130
|
|
|
131
|
+
class Slide < SlidePart
|
|
132
|
+
NOTES_SLIDE_REL_TYPE: String
|
|
133
|
+
|
|
134
|
+
def initialize: (package: Package, part_name: String, ?presentation: Presentation?) -> void
|
|
135
|
+
def name: () -> String
|
|
136
|
+
def title: () -> Shape?
|
|
137
|
+
def notes_slide: () -> NotesSlide?
|
|
138
|
+
def notes: () -> String
|
|
139
|
+
def notes=: (String new_text) -> String
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class NotesSlide < SlidePart
|
|
143
|
+
def notes_placeholder: () -> Shape?
|
|
144
|
+
def text: () -> String
|
|
145
|
+
def text=: (String new_text) -> String
|
|
146
|
+
end
|
|
147
|
+
|
|
60
148
|
class Shape
|
|
149
|
+
include TextBody
|
|
150
|
+
|
|
61
151
|
P_NS: String
|
|
62
152
|
A_NS: String
|
|
63
153
|
|
|
64
|
-
def initialize: (node: untyped, ?slide:
|
|
154
|
+
def initialize: (node: untyped, ?slide: _DirtyTracking?) -> void
|
|
65
155
|
def name: () -> String?
|
|
156
|
+
def placeholder?: () -> bool
|
|
66
157
|
def placeholder_type: () -> Symbol?
|
|
67
|
-
def
|
|
68
|
-
|
|
158
|
+
def placeholder_idx: () -> Integer?
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
class GroupShape
|
|
162
|
+
include ShapeContainer
|
|
163
|
+
|
|
164
|
+
def initialize: (node: untyped, ?slide: _DirtyTracking?) -> void
|
|
165
|
+
def name: () -> String?
|
|
69
166
|
def text: () -> String
|
|
70
|
-
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
class Placeholders
|
|
170
|
+
include Enumerable[Shape]
|
|
171
|
+
|
|
172
|
+
def initialize: (Array[Shape] shapes) -> void
|
|
173
|
+
def each: () { (Shape) -> void } -> self
|
|
174
|
+
| () -> Enumerator[Shape, self]
|
|
175
|
+
def []: (Symbol | Integer key) -> Shape?
|
|
176
|
+
def size: () -> Integer
|
|
177
|
+
def length: () -> Integer
|
|
178
|
+
def empty?: () -> bool
|
|
179
|
+
def to_a: () -> Array[Shape]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class Table
|
|
183
|
+
P_NS: String
|
|
184
|
+
A_NS: String
|
|
185
|
+
|
|
186
|
+
def initialize: (node: untyped, ?slide: _DirtyTracking?) -> void
|
|
187
|
+
def name: () -> String?
|
|
188
|
+
def rows: () -> Array[TableRow]
|
|
189
|
+
def row_count: () -> Integer
|
|
190
|
+
def column_count: () -> Integer
|
|
191
|
+
def cell: (Integer row, Integer col) -> TableCell?
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
class TableRow
|
|
195
|
+
A_NS: String
|
|
196
|
+
|
|
197
|
+
def initialize: (node: untyped, ?slide: _DirtyTracking?) -> void
|
|
198
|
+
def cells: () -> Array[TableCell]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
class TableCell
|
|
202
|
+
include TextBody
|
|
203
|
+
|
|
204
|
+
A_NS: String
|
|
205
|
+
|
|
206
|
+
def initialize: (node: untyped, ?slide: _DirtyTracking?) -> void
|
|
207
|
+
def merge_origin?: () -> bool
|
|
208
|
+
def spanned?: () -> bool
|
|
71
209
|
end
|
|
72
210
|
|
|
73
211
|
class Paragraph
|
|
74
212
|
A_NS: String
|
|
75
213
|
|
|
76
|
-
def initialize: (node: untyped, ?slide:
|
|
214
|
+
def initialize: (node: untyped, ?slide: _DirtyTracking?) -> void
|
|
77
215
|
def runs: () -> Array[Run]
|
|
78
216
|
def text: () -> String
|
|
79
217
|
end
|
|
@@ -81,7 +219,7 @@ module Rbpptx
|
|
|
81
219
|
class Run
|
|
82
220
|
A_NS: String
|
|
83
221
|
|
|
84
|
-
def initialize: (node: untyped, ?slide:
|
|
222
|
+
def initialize: (node: untyped, ?slide: _DirtyTracking?) -> void
|
|
85
223
|
def text: () -> String
|
|
86
224
|
def text=: (String new_text) -> String
|
|
87
225
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rbpptx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Taro KOBAYASHI
|
|
@@ -13,16 +13,22 @@ dependencies:
|
|
|
13
13
|
name: rubyzip
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
18
|
version: '2.3'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '4'
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
25
|
requirements:
|
|
23
|
-
- - "
|
|
26
|
+
- - ">="
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
28
|
version: '2.3'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '4'
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
33
|
name: nokogiri
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -57,11 +63,15 @@ files:
|
|
|
57
63
|
- Rakefile
|
|
58
64
|
- lib/rbpptx.rb
|
|
59
65
|
- lib/rbpptx/errors.rb
|
|
66
|
+
- lib/rbpptx/package.rb
|
|
60
67
|
- lib/rbpptx/paragraph.rb
|
|
61
68
|
- lib/rbpptx/presentation.rb
|
|
62
69
|
- lib/rbpptx/run.rb
|
|
63
70
|
- lib/rbpptx/shape.rb
|
|
71
|
+
- lib/rbpptx/shape_container.rb
|
|
64
72
|
- lib/rbpptx/slide.rb
|
|
73
|
+
- lib/rbpptx/table.rb
|
|
74
|
+
- lib/rbpptx/text_body.rb
|
|
65
75
|
- lib/rbpptx/version.rb
|
|
66
76
|
- sig/rbpptx.rbs
|
|
67
77
|
homepage: https://github.com/matzlika/rbpptx
|