pptxt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: