asciidoctor-pdf 1.5.0.beta.8 → 1.5.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +49 -0
  3. data/LICENSE.adoc +1 -1
  4. data/NOTICE.adoc +1 -1
  5. data/README.adoc +43 -47
  6. data/asciidoctor-pdf.gemspec +5 -1
  7. data/bin/asciidoctor-pdf-optimize +1 -1
  8. data/data/themes/base-theme.yml +4 -3
  9. data/data/themes/default-theme.yml +10 -5
  10. data/docs/theming-guide.adoc +286 -22
  11. data/lib/asciidoctor-pdf.rb +1 -0
  12. data/lib/asciidoctor-pdf/converter.rb +1 -0
  13. data/lib/asciidoctor-pdf/version.rb +1 -0
  14. data/lib/asciidoctor/pdf.rb +13 -2
  15. data/lib/asciidoctor/pdf/converter.rb +3962 -3955
  16. data/lib/asciidoctor/pdf/ext.rb +9 -0
  17. data/lib/asciidoctor/pdf/ext/asciidoctor.rb +1 -0
  18. data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_block.rb +1 -0
  19. data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_node.rb +1 -0
  20. data/lib/asciidoctor/pdf/ext/asciidoctor/document.rb +1 -0
  21. data/lib/asciidoctor/pdf/ext/asciidoctor/image.rb +18 -16
  22. data/lib/asciidoctor/pdf/ext/asciidoctor/list.rb +3 -2
  23. data/lib/asciidoctor/pdf/ext/asciidoctor/list_item.rb +2 -1
  24. data/lib/asciidoctor/pdf/ext/asciidoctor/logging_shim.rb +3 -4
  25. data/lib/asciidoctor/pdf/ext/asciidoctor/section.rb +8 -6
  26. data/lib/asciidoctor/pdf/ext/core.rb +2 -0
  27. data/lib/asciidoctor/pdf/ext/core/array.rb +1 -0
  28. data/lib/asciidoctor/pdf/ext/core/hash.rb +1 -0
  29. data/lib/asciidoctor/pdf/ext/core/numeric.rb +4 -3
  30. data/lib/asciidoctor/pdf/ext/core/object.rb +1 -0
  31. data/lib/asciidoctor/pdf/ext/core/quantifiable_stdout.rb +8 -1
  32. data/lib/asciidoctor/pdf/ext/core/regexp.rb +1 -0
  33. data/lib/asciidoctor/pdf/ext/core/string.rb +6 -7
  34. data/lib/asciidoctor/pdf/ext/pdf-core.rb +1 -0
  35. data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +3 -4
  36. data/lib/asciidoctor/pdf/ext/pdf-core/pdf_object.rb +2 -1
  37. data/lib/asciidoctor/pdf/ext/prawn-svg.rb +1 -0
  38. data/lib/asciidoctor/pdf/ext/prawn-svg/interface.rb +11 -8
  39. data/lib/asciidoctor/pdf/ext/prawn-table.rb +2 -1
  40. data/lib/asciidoctor/pdf/ext/prawn-table/cell.rb +9 -10
  41. data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +62 -57
  42. data/lib/asciidoctor/pdf/ext/prawn-table/cell/text.rb +5 -3
  43. data/lib/asciidoctor/pdf/ext/prawn-templates.rb +1 -0
  44. data/lib/asciidoctor/pdf/ext/prawn.rb +1 -0
  45. data/lib/asciidoctor/pdf/ext/prawn/coderay_encoder.rb +73 -72
  46. data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +814 -818
  47. data/lib/asciidoctor/pdf/ext/prawn/font/afm.rb +4 -3
  48. data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +2 -1
  49. data/lib/asciidoctor/pdf/ext/prawn/formatted_text/fragment.rb +7 -2
  50. data/lib/asciidoctor/pdf/ext/prawn/images.rb +45 -44
  51. data/lib/asciidoctor/pdf/ext/pygments.rb +34 -0
  52. data/lib/asciidoctor/pdf/ext/rouge.rb +1 -1
  53. data/lib/asciidoctor/pdf/ext/rouge/formatters/prawn.rb +181 -149
  54. data/lib/asciidoctor/pdf/ext/rouge/themes/asciidoctor_pdf_default.rb +1 -0
  55. data/lib/asciidoctor/pdf/formatted_text.rb +2 -0
  56. data/lib/asciidoctor/pdf/formatted_text/formatter.rb +35 -34
  57. data/lib/asciidoctor/pdf/formatted_text/fragment_position_renderer.rb +8 -7
  58. data/lib/asciidoctor/pdf/formatted_text/inline_destination_marker.rb +13 -14
  59. data/lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb +112 -133
  60. data/lib/asciidoctor/pdf/formatted_text/inline_image_renderer.rb +43 -41
  61. data/lib/asciidoctor/pdf/formatted_text/inline_text_aligner.rb +15 -14
  62. data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +43 -0
  63. data/lib/asciidoctor/pdf/formatted_text/text_background_and_border_renderer.rb +46 -37
  64. data/lib/asciidoctor/pdf/formatted_text/transform.rb +371 -352
  65. data/lib/asciidoctor/pdf/index_catalog.rb +99 -95
  66. data/lib/asciidoctor/pdf/measurements.rb +51 -48
  67. data/lib/asciidoctor/pdf/optimizer.rb +34 -31
  68. data/lib/asciidoctor/pdf/pdfmark.rb +34 -33
  69. data/lib/asciidoctor/pdf/roman_numeral.rb +80 -79
  70. data/lib/asciidoctor/pdf/sanitizer.rb +38 -37
  71. data/lib/asciidoctor/pdf/temporary_path.rb +10 -9
  72. data/lib/asciidoctor/pdf/text_transformer.rb +101 -100
  73. data/lib/asciidoctor/pdf/theme_loader.rb +258 -256
  74. data/lib/asciidoctor/pdf/version.rb +5 -4
  75. metadata +55 -6
  76. data/lib/asciidoctor/pdf/ext/rouge/themes/bw.rb +0 -39
  77. data/lib/asciidoctor/pdf/ext/ttfunk.rb +0 -9
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Prawn::Table::Cell::Text
3
4
  # Override draw_content method to drop cursor advancement
4
5
  remove_method :draw_content
5
6
  def draw_content
6
7
  with_font do
7
8
  with_text_color do
8
- (text_box width: spanned_content_width + FPTolerance,
9
- height: spanned_content_height + FPTolerance,
10
- at: [0, @pdf.cursor]).render
9
+ (text_box \
10
+ width: spanned_content_width + FPTolerance,
11
+ height: spanned_content_height + FPTolerance,
12
+ at: [0, @pdf.cursor]).render
11
13
  end
12
14
  end
13
15
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Prawn::Document
3
4
  # NOTE allows prawn-templates 0.0.4 to be used with prawn >= 2.2.0
4
5
  const_set :VALID_OPTIONS, (send :remove_const, :VALID_OPTIONS).dup if VALID_OPTIONS.frozen?
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # the following are organized under the Asciidoctor::Prawn namespace
3
4
  require_relative 'prawn/font/afm'
4
5
  require_relative 'prawn/images'
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  ######################################################################
3
4
  #
4
5
  # This file was copied from Prawn (manual/syntax_highlight.rb) and
@@ -32,85 +33,85 @@ require 'coderay'
32
33
  # CodeRay.scan(string, :ruby).to_prawn
33
34
  #
34
35
  module Asciidoctor
35
- module Prawn
36
- class CodeRayEncoder < ::CodeRay::Encoders::Encoder
37
- register_for :to_prawn
36
+ module Prawn
37
+ class CodeRayEncoder < ::CodeRay::Encoders::Encoder
38
+ register_for :to_prawn
38
39
 
39
- # Manni theme from Pygments
40
- COLORS = {
41
- default: '333333',
42
- annotation: '9999FF',
43
- attribute_name: '4F9FCF',
44
- attribute_value: 'D44950',
45
- class: '00AA88',
46
- class_variable: '003333',
47
- color: 'FF6600',
48
- comment: '999999',
49
- constant: '336600',
50
- directive: '006699',
51
- doctype: '009999',
52
- entity: '999999',
53
- float: 'FF6600',
54
- function: 'CC00FF',
55
- important: '9999FF',
56
- inline_delimiter: 'EF804F',
57
- instance_variable: '003333',
58
- integer: 'FF6600',
59
- key: '006699',
60
- keyword: '006699',
61
- method: 'CC00FF',
62
- namespace: '00CCFF',
63
- predefined_type: '007788',
64
- regexp: '33AAAA',
65
- string: 'CC3300',
66
- symbol: 'FFCC33',
67
- tag: '2F6F9F',
68
- type: '007788',
69
- value: '336600'
70
- }
40
+ # Manni theme from Pygments
41
+ COLORS = {
42
+ default: '333333',
43
+ annotation: '9999FF',
44
+ attribute_name: '4F9FCF',
45
+ attribute_value: 'D44950',
46
+ class: '00AA88',
47
+ class_variable: '003333',
48
+ color: 'FF6600',
49
+ comment: '999999',
50
+ constant: '336600',
51
+ directive: '006699',
52
+ doctype: '009999',
53
+ entity: '999999',
54
+ float: 'FF6600',
55
+ function: 'CC00FF',
56
+ important: '9999FF',
57
+ inline_delimiter: 'EF804F',
58
+ instance_variable: '003333',
59
+ integer: 'FF6600',
60
+ key: '006699',
61
+ keyword: '006699',
62
+ method: 'CC00FF',
63
+ namespace: '00CCFF',
64
+ predefined_type: '007788',
65
+ regexp: '33AAAA',
66
+ string: 'CC3300',
67
+ symbol: 'FFCC33',
68
+ tag: '2F6F9F',
69
+ type: '007788',
70
+ value: '336600',
71
+ }
71
72
 
72
- LF = ?\n
73
- NoBreakSpace = ?\u00a0
74
- InnerIndent = LF + ' '
75
- GuardedIndent = ?\u00a0
76
- GuardedInnerIndent = LF + GuardedIndent
73
+ LF = ?\n
74
+ NoBreakSpace = ?\u00a0
75
+ InnerIndent = LF + ' '
76
+ GuardedIndent = ?\u00a0
77
+ GuardedInnerIndent = LF + GuardedIndent
77
78
 
78
- def setup options
79
- super
80
- @out = []
81
- @open = []
82
- # NOTE tracks whether text token begins at the start of a line
83
- @start_of_line = true
84
- end
79
+ def setup options
80
+ super
81
+ @out = []
82
+ @open = []
83
+ # NOTE tracks whether text token begins at the start of a line
84
+ @start_of_line = true
85
+ end
85
86
 
86
- def text_token text, kind
87
- if text == LF
88
- @out << { text: text }
89
- @start_of_line = true
90
- # NOTE text is nil and kind is :error when CodeRay ends parsing on an error
91
- elsif text
92
- # NOTE add guard character to prevent Prawn from trimming indentation
93
- text[0] = GuardedIndent if @start_of_line && (text.start_with? ' ')
94
- text.gsub! InnerIndent, GuardedInnerIndent if text.include? InnerIndent
87
+ def text_token text, kind
88
+ if text == LF
89
+ @out << { text: text }
90
+ @start_of_line = true
91
+ # NOTE text is nil and kind is :error when CodeRay ends parsing on an error
92
+ elsif text
93
+ # NOTE add guard character to prevent Prawn from trimming indentation
94
+ text[0] = GuardedIndent if @start_of_line && (text.start_with? ' ')
95
+ text.gsub! InnerIndent, GuardedInnerIndent if text.include? InnerIndent
95
96
 
96
- # NOTE this optimization assumes we don't support/use background colors
97
- if text.rstrip.empty?
98
- @out << { text: text }
99
- else
100
- # QUESTION should we default to no color?
101
- @out << { text: text, color: (COLORS[kind] || COLORS[@open[-1]] || COLORS[:default]) }
97
+ # NOTE this optimization assumes we don't support/use background colors
98
+ if text.rstrip.empty?
99
+ @out << { text: text }
100
+ else
101
+ # QUESTION should we default to no color?
102
+ @out << { text: text, color: (COLORS[kind] || COLORS[@open[-1]] || COLORS[:default]) }
103
+ end
104
+ @start_of_line = text.end_with? LF
105
+ end
102
106
  end
103
- @start_of_line = text.end_with? LF
104
- end
105
- end
106
107
 
107
- def begin_group kind
108
- @open << kind
109
- end
108
+ def begin_group kind
109
+ @open << kind
110
+ end
110
111
 
111
- def end_group kind
112
- @open.pop
112
+ def end_group _kind
113
+ @open.pop
114
+ end
115
+ end
113
116
  end
114
117
  end
115
- end
116
- end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  Prawn::Font::AFM.instance_variable_set :@hide_m17n_warning, true
3
4
 
4
5
  require 'prawn/icon'
@@ -6,912 +7,907 @@ require 'prawn/icon'
6
7
  Prawn::Icon::Compatibility.send :prepend, (::Module.new { def warning *args; end })
7
8
 
8
9
  module Asciidoctor
9
- module Prawn
10
- module Extensions
11
- include ::Asciidoctor::PDF::Measurements
12
- include ::Asciidoctor::PDF::Sanitizer
13
- include ::Asciidoctor::PDF::TextTransformer
14
-
15
- FontAwesomeIconSets = %w(fab far fas)
16
- IconSets = %w(fab far fas fi pf).to_set
17
- IconSetPrefixes = IconSets.map {|it| it + '-' }
18
- InitialPageContent = %(q\n)
19
- (FontStyleToSet = {
20
- bold: [:bold].to_set,
21
- italic: [:italic].to_set,
22
- bold_italic: [:bold, :italic].to_set,
23
- }).default = ::Set.new
24
-
25
- # - :height is the height of a line
26
- # - :leading is spacing between adjacent lines
27
- # - :padding_top is half line spacing, plus any line_gap in the font
28
- # - :padding_bottom is half line spacing
29
- # - :final_gap determines whether a gap is added below the last line
30
- LineMetrics = ::Struct.new :height, :leading, :padding_top, :padding_bottom, :final_gap
31
-
32
- # Core
33
-
34
- # Retrieves the catalog reference data for the PDF.
35
- #
36
- def catalog
37
- state.store.root
38
- end
10
+ module Prawn
11
+ module Extensions
12
+ include ::Asciidoctor::PDF::Measurements
13
+ include ::Asciidoctor::PDF::Sanitizer
14
+ include ::Asciidoctor::PDF::TextTransformer
15
+
16
+ FontAwesomeIconSets = %w(fab far fas)
17
+ IconSets = %w(fab far fas fi pf).to_set
18
+ IconSetPrefixes = IconSets.map {|it| it + '-' }
19
+ InitialPageContent = %(q\n)
20
+ (FontStyleToSet = {
21
+ bold: [:bold].to_set,
22
+ italic: [:italic].to_set,
23
+ bold_italic: [:bold, :italic].to_set,
24
+ }).default = ::Set.new
25
+
26
+ # - :height is the height of a line
27
+ # - :leading is spacing between adjacent lines
28
+ # - :padding_top is half line spacing, plus any line_gap in the font
29
+ # - :padding_bottom is half line spacing
30
+ # - :final_gap determines whether a gap is added below the last line
31
+ LineMetrics = ::Struct.new :height, :leading, :padding_top, :padding_bottom, :final_gap
32
+
33
+ # Core
34
+
35
+ # Retrieves the catalog reference data for the PDF.
36
+ #
37
+ def catalog
38
+ state.store.root
39
+ end
39
40
 
40
- # Retrieves the compatiblity version of the PDF.
41
- #
42
- def min_version
43
- state.version
44
- end
41
+ # Retrieves the compatiblity version of the PDF.
42
+ #
43
+ def min_version
44
+ state.version
45
+ end
45
46
 
46
- # Measurements
47
+ # Measurements
47
48
 
48
- # Returns the width of the current page from edge-to-edge
49
- #
50
- def page_width
51
- page.dimensions[2]
52
- end
49
+ # Returns the width of the current page from edge-to-edge
50
+ #
51
+ def page_width
52
+ page.dimensions[2]
53
+ end
53
54
 
54
- # Returns the effective (writable) width of the page
55
- #
56
- # If inside a bounding box, returns width of box.
57
- #
58
- def effective_page_width
59
- reference_bounds.width
60
- end
55
+ # Returns the effective (writable) width of the page
56
+ #
57
+ # If inside a bounding box, returns width of box.
58
+ #
59
+ def effective_page_width
60
+ reference_bounds.width
61
+ end
61
62
 
62
- # Returns the height of the current page from edge-to-edge
63
- #
64
- def page_height
65
- page.dimensions[3]
66
- end
63
+ # Returns the height of the current page from edge-to-edge
64
+ #
65
+ def page_height
66
+ page.dimensions[3]
67
+ end
67
68
 
68
- # Returns the effective (writable) height of the page
69
- #
70
- # If inside a fixed-height bounding box, returns width of box.
71
- #
72
- def effective_page_height
73
- reference_bounds.height
74
- end
69
+ # Returns the effective (writable) height of the page
70
+ #
71
+ # If inside a fixed-height bounding box, returns width of box.
72
+ #
73
+ def effective_page_height
74
+ reference_bounds.height
75
+ end
75
76
 
76
- # Set the margins for the current page.
77
- #
78
- def set_page_margin margin
79
- # FIXME is there a cleaner way to set margins? does it make sense to override create_new_page?
80
- apply_margin_options margin: margin
81
- generate_margin_box
82
- end
77
+ # Set the margins for the current page.
78
+ #
79
+ def set_page_margin margin
80
+ # FIXME: is there a cleaner way to set margins? does it make sense to override create_new_page?
81
+ apply_margin_options margin: margin
82
+ generate_margin_box
83
+ end
83
84
 
84
- # Returns the margins for the current page as a 4 element array (top, right, bottom, left)
85
- #
86
- def page_margin
87
- [page.margins[:top], page.margins[:right], page.margins[:bottom], page.margins[:left]]
88
- end
85
+ # Returns the margins for the current page as a 4 element array (top, right, bottom, left)
86
+ #
87
+ def page_margin
88
+ [page.margins[:top], page.margins[:right], page.margins[:bottom], page.margins[:left]]
89
+ end
89
90
 
90
- # Returns the width of the left margin for the current page
91
- #
92
- def page_margin_left
93
- page.margins[:left]
94
- end
95
- # deprecated
96
- alias left_margin page_margin_left
91
+ # Returns the width of the left margin for the current page
92
+ #
93
+ def page_margin_left
94
+ page.margins[:left]
95
+ end
96
+ # deprecated
97
+ alias left_margin page_margin_left
97
98
 
98
- # Returns the width of the right margin for the current page
99
- #
100
- def page_margin_right
101
- page.margins[:right]
102
- end
103
- # deprecated
104
- alias right_margin page_margin_right
99
+ # Returns the width of the right margin for the current page
100
+ #
101
+ def page_margin_right
102
+ page.margins[:right]
103
+ end
104
+ # deprecated
105
+ alias right_margin page_margin_right
105
106
 
106
- # Returns the width of the top margin for the current page
107
- #
108
- def page_margin_top
109
- page.margins[:top]
110
- end
107
+ # Returns the width of the top margin for the current page
108
+ #
109
+ def page_margin_top
110
+ page.margins[:top]
111
+ end
111
112
 
112
- # Returns the width of the bottom margin for the current page
113
- #
114
- def page_margin_bottom
115
- page.margins[:bottom]
116
- end
113
+ # Returns the width of the bottom margin for the current page
114
+ #
115
+ def page_margin_bottom
116
+ page.margins[:bottom]
117
+ end
117
118
 
118
- # Returns the total left margin (to the page edge) for the current bounds.
119
- #
120
- def bounds_margin_left
121
- bounds.absolute_left
122
- end
119
+ # Returns the total left margin (to the page edge) for the current bounds.
120
+ #
121
+ def bounds_margin_left
122
+ bounds.absolute_left
123
+ end
123
124
 
124
- # Returns the total right margin (to the page edge) for the current bounds.
125
- #
126
- def bounds_margin_right
127
- page.dimensions[2] - bounds.absolute_right
128
- end
125
+ # Returns the total right margin (to the page edge) for the current bounds.
126
+ #
127
+ def bounds_margin_right
128
+ page.dimensions[2] - bounds.absolute_right
129
+ end
129
130
 
130
- # Returns the side the current page is facing, :recto or :verso.
131
- #
132
- def page_side pgnum = nil, invert = nil
133
- if invert
134
- (recto_page? pgnum) ? :verso : :recto
135
- else
136
- (recto_page? pgnum) ? :recto : :verso
137
- end
138
- end
131
+ # Returns the side the current page is facing, :recto or :verso.
132
+ #
133
+ def page_side pgnum = nil, invert = nil
134
+ if invert
135
+ (recto_page? pgnum) ? :verso : :recto
136
+ else
137
+ (recto_page? pgnum) ? :recto : :verso
138
+ end
139
+ end
139
140
 
140
- # Returns whether the page is a recto page.
141
- #
142
- def recto_page? pgnum = nil
143
- (pgnum || page_number).odd?
144
- end
141
+ # Returns whether the page is a recto page.
142
+ #
143
+ def recto_page? pgnum = nil
144
+ (pgnum || page_number).odd?
145
+ end
145
146
 
146
- # Returns whether the page is a verso page.
147
- #
148
- def verso_page? pgnum = nil
149
- (pgnum || page_number).even?
150
- end
147
+ # Returns whether the page is a verso page.
148
+ #
149
+ def verso_page? pgnum = nil
150
+ (pgnum || page_number).even?
151
+ end
151
152
 
152
- # Returns whether the cursor is at the top of the page (i.e., margin box).
153
- #
154
- def at_page_top?
155
- @y == @margin_box.absolute_top
156
- end
153
+ # Returns whether the cursor is at the top of the page (i.e., margin box).
154
+ #
155
+ def at_page_top?
156
+ @y == @margin_box.absolute_top
157
+ end
157
158
 
158
- # Returns whether the current page is the last page in the document.
159
- #
160
- def last_page?
161
- page_number == page_count
162
- end
159
+ # Returns whether the current page is the last page in the document.
160
+ #
161
+ def last_page?
162
+ page_number == page_count
163
+ end
163
164
 
164
- # Destinations
165
+ # Destinations
165
166
 
166
- # Generates a destination object that resolves to the top of the page
167
- # specified by the page_num parameter or the current page if no page number
168
- # is provided. The destination preserves the user's zoom level unlike
169
- # the destinations generated by the outline builder.
170
- #
171
- def dest_top page_num = nil
172
- dest_xyz 0, page_height, nil, (page_num ? state.pages[page_num - 1] : page)
173
- end
167
+ # Generates a destination object that resolves to the top of the page
168
+ # specified by the page_num parameter or the current page if no page number
169
+ # is provided. The destination preserves the user's zoom level unlike
170
+ # the destinations generated by the outline builder.
171
+ #
172
+ def dest_top page_num = nil
173
+ dest_xyz 0, page_height, nil, (page_num ? state.pages[page_num - 1] : page)
174
+ end
174
175
 
175
- # Fonts
176
-
177
- # Registers a new custom font described in the data parameter
178
- # after converting the font name to a String.
179
- #
180
- # Example:
181
- #
182
- # register_font Roboto: {
183
- # normal: 'fonts/roboto-normal.ttf',
184
- # italic: 'fonts/roboto-italic.ttf',
185
- # bold: 'fonts/roboto-bold.ttf',
186
- # bold_italic: 'fonts/roboto-bold_italic.ttf'
187
- # }
188
- #
189
- def register_font data
190
- font_families.update data.reduce({}) {|accum, (key, val)| accum[key.to_s] = val; accum }
191
- end
176
+ # Fonts
177
+
178
+ # Registers a new custom font described in the data parameter
179
+ # after converting the font name to a String.
180
+ #
181
+ # Example:
182
+ #
183
+ # register_font Roboto: {
184
+ # normal: 'fonts/roboto-normal.ttf',
185
+ # italic: 'fonts/roboto-italic.ttf',
186
+ # bold: 'fonts/roboto-bold.ttf',
187
+ # bold_italic: 'fonts/roboto-bold_italic.ttf'
188
+ # }
189
+ #
190
+ def register_font data
191
+ font_families.update data.each_with_object({}) {|(key, val), accum| accum[key.to_s] = val }
192
+ end
192
193
 
193
- # Enhances the built-in font method to allow the font
194
- # size to be specified as the second option and to
195
- # lazily load font-based icons.
196
- #
197
- def font name = nil, options = {}
198
- if name
199
- options = { size: options } if ::Numeric === options
200
- if IconSets.include? name
201
- ::Prawn::Icon::FontData.load self, name
202
- options = options.reject {|k| k == :style } if options.key? :style
194
+ # Enhances the built-in font method to allow the font
195
+ # size to be specified as the second option and to
196
+ # lazily load font-based icons.
197
+ #
198
+ def font name = nil, options = {}
199
+ if name
200
+ options = { size: options } if ::Numeric === options
201
+ if IconSets.include? name
202
+ ::Prawn::Icon::FontData.load self, name
203
+ options = options.reject {|k| k == :style } if options.key? :style
204
+ end
205
+ end
206
+ super name, options
203
207
  end
204
- end
205
- super name, options
206
- end
207
208
 
208
- # Retrieves the current font name (i.e., family).
209
- #
210
- def font_family
211
- font.options[:family]
212
- end
209
+ # Retrieves the current font name (i.e., family).
210
+ #
211
+ def font_family
212
+ font.options[:family]
213
+ end
213
214
 
214
- alias font_name font_family
215
+ alias font_name font_family
215
216
 
216
- # Retrieves the current font info (family, style, size) as a Hash
217
- #
218
- def font_info
219
- { family: font.options[:family], style: (font.options[:style] || :normal), size: @font_size }
220
- end
217
+ # Retrieves the current font info (family, style, size) as a Hash
218
+ #
219
+ def font_info
220
+ { family: font.options[:family], style: (font.options[:style] || :normal), size: @font_size }
221
+ end
221
222
 
222
- # Sets the font style for the scope of the block to which this method
223
- # yields. If the style is nil and no block is given, return the current
224
- # font style.
225
- #
226
- def font_style style = nil
227
- if block_given?
228
- font font.options[:family], style: style do
229
- yield
223
+ # Sets the font style for the scope of the block to which this method
224
+ # yields. If the style is nil and no block is given, return the current
225
+ # font style.
226
+ #
227
+ def font_style style = nil
228
+ if block_given?
229
+ font font.options[:family], style: style do
230
+ yield
231
+ end
232
+ elsif style
233
+ font font.options[:family], style: style
234
+ else
235
+ font.options[:style] || :normal
236
+ end
230
237
  end
231
- elsif style
232
- font font.options[:family], style: style
233
- else
234
- font.options[:style] || :normal
235
- end
236
- end
237
238
 
238
- # Applies points as a scale factor of the current font if the value provided
239
- # is less than or equal to 1 or it's a string (e.g., 1.1em), then delegates to the super
240
- # implementation to carry out the built-in functionality.
241
- #
242
- #--
243
- # QUESTION should we round the result?
244
- def font_size points = nil
245
- return @font_size unless points
246
- if points == 1
247
- super @font_size
248
- elsif String === points
249
- if points.end_with? 'rem'
250
- super(@root_font_size * points.to_f)
251
- elsif points.end_with? 'em'
252
- super(@font_size * points.to_f)
253
- elsif points.end_with? '%'
254
- super(@font_size * (points.to_f / 100))
255
- else
256
- super points.to_f
257
- end
258
- # FIXME HACK assume em value
259
- elsif points < 1
260
- super(@font_size * points)
261
- else
262
- super points
263
- end
264
- end
239
+ # Applies points as a scale factor of the current font if the value provided
240
+ # is less than or equal to 1 or it's a string (e.g., 1.1em), then delegates to the super
241
+ # implementation to carry out the built-in functionality.
242
+ #
243
+ #--
244
+ # QUESTION should we round the result?
245
+ def font_size points = nil
246
+ return @font_size unless points
247
+ if points == 1
248
+ super @font_size
249
+ elsif String === points
250
+ if points.end_with? 'rem'
251
+ super @root_font_size * points.to_f
252
+ elsif points.end_with? 'em'
253
+ super @font_size * points.to_f
254
+ elsif points.end_with? '%'
255
+ super @font_size * (points.to_f / 100)
256
+ else
257
+ super points.to_f
258
+ end
259
+ # FIXME: HACK assume em value
260
+ elsif points < 1
261
+ super @font_size * points
262
+ else
263
+ super points
264
+ end
265
+ end
265
266
 
266
- def resolve_font_style styles
267
- if styles.include? :bold
268
- (styles.include? :italic) ? :bold_italic : :bold
269
- elsif styles.include? :italic
270
- :italic
271
- else
272
- :normal
273
- end
274
- end
267
+ def resolve_font_style styles
268
+ if styles.include? :bold
269
+ (styles.include? :italic) ? :bold_italic : :bold
270
+ elsif styles.include? :italic
271
+ :italic
272
+ else
273
+ :normal
274
+ end
275
+ end
275
276
 
276
- # Retreives the collection of font styles from the given font style key,
277
- # which defaults to the current font style.
278
- #
279
- def font_styles style = font_style
280
- FontStyleToSet[style].dup
281
- end
277
+ # Retreives the collection of font styles from the given font style key,
278
+ # which defaults to the current font style.
279
+ #
280
+ def font_styles style = font_style
281
+ FontStyleToSet[style].dup
282
+ end
282
283
 
283
- # Apply the font settings (family, size, styles and character spacing) from
284
- # the fragment to the document, then yield to the block.
285
- #
286
- # The original font settings are restored before this method returns.
287
- #
288
- def fragment_font fragment
289
- f_info = font_info
290
- f_family = fragment[:font] || f_info[:family]
291
- f_size = fragment[:size] || f_info[:size]
292
- if (f_styles = fragment[:styles])
293
- f_style = resolve_font_style f_styles
294
- else
295
- f_style = :normal
296
- end
284
+ # Apply the font settings (family, size, styles and character spacing) from
285
+ # the fragment to the document, then yield to the block.
286
+ #
287
+ # The original font settings are restored before this method returns.
288
+ #
289
+ def fragment_font fragment
290
+ f_info = font_info
291
+ f_family = fragment[:font] || f_info[:family]
292
+ f_size = fragment[:size] || f_info[:size]
293
+ if (f_styles = fragment[:styles])
294
+ f_style = resolve_font_style f_styles
295
+ else
296
+ f_style = :normal
297
+ end
297
298
 
298
- if (c_spacing = fragment[:character_spacing])
299
- character_spacing c_spacing do
300
- font f_family, size: f_size, style: f_style do
301
- yield
299
+ if (c_spacing = fragment[:character_spacing])
300
+ character_spacing c_spacing do
301
+ font f_family, size: f_size, style: f_style do
302
+ yield
303
+ end
304
+ end
305
+ else
306
+ font f_family, size: f_size, style: f_style do
307
+ yield
308
+ end
302
309
  end
303
310
  end
304
- else
305
- font f_family, size: f_size, style: f_style do
306
- yield
307
- end
308
- end
309
- end
310
311
 
311
- def icon_font_data family
312
- ::Prawn::Icon::FontData.load self, family
313
- end
312
+ def icon_font_data family
313
+ ::Prawn::Icon::FontData.load self, family
314
+ end
314
315
 
315
- def resolve_legacy_icon_name name
316
- ::Prawn::Icon::Compatibility::SHIMS[%(fa-#{name})]
317
- end
316
+ def resolve_legacy_icon_name name
317
+ ::Prawn::Icon::Compatibility::SHIMS[%(fa-#{name})]
318
+ end
318
319
 
319
- def calc_line_metrics line_height = 1, font = self.font, font_size = self.font_size
320
- line_height_length = line_height * font_size
321
- leading = line_height_length - font_size
322
- half_leading = leading / 2
323
- padding_top = half_leading + font.line_gap
324
- padding_bottom = half_leading
325
- LineMetrics.new line_height_length, leading, padding_top, padding_bottom, false
326
- end
320
+ def calc_line_metrics line_height = 1, font = self.font, font_size = self.font_size
321
+ line_height_length = line_height * font_size
322
+ leading = line_height_length - font_size
323
+ half_leading = leading / 2
324
+ padding_top = half_leading + font.line_gap
325
+ padding_bottom = half_leading
326
+ LineMetrics.new line_height_length, leading, padding_top, padding_bottom, false
327
+ end
327
328
 
328
329
  =begin
329
- # these line metrics attempted to figure out a correction based on the reported height and the font_size
330
- # however, it only works for some fonts, and breaks down for fonts like Noto Serif
331
- def calc_line_metrics line_height = 1, font = self.font, font_size = self.font_size
332
- line_height_length = font_size * line_height
333
- line_gap = line_height_length - font_size
334
- correction = font.height - font_size
335
- leading = line_gap - correction
336
- shift = (font.line_gap + correction + line_gap) / 2
337
- final_gap = font.line_gap != 0
338
- LineMetrics.new line_height_length, leading, shift, shift, final_gap
339
- end
330
+ # these line metrics attempted to figure out a correction based on the reported height and the font_size
331
+ # however, it only works for some fonts, and breaks down for fonts like Noto Serif
332
+ def calc_line_metrics line_height = 1, font = self.font, font_size = self.font_size
333
+ line_height_length = font_size * line_height
334
+ line_gap = line_height_length - font_size
335
+ correction = font.height - font_size
336
+ leading = line_gap - correction
337
+ shift = (font.line_gap + correction + line_gap) / 2
338
+ final_gap = font.line_gap != 0
339
+ LineMetrics.new line_height_length, leading, shift, shift, final_gap
340
+ end
340
341
  =end
341
342
 
342
- # Parse the text into an array of fragments using the text formatter.
343
- def parse_text string, options = {}
344
- return [] if string.nil?
343
+ # Parse the text into an array of fragments using the text formatter.
344
+ def parse_text string, options = {}
345
+ return [] if string.nil?
345
346
 
346
- options = options.dup
347
- if (format_option = options.delete :inline_format)
348
- format_option = [] unless ::Array === format_option
349
- fragments = self.text_formatter.format string, *format_option
350
- else
351
- fragments = [{text: string}]
352
- end
347
+ options = options.dup
348
+ if (format_option = options.delete :inline_format)
349
+ format_option = [] unless ::Array === format_option
350
+ fragments = text_formatter.format string, *format_option
351
+ else
352
+ fragments = [text: string]
353
+ end
353
354
 
354
- if (color = options.delete :color)
355
- fragments.map do |fragment|
356
- fragment[:color] ? fragment : fragment.merge(color: color)
355
+ if (color = options.delete :color)
356
+ fragments.map do |fragment|
357
+ fragment[:color] ? fragment : fragment.merge(color: color)
358
+ end
359
+ else
360
+ fragments
361
+ end
357
362
  end
358
- else
359
- fragments
360
- end
361
- end
362
363
 
363
- # NOTE override built-in draw_indented_formatted_line to insert leading before second line
364
- def draw_indented_formatted_line string, opts
365
- result = super
366
- unless @no_text_printed || @all_text_printed
367
- # as of Prawn 1.2.1, we have to handle the line gap after the first line manually
368
- move_down opts[:leading]
369
- end
370
- result
371
- end
372
-
373
- # Performs the same work as Prawn::Text.text except that the first_line_opts are applied to the first line of text
374
- # renderered. It's necessary to use low-level APIs in this method so we only style the first line and not the
375
- # remaining lines (which is the default behavior in Prawn).
376
- def text_with_formatted_first_line string, first_line_opts, opts
377
- color = opts.delete :color
378
- fragments = parse_text string, opts
379
- # NOTE the low-level APIs we're using don't recognize the :styles option, so we must resolve
380
- if (styles = opts.delete :styles)
381
- opts[:style] = resolve_font_style styles
382
- end
383
- if (first_line_styles = first_line_opts.delete :styles)
384
- first_line_opts[:style] = resolve_font_style first_line_styles
385
- end
386
- first_line_color = (first_line_opts.delete :color) || color
387
- opts = opts.merge document: self
388
- # QUESTION should we merge more carefully here? (hand-select keys?)
389
- first_line_opts = opts.merge(first_line_opts).merge single_line: true
390
- box = ::Prawn::Text::Formatted::Box.new fragments, first_line_opts
391
- # NOTE get remaining_fragments before we add color to fragments on first line
392
- if (text_indent = opts.delete :indent_paragraphs)
393
- remaining_fragments = indent text_indent do
394
- box.render dry_run: true
395
- end
396
- else
397
- remaining_fragments = box.render dry_run: true
398
- end
399
- # NOTE color must be applied per-fragment
400
- if first_line_color
401
- fragments.each {|fragment| fragment[:color] ||= first_line_color}
402
- end
403
- if text_indent
404
- indent text_indent do
405
- fill_formatted_text_box fragments, first_line_opts
364
+ # NOTE override built-in draw_indented_formatted_line to insert leading before second line
365
+ def draw_indented_formatted_line string, opts
366
+ result = super
367
+ unless @no_text_printed || @all_text_printed
368
+ # as of Prawn 1.2.1, we have to handle the line gap after the first line manually
369
+ move_down opts[:leading]
370
+ end
371
+ result
406
372
  end
407
- else
408
- fill_formatted_text_box fragments, first_line_opts
409
- end
410
- unless remaining_fragments.empty?
411
- # NOTE color must be applied per-fragment
412
- remaining_fragments.each {|fragment| fragment[:color] ||= color } if color
413
- # as of Prawn 1.2.1, we have to handle the line gap after the first line manually
414
- move_down opts[:leading]
415
- remaining_fragments = fill_formatted_text_box remaining_fragments, opts
416
- draw_remaining_formatted_text_on_new_pages remaining_fragments, opts
417
- end
418
- end
419
373
 
420
- # Apply the text transform to the specified text.
421
- #
422
- # Supported transform values are "uppercase", "lowercase", or "none" (passed
423
- # as either a String or a Symbol). When the uppercase transform is applied to
424
- # the text, it correctly uppercases visible text while leaving markup and
425
- # named character entities unchanged. The none transform returns the text
426
- # unmodified.
427
- #
428
- def transform_text text, transform
429
- case transform
430
- when :uppercase, 'uppercase'
431
- uppercase_pcdata text
432
- when :lowercase, 'lowercase'
433
- lowercase_pcdata text
434
- when :capitalize, 'capitalize'
435
- capitalize_words_pcdata text
436
- else
437
- text
438
- end
439
- end
440
-
441
- def hyphenate_text text, hyphenator
442
- hyphenate_words_pcdata text, hyphenator
443
- end
374
+ # Performs the same work as Prawn::Text.text except that the first_line_opts are applied to the first line of text
375
+ # renderered. It's necessary to use low-level APIs in this method so we only style the first line and not the
376
+ # remaining lines (which is the default behavior in Prawn).
377
+ def text_with_formatted_first_line string, first_line_opts, opts
378
+ color = opts.delete :color
379
+ fragments = parse_text string, opts
380
+ # NOTE the low-level APIs we're using don't recognize the :styles option, so we must resolve
381
+ if (styles = opts.delete :styles)
382
+ opts[:style] = resolve_font_style styles
383
+ end
384
+ if (first_line_styles = first_line_opts.delete :styles)
385
+ first_line_opts[:style] = resolve_font_style first_line_styles
386
+ end
387
+ first_line_color = (first_line_opts.delete :color) || color
388
+ opts = opts.merge document: self
389
+ # QUESTION should we merge more carefully here? (hand-select keys?)
390
+ first_line_opts = opts.merge(first_line_opts).merge single_line: true
391
+ box = ::Prawn::Text::Formatted::Box.new fragments, first_line_opts
392
+ # NOTE get remaining_fragments before we add color to fragments on first line
393
+ if (text_indent = opts.delete :indent_paragraphs)
394
+ remaining_fragments = indent text_indent do
395
+ box.render dry_run: true
396
+ end
397
+ else
398
+ remaining_fragments = box.render dry_run: true
399
+ end
400
+ # NOTE color must be applied per-fragment
401
+ fragments.each {|fragment| fragment[:color] ||= first_line_color } if first_line_color
402
+ if text_indent
403
+ indent text_indent do
404
+ fill_formatted_text_box fragments, first_line_opts
405
+ end
406
+ else
407
+ fill_formatted_text_box fragments, first_line_opts
408
+ end
409
+ unless remaining_fragments.empty?
410
+ # NOTE color must be applied per-fragment
411
+ remaining_fragments.each {|fragment| fragment[:color] ||= color } if color
412
+ # as of Prawn 1.2.1, we have to handle the line gap after the first line manually
413
+ move_down opts[:leading]
414
+ remaining_fragments = fill_formatted_text_box remaining_fragments, opts
415
+ draw_remaining_formatted_text_on_new_pages remaining_fragments, opts
416
+ end
417
+ end
444
418
 
445
- # Cursor
419
+ # Apply the text transform to the specified text.
420
+ #
421
+ # Supported transform values are "uppercase", "lowercase", or "none" (passed
422
+ # as either a String or a Symbol). When the uppercase transform is applied to
423
+ # the text, it correctly uppercases visible text while leaving markup and
424
+ # named character entities unchanged. The none transform returns the text
425
+ # unmodified.
426
+ #
427
+ def transform_text text, transform
428
+ case transform
429
+ when :uppercase, 'uppercase'
430
+ uppercase_pcdata text
431
+ when :lowercase, 'lowercase'
432
+ lowercase_pcdata text
433
+ when :capitalize, 'capitalize'
434
+ capitalize_words_pcdata text
435
+ else
436
+ text
437
+ end
438
+ end
446
439
 
447
- # Short-circuits the call to the built-in move_up operation
448
- # when n is 0.
449
- #
450
- def move_up n
451
- super unless n == 0
452
- end
440
+ def hyphenate_text text, hyphenator
441
+ hyphenate_words_pcdata text, hyphenator
442
+ end
453
443
 
454
- # Override built-in move_text_position method to prevent Prawn from advancing
455
- # to next page if image doesn't fit before rendering image.
456
- #--
457
- # NOTE could use :at option when calling image/embed_image instead
458
- def move_text_position h
459
- end
444
+ # Cursor
460
445
 
461
- # Short-circuits the call to the built-in move_down operation
462
- # when n is 0.
463
- #
464
- def move_down n
465
- super unless n == 0
466
- end
446
+ # Short-circuits the call to the built-in move_up operation
447
+ # when n is 0.
448
+ #
449
+ def move_up n
450
+ super unless n == 0
451
+ end
467
452
 
468
- # Bounds
469
-
470
- # Overrides the built-in pad operation to allow for asymmetric paddings.
471
- #
472
- # Example:
473
- #
474
- # pad 20, 10 do
475
- # text 'A paragraph with twice as much top padding as bottom padding.'
476
- # end
477
- #
478
- def pad top, bottom = nil
479
- move_down top
480
- yield
481
- move_down(bottom || top)
482
- end
453
+ # Override built-in move_text_position method to prevent Prawn from advancing
454
+ # to next page if image doesn't fit before rendering image.
455
+ #--
456
+ # NOTE could use :at option when calling image/embed_image instead
457
+ def move_text_position h; end
458
+
459
+ # Short-circuits the call to the built-in move_down operation
460
+ # when n is 0.
461
+ #
462
+ def move_down n
463
+ super unless n == 0
464
+ end
483
465
 
484
- # Combines the built-in pad and indent operations into a single method.
485
- #
486
- # Padding may be specified as an array of four values, or as a single value.
487
- # The single value is used as the padding around all four sides of the box.
488
- #
489
- # If padding is nil, this method simply yields to the block and returns.
490
- #
491
- # Example:
492
- #
493
- # pad_box 20 do
494
- # text 'A paragraph inside a blox with even padding on all sides.'
495
- # end
496
- #
497
- # pad_box [10, 10, 10, 20] do
498
- # text 'An indented paragraph inside a box with equal padding on all sides.'
499
- # end
500
- #
501
- def pad_box padding
502
- if padding
503
- # TODO implement shorthand combinations like in CSS
504
- p_top, p_right, p_bottom, p_left = ::Array === padding ? padding : (::Array.new 4, padding)
505
- begin
506
- # logic is intentionally inlined
507
- move_down p_top
508
- bounds.add_left_padding p_left
509
- bounds.add_right_padding p_right
466
+ # Bounds
467
+
468
+ # Overrides the built-in pad operation to allow for asymmetric paddings.
469
+ #
470
+ # Example:
471
+ #
472
+ # pad 20, 10 do
473
+ # text 'A paragraph with twice as much top padding as bottom padding.'
474
+ # end
475
+ #
476
+ def pad top, bottom = nil
477
+ move_down top
510
478
  yield
511
- # NOTE support negative bottom padding for use with quote block
512
- if p_bottom < 0
513
- # QUESTION should we return to previous page if top of page is reached?
514
- p_bottom < cursor - reference_bounds.top ? (move_cursor_to reference_bounds.top) : (move_down p_bottom)
479
+ move_down(bottom || top)
480
+ end
481
+
482
+ # Combines the built-in pad and indent operations into a single method.
483
+ #
484
+ # Padding may be specified as an array of four values, or as a single value.
485
+ # The single value is used as the padding around all four sides of the box.
486
+ #
487
+ # If padding is nil, this method simply yields to the block and returns.
488
+ #
489
+ # Example:
490
+ #
491
+ # pad_box 20 do
492
+ # text 'A paragraph inside a blox with even padding on all sides.'
493
+ # end
494
+ #
495
+ # pad_box [10, 10, 10, 20] do
496
+ # text 'An indented paragraph inside a box with equal padding on all sides.'
497
+ # end
498
+ #
499
+ def pad_box padding
500
+ if padding
501
+ # TODO: implement shorthand combinations like in CSS
502
+ p_top, p_right, p_bottom, p_left = ::Array === padding ? padding : (::Array.new 4, padding)
503
+ begin
504
+ # logic is intentionally inlined
505
+ move_down p_top
506
+ bounds.add_left_padding p_left
507
+ bounds.add_right_padding p_right
508
+ yield
509
+ # NOTE support negative bottom padding for use with quote block
510
+ if p_bottom < 0
511
+ # QUESTION should we return to previous page if top of page is reached?
512
+ p_bottom < cursor - reference_bounds.top ? (move_cursor_to reference_bounds.top) : (move_down p_bottom)
513
+ else
514
+ p_bottom < cursor ? (move_down p_bottom) : reference_bounds.move_past_bottom
515
+ end
516
+ ensure
517
+ bounds.subtract_left_padding p_left
518
+ bounds.subtract_right_padding p_right
519
+ end
515
520
  else
516
- p_bottom < cursor ? (move_down p_bottom) : reference_bounds.move_past_bottom
521
+ yield
517
522
  end
518
- ensure
519
- bounds.subtract_left_padding p_left
520
- bounds.subtract_right_padding p_right
521
- end
522
- else
523
- yield
524
- end
525
523
 
526
- # alternate, delegated logic
527
- #pad padding[0], padding[2] do
528
- # indent padding[1], padding[3] do
529
- # yield
530
- # end
531
- #end
532
- end
524
+ # alternate, delegated logic
525
+ #pad padding[0], padding[2] do
526
+ # indent padding[1], padding[3] do
527
+ # yield
528
+ # end
529
+ #end
530
+ end
533
531
 
534
- def inflate_indent value
535
- (::Array === value ? (value.slice 0, 2) : (::Array.new 2, value)).map(&:to_f)
536
- end
532
+ def inflate_indent value
533
+ (::Array === value ? (value.slice 0, 2) : (::Array.new 2, value)).map(&:to_f)
534
+ end
537
535
 
538
- # TODO memoize the result
539
- def inflate_padding padding
540
- padding = [*(padding || 0)].slice 0, 4
541
- case padding.size
542
- when 1
543
- [padding[0], padding[0], padding[0], padding[0]]
544
- when 2
545
- [padding[0], padding[1], padding[0], padding[1]]
546
- when 3
547
- [padding[0], padding[1], padding[2], padding[1]]
548
- else
549
- padding
550
- end
551
- end
536
+ # TODO: memoize the result
537
+ def inflate_padding padding
538
+ padding = [*(padding || 0)].slice 0, 4
539
+ case padding.size
540
+ when 1
541
+ [padding[0], padding[0], padding[0], padding[0]]
542
+ when 2
543
+ [padding[0], padding[1], padding[0], padding[1]]
544
+ when 3
545
+ [padding[0], padding[1], padding[2], padding[1]]
546
+ else
547
+ padding
548
+ end
549
+ end
552
550
 
553
- # Stretch the current bounds to the left and right edges of the current page
554
- # while yielding the specified block if the verdict argument is true.
555
- # Otherwise, simply yield the specified block.
556
- #
557
- def span_page_width_if verdict
558
- if verdict
559
- indent(-bounds_margin_left, -bounds_margin_right) do
560
- yield
551
+ # Stretch the current bounds to the left and right edges of the current page
552
+ # while yielding the specified block if the verdict argument is true.
553
+ # Otherwise, simply yield the specified block.
554
+ #
555
+ def span_page_width_if verdict
556
+ if verdict
557
+ indent(-bounds_margin_left, -bounds_margin_right) do
558
+ yield
559
+ end
560
+ else
561
+ yield
562
+ end
561
563
  end
562
- else
563
- yield
564
- end
565
- end
566
564
 
567
- # A flowing version of the bounding_box. If the content runs to another page, the cursor starts
568
- # at the top of the page instead of the original cursor position. Similar to span, except
569
- # you can specify an absolute left position and pass additional options through to bounding_box.
570
- #
571
- def flow_bounding_box left = 0, opts = {}
572
- original_y = self.y
573
- # QUESTION should preserving original_x be an option?
574
- original_x = bounds.absolute_left - margin_box.absolute_left
575
- canvas do
576
- bounding_box [margin_box.absolute_left + original_x + left, margin_box.absolute_top], opts do
577
- self.y = original_y
578
- yield
565
+ # A flowing version of the bounding_box. If the content runs to another page, the cursor starts
566
+ # at the top of the page instead of the original cursor position. Similar to span, except
567
+ # you can specify an absolute left position and pass additional options through to bounding_box.
568
+ #
569
+ def flow_bounding_box left = 0, opts = {}
570
+ original_y = y
571
+ # QUESTION should preserving original_x be an option?
572
+ original_x = bounds.absolute_left - margin_box.absolute_left
573
+ canvas do
574
+ bounding_box [margin_box.absolute_left + original_x + left, margin_box.absolute_top], opts do
575
+ self.y = original_y
576
+ yield
577
+ end
578
+ end
579
579
  end
580
- end
581
- end
582
580
 
583
- # Graphics
584
-
585
- # Fills the current bounding box with the specified fill color. Before
586
- # returning from this method, the original fill color on the document is
587
- # restored.
588
- def fill_bounds f_color = fill_color
589
- if f_color && f_color != 'transparent'
590
- prev_fill_color = fill_color
591
- fill_color f_color
592
- fill_rectangle bounds.top_left, bounds.width, bounds.height
593
- fill_color prev_fill_color
594
- end
595
- end
581
+ # Graphics
582
+
583
+ # Fills the current bounding box with the specified fill color. Before
584
+ # returning from this method, the original fill color on the document is
585
+ # restored.
586
+ def fill_bounds f_color = fill_color
587
+ if f_color && f_color != 'transparent'
588
+ prev_fill_color = fill_color
589
+ fill_color f_color
590
+ fill_rectangle bounds.top_left, bounds.width, bounds.height
591
+ fill_color prev_fill_color
592
+ end
593
+ end
596
594
 
597
- # Fills the absolute bounding box with the specified fill color. Before
598
- # returning from this method, the original fill color on the document is
599
- # restored.
600
- def fill_absolute_bounds f_color = fill_color
601
- canvas { fill_bounds f_color }
602
- end
595
+ # Fills the absolute bounding box with the specified fill color. Before
596
+ # returning from this method, the original fill color on the document is
597
+ # restored.
598
+ def fill_absolute_bounds f_color = fill_color
599
+ canvas { fill_bounds f_color }
600
+ end
603
601
 
604
- # Fills the current bounds using the specified fill color and strokes the
605
- # bounds using the specified stroke color. Sets the line with if specified
606
- # in the options. Before returning from this method, the original fill
607
- # color, stroke color and line width on the document are restored.
608
- #
609
- def fill_and_stroke_bounds f_color = fill_color, s_color = stroke_color, options = {}
610
- no_fill = !f_color || f_color == 'transparent'
611
- no_stroke = !s_color || s_color == 'transparent' || options[:line_width] == 0
612
- return if no_fill && no_stroke
613
- save_graphics_state do
614
- radius = options[:radius] || 0
615
-
616
- # fill
617
- unless no_fill
618
- fill_color f_color
619
- fill_rounded_rectangle bounds.top_left, bounds.width, bounds.height, radius
620
- end
621
-
622
- # stroke
623
- unless no_stroke
624
- stroke_color s_color
625
- line_width(options[:line_width] || 0.5)
626
- # FIXME think about best way to indicate dashed borders
627
- #if options.has_key? :dash_width
628
- # dash options[:dash_width], space: options[:dash_space] || 1
629
- #end
630
- stroke_rounded_rectangle bounds.top_left, bounds.width, bounds.height, radius
631
- #undash if options.has_key? :dash_width
602
+ # Fills the current bounds using the specified fill color and strokes the
603
+ # bounds using the specified stroke color. Sets the line with if specified
604
+ # in the options. Before returning from this method, the original fill
605
+ # color, stroke color and line width on the document are restored.
606
+ #
607
+ def fill_and_stroke_bounds f_color = fill_color, s_color = stroke_color, options = {}
608
+ no_fill = !f_color || f_color == 'transparent'
609
+ no_stroke = !s_color || s_color == 'transparent' || options[:line_width] == 0
610
+ return if no_fill && no_stroke
611
+ save_graphics_state do
612
+ radius = options[:radius] || 0
613
+
614
+ # fill
615
+ unless no_fill
616
+ fill_color f_color
617
+ fill_rounded_rectangle bounds.top_left, bounds.width, bounds.height, radius
618
+ end
619
+
620
+ # stroke
621
+ unless no_stroke
622
+ stroke_color s_color
623
+ line_width(options[:line_width] || 0.5)
624
+ # FIXME: think about best way to indicate dashed borders
625
+ #if options.has_key? :dash_width
626
+ # dash options[:dash_width], space: options[:dash_space] || 1
627
+ #end
628
+ stroke_rounded_rectangle bounds.top_left, bounds.width, bounds.height, radius
629
+ #undash if options.has_key? :dash_width
630
+ end
631
+ end
632
632
  end
633
- end
634
- end
635
633
 
636
- # Fills and, optionally, strokes the current bounds using the fill and
637
- # stroke color specified, then yields to the block. The only_if option can
638
- # be used to conditionally disable this behavior.
639
- #
640
- def shade_box color, line_color = nil, options = {}
641
- if (!options.has_key? :only_if) || options[:only_if]
642
- # FIXME could use save_graphics_state here
643
- previous_fill_color = current_fill_color
644
- fill_color color
645
- fill_rectangle [bounds.left, bounds.top], bounds.right, bounds.top - bounds.bottom
646
- fill_color previous_fill_color
647
- if line_color
648
- line_width 0.5
649
- previous_stroke_color = current_stroke_color
650
- stroke_color line_color
651
- stroke_bounds
652
- stroke_color previous_stroke_color
634
+ # Fills and, optionally, strokes the current bounds using the fill and
635
+ # stroke color specified, then yields to the block. The only_if option can
636
+ # be used to conditionally disable this behavior.
637
+ #
638
+ def shade_box color, line_color = nil, options = {}
639
+ if (!options.key? :only_if) || options[:only_if]
640
+ # FIXME: could use save_graphics_state here
641
+ previous_fill_color = current_fill_color
642
+ fill_color color
643
+ fill_rectangle [bounds.left, bounds.top], bounds.right, bounds.top - bounds.bottom
644
+ fill_color previous_fill_color
645
+ if line_color
646
+ line_width 0.5
647
+ previous_stroke_color = current_stroke_color
648
+ stroke_color line_color
649
+ stroke_bounds
650
+ stroke_color previous_stroke_color
651
+ end
652
+ end
653
+ yield
653
654
  end
654
- end
655
- yield
656
- end
657
655
 
658
- # A compliment to the stroke_horizontal_rule method, strokes a
659
- # vertical line using the current bounds. The width of the line
660
- # can be specified using the line_width option. The horizontal (x)
661
- # position can be specified using the at option.
662
- #
663
- def stroke_vertical_rule rule_color = stroke_color, options = {}
664
- rule_x = options[:at] || 0
665
- rule_y_from = bounds.top
666
- rule_y_to = bounds.bottom
667
- rule_style = options[:line_style]
668
- rule_width = options[:line_width] || 0.5
669
- save_graphics_state do
670
- line_width rule_width
671
- stroke_color rule_color
672
- case rule_style
673
- when :dashed
674
- dash rule_width * 4
675
- when :dotted
676
- dash rule_width
677
- when :double
678
- stroke_vertical_line rule_y_from, rule_y_to, at: (rule_x - rule_width)
679
- rule_x += rule_width
680
- end if rule_style
681
- stroke_vertical_line rule_y_from, rule_y_to, at: rule_x
682
- end
683
- end
656
+ # Strokes a horizontal line using the current bounds. The width of the line
657
+ # can be specified using the line_width option. The offset from the cursor
658
+ # can be set using the at option.
659
+ #
660
+ def stroke_horizontal_rule rule_color = stroke_color, options = {}
661
+ rule_y = cursor - (options[:at] || 0)
662
+ rule_style = options[:line_style]
663
+ rule_width = options[:line_width] || 0.5
664
+ rule_x_start = bounds.left
665
+ rule_x_end = bounds.right
666
+ rule_inked = false
667
+ save_graphics_state do
668
+ line_width rule_width
669
+ stroke_color rule_color
670
+ case rule_style
671
+ when :dashed
672
+ dash rule_width * 4
673
+ when :dotted
674
+ dash rule_width
675
+ when :double
676
+ stroke_horizontal_line rule_x_start, rule_x_end, at: (rule_y + rule_width)
677
+ stroke_horizontal_line rule_x_start, rule_x_end, at: (rule_y - rule_width)
678
+ rule_inked = true
679
+ end if rule_style
680
+ stroke_horizontal_line rule_x_start, rule_x_end, at: rule_y unless rule_inked
681
+ end
682
+ end
684
683
 
685
- # Strokes a horizontal line using the current bounds. The width of the line
686
- # can be specified using the line_width option.
687
- #
688
- def stroke_horizontal_rule rule_color = stroke_color, options = {}
689
- rule_style = options[:line_style]
690
- rule_width = options[:line_width] || 0.5
691
- rule_x_start = bounds.left
692
- rule_x_end = bounds.right
693
- rule_inked = false
694
- save_graphics_state do
695
- line_width rule_width
696
- stroke_color rule_color
697
- case rule_style
698
- when :dashed
699
- dash rule_width * 4
700
- when :dotted
701
- dash rule_width
702
- when :double
703
- move_up rule_width
704
- stroke_horizontal_line rule_x_start, rule_x_end
705
- move_down rule_width * 2
706
- stroke_horizontal_line rule_x_start, rule_x_end
707
- move_up rule_width
708
- rule_inked = true
709
- end if rule_style
710
- stroke_horizontal_line rule_x_start, rule_x_end unless rule_inked
711
- end
712
- end
684
+ # A compliment to the stroke_horizontal_rule method, strokes a
685
+ # vertical line using the current bounds. The width of the line
686
+ # can be specified using the line_width option. The horizontal (x)
687
+ # position can be specified using the at option.
688
+ #
689
+ def stroke_vertical_rule rule_color = stroke_color, options = {}
690
+ rule_x = options[:at] || 0
691
+ rule_y_from = bounds.top
692
+ rule_y_to = bounds.bottom
693
+ rule_style = options[:line_style]
694
+ rule_width = options[:line_width] || 0.5
695
+ save_graphics_state do
696
+ line_width rule_width
697
+ stroke_color rule_color
698
+ case rule_style
699
+ when :dashed
700
+ dash rule_width * 4
701
+ when :dotted
702
+ dash rule_width
703
+ when :double
704
+ stroke_vertical_line rule_y_from, rule_y_to, at: (rule_x - rule_width)
705
+ rule_x += rule_width
706
+ end if rule_style
707
+ stroke_vertical_line rule_y_from, rule_y_to, at: rule_x
708
+ end
709
+ end
713
710
 
714
- # Pages
715
-
716
- # Deletes the current page and move the cursor
717
- # to the previous page.
718
- def delete_page
719
- pg = page_number
720
- pdf_store = state.store
721
- pdf_objs = pdf_store.instance_variable_get :@objects
722
- pdf_ids = pdf_store.instance_variable_get :@identifiers
723
- page_id = pdf_store.object_id_for_page pg
724
- content_id = page.content.identifier
725
- [page_id, content_id].each do |key|
726
- pdf_objs.delete key
727
- pdf_ids.delete key
728
- end
729
- pdf_store.pages.data[:Kids].pop
730
- pdf_store.pages.data[:Count] -= 1
731
- state.pages.pop
732
- if pg > 1
733
- go_to_page pg - 1
734
- else
735
- @page_number = 0
736
- state.page = nil
737
- end
738
- end
711
+ # Pages
712
+
713
+ # Deletes the current page and move the cursor
714
+ # to the previous page.
715
+ def delete_page
716
+ pg = page_number
717
+ pdf_store = state.store
718
+ pdf_objs = pdf_store.instance_variable_get :@objects
719
+ pdf_ids = pdf_store.instance_variable_get :@identifiers
720
+ page_id = pdf_store.object_id_for_page pg
721
+ content_id = page.content.identifier
722
+ [page_id, content_id].each do |key|
723
+ pdf_objs.delete key
724
+ pdf_ids.delete key
725
+ end
726
+ pdf_store.pages.data[:Kids].pop
727
+ pdf_store.pages.data[:Count] -= 1
728
+ state.pages.pop
729
+ if pg > 1
730
+ go_to_page pg - 1
731
+ else
732
+ @page_number = 0
733
+ state.page = nil
734
+ end
735
+ end
739
736
 
740
- # Import the specified page into the current document.
741
- #
742
- # By default, advance to the next page afterwards, creating it if necessary.
743
- # This behavior can be disabled by passing the option `advance: false`.
744
- # However, due to how page creation works in Prawn, understand that advancing
745
- # to the next page is necessary to prevent the size & layout of the imported
746
- # page from affecting a newly created page.
747
- def import_page file, opts = {}
748
- prev_page_layout = page.layout
749
- prev_page_size = page.size
750
- state.compress = false if state.compress # can't use compression if using template
751
- prev_text_rendering_mode = (defined? @text_rendering_mode) ? @text_rendering_mode : nil
752
- delete_page if opts[:replace]
753
- # NOTE use functionality provided by prawn-templates
754
- start_new_page_discretely template: file, template_page: opts[:page]
755
- # prawn-templates sets text_rendering_mode to :unknown, which breaks running content; revert
756
- @text_rendering_mode = prev_text_rendering_mode
757
- if opts.fetch :advance, true
758
- # NOTE set page size & layout explicitly in case imported page differs
759
- # I'm not sure it's right to start a new page here, but unfortunately there's no other
760
- # way atm to prevent the size & layout of the imported page from affecting subsequent pages
761
- advance_page size: prev_page_size, layout: prev_page_layout
762
- end
763
- nil
764
- end
737
+ # Import the specified page into the current document.
738
+ #
739
+ # By default, advance to the next page afterwards, creating it if necessary.
740
+ # This behavior can be disabled by passing the option `advance: false`.
741
+ # However, due to how page creation works in Prawn, understand that advancing
742
+ # to the next page is necessary to prevent the size & layout of the imported
743
+ # page from affecting a newly created page.
744
+ def import_page file, opts = {}
745
+ prev_page_layout = page.layout
746
+ prev_page_size = page.size
747
+ state.compress = false if state.compress # can't use compression if using template
748
+ prev_text_rendering_mode = (defined? @text_rendering_mode) ? @text_rendering_mode : nil
749
+ delete_page if opts[:replace]
750
+ # NOTE use functionality provided by prawn-templates
751
+ start_new_page_discretely template: file, template_page: opts[:page]
752
+ # prawn-templates sets text_rendering_mode to :unknown, which breaks running content; revert
753
+ @text_rendering_mode = prev_text_rendering_mode
754
+ if opts.fetch :advance, true
755
+ # NOTE set page size & layout explicitly in case imported page differs
756
+ # I'm not sure it's right to start a new page here, but unfortunately there's no other
757
+ # way atm to prevent the size & layout of the imported page from affecting subsequent pages
758
+ advance_page size: prev_page_size, layout: prev_page_layout
759
+ end
760
+ nil
761
+ end
765
762
 
766
- # Create a new page for the specified image. If the canvas option is true,
767
- # the image is positioned relative to the boundaries of the page.
768
- def image_page file, options = {}
769
- start_new_page_discretely
770
- image_page_number = page_number
771
- if options.delete :canvas
772
- canvas { image file, ({ position: :center, vposition: :center }.merge options) }
773
- else
774
- image file, (options.merge position: :center, vposition: :center, fit: [bounds.width, bounds.height])
775
- end
776
- # NOTE advance to newly created page just in case the image function threw off the cursor
777
- go_to_page image_page_number
778
- nil
779
- end
763
+ # Create a new page for the specified image. If the canvas option is true,
764
+ # the image is positioned relative to the boundaries of the page.
765
+ def image_page file, options = {}
766
+ start_new_page_discretely
767
+ image_page_number = page_number
768
+ if options.delete :canvas
769
+ canvas { image file, ({ position: :center, vposition: :center }.merge options) }
770
+ else
771
+ image file, (options.merge position: :center, vposition: :center, fit: [bounds.width, bounds.height])
772
+ end
773
+ # NOTE advance to newly created page just in case the image function threw off the cursor
774
+ go_to_page image_page_number
775
+ nil
776
+ end
780
777
 
781
- # Perform an operation (such as creating a new page) without triggering the on_page_create callback
782
- #
783
- def perform_discretely
784
- if (saved_callback = state.on_page_create_callback)
785
- # equivalent to calling `on_page_create`
786
- state.on_page_create_callback = nil
787
- yield
788
- # equivalent to calling `on_page_create &saved_callback`
789
- state.on_page_create_callback = saved_callback
790
- else
791
- yield
792
- end
793
- end
778
+ # Perform an operation (such as creating a new page) without triggering the on_page_create callback
779
+ #
780
+ def perform_discretely
781
+ if (saved_callback = state.on_page_create_callback)
782
+ # equivalent to calling `on_page_create`
783
+ state.on_page_create_callback = nil
784
+ yield
785
+ # equivalent to calling `on_page_create &saved_callback`
786
+ state.on_page_create_callback = saved_callback
787
+ else
788
+ yield
789
+ end
790
+ end
794
791
 
795
- # This method is a smarter version of start_new_page. It calls start_new_page
796
- # if the current page is the last page of the document. Otherwise, it simply
797
- # advances to the next existing page.
798
- def advance_page opts = {}
799
- last_page? ? (start_new_page opts) : (go_to_page page_number + 1)
800
- end
792
+ # This method is a smarter version of start_new_page. It calls start_new_page
793
+ # if the current page is the last page of the document. Otherwise, it simply
794
+ # advances to the next existing page.
795
+ def advance_page opts = {}
796
+ last_page? ? (start_new_page opts) : (go_to_page page_number + 1)
797
+ end
801
798
 
802
- # Start a new page without triggering the on_page_create callback
803
- #
804
- def start_new_page_discretely options = {}
805
- perform_discretely { start_new_page options }
806
- end
799
+ # Start a new page without triggering the on_page_create callback
800
+ #
801
+ def start_new_page_discretely options = {}
802
+ perform_discretely { start_new_page options }
803
+ end
807
804
 
808
- # Grouping
805
+ # Grouping
809
806
 
810
- # Conditional group operation
811
- #
812
- def group_if verdict
813
- if verdict
814
- state.optimize_objects = false # optimize objects breaks group
815
- group { yield }
816
- else
817
- yield
818
- end
819
- end
807
+ # Conditional group operation
808
+ #
809
+ def group_if verdict
810
+ if verdict
811
+ state.optimize_objects = false # optimize_objects breaks group
812
+ group { yield }
813
+ else
814
+ yield
815
+ end
816
+ end
820
817
 
821
- def get_scratch_document
822
- # marshal if not using transaction feature
823
- #Marshal.load Marshal.dump @prototype
824
-
825
- # use cached instance, tests show it's faster
826
- #@prototype ||= ::Prawn::Document.new
827
- @scratch ||= if defined? @prototype
828
- scratch = Marshal.load Marshal.dump @prototype
829
- scratch.instance_variable_set(:@prototype, @prototype)
830
- # TODO set scratch number on scratch document
831
- scratch
832
- else
833
- logger.warn 'no scratch prototype available; instantiating fresh scratch document'
834
- ::Prawn::Document.new
835
- end
836
- end
818
+ def get_scratch_document
819
+ # marshal if not using transaction feature
820
+ #Marshal.load Marshal.dump @prototype
821
+
822
+ # use cached instance, tests show it's faster
823
+ #@prototype ||= ::Prawn::Document.new
824
+ @scratch ||= if defined? @prototype # rubocop:disable Naming/MemoizedInstanceVariableName
825
+ scratch = Marshal.load Marshal.dump @prototype
826
+ scratch.instance_variable_set :@prototype, @prototype
827
+ # TODO: set scratch number on scratch document
828
+ scratch
829
+ else
830
+ logger.warn 'no scratch prototype available; instantiating fresh scratch document'
831
+ ::Prawn::Document.new
832
+ end
833
+ end
837
834
 
838
- def scratch?
839
- (@_label ||= (state.store.info.data[:Scratch] ? :scratch : :primary)) == :scratch
840
- rescue
841
- false # NOTE this method may get called before the state is initialized
842
- end
843
- alias is_scratch? scratch?
844
-
845
- # TODO document me
846
- def dry_run &block
847
- scratch = get_scratch_document
848
- # QUESTION should we use scratch.advance_page instead?
849
- scratch.start_new_page
850
- start_page_number = scratch.page_number
851
- start_y = scratch.y
852
- if (left_padding = bounds.total_left_padding) > 0
853
- scratch.bounds.add_left_padding left_padding
854
- end
855
- if (right_padding = bounds.total_right_padding) > 0
856
- scratch.bounds.add_right_padding right_padding
857
- end
858
- scratch.font font_family, style: font_style, size: font_size do
859
- scratch.instance_exec(&block)
860
- end
861
- # NOTE don't count excess if cursor exceeds writable area (due to padding)
862
- full_page_height = scratch.effective_page_height
863
- partial_page_height = [full_page_height, start_y - scratch.y].min
864
- scratch.bounds.subtract_left_padding left_padding if left_padding > 0
865
- scratch.bounds.subtract_right_padding right_padding if right_padding > 0
866
- whole_pages = scratch.page_number - start_page_number
867
- [(whole_pages * full_page_height + partial_page_height), whole_pages, partial_page_height]
868
- end
835
+ def scratch?
836
+ (@_label ||= (state.store.info.data[:Scratch] ? :scratch : :primary)) == :scratch
837
+ rescue
838
+ false # NOTE this method may get called before the state is initialized
839
+ end
840
+ alias is_scratch? scratch?
841
+
842
+ def dry_run &block
843
+ scratch = get_scratch_document
844
+ # QUESTION should we use scratch.advance_page instead?
845
+ scratch.start_new_page
846
+ start_page_number = scratch.page_number
847
+ start_y = scratch.y
848
+ if (left_padding = bounds.total_left_padding) > 0
849
+ scratch.bounds.add_left_padding left_padding
850
+ end
851
+ if (right_padding = bounds.total_right_padding) > 0
852
+ scratch.bounds.add_right_padding right_padding
853
+ end
854
+ scratch.font font_family, style: font_style, size: font_size do
855
+ scratch.instance_exec(&block)
856
+ end
857
+ # NOTE don't count excess if cursor exceeds writable area (due to padding)
858
+ full_page_height = scratch.effective_page_height
859
+ partial_page_height = [full_page_height, start_y - scratch.y].min
860
+ scratch.bounds.subtract_left_padding left_padding if left_padding > 0
861
+ scratch.bounds.subtract_right_padding right_padding if right_padding > 0
862
+ whole_pages = scratch.page_number - start_page_number
863
+ [(whole_pages * full_page_height + partial_page_height), whole_pages, partial_page_height]
864
+ end
869
865
 
870
- # Attempt to keep the objects generated in the block on the same page
871
- #
872
- # TODO short-circuit nested usage
873
- def keep_together &block
874
- available_space = cursor
875
- total_height, _, _ = dry_run(&block)
876
- # NOTE technically, if we're at the page top, we don't even need to do the
877
- # dry run, except several uses of this method rely on the calculated height
878
- if total_height > available_space && !at_page_top? && total_height <= effective_page_height
879
- advance_page
880
- started_new_page = true
881
- else
882
- started_new_page = false
883
- end
866
+ # Attempt to keep the objects generated in the block on the same page
867
+ #
868
+ # TODO: short-circuit nested usage
869
+ def keep_together &block
870
+ available_space = cursor
871
+ total_height, = dry_run(&block)
872
+ # NOTE technically, if we're at the page top, we don't even need to do the
873
+ # dry run, except several uses of this method rely on the calculated height
874
+ if total_height > available_space && !at_page_top? && total_height <= effective_page_height
875
+ advance_page
876
+ started_new_page = true
877
+ else
878
+ started_new_page = false
879
+ end
884
880
 
885
- # HACK yield doesn't work here on JRuby (at least not when called from AsciidoctorJ)
886
- #yield remainder, started_new_page
887
- instance_exec(total_height, started_new_page, &block)
888
- end
881
+ # HACK: yield doesn't work here on JRuby (at least not when called from AsciidoctorJ)
882
+ #yield remainder, started_new_page
883
+ instance_exec(total_height, started_new_page, &block)
884
+ end
889
885
 
890
- # Attempt to keep the objects generated in the block on the same page
891
- # if the verdict parameter is true.
892
- #
893
- def keep_together_if verdict, &block
894
- if verdict
895
- keep_together(&block)
896
- else
897
- yield
898
- end
899
- end
886
+ # Attempt to keep the objects generated in the block on the same page
887
+ # if the verdict parameter is true.
888
+ #
889
+ def keep_together_if verdict, &block
890
+ if verdict
891
+ keep_together(&block)
892
+ else
893
+ yield
894
+ end
895
+ end
900
896
 
901
897
  =begin
902
- def run_with_trial &block
903
- available_space = cursor
904
- total_height, whole_pages, remainder = dry_run(&block)
905
- if whole_pages > 0 || remainder > available_space
906
- started_new_page = true
907
- else
908
- started_new_page = false
898
+ def run_with_trial &block
899
+ available_space = cursor
900
+ total_height, whole_pages, remainder = dry_run(&block)
901
+ if whole_pages > 0 || remainder > available_space
902
+ started_new_page = true
903
+ else
904
+ started_new_page = false
905
+ end
906
+ # HACK yield doesn't work here on JRuby (at least not when called from AsciidoctorJ)
907
+ #yield remainder, started_new_page
908
+ instance_exec(remainder, started_new_page, &block)
909
+ end
910
+ =end
909
911
  end
910
- # HACK yield doesn't work here on JRuby (at least not when called from AsciidoctorJ)
911
- #yield remainder, started_new_page
912
- instance_exec(remainder, started_new_page, &block)
913
912
  end
914
- =end
915
- end
916
- end
917
913
  end