combine_pdf 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2dad9414d26c8b0a8e197cba983fce4f01aa395
4
- data.tar.gz: c13bcf4b4ca5b8eb1bcd64c2f06bd86a24a6fb1a
3
+ metadata.gz: 01bc76f1d54a27d2c7eb98b55a37655d48edd496
4
+ data.tar.gz: 809d1607c8142ce61b31b078b3f3b24ff2716a05
5
5
  SHA512:
6
- metadata.gz: 9426188b388f66219a205367e0560883905deb20d7d4bd2041dff0b98cd71126edf46a889caf776830d2af6337fd2b2fef93c503fbbc3a24ad1dde1ea93e004f
7
- data.tar.gz: b7089d932bfba7936735ae8e01e0aebf75da0b945b3e9b00033d28e3fe9619d218fd26cf0e011c01f960cd713d45714326370816b78fa7e146d8e13f3d64b95d
6
+ metadata.gz: ce3db7d462887c3ef9e38e675efae4d8ddd5f4a18636566f695ccb3e5fa357e722c463f5d383889bcd67e5f15da65838549fe3d034aa8fd0adf22f3a9b2deca8
7
+ data.tar.gz: 0744547ed7fcdbb415925248ffa0b9e766269b90c236c30904a396dbfdd121dfe6ad3ac8f8fac72ea371dbf2ed47cb29fcdc9f9b8d956e6a03fff1354dbfe668
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ***
4
4
 
5
+ Change log v.0.1.8
6
+
7
+ **fix**: Fixed an issue reported by Saba, where PDF files that were written using bad practices (namely, without wrapping their content streams correctly) would not be stamped correctly due to changes in the space matrix (CTM). Fixed by wrapping all existing streams.
8
+
9
+ ***
10
+
5
11
  Change log v.0.1.7
6
12
 
7
13
  **fix**: PDF `insert` had a typo in the code that would cause failure when unsupported object insertion was attempted - fixed by Nathan Keyes (nkeyes).
data/lib/combine_pdf.rb CHANGED
@@ -17,6 +17,8 @@ load "combine_pdf/combine_pdf_parser.rb"
17
17
  load "combine_pdf/combine_pdf_pdf.rb"
18
18
  require "combine_pdf/version"
19
19
 
20
+ load "combine_pdf/combine_pdf_methods.rb"
21
+
20
22
 
21
23
  # 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).
22
24
  #
@@ -103,185 +105,7 @@ require "combine_pdf/version"
103
105
  #
104
106
  # MIT
105
107
  module CombinePDF
