hexapdf 0.29.0 → 0.31.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 +45 -0
- data/lib/hexapdf/cli/command.rb +16 -1
- data/lib/hexapdf/cli/info.rb +9 -1
- data/lib/hexapdf/cli/inspect.rb +2 -2
- data/lib/hexapdf/composer.rb +76 -28
- data/lib/hexapdf/configuration.rb +17 -4
- data/lib/hexapdf/dictionary_fields.rb +7 -2
- data/lib/hexapdf/document/pages.rb +31 -18
- data/lib/hexapdf/document.rb +8 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +2 -2
- data/lib/hexapdf/filter/flate_decode.rb +20 -8
- data/lib/hexapdf/layout/page_style.rb +144 -0
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/task/optimize.rb +8 -6
- data/lib/hexapdf/type/font_simple.rb +14 -2
- data/lib/hexapdf/type/object_stream.rb +7 -2
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +29 -8
- data/lib/hexapdf/type/xref_stream.rb +11 -4
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +1 -1
- data/test/hexapdf/document/test_pages.rb +25 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +2 -2
- data/test/hexapdf/filter/test_flate_decode.rb +19 -5
- data/test/hexapdf/layout/test_page_style.rb +70 -0
- data/test/hexapdf/task/test_optimize.rb +11 -9
- data/test/hexapdf/test_composer.rb +35 -10
- data/test/hexapdf/test_dictionary_fields.rb +9 -4
- data/test/hexapdf/test_writer.rb +8 -8
- data/test/hexapdf/type/test_font_simple.rb +18 -6
- data/test/hexapdf/type/test_object_stream.rb +16 -7
- data/test/hexapdf/type/test_outline.rb +3 -1
- data/test/hexapdf/type/test_outline_item.rb +3 -1
- data/test/hexapdf/type/test_page.rb +42 -11
- data/test/hexapdf/type/test_xref_stream.rb +6 -1
- metadata +4 -2
@@ -38,6 +38,8 @@ require 'set'
|
|
38
38
|
require 'hexapdf/serializer'
|
39
39
|
require 'hexapdf/content/parser'
|
40
40
|
require 'hexapdf/content/operator'
|
41
|
+
require 'hexapdf/type/xref_stream'
|
42
|
+
require 'hexapdf/type/object_stream'
|
41
43
|
|
42
44
|
module HexaPDF
|
43
45
|
module Task
|
@@ -124,7 +126,7 @@ module HexaPDF
|
|
124
126
|
if object_streams == :generate
|
125
127
|
process_object_streams(doc, :generate, xref_streams)
|
126
128
|
elsif xref_streams == :generate
|
127
|
-
doc.add({
|
129
|
+
doc.add({}, type: Type::XRefStream)
|
128
130
|
end
|
129
131
|
end
|
130
132
|
|
@@ -150,14 +152,14 @@ module HexaPDF
|
|
150
152
|
end
|
151
153
|
objects_to_delete.each {|obj| rev.delete(obj) }
|
152
154
|
if xref_streams == :generate && !xref_stream
|
153
|
-
rev.add(doc.wrap({
|
155
|
+
rev.add(doc.wrap({}, type: Type::XRefStream, oid: doc.revisions.next_oid))
|
154
156
|
end
|
155
157
|
end
|
156
158
|
when :generate
|
157
159
|
doc.revisions.each do |rev|
|
158
160
|
xref_stream = false
|
159
161
|
count = 0
|
160
|
-
objstms = [doc.wrap({
|
162
|
+
objstms = [doc.wrap({}, type: Type::ObjectStream)]
|
161
163
|
old_objstms = []
|
162
164
|
rev.each do |obj|
|
163
165
|
case obj.type
|
@@ -173,7 +175,7 @@ module HexaPDF
|
|
173
175
|
objstms[-1].add_object(obj)
|
174
176
|
count += 1
|
175
177
|
if count == 200
|
176
|
-
objstms << doc.wrap({
|
178
|
+
objstms << doc.wrap({}, type: Type::ObjectStream)
|
177
179
|
count = 0
|
178
180
|
end
|
179
181
|
end
|
@@ -182,7 +184,7 @@ module HexaPDF
|
|
182
184
|
objstm.data.oid = doc.revisions.next_oid
|
183
185
|
rev.add(objstm)
|
184
186
|
end
|
185
|
-
rev.add(doc.wrap({
|
187
|
+
rev.add(doc.wrap({}, type: Type::XRefStream, oid: doc.revisions.next_oid)) unless xref_stream
|
186
188
|
end
|
187
189
|
end
|
188
190
|
end
|
@@ -207,7 +209,7 @@ module HexaPDF
|
|
207
209
|
xref_stream = true if obj.type == :XRef
|
208
210
|
delete_fields_with_defaults(obj)
|
209
211
|
end
|
210
|
-
rev.add(doc.wrap({
|
212
|
+
rev.add(doc.wrap({}, type: Type::XRefStream, oid: doc.revisions.next_oid)) unless xref_stream
|
211
213
|
end
|
212
214
|
end
|
213
215
|
end
|
@@ -171,9 +171,21 @@ module HexaPDF
|
|
171
171
|
yield("Required field #{field} is not set", false) if self[field].nil?
|
172
172
|
end
|
173
173
|
|
174
|
+
widths = self[:Widths]
|
174
175
|
if key?(:Widths) && key?(:LastChar) && key?(:FirstChar) &&
|
175
|
-
|
176
|
-
yield("Invalid number of entries in field Widths",
|
176
|
+
widths.length != (self[:LastChar] - self[:FirstChar] + 1)
|
177
|
+
yield("Invalid number of entries in field Widths", true)
|
178
|
+
difference = self[:LastChar] - self[:FirstChar] + 1 - widths.length
|
179
|
+
if difference > 0
|
180
|
+
missing_value = if widths.count(widths[0]) == widths.length
|
181
|
+
widths[0]
|
182
|
+
else
|
183
|
+
self[:FontDescriptor]&.[](:MissingWidth) || 0
|
184
|
+
end
|
185
|
+
difference.times { widths << missing_value }
|
186
|
+
else
|
187
|
+
widths.slice!(difference, -difference)
|
188
|
+
end
|
177
189
|
end
|
178
190
|
end
|
179
191
|
|
@@ -101,8 +101,8 @@ module HexaPDF
|
|
101
101
|
define_type :ObjStm
|
102
102
|
|
103
103
|
define_field :Type, type: Symbol, required: true, default: type, version: '1.5'
|
104
|
-
define_field :N, type: Integer
|
105
|
-
define_field :First, type: Integer
|
104
|
+
define_field :N, type: Integer, required: true
|
105
|
+
define_field :First, type: Integer, required: true
|
106
106
|
define_field :Extends, type: Stream
|
107
107
|
|
108
108
|
# Parses the stream and returns an ObjectStream::Data object that can be used for retrieving
|
@@ -230,6 +230,11 @@ module HexaPDF
|
|
230
230
|
|
231
231
|
# Validates that the generation number of the object stream is zero.
|
232
232
|
def perform_validation
|
233
|
+
# Assign dummy values so that the validation for required values works since those values
|
234
|
+
# are only set on #write_objects
|
235
|
+
self[:N] ||= 0
|
236
|
+
self[:First] ||= 0
|
237
|
+
|
233
238
|
super
|
234
239
|
yield("Object stream has invalid generation number > 0", false) if gen != 0
|
235
240
|
end
|
data/lib/hexapdf/type/outline.rb
CHANGED
@@ -126,7 +126,7 @@ module HexaPDF
|
|
126
126
|
if (first && !last) || (!first && last)
|
127
127
|
yield('Outline dictionary is missing an endpoint reference', true)
|
128
128
|
node, dir = first ? [first, :Next] : [last, :Prev]
|
129
|
-
node = node[dir] while node
|
129
|
+
node = node[dir] while node[dir]
|
130
130
|
self[dir == :Next ? :Last : :First] = node
|
131
131
|
elsif !first && !last && self[:Count] && self[:Count] != 0
|
132
132
|
yield('Outline dictionary key /Count set but no items exist', true)
|
@@ -397,7 +397,7 @@ module HexaPDF
|
|
397
397
|
if (first && !last) || (!first && last)
|
398
398
|
yield('Outline item dictionary is missing an endpoint reference', true)
|
399
399
|
node, dir = first ? [first, :Next] : [last, :Prev]
|
400
|
-
node = node[dir] while node
|
400
|
+
node = node[dir] while node[dir]
|
401
401
|
self[dir == :Next ? :Last : :First] = node
|
402
402
|
elsif !first && !last && self[:Count] && self[:Count] != 0
|
403
403
|
yield('Outline item dictionary key /Count set but no descendants exist', true)
|
data/lib/hexapdf/type/page.rb
CHANGED
@@ -104,8 +104,16 @@ module HexaPDF
|
|
104
104
|
Executive: [0, 0, 522, 756].freeze,
|
105
105
|
}.freeze
|
106
106
|
|
107
|
-
# Returns the media box for the given paper size
|
107
|
+
# Returns the media box for the given paper size or array.
|
108
|
+
#
|
109
|
+
# If an array is specified, it needs to contain exactly four numbers. The +orientation+
|
110
|
+
# argument is not used in this case.
|
111
|
+
#
|
112
|
+
# See PAPER_SIZE for the defined paper sizes.
|
108
113
|
def self.media_box(paper_size, orientation: :portrait)
|
114
|
+
return paper_size if paper_size.kind_of?(Array) && paper_size.size == 4 &&
|
115
|
+
paper_size.all?(Numeric)
|
116
|
+
|
109
117
|
unless PAPER_SIZE.key?(paper_size)
|
110
118
|
raise HexaPDF::Error, "Invalid paper size specified: #{paper_size}"
|
111
119
|
end
|
@@ -118,9 +126,6 @@ module HexaPDF
|
|
118
126
|
# The inheritable fields.
|
119
127
|
INHERITABLE_FIELDS = [:Resources, :MediaBox, :CropBox, :Rotate].freeze
|
120
128
|
|
121
|
-
# The required inheritable fields.
|
122
|
-
REQUIRED_INHERITABLE_FIELDS = [:Resources, :MediaBox].freeze
|
123
|
-
|
124
129
|
define_type :Page
|
125
130
|
|
126
131
|
define_field :Type, type: Symbol, required: true, default: type
|
@@ -609,10 +614,26 @@ module HexaPDF
|
|
609
614
|
return unless parent_node
|
610
615
|
|
611
616
|
super
|
612
|
-
|
613
|
-
|
614
|
-
yield("
|
615
|
-
resources.validate(&block)
|
617
|
+
|
618
|
+
unless self[:Resources]
|
619
|
+
yield("Required inheritable page field Resources not set", true)
|
620
|
+
resources.validate(&block)
|
621
|
+
end
|
622
|
+
|
623
|
+
unless self[:MediaBox]
|
624
|
+
yield("Required inheritable page field MediaBox not set", true)
|
625
|
+
index = self.index
|
626
|
+
box_before = index == 0 ? nil : document.pages[index - 1][:MediaBox]
|
627
|
+
box_after = index == document.pages.count - 1 ? nil : document.pages[index + 1]&.[](:MediaBox)
|
628
|
+
self[:MediaBox] =
|
629
|
+
if box_before && (box_before&.value == box_after&.value || box_after.nil?)
|
630
|
+
box_before.dup
|
631
|
+
elsif box_after && box_before.nil?
|
632
|
+
box_after
|
633
|
+
else
|
634
|
+
self.class.media_box(document.config['page.default_media_box'],
|
635
|
+
orientation: document.config['page.default_media_orientation'])
|
636
|
+
end
|
616
637
|
end
|
617
638
|
end
|
618
639
|
|
@@ -72,12 +72,10 @@ module HexaPDF
|
|
72
72
|
|
73
73
|
define_field :Type, type: Symbol, default: type, required: true, indirect: false,
|
74
74
|
version: '1.5'
|
75
|
-
|
76
|
-
define_field :Size, type: Integer, indirect: false
|
75
|
+
define_field :Size, type: Integer, indirect: false, required: true
|
77
76
|
define_field :Index, type: PDFArray, indirect: false
|
78
77
|
define_field :Prev, type: Integer, indirect: false
|
79
|
-
|
80
|
-
define_field :W, type: PDFArray, indirect: false
|
78
|
+
define_field :W, type: PDFArray, indirect: false, required: true
|
81
79
|
|
82
80
|
# Returns an XRefSection that represents the content of this cross-reference stream.
|
83
81
|
#
|
@@ -219,6 +217,15 @@ module HexaPDF
|
|
219
217
|
[[1, middle, 2], pack_string]
|
220
218
|
end
|
221
219
|
|
220
|
+
def perform_validation #:nodoc
|
221
|
+
# Size is not required because it will be auto-filled before the object is written
|
222
|
+
# W is not required because it will be auto-filled on #update_with_xref_section_and_trailer
|
223
|
+
# Set both here to dummy values to make validation work for the required values
|
224
|
+
self[:Size] ||= 1
|
225
|
+
self[:W] ||= [1, 1, 1]
|
226
|
+
super
|
227
|
+
end
|
228
|
+
|
222
229
|
end
|
223
230
|
|
224
231
|
end
|
data/lib/hexapdf/version.rb
CHANGED
data/lib/hexapdf/writer.rb
CHANGED
@@ -207,7 +207,7 @@ module HexaPDF
|
|
207
207
|
end
|
208
208
|
|
209
209
|
if (!object_streams.empty? || @use_xref_streams) && xref_stream.nil?
|
210
|
-
xref_stream = @document.wrap({
|
210
|
+
xref_stream = @document.wrap({}, type: Type::XRefStream, oid: @document.revisions.next_oid)
|
211
211
|
rev.add(xref_stream)
|
212
212
|
end
|
213
213
|
|
@@ -14,6 +14,31 @@ describe HexaPDF::Document::Pages do
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
describe "create" do
|
18
|
+
it "uses the defaults from the configuration for missing arguments" do
|
19
|
+
page = @doc.pages.create
|
20
|
+
assert_equal([0, 0, 595, 842], page.box(:media).value)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "allows specifying a reference to a predefined page size" do
|
24
|
+
page = @doc.pages.create(media_box: :A3)
|
25
|
+
assert_equal([0, 0, 842, 1191], page.box(:media).value)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "allows specifying the orientation for a predefined page size" do
|
29
|
+
page = @doc.pages.create(media_box: :A4, orientation: :landscape)
|
30
|
+
assert_equal([0, 0, 842, 595], page.box(:media).value)
|
31
|
+
|
32
|
+
page = @doc.pages.create(orientation: :landscape)
|
33
|
+
assert_equal([0, 0, 842, 595], page.box(:media).value)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "allows using a media box array" do
|
37
|
+
page = @doc.pages.create(media_box: [0, 0, 12, 24], orientation: :landscape)
|
38
|
+
assert_equal([0, 0, 12, 24], page.box(:media).value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
17
42
|
describe "add" do
|
18
43
|
it "adds a new empty page when no page is given" do
|
19
44
|
page = @doc.pages.add
|
@@ -213,14 +213,14 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
213
213
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
214
214
|
@handler.set_up_decryption({Filter: :NonStandard, V: 2})
|
215
215
|
end
|
216
|
-
assert_match(/Invalid \/Filter/i, exp.message)
|
216
|
+
assert_match(/Invalid \/Filter value NonStandard/i, exp.message)
|
217
217
|
end
|
218
218
|
|
219
219
|
it "fails if the /R value is incorrect" do
|
220
220
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
221
221
|
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5})
|
222
222
|
end
|
223
|
-
assert_match(/Invalid \/R/i, exp.message)
|
223
|
+
assert_match(/Invalid \/R value 5/i, exp.message)
|
224
224
|
end
|
225
225
|
|
226
226
|
it "fails if the supplied password is invalid" do
|
@@ -26,12 +26,26 @@ describe HexaPDF::Filter::FlateDecode do
|
|
26
26
|
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded_predictor), @predictor_opts)))
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
describe "invalid input is handled as good as possible" do
|
30
|
+
def strict_mode
|
31
|
+
HexaPDF::GlobalConfiguration['filter.flate.on_error'] = proc { true }
|
32
|
+
yield
|
33
|
+
ensure
|
34
|
+
HexaPDF::GlobalConfiguration['filter.flate.on_error'] = proc { false }
|
32
35
|
end
|
33
|
-
|
34
|
-
|
36
|
+
|
37
|
+
it "handles completely invalid data" do
|
38
|
+
assert_equal('', collector(@obj.decoder(feeder("some data"))))
|
39
|
+
assert_raises(HexaPDF::FilterError) do
|
40
|
+
strict_mode { collector(@obj.decoder(feeder("some data"))) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "handles missing data" do
|
45
|
+
assert_equal('abcdefg', collector(@obj.decoder(feeder(@encoded[0..-2]))))
|
46
|
+
assert_raises(HexaPDF::FilterError) do
|
47
|
+
strict_mode { collector(@obj.decoder(feeder(@encoded[0..-2]))) }
|
48
|
+
end
|
35
49
|
end
|
36
50
|
end
|
37
51
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/layout/page_style'
|
5
|
+
require 'hexapdf/document'
|
6
|
+
|
7
|
+
describe HexaPDF::Layout::PageStyle do
|
8
|
+
it "allows assigning the page size, orientation and template on initialization" do
|
9
|
+
block = lambda {}
|
10
|
+
style = HexaPDF::Layout::PageStyle.new(page_size: :A3, orientation: :landscape, &block)
|
11
|
+
assert_equal(:A3, style.page_size)
|
12
|
+
assert_equal(:landscape, style.orientation)
|
13
|
+
assert_same(block, style.template)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "uses defaults for all values" do
|
17
|
+
style = HexaPDF::Layout::PageStyle.new
|
18
|
+
assert_equal(:A4, style.page_size)
|
19
|
+
assert_equal(:portrait, style.orientation)
|
20
|
+
assert_nil(style.template)
|
21
|
+
assert_nil(style.frame)
|
22
|
+
assert_nil(style.next_style)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "create_page" do
|
26
|
+
before do
|
27
|
+
@doc = HexaPDF::Document.new
|
28
|
+
end
|
29
|
+
|
30
|
+
it "creates a new page object" do
|
31
|
+
style = HexaPDF::Layout::PageStyle.new do |canvas, istyle|
|
32
|
+
canvas.rectangle(0, 0, 10, 10).stroke
|
33
|
+
istyle.frame = :frame
|
34
|
+
istyle.next_style = :other
|
35
|
+
end
|
36
|
+
page = style.create_page(@doc)
|
37
|
+
assert_equal([0, 0, 595, 842], page.box(:media))
|
38
|
+
assert_equal("0 0 10 10 re\nS\n", page.contents)
|
39
|
+
assert_equal(:frame, style.frame)
|
40
|
+
assert_equal(:other, style.next_style)
|
41
|
+
assert_equal(0, @doc.pages.count)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "works when no template is set" do
|
45
|
+
style = HexaPDF::Layout::PageStyle.new
|
46
|
+
page = style.create_page(@doc)
|
47
|
+
assert_equal("", page.contents)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "creates a default frame if none is set beforehand or during template execution" do
|
51
|
+
style = HexaPDF::Layout::PageStyle.new
|
52
|
+
style.create_page(@doc)
|
53
|
+
assert_kind_of(HexaPDF::Layout::Frame, style.frame)
|
54
|
+
assert_equal(36, style.frame.left)
|
55
|
+
assert_equal(36, style.frame.bottom)
|
56
|
+
assert_equal(523, style.frame.width)
|
57
|
+
assert_equal(770, style.frame.height)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it "creates new frame objects given a page and a margin specification" do
|
62
|
+
doc = HexaPDF::Document.new
|
63
|
+
style = HexaPDF::Layout::PageStyle.new
|
64
|
+
frame = style.create_frame(style.create_page(doc), [15, 10])
|
65
|
+
assert_equal(10, frame.left)
|
66
|
+
assert_equal(15, frame.bottom)
|
67
|
+
assert_equal(575, frame.width)
|
68
|
+
assert_equal(812, frame.height)
|
69
|
+
end
|
70
|
+
end
|
@@ -81,8 +81,10 @@ describe HexaPDF::Task::Optimize do
|
|
81
81
|
end
|
82
82
|
|
83
83
|
it "compacts and deletes xref streams" do
|
84
|
-
@doc.revisions.all[0].add(@doc.wrap({
|
85
|
-
|
84
|
+
@doc.revisions.all[0].add(@doc.wrap({}, type: HexaPDF::Type::XRefStream,
|
85
|
+
oid: @doc.revisions.next_oid))
|
86
|
+
@doc.revisions.all[1].add(@doc.wrap({}, type: HexaPDF::Type::XRefStream,
|
87
|
+
oid: @doc.revisions.next_oid))
|
86
88
|
@doc.task(:optimize, compact: true, xref_streams: :delete)
|
87
89
|
assert_no_xrefstms
|
88
90
|
assert_default_deleted
|
@@ -92,8 +94,8 @@ describe HexaPDF::Task::Optimize do
|
|
92
94
|
describe "object_streams" do
|
93
95
|
def reload_document_with_objstm_from_io
|
94
96
|
io = StringIO.new
|
95
|
-
objstm = @doc.add({
|
96
|
-
@doc.add({
|
97
|
+
objstm = @doc.add({}, type: HexaPDF::Type::ObjectStream)
|
98
|
+
@doc.add({}, type: HexaPDF::Type::XRefStream)
|
97
99
|
objstm.add_object(@doc.add({Type: :Test}))
|
98
100
|
@doc.write(io)
|
99
101
|
io.rewind
|
@@ -102,7 +104,7 @@ describe HexaPDF::Task::Optimize do
|
|
102
104
|
|
103
105
|
it "generates object streams" do
|
104
106
|
210.times { @doc.add(5) }
|
105
|
-
objstm = @doc.add({
|
107
|
+
objstm = @doc.add({}, type: HexaPDF::Type::ObjectStream)
|
106
108
|
reload_document_with_objstm_from_io
|
107
109
|
@doc.task(:optimize, object_streams: :generate)
|
108
110
|
assert_objstms_generated
|
@@ -122,8 +124,8 @@ describe HexaPDF::Task::Optimize do
|
|
122
124
|
end
|
123
125
|
|
124
126
|
it "deletes object and generates xref streams" do
|
125
|
-
@doc.add({
|
126
|
-
xref = @doc.add({
|
127
|
+
@doc.add({}, type: HexaPDF::Type::ObjectStream)
|
128
|
+
xref = @doc.add({}, type: HexaPDF::Type::XRefStream)
|
127
129
|
@doc.task(:optimize, object_streams: :delete, xref_streams: :generate)
|
128
130
|
assert_no_objstms
|
129
131
|
assert_xrefstms_generated
|
@@ -140,13 +142,13 @@ describe HexaPDF::Task::Optimize do
|
|
140
142
|
end
|
141
143
|
|
142
144
|
it "reuses an xref stream in generatation mode" do
|
143
|
-
@doc.add({
|
145
|
+
@doc.add({}, type: HexaPDF::Type::XRefStream)
|
144
146
|
@doc.task(:optimize, xref_streams: :generate)
|
145
147
|
assert_xrefstms_generated
|
146
148
|
end
|
147
149
|
|
148
150
|
it "deletes xref streams" do
|
149
|
-
@doc.add({
|
151
|
+
@doc.add({}, type: HexaPDF::Type::XRefStream)
|
150
152
|
@doc.task(:optimize, xref_streams: :delete)
|
151
153
|
assert_no_xrefstms
|
152
154
|
assert_default_deleted
|
@@ -39,6 +39,14 @@ describe HexaPDF::Composer do
|
|
39
39
|
assert_equal(682, composer.frame.height)
|
40
40
|
end
|
41
41
|
|
42
|
+
it "allows skipping the initial page creation" do
|
43
|
+
composer = HexaPDF::Composer.new(skip_page_creation: true)
|
44
|
+
assert_nil(composer.page)
|
45
|
+
assert_nil(composer.canvas)
|
46
|
+
assert_nil(composer.frame)
|
47
|
+
assert_nil(composer.page_style(:default))
|
48
|
+
end
|
49
|
+
|
42
50
|
it "yields itself" do
|
43
51
|
yielded = nil
|
44
52
|
composer = HexaPDF::Composer.new {|c| yielded = c }
|
@@ -56,7 +64,7 @@ describe HexaPDF::Composer do
|
|
56
64
|
end
|
57
65
|
|
58
66
|
describe "new_page" do
|
59
|
-
it "creates a new page
|
67
|
+
it "creates a new page" do
|
60
68
|
c = HexaPDF::Composer.new(page_size: [0, 0, 50, 100], margin: 10)
|
61
69
|
c.new_page
|
62
70
|
assert_equal([0, 0, 50, 100], c.page.box.value)
|
@@ -64,16 +72,33 @@ describe HexaPDF::Composer do
|
|
64
72
|
assert_equal(10, c.frame.bottom)
|
65
73
|
end
|
66
74
|
|
67
|
-
it "uses the
|
68
|
-
@composer.
|
69
|
-
|
70
|
-
assert_equal(
|
71
|
-
|
75
|
+
it "uses the named page style for the new page" do
|
76
|
+
@composer.page_style(:other, page_size: [0, 0, 100, 100])
|
77
|
+
@composer.new_page(:other)
|
78
|
+
assert_equal([0, 0, 100, 100], @composer.page.box.value)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "sets the next page's style to the next_style value of the used page style" do
|
82
|
+
@composer.page_style(:one, page_size: [0, 0, 1, 1]).next_style = :two
|
83
|
+
@composer.page_style(:two, page_size: [0, 0, 2, 2]).next_style = :one
|
84
|
+
@composer.new_page(:one)
|
85
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
86
|
+
@composer.new_page
|
87
|
+
assert_equal([0, 0, 2, 2], @composer.page.box.value)
|
88
|
+
@composer.new_page
|
89
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "uses the current page style for new pages if no next_style value is set" do
|
93
|
+
@composer.page_style(:one, page_size: [0, 0, 1, 1])
|
94
|
+
@composer.new_page(:one)
|
95
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
72
96
|
@composer.new_page
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
97
|
+
assert_equal([0, 0, 1, 1], @composer.page.box.value)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "fails if the specified page style has not been defined" do
|
101
|
+
assert_raises(ArgumentError) { @composer.new_page(:unknown) }
|
77
102
|
end
|
78
103
|
end
|
79
104
|
|
@@ -188,10 +188,15 @@ describe HexaPDF::DictionaryFields do
|
|
188
188
|
["D:19981223-08'00'", [1998, 12, 23, 00, 00, 00, "-08:00"]],
|
189
189
|
["D:199812-08'00'", [1998, 12, 01, 00, 00, 00, "-08:00"]],
|
190
190
|
["D:1998-08'00'", [1998, 01, 01, 00, 00, 00, "-08:00"]],
|
191
|
-
["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], #
|
192
|
-
["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], #
|
193
|
-
["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], #
|
194
|
-
["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], #
|
191
|
+
["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
|
192
|
+
["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
|
193
|
+
["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], # TZ hour too large
|
194
|
+
["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], # TZ min too large
|
195
|
+
["D:19982423195210-08'00'", [1998, 12, 23, 19, 52, 10, "-08:00"]], # months too large
|
196
|
+
["D:19981273195210-08'00'", [1998, 12, 31, 19, 52, 10, "-08:00"]], # day too large
|
197
|
+
["D:19981223275210-08'00'", [1998, 12, 23, 23, 52, 10, "-08:00"]], # hour too large
|
198
|
+
["D:19981223197710-08'00'", [1998, 12, 23, 19, 59, 10, "-08:00"]], # minute too large
|
199
|
+
["D:19981223195280-08'00'", [1998, 12, 23, 19, 52, 59, "-08:00"]], # seconds too large
|
195
200
|
].each do |str, data|
|
196
201
|
obj = @field.convert(str, self)
|
197
202
|
assert_equal(Time.new(*data), obj, "date str used: #{str}")
|
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.31.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.31.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -164,14 +164,14 @@ describe HexaPDF::Writer do
|
|
164
164
|
|
165
165
|
document = HexaPDF::Document.new(io: io)
|
166
166
|
assert_equal(3, document.revisions.count)
|
167
|
-
assert_equal(1, document.revisions.all[0].object(
|
168
|
-
assert_equal(2, document.revisions.all[1].object(
|
169
|
-
assert_equal(3, document.revisions.all[2].object(
|
167
|
+
assert_equal(1, document.revisions.all[0].object(3)[:Kids].length)
|
168
|
+
assert_equal(2, document.revisions.all[1].object(3)[:Kids].length)
|
169
|
+
assert_equal(3, document.revisions.all[2].object(3)[:Kids].length)
|
170
170
|
end
|
171
171
|
|
172
172
|
it "creates an xref stream if no xref stream is in a revision but object streams are" do
|
173
173
|
document = HexaPDF::Document.new
|
174
|
-
document.add({
|
174
|
+
document.add({}, type: HexaPDF::Type::ObjectStream)
|
175
175
|
HexaPDF::Writer.new(document, StringIO.new).write
|
176
176
|
assert_equal(:XRef, document.object(4).type)
|
177
177
|
end
|
@@ -184,7 +184,7 @@ describe HexaPDF::Writer do
|
|
184
184
|
|
185
185
|
document = HexaPDF::Document.new(io: io)
|
186
186
|
document.pages.add
|
187
|
-
document.add({
|
187
|
+
document.add({}, type: HexaPDF::Type::ObjectStream)
|
188
188
|
io2 = StringIO.new
|
189
189
|
HexaPDF::Writer.new(document, io2).write_incremental
|
190
190
|
|
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
|
|
214
214
|
<</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
|
215
215
|
endobj
|
216
216
|
5 0 obj
|
217
|
-
<</Producer(HexaPDF version 0.
|
217
|
+
<</Producer(HexaPDF version 0.31.0)>>
|
218
218
|
endobj
|
219
219
|
4 0 obj
|
220
220
|
<</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
|
@@ -15,7 +15,7 @@ describe HexaPDF::Type::FontSimple do
|
|
15
15
|
EOF
|
16
16
|
font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Embedded, Flags: 0b100,
|
17
17
|
FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
|
18
|
-
Descent: -100, CapHeight: 800, StemV: 20})
|
18
|
+
MissingWidth: 500, Descent: -100, CapHeight: 800, StemV: 20})
|
19
19
|
@font = @doc.add({Type: :Font, Encoding: :WinAnsiEncoding,
|
20
20
|
BaseFont: :Embedded, FontDescriptor: font_descriptor, ToUnicode: cmap,
|
21
21
|
FirstChar: 32, LastChar: 34, Widths: [600, 0, 700]},
|
@@ -130,9 +130,7 @@ describe HexaPDF::Type::FontSimple do
|
|
130
130
|
end
|
131
131
|
|
132
132
|
it "returns the /MissingWidth of a /FontDescriptor if available and the width was not found" do
|
133
|
-
assert_equal(
|
134
|
-
@font[:FontDescriptor][:MissingWidth] = 99
|
135
|
-
assert_equal(99, @font.width(0))
|
133
|
+
assert_equal(500, @font.width(0))
|
136
134
|
end
|
137
135
|
|
138
136
|
it "returns 0 for a missing code point when FontDescriptor is not available" do
|
@@ -169,8 +167,22 @@ describe HexaPDF::Type::FontSimple do
|
|
169
167
|
end
|
170
168
|
|
171
169
|
it "validates the lengths of the /Widths field" do
|
172
|
-
@font[:Widths] = [65]
|
173
|
-
|
170
|
+
@font[:Widths] = [65, 65]
|
171
|
+
assert(@font.validate)
|
172
|
+
assert_equal([65, 65, 65], @font[:Widths])
|
173
|
+
|
174
|
+
@font[:Widths] = [65, 70]
|
175
|
+
assert(@font.validate)
|
176
|
+
assert_equal([65, 70, 500], @font[:Widths])
|
177
|
+
|
178
|
+
@font[:Widths] = [65, 70]
|
179
|
+
@font.delete(:FontDescriptor)
|
180
|
+
assert(@font.validate)
|
181
|
+
assert_equal([65, 70, 0], @font[:Widths])
|
182
|
+
|
183
|
+
@font[:Widths] = [65, 65, 70, 90, 100]
|
184
|
+
assert(@font.validate)
|
185
|
+
assert_equal([65, 65, 70], @font[:Widths])
|
174
186
|
end
|
175
187
|
end
|
176
188
|
end
|