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.
- checksums.yaml +4 -4
- data/CHANGELOG +39 -0
- data/README.md +33 -33
- data/Rakefile +2 -2
- data/lib/pdf/reader/advanced_text_run_filter.rb +152 -0
- data/lib/pdf/reader/aes_v2_security_handler.rb +30 -0
- data/lib/pdf/reader/aes_v3_security_handler.rb +35 -3
- data/lib/pdf/reader/bounding_rectangle_runs_filter.rb +1 -0
- data/lib/pdf/reader/buffer.rb +39 -22
- data/lib/pdf/reader/cid_widths.rb +14 -6
- data/lib/pdf/reader/cmap.rb +16 -5
- data/lib/pdf/reader/encoding.rb +42 -18
- data/lib/pdf/reader/error.rb +6 -4
- data/lib/pdf/reader/filter/ascii85.rb +2 -0
- data/lib/pdf/reader/filter/ascii_hex.rb +2 -0
- data/lib/pdf/reader/filter/depredict.rb +6 -2
- data/lib/pdf/reader/filter/flate.rb +5 -2
- data/lib/pdf/reader/filter/lzw.rb +2 -0
- data/lib/pdf/reader/filter/null.rb +2 -0
- data/lib/pdf/reader/filter/run_length.rb +2 -0
- data/lib/pdf/reader/filter.rb +1 -0
- data/lib/pdf/reader/font.rb +99 -32
- data/lib/pdf/reader/font_descriptor.rb +79 -24
- data/lib/pdf/reader/form_xobject.rb +15 -1
- data/lib/pdf/reader/glyph_hash.rb +41 -8
- data/lib/pdf/reader/key_builder_v5.rb +17 -9
- data/lib/pdf/reader/lzw.rb +42 -16
- data/lib/pdf/reader/no_text_filter.rb +15 -0
- data/lib/pdf/reader/null_security_handler.rb +1 -0
- data/lib/pdf/reader/object_cache.rb +7 -2
- data/lib/pdf/reader/object_hash.rb +129 -16
- data/lib/pdf/reader/object_stream.rb +22 -5
- data/lib/pdf/reader/overlapping_runs_filter.rb +8 -2
- data/lib/pdf/reader/page.rb +66 -13
- data/lib/pdf/reader/page_layout.rb +26 -9
- data/lib/pdf/reader/page_state.rb +12 -3
- data/lib/pdf/reader/page_text_receiver.rb +16 -2
- data/lib/pdf/reader/pages_strategy.rb +1 -1
- data/lib/pdf/reader/parser.rb +52 -13
- data/lib/pdf/reader/point.rb +9 -2
- data/lib/pdf/reader/print_receiver.rb +2 -6
- data/lib/pdf/reader/rc4_security_handler.rb +2 -0
- data/lib/pdf/reader/rectangle.rb +24 -1
- data/lib/pdf/reader/reference.rb +13 -3
- data/lib/pdf/reader/register_receiver.rb +15 -2
- data/lib/pdf/reader/resources.rb +12 -2
- data/lib/pdf/reader/security_handler_factory.rb +13 -0
- data/lib/pdf/reader/standard_key_builder.rb +37 -23
- data/lib/pdf/reader/stream.rb +9 -3
- data/lib/pdf/reader/synchronized_cache.rb +6 -3
- data/lib/pdf/reader/text_run.rb +33 -3
- data/lib/pdf/reader/token.rb +1 -0
- data/lib/pdf/reader/transformation_matrix.rb +41 -10
- data/lib/pdf/reader/type_check.rb +53 -0
- data/lib/pdf/reader/unimplemented_security_handler.rb +2 -0
- data/lib/pdf/reader/validating_receiver.rb +29 -0
- data/lib/pdf/reader/width_calculator/built_in.rb +13 -5
- data/lib/pdf/reader/width_calculator/composite.rb +11 -3
- data/lib/pdf/reader/width_calculator/true_type.rb +14 -12
- data/lib/pdf/reader/width_calculator/type_one_or_three.rb +8 -5
- data/lib/pdf/reader/width_calculator/type_zero.rb +8 -3
- data/lib/pdf/reader/xref.rb +31 -10
- data/lib/pdf/reader/zero_width_runs_filter.rb +1 -0
- data/lib/pdf/reader.rb +24 -12
- data/rbi/pdf-reader.rbi +1504 -1480
- metadata +34 -17
@@ -1,5 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# typed:
|
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
|
-
|
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
|
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
|
-
|
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) + (
|
168
|
-
newb = (@a * b2) + (@b * d2) + (
|
169
|
-
newc = (@c * a2) + (@d * c2) + (
|
170
|
-
newd = (@c * b2) + (@d * d2) + (
|
171
|
-
newe = (@e * a2) + (@f * c2) + (
|
172
|
-
newf = (@e * b2) + (@f * d2) + (
|
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:
|
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:
|
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(
|
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
|
-
|
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:
|
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 =
|
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
|
-
|
34
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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:
|
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 =
|
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
|
-
|
27
|
-
|
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:
|
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
|
-
@
|
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
|