prawn 2.3.0 → 2.5.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
- checksums.yaml.gz.sig +0 -0
- data/lib/prawn/document/bounding_box.rb +223 -143
- data/lib/prawn/document/column_box.rb +61 -26
- data/lib/prawn/document/internals.rb +25 -16
- data/lib/prawn/document/span.rb +21 -18
- data/lib/prawn/document.rb +273 -182
- data/lib/prawn/encoding.rb +2 -5
- data/lib/prawn/errors.rb +23 -34
- data/lib/prawn/font.rb +254 -139
- data/lib/prawn/font_metric_cache.rb +18 -16
- data/lib/prawn/fonts/afm.rb +99 -57
- data/lib/prawn/fonts/dfont.rb +7 -1
- data/lib/prawn/fonts/otf.rb +4 -1
- data/lib/prawn/fonts/to_unicode_cmap.rb +151 -0
- data/lib/prawn/fonts/ttc.rb +7 -2
- data/lib/prawn/fonts/ttf.rb +345 -107
- data/lib/prawn/fonts.rb +14 -0
- data/lib/prawn/graphics/blend_mode.rb +25 -28
- data/lib/prawn/graphics/cap_style.rb +9 -12
- data/lib/prawn/graphics/color.rb +75 -50
- data/lib/prawn/graphics/dash.rb +45 -42
- data/lib/prawn/graphics/join_style.rb +18 -12
- data/lib/prawn/graphics/patterns.rb +239 -110
- data/lib/prawn/graphics/transformation.rb +51 -44
- data/lib/prawn/graphics/transparency.rb +16 -40
- data/lib/prawn/graphics.rb +370 -260
- data/lib/prawn/grid.rb +219 -57
- data/lib/prawn/image_handler.rb +27 -10
- data/lib/prawn/images/image.rb +8 -10
- data/lib/prawn/images/jpg.rb +46 -20
- data/lib/prawn/images/png.rb +94 -42
- data/lib/prawn/images.rb +70 -81
- data/lib/prawn/measurement_extensions.rb +39 -8
- data/lib/prawn/measurements.rb +60 -5
- data/lib/prawn/outline.rb +120 -113
- data/lib/prawn/repeater.rb +52 -36
- data/lib/prawn/security/arcfour.rb +4 -4
- data/lib/prawn/security.rb +106 -98
- data/lib/prawn/soft_mask.rb +42 -30
- data/lib/prawn/stamp.rb +38 -42
- data/lib/prawn/text/box.rb +156 -105
- data/lib/prawn/text/formatted/arranger.rb +121 -41
- data/lib/prawn/text/formatted/box.rb +239 -163
- data/lib/prawn/text/formatted/fragment.rb +130 -14
- data/lib/prawn/text/formatted/line_wrap.rb +49 -38
- data/lib/prawn/text/formatted/parser.rb +116 -74
- data/lib/prawn/text/formatted/wrap.rb +25 -26
- data/lib/prawn/text/formatted.rb +75 -0
- data/lib/prawn/text.rb +456 -211
- data/lib/prawn/transformation_stack.rb +29 -10
- data/lib/prawn/utilities.rb +13 -13
- data/lib/prawn/version.rb +2 -1
- data/lib/prawn/view.rb +69 -54
- data/lib/prawn.rb +24 -18
- data.tar.gz.sig +0 -0
- metadata +55 -262
- metadata.gz.sig +3 -4
- data/.yardopts +0 -10
- data/Gemfile +0 -5
- data/Rakefile +0 -54
- data/manual/absolute_position.pdf +0 -0
- data/manual/basic_concepts/adding_pages.rb +0 -26
- data/manual/basic_concepts/basic_concepts.rb +0 -43
- data/manual/basic_concepts/creation.rb +0 -38
- data/manual/basic_concepts/cursor.rb +0 -32
- data/manual/basic_concepts/measurement.rb +0 -24
- data/manual/basic_concepts/origin.rb +0 -37
- data/manual/basic_concepts/other_cursor_helpers.rb +0 -39
- data/manual/basic_concepts/view.rb +0 -48
- data/manual/bounding_box/bounding_box.rb +0 -41
- data/manual/bounding_box/bounds.rb +0 -48
- data/manual/bounding_box/canvas.rb +0 -23
- data/manual/bounding_box/creation.rb +0 -22
- data/manual/bounding_box/indentation.rb +0 -45
- data/manual/bounding_box/nesting.rb +0 -52
- data/manual/bounding_box/russian_boxes.rb +0 -40
- data/manual/bounding_box/stretchy.rb +0 -29
- data/manual/contents.rb +0 -35
- data/manual/cover.rb +0 -43
- data/manual/document_and_page_options/background.rb +0 -25
- data/manual/document_and_page_options/document_and_page_options.rb +0 -34
- data/manual/document_and_page_options/metadata.rb +0 -25
- data/manual/document_and_page_options/page_margins.rb +0 -36
- data/manual/document_and_page_options/page_size.rb +0 -34
- data/manual/document_and_page_options/print_scaling.rb +0 -22
- data/manual/example_helper.rb +0 -8
- data/manual/graphics/blend_mode.rb +0 -52
- data/manual/graphics/circle_and_ellipse.rb +0 -21
- data/manual/graphics/color.rb +0 -22
- data/manual/graphics/common_lines.rb +0 -29
- data/manual/graphics/fill_and_stroke.rb +0 -41
- data/manual/graphics/fill_rules.rb +0 -37
- data/manual/graphics/gradients.rb +0 -43
- data/manual/graphics/graphics.rb +0 -64
- data/manual/graphics/helper.rb +0 -27
- data/manual/graphics/line_width.rb +0 -36
- data/manual/graphics/lines_and_curves.rb +0 -40
- data/manual/graphics/polygon.rb +0 -27
- data/manual/graphics/rectangle.rb +0 -20
- data/manual/graphics/rotate.rb +0 -25
- data/manual/graphics/scale.rb +0 -42
- data/manual/graphics/soft_masks.rb +0 -44
- data/manual/graphics/stroke_cap.rb +0 -30
- data/manual/graphics/stroke_dash.rb +0 -47
- data/manual/graphics/stroke_join.rb +0 -29
- data/manual/graphics/translate.rb +0 -28
- data/manual/graphics/transparency.rb +0 -33
- data/manual/how_to_read_this_manual.rb +0 -39
- data/manual/images/absolute_position.rb +0 -22
- data/manual/images/fit.rb +0 -20
- data/manual/images/horizontal.rb +0 -24
- data/manual/images/images.rb +0 -41
- data/manual/images/plain_image.rb +0 -17
- data/manual/images/scale.rb +0 -21
- data/manual/images/vertical.rb +0 -27
- data/manual/images/width_and_height.rb +0 -24
- data/manual/layout/boxes.rb +0 -26
- data/manual/layout/content.rb +0 -24
- data/manual/layout/layout.rb +0 -27
- data/manual/layout/simple_grid.rb +0 -22
- data/manual/outline/add_subsection_to.rb +0 -60
- data/manual/outline/insert_section_after.rb +0 -46
- data/manual/outline/outline.rb +0 -33
- data/manual/outline/sections_and_pages.rb +0 -66
- data/manual/repeatable_content/alternate_page_numbering.rb +0 -36
- data/manual/repeatable_content/page_numbering.rb +0 -55
- data/manual/repeatable_content/repeatable_content.rb +0 -35
- data/manual/repeatable_content/repeater.rb +0 -54
- data/manual/repeatable_content/stamp.rb +0 -40
- data/manual/security/encryption.rb +0 -28
- data/manual/security/permissions.rb +0 -41
- data/manual/security/security.rb +0 -28
- data/manual/table.rb +0 -16
- data/manual/text/alignment.rb +0 -43
- data/manual/text/color.rb +0 -24
- data/manual/text/column_box.rb +0 -30
- data/manual/text/fallback_fonts.rb +0 -41
- data/manual/text/font.rb +0 -40
- data/manual/text/font_size.rb +0 -44
- data/manual/text/font_style.rb +0 -22
- data/manual/text/formatted_callbacks.rb +0 -65
- data/manual/text/formatted_text.rb +0 -58
- data/manual/text/free_flowing_text.rb +0 -50
- data/manual/text/inline.rb +0 -40
- data/manual/text/kerning_and_character_spacing.rb +0 -38
- data/manual/text/leading.rb +0 -24
- data/manual/text/line_wrapping.rb +0 -60
- data/manual/text/paragraph_indentation.rb +0 -32
- data/manual/text/positioned_text.rb +0 -37
- data/manual/text/registering_families.rb +0 -51
- data/manual/text/rendering_and_color.rb +0 -36
- data/manual/text/right_to_left_text.rb +0 -54
- data/manual/text/rotation.rb +0 -47
- data/manual/text/single_usage.rb +0 -36
- data/manual/text/text.rb +0 -75
- data/manual/text/text_box_excess.rb +0 -35
- data/manual/text/text_box_extensions.rb +0 -48
- data/manual/text/text_box_overflow.rb +0 -49
- data/manual/text/utf8.rb +0 -27
- data/manual/text/win_ansi_charset.rb +0 -62
- data/prawn.gemspec +0 -57
- data/spec/data/curves.pdf +0 -66
- data/spec/extensions/encoding_helpers.rb +0 -11
- data/spec/prawn/document/bounding_box_spec.rb +0 -546
- data/spec/prawn/document/column_box_spec.rb +0 -75
- data/spec/prawn/document/security_spec.rb +0 -176
- data/spec/prawn/document_annotations_spec.rb +0 -76
- data/spec/prawn/document_destinations_spec.rb +0 -15
- data/spec/prawn/document_grid_spec.rb +0 -99
- data/spec/prawn/document_reference_spec.rb +0 -27
- data/spec/prawn/document_span_spec.rb +0 -36
- data/spec/prawn/document_spec.rb +0 -802
- data/spec/prawn/font_metric_cache_spec.rb +0 -54
- data/spec/prawn/font_spec.rb +0 -542
- data/spec/prawn/graphics/blend_mode_spec.rb +0 -63
- data/spec/prawn/graphics/transparency_spec.rb +0 -81
- data/spec/prawn/graphics_spec.rb +0 -837
- data/spec/prawn/graphics_stroke_styles_spec.rb +0 -229
- data/spec/prawn/image_handler_spec.rb +0 -53
- data/spec/prawn/images/jpg_spec.rb +0 -20
- data/spec/prawn/images/png_spec.rb +0 -283
- data/spec/prawn/images_spec.rb +0 -224
- data/spec/prawn/measurements_extensions_spec.rb +0 -24
- data/spec/prawn/outline_spec.rb +0 -412
- data/spec/prawn/repeater_spec.rb +0 -165
- data/spec/prawn/soft_mask_spec.rb +0 -74
- data/spec/prawn/stamp_spec.rb +0 -172
- data/spec/prawn/text/box_spec.rb +0 -1112
- data/spec/prawn/text/formatted/arranger_spec.rb +0 -466
- data/spec/prawn/text/formatted/box_spec.rb +0 -846
- data/spec/prawn/text/formatted/fragment_spec.rb +0 -343
- data/spec/prawn/text/formatted/line_wrap_spec.rb +0 -494
- data/spec/prawn/text/formatted/parser_spec.rb +0 -697
- data/spec/prawn/text_draw_text_spec.rb +0 -149
- data/spec/prawn/text_rendering_mode_spec.rb +0 -48
- data/spec/prawn/text_spacing_spec.rb +0 -95
- data/spec/prawn/text_spec.rb +0 -603
- data/spec/prawn/text_with_inline_formatting_spec.rb +0 -35
- data/spec/prawn/transformation_stack_spec.rb +0 -66
- data/spec/prawn/view_spec.rb +0 -63
- data/spec/prawn_manual_spec.rb +0 -35
- data/spec/spec_helper.rb +0 -48
data/lib/prawn/fonts/ttf.rb
CHANGED
@@ -9,22 +9,138 @@
|
|
9
9
|
|
10
10
|
require 'ttfunk'
|
11
11
|
require 'ttfunk/subset_collection'
|
12
|
+
require_relative 'to_unicode_cmap'
|
12
13
|
|
13
14
|
module Prawn
|
14
15
|
module Fonts
|
15
|
-
#
|
16
|
+
# TrueType font.
|
17
|
+
#
|
18
|
+
# @note You shouldn't use this class directly.
|
16
19
|
class TTF < Font
|
17
|
-
|
20
|
+
# TrueType font error.
|
21
|
+
class Error < StandardError
|
22
|
+
# @private
|
23
|
+
DEFAULT_MESSAGE = 'TTF font error'
|
18
24
|
|
25
|
+
# @private
|
26
|
+
MESSAGE_WITH_FONT = 'TTF font error in font %<font>s'
|
27
|
+
|
28
|
+
def initialize(message = DEFAULT_MESSAGE, font: nil)
|
29
|
+
if font && message == DEFAULT_MESSAGE
|
30
|
+
super(format(MESSAGE_WITH_FONT, font: font))
|
31
|
+
else
|
32
|
+
super(message)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Signals absence of a Unicode character map in the font.
|
38
|
+
class NoUnicodeCMap < Error
|
39
|
+
# @private
|
40
|
+
DEFAULT_MESSAGE = 'No unicode cmap found in font'
|
41
|
+
|
42
|
+
# @private
|
43
|
+
MESSAGE_WITH_FONT = 'No unicode cmap found in font %<font>s'
|
44
|
+
end
|
45
|
+
|
46
|
+
# Signals absense of a PostScript font name.
|
47
|
+
class NoPostscriptName < Error
|
48
|
+
# @private
|
49
|
+
DEFAULT_MESSAGE = 'Can not detect a postscript name'
|
50
|
+
|
51
|
+
# @private
|
52
|
+
MESSAGE_WITH_FONT = 'Can not detect a postscript name in font %<font>s'
|
53
|
+
end
|
54
|
+
|
55
|
+
# TTFunk font.
|
56
|
+
# @return [TTFunk::File]
|
57
|
+
attr_reader :ttf
|
58
|
+
attr_reader :subsets
|
59
|
+
|
60
|
+
# Does this font support Unicode?
|
61
|
+
#
|
62
|
+
# @return [true]
|
19
63
|
def unicode?
|
20
64
|
true
|
21
65
|
end
|
22
66
|
|
67
|
+
# An adapter for subset collection to represent a full font.
|
68
|
+
#
|
69
|
+
# @private
|
70
|
+
class FullFontSubsetsCollection
|
71
|
+
FULL_FONT = Object.new.tap do |obj|
|
72
|
+
obj.singleton_class.define_method(:inspect) do
|
73
|
+
super().insert(-2, ' FULL_FONT')
|
74
|
+
end
|
75
|
+
end.freeze
|
76
|
+
|
77
|
+
def initialize(original)
|
78
|
+
@original = original
|
79
|
+
|
80
|
+
(@cmap ||= original.cmap.unicode.first) || raise(NoUnicodeCMap.new(font: name))
|
81
|
+
|
82
|
+
@code_space_size =
|
83
|
+
case cmap.code_map.keys.max
|
84
|
+
when 0..0xff then 1
|
85
|
+
when 0x100..0xffff then 2
|
86
|
+
when 0x10000..0xffffff then 3
|
87
|
+
else
|
88
|
+
4
|
89
|
+
end
|
90
|
+
|
91
|
+
# Codespaces are not sequentional, they're ranges in
|
92
|
+
# a multi-dimentional space. Each byte is considered separately. So we
|
93
|
+
# have to maximally extend the lower two bytes in order to allow for
|
94
|
+
# continuos Unicode mapping.
|
95
|
+
# We only keep the highest byte because Unicode only goes to 1FFFFF
|
96
|
+
# and fonts usually cover even less of the space. We don't want to
|
97
|
+
# list all those unmapped charac codes here.
|
98
|
+
@code_space_max = cmap.code_map.keys.max | ('ff' * (code_space_size - 1)).to_i(16)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Encode characters.
|
102
|
+
#
|
103
|
+
# @return [Array<Array(FULL_FONT, String)>]
|
104
|
+
def encode(characters)
|
105
|
+
[
|
106
|
+
[
|
107
|
+
FULL_FONT,
|
108
|
+
characters.map { |c|
|
109
|
+
check_bounds!(c)
|
110
|
+
[cmap[c]].pack('n')
|
111
|
+
}.join(''),
|
112
|
+
],
|
113
|
+
]
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
attr_reader :cmap
|
119
|
+
attr_reader :code_space_size
|
120
|
+
attr_reader :code_space_max
|
121
|
+
|
122
|
+
def check_bounds!(num)
|
123
|
+
if num > code_space_max
|
124
|
+
raise Error, "CID (#{num}) exceedes code space size"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# @param document [Prawn::Document]
|
130
|
+
# @param name [String] font file path
|
131
|
+
# @param options [Hash]
|
132
|
+
# @option options :family [String]
|
133
|
+
# @option options :style [Symbol]
|
23
134
|
def initialize(document, name, options = {})
|
24
135
|
super
|
25
136
|
|
26
137
|
@ttf = read_ttf_file
|
27
|
-
@subsets =
|
138
|
+
@subsets =
|
139
|
+
if full_font_embedding
|
140
|
+
FullFontSubsetsCollection.new(@ttf)
|
141
|
+
else
|
142
|
+
TTFunk::SubsetCollection.new(@ttf)
|
143
|
+
end
|
28
144
|
@italic_angle = nil
|
29
145
|
|
30
146
|
@attributes = {}
|
@@ -37,52 +153,60 @@ module Prawn
|
|
37
153
|
@line_gap = Integer(@ttf.line_gap * scale_factor)
|
38
154
|
end
|
39
155
|
|
40
|
-
#
|
41
|
-
|
156
|
+
# Compute width of a string at the specified size, optionally with kerning
|
157
|
+
# applied.
|
158
|
+
#
|
159
|
+
# @param string [String] *must* be encoded as UTF-8
|
160
|
+
# @param options [Hash{Symbol => any}]
|
161
|
+
# @option options :size [Number]
|
162
|
+
# @option options :kerning [Boolean] (false)
|
163
|
+
# @return [Number]
|
164
|
+
def compute_width_of(string, options = {})
|
42
165
|
scale = (options[:size] || size) / 1000.0
|
43
166
|
if options[:kerning]
|
44
|
-
kern(string).
|
167
|
+
kern(string).reduce(0) { |s, r|
|
45
168
|
if r.is_a?(Numeric)
|
46
169
|
s - r
|
47
170
|
else
|
48
|
-
r.
|
171
|
+
r.reduce(s) { |a, e| a + character_width_by_code(e) }
|
49
172
|
end
|
50
|
-
|
173
|
+
} * scale
|
51
174
|
else
|
52
|
-
string.codepoints.
|
175
|
+
string.codepoints.reduce(0) { |s, r|
|
53
176
|
s + character_width_by_code(r)
|
54
|
-
|
177
|
+
} * scale
|
55
178
|
end
|
56
179
|
end
|
57
180
|
|
58
|
-
# The font bbox
|
181
|
+
# The font bbox.
|
59
182
|
#
|
183
|
+
# @return [Array(Number, Number, Number, Number)]
|
60
184
|
def bbox
|
61
185
|
@bbox ||= @ttf.bbox.map { |i| Integer(i * scale_factor) }
|
62
186
|
end
|
63
187
|
|
64
|
-
#
|
188
|
+
# Does this font contain kerning data.
|
65
189
|
#
|
66
|
-
#
|
67
|
-
def has_kerning_data?
|
190
|
+
# @return [Boolean]
|
191
|
+
def has_kerning_data? # rubocop: disable Naming/PredicateName
|
68
192
|
@has_kerning_data
|
69
193
|
end
|
70
|
-
# rubocop: enable Naming/PredicateName
|
71
194
|
|
72
|
-
# Perform any changes to the string that need to happen
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
# either a string or an array (for kerned text).
|
77
|
-
#
|
78
|
-
# The +text+ parameter must be UTF8-encoded.
|
195
|
+
# Perform any changes to the string that need to happen before it is
|
196
|
+
# rendered to the canvas. Returns an array of subset "chunks", where the
|
197
|
+
# even-numbered indices are the font subset number, and the following
|
198
|
+
# entry element is either a string or an array (for kerned text).
|
79
199
|
#
|
200
|
+
# @param text [String] must be in UTF-8 encoding
|
201
|
+
# @param options [Hash{Symbol => any}]
|
202
|
+
# @option options :kerning [Boolean]
|
203
|
+
# @return [Array<Array(0, (String, Array)>]
|
80
204
|
def encode_text(text, options = {})
|
81
205
|
text = text.chomp
|
82
206
|
|
83
207
|
if options[:kerning]
|
84
208
|
last_subset = nil
|
85
|
-
kern(text).
|
209
|
+
kern(text).reduce([]) do |result, element|
|
86
210
|
if element.is_a?(Numeric)
|
87
211
|
unless result.last[1].is_a?(Array)
|
88
212
|
result.last[1] = [result.last[1]]
|
@@ -110,15 +234,23 @@ module Prawn
|
|
110
234
|
end
|
111
235
|
end
|
112
236
|
|
237
|
+
# Base name of the font.
|
238
|
+
#
|
239
|
+
# @return [String]
|
113
240
|
def basename
|
114
241
|
@basename ||= @ttf.name.postscript_name
|
115
242
|
end
|
116
243
|
|
117
|
-
# not sure how to compute this for true-type fonts...
|
244
|
+
# @devnote not sure how to compute this for true-type fonts...
|
245
|
+
#
|
246
|
+
# @private
|
247
|
+
# @return [Number]
|
118
248
|
def stem_v
|
119
249
|
0
|
120
250
|
end
|
121
251
|
|
252
|
+
# @private
|
253
|
+
# @return [Number]
|
122
254
|
def italic_angle
|
123
255
|
return @italic_angle if @italic_angle
|
124
256
|
|
@@ -127,7 +259,7 @@ module Prawn
|
|
127
259
|
hi = raw >> 16
|
128
260
|
low = raw & 0xFF
|
129
261
|
hi = -((hi ^ 0xFFFF) + 1) if hi & 0x8000 != 0
|
130
|
-
@italic_angle = "#{hi}.#{low}"
|
262
|
+
@italic_angle = Float("#{hi}.#{low}")
|
131
263
|
else
|
132
264
|
@italic_angle = 0
|
133
265
|
end
|
@@ -135,64 +267,90 @@ module Prawn
|
|
135
267
|
@italic_angle
|
136
268
|
end
|
137
269
|
|
270
|
+
# @private
|
271
|
+
# @return [Number]
|
138
272
|
def cap_height
|
139
|
-
@cap_height ||=
|
140
|
-
|
141
|
-
|
142
|
-
|
273
|
+
@cap_height ||=
|
274
|
+
begin
|
275
|
+
height = (@ttf.os2.exists? && @ttf.os2.cap_height) || 0
|
276
|
+
height.zero? ? @ascender : height
|
277
|
+
end
|
143
278
|
end
|
144
279
|
|
280
|
+
# @private
|
281
|
+
# @return [number]
|
145
282
|
def x_height
|
146
283
|
# FIXME: seems like if os2 table doesn't exist, we could
|
147
284
|
# just find the height of the lower-case 'x' glyph?
|
148
|
-
@ttf.os2.exists? && @ttf.os2.x_height || 0
|
285
|
+
(@ttf.os2.exists? && @ttf.os2.x_height) || 0
|
149
286
|
end
|
150
287
|
|
288
|
+
# @private
|
289
|
+
# @return [Number]
|
151
290
|
def family_class
|
152
|
-
@family_class ||= (@ttf.os2.exists? && @ttf.os2.family_class || 0) >> 8
|
291
|
+
@family_class ||= ((@ttf.os2.exists? && @ttf.os2.family_class) || 0) >> 8
|
153
292
|
end
|
154
293
|
|
294
|
+
# @private
|
295
|
+
# @return [Boolean]
|
155
296
|
def serif?
|
156
297
|
@serif ||= [1, 2, 3, 4, 5, 7].include?(family_class)
|
157
298
|
end
|
158
299
|
|
300
|
+
# @private
|
301
|
+
# @return [Boolean]
|
159
302
|
def script?
|
160
303
|
@script ||= family_class == 10
|
161
304
|
end
|
162
305
|
|
306
|
+
# @private
|
307
|
+
# @return [Integer]
|
163
308
|
def pdf_flags
|
164
|
-
@pdf_flags ||=
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
309
|
+
@pdf_flags ||=
|
310
|
+
begin
|
311
|
+
flags = 0
|
312
|
+
flags |= 0x0001 if @ttf.postscript.fixed_pitch?
|
313
|
+
flags |= 0x0002 if serif?
|
314
|
+
flags |= 0x0008 if script?
|
315
|
+
flags |= 0x0040 if italic_angle != 0
|
316
|
+
# Assume the font contains at least some non-latin characters
|
317
|
+
flags | 0x0004
|
318
|
+
end
|
173
319
|
end
|
174
320
|
|
321
|
+
# Normlize text to a compatible encoding.
|
322
|
+
#
|
323
|
+
# @param text [String]
|
324
|
+
# @return [String]
|
175
325
|
def normalize_encoding(text)
|
176
326
|
text.encode(::Encoding::UTF_8)
|
177
|
-
rescue StandardError
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
'Please ensure the encoding of the string you are attempting ' \
|
182
|
-
'to use is set correctly'
|
327
|
+
rescue StandardError
|
328
|
+
raise Prawn::Errors::IncompatibleStringEncoding,
|
329
|
+
"Encoding #{text.encoding} can not be transparently converted to UTF-8. " \
|
330
|
+
'Please ensure the encoding of the string you are attempting to use is set correctly'
|
183
331
|
end
|
184
332
|
|
333
|
+
# Encode text to UTF-8.
|
334
|
+
#
|
335
|
+
# @param text [String]
|
336
|
+
# @return [String]
|
185
337
|
def to_utf8(text)
|
186
338
|
text.encode('UTF-8')
|
187
339
|
end
|
188
340
|
|
341
|
+
# Does this font has a glyph for the character?
|
342
|
+
#
|
343
|
+
# @param char [String]
|
344
|
+
# @return [Boolean]
|
189
345
|
def glyph_present?(char)
|
190
346
|
code = char.codepoints.first
|
191
347
|
cmap[code].positive?
|
192
348
|
end
|
193
349
|
|
194
|
-
# Returns the number of characters in
|
350
|
+
# Returns the number of characters in `str` (a UTF-8-encoded string).
|
195
351
|
#
|
352
|
+
# @param str [String]
|
353
|
+
# @return [Integer]
|
196
354
|
def character_count(str)
|
197
355
|
str.length
|
198
356
|
end
|
@@ -200,7 +358,7 @@ module Prawn
|
|
200
358
|
private
|
201
359
|
|
202
360
|
def cmap
|
203
|
-
(@cmap ||= @ttf.cmap.unicode.first) || raise(
|
361
|
+
(@cmap ||= @ttf.cmap.unicode.first) || raise(NoUnicodeCMap.new(font: name))
|
204
362
|
end
|
205
363
|
|
206
364
|
# +string+ must be UTF8-encoded.
|
@@ -264,27 +422,41 @@ module Prawn
|
|
264
422
|
end
|
265
423
|
|
266
424
|
def embed(reference, subset)
|
267
|
-
|
425
|
+
if full_font_embedding
|
426
|
+
embed_full_font(reference)
|
427
|
+
else
|
428
|
+
embed_subset(reference, subset)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def embed_subset(reference, subset)
|
433
|
+
font = TTFunk::File.new(@subsets[subset].encode)
|
434
|
+
unicode_mapping = @subsets[subset].to_unicode_map
|
435
|
+
embed_simple_font(reference, font, unicode_mapping)
|
436
|
+
end
|
437
|
+
|
438
|
+
def embed_simple_font(reference, font, unicode_mapping)
|
439
|
+
if font_type(font) == :unknown
|
440
|
+
raise Error, %(Simple font embedding is not uspported for font "#{font.name}.")
|
441
|
+
end
|
268
442
|
|
269
|
-
|
270
|
-
|
271
|
-
# rather than by parsing the font that the subset produces?
|
272
|
-
font = TTFunk::File.new(font_content)
|
443
|
+
true_type = font_type(font) == :true_type
|
444
|
+
open_type = font_type(font) == :open_type
|
273
445
|
|
274
446
|
# empirically, it looks like Adobe Reader will not display fonts
|
275
447
|
# if their font name is more than 33 bytes long. Strange. But true.
|
276
448
|
basename = font.name.postscript_name[0, 33].delete("\0")
|
277
449
|
|
278
|
-
raise
|
450
|
+
raise NoPostscriptName.new(font: font) if basename.nil?
|
279
451
|
|
280
|
-
fontfile = @document.ref!(
|
281
|
-
fontfile.
|
282
|
-
fontfile.stream.
|
452
|
+
fontfile = @document.ref!({})
|
453
|
+
fontfile.data[:Length1] = font.contents.size
|
454
|
+
fontfile.stream << font.contents.string
|
455
|
+
fontfile.stream.compress! if @document.compression_enabled?
|
283
456
|
|
284
457
|
descriptor = @document.ref!(
|
285
458
|
Type: :FontDescriptor,
|
286
459
|
FontName: basename.to_sym,
|
287
|
-
FontFile2: fontfile,
|
288
460
|
FontBBox: bbox,
|
289
461
|
Flags: pdf_flags,
|
290
462
|
StemV: stem_v,
|
@@ -292,13 +464,23 @@ module Prawn
|
|
292
464
|
Ascent: @ascender,
|
293
465
|
Descent: @descender,
|
294
466
|
CapHeight: cap_height,
|
295
|
-
XHeight: x_height
|
467
|
+
XHeight: x_height,
|
296
468
|
)
|
297
469
|
|
470
|
+
first_char, last_char = unicode_mapping.keys.minmax
|
298
471
|
hmtx = font.horizontal_metrics
|
299
|
-
widths =
|
300
|
-
|
301
|
-
|
472
|
+
widths =
|
473
|
+
(first_char..last_char).map { |code|
|
474
|
+
if unicode_mapping.key?(code)
|
475
|
+
gid = font.cmap.tables.first.code_map[code]
|
476
|
+
Integer(hmtx.widths[gid] * scale_factor)
|
477
|
+
else
|
478
|
+
# These characters are not in the document so we don't ever use
|
479
|
+
# these values but we need to encode them so let's use as little
|
480
|
+
# sapce as possible.
|
481
|
+
0
|
482
|
+
end
|
483
|
+
}
|
302
484
|
|
303
485
|
# It would be nice to have Encoding set for the macroman subsets,
|
304
486
|
# and only do a ToUnicode cmap for non-encoded unicode subsets.
|
@@ -310,64 +492,120 @@ module Prawn
|
|
310
492
|
# For now, it's simplest to just create a unicode cmap for every font.
|
311
493
|
# It offends my inner purist, but it'll do.
|
312
494
|
|
313
|
-
|
495
|
+
to_unicode = @document.ref!({})
|
496
|
+
to_unicode << ToUnicodeCMap.new(unicode_mapping).generate
|
497
|
+
to_unicode.stream.compress! if @document.compression_enabled?
|
314
498
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
499
|
+
reference.data.update(
|
500
|
+
BaseFont: basename.to_sym,
|
501
|
+
FontDescriptor: descriptor,
|
502
|
+
FirstChar: first_char,
|
503
|
+
LastChar: last_char,
|
504
|
+
Widths: @document.ref!(widths),
|
505
|
+
ToUnicode: to_unicode,
|
506
|
+
)
|
507
|
+
|
508
|
+
if true_type
|
509
|
+
reference.data.update(Subtype: :TrueType)
|
510
|
+
descriptor.data.update(FontFile2: fontfile)
|
511
|
+
elsif open_type
|
512
|
+
@document.renderer.min_version(1.6)
|
513
|
+
reference.data.update(Subtype: :Type1)
|
514
|
+
descriptor.data.update(FontFile3: fontfile)
|
515
|
+
fontfile.data.update(Subtype: :OpenType)
|
324
516
|
end
|
517
|
+
end
|
325
518
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
519
|
+
def embed_full_font(reference)
|
520
|
+
embed_composite_font(reference, @ttf)
|
521
|
+
end
|
522
|
+
|
523
|
+
def embed_composite_font(reference, font)
|
524
|
+
if font_type(font) == :unknown
|
525
|
+
raise Error, %(Composite font embedding is not uspported for font "#{font.name}.")
|
332
526
|
end
|
333
527
|
|
334
|
-
|
528
|
+
true_type = font_type(font) == :true_type
|
529
|
+
open_type = font_type(font) == :open_type
|
335
530
|
|
336
|
-
|
337
|
-
|
338
|
-
|
531
|
+
fontfile = @document.ref!({})
|
532
|
+
fontfile.data[:Length1] = font.contents.size if true_type
|
533
|
+
fontfile.data[:Subtype] = :CIDFontType0C if open_type
|
534
|
+
fontfile.stream << font.contents.string
|
535
|
+
fontfile.stream.compress! if @document.compression_enabled?
|
339
536
|
|
340
|
-
|
341
|
-
|
537
|
+
# empirically, it looks like Adobe Reader will not display fonts
|
538
|
+
# if their font name is more than 33 bytes long. Strange. But true.
|
539
|
+
basename = font.name.postscript_name[0, 33].delete("\0")
|
540
|
+
|
541
|
+
descriptor = @document.ref!(
|
542
|
+
Type: :FontDescriptor,
|
543
|
+
FontName: basename.to_sym,
|
544
|
+
FontBBox: bbox,
|
545
|
+
Flags: pdf_flags,
|
546
|
+
StemV: stem_v,
|
547
|
+
ItalicAngle: italic_angle,
|
548
|
+
Ascent: @ascender,
|
549
|
+
Descent: @descender,
|
550
|
+
CapHeight: cap_height,
|
551
|
+
XHeight: x_height,
|
552
|
+
)
|
553
|
+
descriptor.data[:FontFile2] = fontfile if true_type
|
554
|
+
descriptor.data[:FontFile3] = fontfile if open_type
|
555
|
+
|
556
|
+
to_unicode = @document.ref!({})
|
557
|
+
to_unicode << ToUnicodeCMap.new(
|
558
|
+
font.cmap.unicode.first
|
559
|
+
.code_map
|
560
|
+
.reject { |cid, gid| gid.zero? || (0xd800..0xdfff).cover?(cid) }
|
561
|
+
.invert
|
562
|
+
.sort.to_h,
|
563
|
+
2, # Identity-H is a 2-byte encoding
|
564
|
+
).generate
|
565
|
+
to_unicode.stream.compress! if @document.compression_enabled?
|
566
|
+
|
567
|
+
widths =
|
568
|
+
font.horizontal_metrics.widths.map { |w| (w * scale_factor).round }
|
569
|
+
|
570
|
+
child_font = @document.ref!(
|
571
|
+
Type: :Font,
|
342
572
|
BaseFont: basename.to_sym,
|
573
|
+
CIDSystemInfo: {
|
574
|
+
Registry: 'Adobe',
|
575
|
+
Ordering: 'Identity',
|
576
|
+
Supplement: 0,
|
577
|
+
},
|
343
578
|
FontDescriptor: descriptor,
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
579
|
+
W: [0, widths],
|
580
|
+
)
|
581
|
+
if true_type
|
582
|
+
child_font.data.update(
|
583
|
+
Subtype: :CIDFontType2,
|
584
|
+
CIDToGIDMap: :Identity,
|
585
|
+
)
|
586
|
+
end
|
587
|
+
if open_type
|
588
|
+
child_font.data[:Subtype] = :CIDFontType0
|
589
|
+
end
|
590
|
+
|
591
|
+
reference.data.update(
|
592
|
+
Subtype: :Type0,
|
593
|
+
BaseFont: basename.to_sym,
|
594
|
+
Encoding: :'Identity-H',
|
595
|
+
DescendantFonts: [child_font],
|
596
|
+
ToUnicode: to_unicode,
|
348
597
|
)
|
349
598
|
end
|
350
599
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
/Supplement 0
|
359
|
-
>> def
|
360
|
-
/CMapName /Adobe-Identity-UCS def
|
361
|
-
/CMapType 2 def
|
362
|
-
1 begincodespacerange
|
363
|
-
<00><ff>
|
364
|
-
endcodespacerange
|
365
|
-
%s
|
366
|
-
endcmap
|
367
|
-
CMapName currentdict /CMap defineresource pop
|
368
|
-
end
|
600
|
+
def font_type(font)
|
601
|
+
if font.directory.tables.key?('glyf')
|
602
|
+
:true_type
|
603
|
+
elsif font.directory.tables.key?('CFF ')
|
604
|
+
:open_type
|
605
|
+
else
|
606
|
+
:unknown
|
369
607
|
end
|
370
|
-
|
608
|
+
end
|
371
609
|
|
372
610
|
def read_ttf_file
|
373
611
|
TTFunk::File.open(@name)
|
data/lib/prawn/fonts.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Prawn
|
4
|
+
# Namespace for different fonts.
|
5
|
+
module Fonts
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require_relative 'font'
|
10
|
+
require_relative 'fonts/afm'
|
11
|
+
require_relative 'fonts/ttf'
|
12
|
+
require_relative 'fonts/dfont'
|
13
|
+
require_relative 'fonts/otf'
|
14
|
+
require_relative 'fonts/ttc'
|