combine_pdf 0.2.5 → 0.2.37

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,158 +1,163 @@
1
1
  module CombinePDF
2
+ ################################################################
3
+ ## These are common functions, used within the different classes
4
+ ## These functions aren't open to the public.
5
+ ################################################################
2
6
 
3
- ################################################################
4
- ## These are common functions, used within the different classes
5
- ## These functions aren't open to the public.
6
- ################################################################
7
+ # This is an internal module used to render ruby objects into pdf objects.
8
+ module Renderer
9
+ # @!visibility private
7
10
 
11
+ protected
8
12
 
9
- # This is an internal module used to render ruby objects into pdf objects.
10
- module Renderer
13
+ # Formats an object into PDF format. This is used my the PDF object to format the PDF file and it is used in the secure injection which is still being developed.
14
+ def object_to_pdf(object)
15
+ if object.nil?
16
+ return 'null'
17
+ elsif object.is_a?(String)
18
+ return format_string_to_pdf object
19
+ elsif object.is_a?(Symbol)
20
+ return format_name_to_pdf object
21
+ elsif object.is_a?(Array)
22
+ return format_array_to_pdf object
23
+ elsif object.is_a?(Integer) || object.is_a?(Float) || object.is_a?(TrueClass) || object.is_a?(FalseClass)
24
+ return object.to_s
25
+ elsif object.is_a?(Hash)
26
+ return format_hash_to_pdf object
27
+ else
28
+ return ''
29
+ end
30
+ end
11
31
 
12
- # @!visibility private
32
+ STRING_REPLACEMENT_HASH = { "\x0A" => '\\n',
33
+ "\x0D" => '\\r',
34
+ "\x09" => '\\t',
35
+ "\x08" => '\\b',
36
+ "\x0C" => '\\f', # form-feed (\f) == 0x0C
37
+ "\x28" => '\\(',
38
+ "\x29" => '\\)',
39
+ "\x5C" => '\\\\' }.dup
40
+ 32.times { |i| STRING_REPLACEMENT_HASH[i.chr] ||= "\\#{i}" }
41
+ (256 - 127).times { |i| STRING_REPLACEMENT_HASH[(i + 127).chr] ||= "\\#{i + 127}" }
13
42
 
