prawn 2.4.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 +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/afm.rb
CHANGED
@@ -1,24 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Implements AFM font support for Prawn
|
4
|
-
#
|
5
|
-
# Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
|
6
|
-
#
|
7
|
-
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
-
|
9
3
|
require_relative '../encoding'
|
10
4
|
|
11
5
|
module Prawn
|
12
6
|
module Fonts
|
13
|
-
#
|
14
|
-
|
7
|
+
# AFM font. AFM stands for Adobe Font Metrics. It's not a complete font, it
|
8
|
+
# doesn't provide actual glyph outlines. It only contains glyph metrics to
|
9
|
+
# make text layout possible. AFM is used for PDF built-in fonts. Those
|
10
|
+
# fonts are supposed to be present on the target system making it possible
|
11
|
+
# to save a little bit of space by not embedding the fonts. A file that uses
|
12
|
+
# these fonts can not be read on a system that doesn't have these fonts
|
13
|
+
# installed.
|
14
|
+
#
|
15
|
+
# @note You shouldn't use this class directly.
|
15
16
|
class AFM < Font
|
16
17
|
class << self
|
18
|
+
# Prawn would warn you if you're using non-ASCII glyphs with AFM fonts
|
19
|
+
# as not all implementations provide those glyphs. This attribute
|
20
|
+
# suppresses that warning.
|
21
|
+
#
|
22
|
+
# @return [Boolean] (false)
|
17
23
|
attr_accessor :hide_m17n_warning
|
18
24
|
end
|
19
25
|
|
20
26
|
self.hide_m17n_warning = false
|
21
27
|
|
28
|
+
# List of PDF built-in fonts.
|
22
29
|
BUILT_INS = %w[
|
23
30
|
Courier Helvetica Times-Roman Symbol ZapfDingbats
|
24
31
|
Courier-Bold Courier-Oblique Courier-BoldOblique
|
@@ -26,10 +33,16 @@ module Prawn
|
|
26
33
|
Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique
|
27
34
|
].freeze
|
28
35
|
|
36
|
+
# Does this font support Unicode?
|
37
|
+
#
|
38
|
+
# @return [false]
|
29
39
|
def unicode?
|
30
40
|
false
|
31
41
|
end
|
32
42
|
|
43
|
+
# Paths to look for AFM files at.
|
44
|
+
#
|
45
|
+
# @return [Array<String>]
|
33
46
|
def self.metrics_path
|
34
47
|
@metrics_path ||=
|
35
48
|
if ENV['METRICS']
|
@@ -39,19 +52,27 @@ module Prawn
|
|
39
52
|
'.', '/usr/lib/afm',
|
40
53
|
'/usr/local/lib/afm',
|
41
54
|
'/usr/openwin/lib/fonts/afm',
|
42
|
-
"#{Prawn::DATADIR}/fonts"
|
55
|
+
"#{Prawn::DATADIR}/fonts",
|
43
56
|
]
|
44
57
|
end
|
45
58
|
end
|
46
59
|
|
47
|
-
|
60
|
+
# @private
|
61
|
+
attr_reader :attributes
|
48
62
|
|
49
|
-
#
|
63
|
+
# Parsed AFM data cache.
|
64
|
+
#
|
65
|
+
# @return [SynchronizedCache]
|
50
66
|
def self.font_data
|
51
67
|
@font_data ||= SynchronizedCache.new
|
52
68
|
end
|
53
69
|
|
54
|
-
|
70
|
+
# @param document [Prawn::Document]
|
71
|
+
# @param name [String]
|
72
|
+
# @param options [Hash]
|
73
|
+
# @option options :family [String]
|
74
|
+
# @option options :style [Symbol]
|
75
|
+
def initialize(document, name, options = {})
|
55
76
|
name ||= options[:family]
|
56
77
|
unless BUILT_INS.include?(name)
|
57
78
|
raise Prawn::Errors::UnknownFont,
|
@@ -62,7 +83,7 @@ module Prawn
|
|
62
83
|
|
63
84
|
file_name = @name.dup
|
64
85
|
file_name << '.afm' unless /\.afm$/.match?(file_name)
|
65
|
-
file_name = file_name[0] == '/'
|
86
|
+
file_name = find_font(file_name) unless file_name[0] == '/'
|
66
87
|
|
67
88
|
font_data = self.class.font_data[file_name] ||= parse_afm(file_name)
|
68
89
|
@glyph_widths = font_data[:glyph_widths]
|
@@ -72,19 +93,27 @@ module Prawn
|
|
72
93
|
@kern_pair_table = font_data[:kern_pair_table]
|
73
94
|
@attributes = font_data[:attributes]
|
74
95
|
|
75
|
-
@ascender = @attributes
|
76
|
-
@descender = @attributes
|
96
|
+
@ascender = Integer(@attributes.fetch('ascender', '0'), 10)
|
97
|
+
@descender = Integer(@attributes.fetch('descender', '0'), 10)
|
77
98
|
@line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
|
78
99
|
end
|
79
100
|
|
80
|
-
# The font bbox
|
101
|
+
# The font bbox.
|
81
102
|
#
|
103
|
+
# @return [Array(Number, Number, Number, Number)]
|
82
104
|
def bbox
|
83
105
|
@bbox ||= @attributes['fontbbox'].split(/\s+/).map { |e| Integer(e) }
|
84
106
|
end
|
85
107
|
|
86
|
-
#
|
87
|
-
|
108
|
+
# Compute width of a string at the specified size, optionally with kerning
|
109
|
+
# applied.
|
110
|
+
#
|
111
|
+
# @param string [String] *must* be encoded as WinAnsi
|
112
|
+
# @param options [Hash{Symbol => any}]
|
113
|
+
# @option options :size [Number]
|
114
|
+
# @option options :kerning [Boolean] (false)
|
115
|
+
# @return [Number]
|
116
|
+
def compute_width_of(string, options = {})
|
88
117
|
scale = (options[:size] || size) / 1000.0
|
89
118
|
|
90
119
|
if options[:kerning]
|
@@ -96,18 +125,19 @@ module Prawn
|
|
96
125
|
end
|
97
126
|
end
|
98
127
|
|
99
|
-
#
|
128
|
+
# Does this font contain kerning data.
|
100
129
|
#
|
101
|
-
#
|
102
|
-
def has_kerning_data?
|
130
|
+
# @return [Boolean]
|
131
|
+
def has_kerning_data? # rubocop: disable Naming/PredicateName
|
103
132
|
@kern_pairs.any?
|
104
133
|
end
|
105
|
-
# rubocop: enable Naming/PredicateName
|
106
134
|
|
107
|
-
#
|
135
|
+
# Built-in fonts only work with WinAnsi encoding, so translate the
|
108
136
|
# string. Changes the encoding in-place, so the argument itself
|
109
137
|
# is replaced with a string in WinAnsi encoding.
|
110
138
|
#
|
139
|
+
# @param text [String]
|
140
|
+
# @return [String]
|
111
141
|
def normalize_encoding(text)
|
112
142
|
text.encode('windows-1252')
|
113
143
|
rescue ::Encoding::InvalidByteSequenceError,
|
@@ -115,17 +145,23 @@ module Prawn
|
|
115
145
|
|
116
146
|
raise Prawn::Errors::IncompatibleStringEncoding,
|
117
147
|
"Your document includes text that's not compatible with the " \
|
118
|
-
|
119
|
-
|
120
|
-
|
148
|
+
"Windows-1252 character set.\n" \
|
149
|
+
'If you need full UTF-8 support, use external fonts instead of ' \
|
150
|
+
"PDF's built-in fonts.\n"
|
121
151
|
end
|
122
152
|
|
153
|
+
# Encode text to UTF-8.
|
154
|
+
#
|
155
|
+
# @param text [String]
|
156
|
+
# @return [String]
|
123
157
|
def to_utf8(text)
|
124
158
|
text.encode('UTF-8')
|
125
159
|
end
|
126
160
|
|
127
|
-
# Returns the number of characters in
|
161
|
+
# Returns the number of characters in `str` (a WinAnsi-encoded string).
|
128
162
|
#
|
163
|
+
# @param str [String]
|
164
|
+
# @return [Integer]
|
129
165
|
def character_count(str)
|
130
166
|
str.length
|
131
167
|
end
|
@@ -137,15 +173,23 @@ module Prawn
|
|
137
173
|
# is either a string or an array (for kerned text).
|
138
174
|
#
|
139
175
|
# For Adobe fonts, there is only ever a single subset, so
|
140
|
-
# the first element of the array is
|
176
|
+
# the first element of the array is `0`, and the second is
|
141
177
|
# the string itself (or an array, if kerning is performed).
|
142
178
|
#
|
143
|
-
# The
|
179
|
+
# The `text` argument must be in WinAnsi encoding (cp1252).
|
144
180
|
#
|
181
|
+
# @param text [String]
|
182
|
+
# @param options [Hash{Symbol => any}]
|
183
|
+
# @option options :kerning [Boolean]
|
184
|
+
# @return [Array<Array(0, (String, Array)>]
|
145
185
|
def encode_text(text, options = {})
|
146
186
|
[[0, options[:kerning] ? kern(text) : text]]
|
147
187
|
end
|
148
188
|
|
189
|
+
# Does this font has a glyph for the character?
|
190
|
+
#
|
191
|
+
# @param char [String]
|
192
|
+
# @return [Boolean]
|
149
193
|
def glyph_present?(char)
|
150
194
|
!normalize_encoding(char).nil?
|
151
195
|
rescue Prawn::Errors::IncompatibleStringEncoding
|
@@ -158,7 +202,7 @@ module Prawn
|
|
158
202
|
font_dict = {
|
159
203
|
Type: :Font,
|
160
204
|
Subtype: :Type1,
|
161
|
-
BaseFont: name.to_sym
|
205
|
+
BaseFont: name.to_sym,
|
162
206
|
}
|
163
207
|
|
164
208
|
# Symbolic AFM fonts (Symbol, ZapfDingbats) have their own encodings
|
@@ -172,7 +216,7 @@ module Prawn
|
|
172
216
|
end
|
173
217
|
|
174
218
|
def find_font(file)
|
175
|
-
self.class.metrics_path.find { |f| File.exist?
|
219
|
+
self.class.metrics_path.find { |f| File.exist?("#{f}/#{file}") } +
|
176
220
|
"/#{file}"
|
177
221
|
rescue NoMethodError
|
178
222
|
raise Prawn::Errors::UnknownFont,
|
@@ -185,14 +229,14 @@ module Prawn
|
|
185
229
|
glyph_widths: {},
|
186
230
|
bounding_boxes: {},
|
187
231
|
kern_pairs: {},
|
188
|
-
attributes: {}
|
232
|
+
attributes: {},
|
189
233
|
}
|
190
234
|
section = []
|
191
235
|
|
192
236
|
File.foreach(file_name) do |line|
|
193
237
|
case line
|
194
238
|
when /^Start(\w+)/
|
195
|
-
section.push
|
239
|
+
section.push(Regexp.last_match(1))
|
196
240
|
next
|
197
241
|
when /^End(\w+)/
|
198
242
|
section.pop
|
@@ -204,13 +248,13 @@ module Prawn
|
|
204
248
|
next unless /^CH?\s/.match?(line)
|
205
249
|
|
206
250
|
name = line[/\bN\s+(\.?\w+)\s*;/, 1]
|
207
|
-
data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1]
|
251
|
+
data[:glyph_widths][name] = Integer(line[/\bWX\s+(\d+)\s*;/, 1], 10)
|
208
252
|
data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
|
209
253
|
when %w[FontMetrics KernData KernPairs]
|
210
254
|
next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
|
211
255
|
|
212
256
|
data[:kern_pairs][[Regexp.last_match(1), Regexp.last_match(2)]] =
|
213
|
-
Regexp.last_match(3)
|
257
|
+
Integer(Regexp.last_match(3), 10)
|
214
258
|
when %w[FontMetrics KernData TrackKern],
|
215
259
|
%w[FontMetrics Composites]
|
216
260
|
next
|
@@ -222,15 +266,11 @@ module Prawn
|
|
222
266
|
# process data parsed from AFM file to build tables which
|
223
267
|
# will be used when measuring and kerning text
|
224
268
|
data[:glyph_table] =
|
225
|
-
(0..255).map
|
226
|
-
data[:glyph_widths]
|
227
|
-
|
269
|
+
(0..255).map { |i|
|
270
|
+
data[:glyph_widths].fetch(Encoding::WinAnsi::CHARACTERS[i], 0)
|
271
|
+
}
|
228
272
|
|
229
|
-
character_hash =
|
230
|
-
Encoding::WinAnsi::CHARACTERS.zip(
|
231
|
-
(0..Encoding::WinAnsi::CHARACTERS.size).to_a
|
232
|
-
)
|
233
|
-
]
|
273
|
+
character_hash = Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a).to_h
|
234
274
|
data[:kern_pair_table] =
|
235
275
|
data[:kern_pairs].each_with_object({}) do |p, h|
|
236
276
|
h[p[0].map { |n| character_hash[n] }] = p[1]
|
@@ -254,7 +294,7 @@ module Prawn
|
|
254
294
|
end
|
255
295
|
|
256
296
|
# converts a string into an array with spacing offsets
|
257
|
-
#
|
297
|
+
# between characters that need to be kerned
|
258
298
|
#
|
259
299
|
# String *must* be encoded as WinAnsi
|
260
300
|
#
|
@@ -273,7 +313,7 @@ module Prawn
|
|
273
313
|
end
|
274
314
|
|
275
315
|
kerned.map do |e|
|
276
|
-
e = e.
|
316
|
+
e = e.pack('C*') if e.is_a?(Array)
|
277
317
|
if e.respond_to?(:force_encoding)
|
278
318
|
e.force_encoding(::Encoding::Windows_1252)
|
279
319
|
else
|
data/lib/prawn/fonts/dfont.rb
CHANGED
@@ -4,7 +4,9 @@ require_relative 'ttf'
|
|
4
4
|
|
5
5
|
module Prawn
|
6
6
|
module Fonts
|
7
|
-
#
|
7
|
+
# DFONT font. DFONT is a bunch of TrueType fonts in a single file.
|
8
|
+
#
|
9
|
+
# @note You shouldn't use this class directly.
|
8
10
|
class DFont < TTF
|
9
11
|
# Returns a list of the names of all named fonts in the given dfont file.
|
10
12
|
# Note that fonts are not required to be named in a dfont file, so the
|
@@ -12,6 +14,8 @@ module Prawn
|
|
12
14
|
# the list is returned in no particular order, so the first font in the
|
13
15
|
# list is not necessarily the font at index 0 in the file.
|
14
16
|
#
|
17
|
+
# @param file [String]
|
18
|
+
# @return [Array<String>]
|
15
19
|
def self.named_fonts(file)
|
16
20
|
TTFunk::ResourceFile.open(file) do |f|
|
17
21
|
return f.resources_for('sfnt')
|
@@ -20,6 +24,8 @@ module Prawn
|
|
20
24
|
|
21
25
|
# Returns the number of fonts contained in the dfont file.
|
22
26
|
#
|
27
|
+
# @param file [String]
|
28
|
+
# @return [Integer]
|
23
29
|
def self.font_count(file)
|
24
30
|
TTFunk::ResourceFile.open(file) do |f|
|
25
31
|
return f.map['sfnt'][:list].length
|
data/lib/prawn/fonts/otf.rb
CHANGED
@@ -4,7 +4,10 @@ require_relative 'ttf'
|
|
4
4
|
|
5
5
|
module Prawn
|
6
6
|
module Fonts
|
7
|
-
#
|
7
|
+
# OpenType font. This class is used mostly to distinguish OTF from TTF.
|
8
|
+
# All functionality is in the {Fonts::TTF} class.
|
9
|
+
#
|
10
|
+
# @note You shouldn't use this class directly.
|
8
11
|
class OTF < TTF
|
9
12
|
end
|
10
13
|
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Prawn
|
4
|
+
module Fonts
|
5
|
+
# This class generates ToUnicode CMap for embedde TrueType/OpenType fonts.
|
6
|
+
# It's a separate format and is somewhat complicated so it has its own
|
7
|
+
# place.
|
8
|
+
#
|
9
|
+
# @private
|
10
|
+
class ToUnicodeCMap
|
11
|
+
# mapping is expected to be a hash with keys being character codes (in
|
12
|
+
# broad sense, as used in the showing operation strings) and values being
|
13
|
+
# Unicode code points.
|
14
|
+
def initialize(mapping, code_space_size = nil)
|
15
|
+
@mapping = mapping
|
16
|
+
@code_space_size = code_space_size
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generate CMap.
|
20
|
+
#
|
21
|
+
# @return [String]
|
22
|
+
def generate
|
23
|
+
chunks = []
|
24
|
+
|
25
|
+
# Header
|
26
|
+
chunks << <<~HEADER.chomp
|
27
|
+
/CIDInit /ProcSet findresource begin
|
28
|
+
12 dict begin
|
29
|
+
begincmap
|
30
|
+
/CIDSystemInfo 3 dict dup begin
|
31
|
+
/Registry (Adobe) def
|
32
|
+
/Ordering (UCS) def
|
33
|
+
/Supplement 0 def
|
34
|
+
end def
|
35
|
+
/CMapName /Adobe-Identity-UCS def
|
36
|
+
/CMapType 2 def
|
37
|
+
HEADER
|
38
|
+
|
39
|
+
max_glyph_index = mapping.keys.max
|
40
|
+
# Range
|
41
|
+
code_space_size = (max_glyph_index.bit_length / 8.0).ceil
|
42
|
+
|
43
|
+
used_code_space_size = @code_space_size || code_space_size
|
44
|
+
|
45
|
+
# In CMap codespaces are not sequentional, they're ranges in
|
46
|
+
# a multi-dimentional space. Each byte is considered separately. So we
|
47
|
+
# have to maximally extend the lower bytes in order to allow for
|
48
|
+
# continuos mapping.
|
49
|
+
# We only keep the highest byte because usually it's lower than
|
50
|
+
# maximally allowed and we don't want to cover that unused space.
|
51
|
+
code_space_max = max_glyph_index | ('ff' * (code_space_size - 1)).to_i(16)
|
52
|
+
|
53
|
+
chunks << '1 begincodespacerange'
|
54
|
+
chunks << format("<%0#{used_code_space_size * 2}X><%0#{used_code_space_size * 2}X>", 0, code_space_max)
|
55
|
+
chunks << 'endcodespacerange'
|
56
|
+
|
57
|
+
# Mapping
|
58
|
+
all_spans = mapping_spans(mapping.reject { |gid, cid| gid.zero? || (0xd800..0xdfff).cover?(cid) })
|
59
|
+
|
60
|
+
short_spans, long_spans = all_spans.partition { |span| span[0] == :short }
|
61
|
+
|
62
|
+
long_spans
|
63
|
+
.each_slice(100) do |spans|
|
64
|
+
chunks << "#{spans.length} beginbfrange"
|
65
|
+
|
66
|
+
spans.each do |type, span|
|
67
|
+
# rubocop: disable Lint/FormatParameterMismatch # false positive
|
68
|
+
case type
|
69
|
+
when :fully_sorted
|
70
|
+
chunks << format(
|
71
|
+
"<%0#{code_space_size * 2}X><%0#{code_space_size * 2}X><%s>",
|
72
|
+
span.first[0],
|
73
|
+
span.last[0],
|
74
|
+
span.first[1].chr(::Encoding::UTF_16BE).unpack1('H*'),
|
75
|
+
)
|
76
|
+
when :index_sorted
|
77
|
+
chunks << format(
|
78
|
+
"<%0#{code_space_size * 2}X><%0#{code_space_size * 2}X>[%s]",
|
79
|
+
span.first[0],
|
80
|
+
span.last[0],
|
81
|
+
span.map { |_, cid| "<#{cid.chr(::Encoding::UTF_16BE).unpack1('H*')}>" }.join(''),
|
82
|
+
)
|
83
|
+
end
|
84
|
+
# rubocop: enable Lint/FormatParameterMismatch
|
85
|
+
end
|
86
|
+
|
87
|
+
chunks << 'endbfrange'
|
88
|
+
end
|
89
|
+
|
90
|
+
short_spans
|
91
|
+
.map { |_type, slice| slice.flatten(1) }
|
92
|
+
.each_slice(100) do |mapping|
|
93
|
+
chunks << "#{mapping.length} beginbfchar"
|
94
|
+
chunks.concat(
|
95
|
+
mapping.map { |(gid, cid)|
|
96
|
+
# rubocop: disable Lint/FormatParameterMismatch # false positive
|
97
|
+
format(
|
98
|
+
"<%0#{code_space_size * 2}X><%s>",
|
99
|
+
gid,
|
100
|
+
cid.chr(::Encoding::UTF_16BE).unpack1('H*'),
|
101
|
+
)
|
102
|
+
# rubocop: enable Lint/FormatParameterMismatch
|
103
|
+
},
|
104
|
+
)
|
105
|
+
chunks << 'endbfchar'
|
106
|
+
end
|
107
|
+
|
108
|
+
# Footer
|
109
|
+
chunks << <<~FOOTER.chomp
|
110
|
+
endcmap
|
111
|
+
CMapName currentdict /CMap defineresource pop
|
112
|
+
end
|
113
|
+
end
|
114
|
+
FOOTER
|
115
|
+
|
116
|
+
chunks.join("\n")
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
attr_reader :mapping
|
122
|
+
|
123
|
+
attr_reader :cmap
|
124
|
+
attr_reader :code_space_size
|
125
|
+
attr_reader :code_space_max
|
126
|
+
|
127
|
+
def mapping_spans(mapping)
|
128
|
+
mapping
|
129
|
+
.sort
|
130
|
+
.slice_when { |a, b| (b[0] - a[0]) != 1 } # Slice at key discontinuity
|
131
|
+
.flat_map { |slice|
|
132
|
+
if slice.length == 1
|
133
|
+
[[:short, slice]]
|
134
|
+
else
|
135
|
+
continuous_slices, discontinuous_slices =
|
136
|
+
slice
|
137
|
+
.slice_when { |a, b| b[1] - a[1] != 1 } # Slice at value discontinuity
|
138
|
+
.partition { |subslice| subslice.length > 1 }
|
139
|
+
|
140
|
+
discontinuous_slices
|
141
|
+
.flatten(1) # Join together
|
142
|
+
.slice_when { |a, b| (b[0] - a[0]) != 1 } # Slice at key discontinuity, again
|
143
|
+
.map { |span| span.length > 1 ? [:index_sorted, span] : [:short, slice] } +
|
144
|
+
continuous_slices.map { |span| [:fully_sorted, span] }
|
145
|
+
end
|
146
|
+
} # rubocop: disable Style/MultilineBlockChain
|
147
|
+
.sort_by { |span| span[1][0][0] } # Sort span start key
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
data/lib/prawn/fonts/ttc.rb
CHANGED
@@ -4,11 +4,16 @@ require_relative 'ttf'
|
|
4
4
|
|
5
5
|
module Prawn
|
6
6
|
module Fonts
|
7
|
-
#
|
7
|
+
# TrueType Collection font. It's an SFNT-based format that contains a bunch
|
8
|
+
# of TrueType fonts in a single file.
|
9
|
+
#
|
10
|
+
# @note You shouldn't use this class directly.
|
8
11
|
class TTC < TTF
|
9
12
|
# Returns a list of the names of all named fonts in the given ttc file.
|
10
13
|
# They are returned in order of their appearance in the file.
|
11
14
|
#
|
15
|
+
# @param file [String]
|
16
|
+
# @return [Array<String>]
|
12
17
|
def self.font_names(file)
|
13
18
|
TTFunk::Collection.open(file) do |ttc|
|
14
19
|
ttc.map { |font| font.name.font_name.first }
|
@@ -20,7 +25,7 @@ module Prawn
|
|
20
25
|
def read_ttf_file
|
21
26
|
TTFunk::File.from_ttc(
|
22
27
|
@name,
|
23
|
-
font_option_to_index(@name, @options[:font])
|
28
|
+
font_option_to_index(@name, @options[:font]),
|
24
29
|
)
|
25
30
|
end
|
26
31
|
|