106
- module_function
107
-
108
- # Create an empty PDF object or create a PDF object from a file (parsing the file).
109
- # file_name:: is the name of a file to be parsed.
110
- def load(file_name = "")
111
- raise TypeError, "couldn't parse and data, expecting type String" unless file_name.is_a?(String) || file_name.is_a?(Pathname)
112
- return PDF.new() if file_name == ''
113
- PDF.new( PDFParser.new( IO.read(file_name).force_encoding(Encoding::ASCII_8BIT) ) )
114
- end
115
- def new(file_name = "")
116
- load(file_name)
117
- end
118
-
119
- # Create a PDF object from a raw PDF data (parsing the data).
120
- # data:: is a string that represents the content of a PDF file.
121
- def parse(data)
122
- raise TypeError, "couldn't parse and data, expecting type String" unless data.is_a? String
123
- PDF.new( PDFParser.new(data) )
124
- end
125
- # makes a PDFWriter object
126
- #
127
- # PDFWriter objects reresent an empty page and have the method "textbox"
128
- # that adds content to that page.
129
- #
130
- # PDFWriter objects are used internally for numbering pages (by creating a PDF page
131
- # with the page number and "stamping" it over the existing page).
132
- #
133
- # ::mediabox an Array representing the size of the PDF document. defaults to: [0.0, 0.0, 612.0, 792.0]
134
- #
135
- # if the page is PDFWriter object as a stamp, the final size will be that of the original page.
136
- def create_page(mediabox = [0, 0, 595.3, 841.9])
137
- PDFWriter.new mediabox
138
- end
139
-
140
- # makes a PDF object containing a table
141
- #
142
- # all the pages in this PDF object are PDFWriter objects and are
143
- # writable using the texbox function (should you wish to add a title, or more info)
144
- #
145
- # the main intended use of this method is to create indexes (a table of contents) for merged data.
146
- #
147
- # example:
148
- # pdf = CombinePDF.create_table headers: ["header 1", "another header"], table_data: [ ["this is one row", "with two columns"] , ["this is another row", "also two columns", "the third will be ignored"] ]
149
- # pdf.save "table_file.pdf"
150
- #
151
- # accepts a Hash with any of the following keys as well as any of the PDFWriter#textbox options:
152
- # headers:: an Array of strings with the headers (will be repeated every page).
153
- # table_data:: as Array of Arrays, each containing a string for each column. the first row sets the number of columns. extra columns will be ignored.
154
- # font:: a registered or standard font name (see PDFWriter). defaults to nil (:Helvetica).
155
- # header_font:: a registered or standard font name for the headers (see PDFWriter). defaults to nil (the font for all the table rows).
156
- # max_font_size:: the maximum font size. if the string doesn't fit, it will be resized. defaults to 14.
157
- # column_widths:: an array of relative column widths ([1,2] will display only the first two columns, the second twice as big as the first). defaults to nil (even widths).
158
- # header_color:: the header color. defaults to [0.8, 0.8, 0.8] (light gray).
159
- # main_color:: main row color. defaults to nil (transparent / white).
160
- # alternate_color:: alternate row color. defaults to [0.95, 0.95, 0.95] (very light gray).
161
- # font_color:: font color. defaults to [0,0,0] (black).
162
- # border_color:: border color. defaults to [0,0,0] (black).
163
- # border_width:: border width in PDF units. defaults to 1.
164
- # header_align:: the header text alignment within each column (:right, :left, :center). defaults to :center.
165
- # row_align:: the row text alignment within each column. defaults to :left (:right for RTL table).
166
- # direction:: the table's writing direction (:ltr or :rtl). this reffers to the direction of the columns and doesn't effect text (rtl text is automatically recognized). defaults to :ltr.
167
- # rows_per_page:: the number of rows per page, INCLUDING the header row. deafults to 25.
168
- # page_size:: the size of the page in PDF points. defaults to [0, 0, 595.3, 841.9] (A4).
169
- def create_table(options = {})
170
- defaults = {
171
- headers: nil,
172
- table_data: [[]],
173
- font: nil,
174
- header_font: nil,
175
- max_font_size: 14,
176
- column_widths: nil,
177
- header_color: [0.8, 0.8, 0.8],
178
- main_color: nil,
179
- alternate_color: [0.95, 0.95, 0.95],
180
- font_color: [0,0,0],
181
- border_color: [0,0,0],
182
- border_width: 1,
183
- header_align: :center,
184
- row_align: nil,
185
- direction: :ltr,
186
- rows_per_page: 25,
187
- page_size: [0, 0, 595.3, 841.9] #A4
188
- }
189
- options = defaults.merge options
190
- options[:header_font] = options[:font] unless options[:header_font]
191
- options[:row_align] ||= ( (options[:direction] == :rtl) ? :right : :left )
192
- # assert table_data is an array of arrays
193
- return false unless (options[:table_data].select {|r| !r.is_a?(Array) }).empty?
194
- # compute sizes
195
- page_size = options[:page_size]
196
- top = page_size[3] * 0.9
197
- height = page_size[3] * 0.8 / options[:rows_per_page]
198
- from_side = page_size[2] * 0.1
199
- width = page_size[2] * 0.8
200
- columns = options[:table_data][0].length
201
- column_widths = []
202
- columns.times {|i| column_widths << (width/columns) }
203
- if options[:column_widths]
204
- scale = 0
205
- options[:column_widths].each {|w| scale += w}
206
- column_widths = []
207
- options[:column_widths].each { |w| column_widths << (width*w/scale) }
208
- end
209
- column_widths = column_widths.reverse if options[:direction] == :rtl
210
- # set pdf object and start writing the data
211
- table = PDF.new()
212
- page = nil
213
- rows_per_page = options[:rows_per_page]
214
- row_number = rows_per_page + 1
215
-
216
- options[:table_data].each do |row_data|
217
- if row_number > rows_per_page
218
- page = create_page page_size
219
- table << page
220
- row_number = 1
221
- # add headers
222
- if options[:headers]
223
- x = from_side
224
- headers = options[:headers]
225
- headers = headers.reverse if options[:direction] == :rtl
226
- column_widths.each_index do |i|
227
- text = headers[i].to_s
228
- page.textbox text, {x: x, y: (top - (height*row_number)), width: column_widths[i], height: height, box_color: options[:header_color], text_align: options[:header_align] }.merge(options).merge({font: options[:header_font]})
229
- x += column_widths[i]
230
- end
231
- row_number += 1
232
- end
233
- end
234
- x = from_side
235
- row_data = row_data.reverse if options[:direction] == :rtl
236
- column_widths.each_index do |i|
237
- text = row_data[i].to_s
238
- box_color = options[:main_color]
239
- box_color = options[:alternate_color] if options[:alternate_color] && row_number.odd?
240
- page.textbox text, {x: x, y: (top - (height*row_number)), width: column_widths[i], height: height, box_color: box_color, text_align: options[:row_align]}.merge(options)
241
- x += column_widths[i]
242
- end
243
- row_number += 1
244
- end
245
- table
246
- end
247
- def new_table(options = {})
248
- create_table options
249
- end
250
-
251
- # adds a correctly formatted font object to the font library.
252
- #
253
- # registered fonts will remain in the library and will only be embeded in
254
- # PDF objects when they are used by PDFWriter objects (for example, for numbering pages).
255
- #
256
- # this function enables plug-ins to expend the font functionality of CombinePDF.
257
- #
258
- # font_name:: a Symbol with the name of the font. if the fonts exists in the library, it will be overwritten!
259
- # font_metrics:: a Hash of font metrics, of the format char => {wx: char_width, boundingbox: [left_x, buttom_y, right_x, top_y]} where char == character itself (i.e. " " for space). The Hash should contain a special value :missing for the metrics of missing characters. an optional :wy might be supported in the future, for up to down fonts.
260
- # font_pdf_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
261
- # font_cmap:: a CMap dictionary Hash) which maps unicode characters to the hex CID for the font (i.e. {"a" => "61", "z" => "7a" }).
262
- def register_font(font_name, font_metrics, font_pdf_object, font_cmap = nil)
263
- Fonts.register_font font_name, font_metrics, font_pdf_object, font_cmap
264
- end
265
108
 
