origamindee 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 593f3f22174161d77e20e75ff2a7a9ae577662861a59881073f78f2c51ac89df
4
- data.tar.gz: fe531fa4cc790be0639dcc9703c782bb4f64d9f38ff29a468950fbc131369e52
3
+ metadata.gz: 45413662e5fb14c50c4dd206522be8e187950c6119b3f8a3a1c2632f363d797d
4
+ data.tar.gz: 6a016143c0898b649e1b386af4daf117e3c023f7acb414f7a48dbc578ec0afdc
5
5
  SHA512:
6
- metadata.gz: 92eb2ab3d99b12da581091ecc5958ac21e3a692294d1c41cca586aa38be18f9d0c91a667cdaeee40be40cd3bd8f593747b03bf6528c675d9dd5df91fa671a4b0
7
- data.tar.gz: 9aef422a383150380c49a548429d48cd34905982fae8ef1a0a88f782e5103f5d95944f0c22b037ea2c9781575314f73e84a0f6a780b1164047806a79177cba9d
6
+ metadata.gz: 3612b2edae8776c43acb2715a38e16e47a0416d83b11568136b053ad4f9137f1d827b371d26a25861dfdd12fcd92af871ae02c8ba714ce915fcd0191f6134bf2
7
+ data.tar.gz: 8bacd88b9cb1020e1986aaafe75520f1ec993d299b3dbfb98e70f0041609e57c954a8ca8a14752e3cd6b2091507282570c15f263c9edfe590509846288774bff
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ 4.0.0
2
+ -----
3
+ * Add running tests on Ruby 3.4
4
+ * Drop support for Ruby < 3.0.0
5
+ * Accept Elliptic Curve (EC) private key
6
+
7
+ 3.1.0
8
+ -----
9
+ * Add page deletion methods
10
+ * Add running tests on Ruby 3.2
11
+
1
12
  3.0.0
2
13
  -----
3
14
  * Replaced Colorize with Rainbow for LGPL compatibility
data/README.md CHANGED
@@ -27,7 +27,9 @@ Origami is able to parse PDF, FDF and PPKLite (Adobe certificate store) files.
27
27
 
28
28
  Requirements
29
29
  ------------
30
- The following Ruby versions are tested and supported: 2.6, 2.7, 3.0, 3.1
30
+ The following Ruby versions are tested and supported: 3.0, 3.1, 3.2, 3.3, 3.4
31
+
32
+ (It could maybe, possibly, in some cases run on Ruby 2.7, but no guarantees. Update your stack.)
31
33
 
32
34
  Some optional features require additional gems:
33
35
 
@@ -114,7 +116,7 @@ It was therefore replaced by `Rainbow` which has similar functionality, and is l
114
116
 
115
117
  Furthermore, we are now in a better position to fix any problems related to PDF parsing that are encountered by our users.
116
118
 
117
- As such it is our intention to support functionalities within the scope of our client library.
119
+ As such it is our intention to support functionalities within the scope of our Ruby client library.
118
120
 
119
121
  **We do not claim to be an official successor to Origami.**
120
122
 
