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/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
|
|