266
- # adds an existing font (from any PDF Object) to the font library.
267
- #
268
- # returns the font on success or false on failure.
269
- #
270
- # example:
271
- # fonts = CombinePDF.new("japanese_fonts.pdf").fonts(true)
272
- # CombinePDF.register_font_from_pdf_object :david, fonts[0]
273
- #
274
- # VERY LIMITTED SUPPORT:
275
- # - at the moment it only imports Type0 fonts.
276
- # - also, to extract the Hash of the actual font object you were looking for, is not a trivial matter. I do it on the console.
277
- # font_name:: a Symbol with the name of the font registry. if the fonts exists in the library, it will be overwritten!
278
- # font_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
279
- def register_existing_font font_name, font_object
280
- Fonts.register_font_from_pdf_object font_name, font_object
281
- end
282
- def register_font_from_pdf_object font_name, font_object
283
- register_existing_font font_name, font_object
284
- end
285
109
  end
286
110
 
287
111
 
@@ -301,7 +125,7 @@ end
301
125
  ## test performance with:
302
126
  ## puts Benchmark.measure { pdf = CombinePDF.new(file); pdf.save "test.pdf" } # PDFEditor.new_pdf
303
127
  ## demo: file_name = "~/Ruby/pdfs/encrypted.pdf"; pdf=0; puts Benchmark.measure { pdf = CombinePDF.new(file_name); pdf.save "test.pdf" }
