prawn 2.0.2 → 2.3.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/GPLv2 +20 -21
- data/Gemfile +3 -9
- data/Rakefile +20 -23
- data/lib/prawn.rb +37 -49
- data/lib/prawn/document.rb +181 -133
- data/lib/prawn/document/bounding_box.rb +41 -29
- data/lib/prawn/document/column_box.rb +7 -7
- data/lib/prawn/document/internals.rb +18 -8
- data/lib/prawn/document/span.rb +21 -16
- data/lib/prawn/encoding.rb +69 -68
- data/lib/prawn/errors.rb +12 -7
- data/lib/prawn/font.rb +115 -69
- data/lib/prawn/font_metric_cache.rb +14 -8
- data/lib/prawn/{font → fonts}/afm.rb +102 -68
- data/lib/prawn/{font → fonts}/dfont.rb +5 -11
- data/lib/prawn/fonts/otf.rb +11 -0
- data/lib/prawn/fonts/ttc.rb +36 -0
- data/lib/prawn/{font → fonts}/ttf.rb +87 -68
- data/lib/prawn/graphics.rb +120 -80
- data/lib/prawn/graphics/blend_mode.rb +65 -0
- data/lib/prawn/graphics/cap_style.rb +3 -3
- data/lib/prawn/graphics/color.rb +27 -25
- data/lib/prawn/graphics/dash.rb +23 -11
- data/lib/prawn/graphics/join_style.rb +9 -3
- data/lib/prawn/graphics/patterns.rb +197 -67
- data/lib/prawn/graphics/transformation.rb +17 -8
- data/lib/prawn/graphics/transparency.rb +17 -13
- data/lib/prawn/grid.rb +48 -47
- data/lib/prawn/image_handler.rb +5 -5
- data/lib/prawn/images.rb +39 -30
- data/lib/prawn/images/image.rb +2 -1
- data/lib/prawn/images/jpg.rb +28 -22
- data/lib/prawn/images/png.rb +107 -66
- data/lib/prawn/measurement_extensions.rb +10 -9
- data/lib/prawn/measurements.rb +19 -15
- data/lib/prawn/outline.rb +97 -77
- data/lib/prawn/repeater.rb +14 -10
- data/lib/prawn/security.rb +81 -61
- data/lib/prawn/security/arcfour.rb +2 -2
- data/lib/prawn/soft_mask.rb +26 -26
- data/lib/prawn/stamp.rb +20 -13
- data/lib/prawn/text.rb +68 -52
- data/lib/prawn/text/box.rb +11 -8
- data/lib/prawn/text/formatted.rb +5 -5
- data/lib/prawn/text/formatted/arranger.rb +78 -49
- data/lib/prawn/text/formatted/box.rb +134 -100
- data/lib/prawn/text/formatted/fragment.rb +11 -14
- data/lib/prawn/text/formatted/line_wrap.rb +121 -63
- data/lib/prawn/text/formatted/parser.rb +139 -117
- data/lib/prawn/text/formatted/wrap.rb +43 -31
- data/lib/prawn/transformation_stack.rb +44 -0
- data/lib/prawn/utilities.rb +7 -22
- data/lib/prawn/version.rb +2 -2
- data/lib/prawn/view.rb +17 -7
- data/manual/basic_concepts/adding_pages.rb +6 -7
- data/manual/basic_concepts/basic_concepts.rb +31 -22
- data/manual/basic_concepts/creation.rb +10 -11
- data/manual/basic_concepts/cursor.rb +4 -5
- data/manual/basic_concepts/measurement.rb +6 -7
- data/manual/basic_concepts/origin.rb +5 -6
- data/manual/basic_concepts/other_cursor_helpers.rb +11 -12
- data/manual/basic_concepts/view.rb +22 -16
- data/manual/bounding_box/bounding_box.rb +29 -24
- data/manual/bounding_box/bounds.rb +11 -12
- data/manual/bounding_box/canvas.rb +4 -5
- data/manual/bounding_box/creation.rb +6 -7
- data/manual/bounding_box/indentation.rb +14 -15
- data/manual/bounding_box/nesting.rb +24 -17
- data/manual/bounding_box/russian_boxes.rb +14 -13
- data/manual/bounding_box/stretchy.rb +12 -13
- data/manual/contents.rb +28 -22
- data/manual/cover.rb +33 -28
- data/manual/document_and_page_options/background.rb +11 -13
- data/manual/document_and_page_options/document_and_page_options.rb +25 -20
- data/manual/document_and_page_options/metadata.rb +18 -16
- data/manual/document_and_page_options/page_margins.rb +18 -20
- data/manual/document_and_page_options/page_size.rb +13 -12
- data/manual/document_and_page_options/print_scaling.rb +17 -15
- data/manual/example_helper.rb +5 -4
- data/manual/graphics/blend_mode.rb +52 -0
- data/manual/graphics/circle_and_ellipse.rb +4 -5
- data/manual/graphics/color.rb +7 -9
- data/manual/graphics/common_lines.rb +7 -8
- data/manual/graphics/fill_and_stroke.rb +4 -5
- data/manual/graphics/fill_rules.rb +9 -10
- data/manual/graphics/gradients.rb +27 -21
- data/manual/graphics/graphics.rb +48 -39
- data/manual/graphics/helper.rb +12 -9
- data/manual/graphics/line_width.rb +8 -7
- data/manual/graphics/lines_and_curves.rb +7 -8
- data/manual/graphics/polygon.rb +6 -8
- data/manual/graphics/rectangle.rb +4 -5
- data/manual/graphics/rotate.rb +6 -7
- data/manual/graphics/scale.rb +14 -15
- data/manual/graphics/soft_masks.rb +4 -5
- data/manual/graphics/stroke_cap.rb +6 -7
- data/manual/graphics/stroke_dash.rb +11 -12
- data/manual/graphics/stroke_join.rb +5 -6
- data/manual/graphics/translate.rb +9 -10
- data/manual/graphics/transparency.rb +7 -8
- data/manual/how_to_read_this_manual.rb +6 -6
- data/manual/images/absolute_position.rb +6 -7
- data/manual/images/fit.rb +7 -8
- data/manual/images/horizontal.rb +9 -10
- data/manual/images/images.rb +28 -24
- data/manual/images/plain_image.rb +5 -6
- data/manual/images/scale.rb +9 -10
- data/manual/images/vertical.rb +13 -14
- data/manual/images/width_and_height.rb +10 -11
- data/manual/layout/boxes.rb +5 -6
- data/manual/layout/content.rb +7 -8
- data/manual/layout/layout.rb +18 -16
- data/manual/layout/simple_grid.rb +6 -7
- data/manual/outline/add_subsection_to.rb +20 -21
- data/manual/outline/insert_section_after.rb +15 -16
- data/manual/outline/outline.rb +21 -17
- data/manual/outline/sections_and_pages.rb +17 -18
- data/manual/repeatable_content/alternate_page_numbering.rb +21 -17
- data/manual/repeatable_content/page_numbering.rb +17 -16
- data/manual/repeatable_content/repeatable_content.rb +25 -19
- data/manual/repeatable_content/repeater.rb +14 -15
- data/manual/repeatable_content/stamp.rb +14 -15
- data/manual/security/encryption.rb +9 -10
- data/manual/security/permissions.rb +19 -14
- data/manual/security/security.rb +19 -16
- data/manual/table.rb +3 -3
- data/manual/text/alignment.rb +16 -17
- data/manual/text/color.rb +12 -11
- data/manual/text/column_box.rb +9 -10
- data/manual/text/fallback_fonts.rb +25 -21
- data/manual/text/font.rb +11 -12
- data/manual/text/font_size.rb +13 -14
- data/manual/text/font_style.rb +7 -8
- data/manual/text/formatted_callbacks.rb +25 -21
- data/manual/text/formatted_text.rb +33 -25
- data/manual/text/free_flowing_text.rb +20 -21
- data/manual/text/inline.rb +18 -19
- data/manual/text/kerning_and_character_spacing.rb +14 -15
- data/manual/text/leading.rb +7 -8
- data/manual/text/line_wrapping.rb +37 -18
- data/manual/text/paragraph_indentation.rb +13 -14
- data/manual/text/positioned_text.rb +15 -16
- data/manual/text/registering_families.rb +20 -21
- data/manual/text/rendering_and_color.rb +9 -10
- data/manual/text/right_to_left_text.rb +26 -19
- data/manual/text/rotation.rb +28 -23
- data/manual/text/single_usage.rb +8 -9
- data/manual/text/text.rb +57 -52
- data/manual/text/text_box_excess.rb +20 -17
- data/manual/text/text_box_extensions.rb +18 -15
- data/manual/text/text_box_overflow.rb +18 -19
- data/manual/text/utf8.rb +11 -12
- data/manual/text/win_ansi_charset.rb +21 -19
- data/prawn.gemspec +45 -33
- data/spec/extensions/encoding_helpers.rb +3 -3
- data/spec/prawn/document/bounding_box_spec.rb +546 -0
- data/spec/prawn/document/column_box_spec.rb +75 -0
- data/spec/prawn/document/security_spec.rb +176 -0
- data/spec/prawn/document_annotations_spec.rb +76 -0
- data/spec/prawn/document_destinations_spec.rb +15 -0
- data/spec/prawn/document_grid_spec.rb +99 -0
- data/spec/prawn/document_reference_spec.rb +27 -0
- data/spec/prawn/document_span_spec.rb +36 -0
- data/spec/prawn/document_spec.rb +802 -0
- data/spec/prawn/font_metric_cache_spec.rb +54 -0
- data/spec/prawn/font_spec.rb +542 -0
- data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
- data/spec/prawn/graphics/transparency_spec.rb +81 -0
- data/spec/prawn/graphics_spec.rb +837 -0
- data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
- data/spec/prawn/image_handler_spec.rb +53 -0
- data/spec/prawn/images/jpg_spec.rb +20 -0
- data/spec/prawn/images/png_spec.rb +283 -0
- data/spec/prawn/images_spec.rb +224 -0
- data/spec/prawn/measurements_extensions_spec.rb +24 -0
- data/spec/prawn/outline_spec.rb +412 -0
- data/spec/prawn/repeater_spec.rb +165 -0
- data/spec/prawn/soft_mask_spec.rb +74 -0
- data/spec/prawn/stamp_spec.rb +172 -0
- data/spec/prawn/text/box_spec.rb +1112 -0
- data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
- data/spec/prawn/text/formatted/box_spec.rb +846 -0
- data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
- data/spec/prawn/text/formatted/line_wrap_spec.rb +494 -0
- data/spec/prawn/text/formatted/parser_spec.rb +697 -0
- data/spec/prawn/text_draw_text_spec.rb +149 -0
- data/spec/prawn/text_rendering_mode_spec.rb +48 -0
- data/spec/prawn/text_spacing_spec.rb +95 -0
- data/spec/prawn/text_spec.rb +603 -0
- data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
- data/spec/prawn/transformation_stack_spec.rb +66 -0
- data/spec/prawn/view_spec.rb +63 -0
- data/spec/prawn_manual_spec.rb +35 -0
- data/spec/spec_helper.rb +19 -23
- metadata +145 -185
- metadata.gz.sig +4 -0
- data/data/images/16bit.alpha +0 -0
- data/data/images/16bit.color +0 -0
- data/data/images/16bit.png +0 -0
- data/data/images/arrow.png +0 -0
- data/data/images/arrow2.png +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.color +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/dice_interlaced.png +0 -0
- data/data/images/fractal.jpg +0 -0
- data/data/images/indexed_color.dat +0 -0
- data/data/images/indexed_color.png +0 -0
- data/data/images/letterhead.jpg +0 -0
- data/data/images/license.md +0 -8
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.color +0 -0
- data/data/images/page_white_text.png +0 -0
- data/data/images/pal_bk.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/prawn.png +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/ruport_type0.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/images/tru256.bmp +0 -0
- data/data/images/web-links.dat +0 -1
- data/data/images/web-links.png +0 -0
- data/data/pdfs/complex_template.pdf +0 -0
- data/data/pdfs/contains_ttf_font.pdf +0 -0
- data/data/pdfs/encrypted.pdf +0 -0
- data/data/pdfs/form.pdf +1 -819
- data/data/pdfs/hexagon.pdf +0 -61
- data/data/pdfs/indirect_reference.pdf +0 -86
- data/data/pdfs/multipage_template.pdf +0 -127
- data/data/pdfs/nested_pages.pdf +0 -118
- data/data/pdfs/page_without_mediabox.pdf +0 -193
- data/data/pdfs/resources_as_indirect_object.pdf +0 -83
- data/data/pdfs/two_hexagons.pdf +0 -90
- data/data/pdfs/version_1_6.pdf +0 -61
- data/data/shift_jis_text.txt +0 -1
- data/spec/acceptance/png.rb +0 -24
- data/spec/annotations_spec.rb +0 -67
- data/spec/bounding_box_spec.rb +0 -501
- data/spec/column_box_spec.rb +0 -59
- data/spec/destinations_spec.rb +0 -13
- data/spec/document_spec.rb +0 -742
- data/spec/extensions/mocha.rb +0 -45
- data/spec/font_metric_cache_spec.rb +0 -52
- data/spec/font_spec.rb +0 -475
- data/spec/formatted_text_arranger_spec.rb +0 -423
- data/spec/formatted_text_box_spec.rb +0 -716
- data/spec/formatted_text_fragment_spec.rb +0 -299
- data/spec/graphics_spec.rb +0 -666
- data/spec/grid_spec.rb +0 -95
- data/spec/image_handler_spec.rb +0 -53
- data/spec/images_spec.rb +0 -167
- data/spec/inline_formatted_text_parser_spec.rb +0 -568
- data/spec/jpg_spec.rb +0 -23
- data/spec/line_wrap_spec.rb +0 -366
- data/spec/measurement_units_spec.rb +0 -22
- data/spec/outline_spec.rb +0 -409
- data/spec/png_spec.rb +0 -235
- data/spec/reference_spec.rb +0 -25
- data/spec/repeater_spec.rb +0 -154
- data/spec/security_spec.rb +0 -151
- data/spec/soft_mask_spec.rb +0 -78
- data/spec/span_spec.rb +0 -43
- data/spec/stamp_spec.rb +0 -179
- data/spec/stroke_styles_spec.rb +0 -208
- data/spec/text_at_spec.rb +0 -142
- data/spec/text_box_spec.rb +0 -1038
- data/spec/text_rendering_mode_spec.rb +0 -45
- data/spec/text_spacing_spec.rb +0 -93
- data/spec/text_spec.rb +0 -549
- data/spec/text_with_inline_formatting_spec.rb +0 -35
- data/spec/transparency_spec.rb +0 -91
- data/spec/view_spec.rb +0 -42
data/lib/prawn/errors.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
3
|
# errors.rb : Implements custom error classes for Prawn
|
4
4
|
#
|
5
5
|
# Copyright April 2008, Gregory Brown. All Rights Reserved.
|
@@ -43,9 +43,9 @@ module Prawn
|
|
43
43
|
#
|
44
44
|
UnknownOption = Class.new(StandardError)
|
45
45
|
|
46
|
-
# this error is raised when a user attempts to embed an image of an
|
47
|
-
# type. This can either a completely unsupported format, or
|
48
|
-
# supported format (ie. some types of PNG)
|
46
|
+
# this error is raised when a user attempts to embed an image of an
|
47
|
+
# unsupported type. This can either a completely unsupported format, or
|
48
|
+
# a dialect of a supported format (ie. some types of PNG)
|
49
49
|
UnsupportedImageType = Class.new(StandardError)
|
50
50
|
|
51
51
|
# This error is raised when a named element has alredy been
|
@@ -63,17 +63,22 @@ module Prawn
|
|
63
63
|
# This error is raised when a required option has not been set
|
64
64
|
RequiredOption = Class.new(StandardError)
|
65
65
|
|
66
|
-
# This error is raised when a requested outline item with a given title does
|
66
|
+
# This error is raised when a requested outline item with a given title does
|
67
|
+
# not exist
|
67
68
|
UnknownOutlineTitle = Class.new(StandardError)
|
68
69
|
|
69
70
|
# This error is raised when a block is required, but not provided
|
70
71
|
BlockRequired = Class.new(StandardError)
|
71
72
|
|
72
|
-
# This error is rased when a graphics method is called with improper
|
73
|
+
# This error is rased when a graphics method is called with improper
|
74
|
+
# arguments
|
73
75
|
InvalidGraphicsPath = Class.new(StandardError)
|
74
76
|
|
75
77
|
# Raised when unrecognized content is provided for a table cell.
|
76
78
|
#
|
77
79
|
UnrecognizedTableContent = Class.new(StandardError)
|
80
|
+
|
81
|
+
# This error is raised when an incompatible join style is specified
|
82
|
+
InvalidJoinStyle = Class.new(StandardError)
|
78
83
|
end
|
79
84
|
end
|
data/lib/prawn/font.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
3
|
# font.rb : The Prawn font class
|
4
4
|
#
|
5
5
|
# Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
#
|
9
|
-
require_relative
|
10
|
-
require_relative "font/ttf"
|
11
|
-
require_relative "font/dfont"
|
12
|
-
require_relative "font_metric_cache"
|
9
|
+
require_relative 'font_metric_cache'
|
13
10
|
|
14
11
|
module Prawn
|
15
12
|
class Document
|
@@ -35,8 +32,8 @@ module Prawn
|
|
35
32
|
# end
|
36
33
|
#
|
37
34
|
# The :name parameter must be a string. It can be one of the 14 built-in
|
38
|
-
# fonts supported by PDF, or the location of a TTF file. The
|
39
|
-
# array specifies the valid built in font values.
|
35
|
+
# fonts supported by PDF, or the location of a TTF file. The
|
36
|
+
# Fonts::AFM::BUILT_INS array specifies the valid built in font values.
|
40
37
|
#
|
41
38
|
# If a ttf font is specified, the glyphs necessary to render your document
|
42
39
|
# will be embedded in the rendered PDF. This should be your preferred option
|
@@ -44,14 +41,15 @@ module Prawn
|
|
44
41
|
# make it more portable.
|
45
42
|
#
|
46
43
|
# The options parameter is an optional hash providing size and style. To use
|
47
|
-
# the :style option you need to map those font styles to their respective
|
44
|
+
# the :style option you need to map those font styles to their respective
|
45
|
+
# font files.
|
48
46
|
# See font_families for more information.
|
49
47
|
#
|
50
48
|
def font(name = nil, options = {})
|
51
|
-
return((defined?(@font) && @font) || font(
|
49
|
+
return((defined?(@font) && @font) || font('Helvetica')) if name.nil?
|
52
50
|
|
53
51
|
if state.pages.empty? && !state.page.in_stamp_stream?
|
54
|
-
|
52
|
+
raise Prawn::Errors::NotOnPage
|
55
53
|
end
|
56
54
|
|
57
55
|
new_font = find_font(name.to_s, options)
|
@@ -96,6 +94,7 @@ module Prawn
|
|
96
94
|
#
|
97
95
|
def font_size(points = nil)
|
98
96
|
return @font_size unless points
|
97
|
+
|
99
98
|
size_before_yield = @font_size
|
100
99
|
@font_size = points
|
101
100
|
block_given? ? yield : return
|
@@ -107,33 +106,35 @@ module Prawn
|
|
107
106
|
font_size(size)
|
108
107
|
end
|
109
108
|
|
110
|
-
# Returns the width of the given string using the given font. If :size is
|
111
|
-
# specified as one of the options, the string is measured using the
|
112
|
-
# font size. You can also pass :kerning as an option to indicate
|
113
|
-
# kerning should be used when measuring the width (defaults to
|
109
|
+
# Returns the width of the given string using the given font. If :size is
|
110
|
+
# not specified as one of the options, the string is measured using the
|
111
|
+
# current font size. You can also pass :kerning as an option to indicate
|
112
|
+
# whether kerning should be used when measuring the width (defaults to
|
113
|
+
# +false+).
|
114
114
|
#
|
115
115
|
# Note that the string _must_ be encoded properly for the font being used.
|
116
116
|
# For AFM fonts, this is WinAnsi. For TTF, make sure the font is encoded as
|
117
117
|
# UTF-8. You can use the Font#normalize_encoding method to make sure strings
|
118
118
|
# are in an encoding appropriate for the current font.
|
119
119
|
#--
|
120
|
-
# For the record, this method used to be a method of Font (and still
|
121
|
-
# to width computations on Font). However, having the primary
|
122
|
-
# calculating string widths exist on Font made it tricky to
|
123
|
-
# for Prawn in which widths are computed differently (e.g.,
|
124
|
-
# tags into account, or the like).
|
125
|
-
#
|
126
|
-
# By putting width_of here, on Document itself, extensions may easily
|
127
|
-
# it and redefine the width calculation behavior.
|
120
|
+
# For the record, this method used to be a method of Font (and still
|
121
|
+
# delegates to width computations on Font). However, having the primary
|
122
|
+
# interface for calculating string widths exist on Font made it tricky to
|
123
|
+
# write extensions for Prawn in which widths are computed differently (e.g.,
|
124
|
+
# taking formatting tags into account, or the like).
|
125
|
+
#
|
126
|
+
# By putting width_of here, on Document itself, extensions may easily
|
127
|
+
# override it and redefine the width calculation behavior.
|
128
128
|
#++
|
129
129
|
def width_of(string, options = {})
|
130
|
-
if
|
130
|
+
if options.key? :inline_format
|
131
|
+
p = options[:inline_format]
|
131
132
|
p = [] unless p.is_a?(Array)
|
132
133
|
|
133
134
|
# Build up an Arranger with the entire string on one line, finalize it,
|
134
135
|
# and find its width.
|
135
136
|
arranger = Prawn::Text::Formatted::Arranger.new(self, options)
|
136
|
-
arranger.consumed =
|
137
|
+
arranger.consumed = text_formatter.format(string, *p)
|
137
138
|
arranger.finalize_line
|
138
139
|
|
139
140
|
arranger.line_width
|
@@ -169,20 +170,26 @@ module Prawn
|
|
169
170
|
#
|
170
171
|
def font_families
|
171
172
|
@font_families ||= {}.merge!(
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
173
|
+
'Courier' => {
|
174
|
+
bold: 'Courier-Bold',
|
175
|
+
italic: 'Courier-Oblique',
|
176
|
+
bold_italic: 'Courier-BoldOblique',
|
177
|
+
normal: 'Courier'
|
178
|
+
},
|
179
|
+
|
180
|
+
'Times-Roman' => {
|
181
|
+
bold: 'Times-Bold',
|
182
|
+
italic: 'Times-Italic',
|
183
|
+
bold_italic: 'Times-BoldItalic',
|
184
|
+
normal: 'Times-Roman'
|
185
|
+
},
|
186
|
+
|
187
|
+
'Helvetica' => {
|
188
|
+
bold: 'Helvetica-Bold',
|
189
|
+
italic: 'Helvetica-Oblique',
|
190
|
+
bold_italic: 'Helvetica-BoldOblique',
|
191
|
+
normal: 'Helvetica'
|
192
|
+
}
|
186
193
|
)
|
187
194
|
end
|
188
195
|
|
@@ -200,7 +207,7 @@ module Prawn
|
|
200
207
|
# finishes, the original font is restored.
|
201
208
|
#
|
202
209
|
def save_font
|
203
|
-
@font ||= find_font(
|
210
|
+
@font ||= find_font('Helvetica')
|
204
211
|
original_font = @font
|
205
212
|
original_size = @font_size
|
206
213
|
|
@@ -210,26 +217,27 @@ module Prawn
|
|
210
217
|
end
|
211
218
|
|
212
219
|
# Looks up the given font using the given criteria. Once a font has been
|
213
|
-
# found by that matches the criteria, it will be cached to subsequent
|
214
|
-
# for that font will return the same object.
|
215
|
-
|
220
|
+
# found by that matches the criteria, it will be cached to subsequent
|
221
|
+
# lookups for that font will return the same object.
|
222
|
+
# --
|
216
223
|
# Challenges involved: the name alone is not sufficient to uniquely identify
|
217
224
|
# a font (think dfont suitcases that can hold multiple different fonts in a
|
218
225
|
# single file). Thus, the :name key is included in the cache key.
|
219
226
|
#
|
220
227
|
# It is further complicated, however, since fonts in some formats (like the
|
221
228
|
# dfont suitcases) can be identified either by numeric index, OR by their
|
222
|
-
# name within the suitcase, and both should hash to the same font object
|
223
|
-
#
|
224
|
-
# which means if someone selects a font both by name, and by
|
225
|
-
# font will be embedded twice. Since we do font subsetting, this
|
226
|
-
# embedding won't be catastrophic, just annoying.
|
229
|
+
# name within the suitcase, and both should hash to the same font object (to
|
230
|
+
# avoid the font being embedded multiple times). This is not yet
|
231
|
+
# implemented, which means if someone selects a font both by name, and by
|
232
|
+
# index, the font will be embedded twice. Since we do font subsetting, this
|
233
|
+
# double embedding won't be catastrophic, just annoying.
|
227
234
|
# ++
|
228
235
|
#
|
229
236
|
# @private
|
230
237
|
def find_font(name, options = {}) #:nodoc:
|
231
238
|
if font_families.key?(name)
|
232
|
-
family
|
239
|
+
family = name
|
240
|
+
name = font_families[name][options[:style] || :normal]
|
233
241
|
if name.is_a?(::Hash)
|
234
242
|
options = options.merge(name)
|
235
243
|
name = options[:file]
|
@@ -240,7 +248,8 @@ module Prawn
|
|
240
248
|
if name.is_a? Prawn::Font
|
241
249
|
font_registry[key] = name
|
242
250
|
else
|
243
|
-
font_registry[key] ||=
|
251
|
+
font_registry[key] ||=
|
252
|
+
Font.load(self, name, options.merge(family: family))
|
244
253
|
end
|
245
254
|
end
|
246
255
|
|
@@ -270,6 +279,18 @@ module Prawn
|
|
270
279
|
# Provides font information and helper functions.
|
271
280
|
#
|
272
281
|
class Font
|
282
|
+
require_relative 'fonts/afm'
|
283
|
+
require_relative 'fonts/ttf'
|
284
|
+
require_relative 'fonts/dfont'
|
285
|
+
require_relative 'fonts/otf'
|
286
|
+
require_relative 'fonts/ttc'
|
287
|
+
|
288
|
+
# @deprecated
|
289
|
+
AFM = Fonts::AFM
|
290
|
+
TTF = Fonts::TTF
|
291
|
+
DFont = Fonts::DFont
|
292
|
+
TTC = Fonts::TTC
|
293
|
+
|
273
294
|
# The current font name
|
274
295
|
attr_reader :name
|
275
296
|
|
@@ -280,12 +301,15 @@ module Prawn
|
|
280
301
|
attr_reader :options
|
281
302
|
|
282
303
|
# Shortcut interface for constructing a font object. Filenames of the form
|
283
|
-
# *.ttf will call
|
284
|
-
# will be passed through to
|
304
|
+
# *.ttf will call Fonts::TTF.new, *.dfont Fonts::DFont.new, *.ttc goes to
|
305
|
+
# Fonts::TTC.new, and anything else will be passed through to
|
306
|
+
# Fonts::AFM.new()
|
285
307
|
def self.load(document, src, options = {})
|
286
308
|
case font_format(src, options)
|
287
|
-
when 'ttf'
|
309
|
+
when 'ttf' then TTF.new(document, src, options)
|
310
|
+
when 'otf' then Fonts::OTF.new(document, src, options)
|
288
311
|
when 'dfont' then DFont.new(document, src, options)
|
312
|
+
when 'ttc' then TTC.new(document, src, options)
|
289
313
|
else AFM.new(document, src, options)
|
290
314
|
end
|
291
315
|
end
|
@@ -294,18 +318,20 @@ module Prawn
|
|
294
318
|
return options.fetch(:format, 'ttf') if src.respond_to? :read
|
295
319
|
|
296
320
|
case src.to_s
|
297
|
-
when /\.ttf$/i then
|
298
|
-
when /\.
|
299
|
-
|
321
|
+
when /\.ttf$/i then 'ttf'
|
322
|
+
when /\.otf$/i then 'otf'
|
323
|
+
when /\.dfont$/i then 'dfont'
|
324
|
+
when /\.ttc$/i then 'ttc'
|
325
|
+
else 'afm'
|
300
326
|
end
|
301
327
|
end
|
302
328
|
|
303
329
|
def initialize(document, name, options = {}) #:nodoc:
|
304
|
-
@document
|
305
|
-
@name
|
306
|
-
@options
|
330
|
+
@document = document
|
331
|
+
@name = name
|
332
|
+
@options = options
|
307
333
|
|
308
|
-
@family
|
334
|
+
@family = options[:family]
|
309
335
|
|
310
336
|
@identifier = generate_unique_id
|
311
337
|
|
@@ -334,15 +360,19 @@ module Prawn
|
|
334
360
|
# font. The string is expected to be UTF-8 going in. It will be re-encoded
|
335
361
|
# and the new string will be returned. For an in-place (destructive)
|
336
362
|
# version, see normalize_encoding!.
|
337
|
-
def normalize_encoding(
|
338
|
-
|
363
|
+
def normalize_encoding(_string)
|
364
|
+
raise NotImplementedError,
|
365
|
+
'subclasses of Prawn::Font must implement #normalize_encoding'
|
339
366
|
end
|
340
367
|
|
341
368
|
# Destructive version of normalize_encoding; normalizes the encoding of a
|
342
369
|
# string in place.
|
343
370
|
#
|
371
|
+
# @deprecated
|
344
372
|
def normalize_encoding!(str)
|
345
|
-
|
373
|
+
warn 'Font#normalize_encoding! is deprecated. ' \
|
374
|
+
'Please use non-mutating version Font#normalize_encoding instead.'
|
375
|
+
str.dup.replace(normalize_encoding(str))
|
346
376
|
end
|
347
377
|
|
348
378
|
# Gets height of current font in PDF points at the given font size
|
@@ -364,11 +394,13 @@ module Prawn
|
|
364
394
|
#
|
365
395
|
def add_to_current_page(subset)
|
366
396
|
@references[subset] ||= register(subset)
|
367
|
-
@document.state.page.fonts.merge!(
|
397
|
+
@document.state.page.fonts.merge!(
|
398
|
+
identifier_for(subset) => @references[subset]
|
399
|
+
)
|
368
400
|
end
|
369
401
|
|
370
402
|
def identifier_for(subset) #:nodoc:
|
371
|
-
"#{@identifier}.#{subset}"
|
403
|
+
"#{@identifier}.#{subset}".to_sym
|
372
404
|
end
|
373
405
|
|
374
406
|
def inspect #:nodoc:
|
@@ -381,14 +413,14 @@ module Prawn
|
|
381
413
|
# Prawn::Table::Text#styled_with_of_single_character)
|
382
414
|
#
|
383
415
|
def hash #:nodoc:
|
384
|
-
[
|
416
|
+
[self.class, name, family, size].hash
|
385
417
|
end
|
386
418
|
|
387
419
|
# Compliments the #hash implementation above
|
388
420
|
#
|
389
421
|
def eql?(other) #:nodoc:
|
390
|
-
self.class == other.class &&
|
391
|
-
|
422
|
+
self.class == other.class && name == other.name &&
|
423
|
+
family == other.family && size == other.send(:size)
|
392
424
|
end
|
393
425
|
|
394
426
|
private
|
@@ -396,7 +428,21 @@ module Prawn
|
|
396
428
|
# generate a font identifier that hasn't been used on the current page yet
|
397
429
|
#
|
398
430
|
def generate_unique_id
|
399
|
-
|
431
|
+
key = nil
|
432
|
+
font_count = @document.font_registry.size + 1
|
433
|
+
loop do
|
434
|
+
key = :"F#{font_count}"
|
435
|
+
break if key_is_unique?(key)
|
436
|
+
|
437
|
+
font_count += 1
|
438
|
+
end
|
439
|
+
key
|
440
|
+
end
|
441
|
+
|
442
|
+
def key_is_unique?(test_key)
|
443
|
+
@document.state.page.fonts.keys.none? do |key|
|
444
|
+
key.to_s.start_with?("#{test_key}.")
|
445
|
+
end
|
400
446
|
end
|
401
447
|
|
402
448
|
def size
|
@@ -1,5 +1,5 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
3
|
# font_metric_cache.rb : The Prawn font class
|
4
4
|
#
|
5
5
|
# Copyright Dec 2012, Kenneth Kalmer. All Rights Reserved.
|
@@ -24,19 +24,25 @@ module Prawn
|
|
24
24
|
def width_of(string, options)
|
25
25
|
f = if options[:style]
|
26
26
|
# override style with :style => :bold
|
27
|
-
@document.find_font(@document.font.family, :
|
27
|
+
@document.find_font(@document.font.family, style: options[:style])
|
28
28
|
else
|
29
29
|
@document.font
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
encoded_string = f.normalize_encoding(string)
|
33
|
+
|
34
|
+
key = CacheEntry.new(f, options, encoded_string)
|
35
|
+
|
36
|
+
@cache[key] ||= f.compute_width_of(encoded_string, options)
|
37
|
+
|
38
|
+
length = @cache[key]
|
33
39
|
|
34
|
-
|
35
|
-
|
40
|
+
character_count = @document.font.character_count(encoded_string)
|
41
|
+
if character_count.positive?
|
42
|
+
length += @document.character_spacing * (character_count - 1)
|
36
43
|
end
|
37
44
|
|
38
|
-
length
|
39
|
-
(@document.character_spacing * @document.font.character_count(string))
|
45
|
+
length
|
40
46
|
end
|
41
47
|
end
|
42
48
|
end
|
@@ -1,15 +1,15 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
3
|
+
# Implements AFM font support for Prawn
|
4
4
|
#
|
5
5
|
# Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
|
9
|
-
require_relative
|
9
|
+
require_relative '../encoding'
|
10
10
|
|
11
11
|
module Prawn
|
12
|
-
|
12
|
+
module Fonts
|
13
13
|
# @private
|
14
14
|
|
15
15
|
class AFM < Font
|
@@ -19,54 +19,61 @@ module Prawn
|
|
19
19
|
|
20
20
|
self.hide_m17n_warning = false
|
21
21
|
|
22
|
-
BUILT_INS = %w[
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
BUILT_INS = %w[
|
23
|
+
Courier Helvetica Times-Roman Symbol ZapfDingbats
|
24
|
+
Courier-Bold Courier-Oblique Courier-BoldOblique
|
25
|
+
Times-Bold Times-Italic Times-BoldItalic
|
26
|
+
Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique
|
27
|
+
].freeze
|
26
28
|
|
27
29
|
def unicode?
|
28
30
|
false
|
29
31
|
end
|
30
32
|
|
31
33
|
def self.metrics_path
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
@metrics_path ||= if ENV['METRICS']
|
35
|
+
ENV['METRICS'].split(':')
|
36
|
+
else
|
37
|
+
[
|
38
|
+
'.', '/usr/lib/afm',
|
39
|
+
'/usr/local/lib/afm',
|
40
|
+
'/usr/openwin/lib/fonts/afm',
|
41
|
+
Prawn::DATADIR + '/fonts'
|
42
|
+
]
|
43
|
+
end
|
42
44
|
end
|
43
45
|
|
44
46
|
attr_reader :attributes #:nodoc:
|
45
47
|
|
48
|
+
# parse each ATM font file once only
|
49
|
+
def self.font_data
|
50
|
+
@font_data ||= SynchronizedCache.new
|
51
|
+
end
|
52
|
+
|
46
53
|
def initialize(document, name, options = {}) #:nodoc:
|
54
|
+
name ||= options[:family]
|
47
55
|
unless BUILT_INS.include?(name)
|
48
|
-
|
56
|
+
raise Prawn::Errors::UnknownFont,
|
57
|
+
"#{name} (#{options[:style] || 'normal'}) is not a known font."
|
49
58
|
end
|
50
59
|
|
51
60
|
super
|
52
61
|
|
53
|
-
@@font_data ||= SynchronizedCache.new # parse each ATM font file once only
|
54
|
-
|
55
62
|
file_name = @name.dup
|
56
|
-
file_name <<
|
63
|
+
file_name << '.afm' unless /\.afm$/.match?(file_name)
|
57
64
|
file_name = file_name[0] == '/' ? file_name : find_font(file_name)
|
58
65
|
|
59
|
-
font_data =
|
60
|
-
@glyph_widths
|
61
|
-
@glyph_table
|
62
|
-
@bounding_boxes
|
63
|
-
@kern_pairs
|
66
|
+
font_data = self.class.font_data[file_name] ||= parse_afm(file_name)
|
67
|
+
@glyph_widths = font_data[:glyph_widths]
|
68
|
+
@glyph_table = font_data[:glyph_table]
|
69
|
+
@bounding_boxes = font_data[:bounding_boxes]
|
70
|
+
@kern_pairs = font_data[:kern_pairs]
|
64
71
|
@kern_pair_table = font_data[:kern_pair_table]
|
65
|
-
@attributes
|
72
|
+
@attributes = font_data[:attributes]
|
66
73
|
|
67
|
-
@ascender
|
68
|
-
@descender = @attributes[
|
69
|
-
@line_gap
|
74
|
+
@ascender = @attributes['ascender'].to_i
|
75
|
+
@descender = @attributes['descender'].to_i
|
76
|
+
@line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
|
70
77
|
end
|
71
78
|
|
72
79
|
# The font bbox, as an array of integers
|
@@ -81,7 +88,7 @@ module Prawn
|
|
81
88
|
|
82
89
|
if options[:kerning]
|
83
90
|
strings, numbers = kern(string).partition { |e| e.is_a?(String) }
|
84
|
-
total_kerning_offset = numbers.inject(0.0) { |
|
91
|
+
total_kerning_offset = numbers.inject(0.0) { |a, e| a + e }
|
85
92
|
(unscaled_width_of(strings.join) - total_kerning_offset) * scale
|
86
93
|
else
|
87
94
|
unscaled_width_of(string) * scale
|
@@ -90,26 +97,30 @@ module Prawn
|
|
90
97
|
|
91
98
|
# Returns true if the font has kerning data, false otherwise
|
92
99
|
#
|
100
|
+
# rubocop: disable Naming/PredicateName
|
93
101
|
def has_kerning_data?
|
94
102
|
@kern_pairs.any?
|
95
103
|
end
|
104
|
+
# rubocop: enable Naming/PredicateName
|
96
105
|
|
97
106
|
# built-in fonts only work with winansi encoding, so translate the
|
98
107
|
# string. Changes the encoding in-place, so the argument itself
|
99
108
|
# is replaced with a string in WinAnsi encoding.
|
100
109
|
#
|
101
110
|
def normalize_encoding(text)
|
102
|
-
text.encode(
|
111
|
+
text.encode('windows-1252')
|
103
112
|
rescue ::Encoding::InvalidByteSequenceError,
|
104
113
|
::Encoding::UndefinedConversionError
|
105
114
|
|
106
115
|
raise Prawn::Errors::IncompatibleStringEncoding,
|
107
|
-
|
108
|
-
|
116
|
+
"Your document includes text that's not compatible with the " \
|
117
|
+
"Windows-1252 character set.\n" \
|
118
|
+
'If you need full UTF-8 support, use external fonts instead of ' \
|
119
|
+
"PDF's built-in fonts.\n"
|
109
120
|
end
|
110
121
|
|
111
122
|
def to_utf8(text)
|
112
|
-
text.encode(
|
123
|
+
text.encode('UTF-8')
|
113
124
|
end
|
114
125
|
|
115
126
|
# Returns the number of characters in +str+ (a WinAnsi-encoded string).
|
@@ -135,45 +146,52 @@ module Prawn
|
|
135
146
|
end
|
136
147
|
|
137
148
|
def glyph_present?(char)
|
138
|
-
|
149
|
+
!normalize_encoding(char).nil?
|
139
150
|
rescue Prawn::Errors::IncompatibleStringEncoding
|
140
151
|
false
|
141
152
|
end
|
142
153
|
|
143
154
|
private
|
144
155
|
|
145
|
-
def register(
|
156
|
+
def register(_subset)
|
146
157
|
font_dict = {
|
147
|
-
:
|
148
|
-
:
|
149
|
-
:
|
158
|
+
Type: :Font,
|
159
|
+
Subtype: :Type1,
|
160
|
+
BaseFont: name.to_sym
|
150
161
|
}
|
151
162
|
|
152
163
|
# Symbolic AFM fonts (Symbol, ZapfDingbats) have their own encodings
|
153
|
-
font_dict
|
164
|
+
font_dict[:Encoding] = :WinAnsiEncoding unless symbolic?
|
154
165
|
|
155
166
|
@document.ref!(font_dict)
|
156
167
|
end
|
157
168
|
|
158
169
|
def symbolic?
|
159
|
-
attributes[
|
170
|
+
attributes['characterset'] == 'Special'
|
160
171
|
end
|
161
172
|
|
162
173
|
def find_font(file)
|
163
|
-
self.class.metrics_path.find { |f| File.exist? "#{f}/#{file}" } +
|
174
|
+
self.class.metrics_path.find { |f| File.exist? "#{f}/#{file}" } +
|
175
|
+
"/#{file}"
|
164
176
|
rescue NoMethodError
|
165
177
|
raise Prawn::Errors::UnknownFont,
|
166
|
-
|
178
|
+
"Couldn't find the font: #{file} in any of:\n" +
|
179
|
+
self.class.metrics_path.join("\n")
|
167
180
|
end
|
168
181
|
|
169
182
|
def parse_afm(file_name)
|
170
|
-
data
|
183
|
+
data = {
|
184
|
+
glyph_widths: {},
|
185
|
+
bounding_boxes: {},
|
186
|
+
kern_pairs: {},
|
187
|
+
attributes: {}
|
188
|
+
}
|
171
189
|
section = []
|
172
190
|
|
173
191
|
File.foreach(file_name) do |line|
|
174
192
|
case line
|
175
193
|
when /^Start(\w+)/
|
176
|
-
section.push
|
194
|
+
section.push Regexp.last_match(1)
|
177
195
|
next
|
178
196
|
when /^End(\w+)/
|
179
197
|
section.pop
|
@@ -181,17 +199,19 @@ module Prawn
|
|
181
199
|
end
|
182
200
|
|
183
201
|
case section
|
184
|
-
when [
|
185
|
-
next unless
|
202
|
+
when %w[FontMetrics CharMetrics]
|
203
|
+
next unless /^CH?\s/.match?(line)
|
186
204
|
|
187
|
-
name
|
188
|
-
data[:glyph_widths][name]
|
205
|
+
name = line[/\bN\s+(\.?\w+)\s*;/, 1]
|
206
|
+
data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
|
189
207
|
data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
|
190
|
-
when [
|
208
|
+
when %w[FontMetrics KernData KernPairs]
|
191
209
|
next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
|
192
|
-
|
193
|
-
|
194
|
-
|
210
|
+
|
211
|
+
data[:kern_pairs][[Regexp.last_match(1), Regexp.last_match(2)]] =
|
212
|
+
Regexp.last_match(3).to_i
|
213
|
+
when %w[FontMetrics KernData TrackKern],
|
214
|
+
%w[FontMetrics Composites]
|
195
215
|
next
|
196
216
|
else
|
197
217
|
parse_generic_afm_attribute(line, data)
|
@@ -204,10 +224,15 @@ module Prawn
|
|
204
224
|
data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
|
205
225
|
end
|
206
226
|
|
207
|
-
character_hash = Hash[
|
208
|
-
|
209
|
-
|
210
|
-
|
227
|
+
character_hash = Hash[
|
228
|
+
Encoding::WinAnsi::CHARACTERS.zip(
|
229
|
+
(0..Encoding::WinAnsi::CHARACTERS.size).to_a
|
230
|
+
)
|
231
|
+
]
|
232
|
+
data[:kern_pair_table] =
|
233
|
+
data[:kern_pairs].each_with_object({}) do |p, h|
|
234
|
+
h[p[0].map { |n| character_hash[n] }] = p[1]
|
235
|
+
end
|
211
236
|
|
212
237
|
data.each_value(&:freeze)
|
213
238
|
data.freeze
|
@@ -215,9 +240,15 @@ module Prawn
|
|
215
240
|
|
216
241
|
def parse_generic_afm_attribute(line, hash)
|
217
242
|
line =~ /(^\w+)\s+(.*)/
|
218
|
-
key
|
243
|
+
key = Regexp.last_match(1).to_s.downcase
|
244
|
+
value = Regexp.last_match(2)
|
219
245
|
|
220
|
-
hash[:attributes][key] =
|
246
|
+
hash[:attributes][key] =
|
247
|
+
if hash[:attributes][key]
|
248
|
+
Array(hash[:attributes][key]) << value
|
249
|
+
else
|
250
|
+
value
|
251
|
+
end
|
221
252
|
end
|
222
253
|
|
223
254
|
# converts a string into an array with spacing offsets
|
@@ -230,7 +261,8 @@ module Prawn
|
|
230
261
|
last_byte = nil
|
231
262
|
|
232
263
|
string.each_byte do |byte|
|
233
|
-
|
264
|
+
k = last_byte && @kern_pair_table[[last_byte, byte]]
|
265
|
+
if k
|
234
266
|
kerned << -k << [byte]
|
235
267
|
else
|
236
268
|
kerned.last << byte
|
@@ -238,14 +270,16 @@ module Prawn
|
|
238
270
|
last_byte = byte
|
239
271
|
end
|
240
272
|
|
241
|
-
kerned.map
|
242
|
-
e = (Array
|
243
|
-
e.respond_to?(:force_encoding)
|
244
|
-
|
273
|
+
kerned.map do |e|
|
274
|
+
e = e.is_a?(Array) ? e.pack('C*') : e
|
275
|
+
if e.respond_to?(:force_encoding)
|
276
|
+
e.force_encoding(::Encoding::Windows_1252)
|
277
|
+
else
|
278
|
+
e
|
279
|
+
end
|
280
|
+
end
|
245
281
|
end
|
246
282
|
|
247
|
-
private
|
248
|
-
|
249
283
|
def unscaled_width_of(string)
|
250
284
|
string.bytes.inject(0) do |s, r|
|
251
285
|
s + @glyph_table[r]
|