hexapdf 0.12.3 → 0.14.3
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 +132 -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/dictionary.rb +12 -6
- data/lib/hexapdf/dictionary_fields.rb +2 -10
- 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 +16 -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 +107 -7
- data/lib/hexapdf/pdf_array.rb +15 -5
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +37 -10
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +22 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +130 -27
- data/lib/hexapdf/type/acro_form/button_field.rb +5 -2
- 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 +6 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +21 -12
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +5 -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 +2 -2
- 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 +10 -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_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 +9 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +205 -51
- data/test/hexapdf/test_pdf_array.rb +8 -1
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_serializer.rb +7 -0
- data/test/hexapdf/test_tokenizer.rb +28 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +288 -35
- data/test/hexapdf/type/acro_form/test_button_field.rb +15 -0
- 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 +8 -1
- data/test/hexapdf/type/test_page.rb +8 -1
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/type/test_resources.rb +6 -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 -12
|
@@ -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
|
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,12 @@ 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: If field information is available for the entry, a Hash or Array value will always be
|
|
160
|
+
# wrapped by Dictionary or PDFArray. Otherwise, the value will be returned as-is.
|
|
161
|
+
#
|
|
162
|
+
# Note: This method may throw a "can't add a new key into hash during iteration" error in
|
|
163
|
+
# certain cases because it potentially modifies the underlying hash!
|
|
158
164
|
def [](name)
|
|
159
165
|
field = self.class.field(name)
|
|
160
166
|
data = if key?(name)
|
|
@@ -163,7 +169,7 @@ module HexaPDF
|
|
|
163
169
|
value[name] = field.default
|
|
164
170
|
end
|
|
165
171
|
value[name] = data = document.deref(data) if data.kind_of?(HexaPDF::Reference)
|
|
166
|
-
if data.
|
|
172
|
+
if data.instance_of?(HexaPDF::Object) || (data.kind_of?(HexaPDF::Object) && data.value.nil?)
|
|
167
173
|
data = data.value
|
|
168
174
|
end
|
|
169
175
|
if (result = field&.convert(data, document))
|
|
@@ -182,7 +188,7 @@ module HexaPDF
|
|
|
182
188
|
raise ArgumentError, "Only Symbol (Name) keys are allowed to be used in PDF dictionaries"
|
|
183
189
|
end
|
|
184
190
|
|
|
185
|
-
if value[name].
|
|
191
|
+
if value[name].instance_of?(HexaPDF::Object) && !data.kind_of?(HexaPDF::Object) &&
|
|
186
192
|
!data.kind_of?(HexaPDF::Reference)
|
|
187
193
|
value[name].value = data
|
|
188
194
|
else
|
|
@@ -255,7 +261,7 @@ module HexaPDF
|
|
|
255
261
|
|
|
256
262
|
# Iterates over all currently set fields and those that are required.
|
|
257
263
|
def each_set_key_or_required_field #:yields: name, field
|
|
258
|
-
value.
|
|
264
|
+
value.keys.each {|name| yield(name, self.class.field(name)) }
|
|
259
265
|
self.class.each_field do |name, field|
|
|
260
266
|
yield(name, field) if field.required? && !value.key?(name)
|
|
261
267
|
end
|
|
@@ -273,7 +279,7 @@ module HexaPDF
|
|
|
273
279
|
# Check that required fields are set
|
|
274
280
|
if field.required? && obj.nil?
|
|
275
281
|
yield("Required field #{name} is not set", field.default?)
|
|
276
|
-
self[name] = obj = field.default
|
|
282
|
+
self[name] = obj = field.default if field.default?
|
|
277
283
|
end
|
|
278
284
|
|
|
279
285
|
# Check if the document version is set high enough
|
|
@@ -301,7 +307,7 @@ module HexaPDF
|
|
|
301
307
|
|
|
302
308
|
# Check the value of the field against the allowed values.
|
|
303
309
|
if field.allowed_values && !field.allowed_values.include?(obj)
|
|
304
|
-
yield("Field #{name} does not contain an allowed value")
|
|
310
|
+
yield("Field #{name} does not contain an allowed value: #{obj.inspect}")
|
|
305
311
|
end
|
|
306
312
|
|
|
307
313
|
# 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) } ||
|
|
@@ -352,7 +344,7 @@ module HexaPDF
|
|
|
352
344
|
# Wraps a given array in the Rectangle class. Otherwise returns +nil+.
|
|
353
345
|
def self.convert(data, _type, document)
|
|
354
346
|
return unless data.kind_of?(Array) || data.kind_of?(HexaPDF::PDFArray)
|
|
355
|
-
document.wrap(data, type: Rectangle)
|
|
347
|
+
data.empty? ? document.wrap(nil) : document.wrap(data, type: Rectangle)
|
|
356
348
|
end
|
|
357
349
|
|
|
358
350
|
end
|
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,16 @@ 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 (13), (, ) (40, 41) and \ (92) specially so that
|
|
67
|
+
# they never appear in the output (PDF serialization would need to escape them)
|
|
68
|
+
if @last_id == 13 || @last_id == 40 || @last_id == 92
|
|
69
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
|
70
|
+
if @last_id == 40
|
|
71
|
+
@last_id += 1
|
|
72
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
|
73
|
+
end
|
|
74
|
+
@last_id += 1
|
|
75
|
+
end
|
|
66
76
|
@glyph_map[glyph_id] = @last_id
|
|
67
77
|
end
|
|
68
78
|
|
|
@@ -107,7 +117,7 @@ module HexaPDF
|
|
|
107
117
|
locations = []
|
|
108
118
|
|
|
109
119
|
@glyph_map.each_key do |old_gid|
|
|
110
|
-
glyph = orig_glyf[old_gid]
|
|
120
|
+
glyph = orig_glyf[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
|
111
121
|
locations << table.size
|
|
112
122
|
data = glyph.raw_data
|
|
113
123
|
if glyph.compound?
|
|
@@ -134,7 +144,7 @@ module HexaPDF
|
|
|
134
144
|
hmtx = @font[:hmtx]
|
|
135
145
|
data = ''.b
|
|
136
146
|
@glyph_map.each_key do |old_gid|
|
|
137
|
-
metric = hmtx[old_gid]
|
|
147
|
+
metric = hmtx[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
|
138
148
|
data << [metric.advance_width, metric.left_side_bearing].pack('n2')
|
|
139
149
|
end
|
|
140
150
|
data
|
|
@@ -166,7 +176,10 @@ module HexaPDF
|
|
|
166
176
|
# Adds the components of compound glyphs to the subset.
|
|
167
177
|
def add_glyph_components
|
|
168
178
|
glyf = @font[:glyf]
|
|
169
|
-
@glyph_map.keys.each
|
|
179
|
+
@glyph_map.keys.each do |gid|
|
|
180
|
+
next if gid.kind_of?(Symbol)
|
|
181
|
+
glyf[gid].components&.each {|cgid| use_glyph(cgid) }
|
|
182
|
+
end
|
|
170
183
|
end
|
|
171
184
|
|
|
172
185
|
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
|
|