304
- ## at the moment... my code it terribly slow for larger files... :(
128
+ ## at the moment... my code is terribly slow for larger files... :(
305
129
  ## The file saving is solved (I hope)... but file loading is an issue.
306
130
  ## pdf.each_object {|obj| puts "Stream length: #{obj[:raw_stream_content].length} was registered as #{obj[:Length].is_a?(Hash)? obj[:Length][:referenced_object][:indirect_without_dictionary] : obj[:Length]}" if obj[:raw_stream_content] }
307
131
  ## pdf.objects.each {|obj| puts "#{obj.class.name}: #{obj[:indirect_reference_id]}, #{obj[:indirect_generation_number]} is: #{obj[:Type] || obj[:indirect_without_dictionary]}" }
@@ -114,7 +114,8 @@ module CombinePDF
114
114
  border_color: nil,
115
115
  border_width: 0,
116
116
  box_radius: 0,
117
- opacity: 1
117
+ opacity: 1,
118
+ ctm: [1,0,0,1,0,0]
118
119
  }
119
120
  options.update properties
120
121
  # reset the length and height to meaningful values, if negative
@@ -134,7 +135,7 @@ module CombinePDF
134
135
 
135
136
  # set graphic state for the box
136
137
  box_stream << "q\n"
137
- box_graphic_state = { ca: options[:opacity], CA: options[:opacity], LW: options[:border_width], LC: 0, LJ: 0, LD: 0 }
138
+ box_graphic_state = { ca: options[:opacity], CA: options[:opacity], LW: options[:border_width], LC: 0, LJ: 0, LD: 0, CTM: options[:ctm]}
138
139
  if options[:box_radius] != 0 # if the text box has rounded corners
139
140
  box_graphic_state[:LC], box_graphic_state[:LJ] = 2, 1
140
141
  end
@@ -228,7 +229,7 @@ module CombinePDF
228
229
 
229
230
  # set graphic state for text
230
231
  text_stream << "q\n"
231
- text_graphic_state = graphic_state({ca: options[:opacity], CA: options[:opacity], LW: options[:stroke_width].to_f, LC: 2, LJ: 1, LD: 0})
232
+ text_graphic_state = graphic_state({ca: options[:opacity], CA: options[:opacity], LW: options[:stroke_width].to_f, LC: 2, LJ: 1, LD: 0, CTM: options[:ctm]})
232
233
  text_stream << "#{PDFOperations._object_to_pdf text_graphic_state} gs\n"
233
234
  text_stream << "DeviceRGB CS\nDeviceRGB cs\n"
234
235
  # set text render mode
@@ -45,6 +45,19 @@ module CombinePDF
45
45
  when 1,2
46
46
  # raise_encrypted_error
47
47
  _perform_decrypt_proc_ @objects, self.method(:decrypt_RC4)
48
+ when 4
49
+ # raise unsupported error for now
50
+ raise_encrypted_error
51
+ # make sure CF is a Hash (as required by the PDF standard for this type of encryption).
52
+ raise_encrypted_error unless @encryption_dictionary[:CF].is_a?(Hash)
53
+
54
+ # do nothing if there is no data to decrypt except embeded files...?
55
+ return true unless (@encryption_dictionary[:CF].values.select { |v| !v[:AuthEvent] || v[:AuthEvent] == :DocOpen } ).empty?
56
+
57
+ # attempt to decrypt all strings?
58
+ # attempt to decrypy all streams
59
+ # attempt to decrypt all embeded files?
60
+
48
61
  else
49
62
  raise_encrypted_error
50
63
  end
@@ -0,0 +1,187 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+
4
+
5
+
6
+
7
+ module CombinePDF
8
+ module_function
9
+
10
+ # Create an empty PDF object or create a PDF object from a file (parsing the file).
11
+ # file_name:: is the name of a file to be parsed.
12
+ def load(file_name = "")
13
+ raise TypeError, "couldn't parse and data, expecting type String" unless file_name.is_a?(String) || file_name.is_a?(Pathname)
14
+ return PDF.new() if file_name == ''
15
+ PDF.new( PDFParser.new( IO.read(file_name).force_encoding(Encoding::ASCII_8BIT) ) )
16
+ end
17
+ def new(file_name = "")
18
+ load(file_name)
19
+ end
20
+
21
+ # Create a PDF object from a raw PDF data (parsing the data).
22
+ # data:: is a string that represents the content of a PDF file.
23
+ def parse(data)
24
+ raise TypeError, "couldn't parse and data, expecting type String" unless data.is_a? String
25
+ PDF.new( PDFParser.new(data) )
26
+ end
27
+ # makes a PDFWriter object
28
+ #
29
+ # PDFWriter objects reresent an empty page and have the method "textbox"
30
+ # that adds content to that page.
31
+ #
32
+ # PDFWriter objects are used internally for numbering pages (by creating a PDF page
33
+ # with the page number and "stamping" it over the existing page).
34
+ #
35
+ # ::mediabox an Array representing the size of the PDF document. defaults to: [0.0, 0.0, 612.0, 792.0]
36
+ #
37
+ # if the page is PDFWriter object as a stamp, the final size will be that of the original page.
38
+ def create_page(mediabox = [0, 0, 595.3, 841.9])
39
+ PDFWriter.new mediabox
40
+ end
41
+
42
+ # makes a PDF object containing a table
43
+ #
44
+ # all the pages in this PDF object are PDFWriter objects and are
45
+ # writable using the texbox function (should you wish to add a title, or more info)
46
+ #
47
+ # the main intended use of this method is to create indexes (a table of contents) for merged data.
48
+ #
49
+ # example:
50
+ # pdf = CombinePDF.create_table headers: ["header 1", "another header"], table_data: [ ["this is one row", "with two columns"] , ["this is another row", "also two columns", "the third will be ignored"] ]
51
+ # pdf.save "table_file.pdf"
52
+ #
53
+ # accepts a Hash with any of the following keys as well as any of the PDFWriter#textbox options:
54
+ # headers:: an Array of strings with the headers (will be repeated every page).
55
+ # table_data:: as Array of Arrays, each containing a string for each column. the first row sets the number of columns. extra columns will be ignored.
56
+ # font:: a registered or standard font name (see PDFWriter). defaults to nil (:Helvetica).
57
+ # header_font:: a registered or standard font name for the headers (see PDFWriter). defaults to nil (the font for all the table rows).
58
+ # max_font_size:: the maximum font size. if the string doesn't fit, it will be resized. defaults to 14.
59
+ # column_widths:: an array of relative column widths ([1,2] will display only the first two columns, the second twice as big as the first). defaults to nil (even widths).
60
+ # header_color:: the header color. defaults to [0.8, 0.8, 0.8] (light gray).
61
+ # main_color:: main row color. defaults to nil (transparent / white).
62
+ # alternate_color:: alternate row color. defaults to [0.95, 0.95, 0.95] (very light gray).
63
+ # font_color:: font color. defaults to [0,0,0] (black).
64
+ # border_color:: border color. defaults to [0,0,0] (black).
65
+ # border_width:: border width in PDF units. defaults to 1.
66
+ # header_align:: the header text alignment within each column (:right, :left, :center). defaults to :center.
67
+ # row_align:: the row text alignment within each column. defaults to :left (:right for RTL table).
68
+ # direction:: the table's writing direction (:ltr or :rtl). this reffers to the direction of the columns and doesn't effect text (rtl text is automatically recognized). defaults to :ltr.
69
+ # rows_per_page:: the number of rows per page, INCLUDING the header row. deafults to 25.
70
+ # page_size:: the size of the page in PDF points. defaults to [0, 0, 595.3, 841.9] (A4).
71
+ def create_table(options = {})
72
+ defaults = {
73
+ headers: nil,
74
+ table_data: [[]],
75
+ font: nil,
76
+ header_font: nil,
77
+ max_font_size: 14,
78
+ column_widths: nil,
79
+ header_color: [0.8, 0.8, 0.8],
80
+ main_color: nil,
81
+ alternate_color: [0.95, 0.95, 0.95],
82
+ font_color: [0,0,0],
83
+ border_color: [0,0,0],
84
+ border_width: 1,
85
+ header_align: :center,
86
+ row_align: nil,
87
+ direction: :ltr,
88
+ rows_per_page: 25,
89
+ page_size: [0, 0, 595.3, 841.9] #A4
90
+ }
91
+ options = defaults.merge options
92
+ options[:header_font] = options[:font] unless options[:header_font]
93
+ options[:row_align] ||= ( (options[:direction] == :rtl) ? :right : :left )
94
+ # assert table_data is an array of arrays
95
+ return false unless (options[:table_data].select {|r| !r.is_a?(Array) }).empty?
96
+ # compute sizes
97
+ page_size = options[:page_size]
98
+ top = page_size[3] * 0.9
99
+ height = page_size[3] * 0.8 / options[:rows_per_page]
100
+ from_side = page_size[2] * 0.1
101
+ width = page_size[2] * 0.8
102
+ columns = options[:table_data][0].length
103
+ column_widths = []
104
+ columns.times {|i| column_widths << (width/columns) }
105
+ if options[:column_widths]
106
+ scale = 0
107
+ options[:column_widths].each {|w| scale += w}
108
+ column_widths = []
109
+ options[:column_widths].each { |w| column_widths << (width*w/scale) }
110
+ end
111
+ column_widths = column_widths.reverse if options[:direction] == :rtl
112
+ # set pdf object and start writing the data
113
+ table = PDF.new()
114
+ page = nil
115
+ rows_per_page = options[:rows_per_page]
116
+ row_number = rows_per_page + 1
117
+
118
+ options[:table_data].each do |row_data|
119
+ if row_number > rows_per_page
120
+ page = create_page page_size
121
+ table << page
122
+ row_number = 1
123
+ # add headers
124
+ if options[:headers]
125
+ x = from_side
126
+ headers = options[:headers]
127
+ headers = headers.reverse if options[:direction] == :rtl
128
+ column_widths.each_index do |i|
129
+ text = headers[i].to_s
130
+ page.textbox text, {x: x, y: (top - (height*row_number)), width: column_widths[i], height: height, box_color: options[:header_color], text_align: options[:header_align] }.merge(options).merge({font: options[:header_font]})
131
+ x += column_widths[i]
132
+ end
133
+ row_number += 1
134
+ end
135
+ end
136
+ x = from_side
137
+ row_data = row_data.reverse if options[:direction] == :rtl
138
+ column_widths.each_index do |i|
139
+ text = row_data[i].to_s
140
+ box_color = options[:main_color]
141
+ box_color = options[:alternate_color] if options[:alternate_color] && row_number.odd?
142
+ page.textbox text, {x: x, y: (top - (height*row_number)), width: column_widths[i], height: height, box_color: box_color, text_align: options[:row_align]}.merge(options)
143
+ x += column_widths[i]
144
+ end
145
+ row_number += 1
146
+ end
147
+ table
148
+ end
149
+ def new_table(options = {})
150
+ create_table options
151
+ end
152
+
153
+ # adds a correctly formatted font object to the font library.
154
+ #
155
+ # registered fonts will remain in the library and will only be embeded in
156
+ # PDF objects when they are used by PDFWriter objects (for example, for numbering pages).
157
+ #
158
+ # this function enables plug-ins to expend the font functionality of CombinePDF.
159
+ #
160
+ # font_name:: a Symbol with the name of the font. if the fonts exists in the library, it will be overwritten!
161
+ # font_metrics:: a Hash of font metrics, of the format char => {wx: char_width, boundingbox: [left_x, buttom_y, right_x, top_y]} where char == character itself (i.e. " " for space). The Hash should contain a special value :missing for the metrics of missing characters. an optional :wy might be supported in the future, for up to down fonts.
162
+ # font_pdf_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
163
+ # font_cmap:: a CMap dictionary Hash) which maps unicode characters to the hex CID for the font (i.e. {"a" => "61", "z" => "7a" }).
164
+ def register_font(font_name, font_metrics, font_pdf_object, font_cmap = nil)
165
+ Fonts.register_font font_name, font_metrics, font_pdf_object, font_cmap
166
+ end
167
+
168
+ # adds an existing font (from any PDF Object) to the font library.
169
+ #
170
+ # returns the font on success or false on failure.
171
+ #
172
+ # example:
173
+ # fonts = CombinePDF.new("japanese_fonts.pdf").fonts(true)
174
+ # CombinePDF.register_font_from_pdf_object :david, fonts[0]
175
+ #
176
+ # VERY LIMITTED SUPPORT:
177
+ # - at the moment it only imports Type0 fonts.
178
+ # - also, to extract the Hash of the actual font object you were looking for, is not a trivial matter. I do it on the console.
179
+ # font_name:: a Symbol with the name of the font registry. if the fonts exists in the library, it will be overwritten!
180
+ # font_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
181
+ def register_existing_font font_name, font_object
182
+ Fonts.register_font_from_pdf_object font_name, font_object
183
+ end
184
+ def register_font_from_pdf_object font_name, font_object
185
+ register_existing_font font_name, font_object
186
+ end
187
+ end
@@ -16,6 +16,13 @@ module CombinePDF
16
16
  # differentiate between complex PDF objects.
