hexapdf 0.12.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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
|