14
- protected
43
+ def format_string_to_pdf(object)
44
+ # object.force_encoding(Encoding::ASCII_8BIT)
45
+ if !object.match(/[^D\:\d\+\-\Z\']/) # if format is set to Literal and string isn't a date
46
+ ('(' + ([].tap { |out| object.bytes.to_a.each { |byte| STRING_REPLACEMENT_HASH[byte.chr] ? (STRING_REPLACEMENT_HASH[byte.chr].bytes.each { |b| out << b }) : out << byte } }).pack('C*') + ')').force_encoding(Encoding::ASCII_8BIT)
47
+ else
48
+ # A hexadecimal string shall be written as a sequence of hexadecimal digits (0–9 and either A–F or a–f)
49
+ # encoded as ASCII characters and enclosed within angle brackets (using LESS-THAN SIGN (3Ch) and GREATER- THAN SIGN (3Eh)).
50
+ "<#{object.unpack('H*')[0]}>".force_encoding(Encoding::ASCII_8BIT)
51
+ end
52
+ end
15
53
 
54
+ def format_name_to_pdf(object)
55
+ # a name object is an atomic symbol uniquely defined by a sequence of ANY characters (8-bit values) except null (character code 0).
56
+ # print name as a simple string. all characters between ~ and ! (except #) can be raw
57
+ # the rest will have a number sign and their HEX equivalant
58
+ # from the standard:
59
+ # When writing a name in a PDF file, a SOLIDUS (2Fh) (/) shall be used to introduce a name. The SOLIDUS is not part of the name but is a prefix indicating that what follows is a sequence of characters representing the name in the PDF file and shall follow these rules:
60
+ # a) A NUMBER SIGN (23h) (#) in a name shall be written by using its 2-digit hexadecimal code (23), preceded by the NUMBER SIGN.
61
+ # b) Any character in a name that is a regular character (other than NUMBER SIGN) shall be written as itself or by using its 2-digit hexadecimal code, preceded by the NUMBER SIGN.
62
+ # c) Any character that is not a regular character shall be written using its 2-digit hexadecimal code, preceded by the NUMBER SIGN only.
63
+ # [0x00, 0x09, 0x0a, 0x0c, 0x0d, 0x20, 0x28, 0x29, 0x3c, 0x3e, 0x5b, 0x5d, 0x7b, 0x7d, 0x2f, 0x25]
64
+ out = object.to_s.bytes.to_a.map do |b|
65
+ case b
66
+ when 0..15
67
+ '#0' + b.to_s(16)
68
+ when 15..32, 35, 37, 40, 41, 47, 60, 62, 91, 93, 123, 125, 127..256
69
+ '#' + b.to_s(16)
70
+ else
71
+ b.chr
72
+ end
73
+ end
74
+ '/' + out.join
75
+ end
16
76
 
17
- # Formats an object into PDF format. This is used my the PDF object to format the PDF file and it is used in the secure injection which is still being developed.
18
- def object_to_pdf object
19
- case
20
- when object.nil?
21
- return "null"
22
- when object.is_a?(String)
23
- return format_string_to_pdf object
24
- when object.is_a?(Symbol)
25
- return format_name_to_pdf object
26
- when object.is_a?(Array)
27
- return format_array_to_pdf object
28
- when object.is_a?(Fixnum), object.is_a?(Float), object.is_a?(TrueClass), object.is_a?(FalseClass)
29
- return object.to_s + " "
30
- when object.is_a?(Hash)
31
- return format_hash_to_pdf object
32
- else
33
- return ''
34
- end
35
- end
77
+ def format_array_to_pdf(object)
78
+ # An array shall be written as a sequence of objects enclosed in SQUARE BRACKETS (using LEFT SQUARE BRACKET (5Bh) and RIGHT SQUARE BRACKET (5Dh)).
79
+ # EXAMPLE [549 3.14 false (Ralph) /SomeName]
80
+ ('[' + (object.collect { |item| object_to_pdf(item) }).join(' ') + ']').force_encoding(Encoding::ASCII_8BIT)
81
+ end
36
82
 
37
- STRING_REPLACEMENT_HASH = {
38
- "\x0A" => "\\n",
39
- "\x0D" => "\\r",
40
- "\x09" => "\\t",
41
- "\x08" => "\\b",
42
- "\xFF" => "\\f",
43
- "\x28" => "\\(",
44
- "\x29" => "\\)",
45
- "\x5C" => "\\\\"
46
- }
47
- 32.times {|i| STRING_REPLACEMENT_HASH[i.chr] ||= "\\#{i}"}
48
- (256-128).times {|i| STRING_REPLACEMENT_HASH[(i + 127).chr] ||= "\\#{i+127}"}
83
+ EMPTY_PAGE_CONTENT_STREAM = {is_reference_only: true, referenced_object: { indirect_reference_id: 0, raw_stream_content: '' }}
49
84
 
50
- def format_string_to_pdf(object)
51
- # object.force_encoding(Encoding::ASCII_8BIT)
52
- if !object.match(/[^D\:\d\+\-\Z\']/) #if format is set to Literal
53
- ("(" + ([].tap {|out| object.bytes.each {|byte| STRING_REPLACEMENT_HASH[ byte.chr ] ? (STRING_REPLACEMENT_HASH[ byte.chr ].bytes.each {|b| out << b}) : out << byte } }).pack('C*') + ")").force_encoding(Encoding::ASCII_8BIT)
54
- else
55
- # A hexadecimal string shall be written as a sequence of hexadecimal digits (0–9 and either A–F or a–f)
56
- # encoded as ASCII characters and enclosed within angle brackets (using LESS-THAN SIGN (3Ch) and GREATER- THAN SIGN (3Eh)).
57
- ("<" + object.unpack('H*')[0] + ">").force_encoding(Encoding::ASCII_8BIT)
58
- end
59
- end
60
- def format_name_to_pdf(object)
61
- # a name object is an atomic symbol uniquely defined by a sequence of ANY characters (8-bit values) except null (character code 0).
62
- # print name as a simple string. all characters between ~ and ! (except #) can be raw
63
- # the rest will have a number sign and their HEX equivalant
64
- # from the standard:
65
- # When writing a name in a PDF file, a SOLIDUS (2Fh) (/) shall be used to introduce a name. The SOLIDUS is not part of the name but is a prefix indicating that what follows is a sequence of characters representing the name in the PDF file and shall follow these rules:
66
- # a) A NUMBER SIGN (23h) (#) in a name shall be written by using its 2-digit hexadecimal code (23), preceded by the NUMBER SIGN.
67
- # b) Any character in a name that is a regular character (other than NUMBER SIGN) shall be written as itself or by using its 2-digit hexadecimal code, preceded by the NUMBER SIGN.
68
- # c) Any character that is not a regular character shall be written using its 2-digit hexadecimal code, preceded by the NUMBER SIGN only.
69
- # [0x00, 0x09, 0x0a, 0x0c, 0x0d, 0x20, 0x28, 0x29, 0x3c, 0x3e, 0x5b, 0x5d, 0x7b, 0x7d, 0x2f, 0x25]
70
- out = object.to_s.bytes.to_a.map do |b|
71
- case b
72
- when 0..15
73
- '#0' + b.to_s(16)
74
- when 15..32, 35, 37, 40, 41, 47, 60, 62, 91, 93, 123, 125, 127..256
75
- '#' + b.to_s(16)
76
- else
77
- b.chr
78
- end
79
- end
80
- "/" + out.join()
81
- end
82
- def format_array_to_pdf(object)
83
- # An array shall be written as a sequence of objects enclosed in SQUARE BRACKETS (using LEFT SQUARE BRACKET (5Bh) and RIGHT SQUARE BRACKET (5Dh)).
84
- # EXAMPLE [549 3.14 false (Ralph) /SomeName]
85
- ("[" + (object.collect {|item| object_to_pdf(item)}).join(' ') + "]").force_encoding(Encoding::ASCII_8BIT)
86
-
87
- end
85
+ def format_hash_to_pdf(object)
86
+ # if the object is only a reference:
87
+ # special conditions apply, and there is only the setting of the reference (if needed) and output
88
+ if object[:is_reference_only]
89
+ #
90
+ if object[:referenced_object] && object[:referenced_object].is_a?(Hash)
91
+ object[:indirect_reference_id] = object[:referenced_object][:indirect_reference_id]
92
+ object[:indirect_generation_number] = object[:referenced_object][:indirect_generation_number]
93
+ end
94
+ object[:indirect_reference_id] ||= 0
95
+ object[:indirect_generation_number] ||= 0
96
+ return "#{object[:indirect_reference_id]} #{object[:indirect_generation_number]} R".force_encoding(Encoding::ASCII_8BIT)
97
+ end
88
98
 
89
- def format_hash_to_pdf(object)
90
- # if the object is only a reference:
91
- # special conditions apply, and there is only the setting of the reference (if needed) and output
92
- if object[:is_reference_only]
93
- #
94
- if object[:referenced_object] && object[:referenced_object].is_a?(Hash)
95
- object[:indirect_reference_id] = object[:referenced_object][:indirect_reference_id]
96
- object[:indirect_generation_number] = object[:referenced_object][:indirect_generation_number]
97
- end
98
- object[:indirect_reference_id] ||= 0
99
- object[:indirect_generation_number] ||= 0
100
- return "#{object[:indirect_reference_id].to_s} #{object[:indirect_generation_number].to_s} R".force_encoding(Encoding::ASCII_8BIT)
101
- end
99
+ # if the object is indirect...
100
+ out = []
101
+ if object[:indirect_reference_id]
102
+ object[:indirect_reference_id] ||= 0
103
+ object[:indirect_generation_number] ||= 0
104
+ out << "#{object[:indirect_reference_id]} #{object[:indirect_generation_number]} obj\n".force_encoding(Encoding::ASCII_8BIT)
105
+ if object[:indirect_without_dictionary]
106
+ out << object_to_pdf(object[:indirect_without_dictionary])
107
+ out << "\nendobj\n"
108
+ return out.join.force_encoding(Encoding::ASCII_8BIT)
109
+ end
110
+ end
111
+ # remove extra page references.
112
+ object[:Contents].delete(EMPTY_PAGE_CONTENT_STREAM) if object[:Type] == :Page && object[:Contents].is_a?(Array)
113
+ # correct stream length, if the object is a stream.
114
+ object[:Length] = object[:raw_stream_content].bytesize if object[:raw_stream_content]
102
115
 
103
- # if the object is indirect...
104
- out = []
105
- if object[:indirect_reference_id]
106
- object[:indirect_reference_id] ||= 0
107
- object[:indirect_generation_number] ||= 0
108
- out << "#{object[:indirect_reference_id].to_s} #{object[:indirect_generation_number].to_s} obj\n".force_encoding(Encoding::ASCII_8BIT)
109
- if object[:indirect_without_dictionary]
110
- out << object_to_pdf(object[:indirect_without_dictionary])
111
- out << "\nendobj\n"
112
- return out.join().force_encoding(Encoding::ASCII_8BIT)
113
- end
114
- end
115
- # correct stream length, if the object is a stream.
116
- object[:Length] = object[:raw_stream_content].bytesize if object[:raw_stream_content]
116
+ # if the object is not a simple object, it is a dictionary
117
+ # A dictionary shall be written as a sequence of key-value pairs enclosed in double angle brackets (<<...>>)
118
+ # (using LESS-THAN SIGNs (3Ch) and GREATER-THAN SIGNs (3Eh)).
119
+ out << "<<\n".force_encoding(Encoding::ASCII_8BIT)
120
+ object.each do |key, value|
121
+ out << "#{object_to_pdf key} #{object_to_pdf value}\n".force_encoding(Encoding::ASCII_8BIT) unless PDF::PRIVATE_HASH_KEYS.include? key
122
+ end
123
+ object.delete :Length
124
+ out << '>>'.force_encoding(Encoding::ASCII_8BIT)
125
+ out << "\nstream\n#{object[:raw_stream_content]}\nendstream".force_encoding(Encoding::ASCII_8BIT) if object[:raw_stream_content]
126
+ out << "\nendobj\n" if object[:indirect_reference_id]
127
+ out.join.force_encoding(Encoding::ASCII_8BIT)
128
+ end
117
129
 
118
- # if the object is not a simple object, it is a dictionary
119
- # A dictionary shall be written as a sequence of key-value pairs enclosed in double angle brackets (<<...>>)
120
- # (using LESS-THAN SIGNs (3Ch) and GREATER-THAN SIGNs (3Eh)).
121
- out << "<<\n".force_encoding(Encoding::ASCII_8BIT)
122
- object.each do |key, value|
123
- out << "#{object_to_pdf key} #{object_to_pdf value}\n".force_encoding(Encoding::ASCII_8BIT) unless PDF::PRIVATE_HASH_KEYS.include? key
124
- end
125
- out << ">>".force_encoding(Encoding::ASCII_8BIT)
126
- out << "\nstream\n#{object[:raw_stream_content]}\nendstream".force_encoding(Encoding::ASCII_8BIT) if object[:raw_stream_content]
127
- out << "\nendobj\n" if object[:indirect_reference_id]
128
- out.join().force_encoding(Encoding::ASCII_8BIT)
129
- end
130
+ def actual_object(obj)
131
+ obj.is_a?(Hash) ? (obj[:referenced_object] || obj) : obj
132
+ end
130
133
 
131
- def actual_object obj
132
- obj.is_a?(Hash) ? (obj[:referenced_object] || obj) : obj
133
- end
134
+ def actual_value(obj)
135
+ return obj unless obj.is_a?(Hash)
136
+ obj = obj[:referenced_object] || obj
137
+ obj[:indirect_without_dictionary] || obj
138
+ end
134
139
 
135
- # Ruby normally assigns pointes.
136
- # noramlly:
137
- # a = [1,2,3] # => [1,2,3]
138
- # b = a # => [1,2,3]
139
- # a << 4 # => [1,2,3,4]
140
- # b # => [1,2,3,4]
141
- # This method makes sure that the memory is copied instead of a pointer assigned.
142
- # this works using recursion, so that arrays and hashes within arrays and hashes are also copied and not pointed to.
143
- # One needs to be careful of infinit loops using this function.
144
- def create_deep_copy object
145
- if object.is_a?(Array)
146
- return object.map { |e| create_deep_copy e }
147
- elsif object.is_a?(Hash)
148
- return {}.tap {|out| object.each {|k,v| out[create_deep_copy(k)] = create_deep_copy(v) unless k == :Parent} }
149
- elsif object.is_a?(String)
150
- return object.dup
151
- else
152
- return object # objects that aren't Strings, Arrays or Hashes (such as Symbols and Fixnums) won't be edited inplace.
153
- end
154
- end
155
- end
140
+ # Ruby normally assigns pointes.
141
+ # noramlly:
142
+ # a = [1,2,3] # => [1,2,3]
143
+ # b = a # => [1,2,3]
144
+ # a << 4 # => [1,2,3,4]
145
+ # b # => [1,2,3,4]
146
+ # This method makes sure that the memory is copied instead of a pointer assigned.
147
+ # this works using recursion, so that arrays and hashes within arrays and hashes are also copied and not pointed to.
148
+ # One needs to be careful of infinit loops using this function.
149
+ def create_deep_copy(object)
150
+ if object.is_a?(Array)
151
+ return object.map { |e| create_deep_copy e }
152
+ elsif object.is_a?(Hash)
153
+ return {}.tap { |out| object.each { |k, v| out[create_deep_copy(k)] = create_deep_copy(v) unless k == :Parent } }
154
+ elsif object.is_a?(String)
155
+ return object.dup
156
+ else
157
+ return object # objects that aren't Strings, Arrays or Hashes (such as Symbols and Integers) won't be edited inplace.
158
+ end
159
+ end
160
+ end
156
161
  end
157
162
 
158
163
  #########################################################
@@ -164,5 +169,5 @@ end
164
169
  # arrays are Array
165
170
  # strings are String
166
171
  # names are Symbols (String.to_sym)
167
- # numbers are Fixnum or Float
172
+ # numbers are Integers or Float
168
173
  # boolean are TrueClass or FalseClass
@@ -1,4 +1,3 @@
1
1
  module CombinePDF
2
- VERSION = "0.2.5"
2
+ VERSION = '0.2.37'.freeze
3
3
  end
4
-
data/lib/combine_pdf.rb CHANGED
@@ -4,26 +4,26 @@ require 'zlib'
4
4
  require 'securerandom'
5
5
  require 'strscan'
6
6
  require 'matrix'
7
+ require 'set'
7
8
 
8
- #require the RC4 Gem
9
+ # require the RC4 Gem
9
10
  require 'rc4'
10
11
 
11
-
12
- load "combine_pdf/api.rb"
13
- load "combine_pdf/renderer.rb"
14
- load "combine_pdf/page_methods.rb"
15
- load "combine_pdf/basic_writer.rb"
16
- load "combine_pdf/decrypt.rb"
17
- load "combine_pdf/fonts.rb"
18
- load "combine_pdf/filter.rb"
19
- load "combine_pdf/parser.rb"
20
- load "combine_pdf/pdf_public.rb"
21
- load "combine_pdf/pdf_protected.rb"
12
+ load 'combine_pdf/api.rb'
13
+ load 'combine_pdf/renderer.rb'
14
+ load 'combine_pdf/page_methods.rb'
15
+ load 'combine_pdf/basic_writer.rb'
16
+ load 'combine_pdf/decrypt.rb'
17
+ load 'combine_pdf/fonts.rb'
18
+ load 'combine_pdf/filter.rb'
19
+ load 'combine_pdf/parser.rb'
20
+ load 'combine_pdf/pdf_public.rb'
21
+ load 'combine_pdf/pdf_protected.rb'
22
+ load 'combine_pdf/exceptions.rb'
22
23
 
23
24
  # load "combine_pdf/operations.rb"
24
25
 
25
- load "combine_pdf/version.rb"
26
-
26
+ load 'combine_pdf/version.rb'
27
27
 
28
28
  # This is a pure ruby library to combine/merge, stmap/overlay and number PDF files - as well as to create tables (ment for indexing combined files).
29
29
  #
@@ -127,11 +127,8 @@ load "combine_pdf/version.rb"
127
127
  #
128
128
  # MIT
129
129
  module CombinePDF
130
-
131
130
  end
132
131
 
133
-
134
-
135
132
  #########################################################
136
133
  # this file is part of the CombinePDF library and the code
137
134
  # is subject to the same license (MIT).
@@ -180,4 +177,3 @@ end
180
177
  ### gives 0.000000 0.000000 1.010000 ( 1.147135)
181
178
  ### file merge FAILS with 1,000 empty pages (undecrypted)
182
179
  ####### gives: 0.000000 0.000000 1.770000 ( 1.775513) with draft. file size 4.9MB (!!!)
183
-
data/test/automated ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'benchmark'
4
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
5
+ require 'combine_pdf'
6
+ require 'bundler/setup'
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ pdf = CombinePDF.load "./Ruby/test\ pdfs/filled_form.pdf"
16
+ pdf.save '01_check_radio_buttuns.pdf'
17
+ pdf << CombinePDF.load("./Ruby/test\ pdfs/empty_form.pdf")
18
+ pdf << CombinePDF.load("./Ruby/test\ pdfs/filled_form.pdf")
19
+ pdf.save '02_check_form_unification_middle_is_empty.pdf'
20
+
21
+ pdf = CombinePDF.load './Ruby/test pdfs/share-font-background.pdf'
22
+ pdf2 = CombinePDF.load './Ruby/test pdfs/share-font-foreground.pdf'
23
+ i = 0
24
+ pdf.pages.each { |pg| pg << pdf2.pages[i] }
25
+ pdf.save '03_check_font_conflict.pdf'
26
+
27
+ pdf = CombinePDF.load './Ruby/test pdfs/nil_1.pdf'
28
+ pdf2 = CombinePDF.load './Ruby/test pdfs/nil_2.pdf'
29
+ pdf << pdf2
30
+ pdf.save '03_01_nil_value_conflict.pdf'
31
+
32
+ pdf = CombinePDF.load './Ruby/test pdfs/names_go_haywire_0.pdf'
33
+ pdf << CombinePDF.load('./Ruby/test pdfs/names_go_haywire_1.pdf')
34
+ pdf.save '04_check_view_and_names_reference.pdf'
35
+
36
+ pdf = CombinePDF.load('./Ruby/test pdfs/outlines/self_merge_err.pdf')
37
+ pdf.save '05_x1_scribus_test.pdf'
38
+ pdf = CombinePDF.load('./Ruby/test pdfs/outlines/self_merge_err.pdf')
39
+ pdf << CombinePDF.load('./Ruby/test pdfs/outlines/self_merge_err.pdf')
40
+ pdf.save '05_x2_scribus_test.pdf'
41
+ # pdf = CombinePDF.load "./Ruby/test pdfs/named_dest.pdf";nil
42
+ # pdf.save '05_check_named_dest_links.pdf' # this will take a while
43
+ # pdf = CombinePDF.load "./Ruby/test pdfs/named_dest.pdf";nil
44
+ pdf << CombinePDF.load('./Ruby/test pdfs/named_dest.pdf'); nil
45
+ pdf.save '05_1_timeless_check_named_dest_links.pdf' # never ends... :-(
46
+
47
+ pdf = CombinePDF.load './Ruby/test pdfs/outline_small.pdf'
48
+ pdf << CombinePDF.load('./Ruby/test pdfs/outline_small.pdf')
49
+ pdf.save '06_check_links_to_second_copy.pdf'
50
+
51
+ lists = %w(./Ruby/test\ pdfs/outlines/self_merge_err.pdf ./Ruby/test\ pdfs/outlines/big_toc.pdf ./Ruby/test\ pdfs/outlines/bigger_toc.pdf ./Ruby/test\ pdfs/outlines/named_dest_no_toc.pdf ./Ruby/test\ pdfs/outlines/named_dest_no_toc2.pdf ./Ruby/test\ pdfs/outlines/named_dest.pdf ./Ruby/test\ pdfs/outlines/named_dest2.pdf)
52
+
53
+ i = 0
54
+ lists.each { |n| CombinePDF.load(n).save("07_#{(i += 1)}_#{n.split('/')[-1]}"); (CombinePDF.load(n) << CombinePDF.load(n)).save("07_#{i}x2_#{n.split('/')[-1]}") }
55
+ pdf = CombinePDF.new
56
+ lists.each { |n| pdf << CombinePDF.load(n) }
57
+ pdf.save('07_named destinations.pdf')
58
+
59
+ pdf = CombinePDF.new
60
+ lists.each { |n| pdf << CombinePDF.load(n) }
61
+ pdf.number_pages(start_at: 1,
62
+ font_size: 14,
63
+ font_color: [0, 0, 0.4],
64
+ box_color: [0.8, 0.8, 0.8],
65
+ border_width: 1,
66
+ border_color: [0.3, 0.3, 0.3],
67
+ box_radius: 8,
68
+ number_location: [:top, :bottom],
69
+ opacity: 0.75)
70
+
71
+ pdf.save('07_named destinations_numbered.pdf')
72
+
73
+ CombinePDF.load("./Ruby/test\ pdfs/Scribus-unknown_err.pdf").save '08_1-unknown-err-empty-str.pdf'
74
+ CombinePDF.load("./Ruby/test\ pdfs/Scribus-unknown_err2.pdf").save '08_2-unknown-err-empty-str.pdf'
75
+ CombinePDF.load("./Ruby/test\ pdfs/Scribus-unknown_err3.pdf").save '08_3-unknown-err-empty-str.pdf'
76
+
77
+ CombinePDF.load("/Users/2Be/Ruby/test\ pdfs/nil_object.pdf").save('09_nil_in_parsed_array.pdf')
78
+
79
+ encrypted = [ "./Ruby/test\ pdfs/pdf-reader/encrypted_version4_revision4_128bit_aes_user_pass_apples_enc_metadata.pdf",
80
+ "./Ruby/test\ pdfs/AESv2\ encrypted.pdf",
81
+ "./Ruby/test\ pdfs/pdf-reader/encrypted_version2_revision3_128bit_rc4_blank_user_pass.pdf",
82
+ "./Ruby/test\ pdfs/AES\ enc.pdf",
83
+ "./Ruby/test\ pdfs/RC4\ enc.pdf"]
84
+
85
+ encrypted.length.times do |i|
86
+ fname = File.basename encrypted[i]
87
+ begin
88
+ CombinePDF.load(encrypted[i]).save "10_#{i}_#{fname}"
89
+ rescue => e
90
+ puts e.class.name, e.message
91
+ if(i == 0)
92
+ puts "CombinePDF expected to fail to read AESv2 #{fname}"
93
+ else
94
+ puts "ERROR: CombinePDF failed to open #{fname}"
95
+ end
96
+ end
97
+ end
98
+
99
+ require 'prawn'
100
+ IO.binwrite '11_prawn.pdf', (Prawn::Document.new { text 'Hello World!' }).render
101
+ page = CombinePDF.parse((Prawn::Document.new { text 'Hello World!' }).render)
102
+ pdf = CombinePDF.new
103
+ pdf << page
104
+ pdf.save '11_parsed_from_prawn.pdf'
105
+ pdf = CombinePDF.new
106
+ pdf << page << page
107
+ pdf.save('11_AcrobatReader_is_unique_page.pdf')
108
+
109
+ puts GC.stat.inspect
110
+ # unify = [
111
+ # "./Ruby/test\ pdfs/AESv2\ encrypted.pdf",
112
+ # "./Ruby/test\ pdfs/data-in-comment.pdf",
113
+ # "./Ruby/test\ pdfs/file_name.pdf",
114
+ # "./Ruby/test\ pdfs/garbage_after_eof.pdf",
115
+ # "./Ruby/test\ pdfs/Many\ comments.pdf",
116
+ # "./Ruby/test\ pdfs/nested\ contents\ array.PDF",
117
+ # "./Ruby/test\ pdfs/nested_resources.pdf",
118
+ # "./Ruby/test\ pdfs/original-missing-endobje.pdf",
119
+ # "./Ruby/test\ pdfs/original-multi-issue.pdf",
120
+ # "./Ruby/test\ pdfs/page_stap_nil_secure.pdf",
121
+ # "./Ruby/test\ pdfs/referenced\ decryption.pdf",
122
+ # '',
123
+ # '',
124
+ # '',
125
+ # '',
126
+ # '',
127
+ # '',
128
+ # ''
129
+ # ]
130
+
131
+ # require 'irb'
132
+ # IRB.start
data/test/console CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'benchmark'
4
- $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__ )
5
- require "combine_pdf"
6
- require "bundler/setup"
4
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
5
+ require 'combine_pdf'
6
+ require 'bundler/setup'
7
7
 
8
8
  # You can add fixtures and/or initialization code here to make experimenting
9
9
  # with your gem easier. You can also use a different console, if you like.
@@ -12,5 +12,5 @@ require "bundler/setup"
12
12
  # require "pry"
13
13
  # Pry.start
14
14
 
15
- require "irb"
15
+ require 'irb'
16
16
  IRB.start
data/test/named_dest ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'benchmark'
4
+ require 'prawn'
5
+
6
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
7
+ require 'combine_pdf'
8
+ require 'bundler/setup'
9
+
10
+ def test_with_non_asci
11
+ b_pdf = Prawn::Document.new do
12
+ text('Hi prawn')
13
+ outline.page title: 'First pdf'
14
+ text '<link anchor="hʤä.l,l o">Click me, to go to päge 3</link>', inline_format: true
15
+ 19.times do |t|
16
+ text "<link anchor=\"hʤä.l,l #{t}\">Click me, to go to päge 3 6</link>", inline_format: true
17
+ end
18
+ start_new_page
19
+ text('Heeeeelloh!')
20
+ text '<link anchor="hʤä.l,l o">Click mee, to go to päge 3</link>', inline_format: true
21
+ start_new_page
22
+ text('Blub')
23
+ add_dest('hʤä.l,l o', dest_fit(page))
24
+ 19.times do |t|
25
+ add_dest("hʤä.l,l #{t}", dest_fit(page))
26
+ end
27
+ outline.page title: '1st pdf page 2'
28
+ outline.define do
29
+ 2.times do |t|
30
+ section "Chapter #{t}", closed: false do
31
+ page title: 'Page 1', destination: 1
32
+ page title: 'Page 2', destination: 2
33
+ page title: 'Page 3', destination: 1
34
+ page title: 'Page 4', destination: 2
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ c_pdf = Prawn::Document.new do
41
+ text('Hi prawn')
42
+ outline.page title: 'First pdf'
43
+ 27.times do |t|
44
+ text "<link anchor=\"hʢä.l,l #{t}\">Click me, to go to päge 3 6</link>", inline_format: true
45
+ end
46
+ start_new_page
47
+ text('Heeeeelloh!')
48
+ text '<link anchor="hʢä.l,l o">Click mee, to go to päge 3</link>', inline_format: true
49
+ start_new_page
50
+ text('Blub')
51
+ add_dest('hʢä.l,l o', dest_fit(page))
52
+ 27.times do |t|
53
+ add_dest("hʢä.l,l #{t}", dest_fit(page))
54
+ end
55
+ outline.page title: '1st pdf page 2'
56
+ outline.define do
57
+ 2.times do |t|
58
+ section "Chapter #{t}", closed: false do
59
+ page title: 'Page 1', destination: 1
60
+ page title: 'Page 2', destination: 2
61
+ page title: 'Page 3', destination: 1
62
+ page title: 'Page 4', destination: 2
63
+ end
64
+ end
65
+ end
66
+ end
67
+ report = CombinePDF.new
68
+ tmp = b_pdf.render
69
+ IO.binwrite('01-named-src1', tmp)
70
+ report << CombinePDF.parse(tmp)
71
+ tmp = c_pdf.render
72
+ IO.binwrite('01-named-src2', tmp)
73
+ report << CombinePDF.parse(tmp)
74
+ report.save('01-named.pdf')
75
+
76
+ report = CombinePDF.new
77
+ report << CombinePDF.parse(c_pdf.render)
78
+ report << CombinePDF.parse(b_pdf.render)
79
+ report.save('01.1-named.pdf')
80
+ end
81
+
82
+ test_with_non_asci
83
+ # require 'irb'
84
+ # IRB.start