hexapdf 0.7.0 → 0.8.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 +39 -1
- data/CONTRIBUTERS +1 -1
- data/LICENSE +3 -0
- data/README.md +2 -1
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/examples/{hello_world.rb → 001-hello_world.rb} +0 -0
- data/examples/{graphics.rb → 002-graphics.rb} +1 -1
- data/examples/{arc.rb → 003-arcs.rb} +2 -2
- data/examples/{optimizing.rb → 004-optimizing.rb} +0 -0
- data/examples/{merging.rb → 005-merging.rb} +0 -0
- data/examples/{standard_pdf_fonts.rb → 006-standard_pdf_fonts.rb} +0 -0
- data/examples/{truetype.rb → 007-truetype.rb} +0 -0
- data/examples/{show_char_bboxes.rb → 008-show_char_bboxes.rb} +0 -0
- data/examples/{text_layouter_alignment.rb → 009-text_layouter_alignment.rb} +3 -3
- data/examples/{text_layouter_inline_boxes.rb → 010-text_layouter_inline_boxes.rb} +7 -9
- data/examples/{text_layouter_line_wrapping.rb → 011-text_layouter_line_wrapping.rb} +6 -5
- data/examples/{text_layouter_styling.rb → 012-text_layouter_styling.rb} +6 -8
- data/examples/013-text_layouter_shapes.rb +176 -0
- data/examples/014-text_in_polygon.rb +60 -0
- data/examples/{boxes.rb → 015-boxes.rb} +29 -21
- data/examples/016-frame_automatic_box_placement.rb +90 -0
- data/examples/017-frame_text_flow.rb +60 -0
- data/lib/hexapdf/cli/command.rb +4 -3
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +0 -1
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/configuration.rb +2 -0
- data/lib/hexapdf/content/canvas.rb +3 -3
- data/lib/hexapdf/content/graphic_object.rb +1 -0
- data/lib/hexapdf/content/graphic_object/geom2d.rb +132 -0
- data/lib/hexapdf/dictionary.rb +7 -1
- data/lib/hexapdf/dictionary_fields.rb +35 -83
- data/lib/hexapdf/document.rb +9 -5
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +2 -2
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +3 -3
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/layout.rb +3 -0
- data/lib/hexapdf/layout/box.rb +64 -46
- data/lib/hexapdf/layout/frame.rb +348 -0
- data/lib/hexapdf/layout/inline_box.rb +2 -2
- data/lib/hexapdf/layout/line.rb +3 -3
- data/lib/hexapdf/layout/style.rb +81 -14
- data/lib/hexapdf/layout/text_box.rb +84 -0
- data/lib/hexapdf/layout/text_fragment.rb +8 -8
- data/lib/hexapdf/layout/text_layouter.rb +278 -169
- data/lib/hexapdf/layout/width_from_polygon.rb +246 -0
- data/lib/hexapdf/rectangle.rb +9 -9
- data/lib/hexapdf/stream.rb +2 -2
- data/lib/hexapdf/type.rb +1 -0
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +2 -1
- data/lib/hexapdf/type/font.rb +0 -1
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +3 -3
- data/lib/hexapdf/type/font_true_type.rb +8 -0
- data/lib/hexapdf/type/font_type0.rb +2 -1
- data/lib/hexapdf/type/font_type1.rb +7 -1
- data/lib/hexapdf/type/font_type3.rb +61 -0
- data/lib/hexapdf/type/graphics_state_parameter.rb +8 -8
- data/lib/hexapdf/type/image.rb +10 -0
- data/lib/hexapdf/type/page.rb +83 -10
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +1 -1
- data/test/hexapdf/font/test_true_type_wrapper.rb +1 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_directory.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_head.rb +7 -3
- data/test/hexapdf/layout/test_box.rb +57 -15
- data/test/hexapdf/layout/test_frame.rb +313 -0
- data/test/hexapdf/layout/test_inline_box.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +74 -0
- data/test/hexapdf/layout/test_text_box.rb +77 -0
- data/test/hexapdf/layout/test_text_layouter.rb +220 -239
- data/test/hexapdf/layout/test_width_from_polygon.rb +108 -0
- data/test/hexapdf/test_dictionary_fields.rb +22 -26
- data/test/hexapdf/test_document.rb +3 -3
- data/test/hexapdf/test_reference.rb +1 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_font_true_type.rb +25 -0
- data/test/hexapdf/type/test_font_type1.rb +6 -0
- data/test/hexapdf/type/test_font_type3.rb +26 -0
- data/test/hexapdf/type/test_image.rb +10 -0
- data/test/hexapdf/type/test_page.rb +114 -0
- data/test/test_helper.rb +1 -1
- metadata +65 -17
- data/examples/text_layouter_shapes.rb +0 -170
data/lib/hexapdf/dictionary.rb
CHANGED
|
@@ -78,6 +78,10 @@ module HexaPDF
|
|
|
78
78
|
# If a Symbol object instead of a class is provided, the class is looked up using the
|
|
79
79
|
# 'object.type_map' global configuration option when necessary to support lazy loading.
|
|
80
80
|
#
|
|
81
|
+
# Note that if multiple types are allowed and one of the allowed types is Dictionary (or
|
|
82
|
+
# a Symbol), it has to be the first in the list. Otherwise automatic type conversion
|
|
83
|
+
# functions won't work correctly.
|
|
84
|
+
#
|
|
81
85
|
# required:: Specifies whether this field is required.
|
|
82
86
|
#
|
|
83
87
|
# default:: Specifies the default value for the field, if any.
|
|
@@ -156,7 +160,9 @@ module HexaPDF
|
|
|
156
160
|
if data.class == HexaPDF::Object || (data.kind_of?(HexaPDF::Object) && data.value.nil?)
|
|
157
161
|
data = data.value
|
|
158
162
|
end
|
|
159
|
-
|
|
163
|
+
if (result = field&.convert(data, document))
|
|
164
|
+
self[name] = data = result
|
|
165
|
+
end
|
|
160
166
|
data
|
|
161
167
|
end
|
|
162
168
|
|
|
@@ -55,13 +55,10 @@ module HexaPDF
|
|
|
55
55
|
# Should return +nil+, a single type class or an array of type classes which will additionally
|
|
56
56
|
# be allowed for the field.
|
|
57
57
|
#
|
|
58
|
-
# convert?(data, type)::
|
|
59
|
-
# Should return +true+ if the given +data+ object can be converted. The +type+ argument is the
|
|
60
|
-
# result of the Field#type method call.
|
|
61
|
-
#
|
|
62
58
|
# convert(data, type, document)::
|
|
63
|
-
# Should return the +converted+ data
|
|
64
|
-
# call and +document+ is the HexaPDF::Document
|
|
59
|
+
# Should return the +converted+ data if conversion is possible and +nil+ otherwise. The +type+
|
|
60
|
+
# argument is the result of the Field#type method call and +document+ is the HexaPDF::Document
|
|
61
|
+
# for which the data should be converted.
|
|
65
62
|
module DictionaryFields
|
|
66
63
|
|
|
67
64
|
# This constant should *always* be used for boolean fields.
|
|
@@ -112,14 +109,14 @@ module HexaPDF
|
|
|
112
109
|
@type = [type].flatten
|
|
113
110
|
@type_mapped = false
|
|
114
111
|
@required, @default, @indirect, @version = required, default, indirect, version
|
|
115
|
-
@
|
|
112
|
+
@converters = @type.map {|t| self.class.converter_for(t) }.compact
|
|
116
113
|
end
|
|
117
114
|
|
|
118
115
|
# Returns the array with valid types for this field.
|
|
119
116
|
def type
|
|
120
117
|
return @type if @type_mapped
|
|
121
118
|
@type_mapped = true
|
|
122
|
-
@type.concat(
|
|
119
|
+
@type.concat(@converters.map(&:additional_types).compact.flatten)
|
|
123
120
|
@type.map! do |type|
|
|
124
121
|
if type.kind_of?(Symbol)
|
|
125
122
|
HexaPDF::GlobalConfiguration.constantize('object.type_map', type)
|
|
@@ -149,7 +146,7 @@ module HexaPDF
|
|
|
149
146
|
|
|
150
147
|
# Returns +true+ if the default value can safely be duplicated with #dup.
|
|
151
148
|
def duplicatable_default?
|
|
152
|
-
@
|
|
149
|
+
@duplicatable_default ||= HexaPDF::Object::NOT_DUPLICATABLE_CLASSES.none? do |klass|
|
|
153
150
|
@default.kind_of?(klass)
|
|
154
151
|
end
|
|
155
152
|
end
|
|
@@ -161,39 +158,13 @@ module HexaPDF
|
|
|
161
158
|
(obj.kind_of?(HexaPDF::Object) && type.any? {|t| obj.value.kind_of?(t) })
|
|
162
159
|
end
|
|
163
160
|
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# See: #convert
|
|
167
|
-
def convert?(data)
|
|
168
|
-
@converter.convert?(data, type)
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# If a converter was defined, it is used for converting the data. Otherwise this is a Noop -
|
|
172
|
-
# it just returns the data.
|
|
173
|
-
#
|
|
174
|
-
# See: #convert?
|
|
161
|
+
# Converts the data into a useful object if possible. Otherwise returns +nil+.
|
|
175
162
|
def convert(data, document)
|
|
176
|
-
@
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
# Does nothing.
|
|
182
|
-
module IdentityConverter
|
|
183
|
-
|
|
184
|
-
def self.usable_for?(_type) #:nodoc:
|
|
185
|
-
true
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def self.additional_types #:nodoc:
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def self.convert?(_data, _type) #:nodoc:
|
|
192
|
-
false
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
def self.convert(data, _type, _document) #:nodoc:
|
|
196
|
-
data
|
|
163
|
+
@converters.each do |converter|
|
|
164
|
+
result = converter.convert(data, type, document)
|
|
165
|
+
return result unless result.nil?
|
|
166
|
+
end
|
|
167
|
+
nil
|
|
197
168
|
end
|
|
198
169
|
|
|
199
170
|
end
|
|
@@ -214,15 +185,11 @@ module HexaPDF
|
|
|
214
185
|
Hash
|
|
215
186
|
end
|
|
216
187
|
|
|
217
|
-
#
|
|
218
|
-
#
|
|
219
|
-
def self.convert?(data, type)
|
|
220
|
-
!data.kind_of?(type.first) && (data.kind_of?(Hash) ||
|
|
221
|
-
data.kind_of?(HexaPDF::Dictionary))
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# Wraps the given data value in the PDF specific type class.
|
|
188
|
+
# Wraps the given data value in the PDF specific type class if it can be converted. Otherwise
|
|
189
|
+
# returns +nil+.
|
|
225
190
|
def self.convert(data, type, document)
|
|
191
|
+
return if data.kind_of?(type.first) || !(data.kind_of?(Hash) ||
|
|
192
|
+
data.kind_of?(HexaPDF::Dictionary))
|
|
226
193
|
document.wrap(data, type: type.first)
|
|
227
194
|
end
|
|
228
195
|
|
|
@@ -240,13 +207,11 @@ module HexaPDF
|
|
|
240
207
|
def self.additional_types
|
|
241
208
|
end
|
|
242
209
|
|
|
243
|
-
#
|
|
244
|
-
|
|
245
|
-
data.kind_of?(String) && data.encoding == Encoding::BINARY
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
# Converts the string into UTF-8 encoding, assuming it is currently a binary string.
|
|
210
|
+
# Converts the string into UTF-8 encoding, assuming it is a binary string. Otherwise +nil+ is
|
|
211
|
+
# returned.
|
|
249
212
|
def self.convert(str, _type, _document)
|
|
213
|
+
return unless str.kind_of?(String) && str.encoding == Encoding::BINARY
|
|
214
|
+
|
|
250
215
|
if str.getbyte(0) == 254 && str.getbyte(1) == 255
|
|
251
216
|
str[2..-1].force_encoding(Encoding::UTF_16BE).encode(Encoding::UTF_8)
|
|
252
217
|
else
|
|
@@ -270,13 +235,10 @@ module HexaPDF
|
|
|
270
235
|
String
|
|
271
236
|
end
|
|
272
237
|
|
|
273
|
-
#
|
|
274
|
-
|
|
275
|
-
data.kind_of?(String) && data.encoding != Encoding::BINARY
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
# Converts the string into UTF-8 encoding, assuming it is currently a binary string.
|
|
238
|
+
# Converts the string into binary encoding, assuming it is a non-binary string. Otherwise
|
|
239
|
+
# returns +nil+.
|
|
279
240
|
def self.convert(str, _type, _document)
|
|
241
|
+
return if !str.kind_of?(String) || str.encoding == Encoding::BINARY
|
|
280
242
|
str.force_encoding(Encoding::BINARY)
|
|
281
243
|
end
|
|
282
244
|
|
|
@@ -304,14 +266,11 @@ module HexaPDF
|
|
|
304
266
|
# :nodoc:
|
|
305
267
|
DATE_RE = /\AD:(\d{4})(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([Z+-])?(?:(\d\d)(?:'|'(\d\d)'?|\z)?)?\z/n
|
|
306
268
|
|
|
307
|
-
#
|
|
308
|
-
|
|
309
|
-
data.kind_of?(String) && data.match?(DATE_RE)
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
# Converts the string into a Time object.
|
|
269
|
+
# Checks if the given object is a string and converts into a Time object if possible.
|
|
270
|
+
# Otherwise returns +nil+.
|
|
313
271
|
def self.convert(str, _type, _document)
|
|
314
|
-
m =
|
|
272
|
+
return unless str.kind_of?(String) && (m = str.match(DATE_RE))
|
|
273
|
+
|
|
315
274
|
utc_offset = (m[7].nil? || m[7] == 'Z' ? 0 : "#{m[7]}#{m[8]}:#{m[9] || '00'}")
|
|
316
275
|
Time.new(m[1].to_i, (m[2] ? m[2].to_i : 1), (m[3] ? m[3].to_i : 1),
|
|
317
276
|
m[4].to_i, m[5].to_i, m[6].to_i, utc_offset)
|
|
@@ -333,14 +292,12 @@ module HexaPDF
|
|
|
333
292
|
[Hash, String]
|
|
334
293
|
end
|
|
335
294
|
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
!data.kind_of?(type.first) &&
|
|
339
|
-
(data.kind_of?(Hash) || data.kind_of?(HexaPDF::Dictionary) || data.kind_of?(String))
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
# Converts a string file specification or a hash into a full file specification.
|
|
295
|
+
# Converts a string file specification or a hash into a full file specification. Otherwise
|
|
296
|
+
# returns +nil+.
|
|
343
297
|
def self.convert(data, type, document)
|
|
298
|
+
return if data.kind_of?(type.first) ||
|
|
299
|
+
!(data.kind_of?(Hash) || data.kind_of?(HexaPDF::Dictionary) || data.kind_of?(String))
|
|
300
|
+
|
|
344
301
|
data = {F: data} if data.kind_of?(String)
|
|
345
302
|
document.wrap(data, type: type.first)
|
|
346
303
|
end
|
|
@@ -360,21 +317,16 @@ module HexaPDF
|
|
|
360
317
|
Array
|
|
361
318
|
end
|
|
362
319
|
|
|
363
|
-
#
|
|
364
|
-
def self.convert?(data, _type)
|
|
365
|
-
data.kind_of?(Array)
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
# Wraps the given data value in the Rectangle class.
|
|
320
|
+
# Wraps a given array in the Rectangle class. Otherwise returns +nil+.
|
|
369
321
|
def self.convert(data, _type, document)
|
|
322
|
+
return unless data.kind_of?(Array)
|
|
370
323
|
document.wrap(data, type: Rectangle)
|
|
371
324
|
end
|
|
372
325
|
|
|
373
326
|
end
|
|
374
327
|
|
|
375
328
|
Field.converters.replace([FileSpecificationConverter, DictionaryConverter, StringConverter,
|
|
376
|
-
PDFByteStringConverter, DateConverter, RectangleConverter
|
|
377
|
-
IdentityConverter])
|
|
329
|
+
PDFByteStringConverter, DateConverter, RectangleConverter])
|
|
378
330
|
|
|
379
331
|
end
|
|
380
332
|
|
data/lib/hexapdf/document.rb
CHANGED
|
@@ -554,8 +554,8 @@ module HexaPDF
|
|
|
554
554
|
end
|
|
555
555
|
|
|
556
556
|
# :call-seq:
|
|
557
|
-
# doc.validate(auto_correct: true)
|
|
558
|
-
# doc.validate(auto_correct: true) {|msg, correctable| block } -> true or false
|
|
557
|
+
# doc.validate(auto_correct: true) -> true or false
|
|
558
|
+
# doc.validate(auto_correct: true) {|object, msg, correctable| block } -> true or false
|
|
559
559
|
#
|
|
560
560
|
# Validates all objects of the document, with optional auto-correction, and returns +true+ if
|
|
561
561
|
# everything is fine.
|
|
@@ -563,9 +563,13 @@ module HexaPDF
|
|
|
563
563
|
# If a block is given, it is called on validation problems.
|
|
564
564
|
#
|
|
565
565
|
# See HexaPDF::Object#validate for more information.
|
|
566
|
-
def validate(auto_correct: true
|
|
566
|
+
def validate(auto_correct: true)
|
|
567
|
+
cur_obj = trailer
|
|
568
|
+
block = (block_given? ? lambda {|msg, correctable| yield(cur_obj, msg, correctable) } : nil)
|
|
569
|
+
|
|
567
570
|
result = trailer.validate(auto_correct: auto_correct, &block)
|
|
568
571
|
each(current: false) do |obj|
|
|
572
|
+
cur_obj = obj
|
|
569
573
|
result &&= obj.validate(auto_correct: auto_correct, &block)
|
|
570
574
|
end
|
|
571
575
|
result
|
|
@@ -601,9 +605,9 @@ module HexaPDF
|
|
|
601
605
|
end
|
|
602
606
|
|
|
603
607
|
if validate
|
|
604
|
-
self.validate(auto_correct: true) do |msg, correctable|
|
|
608
|
+
self.validate(auto_correct: true) do |obj, msg, correctable|
|
|
605
609
|
next if correctable
|
|
606
|
-
raise HexaPDF::Error, "Validation error: #{msg}"
|
|
610
|
+
raise HexaPDF::Error, "Validation error for (#{obj.oid},#{obj.gen}): #{msg}"
|
|
607
611
|
end
|
|
608
612
|
end
|
|
609
613
|
|
|
@@ -50,7 +50,7 @@ module HexaPDF
|
|
|
50
50
|
# :call-seq:
|
|
51
51
|
# fonts.add(name, **options) -> font
|
|
52
52
|
#
|
|
53
|
-
# Adds the font to the document and returns
|
|
53
|
+
# Adds the font to the document and returns it (using the loaders specified with the
|
|
54
54
|
# configuration option 'font_loaders').
|
|
55
55
|
#
|
|
56
56
|
# If a font with the same parameters has been loaded before, the cached font object is used.
|
|
@@ -524,7 +524,7 @@ module HexaPDF
|
|
|
524
524
|
decrypted = aes_algorithm.new(encryption_key, "\0" * 16, :decrypt).process(dict[:Perms])
|
|
525
525
|
if decrypted[9, 3] != "adb"
|
|
526
526
|
raise HexaPDF::EncryptionError, "/Perms field cannot be decrypted"
|
|
527
|
-
elsif (dict[:P] & 0xFFFFFFFF) != (decrypted[0, 4].
|
|
527
|
+
elsif (dict[:P] & 0xFFFFFFFF) != (decrypted[0, 4].unpack1('V') & 0xFFFFFFFF)
|
|
528
528
|
raise HexaPDF::EncryptionError, "Decrypted permissions don't match /P"
|
|
529
529
|
elsif decrypted[8] != (dict[:EncryptMetadata] ? 'T' : 'F')
|
|
530
530
|
raise HexaPDF::EncryptionError, "Decrypted /Perms field doesn't match /EncryptMetadata"
|
|
@@ -144,7 +144,7 @@ module HexaPDF
|
|
|
144
144
|
|
|
145
145
|
if rest
|
|
146
146
|
rlen = rest.length
|
|
147
|
-
num = (rest + "\0" * (4 - rlen)).
|
|
147
|
+
num = (rest + "\0" * (4 - rlen)).unpack1('N')
|
|
148
148
|
((VALUE_TO_CHAR[num / POW85_4 % 85] + VALUE_TO_CHAR[num / POW85_3 % 85] <<
|
|
149
149
|
VALUE_TO_CHAR[num / POW85_2 % 85] << VALUE_TO_CHAR[num / POW85_1 % 85] <<
|
|
150
150
|
VALUE_TO_CHAR[num % 85])[0, rlen + 1] << "~>").force_encoding(Encoding::BINARY)
|
|
@@ -70,7 +70,7 @@ module HexaPDF
|
|
|
70
70
|
def self.encoder(source, _ = nil)
|
|
71
71
|
Fiber.new do
|
|
72
72
|
while source.alive? && (data = source.resume)
|
|
73
|
-
Fiber.yield(data.
|
|
73
|
+
Fiber.yield(data.unpack1('H*').force_encoding(Encoding::BINARY))
|
|
74
74
|
end
|
|
75
75
|
'>'.b
|
|
76
76
|
end
|
|
@@ -57,14 +57,14 @@ module HexaPDF
|
|
|
57
57
|
result = create_sections("bfchar", chars.size / 2) do |index|
|
|
58
58
|
index *= 2
|
|
59
59
|
sprintf("<%04X>", chars[index]) << "<" <<
|
|
60
|
-
((+'').force_encoding(::Encoding::UTF_16BE) << chars[index + 1]).
|
|
60
|
+
((+'').force_encoding(::Encoding::UTF_16BE) << chars[index + 1]).unpack1('H*') <<
|
|
61
61
|
">\n"
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
result << create_sections("bfrange", ranges.size / 3) do |index|
|
|
65
65
|
index *= 3
|
|
66
66
|
sprintf("<%04X><%04X>", ranges[index], ranges[index + 1]) << "<" <<
|
|
67
|
-
((+'').force_encoding(::Encoding::UTF_16BE) << ranges[index + 2]).
|
|
67
|
+
((+'').force_encoding(::Encoding::UTF_16BE) << ranges[index + 2]).unpack1('H*') <<
|
|
68
68
|
">\n"
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -63,7 +63,7 @@ module HexaPDF
|
|
|
63
63
|
table_checksum = Table.calculate_checksum(data)
|
|
64
64
|
data << "\0" * (4 - original_data_length % 4) if original_data_length % 4 != 0
|
|
65
65
|
# tag, offset, data.length are all 32bit uint, table_checksum for header and body
|
|
66
|
-
checksum += tag.
|
|
66
|
+
checksum += tag.unpack1('N') + 2 * table_checksum + offset + data.length
|
|
67
67
|
font_data << [tag, table_checksum, offset, original_data_length].pack('a4N3')
|
|
68
68
|
offset += data.length
|
|
69
69
|
end
|
|
@@ -117,7 +117,7 @@ module HexaPDF
|
|
|
117
117
|
# +true+ is returned. Otherwise nothing is done and +false+ is returned.
|
|
118
118
|
def parse(io, offset)
|
|
119
119
|
io.pos = offset
|
|
120
|
-
@format = io.read(2).
|
|
120
|
+
@format = io.read(2).unpack1('n')
|
|
121
121
|
if [8, 10, 12].include?(@format)
|
|
122
122
|
io.pos += 2
|
|
123
123
|
length, @language = io.read(8).unpack('N2')
|
|
@@ -245,7 +245,7 @@ module HexaPDF
|
|
|
245
245
|
#
|
|
246
246
|
# It is assumed that the first six bytes of the subtable have already been consumed.
|
|
247
247
|
def self.parse(io, length)
|
|
248
|
-
seg_count_x2 = io.read(8).
|
|
248
|
+
seg_count_x2 = io.read(8).unpack1('n')
|
|
249
249
|
end_codes = io.read(seg_count_x2).unpack('n*')
|
|
250
250
|
io.pos += 2
|
|
251
251
|
start_codes = io.read(seg_count_x2).unpack('n*')
|
|
@@ -346,7 +346,7 @@ module HexaPDF
|
|
|
346
346
|
#
|
|
347
347
|
# It is assumed that the first twelve bytes of the subtable have already been consumed.
|
|
348
348
|
def self.parse(io, _length)
|
|
349
|
-
mapper(Array.new(io.read(4).
|
|
349
|
+
mapper(Array.new(io.read(4).unpack1('N')) { io.read(12).unpack('N3') })
|
|
350
350
|
end
|
|
351
351
|
|
|
352
352
|
# The parameter +groups+ is an array containing [start_code, end_code, start_glyph_id]
|
|
@@ -152,7 +152,7 @@ module HexaPDF
|
|
|
152
152
|
# Parses the format 0 subtable and returns a hash of the form
|
|
153
153
|
# {left_char: {right_char: kern_value}}
|
|
154
154
|
def self.parse(io, _length)
|
|
155
|
-
number_of_pairs = io.read(8).
|
|
155
|
+
number_of_pairs = io.read(8).unpack1('n')
|
|
156
156
|
pairs = Hash.new {|h, k| h[k] = {} }
|
|
157
157
|
io.read(number_of_pairs * 6).unpack('n*').each_slice(3) do |left, right, value|
|
|
158
158
|
pairs[left][right] = (value < 0x8000 ? value : -(value ^ 0xffff) - 1)
|
|
@@ -160,7 +160,7 @@ module HexaPDF
|
|
|
160
160
|
# returns the contained glyph name map.
|
|
161
161
|
def self.parse(io, length)
|
|
162
162
|
end_pos = io.pos + length
|
|
163
|
-
num_glyphs = io.read(2).
|
|
163
|
+
num_glyphs = io.read(2).unpack1('n')
|
|
164
164
|
glyph_name_index = io.read(2 * num_glyphs).unpack('n*')
|
|
165
165
|
names = []
|
|
166
166
|
names << io.read(io.getbyte).force_encoding(::Encoding::UTF_8) while io.pos < end_pos
|
|
@@ -48,7 +48,7 @@ module HexaPDF
|
|
|
48
48
|
attr_accessor :name
|
|
49
49
|
|
|
50
50
|
# Character bounding box as array of four numbers, specifying the x- and y-coordinates of
|
|
51
|
-
# the
|
|
51
|
+
# the bottom left corner and the x- and y-coordinates of the top right corner.
|
|
52
52
|
attr_accessor :bbox
|
|
53
53
|
|
|
54
54
|
end
|
|
@@ -60,7 +60,7 @@ module HexaPDF
|
|
|
60
60
|
attr_accessor :weight
|
|
61
61
|
|
|
62
62
|
# The font bounding box as array of four numbers, specifying the x- and y-coordinates of the
|
|
63
|
-
#
|
|
63
|
+
# bottom left corner and the x- and y-coordinates of the top right corner.
|
|
64
64
|
attr_accessor :bounding_box
|
|
65
65
|
|
|
66
66
|
# The y-value of the top of the capital H (or 0 or nil if the font doesn't contain a capital
|
|
@@ -116,7 +116,7 @@ module HexaPDF
|
|
|
116
116
|
|
|
117
117
|
# B1.1.4 - next two bytes are the length of the segment (except for RSTm or TEM markers
|
|
118
118
|
# but those shouldn't appear here)
|
|
119
|
-
length = io.read(2).
|
|
119
|
+
length = io.read(2).unpack1('n')
|
|
120
120
|
|
|
121
121
|
if code1 == ADOBE_MARKER # Adobe apps invert the colors when using CMYK color space
|
|
122
122
|
invert_colors = true
|
|
@@ -167,12 +167,12 @@ module HexaPDF
|
|
|
167
167
|
io.seek(length, IO::SEEK_CUR)
|
|
168
168
|
end
|
|
169
169
|
when 'sRGB' # PNG s11.3.3.5
|
|
170
|
-
@intent = io.read(length).
|
|
170
|
+
@intent = io.read(length).unpack1('C')
|
|
171
171
|
dict[:Intent] = RENDERING_INTENT_MAP[@intent]
|
|
172
172
|
@chrm = SRGB_CHRM
|
|
173
173
|
@gamma = 2.2
|
|
174
174
|
when 'gAMA' # PNG s11.3.3.2
|
|
175
|
-
gamma = 100_000.0 / io.read(length).
|
|
175
|
+
gamma = 100_000.0 / io.read(length).unpack1('N')
|
|
176
176
|
unless @intent || gamma == 1.0 # sRGB trumps gAMA
|
|
177
177
|
@gamma = gamma
|
|
178
178
|
@chrm ||= SRGB_CHRM # don't overwrite data from a cHRM chunk
|
data/lib/hexapdf/layout.rb
CHANGED
|
@@ -46,6 +46,9 @@ module HexaPDF
|
|
|
46
46
|
autoload(:TextShaper, 'hexapdf/layout/text_shaper')
|
|
47
47
|
autoload(:TextLayouter, 'hexapdf/layout/text_layouter')
|
|
48
48
|
autoload(:Box, 'hexapdf/layout/box')
|
|
49
|
+
autoload(:Frame, 'hexapdf/layout/frame')
|
|
50
|
+
autoload(:WidthFromPolygon, 'hexapdf/layout/width_from_polygon')
|
|
51
|
+
autoload(:TextBox, 'hexapdf/layout/text_box')
|
|
49
52
|
|
|
50
53
|
end
|
|
51
54
|
|