combine_pdf 1.0.25 → 1.0.27

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
  SHA256:
3
- metadata.gz: 54586af58c21708b23e6c368b9a0841b4363ebd8113103d9d37deb045032485f
4
- data.tar.gz: 121335c835d1902872e6b53de3b8bfc798dce9c83c0fdfaa0927c47e2771d0cc
3
+ metadata.gz: 2ccb4f95b5400a214f06d343f8b0a5b1a7a38b94ce400f2bbe80cefccd902005
4
+ data.tar.gz: a923391ccc4a37ccf77b89a5113e723d1065e09f0404f0e9c5104034d679363e
5
5
  SHA512:
6
- metadata.gz: a52629b113303a5df9000ac65d33fa5d7843ee74d8977ee7005ae43a476e63917fb462265915b25f8b5b7c6e902cf9a0f4d36f504dbc47651015c058b322c96b
7
- data.tar.gz: 6fb00ab0f0ad9e482a850402e83183e9c1fbc0603093d069930a42ba51d704a4cad5cfb8086a27123cbe06f8b735dade401dc014b843b542cb8b1f84d0a680e0
6
+ metadata.gz: cc63f8c2c83a90a23579a517314248924207b578fc713c520c0ac4596c214cc2c7df50a173638e0659f58e62ae19ae0958b72eb29b6ed29f27d6ea65b9cdf88f
7
+ data.tar.gz: 840b62aa7abda71f22c4b83b697046a666043b73d063f5f60046cb76ed9d6f97086d4e7dc9645ece7ef97184d6e38523d90376ba9d55f9138e0b68519ce8b55e
@@ -9,7 +9,11 @@ jobs:
9
9
  strategy:
10
10
  fail-fast: false
11
11
  matrix:
12
- ruby: ["2.7", "3.0", "3.1", "3.2"]
12
+ ruby: ["2.7", "3.0", "3.1", "3.2", "3.3"]
13
+ rubyopt: [""]
14
+ include:
15
+ - ruby: "3.3"
16
+ rubyopt: "--enable-frozen-string-literal --debug-frozen-string-literal"
13
17
 
14
18
  steps:
15
19
  - name: Checkout code
@@ -25,4 +29,4 @@ jobs:
25
29
  run: bundle lock
26
30
 
27
31
  - name: Run tests
28
- run: bundle exec rake test
32
+ run: bundle exec rake test RUBYOPT="${{ matrix.rubyopt }}"
data/CHANGELOG.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Change Log
2
2
 
3
+ #### Change log v.1.0.26 (2023-12-22)
4
+
5
+ **Performance**: possible performance bump. Credit to @denislavski (Denislav Naydenov) for opening PR #235.
6
+
3
7
  #### Change log v.1.0.25 (2023-12-19)
4
8
 
5
- **Fix**: possible improve memory usage. Credit to @denislavski (Denislav Naydenov) for opening PR #223 and suggesting this change.
9
+ **Fix**: possible improve memory usage. Credit to @denislavski (Denislav Naydenov) for opening PR #233 and suggesting this change.
6
10
 
7
11
  #### Change log v.1.0.24 (2023-10-19)
8
12
 
data/README.md CHANGED
@@ -7,23 +7,23 @@
7
7
 
8
8
  CombinePDF is a nifty model, written in pure Ruby, to parse PDF files and combine (merge) them with other PDF files, watermark them or stamp them (all using the PDF file format and pure Ruby code).
9
9
 
10
- ## Install
10
+ ## Unmaintained - Help Wanted(!)
11
11
 
12
- Install with ruby gems:
12
+ I decided to stop maintaining this gem and hope someone could take over the PR reviews and maintenance of this gem (or simply open a successful fork).
13
13
 
14
- ```ruby
15
- gem install combine_pdf
16
- ```
14
+ I wrote this gem because I needed to solve an issue with bates-numbering existing PDF documents.
17
15
 
18
- ## Help Wanted
16
+ However, since 2014 I have been maintaining the gem for free and for no reason at all, except that I enjoyed sharing it with the community.
19
17
 
20
- I need help maintaining the CombinePDF Ruby gem.
18
+ I love this gem, but I cannot keep maintaining it as I have my own projects to focus own and I need both the time and (more importantly) the mindspace.
21
19
 
22
- I wrote this gem because I needed to solve an issue with bates-numbering existing PDF documents. However, during the last three years or so I have been maintaining the project for no reason at all, except that I enjoyed sharing it with the community.
20
+ ## Install
23
21
 
24
- I love this gem, but I feel it's time I took a step back from maintaining it and concentrate on my music and other things I want to develop.
22
+ Install with ruby gems:
25
23
 
26
- Please hit me up if you would like to join in and eventually take over.
24
+ ```ruby
25
+ gem install combine_pdf
26
+ ```
27
27
 
28
28
  ## Known Limitations
29
29
 
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  module CombinePDF
4
5
  module_function
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  ########################################################
3
4
  ## Thoughts from reading the ISO 32000-1:2008
4
5
  ## this file is part of the CombinePDF library and the code
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  ########################################################
3
4
  ## Thoughts from reading the ISO 32000-1:2008
4
5
  ## this file is part of the CombinePDF library and the code
@@ -137,7 +138,7 @@ module CombinePDF
137
138
  object_key = @key.dup
138
139
  object_key << [encrypted_id].pack('i')[0..2]
139
140
  object_key << [encrypted_generation].pack('i')[0..1]
140
- object_key << 'sAlT'.force_encoding(Encoding::ASCII_8BIT)
141
+ object_key << 'sAlT'.b
141
142
  key_length = object_key.length < 16 ? object_key.length : 16
142
143
 
143
144
  begin
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CombinePDF
2
4
  class EncryptionError < StandardError
3
5
  end
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  ########################################################
3
4
  ## Thoughts from reading the ISO 32000-1:2008
4
5
  ## this file is part of the CombinePDF library and the code
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  ########################################################
3
4
  ## Thoughts from reading the ISO 32000-1:2008
4
5
  ## this file is part of the CombinePDF library and the code
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  ########################################################
3
4
  ## Thoughts from reading the ISO 32000-1:2008
4
5
  ## this file is part of the CombinePDF library and the code
@@ -214,7 +215,7 @@ module CombinePDF
214
215
  options[:text_padding] = 0 if options[:text_padding].to_f >= 1
215
216
 
216
217
  # create box stream
217
- box_stream = ''
218
+ box_stream = +''
218
219
  # set graphic state for box
219
220
  if options[:box_color] || (options[:border_width].to_i > 0 && options[:border_color])
220
221
  # compute x and y position for text
@@ -290,7 +291,7 @@ module CombinePDF
290
291
  # reset x,y by text alignment - x,y are calculated from the bottom left
291
292
  # each unit (1) is 1/72 Inch
292
293
  # create text stream
293
- text_stream = ''
294
+ text_stream = +''
294
295
  if !text.to_s.empty? && options[:font_size] != 0 && (options[:font_color] || options[:stroke_color])
295
296
  # compute x and y position for text
296
297
  x = options[:x] + (options[:width] * options[:text_padding])
@@ -679,7 +680,7 @@ module CombinePDF
679
680
  insert_content 'Q'
680
681
 
681
682
  # Prep content
682
- @contents = ''
683
+ @contents = +''
683
684
  insert_content @contents
684
685
  @contents
685
686
  end
@@ -788,7 +789,7 @@ module CombinePDF
788
789
  # add to array
789
790
  if out.last.nil? || out.last[0] != fonts[i]
790
791
  out.last[1] << '>' unless out.last.nil?
791
- out << [fonts[i], '<', 0, 0]
792
+ out << [fonts[i], (+'<'), 0, 0]
792
793
  end
793
794
  out.last[1] << (fonts_array[i].cmap.nil? ? (c.unpack('H*')[0]) : fonts_array[i].cmap[c])
794
795
  if fonts_array[i].metrics[c]
@@ -262,7 +262,7 @@ module CombinePDF
262
262
  ##########################################
263
263
  elsif @scanner.scan(/\(/)
264
264
  # warn "Found a literal string"
265
- str = ''.force_encoding(Encoding::ASCII_8BIT)
265
+ str = ''.b
266
266
  count = 1
267
267
  while count > 0 && @scanner.rest?
268
268
  scn = @scanner.scan_until(/[\(\)]/)
@@ -369,7 +369,7 @@ module CombinePDF
369
369
  # the following was dicarded because some PDF files didn't have an EOL marker as required
370
370
  # str = @scanner.scan_until(/(\r\n|\r|\n)endstream/)
371
371
  # instead, a non-strict RegExp is used:
372
-
372
+
373
373
 
374
374
  # raise error if the stream doesn't end.
375
375
  unless @scanner.skip_until(/endstream/)
@@ -379,7 +379,7 @@ module CombinePDF
379
379
  length = 0 if(length < 0)
380
380
  length -= 1 if(@scanner.string[old_pos + length - 1] == "\n")
381
381
  length -= 1 if(@scanner.string[old_pos + length - 1] == "\r")
382
- str = (length > 0) ? @scanner.string.slice(old_pos, length) : ''
382
+ str = (length > 0) ? @scanner.string.slice(old_pos, length) : +''
383
383
 
384
384
  # warn "CombinePDF parser: detected Stream #{str.length} bytes long #{str[0..3]}...#{str[-4..-1]}"
385
385
 
@@ -632,17 +632,17 @@ module CombinePDF
632
632
  #
633
633
  def serialize_objects_and_references
634
634
  obj_dir = {}
635
- objid_cache = {}
635
+ objid_cache = {}.compare_by_identity
636
636
  # create a dictionary for referenced objects (no value resolution at this point)
637
637
  # at the same time, delete duplicates and old versions when objects have multiple versions
638
638
  @parsed.uniq!
639
639
  @parsed.length.times do |i|
640
640
  o = @parsed[i]
641
- objid_cache[o.object_id] = i
641
+ objid_cache[o] = i
642
642
  tmp_key = [o[:indirect_reference_id], o[:indirect_generation_number]]
643
643
  if tmp_found = obj_dir[tmp_key]
644
644
  tmp_found.clear
645
- @parsed[objid_cache[tmp_found.object_id]] = nil
645
+ @parsed[objid_cache[tmp_found]] = nil
646
646
  end
647
647
  obj_dir[tmp_key] = o
648
648
  end
@@ -765,9 +765,9 @@ module CombinePDF
765
765
  # end
766
766
 
767
767
  # # run block of code on evey PDF object (PDF objects are class Hash)
768
- # def each_object(object, limit_references = true, already_visited = {}, &block)
768
+ # def each_object(object, limit_references = true, already_visited = {}.compare_by_identity, &block)
769
769
  # unless limit_references
770
- # already_visited[object.object_id] = true
770
+ # already_visited[object] = true
771
771
  # end
772
772
  # case
773
773
  # when object.is_a?(Array)
@@ -776,7 +776,7 @@ module CombinePDF
776
776
  # yield(object)
777
777
  # unless limit_references && object[:is_reference_only]
778
778
  # object.each do |k,v|
779
- # each_object(v, limit_references, already_visited, &block) unless already_visited[v.object_id]
779
+ # each_object(v, limit_references, already_visited, &block) unless already_visited[v]
780
780
  # end
781
781
  # end
782
782
  # end
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  ########################################################
3
4
  ## Thoughts from reading the ISO 32000-1:2008
4
5
  ## this file is part of the CombinePDF library and the code
@@ -21,19 +22,19 @@ module CombinePDF
21
22
  # this is used for internal operations, such as injectng data using the << operator.
22
23
  def add_referenced()
23
24
  # an existing object map
24
- resolved = {}.dup
25
- existing = {}.dup
26
- should_resolve = [].dup
25
+ resolved = {}.compare_by_identity
26
+ existing = {}
27
+ should_resolve = []
27
28
  #set all existing objects as resolved and register their children for future resolution
28
- @objects.each { |obj| existing[obj] = obj ; resolved[obj.object_id] = obj; should_resolve << obj.values}
29
+ @objects.each { |obj| existing[obj] = obj ; resolved[obj] = obj; should_resolve << obj.values}
29
30
  # loop until should_resolve is empty
30
31
  while should_resolve.any?
31
32
  obj = should_resolve.pop
32
- next if resolved[obj.object_id] # the object exists
33
+ next if resolved[obj] # the object exists
33
34
  if obj.is_a?(Hash)
34
35
  referenced = obj[:referenced_object]
35
36
  if referenced && referenced.any?
36
- tmp = resolved[referenced.object_id]
37
+ tmp = resolved[referenced]
37
38
  if !tmp && referenced[:raw_stream_content]
38
39
  tmp = existing[referenced[:raw_stream_content]]
39
40
  # Avoid endless recursion by limiting it to a number of layers (default == 2)
@@ -42,18 +43,18 @@ module CombinePDF
42
43
  if tmp
43
44
  obj[:referenced_object] = tmp
44
45
  else
45
- resolved[obj.object_id] = referenced
46
+ resolved[obj] = referenced
46
47
  # existing[referenced] = referenced
47
48
  existing[referenced[:raw_stream_content]] = referenced
48
49
  should_resolve << referenced
49
50
  @objects << referenced
50
51
  end
51
52
  else
52
- resolved[obj.object_id] = obj
53
- obj.keys.each { |k| should_resolve << obj[k] unless !obj[k].is_a?(Enumerable) || resolved[obj[k].object_id] }
53
+ resolved[obj] = obj
54
+ obj.keys.each { |k| should_resolve << obj[k] unless !obj[k].is_a?(Enumerable) || resolved[obj[k]] }
54
55
  end
55
56
  elsif obj.is_a?(Array)
56
- resolved[obj.object_id] = obj
57
+ resolved[obj] = obj
57
58
  should_resolve.concat obj
58
59
  end
59
60
  end
@@ -78,14 +79,14 @@ module CombinePDF
78
79
  page_list.concat(with_pages) unless with_pages.empty?
79
80
 
80
81
  # duplicate any non-unique pages - This is a special case to resolve Adobe Acrobat Reader issues (see issues #19 and #81)
81
- uniqueness = {}.dup
82
- page_list.each { |page| page = page[:referenced_object] || page; page = page.dup if uniqueness[page.object_id]; uniqueness[page.object_id] = page }
82
+ uniqueness = {}.compare_by_identity
83
+ page_list.each { |page| page = page[:referenced_object] || page; page = page.dup if uniqueness[page]; uniqueness[page] = page }
83
84
  page_list.clear
84
85
  page_list = uniqueness.values
85
86
  uniqueness.clear
86
87
 
87
88
  # build new Pages object
88
- page_object_kids = [].dup
89
+ page_object_kids = []
89
90
  pages_object = { Type: :Pages, Count: page_list.length, Kids: page_object_kids }
90
91
  pages_object_reference = { referenced_object: pages_object, is_reference_only: true }
91
92
  page_list.each { |pg| pg[:Parent] = pages_object_reference; page_object_kids << ({ referenced_object: pg, is_reference_only: true }) }
@@ -186,17 +187,18 @@ module CombinePDF
186
187
  POSSIBLE_NAME_TREES = [:Dests, :AP, :Pages, :IDS, :Templates, :URLS, :JavaScript, :EmbeddedFiles, :AlternatePresentations, :Renditions].to_set.freeze
187
188
 
188
189
  def rebuild_names(name_tree = nil, base = 'CombinePDF_0000000')
190
+ base = +base
189
191
  if name_tree
190
192
  return nil unless name_tree.is_a?(Hash)
191
193
  name_tree = name_tree[:referenced_object] || name_tree
192
194
  dic = []
193
195
  # map a names tree and return a valid name tree. Do not recourse.
194
196
  should_resolve = [name_tree[:Kids], name_tree[:Names]]
195
- resolved = [].to_set
197
+ resolved = Set.new.compare_by_identity
196
198
  while should_resolve.any?
197
199
  pos = should_resolve.pop
198
200
  if pos.is_a? Array
199
- next if resolved.include?(pos.object_id)
201
+ next if resolved.include?(pos)
200
202
  if pos[0].is_a? String
201
203
  (pos.length / 2).times do |i|
202
204
  dic << (pos[i * 2].clear << base.next!)
@@ -209,16 +211,16 @@ module CombinePDF
209
211
  end
210
212
  elsif pos.is_a? Hash
211
213
  pos = pos[:referenced_object] || pos
212
- next if resolved.include?(pos.object_id)
214
+ next if resolved.include?(pos)
213
215
  should_resolve << pos[:Kids] if pos[:Kids]
214
216
  should_resolve << pos[:Names] if pos[:Names]
215
217
  end
216
- resolved << pos.object_id
218
+ resolved << pos
217
219
  end
218
220
  return { referenced_object: { Names: dic }, is_reference_only: true }
219
221
  end
220
222
  @names ||= @names[:referenced_object]
221
- new_names = { Type: :Names }.dup
223
+ new_names = { Type: :Names }
222
224
  POSSIBLE_NAME_TREES.each do |ntree|
223
225
  if @names[ntree]
224
226
  new_names[ntree] = rebuild_names(@names[ntree], base)
@@ -373,7 +375,7 @@ module CombinePDF
373
375
  private
374
376
 
375
377
  def equal_layers obj1, obj2, layer = CombinePDF.eq_depth_limit
376
- return true if obj1.object_id == obj2.object_id
378
+ return true if obj1.equal?(obj2)
377
379
  if obj1.is_a? Hash
378
380
  return false unless obj2.is_a? Hash
379
381
  return false unless obj1.length == obj2.length
@@ -1,5 +1,6 @@
1
1
  # -*- encoding : utf-8 -*-
2
- ########################################################
2
+ ## frozen_string_literal: true
3
+ #######################################################
3
4
  ## Thoughts from reading the ISO 32000-1:2008
4
5
  ## this file is part of the CombinePDF library and the code
5
6
  ## is subject to the same license.
@@ -93,7 +94,7 @@ module CombinePDF
93
94
  @version = 0
94
95
  @viewer_preferences = {}
95
96
  @info = {}
96
- parser ||= PDFParser.new('')
97
+ parser ||= PDFParser.new(+'')
97
98
  raise TypeError, "initialization error, expecting CombinePDF::PDFParser or nil, but got #{parser.class.name}" unless parser.is_a? PDFParser
98
99
  @objects = parser.parse
99
100
 
@@ -207,7 +208,7 @@ module CombinePDF
207
208
  # xref_location = 0
208
209
  # out.each { |line| xref_location += line.bytesize + 1}
209
210
  out << "xref\n0 #{indirect_object_count}\n0000000000 65535 f "
210
- xref.each { |offset| out << ("%010d 00000 n " % offset) }
211
+ xref.each { |offset| out << ("%010d 00000 n ".freeze % offset) }
211
212
  out << 'trailer'.freeze
212
213
  out << "<<\n/Root #{false || "#{catalog[:indirect_reference_id]} #{catalog[:indirect_generation_number]} R"}"
213
214
  out << "/Size #{indirect_object_count}"
@@ -216,7 +217,7 @@ module CombinePDF
216
217
  # when finished, remove the numbering system and keep only pointers
217
218
  remove_old_ids
218
219
  # output the pdf stream
219
- out.join("\n".force_encoding(Encoding::ASCII_8BIT)).force_encoding(Encoding::ASCII_8BIT)
220
+ out.join("\n".b).force_encoding(Encoding::ASCII_8BIT)
220
221
  end
221
222
 
222
223
  # this method returns all the pages cataloged in the catalog.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CombinePDF
2
3
  ################################################################
3
4
  ## These are common functions, used within the different classes
@@ -123,12 +124,12 @@ module CombinePDF
123
124
  # if the object is not a simple object, it is a dictionary
124
125
  # A dictionary shall be written as a sequence of key-value pairs enclosed in double angle brackets (<<...>>)
125
126
  # (using LESS-THAN SIGNs (3Ch) and GREATER-THAN SIGNs (3Eh)).
126
- out << "<<\n".force_encoding(Encoding::ASCII_8BIT)
127
+ out << "<<\n".b
127
128
  object.each do |key, value|
128
129
  out << "#{object_to_pdf key} #{object_to_pdf value}\n".force_encoding(Encoding::ASCII_8BIT) unless PDF::PRIVATE_HASH_KEYS.include? key
129
130
  end
130
131
  object.delete :Length
131
- out << '>>'.force_encoding(Encoding::ASCII_8BIT)
132
+ out << '>>'.b
132
133
  out << "\nstream\n#{object[:raw_stream_content]}\nendstream".force_encoding(Encoding::ASCII_8BIT) if object[:raw_stream_content]
133
134
  out << "\nendobj\n" if object[:indirect_reference_id]
134
135
  out.join.force_encoding(Encoding::ASCII_8BIT)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CombinePDF
2
- VERSION = '1.0.25'.freeze
4
+ VERSION = '1.0.27'
3
5
  end
data/lib/combine_pdf.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'zlib'
4
5
  require 'securerandom'
@@ -12,7 +12,7 @@ class CombinePDFRendererTest < Minitest::Test
12
12
 
13
13
  def test_numeric_array_to_pdf
14
14
  input = [1.234567, 0.000054, 5, -0.000099]
15
- expected = "[1.234567 0.000054 5 -0.000099]".force_encoding('BINARY')
15
+ expected = "[1.234567 0.000054 5 -0.000099]".b
16
16
  actual = TestRenderer.new.test_object(input)
17
17
 
18
18
  assert_equal(expected, actual)
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: 1.0.25
4
+ version: 1.0.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-19 00:00:00.000000000 Z
11
+ date: 2024-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-rc4