hexapdf 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +106 -0
  3. data/lib/hexapdf/cli/form.rb +30 -8
  4. data/lib/hexapdf/configuration.rb +19 -4
  5. data/lib/hexapdf/dictionary.rb +8 -2
  6. data/lib/hexapdf/dictionary_fields.rb +1 -1
  7. data/lib/hexapdf/encryption/security_handler.rb +7 -2
  8. data/lib/hexapdf/error.rb +4 -3
  9. data/lib/hexapdf/filter.rb +1 -0
  10. data/lib/hexapdf/filter/crypt.rb +60 -0
  11. data/lib/hexapdf/font/true_type/subsetter.rb +16 -3
  12. data/lib/hexapdf/font/true_type/table/post.rb +15 -10
  13. data/lib/hexapdf/font/type1/afm_parser.rb +2 -1
  14. data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
  15. data/lib/hexapdf/font_loader/from_file.rb +18 -8
  16. data/lib/hexapdf/importer.rb +3 -2
  17. data/lib/hexapdf/parser.rb +55 -15
  18. data/lib/hexapdf/pdf_array.rb +4 -1
  19. data/lib/hexapdf/revision.rb +16 -0
  20. data/lib/hexapdf/serializer.rb +43 -10
  21. data/lib/hexapdf/tokenizer.rb +44 -3
  22. data/lib/hexapdf/type/acro_form.rb +1 -0
  23. data/lib/hexapdf/type/acro_form/appearance_generator.rb +32 -17
  24. data/lib/hexapdf/type/acro_form/button_field.rb +8 -4
  25. data/lib/hexapdf/type/acro_form/field.rb +1 -0
  26. data/lib/hexapdf/type/acro_form/form.rb +37 -0
  27. data/lib/hexapdf/type/acro_form/signature_field.rb +223 -0
  28. data/lib/hexapdf/type/annotation.rb +13 -9
  29. data/lib/hexapdf/type/annotations/widget.rb +3 -1
  30. data/lib/hexapdf/type/font_descriptor.rb +9 -2
  31. data/lib/hexapdf/type/page.rb +81 -0
  32. data/lib/hexapdf/type/resources.rb +4 -0
  33. data/lib/hexapdf/type/xref_stream.rb +7 -0
  34. data/lib/hexapdf/version.rb +1 -1
  35. data/test/hexapdf/encryption/test_security_handler.rb +15 -0
  36. data/test/hexapdf/filter/test_crypt.rb +21 -0
  37. data/test/hexapdf/font/true_type/table/test_post.rb +1 -1
  38. data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
  39. data/test/hexapdf/font/type1/test_afm_parser.rb +5 -0
  40. data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
  41. data/test/hexapdf/font_loader/test_from_file.rb +7 -0
  42. data/test/hexapdf/test_dictionary.rb +5 -0
  43. data/test/hexapdf/test_dictionary_fields.rb +7 -0
  44. data/test/hexapdf/test_parser.rb +94 -2
  45. data/test/hexapdf/test_revision.rb +21 -0
  46. data/test/hexapdf/test_serializer.rb +10 -0
  47. data/test/hexapdf/test_tokenizer.rb +50 -0
  48. data/test/hexapdf/test_writer.rb +2 -2
  49. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +24 -3
  50. data/test/hexapdf/type/acro_form/test_button_field.rb +13 -7
  51. data/test/hexapdf/type/acro_form/test_field.rb +5 -0
  52. data/test/hexapdf/type/acro_form/test_form.rb +46 -2
  53. data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
  54. data/test/hexapdf/type/annotations/test_widget.rb +2 -0
  55. data/test/hexapdf/type/test_annotation.rb +20 -10
  56. data/test/hexapdf/type/test_font_descriptor.rb +7 -0
  57. data/test/hexapdf/type/test_page.rb +187 -49
  58. data/test/hexapdf/type/test_resources.rb +6 -0
  59. data/test/hexapdf/type/test_xref_stream.rb +7 -0
  60. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0cea16b918ff9aa6e7b32759295ef4ab38c899bcbd227d76ad42e0c971360239
4
- data.tar.gz: 932c5edf01114a59d0a64776f304e29f3c8865a2c2c52c340064180464aabad7
3
+ metadata.gz: bcd3bc77b70872416b1377b4fdf97804de083cf4d7213dfd200738fd8b2adae7
4
+ data.tar.gz: 53a8a850610a744570999cf56c656d6bd65c8ab691a5658b81172111bdd44804
5
5
  SHA512:
6
- metadata.gz: 5883c5788487830b0403459b38b4ed1761c1015688977e9823f3c572f1ad645b06eb0578b185ce26f7f02c560050dd8ec7c09e8524b59cd35df4fd6abd1fb4aa
7
- data.tar.gz: cdda51a089c86f27319fe424c9a74dc599ed60860338ef49958cd6a820141fa87a0624f2c657565e3f1b4a2392300807b89886178da6af62d22fa03fb543e372
6
+ metadata.gz: 54c99dbd44c4ae146496912f295982d47bb5ca297d4d2b76475c1f3151670068cd3be0c2dedee413b0a52e493383229bbd36d819ff2db2a9c04b377731cc107e
7
+ data.tar.gz: 8426e942709633b921f7a644e01b645d555cfcdecbdbd25bae03d0154cf07df3a3cd1f108adb16a8b449801c203014698a6b382133bbb63033a1d590351b61f7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,109 @@
1
+ ## 0.15.0 - 2021-04-12
2
+
3
+ ### Added
4
+
5
+ * [HexaPDF::Type::Page#flatten_annotations] for flattening the annotations of a
6
+ page
7
+ * [HexaPDF::Type::AcroForm::Form#flatten] for flattening interactive forms
8
+ * [HexaPDF::Revision#update] for updating the stored wrapper class of a PDF
9
+ object
10
+ * [HexaPDF::Type::AcroForm::SignatureField] for working with AcroForm signature
11
+ fields
12
+ * Support for form field flattening to the `hexapdf form` CLI command
13
+
14
+ ### Changed
15
+
16
+ * **Breaking change**: Overhauled the interface for accessing appearances of
17
+ annotations to make it more convenient
18
+ * Validation of [HexaPDF::Type::FontDescriptor] to delete invalid `/FontWeight`
19
+ value
20
+ * [HexaPDF::MalformedPDFError#pos] an accessor instead of a reader and update
21
+ the exception message
22
+ * Configuration option 'acro_form.fallback_font' to allow a callable object for
23
+ more advanced fallback font handling
24
+
25
+ ### Fixed
26
+
27
+ * [HexaPDF::Type::Annotations::Widget#background_color] to correctly handle
28
+ empty background color arrays
29
+ * [HexaPDF::Type::AcroForm::Field#delete_widget] to update the wrapper object
30
+ stored in the document in case the widget is embedded
31
+ * Processing of invalid PDF files containing a space,CR,LF combination after
32
+ the 'stream' keyword
33
+ * Cross-reference stream reconstruction with respect to detection of linearized
34
+ files
35
+ * Detection of existing appearances for AcroForm push button fields when
36
+ creating appearances
37
+
38
+
39
+ ## 0.14.4 - 2021-02-27
40
+
41
+ ### Added
42
+
43
+ * Support for the Crypt filters
44
+
45
+ ### Changed
46
+
47
+ * [HexaPDF::MalformedPDFError] to make the `pos` argument optional
48
+
49
+ ### Fixed
50
+
51
+ * Handling of invalid floating point numbers NaN, Inf and -Inf when serializing
52
+ * Processing of invalid PDF files containing NaN and Inf instead of numbers
53
+ * Bug in Type1 font AFM parser that occured if the file doesn't end with a new
54
+ line character
55
+ * Cross-reference table reconstruction to handle the case of an entry specifying
56
+ a non-existent indirect object
57
+ * Cross-reference table reconstruction to handle trailers specified by cross-
58
+ reference streams
59
+ * Cross-reference table reconstruction to use the set security handle for
60
+ decrypting indirect objects
61
+ * Parsing of cross-reference streams where data is missing
62
+
63
+
64
+ ## 0.14.3 - 2021-02-16
65
+
66
+ ### Fixed
67
+
68
+ * Bug in [HexaPDF::Font::TrueType::Subsetter#use_glyph] which lead to corrupt
69
+ text output
70
+ * [HexaPDF::Serializer] to handle infinite recursion problem
71
+ * Cross-reference table reconstruction to avoid an O(n^2) performance problem
72
+ * [HexaPDF::Type::Resources] validation to handle an invalid `/ProcSet` entry
73
+ containing a single value instead of an array
74
+ * Processing of invalid PDF files missing a required value in appearance streams
75
+ * Processing of invalid empty arrays that should be rectangles by converting
76
+ them to PDF null objects
77
+ * Processing of invalid PDF files containing indirect objects with offset 0
78
+ * Processing of invalid PDF files containing a space/CR or space/LF combination
79
+ after the 'stream' keyword
80
+
81
+
82
+ ## 0.14.2 - 2021-01-22
83
+
84
+ ### Fixed
85
+
86
+ * [HexaPDF::Font::TrueType::Subsetter#use_glyph] to really avoid using subset
87
+ glyph ID 41 (`)`)
88
+
89
+
90
+ ## 0.14.1 - 2021-01-21
91
+
92
+ ### Changed
93
+
94
+ * Validation message when checking for allowed values to include the invalid
95
+ object
96
+ * [HexaPDF::FontLoader::FromFile] to allow (re)using an existing font object
97
+ * [HexaPDF::Importer] internals to avoid problems with retained memory
98
+
99
+ ### Fixed
100
+
101
+ * Parsing of invalid PDF files where whitespace is missing after the integer
102
+ value of an indirect object
103
+ * [HexaPDF::Dictionary] so that adding new key-value pairs during validation is
104
+ possible
105
+
106
+
1
107
  ## 0.14.0 - 2020-12-30
2
108
 
3
109
  ### Added
@@ -52,18 +52,26 @@ module HexaPDF
52
52
  If the the output file name is not given, all form fields are listed in page order. Use
53
53
  the global --verbose option to show additional information like field type and location.
54
54
 
55
- If the output file name is given, the fields can be interactively filled out. By
56
- additionally using the --template option, the data for the fields is read from the given
57
- template file instead of the standard input.
55
+ If the output file name is given, the fields can be filled out interactively, via a
56
+ template or just flattened by using the respective options. Form field flattening can also
57
+ be activated in addition to filling out the form. If neither --fill, --template nor
58
+ --flatten is specified, --fill is implied.
58
59
  EOF
59
60
 
60
61
  options.on("--password PASSWORD", "-p", String,
61
62
  "The password for decryption. Use - for reading from standard input.") do |pwd|
62
63
  @password = (pwd == '-' ? read_password : pwd)
63
64
  end
65
+ options.on("--fill", "Fill out the form") do
66
+ @fill = true
67
+ end
64
68
  options.on("--template TEMPLATE_FILE", "-t TEMPLATE_FILE",
65
- "Use the template file for the field values") do |template|
69
+ "Use the template file for the field values (implies --fill)") do |template|
66
70
  @template = template
71
+ @fill = true
72
+ end
73
+ options.on('--flatten', 'Flatten the form fields') do
74
+ @flatten = true
67
75
  end
68
76
  options.on("--[no-]viewer-override", "Let the PDF viewer override the visual " \
69
77
  "appearance. Default: use setting from input PDF") do |need_appearances|
@@ -75,6 +83,8 @@ module HexaPDF
75
83
  end
76
84
 
77
85
  @password = nil
86
+ @fill = false
87
+ @flatten = false
78
88
  @template = nil
79
89
  @need_appearances = nil
80
90
  @incremental = true
@@ -82,16 +92,28 @@ module HexaPDF
82
92
 
83
93
  def execute(in_file, out_file = nil) #:nodoc:
84
94
  maybe_raise_on_existing_file(out_file) if out_file
95
+ if (@fill || @flatten) && !out_file
96
+ raise "Output file missing"
97
+ end
85
98
  with_document(in_file, password: @password, out_file: out_file,
86
99
  incremental: @incremental) do |doc|
87
100
  if !doc.acro_form
88
101
  raise "This PDF doesn't contain an interactive form"
89
102
  elsif out_file
90
103
  doc.acro_form[:NeedAppearances] = @need_appearances unless @need_appearances.nil?
91
- if @template
92
- fill_form_with_template(doc)
93
- else
94
- fill_form(doc)
104
+ if @fill || !@flatten
105
+ if @template
106
+ fill_form_with_template(doc)
107
+ else
108
+ fill_form(doc)
109
+ end
110
+ end
111
+ if @flatten
112
+ unless doc.acro_form.flatten.empty?
113
+ $stderr.puts "Warning: Not all form fields could be flattened"
114
+ doc.catalog.delete(:AcroForm)
115
+ doc.delete(doc.acro_form)
116
+ end
95
117
  end
96
118
  else
97
119
  list_form_fields(doc)
@@ -164,9 +164,20 @@ module HexaPDF
164
164
  # acro_form.fallback_font::
165
165
  # The font that should be used when a variable text field references a font that cannot be used.
166
166
  #
167
- # Can either be the name of a font, like 'Helvetica', or an array consisting of the font name
168
- # and a hash of font options, like ['Helvetica', variant: :italic]. If set to +nil+, the use of
169
- # the fallback font is disabled.
167
+ # Can be one of the following:
168
+ #
169
+ # * The name of a font, like 'Helvetica'.
170
+ #
171
+ # * An array consisting of the font name and a hash of font options, like ['Helvetica',
172
+ # variant: :italic].
173
+ #
174
+ # * A callable object receiving the field and the font object (or +nil+ if no valid font object
175
+ # was found) and which has to return either a font name or an array consisting of the font
176
+ # name and a hash of font options. This way the response can be different depending on the
177
+ # original font and it would also allow e.g. modifying the configured fonts to add custom
178
+ # ones.
179
+ #
180
+ # If set to +nil+, the use of the fallback font is disabled.
170
181
  #
171
182
  # Default is 'Helvetica'.
172
183
  #
@@ -393,7 +404,7 @@ module HexaPDF
393
404
  DCTDecode: 'HexaPDF::Filter::PassThrough',
394
405
  DCT: 'HexaPDF::Filter::PassThrough',
395
406
  JPXDecode: 'HexaPDF::Filter::PassThrough',
396
- Crypt: nil,
407
+ Crypt: 'HexaPDF::Filter::Crypt',
397
408
  Encryption: 'HexaPDF::Filter::Encryption',
398
409
  },
399
410
  'font.map' => {},
@@ -516,6 +527,9 @@ module HexaPDF
516
527
  XXAcroFormField: 'HexaPDF::Type::AcroForm::Field',
517
528
  XXAppearanceDictionary: 'HexaPDF::Type::Annotation::AppearanceDictionary',
518
529
  Border: 'HexaPDF::Type::Annotation::Border',
530
+ SigFieldLock: 'HexaPDF::Type::AcroForm::SignatureField::LockDictionary',
531
+ SV: 'HexaPDF::Type::AcroForm::SignatureField::SeedValueDictionary',
532
+ SVCert: 'HexaPDF::Type::AcroForm::SignatureField::CertificateSeedValueDictionary',
519
533
  },
520
534
  'object.subtype_map' => {
521
535
  nil => {
@@ -561,6 +575,7 @@ module HexaPDF
561
575
  Tx: 'HexaPDF::Type::AcroForm::TextField',
562
576
  Btn: 'HexaPDF::Type::AcroForm::ButtonField',
563
577
  Ch: 'HexaPDF::Type::AcroForm::ChoiceField',
578
+ Sig: 'HexaPDF::Type::AcroForm::SignatureField',
564
579
  },
565
580
  })
566
581
 
@@ -155,6 +155,12 @@ module HexaPDF
155
155
  # available (see ::define_field).
156
156
  #
157
157
  # * Returns the default value if one is specified and no value is available.
158
+ #
159
+ # Note: If field information is available for the entry, a Hash or Array value will always be
160
+ # wrapped by Dictionary or PDFArray. Otherwise, the value will be returned as-is.
161
+ #
162
+ # Note: This method may throw a "can't add a new key into hash during iteration" error in
163
+ # certain cases because it potentially modifies the underlying hash!
158
164
  def [](name)
159
165
  field = self.class.field(name)
160
166
  data = if key?(name)
@@ -255,7 +261,7 @@ module HexaPDF
255
261
 
256
262
  # Iterates over all currently set fields and those that are required.
257
263
  def each_set_key_or_required_field #:yields: name, field
258
- value.each_key {|name| yield(name, self.class.field(name)) }
264
+ value.keys.each {|name| yield(name, self.class.field(name)) }
259
265
  self.class.each_field do |name, field|
260
266
  yield(name, field) if field.required? && !value.key?(name)
261
267
  end
@@ -301,7 +307,7 @@ module HexaPDF
301
307
 
302
308
  # Check the value of the field against the allowed values.
303
309
  if field.allowed_values && !field.allowed_values.include?(obj)
304
- yield("Field #{name} does not contain an allowed value")
310
+ yield("Field #{name} does not contain an allowed value: #{obj.inspect}")
305
311
  end
306
312
 
307
313
  # Check if field value needs to be (in)direct
@@ -344,7 +344,7 @@ module HexaPDF
344
344
  # Wraps a given array in the Rectangle class. Otherwise returns +nil+.
345
345
  def self.convert(data, _type, document)
346
346
  return unless data.kind_of?(Array) || data.kind_of?(HexaPDF::PDFArray)
347
- document.wrap(data, type: Rectangle)
347
+ data.empty? ? document.wrap(nil) : document.wrap(data, type: Rectangle)
348
348
  end
349
349
 
350
350
  end
@@ -268,7 +268,7 @@ module HexaPDF
268
268
  str.replace(string_algorithm.decrypt(key, str))
269
269
  end
270
270
 
271
- if obj.kind_of?(HexaPDF::Stream)
271
+ if obj.kind_of?(HexaPDF::Stream) && obj.raw_stream.filter[0] != :Crypt
272
272
  unless string_algorithm == stream_algorithm
273
273
  key = object_key(obj.oid, obj.gen, stream_algorithm)
274
274
  end
@@ -300,7 +300,12 @@ module HexaPDF
300
300
  obj.raw_stream.key == key && obj.raw_stream.algorithm == stream_algorithm
301
301
  obj.raw_stream.undecrypted_fiber
302
302
  else
303
- stream_algorithm.encryption_fiber(key, result)
303
+ filter = obj[:Filter]
304
+ if filter == :Crypt || (filter.kind_of?(PDFArray) && filter[0] == :Crypt)
305
+ result
306
+ else
307
+ stream_algorithm.encryption_fiber(key, result)
308
+ end
304
309
  end
305
310
  end
306
311
 
data/lib/hexapdf/error.rb CHANGED
@@ -43,12 +43,13 @@ module HexaPDF
43
43
  class MalformedPDFError < Error
44
44
 
45
45
  # The byte position in the PDF file where the error occured.
46
- attr_reader :pos
46
+ attr_accessor :pos
47
47
 
48
48
  # Creates a new malformed PDF error object for the given exception message.
49
49
  #
50
- # The byte position where the error occured has to be given via the +pos+ argument.
51
- def initialize(message, pos:)
50
+ # The byte position where the error occured can either be given via the +pos+ argument or later
51
+ # via the #pos accessor but must be set before the exception message is retrieved.
52
+ def initialize(message, pos: nil)
52
53
  super(message)
53
54
  @pos = pos
54
55
  end
@@ -95,6 +95,7 @@ module HexaPDF
95
95
  autoload(:Predictor, 'hexapdf/filter/predictor')
96
96
 
97
97
  autoload(:Encryption, 'hexapdf/filter/encryption')
98
+ autoload(:Crypt, 'hexapdf/filter/crypt')
98
99
 
99
100
  autoload(:PassThrough, 'hexapdf/filter/pass_through')
100
101
 
@@ -0,0 +1,60 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/error'
38
+
39
+ module HexaPDF
40
+ module Filter
41
+
42
+ # This filter module implements the Crypt filter. The only supported part is using the Identity
43
+ # filter.
44
+ module Crypt
45
+
46
+ # See HexaPDF::Filter
47
+ def self.decoder(source, options)
48
+ if !options || !options.key?(:Name) || options[:Name] == :Identity
49
+ source
50
+ else
51
+ raise FilterError, "Handling of Crypt filters besides Identity is not implemented"
52
+ end
53
+ end
54
+
55
+ singleton_class.send(:alias_method, :encoder, :decoder)
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -63,6 +63,16 @@ module HexaPDF
63
63
  def use_glyph(glyph_id)
64
64
  return @glyph_map[glyph_id] if @glyph_map.key?(glyph_id)
65
65
  @last_id += 1
66
+ # Handle codes for ASCII characters \r (13), (, ) (40, 41) and \ (92) specially so that
67
+ # they never appear in the output (PDF serialization would need to escape them)
68
+ if @last_id == 13 || @last_id == 40 || @last_id == 92
69
+ @glyph_map[:"s#{@last_id}"] = @last_id
70
+ if @last_id == 40
71
+ @last_id += 1
72
+ @glyph_map[:"s#{@last_id}"] = @last_id
73
+ end
74
+ @last_id += 1
75
+ end
66
76
  @glyph_map[glyph_id] = @last_id
67
77
  end
68
78
 
@@ -107,7 +117,7 @@ module HexaPDF
107
117
  locations = []
108
118
 
109
119
  @glyph_map.each_key do |old_gid|
110
- glyph = orig_glyf[old_gid]
120
+ glyph = orig_glyf[old_gid.kind_of?(Symbol) ? 0 : old_gid]
111
121
  locations << table.size
112
122
  data = glyph.raw_data
113
123
  if glyph.compound?
@@ -134,7 +144,7 @@ module HexaPDF
134
144
  hmtx = @font[:hmtx]
135
145
  data = ''.b
136
146
  @glyph_map.each_key do |old_gid|
137
- metric = hmtx[old_gid]
147
+ metric = hmtx[old_gid.kind_of?(Symbol) ? 0 : old_gid]
138
148
  data << [metric.advance_width, metric.left_side_bearing].pack('n2')
139
149
  end
140
150
  data
@@ -166,7 +176,10 @@ module HexaPDF
166
176
  # Adds the components of compound glyphs to the subset.
167
177
  def add_glyph_components
168
178
  glyf = @font[:glyf]
169
- @glyph_map.keys.each {|gid| glyf[gid].components&.each {|cgid| use_glyph(cgid) } }
179
+ @glyph_map.keys.each do |gid|
180
+ next if gid.kind_of?(Symbol)
181
+ glyf[gid].components&.each {|cgid| use_glyph(cgid) }
182
+ end
170
183
  end
171
184
 
172
185
  end
@@ -99,18 +99,23 @@ module HexaPDF
99
99
  @max_mem_type42, @min_mem_type1, @max_mem_type1 = read_formatted(24, 's>2N5')
100
100
 
101
101
  sub_table_length = directory_entry.length - 32
102
- @glyph_names = case @format
103
- when 1 then Format1.parse(io, sub_table_length)
104
- when 2 then Format2.parse(io, sub_table_length)
105
- when 3 then Format3.parse(io, sub_table_length)
106
- when 4 then Format4.parse(io, sub_table_length)
107
- else
108
- if font.config['font.true_type.unknown_format'] == :raise
109
- raise HexaPDF::Error, "Unsupported post table format: #{@format}"
102
+ cur_pos = io.pos
103
+ @glyph_names = lambda do |glyph_id|
104
+ io.pos = cur_pos
105
+ @glyph_names = case @format
106
+ when 1 then Format1.parse(io, sub_table_length)
107
+ when 2 then Format2.parse(io, sub_table_length)
108
+ when 3 then Format3.parse(io, sub_table_length)
109
+ when 4 then Format4.parse(io, sub_table_length)
110
110
  else
111
- []
111
+ if font.config['font.true_type.unknown_format'] == :raise
112
+ raise HexaPDF::Error, "Unsupported post table format: #{@format}"
113
+ else
114
+ []
115
+ end
112
116
  end
113
- end
117
+ @glyph_names[glyph_id]
118
+ end
114
119
  end
115
120
 
116
121
  # 'post' table format 1