origami 1.2.2 → 1.2.3
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/lib/origami/dictionary.rb +2 -0
- data/lib/origami/encryption.rb +3 -3
- data/lib/origami/file.rb +8 -2
- data/lib/origami/graphics/xobject.rb +55 -0
- data/lib/origami/javascript.rb +1 -1
- data/lib/origami/name.rb +3 -3
- data/lib/origami/page.rb +10 -0
- data/lib/origami/pdf.rb +338 -343
- data/lib/origami/signature.rb +4 -4
- data/lib/origami/xreftable.rb +13 -4
- metadata +14 -14
data/lib/origami/dictionary.rb
CHANGED
@@ -184,6 +184,8 @@ module Origami
|
|
184
184
|
alias value to_h
|
185
185
|
|
186
186
|
def method_missing(field, *args) #:nodoc:
|
187
|
+
raise NoMethodError, "No method `#{field}' for #{self.class}" unless field.to_s[0,1] =~ /[A-Z]/
|
188
|
+
|
187
189
|
if field.to_s[-1,1] == '='
|
188
190
|
self[field.to_s[0..-2].to_sym] = args.first
|
189
191
|
else
|
data/lib/origami/encryption.rb
CHANGED
@@ -302,6 +302,8 @@ module Origami
|
|
302
302
|
attr_writer :stm_algo
|
303
303
|
attr_writer :str_algo
|
304
304
|
|
305
|
+
private
|
306
|
+
|
305
307
|
def physicalize(options = {})
|
306
308
|
|
307
309
|
def build(obj, revision, options) #:nodoc:
|
@@ -1357,9 +1359,7 @@ module Origami
|
|
1357
1359
|
end
|
1358
1360
|
|
1359
1361
|
def password_to_utf8(passwd) #:nodoc:
|
1360
|
-
|
1361
|
-
hexprint p
|
1362
|
-
p
|
1362
|
+
Origami::ByteString.new(passwd).to_utf8[0, 127]
|
1363
1363
|
end
|
1364
1364
|
|
1365
1365
|
end
|
data/lib/origami/file.rb
CHANGED
@@ -48,12 +48,18 @@ module Origami
|
|
48
48
|
fd = path
|
49
49
|
params[:EmbeddedName] ||= ''
|
50
50
|
else
|
51
|
-
fd = File.open(path, 'r').binmode
|
51
|
+
fd = File.open(File.expand_path(path), 'r').binmode
|
52
52
|
params[:EmbeddedName] ||= File.basename(path)
|
53
53
|
end
|
54
54
|
|
55
55
|
fstream = EmbeddedFileStream.new
|
56
|
-
|
56
|
+
|
57
|
+
if ''.respond_to? :force_encoding
|
58
|
+
fstream.data = fd.read.force_encoding('binary') # 1.9
|
59
|
+
else
|
60
|
+
fstream.data = fd.read
|
61
|
+
end
|
62
|
+
|
57
63
|
fstream.setFilter(params[:Filter])
|
58
64
|
|
59
65
|
name = params[:EmbeddedName]
|
@@ -78,6 +78,17 @@ module Origami
|
|
78
78
|
@instructions
|
79
79
|
end
|
80
80
|
|
81
|
+
def draw_image(name, attr = {})
|
82
|
+
load! if @instructions.nil?
|
83
|
+
|
84
|
+
x, y = attr[:x], attr[:y]
|
85
|
+
|
86
|
+
@instructions << PDF::Instruction.new('q')
|
87
|
+
@instructions << PDF::Instruction.new('cm', 300, 0, 0, 300, x, y)
|
88
|
+
@instructions << PDF::Instruction.new('Do', name)
|
89
|
+
@instructions << PDF::Instruction.new('Q')
|
90
|
+
end
|
91
|
+
|
81
92
|
#
|
82
93
|
# Draw a straight line from the point at coord _from_, to the point at coord _to_.
|
83
94
|
#
|
@@ -492,6 +503,50 @@ module Origami
|
|
492
503
|
field :Measure, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
|
493
504
|
field :PtData, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
|
494
505
|
|
506
|
+
def self.from_image_file(path, format = nil)
|
507
|
+
|
508
|
+
if path.respond_to?(:read)
|
509
|
+
fd = path
|
510
|
+
else
|
511
|
+
fd = File.open(File.expand_path(path), 'r').binmode
|
512
|
+
format ||= File.extname(path)
|
513
|
+
format.slice!(0) if format and format[0,1] == '.'
|
514
|
+
end
|
515
|
+
|
516
|
+
if ''.respond_to? :force_encoding
|
517
|
+
data = fd.read.force_encoding('binary') # 1.9
|
518
|
+
else
|
519
|
+
data = fd.read
|
520
|
+
end
|
521
|
+
|
522
|
+
fd.close
|
523
|
+
|
524
|
+
image = ImageXObject.new
|
525
|
+
|
526
|
+
raise ArgumentError, "Missing file format" if format.nil?
|
527
|
+
case format.downcase
|
528
|
+
when 'jpg', 'jpeg', 'jpe', 'jif', 'jfif', 'jfi'
|
529
|
+
image.setFilter :DCTDecode
|
530
|
+
image.rawdata = data
|
531
|
+
|
532
|
+
image
|
533
|
+
|
534
|
+
when 'jp2','jpx','j2k','jpf','jpm','mj2'
|
535
|
+
image.setFilter :JPXDecode
|
536
|
+
image.rawdata = data
|
537
|
+
|
538
|
+
image
|
539
|
+
|
540
|
+
when 'jb2', 'jbig', 'jbig2'
|
541
|
+
image.setFilter :JBIG2Decode
|
542
|
+
image.rawdata = data
|
543
|
+
|
544
|
+
image
|
545
|
+
else
|
546
|
+
raise NotImplementedError, "Unknown file format: '#{format}'"
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
495
550
|
#
|
496
551
|
# Converts an ImageXObject stream into an image file data.
|
497
552
|
# Output format depends on the stream encoding:
|
data/lib/origami/javascript.rb
CHANGED
data/lib/origami/name.rb
CHANGED
@@ -128,6 +128,8 @@ module Origami
|
|
128
128
|
def self.contract(name) #:nodoc:
|
129
129
|
|
130
130
|
i = 0
|
131
|
+
name = name.dup
|
132
|
+
|
131
133
|
while i < name.length
|
132
134
|
|
133
135
|
if name[i,1] == "#"
|
@@ -156,11 +158,9 @@ module Origami
|
|
156
158
|
|
157
159
|
forbiddenchars = /[ #\t\r\n\0\[\]<>()%\/]/
|
158
160
|
|
159
|
-
name.gsub
|
161
|
+
name.gsub(forbiddenchars) do |c|
|
160
162
|
"#" + c[0].ord.to_s(16).rjust(2,"0")
|
161
163
|
end
|
162
|
-
|
163
|
-
name
|
164
164
|
end
|
165
165
|
|
166
166
|
def real_type ; Name end
|
data/lib/origami/page.rb
CHANGED
@@ -423,6 +423,16 @@ module Origami
|
|
423
423
|
end
|
424
424
|
end
|
425
425
|
|
426
|
+
#
|
427
|
+
# Returns the array of Annotation objects of the Page.
|
428
|
+
#
|
429
|
+
def annotations
|
430
|
+
annots = self.Annots
|
431
|
+
return [] unless annots.is_a?(Array)
|
432
|
+
|
433
|
+
annots.map{|annot| annot.solve}
|
434
|
+
end
|
435
|
+
|
426
436
|
#
|
427
437
|
# Embed a SWF Flash application in the page.
|
428
438
|
#
|
data/lib/origami/pdf.rb
CHANGED
@@ -61,8 +61,8 @@ require 'origami/parsers/pdf'
|
|
61
61
|
|
62
62
|
module Origami
|
63
63
|
|
64
|
-
VERSION = "1.2.
|
65
|
-
REVISION = "$Revision: rev
|
64
|
+
VERSION = "1.2.3"
|
65
|
+
REVISION = "$Revision: rev 143/, 2011/10/20 16:22:40 $" #:nodoc:
|
66
66
|
|
67
67
|
#
|
68
68
|
# Global options for Origami.
|
@@ -264,13 +264,6 @@ module Origami
|
|
264
264
|
self
|
265
265
|
end
|
266
266
|
|
267
|
-
#
|
268
|
-
# Returns the virtual file size as it would be taking on disk.
|
269
|
-
#
|
270
|
-
def filesize
|
271
|
-
self.to_bin(:rebuildxrefs => false).size
|
272
|
-
end
|
273
|
-
|
274
267
|
#
|
275
268
|
# Saves the current document.
|
276
269
|
# _filename_:: The path where to save this PDF.
|
@@ -301,9 +294,9 @@ module Origami
|
|
301
294
|
|
302
295
|
intents_as_pdfa1 if options[:intent] =~ /pdf[\/-]?A1?/i
|
303
296
|
self.delinearize! if options[:delinearize] and self.is_linearized?
|
304
|
-
|
297
|
+
compile(options) if options[:recompile]
|
305
298
|
|
306
|
-
fd.write
|
299
|
+
fd.write output(options)
|
307
300
|
fd.close
|
308
301
|
|
309
302
|
self
|
@@ -541,6 +534,130 @@ module Origami
|
|
541
534
|
object.reference
|
542
535
|
end
|
543
536
|
|
537
|
+
#
|
538
|
+
# Ends the current Revision, and starts a new one.
|
539
|
+
#
|
540
|
+
def add_new_revision
|
541
|
+
|
542
|
+
root = @revisions.last.trailer[:Root] unless @revisions.empty?
|
543
|
+
|
544
|
+
@revisions << Revision.new(self)
|
545
|
+
@revisions.last.trailer = Trailer.new
|
546
|
+
@revisions.last.trailer.Root = root
|
547
|
+
|
548
|
+
self
|
549
|
+
end
|
550
|
+
|
551
|
+
#
|
552
|
+
# Removes a whole document revision.
|
553
|
+
# _index_:: Revision index, first is 0.
|
554
|
+
#
|
555
|
+
def remove_revision(index)
|
556
|
+
if index < 0 or index > @revisions.size
|
557
|
+
raise IndexError, "Not a valid revision index"
|
558
|
+
end
|
559
|
+
|
560
|
+
if @revisions.size == 1
|
561
|
+
raise InvalidPDFError, "Cannot remove last revision"
|
562
|
+
end
|
563
|
+
|
564
|
+
@revisions.delete_at(index)
|
565
|
+
self
|
566
|
+
end
|
567
|
+
|
568
|
+
#
|
569
|
+
# Looking for an object present at a specified file offset.
|
570
|
+
#
|
571
|
+
def get_object_by_offset(offset) #:nodoc:
|
572
|
+
self.indirect_objects.find { |obj| obj.file_offset == offset }
|
573
|
+
end
|
574
|
+
|
575
|
+
#
|
576
|
+
# Remove an object.
|
577
|
+
#
|
578
|
+
def delete_object(no, generation = 0)
|
579
|
+
|
580
|
+
case no
|
581
|
+
when Reference
|
582
|
+
target = no
|
583
|
+
when ::Integer
|
584
|
+
target = Reference.new(no, generation)
|
585
|
+
else
|
586
|
+
raise TypeError, "Invalid parameter type : #{no.class}"
|
587
|
+
end
|
588
|
+
|
589
|
+
@revisions.each do |rev|
|
590
|
+
rev.body.delete(target)
|
591
|
+
end
|
592
|
+
|
593
|
+
end
|
594
|
+
|
595
|
+
#
|
596
|
+
# Search for an indirect object in the document.
|
597
|
+
# _no_:: Reference or number of the object.
|
598
|
+
# _generation_:: Object generation.
|
599
|
+
#
|
600
|
+
def get_object(no, generation = 0, use_xrefstm = true) #:nodoc:
|
601
|
+
case no
|
602
|
+
when Reference
|
603
|
+
target = no
|
604
|
+
when ::Integer
|
605
|
+
target = Reference.new(no, generation)
|
606
|
+
when Origami::Object
|
607
|
+
return no
|
608
|
+
else
|
609
|
+
raise TypeError, "Invalid parameter type : #{no.class}"
|
610
|
+
end
|
611
|
+
|
612
|
+
set = indirect_objects_table
|
613
|
+
|
614
|
+
#
|
615
|
+
# Search through accessible indirect objects.
|
616
|
+
#
|
617
|
+
if set.include?(target)
|
618
|
+
set[target]
|
619
|
+
elsif use_xrefstm == true
|
620
|
+
# Look into XRef streams.
|
621
|
+
|
622
|
+
if @revisions.last.has_xrefstm?
|
623
|
+
xrefstm = @revisions.last.xrefstm
|
624
|
+
|
625
|
+
done = []
|
626
|
+
while xrefstm.is_a?(XRefStream) and not done.include?(xrefstm)
|
627
|
+
xref = xrefstm.find(target.refno)
|
628
|
+
|
629
|
+
#
|
630
|
+
# We found a matching XRef.
|
631
|
+
#
|
632
|
+
if xref.is_a?(XRefToCompressedObj)
|
633
|
+
objstm = get_object(xref.objstmno, 0, false)
|
634
|
+
|
635
|
+
object = objstm.extract_by_index(xref.index)
|
636
|
+
if object.is_a?(Origami::Object) and object.no == target.refno
|
637
|
+
return object
|
638
|
+
else
|
639
|
+
return objstm.extract(target.refno)
|
640
|
+
end
|
641
|
+
elsif xrefstm.has_field?(:Prev)
|
642
|
+
done << xrefstm
|
643
|
+
xrefstm = get_object_by_offset(xrefstm.Prev)
|
644
|
+
else
|
645
|
+
break
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
#
|
651
|
+
# Lastly search directly into Object streams (might be very slow).
|
652
|
+
#
|
653
|
+
stream = set.values.find_all{|obj| obj.is_a?(ObjectStream)}.find do |objstm| objstm.include?(target.refno) end
|
654
|
+
stream && stream.extract(target.refno)
|
655
|
+
end
|
656
|
+
|
657
|
+
end
|
658
|
+
|
659
|
+
alias :[] :get_object
|
660
|
+
|
544
661
|
#
|
545
662
|
# Returns a new number/generation for future object.
|
546
663
|
#
|
@@ -561,6 +678,37 @@ module Origami
|
|
561
678
|
[ no, 0 ]
|
562
679
|
end
|
563
680
|
|
681
|
+
##########################
|
682
|
+
private
|
683
|
+
##########################
|
684
|
+
|
685
|
+
#
|
686
|
+
# Compute and update XRef::Section for each Revision.
|
687
|
+
#
|
688
|
+
def rebuildxrefs
|
689
|
+
|
690
|
+
size = 0
|
691
|
+
startxref = @header.to_s.size
|
692
|
+
|
693
|
+
@revisions.each do |revision|
|
694
|
+
|
695
|
+
revision.objects.each do |object|
|
696
|
+
startxref += object.to_s.size
|
697
|
+
end
|
698
|
+
|
699
|
+
size += revision.body.size
|
700
|
+
revision.xreftable = buildxrefs(revision.objects)
|
701
|
+
|
702
|
+
revision.trailer ||= Trailer.new
|
703
|
+
revision.trailer.Size = size + 1
|
704
|
+
revision.trailer.startxref = startxref
|
705
|
+
|
706
|
+
startxref += revision.xreftable.to_s.size + revision.trailer.to_s.size
|
707
|
+
end
|
708
|
+
|
709
|
+
self
|
710
|
+
end
|
711
|
+
|
564
712
|
#
|
565
713
|
# This method is meant to recompute, verify and correct main PDF structures, in order to output a proper file.
|
566
714
|
# * Allocates objects references.
|
@@ -596,90 +744,192 @@ module Origami
|
|
596
744
|
end
|
597
745
|
|
598
746
|
#
|
599
|
-
#
|
600
|
-
#
|
601
|
-
#
|
747
|
+
# Cleans the document from its references.
|
748
|
+
# Indirects objects are made direct whenever possible.
|
749
|
+
# TODO: Circuit-checking to avoid infinite induction
|
602
750
|
#
|
603
|
-
def
|
604
|
-
|
605
|
-
has_objstm = self.indirect_objects.any?{|obj| obj.is_a?(ObjectStream)}
|
606
|
-
|
607
|
-
options =
|
608
|
-
{
|
609
|
-
:rebuildxrefs => true,
|
610
|
-
:noindent => false,
|
611
|
-
:obfuscate => false,
|
612
|
-
:use_xrefstm => has_objstm,
|
613
|
-
:use_xreftable => (not has_objstm),
|
614
|
-
:up_to_revision => @revisions.size
|
615
|
-
}
|
616
|
-
options.update(params)
|
617
|
-
|
618
|
-
options[:up_to_revision] = @revisions.size if options[:up_to_revision] > @revisions.size
|
751
|
+
def logicalize #:nodoc:
|
619
752
|
|
620
|
-
|
621
|
-
if options[:use_xrefstm] == options[:use_xreftable]
|
622
|
-
options[:use_xrefstm] = has_objstm
|
623
|
-
options[:use_xreftable] = (not has_objstm)
|
624
|
-
end
|
753
|
+
fail "Not yet supported"
|
625
754
|
|
626
|
-
|
627
|
-
trailer_info = get_trailer_info
|
628
|
-
if trailer_info.nil?
|
629
|
-
raise InvalidPDFError, "No trailer information found"
|
630
|
-
end
|
631
|
-
trailer_dict = trailer_info.dictionary
|
632
|
-
|
633
|
-
prev_xref_offset = nil
|
634
|
-
xrefstm_offset = nil
|
635
|
-
xreftable_offset = nil
|
636
|
-
|
637
|
-
# Header
|
638
|
-
bin = ""
|
639
|
-
bin << @header.to_s
|
755
|
+
processed = []
|
640
756
|
|
641
|
-
|
642
|
-
@revisions[0, options[:up_to_revision]].each do |rev|
|
643
|
-
|
644
|
-
# Create xref table/stream.
|
645
|
-
if options[:rebuildxrefs] == true
|
646
|
-
lastno_table, lastno_stm = 0, 0
|
647
|
-
brange_table, brange_stm = 0, 0
|
648
|
-
|
649
|
-
xrefs_stm = [ XRef.new(0, 0, XRef::FREE) ]
|
650
|
-
xrefs_table = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]
|
757
|
+
def convert(root) #:nodoc:
|
651
758
|
|
652
|
-
|
653
|
-
|
654
|
-
|
759
|
+
replaced = []
|
760
|
+
if root.is_a?(Dictionary) or root.is_a?(Array)
|
761
|
+
|
762
|
+
root.each { |obj|
|
763
|
+
convert(obj)
|
764
|
+
}
|
655
765
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
766
|
+
root.map! { |obj|
|
767
|
+
if obj.is_a?(Reference)
|
768
|
+
target = obj.solve
|
769
|
+
# Streams can't be direct objects
|
770
|
+
if target.is_a?(Stream)
|
771
|
+
obj
|
772
|
+
else
|
773
|
+
replaced << obj
|
774
|
+
target
|
775
|
+
end
|
660
776
|
else
|
661
|
-
|
777
|
+
obj
|
662
778
|
end
|
663
|
-
|
779
|
+
}
|
780
|
+
|
664
781
|
end
|
665
|
-
|
666
|
-
objset = rev.objects
|
667
|
-
|
668
|
-
objset.find_all{|obj| obj.is_a?(ObjectStream)}.each do |objstm|
|
669
|
-
objset |= objstm.objects
|
670
|
-
end if options[:rebuildxrefs] == true and options[:use_xrefstm] == true
|
671
782
|
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
# Create xref entry.
|
676
|
-
if options[:rebuildxrefs] == true
|
677
|
-
|
678
|
-
# Adding subsections if needed
|
679
|
-
if options[:use_xreftable] and (obj.no - lastno_table).abs > 1
|
680
|
-
xrefsection << XRef::Subsection.new(brange_table, xrefs_table)
|
783
|
+
replaced
|
784
|
+
end
|
681
785
|
|
682
|
-
|
786
|
+
@revisions.each do |revision|
|
787
|
+
revision.objects.each do |obj|
|
788
|
+
processed.concat(convert(obj))
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
792
|
+
end
|
793
|
+
|
794
|
+
#
|
795
|
+
# Converts a logical PDF view into a physical view ready for writing.
|
796
|
+
#
|
797
|
+
def physicalize
|
798
|
+
|
799
|
+
#
|
800
|
+
# Indirect objects are added to the revision and assigned numbers.
|
801
|
+
#
|
802
|
+
def build(obj, revision) #:nodoc:
|
803
|
+
|
804
|
+
#
|
805
|
+
# Finalize any subobjects before building the stream.
|
806
|
+
#
|
807
|
+
if obj.is_a?(ObjectStream)
|
808
|
+
obj.each do |subobj|
|
809
|
+
build(subobj, revision)
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
obj.pre_build
|
814
|
+
|
815
|
+
if obj.is_a?(Dictionary) or obj.is_a?(Array)
|
816
|
+
|
817
|
+
obj.map! do |subobj|
|
818
|
+
if subobj.is_indirect?
|
819
|
+
if get_object(subobj.reference)
|
820
|
+
subobj.reference
|
821
|
+
else
|
822
|
+
ref = add_to_revision(subobj, revision)
|
823
|
+
build(subobj, revision)
|
824
|
+
ref
|
825
|
+
end
|
826
|
+
else
|
827
|
+
subobj
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
obj.each do |subobj|
|
832
|
+
build(subobj, revision)
|
833
|
+
end
|
834
|
+
|
835
|
+
elsif obj.is_a?(Stream)
|
836
|
+
build(obj.dictionary, revision)
|
837
|
+
end
|
838
|
+
|
839
|
+
obj.post_build
|
840
|
+
|
841
|
+
end
|
842
|
+
|
843
|
+
indirect_objects_by_rev.each do |obj, revision|
|
844
|
+
build(obj, revision)
|
845
|
+
end
|
846
|
+
|
847
|
+
self
|
848
|
+
end
|
849
|
+
|
850
|
+
#
|
851
|
+
# Returns the final binary representation of the current document.
|
852
|
+
#
|
853
|
+
def output(params = {})
|
854
|
+
|
855
|
+
has_objstm = self.indirect_objects.any?{|obj| obj.is_a?(ObjectStream)}
|
856
|
+
|
857
|
+
options =
|
858
|
+
{
|
859
|
+
:rebuildxrefs => true,
|
860
|
+
:noindent => false,
|
861
|
+
:obfuscate => false,
|
862
|
+
:use_xrefstm => has_objstm,
|
863
|
+
:use_xreftable => (not has_objstm),
|
864
|
+
:up_to_revision => @revisions.size
|
865
|
+
}
|
866
|
+
options.update(params)
|
867
|
+
|
868
|
+
options[:up_to_revision] = @revisions.size if options[:up_to_revision] > @revisions.size
|
869
|
+
|
870
|
+
# Reset to default params if no xrefs are chosen (hybrid files not supported yet)
|
871
|
+
if options[:use_xrefstm] == options[:use_xreftable]
|
872
|
+
options[:use_xrefstm] = has_objstm
|
873
|
+
options[:use_xreftable] = (not has_objstm)
|
874
|
+
end
|
875
|
+
|
876
|
+
# Get trailer dictionary
|
877
|
+
trailer_info = get_trailer_info
|
878
|
+
if trailer_info.nil?
|
879
|
+
raise InvalidPDFError, "No trailer information found"
|
880
|
+
end
|
881
|
+
trailer_dict = trailer_info.dictionary
|
882
|
+
|
883
|
+
prev_xref_offset = nil
|
884
|
+
xrefstm_offset = nil
|
885
|
+
xreftable_offset = nil
|
886
|
+
|
887
|
+
# Header
|
888
|
+
bin = ""
|
889
|
+
bin << @header.to_s
|
890
|
+
|
891
|
+
# For each revision
|
892
|
+
@revisions[0, options[:up_to_revision]].each do |rev|
|
893
|
+
|
894
|
+
# Create xref table/stream.
|
895
|
+
if options[:rebuildxrefs] == true
|
896
|
+
lastno_table, lastno_stm = 0, 0
|
897
|
+
brange_table, brange_stm = 0, 0
|
898
|
+
|
899
|
+
xrefs_stm = [ XRef.new(0, 0, XRef::FREE) ]
|
900
|
+
xrefs_table = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]
|
901
|
+
|
902
|
+
if options[:use_xreftable] == true
|
903
|
+
xrefsection = XRef::Section.new
|
904
|
+
end
|
905
|
+
|
906
|
+
if options[:use_xrefstm] == true
|
907
|
+
xrefstm = rev.xrefstm || XRefStream.new
|
908
|
+
if xrefstm == rev.xrefstm
|
909
|
+
xrefstm.clear
|
910
|
+
else
|
911
|
+
add_to_revision(xrefstm, rev)
|
912
|
+
end
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
916
|
+
objset = rev.objects
|
917
|
+
|
918
|
+
objset.find_all{|obj| obj.is_a?(ObjectStream)}.each do |objstm|
|
919
|
+
objset.concat objstm.objects
|
920
|
+
end if options[:rebuildxrefs] == true and options[:use_xrefstm] == true
|
921
|
+
|
922
|
+
# For each object, in number order
|
923
|
+
objset.sort.each do |obj|
|
924
|
+
|
925
|
+
# Create xref entry.
|
926
|
+
if options[:rebuildxrefs] == true
|
927
|
+
|
928
|
+
# Adding subsections if needed
|
929
|
+
if options[:use_xreftable] and (obj.no - lastno_table).abs > 1
|
930
|
+
xrefsection << XRef::Subsection.new(brange_table, xrefs_table)
|
931
|
+
|
932
|
+
xrefs_table.clear
|
683
933
|
brange_table = obj.no
|
684
934
|
end
|
685
935
|
if options[:use_xrefstm] and (obj.no - lastno_stm).abs > 1
|
@@ -774,265 +1024,6 @@ module Origami
|
|
774
1024
|
bin
|
775
1025
|
end
|
776
1026
|
|
777
|
-
#
|
778
|
-
# Compute and update XRef::Section for each Revision.
|
779
|
-
#
|
780
|
-
def rebuildxrefs
|
781
|
-
|
782
|
-
size = 0
|
783
|
-
startxref = @header.to_s.size
|
784
|
-
|
785
|
-
@revisions.each do |revision|
|
786
|
-
|
787
|
-
revision.objects.each do |object|
|
788
|
-
startxref += object.to_s.size
|
789
|
-
end
|
790
|
-
|
791
|
-
size += revision.body.size
|
792
|
-
revision.xreftable = buildxrefs(revision.objects)
|
793
|
-
|
794
|
-
revision.trailer ||= Trailer.new
|
795
|
-
revision.trailer.Size = size + 1
|
796
|
-
revision.trailer.startxref = startxref
|
797
|
-
|
798
|
-
startxref += revision.xreftable.to_s.size + revision.trailer.to_s.size
|
799
|
-
end
|
800
|
-
|
801
|
-
self
|
802
|
-
end
|
803
|
-
|
804
|
-
#
|
805
|
-
# Ends the current Revision, and starts a new one.
|
806
|
-
#
|
807
|
-
def add_new_revision
|
808
|
-
|
809
|
-
root = @revisions.last.trailer[:Root] unless @revisions.empty?
|
810
|
-
|
811
|
-
@revisions << Revision.new(self)
|
812
|
-
@revisions.last.trailer = Trailer.new
|
813
|
-
@revisions.last.trailer.Root = root
|
814
|
-
|
815
|
-
self
|
816
|
-
end
|
817
|
-
|
818
|
-
#
|
819
|
-
# Removes a whole document revision.
|
820
|
-
# _index_:: Revision index, first is 0.
|
821
|
-
#
|
822
|
-
def remove_revision(index)
|
823
|
-
if index < 0 or index > @revisions.size
|
824
|
-
raise IndexError, "Not a valid revision index"
|
825
|
-
end
|
826
|
-
|
827
|
-
if @revisions.size == 1
|
828
|
-
raise InvalidPDFError, "Cannot remove last revision"
|
829
|
-
end
|
830
|
-
|
831
|
-
@revisions.delete_at(index)
|
832
|
-
self
|
833
|
-
end
|
834
|
-
|
835
|
-
#
|
836
|
-
# Looking for an object present at a specified file offset.
|
837
|
-
#
|
838
|
-
def get_object_by_offset(offset) #:nodoc:
|
839
|
-
self.indirect_objects.find { |obj| obj.file_offset == offset }
|
840
|
-
end
|
841
|
-
|
842
|
-
#
|
843
|
-
# Remove an object.
|
844
|
-
#
|
845
|
-
def delete_object(no, generation = 0)
|
846
|
-
|
847
|
-
case no
|
848
|
-
when Reference
|
849
|
-
target = no
|
850
|
-
when ::Integer
|
851
|
-
target = Reference.new(no, generation)
|
852
|
-
else
|
853
|
-
raise TypeError, "Invalid parameter type : #{no.class}"
|
854
|
-
end
|
855
|
-
|
856
|
-
@revisions.each do |rev|
|
857
|
-
rev.body.delete(target)
|
858
|
-
end
|
859
|
-
|
860
|
-
end
|
861
|
-
|
862
|
-
#
|
863
|
-
# Search for an indirect object in the document.
|
864
|
-
# _no_:: Reference or number of the object.
|
865
|
-
# _generation_:: Object generation.
|
866
|
-
#
|
867
|
-
def get_object(no, generation = 0, use_xrefstm = true) #:nodoc:
|
868
|
-
case no
|
869
|
-
when Reference
|
870
|
-
target = no
|
871
|
-
when ::Integer
|
872
|
-
target = Reference.new(no, generation)
|
873
|
-
when Origami::Object
|
874
|
-
return no
|
875
|
-
else
|
876
|
-
raise TypeError, "Invalid parameter type : #{no.class}"
|
877
|
-
end
|
878
|
-
|
879
|
-
set = indirect_objects_table
|
880
|
-
|
881
|
-
#
|
882
|
-
# Search through accessible indirect objects.
|
883
|
-
#
|
884
|
-
if set.include?(target)
|
885
|
-
set[target]
|
886
|
-
elsif use_xrefstm == true
|
887
|
-
# Look into XRef streams.
|
888
|
-
|
889
|
-
if @revisions.last.has_xrefstm?
|
890
|
-
xrefstm = @revisions.last.xrefstm
|
891
|
-
|
892
|
-
done = []
|
893
|
-
while xrefstm.is_a?(XRefStream) and not done.include?(xrefstm)
|
894
|
-
xref = xrefstm.find(target.refno)
|
895
|
-
|
896
|
-
#
|
897
|
-
# We found a matching XRef.
|
898
|
-
#
|
899
|
-
if xref.is_a?(XRefToCompressedObj)
|
900
|
-
objstm = get_object(xref.objstmno, 0, false)
|
901
|
-
|
902
|
-
object = objstm.extract_by_index(xref.index)
|
903
|
-
if object.is_a?(Origami::Object) and object.no == target.refno
|
904
|
-
return object
|
905
|
-
else
|
906
|
-
return objstm.extract(target.refno)
|
907
|
-
end
|
908
|
-
elsif xrefstm.has_field?(:Prev)
|
909
|
-
done << xrefstm
|
910
|
-
xrefstm = get_object_by_offset(xrefstm.Prev)
|
911
|
-
else
|
912
|
-
break
|
913
|
-
end
|
914
|
-
end
|
915
|
-
end
|
916
|
-
|
917
|
-
#
|
918
|
-
# Lastly search directly into Object streams (might be very slow).
|
919
|
-
#
|
920
|
-
stream = set.values.find_all{|obj| obj.is_a?(ObjectStream)}.find do |objstm| objstm.include?(target.refno) end
|
921
|
-
stream && stream.extract(target.refno)
|
922
|
-
end
|
923
|
-
|
924
|
-
end
|
925
|
-
|
926
|
-
alias :[] :get_object
|
927
|
-
|
928
|
-
#
|
929
|
-
# Converts a logical PDF view into a physical view ready for writing.
|
930
|
-
#
|
931
|
-
def physicalize
|
932
|
-
|
933
|
-
#
|
934
|
-
# Indirect objects are added to the revision and assigned numbers.
|
935
|
-
#
|
936
|
-
def build(obj, revision) #:nodoc:
|
937
|
-
|
938
|
-
#
|
939
|
-
# Finalize any subobjects before building the stream.
|
940
|
-
#
|
941
|
-
if obj.is_a?(ObjectStream)
|
942
|
-
obj.each do |subobj|
|
943
|
-
build(subobj, revision)
|
944
|
-
end
|
945
|
-
end
|
946
|
-
|
947
|
-
obj.pre_build
|
948
|
-
|
949
|
-
if obj.is_a?(Dictionary) or obj.is_a?(Array)
|
950
|
-
|
951
|
-
obj.map! do |subobj|
|
952
|
-
if subobj.is_indirect?
|
953
|
-
if get_object(subobj.reference)
|
954
|
-
subobj.reference
|
955
|
-
else
|
956
|
-
ref = add_to_revision(subobj, revision)
|
957
|
-
build(subobj, revision)
|
958
|
-
ref
|
959
|
-
end
|
960
|
-
else
|
961
|
-
subobj
|
962
|
-
end
|
963
|
-
end
|
964
|
-
|
965
|
-
obj.each do |subobj|
|
966
|
-
build(subobj, revision)
|
967
|
-
end
|
968
|
-
|
969
|
-
elsif obj.is_a?(Stream)
|
970
|
-
build(obj.dictionary, revision)
|
971
|
-
end
|
972
|
-
|
973
|
-
obj.post_build
|
974
|
-
|
975
|
-
end
|
976
|
-
|
977
|
-
indirect_objects_by_rev.each do |obj, revision|
|
978
|
-
build(obj, revision)
|
979
|
-
end
|
980
|
-
|
981
|
-
self
|
982
|
-
end
|
983
|
-
|
984
|
-
#
|
985
|
-
# Cleans the document from its references.
|
986
|
-
# Indirects objects are made direct whenever possible.
|
987
|
-
# TODO: Circuit-checking to avoid infinite induction
|
988
|
-
#
|
989
|
-
def logicalize #:nodoc:
|
990
|
-
|
991
|
-
fail "Not yet supported"
|
992
|
-
|
993
|
-
processed = []
|
994
|
-
|
995
|
-
def convert(root) #:nodoc:
|
996
|
-
|
997
|
-
replaced = []
|
998
|
-
if root.is_a?(Dictionary) or root.is_a?(Array)
|
999
|
-
|
1000
|
-
root.each { |obj|
|
1001
|
-
convert(obj)
|
1002
|
-
}
|
1003
|
-
|
1004
|
-
root.map! { |obj|
|
1005
|
-
if obj.is_a?(Reference)
|
1006
|
-
target = obj.solve
|
1007
|
-
# Streams can't be direct objects
|
1008
|
-
if target.is_a?(Stream)
|
1009
|
-
obj
|
1010
|
-
else
|
1011
|
-
replaced << obj
|
1012
|
-
target
|
1013
|
-
end
|
1014
|
-
else
|
1015
|
-
obj
|
1016
|
-
end
|
1017
|
-
}
|
1018
|
-
|
1019
|
-
end
|
1020
|
-
|
1021
|
-
replaced
|
1022
|
-
end
|
1023
|
-
|
1024
|
-
@revisions.each do |revision|
|
1025
|
-
revision.objects.each do |obj|
|
1026
|
-
processed.concat(convert(obj))
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
end
|
1031
|
-
|
1032
|
-
##########################
|
1033
|
-
private
|
1034
|
-
##########################
|
1035
|
-
|
1036
1027
|
#
|
1037
1028
|
# Instanciates basic structures required for a valid PDF file.
|
1038
1029
|
#
|
@@ -1044,6 +1035,10 @@ module Origami
|
|
1044
1035
|
self
|
1045
1036
|
end
|
1046
1037
|
|
1038
|
+
def filesize #:nodoc:
|
1039
|
+
output(:rebuildxrefs => false).size
|
1040
|
+
end
|
1041
|
+
|
1047
1042
|
def version_required #:nodoc:
|
1048
1043
|
|
1049
1044
|
max = [ 1.0, 0 ]
|
data/lib/origami/signature.rb
CHANGED
@@ -196,7 +196,7 @@ module Origami
|
|
196
196
|
#
|
197
197
|
# Flattening the PDF to get file view.
|
198
198
|
#
|
199
|
-
|
199
|
+
compile
|
200
200
|
|
201
201
|
#
|
202
202
|
# Creating an empty Xref table to compute signature byte range.
|
@@ -218,7 +218,7 @@ module Origami
|
|
218
218
|
#
|
219
219
|
rebuildxrefs
|
220
220
|
|
221
|
-
filedata =
|
221
|
+
filedata = output()
|
222
222
|
signable_data = filedata[digsig.ByteRange[0],digsig.ByteRange[1]] + filedata[digsig.ByteRange[2],digsig.ByteRange[3]]
|
223
223
|
|
224
224
|
signature =
|
@@ -321,7 +321,7 @@ module Origami
|
|
321
321
|
#
|
322
322
|
# Flattening the PDF to get file view.
|
323
323
|
#
|
324
|
-
|
324
|
+
compile
|
325
325
|
|
326
326
|
#
|
327
327
|
# Creating an empty Xref table to compute signature byte range.
|
@@ -343,7 +343,7 @@ module Origami
|
|
343
343
|
#
|
344
344
|
rebuildxrefs
|
345
345
|
|
346
|
-
filedata =
|
346
|
+
filedata = output()
|
347
347
|
signable_data = filedata[digsig.ByteRange[0],digsig.ByteRange[1]] + filedata[digsig.ByteRange[2],digsig.ByteRange[3]]
|
348
348
|
|
349
349
|
signature = OpenSSL::PKCS7.sign(certificate, key, signable_data, [], OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
|
data/lib/origami/xreftable.rb
CHANGED
@@ -336,6 +336,15 @@ module Origami
|
|
336
336
|
@xrefs = nil
|
337
337
|
end
|
338
338
|
|
339
|
+
def entries
|
340
|
+
load! if @xrefs.nil?
|
341
|
+
|
342
|
+
@xrefs
|
343
|
+
end
|
344
|
+
|
345
|
+
#
|
346
|
+
# Returns XRef entries present in this stream.
|
347
|
+
#
|
339
348
|
def pre_build #:nodoc:
|
340
349
|
load! if @xrefs.nil?
|
341
350
|
|
@@ -386,7 +395,7 @@ module Origami
|
|
386
395
|
end
|
387
396
|
|
388
397
|
def clear
|
389
|
-
|
398
|
+
self.data = ''
|
390
399
|
@xrefs = []
|
391
400
|
self.Index = []
|
392
401
|
end
|
@@ -410,11 +419,11 @@ module Origami
|
|
410
419
|
entrymask = "B#{type_w << 3}B#{field1_w << 3}B#{field2_w << 3}"
|
411
420
|
size = @data.size / (type_w + field1_w + field2_w)
|
412
421
|
|
413
|
-
|
422
|
+
xentries = @data.unpack(entrymask * size).map!{|field| field.to_i(2) }
|
414
423
|
|
415
424
|
@xrefs = []
|
416
425
|
size.times do |i|
|
417
|
-
type,field1,field2 =
|
426
|
+
type,field1,field2 = xentries[i*3].ord,xentries[i*3+1].ord,xentries[i*3+2].ord
|
418
427
|
case type
|
419
428
|
when XREF_FREE
|
420
429
|
@xrefs << XRef.new(field1, field2, XRef::FREE)
|
@@ -430,7 +439,7 @@ module Origami
|
|
430
439
|
end
|
431
440
|
|
432
441
|
def save! #:nodoc:
|
433
|
-
|
442
|
+
self.data = ""
|
434
443
|
|
435
444
|
type_w, field1_w, field2_w = self.W
|
436
445
|
@xrefs.each do |xref| @data << xref.to_xrefstm_data(type_w, field1_w, field2_w) end
|
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:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 1.2.
|
9
|
+
- 3
|
10
|
+
version: 1.2.3
|
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-10-
|
18
|
+
date: 2011-10-20 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -51,20 +51,18 @@ files:
|
|
51
51
|
- lib/origami/boolean.rb
|
52
52
|
- lib/origami/catalog.rb
|
53
53
|
- lib/origami/destinations.rb
|
54
|
-
- lib/origami/dictionary.rb
|
55
54
|
- lib/origami/export.rb
|
56
|
-
- lib/origami/file.rb
|
57
55
|
- lib/origami/filters.rb
|
58
56
|
- lib/origami/filters/ascii.rb
|
59
57
|
- lib/origami/filters/crypt.rb
|
60
58
|
- lib/origami/filters/dct.rb
|
61
59
|
- lib/origami/filters/flate.rb
|
62
|
-
- lib/origami/filters/jbig2.rb
|
63
|
-
- lib/origami/filters/jpx.rb
|
64
60
|
- lib/origami/filters/lzw.rb
|
65
61
|
- lib/origami/filters/predictors.rb
|
66
62
|
- lib/origami/filters/runlength.rb
|
67
63
|
- lib/origami/filters/ccitt.rb
|
64
|
+
- lib/origami/filters/jpx.rb
|
65
|
+
- lib/origami/filters/jbig2.rb
|
68
66
|
- lib/origami/font.rb
|
69
67
|
- lib/origami/functions.rb
|
70
68
|
- lib/origami/graphics.rb
|
@@ -77,16 +75,13 @@ files:
|
|
77
75
|
- lib/origami/graphics/colors.rb
|
78
76
|
- lib/origami/graphics/xobject.rb
|
79
77
|
- lib/origami/header.rb
|
80
|
-
- lib/origami/javascript.rb
|
81
78
|
- lib/origami/linearization.rb
|
82
79
|
- lib/origami/metadata.rb
|
83
|
-
- lib/origami/name.rb
|
84
80
|
- lib/origami/null.rb
|
85
81
|
- lib/origami/numeric.rb
|
86
82
|
- lib/origami/obfuscation.rb
|
87
83
|
- lib/origami/outline.rb
|
88
84
|
- lib/origami/outputintents.rb
|
89
|
-
- lib/origami/page.rb
|
90
85
|
- lib/origami/parsers/pdf/linear.rb
|
91
86
|
- lib/origami/parsers/pdf.rb
|
92
87
|
- lib/origami/parsers/fdf.rb
|
@@ -101,12 +96,17 @@ files:
|
|
101
96
|
- lib/origami/object.rb
|
102
97
|
- lib/origami/extensions/fdf.rb
|
103
98
|
- lib/origami/extensions/ppklite.rb
|
104
|
-
- lib/origami/xreftable.rb
|
105
99
|
- lib/origami/parser.rb
|
106
|
-
- lib/origami/
|
100
|
+
- lib/origami/javascript.rb
|
107
101
|
- lib/origami/signature.rb
|
108
|
-
- lib/origami/
|
102
|
+
- lib/origami/xreftable.rb
|
103
|
+
- lib/origami/page.rb
|
109
104
|
- lib/origami/acroform.rb
|
105
|
+
- lib/origami/name.rb
|
106
|
+
- lib/origami/encryption.rb
|
107
|
+
- lib/origami/dictionary.rb
|
108
|
+
- lib/origami/pdf.rb
|
109
|
+
- lib/origami/file.rb
|
110
110
|
- lib/origami.rb
|
111
111
|
- bin/config/pdfcop.conf.yml
|
112
112
|
- bin/gui/about.rb
|