prawn 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/prawn/document/bounding_box.rb +213 -141
- data/lib/prawn/document/column_box.rb +61 -26
- data/lib/prawn/document/internals.rb +25 -16
- data/lib/prawn/document/span.rb +20 -18
- data/lib/prawn/document.rb +257 -171
- data/lib/prawn/encoding.rb +2 -5
- data/lib/prawn/errors.rb +23 -34
- data/lib/prawn/font.rb +248 -135
- data/lib/prawn/font_metric_cache.rb +11 -10
- data/lib/prawn/fonts/afm.rb +85 -45
- 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 +305 -93
- 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 +57 -34
- data/lib/prawn/graphics/dash.rb +45 -42
- data/lib/prawn/graphics/join_style.rb +17 -11
- data/lib/prawn/graphics/patterns.rb +190 -69
- data/lib/prawn/graphics/transformation.rb +48 -41
- data/lib/prawn/graphics/transparency.rb +16 -40
- data/lib/prawn/graphics.rb +363 -253
- data/lib/prawn/grid.rb +184 -57
- data/lib/prawn/image_handler.rb +27 -10
- data/lib/prawn/images/image.rb +8 -10
- data/lib/prawn/images/jpg.rb +42 -19
- data/lib/prawn/images/png.rb +92 -41
- data/lib/prawn/images.rb +44 -57
- data/lib/prawn/measurement_extensions.rb +39 -8
- data/lib/prawn/measurements.rb +60 -5
- data/lib/prawn/outline.rb +114 -108
- data/lib/prawn/repeater.rb +51 -35
- data/lib/prawn/security/arcfour.rb +4 -4
- data/lib/prawn/security.rb +75 -70
- data/lib/prawn/soft_mask.rb +42 -30
- data/lib/prawn/stamp.rb +38 -42
- data/lib/prawn/text/box.rb +146 -96
- data/lib/prawn/text/formatted/arranger.rb +87 -26
- data/lib/prawn/text/formatted/box.rb +221 -150
- data/lib/prawn/text/formatted/fragment.rb +130 -14
- data/lib/prawn/text/formatted/line_wrap.rb +33 -24
- data/lib/prawn/text/formatted/parser.rb +112 -72
- data/lib/prawn/text/formatted/wrap.rb +12 -17
- data/lib/prawn/text/formatted.rb +75 -0
- data/lib/prawn/text.rb +441 -196
- 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 +68 -53
- data/lib/prawn.rb +23 -18
- data.tar.gz.sig +0 -0
- metadata +54 -177
- metadata.gz.sig +0 -0
- data/.yardopts +0 -10
- data/Gemfile +0 -5
- data/Rakefile +0 -25
- 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 -29
- 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 -23
- 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 -38
- data/manual/graphics/gradients.rb +0 -43
- data/manual/graphics/graphics.rb +0 -64
- data/manual/graphics/helper.rb +0 -34
- 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 -29
- 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 -30
- 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 -43
- 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 -25
- data/manual/text/formatted_callbacks.rb +0 -70
- data/manual/text/formatted_text.rb +0 -61
- 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 -31
- 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 -52
- 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 -51
- data/manual/text/utf8.rb +0 -27
- data/manual/text/win_ansi_charset.rb +0 -62
- data/prawn.gemspec +0 -51
- data/spec/data/curves.pdf +0 -66
- data/spec/extensions/encoding_helpers.rb +0 -11
- data/spec/prawn/document/bounding_box_spec.rb +0 -550
- 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 -44
- data/spec/prawn/document_spec.rb +0 -805
- data/spec/prawn/font_metric_cache_spec.rb +0 -54
- data/spec/prawn/font_spec.rb +0 -544
- 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 -872
- 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 -229
- data/spec/prawn/measurements_extensions_spec.rb +0 -24
- data/spec/prawn/outline_spec.rb +0 -512
- data/spec/prawn/repeater_spec.rb +0 -166
- data/spec/prawn/soft_mask_spec.rb +0 -74
- data/spec/prawn/stamp_spec.rb +0 -173
- data/spec/prawn/text/box_spec.rb +0 -1110
- data/spec/prawn/text/formatted/arranger_spec.rb +0 -466
- data/spec/prawn/text/formatted/box_spec.rb +0 -849
- data/spec/prawn/text/formatted/fragment_spec.rb +0 -343
- data/spec/prawn/text/formatted/line_wrap_spec.rb +0 -495
- data/spec/prawn/text/formatted/parser_spec.rb +0 -697
- data/spec/prawn/text_draw_text_spec.rb +0 -150
- 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,45 +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
|
20
|
+
# TrueType font error.
|
17
21
|
class Error < StandardError
|
22
|
+
# @private
|
18
23
|
DEFAULT_MESSAGE = 'TTF font error'
|
24
|
+
|
25
|
+
# @private
|
19
26
|
MESSAGE_WITH_FONT = 'TTF font error in font %<font>s'
|
20
27
|
|
21
28
|
def initialize(message = DEFAULT_MESSAGE, font: nil)
|
22
29
|
if font && message == DEFAULT_MESSAGE
|
23
|
-
super
|
30
|
+
super(format(MESSAGE_WITH_FONT, font: font))
|
24
31
|
else
|
25
|
-
super
|
32
|
+
super(message)
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
37
|
+
# Signals absence of a Unicode character map in the font.
|
30
38
|
class NoUnicodeCMap < Error
|
39
|
+
# @private
|
31
40
|
DEFAULT_MESSAGE = 'No unicode cmap found in font'
|
41
|
+
|
42
|
+
# @private
|
32
43
|
MESSAGE_WITH_FONT = 'No unicode cmap found in font %<font>s'
|
33
44
|
end
|
34
45
|
|
46
|
+
# Signals absense of a PostScript font name.
|
35
47
|
class NoPostscriptName < Error
|
48
|
+
# @private
|
36
49
|
DEFAULT_MESSAGE = 'Can not detect a postscript name'
|
50
|
+
|
51
|
+
# @private
|
37
52
|
MESSAGE_WITH_FONT = 'Can not detect a postscript name in font %<font>s'
|
38
53
|
end
|
39
54
|
|
40
|
-
|
55
|
+
# TTFunk font.
|
56
|
+
# @return [TTFunk::File]
|
57
|
+
attr_reader :ttf
|
58
|
+
attr_reader :subsets
|
41
59
|
|
60
|
+
# Does this font support Unicode?
|
61
|
+
#
|
62
|
+
# @return [true]
|
42
63
|
def unicode?
|
43
64
|
true
|
44
65
|
end
|
45
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]
|
46
134
|
def initialize(document, name, options = {})
|
47
135
|
super
|
48
136
|
|
49
137
|
@ttf = read_ttf_file
|
50
|
-
@subsets =
|
138
|
+
@subsets =
|
139
|
+
if full_font_embedding
|
140
|
+
FullFontSubsetsCollection.new(@ttf)
|
141
|
+
else
|
142
|
+
TTFunk::SubsetCollection.new(@ttf)
|
143
|
+
end
|
51
144
|
@italic_angle = nil
|
52
145
|
|
53
146
|
@attributes = {}
|
@@ -60,46 +153,54 @@ module Prawn
|
|
60
153
|
@line_gap = Integer(@ttf.line_gap * scale_factor)
|
61
154
|
end
|
62
155
|
|
63
|
-
#
|
64
|
-
|
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 = {})
|
65
165
|
scale = (options[:size] || size) / 1000.0
|
66
166
|
if options[:kerning]
|
67
|
-
kern(string).reduce(0)
|
167
|
+
kern(string).reduce(0) { |s, r|
|
68
168
|
if r.is_a?(Numeric)
|
69
169
|
s - r
|
70
170
|
else
|
71
171
|
r.reduce(s) { |a, e| a + character_width_by_code(e) }
|
72
172
|
end
|
73
|
-
|
173
|
+
} * scale
|
74
174
|
else
|
75
|
-
string.codepoints.reduce(0)
|
175
|
+
string.codepoints.reduce(0) { |s, r|
|
76
176
|
s + character_width_by_code(r)
|
77
|
-
|
177
|
+
} * scale
|
78
178
|
end
|
79
179
|
end
|
80
180
|
|
81
|
-
# The font bbox
|
181
|
+
# The font bbox.
|
82
182
|
#
|
183
|
+
# @return [Array(Number, Number, Number, Number)]
|
83
184
|
def bbox
|
84
185
|
@bbox ||= @ttf.bbox.map { |i| Integer(i * scale_factor) }
|
85
186
|
end
|
86
187
|
|
87
|
-
#
|
188
|
+
# Does this font contain kerning data.
|
88
189
|
#
|
89
|
-
#
|
90
|
-
def has_kerning_data?
|
190
|
+
# @return [Boolean]
|
191
|
+
def has_kerning_data? # rubocop: disable Naming/PredicateName
|
91
192
|
@has_kerning_data
|
92
193
|
end
|
93
|
-
# rubocop: enable Naming/PredicateName
|
94
194
|
|
95
|
-
# Perform any changes to the string that need to happen
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
# either a string or an array (for kerned text).
|
100
|
-
#
|
101
|
-
# 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).
|
102
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)>]
|
103
204
|
def encode_text(text, options = {})
|
104
205
|
text = text.chomp
|
105
206
|
|
@@ -133,15 +234,23 @@ module Prawn
|
|
133
234
|
end
|
134
235
|
end
|
135
236
|
|
237
|
+
# Base name of the font.
|
238
|
+
#
|
239
|
+
# @return [String]
|
136
240
|
def basename
|
137
241
|
@basename ||= @ttf.name.postscript_name
|
138
242
|
end
|
139
243
|
|
140
|
-
# 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]
|
141
248
|
def stem_v
|
142
249
|
0
|
143
250
|
end
|
144
251
|
|
252
|
+
# @private
|
253
|
+
# @return [Number]
|
145
254
|
def italic_angle
|
146
255
|
return @italic_angle if @italic_angle
|
147
256
|
|
@@ -150,7 +259,7 @@ module Prawn
|
|
150
259
|
hi = raw >> 16
|
151
260
|
low = raw & 0xFF
|
152
261
|
hi = -((hi ^ 0xFFFF) + 1) if hi & 0x8000 != 0
|
153
|
-
@italic_angle = "#{hi}.#{low}"
|
262
|
+
@italic_angle = Float("#{hi}.#{low}")
|
154
263
|
else
|
155
264
|
@italic_angle = 0
|
156
265
|
end
|
@@ -158,32 +267,44 @@ module Prawn
|
|
158
267
|
@italic_angle
|
159
268
|
end
|
160
269
|
|
270
|
+
# @private
|
271
|
+
# @return [Number]
|
161
272
|
def cap_height
|
162
273
|
@cap_height ||=
|
163
274
|
begin
|
164
|
-
height = @ttf.os2.exists? && @ttf.os2.cap_height || 0
|
275
|
+
height = (@ttf.os2.exists? && @ttf.os2.cap_height) || 0
|
165
276
|
height.zero? ? @ascender : height
|
166
277
|
end
|
167
278
|
end
|
168
279
|
|
280
|
+
# @private
|
281
|
+
# @return [number]
|
169
282
|
def x_height
|
170
283
|
# FIXME: seems like if os2 table doesn't exist, we could
|
171
284
|
# just find the height of the lower-case 'x' glyph?
|
172
|
-
@ttf.os2.exists? && @ttf.os2.x_height || 0
|
285
|
+
(@ttf.os2.exists? && @ttf.os2.x_height) || 0
|
173
286
|
end
|
174
287
|
|
288
|
+
# @private
|
289
|
+
# @return [Number]
|
175
290
|
def family_class
|
176
|
-
@family_class ||= (@ttf.os2.exists? && @ttf.os2.family_class || 0) >> 8
|
291
|
+
@family_class ||= ((@ttf.os2.exists? && @ttf.os2.family_class) || 0) >> 8
|
177
292
|
end
|
178
293
|
|
294
|
+
# @private
|
295
|
+
# @return [Boolean]
|
179
296
|
def serif?
|
180
297
|
@serif ||= [1, 2, 3, 4, 5, 7].include?(family_class)
|
181
298
|
end
|
182
299
|
|
300
|
+
# @private
|
301
|
+
# @return [Boolean]
|
183
302
|
def script?
|
184
303
|
@script ||= family_class == 10
|
185
304
|
end
|
186
305
|
|
306
|
+
# @private
|
307
|
+
# @return [Integer]
|
187
308
|
def pdf_flags
|
188
309
|
@pdf_flags ||=
|
189
310
|
begin
|
@@ -197,27 +318,39 @@ module Prawn
|
|
197
318
|
end
|
198
319
|
end
|
199
320
|
|
321
|
+
# Normlize text to a compatible encoding.
|
322
|
+
#
|
323
|
+
# @param text [String]
|
324
|
+
# @return [String]
|
200
325
|
def normalize_encoding(text)
|
201
326
|
text.encode(::Encoding::UTF_8)
|
202
|
-
rescue StandardError
|
203
|
-
puts e
|
327
|
+
rescue StandardError
|
204
328
|
raise Prawn::Errors::IncompatibleStringEncoding,
|
205
329
|
"Encoding #{text.encoding} can not be transparently converted to UTF-8. " \
|
206
|
-
|
207
|
-
'to use is set correctly'
|
330
|
+
'Please ensure the encoding of the string you are attempting to use is set correctly'
|
208
331
|
end
|
209
332
|
|
333
|
+
# Encode text to UTF-8.
|
334
|
+
#
|
335
|
+
# @param text [String]
|
336
|
+
# @return [String]
|
210
337
|
def to_utf8(text)
|
211
338
|
text.encode('UTF-8')
|
212
339
|
end
|
213
340
|
|
341
|
+
# Does this font has a glyph for the character?
|
342
|
+
#
|
343
|
+
# @param char [String]
|
344
|
+
# @return [Boolean]
|
214
345
|
def glyph_present?(char)
|
215
346
|
code = char.codepoints.first
|
216
347
|
cmap[code].positive?
|
217
348
|
end
|
218
349
|
|
219
|
-
# Returns the number of characters in
|
350
|
+
# Returns the number of characters in `str` (a UTF-8-encoded string).
|
220
351
|
#
|
352
|
+
# @param str [String]
|
353
|
+
# @return [Integer]
|
221
354
|
def character_count(str)
|
222
355
|
str.length
|
223
356
|
end
|
@@ -289,12 +422,26 @@ module Prawn
|
|
289
422
|
end
|
290
423
|
|
291
424
|
def embed(reference, subset)
|
292
|
-
|
425
|
+
if full_font_embedding
|
426
|
+
embed_full_font(reference)
|
427
|
+
else
|
428
|
+
embed_subset(reference, subset)
|
429
|
+
end
|
430
|
+
end
|
293
431
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
font
|
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
|
442
|
+
|
443
|
+
true_type = font_type(font) == :true_type
|
444
|
+
open_type = font_type(font) == :open_type
|
298
445
|
|
299
446
|
# empirically, it looks like Adobe Reader will not display fonts
|
300
447
|
# if their font name is more than 33 bytes long. Strange. But true.
|
@@ -302,14 +449,14 @@ module Prawn
|
|
302
449
|
|
303
450
|
raise NoPostscriptName.new(font: font) if basename.nil?
|
304
451
|
|
305
|
-
fontfile = @document.ref!(
|
306
|
-
fontfile.
|
307
|
-
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?
|
308
456
|
|
309
457
|
descriptor = @document.ref!(
|
310
458
|
Type: :FontDescriptor,
|
311
459
|
FontName: basename.to_sym,
|
312
|
-
FontFile2: fontfile,
|
313
460
|
FontBBox: bbox,
|
314
461
|
Flags: pdf_flags,
|
315
462
|
StemV: stem_v,
|
@@ -317,13 +464,23 @@ module Prawn
|
|
317
464
|
Ascent: @ascender,
|
318
465
|
Descent: @descender,
|
319
466
|
CapHeight: cap_height,
|
320
|
-
XHeight: x_height
|
467
|
+
XHeight: x_height,
|
321
468
|
)
|
322
469
|
|
470
|
+
first_char, last_char = unicode_mapping.keys.minmax
|
323
471
|
hmtx = font.horizontal_metrics
|
324
|
-
widths =
|
325
|
-
|
326
|
-
|
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
|
+
}
|
327
484
|
|
328
485
|
# It would be nice to have Encoding set for the macroman subsets,
|
329
486
|
# and only do a ToUnicode cmap for non-encoded unicode subsets.
|
@@ -335,65 +492,120 @@ module Prawn
|
|
335
492
|
# For now, it's simplest to just create a unicode cmap for every font.
|
336
493
|
# It offends my inner purist, but it'll do.
|
337
494
|
|
338
|
-
|
495
|
+
to_unicode = @document.ref!({})
|
496
|
+
to_unicode << ToUnicodeCMap.new(unicode_mapping).generate
|
497
|
+
to_unicode.stream.compress! if @document.compression_enabled?
|
339
498
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
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)
|
349
516
|
end
|
517
|
+
end
|
350
518
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
"%<lenght>d beginbfchar\n%<list>s\nendbfchar\n",
|
355
|
-
lenght: list.length,
|
356
|
-
list: list.join("\n")
|
357
|
-
)
|
358
|
-
end
|
519
|
+
def embed_full_font(reference)
|
520
|
+
embed_composite_font(reference, @ttf)
|
521
|
+
end
|
359
522
|
|
360
|
-
|
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}.")
|
526
|
+
end
|
361
527
|
|
362
|
-
|
363
|
-
|
364
|
-
cmap.stream.compress!
|
528
|
+
true_type = font_type(font) == :true_type
|
529
|
+
open_type = font_type(font) == :open_type
|
365
530
|
|
366
|
-
|
367
|
-
|
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?
|
536
|
+
|
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,
|
368
572
|
BaseFont: basename.to_sym,
|
573
|
+
CIDSystemInfo: {
|
574
|
+
Registry: 'Adobe',
|
575
|
+
Ordering: 'Identity',
|
576
|
+
Supplement: 0,
|
577
|
+
},
|
369
578
|
FontDescriptor: descriptor,
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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,
|
374
597
|
)
|
375
598
|
end
|
376
599
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
/Supplement 0
|
385
|
-
>> def
|
386
|
-
/CMapName /Adobe-Identity-UCS def
|
387
|
-
/CMapType 2 def
|
388
|
-
1 begincodespacerange
|
389
|
-
<00><ff>
|
390
|
-
endcodespacerange
|
391
|
-
%s
|
392
|
-
endcmap
|
393
|
-
CMapName currentdict /CMap defineresource pop
|
394
|
-
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
|
395
607
|
end
|
396
|
-
|
608
|
+
end
|
397
609
|
|
398
610
|
def read_ttf_file
|
399
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'
|
@@ -1,41 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# blend_mode.rb : Implements blend modes
|
4
|
-
#
|
5
|
-
# Contributed by John Ford. October, 2015
|
6
|
-
#
|
7
|
-
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
-
#
|
9
|
-
|
10
3
|
module Prawn
|
11
4
|
module Graphics
|
12
|
-
# The Prawn::BlendMode module is used to change the way
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# Passing an array of blend modes is allowed. PDF viewers should
|
16
|
-
# blend layers based on the first recognized blend mode.
|
17
|
-
#
|
18
|
-
# Valid blend modes in v1.4 of the PDF spec include :Normal, :Multiply,
|
19
|
-
# :Screen, :Overlay, :Darken, :Lighten, :ColorDodge, :ColorBurn, :HardLight,
|
20
|
-
# :SoftLight, :Difference, :Exclusion, :Hue, :Saturation, :Color, and
|
21
|
-
# :Luminosity.
|
22
|
-
#
|
23
|
-
# Example:
|
24
|
-
# pdf.fill_color('0000ff')
|
25
|
-
# pdf.fill_rectangle([x, y+25], 50, 50)
|
26
|
-
# pdf.blend_mode(:Multiply) do
|
27
|
-
# pdf.fill_color('ff0000')
|
28
|
-
# pdf.fill_circle([x, y], 25)
|
29
|
-
# end
|
30
|
-
#
|
5
|
+
# The {Prawn::BlendMode} module is used to change the way two graphic
|
6
|
+
# objects are blended together.
|
31
7
|
module BlendMode
|
32
8
|
# @group Stable API
|
33
9
|
|
10
|
+
# Set blend mode. If a block is passed blend mode is restored afterwards.
|
11
|
+
#
|
12
|
+
# Passing an array of blend modes is allowed. PDF viewers should blend
|
13
|
+
# layers based on the first recognized blend mode.
|
14
|
+
#
|
15
|
+
# Valid blend modes since PDF 1.4 include `:Normal`, `:Multiply`, `:Screen`,
|
16
|
+
# `:Overlay`, `:Darken`, `:Lighten`, `:ColorDodge`, `:ColorBurn`,
|
17
|
+
# `:HardLight`, `:SoftLight`, `:Difference`, `:Exclusion`, `:Hue`,
|
18
|
+
# `:Saturation`, `:Color`, and `:Luminosity`.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# pdf.fill_color('0000ff')
|
22
|
+
# pdf.fill_rectangle([x, y + 25], 50, 50)
|
23
|
+
# pdf.blend_mode(:Multiply) do
|
24
|
+
# pdf.fill_color('ff0000')
|
25
|
+
# pdf.fill_circle([x, y], 25)
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @param blend_mode [Symbol, Array<Symbol>]
|
29
|
+
# @yield
|
30
|
+
# @return [void]
|
34
31
|
def blend_mode(blend_mode = :Normal)
|
35
32
|
renderer.min_version(1.4)
|
36
33
|
|
37
34
|
save_graphics_state if block_given?
|
38
|
-
renderer.add_content
|
35
|
+
renderer.add_content("/#{blend_mode_dictionary_name(blend_mode)} gs")
|
39
36
|
if block_given?
|
40
37
|
yield
|
41
38
|
restore_graphics_state
|
@@ -54,7 +51,7 @@ module Prawn
|
|
54
51
|
|
55
52
|
dictionary = blend_mode_dictionary_registry[dictionary_name] ||= ref!(
|
56
53
|
Type: :ExtGState,
|
57
|
-
BM: blend_mode
|
54
|
+
BM: blend_mode,
|
58
55
|
)
|
59
56
|
|
60
57
|
page.ext_gstates[dictionary_name] = dictionary
|