hexapdf 0.12.1 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +130 -0
- data/examples/019-acro_form.rb +41 -4
- data/lib/hexapdf/cli/command.rb +4 -2
- data/lib/hexapdf/cli/image2pdf.rb +2 -1
- data/lib/hexapdf/cli/info.rb +51 -2
- data/lib/hexapdf/cli/inspect.rb +30 -8
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/split.rb +74 -14
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/dictionary.rb +9 -6
- data/lib/hexapdf/dictionary_fields.rb +1 -9
- data/lib/hexapdf/document.rb +41 -16
- data/lib/hexapdf/document/files.rb +0 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
- data/lib/hexapdf/font/cmap.rb +1 -4
- data/lib/hexapdf/font/true_type/subsetter.rb +12 -3
- data/lib/hexapdf/font/true_type/table/head.rb +1 -0
- data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
- data/lib/hexapdf/font/true_type/table/post.rb +15 -10
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
- data/lib/hexapdf/font_loader/from_file.rb +18 -8
- data/lib/hexapdf/image_loader/png.rb +3 -2
- data/lib/hexapdf/importer.rb +3 -2
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +23 -23
- data/lib/hexapdf/layout/text_layouter.rb +2 -2
- data/lib/hexapdf/layout/text_shaper.rb +3 -2
- data/lib/hexapdf/object.rb +52 -25
- data/lib/hexapdf/parser.rb +96 -4
- data/lib/hexapdf/pdf_array.rb +12 -5
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +34 -8
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +4 -3
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +132 -28
- data/lib/hexapdf/type/acro_form/button_field.rb +21 -13
- data/lib/hexapdf/type/acro_form/choice_field.rb +68 -14
- data/lib/hexapdf/type/acro_form/field.rb +35 -5
- data/lib/hexapdf/type/acro_form/form.rb +139 -14
- data/lib/hexapdf/type/acro_form/text_field.rb +70 -4
- data/lib/hexapdf/type/actions/uri.rb +3 -2
- data/lib/hexapdf/type/annotations/widget.rb +3 -4
- data/lib/hexapdf/type/catalog.rb +2 -2
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +6 -2
- data/lib/hexapdf/type/font_type0.rb +4 -4
- data/lib/hexapdf/type/form.rb +15 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +37 -13
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +1 -0
- data/lib/hexapdf/type/trailer.rb +2 -3
- data/lib/hexapdf/utils/object_hash.rb +0 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +6 -1
- data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
- data/test/hexapdf/content/test_canvas.rb +3 -3
- data/test/hexapdf/content/test_color_space.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +4 -4
- data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_post.rb +1 -1
- data/test/hexapdf/font/true_type/test_subsetter.rb +5 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
- data/test/hexapdf/font_loader/test_from_file.rb +7 -0
- data/test/hexapdf/layout/test_style.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +12 -5
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +8 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +171 -53
- data/test/hexapdf/test_pdf_array.rb +8 -1
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +296 -38
- data/test/hexapdf/type/acro_form/test_button_field.rb +22 -2
- data/test/hexapdf/type/acro_form/test_choice_field.rb +92 -9
- data/test/hexapdf/type/acro_form/test_field.rb +39 -0
- data/test/hexapdf/type/acro_form/test_form.rb +87 -15
- data/test/hexapdf/type/acro_form/test_text_field.rb +77 -1
- data/test/hexapdf/type/test_font_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_form.rb +26 -1
- data/test/hexapdf/type/test_page.rb +45 -7
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/utils/test_bit_field.rb +2 -0
- data/test/hexapdf/utils/test_object_hash.rb +5 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- data/test/test_helper.rb +2 -0
- metadata +6 -11
@@ -334,6 +334,20 @@ module HexaPDF
|
|
334
334
|
# The value needs to be an object that responds to \#call(document, message, position) and
|
335
335
|
# returns +true+ if an error should be raised.
|
336
336
|
#
|
337
|
+
# parser.try_xref_reconstruction::
|
338
|
+
# A boolean specifying whether non-recoverable parsing errors should lead to reconstructing the
|
339
|
+
# main cross-reference table.
|
340
|
+
#
|
341
|
+
# The reconstructed cross-reference table might make damaged files usable but there is no way
|
342
|
+
# to ensure that the reconstructed file is equal to the undamaged original file (though
|
343
|
+
# generally it works out).
|
344
|
+
#
|
345
|
+
# There is also the possibility that reconstructing doesn't work because the algorithm has to
|
346
|
+
# assume that the PDF was written in a certain way (which is recommended by the PDF
|
347
|
+
# specification).
|
348
|
+
#
|
349
|
+
# Defaults to +true+.
|
350
|
+
#
|
337
351
|
# sorted_tree.max_leaf_node_size::
|
338
352
|
# The maximum number of nodes that should be in a leaf node of a node tree.
|
339
353
|
#
|
@@ -412,6 +426,7 @@ module HexaPDF
|
|
412
426
|
'page.default_media_box' => :A4,
|
413
427
|
'page.default_media_orientation' => :portrait,
|
414
428
|
'parser.on_correctable_error' => proc { false },
|
429
|
+
'parser.try_xref_reconstruction' => true,
|
415
430
|
'sorted_tree.max_leaf_node_size' => 64,
|
416
431
|
'style.layers_map' => {
|
417
432
|
link: 'HexaPDF::Layout::Style::LinkLayer',
|
@@ -45,7 +45,7 @@ module HexaPDF
|
|
45
45
|
# all either in clockwise or counterclockwise direction and optionally inclined in respect to
|
46
46
|
# the x-axis.
|
47
47
|
#
|
48
|
-
# See: ELL - https://
|
48
|
+
# See: ELL - https://spaceroots.org/documents/ellipse/elliptical-arc.pdf
|
49
49
|
class Arc
|
50
50
|
|
51
51
|
include HexaPDF::Utils::MathHelpers
|
@@ -202,8 +202,8 @@ module HexaPDF
|
|
202
202
|
p2x_prime, p2y_prime = derivative_evaluate(eta2)
|
203
203
|
|
204
204
|
result << [p2x, p2y,
|
205
|
-
p1: [p1x + alpha * p1x_prime, p1y + alpha * p1y_prime],
|
206
|
-
|
205
|
+
{p1: [p1x + alpha * p1x_prime, p1y + alpha * p1y_prime],
|
206
|
+
p2: [p2x - alpha * p2x_prime, p2y - alpha * p2y_prime]}]
|
207
207
|
end
|
208
208
|
|
209
209
|
result
|
@@ -177,7 +177,7 @@ module HexaPDF
|
|
177
177
|
def parse(contents, processor = nil, &block) #:yields: object, params
|
178
178
|
raise ArgumentError, "Argument processor or block is needed" if processor.nil? && block.nil?
|
179
179
|
if processor.nil?
|
180
|
-
block.singleton_class.
|
180
|
+
block.singleton_class.send(:alias_method, :process, :call)
|
181
181
|
processor = block
|
182
182
|
end
|
183
183
|
|
data/lib/hexapdf/dictionary.rb
CHANGED
@@ -97,7 +97,7 @@ module HexaPDF
|
|
97
97
|
#
|
98
98
|
# version:: Specifies the minimum version of the PDF specification needed for this value.
|
99
99
|
def self.define_field(name, type:, required: false, default: nil, indirect: nil,
|
100
|
-
allowed_values: nil, version: '1.
|
100
|
+
allowed_values: nil, version: '1.0')
|
101
101
|
@fields ||= {}
|
102
102
|
@fields[name] = Field.new(type, required: required, default: default, indirect: indirect,
|
103
103
|
allowed_values: allowed_values, version: version)
|
@@ -155,6 +155,9 @@ module HexaPDF
|
|
155
155
|
# available (see ::define_field).
|
156
156
|
#
|
157
157
|
# * Returns the default value if one is specified and no value is available.
|
158
|
+
#
|
159
|
+
# Note: This method may throw a "can't add a new key into hash during iteration" error in
|
160
|
+
# certain cases because it potentially modifies the underlying hash!
|
158
161
|
def [](name)
|
159
162
|
field = self.class.field(name)
|
160
163
|
data = if key?(name)
|
@@ -163,7 +166,7 @@ module HexaPDF
|
|
163
166
|
value[name] = field.default
|
164
167
|
end
|
165
168
|
value[name] = data = document.deref(data) if data.kind_of?(HexaPDF::Reference)
|
166
|
-
if data.
|
169
|
+
if data.instance_of?(HexaPDF::Object) || (data.kind_of?(HexaPDF::Object) && data.value.nil?)
|
167
170
|
data = data.value
|
168
171
|
end
|
169
172
|
if (result = field&.convert(data, document))
|
@@ -182,7 +185,7 @@ module HexaPDF
|
|
182
185
|
raise ArgumentError, "Only Symbol (Name) keys are allowed to be used in PDF dictionaries"
|
183
186
|
end
|
184
187
|
|
185
|
-
if value[name].
|
188
|
+
if value[name].instance_of?(HexaPDF::Object) && !data.kind_of?(HexaPDF::Object) &&
|
186
189
|
!data.kind_of?(HexaPDF::Reference)
|
187
190
|
value[name].value = data
|
188
191
|
else
|
@@ -255,7 +258,7 @@ module HexaPDF
|
|
255
258
|
|
256
259
|
# Iterates over all currently set fields and those that are required.
|
257
260
|
def each_set_key_or_required_field #:yields: name, field
|
258
|
-
value.
|
261
|
+
value.keys.each {|name| yield(name, self.class.field(name)) }
|
259
262
|
self.class.each_field do |name, field|
|
260
263
|
yield(name, field) if field.required? && !value.key?(name)
|
261
264
|
end
|
@@ -273,7 +276,7 @@ module HexaPDF
|
|
273
276
|
# Check that required fields are set
|
274
277
|
if field.required? && obj.nil?
|
275
278
|
yield("Required field #{name} is not set", field.default?)
|
276
|
-
self[name] = obj = field.default
|
279
|
+
self[name] = obj = field.default if field.default?
|
277
280
|
end
|
278
281
|
|
279
282
|
# Check if the document version is set high enough
|
@@ -301,7 +304,7 @@ module HexaPDF
|
|
301
304
|
|
302
305
|
# Check the value of the field against the allowed values.
|
303
306
|
if field.allowed_values && !field.allowed_values.include?(obj)
|
304
|
-
yield("Field #{name} does not contain an allowed value")
|
307
|
+
yield("Field #{name} does not contain an allowed value: #{obj.inspect}")
|
305
308
|
end
|
306
309
|
|
307
310
|
# Check if field value needs to be (in)direct
|
@@ -151,17 +151,9 @@ module HexaPDF
|
|
151
151
|
# Returns a duplicated default value, automatically taking unduplicatable classes into
|
152
152
|
# account.
|
153
153
|
def default
|
154
|
-
|
154
|
+
@default.dup
|
155
155
|
end
|
156
156
|
|
157
|
-
# Returns +true+ if the default value can safely be duplicated with #dup.
|
158
|
-
def duplicatable_default?
|
159
|
-
@duplicatable_default ||= HexaPDF::Object::NOT_DUPLICATABLE_CLASSES.none? do |klass|
|
160
|
-
@default.kind_of?(klass)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
private :duplicatable_default?
|
164
|
-
|
165
157
|
# Returns +true+ if the given object is valid for this field.
|
166
158
|
def valid_object?(obj)
|
167
159
|
type.any? {|t| obj.kind_of?(t) } ||
|
data/lib/hexapdf/document.rb
CHANGED
@@ -69,15 +69,35 @@ module HexaPDF
|
|
69
69
|
|
70
70
|
autoload(:Composer, 'hexapdf/composer')
|
71
71
|
|
72
|
+
# == HexaPDF::Document
|
73
|
+
#
|
72
74
|
# Represents one PDF document.
|
73
75
|
#
|
74
76
|
# A PDF document consists of (indirect) objects, so the main job of this class is to provide
|
75
77
|
# methods for working with these objects. However, since a PDF document may also be
|
76
78
|
# incrementally updated and can therefore contain one or more revisions, there are also methods
|
77
|
-
#
|
79
|
+
# for working with these revisions.
|
78
80
|
#
|
79
81
|
# Note: This class provides everything to work on PDF documents on a low-level basis. This means
|
80
|
-
# that there are no convenience methods for higher PDF functionality
|
82
|
+
# that there are no convenience methods for higher PDF functionality. Those can be found in the
|
83
|
+
# objects linked from here, like #catalog.
|
84
|
+
#
|
85
|
+
# == Known Messages
|
86
|
+
#
|
87
|
+
# The document object provides a basic message dispatch system via #register_listener and
|
88
|
+
# #dispatch_message.
|
89
|
+
#
|
90
|
+
# Following are the messages that are used by HexaPDF itself:
|
91
|
+
#
|
92
|
+
# :complete_objects::
|
93
|
+
# This message is called before the first step of writing a document. Listeners should
|
94
|
+
# complete PDF objects that are missing some information.
|
95
|
+
#
|
96
|
+
# For example, the font system uses this message to complete the font objects with
|
97
|
+
# information that is only available once all the used glyphs are known.
|
98
|
+
#
|
99
|
+
# :before_write::
|
100
|
+
# This message is called before a document is actually serialized and written.
|
81
101
|
class Document
|
82
102
|
|
83
103
|
autoload(:Pages, 'hexapdf/document/pages')
|
@@ -400,11 +420,11 @@ module HexaPDF
|
|
400
420
|
# object in the PDF document. The block may either accept only the object or the object and the
|
401
421
|
# revision it is in.
|
402
422
|
#
|
403
|
-
# By default, only the current version of each object is returned which implies that each
|
404
|
-
#
|
405
|
-
#
|
423
|
+
# By default, only the current version of each object is returned which implies that each object
|
424
|
+
# number is yielded exactly once. If the +only_current+ option is +false+, all stored objects
|
425
|
+
# from newest to oldest are returned, not only the current version of each object.
|
406
426
|
#
|
407
|
-
# The +
|
427
|
+
# The +only_current+ option can make a difference because the document can contain multiple
|
408
428
|
# revisions:
|
409
429
|
#
|
410
430
|
# * Multiple revisions may contain objects with the same object and generation numbers, e.g.
|
@@ -442,19 +462,28 @@ module HexaPDF
|
|
442
462
|
end
|
443
463
|
|
444
464
|
# Dispatches the message +name+ with the given arguments to all registered listeners.
|
465
|
+
#
|
466
|
+
# See the main Document documentation for an overview of messages that are used by HexaPDF
|
467
|
+
# itself.
|
445
468
|
def dispatch_message(name, *args)
|
446
469
|
@listeners[name]&.each {|obj| obj.call(*args) }
|
447
470
|
end
|
448
471
|
|
449
|
-
|
450
|
-
|
472
|
+
UNSET = ::Object.new # :nordoc:
|
473
|
+
|
474
|
+
# Caches and returns the given +value+ or the value of the given block using the given
|
475
|
+
# +pdf_data+ and +key+ arguments as composite cache key. If a cached value already exists and
|
476
|
+
# +update+ is +false+, the cached value is just returned.
|
477
|
+
#
|
478
|
+
# Set +update+ to +true+ to force an update of the cached value.
|
451
479
|
#
|
452
480
|
# This facility can be used to cache expensive operations in PDF objects that are easy to
|
453
481
|
# compute again.
|
454
482
|
#
|
455
483
|
# Use #clear_cache to clear the cache if necessary.
|
456
|
-
def cache(pdf_data, key, value =
|
457
|
-
@cache[pdf_data][key]
|
484
|
+
def cache(pdf_data, key, value = UNSET, update: false)
|
485
|
+
return @cache[pdf_data][key] if cached?(pdf_data, key) && !update
|
486
|
+
@cache[pdf_data][key] = (value == UNSET ? yield : value)
|
458
487
|
end
|
459
488
|
|
460
489
|
# Returns +true+ if there is a value cached for the composite key consisting of the given
|
@@ -594,13 +623,9 @@ module HexaPDF
|
|
594
623
|
# If a block is given, it is called on validation problems.
|
595
624
|
#
|
596
625
|
# See HexaPDF::Object#validate for more information.
|
597
|
-
def validate(auto_correct: true, only_loaded: false) #:yield:
|
598
|
-
cur_obj = trailer
|
599
|
-
block = (block_given? ? lambda {|msg, correctable| yield(cur_obj, msg, correctable) } : nil)
|
600
|
-
|
626
|
+
def validate(auto_correct: true, only_loaded: false, &block) #:yield: msg, correctable, object
|
601
627
|
result = trailer.validate(auto_correct: auto_correct, &block)
|
602
628
|
each(only_current: false, only_loaded: only_loaded) do |obj|
|
603
|
-
cur_obj = obj
|
604
629
|
result &&= obj.validate(auto_correct: auto_correct, &block)
|
605
630
|
end
|
606
631
|
result
|
@@ -643,7 +668,7 @@ module HexaPDF
|
|
643
668
|
end
|
644
669
|
|
645
670
|
if validate
|
646
|
-
self.validate(auto_correct: true) do |
|
671
|
+
self.validate(auto_correct: true) do |msg, correctable, obj|
|
647
672
|
next if correctable
|
648
673
|
raise HexaPDF::Error, "Validation error for (#{obj.oid},#{obj.gen}): #{msg}"
|
649
674
|
end
|
@@ -69,6 +69,7 @@ module HexaPDF
|
|
69
69
|
when 6
|
70
70
|
if !key?(:OE) || !key?(:UE) || !key?(:Perms)
|
71
71
|
yield("Value of /OE, /UE or /Perms is missing for dictionary revision 6", false)
|
72
|
+
return
|
72
73
|
end
|
73
74
|
if value[:U].length != 48 || value[:O].length != 48 || value[:UE].length != 32 ||
|
74
75
|
value[:OE].length != 32 || value[:Perms].length != 16
|
data/lib/hexapdf/font/cmap.rb
CHANGED
@@ -100,10 +100,7 @@ module HexaPDF
|
|
100
100
|
# The writing mode of the CMap: 0 for horizontal, 1 for vertical writing.
|
101
101
|
attr_accessor :wmode
|
102
102
|
|
103
|
-
attr_reader :codespace_ranges
|
104
|
-
attr_reader :cid_mapping # :nodoc:
|
105
|
-
attr_reader :cid_range_mappings # :nodoc:
|
106
|
-
attr_reader :unicode_mapping # :nodoc:
|
103
|
+
attr_reader :codespace_ranges, :cid_mapping, :cid_range_mappings, :unicode_mapping # :nodoc:
|
107
104
|
protected :codespace_ranges, :cid_mapping, :cid_range_mappings, :unicode_mapping
|
108
105
|
|
109
106
|
# Creates a new CMap object.
|
@@ -63,6 +63,12 @@ module HexaPDF
|
|
63
63
|
def use_glyph(glyph_id)
|
64
64
|
return @glyph_map[glyph_id] if @glyph_map.key?(glyph_id)
|
65
65
|
@last_id += 1
|
66
|
+
# Handle codes for ASCII characters \r, (, ) and \ specially so that they never appear in
|
67
|
+
# the output (PDF serialization would need to escape them)
|
68
|
+
if @last_id == 13 || @last_id == 40 || @last_id == 41 || @last_id == 92
|
69
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
70
|
+
@last_id += 1
|
71
|
+
end
|
66
72
|
@glyph_map[glyph_id] = @last_id
|
67
73
|
end
|
68
74
|
|
@@ -107,7 +113,7 @@ module HexaPDF
|
|
107
113
|
locations = []
|
108
114
|
|
109
115
|
@glyph_map.each_key do |old_gid|
|
110
|
-
glyph = orig_glyf[old_gid]
|
116
|
+
glyph = orig_glyf[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
111
117
|
locations << table.size
|
112
118
|
data = glyph.raw_data
|
113
119
|
if glyph.compound?
|
@@ -134,7 +140,7 @@ module HexaPDF
|
|
134
140
|
hmtx = @font[:hmtx]
|
135
141
|
data = ''.b
|
136
142
|
@glyph_map.each_key do |old_gid|
|
137
|
-
metric = hmtx[old_gid]
|
143
|
+
metric = hmtx[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
138
144
|
data << [metric.advance_width, metric.left_side_bearing].pack('n2')
|
139
145
|
end
|
140
146
|
data
|
@@ -166,7 +172,10 @@ module HexaPDF
|
|
166
172
|
# Adds the components of compound glyphs to the subset.
|
167
173
|
def add_glyph_components
|
168
174
|
glyf = @font[:glyf]
|
169
|
-
@glyph_map.keys.each
|
175
|
+
@glyph_map.keys.each do |gid|
|
176
|
+
next if gid.kind_of?(Symbol)
|
177
|
+
glyf[gid].components&.each {|cgid| use_glyph(cgid) }
|
178
|
+
end
|
170
179
|
end
|
171
180
|
|
172
181
|
end
|
@@ -65,6 +65,7 @@ module HexaPDF
|
|
65
65
|
|
66
66
|
# Characteristics and properties of this font.
|
67
67
|
attr_accessor :type
|
68
|
+
|
68
69
|
bit_field(:type, {restricted_license_embedding: 1, preview_and_print_embedding: 2,
|
69
70
|
editable_embedding: 3, no_subsetting: 8, bitmap_embedding_only: 9})
|
70
71
|
|
@@ -112,6 +113,7 @@ module HexaPDF
|
|
112
113
|
|
113
114
|
# Information concerning the nature of the font patterns.
|
114
115
|
attr_accessor :selection
|
116
|
+
|
115
117
|
bit_field(:selection, {italic: 0, underscore: 1, negative: 2, outlined: 3, strikeout: 4,
|
116
118
|
bold: 5, regular: 6, use_typo_metrics: 7, wws: 8, oblique: 9})
|
117
119
|
|
@@ -99,18 +99,23 @@ module HexaPDF
|
|
99
99
|
@max_mem_type42, @min_mem_type1, @max_mem_type1 = read_formatted(24, 's>2N5')
|
100
100
|
|
101
101
|
sub_table_length = directory_entry.length - 32
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
102
|
+
cur_pos = io.pos
|
103
|
+
@glyph_names = lambda do |glyph_id|
|
104
|
+
io.pos = cur_pos
|
105
|
+
@glyph_names = case @format
|
106
|
+
when 1 then Format1.parse(io, sub_table_length)
|
107
|
+
when 2 then Format2.parse(io, sub_table_length)
|
108
|
+
when 3 then Format3.parse(io, sub_table_length)
|
109
|
+
when 4 then Format4.parse(io, sub_table_length)
|
110
110
|
else
|
111
|
-
[]
|
111
|
+
if font.config['font.true_type.unknown_format'] == :raise
|
112
|
+
raise HexaPDF::Error, "Unsupported post table format: #{@format}"
|
113
|
+
else
|
114
|
+
[]
|
115
|
+
end
|
112
116
|
end
|
113
|
-
|
117
|
+
@glyph_names[glyph_id]
|
118
|
+
end
|
114
119
|
end
|
115
120
|
|
116
121
|
# 'post' table format 1
|
@@ -63,8 +63,8 @@ module HexaPDF
|
|
63
63
|
file = document.config['font.map'].dig(name, variant)
|
64
64
|
return nil if file.nil?
|
65
65
|
|
66
|
-
unless File.file?(file)
|
67
|
-
raise HexaPDF::Error, "The configured font file #{file}
|
66
|
+
unless file.kind_of?(HexaPDF::Font::TrueType::Font) || File.file?(file)
|
67
|
+
raise HexaPDF::Error, "The configured font file #{file} is not a valid value"
|
68
68
|
end
|
69
69
|
FromFile.call(document, file, subset: subset)
|
70
70
|
end
|
@@ -39,26 +39,36 @@ require 'hexapdf/font/true_type_wrapper'
|
|
39
39
|
module HexaPDF
|
40
40
|
module FontLoader
|
41
41
|
|
42
|
-
# This module interprets the font name as file name and tries to load it
|
42
|
+
# This module interprets the font name either as file name and tries to load it, or as font
|
43
|
+
# object to be wrapped directly.
|
43
44
|
module FromFile
|
44
45
|
|
45
|
-
#
|
46
|
+
# :call-seq:
|
47
|
+
# FromFile.call(document, file_name, subset: true, **) -> wrapped_font
|
48
|
+
# FromFile.call(document, font_object, subset: true, **) -> wrapped_font
|
46
49
|
#
|
47
|
-
#
|
48
|
-
#
|
50
|
+
# Returns an appropriate font wrapper for the given file name or font object.
|
51
|
+
#
|
52
|
+
# If a file name is given, the file object representing the font file is *not* closed and if
|
53
|
+
# needed must be closed by the caller once the font is not needed anymore.
|
54
|
+
#
|
55
|
+
# The first form using a file name is easier to use in one-off cases. However, if multiple
|
56
|
+
# documents always refer to the same font, the second form is better to avoid re-parsing the
|
57
|
+
# font file.
|
49
58
|
#
|
50
59
|
# +document+::
|
51
60
|
# The PDF document to associate the font object with.
|
52
61
|
#
|
53
|
-
# +
|
54
|
-
# The file name.
|
62
|
+
# +file_name+/+font_object+::
|
63
|
+
# The file name or TrueType font object.
|
55
64
|
#
|
56
65
|
# +subset+::
|
57
66
|
# Specifies whether the font should be subset if possible.
|
58
67
|
def self.call(document, name, subset: true, **)
|
59
|
-
|
68
|
+
is_font = name.kind_of?(HexaPDF::Font::TrueType::Font)
|
69
|
+
return nil unless is_font || File.file?(name)
|
60
70
|
|
61
|
-
font = HexaPDF::Font::TrueType::Font.new(File.open(name, 'rb'))
|
71
|
+
font = is_font ? name : HexaPDF::Font::TrueType::Font.new(File.open(name, 'rb'))
|
62
72
|
HexaPDF::Font::TrueTypeWrapper.new(document, font, subset: subset)
|
63
73
|
end
|
64
74
|
|