pptxt 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af8adcaf1ae4e8b2e76aaadff580d8d0ff48534f
4
+ data.tar.gz: 3324a84c53ff4ae4865a92530d62875c0a662492
5
+ SHA512:
6
+ metadata.gz: fabd38653a74ae682deb4e74e081cca977a50f3cb4186c5b3daba60e98f4f2ffda3e976ec54a003742732fa7f85c8c7a1060d0f06ed56a56b86c472933688469
7
+ data.tar.gz: 974f4152ce33df205b76afaa4fd669fb052de111e6ee6fe6f059dc1d01bcb5c025b77a4d3b95401b3cff7088c2cf83b1e081e2e12bffafecebd8f7bb455a6e7b
data/bin/pptxt ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "io/wait"
4
+ require "optparse"
5
+ require "pathname"
6
+ require "pptxt"
7
+ require "pptxt_exit_status"
8
+ require "pptxt_slide"
9
+
10
+ def parse(args)
11
+ options = Hash.new
12
+ options["detailed"] = false
13
+ options["git"] = false
14
+ options["pptx"] = nil
15
+ options["slideshow"] = false
16
+
17
+ parser = OptionParser.new do |opts|
18
+ opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [pptx]"
19
+
20
+ opts.on(
21
+ "-c",
22
+ "--configure",
23
+ "Configure git repo to use pptxt"
24
+ ) do
25
+ PPtxt.configure_git
26
+ exit PPtxtExitStatus::GOOD
27
+ end
28
+
29
+ opts.on("-d", "--detailed", "Display full xml") do
30
+ options["detailed"] = true
31
+ end
32
+
33
+ opts.on("--git", "Hide the slide dividers for git-diff") do
34
+ options["git"] = true
35
+ end
36
+
37
+ opts.on(
38
+ "-g",
39
+ "--global-config",
40
+ "Configure git to use pptxt globally"
41
+ ) do
42
+ PPtxt.configure_git(true)
43
+ exit PPtxtExitStatus::GOOD
44
+ end
45
+
46
+ opts.on("-h", "--help", "Display this help message") do
47
+ puts opts
48
+ exit PPtxtExitStatus::GOOD
49
+ end
50
+
51
+ opts.on("-s", "--slideshow", "Display as slideshow") do
52
+ options["slideshow"] = true
53
+ end
54
+ end
55
+
56
+ begin
57
+ parser.parse!
58
+ rescue OptionParser::InvalidOption => e
59
+ puts e.message
60
+ puts parser
61
+ exit PPtxtExitStatus::INVALID_OPTION
62
+ rescue OptionParser::InvalidArgument => e
63
+ puts e.message
64
+ puts parser
65
+ exit PPtxtExitStatus::INVALID_ARGUMENT
66
+ rescue OptionParser::MissingArgument => e
67
+ puts e.message
68
+ puts parser
69
+ exit PPtxtExitStatus::MISSING_ARGUMENT
70
+ end
71
+
72
+ if (args.empty?)
73
+ puts parser
74
+ exit PPtxtExitStatus::MISSING_ARGUMENT
75
+ elsif (args.length > 1)
76
+ puts parser
77
+ exit PPtxtExitStatus::EXTRA_ARGUMENTS
78
+ end
79
+
80
+ if (!Pathname.new(args[0]).expand_path.exist?)
81
+ puts "#{args[0]} does not exist!"
82
+ exit PPtxtExitStatus::FILE_DOES_NOT_EXIST
83
+ end
84
+
85
+ options["pptx"] = args[0].strip
86
+ if (options["pptx"] == "/dev/null")
87
+ exit PPtxtExitStatus::GOOD
88
+ end
89
+
90
+ return options
91
+ end
92
+
93
+ # Parse CLI args
94
+ options = parse(ARGV)
95
+
96
+ # Get slides
97
+ slides = PPtxt.new(options["pptx"]).slides
98
+
99
+ if (!options["slideshow"])
100
+ # Loop through slides
101
+ slides.each do |slide|
102
+ if (options["detailed"])
103
+ puts slide.detailed
104
+ elsif (options["git"])
105
+ puts slide.diffable
106
+ puts
107
+ else
108
+ puts slide
109
+ puts
110
+ end
111
+ end
112
+ else
113
+ quit = false
114
+ count = 0
115
+
116
+ while (!quit)
117
+ slide = slides[count]
118
+
119
+ # Make it human readable and parse
120
+ system("clear")
121
+ puts
122
+ puts slide
123
+ puts
124
+ puts "j:Next k:Previous q:Quit"
125
+
126
+ answer = nil
127
+ while (!answer)
128
+ begin
129
+ system("stty raw -echo")
130
+ if $stdin.ready?
131
+ answer = $stdin.getc.chr
132
+ else
133
+ sleep 0.1
134
+ end
135
+ ensure
136
+ system("stty -raw echo")
137
+ end
138
+ end
139
+ puts
140
+
141
+ case answer
142
+ when "j", "J"
143
+ count += 1 if (count < (slides.length - 1))
144
+ when "k", "K"
145
+ count -= 1 if (count > 0)
146
+ when "q", "Q", "\x03"
147
+ # Quit or ^C
148
+ quit = true
149
+ end
150
+ end
151
+ end
data/lib/pptxt.rb ADDED
@@ -0,0 +1,76 @@
1
+ require "pptxt_error"
2
+ require "pptxt_slide"
3
+ require "scoobydoo"
4
+
5
+ class PPtxt
6
+ attr_accessor :slides
7
+
8
+ def self.configure_git(global = false)
9
+ if (ScoobyDoo.where_are_you("git").nil?)
10
+ raise PPtxtError::MissingDependency.new("git")
11
+ end
12
+
13
+ # Configure git
14
+ flag = ""
15
+ flag = "--global" if (global)
16
+ system(
17
+ "git config #{flag} diff.pptxt.textconv \"pptxt --git\""
18
+ )
19
+
20
+ # Setup .gitattributes
21
+ filename = ".gitattributes"
22
+ if (global)
23
+ cfg = "git config --global core.attributesfile"
24
+ filename = %x(#{cfg}).strip
25
+ if (filename.nil? || filename.empty?)
26
+ filename = "~/.gitattributes"
27
+ system("#{cfg} \"#{filename}\"")
28
+ end
29
+ end
30
+ new_line = "*.pptx diff=pptxt\n"
31
+
32
+ file = Pathname.new(filename).expand_path
33
+ if (file.exist?)
34
+ File.open(file) do |f|
35
+ f.each_line do |line|
36
+ if (line == new_line)
37
+ return
38
+ end
39
+ end
40
+ end
41
+ File.open(file, "a") do |f|
42
+ f.write(new_line)
43
+ end
44
+ else
45
+ File.open(file, "w") do |f|
46
+ f.write(new_line)
47
+ end
48
+ end
49
+ end
50
+
51
+ def create_slides
52
+ slides = %x(
53
+ unzip -l "#{@pptx}" | \grep -E "ppt/slides/[^_]" |
54
+ awk '{print $4}' | sort -k 1.17n
55
+ ).split("\n")
56
+
57
+ count = 0
58
+ slides.each do |slide|
59
+ xml = %x(unzip -qc "#{@pptx}" #{slide}).gsub("<", "\n<")
60
+ count += 1
61
+ @slides.push(PPtxtSlide.new(xml, count))
62
+ end
63
+ end
64
+ private :create_slides
65
+
66
+ def initialize(pptx)
67
+ @pptx = pptx
68
+ @slides = Array.new
69
+
70
+ if (ScoobyDoo.where_are_you("unzip").nil?)
71
+ raise PPtxtError::MissingDependency.new("unzip")
72
+ end
73
+
74
+ create_slides
75
+ end
76
+ end
@@ -0,0 +1,16 @@
1
+ module PPtxtError
2
+ class Error < RuntimeError
3
+ end
4
+
5
+ class MissingDependency < Error
6
+ def initialize(tool)
7
+ super("Missing dependency: #{tool}")
8
+ end
9
+ end
10
+
11
+ class UnknownXML < Error
12
+ def initialize(line)
13
+ super("Unknown line in xml: #{line}")
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ class PPtxtExitStatus
2
+ GOOD = 0
3
+ INVALID_OPTION = 1
4
+ INVALID_ARGUMENT = 2
5
+ MISSING_ARGUMENT = 3
6
+ EXTRA_ARGUMENTS = 4
7
+ FILE_DOES_NOT_EXIST = 5
8
+ MISSING_UTILITY = 6
9
+ end
@@ -0,0 +1,176 @@
1
+ class PPtxtSlide
2
+ def detailed
3
+ ret = Array.new
4
+ num_indents = 0
5
+
6
+ @xml.each_line do |line|
7
+ line.strip!
8
+ indents = Array.new(num_indents, " ").join
9
+
10
+ case line
11
+ when %r{^<\?.+$}, ""
12
+ # Ignore xml version and blank lines
13
+ when %r{^<.+/> *$}, %r{^<[^/].+>.*</.+> *$}
14
+ # Don't indent if one-liner
15
+ ret.push("#{indents}#{line}")
16
+ when %r{^<[^/].+$}
17
+ # Indent after opening tag
18
+ ret.push("#{indents}#{line}")
19
+ num_indents += 1
20
+ when %r{^</.+> *$}
21
+ # Remove indent after closing tag
22
+ num_indents -= 1
23
+ indents = Array.new(num_indents, " ").join
24
+ ret.push("#{indents}#{line}")
25
+ else
26
+ raise PPtxtError::UnknownXML.new(line)
27
+ end
28
+ end
29
+
30
+ return ret.join("\n")
31
+ end
32
+
33
+ def diffable
34
+ out = Array.new
35
+ out.push(@title) if (!@title.empty?)
36
+ out.push(@subtitle) if (!@subtitle.empty?)
37
+ out.push("\n") if (!@title.empty? || !@subtitle.empty?)
38
+ out.push(@content) if (!@content.empty?)
39
+ return out.join.strip
40
+ end
41
+
42
+ def handle_format(str, format, lvl, list_index)
43
+ case format
44
+ when "bullet"
45
+ return "#{Array.new(lvl, " ").join}- #{str}"
46
+ when "number"
47
+ return "#{Array.new(lvl, " ").join}#{list_index}. #{str}"
48
+ else
49
+ return str
50
+ end
51
+ end
52
+ private :handle_format
53
+
54
+ def initialize(xml, count)
55
+ @content = ""
56
+ @count = count
57
+ @subtitle = ""
58
+ @title = ""
59
+ @xml = xml
60
+ parse_xml
61
+ end
62
+
63
+ def parse_xml
64
+ can_be_newline = false
65
+ first_time = false
66
+ ignore = false
67
+ in_subtitle = false
68
+ in_title = false
69
+ was_newline = true
70
+
71
+ format = "bullet"
72
+ lvl = 0
73
+ numlist_count = Array.new
74
+
75
+ @xml.each_line do |line|
76
+ line.strip!
77
+ case line
78
+ when "<a:p>"
79
+ # Assume bullet list
80
+ format = "bullet"
81
+ when %r{<a:pPr.*lvl="[^"]+".*}
82
+ # Sub bullet/item
83
+ lvl = line[%r{<a:pPr.*lvl="([^"]+)".*}, 1].to_i
84
+ when %r{<.?a:buNone}
85
+ # Regular text
86
+ format = "text"
87
+ when %r{<.?a:buAutoNum}
88
+ # Numbered list
89
+ format = "number"
90
+ when %r{<p:cNvPr .*Title}
91
+ # Setup titles
92
+ in_title = true
93
+ first_time = true
94
+ @title += "# "
95
+ when %r{<p:cNvPr .*Subtitle}
96
+ # Setup subtitles
97
+ in_subtitle = true
98
+ first_time = true
99
+ @subtitle += "## "
100
+ when %r{<a:t>.*}
101
+ # Handle text
102
+ if (in_title)
103
+ @title += " " if (!first_time && was_newline)
104
+ @title += line[5..-1]
105
+ elsif (in_subtitle)
106
+ @subtitle += " " if (!first_time && was_newline)
107
+ @subtitle += line[5..-1]
108
+ elsif(ignore)
109
+ break
110
+ else
111
+ if (was_newline)
112
+ list_index = numlist_count[lvl] || 1
113
+ @content += handle_format(
114
+ line[5..-1],
115
+ format,
116
+ lvl,
117
+ list_index
118
+ )
119
+ if (format == "number")
120
+ numlist_count[lvl] = list_index + 1
121
+ end
122
+ else
123
+ @content += line[5..-1]
124
+ end
125
+ end
126
+
127
+ first_time = false
128
+ was_newline = false
129
+ lvl = 0
130
+ when "</a:t>"
131
+ # Setup newlines
132
+ can_be_newline = true
133
+ when "<a:br>", "</a:p>", "</p:txBody>"
134
+ # Handle newlines
135
+ if (in_title)
136
+ @title += "\n" if (can_be_newline)
137
+ elsif (in_subtitle)
138
+ @subtitle += "\n" if (can_be_newline)
139
+ elsif(ignore)
140
+ break
141
+ else
142
+ @content += "\n" if (can_be_newline)
143
+ end
144
+
145
+ can_be_newline = false
146
+ was_newline = true
147
+
148
+ if (line == "</p:txBody>")
149
+ in_title = false
150
+ in_subtitle = false
151
+ end
152
+ when "<p:graphicFrame>"
153
+ # Ignore graphics for now
154
+ # FIXME
155
+ ignore = true
156
+ when "</p:graphicFrame>"
157
+ ignore = false
158
+ else
159
+ # Ignore
160
+ end
161
+ end
162
+ end
163
+ private :parse_xml
164
+
165
+ def to_s()
166
+ div = Array.new(70, "-").join
167
+ out = Array.new
168
+ out.push("#{div}\n")
169
+ out.push(@title) if (!@title.empty?)
170
+ out.push(@subtitle) if (!@subtitle.empty?)
171
+ out.push("\n") if (!@title.empty? || !@subtitle.empty?)
172
+ out.push(@content) if (!@content.empty?)
173
+ out.push("#{div} #{@count}\n")
174
+ return out.join.strip
175
+ end
176
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pptxt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Miles Whittaker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.8.1
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '5.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.8.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: scoobydoo
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.1.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.1.0
53
+ description: This gem can extract the xml info from a pptx and convert to human-readable
54
+ text. It was intended to be used with git for seeing changes between revisions.
55
+ email: mjwhitta@gmail.com
56
+ executables:
57
+ - pptxt
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - bin/pptxt
62
+ - lib/pptxt.rb
63
+ - lib/pptxt_error.rb
64
+ - lib/pptxt_exit_status.rb
65
+ - lib/pptxt_slide.rb
66
+ homepage: http://mjwhitta.github.io/pptxt
67
+ licenses:
68
+ - GPL-3.0
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.4.8
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Converts pptx files to human-readable text
90
+ test_files: []
91
+ has_rdoc: