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.
data/lib/rbpptx/slide.rb CHANGED
@@ -1,78 +1,171 @@
1
1
  module Rbpptx
2
- # A single slide loaded from a presentation's ZIP container.
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 of the slide's XML payload is deferred until {#shapes} is first
5
- # called and then cached, so simply enumerating slides via
6
- # {Presentation#slides} does not pay the per-slide parse cost.
7
- class Slide
8
- P_NS = "http://schemas.openxmlformats.org/presentationml/2006/main".freeze
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 of the slide inside the package, e.g.
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 zip [Zip::File] the open package
15
+ # @param package [Package] the open package
15
16
  # @param part_name [String] absolute part name (no leading slash)
16
- def initialize(zip:, part_name:)
17
- @zip = zip
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 [String] the slide's basename without extension, e.g. +"slide1"+
23
- def name
24
- File.basename(@part_name, ".xml")
25
- end
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 slide has been mutated since
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 slide as dirty. Called by {Shape} setters; not part of 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 ||= parse_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
- def parse_doc
65
- entry = @zip.find_entry(@part_name)
66
- raise SlideFormatError, "missing slide part #{@part_name}" unless entry
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
- parsed = Nokogiri::XML(entry.get_input_stream.read)
69
- raise SlideFormatError, "#{@part_name}: #{parsed.errors.first}" unless parsed.errors.empty?
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
- parsed
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
- def build_shapes
75
- doc.xpath("/p:sld/p:cSld/p:spTree/p:sp", "p" => P_NS).map { |sp| Shape.new(node: sp, slide: self) }
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
@@ -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
@@ -1,4 +1,4 @@
1
1
  module Rbpptx
2
2
  # Gem version string, tracked with semantic versioning.
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
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
- class Slide
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: (zip: untyped, part_name: String) -> void
52
- def name: () -> String
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: Slide?) -> void
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 paragraphs: () -> Array[Paragraph]
68
- def runs: () -> Array[Run]
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
- def text=: (String new_text) -> String
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: Slide?) -> void
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: Slide?) -> void
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.1.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