hexapdf 0.20.4 → 0.21.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 +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +5 -3
- data/Rakefile +10 -1
- data/examples/018-composer.rb +10 -10
- data/lib/hexapdf/cli/batch.rb +4 -6
- data/lib/hexapdf/cli/info.rb +5 -1
- data/lib/hexapdf/cli/inspect.rb +59 -0
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/composer.rb +147 -53
- data/lib/hexapdf/configuration.rb +7 -3
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/color_space.rb +1 -1
- data/lib/hexapdf/content/operator.rb +7 -7
- data/lib/hexapdf/content/parser.rb +3 -3
- data/lib/hexapdf/content/processor.rb +9 -9
- data/lib/hexapdf/document/signatures.rb +5 -4
- data/lib/hexapdf/document.rb +7 -0
- data/lib/hexapdf/font/true_type/font.rb +7 -7
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +3 -3
- data/lib/hexapdf/font/true_type_wrapper.rb +9 -14
- data/lib/hexapdf/font/type1/font.rb +10 -12
- data/lib/hexapdf/font/type1_wrapper.rb +1 -2
- data/lib/hexapdf/layout/box.rb +12 -9
- data/lib/hexapdf/layout/image_box.rb +1 -1
- data/lib/hexapdf/layout/style.rb +28 -8
- data/lib/hexapdf/layout/text_fragment.rb +10 -9
- data/lib/hexapdf/parser.rb +5 -0
- data/lib/hexapdf/tokenizer.rb +3 -3
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +6 -4
- data/lib/hexapdf/type/acro_form/choice_field.rb +2 -2
- data/lib/hexapdf/type/acro_form/field.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/resources.rb +4 -4
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -1
- data/lib/hexapdf/type/signature.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +3 -3
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/xref_section.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +5 -5
- data/test/hexapdf/content/test_graphics_state.rb +1 -0
- data/test/hexapdf/content/test_operator.rb +2 -2
- data/test/hexapdf/content/test_processor.rb +1 -1
- data/test/hexapdf/encryption/test_standard_security_handler.rb +23 -29
- data/test/hexapdf/filter/test_predictor.rb +16 -20
- data/test/hexapdf/font/test_type1_wrapper.rb +1 -1
- data/test/hexapdf/font/true_type/table/common.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +1 -1
- data/test/hexapdf/image_loader/test_pdf.rb +6 -8
- data/test/hexapdf/image_loader/test_png.rb +2 -2
- data/test/hexapdf/layout/test_box.rb +11 -1
- data/test/hexapdf/layout/test_style.rb +23 -0
- data/test/hexapdf/layout/test_text_fragment.rb +21 -21
- data/test/hexapdf/test_composer.rb +115 -52
- data/test/hexapdf/test_dictionary.rb +2 -2
- data/test/hexapdf/test_document.rb +11 -9
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_parser.rb +13 -7
- data/test/hexapdf/test_serializer.rb +20 -22
- data/test/hexapdf/test_stream.rb +7 -9
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
- data/test/hexapdf/type/acro_form/test_choice_field.rb +1 -1
- data/test/hexapdf/type/signature/common.rb +1 -1
- data/test/hexapdf/type/test_font_type0.rb +1 -1
- data/test/hexapdf/type/test_font_type1.rb +7 -7
- data/test/hexapdf/type/test_image.rb +13 -17
- metadata +2 -2
@@ -476,16 +476,14 @@ describe HexaPDF::Document do
|
|
476
476
|
|
477
477
|
describe "write" do
|
478
478
|
it "writes the document to a file" do
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
assert_equal(200, doc.object(2).value)
|
485
|
-
end
|
486
|
-
ensure
|
487
|
-
file.unlink
|
479
|
+
file = Tempfile.new('hexapdf-write')
|
480
|
+
file.close
|
481
|
+
@io_doc.write(file.path)
|
482
|
+
HexaPDF::Document.open(file.path) do |doc|
|
483
|
+
assert_equal(200, doc.object(2).value)
|
488
484
|
end
|
485
|
+
ensure
|
486
|
+
file.unlink
|
489
487
|
end
|
490
488
|
|
491
489
|
it "writes the document to an IO object" do
|
@@ -666,4 +664,8 @@ describe HexaPDF::Document do
|
|
666
664
|
assert_raises(LocalJumpError) { @doc.cache(:a, :b) }
|
667
665
|
end
|
668
666
|
end
|
667
|
+
|
668
|
+
it "can be inspected and the output is not too large" do
|
669
|
+
assert_match(/HexaPDF::Document:\d+/, @doc.inspect)
|
670
|
+
end
|
669
671
|
end
|
data/test/hexapdf/test_object.rb
CHANGED
@@ -68,7 +68,7 @@ describe HexaPDF::Object do
|
|
68
68
|
|
69
69
|
it "works for arrays" do
|
70
70
|
obj = HexaPDF::PDFArray.new([:b, HexaPDF::Object.new(:a, oid: 3, document: @doc)],
|
71
|
-
|
71
|
+
oid: 1, document: @doc)
|
72
72
|
assert_equal([:b, :a], HexaPDF::Object.make_direct(obj))
|
73
73
|
end
|
74
74
|
end
|
data/test/hexapdf/test_parser.rb
CHANGED
@@ -109,7 +109,7 @@ describe HexaPDF::Parser do
|
|
109
109
|
|
110
110
|
it "treats indirect objects with invalid values as null objects" do
|
111
111
|
create_parser("1 0 obj <</test ( /other (end)>> endobj")
|
112
|
-
object, * =
|
112
|
+
object, * = @parser.parse_indirect_object
|
113
113
|
assert_nil(object)
|
114
114
|
end
|
115
115
|
|
@@ -475,33 +475,33 @@ describe HexaPDF::Parser do
|
|
475
475
|
describe "invalid numbering of main xref section" do
|
476
476
|
it "handles the xref if the numbering is off by N" do
|
477
477
|
create_parser(" 1 0 obj 1 endobj\n" \
|
478
|
-
|
478
|
+
"xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
|
479
479
|
section, _trailer = @parser.parse_xref_section_and_trailer(17)
|
480
480
|
assert_equal(HexaPDF::XRefSection.in_use_entry(1, 0, 1), section[1])
|
481
481
|
end
|
482
482
|
|
483
483
|
it "fails if the first entry is not the one for oid=0" do
|
484
484
|
create_parser(" 1 0 obj 1 endobj\n" \
|
485
|
-
|
485
|
+
"xref\n1 2\n0000000000 00005 f \n0000000001 00000 n \ntrailer\n<<>>\n")
|
486
486
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
|
487
487
|
assert_match(/Main.*invalid numbering/i, exp.message)
|
488
488
|
|
489
489
|
create_parser(" 1 0 obj 1 endobj\n" \
|
490
|
-
|
490
|
+
"xref\n1 2\n0000000001 00000 n \n0000000001 00000 n \ntrailer\n<<>>\n")
|
491
491
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
|
492
492
|
assert_match(/Main.*invalid numbering/i, exp.message)
|
493
493
|
end
|
494
494
|
|
495
495
|
it "fails if the tested entry position is invalid" do
|
496
496
|
create_parser(" 1 0 obj 1 endobj\n" \
|
497
|
-
|
497
|
+
"xref\n1 2\n0000000000 65535 f \n0000000005 00000 n \ntrailer\n<<>>\n")
|
498
498
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
|
499
499
|
assert_match(/Main.*invalid numbering/i, exp.message)
|
500
500
|
end
|
501
501
|
|
502
502
|
it "fails if the tested entry position's oid doesn't match the corrected entry oid" do
|
503
503
|
create_parser(" 2 0 obj 1 endobj\n" \
|
504
|
-
|
504
|
+
"xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
|
505
505
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
|
506
506
|
assert_match(/Main.*invalid numbering/i, exp.message)
|
507
507
|
end
|
@@ -580,6 +580,12 @@ describe HexaPDF::Parser do
|
|
580
580
|
@xref = HexaPDF::XRefSection.in_use_entry(1, 0, 100)
|
581
581
|
end
|
582
582
|
|
583
|
+
it "can tell us if the cross-reference table was reconstructed" do
|
584
|
+
create_parser("1 0 obj\n5\nendobj\ntrailer\n<</Size 1>>")
|
585
|
+
@parser.load_object(@xref)
|
586
|
+
assert(@parser.reconstructed?)
|
587
|
+
end
|
588
|
+
|
583
589
|
it "serially parses the contents" do
|
584
590
|
create_parser("1 0 obj\n5\nendobj\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
|
585
591
|
assert_equal(6, @parser.load_object(@xref).value)
|
@@ -643,7 +649,7 @@ describe HexaPDF::Parser do
|
|
643
649
|
end
|
644
650
|
|
645
651
|
it "uses the first trailer in case of a linearized file" do
|
646
|
-
create_parser("1 0 obj\n<</Linearized true>>\nendobj\ntrailer <</Size 1/Prev
|
652
|
+
create_parser("1 0 obj\n<</Linearized true>>\nendobj\ntrailer <</Size 1/Prev 34>>\ntrailer <</Size 2>>")
|
647
653
|
assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
|
648
654
|
end
|
649
655
|
|
@@ -70,16 +70,16 @@ describe HexaPDF::Serializer do
|
|
70
70
|
|
71
71
|
it "serializes symbols" do
|
72
72
|
assert_serialized("/Name", :Name)
|
73
|
-
assert_serialized("/A;Name_With-Various***Chars?", 'A;Name_With-Various***Chars?'
|
74
|
-
assert_serialized("/1.2", '1.2'
|
75
|
-
assert_serialized("/$$",
|
76
|
-
assert_serialized("/@pattern",
|
77
|
-
assert_serialized('/.notdef', '.notdef'
|
78
|
-
assert_serialized('/lime#20Green', 'lime Green'
|
79
|
-
assert_serialized('/paired#28#29parentheses', 'paired()parentheses'
|
80
|
-
assert_serialized('/The_Key_of_F#23_Minor', 'The_Key_of_F#_Minor'
|
81
|
-
assert_serialized('/ ',
|
82
|
-
assert_serialized('/H#c3#b6#c3#9fgang',
|
73
|
+
assert_serialized("/A;Name_With-Various***Chars?", :'A;Name_With-Various***Chars?')
|
74
|
+
assert_serialized("/1.2", :'1.2')
|
75
|
+
assert_serialized("/$$", :$$)
|
76
|
+
assert_serialized("/@pattern", :@pattern)
|
77
|
+
assert_serialized('/.notdef', :'.notdef')
|
78
|
+
assert_serialized('/lime#20Green', :'lime Green')
|
79
|
+
assert_serialized('/paired#28#29parentheses', :'paired()parentheses')
|
80
|
+
assert_serialized('/The_Key_of_F#23_Minor', :'The_Key_of_F#_Minor')
|
81
|
+
assert_serialized('/ ', :"")
|
82
|
+
assert_serialized('/H#c3#b6#c3#9fgang', :Hößgang)
|
83
83
|
assert_serialized('/H#e8lp', "H\xE8lp".force_encoding('BINARY').intern)
|
84
84
|
end
|
85
85
|
|
@@ -101,18 +101,16 @@ describe HexaPDF::Serializer do
|
|
101
101
|
end
|
102
102
|
|
103
103
|
it "serializes time like objects" do
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
ENV['TZ'] = tz
|
115
|
-
end
|
104
|
+
tz = ENV['TZ']
|
105
|
+
ENV['TZ'] = 'Europe/Vienna'
|
106
|
+
assert_serialized("(D:20150416094100)", Time.new(2015, 04, 16, 9, 41, 0, 0))
|
107
|
+
assert_serialized("(D:20150416094100+01'00')", Time.new(2015, 04, 16, 9, 41, 0, 3600))
|
108
|
+
assert_serialized("(D:20150416094100-01'20')", Time.new(2015, 04, 16, 9, 41, 0, -4800))
|
109
|
+
assert_serialized("(D:20150416000000+02'00')", Date.parse("2015-04-16 9:41:00 +02:00"))
|
110
|
+
assert_serialized("(D:20150416094100+02'00')",
|
111
|
+
Time.parse("2015-04-16 9:41:00 +02:00").to_datetime)
|
112
|
+
ensure
|
113
|
+
ENV['TZ'] = tz
|
116
114
|
end
|
117
115
|
|
118
116
|
it "serializes HexaPDF objects" do
|
data/test/hexapdf/test_stream.rb
CHANGED
@@ -47,15 +47,13 @@ describe HexaPDF::StreamData do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
it "returns a fiber for a string representing a file name" do
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
file.unlink
|
58
|
-
end
|
50
|
+
file = Tempfile.new('hexapdf-stream')
|
51
|
+
file.write('source')
|
52
|
+
file.close
|
53
|
+
s = HexaPDF::StreamData.new(file.path)
|
54
|
+
assert_equal('source', s.fiber.resume)
|
55
|
+
ensure
|
56
|
+
file.unlink
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
data/test/hexapdf/test_writer.rb
CHANGED
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
|
|
40
40
|
219
|
41
41
|
%%EOF
|
42
42
|
3 0 obj
|
43
|
-
<</Producer(HexaPDF version 0.
|
43
|
+
<</Producer(HexaPDF version 0.21.0)>>
|
44
44
|
endobj
|
45
45
|
xref
|
46
46
|
3 1
|
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
|
|
72
72
|
141
|
73
73
|
%%EOF
|
74
74
|
6 0 obj
|
75
|
-
<</Producer(HexaPDF version 0.
|
75
|
+
<</Producer(HexaPDF version 0.21.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -120,7 +120,7 @@ describe HexaPDF::Type::AcroForm::ChoiceField do
|
|
120
120
|
|
121
121
|
describe "option items" do
|
122
122
|
before do
|
123
|
-
@items = [["a", "Zx"], "\xFE\xFF".b << "Töne".encode('UTF-16BE').b, "H\xe4llo".b
|
123
|
+
@items = [["a", "Zx"], "\xFE\xFF".b << "Töne".encode('UTF-16BE').b, "H\xe4llo".b]
|
124
124
|
end
|
125
125
|
|
126
126
|
it "sets the option items" do
|
@@ -112,7 +112,7 @@ describe HexaPDF::Type::FontType0 do
|
|
112
112
|
end
|
113
113
|
|
114
114
|
it "calls the configured proc if no mapping is available" do
|
115
|
-
@font[:Encoding] = :
|
115
|
+
@font[:Encoding] = :'Identity-H'
|
116
116
|
@cid_font[:CIDSystemInfo][:Registry] = :Unknown
|
117
117
|
assert_raises(HexaPDF::Error) { @font.to_utf8(32) }
|
118
118
|
end
|
@@ -10,24 +10,24 @@ describe HexaPDF::Type::FontType1::StandardFonts do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it "checks whether a given name corresponds to a standard font via #standard_font?" do
|
13
|
-
assert(@obj.standard_font?(:
|
13
|
+
assert(@obj.standard_font?(:'Times-Roman'))
|
14
14
|
assert(@obj.standard_font?(:TimesNewRoman))
|
15
15
|
refute(@obj.standard_font?(:LibreSans))
|
16
16
|
end
|
17
17
|
|
18
18
|
it "returns the standard PDF name for an alias via #standard_name" do
|
19
|
-
assert_equal(:
|
19
|
+
assert_equal(:'Times-Roman', @obj.standard_name(:TimesNewRoman))
|
20
20
|
end
|
21
21
|
|
22
22
|
describe "font" do
|
23
23
|
it "returns the Type1 font object for a given standard name" do
|
24
|
-
font = @obj.font(:
|
24
|
+
font = @obj.font(:'Times-Roman')
|
25
25
|
assert_equal("Times Roman", font.full_name)
|
26
26
|
end
|
27
27
|
|
28
28
|
it "caches the font for reuse" do
|
29
|
-
font = @obj.font(:
|
30
|
-
assert_same(font, @obj.font(:
|
29
|
+
font = @obj.font(:'Times-Roman')
|
30
|
+
assert_same(font, @obj.font(:'Times-Roman'))
|
31
31
|
end
|
32
32
|
|
33
33
|
it "returns nil if the given name doesn't belong to a standard font" do
|
@@ -40,7 +40,7 @@ describe HexaPDF::Type::FontType1 do
|
|
40
40
|
before do
|
41
41
|
@doc = HexaPDF::Document.new
|
42
42
|
@font = @doc.add({Type: :Font, Subtype: :Type1, Encoding: :WinAnsiEncoding,
|
43
|
-
BaseFont: :
|
43
|
+
BaseFont: :'Times-Roman'})
|
44
44
|
|
45
45
|
font_file = @doc.add({}, stream: <<-EOF)
|
46
46
|
/Encoding 256 array
|
@@ -95,7 +95,7 @@ describe HexaPDF::Type::FontType1 do
|
|
95
95
|
|
96
96
|
describe "bounding_box" do
|
97
97
|
it "returns the bounding box for a standard font" do
|
98
|
-
font = HexaPDF::Type::FontType1::StandardFonts.font(:
|
98
|
+
font = HexaPDF::Type::FontType1::StandardFonts.font(:'Times-Roman')
|
99
99
|
assert_equal(font.bounding_box, @font.bounding_box)
|
100
100
|
end
|
101
101
|
|
@@ -165,26 +165,22 @@ describe HexaPDF::Type::Image do
|
|
165
165
|
end
|
166
166
|
|
167
167
|
it "writes JPEG images to a file with .jpg extension" do
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
file.unlink
|
175
|
-
end
|
168
|
+
file = Tempfile.new(['hexapdf-image-write-test', '.jpg'])
|
169
|
+
image = @doc.images.add(@jpg)
|
170
|
+
image.write(file.path)
|
171
|
+
assert_equal(File.binread(@jpg), File.binread(file.path))
|
172
|
+
ensure
|
173
|
+
file.unlink
|
176
174
|
end
|
177
175
|
|
178
176
|
it "writes JPEG2000 images to a file with .jpx extension" do
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
file.unlink
|
187
|
-
end
|
177
|
+
file = Tempfile.new(['hexapdf-image-write-test', '.jpx'])
|
178
|
+
image = @doc.images.add(@jpg)
|
179
|
+
image.set_filter(:JPXDecode) # fake it
|
180
|
+
image.write(file.path)
|
181
|
+
assert_equal(File.binread(@jpg), File.binread(file.path))
|
182
|
+
ensure
|
183
|
+
file.unlink
|
188
184
|
end
|
189
185
|
|
190
186
|
Dir.glob(File.join(TEST_DATA_DIR, 'images', '*.png')).each do |png_file|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|