17
17
  PRIVATE_HASH_KEYS = [:indirect_reference_id, :indirect_generation_number, :raw_stream_content, :is_reference_only, :referenced_object, :indirect_without_dictionary]
18
18
 
19
+ # holds a simple content stream that starts a PDF graphic state container - used for wrapping malformed PDF content streams.
20
+ CONTENT_CONTAINER_START = { is_reference_only: true , referenced_object: {indirect_reference_id: 0, raw_stream_content: 'q'} }
21
+ # holds a simple content stream that ends a PDF graphic state container - used for wrapping malformed PDF content streams.
22
+ CONTENT_CONTAINER_MIDDLE = { is_reference_only: true , referenced_object: {indirect_reference_id: 0, raw_stream_content: "Q\nq"} }
23
+ # holds a simple content stream that ends a PDF graphic state container - used for wrapping malformed PDF content streams.
24
+ CONTENT_CONTAINER_END = { is_reference_only: true , referenced_object: {indirect_reference_id: 0, raw_stream_content: 'Q'} }
25
+
19
26
  # @private
20
27
  # @!visibility private
21
28
  #:nodoc: all
@@ -75,10 +82,16 @@ module CombinePDF
75
82
 
76
83
  if top # if this is a stamp (overlay)
77
84
  page[:Contents] = original_contents
85
+ page[:Contents].unshift CONTENT_CONTAINER_START.dup
86
+ page[:Contents].push CONTENT_CONTAINER_MIDDLE.dup
78
87
  page[:Contents].push *stream_contents
88
+ page[:Contents].push CONTENT_CONTAINER_END.dup
79
89
  else #if this was a watermark (underlay? would be lost if the page was scanned, as white might not be transparent)
80
90
  page[:Contents] = stream_contents
91
+ page[:Contents].unshift CONTENT_CONTAINER_START.dup
92
+ page[:Contents].push CONTENT_CONTAINER_MIDDLE.dup
81
93
  page[:Contents].push *original_contents
94
+ page[:Contents].push CONTENT_CONTAINER_END.dup
82
95
  end
83
96
 
84
97
  page
@@ -139,7 +152,8 @@ module CombinePDF
139
152
  stream[:raw_stream_content].gsub! _object_to_pdf(old_key), _object_to_pdf(new_key) ##### PRAY(!) that the parsed datawill be correctly reproduced!
140
153
  end
141
154
  # patch back to PDF defaults, for OCRed PDF files.
142
- stream[:raw_stream_content] = "q\nq\nq\nDeviceRGB CS\nDeviceRGB cs\n0 0 0 rg\n0 0 0 RG\n0 Tr\n%s\nQ\nQ\nQ\n" % stream[:raw_stream_content]
155
+ # stream[:raw_stream_content] = "q\nq\nq\nDeviceRGB CS\nDeviceRGB cs\n0 0 0 rg\n0 0 0 RG\n0 Tr\n%s\nQ\nQ\nQ\n" % stream[:raw_stream_content]
156
+ stream[:raw_stream_content] = "q\nq\nq\nDeviceRGB CS\nDeviceRGB cs\n0 0 0 rg\n0 0 0 RG\n0 Tr\n1 0 0 1 0 0 cm\n%s\nQ\nQ\nQ\n" % stream[:raw_stream_content]
143
157
  end
144
158
 
145
159
  new_page
@@ -158,6 +172,13 @@ module CombinePDF
158
172
  end
159
173
  end
160
174
 
175
+ # returns the PDF Object Hash holding the acutal data (if exists) or the original hash (if it wasn't a reference)
176
+ #
177
+ # works only AFTER references have been connected.
178
+ def get_referenced object
179
+ object[:referenced_object] || object
180
+ end
181
+
161
182
 
162
183
  # Ruby normally assigns pointes.
163
184
  # noramlly:
@@ -531,7 +531,7 @@ module CombinePDF
531
531
  each_object do |obj|
532
532
  if obj[:is_reference_only]
533
533
  obj[:referenced_object] = objects_reference_hash[ [obj[:indirect_reference_id], obj[:indirect_generation_number] ] ]
534
- warn "couldn't connect a reference!!! could be a null object, Silent error!!!" unless obj[:referenced_object]
534
+ warn "couldn't connect a reference!!! could be a null or removed (empty) object, Silent error!!!" unless obj[:referenced_object]
535
535
  end
536
536
  end
537
537
 
@@ -1,3 +1,3 @@
1
1
  module CombinePdf
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: combine_pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-18 00:00:00.000000000 Z
11
+ date: 2014-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-rc4
@@ -73,6 +73,7 @@ files:
73
73
  - lib/combine_pdf/combine_pdf_decrypt.rb
74
74
  - lib/combine_pdf/combine_pdf_filter.rb
75
75
  - lib/combine_pdf/combine_pdf_fonts.rb
76
+ - lib/combine_pdf/combine_pdf_methods.rb
76
77
  - lib/combine_pdf/combine_pdf_operations.rb
77
78
  - lib/combine_pdf/combine_pdf_parser.rb
78
79
  - lib/combine_pdf/combine_pdf_pdf.rb
@@ -102,4 +103,3 @@ signing_key:
102
103
  specification_version: 4
103
104
  summary: Combine, stamp and watermark PDF files in pure Ruby.
104
105
  test_files: []
105
- has_rdoc: