pdf-reader 2.9.2 → 2.15.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +39 -0
  3. data/README.md +33 -33
  4. data/Rakefile +2 -2
  5. data/lib/pdf/reader/advanced_text_run_filter.rb +152 -0
  6. data/lib/pdf/reader/aes_v2_security_handler.rb +30 -0
  7. data/lib/pdf/reader/aes_v3_security_handler.rb +35 -3
  8. data/lib/pdf/reader/bounding_rectangle_runs_filter.rb +1 -0
  9. data/lib/pdf/reader/buffer.rb +39 -22
  10. data/lib/pdf/reader/cid_widths.rb +14 -6
  11. data/lib/pdf/reader/cmap.rb +16 -5
  12. data/lib/pdf/reader/encoding.rb +42 -18
  13. data/lib/pdf/reader/error.rb +6 -4
  14. data/lib/pdf/reader/filter/ascii85.rb +2 -0
  15. data/lib/pdf/reader/filter/ascii_hex.rb +2 -0
  16. data/lib/pdf/reader/filter/depredict.rb +6 -2
  17. data/lib/pdf/reader/filter/flate.rb +5 -2
  18. data/lib/pdf/reader/filter/lzw.rb +2 -0
  19. data/lib/pdf/reader/filter/null.rb +2 -0
  20. data/lib/pdf/reader/filter/run_length.rb +2 -0
  21. data/lib/pdf/reader/filter.rb +1 -0
  22. data/lib/pdf/reader/font.rb +99 -32
  23. data/lib/pdf/reader/font_descriptor.rb +79 -24
  24. data/lib/pdf/reader/form_xobject.rb +15 -1
  25. data/lib/pdf/reader/glyph_hash.rb +41 -8
  26. data/lib/pdf/reader/key_builder_v5.rb +17 -9
  27. data/lib/pdf/reader/lzw.rb +42 -16
  28. data/lib/pdf/reader/no_text_filter.rb +15 -0
  29. data/lib/pdf/reader/null_security_handler.rb +1 -0
  30. data/lib/pdf/reader/object_cache.rb +7 -2
  31. data/lib/pdf/reader/object_hash.rb +129 -16
  32. data/lib/pdf/reader/object_stream.rb +22 -5
  33. data/lib/pdf/reader/overlapping_runs_filter.rb +8 -2
  34. data/lib/pdf/reader/page.rb +66 -13
  35. data/lib/pdf/reader/page_layout.rb +26 -9
  36. data/lib/pdf/reader/page_state.rb +12 -3
  37. data/lib/pdf/reader/page_text_receiver.rb +16 -2
  38. data/lib/pdf/reader/pages_strategy.rb +1 -1
  39. data/lib/pdf/reader/parser.rb +52 -13
  40. data/lib/pdf/reader/point.rb +9 -2
  41. data/lib/pdf/reader/print_receiver.rb +2 -6
  42. data/lib/pdf/reader/rc4_security_handler.rb +2 -0
  43. data/lib/pdf/reader/rectangle.rb +24 -1
  44. data/lib/pdf/reader/reference.rb +13 -3
  45. data/lib/pdf/reader/register_receiver.rb +15 -2
  46. data/lib/pdf/reader/resources.rb +12 -2
  47. data/lib/pdf/reader/security_handler_factory.rb +13 -0
  48. data/lib/pdf/reader/standard_key_builder.rb +37 -23
  49. data/lib/pdf/reader/stream.rb +9 -3
  50. data/lib/pdf/reader/synchronized_cache.rb +6 -3
  51. data/lib/pdf/reader/text_run.rb +33 -3
  52. data/lib/pdf/reader/token.rb +1 -0
  53. data/lib/pdf/reader/transformation_matrix.rb +41 -10
  54. data/lib/pdf/reader/type_check.rb +53 -0
  55. data/lib/pdf/reader/unimplemented_security_handler.rb +2 -0
  56. data/lib/pdf/reader/validating_receiver.rb +29 -0
  57. data/lib/pdf/reader/width_calculator/built_in.rb +13 -5
  58. data/lib/pdf/reader/width_calculator/composite.rb +11 -3
  59. data/lib/pdf/reader/width_calculator/true_type.rb +14 -12
  60. data/lib/pdf/reader/width_calculator/type_one_or_three.rb +8 -5
  61. data/lib/pdf/reader/width_calculator/type_zero.rb +8 -3
  62. data/lib/pdf/reader/xref.rb +31 -10
  63. data/lib/pdf/reader/zero_width_runs_filter.rb +1 -0
  64. data/lib/pdf/reader.rb +24 -12
  65. data/rbi/pdf-reader.rbi +1504 -1480
  66. metadata +34 -17
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # typed: true
2
+ # typed: strict
3
3
  # frozen_string_literal: true
4
4
 
5
5
  class PDF::Reader
@@ -14,16 +14,40 @@ class PDF::Reader
14
14
  # only 6 numbers. This is important to save CPU time, memory and GC pressure
15
15
  # caused by allocating too many unnecessary objects.
16
16
  class TransformationMatrix
17
- attr_reader :a, :b, :c, :d, :e, :f
17
+ #: Numeric
18
+ attr_reader :a
18
19
 
20
+ #: Numeric
21
+ attr_reader :b
22
+
23
+ #: Numeric
24
+ attr_reader :c
25
+
26
+ #: Numeric
27
+ attr_reader :d
28
+
29
+ #: Numeric
30
+ attr_reader :e
31
+
32
+ #: Numeric
33
+ attr_reader :f
34
+
35
+ #: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void
19
36
  def initialize(a, b, c, d, e, f)
20
- @a, @b, @c, @d, @e, @f = a, b, c, d, e, f
37
+ @a = a
38
+ @b = b
39
+ @c = c
40
+ @d = d
41
+ @e = e
42
+ @f = f
21
43
  end
22
44
 
45
+ #: () -> String
23
46
  def inspect
24
47
  "#{a}, #{b}, 0,\n#{c}, #{d}, #{0},\n#{e}, #{f}, 1"
25
48
  end
26
49
 
50
+ #: () -> [Numeric]
27
51
  def to_a
28
52
  [@a,@b,0,
29
53
  @c,@d,0,
@@ -51,7 +75,8 @@ class PDF::Reader
51
75
  # displacement to speed up processing documents that use vertical
52
76
  # writing systems
53
77
  #
54
- def multiply!(a,b=nil,c=nil, d=nil,e=nil,f=nil)
78
+ #: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> PDF::Reader::TransformationMatrix
79
+ def multiply!(a,b,c, d,e,f)
55
80
  if a == 1 && b == 0 && c == 0 && d == 1 && e == 0 && f == 0
56
81
  # the identity matrix, no effect
57
82
  self
@@ -90,6 +115,7 @@ class PDF::Reader
90
115
  # [ 3 4 0 ] x [ 0 1 0 ]
91
116
  # [ 5 6 1 ] [ e2 0 1 ]
92
117
  #
118
+ #: (Numeric) -> void
93
119
  def horizontal_displacement_multiply!(e2)
94
120
  @e = @e + e2
95
121
  end
@@ -105,6 +131,7 @@ class PDF::Reader
105
131
  # [ 0 1 0 ] x [ 3 4 0 ]
106
132
  # [ 5 0 1 ] [ 5 6 1 ]
107
133
  #
134
+ #: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void
108
135
  def horizontal_displacement_multiply_reversed!(a2,b2,c2,d2,e2,f2)
109
136
  newa = a2
110
137
  newb = b2
@@ -124,6 +151,7 @@ class PDF::Reader
124
151
  # [ 3 4 0 ] x [ 0 5 0 ]
125
152
  # [ 5 6 1 ] [ 0 0 1 ]
126
153
  #
154
+ #: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void
127
155
  def xy_scaling_multiply!(a2,b2,c2,d2,e2,f2)
128
156
  newa = @a * a2
129
157
  newb = @b * d2
@@ -143,6 +171,7 @@ class PDF::Reader
143
171
  # [ 0 5 0 ] x [ 3 4 0 ]
144
172
  # [ 0 0 1 ] [ 5 6 1 ]
145
173
  #
174
+ #: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void
146
175
  def xy_scaling_multiply_reversed!(a2,b2,c2,d2,e2,f2)
147
176
  newa = @a * a2
148
177
  newb = @a * b2
@@ -163,13 +192,14 @@ class PDF::Reader
163
192
  # [ c d 0 ] x [ c d 0 ]
164
193
  # [ e f 1 ] [ e f 1 ]
165
194
  #
195
+ #: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void
166
196
  def regular_multiply!(a2,b2,c2,d2,e2,f2)
167
- newa = (@a * a2) + (@b * c2) + (0 * e2)
168
- newb = (@a * b2) + (@b * d2) + (0 * f2)
169
- newc = (@c * a2) + (@d * c2) + (0 * e2)
170
- newd = (@c * b2) + (@d * d2) + (0 * f2)
171
- newe = (@e * a2) + (@f * c2) + (1 * e2)
172
- newf = (@e * b2) + (@f * d2) + (1 * f2)
197
+ newa = (@a * a2) + (@b * c2) + (e2 * 0)
198
+ newb = (@a * b2) + (@b * d2) + (f2 * 0)
199
+ newc = (@c * a2) + (@d * c2) + (e2 * 0)
200
+ newd = (@c * b2) + (@d * d2) + (f2 * 0)
201
+ newe = (@e * a2) + (@f * c2) + (e2 * 1)
202
+ newf = (@e * b2) + (@f * d2) + (f2 * 1)
173
203
  @a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
174
204
  end
175
205
 
@@ -183,6 +213,7 @@ class PDF::Reader
183
213
  # [ c d 0 ] x [ c d 0 ]
184
214
  # [ e f 1 ] [ e f 1 ]
185
215
  #
216
+ #: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void
186
217
  def faster_multiply!(a2,b2,c2, d2,e2,f2)
187
218
  newa = (@a * a2) + (@b * c2)
188
219
  newb = (@a * b2) + (@b * d2)
@@ -9,6 +9,20 @@ module PDF
9
9
  #
10
10
  class TypeCheck
11
11
 
12
+ #: (untyped) -> Integer
13
+ def self.cast_to_int!(obj)
14
+ if obj.is_a?(Integer)
15
+ obj
16
+ elsif obj.nil?
17
+ 0
18
+ elsif obj.respond_to?(:to_i)
19
+ obj.to_i
20
+ else
21
+ raise MalformedPDFError, "Unable to cast to integer"
22
+ end
23
+ end
24
+
25
+ #: (untyped) -> Numeric
12
26
  def self.cast_to_numeric!(obj)
13
27
  if obj.is_a?(Numeric)
14
28
  obj
@@ -23,6 +37,7 @@ module PDF
23
37
  end
24
38
  end
25
39
 
40
+ #: (untyped) -> String
26
41
  def self.cast_to_string!(string)
27
42
  if string.is_a?(String)
28
43
  string
@@ -35,6 +50,7 @@ module PDF
35
50
  end
36
51
  end
37
52
 
53
+ #: (untyped) -> Symbol | nil
38
54
  def self.cast_to_symbol(obj)
39
55
  if obj.is_a?(Symbol)
40
56
  obj
@@ -46,6 +62,43 @@ module PDF
46
62
  raise MalformedPDFError, "Unable to cast to symbol"
47
63
  end
48
64
  end
65
+
66
+ #: (untyped) -> Symbol
67
+ def self.cast_to_symbol!(obj)
68
+ res = cast_to_symbol(obj)
69
+ if res.nil?
70
+ raise MalformedPDFError, "Unable to cast to symbol"
71
+ else
72
+ res
73
+ end
74
+ end
75
+
76
+ #: (untyped) -> Hash[Symbol, untyped]
77
+ def self.cast_to_pdf_dict!(obj)
78
+ if obj.is_a?(Hash)
79
+ obj
80
+ elsif obj.respond_to?(:to_h)
81
+ obj.to_h
82
+ else
83
+ raise MalformedPDFError, "Unable to cast to hash"
84
+ end
85
+ end
86
+
87
+ #: (untyped) -> Hash[Symbol, PDF::Reader::Stream]
88
+ def self.cast_to_pdf_dict_with_stream_values!(obj)
89
+ if obj.is_a?(Hash)
90
+ result = Hash.new
91
+ obj.each do |k, v|
92
+ raise MalformedPDFError, "Expected a stream" unless v.is_a?(PDF::Reader::Stream)
93
+ result[cast_to_symbol!(k)] = v
94
+ end
95
+ result
96
+ elsif obj.respond_to?(:to_h)
97
+ cast_to_pdf_dict_with_stream_values!(obj.to_h)
98
+ else
99
+ raise MalformedPDFError, "Unable to cast to hash"
100
+ end
101
+ end
49
102
  end
50
103
  end
51
104
  end
@@ -7,10 +7,12 @@ class PDF::Reader
7
7
  # Security handler for when we don't support the flavour of encryption
8
8
  # used in a PDF.
9
9
  class UnimplementedSecurityHandler
10
+ #: (Hash[Symbol, untyped]) -> bool
10
11
  def self.supports?(encrypt)
11
12
  true
12
13
  end
13
14
 
15
+ #: (String, PDF::Reader::Reference) -> String
14
16
  def decrypt(buf, ref)
15
17
  raise PDF::Reader::EncryptedPDFError, "Unsupported encryption style"
16
18
  end
@@ -16,10 +16,12 @@ module PDF
16
16
  # Not all operators have type safety implemented yet, but we can expand the number over time.
17
17
  class ValidatingReceiver
18
18
 
19
+ #: (untyped) -> void
19
20
  def initialize(wrapped)
20
21
  @wrapped = wrapped
21
22
  end
22
23
 
24
+ #: (PDF::Reader::Page) -> void
23
25
  def page=(page)
24
26
  call_wrapped(:page=, page)
25
27
  end
@@ -27,10 +29,12 @@ module PDF
27
29
  #####################################################
28
30
  # Graphics State Operators
29
31
  #####################################################
32
+ #: (*untyped) -> void
30
33
  def save_graphics_state(*args)
31
34
  call_wrapped(:save_graphics_state)
32
35
  end
33
36
 
37
+ #: (*untyped) -> void
34
38
  def restore_graphics_state(*args)
35
39
  call_wrapped(:restore_graphics_state)
36
40
  end
@@ -39,6 +43,7 @@ module PDF
39
43
  # Matrix Operators
40
44
  #####################################################
41
45
 
46
+ #: (*untyped) -> void
42
47
  def concatenate_matrix(*args)
43
48
  a, b, c, d, e, f = *args
44
49
  call_wrapped(
@@ -56,10 +61,12 @@ module PDF
56
61
  # Text Object Operators
57
62
  #####################################################
58
63
 
64
+ #: (*untyped) -> void
59
65
  def begin_text_object(*args)
60
66
  call_wrapped(:begin_text_object)
61
67
  end
62
68
 
69
+ #: (*untyped) -> void
63
70
  def end_text_object(*args)
64
71
  call_wrapped(:end_text_object)
65
72
  end
@@ -67,6 +74,7 @@ module PDF
67
74
  #####################################################
68
75
  # Text State Operators
69
76
  #####################################################
77
+ #: (*untyped) -> void
70
78
  def set_character_spacing(*args)
71
79
  char_spacing, _ = *args
72
80
  call_wrapped(
@@ -75,6 +83,7 @@ module PDF
75
83
  )
76
84
  end
77
85
 
86
+ #: (*untyped) -> void
78
87
  def set_horizontal_text_scaling(*args)
79
88
  h_scaling, _ = *args
80
89
  call_wrapped(
@@ -83,6 +92,7 @@ module PDF
83
92
  )
84
93
  end
85
94
 
95
+ #: (*untyped) -> void
86
96
  def set_text_font_and_size(*args)
87
97
  label, size, _ = *args
88
98
  call_wrapped(
@@ -92,6 +102,7 @@ module PDF
92
102
  )
93
103
  end
94
104
 
105
+ #: (*untyped) -> void
95
106
  def set_text_leading(*args)
96
107
  leading, _ = *args
97
108
  call_wrapped(
@@ -100,6 +111,7 @@ module PDF
100
111
  )
101
112
  end
102
113
 
114
+ #: (*untyped) -> void
103
115
  def set_text_rendering_mode(*args)
104
116
  mode, _ = *args
105
117
  call_wrapped(
@@ -108,6 +120,7 @@ module PDF
108
120
  )
109
121
  end
110
122
 
123
+ #: (*untyped) -> void
111
124
  def set_text_rise(*args)
112
125
  rise, _ = *args
113
126
  call_wrapped(
@@ -116,6 +129,7 @@ module PDF
116
129
  )
117
130
  end
118
131
 
132
+ #: (*untyped) -> void
119
133
  def set_word_spacing(*args)
120
134
  word_spacing, _ = *args
121
135
  call_wrapped(
@@ -128,6 +142,7 @@ module PDF
128
142
  # Text Positioning Operators
129
143
  #####################################################
130
144
 
145
+ #: (*untyped) -> void
131
146
  def move_text_position(*args) # Td
132
147
  x, y, _ = *args
133
148
  call_wrapped(
@@ -137,6 +152,7 @@ module PDF
137
152
  )
138
153
  end
139
154
 
155
+ #: (*untyped) -> void
140
156
  def move_text_position_and_set_leading(*args) # TD
141
157
  x, y, _ = *args
142
158
  call_wrapped(
@@ -146,6 +162,7 @@ module PDF
146
162
  )
147
163
  end
148
164
 
165
+ #: (*untyped) -> void
149
166
  def set_text_matrix_and_text_line_matrix(*args) # Tm
150
167
  a, b, c, d, e, f = *args
151
168
  call_wrapped(
@@ -159,6 +176,7 @@ module PDF
159
176
  )
160
177
  end
161
178
 
179
+ #: (*untyped) -> void
162
180
  def move_to_start_of_next_line(*args) # T*
163
181
  call_wrapped(:move_to_start_of_next_line)
164
182
  end
@@ -166,6 +184,7 @@ module PDF
166
184
  #####################################################
167
185
  # Text Showing Operators
168
186
  #####################################################
187
+ #: (*untyped) -> void
169
188
  def show_text(*args) # Tj (AWAY)
170
189
  string, _ = *args
171
190
  call_wrapped(
@@ -174,6 +193,7 @@ module PDF
174
193
  )
175
194
  end
176
195
 
196
+ #: (*untyped) -> void
177
197
  def show_text_with_positioning(*args) # TJ [(A) 120 (WA) 20 (Y)]
178
198
  params, _ = *args
179
199
  unless params.is_a?(Array)
@@ -186,6 +206,7 @@ module PDF
186
206
  )
187
207
  end
188
208
 
209
+ #: (*untyped) -> void
189
210
  def move_to_next_line_and_show_text(*args) # '
190
211
  string, _ = *args
191
212
  call_wrapped(
@@ -194,6 +215,7 @@ module PDF
194
215
  )
195
216
  end
196
217
 
218
+ #: (*untyped) -> void
197
219
  def set_spacing_next_line_show_text(*args) # "
198
220
  aw, ac, string = *args
199
221
  call_wrapped(
@@ -208,6 +230,7 @@ module PDF
208
230
  # Form XObject Operators
209
231
  #####################################################
210
232
 
233
+ #: (*untyped) -> void
211
234
  def invoke_xobject(*args)
212
235
  label, _ = *args
213
236
 
@@ -221,16 +244,19 @@ module PDF
221
244
  # Inline Image Operators
222
245
  #####################################################
223
246
 
247
+ #: (*untyped) -> void
224
248
  def begin_inline_image(*args)
225
249
  call_wrapped(:begin_inline_image)
226
250
  end
227
251
 
252
+ #: (*untyped) -> void
228
253
  def begin_inline_image_data(*args)
229
254
  # We can't use call_wrapped() here because sorbet won't allow splat args with a dynamic
230
255
  # number of elements
231
256
  @wrapped.begin_inline_image_data(*args) if @wrapped.respond_to?(:begin_inline_image_data)
232
257
  end
233
258
 
259
+ #: (*untyped) -> void
234
260
  def end_inline_image(*args)
235
261
  data, _ = *args
236
262
 
@@ -244,16 +270,19 @@ module PDF
244
270
  # Final safety net for any operators that don't have type checking enabled yet
245
271
  #####################################################
246
272
 
273
+ #: (untyped) -> bool
247
274
  def respond_to?(meth)
248
275
  @wrapped.respond_to?(meth)
249
276
  end
250
277
 
278
+ #: (Symbol, *untyped) -> void
251
279
  def method_missing(methodname, *args)
252
280
  @wrapped.send(methodname, *args)
253
281
  end
254
282
 
255
283
  private
256
284
 
285
+ #: (untyped, *untyped) -> void
257
286
  def call_wrapped(methodname, *args)
258
287
  @wrapped.send(methodname, *args) if @wrapped.respond_to?(methodname)
259
288
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # typed: true
2
+ # typed: strict
3
3
  # frozen_string_literal: true
4
4
 
5
5
  require 'afm'
@@ -12,6 +12,7 @@ class PDF::Reader
12
12
  # the reader is expected to have it's own copy of the font metrics.
13
13
  # see Section 9.6.2.2, PDF 32000-1:2008, pp 256
14
14
  class BuiltIn
15
+ @@all_metrics = nil #: PDF::Reader::SynchronizedCache | nil
15
16
 
16
17
  BUILTINS = [
17
18
  :Courier, :"Courier-Bold", :"Courier-BoldOblique", :"Courier-Oblique",
@@ -19,11 +20,13 @@ class PDF::Reader
19
20
  :Symbol,
20
21
  :"Times-Roman", :"Times-Bold", :"Times-BoldItalic", :"Times-Italic",
21
22
  :ZapfDingbats
22
- ]
23
+ ] #: Array[Symbol]
23
24
 
25
+ #: (PDF::Reader::Font) -> void
24
26
  def initialize(font)
25
27
  @font = font
26
28
  @@all_metrics ||= PDF::Reader::SynchronizedCache.new
29
+ @metrics = nil #: AFM::Font?
27
30
 
28
31
  basefont = extract_basefont(font.basefont)
29
32
  metrics_path = File.join(File.dirname(__FILE__), "..","afm","#{basefont}.afm")
@@ -35,8 +38,10 @@ class PDF::Reader
35
38
  end
36
39
  end
37
40
 
41
+ #: (Integer?) -> Numeric
38
42
  def glyph_width(code_point)
39
- return 0 if code_point.nil? || code_point < 0
43
+ return 0 if code_point.nil? || code_point < 0 || @metrics.nil?
44
+
40
45
 
41
46
  names = @font.encoding.int_to_name(code_point)
42
47
  metrics = names.map { |name|
@@ -52,13 +57,16 @@ class PDF::Reader
52
57
 
53
58
  private
54
59
 
60
+ #: (Integer) -> bool
55
61
  def control_character?(code_point)
56
- @font.encoding.int_to_name(code_point).first.to_s[/\Acontrol..\Z/]
62
+ match = @font.encoding.int_to_name(code_point).first.to_s[/\Acontrol..\Z/]
63
+ match ? true : false
57
64
  end
58
65
 
66
+ #: (Symbol?) -> String
59
67
  def extract_basefont(font_name)
60
68
  if BUILTINS.include?(font_name)
61
- font_name
69
+ font_name.to_s
62
70
  else
63
71
  "Times-Roman"
64
72
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # typed: true
2
+ # typed: strict
3
3
  # frozen_string_literal: true
4
4
 
5
5
  class PDF::Reader
@@ -12,17 +12,25 @@ class PDF::Reader
12
12
  # see Section 9.7.4.1, PDF 32000-1:2008, pp 269-270
13
13
  class Composite
14
14
 
15
+ #: (PDF::Reader::Font) -> void
15
16
  def initialize(font)
16
17
  @font = font
17
- @widths = PDF::Reader::CidWidths.new(@font.cid_default_width, @font.cid_widths)
18
+ @widths = PDF::Reader::CidWidths.new(
19
+ @font.cid_default_width, @font.cid_widths
20
+ ) #: PDF::Reader::CidWidths
18
21
  end
19
22
 
23
+ #: (Integer?) -> Numeric
20
24
  def glyph_width(code_point)
21
25
  return 0 if code_point.nil? || code_point < 0
22
26
 
23
27
  w = @widths[code_point]
24
28
  # 0 is a valid width
25
- return w.to_f unless w.nil?
29
+ if w
30
+ w.to_f
31
+ else
32
+ 0
33
+ end
26
34
  end
27
35
  end
28
36
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # typed: true
2
+ # typed: strict
3
3
  # frozen_string_literal: true
4
4
 
5
5
  class PDF::Reader
@@ -7,16 +7,18 @@ class PDF::Reader
7
7
  # Calculates the width of a glyph in a TrueType font
8
8
  class TrueType
9
9
 
10
+ #: (PDF::Reader::Font) -> void
10
11
  def initialize(font)
11
12
  @font = font
12
13
 
13
- if @font.font_descriptor
14
- @missing_width = @font.font_descriptor.missing_width
14
+ if fd = @font.font_descriptor
15
+ @missing_width = fd.missing_width #: Numeric
15
16
  else
16
17
  @missing_width = 0
17
18
  end
18
19
  end
19
20
 
21
+ #: (Integer?) -> Numeric
20
22
  def glyph_width(code_point)
21
23
  return 0 if code_point.nil? || code_point < 0
22
24
  glyph_width_from_font(code_point) || glyph_width_from_descriptor(code_point) || 0
@@ -25,30 +27,30 @@ class PDF::Reader
25
27
  private
26
28
 
27
29
  #TODO convert Type3 units 1000 units => 1 text space unit
30
+ #: (Integer) -> Numeric?
28
31
  def glyph_width_from_font(code_point)
29
32
  return if @font.widths.nil? || @font.widths.count == 0
30
33
 
31
34
  # in ruby a negative index is valid, and will go from the end of the array
32
35
  # which is undesireable in this case.
33
- if @font.first_char && @font.first_char <= code_point
34
- @font.widths.fetch(code_point - @font.first_char, @missing_width).to_f
36
+ first_char = @font.first_char
37
+ if first_char && first_char <= code_point
38
+ @font.widths.fetch(code_point - first_char, @missing_width.to_i).to_f
35
39
  else
36
40
  @missing_width.to_f
37
41
  end
38
42
  end
39
43
 
44
+ #: (Integer) -> Numeric?
40
45
  def glyph_width_from_descriptor(code_point)
41
- return unless @font.font_descriptor
42
-
43
46
  # true type fonts will have most of their information contained
44
47
  # with-in a program inside the font descriptor, however the widths
45
48
  # may not be in standard PDF glyph widths (1000 units => 1 text space unit)
46
49
  # so this width will need to be scaled
47
- w = @font.font_descriptor.glyph_width(code_point)
48
- if w
49
- w.to_f * @font.font_descriptor.glyph_to_pdf_scale_factor
50
- else
51
- nil
50
+ if fd = @font.font_descriptor
51
+ if w = fd.glyph_width(code_point)
52
+ w.to_f * fd.glyph_to_pdf_scale_factor.to_f
53
+ end
52
54
  end
53
55
  end
54
56
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # typed: true
2
+ # typed: strict
3
3
  # frozen_string_literal: true
4
4
 
5
5
  class PDF::Reader
@@ -7,24 +7,27 @@ class PDF::Reader
7
7
  # Calculates the width of a glyph in a Type One or Type Three
8
8
  class TypeOneOrThree
9
9
 
10
+ #: (PDF::Reader::Font) -> void
10
11
  def initialize(font)
11
12
  @font = font
12
13
 
13
- if @font.font_descriptor
14
- @missing_width = @font.font_descriptor.missing_width
14
+ if fd = @font.font_descriptor
15
+ @missing_width = fd.missing_width #: Numeric
15
16
  else
16
17
  @missing_width = 0
17
18
  end
18
19
  end
19
20
 
21
+ #: (Integer?) -> Numeric
20
22
  def glyph_width(code_point)
21
23
  return 0 if code_point.nil? || code_point < 0
22
24
  return 0 if @font.widths.nil? || @font.widths.count == 0
23
25
 
24
26
  # in ruby a negative index is valid, and will go from the end of the array
25
27
  # which is undesireable in this case.
26
- if @font.first_char <= code_point
27
- @font.widths.fetch(code_point - @font.first_char, @missing_width).to_f
28
+ first_char = @font.first_char
29
+ if first_char && first_char <= code_point
30
+ @font.widths.fetch(code_point - first_char, @missing_width.to_i).to_f
28
31
  else
29
32
  @missing_width.to_f
30
33
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # typed: true
2
+ # typed: strict
3
3
  # frozen_string_literal: true
4
4
 
5
5
  class PDF::Reader
@@ -11,15 +11,20 @@ class PDF::Reader
11
11
  # the descendant font
12
12
  class TypeZero
13
13
 
14
+ #: (PDF::Reader::Font) -> void
14
15
  def initialize(font)
15
16
  @font = font
16
- @descendant_font = @font.descendantfonts.first
17
17
  end
18
18
 
19
+ #: (Integer?) -> Numeric
19
20
  def glyph_width(code_point)
20
21
  return 0 if code_point.nil? || code_point < 0
21
22
 
22
- @descendant_font.glyph_width(code_point).to_f
23
+ if descendant_font = @font.descendantfonts.first
24
+ descendant_font.glyph_width(code_point).to_f
25
+ else
26
+ 0
27
+ end
23
28
  end
24
29
  end
25
30
  end