hexapdf 0.20.4 → 0.21.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 +27 -0
- data/README.md +5 -3
- data/Rakefile +10 -1
- data/examples/018-composer.rb +10 -10
- data/lib/hexapdf/cli/batch.rb +4 -6
- data/lib/hexapdf/cli/info.rb +5 -1
- data/lib/hexapdf/cli/inspect.rb +59 -0
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/composer.rb +147 -53
- data/lib/hexapdf/configuration.rb +7 -3
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/color_space.rb +1 -1
- data/lib/hexapdf/content/operator.rb +7 -7
- data/lib/hexapdf/content/parser.rb +3 -3
- data/lib/hexapdf/content/processor.rb +9 -9
- data/lib/hexapdf/document/signatures.rb +5 -4
- data/lib/hexapdf/document.rb +7 -0
- data/lib/hexapdf/font/true_type/font.rb +7 -7
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +3 -3
- data/lib/hexapdf/font/true_type_wrapper.rb +9 -14
- data/lib/hexapdf/font/type1/font.rb +10 -12
- data/lib/hexapdf/font/type1_wrapper.rb +1 -2
- data/lib/hexapdf/layout/box.rb +12 -9
- data/lib/hexapdf/layout/image_box.rb +1 -1
- data/lib/hexapdf/layout/style.rb +28 -8
- data/lib/hexapdf/layout/text_fragment.rb +10 -9
- data/lib/hexapdf/parser.rb +5 -0
- data/lib/hexapdf/tokenizer.rb +3 -3
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +6 -4
- data/lib/hexapdf/type/acro_form/choice_field.rb +2 -2
- data/lib/hexapdf/type/acro_form/field.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/resources.rb +4 -4
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -1
- data/lib/hexapdf/type/signature.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +3 -3
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/xref_section.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +5 -5
- data/test/hexapdf/content/test_graphics_state.rb +1 -0
- data/test/hexapdf/content/test_operator.rb +2 -2
- data/test/hexapdf/content/test_processor.rb +1 -1
- data/test/hexapdf/encryption/test_standard_security_handler.rb +23 -29
- data/test/hexapdf/filter/test_predictor.rb +16 -20
- data/test/hexapdf/font/test_type1_wrapper.rb +1 -1
- data/test/hexapdf/font/true_type/table/common.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +1 -1
- data/test/hexapdf/image_loader/test_pdf.rb +6 -8
- data/test/hexapdf/image_loader/test_png.rb +2 -2
- data/test/hexapdf/layout/test_box.rb +11 -1
- data/test/hexapdf/layout/test_style.rb +23 -0
- data/test/hexapdf/layout/test_text_fragment.rb +21 -21
- data/test/hexapdf/test_composer.rb +115 -52
- data/test/hexapdf/test_dictionary.rb +2 -2
- data/test/hexapdf/test_document.rb +11 -9
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_parser.rb +13 -7
- data/test/hexapdf/test_serializer.rb +20 -22
- data/test/hexapdf/test_stream.rb +7 -9
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
- data/test/hexapdf/type/acro_form/test_choice_field.rb +1 -1
- data/test/hexapdf/type/signature/common.rb +1 -1
- data/test/hexapdf/type/test_font_type0.rb +1 -1
- data/test/hexapdf/type/test_font_type1.rb +7 -7
- data/test/hexapdf/type/test_image.rb +13 -17
- metadata +2 -2
@@ -187,7 +187,7 @@ module HexaPDF
|
|
187
187
|
|
188
188
|
# Returns the concatenated text of the boxes.
|
189
189
|
def string
|
190
|
-
@boxes.map(&:string).join
|
190
|
+
@boxes.map(&:string).join
|
191
191
|
end
|
192
192
|
|
193
193
|
# :call-seq:
|
@@ -260,14 +260,14 @@ module HexaPDF
|
|
260
260
|
s: :close_and_stroke_path,
|
261
261
|
f: :fill_path_non_zero,
|
262
262
|
F: :fill_path_non_zero,
|
263
|
-
'f*'
|
263
|
+
'f*': :fill_path_even_odd,
|
264
264
|
B: :fill_and_stroke_path_non_zero,
|
265
|
-
'B*'
|
265
|
+
'B*': :fill_and_stroke_path_even_odd,
|
266
266
|
b: :close_fill_and_stroke_path_non_zero,
|
267
|
-
'b*'
|
267
|
+
'b*': :close_fill_and_stroke_path_even_odd,
|
268
268
|
n: :end_path,
|
269
269
|
W: :clip_path_non_zero,
|
270
|
-
'W*'
|
270
|
+
'W*': :clip_path_even_odd,
|
271
271
|
BT: :begin_text,
|
272
272
|
ET: :end_text,
|
273
273
|
Tc: :set_character_spacing,
|
@@ -280,10 +280,10 @@ module HexaPDF
|
|
280
280
|
Td: :move_text,
|
281
281
|
TD: :move_text_and_set_leading,
|
282
282
|
Tm: :set_text_matrix,
|
283
|
-
'T*'
|
283
|
+
'T*': :move_text_next_line,
|
284
284
|
Tj: :show_text,
|
285
|
-
'
|
286
|
-
'"'
|
285
|
+
"'": :move_text_next_line_and_show_text,
|
286
|
+
'"': :set_spacing_move_text_next_line_and_show_text,
|
287
287
|
TJ: :show_text_with_positioning,
|
288
288
|
d0: :set_glyph_width, # only for Type 3 fonts
|
289
289
|
d1: :set_glyph_width_and_bounding_box, # only for Type 3 fonts
|
@@ -392,7 +392,7 @@ module HexaPDF
|
|
392
392
|
data = data.each_with_object(''.b) {|obj, result| result << obj if obj.kind_of?(String) }
|
393
393
|
end
|
394
394
|
font = graphics_state.font
|
395
|
-
font.decode(data).map {|code_point| font.to_utf8(code_point) }.join
|
395
|
+
font.decode(data).map {|code_point| font.to_utf8(code_point) }.join
|
396
396
|
end
|
397
397
|
|
398
398
|
# Decodes the given text object and returns it as a CompositeBox object.
|
@@ -93,12 +93,12 @@ module HexaPDF
|
|
93
93
|
|
94
94
|
# Returns the name to be set on the /Filter key when using this signing handler.
|
95
95
|
def filter_name
|
96
|
-
:
|
96
|
+
:'Adobe.PPKLite'
|
97
97
|
end
|
98
98
|
|
99
99
|
# Returns the name to be set on the /SubFilter key when using this signing handler.
|
100
100
|
def sub_filter_name
|
101
|
-
:
|
101
|
+
:'adbe.pkcs7.detached'
|
102
102
|
end
|
103
103
|
|
104
104
|
# Sets the DocMDP permissions that should be applied to the document.
|
@@ -171,7 +171,7 @@ module HexaPDF
|
|
171
171
|
# Creates a signing handler with the given options and returns it.
|
172
172
|
#
|
173
173
|
# A signing handler name is mapped to a class via the 'signature.signing_handler'
|
174
|
-
# configuration option.
|
174
|
+
# configuration option. The default signing handler is DefaultHandler.
|
175
175
|
def handler(name: :default, **options)
|
176
176
|
handler = @document.config.constantize('signature.signing_handler', name) do
|
177
177
|
raise HexaPDF::Error, "No signing handler named '#{name}' is available"
|
@@ -183,7 +183,8 @@ module HexaPDF
|
|
183
183
|
#
|
184
184
|
# This method will add a new signature to the document and write the updated document to the
|
185
185
|
# given file or IO stream. Afterwards the document can't be modified anymore and still retain
|
186
|
-
# a correct digital signature
|
186
|
+
# a correct digital signature. To modify the signed document (e.g. for adding another
|
187
|
+
# signature) create a new document based on the given file or IO stream instead.
|
187
188
|
#
|
188
189
|
# +signature+::
|
189
190
|
# Can either be a signature object (determined via the /Type key), a signature field or
|
data/lib/hexapdf/document.rb
CHANGED
@@ -638,6 +638,9 @@ module HexaPDF
|
|
638
638
|
# of the keyword arguments (see HexaPDF::Document::Signatures#handler for details).
|
639
639
|
#
|
640
640
|
# If not changed, the default signing handler is HexaPDF::Document::Signatures::DefaultHandler.
|
641
|
+
#
|
642
|
+
# *Note*: Once signing is done the document cannot be changed anymore since it was written. If a
|
643
|
+
# document needs to be signed multiple times, it needs to be loaded again after writing.
|
641
644
|
def sign(file_or_io, handler: :default, signature: nil, write_options: {}, **handler_options)
|
642
645
|
handler = signatures.handler(name: handler, **handler_options)
|
643
646
|
signatures.add(file_or_io, handler, signature: signature, write_options: write_options)
|
@@ -714,6 +717,10 @@ module HexaPDF
|
|
714
717
|
end
|
715
718
|
end
|
716
719
|
|
720
|
+
def inspect #:nodoc:
|
721
|
+
"<#{self.class.name}:#{object_id}>"
|
722
|
+
end
|
723
|
+
|
717
724
|
end
|
718
725
|
|
719
726
|
end
|
@@ -123,7 +123,7 @@ module HexaPDF
|
|
123
123
|
|
124
124
|
# Returns the weight of the font.
|
125
125
|
def weight
|
126
|
-
self[:
|
126
|
+
self[:'OS/2'].weight_class || 0
|
127
127
|
end
|
128
128
|
|
129
129
|
# Returns the bounding of the font.
|
@@ -133,22 +133,22 @@ module HexaPDF
|
|
133
133
|
|
134
134
|
# Returns the cap height of the font.
|
135
135
|
def cap_height
|
136
|
-
self[:
|
136
|
+
self[:'OS/2'].cap_height
|
137
137
|
end
|
138
138
|
|
139
139
|
# Returns the x-height of the font.
|
140
140
|
def x_height
|
141
|
-
self[:
|
141
|
+
self[:'OS/2'].x_height
|
142
142
|
end
|
143
143
|
|
144
144
|
# Returns the ascender of the font.
|
145
145
|
def ascender
|
146
|
-
self[:
|
146
|
+
self[:'OS/2'].typo_ascender || self[:hhea].ascent
|
147
147
|
end
|
148
148
|
|
149
149
|
# Returns the descender of the font.
|
150
150
|
def descender
|
151
|
-
self[:
|
151
|
+
self[:'OS/2'].typo_descender || self[:hhea].descent
|
152
152
|
end
|
153
153
|
|
154
154
|
# Returns the italic angle of the font, in degrees counter-clockwise from the vertical.
|
@@ -176,12 +176,12 @@ module HexaPDF
|
|
176
176
|
|
177
177
|
# Returns the distance from the baseline to the top of the strikeout line.
|
178
178
|
def strikeout_position
|
179
|
-
self[:
|
179
|
+
self[:'OS/2'].strikeout_position
|
180
180
|
end
|
181
181
|
|
182
182
|
# Returns the stroke width for the strikeout line.
|
183
183
|
def strikeout_thickness
|
184
|
-
self[:
|
184
|
+
self[:'OS/2'].strikeout_size
|
185
185
|
end
|
186
186
|
|
187
187
|
# Returns th glyph ID of the missing glyph, i.e. 0.
|
@@ -55,7 +55,7 @@ module HexaPDF
|
|
55
55
|
'hmtx' => font[:hmtx].raw_data,
|
56
56
|
}
|
57
57
|
tables['cmap'] = font[:cmap].raw_data if font[:cmap]
|
58
|
-
tables['cvt '] = font[:
|
58
|
+
tables['cvt '] = font[:'cvt '].raw_data if font[:'cvt ']
|
59
59
|
tables['fpgm'] = font[:fpgm].raw_data if font[:fpgm]
|
60
60
|
tables['prep'] = font[:prep].raw_data if font[:prep]
|
61
61
|
Builder.build(tables)
|
@@ -99,7 +99,7 @@ module HexaPDF
|
|
99
99
|
'loca' => loca,
|
100
100
|
'hmtx' => hmtx,
|
101
101
|
}
|
102
|
-
tables['cvt '] = @font[:
|
102
|
+
tables['cvt '] = @font[:'cvt '].raw_data if @font[:'cvt ']
|
103
103
|
tables['fpgm'] = @font[:fpgm].raw_data if @font[:fpgm]
|
104
104
|
tables['prep'] = @font[:prep].raw_data if @font[:prep]
|
105
105
|
|
@@ -282,12 +282,12 @@ module HexaPDF
|
|
282
282
|
def self.mapper(end_codes, start_codes, id_deltas, id_range_offsets, glyph_indexes)
|
283
283
|
compute_glyph_id = lambda do |index, code|
|
284
284
|
offset = id_range_offsets[index]
|
285
|
-
if offset
|
285
|
+
if offset == 0
|
286
|
+
glyph_id = (code + id_deltas[index]) % 65536
|
287
|
+
else
|
286
288
|
glyph_id = glyph_indexes[offset - end_codes.length + (code - start_codes[index])]
|
287
289
|
glyph_id ||= 0 # Handle invalid subtable entries
|
288
290
|
glyph_id = (glyph_id + id_deltas[index]) % 65536 if glyph_id != 0
|
289
|
-
else
|
290
|
-
glyph_id = (code + id_deltas[index]) % 65536
|
291
291
|
end
|
292
292
|
glyph_id
|
293
293
|
end
|
@@ -126,7 +126,6 @@ module HexaPDF
|
|
126
126
|
# If +subset+ is true, the font is subset.
|
127
127
|
def initialize(document, font, pdf_object: nil, subset: true)
|
128
128
|
@wrapped_font = font
|
129
|
-
@missing_glyph_callable = document.config['font.on_missing_glyph']
|
130
129
|
|
131
130
|
@subsetter = (subset ? HexaPDF::Font::TrueType::Subsetter.new(font) : nil)
|
132
131
|
|
@@ -165,12 +164,10 @@ module HexaPDF
|
|
165
164
|
# Note: Although this method is public, it should normally not be used by application code!
|
166
165
|
def glyph(id, str = nil)
|
167
166
|
@id_to_glyph[id] ||=
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
@missing_glyph_callable.call("\u{FFFD}", font_type, @wrapped_font)
|
173
|
-
end
|
167
|
+
if id >= 0 && id < @wrapped_font[:maxp].num_glyphs
|
168
|
+
Glyph.new(@wrapped_font, id, str || (+'' << (@cmap.gid_to_code(id) || 0xFFFD)))
|
169
|
+
else
|
170
|
+
@pdf_object.document.config['font.on_missing_glyph'].call("\u{FFFD}", self)
|
174
171
|
end
|
175
172
|
end
|
176
173
|
|
@@ -178,12 +175,10 @@ module HexaPDF
|
|
178
175
|
def decode_utf8(str)
|
179
176
|
str.codepoints.map! do |c|
|
180
177
|
@codepoint_to_glyph[c] ||=
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
@missing_glyph_callable.call(+'' << c, font_type, @wrapped_font)
|
186
|
-
end
|
178
|
+
if (gid = @cmap[c])
|
179
|
+
glyph(gid, +'' << c)
|
180
|
+
else
|
181
|
+
@pdf_object.document.config['font.on_missing_glyph'].call(+'' << c, self)
|
187
182
|
end
|
188
183
|
end
|
189
184
|
end
|
@@ -250,7 +245,7 @@ module HexaPDF
|
|
250
245
|
Supplement: 0},
|
251
246
|
CIDToGIDMap: :Identity})
|
252
247
|
dict = document.add({Type: :Font, Subtype: :Type0, BaseFont: cid_font[:BaseFont],
|
253
|
-
Encoding: :
|
248
|
+
Encoding: :'Identity-H', DescendantFonts: [cid_font]})
|
254
249
|
dict.font_wrapper = self
|
255
250
|
|
256
251
|
document.register_listener(:complete_objects) do
|
@@ -89,19 +89,17 @@ module HexaPDF
|
|
89
89
|
# Returns the built-in encoding of the font.
|
90
90
|
def encoding
|
91
91
|
@encoding ||=
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
encoding.code_to_name[key] = char_metric.name
|
102
|
-
end
|
103
|
-
encoding
|
92
|
+
if @metrics.encoding_scheme == 'AdobeStandardEncoding'
|
93
|
+
Encoding.for_name(:StandardEncoding)
|
94
|
+
elsif font_name == 'ZapfDingbats' || font_name == 'Symbol'
|
95
|
+
Encoding.for_name("#{font_name}Encoding".to_sym)
|
96
|
+
else
|
97
|
+
encoding = Encoding::Base.new
|
98
|
+
@metrics.character_metrics.each do |key, char_metric|
|
99
|
+
next unless key.kind_of?(Integer) && key >= 0
|
100
|
+
encoding.code_to_name[key] = char_metric.name
|
104
101
|
end
|
102
|
+
encoding
|
105
103
|
end
|
106
104
|
end
|
107
105
|
|
@@ -119,7 +119,6 @@ module HexaPDF
|
|
119
119
|
def initialize(document, font, pdf_object: nil, custom_encoding: false)
|
120
120
|
@wrapped_font = font
|
121
121
|
@pdf_object = pdf_object || create_pdf_object(document)
|
122
|
-
@missing_glyph_callable = document.config['font.on_missing_glyph']
|
123
122
|
|
124
123
|
if pdf_object
|
125
124
|
@encoding = pdf_object.encoding
|
@@ -160,7 +159,7 @@ module HexaPDF
|
|
160
159
|
if @wrapped_font.metrics.character_metrics.key?(name)
|
161
160
|
Glyph.new(@wrapped_font, name, str)
|
162
161
|
else
|
163
|
-
@
|
162
|
+
@pdf_object.document.config['font.on_missing_glyph'].call(str, self)
|
164
163
|
end
|
165
164
|
end
|
166
165
|
end
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -51,13 +51,16 @@ module HexaPDF
|
|
51
51
|
# * If width or height is set to zero, they are determined automatically during layouting.
|
52
52
|
class Box
|
53
53
|
|
54
|
-
# Creates a new Box object, using the provided block as drawing block (see ::new).
|
55
|
-
# additional keyword arguments are used for creating the box's Style object.
|
54
|
+
# Creates a new Box object, using the provided block as drawing block (see ::new).
|
56
55
|
#
|
57
56
|
# If +content_box+ is +true+, the width and height are taken to mean the content width and
|
58
57
|
# height and the style's padding and border are removed from them appropriately.
|
59
|
-
|
60
|
-
|
58
|
+
#
|
59
|
+
# The +style+ argument defines the Style object (see Style::create for details) for the box.
|
60
|
+
# Any additional keyword arguments have to be style properties and are applied to the style
|
61
|
+
# object.
|
62
|
+
def self.create(width: 0, height: 0, content_box: false, style: nil, **style_properties, &block)
|
63
|
+
style = Style.create(style).update(**style_properties)
|
61
64
|
if content_box
|
62
65
|
width += style.padding.left + style.padding.right +
|
63
66
|
style.border.width.left + style.border.width.right
|
@@ -81,12 +84,12 @@ module HexaPDF
|
|
81
84
|
# * Style#background_alpha
|
82
85
|
# * Style#padding
|
83
86
|
# * Style#border
|
84
|
-
# * Style#
|
85
|
-
# * Style#
|
87
|
+
# * Style#overlays
|
88
|
+
# * Style#underlays
|
86
89
|
attr_reader :style
|
87
90
|
|
88
91
|
# :call-seq:
|
89
|
-
# Box.new(width: 0, height: 0, style:
|
92
|
+
# Box.new(width: 0, height: 0, style: nil) {|canv, box| block} -> box
|
90
93
|
#
|
91
94
|
# Creates a new Box object with the given width and height that uses the provided block when
|
92
95
|
# it is asked to draw itself on a canvas (see #draw).
|
@@ -94,10 +97,10 @@ module HexaPDF
|
|
94
97
|
# Since the final location of the box is not known beforehand, the drawing operations inside
|
95
98
|
# the block should draw inside the rectangle (0, 0, content_width, content_height) - note that
|
96
99
|
# the width and height of the box may not be known beforehand.
|
97
|
-
def initialize(width: 0, height: 0, style:
|
100
|
+
def initialize(width: 0, height: 0, style: nil, &block)
|
98
101
|
@width = @initial_width = width
|
99
102
|
@height = @initial_height = height
|
100
|
-
@style =
|
103
|
+
@style = Style.create(style)
|
101
104
|
@draw_block = block
|
102
105
|
end
|
103
106
|
|
@@ -56,7 +56,7 @@ module HexaPDF
|
|
56
56
|
# Creates a new Image box object for the given +image+ argument which needs to be an image
|
57
57
|
# object (e.g. returned by HexaPDF::Document::Images#add).
|
58
58
|
def initialize(image, **kwargs)
|
59
|
-
super(**kwargs
|
59
|
+
super(**kwargs)
|
60
60
|
@image = image
|
61
61
|
end
|
62
62
|
|
data/lib/hexapdf/layout/style.rb
CHANGED
@@ -518,6 +518,27 @@ module HexaPDF
|
|
518
518
|
|
519
519
|
UNSET = ::Object.new # :nodoc:
|
520
520
|
|
521
|
+
# :call-seq:
|
522
|
+
# Style.create(style) -> style
|
523
|
+
# Style.create(properties_hash) -> style
|
524
|
+
#
|
525
|
+
# Creates a Style object based on the +style+ argument and returns it:
|
526
|
+
#
|
527
|
+
# * If +style+ is already a Style object, it is just returned.
|
528
|
+
#
|
529
|
+
# * If +style+ is a hash, a new Style object with the style properties specified by the hash
|
530
|
+
# * is created.
|
531
|
+
#
|
532
|
+
# * If +style+ is +nil+, a new Style object with only default values is created.
|
533
|
+
def self.create(style)
|
534
|
+
case style
|
535
|
+
when self then style
|
536
|
+
when Hash then new(**style)
|
537
|
+
when nil then new
|
538
|
+
else raise ArgumentError, "Invalid argument class #{style.class}"
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
521
542
|
# Creates a new Style object.
|
522
543
|
#
|
523
544
|
# The +properties+ hash may be used to set the initial values of properties by using keys
|
@@ -945,7 +966,8 @@ module HexaPDF
|
|
945
966
|
[:valign, :top, {valid_values: [:top, :center, :bottom]}],
|
946
967
|
[:text_indent, 0],
|
947
968
|
[:line_spacing, "LineSpacing.new(type: :single)",
|
948
|
-
{setter: "LineSpacing.new(**(value.kind_of?(Symbol)
|
969
|
+
{setter: "LineSpacing.new(**(value.kind_of?(Symbol) || value.kind_of?(Numeric) ? " \
|
970
|
+
"{type: value, value: extra_arg} : value))",
|
949
971
|
extra_args: ", extra_arg = nil"}],
|
950
972
|
[:last_line_gap, false, {valid_values: [true, false]}],
|
951
973
|
[:background_color, nil],
|
@@ -1115,13 +1137,11 @@ module HexaPDF
|
|
1115
1137
|
# inside a TextFragment.
|
1116
1138
|
def scaled_item_width(item)
|
1117
1139
|
@scaled_item_widths[item] ||=
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
item.
|
1123
|
-
(item.apply_word_spacing? ? scaled_word_spacing : 0)
|
1124
|
-
end
|
1140
|
+
if item.kind_of?(Numeric)
|
1141
|
+
-item * scaled_font_size
|
1142
|
+
else
|
1143
|
+
item.width * scaled_font_size + scaled_character_spacing +
|
1144
|
+
(item.apply_word_spacing? ? scaled_word_spacing : 0)
|
1125
1145
|
end
|
1126
1146
|
end
|
1127
1147
|
|
@@ -61,11 +61,11 @@ module HexaPDF
|
|
61
61
|
|
62
62
|
# Creates a new TextFragment object for the given text, shapes it and returns it.
|
63
63
|
#
|
64
|
-
# The needed style of the text fragment
|
65
|
-
#
|
66
|
-
#
|
67
|
-
def self.create(text, style
|
68
|
-
style =
|
64
|
+
# The needed style of the text fragment is specified by the +style+ argument (see
|
65
|
+
# Style::create for details). Note that the resulting style object needs at least the font
|
66
|
+
# set.
|
67
|
+
def self.create(text, style)
|
68
|
+
style = Style.create(style)
|
69
69
|
fragment = new(style.font.decode_utf8(text), style)
|
70
70
|
TextShaper.new.shape_text(fragment)
|
71
71
|
end
|
@@ -97,16 +97,17 @@ module HexaPDF
|
|
97
97
|
# * Style#stroke_join_style
|
98
98
|
# * Style#stroke_miter_limit
|
99
99
|
# * Style#stroke_dash_pattern
|
100
|
-
# * Style#
|
101
|
-
# * Style#
|
100
|
+
# * Style#underlays
|
101
|
+
# * Style#overlays
|
102
102
|
attr_reader :style
|
103
103
|
|
104
104
|
# Creates a new TextFragment object with the given items and style.
|
105
105
|
#
|
106
|
-
# The argument +style+ can either be a Style object or a hash of style
|
106
|
+
# The argument +style+ can either be a Style object or a hash of style properties, see
|
107
|
+
# Style::create for details.
|
107
108
|
def initialize(items, style)
|
108
109
|
@items = items
|
109
|
-
@style =
|
110
|
+
@style = Style.create(style)
|
110
111
|
end
|
111
112
|
|
112
113
|
# The precision used to determine whether two floats represent the same value.
|
data/lib/hexapdf/parser.rb
CHANGED
@@ -71,6 +71,11 @@ module HexaPDF
|
|
71
71
|
@contains_xref_streams
|
72
72
|
end
|
73
73
|
|
74
|
+
# Returns +true+ if the PDF file was damaged and could be reconstructed.
|
75
|
+
def reconstructed?
|
76
|
+
!@reconstructed_revision.nil?
|
77
|
+
end
|
78
|
+
|
74
79
|
# Loads the indirect (potentially compressed) object specified by the given cross-reference
|
75
80
|
# entry.
|
76
81
|
#
|
data/lib/hexapdf/tokenizer.rb
CHANGED
@@ -125,11 +125,11 @@ module HexaPDF
|
|
125
125
|
elsif byte == 40 # (
|
126
126
|
parse_literal_string
|
127
127
|
elsif byte == 60 # <
|
128
|
-
if @ss.string.getbyte(@ss.pos + 1)
|
129
|
-
parse_hex_string
|
130
|
-
else
|
128
|
+
if @ss.string.getbyte(@ss.pos + 1) == 60
|
131
129
|
@ss.pos += 2
|
132
130
|
TOKEN_DICT_START
|
131
|
+
else
|
132
|
+
parse_hex_string
|
133
133
|
end
|
134
134
|
elsif byte == 62 # >
|
135
135
|
unless @ss.string.getbyte(@ss.pos + 1) == 62
|
@@ -260,9 +260,10 @@ module HexaPDF
|
|
260
260
|
canvas.save_graphics_state do
|
261
261
|
canvas.rectangle(padding, padding, rect.width - 2 * padding,
|
262
262
|
rect.height - 2 * padding).clip_path.end_path
|
263
|
-
|
263
|
+
case @field.concrete_field_type
|
264
|
+
when :multiline_text_field
|
264
265
|
draw_multiline_text(canvas, rect, style, padding)
|
265
|
-
|
266
|
+
when :list_box
|
266
267
|
draw_list_box(canvas, rect, style, padding)
|
267
268
|
else
|
268
269
|
draw_single_line_text(canvas, rect, style, padding)
|
@@ -503,9 +504,10 @@ module HexaPDF
|
|
503
504
|
# appearance string, the annotation rectangle and the border style.
|
504
505
|
def calculate_font_size(font, font_size, rect, border_style)
|
505
506
|
if font_size == 0
|
506
|
-
|
507
|
+
case @field.concrete_field_type
|
508
|
+
when :multiline_text_field
|
507
509
|
0 # Handled by multiline drawing code
|
508
|
-
|
510
|
+
when :list_box
|
509
511
|
12 # Seems to be Adobe's default
|
510
512
|
else
|
511
513
|
unit_font_size = (font.wrapped_font.bounding_box[3] - font.wrapped_font.bounding_box[1]) *
|
@@ -128,8 +128,8 @@ module HexaPDF
|
|
128
128
|
items = option_items
|
129
129
|
array_value = [value].flatten
|
130
130
|
all_included = array_value.all? {|v| items.include?(v) }
|
131
|
-
self[:V] = if
|
132
|
-
(flagged?(:edit) || all_included)
|
131
|
+
self[:V] = if combo_box? && value.kind_of?(String) &&
|
132
|
+
(flagged?(:edit) || all_included)
|
133
133
|
delete(:I)
|
134
134
|
value
|
135
135
|
elsif list_box? && all_included &&
|
@@ -258,7 +258,7 @@ module HexaPDF
|
|
258
258
|
#
|
259
259
|
# See: HexaPDF::Type::Annotations::Widget
|
260
260
|
def each_widget(direct_only: false, &block) # :yields: widget
|
261
|
-
return to_enum(__method__) unless block_given?
|
261
|
+
return to_enum(__method__, direct_only: direct_only) unless block_given?
|
262
262
|
|
263
263
|
if embedded_widget?
|
264
264
|
yield(document.wrap(self))
|
@@ -336,7 +336,7 @@ module HexaPDF
|
|
336
336
|
|
337
337
|
if embedded_widget?
|
338
338
|
WIDGET_FIELDS.each {|key| delete(key) }
|
339
|
-
document.revisions.each {|revision| break if revision.update(self)}
|
339
|
+
document.revisions.each {|revision| break if revision.update(self) }
|
340
340
|
else
|
341
341
|
self[:Kids].delete_at(widget_index)
|
342
342
|
document.delete(widget)
|
@@ -141,7 +141,7 @@ module HexaPDF
|
|
141
141
|
registry = system_info[:Registry]
|
142
142
|
ordering = system_info[:Ordering]
|
143
143
|
if (encoding.kind_of?(Symbol) && HexaPDF::Font::CMap.predefined?(encoding.to_s) &&
|
144
|
-
encoding != :
|
144
|
+
encoding != :'Identity-H' && encoding != :'Identity-V') ||
|
145
145
|
(registry == "Adobe" && ['GB1', 'CNS1', 'Japan1', 'Korea1'].include?(ordering))
|
146
146
|
HexaPDF::Font::CMap.for_name("#{registry}-#{ordering}-UCS2")
|
147
147
|
end
|
@@ -59,7 +59,7 @@ module HexaPDF
|
|
59
59
|
def bounding_box
|
60
60
|
matrix = self[:FontMatrix]
|
61
61
|
bbox = self[:FontBBox].value
|
62
|
-
if matrix[3] < 0
|
62
|
+
if matrix[3] < 0 # Some writers invert the y-axis
|
63
63
|
bbox = bbox.dup
|
64
64
|
bbox[1], bbox[3] = -bbox[3], -bbox[1]
|
65
65
|
end
|
@@ -218,10 +218,7 @@ module HexaPDF
|
|
218
218
|
def perform_validation
|
219
219
|
super
|
220
220
|
val = self[:ProcSet]
|
221
|
-
if
|
222
|
-
yield("No procedure set specified", true)
|
223
|
-
self[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
|
224
|
-
else
|
221
|
+
if val
|
225
222
|
if val.kind_of?(Symbol)
|
226
223
|
yield("Procedure set is a single value instead of an Array", true)
|
227
224
|
val = value[:ProcSet] = [val]
|
@@ -235,6 +232,9 @@ module HexaPDF
|
|
235
232
|
true
|
236
233
|
end
|
237
234
|
end
|
235
|
+
else
|
236
|
+
yield("No procedure set specified", true)
|
237
|
+
self[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
|
238
238
|
end
|
239
239
|
end
|
240
240
|
|
@@ -100,7 +100,7 @@ module HexaPDF
|
|
100
100
|
end
|
101
101
|
|
102
102
|
key_usage = signer_certificate.extensions.find {|ext| ext.oid == 'keyUsage' }
|
103
|
-
unless key_usage.value.split(', ').include?("Digital Signature")
|
103
|
+
unless key_usage && key_usage.value.split(', ').include?("Digital Signature")
|
104
104
|
result.log(:error, "Certificate key usage is missing 'Digital Signature'")
|
105
105
|
end
|
106
106
|
|
@@ -75,7 +75,7 @@ module HexaPDF
|
|
75
75
|
|
76
76
|
# For DocMDP, also used by UR
|
77
77
|
define_field :P, type: [Integer, Boolean]
|
78
|
-
define_field :V, type: Symbol, allowed_values: [:
|
78
|
+
define_field :V, type: Symbol, allowed_values: [:'1.2', :'2.2']
|
79
79
|
|
80
80
|
# For UR
|
81
81
|
define_field :Document, type: PDFArray
|
data/lib/hexapdf/type/trailer.rb
CHANGED
@@ -87,10 +87,10 @@ module HexaPDF
|
|
87
87
|
# Updates the second part of the /ID field (the first part should always be the same for a
|
88
88
|
# PDF file, the second part should change with each write).
|
89
89
|
def update_id
|
90
|
-
if
|
91
|
-
set_random_id
|
92
|
-
else
|
90
|
+
if self[:ID].kind_of?(PDFArray)
|
93
91
|
value[:ID][1] = Digest::MD5.digest(rand.to_s)
|
92
|
+
else
|
93
|
+
set_random_id
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
data/lib/hexapdf/version.rb
CHANGED
data/lib/hexapdf/xref_section.rb
CHANGED