hexapdf 0.12.0 → 0.14.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 +126 -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 +4 -4
- 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/encoding/base.rb +8 -0
- data/lib/hexapdf/font/encoding/difference_encoding.rb +6 -0
- 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/type1_wrapper.rb +1 -1
- data/lib/hexapdf/image_loader/png.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 +87 -3
- data/lib/hexapdf/pdf_array.rb +11 -4
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +1 -1
- 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/encoding/test_base.rb +10 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +4 -3
- 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 +3 -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 +159 -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)
|
|
@@ -163,7 +163,7 @@ module HexaPDF
|
|
|
163
163
|
value[name] = field.default
|
|
164
164
|
end
|
|
165
165
|
value[name] = data = document.deref(data) if data.kind_of?(HexaPDF::Reference)
|
|
166
|
-
if data.
|
|
166
|
+
if data.instance_of?(HexaPDF::Object) || (data.kind_of?(HexaPDF::Object) && data.value.nil?)
|
|
167
167
|
data = data.value
|
|
168
168
|
end
|
|
169
169
|
if (result = field&.convert(data, document))
|
|
@@ -182,7 +182,7 @@ module HexaPDF
|
|
|
182
182
|
raise ArgumentError, "Only Symbol (Name) keys are allowed to be used in PDF dictionaries"
|
|
183
183
|
end
|
|
184
184
|
|
|
185
|
-
if value[name].
|
|
185
|
+
if value[name].instance_of?(HexaPDF::Object) && !data.kind_of?(HexaPDF::Object) &&
|
|
186
186
|
!data.kind_of?(HexaPDF::Reference)
|
|
187
187
|
value[name].value = data
|
|
188
188
|
else
|
|
@@ -273,7 +273,7 @@ module HexaPDF
|
|
|
273
273
|
# Check that required fields are set
|
|
274
274
|
if field.required? && obj.nil?
|
|
275
275
|
yield("Required field #{name} is not set", field.default?)
|
|
276
|
-
self[name] = obj = field.default
|
|
276
|
+
self[name] = obj = field.default if field.default?
|
|
277
277
|
end
|
|
278
278
|
|
|
279
279
|
# Check if the document version is set high enough
|
|
@@ -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.
|
|
@@ -73,6 +73,14 @@ module HexaPDF
|
|
|
73
73
|
@unicode_cache[code] ||= GlyphList.name_to_unicode(name(code))
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
# Returns the code for the given glyph name (a Symbol) or +nil+ if there is no code for the
|
|
77
|
+
# given glyph name.
|
|
78
|
+
#
|
|
79
|
+
# If multiple codes reference the given glyph name, the first found is always returned.
|
|
80
|
+
def code(name)
|
|
81
|
+
@code_to_name.key(name)
|
|
82
|
+
end
|
|
83
|
+
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
end
|
|
@@ -60,6 +60,12 @@ module HexaPDF
|
|
|
60
60
|
code_to_name[code] || base_encoding.name(code)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# Returns the code for the given glyph name, either from this object, if a code references
|
|
64
|
+
# the name, or from the base encoding.
|
|
65
|
+
def code(name)
|
|
66
|
+
code_to_name.key(name) || base_encoding.code(name)
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
end
|
|
64
70
|
|
|
65
71
|
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
|
|
|
@@ -192,7 +192,7 @@ module HexaPDF
|
|
|
192
192
|
if glyph.name == @wrapped_font.missing_glyph_id
|
|
193
193
|
raise HexaPDF::Error, "Glyph for #{glyph.str.inspect} missing"
|
|
194
194
|
end
|
|
195
|
-
code = @encoding.
|
|
195
|
+
code = @encoding.code(glyph.name)
|
|
196
196
|
if code
|
|
197
197
|
code.chr.freeze
|
|
198
198
|
elsif @max_code < 255
|
|
@@ -162,9 +162,10 @@ module HexaPDF
|
|
|
162
162
|
io.seek(length, IO::SEEK_CUR)
|
|
163
163
|
end
|
|
164
164
|
when 'tRNS' # PNG s11.3.2
|
|
165
|
-
|
|
165
|
+
case @color_type
|
|
166
|
+
when INDEXED
|
|
166
167
|
trns = io.read(length).unpack('C*')
|
|
167
|
-
|
|
168
|
+
when TRUECOLOR, GREYSCALE
|
|
168
169
|
dict[:Mask] = io.read(length).unpack('n*').map {|val| [val, val] }.flatten
|
|
169
170
|
else
|
|
170
171
|
io.seek(length, IO::SEEK_CUR)
|
data/lib/hexapdf/layout/line.rb
CHANGED
|
@@ -198,7 +198,7 @@ module HexaPDF
|
|
|
198
198
|
# Note: The cache is not cleared!
|
|
199
199
|
def add(item)
|
|
200
200
|
last = @items.last
|
|
201
|
-
if last.
|
|
201
|
+
if last.instance_of?(item.class) && item.kind_of?(TextFragment) && last.style == item.style
|
|
202
202
|
if last.items.frozen?
|
|
203
203
|
@items[-1] = last = last.dup
|
|
204
204
|
last.items = last.items.dup
|
data/lib/hexapdf/layout/style.rb
CHANGED
|
@@ -524,7 +524,7 @@ module HexaPDF
|
|
|
524
524
|
# Style.new(font_size: 15, align: :center, valign: center)
|
|
525
525
|
def initialize(**properties)
|
|
526
526
|
update(**properties)
|
|
527
|
-
@scaled_item_widths = {}
|
|
527
|
+
@scaled_item_widths = {}.compare_by_identity
|
|
528
528
|
end
|
|
529
529
|
|
|
530
530
|
# Duplicates the complex properties that can be modified, as well as the cache.
|
|
@@ -883,41 +883,41 @@ module HexaPDF
|
|
|
883
883
|
[:text_rise, 0],
|
|
884
884
|
[:font_features, {}],
|
|
885
885
|
[:text_rendering_mode, "Content::TextRenderingMode::FILL",
|
|
886
|
-
setter: "Content::TextRenderingMode.normalize(value)"],
|
|
886
|
+
{setter: "Content::TextRenderingMode.normalize(value)"}],
|
|
887
887
|
[:subscript, false,
|
|
888
|
-
setter: "value; superscript(false) if superscript",
|
|
889
|
-
|
|
888
|
+
{setter: "value; superscript(false) if superscript",
|
|
889
|
+
valid_values: [true, false]}],
|
|
890
890
|
[:superscript, false,
|
|
891
|
-
setter: "value; subscript(false) if subscript",
|
|
892
|
-
|
|
893
|
-
[:underline, false, valid_values: [true, false]],
|
|
894
|
-
[:strikeout, false, valid_values: [true, false]],
|
|
891
|
+
{setter: "value; subscript(false) if subscript",
|
|
892
|
+
valid_values: [true, false]}],
|
|
893
|
+
[:underline, false, {valid_values: [true, false]}],
|
|
894
|
+
[:strikeout, false, {valid_values: [true, false]}],
|
|
895
895
|
[:fill_color, "default_color"],
|
|
896
896
|
[:fill_alpha, 1],
|
|
897
897
|
[:stroke_color, "default_color"],
|
|
898
898
|
[:stroke_alpha, 1],
|
|
899
899
|
[:stroke_width, 1],
|
|
900
900
|
[:stroke_cap_style, "Content::LineCapStyle::BUTT_CAP",
|
|
901
|
-
setter: "Content::LineCapStyle.normalize(value)"],
|
|
901
|
+
{setter: "Content::LineCapStyle.normalize(value)"}],
|
|
902
902
|
[:stroke_join_style, "Content::LineJoinStyle::MITER_JOIN",
|
|
903
|
-
setter: "Content::LineJoinStyle.normalize(value)"],
|
|
903
|
+
{setter: "Content::LineJoinStyle.normalize(value)"}],
|
|
904
904
|
[:stroke_miter_limit, 10.0],
|
|
905
905
|
[:stroke_dash_pattern, "Content::LineDashPattern.new",
|
|
906
|
-
setter: "Content::LineDashPattern.normalize(value, phase)", extra_args: ", phase = 0"],
|
|
907
|
-
[:align, :left, valid_values: [:left, :center, :right, :justify]],
|
|
908
|
-
[:valign, :top, valid_values: [:top, :center, :bottom]],
|
|
906
|
+
{setter: "Content::LineDashPattern.normalize(value, phase)", extra_args: ", phase = 0"}],
|
|
907
|
+
[:align, :left, {valid_values: [:left, :center, :right, :justify]}],
|
|
908
|
+
[:valign, :top, {valid_values: [:top, :center, :bottom]}],
|
|
909
909
|
[:text_indent, 0],
|
|
910
910
|
[:line_spacing, "LineSpacing.new(type: :single)",
|
|
911
|
-
setter: "LineSpacing.new(**(value.kind_of?(Symbol) ? {type: value, value: extra_arg} : value))",
|
|
912
|
-
|
|
913
|
-
[:last_line_gap, false, valid_values: [true, false]],
|
|
911
|
+
{setter: "LineSpacing.new(**(value.kind_of?(Symbol) ? {type: value, value: extra_arg} : value))",
|
|
912
|
+
extra_args: ", extra_arg = nil"}],
|
|
913
|
+
[:last_line_gap, false, {valid_values: [true, false]}],
|
|
914
914
|
[:background_color, nil],
|
|
915
|
-
[:padding, "Quad.new(0)", setter: "Quad.new(value)"],
|
|
916
|
-
[:margin, "Quad.new(0)", setter: "Quad.new(value)"],
|
|
917
|
-
[:border, "Border.new", setter: "Border.new(**value)"],
|
|
918
|
-
[:overlays, "Layers.new", setter: "Layers.new(value)"],
|
|
919
|
-
[:underlays, "Layers.new", setter: "Layers.new(value)"],
|
|
920
|
-
[:position, :default, valid_values: [:default, :float, :flow, :absolute]],
|
|
915
|
+
[:padding, "Quad.new(0)", {setter: "Quad.new(value)"}],
|
|
916
|
+
[:margin, "Quad.new(0)", {setter: "Quad.new(value)"}],
|
|
917
|
+
[:border, "Border.new", {setter: "Border.new(**value)"}],
|
|
918
|
+
[:overlays, "Layers.new", {setter: "Layers.new(value)"}],
|
|
919
|
+
[:underlays, "Layers.new", {setter: "Layers.new(value)"}],
|
|
920
|
+
[:position, :default, {valid_values: [:default, :float, :flow, :absolute]}],
|
|
921
921
|
[:position_hint, nil],
|
|
922
922
|
].each do |name, default, options = {}|
|
|
923
923
|
default = default.inspect unless default.kind_of?(String)
|
|
@@ -1075,7 +1075,7 @@ module HexaPDF
|
|
|
1075
1075
|
# The item may be a (singleton) glyph object or an integer/float, i.e. items that can appear
|
|
1076
1076
|
# inside a TextFragment.
|
|
1077
1077
|
def scaled_item_width(item)
|
|
1078
|
-
@scaled_item_widths[item
|
|
1078
|
+
@scaled_item_widths[item] ||=
|
|
1079
1079
|
begin
|
|
1080
1080
|
if item.kind_of?(Numeric)
|
|
1081
1081
|
-item * scaled_font_size
|
|
@@ -388,7 +388,7 @@ module HexaPDF
|
|
|
388
388
|
end
|
|
389
389
|
when :penalty
|
|
390
390
|
if item.penalty <= -Penalty::INFINITY
|
|
391
|
-
add_box_item(item.item) if item.
|
|
391
|
+
add_box_item(item.item) if item.width > 0
|
|
392
392
|
break unless yield(create_unjustified_line, item)
|
|
393
393
|
reset_after_line_break(index + 1)
|
|
394
394
|
elsif item.penalty >= Penalty::INFINITY
|
|
@@ -458,7 +458,7 @@ module HexaPDF
|
|
|
458
458
|
end
|
|
459
459
|
when :penalty
|
|
460
460
|
if item.penalty <= -Penalty::INFINITY
|
|
461
|
-
add_box_item(item.item) if item.
|
|
461
|
+
add_box_item(item.item) if item.width > 0
|
|
462
462
|
break unless (action = yield(create_unjustified_line, item))
|
|
463
463
|
reset_after_line_break_variable_width(index + 1, true, action)
|
|
464
464
|
elsif item.penalty >= Penalty::INFINITY
|