prawn 2.1.0 → 2.4.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 +9 -41
- data/lib/prawn.rb +37 -49
- data/lib/prawn/document.rb +193 -141
- data/lib/prawn/document/bounding_box.rb +50 -30
- data/lib/prawn/document/column_box.rb +7 -7
- data/lib/prawn/document/internals.rb +8 -6
- data/lib/prawn/document/span.rb +22 -16
- data/lib/prawn/encoding.rb +69 -68
- data/lib/prawn/errors.rb +12 -7
- data/lib/prawn/font.rb +104 -69
- data/lib/prawn/font_metric_cache.rb +20 -13
- data/lib/prawn/{font → fonts}/afm.rb +108 -72
- 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 +126 -81
- data/lib/prawn/graphics.rb +119 -81
- data/lib/prawn/graphics/blend_mode.rb +9 -8
- data/lib/prawn/graphics/cap_style.rb +3 -3
- data/lib/prawn/graphics/color.rb +43 -39
- data/lib/prawn/graphics/dash.rb +23 -11
- data/lib/prawn/graphics/join_style.rb +9 -3
- data/lib/prawn/graphics/patterns.rb +204 -102
- data/lib/prawn/graphics/transformation.rb +15 -9
- data/lib/prawn/graphics/transparency.rb +17 -13
- data/lib/prawn/grid.rb +84 -48
- data/lib/prawn/image_handler.rb +5 -5
- data/lib/prawn/images.rb +60 -49
- data/lib/prawn/images/image.rb +2 -1
- data/lib/prawn/images/jpg.rb +31 -22
- data/lib/prawn/images/png.rb +67 -63
- data/lib/prawn/measurement_extensions.rb +10 -9
- data/lib/prawn/measurements.rb +19 -15
- data/lib/prawn/outline.rb +98 -77
- data/lib/prawn/repeater.rb +15 -11
- data/lib/prawn/security.rb +93 -70
- 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 +76 -60
- data/lib/prawn/text/box.rb +18 -14
- data/lib/prawn/text/formatted.rb +5 -5
- data/lib/prawn/text/formatted/arranger.rb +80 -40
- data/lib/prawn/text/formatted/box.rb +140 -101
- data/lib/prawn/text/formatted/fragment.rb +11 -14
- data/lib/prawn/text/formatted/line_wrap.rb +128 -67
- data/lib/prawn/text/formatted/parser.rb +147 -123
- data/lib/prawn/text/formatted/wrap.rb +48 -32
- data/lib/prawn/transformation_stack.rb +7 -5
- 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 +7 -8
- 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 +7 -8
- data/manual/bounding_box/creation.rb +6 -7
- data/manual/bounding_box/indentation.rb +14 -15
- data/manual/bounding_box/nesting.rb +25 -18
- 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 +15 -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 +18 -15
- data/manual/example_helper.rb +5 -4
- data/manual/graphics/blend_mode.rb +12 -9
- 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 +5 -6
- data/manual/graphics/fill_rules.rb +10 -10
- data/manual/graphics/gradients.rb +27 -21
- data/manual/graphics/graphics.rb +48 -40
- data/manual/graphics/helper.rb +19 -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 +3 -4
- data/manual/graphics/stroke_cap.rb +6 -7
- data/manual/graphics/stroke_dash.rb +15 -16
- data/manual/graphics/stroke_join.rb +5 -6
- data/manual/graphics/translate.rb +10 -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 +10 -11
- 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 +16 -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 +21 -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 +10 -8
- data/manual/text/formatted_callbacks.rb +33 -24
- data/manual/text/formatted_text.rb +36 -25
- data/manual/text/free_flowing_text.rb +22 -23
- 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 +12 -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 +33 -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 +20 -19
- data/manual/text/utf8.rb +11 -12
- data/manual/text/win_ansi_charset.rb +27 -25
- data/prawn.gemspec +41 -34
- data/spec/extensions/encoding_helpers.rb +3 -3
- data/spec/prawn/document/bounding_box_spec.rb +550 -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 +44 -0
- data/spec/prawn/document_spec.rb +805 -0
- data/spec/prawn/font_metric_cache_spec.rb +54 -0
- data/spec/prawn/font_spec.rb +544 -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 +872 -0
- data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
- data/spec/{image_handler_spec.rb → prawn/image_handler_spec.rb} +14 -14
- data/spec/prawn/images/jpg_spec.rb +20 -0
- data/spec/prawn/images/png_spec.rb +283 -0
- data/spec/prawn/images_spec.rb +229 -0
- data/spec/prawn/measurements_extensions_spec.rb +24 -0
- data/spec/prawn/outline_spec.rb +512 -0
- data/spec/prawn/repeater_spec.rb +166 -0
- data/spec/prawn/soft_mask_spec.rb +74 -0
- data/spec/prawn/stamp_spec.rb +173 -0
- data/spec/prawn/text/box_spec.rb +1110 -0
- data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
- data/spec/prawn/text/formatted/box_spec.rb +849 -0
- data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
- data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
- data/spec/prawn/text/formatted/parser_spec.rb +697 -0
- data/spec/prawn/text_draw_text_spec.rb +150 -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/{transformation_stack_spec.rb → prawn/transformation_stack_spec.rb} +22 -19
- data/spec/prawn/view_spec.rb +63 -0
- data/spec/prawn_manual_spec.rb +35 -0
- data/spec/spec_helper.rb +18 -19
- metadata +102 -222
- metadata.gz.sig +0 -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/blend_modes_bottom_layer.jpg +0 -0
- data/data/images/blend_modes_top_layer.jpg +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/indexed_transparency.png +0 -0
- data/data/images/indexed_transparency_alpha.dat +0 -0
- data/data/images/indexed_transparency_color.dat +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/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_spec.rb +0 -35
- data/spec/annotations_spec.rb +0 -67
- data/spec/blend_mode_spec.rb +0 -71
- 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 -738
- data/spec/font_metric_cache_spec.rb +0 -52
- data/spec/font_spec.rb +0 -475
- data/spec/formatted_text_arranger_spec.rb +0 -452
- data/spec/formatted_text_box_spec.rb +0 -716
- data/spec/formatted_text_fragment_spec.rb +0 -299
- data/spec/graphics_spec.rb +0 -705
- data/spec/grid_spec.rb +0 -95
- 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 -257
- 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 -1042
- data/spec/text_rendering_mode_spec.rb +0 -45
- data/spec/text_spacing_spec.rb +0 -93
- data/spec/text_spec.rb +0 -543
- 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/outline.rb
CHANGED
@@ -1,26 +1,28 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Prawn
|
4
4
|
class Document
|
5
5
|
# @group Stable API
|
6
6
|
|
7
|
-
# Lazily instantiates a Prawn::Outline object for document. This is used as
|
8
|
-
# to methods to build the outline tree for a document's table
|
7
|
+
# Lazily instantiates a Prawn::Outline object for document. This is used as
|
8
|
+
# point of entry to methods to build the outline tree for a document's table
|
9
|
+
# of contents.
|
9
10
|
def outline
|
10
11
|
@outline ||= Outline.new(self)
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
14
15
|
# The Outline class organizes the outline tree items for the document.
|
15
|
-
# Note that the prev and parent instance variables are adjusted while
|
16
|
-
# through the nested blocks. These variables along with the
|
17
|
-
# of blocks are the primary means by which the relations
|
18
|
-
# OutlineItems and the OutlineRoot are set. Unfortunately, the
|
19
|
-
# understand how this works is to follow the method calls through
|
16
|
+
# Note that the prev and parent instance variables are adjusted while
|
17
|
+
# navigating through the nested blocks. These variables along with the
|
18
|
+
# presence or absense of blocks are the primary means by which the relations
|
19
|
+
# for the various OutlineItems and the OutlineRoot are set. Unfortunately, the
|
20
|
+
# best way to understand how this works is to follow the method calls through
|
21
|
+
# a real example.
|
20
22
|
#
|
21
|
-
# Some ideas for the organization of this class were gleaned from name_tree.
|
22
|
-
# particular the way in which the OutlineItems are finally rendered into
|
23
|
-
# objects in PdfObject through a hash.
|
23
|
+
# Some ideas for the organization of this class were gleaned from name_tree.
|
24
|
+
# In particular the way in which the OutlineItems are finally rendered into
|
25
|
+
# document objects in PdfObject through a hash.
|
24
26
|
#
|
25
27
|
class Outline
|
26
28
|
# @private
|
@@ -42,10 +44,11 @@ module Prawn
|
|
42
44
|
|
43
45
|
# Defines/Updates an outline for the document.
|
44
46
|
# The outline is an optional nested index that appears on the side of a PDF
|
45
|
-
# document usually with direct links to pages. The outline DSL is defined by
|
46
|
-
# blocks involving two methods: section and page; see the
|
47
|
-
# for their arguments and options. Note that
|
48
|
-
# to add more sections to the end of the
|
47
|
+
# document usually with direct links to pages. The outline DSL is defined by
|
48
|
+
# nested blocks involving two methods: section and page; see the
|
49
|
+
# documentation on those methods for their arguments and options. Note that
|
50
|
+
# one can also use outline#update to add more sections to the end of the
|
51
|
+
# outline tree using the same syntax and scope.
|
49
52
|
#
|
50
53
|
# The syntax is best illustrated with an example:
|
51
54
|
#
|
@@ -69,28 +72,31 @@ module Prawn
|
|
69
72
|
# end
|
70
73
|
#
|
71
74
|
def define(&block)
|
72
|
-
instance_eval(&block)
|
75
|
+
instance_eval(&block) if block
|
73
76
|
end
|
74
77
|
|
75
|
-
alias
|
78
|
+
alias update define
|
76
79
|
|
77
80
|
# Inserts an outline section to the outline tree (see outline#define).
|
78
81
|
# Although you will probably choose to exclusively use outline#define so
|
79
|
-
# that your outline tree is contained and easy to manage, this method
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
82
|
+
# that your outline tree is contained and easy to manage, this method gives
|
83
|
+
# you the option to insert sections to the outline tree at any point during
|
84
|
+
# document generation. This method allows you to add a child subsection to
|
85
|
+
# any other item at any level in the outline tree. Currently the only way
|
86
|
+
# to locate the place of entry is with the title for the item. If your title
|
87
|
+
# names are not unique consider using define_outline.
|
85
88
|
# The method takes the following arguments:
|
86
|
-
# title: a string that must match an outline title to add
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
89
|
+
# title: a string that must match an outline title to add
|
90
|
+
# the subsection to
|
91
|
+
# position: either :first or :last (the default) where the subsection will
|
92
|
+
# be placed relative to other child elements. If you need to position
|
93
|
+
# your subsection in between other elements then consider using
|
94
|
+
# #insert_section_after
|
90
95
|
# block: uses the same DSL syntax as outline#define, for example:
|
91
96
|
#
|
92
|
-
# Consider using this method inside of outline.update if you want to have
|
93
|
-
# to be scoped as self (see #insert_section_after
|
97
|
+
# Consider using this method inside of outline.update if you want to have
|
98
|
+
# the outline object to be scoped as self (see #insert_section_after
|
99
|
+
# example).
|
94
100
|
#
|
95
101
|
# go_to_page 2
|
96
102
|
# start_new_page
|
@@ -101,8 +107,10 @@ module Prawn
|
|
101
107
|
#
|
102
108
|
def add_subsection_to(title, position = :last, &block)
|
103
109
|
@parent = items[title]
|
104
|
-
|
105
|
-
|
110
|
+
unless @parent
|
111
|
+
raise Prawn::Errors::UnknownOutlineTitle,
|
112
|
+
"\n No outline item with title: '#{title}' exists in the outline tree"
|
113
|
+
end
|
106
114
|
@prev = position == :first ? nil : @parent.data.last
|
107
115
|
nxt = position == :first ? @parent.data.first : nil
|
108
116
|
insert_section(nxt, &block)
|
@@ -110,12 +118,13 @@ module Prawn
|
|
110
118
|
|
111
119
|
# Inserts an outline section to the outline tree (see outline#define).
|
112
120
|
# Although you will probably choose to exclusively use outline#define so
|
113
|
-
# that your outline tree is contained and easy to manage, this method
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
# Currently the only way to locate the place of entry is with the title for
|
118
|
-
# item. If your title names are not unique consider using
|
121
|
+
# that your outline tree is contained and easy to manage, this method gives
|
122
|
+
# you the option to insert sections to the outline tree at any point during
|
123
|
+
# document generation. Unlike outline.add_section, this method allows you to
|
124
|
+
# enter a section after any other item at any level in the outline tree.
|
125
|
+
# Currently the only way to locate the place of entry is with the title for
|
126
|
+
# the item. If your title names are not unique consider using
|
127
|
+
# define_outline.
|
119
128
|
# The method takes the following arguments:
|
120
129
|
# title: the title of other section or page to insert new section after
|
121
130
|
# block: uses the same DSL syntax as outline#define, for example:
|
@@ -131,30 +140,36 @@ module Prawn
|
|
131
140
|
#
|
132
141
|
def insert_section_after(title, &block)
|
133
142
|
@prev = items[title]
|
134
|
-
|
135
|
-
|
143
|
+
unless @prev
|
144
|
+
raise Prawn::Errors::UnknownOutlineTitle,
|
145
|
+
"\n No outline item with title: '#{title}' exists in the outline tree"
|
146
|
+
end
|
136
147
|
@parent = @prev.data.parent
|
137
148
|
nxt = @prev.data.next
|
138
149
|
insert_section(nxt, &block)
|
139
150
|
end
|
140
151
|
|
141
|
-
# See outline#define above for documentation on how this is used in that
|
152
|
+
# See outline#define above for documentation on how this is used in that
|
153
|
+
# context
|
142
154
|
#
|
143
155
|
# Adds an outine section to the outline tree.
|
144
156
|
# Although you will probably choose to exclusively use outline#define so
|
145
|
-
# that your outline tree is contained and easy to manage, this method
|
146
|
-
#
|
147
|
-
#
|
148
|
-
# the section will be added at the top level after the other root
|
149
|
-
# For more flexible placement try using
|
150
|
-
# outline#add_subsection_to
|
157
|
+
# that your outline tree is contained and easy to manage, this method gives
|
158
|
+
# you the option to add sections to the outline tree at any point during
|
159
|
+
# document generation. When not being called from within another #section
|
160
|
+
# block the section will be added at the top level after the other root
|
161
|
+
# elements of the outline. For more flexible placement try using
|
162
|
+
# outline#insert_section_after and/or outline#add_subsection_to
|
163
|
+
#
|
151
164
|
# Takes the following arguments:
|
152
165
|
# title: the outline text that appears for the section.
|
153
|
-
# options: destination - optional integer defining the page number for
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
166
|
+
# options: destination - optional integer defining the page number for
|
167
|
+
# a destination link to the top of the page (using a :FIT
|
168
|
+
# destination).
|
169
|
+
# - or an array with a custom destination (see the #dest_*
|
170
|
+
# methods of the PDF::Destination module)
|
171
|
+
# closed - whether the section should show its nested outline
|
172
|
+
# elements.
|
158
173
|
# - defaults to false.
|
159
174
|
# block: more nested subsections and/or page blocks
|
160
175
|
#
|
@@ -167,36 +182,40 @@ module Prawn
|
|
167
182
|
add_outline_item(title, options, &block)
|
168
183
|
end
|
169
184
|
|
170
|
-
# See Outline#define above for more documentation on how it is used in that
|
185
|
+
# See Outline#define above for more documentation on how it is used in that
|
186
|
+
# context
|
171
187
|
#
|
172
188
|
# Adds a page to the outline.
|
173
189
|
# Although you will probably choose to exclusively use outline#define so
|
174
190
|
# that your outline tree is contained and easy to manage, this method also
|
175
191
|
# gives you the option to add pages to the root of outline tree at any point
|
176
|
-
# during document generation. Note that the page will be added at the
|
177
|
-
#
|
178
|
-
# using outline#insert_section_after and/or outline#add_subsection_to.
|
192
|
+
# during document generation. Note that the page will be added at the top
|
193
|
+
# level after the other root outline elements. For more flexible placement
|
194
|
+
# try using outline#insert_section_after and/or outline#add_subsection_to.
|
179
195
|
#
|
180
196
|
# Takes the following arguments:
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
197
|
+
# options:
|
198
|
+
# title - REQUIRED. The outline text that appears for the page.
|
199
|
+
# destination - optional integer defining the page number for
|
200
|
+
# a destination link to the top of the page (using a :FIT
|
201
|
+
# destination).
|
202
|
+
# or an array with a custom destination (see the dest_* methods
|
203
|
+
# of the PDF::Destination module)
|
204
|
+
# closed - whether the section should show its nested outline elements.
|
205
|
+
# - defaults to false.
|
189
206
|
# example usage:
|
190
207
|
#
|
191
208
|
# outline.page :title => "Very Last Page"
|
192
|
-
#
|
193
|
-
#
|
209
|
+
#
|
210
|
+
# Note: this method is almost identical to section except that it does not
|
211
|
+
# accept a block thereby defining the outline item as a leaf on the outline
|
212
|
+
# tree structure.
|
194
213
|
def page(options = {})
|
195
214
|
if options[:title]
|
196
215
|
title = options[:title]
|
197
216
|
else
|
198
|
-
|
199
|
-
|
217
|
+
raise Prawn::Errors::RequiredOption,
|
218
|
+
"\nTitle is a required option for page"
|
200
219
|
end
|
201
220
|
add_outline_item(title, options)
|
202
221
|
end
|
@@ -207,15 +226,16 @@ module Prawn
|
|
207
226
|
# lazily initialized, so that documents that do not have an outline
|
208
227
|
# do not incur the additional overhead.
|
209
228
|
def root
|
210
|
-
document.state.store.root.data[:Outlines] ||=
|
229
|
+
document.state.store.root.data[:Outlines] ||=
|
230
|
+
document.ref!(PDF::Core::OutlineRoot.new)
|
211
231
|
end
|
212
232
|
|
213
233
|
def add_outline_item(title, options, &block)
|
214
234
|
outline_item = create_outline_item(title, options)
|
215
|
-
|
235
|
+
establish_relations(outline_item)
|
216
236
|
increase_count
|
217
237
|
set_variables_for_block(outline_item, block)
|
218
|
-
|
238
|
+
yield if block
|
219
239
|
reset_parent(outline_item)
|
220
240
|
end
|
221
241
|
|
@@ -234,7 +254,7 @@ module Prawn
|
|
234
254
|
items[title] = document.ref!(outline_item)
|
235
255
|
end
|
236
256
|
|
237
|
-
def
|
257
|
+
def establish_relations(outline_item)
|
238
258
|
prev.data.next = outline_item if prev
|
239
259
|
parent.data.first = outline_item unless prev
|
240
260
|
parent.data.last = outline_item
|
@@ -244,11 +264,12 @@ module Prawn
|
|
244
264
|
counting_parent = parent
|
245
265
|
while counting_parent
|
246
266
|
counting_parent.data.count += 1
|
247
|
-
|
248
|
-
counting_parent
|
249
|
-
|
250
|
-
|
251
|
-
|
267
|
+
counting_parent =
|
268
|
+
if counting_parent == root
|
269
|
+
nil
|
270
|
+
else
|
271
|
+
counting_parent.data.parent
|
272
|
+
end
|
252
273
|
end
|
253
274
|
end
|
254
275
|
|
@@ -267,7 +288,7 @@ module Prawn
|
|
267
288
|
def insert_section(nxt, &block)
|
268
289
|
last = @parent.data.last
|
269
290
|
if block
|
270
|
-
|
291
|
+
yield
|
271
292
|
end
|
272
293
|
adjust_relations(nxt, last)
|
273
294
|
reset_root_positioning
|
data/lib/prawn/repeater.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
3
|
# repeater.rb : Implements repeated page elements.
|
4
4
|
# Heavy inspired by repeating_element() in PDF::Wrapper
|
5
5
|
# http://pdf-wrapper.rubyforge.org/
|
@@ -20,9 +20,9 @@ module Prawn
|
|
20
20
|
|
21
21
|
# @group Experimental API
|
22
22
|
|
23
|
-
# Provides a way to execute a block of code repeatedly based on
|
24
|
-
# page_filter. Since Stamp is used under the hood, this method is very
|
25
|
-
# efficient.
|
23
|
+
# Provides a way to execute a block of code repeatedly based on
|
24
|
+
# a page_filter. Since Stamp is used under the hood, this method is very
|
25
|
+
# space efficient.
|
26
26
|
#
|
27
27
|
# Available page filters are:
|
28
28
|
# :all -- repeats on every page
|
@@ -32,8 +32,9 @@ module Prawn
|
|
32
32
|
# some_range -- repeats on every page included in the range
|
33
33
|
# some_lambda -- yields page number and repeats for true return values
|
34
34
|
#
|
35
|
-
# Also accepts an optional second argument for dynamic content which
|
36
|
-
# in the context of the filtered pages without using
|
35
|
+
# Also accepts an optional second argument for dynamic content which
|
36
|
+
# executes the code in the context of the filtered pages without using
|
37
|
+
# a Stamp.
|
37
38
|
#
|
38
39
|
# Example:
|
39
40
|
#
|
@@ -75,7 +76,10 @@ module Prawn
|
|
75
76
|
# end
|
76
77
|
#
|
77
78
|
def repeat(page_filter, options = {}, &block)
|
78
|
-
|
79
|
+
dynamic = options.fetch(:dynamic, false)
|
80
|
+
repeaters << Prawn::Repeater.new(
|
81
|
+
self, page_filter, dynamic, &block
|
82
|
+
)
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
@@ -91,10 +95,10 @@ module Prawn
|
|
91
95
|
attr_reader :name
|
92
96
|
|
93
97
|
def initialize(document, page_filter, dynamic = false, &block)
|
94
|
-
@document
|
98
|
+
@document = document
|
95
99
|
@page_filter = page_filter
|
96
100
|
@dynamic = dynamic
|
97
|
-
@stamp_name
|
101
|
+
@stamp_name = "prawn_repeater(#{Repeater.count})"
|
98
102
|
@document.create_stamp(@stamp_name, &block) unless dynamic
|
99
103
|
@block = block if dynamic
|
100
104
|
@graphic_state = document.state.page.graphic_state.dup
|
@@ -111,7 +115,7 @@ module Prawn
|
|
111
115
|
@document.stamp(@stamp_name) if match?(page_number)
|
112
116
|
elsif @block && match?(page_number)
|
113
117
|
@document.save_graphics_state(@graphic_state) do
|
114
|
-
@document.
|
118
|
+
@document.__send__(:freeze_stamp_graphics)
|
115
119
|
@block.call
|
116
120
|
end
|
117
121
|
end
|
data/lib/prawn/security.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
3
|
# encryption.rb : Implements encrypted PDF and access permissions.
|
4
4
|
#
|
5
5
|
# Copyright August 2008, Brad Ediger. All Rights Reserved.
|
@@ -8,8 +8,6 @@
|
|
8
8
|
|
9
9
|
require 'digest/md5'
|
10
10
|
|
11
|
-
require 'pdf/core/byte_string'
|
12
|
-
|
13
11
|
require_relative 'security/arcfour'
|
14
12
|
|
15
13
|
module Prawn
|
@@ -88,14 +86,14 @@ module Prawn
|
|
88
86
|
# PDF format.
|
89
87
|
#
|
90
88
|
def encrypt_document(options = {})
|
91
|
-
Prawn.verify_options [
|
92
|
-
|
93
|
-
@user_password = options.delete(:user_password) ||
|
89
|
+
Prawn.verify_options %i[user_password owner_password permissions],
|
90
|
+
options
|
91
|
+
@user_password = options.delete(:user_password) || ''
|
94
92
|
|
95
93
|
@owner_password = options.delete(:owner_password) || @user_password
|
96
94
|
if @owner_password == :random
|
97
95
|
# Generate a completely ridiculous password
|
98
|
-
@owner_password = (1..32).map{ rand(256) }.pack(
|
96
|
+
@owner_password = (1..32).map { rand(256) }.pack('c*')
|
99
97
|
end
|
100
98
|
|
101
99
|
self.permissions = options.delete(:permissions) || {}
|
@@ -124,32 +122,41 @@ module Prawn
|
|
124
122
|
|
125
123
|
# Provides the values for the trailer encryption dictionary.
|
126
124
|
def encryption_dictionary
|
127
|
-
{
|
128
|
-
:
|
129
|
-
:
|
130
|
-
:
|
131
|
-
:
|
132
|
-
:
|
125
|
+
{
|
126
|
+
Filter: :Standard, # default PDF security handler
|
127
|
+
V: 1, # "Algorithm 3.1", PDF reference 1.3
|
128
|
+
R: 2, # Revision 2 of the algorithm
|
129
|
+
O: PDF::Core::ByteString.new(owner_password_hash),
|
130
|
+
U: PDF::Core::ByteString.new(user_password_hash),
|
131
|
+
P: permissions_value
|
132
|
+
}
|
133
133
|
end
|
134
134
|
|
135
135
|
# Flags in the permissions word, numbered as LSB = 1
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
136
|
+
PERMISSIONS_BITS = {
|
137
|
+
print_document: 3,
|
138
|
+
modify_contents: 4,
|
139
|
+
copy_contents: 5,
|
140
|
+
modify_annotations: 6
|
141
|
+
}.freeze
|
142
|
+
private_constant :PERMISSIONS_BITS
|
140
143
|
|
141
|
-
|
144
|
+
FULL_PERMISSIONS = 0b1111_1111_1111_1111_1111_1111_1111_1111
|
145
|
+
private_constant :FULL_PERMISSIONS
|
142
146
|
|
143
147
|
def permissions=(perms = {})
|
144
|
-
@permissions ||=
|
148
|
+
@permissions ||= FULL_PERMISSIONS
|
145
149
|
perms.each do |key, value|
|
146
|
-
unless
|
147
|
-
|
148
|
-
|
150
|
+
unless PERMISSIONS_BITS[key]
|
151
|
+
raise(
|
152
|
+
ArgumentError,
|
153
|
+
"Unknown permission :#{key}. Valid options: " +
|
154
|
+
PERMISSIONS_BITS.keys.map(&:inspect).join(', ')
|
155
|
+
)
|
149
156
|
end
|
150
157
|
|
151
158
|
# 0-based bit number, from LSB
|
152
|
-
bit_position =
|
159
|
+
bit_position = PERMISSIONS_BITS[key] - 1
|
153
160
|
|
154
161
|
if value # set bit
|
155
162
|
@permissions |= (1 << bit_position)
|
@@ -160,40 +167,42 @@ module Prawn
|
|
160
167
|
end
|
161
168
|
|
162
169
|
def permissions_value
|
163
|
-
@permissions ||
|
170
|
+
@permissions || FULL_PERMISSIONS
|
164
171
|
end
|
165
172
|
|
166
|
-
|
167
|
-
|
168
|
-
|
173
|
+
PASSWORD_PADDING =
|
174
|
+
'28BF4E5E4E758A4164004E56FFFA01082E2E00B6D0683E802F0CA9FE6453697A'
|
175
|
+
.scan(/../).map { |x| x.to_i(16) }.pack('c*')
|
169
176
|
|
170
177
|
# Pads or truncates a password to 32 bytes as per Alg 3.2.
|
171
178
|
def pad_password(password)
|
172
179
|
password = password[0, 32]
|
173
|
-
password +
|
180
|
+
password + PASSWORD_PADDING[0, 32 - password.length]
|
174
181
|
end
|
175
182
|
|
176
183
|
def user_encryption_key
|
177
|
-
@user_encryption_key ||=
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
+
@user_encryption_key ||=
|
185
|
+
begin
|
186
|
+
md5 = Digest::MD5.new
|
187
|
+
md5 << pad_password(@user_password)
|
188
|
+
md5 << owner_password_hash
|
189
|
+
md5 << [permissions_value].pack('V')
|
190
|
+
md5.digest[0, 5]
|
191
|
+
end
|
184
192
|
end
|
185
193
|
|
186
194
|
# The O (owner) value in the encryption dictionary. Algorithm 3.3.
|
187
195
|
def owner_password_hash
|
188
|
-
@owner_password_hash ||=
|
189
|
-
|
190
|
-
|
191
|
-
|
196
|
+
@owner_password_hash ||=
|
197
|
+
begin
|
198
|
+
key = Digest::MD5.digest(pad_password(@owner_password))[0, 5]
|
199
|
+
Arcfour.new(key).encrypt(pad_password(@user_password))
|
200
|
+
end
|
192
201
|
end
|
193
202
|
|
194
203
|
# The U (user) value in the encryption dictionary. Algorithm 3.4.
|
195
204
|
def user_password_hash
|
196
|
-
Arcfour.new(user_encryption_key).encrypt(
|
205
|
+
Arcfour.new(user_encryption_key).encrypt(PASSWORD_PADDING)
|
197
206
|
end
|
198
207
|
end
|
199
208
|
end
|
@@ -204,46 +213,53 @@ module PDF
|
|
204
213
|
module Core
|
205
214
|
module_function
|
206
215
|
|
207
|
-
# Like
|
216
|
+
# Like pdf_object, but returns an encrypted result if required.
|
208
217
|
# For direct objects, requires the object identifier and generation number
|
209
218
|
# from the indirect object referencing obj.
|
210
219
|
#
|
211
220
|
# @private
|
212
|
-
def
|
221
|
+
def encrypted_pdf_object(obj, key, id, gen, in_content_stream = false)
|
213
222
|
case obj
|
214
223
|
when Array
|
215
|
-
|
216
|
-
|
217
|
-
|
224
|
+
array_content = obj.map do |e|
|
225
|
+
encrypted_pdf_object(e, key, id, gen, in_content_stream)
|
226
|
+
end.join(' ')
|
227
|
+
"[#{array_content}]"
|
218
228
|
when LiteralString
|
219
|
-
obj =
|
229
|
+
obj =
|
230
|
+
ByteString.new(
|
231
|
+
Prawn::Document::Security.encrypt_string(obj, key, id, gen)
|
232
|
+
).gsub(/[\\\n()]/) { |m| "\\#{m}" }
|
220
233
|
"(#{obj})"
|
221
234
|
when Time
|
222
|
-
obj = obj.strftime(
|
223
|
-
obj =
|
235
|
+
obj = "#{obj.strftime('D:%Y%m%d%H%M%S%z').chop.chop}'00'"
|
236
|
+
obj =
|
237
|
+
ByteString.new(
|
238
|
+
Prawn::Document::Security.encrypt_string(obj, key, id, gen)
|
239
|
+
).gsub(/[\\\n()]/) { |m| "\\#{m}" }
|
224
240
|
"(#{obj})"
|
225
241
|
when String
|
226
|
-
|
242
|
+
pdf_object(
|
227
243
|
ByteString.new(
|
228
|
-
Prawn::Document::Security.encrypt_string(obj, key, id, gen)
|
229
|
-
|
244
|
+
Prawn::Document::Security.encrypt_string(obj, key, id, gen)
|
245
|
+
),
|
246
|
+
in_content_stream
|
247
|
+
)
|
230
248
|
when ::Hash
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
"A PDF Dictionary must be keyed by names"
|
249
|
+
hash_content = obj.map do |k, v|
|
250
|
+
unless k.is_a?(String) || k.is_a?(Symbol)
|
251
|
+
raise PDF::Core::Errors::FailedObjectConversion,
|
252
|
+
'A PDF Dictionary must be keyed by names'
|
236
253
|
end
|
237
|
-
|
238
|
-
end
|
239
|
-
|
254
|
+
"#{pdf_object(k.to_sym, in_content_stream)} #{encrypted_pdf_object(v, key, id, gen, in_content_stream)}\n"
|
255
|
+
end.join('')
|
256
|
+
"<< #{hash_content}>>"
|
240
257
|
when NameTree::Value
|
241
|
-
|
242
|
-
EncryptedPdfObject(obj.value, key, id, gen, in_content_stream)
|
258
|
+
"#{pdf_object(obj.name)} #{encrypted_pdf_object(obj.value, key, id, gen, in_content_stream)}"
|
243
259
|
when PDF::Core::OutlineRoot, PDF::Core::OutlineItem
|
244
|
-
|
245
|
-
else # delegate back to
|
246
|
-
|
260
|
+
encrypted_pdf_object(obj.to_hash, key, id, gen, in_content_stream)
|
261
|
+
else # delegate back to pdf_object
|
262
|
+
pdf_object(obj, in_content_stream)
|
247
263
|
end
|
248
264
|
end
|
249
265
|
|
@@ -251,7 +267,11 @@ module PDF
|
|
251
267
|
class Stream
|
252
268
|
def encrypted_object(key, id, gen)
|
253
269
|
if filtered_stream
|
254
|
-
"stream\n#{
|
270
|
+
"stream\n#{
|
271
|
+
Prawn::Document::Security.encrypt_string(
|
272
|
+
filtered_stream, key, id, gen
|
273
|
+
)
|
274
|
+
}\nendstream\n"
|
255
275
|
else
|
256
276
|
''
|
257
277
|
end
|
@@ -263,13 +283,16 @@ module PDF
|
|
263
283
|
# Returns the object definition for the object this references, keyed from
|
264
284
|
# +key+.
|
265
285
|
def encrypted_object(key)
|
266
|
-
@on_encode
|
286
|
+
@on_encode&.call(self)
|
267
287
|
|
268
|
-
output = "#{@identifier} #{gen} obj\n"
|
288
|
+
output = +"#{@identifier} #{gen} obj\n"
|
269
289
|
if @stream.empty?
|
270
|
-
output <<
|
290
|
+
output <<
|
291
|
+
PDF::Core.encrypted_pdf_object(data, key, @identifier, gen) << "\n"
|
271
292
|
else
|
272
|
-
output << PDF::Core
|
293
|
+
output << PDF::Core.encrypted_pdf_object(
|
294
|
+
data.merge(@stream.data), key, @identifier, gen
|
295
|
+
) << "\n" <<
|
273
296
|
@stream.encrypted_object(key, @identifier, gen)
|
274
297
|
end
|
275
298
|
|