hexapdf 0.25.0 → 0.26.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 +20 -0
- data/lib/hexapdf/configuration.rb +2 -0
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/document/pages.rb +99 -2
- data/lib/hexapdf/rectangle.rb +11 -2
- data/lib/hexapdf/type/catalog.rb +18 -1
- data/lib/hexapdf/type/file_specification.rb +2 -1
- data/lib/hexapdf/type/mark_information.rb +57 -0
- data/lib/hexapdf/type/page.rb +8 -0
- data/lib/hexapdf/type/page_label.rb +222 -0
- data/lib/hexapdf/type.rb +2 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_pages.rb +86 -0
- data/test/hexapdf/test_dictionary_fields.rb +3 -1
- data/test/hexapdf/test_rectangle.rb +18 -4
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/test_catalog.rb +17 -0
- data/test/hexapdf/type/test_file_specification.rb +1 -0
- data/test/hexapdf/type/test_page.rb +8 -0
- data/test/hexapdf/type/test_page_label.rb +118 -0
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9e41c50e1ddb3dc2b063e8a858ccf612fb069df46189d592dccec3778d58d738
|
|
4
|
+
data.tar.gz: cf62d302bd4728816c9920d09fcf27224004b6e70bc23b19d9a0f8b37dc27a3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 83afdc841024adb116efbeb75bd34c2feb71042cbccb368e32b220634f3f8122f1a29bf07d8b7532836953fe1d3edb0733cb6c31fd50177a28bddea9bcdee2a2
|
|
7
|
+
data.tar.gz: fa3e26f634fc7e3e6461ade1555a063bffbf52c4e956d802b4bee5d01290bc2415571501b4afa083aaefd9f514a38a10fb10546418dbd5b7ef9bfbd121abd37f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## 0.26.0 - 2022-10-14
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
* Support for page labels
|
|
6
|
+
* [HexaPDF::Type::MarkInformation]
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
* [HexaPDF::Rectangle] to recover from invalid values by defaulting to
|
|
11
|
+
`[0, 0, 0, 0]`
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* [HexaPDF::DictionaryFields::PDFByteStringConverter] to duplicate the string
|
|
16
|
+
before conversion
|
|
17
|
+
* [HexaPDF::Type::FileSpecification#path=] to duplicate the given string value
|
|
18
|
+
due to using it for two different fields
|
|
19
|
+
|
|
20
|
+
|
|
1
21
|
## 0.25.0 - 2022-10-02
|
|
2
22
|
|
|
3
23
|
### Added
|
|
@@ -588,6 +588,8 @@ module HexaPDF
|
|
|
588
588
|
TransformParams: 'HexaPDF::Type::Signature::TransformParams',
|
|
589
589
|
Outlines: 'HexaPDF::Type::Outline',
|
|
590
590
|
XXOutlineItem: 'HexaPDF::Type::OutlineItem',
|
|
591
|
+
PageLabel: 'HexaPDF::Type::PageLabel',
|
|
592
|
+
XXMarkInformation: 'HexaPDF::Type::MarkInformation',
|
|
591
593
|
},
|
|
592
594
|
'object.subtype_map' => {
|
|
593
595
|
nil => {
|
|
@@ -268,7 +268,7 @@ module HexaPDF
|
|
|
268
268
|
# returns +nil+.
|
|
269
269
|
def self.convert(str, _type, _document)
|
|
270
270
|
return if !str.kind_of?(String) || str.encoding == Encoding::BINARY
|
|
271
|
-
str.force_encoding(Encoding::BINARY)
|
|
271
|
+
str.dup.force_encoding(Encoding::BINARY)
|
|
272
272
|
end
|
|
273
273
|
|
|
274
274
|
end
|
|
@@ -41,8 +41,26 @@ module HexaPDF
|
|
|
41
41
|
|
|
42
42
|
# This class provides methods for managing the pages of a PDF file.
|
|
43
43
|
#
|
|
44
|
-
#
|
|
45
|
-
# interface.
|
|
44
|
+
# For page manipulation it uses the methods of HexaPDF::Type::PageTreeNode underneath but
|
|
45
|
+
# provides a more convenient interface.
|
|
46
|
+
#
|
|
47
|
+
# == Page Labels
|
|
48
|
+
#
|
|
49
|
+
# In addition to page manipulation, the class provides methods for managing the page labels
|
|
50
|
+
# which are alternative descriptions for the pages. In contrast to the page indices which are
|
|
51
|
+
# fixed the page labels can be freely defined.
|
|
52
|
+
#
|
|
53
|
+
# The way this works is that one can assign page label objects (HexaPDF::Type::PageLabel) to
|
|
54
|
+
# page ranges via the /PageLabels number tree in the catalog. The page label objects specify how
|
|
55
|
+
# the pages in their range shall be labeled. See HexaPDF::Type::PageLabel for examples of page
|
|
56
|
+
# labels.
|
|
57
|
+
#
|
|
58
|
+
# To facilitate the easy use of page labels the following methods are provided:
|
|
59
|
+
#
|
|
60
|
+
# * #page_label
|
|
61
|
+
# * #each_labelling_range
|
|
62
|
+
# * #add_labelling_range
|
|
63
|
+
# * #delete_labelling_range
|
|
46
64
|
class Pages
|
|
47
65
|
|
|
48
66
|
include Enumerable
|
|
@@ -154,6 +172,85 @@ module HexaPDF
|
|
|
154
172
|
alias size count
|
|
155
173
|
alias length count
|
|
156
174
|
|
|
175
|
+
# Returns the constructed page label for the given page index.
|
|
176
|
+
#
|
|
177
|
+
# If no page labels are defined, +nil+ is returned.
|
|
178
|
+
#
|
|
179
|
+
# See HexaPDF::Type::PageLabel for examples.
|
|
180
|
+
def page_label(page_index)
|
|
181
|
+
raise(ArgumentError, 'Page index out of range') if page_index < 0 || page_index >= count
|
|
182
|
+
each_labelling_range do |index, count, label|
|
|
183
|
+
if page_index < index + count
|
|
184
|
+
return label.construct_label(page_index - index)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# :call-seq:
|
|
190
|
+
# pages.each_labelling_range {|first_index, count, page_label| block } -> pages
|
|
191
|
+
# pages.each_labelling_range -> Enumerator
|
|
192
|
+
#
|
|
193
|
+
# Iterates over all defined labelling ranges inorder, yielding the page index of the first
|
|
194
|
+
# page in the labelling range, the number of pages in the range, and the associated page label
|
|
195
|
+
# object.
|
|
196
|
+
#
|
|
197
|
+
# The last yielded count might be equal or lower than zero in case the document has fewer
|
|
198
|
+
# pages than anticipated by the labelling ranges.
|
|
199
|
+
def each_labelling_range
|
|
200
|
+
return to_enum(__method__) unless block_given?
|
|
201
|
+
return unless @document.catalog.page_labels
|
|
202
|
+
|
|
203
|
+
last_start = nil
|
|
204
|
+
last_label = nil
|
|
205
|
+
@document.catalog.page_labels.each_entry do |s1, p1|
|
|
206
|
+
yield(last_start, s1 - last_start, @document.wrap(last_label, type: :PageLabel)) if last_start
|
|
207
|
+
last_start = s1
|
|
208
|
+
last_label = p1
|
|
209
|
+
end
|
|
210
|
+
if last_start
|
|
211
|
+
yield(last_start, count - last_start, @document.wrap(last_label, type: :PageLabel))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
self
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Adds a new labelling range starting at +start_index+ and returns it.
|
|
218
|
+
#
|
|
219
|
+
# See HexaPDF::Type::PageLabel for information on the arguments +numbering_style+, +prefix+,
|
|
220
|
+
# and +start_number+.
|
|
221
|
+
#
|
|
222
|
+
# If a labelling range already exists for the given +start_index+, its value will be
|
|
223
|
+
# overwritten.
|
|
224
|
+
#
|
|
225
|
+
# If there are no existing labelling ranges and the given +start_index+ isn't 0, a default
|
|
226
|
+
# labelling range using start index 0 and numbering style :decimal is added.
|
|
227
|
+
def add_labelling_range(start_index, numbering_style: nil, prefix: nil, start_number: nil)
|
|
228
|
+
page_label = @document.wrap({}, type: :PageLabel)
|
|
229
|
+
page_label.numbering_style(numbering_style) if numbering_style
|
|
230
|
+
page_label.prefix(prefix) if prefix
|
|
231
|
+
page_label.start_number(start_number) if start_number
|
|
232
|
+
|
|
233
|
+
labels = @document.catalog.page_labels(create: true)
|
|
234
|
+
labels.add_entry(start_index, page_label)
|
|
235
|
+
labels.add_entry(0, {S: :d}) unless labels.find_entry(0)
|
|
236
|
+
|
|
237
|
+
page_label
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Deletes the page labelling range starting at +start_index+ and returns the associated page
|
|
241
|
+
# label object.
|
|
242
|
+
#
|
|
243
|
+
# Note: The page label for the range starting at zero can only be deleted last!
|
|
244
|
+
def delete_labelling_range(start_index)
|
|
245
|
+
return unless (labels = @document.catalog.page_labels)
|
|
246
|
+
if start_index == 0 && labels.each_entry.first(2).size == 2
|
|
247
|
+
raise HexaPDF::Error, "Page labelling range starting at 0 must be deleted last"
|
|
248
|
+
end
|
|
249
|
+
page_label = labels.delete_entry(start_index)
|
|
250
|
+
@document.catalog.delete(:PageLabels) if start_index == 0
|
|
251
|
+
page_label
|
|
252
|
+
end
|
|
253
|
+
|
|
157
254
|
end
|
|
158
255
|
|
|
159
256
|
end
|
data/lib/hexapdf/rectangle.rb
CHANGED
|
@@ -116,12 +116,19 @@ module HexaPDF
|
|
|
116
116
|
|
|
117
117
|
private
|
|
118
118
|
|
|
119
|
+
#:nodoc:
|
|
120
|
+
RECTANGLE_ERROR_MSG = "A PDF rectangle structure must contain an array of four numbers"
|
|
121
|
+
|
|
119
122
|
# Ensures that the value is an array containing four numbers that specify the bottom left and
|
|
120
123
|
# top right corner.
|
|
121
124
|
def after_data_change
|
|
122
125
|
super
|
|
123
126
|
unless value.size == 4 && all? {|v| v.kind_of?(Numeric) }
|
|
124
|
-
|
|
127
|
+
if !document? ||
|
|
128
|
+
document.config['parser.on_correctable_error'].call(document, RECTANGLE_ERROR_MSG, 0)
|
|
129
|
+
raise ArgumentError, RECTANGLE_ERROR_MSG
|
|
130
|
+
end
|
|
131
|
+
value.replace([0, 0, 0, 0])
|
|
125
132
|
end
|
|
126
133
|
self[0], self[2] = self[2], self[0] if self[0] > self[2]
|
|
127
134
|
self[1], self[3] = self[3], self[1] if self[1] > self[3]
|
|
@@ -130,7 +137,9 @@ module HexaPDF
|
|
|
130
137
|
def perform_validation #:nodoc:
|
|
131
138
|
super
|
|
132
139
|
unless value.size == 4 && all? {|v| v.kind_of?(Numeric) }
|
|
133
|
-
yield("A PDF rectangle structure must contain an array of four numbers"
|
|
140
|
+
yield("A PDF rectangle structure must contain an array of four numbers; replacing " \
|
|
141
|
+
"it with [0, 0, 0, 0]", true)
|
|
142
|
+
value.replace([0, 0, 0, 0])
|
|
134
143
|
end
|
|
135
144
|
end
|
|
136
145
|
|
data/lib/hexapdf/type/catalog.rb
CHANGED
|
@@ -73,7 +73,7 @@ module HexaPDF
|
|
|
73
73
|
define_field :AcroForm, type: :XXAcroForm, version: '1.2'
|
|
74
74
|
define_field :Metadata, type: Stream, indirect: true, version: '1.4'
|
|
75
75
|
define_field :StructTreeRoot, type: Dictionary, version: '1.3'
|
|
76
|
-
define_field :MarkInfo, type:
|
|
76
|
+
define_field :MarkInfo, type: :XXMarkInformation, version: '1.4'
|
|
77
77
|
define_field :Lang, type: String, version: '1.4'
|
|
78
78
|
define_field :SpiderInfo, type: Dictionary, version: '1.3'
|
|
79
79
|
define_field :OutputIntents, type: PDFArray, version: '1.4'
|
|
@@ -132,6 +132,23 @@ module HexaPDF
|
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
|
|
135
|
+
# Returns the page labels number tree.
|
|
136
|
+
#
|
|
137
|
+
# * If a page labels number tree exists, the +create+ argument is not used.
|
|
138
|
+
#
|
|
139
|
+
# * If no page labels number tree exists and +create+ is +true+, a new one is created.
|
|
140
|
+
#
|
|
141
|
+
# * If no page labels number tree exists and +create+ is +false+, +nil+ is returned.
|
|
142
|
+
#
|
|
143
|
+
# See: HexaPDF::Document::Pages
|
|
144
|
+
def page_labels(create: false)
|
|
145
|
+
if (object = self[:PageLabels])
|
|
146
|
+
object
|
|
147
|
+
elsif create
|
|
148
|
+
self[:PageLabels] = document.wrap({}, type: NumberTreeNode)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
135
152
|
private
|
|
136
153
|
|
|
137
154
|
# Ensures that there is a valid page tree.
|
|
@@ -116,7 +116,8 @@ module HexaPDF
|
|
|
116
116
|
#
|
|
117
117
|
# Since the /Unix, /Mac and /DOS fields are obsolescent, only the /F and /UF fields are set.
|
|
118
118
|
def path=(filename)
|
|
119
|
-
self[:UF] =
|
|
119
|
+
self[:UF] = filename
|
|
120
|
+
self[:F] = filename.b
|
|
120
121
|
delete(:FS)
|
|
121
122
|
delete(:Unix)
|
|
122
123
|
delete(:Mac)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
|
2
|
+
#
|
|
3
|
+
#--
|
|
4
|
+
# This file is part of HexaPDF.
|
|
5
|
+
#
|
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
+
# Copyright (C) 2014-2022 Thomas Leitner
|
|
8
|
+
#
|
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
16
|
+
#
|
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
20
|
+
# License for more details.
|
|
21
|
+
#
|
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
|
24
|
+
#
|
|
25
|
+
# The interactive user interfaces in modified source and object code
|
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
|
28
|
+
#
|
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
|
31
|
+
# is created or manipulated using HexaPDF.
|
|
32
|
+
#
|
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
|
35
|
+
#++
|
|
36
|
+
|
|
37
|
+
require 'hexapdf/dictionary'
|
|
38
|
+
|
|
39
|
+
module HexaPDF
|
|
40
|
+
module Type
|
|
41
|
+
|
|
42
|
+
# Represents the mark information dictionary which provides some general information related to
|
|
43
|
+
# structured PDF documents.
|
|
44
|
+
#
|
|
45
|
+
# See: PDF1.7 s14.7.1
|
|
46
|
+
class MarkInformation < Dictionary
|
|
47
|
+
|
|
48
|
+
define_type :XXMarkInformation
|
|
49
|
+
|
|
50
|
+
define_field :Marked, type: Boolean, default: false
|
|
51
|
+
define_field :UserProperties, type: Boolean, default: false
|
|
52
|
+
define_field :Suspects, type: Boolean, default: false
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/hexapdf/type/page.rb
CHANGED
|
@@ -353,6 +353,14 @@ module HexaPDF
|
|
|
353
353
|
idx
|
|
354
354
|
end
|
|
355
355
|
|
|
356
|
+
# Returns the label of the page which is an optional, alternative description of the page
|
|
357
|
+
# index.
|
|
358
|
+
#
|
|
359
|
+
# See HexaPDF::Document::Pages for details.
|
|
360
|
+
def label
|
|
361
|
+
document.pages.page_label(index)
|
|
362
|
+
end
|
|
363
|
+
|
|
356
364
|
# Returns all parent nodes of the page up to the root of the page tree.
|
|
357
365
|
#
|
|
358
366
|
# The direct parent is the first node in the array and the root node the last.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
|
2
|
+
#
|
|
3
|
+
#--
|
|
4
|
+
# This file is part of HexaPDF.
|
|
5
|
+
#
|
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
+
# Copyright (C) 2014-2022 Thomas Leitner
|
|
8
|
+
#
|
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
16
|
+
#
|
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
20
|
+
# License for more details.
|
|
21
|
+
#
|
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
|
24
|
+
#
|
|
25
|
+
# The interactive user interfaces in modified source and object code
|
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
|
28
|
+
#
|
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
|
31
|
+
# is created or manipulated using HexaPDF.
|
|
32
|
+
#
|
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
|
35
|
+
#++
|
|
36
|
+
|
|
37
|
+
require 'hexapdf/dictionary'
|
|
38
|
+
|
|
39
|
+
module HexaPDF
|
|
40
|
+
module Type
|
|
41
|
+
|
|
42
|
+
# Represents a page label dictionary.
|
|
43
|
+
#
|
|
44
|
+
# A page label dictionary contains information about the numbering style, the label prefix and
|
|
45
|
+
# the start number to construct page labels like 'A-1' or 'iii'. What is not stored is the page
|
|
46
|
+
# to which it is applied since that is stored in a number tree referenced through the
|
|
47
|
+
# /PageLabels entry in the document catalog.
|
|
48
|
+
#
|
|
49
|
+
# See HexaPDF::Document::Pages for details on how to create and manage page labels.
|
|
50
|
+
#
|
|
51
|
+
# Examples:
|
|
52
|
+
#
|
|
53
|
+
# * numbering style :decimal, prefix none, start number default value
|
|
54
|
+
#
|
|
55
|
+
# 1, 2, 3, 4, ...
|
|
56
|
+
#
|
|
57
|
+
# * numbering style :lowercase_letters, prefix 'Appendix ', start number 5
|
|
58
|
+
#
|
|
59
|
+
# Appendix e, Appendix f, Appendix g, ...
|
|
60
|
+
#
|
|
61
|
+
# * numbering style :uppercase_roman, prefix none, start number 10
|
|
62
|
+
#
|
|
63
|
+
# X, XI, XII, XIII, ...
|
|
64
|
+
#
|
|
65
|
+
# * numbering style :none, prefix 'Page', start number default value
|
|
66
|
+
#
|
|
67
|
+
# Page, Page, Page, Page, ...
|
|
68
|
+
#
|
|
69
|
+
# * numbering style :none, prefix none, start number default value
|
|
70
|
+
#
|
|
71
|
+
# "", "", "", ... (i.e. always the empty string)
|
|
72
|
+
#
|
|
73
|
+
# See: PDF1.7 s12.4.2, HexaPDF::Document::Pages, HexaPDF::Type::Catalog
|
|
74
|
+
class PageLabel < Dictionary
|
|
75
|
+
|
|
76
|
+
define_type :PageLabel
|
|
77
|
+
|
|
78
|
+
define_field :Type, type: Symbol, default: type
|
|
79
|
+
define_field :S, type: Symbol, allowed_values: [:D, :R, :r, :A, :a]
|
|
80
|
+
define_field :P, type: String
|
|
81
|
+
define_field :St, type: Integer, default: 1
|
|
82
|
+
|
|
83
|
+
# Constructs the page label for the given index which needs to be relative to the page index
|
|
84
|
+
# of the first page in the associated labelling range.
|
|
85
|
+
#
|
|
86
|
+
# This method is usually not called directly but through HexaPDF::Document::Pages#page_label.
|
|
87
|
+
def construct_label(index)
|
|
88
|
+
label = (prefix || '').dup
|
|
89
|
+
number = start_number + index
|
|
90
|
+
case numbering_style
|
|
91
|
+
when :decimal
|
|
92
|
+
label + number.to_s
|
|
93
|
+
when :uppercase_roman
|
|
94
|
+
label + number_to_roman_numeral(number)
|
|
95
|
+
when :lowercase_roman
|
|
96
|
+
label + number_to_roman_numeral(number, lowercase: true)
|
|
97
|
+
when :uppercase_letters
|
|
98
|
+
label + number_to_letters(number)
|
|
99
|
+
when :lowercase_letters
|
|
100
|
+
label + number_to_letters(number, lowercase: true)
|
|
101
|
+
when :none
|
|
102
|
+
label
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# :nodoc:
|
|
107
|
+
NUMBERING_STYLE_MAPPING = {
|
|
108
|
+
decimal: :D, D: :D,
|
|
109
|
+
uppercase_roman: :R, R: :R,
|
|
110
|
+
lowercase_roman: :r, r: :r,
|
|
111
|
+
uppercase_letters: :A, A: :A,
|
|
112
|
+
lowercase_letters: :a, a: :a,
|
|
113
|
+
none: nil
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# :nodoc:
|
|
117
|
+
REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse]
|
|
118
|
+
|
|
119
|
+
# :call-seq:
|
|
120
|
+
# page_label.numbering_style -> numbering_style
|
|
121
|
+
# page_label.numbering_style(value) -> numbering_style
|
|
122
|
+
#
|
|
123
|
+
# Returns the numbering style if no argument is given. Otherwise sets the numbering style to
|
|
124
|
+
# the given value.
|
|
125
|
+
#
|
|
126
|
+
# The following numbering styles are available:
|
|
127
|
+
#
|
|
128
|
+
# :none:: No numbering is done; the label only consists of the prefix.
|
|
129
|
+
# :decimal:: Decimal arabic numerals (1, 2, 3, 4, ...).
|
|
130
|
+
# :uppercase_roman:: Uppercase roman numerals (I, II, III, IV, ...)
|
|
131
|
+
# :lowercase_roman:: Lowercase roman numerals (i, ii, iii, iv, ...)
|
|
132
|
+
# :uppercase_letters:: Uppercase letters (A, B, C, D, ...)
|
|
133
|
+
# :lowercase_letters:: Lowercase letters (a, b, c, d, ...)
|
|
134
|
+
def numbering_style(value = nil)
|
|
135
|
+
if value
|
|
136
|
+
self[:S] = NUMBERING_STYLE_MAPPING.fetch(value) do
|
|
137
|
+
raise ArgumentError, "Invalid numbering style specified: #{value}"
|
|
138
|
+
end
|
|
139
|
+
else
|
|
140
|
+
REVERSE_NUMBERING_STYLE_MAPPING.fetch(self[:S], :none)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# :call-seq:
|
|
145
|
+
# page_label.prefix -> prefix
|
|
146
|
+
# page_label.prefix(value) -> prefix
|
|
147
|
+
#
|
|
148
|
+
# Returns the label prefix if no argument is given. Otherwise sets the label prefix to the
|
|
149
|
+
# given string value.
|
|
150
|
+
def prefix(value = nil)
|
|
151
|
+
if value
|
|
152
|
+
self[:P] = value
|
|
153
|
+
else
|
|
154
|
+
self[:P]
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# :call-seq:
|
|
159
|
+
# page_label.start_number -> start_number
|
|
160
|
+
# page_label.start_number(value) -> start_number
|
|
161
|
+
#
|
|
162
|
+
# Returns the start number if no argument is given. Otherwise sets the start number to the
|
|
163
|
+
# given integer value.
|
|
164
|
+
def start_number(value = nil)
|
|
165
|
+
if value
|
|
166
|
+
if !value.kind_of?(Integer) || value < 1
|
|
167
|
+
raise ArgumentError, "Start number must be an integer greater than or equal to 1"
|
|
168
|
+
end
|
|
169
|
+
self[:St] = value
|
|
170
|
+
else
|
|
171
|
+
self[:St]
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
private
|
|
176
|
+
|
|
177
|
+
# :nodoc:
|
|
178
|
+
ALPHABET = ('A'..'Z').to_a
|
|
179
|
+
|
|
180
|
+
# Maps the given number to uppercase (or, if +lowercase+ is +true+, lowercase) letters (e.g. 1
|
|
181
|
+
# -> A, 27 -> AA, 28 -> AB, ...).
|
|
182
|
+
def number_to_letters(number, lowercase: false)
|
|
183
|
+
result = "".dup
|
|
184
|
+
while number > 0
|
|
185
|
+
number, rest = (number - 1).divmod(26)
|
|
186
|
+
result.prepend(ALPHABET[rest])
|
|
187
|
+
end
|
|
188
|
+
lowercase ? result.downcase : result
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# :nodoc:
|
|
192
|
+
ROMAN_NUMERAL_MAPPING = {
|
|
193
|
+
1000 => "M",
|
|
194
|
+
900 => "CM",
|
|
195
|
+
500 => "D",
|
|
196
|
+
400 => "CD",
|
|
197
|
+
100 => "C",
|
|
198
|
+
90 => "XC",
|
|
199
|
+
50 => "L",
|
|
200
|
+
40 => "XL",
|
|
201
|
+
10 => "X",
|
|
202
|
+
9 => "IX",
|
|
203
|
+
5 => "V",
|
|
204
|
+
4 => "IV",
|
|
205
|
+
1 => "I",
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# Maps the given number to an uppercase (or, if +lowercase+ is +true+, lowercase) roman
|
|
209
|
+
# numeral.
|
|
210
|
+
def number_to_roman_numeral(number, lowercase: false)
|
|
211
|
+
result = ROMAN_NUMERAL_MAPPING.inject("".dup) do |memo, (base, roman_numeral)|
|
|
212
|
+
next memo if number < base
|
|
213
|
+
quotient, number = number.divmod(base)
|
|
214
|
+
memo << roman_numeral * quotient
|
|
215
|
+
end
|
|
216
|
+
lowercase ? result.downcase : result
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
end
|
|
222
|
+
end
|
data/lib/hexapdf/type.rb
CHANGED
|
@@ -75,6 +75,8 @@ module HexaPDF
|
|
|
75
75
|
autoload(:Signature, 'hexapdf/type/signature')
|
|
76
76
|
autoload(:Outline, 'hexapdf/type/outline')
|
|
77
77
|
autoload(:OutlineItem, 'hexapdf/type/outline_item')
|
|
78
|
+
autoload(:PageLabel, 'hexapdf/type/page_label')
|
|
79
|
+
autoload(:MarkInformation, 'hexapdf/type/mark_information')
|
|
78
80
|
|
|
79
81
|
end
|
|
80
82
|
|
data/lib/hexapdf/version.rb
CHANGED
|
@@ -136,4 +136,90 @@ describe HexaPDF::Document::Pages do
|
|
|
136
136
|
assert_equal(3, @doc.pages.count)
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
|
+
|
|
140
|
+
describe "page_label" do
|
|
141
|
+
it "returns the page label object for the given range start index" do
|
|
142
|
+
11.times { @doc.pages.add }
|
|
143
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :D}, 5, {S: :r, St: 2}, 10, {P: 'A-', S: :a}]}
|
|
144
|
+
assert_equal("1", @doc.pages.page_label(0))
|
|
145
|
+
assert_equal("5", @doc.pages.page_label(4))
|
|
146
|
+
assert_equal("ii", @doc.pages.page_label(5))
|
|
147
|
+
assert_equal("vi", @doc.pages.page_label(9))
|
|
148
|
+
assert_equal("A-a", @doc.pages.page_label(10))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "fails if the page index is out of range" do
|
|
152
|
+
assert_raises(ArgumentError) { @doc.pages.page_label(-1) }
|
|
153
|
+
assert_raises(ArgumentError) { @doc.pages.page_label(0) }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe "each_labelling_range" do
|
|
158
|
+
before do
|
|
159
|
+
10.times { @doc.pages.add }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "returns no entries for an empty or non-existing /PageLabels entry" do
|
|
163
|
+
assert(@doc.pages.each_labelling_range.to_a.empty?)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "works for a single page label entry" do
|
|
167
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}]}
|
|
168
|
+
result = @doc.pages.each_labelling_range.to_a
|
|
169
|
+
assert_equal([[0, 10, {S: :r}]], result.map {|s, c, l| [s, c, l.value]})
|
|
170
|
+
assert_equal(:lowercase_roman, result[0].last.numbering_style)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "works for multiple page label entries" do
|
|
174
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :d}, 7, {S: :A}]}
|
|
175
|
+
result = @doc.pages.each_labelling_range.to_a
|
|
176
|
+
assert_equal([[0, 2, {S: :r}], [2, 5, {S: :d}], [7, 3, {S: :A}]],
|
|
177
|
+
result.map {|s, c, l| [s, c, l.value]})
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "returns a zero or negative count for the last range if there aren't enough pages" do
|
|
181
|
+
assert_equal(10, @doc.pages.count)
|
|
182
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :d}, 10, {S: :r}]}
|
|
183
|
+
assert_equal(0, @doc.pages.each_labelling_range.to_a[-1][1])
|
|
184
|
+
@doc.catalog[:PageLabels][:Nums][2] = 11
|
|
185
|
+
assert_equal(-1, @doc.pages.each_labelling_range.to_a[-1][1])
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
describe "add_labelling_range" do
|
|
190
|
+
it "creates a new page label object for the given arguments" do
|
|
191
|
+
label = @doc.pages.add_labelling_range(5, numbering_style: :lowercase_roman,
|
|
192
|
+
start_number: 5, prefix: 'a')
|
|
193
|
+
assert_equal({S: :r, St: 5, P: 'a'}, label.value)
|
|
194
|
+
assert_equal(label, @doc.catalog.page_labels.find_entry(5))
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "adds an entry for the range starting at 0 if it doesn't exist" do
|
|
198
|
+
label = @doc.pages.add_labelling_range(5)
|
|
199
|
+
assert_equal([{S: :d}, label],
|
|
200
|
+
@doc.catalog.page_labels[:Nums].value.values_at(1, 3))
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
describe "delete_labelling_range" do
|
|
205
|
+
before do
|
|
206
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :d}]}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it "deletes the labelling range for a given start index" do
|
|
210
|
+
label = @doc.pages.delete_labelling_range(5)
|
|
211
|
+
assert_equal({S: :d}, label)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "deletes the labelling range for 0 if it is the last, together with the number tree" do
|
|
215
|
+
@doc.pages.delete_labelling_range(5)
|
|
216
|
+
label = @doc.pages.delete_labelling_range(0)
|
|
217
|
+
assert_equal({S: :r}, label)
|
|
218
|
+
assert_nil(@doc.catalog[:PageLabels])
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
it "fails if the range starting at zero is deleted when other ranges still exist" do
|
|
222
|
+
assert_raises(HexaPDF::Error) { @doc.pages.delete_labelling_range(0) }
|
|
223
|
+
end
|
|
224
|
+
end
|
|
139
225
|
end
|
|
@@ -156,8 +156,10 @@ describe HexaPDF::DictionaryFields do
|
|
|
156
156
|
it "allows conversion to a binary string" do
|
|
157
157
|
refute(@field.convert('test'.b, self))
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
input = "test"
|
|
160
|
+
str = @field.convert(input, self)
|
|
160
161
|
assert_equal('test', str)
|
|
162
|
+
refute_same(input, str)
|
|
161
163
|
assert_equal(Encoding::BINARY, str.encoding)
|
|
162
164
|
end
|
|
163
165
|
end
|
|
@@ -6,11 +6,23 @@ require 'hexapdf/document'
|
|
|
6
6
|
|
|
7
7
|
describe HexaPDF::Rectangle do
|
|
8
8
|
describe "after_data_change" do
|
|
9
|
-
it "fails if the rectangle doesn't contain four numbers" do
|
|
9
|
+
it "fails if the rectangle doesn't contain four numbers, without document" do
|
|
10
10
|
assert_raises(ArgumentError) { HexaPDF::Rectangle.new([1, 2, 3]) }
|
|
11
11
|
assert_raises(ArgumentError) { HexaPDF::Rectangle.new([1, 2, 3, :a]) }
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
it "fails if the rectangle doesn't contain four numbers, with document and strict mode" do
|
|
15
|
+
doc = HexaPDF::Document.new(config: {'parser.on_correctable_error' => lambda { true }})
|
|
16
|
+
assert_raises(ArgumentError) { HexaPDF::Rectangle.new([1, 2, 3], document: doc) }
|
|
17
|
+
assert_raises(ArgumentError) { HexaPDF::Rectangle.new([1, 2, 3, :a], document: doc) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "recovers if the rectangle doesn't contain four numbers, with document default mode" do
|
|
21
|
+
doc = HexaPDF::Document.new
|
|
22
|
+
assert_equal([0, 0, 0, 0], HexaPDF::Rectangle.new([1, 2, 3], document: doc).value)
|
|
23
|
+
assert_equal([0, 0, 0, 0], HexaPDF::Rectangle.new([1, 2, 3, :a], document: doc).value)
|
|
24
|
+
end
|
|
25
|
+
|
|
14
26
|
it "normalizes the array values" do
|
|
15
27
|
rect = HexaPDF::Rectangle.new([0, 1, 2, 3])
|
|
16
28
|
assert_equal([0, 1, 2, 3], rect.value)
|
|
@@ -57,10 +69,12 @@ describe HexaPDF::Rectangle do
|
|
|
57
69
|
assert(rect.validate)
|
|
58
70
|
|
|
59
71
|
rect.value.shift
|
|
60
|
-
|
|
72
|
+
assert(rect.validate)
|
|
73
|
+
assert_equal([0, 0, 0, 0], rect.value)
|
|
61
74
|
|
|
62
|
-
rect.value
|
|
63
|
-
|
|
75
|
+
rect.value[-1] = :A
|
|
76
|
+
assert(rect.validate)
|
|
77
|
+
assert_equal([0, 0, 0, 0], rect.value)
|
|
64
78
|
end
|
|
65
79
|
end
|
|
66
80
|
end
|
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.26.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.26.0)>>
|
|
76
76
|
endobj
|
|
77
77
|
2 0 obj
|
|
78
78
|
<</Length 10>>stream
|
|
@@ -206,7 +206,7 @@ describe HexaPDF::Writer do
|
|
|
206
206
|
<</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
|
|
207
207
|
endobj
|
|
208
208
|
5 0 obj
|
|
209
|
-
<</Producer(HexaPDF version 0.
|
|
209
|
+
<</Producer(HexaPDF version 0.26.0)>>
|
|
210
210
|
endobj
|
|
211
211
|
4 0 obj
|
|
212
212
|
<</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
|
|
@@ -54,6 +54,23 @@ describe HexaPDF::Type::Catalog do
|
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
describe "page_labels" do
|
|
58
|
+
it "returns an existing page labels number tree" do
|
|
59
|
+
@catalog[:PageLabels] = {Nums: []}
|
|
60
|
+
assert_equal({Nums: []}, @catalog.page_labels.value)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "returns an existing page labels number tree even if create: true" do
|
|
64
|
+
obj = @catalog[:PageLabels] = {Nums: []}
|
|
65
|
+
assert_same(obj, @catalog.page_labels(create: true).value)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "creates a new page labels number tree if create: true" do
|
|
69
|
+
tree = @catalog.page_labels(create: true)
|
|
70
|
+
assert_kind_of(HexaPDF::NumberTreeNode, tree)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
57
74
|
describe "validation" do
|
|
58
75
|
it "creates the page tree if necessary" do
|
|
59
76
|
refute(@catalog.validate(auto_correct: false))
|
|
@@ -37,6 +37,7 @@ describe HexaPDF::Type::FileSpecification do
|
|
|
37
37
|
it "only sets /UF and /F, deleting /Mac, /Unix, /DOS entries if they exist" do
|
|
38
38
|
@obj[:Unix] = @obj[:Mac] = @obj[:DOS] = 'a'
|
|
39
39
|
@obj.path = 'file/test'
|
|
40
|
+
refute_same(@obj.value[:UF], @obj.value[:F])
|
|
40
41
|
assert_equal('file/test', @obj[:UF])
|
|
41
42
|
assert_equal('file/test', @obj[:F])
|
|
42
43
|
refute(@obj.key?(:Unix))
|
|
@@ -327,6 +327,14 @@ describe HexaPDF::Type::Page do
|
|
|
327
327
|
end
|
|
328
328
|
end
|
|
329
329
|
|
|
330
|
+
describe "label" do
|
|
331
|
+
it "returns the label for the page" do
|
|
332
|
+
5.times { @doc.pages.add }
|
|
333
|
+
@doc.pages.add_labelling_range(0, numbering_style: :uppercase_letters)
|
|
334
|
+
assert_equal(%w[A B C D E], @doc.pages.each.map(&:label))
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
330
338
|
it "returns all ancestor page tree nodes of a page" do
|
|
331
339
|
root = @doc.add({Type: :Pages})
|
|
332
340
|
kid = @doc.add({Type: :Pages, Parent: root})
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/page_label'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::PageLabel do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@page_label = @doc.wrap({Type: :PageLabel})
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "construct_label" do
|
|
14
|
+
it "returns an empty label if nothing is set" do
|
|
15
|
+
assert_equal('', @page_label.construct_label(0))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "returns the prefix if no numbering style is set" do
|
|
19
|
+
@page_label.prefix('hello')
|
|
20
|
+
assert_equal('hello', @page_label.construct_label(0))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "works for decimal numbers" do
|
|
24
|
+
@page_label.numbering_style(:decimal)
|
|
25
|
+
assert_equal("10", @page_label.construct_label(9))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "works for uppercase letters" do
|
|
29
|
+
@page_label.numbering_style(:uppercase_letters)
|
|
30
|
+
assert_equal("J", @page_label.construct_label(9))
|
|
31
|
+
assert_equal("AJ", @page_label.construct_label(35))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "works for lowercase letters" do
|
|
35
|
+
@page_label.numbering_style(:lowercase_letters)
|
|
36
|
+
assert_equal("a", @page_label.construct_label(0))
|
|
37
|
+
assert_equal("aa", @page_label.construct_label(26))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "works for uppercase roman numerals" do
|
|
41
|
+
@page_label.numbering_style(:uppercase_roman)
|
|
42
|
+
assert_equal("X", @page_label.construct_label(9))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "works for lowercase roman numerals" do
|
|
46
|
+
@page_label.numbering_style(:lowercase_roman)
|
|
47
|
+
assert_equal("i", @page_label.construct_label(0))
|
|
48
|
+
assert_equal("iv", @page_label.construct_label(3))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "combines the prefix with the numeric portion" do
|
|
52
|
+
@page_label.prefix('hello-')
|
|
53
|
+
@page_label.numbering_style(:decimal)
|
|
54
|
+
assert_equal('hello-1', @page_label.construct_label(0))
|
|
55
|
+
assert_equal('hello-101', @page_label.construct_label(100))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe "numbering_style" do
|
|
60
|
+
it "returns the set numbering style" do
|
|
61
|
+
assert_equal(:none, @page_label.numbering_style)
|
|
62
|
+
@page_label[:S] = :D
|
|
63
|
+
assert_equal(:decimal, @page_label.numbering_style)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "sets the numbering style to the given value" do
|
|
67
|
+
@page_label.numbering_style(:decimal)
|
|
68
|
+
assert_equal(:D, @page_label[:S])
|
|
69
|
+
@page_label.numbering_style(:none)
|
|
70
|
+
assert_nil(@page_label[:S])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "returns :none for an unknown numbering style" do
|
|
74
|
+
@page_label[:S] = :d
|
|
75
|
+
assert_equal(:none, @page_label.numbering_style)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "fails if the given value is not mapped to a numbering_style" do
|
|
79
|
+
assert_raises(ArgumentError) { @page_label.numbering_style("Nomad") }
|
|
80
|
+
assert_raises(ArgumentError) { @page_label.numbering_style(:unknown) }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "prefix" do
|
|
85
|
+
it "returns the set prefix" do
|
|
86
|
+
assert_nil(@page_label.prefix)
|
|
87
|
+
@page_label[:P] = 'Prefix'
|
|
88
|
+
assert_equal('Prefix', @page_label.prefix)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "sets the prefix to the given value" do
|
|
92
|
+
@page_label.prefix('Hallo')
|
|
93
|
+
assert_equal('Hallo', @page_label[:P])
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "start_number" do
|
|
98
|
+
it "returns the set start number" do
|
|
99
|
+
assert_equal(1, @page_label.start_number)
|
|
100
|
+
@page_label[:St] = 5
|
|
101
|
+
assert_equal(5, @page_label.start_number)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "set the start number to the given value" do
|
|
105
|
+
@page_label.start_number(5)
|
|
106
|
+
assert_equal(5, @page_label[:St])
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "fails if the provided value is not an integer" do
|
|
110
|
+
assert_raises(ArgumentError) { @page_label.start_number("6") }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "fails if the value is lower than 1" do
|
|
114
|
+
assert_raises(ArgumentError) { @page_label.start_number("-1") }
|
|
115
|
+
assert_raises(ArgumentError) { @page_label.start_number("0") }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
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.26.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-10-
|
|
11
|
+
date: 2022-10-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cmdparse
|
|
@@ -420,11 +420,13 @@ files:
|
|
|
420
420
|
- lib/hexapdf/type/icon_fit.rb
|
|
421
421
|
- lib/hexapdf/type/image.rb
|
|
422
422
|
- lib/hexapdf/type/info.rb
|
|
423
|
+
- lib/hexapdf/type/mark_information.rb
|
|
423
424
|
- lib/hexapdf/type/names.rb
|
|
424
425
|
- lib/hexapdf/type/object_stream.rb
|
|
425
426
|
- lib/hexapdf/type/outline.rb
|
|
426
427
|
- lib/hexapdf/type/outline_item.rb
|
|
427
428
|
- lib/hexapdf/type/page.rb
|
|
429
|
+
- lib/hexapdf/type/page_label.rb
|
|
428
430
|
- lib/hexapdf/type/page_tree_node.rb
|
|
429
431
|
- lib/hexapdf/type/resources.rb
|
|
430
432
|
- lib/hexapdf/type/signature.rb
|
|
@@ -670,6 +672,7 @@ files:
|
|
|
670
672
|
- test/hexapdf/type/test_outline.rb
|
|
671
673
|
- test/hexapdf/type/test_outline_item.rb
|
|
672
674
|
- test/hexapdf/type/test_page.rb
|
|
675
|
+
- test/hexapdf/type/test_page_label.rb
|
|
673
676
|
- test/hexapdf/type/test_page_tree_node.rb
|
|
674
677
|
- test/hexapdf/type/test_resources.rb
|
|
675
678
|
- test/hexapdf/type/test_signature.rb
|
|
@@ -703,7 +706,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
703
706
|
- !ruby/object:Gem::Version
|
|
704
707
|
version: '0'
|
|
705
708
|
requirements: []
|
|
706
|
-
rubygems_version: 3.
|
|
709
|
+
rubygems_version: 3.2.32
|
|
707
710
|
signing_key:
|
|
708
711
|
specification_version: 4
|
|
709
712
|
summary: HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|