hexapdf 0.29.0 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|