asciidoctor-pdf 2.0.0.beta.2 → 2.0.1

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.
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ Asciidoctor::AbstractBlock.prepend (Module.new do
4
+ def empty?
5
+ blocks.empty?
6
+ end
7
+
8
+ def first_child
9
+ blocks[0]
10
+ end
11
+
12
+ def last_child
13
+ blocks[-1]
14
+ end
15
+
16
+ def last_child?
17
+ self == parent.blocks[-1]
18
+ end
19
+
20
+ def next_sibling
21
+ (siblings = parent.blocks)[(siblings.index self) + 1]
22
+ end
23
+
24
+ def previous_sibling
25
+ (self_idx = (siblings = parent.blocks).index self) > 0 ? siblings[self_idx - 1] : nil
26
+ end
27
+
28
+ def remove
29
+ parent.blocks.delete self
30
+ end
31
+ end)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # NOTE: these are either candidates for inclusion in Asciidoctor core or backports
4
+ require_relative 'asciidoctor/abstract_block'
4
5
  require_relative 'asciidoctor/document'
5
6
  require_relative 'asciidoctor/section'
6
7
  require_relative 'asciidoctor/list'
@@ -5,12 +5,16 @@ Prawn::Document::ColumnBox.prepend (Module.new do
5
5
  stretchy? ? @parent.absolute_bottom : super
6
6
  end
7
7
 
8
- def move_past_bottom *_args
9
- initial_page = @document.page
10
- super
11
- if (page = @document.page) != initial_page && page.margins != initial_page.margins
12
- @document.bounds = self.class.new @document, @parent, (margin_box = @document.margin_box).absolute_top_left,
8
+ def move_past_bottom
9
+ (doc = @document).y = @y
10
+ return if (@current_column = (@current_column + 1) % @columns) > 0
11
+ @y = (par = @parent).absolute_top if @reflow_margins
12
+ initial_margins = doc.page.margins
13
+ par.move_past_bottom
14
+ if doc.page.margins != initial_margins
15
+ doc.bounds = self.class.new doc, par, (margin_box = doc.margin_box).absolute_top_left,
13
16
  columns: @columns, reflow_margins: true, spacer: @spacer, width: margin_box.width
14
17
  end
18
+ nil
15
19
  end
16
20
  end)
@@ -4,7 +4,7 @@ Prawn::Font::AFM.instance_variable_set :@hide_m17n_warning, true
4
4
 
5
5
  require 'prawn/icon'
6
6
 
7
- Prawn::Icon::Compatibility.send :prepend, (::Module.new { def warning *_args; end })
7
+ Prawn::Icon::Compatibility.prepend (::Module.new { def warning *_args; end })
8
8
 
9
9
  module Asciidoctor
10
10
  module Prawn
@@ -13,6 +13,8 @@ module Asciidoctor
13
13
  include ::Asciidoctor::PDF::Sanitizer
14
14
  include ::Asciidoctor::PDF::TextTransformer
15
15
 
16
+ ColumnBox = ::Prawn::Document::ColumnBox
17
+
16
18
  FontAwesomeIconSets = %w(fab far fas)
17
19
  IconSets = %w(fab far fas fi pf).to_set
18
20
  IconSetPrefixes = IconSets.map {|it| it + '-' }
@@ -429,20 +431,27 @@ module Asciidoctor
429
431
 
430
432
  # NOTE: override built-in fill_formatted_text_box to insert leading before second line when :first_line is true
431
433
  def fill_formatted_text_box text, options
434
+ if (initial_gap = options[:initial_gap]) && !text.empty? && text[0][:from_page] != page_number
435
+ self.y -= initial_gap
436
+ end
432
437
  merge_text_box_positioning_options options
433
438
  box = ::Prawn::Text::Formatted::Box.new text, options
434
- remaining_text = box.render
439
+ remaining_fragments = box.render
435
440
  @no_text_printed = box.nothing_printed?
436
441
  @all_text_printed = box.everything_printed?
442
+ unless remaining_fragments.empty? || (remaining_fragments[0][:from_page] ||= page_number) == page_number
443
+ log :error, %(cannot fit formatted text on page: #{remaining_fragments.map {|it| it[:image_path] || it[:text] }.join})
444
+ page.tare_content_stream
445
+ remaining_fragments = {}
446
+ end
437
447
 
438
- if ((defined? @final_gap) && @final_gap) ||
439
- (options[:first_line] && (options[:final_gap] || !(@no_text_printed || @all_text_printed)))
448
+ if @final_gap || (options[:first_line] && !(@no_text_printed || @all_text_printed))
440
449
  self.y -= box.height + box.line_gap + box.leading
441
450
  else
442
451
  self.y -= box.height
443
452
  end
444
453
 
445
- remaining_text
454
+ remaining_fragments
446
455
  end
447
456
 
448
457
  # NOTE: override built-in draw_indented_formatted_line to set first_line flag
@@ -455,7 +464,7 @@ module Asciidoctor
455
464
  # remaining lines (which is the default behavior in Prawn).
456
465
  def text_with_formatted_first_line string, first_line_options, options
457
466
  if (first_line_font_color = first_line_options.delete :color)
458
- other_lines_font_color, options[:color] = options[:color], first_line_font_color
467
+ remaining_lines_font_color, options[:color] = options[:color], first_line_font_color
459
468
  end
460
469
  fragments = parse_text string, options
461
470
  # NOTE: the low-level APIs we're using don't recognize the :styles option, so we must resolve
@@ -468,19 +477,25 @@ module Asciidoctor
468
477
  end
469
478
  first_line_text_transform = first_line_options.delete :text_transform
470
479
  options = options.merge document: self
480
+ @final_gap = final_gap = options.delete :final_gap
471
481
  text_indent = options.delete :indent_paragraphs
472
482
  # QUESTION: should we merge more carefully here? (hand-select keys?)
473
483
  first_line_options = (options.merge first_line_options).merge single_line: true, first_line: true
474
484
  box = ::Prawn::Text::Formatted::Box.new fragments, first_line_options
475
485
  if text_indent
476
- remaining_fragments = indent text_indent do
477
- box.render dry_run: true
478
- end
486
+ remaining_fragments = indent(text_indent) { box.render dry_run: true }
479
487
  else
480
488
  remaining_fragments = box.render dry_run: true
481
489
  end
490
+ if remaining_fragments.empty?
491
+ remaining_fragments = nil
492
+ elsif (remaining_fragments[0][:from_page] ||= page_number) != page_number
493
+ log :error, %(cannot fit formatted text on page: #{remaining_fragments.map {|it| it[:image_path] || it[:text] }.join})
494
+ page.tare_content_stream
495
+ remaining_fragments = nil
496
+ end
482
497
  if first_line_text_transform
483
- # NOTE: applying text transform here could alter the wrapping, so we need to isolate first line and shrink to fit
498
+ # NOTE: applying text transform here could alter the wrapping, so isolate first line and shrink it to fit
484
499
  first_line_text = (box.instance_variable_get :@printed_lines)[0]
485
500
  unless first_line_text == fragments[0][:text]
486
501
  original_fragments, fragments = fragments, []
@@ -496,17 +511,16 @@ module Asciidoctor
496
511
  end
497
512
  fragments.each {|fragment| fragment[:text] = transform_text fragment[:text], first_line_text_transform }
498
513
  first_line_options[:overflow] = :shrink_to_fit
499
- first_line_options[:final_gap] = first_line_options[:force_justify] = true unless remaining_fragments.empty?
514
+ @final_gap = first_line_options[:force_justify] = true if remaining_fragments
500
515
  end
501
516
  if text_indent
502
- indent text_indent do
503
- fill_formatted_text_box fragments, first_line_options
504
- end
517
+ indent(text_indent) { fill_formatted_text_box fragments, first_line_options }
505
518
  else
506
519
  fill_formatted_text_box fragments, first_line_options
507
520
  end
508
- unless remaining_fragments.empty?
509
- options[:color] = other_lines_font_color if first_line_font_color
521
+ if remaining_fragments
522
+ options[:color] = remaining_lines_font_color if first_line_font_color
523
+ @final_gap = final_gap if first_line_text_transform
510
524
  remaining_fragments = fill_formatted_text_box remaining_fragments, options
511
525
  draw_remaining_formatted_text_on_new_pages remaining_fragments, options
512
526
  end
@@ -541,6 +555,18 @@ module Asciidoctor
541
555
 
542
556
  # Cursor
543
557
 
558
+ # Override the built-in float method to add support for restoring the current column of a ColumnBox
559
+ #
560
+ def float
561
+ original_page_number = page_number
562
+ original_y = y
563
+ original_column = bounds.instance_variable_get :@current_column if ColumnBox === bounds
564
+ yield
565
+ go_to_page original_page_number unless page_number == original_page_number
566
+ self.y = original_y
567
+ bounds.instance_variable_set :@current_column, original_column if original_column
568
+ end
569
+
544
570
  # Short-circuits the call to the built-in move_up operation
545
571
  # when n is 0.
546
572
  #
@@ -589,7 +615,7 @@ module Asciidoctor
589
615
  p_top, p_right, p_bottom, p_left = expand_padding_value padding
590
616
  # logic is intentionally inlined
591
617
  begin
592
- if node && ((last_block = node).content_model != :compound || (last_block = node.blocks[-1])&.context == :paragraph)
618
+ if node && ((last_block = node).content_model != :compound || (last_block = node.last_child)&.context == :paragraph)
593
619
  @bottom_gutters << { last_block => p_bottom }
594
620
  else
595
621
  @bottom_gutters << {}
@@ -599,7 +625,7 @@ module Asciidoctor
599
625
  bounds.add_right_padding p_right
600
626
  yield
601
627
  ensure
602
- cursor > p_bottom ? (move_down p_bottom) : reference_bounds.move_past_bottom unless at_page_top?
628
+ cursor > p_bottom ? (move_down p_bottom) : bounds.move_past_bottom unless at_page_top?
603
629
  @bottom_gutters.pop
604
630
  bounds.subtract_left_padding p_left
605
631
  bounds.subtract_right_padding p_right
@@ -943,7 +969,7 @@ module Asciidoctor
943
969
  # if the current page is the last page of the document. Otherwise, it simply
944
970
  # advances to the next existing page.
945
971
  def advance_page options = {}
946
- last_page? ? (start_new_page options) : (go_to_page page_number + 1)
972
+ !options.empty? && last_page? ? (start_new_page options) : bounds.move_past_bottom
947
973
  end
948
974
 
949
975
  # Start a new page without triggering the on_page_create callback
@@ -952,14 +978,14 @@ module Asciidoctor
952
978
  perform_discretely { start_new_page options }
953
979
  end
954
980
 
955
- # Grouping
981
+ # Scratch
956
982
 
957
- def allocate_prototype
958
- @prototype = create_prototype { ::Marshal.load ::Marshal.dump self }
983
+ def allocate_scratch_prototype
984
+ @scratch_prototype = create_scratch_prototype { ::Marshal.load ::Marshal.dump self }
959
985
  end
960
986
 
961
987
  def scratch
962
- @scratch ||= ((Marshal.load Marshal.dump @prototype).send :init_scratch, self)
988
+ @scratch ||= ((Marshal.load Marshal.dump @scratch_prototype).send :init_scratch, self)
963
989
  end
964
990
 
965
991
  def scratch?
@@ -1116,10 +1142,14 @@ module Asciidoctor
1116
1142
  # Returns an Extent or ScratchExtent object that describes the bounds of the content block.
1117
1143
  def dry_run keep_together: nil, pages_advanced: 0, single_page: nil, onto: nil, &block
1118
1144
  (scratch_pdf = scratch).start_new_page
1119
- scratch_bounds = scratch_pdf.bounds
1120
- restore_bounds = [:@total_left_padding, :@total_right_padding, :@width, :@x].each_with_object({}) do |name, accum|
1121
- accum[name] = scratch_bounds.instance_variable_get name
1122
- scratch_bounds.instance_variable_set name, (bounds.instance_variable_get name)
1145
+ saved_bounds = scratch_pdf.bounds
1146
+ scratch_pdf.bounds = bounds.dup.tap do |bounds_copy|
1147
+ bounds_copy.instance_variable_set :@document, scratch_pdf
1148
+ bounds_copy.instance_variable_set :@parent, saved_bounds
1149
+ if ColumnBox === bounds_copy
1150
+ bounds_copy.instance_variable_set :@width, bounds_copy.bare_column_width
1151
+ bounds_copy.instance_variable_set :@current_column, (bounds_copy.instance_variable_set :@columns, 1) - 1
1152
+ end
1123
1153
  end
1124
1154
  scratch_pdf.move_cursor_to cursor unless (scratch_start_at_top = keep_together || pages_advanced > 0 || at_page_top?)
1125
1155
  scratch_start_cursor = scratch_pdf.cursor
@@ -1161,7 +1191,7 @@ module Asciidoctor
1161
1191
  extent = ScratchExtent.new scratch_start_page, scratch_start_cursor, scratch_end_page, scratch_end_cursor
1162
1192
  onto ? extent.position_onto(*onto) : extent
1163
1193
  ensure
1164
- restore_bounds.each {|name, val| scratch_bounds.instance_variable_set name, val }
1194
+ scratch_pdf.bounds = saved_bounds
1165
1195
  end
1166
1196
  end
1167
1197
  end
@@ -2,12 +2,15 @@
2
2
 
3
3
  module Prawn::Text::Formatted::ProtectBottomGutter
4
4
  def enough_height_for_this_line?
5
- return super unless @arranger.finished?
6
- begin
7
- @height -= @bottom_gutter
5
+ if @arranger.finished? && @arranger.fragments.none? {|it| it.format_state[:full_height] }
6
+ begin
7
+ @height -= @bottom_gutter
8
+ super
9
+ ensure
10
+ @height += @bottom_gutter
11
+ end
12
+ else
8
13
  super
9
- ensure
10
- @height += @bottom_gutter
11
14
  end
12
15
  end
13
16
  end
@@ -38,7 +38,7 @@ module Asciidoctor::PDF::FormattedText
38
38
  return if (raw_image_fragments = fragments.select {|f| (f.key? :image_path) && !(f.key? :image_obj) }).empty?
39
39
  scratch = doc.scratch?
40
40
  available_w = available_width
41
- available_h = doc.page.empty? ? doc.cursor : doc.bounds.height
41
+ available_h = doc.bounds.height
42
42
  last_fragment = {}
43
43
  raw_image_fragments.each do |fragment|
44
44
  if fragment[:object_id] == last_fragment[:object_id]
@@ -96,6 +96,10 @@ module Asciidoctor::PDF::FormattedText
96
96
  if (f_height = image_h) > (line_font = doc.font).height * 1.5
97
97
  # align with descender (equivalent to vertical-align: bottom in CSS)
98
98
  fragment[:ascender] = f_height - (fragment[:descender] = line_font.descender)
99
+ if f_height == available_h
100
+ fragment[:ascender] -= (doc.calc_line_metrics (doc.instance_variable_get :@base_line_height), line_font, doc.font_size).padding_top
101
+ fragment[:full_height] = true
102
+ end
99
103
  doc.font_size (fragment[:size] = f_height * (doc.font_size / line_font.height))
100
104
  # align with baseline (roughly equivalent to vertical-align: baseline in CSS)
101
105
  #fragment[:ascender] = f_height
@@ -11,15 +11,15 @@ module Asciidoctor
11
11
  def wrap array
12
12
  return super unless array[0][:linenum] # sanity check
13
13
  initialize_wrap array
14
+ @line_wrap.extend SourceLineWrap
14
15
  highlight_line = stop = nil
15
16
  unconsumed = @arranger.unconsumed
16
17
  until stop
17
18
  if (first_fragment = unconsumed[0])[:linenum]
18
19
  linenum_text = first_fragment[:text]
19
- linenum_spacer ||= { text: (NoBreakSpace.encode linenum_text.encoding) + (' ' * (linenum_text.length - 1)) }
20
+ linenum_spacer ||= { text: (NoBreakSpace.encode linenum_text.encoding) + (' ' * (linenum_text.length - 1)), linenum: :spacer }
20
21
  highlight_line = (second_fragment = unconsumed[1])[:highlight] ? second_fragment.dup : nil
21
- else
22
- # NOTE: a wrapped line
22
+ else # wrapped line
23
23
  first_fragment[:text] = first_fragment[:text].lstrip
24
24
  @arranger.unconsumed.unshift highlight_line if highlight_line
25
25
  @arranger.unconsumed.unshift linenum_spacer.dup
@@ -43,6 +43,12 @@ module Asciidoctor
43
43
  @arranger.unconsumed
44
44
  end
45
45
  end
46
+
47
+ module SourceLineWrap
48
+ def update_line_status_based_on_last_output
49
+ @arranger.current_format_state[:linenum] ? nil : super
50
+ end
51
+ end
46
52
  end
47
53
  end
48
54
  end
@@ -95,22 +95,24 @@ module Asciidoctor
95
95
  styles: (to_styles theme.menu_font_style),
96
96
  }.compact,
97
97
  }
