origami 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/pdf2pdfa ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+
5
+ = Author:
6
+ Guillaume Delugré <guillaume/at/security-labs.org>
7
+
8
+ = Info:
9
+ Enforces a document to be rendered as PDF/A.
10
+ This will disable multimedia features and JavaScript execution in Adobe Reader.
11
+
12
+ = License:
13
+ Origami is free software: you can redistribute it and/or modify
14
+ it under the terms of the GNU Lesser General Public License as published by
15
+ the Free Software Foundation, either version 3 of the License, or
16
+ (at your option) any later version.
17
+
18
+ Origami is distributed in the hope that it will be useful,
19
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ GNU Lesser General Public License for more details.
22
+
23
+ You should have received a copy of the GNU Lesser General Public License
24
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
25
+
26
+ =end
27
+
28
+ begin
29
+ require 'origami'
30
+ rescue LoadError
31
+ ORIGAMIDIR = "#{File.dirname(__FILE__)}/.."
32
+ $: << ORIGAMIDIR
33
+ require 'origami'
34
+ end
35
+ include Origami
36
+
37
+ require 'optparse'
38
+
39
+ class OptParser
40
+ BANNER = <<USAGE
41
+ Usage: #{$0} [<PDF-file>] [-o <output-file>]
42
+ Enforces a document to be rendered as PDF/A.
43
+ This will disable multimedia features and JavaScript execution in Adobe Reader.
44
+ Bug reports or feature requests at: http://origami-pdf.googlecode.com/
45
+
46
+ Options:
47
+ USAGE
48
+
49
+ def self.parser(options)
50
+ OptionParser.new do |opts|
51
+ opts.banner = BANNER
52
+
53
+ opts.on("-o", "--output FILE", "Output PDF file (stdout by default)") do |o|
54
+ options[:output] = o
55
+ end
56
+
57
+ opts.on_tail("-h", "--help", "Show this message") do
58
+ puts opts
59
+ exit
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.parse(args)
65
+ options =
66
+ {
67
+ :output => STDOUT,
68
+ }
69
+
70
+ self.parser(options).parse!(args)
71
+
72
+ options
73
+ end
74
+ end
75
+
76
+ begin
77
+ @options = OptParser.parse(ARGV)
78
+
79
+ target = (ARGV.empty?) ? STDIN : ARGV.shift
80
+ params =
81
+ {
82
+ :verbosity => Parser::VERBOSE_QUIET,
83
+ }
84
+
85
+ PDF.read(target, params).save(@options[:output], :intent => 'PDF/A', :noindent => true)
86
+ rescue SystemExit
87
+ rescue Exception => e
88
+ STDERR.puts "#{e.class}: #{e.message}"
89
+ exit 1
90
+ end
91
+
data/bin/shell/console.rb CHANGED
@@ -45,12 +45,12 @@ module Origami
45
45
  require 'tempfile'
46
46
 
47
47
  class Stream
48
- def edit(editor = 'vim')
48
+ def edit(editor = ENV['EDITOR'])
49
49
  tmpfile = Tempfile.new("origami")
50
50
  tmpfile.write(self.data)
51
51
  tmpfile.close
52
52
 
53
- Process.wait Kernel.spawn "#{editor} #{tmpfile.path}"
53
+ Process.wait Kernel.spawn "#{editor or 'vim'} #{tmpfile.path}"
54
54
 
55
55
  self.data = File.read(tmpfile.path)
56
56
  tmpfile.unlink
@@ -71,6 +71,20 @@ module Origami
71
71
  end
72
72
 
73
73
  class PDF
74
+
75
+ if defined?(PDF::JavaScript::Engine)
76
+ class JavaScript::Engine
77
+ def shell
78
+ while (print 'js> '; line = gets)
79
+ begin
80
+ puts exec(line)
81
+ rescue V8::JSError => e
82
+ puts "Error: #{e.message}"
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
74
88
 
75
89
  class Revision
76
90
  def to_s
data/origami/catalog.rb CHANGED
@@ -200,9 +200,9 @@ module Origami
200
200
  unless browsed_nodes.any? {|browsed| browsed.equal?(node)}
201
201
  browsed_nodes.push(node)
202
202
  if node.has_key?(:Names) # leaf node
203
- children.concat(node[:Names].solve)
203
+ children.concat(node.Names)
204
204
  elsif node.has_key?(:Kids) # intermediate node
205
- node[:Kids].solve.each do |kid|
205
+ node.Kids.each do |kid|
206
206
  children.concat(names_from_node(kid.solve, browsed_nodes))
207
207
  end
208
208
  end
@@ -216,26 +216,25 @@ module Origami
216
216
  browsed_nodes.push(node)
217
217
 
218
218
  if node.has_key?(:Names) # leaf node
219
- limits = node[:Limits]
219
+ limits = node.Limits
220
220
 
221
221
  if limits
222
- limits = limits.solve
223
222
  min, max = limits[0].value, limits[1].value
224
223
  if (min..max) === name.to_str
225
- names = Hash[*node[:Names].solve]
224
+ names = Hash[*node.Names]
226
225
  target = names[name]
227
226
  return target && target.solve
228
227
  end
229
228
  else
230
- names = Hash[*node[:Names].solve]
229
+ names = Hash[*node.Names]
231
230
  target = names[name]
232
231
  return target && target.solve
233
232
  end
234
233
 
235
234
  elsif node.has_key?(:Kids) # intermediate node
236
- node[:Kids].solve.each do |kid|
235
+ node.Kids.each do |kid|
237
236
  kid = kid.solve
238
- limits = kid[:Limits].solve
237
+ limits = kid.Limits
239
238
  min, max = limits[0].value, limits[1].value
240
239
 
241
240
  if (min..max) === name.to_str
@@ -248,12 +247,12 @@ module Origami
248
247
 
249
248
  def each_name_from_node(node, browsed_nodes = [], &b) #:nodoc:
250
249
  if node.has_key?(:Names) # leaf node
251
- names = Hash[*node[:Names].solve]
250
+ names = Hash[*node.Names]
252
251
  names.each_pair do |name, value|
253
252
  b.call(name, value.solve)
254
253
  end
255
254
  elsif node.has_key?(:Kids) # intermediate node
256
- node[:Kids].solve.each do |kid|
255
+ node.Kids.each do |kid|
257
256
  each_name_from_node(kid.solve, browsed_nodes, &b)
258
257
  end
259
258
  end
@@ -166,17 +166,7 @@ module Origami
166
166
  end
167
167
 
168
168
  def [](key)
169
- #if key.is_a?(::Array) and key.size == 1
170
- # follow = true
171
- # key = key.first
172
- #end
173
-
174
- val = super(key.to_o)
175
- #if follow and val.is_a?(Reference)
176
- # val.solve
177
- #else
178
- # val
179
- #end
169
+ super(key.to_o)
180
170
  end
181
171
 
182
172
  def has_key?(key)
@@ -192,7 +182,16 @@ module Origami
192
182
  def real_type ; Dictionary end
193
183
 
194
184
  alias value to_h
195
-
185
+
186
+ def method_missing(field, *args) #:nodoc:
187
+ if field.to_s[-1,1] == '='
188
+ self[field.to_s[0..-2].to_sym] = args.first
189
+ else
190
+ obj = self[field];
191
+ obj.is_a?(Reference) ? obj.solve : obj
192
+ end
193
+ end
194
+
196
195
  end #class
197
196
 
198
197
  end # Origami
@@ -318,12 +318,8 @@ module Origami
318
318
  def dataObjects
319
319
  data_objs = []
320
320
  @pdf.ls_names(Names::Root::EMBEDDEDFILES).each do |name, file_desc|
321
- if file_desc
322
- ef = file_desc[:EF].solve
323
- if ef
324
- f = ef[:F].solve
325
- data_objs.push Data.new(@engine, name, f.data.size) if f.is_a?(Stream)
326
- end
321
+ if file_desc and file_desc.EF and (f = file_desc.EF.F)
322
+ data_objs.push Data.new(@engine, name, f.data.size) if f.is_a?(Stream)
327
323
  end
328
324
 
329
325
  end
@@ -339,16 +335,10 @@ module Origami
339
335
  Arg[:name => 'cName', :type => ::String, :required => true] do |cName|
340
336
  file_desc = @pdf.resolve_name(Names::Root::EMBEDDEDFILES, cName)
341
337
 
342
- if file_desc
343
- ef = file_desc[:EF].solve
344
- if ef
345
- f = ef[:F].solve
346
- else raise TypeError
347
- end
338
+ if file_desc and file_desc.EF and (f = file_desc.EF.F)
339
+ Data.new(@engine, cName, f.data.size) if f.is_a?(Stream)
348
340
  else raise TypeError
349
341
  end
350
-
351
- Data.new(@engine, cName, f.data.size) if f.is_a?(Stream)
352
342
  end
353
343
 
354
344
  acro_method 'getDataObjectContents',
@@ -356,16 +346,10 @@ module Origami
356
346
  Arg[:name => 'bAllowAuth', :default => false] do |cName, bAllowAuth|
357
347
  file_desc = @pdf.resolve_name(Names::Root::EMBEDDEDFILES, cName)
358
348
 
359
- if file_desc
360
- ef = file_desc[:EF].solve
361
- if ef
362
- f = ef[:F].solve
363
- else raise TypeError
364
- end
349
+ if file_desc and file_desc.EF and (f = file_desc.EF.F)
350
+ ReadStream.new(@engine, f.data) if f.is_a?(Stream)
365
351
  else raise TypeError
366
352
  end
367
-
368
- ReadStream.new(@engine, f.data) if f.is_a?(Stream)
369
353
  end
370
354
 
371
355
  acro_method 'exportDataObject',
@@ -375,12 +359,7 @@ module Origami
375
359
  Arg[:name => 'nLaunch'] do |cName, cDIPath, bAllowAuth, nLaunch|
376
360
  file_desc = @pdf.resolve_name(Names::Root::EMBEDDEDFILES, cName)
377
361
 
378
- if file_desc
379
- ef = file_desc[:EF].solve
380
- if ef
381
- f = ef[:F].solve
382
- else raise TypeError
383
- end
362
+ if file_desc and file_desc.EF and (f = file_desc.EF.F)
384
363
  else raise TypeError
385
364
  end
386
365
 
@@ -495,11 +474,11 @@ module Origami
495
474
 
496
475
  def doc; Doc.new(@field.pdf) end
497
476
  def name
498
- (@field[:T].solve.value if @field.has_key?(:T)).to_s
477
+ (@field.T.value if @field.has_key?(:T)).to_s
499
478
  end
500
479
 
501
480
  def value
502
- @field[:V].solve.value if @field.has_key?(:V)
481
+ @field.V.value if @field.has_key?(:V)
503
482
  end
504
483
 
505
484
  def valueAsString
@@ -508,10 +487,10 @@ module Origami
508
487
 
509
488
  def type
510
489
  (if @field.has_key?(:FT)
511
- case @field[:FT].solve.value
490
+ case @field.FT.value
512
491
  when PDF::Field::Type::BUTTON
513
492
  if @fields.has_key?(:Ff)
514
- flags = @fields[:Ff].solve.value
493
+ flags = @field.Ff.value
515
494
 
516
495
  if (flags & Origami::Annotation::Widget::Button::Flags::PUSHBUTTON) != 0
517
496
  'button'
@@ -525,7 +504,7 @@ module Origami
525
504
  when PDF::Field::Type::SIGNATURE then 'signature'
526
505
  when PDF::Field::Type::CHOICE
527
506
  if @field.has_key?(:Ff)
528
- if (@field[:Ff].solve.value & Origami::Annotation::Widget::Choice::Flags::COMBO).zero?
507
+ if (@field.Ff.value & Origami::Annotation::Widget::Choice::Flags::COMBO).zero?
529
508
  'listbox'
530
509
  else
531
510
  'combobox'
@@ -662,9 +641,13 @@ module Origami
662
641
  #
663
642
  # Binds the V8 remote debugging agent on the specified TCP _port_.
664
643
  #
665
- def enable_debugger(port)
644
+ def enable_debugger(port = 5858)
666
645
  V8::C::Debug.EnableAgent("Origami", port)
667
646
  end
647
+
648
+ def debugger_break
649
+ exec 'debugger'
650
+ end
668
651
  end
669
652
 
670
653
  end
data/origami/metadata.rb CHANGED
@@ -40,7 +40,7 @@ module Origami
40
40
  # Returns true if the document has a catalog metadata stream.
41
41
  #
42
42
  def has_metadata?
43
- self.Catalog.has_key? :Metadata
43
+ self.Catalog.Metadata.is_a?(Stream)
44
44
  end
45
45
 
46
46
  #
@@ -74,7 +74,7 @@ module Origami
74
74
 
75
75
  description.attributes.each_attribute do |attr|
76
76
  case attr.prefix
77
- when 'pdf','xap','pdf'
77
+ when 'pdf','xap'
78
78
  info[attr.name] = attr.value
79
79
  end
80
80
  end
@@ -86,10 +86,53 @@ module Origami
86
86
 
87
87
  end
88
88
 
89
- return info
89
+ info
90
90
  end
91
91
  end
92
92
 
93
+ #
94
+ # Modifies or creates a metadata stream.
95
+ #
96
+ def create_metadata(info = {})
97
+ skeleton = <<-XMP
98
+ <?packet begin="#{"\xef\xbb\xbf"}" id="W5M0MpCehiHzreSzNTczkc9d"?>
99
+ <x:xmpmeta xmlns:x="adobe:ns:meta/">
100
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
101
+ <rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
102
+ </rdf:Description>
103
+ </rdf:RDF>
104
+ </x:xmpmeta>
105
+ <?xpacket end="w"?>
106
+ XMP
107
+
108
+ xml =
109
+ if self.Catalog.Metadata.is_a?(Stream)
110
+ self.Catalog.Metadata.data
111
+ else
112
+ skeleton
113
+ end
114
+
115
+ doc = REXML::Document.new(xml)
116
+ desc = doc.elements['*/*/rdf:Description']
117
+
118
+ info.each do |name, value|
119
+ elt = REXML::Element.new "pdf:#{name}"
120
+ elt.text = value
121
+
122
+ desc.elements << elt
123
+ end
124
+
125
+ xml = ""; doc.write(xml, 3)
126
+
127
+ if self.Catalog.Metadata.is_a?(Stream)
128
+ self.Catalog.Metadata.data = xml
129
+ else
130
+ self.Catalog.Metadata = Stream.new(xml)
131
+ end
132
+
133
+ self.Catalog.Metadata
134
+ end
135
+
93
136
  private
94
137
 
95
138
  def get_document_info_field(field) #:nodoc:
@@ -98,8 +141,8 @@ module Origami
98
141
 
99
142
  if doc_info.has_key?(field)
100
143
  case obj = get_document_info[field].solve
101
- when String then return obj.value
102
- when Stream then return obj.data
144
+ when String then obj.value
145
+ when Stream then obj.data
103
146
  end
104
147
  end
105
148
  end
@@ -0,0 +1,90 @@
1
+ =begin
2
+
3
+ = File
4
+ outputintents.rb
5
+
6
+ = Info
7
+ Origami is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Origami is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+
22
+ module Origami
23
+
24
+ class OutputIntent < Dictionary
25
+ include StandardObject
26
+
27
+ module Intent
28
+ PDFX = :GTS_PDFX
29
+ PDFA1 = :GTS_PDFA1
30
+ PDFE1 = :GTS_PDFE1
31
+ end
32
+
33
+ field :Type, :Type => Name, :Default => :OutputIntent
34
+ field :S, :Type => Name, :Version => '1.4', :Required => true
35
+ field :OutputCondition, :Type => String
36
+ field :OutputConditionIdentifier, :Type => String
37
+ field :RegistryName, :Type => String
38
+ field :Info, :Type => String
39
+ field :DestOutputProfile, :Type => Stream
40
+ end
41
+
42
+ class PDF
43
+
44
+ def is_a_pdfa1?
45
+ self.Catalog.OutputIntents.is_a?(Array) and
46
+ self.Catalog.OutputIntents.any?{|intent|
47
+ intent = intent.solve;
48
+ intent.S == OutputIntent::Intent::PDFA1
49
+ } and
50
+ self.has_metadata? and (
51
+ doc = REXML::Document.new self.Catalog.Metadata.data;
52
+ REXML::XPath.match(doc, "*/*/rdf:Description[@xmlns:pdfaid]").any? {|desc|
53
+ desc.elements["pdfaid:conformance"].text == "A" and
54
+ desc.elements["pdfaid:part"].text == "1"
55
+ }
56
+ )
57
+ end
58
+
59
+ private
60
+
61
+ def intents_as_pdfa1
62
+ unless self.is_a_pdfa1?
63
+ self.Catalog.OutputIntents ||= []
64
+ self.Catalog.OutputIntents << self.insert(
65
+ OutputIntent.new(
66
+ :Type => :OutputIntent,
67
+ :S => OutputIntent::Intent::PDFA1,
68
+ :OutputConditionIdentifier => "RGB"
69
+ )
70
+ )
71
+
72
+ metadata = self.create_metadata
73
+ doc = REXML::Document.new(metadata.data)
74
+
75
+ desc = REXML::Element.new 'rdf:Description'
76
+ desc.add_attribute 'rdf:about', ''
77
+ desc.add_attribute 'xmlns:pdfaid', 'http://www.aiim.org/pdfa/ns/id/'
78
+ desc.add REXML::Element.new('pdfaid:conformance').add_text('A')
79
+ desc.add REXML::Element.new('pdfaid:part').add_text('1')
80
+ doc.elements["*/rdf:RDF"].add desc
81
+
82
+ xml = ""; doc.write(xml, 3)
83
+ metadata.data = xml
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+
90
+
data/origami/pdf.rb CHANGED
@@ -55,11 +55,12 @@ require 'origami/linearization'
55
55
  require 'origami/obfuscation'
56
56
  require 'origami/xfa'
57
57
  require 'origami/javascript'
58
+ require 'origami/outputintents'
58
59
 
59
60
  module Origami
60
61
 
61
- VERSION = "1.2.0"
62
- REVISION = "$Revision: rev 125/, 2011/09/29 15:18:36 darko $" #:nodoc:
62
+ VERSION = "1.2.1"
63
+ REVISION = "$Revision: rev 130/, 2011/10/05 12:33:53 darko $" #:nodoc:
63
64
 
64
65
  #
65
66
  # Global options for Origami.
@@ -91,6 +92,7 @@ module Origami
91
92
  :Annot => Annotation,
92
93
  :Border => Annotation::BorderStyle,
93
94
  :Outlines => Outline,
95
+ :OutputIntent => OutputIntent,
94
96
  :Sig => Signature::DigitalSignature,
95
97
  :SigRef => Signature::Reference,
96
98
  :SigFieldLock => Field::SignatureLock,
@@ -294,6 +296,7 @@ module Origami
294
296
  fd = File.open(path, 'w').binmode
295
297
  end
296
298
 
299
+ intents_as_pdfa1 if options[:intent] =~ /pdf[\/-]?A1?/i
297
300
  self.delinearize! if options[:delinearize] and self.is_linearized?
298
301
  self.compile(options) if options[:recompile]
299
302
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: origami
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 2
9
- - 0
10
- version: 1.2.0
9
+ - 1
10
+ version: 1.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - "Guillaume Delugr\xC3\xA9"
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-09-29 00:00:00 +02:00
18
+ date: 2011-10-05 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -106,6 +106,7 @@ files:
106
106
  - origami/webcapture.rb
107
107
  - origami/pdf.rb
108
108
  - origami/encryption.rb
109
+ - origami/outputintents.rb
109
110
  - bin/config/pdfcop.conf.yml
110
111
  - bin/gui/about.rb
111
112
  - bin/gui/config.rb
@@ -133,6 +134,7 @@ files:
133
134
  - bin/shell/hexdump.rb
134
135
  - bin/pdfencrypt
135
136
  - bin/pdfcop
137
+ - bin/pdf2pdfa
136
138
  - tests/dataset/test.dummycrt
137
139
  - tests/dataset/test.dummykey
138
140
  - tests/tc_actions.rb