data/bin/shell/console.rb CHANGED
@@ -1,128 +1,128 @@
1
- =begin
2
-
3
- This file is part of Origami, PDF manipulation framework for Ruby
4
- Copyright (C) 2016 Guillaume Delugré.
5
-
6
- Origami is free software: you can redistribute it and/or modify
7
- it under the terms of the GNU Lesser General Public License as published by
8
- the Free Software Foundation, either version 3 of the License, or
9
- (at your option) any later version.
10
-
11
- Origami is distributed in the hope that it will be useful,
12
- but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- GNU Lesser General Public License for more details.
15
-
16
- You should have received a copy of the GNU Lesser General Public License
17
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
-
19
- =end
20
-
21
- require 'tempfile'
22
- require 'hexdump'
23
- require 'colorize'
24
-
25
- String.disable_colorization(false)
26
-
27
- module Origami
28
- module Object
29
- def inspect
30
- to_s
31
- end
32
- end
33
-
34
- class Stream
35
- def edit(editor = ENV['EDITOR'])
36
- Tempfile.open("origami") do |tmpfile|
37
- tmpfile.write(self.data)
38
- tmpfile.flush
39
-
40
- Process.wait Kernel.spawn "#{editor} #{tmpfile.path}"
41
-
42
- self.data = File.read(tmpfile.path)
43
- tmpfile.unlink
44
- end
45
-
46
- true
47
- end
48
-
49
- def inspect
50
- self.data.hexdump
51
- end
52
- end
53
-
54
- class Page < Dictionary
55
- def edit
56
- each_content_stream do |stream|
57
- stream.edit
58
- end
59
- end
60
- end
61
-
62
- class PDF
63
- if defined?(PDF::JavaScript::Engine)
64
- class JavaScript::Engine
65
- def shell
66
- loop do
67
- print "js > ".magenta
68
- break if (line = gets).nil?
69
-
70
- begin
71
- puts exec(line)
72
- rescue V8::JSError => e
73
- puts "Error: #{e.message}"
74
- end
75
- end
76
- end
77
- end
78
- end
79
-
80
- class Revision
81
- def to_s
82
- puts "---------- Body ----------".white.bold
83
- @body.each_value do |obj|
84
- print "#{obj.reference.to_s.rjust(8,' ')}".ljust(10).magenta
85
- puts "#{obj.type}".yellow
86
- end
87
-
88
- puts "---------- Trailer ---------".white.bold
89
- if not @trailer.dictionary
90
- puts " [x] No trailer found.".blue
91
- else
92
- @trailer.dictionary.each_pair do |entry, value|
93
- print " [*] ".magenta
94
- print "#{entry}: ".yellow
95
- puts "#{value}".red
96
- end
97
-
98
- print " [+] ".magenta
99
- print "startxref: ".yellow
100
- puts "#{@trailer.startxref}".red
101
- end
102
- end
103
-
104
- def inspect
105
- to_s
106
- end
107
- end
108
-
109
- def to_s
110
- puts
111
-
112
- puts "---------- Header ----------".white.bold
113
- print " [+] ".magenta
114
- print "Version: ".yellow
115
- puts "#{@header.major_version}.#{@header.minor_version}".red
116
-
117
- @revisions.each do |revision|
118
- revision.to_s
119
- end
120
- puts
121
- end
122
-
123
- def inspect
124
- to_s
125
- end
126
- end
127
-
128
- end
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ require 'tempfile'
22
+ require 'hexdump'
23
+ require 'colorize'
24
+
25
+ String.disable_colorization(false)
26
+
27
+ module Origami
28
+ module Object
29
+ def inspect
30
+ to_s
31
+ end
32
+ end
33
+
34
+ class Stream
35
+ def edit(editor = ENV['EDITOR'])
36
+ Tempfile.open("origami") do |tmpfile|
37
+ tmpfile.write(self.data)
38
+ tmpfile.flush
39
+
40
+ Process.wait Kernel.spawn "#{editor} #{tmpfile.path}"
41
+
42
+ self.data = File.read(tmpfile.path)
43
+ tmpfile.unlink
44
+ end
45
+
46
+ true
47
+ end
48
+
49
+ def inspect
50
+ self.data.hexdump
51
+ end
52
+ end
53
+
54
+ class Page < Dictionary
55
+ def edit
56
+ each_content_stream do |stream|
57
+ stream.edit
58
+ end
59
+ end
60
+ end
61
+
62
+ class PDF
63
+ if defined?(PDF::JavaScript::Engine)
64
+ class JavaScript::Engine
65
+ def shell
66
+ loop do
67
+ print "js > ".magenta
68
+ break if (line = gets).nil?
69
+
70
+ begin
71
+ puts exec(line)
72
+ rescue V8::JSError => e
73
+ puts "Error: #{e.message}"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ class Revision
81
+ def to_s
82
+ puts "---------- Body ----------".white.bold
83
+ @body.each_value do |obj|
84
+ print "#{obj.reference.to_s.rjust(8,' ')}".ljust(10).magenta
85
+ puts "#{obj.type}".yellow
86
+ end
87
+
88
+ puts "---------- Trailer ---------".white.bold
89
+ if not @trailer.dictionary
90
+ puts " [x] No trailer found.".blue
91
+ else
92
+ @trailer.dictionary.each_pair do |entry, value|
93
+ print " [*] ".magenta
94
+ print "#{entry}: ".yellow
95
+ puts "#{value}".red
96
+ end
97
+
98
+ print " [+] ".magenta
99
+ print "startxref: ".yellow
100
+ puts "#{@trailer.startxref}".red
101
+ end
102
+ end
103
+
104
+ def inspect
105
+ to_s
106
+ end
107
+ end
108
+
109
+ def to_s
110
+ puts
111
+
112
+ puts "---------- Header ----------".white.bold
113
+ print " [+] ".magenta
114
+ print "Version: ".yellow
115
+ puts "#{@header.major_version}.#{@header.minor_version}".red
116
+
117
+ @revisions.each do |revision|
118
+ revision.to_s
119
+ end
120
+ puts
121
+ end
122
+
123
+ def inspect
124
+ to_s
125
+ end
126
+ end
127
+
128
+ end
@@ -28,7 +28,7 @@ module Origami
28
28
  attr_reader :operator
29
29
  attr_accessor :operands
30
30
 
31
- @insns = Hash.new(operands: [], render: lambda{})
31
+ @insns = Hash.new({ operands: [], render: lambda{} })
32
32
 
33
33
  def initialize(operator, *operands)
34
34
  @operator = operator
data/lib/origami/page.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin
2
4
 
3
5
  This file is part of Origami, PDF manipulation framework for Ruby
@@ -57,6 +59,28 @@ module Origami
57
59
  self
58
60
  end
59
61
 
62
+ #
63
+ # Deletes a page at position _index_ from the document.
64
+ # _index_:: Page index (starting from one).
65
+ #
66
+ def delete_page_at(index)
67
+ init_page_tree
68
+
69
+ self.Catalog.Pages.delete_page_at(index)
70
+ self
71
+ end
72
+
73
+ #
74
+ # Deletes a page at position _index_ from the document.
75
+ # _index_:: Page index (starting from one).
76
+ #
77
+ def delete_pages_at(delete_idx)
78
+ init_page_tree
79
+
80
+ self.Catalog.Pages.delete_pages_at(delete_idx)
81
+ self
82
+ end
83
+
60
84
  #
61
85
  # Returns an Enumerator of Page
62
86
  #
@@ -107,7 +131,7 @@ module Origami
107
131
  end
108
132
 
109
133
  unless self.Catalog.Pages.is_a?(PageTreeNode)
110
- raise InvalidPageTreeError, "Root page node is not a PageTreeNode"
134
+ raise InvalidPageTreeError, 'Root page node is not a PageTreeNode'
111
135
  end
112
136
  end
113
137
  end
@@ -250,11 +274,11 @@ module Origami
250
274
  field EXTGSTATE, :Type => Dictionary
251
275
  field COLORSPACE, :Type => Dictionary
252
276
  field PATTERN, :Type => Dictionary
253
- field SHADING, :Type => Dictionary, :Version => "1.3"
277
+ field SHADING, :Type => Dictionary, :Version => '1.3'
254
278
  field XOBJECT, :Type => Dictionary
255
279
  field FONT, :Type => Dictionary
256
280
  field :ProcSet, :Type => Array.of(Name)
257
- field PROPERTIES, :Type => Dictionary, :Version => "1.2"
281
+ field PROPERTIES, :Type => Dictionary, :Version => '1.2'
258
282
 
259
283
  def pre_build
260
284
  add_font(Font::Type1::Standard::Helvetica.new.pre_build) unless self.Font
@@ -294,11 +318,11 @@ module Origami
294
318
  # Inserts a page into the node at a specified position (starting from 1).
295
319
  #
296
320
  def insert_page(n, page)
297
- raise IndexError, "Page numbers are referenced starting from 1" if n < 1
321
+ raise IndexError, 'Page numbers are referenced starting from 1' if n < 1
298
322
 
299
323
  kids = self.Kids
300
324
  unless kids.is_a?(Array)
301
- raise InvalidPageTreeError, "Kids must be an Array"
325
+ raise InvalidPageTreeError, 'Kids must be an Array'
302
326
  end
303
327
 
304
328
  count = 0
@@ -323,15 +347,87 @@ module Origami
323
347
  return self
324
348
  end
325
349
  else
326
- raise InvalidPageTreeError, "not a Page or PageTreeNode"
350
+ raise InvalidPageTreeError, 'not a Page or PageTreeNode'
327
351
  end
328
352
  end
329
353
 
330
- raise IndexError, "Out of order page index" unless count + 1 == n
354
+ raise IndexError, 'Out of order page index' unless count + 1 == n
331
355
 
332
356
  self.append_page(page)
333
357
  end
334
358
 
359
+ #
360
+ # Delete a page in the node at the specified position (starting from 1).
361
+ #
362
+ def delete_page_at(n)
363
+ raise IndexError, 'Page numbers are referenced starting from 1' if n < 1
364
+
365
+ kids = self.Kids
366
+ unless kids.is_a?(Array)
367
+ raise InvalidPageTreeError, 'Kids must be an Array'
368
+ end
369
+
370
+ count = 0
371
+ kids.each_with_index do |kid, index|
372
+ node = kid.solve
373
+
374
+ case node
375
+ when Page
376
+ count = count + 1
377
+ if count == n
378
+ kids.delete_at(index)
379
+ self.Count -= 1
380
+ return self
381
+ end
382
+
383
+ when PageTreeNode
384
+ count = count + node.Count
385
+ if count >= n
386
+ node.delete_page_at(n - count + node.Count)
387
+ self.Count -= 1
388
+ return self
389
+ end
390
+ else
391
+ raise InvalidPageTreeError, 'not a Page or PageTreeNode'
392
+ end
393
+ end
394
+
395
+ raise IndexError, 'Out of order page index' unless count + 1 == n
396
+ end
397
+
398
+ #
399
+ # Delete a set of page in the node at the specified positions (starting from 0).
400
+ #
401
+ def delete_pages_at(delete_idx, offset = 0)
402
+ kids = self.Kids
403
+ unless kids.is_a?(Array)
404
+ raise InvalidPageTreeError, 'Kids must be an Array'
405
+ end
406
+
407
+ deleted = 0
408
+ kids.delete_if.with_index do |kid, idx|
409
+ node = kid.solve
410
+ idx = idx + offset
411
+
412
+ case node
413
+ when Page
414
+ if delete_idx.include?(idx)
415
+ deleted += 1
416
+ true
417
+ else
418
+ false
419
+ end
420
+ when PageTreeNode
421
+ deleted += node.delete_pages_at(delete_idx, idx)
422
+ false
423
+ else
424
+ raise InvalidPageTreeError, 'not a Page or PageTreeNode'
425
+ end
426
+ end
427
+ self.Count -= deleted
428
+ deleted
429
+ end
430
+
335
431
  #
336
432
  # Returns an Array of Pages inheriting this tree node.
337
433
  #
@@ -346,11 +442,11 @@ module Origami
346
442
  return enum_for(__method__) { self.Count.to_i } unless block_given?
347
443
 
348
444
  if browsed_nodes.any?{|node| node.equal?(self)}
349
- raise InvalidPageTreeError, "Cyclic tree graph detected"
445
+ raise InvalidPageTreeError, 'Cyclic tree graph detected'
350
446
  end
351
447
 
352
448
  unless self.Kids.is_a?(Array)
353
- raise InvalidPageTreeError, "Kids must be an Array"
449
+ raise InvalidPageTreeError, 'Kids must be an Array'
354
450
  end
355
451
 
356
452
  browsed_nodes.push(self)
@@ -363,7 +459,7 @@ module Origami
363
459
  when PageTreeNode then node.each_page(browsed_nodes: browsed_nodes, &block)
364
460
  when Page then yield(node)
365
461
  else
366
- raise InvalidPageTreeError, "not a Page or PageTreeNode"
462
+ raise InvalidPageTreeError, 'not a Page or PageTreeNode'
367
463
  end
368
464
  end
369
465
  end
@@ -375,10 +471,10 @@ module Origami
375
471
  # Get the n-th Page object in this node, starting from 1.
376
472
  #
377
473
  def get_page(n)
378
- raise IndexError, "Page numbers are referenced starting from 1" if n < 1
379
- raise IndexError, "Page not found" if n > self.Count.to_i
474
+ raise IndexError, 'Page numbers are referenced starting from 1' if n < 1
475
+ raise IndexError, 'Page not found' if n > self.Count.to_i
380
476
 
381
- self.each_page.lazy.drop(n - 1).first or raise IndexError, "Page not found"
477
+ self.each_page.lazy.drop(n - 1).first or raise IndexError, 'Page not found'
382
478
  end
383
479
 
384
480
  #
@@ -481,8 +577,8 @@ module Origami
481
577
  class AdditionalActions < Dictionary
482
578
  include StandardObject
483
579
 
484
- field :O, :Type => Dictionary, :Version => "1.2" # Page Open
485
- field :C, :Type => Dictionary, :Version => "1.2" # Page Close
580
+ field :O, :Type => Dictionary, :Version => '1.2' # Page Open
581
+ field :C, :Type => Dictionary, :Version => '1.2' # Page Close
486
582
  end
487
583
 
488
584
  module Format
@@ -513,34 +609,34 @@ module Origami
513
609
 
514
610
  field :Type, :Type => Name, :Default => :Page, :Required => true
515
611
  field :Parent, :Type => PageTreeNode, :Required => true
516
- field :LastModified, :Type => String, :Version => "1.3"
612
+ field :LastModified, :Type => String, :Version => '1.3'
517
613
  field :Resources, :Type => Resources, :Required => true
518
614
  field :MediaBox, :Type => Rectangle, :Default => Format::A4, :Required => true
519
615
  field :CropBox, :Type => Rectangle
520
- field :BleedBox, :Type => Rectangle, :Version => "1.3"
521
- field :TrimBox, :Type => Rectangle, :Version => "1.3"
522
- field :ArtBox, :Type => Rectangle, :Version => "1.3"
523
- field :BoxColorInfo, :Type => BoxColorInformation, :Version => "1.4"
616
+ field :BleedBox, :Type => Rectangle, :Version => '1.3'
617
+ field :TrimBox, :Type => Rectangle, :Version => '1.3'
618
+ field :ArtBox, :Type => Rectangle, :Version => '1.3'
619
+ field :BoxColorInfo, :Type => BoxColorInformation, :Version => '1.4'
524
620
  field :Contents, :Type => [ ContentStream, Array.of(ContentStream) ]
525
621
  field :Rotate, :Type => Integer, :Default => 0
526
- field :Group, :Type => Dictionary, :Version => "1.4"
622
+ field :Group, :Type => Dictionary, :Version => '1.4'
527
623
  field :Thumb, :Type => Graphics::ImageXObject
528
- field :B, :Type => Array, :Version => "1.1"
529
- field :Dur, :Type => Integer, :Version => "1.1"
530
- field :Trans, :Type => Dictionary, :Version => "1.1"
624
+ field :B, :Type => Array, :Version => '1.1'
625
+ field :Dur, :Type => Integer, :Version => '1.1'
626
+ field :Trans, :Type => Dictionary, :Version => '1.1'
531
627
  field :Annots, :Type => Array.of(Annotation)
532
- field :AA, :Type => AdditionalActions, :Version => "1.2"
533
- field :Metadata, :Type => MetadataStream, :Version => "1.4"
534
- field :PieceInfo, :Type => Dictionary, :Version => "1.2"
535
- field :StructParents, :Type => Integer, :Version => "1.3"
628
+ field :AA, :Type => AdditionalActions, :Version => '1.2'
629
+ field :Metadata, :Type => MetadataStream, :Version => '1.4'
630
+ field :PieceInfo, :Type => Dictionary, :Version => '1.2'
631
+ field :StructParents, :Type => Integer, :Version => '1.3'
536
632
  field :ID, :Type => String
537
633
  field :PZ, :Type => Number
538
- field :SeparationInfo, :Type => Dictionary, :Version => "1.3"
539
- field :Tabs, :Type => Name, :Version => "1.5"
540
- field :TemplateAssociated, :Type => Name, :Version => "1.5"
541
- field :PresSteps, :Type => NavigationNode, :Version => "1.5"
542
- field :UserUnit, :Type => Number, :Default => 1.0, :Version => "1.6"
543
- field :VP, :Type => Dictionary, :Version => "1.6"
634
+ field :SeparationInfo, :Type => Dictionary, :Version => '1.3'
635
+ field :Tabs, :Type => Name, :Version => '1.5'
636
+ field :TemplateAssociated, :Type => Name, :Version => '1.5'
637
+ field :PresSteps, :Type => NavigationNode, :Version => '1.5'
638
+ field :UserUnit, :Type => Number, :Default => 1.0, :Version => '1.6'
639
+ field :VP, :Type => Dictionary, :Version => '1.6'
544
640
 
545
641
  def initialize(hash = {}, parser = nil)
546
642
  super(hash, parser)
@@ -70,7 +70,7 @@ module Origami
70
70
  #
71
71
  # Sign the document with the given key and x509 certificate.
72
72
  # _certificate_:: The X509 certificate containing the public key.
73
- # _key_:: The private key associated with the certificate.
73
+ # _key_:: The RSA or EC private key associated with the certificate.
74
74
  # _method_:: The PDF signature identifier.
75
75
  # _ca_:: Optional CA certificates used to sign the user certificate.
76
76
  # _annotation_:: Annotation associated with the signature.
@@ -89,19 +89,19 @@ module Origami
89
89
  reason: nil)
90
90
 
91
91
  unless certificate.is_a?(OpenSSL::X509::Certificate)
92
- raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
92
+ raise TypeError, 'A OpenSSL::X509::Certificate object must be passed.'
93
93
  end
94
94
 
95
- unless key.is_a?(OpenSSL::PKey::RSA)
96
- raise TypeError, "A OpenSSL::PKey::RSA object must be passed."
95
+ unless (key.is_a?(OpenSSL::PKey::RSA) || key.is_a?(OpenSSL::PKey::EC))
96
+ raise TypeError, 'An OpenSSL::PKey::RSA or OpenSSL::PKey::EC object must be passed.'
97
97
  end
98
98
 
99
99
  unless ca.is_a?(::Array)
100
- raise TypeError, "Expected an Array of CA certificates."
100
+ raise TypeError, 'Expected an Array of CA certificates.'
101
101
  end
102
102
 
103
103
  unless annotation.nil? or annotation.is_a?(Annotation::Widget::Signature)
104
- raise TypeError, "Expected a Annotation::Widget::Signature object."
104
+ raise TypeError, 'Expected a Annotation::Widget::Signature object.'
105
105
  end
106
106
 
107
107
  #
@@ -125,7 +125,8 @@ module Origami
125
125
  InteractiveForm::SigFlags::SIGNATURES_EXIST | InteractiveForm::SigFlags::APPEND_ONLY
126
126
 
127
127
  digsig.Type = :Sig
128
- digsig.Contents = HexaString.new("\x00" * Signature::required_size(method, certificate, key, ca))
128
+ placeholder_size = Signature::required_size(method, certificate, key, ca) + 128
129
+ digsig.Contents = HexaString.new("\x00" * placeholder_size)
129
130
  digsig.Filter = :"Adobe.PPKLite"
130
131
  digsig.SubFilter = Name.new(method)
131
132
  digsig.ByteRange = [0, 0, 0, 0]
@@ -327,7 +328,7 @@ module Origami
327
328
  r1.end != start_sig or
328
329
  r2.begin != end_sig
329
330
 
330
- raise SignatureError, "Invalid signature byte range"
331
+ raise SignatureError, 'Invalid signature byte range'
331
332
  end
332
333
 
333
334
  self.original_data[r1] + self.original_data[r2]
@@ -589,7 +590,7 @@ module Origami
589
590
  byte_range = self.ByteRange
590
591
 
591
592
  unless byte_range.is_a?(Array) and byte_range.length == 4 and byte_range.all? {|i| i.is_a?(Integer) }
592
- raise SignatureError, "Invalid ByteRange field value"
593
+ raise SignatureError, 'Invalid ByteRange field value'
593
594
  end
594
595
 
595
596
  byte_range.map(&:to_i).each_slice(2).map do |start, length|
@@ -608,7 +609,7 @@ module Origami
608
609
 
609
610
  chain = self.Cert
610
611
  unless chain.is_a?(String) or (chain.is_a?(Array) and chain.all?{|cert| cert.is_a?(String)})
611
- return SignatureError, "Invalid embedded certificate chain"
612
+ return SignatureError, 'Invalid embedded certificate chain'
612
613
  end
613
614
 
614
615
  [ chain ].flatten.map! {|str| OpenSSL::X509::Certificate.new(str) }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin
2
4
 
3
5
  This file is part of Origami, PDF manipulation framework for Ruby
@@ -19,5 +21,5 @@
19
21
  =end
20
22
 
21
23
  module Origami
22
- VERSION = "3.0.0"
24
+ VERSION = '4.0.0'
23
25
  end
data/lib/origami.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  =begin
2
3
 
3
4
  This file is part of Origami, PDF manipulation framework for Ruby
Binary file
Binary file
Binary file
data/test/test_actions.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'minitest/autorun'
2
2
 
3
- class TestActions < MiniTest::Test
3
+ class TestActions < Minitest::Test
4
4
  def setup
5
5
  @target = PDF.new
6
6
  @page = Page.new
data/test/test_pages.rb CHANGED
@@ -51,10 +51,33 @@ class TestPages < Minitest::Test
51
51
  assert_raises(IndexError) { @target.insert_page(1000, Page.new) }
52
52
  end
53
53
 
54
- def test_example_write_page
54
+ def test_example_write_delete_page
55
55
  @target.append_page
56
- @target.pages.first.write 'Hello, world!', size: 30
56
+ @target.pages.last.write 'Hello, page 1 world!', size: 30
57
+ @target.append_page
58
+ @target.pages.last.write 'Hello, page 2 world!', size: 30
59
+ @target.save(@output)
60
+ assert_equal @target.Catalog.Pages.Count, 2
61
+
62
+ @target.delete_page_at(1)
57
63
  @target.save(@output)
58
64
  assert_equal @target.Catalog.Pages.Count, 1
59
65
  end
66
+
67
+ def test_delete_pages_generated
68
+ to_append = [Page.new, Page.new, Page.new, PageTreeNode.new, Page.new, Page.new, Page.new]
69
+ to_append.each { |page| @target.append_page(page) }
70
+ to_del = [0,1]
71
+ assert_equal @target.Catalog.Pages.Count, to_append.length
72
+ @target.delete_pages_at(to_del)
73
+ assert_equal @target.Catalog.Pages.Count, to_append.length - to_del.length
74
+ end
75
+
76
+ def test_delete_pages_parsed
77
+ file = File.join(__dir__, 'dataset', '3_pages.pdf')
78
+ pdf = PDF.read(file, ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
79
+ assert_equal pdf.Catalog.Pages.Count, 3
80
+ pdf.delete_pages_at([0,2])
81
+ assert_equal pdf.Catalog.Pages.Count, 1
82
+ end
60
83
  end
@@ -1,18 +1,11 @@
1
1
  require 'minitest/autorun'
2
2
 
3
3
  class TestPDFParser < Minitest::Test
4
- def setup
5
- @files =
6
- %w{
7
- dataset/empty.pdf
8
- dataset/calc.pdf
9
- dataset/crypto.pdf
10
- }
11
- end
12
4
 
13
5
  def test_parse_pdf
14
- @files.each do |file|
15
- pdf = PDF.read(File.join(__dir__, file), ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
6
+ dir = File.join(__dir__, 'dataset')
7
+ Dir.children(dir).each do | filename |
8
+ pdf = PDF.read(File.join(dir, filename), ignore_errors: false, verbosity: Parser::VERBOSE_QUIET)
16
9
 
17
10
  assert_instance_of PDF, pdf
18
11
 
@@ -4,9 +4,7 @@ require 'openssl'
4
4
 
5
5
  class TestSign < Minitest::Test
6
6
 
7
- def create_self_signed_ca_certificate(key_size, expires)
8
- key = OpenSSL::PKey::RSA.new key_size
9
-
7
+ def create_self_signed_ca_certificate(key, expires)
10
8
  name = OpenSSL::X509::Name.parse 'CN=origami/DC=example'
11
9
 
12
10
  cert = OpenSSL::X509::Certificate.new
@@ -14,8 +12,7 @@ class TestSign < Minitest::Test
14
12
  cert.serial = 0
15
13
  cert.not_before = Time.now
16
14
  cert.not_after = Time.now + expires
17
-
18
- cert.public_key = key.public_key
15
+ cert.public_key = key
19
16
  cert.subject = name
20
17
 
21
18
  extension_factory = OpenSSL::X509::ExtensionFactory.new
@@ -29,12 +26,28 @@ class TestSign < Minitest::Test
29
26
  cert.issuer = name
30
27
  cert.sign key, OpenSSL::Digest::SHA256.new
31
28
 
32
- [ cert, key ]
29
+ cert
30
+ end
31
+
32
+ def ec_test_data(curve_name)
33
+ key = OpenSSL::PKey::EC.generate(curve_name)
34
+ other_key = OpenSSL::PKey::EC.generate(curve_name)
35
+ cert = create_self_signed_ca_certificate(key, 3600)
36
+ other_cert = create_self_signed_ca_certificate(other_key, 3600)
37
+ [ cert, key, other_cert ]
38
+ end
39
+
40
+ def rsa_test_data(key_size)
41
+ key = OpenSSL::PKey::RSA.new(key_size)
42
+ other_key = OpenSSL::PKey::RSA.new(key_size)
43
+ cert = create_self_signed_ca_certificate(key, 3600)
44
+ other_cert = create_self_signed_ca_certificate(other_key, 3600)
45
+ [ cert, key, other_cert ]
33
46
  end
34
47
 
35
48
  def setup
36
- @cert, @key = create_self_signed_ca_certificate(1024, 3600)
37
- @other_cert, @other_key = create_self_signed_ca_certificate(1024, 3600)
49
+ @rsa_1024_data = rsa_test_data(1024)
50
+ @ec_prime256v1_data = ec_test_data('prime256v1')
38
51
  end
39
52
 
40
53
  def setup_document_with_annotation
@@ -51,10 +64,10 @@ class TestSign < Minitest::Test
51
64
  [ document, annotation ]
52
65
  end
53
66
 
54
- def sign_document_with_method(method)
67
+ def sign_document_with_method(method, cert, key, other_cert)
55
68
  document, annotation = setup_document_with_annotation
56
69
 
57
- document.sign(@cert, @key,
70
+ document.sign(cert, key,
58
71
  method: method,
59
72
  annotation: annotation,
60
73
  issuer: "Guillaume Delugré",
@@ -73,25 +86,37 @@ class TestSign < Minitest::Test
73
86
 
74
87
  refute document.verify
75
88
  assert document.verify(allow_self_signed: true)
76
- assert document.verify(trusted_certs: [@cert])
77
- refute document.verify(trusted_certs: [@other_cert])
89
+ assert document.verify(trusted_certs: [cert])
90
+ refute document.verify(trusted_certs: [other_cert])
78
91
 
79
92
  result = document.verify do |ctx|
80
- ctx.error == OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT and ctx.current_cert.to_pem == @cert.to_pem
93
+ ctx.error == OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT and ctx.current_cert.to_pem == cert.to_pem
81
94
  end
82
95
 
83
96
  assert result
84
97
  end
85
98
 
86
- def test_sign_pkcs7_sha1
87
- sign_document_with_method(Signature::PKCS7_SHA1)
99
+ def test_rsa_sign_pkcs7_sha1
100
+ sign_document_with_method(Signature::PKCS7_SHA1, *@rsa_1024_data)
101
+ end
102
+
103
+ def test_rsa_sign_pkcs7_detached
104
+ sign_document_with_method(Signature::PKCS7_DETACHED, *@rsa_1024_data)
105
+ end
106
+
107
+ def test_rsa_sign_x509_sha1
108
+ sign_document_with_method(Signature::PKCS1_RSA_SHA1, *@rsa_1024_data)
109
+ end
110
+
111
+ def test_ec_sign_pkcs7_sha1
112
+ sign_document_with_method(Signature::PKCS7_SHA1, *@ec_prime256v1_data)
88
113
  end
89
114
 
90
- def test_sign_pkcs7_detached
91
- sign_document_with_method(Signature::PKCS7_DETACHED)
115
+ def test_ec_sign_pkcs7_detached
116
+ sign_document_with_method(Signature::PKCS7_DETACHED, *@ec_prime256v1_data)
92
117
  end
93
118
 
94
- def test_sign_x509_sha1
95
- sign_document_with_method(Signature::PKCS1_RSA_SHA1)
119
+ def test_ec_sign_x509_sha1
120
+ sign_document_with_method(Signature::PKCS1_RSA_SHA1, *@ec_prime256v1_data)
96
121
  end
97
122
  end
data/test/test_xrefs.rb CHANGED
@@ -2,7 +2,7 @@ require 'minitest/autorun'
2
2
  require 'stringio'
3
3
  require 'strscan'
4
4
 
5
- class TestXrefs < MiniTest::Test
5
+ class TestXrefs < Minitest::Test
6
6
 
7
7
  def setup
8
8
  @target = PDF.new
metadata CHANGED
@@ -1,16 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: origamindee
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Delugré
8
8
  - Mindee, SA
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-12-26 00:00:00.000000000 Z
12
+ date: 2025-02-25 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: base64
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.1.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.1.0
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: rainbow
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -82,23 +96,23 @@ dependencies:
82
96
  - !ruby/object:Gem::Version
83
97
  version: '12.3'
84
98
  - !ruby/object:Gem::Dependency
85
- name: rdoc
99
+ name: yard
86
100
  requirement: !ruby/object:Gem::Requirement
87
101
  requirements:
88
102
  - - "~>"
89
103
  - !ruby/object:Gem::Version
90
- version: '5.0'
104
+ version: '0.9'
91
105
  type: :development
92
106
  prerelease: false
93
107
  version_requirements: !ruby/object:Gem::Requirement
94
108
  requirements:
95
109
  - - "~>"
96
110
  - !ruby/object:Gem::Version
97
- version: '5.0'
111
+ version: '0.9'
98
112
  description: Mindee's fork of Origami, a pure Ruby library to parse, modify and generate
99
113
  PDF documents.
100
114
  email:
101
- - devrel@mindee.co
115
+ - opensource@mindee.co
102
116
  executables:
103
117
  - pdfsh
104
118
  - pdf2pdfa
@@ -232,9 +246,12 @@ files:
232
246
  - lib/origami/xfa/xfdf.rb
233
247
  - lib/origami/xfa/xmpmeta.rb
234
248
  - lib/origami/xreftable.rb
249
+ - test/dataset/3_pages.pdf
235
250
  - test/dataset/calc.pdf
236
251
  - test/dataset/crypto.pdf
237
252
  - test/dataset/empty.pdf
253
+ - test/dataset/image_only.pdf
254
+ - test/dataset/invoice.pdf
238
255
  - test/test_actions.rb
239
256
  - test/test_annotations.rb
240
257
  - test/test_forms.rb
@@ -257,7 +274,7 @@ metadata:
257
274
  source_code_uri: https://github.com/mindee/origamindee
258
275
  changelog_uri: https://github.com/mindee/origamindee/blob/main/CHANGELOG.md
259
276
  rubygems_mfa_required: 'true'
260
- post_install_message:
277
+ post_install_message:
261
278
  rdoc_options: []
262
279
  require_paths:
263
280
  - lib
@@ -265,15 +282,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
265
282
  requirements:
266
283
  - - ">="
267
284
  - !ruby/object:Gem::Version
268
- version: '2.6'
285
+ version: '2.7'
269
286
  required_rubygems_version: !ruby/object:Gem::Requirement
270
287
  requirements:
271
288
  - - ">="
272
289
  - !ruby/object:Gem::Version
273
290
  version: '0'
274
291
  requirements: []
275
- rubygems_version: 3.1.6
276
- signing_key:
292
+ rubygems_version: 3.2.33
293
+ signing_key:
277
294
  specification_version: 4
278
295
  summary: Ruby framework to manipulate PDF documents
279
296
  test_files: