Class: Ballmer::Presentation::Slides

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/ballmer/presentation/slides.rb

Overview

Manages concerns around keeping slide and notesSlides files in sync with an array of slides. These basically needs to trasnact the slided+ and slideNoted+ numbers to be in sync with an array. Its a big, ugly ass complicated beast. Send your thank you cards to Bill Gates.

Instance Method Summary collapse

Constructor Details

#initialize(doc) ⇒ Slides

Returns a new instance of Slides.



10
11
12
# File 'lib/ballmer/presentation/slides.rb', line 10

def initialize(doc)
  @doc = doc
end

Instance Method Details

#delete(slide) ⇒ Object

Removes a slide from the slides collection



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/ballmer/presentation/slides.rb', line 111

def delete(slide)
  #   ./_rels/presentation.xml.rels
  #     Update Relationship ids
  #     Insert a new one slideRef
  @doc.edit_xml @doc.presentation.rels.path do |xml|
    # Calucate the next id
      # next_id = xml.xpath('//xmlns:Relationship[@Id]').map{ |n| n['Id'] }.sort.last.succ
    # TODO - Figure out how to make this more MS idiomatic up 9->10 instead of incrementing
    # the character....
    # Insert that into the slide and crakc open the presentation.xml file

    target = slide.path.relative_path_from(@doc.presentation.path.dirname)
    relationship = xml.at_xpath("/xmlns:Relationships/xmlns:Relationship[@Type='#{Slide::REL_TYPE}' and @Target='#{target}']")
    #   ./presentation.xml
    #     Update attr
    #       p:notesMasterId
    #     Insert attr
    #       p:sldId, increment, etc.
    @doc.edit_xml '/ppt/presentation.xml' do |xml|
      xml.at_xpath("/p:presentation/p:sldIdLst/p:sldId[@r:id='#{relationship['Id']}']").remove
    end
    relationship.remove
  end

  # Delete slide link and slideNotes link from ./[Content-Types].xml 
  @doc.edit_xml @doc.content_types.path do |xml|
    xml.at_xpath("/xmlns:Types/xmlns:Override[@ContentType='#{Slide::CONTENT_TYPE}' and @PartName='#{slide.path}']").remove
    xml.at_xpath("/xmlns:Types/xmlns:Override[@ContentType='#{Notes::CONTENT_TYPE}' and @PartName='#{slide.notes.path}']").remove
  end

  # Update ./ppt
  #   !!! DESTROY !!!
  #   ./slides
  #     Delete files
  #       ./_rels/notesSlide(\d+).xml.rels
  @doc.delete slide.notes.rels.path
  #       ./notesSlide(\d+).xml file
  @doc.delete slide.notes.path
  #       ./_rels/slide(\d+).xml.rels
  @doc.delete slide.rels.path
  #       ./slide(\d+).xml file
  @doc.delete slide.path
  #   ./notesSlides
  #     Delete files

  # Hooray! We're done! Ummm, what should we return though? can't be the slide since
  # its destroyed and there's no practical way to keep it around in memory.
end

#each(&block) ⇒ Object



14
15
16
17
# File 'lib/ballmer/presentation/slides.rb', line 14

def each(&block)
  # TODO - Do NOT read content-types, but read Rels instead (and move this type casting in there.)
  slides.each { |path| block.call slide path }
end

#push(slide) ⇒ Object

This method is crazy because it has to manipulate a ton of files within the PPTX. Most of what happens in here I figured out by diff-ing PPTX files that had copies of identical slides, but a different number of slides.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ballmer/presentation/slides.rb', line 27

def push(slide)
  n = to_a.size + 1
  # Paths within the zip file of new files we have to write.
  slide_path = Pathname.new("/ppt/slides/slide#{n}.xml")
  slide_rels_path = Pathname.new("/ppt/slides/_rels/slide#{n}.xml.rels")
  slide_notes_path = Pathname.new("/ppt/notesSlides/notesSlide#{n}.xml")
  slide_notes_rels_path = Pathname.new("/ppt/notesSlides/_rels/notesSlide#{n}.xml.rels")

  # Update ./ppt
  #   !!! CREATE !!!
  #   ./slides
  #     Create new files
  #       ./slide(\d+).xml file
  @doc.copy slide_path, slide.path
  #       ./_rels/slide(\d+).xml.rels
  @doc.copy slide_rels_path, slide.rels.path
  #   ./notesSlides
  #     Create new files
  #       ./notesSlide(\d+).xml file
  @doc.copy slide_notes_path, slide.notes.path
  #       ./_rels/notesSlide(\d+).xml.rels
  @doc.copy slide_notes_rels_path, slide.notes.rels.path
  
  #   !!! UPDATES !!!
  # Update the notes in the new slide to point at the new notes
  @doc.edit_xml slide_rels_path do |xml|
    # TODO - Move this rel logic into the parts so that we don't have to repeat ourselves when calculating this stuff out.
    xml.at_xpath("//xmlns:Relationship[@Type='#{Notes::REL_TYPE}']")['Target'] = slide_notes_path.relative_path_from(slide_path.dirname)
  end

  # Update teh slideNotes reference to point at the new slide
  @doc.edit_xml slide_notes_rels_path do |xml|
    xml.at_xpath("//xmlns:Relationship[@Type='#{Slide::REL_TYPE}']")['Target'] = slide_path.relative_path_from(slide_notes_path.dirname)
  end

  #   ./_rels/presentation.xml.rels
  #     Update Relationship ids
  #     Insert a new one slideRef
  @doc.edit_xml @doc.presentation.rels.path do |xml|
    # Calucate the next id
    next_id = xml.xpath('//xmlns:Relationship[@Id]').map{ |n| n['Id'] }.sort.last.succ
    # TODO - Figure out how to make this more MS idiomatic up 9->10 instead of incrementing
    # the character....
    # Insert that into the slide and crakc open the presentation.xml file
    types = xml.at_xpath('/xmlns:Relationships')
    types << Nokogiri::XML::Node.new("Relationship", xml).tap do |n|
      n['Id'] = next_id
      n['Type'] = Slide::REL_TYPE
      n['Target'] = slide_path.relative_path_from(@doc.presentation.path.dirname)
    end
    #   ./presentation.xml
    #     Update attr
    #       p:notesMasterId
    #     Insert attr
    #       p:sldId, increment, etc.
    @doc.edit_xml '/ppt/presentation.xml' do |xml|
      slides = xml.at_xpath('/p:presentation/p:sldIdLst')
      next_slide_id = slides.xpath('//p:sldId[@id]').map{ |n| n['id'] }.sort.last.succ
      slides << Nokogiri::XML::Node.new("p:sldId", xml).tap do |n|
        # TODO - Fix the ID that's jacked up.
        n['id'] = next_slide_id
        n['r:id'] = next_id
      end
    end
  end

  # Update ./[Content-Types].xml with new slide link and slideNotes link
  @doc.edit_xml @doc.content_types.path do |xml|
    types = xml.at_xpath('/xmlns:Types')
    types << Nokogiri::XML::Node.new("Override", xml).tap do |n|
      n['PartName'] = slide_path
      n['ContentType'] = Slide::CONTENT_TYPE
    end
    types << Nokogiri::XML::Node.new("Override", xml).tap do |n|
      n['PartName'] = slide_notes_path
      n['ContentType'] = Notes::CONTENT_TYPE
    end
  end

  # Great, that's all done, so lets return the slide eh?
  slide slide_path
end

#sizeObject Also known as: length



19
20
21
# File 'lib/ballmer/presentation/slides.rb', line 19

def size
  slides.size
end