98
- revise_roles = [].to_set
99
- theme.each_pair.each_with_object @theme_settings do |(key, val), accum|
100
- next unless (key = key.to_s).start_with? 'role_'
101
- role, key = (key.slice 5, key.length).split '_', 2
102
- if (prop = ThemeKeyToFragmentProperty[key])
103
- (accum[role] ||= {})[prop] = val
104
- #elsif key == 'font_kerning'
105
- # unless (resolved_val = val == 'none' ? false : (val == 'normal' ? true : nil)).nil?
106
- # (accum[role] ||= {})[:kerning] = resolved_val
107
- # end
108
- elsif key == 'font_style' || key == 'text_decoration'
109
- revise_roles << role
98
+ @theme_settings.tap do |accum|
99
+ revise_roles = [].to_set
100
+ theme.each_pair do |key, val|
101
+ next unless (key = key.to_s).start_with? 'role_'
102
+ role, key = (key.slice 5, key.length).split '_', 2
103
+ if (prop = ThemeKeyToFragmentProperty[key])
104
+ (accum[role] ||= {})[prop] = val
105
+ #elsif key == 'font_kerning'
106
+ # unless (resolved_val = val == 'none' ? false : (val == 'normal' ? true : nil)).nil?
107
+ # (accum[role] ||= {})[:kerning] = resolved_val
108
+ # end
109
+ elsif key == 'font_style' || key == 'text_decoration'
110
+ revise_roles << role
111
+ end
112
+ end
113
+ revise_roles.each do |role|
114
+ (accum[role] ||= {})[:styles] = to_styles theme[%(role_#{role}_font_style)], theme[%(role_#{role}_text_decoration)]
110
115
  end
111
- end
112
- revise_roles.each_with_object @theme_settings do |role, accum|
113
- (accum[role] ||= {})[:styles] = to_styles theme[%(role_#{role}_font_style)], theme[%(role_#{role}_text_decoration)]
114
116
  end
115
117
  @theme_settings['line-through'] = { styles: [:strikethrough].to_set } unless @theme_settings.key? 'line-through'
116
118
  @theme_settings['underline'] = { styles: [:underline].to_set } unless @theme_settings.key? 'underline'
@@ -276,8 +278,10 @@ module Asciidoctor
276
278
  when '#' # hex string (e.g., #FF0000)
277
279
  fragment[:color] = value.length == 7 ? (value.slice 1, 6) : (value.slice 1, 3).each_char.map {|c| c * 2 }.join if HexColorRx.match? value
278
280
  when '[' # CMYK array (e.g., [50, 100, 0, 0])
279
- fragment[:color] = ((((value.slice 1, value.length).chomp ']').split ', ', 4).each_with_object ::Array.new 4, 0).with_index do |(it, accum), idx|
280
- accum[idx] = (ival = it.to_i) == (fval = it.to_f) ? ival : fval
281
+ fragment[:color] = [0, 0, 0, 0].tap do |accum|
282
+ (((value.slice 1, value.length).chomp ']').split ', ', 4).each_with_index do |it, idx|
283
+ accum[idx] = (ival = it.to_i) == (fval = it.to_f) ? ival : fval
284
+ end
281
285
  end
282
286
  else # assume a 6-character hex color (internal only)
283
287
  fragment[:color] = value
@@ -15,9 +15,7 @@ module Asciidoctor
15
15
  BaseThemePath = ::File.join ThemesDir, 'base-theme.yml'
16
16
  BundledThemeNames = (::Dir.children ThemesDir).map {|it| it.slice 0, it.length - 10 }
17
17
  DeprecatedCategoryKeys = { 'blockquote' => 'quote', 'key' => 'kbd', 'literal' => 'codespan', 'outline_list' => 'list' }
18
- DeprecatedKeys = %w(base heading heading_h1 heading_h2 heading_h3 heading_h4 heading_h5 heading_h6 title_page abstract abstract_title admonition_label sidebar_title toc_title).each_with_object({ 'table_caption_side' => 'table_caption_end' }) do |prefix, accum|
19
- accum[%(#{prefix}_align)] = %(#{prefix}_text_align)
20
- end
18
+ DeprecatedKeys = { 'table_caption_side' => 'table_caption_end' }.tap {|accum| %w(base heading heading_h1 heading_h2 heading_h3 heading_h4 heading_h5 heading_h6 title_page abstract abstract_title admonition_label sidebar_title toc_title).each {|prefix| accum[%(#{prefix}_align)] = %(#{prefix}_text_align) } }
21
19
  PaddingBottomHackKeys = %w(example_padding quote_padding sidebar_padding verse_padding)
22
20
 
23
21
  VariableRx = /\$([a-z0-9_-]+)/
@@ -159,10 +157,12 @@ module Asciidoctor
159
157
  elsif key == 'font_fallbacks'
160
158
  data[key] = ::Array === val ? val.map {|name| expand_vars name.to_s, data } : []
161
159
  elsif key.start_with? 'admonition_icon_'
162
- data[key] = val.map do |(key2, val2)|
163
- key2 = key2.tr '-', '_' if key2.include? '-'
164
- [key2.to_sym, (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data)]
165
- end.to_h if val
160
+ data[key] = {}.tap do |accum|
161
+ val.each do |key2, val2|
162
+ key2 = key2.tr '-', '_' if key2.include? '-'
163
+ accum[key2.to_sym] = (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data)
164
+ end
165
+ end if val
166
166
  elsif ::Hash === val
167
167
  if (rekey = DeprecatedCategoryKeys[key])
168
168
  logger.warn %(the #{key.tr '_', '-'} theme category is deprecated; use the #{rekey.tr '_', '-'} category instead)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module PDF
5
- VERSION = '2.0.0.beta.2'
5
+ VERSION = '2.0.1'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta.2
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Allen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-05-14 00:00:00.000000000 Z
12
+ date: 2022-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: asciidoctor
@@ -246,6 +246,7 @@ files:
246
246
  - lib/asciidoctor/pdf/converter.rb
247
247
  - lib/asciidoctor/pdf/ext.rb
248
248
  - lib/asciidoctor/pdf/ext/asciidoctor.rb
249
+ - lib/asciidoctor/pdf/ext/asciidoctor/abstract_block.rb
249
250
  - lib/asciidoctor/pdf/ext/asciidoctor/document.rb
250
251
  - lib/asciidoctor/pdf/ext/asciidoctor/image.rb
251
252
  - lib/asciidoctor/pdf/ext/asciidoctor/list.rb
@@ -324,9 +325,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
324
325
  version: '0'
325
326
  required_rubygems_version: !ruby/object:Gem::Requirement
326
327
  requirements:
327
- - - ">"
328
+ - - ">="
328
329
  - !ruby/object:Gem::Version
329
- version: 1.3.1
330
+ version: '0'
330
331
  requirements: []
331
332
  rubygems_version: 3.3.7
332
333
  signing_key: