hexapdf 0.35.1 → 0.37.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 +36 -0
- data/Rakefile +1 -1
- data/lib/hexapdf/configuration.rb +3 -0
- data/lib/hexapdf/content/canvas_composer.rb +1 -1
- data/lib/hexapdf/document/layout.rb +15 -6
- data/lib/hexapdf/document/metadata.rb +488 -0
- data/lib/hexapdf/document.rb +18 -3
- data/lib/hexapdf/filter.rb +2 -2
- data/lib/hexapdf/layout/box.rb +104 -29
- data/lib/hexapdf/layout/container_box.rb +159 -0
- data/lib/hexapdf/layout/frame.rb +7 -4
- data/lib/hexapdf/layout/list_box.rb +2 -1
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/type/annotation.rb +71 -0
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +2 -1
- data/lib/hexapdf/type/metadata.rb +63 -0
- data/lib/hexapdf/type.rb +1 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_layout.rb +6 -0
- data/test/hexapdf/document/test_metadata.rb +192 -0
- data/test/hexapdf/layout/test_box.rb +39 -13
- data/test/hexapdf/layout/test_container_box.rb +84 -0
- data/test/hexapdf/layout/test_frame.rb +3 -2
- data/test/hexapdf/test_filter.rb +12 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_font_simple.rb +9 -1
- metadata +8 -3
@@ -0,0 +1,192 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'stringio'
|
5
|
+
require 'hexapdf/document'
|
6
|
+
|
7
|
+
describe HexaPDF::Document::Metadata do
|
8
|
+
before do
|
9
|
+
@doc = HexaPDF::Document.new
|
10
|
+
@doc.trailer.info[:Title] = 'Title'
|
11
|
+
@metadata = @doc.metadata
|
12
|
+
end
|
13
|
+
|
14
|
+
it "parses the info dictionary on creation" do
|
15
|
+
assert_equal('Title', @metadata.title)
|
16
|
+
@doc.trailer.info[:Trapped] = :Unknown
|
17
|
+
assert_nil(HexaPDF::Document::Metadata.new(@doc).trapped)
|
18
|
+
@doc.trailer.info[:Trapped] = :True
|
19
|
+
assert_equal(true, HexaPDF::Document::Metadata.new(@doc).trapped)
|
20
|
+
@doc.trailer.info[:Trapped] = :False
|
21
|
+
assert_equal(false, HexaPDF::Document::Metadata.new(@doc).trapped)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "default_language" do
|
25
|
+
it "use the document's language as default" do
|
26
|
+
@doc.catalog[:Lang] = 'de'
|
27
|
+
assert_equal("de", HexaPDF::Document::Metadata.new(@doc).default_language)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "falls back to English if the document doesn't have a default language set" do
|
31
|
+
assert_equal('en', @metadata.default_language)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "allows changing the default language" do
|
35
|
+
@metadata.default_language('de')
|
36
|
+
assert_equal('de', @metadata.default_language)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "enables writing the info dict by default" do
|
41
|
+
assert(@metadata.write_info_dict?)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "allows setting whether the info dict is written" do
|
45
|
+
@metadata.write_info_dict(false)
|
46
|
+
refute(@metadata.write_info_dict?)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "enables writing the metadata stream by default" do
|
50
|
+
assert(@metadata.write_metadata_stream?)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "allows setting whether the metadata stream is written" do
|
54
|
+
@metadata.write_metadata_stream(false)
|
55
|
+
refute(@metadata.write_metadata_stream?)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "resolves namespace URI via a prefix" do
|
59
|
+
assert_equal('http://www.w3.org/1999/02/22-rdf-syntax-ns#', @metadata.namespace('rdf'))
|
60
|
+
end
|
61
|
+
|
62
|
+
it "allows registering prefixes for namespaces" do
|
63
|
+
err = assert_raises(HexaPDF::Error) { @metadata.namespace('hexa') }
|
64
|
+
assert_match(/prefix.*hexa.*not registered/, err.message)
|
65
|
+
@metadata.register_namespace('hexa', 'hexa:')
|
66
|
+
assert_equal('hexa:', @metadata.namespace('hexa'))
|
67
|
+
end
|
68
|
+
|
69
|
+
it "allows registering property types" do
|
70
|
+
@metadata.register_property_type('dc', 'title', 'Boolean')
|
71
|
+
assert_equal('Boolean', @metadata.instance_variable_get(:@properties)[@metadata.namespace('dc')]['title'])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "allows reading and setting properties" do
|
75
|
+
assert_equal('Title', @metadata.property('dc', 'title'))
|
76
|
+
@metadata.property('dc', 'title', 'another')
|
77
|
+
assert_equal('another', @metadata.property('dc', 'title'))
|
78
|
+
@metadata.property('dc', 'title', nil)
|
79
|
+
assert_nil(@metadata.property('dc', 'title'))
|
80
|
+
refute(@metadata.instance_variable_get(:@metadata)[@metadata.namespace('dc')].key?('title'))
|
81
|
+
end
|
82
|
+
|
83
|
+
it "allows reading and setting all info dictionary properties" do
|
84
|
+
[['title', 'dc', 'title'], ['author', 'dc', 'creator'], ['subject', 'dc', 'description'],
|
85
|
+
['keywords', 'pdf', 'Keywords'], ['creator', 'xmp', 'CreatorTool'],
|
86
|
+
['producer', 'pdf', 'Producer'], ['creation_date', 'xmp', 'CreateDate'],
|
87
|
+
['modification_date', 'xmp', 'ModifyDate'], ['trapped', 'pdf', 'Trapped']].each do |name, ns, property|
|
88
|
+
@metadata.property(ns, property, 'value')
|
89
|
+
assert_equal('value', @metadata.send(name), name)
|
90
|
+
@metadata.send(name, 'modified')
|
91
|
+
assert_equal('modified', @metadata.property(ns, property), name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "metadata writing" do
|
96
|
+
before do
|
97
|
+
@time = Time.now.floor
|
98
|
+
@metadata.title('Title')
|
99
|
+
@metadata.author('Author')
|
100
|
+
@metadata.subject('Subject')
|
101
|
+
@metadata.keywords('Keywords')
|
102
|
+
@metadata.creator('Creator')
|
103
|
+
@metadata.producer('Producer')
|
104
|
+
@metadata.creation_date(@time)
|
105
|
+
@metadata.modification_date(@time)
|
106
|
+
@metadata.trapped(true)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "writes the info dictionary properties" do
|
110
|
+
info = @doc.trailer.info
|
111
|
+
@doc.write(StringIO.new, update_fields: false)
|
112
|
+
assert_equal('Title', info[:Title])
|
113
|
+
assert_equal('Author', info[:Author])
|
114
|
+
assert_equal('Subject', info[:Subject])
|
115
|
+
assert_equal('Keywords', info[:Keywords])
|
116
|
+
assert_equal('Creator', info[:Creator])
|
117
|
+
assert_match(/HexaPDF/, info[:Producer])
|
118
|
+
assert_same(@time, info[:CreationDate])
|
119
|
+
assert_same(@time, info[:ModDate])
|
120
|
+
assert_equal(:True, info[:Trapped])
|
121
|
+
end
|
122
|
+
|
123
|
+
it "uses a correctly updated modification date if set so by Document#write" do
|
124
|
+
info = @doc.trailer.info
|
125
|
+
sleep(0.1)
|
126
|
+
@doc.write(StringIO.new)
|
127
|
+
assert_same(@time, info[:CreationDate])
|
128
|
+
refute_same(@time, info[:ModDate])
|
129
|
+
assert(@time < info[:ModDate])
|
130
|
+
end
|
131
|
+
|
132
|
+
it "correctly handles array values for title, author, and subject for info dictionary" do
|
133
|
+
@metadata.title(['Title', 'Another'])
|
134
|
+
@metadata.author(['Author', 'Author2'])
|
135
|
+
@metadata.subject(['Subject', 'Another'])
|
136
|
+
@doc.write(StringIO.new)
|
137
|
+
info = @doc.trailer.info
|
138
|
+
assert_equal('Title', info[:Title])
|
139
|
+
assert_equal('Author, Author2', info[:Author])
|
140
|
+
assert_equal('Subject', info[:Subject])
|
141
|
+
end
|
142
|
+
|
143
|
+
it "writes the XMP metadata" do
|
144
|
+
title = HexaPDF::Document::Metadata::LocalizedString.new('Der Titel')
|
145
|
+
title.language = 'de'
|
146
|
+
@metadata.title(['Title', title])
|
147
|
+
@metadata.author(['Author 1', 'Author 2'])
|
148
|
+
@metadata.register_property_type('dc', 'other', 'URI')
|
149
|
+
@metadata.property('dc', 'other', 'https://test.org/example')
|
150
|
+
@doc.write(StringIO.new, update_fields: false)
|
151
|
+
metadata = <<~XMP
|
152
|
+
<?xpacket begin="" id=""?>
|
153
|
+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
154
|
+
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
155
|
+
<dc:title><rdf:Alt>
|
156
|
+
<rdf:li xml:lang="en">Title</rdf:li>
|
157
|
+
<rdf:li xml:lang="de">Der Titel</rdf:li>
|
158
|
+
</rdf:Alt></dc:title>
|
159
|
+
<dc:creator><rdf:Seq>
|
160
|
+
<rdf:li>Author 1</rdf:li>
|
161
|
+
<rdf:li>Author 2</rdf:li>
|
162
|
+
</rdf:Seq></dc:creator>
|
163
|
+
<dc:description><rdf:Alt>
|
164
|
+
<rdf:li xml:lang="en">Subject</rdf:li>
|
165
|
+
</rdf:Alt></dc:description>
|
166
|
+
<dc:other rdf:resource="https://test.org/example" />
|
167
|
+
</rdf:Description>
|
168
|
+
<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
|
169
|
+
<pdf:Keywords>Keywords</pdf:Keywords>
|
170
|
+
<pdf:Producer>Producer</pdf:Producer>
|
171
|
+
<pdf:Trapped>True</pdf:Trapped>
|
172
|
+
</rdf:Description>
|
173
|
+
<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
|
174
|
+
<xmp:CreatorTool>Creator</xmp:CreatorTool>
|
175
|
+
<xmp:CreateDate>#{@metadata.send(:xmp_date, @time)}</xmp:CreateDate>
|
176
|
+
<xmp:ModifyDate>#{@metadata.send(:xmp_date, @time)}</xmp:ModifyDate>
|
177
|
+
</rdf:Description>
|
178
|
+
</rdf:RDF>
|
179
|
+
<?xpacket end="r"?>
|
180
|
+
XMP
|
181
|
+
assert_equal(metadata, @doc.catalog[:Metadata].stream.sub(/(?<=id=")\w+/, ''))
|
182
|
+
end
|
183
|
+
|
184
|
+
it "respects the write settings for info dictionary and metadata stream" do
|
185
|
+
@metadata.write_info_dict(false)
|
186
|
+
@metadata.write_metadata_stream(false)
|
187
|
+
@doc.write(StringIO.new)
|
188
|
+
assert_nil(@doc.trailer.info[:Author])
|
189
|
+
refute(@doc.catalog.key?(:Metadata))
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -5,6 +5,12 @@ require 'hexapdf/document'
|
|
5
5
|
require 'hexapdf/layout/box'
|
6
6
|
|
7
7
|
describe HexaPDF::Layout::Box do
|
8
|
+
before do
|
9
|
+
@frame = Object.new
|
10
|
+
def @frame.x; 0; end
|
11
|
+
def @frame.y; 100; end
|
12
|
+
end
|
13
|
+
|
8
14
|
def create_box(**args, &block)
|
9
15
|
HexaPDF::Layout::Box.new(**args, &block)
|
10
16
|
end
|
@@ -70,15 +76,13 @@ describe HexaPDF::Layout::Box do
|
|
70
76
|
end
|
71
77
|
|
72
78
|
describe "fit" do
|
73
|
-
before do
|
74
|
-
@frame = Object.new
|
75
|
-
end
|
76
|
-
|
77
79
|
it "fits a fixed sized box" do
|
78
|
-
box = create_box(width: 50, height: 50)
|
80
|
+
box = create_box(width: 50, height: 50, style: {padding: 5})
|
79
81
|
assert(box.fit(100, 100, @frame))
|
80
82
|
assert_equal(50, box.width)
|
81
83
|
assert_equal(50, box.height)
|
84
|
+
assert_equal(5, box.instance_variable_get(:@fit_x))
|
85
|
+
assert_equal(55, box.instance_variable_get(:@fit_y))
|
82
86
|
end
|
83
87
|
|
84
88
|
it "uses the maximum available width" do
|
@@ -106,49 +110,71 @@ describe HexaPDF::Layout::Box do
|
|
106
110
|
box = create_box(width: 101)
|
107
111
|
refute(box.fit(100, 100, @frame))
|
108
112
|
end
|
113
|
+
|
114
|
+
it "can use the #content_width/#content_height helper methods" do
|
115
|
+
box = create_box
|
116
|
+
box.define_singleton_method(:fit_content) do |aw, ah, frame|
|
117
|
+
update_content_width { 10 }
|
118
|
+
update_content_height { 20 }
|
119
|
+
true
|
120
|
+
end
|
121
|
+
assert(box.fit(100, 100, @frame))
|
122
|
+
assert_equal(10, box.width)
|
123
|
+
assert_equal(20, box.height)
|
124
|
+
|
125
|
+
box = create_box(width: 30, height: 50)
|
126
|
+
box.define_singleton_method(:fit_content) do |aw, ah, frame|
|
127
|
+
update_content_width { 10 }
|
128
|
+
update_content_height { 20 }
|
129
|
+
true
|
130
|
+
end
|
131
|
+
assert(box.fit(100, 100, @frame))
|
132
|
+
assert_equal(30, box.width)
|
133
|
+
assert_equal(50, box.height)
|
134
|
+
end
|
109
135
|
end
|
110
136
|
|
111
137
|
describe "split" do
|
112
138
|
before do
|
113
139
|
@box = create_box(width: 100, height: 100)
|
114
|
-
@box.fit(100, 100,
|
140
|
+
@box.fit(100, 100, @frame)
|
115
141
|
end
|
116
142
|
|
117
143
|
it "doesn't need to be split if it completely fits" do
|
118
|
-
assert_equal([@box, nil], @box.split(100, 100,
|
144
|
+
assert_equal([@box, nil], @box.split(100, 100, @frame))
|
119
145
|
end
|
120
146
|
|
121
147
|
it "can't be split if it doesn't (completely) fit and its width is greater than the available width" do
|
122
148
|
@box.fit(90, 100, nil)
|
123
|
-
assert_equal([nil, @box], @box.split(50, 150,
|
149
|
+
assert_equal([nil, @box], @box.split(50, 150, @frame))
|
124
150
|
end
|
125
151
|
|
126
152
|
it "can't be split if it doesn't (completely) fit and its height is greater than the available height" do
|
127
153
|
@box.fit(90, 100, nil)
|
128
|
-
assert_equal([nil, @box], @box.split(150, 50,
|
154
|
+
assert_equal([nil, @box], @box.split(150, 50, @frame))
|
129
155
|
end
|
130
156
|
|
131
157
|
it "can't be split if it doesn't (completely) fit and its content width is zero" do
|
132
158
|
box = create_box(width: 0, height: 100)
|
133
|
-
assert_equal([nil, box], box.split(150, 150,
|
159
|
+
assert_equal([nil, box], box.split(150, 150, @frame))
|
134
160
|
end
|
135
161
|
|
136
162
|
it "can't be split if it doesn't (completely) fit and its content height is zero" do
|
137
163
|
box = create_box(width: 100, height: 0)
|
138
|
-
assert_equal([nil, box], box.split(150, 150,
|
164
|
+
assert_equal([nil, box], box.split(150, 150, @frame))
|
139
165
|
end
|
140
166
|
|
141
167
|
it "can't be split if it doesn't (completely) fit as the default implementation " \
|
142
168
|
"knows nothing about the content" do
|
143
169
|
@box.style.position = :flow # make sure we would generally be splitable
|
144
170
|
@box.fit(90, 100, nil)
|
145
|
-
assert_equal([nil, @box], @box.split(150, 150,
|
171
|
+
assert_equal([nil, @box], @box.split(150, 150, @frame))
|
146
172
|
end
|
147
173
|
end
|
148
174
|
|
149
175
|
it "can create a cloned box for splitting" do
|
150
176
|
box = create_box
|
151
|
-
box.fit(100, 100,
|
177
|
+
box.fit(100, 100, @frame)
|
152
178
|
cloned_box = box.send(:create_split_box)
|
153
179
|
assert(cloned_box.split_box?)
|
154
180
|
refute(cloned_box.instance_variable_get(:@fit_successful))
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/layout/container_box'
|
6
|
+
|
7
|
+
describe HexaPDF::Layout::ContainerBox do
|
8
|
+
before do
|
9
|
+
@doc = HexaPDF::Document.new
|
10
|
+
@frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_box(children, **kwargs)
|
14
|
+
HexaPDF::Layout::ContainerBox.new(children: Array(children), **kwargs)
|
15
|
+
end
|
16
|
+
|
17
|
+
def check_box(box, width, height, fit_pos = nil)
|
18
|
+
assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box didn't fit")
|
19
|
+
assert_equal(width, box.width, "box width")
|
20
|
+
assert_equal(height, box.height, "box height")
|
21
|
+
if fit_pos
|
22
|
+
box_fitter = box.instance_variable_get(:@box_fitter)
|
23
|
+
assert_equal(fit_pos.size, box_fitter.fit_results.size)
|
24
|
+
fit_pos.each_with_index do |(x, y), index|
|
25
|
+
assert_equal(x, box_fitter.fit_results[index].x, "result[#{index}].x")
|
26
|
+
assert_equal(y, box_fitter.fit_results[index].y, "result[#{index}].y")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "empty?" do
|
32
|
+
it "is empty if nothing is fit yet" do
|
33
|
+
assert(create_box([]).empty?)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "is empty if no box fits" do
|
37
|
+
box = create_box(@doc.layout.box(width: 110))
|
38
|
+
box.fit(@frame.available_width, @frame.available_height, @frame)
|
39
|
+
assert(box.empty?)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "is not empty if at least one box fits" do
|
43
|
+
box = create_box(@doc.layout.box(width: 50, height: 20))
|
44
|
+
check_box(box, 100, 20)
|
45
|
+
refute(box.empty?)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "fit_content" do
|
50
|
+
it "fits the children according to their mask_mode, valign, and align style properties" do
|
51
|
+
box = create_box([@doc.layout.box(height: 20),
|
52
|
+
@doc.layout.box(height: 20, style: {valign: :bottom, mask_mode: :fill_horizontal}),
|
53
|
+
@doc.layout.box(width: 20, style: {align: :right, mask_mode: :fill_vertical})])
|
54
|
+
check_box(box, 100, 100, [[0, 80], [0, 0], [80, 20]])
|
55
|
+
end
|
56
|
+
|
57
|
+
it "respects the initially set width/height as well as border/padding" do
|
58
|
+
box = create_box(@doc.layout.box(height: 20), height: 50, width: 40,
|
59
|
+
style: {padding: 2, border: {width: 3}})
|
60
|
+
check_box(box, 40, 50, [[5, 75]])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "draw_content" do
|
65
|
+
before do
|
66
|
+
@canvas = @doc.pages.add.canvas
|
67
|
+
end
|
68
|
+
|
69
|
+
it "draws the result onto the canvas" do
|
70
|
+
child_box = @doc.layout.box(height: 20) do |canvas, b|
|
71
|
+
canvas.line(0, 0, b.content_width, b.content_height).end_path
|
72
|
+
end
|
73
|
+
box = create_box(child_box)
|
74
|
+
box.fit(100, 100, @frame)
|
75
|
+
box.draw(@canvas, 0, 50)
|
76
|
+
assert_operators(@canvas.contents, [[:save_graphics_state],
|
77
|
+
[:concatenate_matrix, [1, 0, 0, 1, 0, 50]],
|
78
|
+
[:move_to, [0, 0]],
|
79
|
+
[:line_to, [100, 20]],
|
80
|
+
[:end_path],
|
81
|
+
[:restore_graphics_state]])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -13,10 +13,11 @@ describe HexaPDF::Layout::Frame::FitResult do
|
|
13
13
|
result = HexaPDF::Layout::Frame::FitResult.new(box)
|
14
14
|
result.mask = Geom2D::Rectangle(0, 0, 20, 20)
|
15
15
|
result.x = result.y = 0
|
16
|
-
result.draw(canvas)
|
16
|
+
result.draw(canvas, dx: 10, dy: 15)
|
17
17
|
assert_equal(<<~CONTENTS, canvas.contents)
|
18
18
|
/OC /P1 BDC
|
19
19
|
q
|
20
|
+
1 0 0 1 10 15 cm
|
20
21
|
0.0 0.501961 0.0 rg
|
21
22
|
0.0 0.392157 0.0 RG
|
22
23
|
/GS1 gs
|
@@ -25,7 +26,7 @@ describe HexaPDF::Layout::Frame::FitResult do
|
|
25
26
|
Q
|
26
27
|
EMC
|
27
28
|
q
|
28
|
-
1 0 0 1
|
29
|
+
1 0 0 1 10 15 cm
|
29
30
|
Q
|
30
31
|
CONTENTS
|
31
32
|
ocg = doc.optional_content.ocgs.first
|
data/test/hexapdf/test_filter.rb
CHANGED
@@ -18,6 +18,12 @@ describe HexaPDF::Filter do
|
|
18
18
|
assert_equal(@str, collector(fib))
|
19
19
|
assert_equal('', collector(fib))
|
20
20
|
end
|
21
|
+
|
22
|
+
it "returns the correct length of the fiber" do
|
23
|
+
str = "\u{FEFF}Öl"
|
24
|
+
fib = @obj.source_from_proc { str }
|
25
|
+
assert_equal(6, fib.length)
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
describe "source_from_string" do
|
@@ -30,6 +36,12 @@ describe HexaPDF::Filter do
|
|
30
36
|
it "returns the whole string" do
|
31
37
|
assert_equal(@str, collector(@obj.source_from_string(@str)))
|
32
38
|
end
|
39
|
+
|
40
|
+
it "returns the correct size of the fiber" do
|
41
|
+
str = "\u{FEFF}Öl"
|
42
|
+
fib = @obj.source_from_string(str)
|
43
|
+
assert_equal(6, fib.length)
|
44
|
+
end
|
33
45
|
end
|
34
46
|
|
35
47
|
describe "source_from_io" do
|
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.37.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.37.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -116,9 +116,17 @@ describe HexaPDF::Type::FontSimple do
|
|
116
116
|
assert_equal(" ", @font.to_utf8(32))
|
117
117
|
end
|
118
118
|
|
119
|
+
it "swallows errors during retrieving the font's encoding" do
|
120
|
+
@font.delete(:ToUnicode)
|
121
|
+
@font.delete(:Encoding)
|
122
|
+
err = assert_raises(HexaPDF::Error) { @font.to_utf8(32) }
|
123
|
+
assert_match(/No Unicode mapping/, err.message)
|
124
|
+
end
|
125
|
+
|
119
126
|
it "calls the configured proc if no correct mapping could be found" do
|
120
127
|
@font.delete(:ToUnicode)
|
121
|
-
assert_raises(HexaPDF::Error) { @font.to_utf8(0) }
|
128
|
+
err = assert_raises(HexaPDF::Error) { @font.to_utf8(0) }
|
129
|
+
assert_match(/No Unicode mapping/, err.message)
|
122
130
|
end
|
123
131
|
end
|
124
132
|
|
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.37.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: 2024-01-
|
11
|
+
date: 2024-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -358,6 +358,7 @@ files:
|
|
358
358
|
- lib/hexapdf/document/fonts.rb
|
359
359
|
- lib/hexapdf/document/images.rb
|
360
360
|
- lib/hexapdf/document/layout.rb
|
361
|
+
- lib/hexapdf/document/metadata.rb
|
361
362
|
- lib/hexapdf/document/pages.rb
|
362
363
|
- lib/hexapdf/encryption.rb
|
363
364
|
- lib/hexapdf/encryption/aes.rb
|
@@ -434,6 +435,7 @@ files:
|
|
434
435
|
- lib/hexapdf/layout/box.rb
|
435
436
|
- lib/hexapdf/layout/box_fitter.rb
|
436
437
|
- lib/hexapdf/layout/column_box.rb
|
438
|
+
- lib/hexapdf/layout/container_box.rb
|
437
439
|
- lib/hexapdf/layout/frame.rb
|
438
440
|
- lib/hexapdf/layout/image_box.rb
|
439
441
|
- lib/hexapdf/layout/inline_box.rb
|
@@ -504,6 +506,7 @@ files:
|
|
504
506
|
- lib/hexapdf/type/image.rb
|
505
507
|
- lib/hexapdf/type/info.rb
|
506
508
|
- lib/hexapdf/type/mark_information.rb
|
509
|
+
- lib/hexapdf/type/metadata.rb
|
507
510
|
- lib/hexapdf/type/names.rb
|
508
511
|
- lib/hexapdf/type/object_stream.rb
|
509
512
|
- lib/hexapdf/type/optional_content_configuration.rb
|
@@ -630,6 +633,7 @@ files:
|
|
630
633
|
- test/hexapdf/document/test_fonts.rb
|
631
634
|
- test/hexapdf/document/test_images.rb
|
632
635
|
- test/hexapdf/document/test_layout.rb
|
636
|
+
- test/hexapdf/document/test_metadata.rb
|
633
637
|
- test/hexapdf/document/test_pages.rb
|
634
638
|
- test/hexapdf/encryption/common.rb
|
635
639
|
- test/hexapdf/encryption/test_aes.rb
|
@@ -695,6 +699,7 @@ files:
|
|
695
699
|
- test/hexapdf/layout/test_box.rb
|
696
700
|
- test/hexapdf/layout/test_box_fitter.rb
|
697
701
|
- test/hexapdf/layout/test_column_box.rb
|
702
|
+
- test/hexapdf/layout/test_container_box.rb
|
698
703
|
- test/hexapdf/layout/test_frame.rb
|
699
704
|
- test/hexapdf/layout/test_image_box.rb
|
700
705
|
- test/hexapdf/layout/test_inline_box.rb
|
@@ -794,7 +799,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
794
799
|
requirements:
|
795
800
|
- - ">="
|
796
801
|
- !ruby/object:Gem::Version
|
797
|
-
version: '2.
|
802
|
+
version: '2.7'
|
798
803
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
799
804
|
requirements:
|
800
805
|
- - ">="
|