origami 1.2.0 → 1.2.1

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/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