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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +273 -27
- data/LICENSE.txt +2 -1
- data/README.md +69 -4
- data/lib/combine_pdf/api.rb +156 -153
- data/lib/combine_pdf/basic_writer.rb +41 -53
- data/lib/combine_pdf/decrypt.rb +238 -228
- data/lib/combine_pdf/exceptions.rb +4 -0
- data/lib/combine_pdf/filter.rb +79 -85
- data/lib/combine_pdf/fonts.rb +451 -462
- data/lib/combine_pdf/page_methods.rb +891 -946
- data/lib/combine_pdf/parser.rb +663 -531
- data/lib/combine_pdf/pdf_protected.rb +341 -126
- data/lib/combine_pdf/pdf_public.rb +492 -454
- data/lib/combine_pdf/renderer.rb +146 -141
- data/lib/combine_pdf/version.rb +1 -2
- data/lib/combine_pdf.rb +14 -18
- data/test/automated +132 -0
- data/test/console +4 -4
- data/test/named_dest +84 -0
- metadata +8 -5
- data/lib/combine_pdf/operations.rb +0 -416
data/lib/combine_pdf/renderer.rb
CHANGED
@@ -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
|
-
|
5
|
-
|
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
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
172
|
+
# numbers are Integers or Float
|
168
173
|
# boolean are TrueClass or FalseClass
|
data/lib/combine_pdf/version.rb
CHANGED
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
|
13
|
-
load
|
14
|
-
load
|
15
|
-
load
|
16
|
-
load
|
17
|
-
load
|
18
|
-
load
|
19
|
-
load
|
20
|
-
load
|
21
|
-
load
|
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
|
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
|
6
|
-
require
|
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
|
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
|