asciidoctor-pdf 2.0.0.alpha.1 → 2.0.0.alpha.2

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.
@@ -164,8 +164,11 @@ module Asciidoctor
164
164
  if node.blocks?
165
165
  node.content
166
166
  elsif node.content_model != :compound && (string = node.content)
167
- # TODO: this content could be cached on repeat invocations!
168
- layout_prose string, (opts.merge hyphenate: true, margin_bottom: 0)
167
+ prose_opts = opts.merge hyphenate: true, margin_bottom: 0
168
+ if (bottom_gutter = @bottom_gutters[-1][node])
169
+ prose_opts[:bottom_gutter] = bottom_gutter
170
+ end
171
+ inscribe_prose string, prose_opts
169
172
  end
170
173
  ensure
171
174
  node.document.instance_variable_set :@converter, prev_converter if prev_converter
@@ -187,25 +190,22 @@ module Asciidoctor
187
190
 
188
191
  marked_page_number = page_number
189
192
  # NOTE: a new page will already be started (page_number = 2) if the front cover image is a PDF
190
- layout_cover_page doc, :front
193
+ inscribe_cover_page doc, :front
191
194
  has_front_cover = page_number > marked_page_number
192
-
193
- if (use_title_page = doc.doctype == 'book' || (doc.attr? 'title-page'))
194
- layout_title_page doc
195
- has_title_page = page_number == (has_front_cover ? 2 : 1)
196
- end
195
+ has_title_page = inscribe_title_page doc if (title_page_on = doc.doctype == 'book' || (doc.attr? 'title-page'))
197
196
 
198
197
  @page_margin_by_side[:cover] = @page_margin_by_side[:recto] if @media == 'prepress' && page_number == 0
199
198
 
200
199
  start_new_page unless page&.empty? # rubocop:disable Lint/SafeNavigationWithEmpty
201
200
 
202
- # NOTE: font must be set before content is written to the main or scratch document
201
+ # NOTE: the base font must be set before any content is written to the main or scratch document
202
+ # this method is called inside inscribe_title_page if the title page is active
203
203
  font @theme.base_font_family, size: @root_font_size, style: @theme.base_font_style unless has_title_page
204
204
 
205
- unless use_title_page
205
+ unless title_page_on
206
206
  body_start_page_number = page_number
207
207
  theme_font :heading, level: 1 do
208
- layout_general_heading doc, doc.doctitle, align: (@theme.heading_h1_align&.to_sym || :center), level: 1, role: :doctitle
208
+ inscribe_general_heading doc, doc.doctitle, align: (@theme.heading_h1_text_align&.to_sym || :center), level: 1, role: :doctitle
209
209
  end if doc.header? && !doc.notitle
210
210
  end
211
211
 
@@ -216,14 +216,14 @@ module Asciidoctor
216
216
  if (insert_toc = (doc.attr? 'toc') && !((toc_placement = doc.attr 'toc-placement') == 'macro' || toc_placement == 'preamble') && doc.sections?)
217
217
  start_new_page if @ppbook && verso_page?
218
218
  add_dest_for_block doc, id: 'toc', y: (at_page_top? ? page_height : nil)
219
- allocate_toc doc, toc_num_levels, cursor, use_title_page
219
+ allocate_toc doc, toc_num_levels, cursor, title_page_on
220
220
  else
221
221
  @toc_extent = nil
222
222
  end
223
223
 
224
224
  start_new_page if @ppbook && verso_page? && !(((next_block = doc.blocks[0])&.context == :preamble ? next_block.blocks[0] : next_block)&.option? 'nonfacing')
225
225
 
226
- if use_title_page
226
+ if title_page_on
227
227
  zero_page_offset = has_front_cover ? 1 : 0
228
228
  first_page_offset = has_title_page ? zero_page_offset.next : zero_page_offset
229
229
  body_offset = (body_start_page_number = page_number) - 1
@@ -301,14 +301,14 @@ module Asciidoctor
301
301
  traverse doc
302
302
 
303
303
  # NOTE: for a book, these are leftover footnotes; for an article this is everything
304
- outdent_section { layout_footnotes doc }
304
+ outdent_section { inscribe_footnotes doc }
305
305
 
306
306
  if @toc_extent
307
- if use_title_page && !insert_toc
307
+ if title_page_on && !insert_toc
308
308
  num_front_matter_pages[0] = @toc_extent.to.page if @theme.running_content_start_at == 'after-toc'
309
309
  num_front_matter_pages[1] = @toc_extent.to.page if @theme.page_numbering_start_at == 'after-toc'
310
310
  end
311
- toc_page_nums = layout_toc doc, toc_num_levels, @toc_extent.from.page, @toc_extent.from.cursor, num_front_matter_pages[1]
311
+ toc_page_nums = inscribe_toc doc, toc_num_levels, @toc_extent.from.page, @toc_extent.from.cursor, num_front_matter_pages[1]
312
312
  else
313
313
  toc_page_nums = []
314
314
  end
@@ -319,8 +319,8 @@ module Asciidoctor
319
319
  end
320
320
 
321
321
  unless page_count < body_start_page_number
322
- layout_running_content :header, doc, num_front_matter_pages, body_start_page_number unless doc.noheader || @theme.header_height.to_f == 0 # rubocop:disable Lint/FloatComparison
323
- layout_running_content :footer, doc, num_front_matter_pages, body_start_page_number unless doc.nofooter || @theme.footer_height.to_f == 0 # rubocop:disable Lint/FloatComparison
322
+ inscribe_running_content :header, doc, num_front_matter_pages, body_start_page_number unless doc.noheader || @theme.header_height.to_f == 0 # rubocop:disable Lint/FloatComparison
323
+ inscribe_running_content :footer, doc, num_front_matter_pages, body_start_page_number unless doc.nofooter || @theme.footer_height.to_f == 0 # rubocop:disable Lint/FloatComparison
324
324
  end
325
325
 
326
326
  add_outline doc, (doc.attr 'outlinelevels', toc_num_levels), toc_page_nums, num_front_matter_pages[1], has_front_cover
@@ -337,7 +337,7 @@ module Asciidoctor
337
337
  catalog.data[:ViewerPreferences] = { DisplayDocTitle: true }
338
338
 
339
339
  stamp_foreground_image doc, has_front_cover
340
- layout_cover_page doc, :back
340
+ inscribe_cover_page doc, :back
341
341
  add_dest_for_top doc
342
342
  nil
343
343
  end
@@ -402,7 +402,7 @@ module Asciidoctor
402
402
  @font_scale = 1
403
403
  @font_color = theme.base_font_color
404
404
  @text_decoration_width = theme.base_text_decoration_width
405
- @base_align = (align = doc.attr 'text-align') && (TextAlignmentNames.include? align) ? align : theme.base_align
405
+ @base_text_align = (align = doc.attr 'text-align') && (TextAlignmentNames.include? align) ? align : theme.base_text_align
406
406
  @base_line_height = theme.base_line_height
407
407
  @cjk_line_breaks = doc.attr? 'scripts', 'cjk'
408
408
  if (hyphen_lang = doc.attr 'hyphens') &&
@@ -415,6 +415,7 @@ module Asciidoctor
415
415
  @text_transform = nil
416
416
  @list_numerals = []
417
417
  @list_bullets = []
418
+ @bottom_gutters = [{}]
418
419
  @rendered_footnotes = []
419
420
  @conum_glyphs = ConumSets[@theme.conum_glyphs || 'circled'] || (@theme.conum_glyphs.split ',').map do |r|
420
421
  from, to = r.lstrip.split '-', 2
@@ -661,22 +662,23 @@ module Asciidoctor
661
662
  title = %(#{title}\n<em class="subtitle">#{subtitle}</em>)
662
663
  end
663
664
  hlevel = sect.level + 1
664
- align = (@theme[%(heading_h#{hlevel}_align)] || @theme.heading_align || @base_align).to_sym
665
+ align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
665
666
  chapterlike = !(part = sectname == 'part') && (sectname == 'chapter' || (sect.document.doctype == 'book' && sect.level == 1))
666
- hopts = { align: align, level: hlevel, outdent: !(part || chapterlike) }
667
+ hidden = sect.option? 'notitle'
668
+ hopts = { align: align, level: hlevel, part: part, chapterlike: chapterlike, outdent: !(part || chapterlike) }
667
669
  if part
668
670
  unless @theme.heading_part_break_before == 'auto'
669
671
  start_new = true
670
- theme_font(:heading, level: hlevel) { start_new_part sect }
672
+ start_new_part sect
671
673
  end
672
674
  elsif chapterlike
673
675
  if @theme.heading_chapter_break_before != 'auto' ||
674
676
  (@theme.heading_part_break_after == 'always' && sect == sect.parent.sections[0])
675
677
  start_new = true
676
- theme_font(:heading, level: hlevel) { start_new_chapter sect }
678
+ start_new_chapter sect
677
679
  end
678
680
  end
679
- arrange_section sect, title, hopts unless start_new || at_page_top?
681
+ arrange_section sect, title, hopts unless hidden || start_new || at_page_top?
680
682
  # QUESTION: should we store pdf-page-start, pdf-anchor & pdf-destination in internal map?
681
683
  sect.set_attr 'pdf-page-start', (start_pgnum = page_number)
682
684
  # QUESTION: should we just assign the section this generated id?
@@ -685,20 +687,20 @@ module Asciidoctor
685
687
  add_dest_for_block sect, id: sect_anchor, y: (at_page_top? ? page_height : nil)
686
688
  theme_font :heading, level: hlevel do
687
689
  if part
688
- layout_part_title sect, title, hopts
690
+ inscribe_part_title sect, title, hopts
689
691
  elsif chapterlike
690
- layout_chapter_title sect, title, hopts unless sect.special && (sect.option? 'untitled')
692
+ inscribe_chapter_title sect, title, hopts
691
693
  else
692
- layout_general_heading sect, title, hopts
694
+ inscribe_general_heading sect, title, hopts
693
695
  end
694
- end
696
+ end unless hidden
695
697
 
696
698
  if index_section
697
699
  outdent_section { convert_index_section sect }
698
700
  else
699
701
  traverse sect
700
702
  end
701
- outdent_section { layout_footnotes sect } if chapterlike
703
+ outdent_section { inscribe_footnotes sect } if chapterlike
702
704
  sect.set_attr 'pdf-page-end', page_number
703
705
  end
704
706
 
@@ -719,7 +721,7 @@ module Asciidoctor
719
721
  end
720
722
 
721
723
  # QUESTION: if a footnote ref appears in a separate chapter, should the footnote def be duplicated?
722
- def layout_footnotes node
724
+ def inscribe_footnotes node
723
725
  return if (fns = (doc = node.document).footnotes - @rendered_footnotes).empty?
724
726
  theme_margin :block, :bottom if node.context == :document || node == node.document.blocks[-1]
725
727
  theme_margin :footnotes, :top
@@ -728,7 +730,7 @@ module Asciidoctor
728
730
  move_down delta
729
731
  end
730
732
  theme_font :footnotes do
731
- (title = doc.attr 'footnotes-title') && (layout_caption title, category: :footnotes)
733
+ (title = doc.attr 'footnotes-title') && (inscribe_caption title, category: :footnotes)
732
734
  item_spacing = @theme.footnotes_item_spacing
733
735
  index_offset = @rendered_footnotes.length
734
736
  sect_xreftext = node.context == :section && (node.xreftext node.document.attr 'xrefstyle')
@@ -738,7 +740,7 @@ module Asciidoctor
738
740
  fn.singleton_class.send :attr_accessor, :label unless fn.respond_to? :label=
739
741
  fn.label = %(#{label} - #{sect_xreftext})
740
742
  end
741
- layout_prose %(<a id="_footnotedef_#{index}">#{DummyText}</a>[<a anchor="_footnoteref_#{index}">#{label}</a>] #{fn.text}), margin_bottom: item_spacing, hyphenate: true
743
+ inscribe_prose %(<a id="_footnotedef_#{index}">#{DummyText}</a>[<a anchor="_footnoteref_#{index}">#{label}</a>] #{fn.text}), margin_bottom: item_spacing, hyphenate: true
742
744
  end
743
745
  @rendered_footnotes += fns if extent
744
746
  end
@@ -750,11 +752,11 @@ module Asciidoctor
750
752
  add_dest_for_block node if node.id
751
753
  hlevel = node.level.next
752
754
  unless (align = resolve_alignment_from_role node.roles)
753
- align = (@theme[%(heading_h#{hlevel}_align)] || @theme.heading_align || @base_align).to_sym
755
+ align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
754
756
  end
755
757
  # QUESTION: should we decouple styles from section titles?
756
758
  theme_font :heading, level: hlevel do
757
- layout_general_heading node, node.title, align: align, level: hlevel, outdent: (node.parent.context == :section)
759
+ inscribe_general_heading node, node.title, align: align, level: hlevel, outdent: (node.parent.context == :section)
758
760
  end
759
761
  end
760
762
 
@@ -763,10 +765,10 @@ module Asciidoctor
763
765
  outdent_section do
764
766
  pad_box @theme.abstract_padding do
765
767
  theme_font :abstract_title do
766
- layout_prose node.title, align: (@theme.abstract_title_align || @base_align).to_sym, margin_top: @theme.heading_margin_top, margin_bottom: @theme.heading_margin_bottom, line_height: (@theme.heading_line_height || @theme.base_line_height)
768
+ inscribe_prose node.title, align: (@theme.abstract_title_text_align || @base_text_align).to_sym, margin_top: @theme.heading_margin_top, margin_bottom: @theme.heading_margin_bottom, line_height: (@theme.heading_line_height || @theme.base_line_height)
767
769
  end if node.title?
768
770
  theme_font :abstract do
769
- prose_opts = { align: (@theme.abstract_align || @base_align).to_sym, hyphenate: true }
771
+ prose_opts = { align: (@theme.abstract_text_align || @base_text_align).to_sym, hyphenate: true }
770
772
  if (text_indent = @theme.prose_text_indent) > 0
771
773
  prose_opts[:indent_paragraphs] = text_indent
772
774
  end
@@ -785,7 +787,7 @@ module Asciidoctor
785
787
  if child.context == :paragraph
786
788
  child.document.playback_attributes child.attributes
787
789
  prose_opts[:margin_bottom] = 0 if child == last_block
788
- layout_prose child.content, ((align = resolve_alignment_from_role child.roles) ? (prose_opts.merge align: align) : prose_opts.dup)
790
+ inscribe_prose child.content, ((align = resolve_alignment_from_role child.roles) ? (prose_opts.merge align: align) : prose_opts.dup)
789
791
  prose_opts.delete :first_line_options
790
792
  prose_opts.delete :margin_bottom
791
793
  else
@@ -797,7 +799,7 @@ module Asciidoctor
797
799
  if (align = resolve_alignment_from_role node.roles)
798
800
  prose_opts[:align] = align
799
801
  end
800
- layout_prose string, (prose_opts.merge margin_bottom: 0)
802
+ inscribe_prose string, (prose_opts.merge margin_bottom: 0)
801
803
  end
802
804
  end
803
805
  end
@@ -809,7 +811,7 @@ module Asciidoctor
809
811
  def convert_preamble node
810
812
  # FIXME: core should not be promoting paragraph to preamble if there are no sections
811
813
  if node.blocks? && (first_block = node.blocks[0]).context == :paragraph && node.document.sections? && !first_block.role?
812
- first_block.add_role 'lead'
814
+ first_block.role = 'lead'
813
815
  end
814
816
  traverse node
815
817
  theme_margin :block, :bottom, (next_enclosed_block node)
@@ -832,13 +834,17 @@ module Asciidoctor
832
834
 
833
835
  # TODO: check if we're within one line of the bottom of the page
834
836
  # and advance to the next page if so (similar to logic for section titles)
835
- layout_caption node, labeled: false if node.title?
837
+ inscribe_caption node, labeled: false if node.title?
838
+
839
+ if (bottom_gutter = @bottom_gutters[-1][node])
840
+ prose_opts[:bottom_gutter] = bottom_gutter
841
+ end
836
842
 
837
843
  if roles.empty?
838
- layout_prose node.content, prose_opts
844
+ inscribe_prose node.content, prose_opts
839
845
  else
840
846
  theme_font_cascade (roles.map {|role| %(role_#{role}).to_sym }) do
841
- layout_prose node.content, prose_opts
847
+ inscribe_prose node.content, prose_opts
842
848
  end
843
849
  end
844
850
 
@@ -852,7 +858,7 @@ module Asciidoctor
852
858
 
853
859
  def convert_admonition node
854
860
  type = node.attr 'name'
855
- label_align = @theme.admonition_label_align&.to_sym || :center
861
+ label_align = @theme.admonition_label_text_align&.to_sym || :center
856
862
  # TODO: allow vertical_align to be a number
857
863
  if (label_valign = @theme.admonition_label_vertical_align&.to_sym || :middle) == :middle
858
864
  label_valign = :center
@@ -861,18 +867,21 @@ module Asciidoctor
861
867
  label_min_width = label_min_width.to_f
862
868
  end
863
869
  if (doc = node.document).attr? 'icons'
864
- if (doc.attr 'icons') == 'font' && !(node.attr? 'icon')
870
+ if !(has_icon = node.attr? 'icon') && (doc.attr 'icons') == 'font'
865
871
  icons = 'font'
866
872
  label_text = type.to_sym
867
873
  icon_data = admonition_icon_data label_text
868
874
  icon_size = icon_data[:size] || 24
869
875
  label_width = label_min_width || (icon_size * 1.5)
870
- elsif (icon_path = resolve_icon_image_path node, type) && (::File.readable? icon_path)
876
+ elsif (icon_path = has_icon || !(icon_path = (@theme[%(admonition_icon_#{type})] || {})[:image]) ?
877
+ (resolve_icon_image_path node, type) :
878
+ (ThemeLoader.resolve_theme_asset (apply_subs_discretely doc, icon_path, subs: [:attributes]), @themesdir)) &&
879
+ (::File.readable? icon_path)
871
880
  icons = true
872
881
  # TODO: introduce @theme.admonition_image_width? or use size key from admonition_icon_<name>?
873
882
  label_width = label_min_width || 36.0
874
883
  else
875
- log :warn, %(admonition icon not found or not readable: #{icon_path || (resolve_icon_image_path node, type, false)})
884
+ log :warn, %(admonition icon image#{has_icon ? '' : ' for ' + type.upcase} not found or not readable: #{icon_path || (resolve_icon_image_path node, type, false)})
876
885
  end
877
886
  end
878
887
  unless icons
@@ -945,7 +954,7 @@ module Asciidoctor
945
954
  log :warn, %(problem encountered in image: #{icon_path}; #{icon_warning})
946
955
  end unless scratch?
947
956
  rescue
948
- log :warn, %(could not embed admonition icon: #{icon_path}; #{$!.message})
957
+ log :warn, %(could not embed admonition icon image: #{icon_path}; #{$!.message})
949
958
  icons = nil
950
959
  end
951
960
  else
@@ -959,7 +968,7 @@ module Asciidoctor
959
968
  end
960
969
  embed_image image_obj, image_info, width: icon_width, position: label_align, vposition: label_valign
961
970
  rescue
962
- log :warn, %(could not embed admonition icon: #{icon_path}; #{$!.message})
971
+ log :warn, %(could not embed admonition icon image: #{icon_path}; #{$!.message})
963
972
  icons = nil
964
973
  end
965
974
  end
@@ -987,7 +996,7 @@ module Asciidoctor
987
996
  end
988
997
  end
989
998
  @text_transform = nil # already applied to label
990
- layout_prose label_text,
999
+ inscribe_prose label_text,
991
1000
  align: label_align,
992
1001
  valign: label_valign,
993
1002
  line_height: 1,
@@ -1000,8 +1009,8 @@ module Asciidoctor
1000
1009
  end
1001
1010
  end
1002
1011
  end
1003
- pad_box [cpad[0], 0, cpad[2], label_width + lpad[1] + cpad[3]] do
1004
- layout_caption node, category: :admonition, labeled: false if node.title?
1012
+ pad_box [cpad[0], 0, cpad[2], label_width + lpad[1] + cpad[3]], node do
1013
+ inscribe_caption node, category: :admonition, labeled: false if node.title?
1005
1014
  theme_font :admonition do
1006
1015
  traverse node
1007
1016
  end
@@ -1018,7 +1027,7 @@ module Asciidoctor
1018
1027
  tare_first_page_content_stream do
1019
1028
  theme_fill_and_stroke_block :example, extent, caption_node: node
1020
1029
  end
1021
- pad_box @theme.example_padding do
1030
+ pad_box @theme.example_padding, node do
1022
1031
  theme_font :example do
1023
1032
  traverse node
1024
1033
  end
@@ -1035,13 +1044,13 @@ module Asciidoctor
1035
1044
  arrange_block node do
1036
1045
  add_dest_for_block node if id
1037
1046
  tare_first_page_content_stream do
1038
- node.context == :example ? (layout_caption %(\u25bc #{node.title})) : (layout_caption node, labeled: false)
1047
+ node.context == :example ? (inscribe_caption %(\u25bc #{node.title})) : (inscribe_caption node, labeled: false)
1039
1048
  end if has_title
1040
1049
  traverse node
1041
1050
  end
1042
1051
  else
1043
1052
  add_dest_for_block node if id
1044
- node.context == :example ? (layout_caption %(\u25bc #{node.title})) : (layout_caption node, labeled: false) if has_title
1053
+ node.context == :example ? (inscribe_caption %(\u25bc #{node.title})) : (inscribe_caption node, labeled: false) if has_title
1045
1054
  traverse node
1046
1055
  end
1047
1056
  end
@@ -1055,6 +1064,13 @@ module Asciidoctor
1055
1064
  b_left_width = nil
1056
1065
  b_width = nil if (b_width = @theme[%(#{category}_border_width)]) == 0
1057
1066
  end
1067
+ if (attribution = (node.attr? 'attribution') && (node.attr 'attribution'))
1068
+ # NOTE: temporary workaround to allow bare & to be used without having to wrap value in single quotes
1069
+ attribution = escape_amp attribution if attribution.include? '&'
1070
+ if (citetitle = node.attr 'citetitle') && (citetitle.include? '&')
1071
+ citetitle = escape_amp citetitle
1072
+ end
1073
+ end
1058
1074
  arrange_block node do |extent|
1059
1075
  add_dest_for_block node if node.id
1060
1076
  tare_first_page_content_stream do
@@ -1072,27 +1088,25 @@ module Asciidoctor
1072
1088
  end
1073
1089
  end
1074
1090
  end
1075
- pad_box @theme[%(#{category}_padding)] do
1091
+ pad_box @theme[%(#{category}_padding)], (attribution ? nil : node) do
1076
1092
  theme_font category do
1077
1093
  if category == :quote
1078
1094
  traverse node
1079
1095
  else # :verse
1080
1096
  content = guard_indentation node.content
1081
- layout_prose content, normalize: false, align: :left, hyphenate: true, margin_bottom: 0
1097
+ inscribe_prose content,
1098
+ normalize: false,
1099
+ align: :left,
1100
+ hyphenate: true,
1101
+ margin_bottom: 0,
1102
+ bottom_gutter: (attribution ? nil : @bottom_gutters[-1][node])
1082
1103
  end
1083
1104
  end
1084
- if node.attr? 'attribution'
1105
+ if attribution
1085
1106
  margin_bottom @theme.block_margin_bottom
1086
1107
  theme_font %(#{category}_cite) do
1087
- # NOTE: temporary workaround to allow bare & to be used without having to wrap value in single quotes
1088
- attribution = node.attr 'attribution'
1089
- attribution = escape_amp attribution if attribution.include? '&'
1090
- attribution_parts = [attribution]
1091
- if (citetitle = node.attr 'citetitle')
1092
- citetitle = escape_amp citetitle if citetitle.include? '&'
1093
- attribution_parts << citetitle
1094
- end
1095
- layout_prose %(#{EmDash} #{attribution_parts.join ', '}), align: :left, normalize: false, margin_bottom: 0
1108
+ attribution_parts = citetitle ? [attribution, citetitle] : [attribution]
1109
+ inscribe_prose %(#{EmDash} #{attribution_parts.join ', '}), align: :left, normalize: false, margin_bottom: 0
1096
1110
  end
1097
1111
  end
1098
1112
  end
@@ -1107,10 +1121,10 @@ module Asciidoctor
1107
1121
  arrange_block node do |extent|
1108
1122
  add_dest_for_block node if node.id
1109
1123
  theme_fill_and_stroke_block :sidebar, extent if extent
1110
- pad_box @theme.sidebar_padding do
1124
+ pad_box @theme.sidebar_padding, node do
1111
1125
  theme_font :sidebar_title do
1112
1126
  # QUESTION: should we allow margins of sidebar title to be customized?
1113
- layout_prose node.title, align: (@theme.sidebar_title_align || @theme.heading_align || @base_align).to_sym, margin_bottom: @theme.heading_margin_bottom, line_height: (@theme.heading_line_height || @theme.base_line_height)
1127
+ inscribe_prose node.title, align: (@theme.sidebar_title_text_align || @theme.heading_text_align || @base_text_align).to_sym, margin_bottom: @theme.heading_margin_bottom, line_height: (@theme.heading_line_height || @theme.base_line_height)
1114
1128
  end if node.title?
1115
1129
  theme_font :sidebar do
1116
1130
  traverse node
@@ -1149,7 +1163,7 @@ module Asciidoctor
1149
1163
  marker_width = rendered_width_of_string %(#{marker = conum_glyph index}x)
1150
1164
  float do
1151
1165
  bounding_box [0, cursor], width: marker_width do
1152
- layout_prose marker, align: :center, inline_format: false, margin: 0
1166
+ inscribe_prose marker, align: :center, inline_format: false, margin: 0
1153
1167
  end
1154
1168
  end
1155
1169
  end
@@ -1233,7 +1247,7 @@ module Asciidoctor
1233
1247
  max_term_width += (term_padding[1] + term_padding[3])
1234
1248
  term_column_width = [max_term_width, bounds.width * 0.5].min
1235
1249
  table table_data, position: :left, cell_style: { border_width: 0 }, column_widths: [term_column_width] do
1236
- @pdf.layout_table_caption node if node.title?
1250
+ @pdf.inscribe_table_caption node if node.title?
1237
1251
  end
1238
1252
  theme_margin :prose, :bottom, (next_enclosed_block actual_node) #unless actual_node.nested?
1239
1253
  when 'qanda'
@@ -1243,7 +1257,7 @@ module Asciidoctor
1243
1257
  else
1244
1258
  # TODO: check if we're within one line of the bottom of the page
1245
1259
  # and advance to the next page if so (similar to logic for section titles)
1246
- layout_caption node, category: :description_list, labeled: false if node.title?
1260
+ inscribe_caption node, category: :description_list, labeled: false if node.title?
1247
1261
 
1248
1262
  term_spacing = @theme.description_list_term_spacing
1249
1263
  term_height = theme_font(:description_list_term) { height_of_typeset_text 'A' }
@@ -1255,8 +1269,8 @@ module Asciidoctor
1255
1269
  term_font_styles = nil
1256
1270
  end
1257
1271
  terms.each_with_index do |term, idx|
1258
- # QUESTION: should we pass down styles in other calls to layout_prose
1259
- layout_prose term.text, margin_top: (idx > 0 ? term_spacing : 0), margin_bottom: 0, align: :left, normalize_line_height: true, styles: term_font_styles
1272
+ # QUESTION: should we pass down styles in other calls to inscribe_prose
1273
+ inscribe_prose term.text, margin_top: (idx > 0 ? term_spacing : 0), margin_bottom: 0, align: :left, normalize_line_height: true, styles: term_font_styles
1260
1274
  end
1261
1275
  end
1262
1276
  indent @theme.description_list_description_indent do
@@ -1343,7 +1357,7 @@ module Asciidoctor
1343
1357
  def convert_list node
1344
1358
  # TODO: check if we're within one line of the bottom of the page
1345
1359
  # and advance to the next page if so (similar to logic for section titles)
1346
- layout_caption node, category: :list, labeled: false if node.title?
1360
+ inscribe_caption node, category: :list, labeled: false if node.title?
1347
1361
 
1348
1362
  opts = {}
1349
1363
  if (align = resolve_alignment_from_role node.roles)
@@ -1440,7 +1454,7 @@ module Asciidoctor
1440
1454
  float do
1441
1455
  advance_page if @media == 'prepress' && cursor < marker_height
1442
1456
  flow_bounding_box position: start_position, width: marker_width do
1443
- layout_prose marker,
1457
+ inscribe_prose marker,
1444
1458
  align: :right,
1445
1459
  character_spacing: -0.5,
1446
1460
  color: marker_style[:font_color],
@@ -1468,16 +1482,16 @@ module Asciidoctor
1468
1482
  def traverse_list_item node, list_type, opts = {}
1469
1483
  if list_type == :dlist # qanda
1470
1484
  terms, desc = node
1471
- terms.each {|term| layout_prose %(<em>#{term.text}</em>), (opts.merge margin_bottom: @theme.description_list_term_spacing) }
1485
+ terms.each {|term| inscribe_prose %(<em>#{term.text}</em>), (opts.merge margin_bottom: @theme.description_list_term_spacing) }
1472
1486
  if desc
1473
- layout_prose desc.text, (opts.merge hyphenate: true) if desc.text?
1487
+ inscribe_prose desc.text, (opts.merge hyphenate: true) if desc.text?
1474
1488
  traverse desc
1475
1489
  end
1476
1490
  else
1477
1491
  if (primary_text = node.text).nil_or_empty?
1478
- layout_prose DummyText, opts unless node.blocks?
1492
+ inscribe_prose DummyText, opts unless node.blocks?
1479
1493
  else
1480
- layout_prose primary_text, (opts.merge hyphenate: true)
1494
+ inscribe_prose primary_text, (opts.merge hyphenate: true)
1481
1495
  end
1482
1496
  traverse node
1483
1497
  end
@@ -1498,24 +1512,33 @@ module Asciidoctor
1498
1512
  elsif (image_path = resolve_image_path node, target, image_format, (opts.fetch :relative_to_imagesdir, true))
1499
1513
  if image_format == 'pdf'
1500
1514
  if ::File.readable? image_path
1501
- if (id = node.id)
1515
+ if (replace = page.empty?) && ((parent = node.parent).attr? 'pdf-page-start', page_number) && (parent.attr? 'pdf-anchor')
1516
+ replace_parent = parent
1517
+ end
1518
+ if (id = node.id) || replace_parent
1502
1519
  add_dest_block = proc do
1503
- node.set_attr 'pdf-destination', (node_dest = dest_top)
1504
- add_dest id, node_dest
1520
+ node_dest = dest_top
1521
+ if id
1522
+ node.set_attr 'pdf-destination', node_dest
1523
+ add_dest id, node_dest
1524
+ end
1525
+ if replace_parent
1526
+ replace_parent.set_attr 'pdf-destination', node_dest
1527
+ add_dest (replace_parent.attr 'pdf-anchor'), node_dest
1528
+ end
1505
1529
  end
1506
1530
  end
1507
1531
  # NOTE: import_page automatically advances to next page afterwards
1508
- # QUESTION: should we add destination to top of imported page?
1509
1532
  if (pgnums = node.attr 'pages')
1510
1533
  (resolve_pagenums pgnums).each_with_index do |pgnum, idx|
1511
1534
  if idx == 0
1512
- import_page image_path, page: pgnum, replace: page.empty?, &add_dest_block
1535
+ import_page image_path, page: pgnum, replace: replace, &add_dest_block
1513
1536
  else
1514
1537
  import_page image_path, page: pgnum, replace: true
1515
1538
  end
1516
1539
  end
1517
1540
  else
1518
- import_page image_path, page: [(node.attr 'page', nil, 1).to_i, 1].max, replace: page.empty?, &add_dest_block
1541
+ import_page image_path, page: [(node.attr 'page', nil, 1).to_i, 1].max, replace: replace, &add_dest_block
1519
1542
  end
1520
1543
  return
1521
1544
  else
@@ -1532,14 +1555,14 @@ module Asciidoctor
1532
1555
 
1533
1556
  alignment = (alignment = node.attr 'align') ?
1534
1557
  ((BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left) :
1535
- (resolve_alignment_from_role node.roles) || (@theme.image_align&.to_sym || :left)
1558
+ (resolve_alignment_from_role node.roles) || @theme.image_align&.to_sym || :left
1536
1559
  # TODO: support cover (aka canvas) image layout using "canvas" (or "cover") role
1537
1560
  width = resolve_explicit_width node.attributes, bounds_width: (available_w = bounds.width), support_vw: true, use_fallback: true, constrain_to_bounds: true
1538
1561
  # TODO: add `to_pt page_width` method to ViewportWidth type
1539
1562
  width = (width.to_f / 100) * page_width if ViewportWidth === width
1540
1563
 
1541
1564
  # NOTE: if width is not set explicitly and max-width is fit-content, caption height may not be accurate
1542
- caption_h = node.title? ? (layout_caption node, category: :image, side: :bottom, block_align: alignment, block_width: width, max_width: @theme.image_caption_max_width, dry_run: true, force_top_margin: true) : 0
1565
+ caption_h = node.title? ? (inscribe_caption node, category: :image, side: :bottom, block_align: alignment, block_width: width, max_width: @theme.image_caption_max_width, dry_run: true, force_top_margin: true) : 0
1543
1566
 
1544
1567
  align_to_page = node.option? 'align-to-page'
1545
1568
  pinned = opts[:pinned]
@@ -1622,7 +1645,7 @@ module Asciidoctor
1622
1645
  move_down rendered_h if y == image_y
1623
1646
  end
1624
1647
  end
1625
- layout_caption node, category: :image, side: :bottom, block_align: alignment, block_width: rendered_w, max_width: @theme.image_caption_max_width if node.title?
1648
+ inscribe_caption node, category: :image, side: :bottom, block_align: alignment, block_width: rendered_w, max_width: @theme.image_caption_max_width if node.title?
1626
1649
  theme_margin :block, :bottom, (next_enclosed_block node) unless pinned
1627
1650
  rescue => e
1628
1651
  raise if ::StopIteration === e
@@ -1662,9 +1685,9 @@ module Asciidoctor
1662
1685
  alignment = (alignment = node.attr 'align') ?
1663
1686
  ((BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left) :
1664
1687
  (resolve_alignment_from_role node.roles) || (@theme.image_align&.to_sym || :left)
1665
- layout_prose alt_text_template % alt_text_vars, align: alignment, margin: 0, normalize: false, single_line: true
1688
+ inscribe_prose alt_text_template % alt_text_vars, align: alignment, margin: 0, normalize: false, single_line: true
1666
1689
  end
1667
- layout_caption node, category: :image, side: :bottom if node.title?
1690
+ inscribe_caption node, category: :image, side: :bottom if node.title?
1668
1691
  theme_margin :block, :bottom, (next_enclosed_block node) unless opts[:pinned]
1669
1692
  nil
1670
1693
  end
@@ -1673,8 +1696,8 @@ module Asciidoctor
1673
1696
  add_dest_for_block node if node.id
1674
1697
  audio_path = node.media_uri node.attr 'target'
1675
1698
  play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer
1676
- layout_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{audio_path}">#{audio_path}</a> <em>(audio)</em>), normalize: false, margin: 0, single_line: true
1677
- layout_caption node, labeled: false, side: :bottom if node.title?
1699
+ inscribe_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{audio_path}">#{audio_path}</a> <em>(audio)</em>), normalize: false, margin: 0, single_line: true
1700
+ inscribe_caption node, labeled: false, side: :bottom if node.title?
1678
1701
  theme_margin :block, :bottom, (next_enclosed_block node)
1679
1702
  end
1680
1703
 
@@ -1701,8 +1724,8 @@ module Asciidoctor
1701
1724
  if poster.nil_or_empty?
1702
1725
  add_dest_for_block node if node.id
1703
1726
  play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer
1704
- layout_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{video_path}">#{video_path}</a> <em>(#{type})</em>), normalize: false, margin: 0, single_line: true
1705
- layout_caption node, labeled: false, side: :bottom if node.title?
1727
+ inscribe_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{video_path}">#{video_path}</a> <em>(#{type})</em>), normalize: false, margin: 0, single_line: true
1728
+ inscribe_caption node, labeled: false, side: :bottom if node.title?
1706
1729
  theme_margin :block, :bottom, (next_enclosed_block node)
1707
1730
  else
1708
1731
  original_attributes = node.attributes.dup
@@ -1717,7 +1740,8 @@ module Asciidoctor
1717
1740
 
1718
1741
  # QUESTION: can we avoid arranging fragments multiple times (conums & autofit) by eagerly preparing arranger?
1719
1742
  def convert_listing_or_literal node
1720
- wrap_ext = source_chunks = bg_color_override = font_color_override = adjusted_font_size = nil
1743
+ extensions = []
1744
+ source_chunks = bg_color_override = font_color_override = adjusted_font_size = nil
1721
1745
  theme_font :code do
1722
1746
  # HACK: disable built-in syntax highlighter; must be done before calling node.content!
1723
1747
  if node.style == 'source' && (highlighter = (syntax_hl = node.document.syntax_highlighter)&.highlight? && syntax_hl.name)
@@ -1797,7 +1821,7 @@ module Asciidoctor
1797
1821
  if (node.option? 'linenums') || (node.attr? 'linenums')
1798
1822
  linenums = (node.attr 'start', 1).to_i
1799
1823
  postprocess = true
1800
- wrap_ext = FormattedText::SourceWrap
1824
+ extensions << FormattedText::SourceWrap
1801
1825
  elsif conum_mapping || highlight_lines
1802
1826
  postprocess = true
1803
1827
  end
@@ -1814,7 +1838,7 @@ module Asciidoctor
1814
1838
  else
1815
1839
  if (node.option? 'linenums') || (node.attr? 'linenums')
1816
1840
  formatter_opts = { line_numbers: true, start_line: (node.attr 'start', 1).to_i }
1817
- wrap_ext = FormattedText::SourceWrap
1841
+ extensions << FormattedText::SourceWrap
1818
1842
  else
1819
1843
  formatter_opts = {}
1820
1844
  end
@@ -1848,14 +1872,13 @@ module Asciidoctor
1848
1872
  tare_first_page_content_stream do
1849
1873
  theme_fill_and_stroke_block :code, extent, background_color: bg_color_override, caption_node: node
1850
1874
  end
1851
- pad_box @theme.code_padding do
1875
+ pad_box @theme.code_padding, node do
1852
1876
  theme_font :code do
1853
- ::Prawn::Text::Formatted::Box.extensions << wrap_ext if wrap_ext
1854
1877
  typeset_formatted_text source_chunks, (calc_line_metrics @base_line_height),
1855
1878
  color: (font_color_override || @theme.code_font_color || @font_color),
1856
- size: adjusted_font_size
1857
- ensure
1858
- ::Prawn::Text::Formatted::Box.extensions.pop if wrap_ext
1879
+ size: adjusted_font_size,
1880
+ bottom_gutter: @bottom_gutters[-1][node],
1881
+ extensions: extensions.empty? ? nil : extensions
1859
1882
  end
1860
1883
  end
1861
1884
  end
@@ -2159,45 +2182,36 @@ module Asciidoctor
2159
2182
  end
2160
2183
  end
2161
2184
 
2162
- # NOTE: Prawn aborts if table data is empty, so ensure there's at least one row
2185
+ # NOTE: Prawn crashes if table data is empty, so ensure there's at least one row
2163
2186
  if table_data.empty?
2164
2187
  log(:warn) { message_with_context 'no rows found in table', source_location: node.source_location }
2165
2188
  table_data << ::Array.new([node.columns.size, 1].max) { { content: '' } }
2166
2189
  end
2167
2190
 
2168
- border_width = {}
2169
- table_border_color = theme.table_border_color || ((Array theme.table_grid_color).size == 2 ? nil : theme.table_grid_color) || theme.base_border_color
2170
- table_border_style = theme.table_border_style&.to_sym || :solid
2171
- table_border_width = theme.table_border_width
2172
- if table_header_size
2173
- head_border_bottom_color = theme.table_head_border_bottom_color || table_border_color
2174
- head_border_bottom_style = (theme.table_head_border_bottom_style || table_border_style).to_sym
2175
- head_border_bottom_width = theme.table_head_border_bottom_width || table_border_width
2176
- end
2177
- [:top, :bottom, :left, :right].each {|edge| border_width[edge] = table_border_width }
2191
+ rect_side_names = [:top, :right, :bottom, :left]
2192
+ grid_axis_names = [:rows, :cols]
2193
+ border_color = (rect_side_names.zip expand_rect_values theme.table_border_color, (theme.base_border_color || 'transparent')).to_h
2194
+ border_style = (rect_side_names.zip (expand_rect_values theme.table_border_style, :solid).map(&:to_sym)).to_h
2195
+ border_width = (rect_side_names.zip expand_rect_values theme.table_border_width, 0).to_h
2196
+ grid_color = (grid_axis_names.zip expand_grid_values (theme.table_grid_color || [border_color[:top], border_color[:left]]), 'transparent').to_h
2197
+ grid_style = (grid_axis_names.zip (expand_grid_values (theme.table_grid_style || [border_style[:top], border_style[:left]]), :solid).map(&:to_sym)).to_h
2198
+ grid_width = (grid_axis_names.zip expand_grid_values theme.table_grid_width, 0).to_h
2178
2199
 
2179
- table_grid_color = theme.table_grid_color || table_border_color
2180
- if ::Array === (table_grid_style = theme.table_grid_style || table_border_style)
2181
- table_grid_style = table_grid_style.map(&:to_sym)
2182
- else
2183
- table_grid_style = [table_grid_style.to_sym]
2184
- end
2185
- if ::Array === (table_grid_width = theme.table_grid_width || theme.table_border_width)
2186
- border_width[:rows] = table_grid_width[0]
2187
- border_width[:cols] = table_grid_width[1]
2188
- else
2189
- [:cols, :rows].each {|edge| border_width[edge] = table_grid_width }
2200
+ if table_header_size
2201
+ head_border_bottom_color = theme.table_head_border_bottom_color || grid_color[:rows]
2202
+ head_border_bottom_style = theme.table_head_border_bottom_style&.to_sym || grid_style[:rows]
2203
+ head_border_bottom_width = theme.table_head_border_bottom_width || grid_width[:rows]
2190
2204
  end
2191
2205
 
2192
2206
  case (grid = node.attr 'grid', 'all', 'table-grid')
2193
2207
  when 'all'
2194
2208
  # keep inner borders
2195
2209
  when 'cols'
2196
- border_width[:rows] = 0
2210
+ grid_width[:rows] = 0
2197
2211
  when 'rows'
2198
- border_width[:cols] = 0
2212
+ grid_width[:cols] = 0
2199
2213
  else # none
2200
- border_width[:rows] = border_width[:cols] = 0
2214
+ grid_width[:rows] = grid_width[:cols] = 0
2201
2215
  end
2202
2216
 
2203
2217
  case (frame = node.attr 'frame', 'all', 'table-frame')
@@ -2234,13 +2248,8 @@ module Asciidoctor
2234
2248
  header: table_header_size,
2235
2249
  # NOTE: position is handled by this method
2236
2250
  position: :left,
2237
- cell_style: {
2238
- # NOTE: the border color and style of the outer frame is set later
2239
- border_color: table_grid_color,
2240
- border_lines: table_grid_style,
2241
- # NOTE: the border width is set later
2242
- border_width: 0,
2243
- },
2251
+ # NOTE: the border color, style, and width of the outer frame is set in the table callback block
2252
+ cell_style: { border_color: grid_color.values, border_lines: grid_style.values, border_width: grid_width.values },
2244
2253
  width: table_width,
2245
2254
  column_widths: column_widths,
2246
2255
  }
@@ -2261,7 +2270,7 @@ module Asciidoctor
2261
2270
  table table_data, table_settings do
2262
2271
  # NOTE: call width to capture resolved table width
2263
2272
  table_width = width
2264
- @pdf.layout_table_caption node, alignment, table_width, caption_max_width if node.title? && caption_side == :top
2273
+ @pdf.inscribe_table_caption node, alignment, table_width, caption_max_width if node.title? && caption_side == :top
2265
2274
  # NOTE: align using padding instead of bounding_box as prawn-table does
2266
2275
  # using a bounding_box across pages mangles the margin box of subsequent pages
2267
2276
  if alignment != :left && table_width != (this_bounds = @pdf.bounds).width
@@ -2282,7 +2291,7 @@ module Asciidoctor
2282
2291
  end if table_header_size
2283
2292
  else
2284
2293
  # apply the grid setting first across all cells
2285
- cells.border_width = [border_width[:rows], border_width[:cols], border_width[:rows], border_width[:cols]]
2294
+ cells.border_width = [grid_width[:rows], grid_width[:cols], grid_width[:rows], grid_width[:cols]]
2286
2295
 
2287
2296
  if table_header_size
2288
2297
  (rows table_header_size - 1).tap do |r|
@@ -2299,19 +2308,19 @@ module Asciidoctor
2299
2308
 
2300
2309
  # top edge of table
2301
2310
  (rows 0).tap do |r|
2302
- r.border_top_color, r.border_top_line, r.border_top_width = table_border_color, table_border_style, border_width[:top]
2311
+ r.border_top_color, r.border_top_line, r.border_top_width = border_color[:top], border_style[:top], border_width[:top]
2303
2312
  end
2304
2313
  # right edge of table
2305
2314
  (columns num_cols - 1).tap do |r|
2306
- r.border_right_color, r.border_right_line, r.border_right_width = table_border_color, table_border_style, border_width[:right]
2315
+ r.border_right_color, r.border_right_line, r.border_right_width = border_color[:right], border_style[:right], border_width[:right]
2307
2316
  end
2308
2317
  # bottom edge of table
2309
2318
  (rows num_rows - 1).tap do |r|
2310
- r.border_bottom_color, r.border_bottom_line, r.border_bottom_width = table_border_color, table_border_style, border_width[:bottom]
2319
+ r.border_bottom_color, r.border_bottom_line, r.border_bottom_width = border_color[:bottom], border_style[:bottom], border_width[:bottom]
2311
2320
  end
2312
2321
  # left edge of table
2313
2322
  (columns 0).tap do |r|
2314
- r.border_left_color, r.border_left_line, r.border_left_width = table_border_color, table_border_style, border_width[:left]
2323
+ r.border_left_color, r.border_left_line, r.border_left_width = border_color[:left], border_style[:left], border_width[:left]
2315
2324
  end
2316
2325
  end
2317
2326
 
@@ -2334,7 +2343,7 @@ module Asciidoctor
2334
2343
  bounds.subtract_left_padding left_padding
2335
2344
  bounds.subtract_right_padding right_padding if right_padding
2336
2345
  end
2337
- layout_table_caption node, alignment, table_width, caption_max_width, caption_side if node.title? && caption_side == :bottom
2346
+ inscribe_table_caption node, alignment, table_width, caption_max_width, caption_side if node.title? && caption_side == :bottom
2338
2347
  theme_margin :block, :bottom, (next_enclosed_block node)
2339
2348
  rescue ::Prawn::Errors::CannotFit
2340
2349
  log :error, (message_with_context 'cannot fit contents of table cell into specified column width', source_location: node.source_location)
@@ -2356,8 +2365,8 @@ module Asciidoctor
2356
2365
  start_new_page if @ppbook && verso_page? && !(is_macro && (node.option? 'nonfacing'))
2357
2366
  end
2358
2367
  add_dest_for_block node, id: (node.id || 'toc') if is_macro
2359
- allocate_toc doc, (doc.attr 'toclevels', 2).to_i, cursor, (use_title_page = is_book || (doc.attr? 'title-page'))
2360
- @index.start_page_number = @toc_extent.to.page + 1 if use_title_page && @theme.page_numbering_start_at == 'after-toc'
2368
+ allocate_toc doc, (doc.attr 'toclevels', 2).to_i, cursor, (title_page_on = is_book || (doc.attr? 'title-page'))
2369
+ @index.start_page_number = @toc_extent.to.page + 1 if title_page_on && @theme.page_numbering_start_at == 'after-toc'
2361
2370
  if is_macro
2362
2371
  @disable_running_content[:header] += @toc_extent.page_range if node.option? 'noheader'
2363
2372
  @disable_running_content[:footer] += @toc_extent.page_range if node.option? 'nofooter'
@@ -2398,7 +2407,7 @@ module Asciidoctor
2398
2407
  @index.categories.each do |category|
2399
2408
  # NOTE: cursor method always returns 0 inside column_box; breaks reference_bounds.move_past_bottom
2400
2409
  bounds.move_past_bottom if space_needed_for_category > y - reference_bounds.absolute_bottom
2401
- layout_prose category.name,
2410
+ inscribe_prose category.name,
2402
2411
  align: :left,
2403
2412
  inline_format: false,
2404
2413
  margin_bottom: @theme.description_list_term_spacing,
@@ -2426,7 +2435,7 @@ module Asciidoctor
2426
2435
  text = %(#{text}, #{pagenums.join ', '})
2427
2436
  end
2428
2437
  subterm_indent = @theme.description_list_description_indent
2429
- layout_prose text, align: :left, margin: 0, hanging_indent: subterm_indent * 2
2438
+ inscribe_prose text, align: :left, margin: 0, hanging_indent: subterm_indent * 2
2430
2439
  indent subterm_indent do
2431
2440
  term.subterms.each do |subterm|
2432
2441
  convert_index_list_item subterm
@@ -2527,14 +2536,14 @@ module Asciidoctor
2527
2536
  end
2528
2537
 
2529
2538
  def convert_inline_icon node
2530
- if node.document.attr? 'icons', 'font'
2539
+ if (icons = (doc = node.document).attr 'icons') == 'font'
2531
2540
  if (icon_name = node.target).include? '@'
2532
2541
  icon_name, icon_set = icon_name.split '@', 2
2533
2542
  explicit_icon_set = true
2534
2543
  elsif (icon_set = node.attr 'set')
2535
2544
  explicit_icon_set = true
2536
2545
  else
2537
- icon_set = node.document.attr 'icon-set', 'fa'
2546
+ icon_set = doc.attr 'icon-set', 'fa'
2538
2547
  end
2539
2548
  if icon_set == 'fa' || !(IconSets.include? icon_set)
2540
2549
  icon_set = 'fa'
@@ -2580,6 +2589,14 @@ module Asciidoctor
2580
2589
  log :warn, %(#{icon_name} is not a valid icon name in the #{icon_set} icon set)
2581
2590
  %([#{node.attr 'alt'}&#93;)
2582
2591
  end
2592
+ elsif icons
2593
+ image_path = ::File.absolute_path %(#{icon_name = node.target}.#{image_format = doc.attr 'icontype', 'png'}), (doc.attr 'iconsdir')
2594
+ if ::File.readable? image_path
2595
+ %(<img src="#{image_path}" format="#{image_format}" alt="#{node.attr 'alt'}" fit="line">)
2596
+ else
2597
+ log :warn, %(image icon for '#{icon_name}' not found or not readable: #{image_path})
2598
+ %([#{icon_name}&#93;)
2599
+ end
2583
2600
  else
2584
2601
  %([#{node.attr 'alt'}&#93;)
2585
2602
  end
@@ -2616,15 +2633,16 @@ module Asciidoctor
2616
2633
  end
2617
2634
 
2618
2635
  def convert_inline_indexterm node
2636
+ visible = node.type == :visible
2619
2637
  if scratch?
2620
- node.type == :visible ? node.text : ''
2638
+ visible ? node.text : ''
2621
2639
  else
2622
2640
  # NOTE: initialize index in case converter is called before PDF is initialized
2623
2641
  @index ||= IndexCatalog.new
2624
2642
  # NOTE: page number (:page key) is added by InlineDestinationMarker
2625
2643
  dest = { anchor: (anchor_name = @index.next_anchor_name) }
2626
- anchor = %(<a id="#{anchor_name}" type="indexterm">#{DummyText}</a>)
2627
- if node.type == :visible
2644
+ anchor = %(<a id="#{anchor_name}" type="indexterm"#{visible ? ' visible="true"' : ''}>#{DummyText}</a>)
2645
+ if visible
2628
2646
  visible_term = node.text
2629
2647
  @index.store_primary_term (sanitize visible_term), dest
2630
2648
  %(#{anchor}#{visible_term})
@@ -2671,8 +2689,10 @@ module Asciidoctor
2671
2689
  open, close, is_tag = ['<sub>', '</sub>', true]
2672
2690
  when :double
2673
2691
  open, close, is_tag = [theme.quotes[0], theme.quotes[1], false]
2692
+ quotes = true
2674
2693
  when :single
2675
2694
  open, close, is_tag = [theme.quotes[2], theme.quotes[3], false]
2695
+ quotes = true
2676
2696
  when :mark
2677
2697
  open, close, is_tag = ['<mark>', '</mark>', true]
2678
2698
  else
@@ -2681,6 +2701,11 @@ module Asciidoctor
2681
2701
 
2682
2702
  inner_text = node.text
2683
2703
 
2704
+ if quotes && (len = inner_text.length) > 3 &&
2705
+ (inner_text.end_with? '...') && !((inner_text_trunc = inner_text.slice 0, len - 3).end_with? ?\\)
2706
+ inner_text = inner_text_trunc + '&#8230;'
2707
+ end
2708
+
2684
2709
  if (roles = node.role)
2685
2710
  roles.split.each do |role|
2686
2711
  if (text_transform = theme[%(role_#{role}_text_transform)])
@@ -2697,7 +2722,8 @@ module Asciidoctor
2697
2722
  node.id ? %(<a id="#{node.id}">#{DummyText}</a>#{quoted_text}) : quoted_text
2698
2723
  end
2699
2724
 
2700
- def layout_title_page doc
2725
+ # Returns a Boolean indicating whether the title page was created
2726
+ def inscribe_title_page doc
2701
2727
  return unless doc.header? && !doc.notitle && @theme.title_page != false
2702
2728
 
2703
2729
  # NOTE: a new page may have already been started at this point, so decide what to do with it
@@ -2724,7 +2750,7 @@ module Asciidoctor
2724
2750
  font @theme.base_font_family, size: @root_font_size, style: @theme.base_font_style
2725
2751
 
2726
2752
  # QUESTION: allow alignment per element on title page?
2727
- title_align = (@theme.title_page_align || @base_align).to_sym
2753
+ title_align = (@theme.title_page_text_align || @base_text_align).to_sym
2728
2754
 
2729
2755
  if @theme.title_page_logo_display != 'none' && (logo_image_path = (doc.attr 'title-logo-image') || (logo_image_from_theme = @theme.title_page_logo_image))
2730
2756
  if (logo_image_path.include? ':') && logo_image_path =~ ImageAttributeValueRx
@@ -2773,7 +2799,7 @@ module Asciidoctor
2773
2799
  move_down @theme.title_page_title_margin_top || 0
2774
2800
  indent (@theme.title_page_title_margin_left || 0), (@theme.title_page_title_margin_right || 0) do
2775
2801
  theme_font :title_page_title do
2776
- layout_prose doctitle.main, align: title_align, margin: 0
2802
+ inscribe_prose doctitle.main, align: title_align, margin: 0
2777
2803
  end
2778
2804
  end
2779
2805
  move_down @theme.title_page_title_margin_bottom || 0
@@ -2782,7 +2808,7 @@ module Asciidoctor
2782
2808
  move_down @theme.title_page_subtitle_margin_top || 0
2783
2809
  indent (@theme.title_page_subtitle_margin_left || 0), (@theme.title_page_subtitle_margin_right || 0) do
2784
2810
  theme_font :title_page_subtitle do
2785
- layout_prose subtitle, align: title_align, margin: 0
2811
+ inscribe_prose subtitle, align: title_align, margin: 0
2786
2812
  end
2787
2813
  end
2788
2814
  move_down @theme.title_page_subtitle_margin_bottom || 0
@@ -2807,7 +2833,7 @@ module Asciidoctor
2807
2833
  end
2808
2834
  end.join @theme.title_page_authors_delimiter
2809
2835
  theme_font :title_page_authors do
2810
- layout_prose authors, align: title_align, margin: 0, normalize: true
2836
+ inscribe_prose authors, align: title_align, margin: 0, normalize: true
2811
2837
  end
2812
2838
  end
2813
2839
  move_down @theme.title_page_authors_margin_bottom || 0
@@ -2820,17 +2846,18 @@ module Asciidoctor
2820
2846
  end
2821
2847
  indent (@theme.title_page_revision_margin_left || 0), (@theme.title_page_revision_margin_right || 0) do
2822
2848
  theme_font :title_page_revision do
2823
- layout_prose revision_text, align: title_align, margin: 0, normalize: false
2849
+ inscribe_prose revision_text, align: title_align, margin: 0, normalize: false
2824
2850
  end
2825
2851
  end
2826
2852
  move_down @theme.title_page_revision_margin_bottom || 0
2827
2853
  end
2828
2854
  end
2829
2855
 
2830
- layout_prose DummyText, margin: 0, line_height: 1, normalize: false if page.empty?
2856
+ inscribe_prose DummyText, margin: 0, line_height: 1, normalize: false if page.empty?
2857
+ true
2831
2858
  end
2832
2859
 
2833
- def layout_cover_page doc, face
2860
+ def inscribe_cover_page doc, face
2834
2861
  image_path, image_opts = resolve_background_image doc, @theme, %(#{face}-cover-image), theme_key: %(cover_#{face}_image).to_sym, symbolic_paths: ['', '~']
2835
2862
  if image_path
2836
2863
  if image_path.empty?
@@ -2876,37 +2903,58 @@ module Asciidoctor
2876
2903
 
2877
2904
  def start_new_chapter chapter
2878
2905
  start_new_page unless at_page_top?
2879
- # TODO: must call update_colors before advancing to next page if start_new_page is called in layout_chapter_title
2906
+ # TODO: must call update_colors before advancing to next page if start_new_page is called in inscribe_chapter_title
2880
2907
  start_new_page if @ppbook && verso_page? && !(chapter.option? 'nonfacing')
2881
2908
  end
2882
2909
 
2883
2910
  alias start_new_part start_new_chapter
2884
2911
 
2885
2912
  def arrange_section sect, title, opts
2886
- theme_font :heading, level: (hlevel = opts[:level]) do
2887
- # FIXME: this height doesn't account for impact of text transform or inline formatting
2888
- heading_height =
2889
- (height_of_typeset_text title) +
2890
- (@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
2891
- (@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom)
2892
- heading_height += @theme.heading_min_height_after if sect.blocks? && @theme.heading_min_height_after
2893
- start_new_page unless cursor > heading_height
2894
- nil
2913
+ if sect.option? 'breakable'
2914
+ orphaned = nil
2915
+ dry_run single_page: true do
2916
+ start_page = page
2917
+ theme_font :heading, level: opts[:level] do
2918
+ if opts[:part]
2919
+ inscribe_part_title sect, title, opts
2920
+ elsif opts[:chapterlike]
2921
+ inscribe_chapter_title sect, title, opts
2922
+ else
2923
+ inscribe_general_heading sect, title, opts
2924
+ end
2925
+ end
2926
+ if page == start_page
2927
+ page.tare_content_stream
2928
+ orphaned = stop_if_first_page_empty { traverse sect }
2929
+ end
2930
+ end
2931
+ start_new_page if orphaned
2932
+ else
2933
+ theme_font :heading, level: (hlevel = opts[:level]) do
2934
+ # FIXME: this height doesn't account for impact of text transform or inline formatting
2935
+ heading_height =
2936
+ (height_of_typeset_text title) +
2937
+ (@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
2938
+ (@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom)
2939
+ heading_height += @theme.heading_min_height_after if sect.blocks? && @theme.heading_min_height_after
2940
+ start_new_page unless cursor > heading_height
2941
+ end
2895
2942
  end
2943
+ nil
2896
2944
  end
2897
2945
 
2898
- def layout_chapter_title node, title, opts = {}
2899
- layout_general_heading node, title, (opts.merge outdent: true)
2946
+ def inscribe_chapter_title node, title, opts = {}
2947
+ inscribe_general_heading node, title, (opts.merge outdent: true)
2900
2948
  end
2901
2949
 
2902
- alias layout_part_title layout_chapter_title
2950
+ alias inscribe_part_title inscribe_chapter_title
2903
2951
 
2904
- def layout_general_heading _node, title, opts = {}
2905
- layout_heading title, opts
2952
+ def inscribe_general_heading _node, title, opts = {}
2953
+ inscribe_heading title, opts
2906
2954
  end
2907
2955
 
2908
- # NOTE: layout_heading doesn't set the theme font because it's used for various types of headings
2909
- def layout_heading string, opts = {}
2956
+ # NOTE: inscribe_heading doesn't set the theme font because it's used for various types of headings
2957
+ def inscribe_heading string, opts = {}
2910
2958
  hlevel = opts[:level]
2911
2959
  unless (top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top))
2912
2960
  if at_page_top?
@@ -2933,14 +2981,14 @@ module Asciidoctor
2933
2981
  typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
2934
2982
  color: @font_color,
2935
2983
  inline_format: inline_format_opts,
2936
- align: @base_align.to_sym,
2984
+ align: @base_text_align.to_sym,
2937
2985
  }.merge(opts)
2938
2986
  margin_bottom bot_margin
2939
2987
  end
2940
2988
  end
2941
2989
 
2942
2990
  # NOTE: inline_format is true by default
2943
- def layout_prose string, opts = {}
2991
+ def inscribe_prose string, opts = {}
2944
2992
  top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top) || 0
2945
2993
  bot_margin = margin || (opts.delete :margin_bottom) || @theme.prose_margin_bottom
2946
2994
  if (transform = resolve_text_transform opts)
@@ -2964,7 +3012,7 @@ module Asciidoctor
2964
3012
  typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
2965
3013
  color: @font_color,
2966
3014
  inline_format: [inline_format_opts],
2967
- align: @base_align.to_sym,
3015
+ align: @base_text_align.to_sym,
2968
3016
  }.merge(opts)
2969
3017
  margin_bottom bot_margin
2970
3018
  end
@@ -2987,13 +3035,13 @@ module Asciidoctor
2987
3035
  # The subject argument can either be a String or an AbstractNode. If
2988
3036
  # subject is an AbstractNode, only call this method if the node has a
2989
3037
  # title (i.e., subject.title? returns true).
2990
- def layout_caption subject, opts = {}
3038
+ def inscribe_caption subject, opts = {}
2991
3039
  if opts.delete :dry_run
2992
3040
  force_top_margin = !at_page_top? if (force_top_margin = opts.delete :force_top_margin).nil?
2993
3041
  return (dry_run keep_together: true, single_page: :enforce do
2994
3042
  # TODO: encapsulate this logic to force top margin to be applied
2995
3043
  margin_box.instance_variable_set :@y, margin_box.absolute_top + 0.0001 if force_top_margin
2996
- layout_caption subject, opts
3044
+ inscribe_caption subject, opts
2997
3045
  end).single_page_height
2998
3046
  end
2999
3047
  if ::Asciidoctor::AbstractBlock === subject
@@ -3001,21 +3049,23 @@ module Asciidoctor
3001
3049
  else
3002
3050
  string = subject.to_s
3003
3051
  end
3052
+ block_align = opts.delete :block_align
3053
+ block_width = opts.delete :block_width
3004
3054
  category_caption = (category = opts[:category]) ? %(#{category}_caption) : 'caption'
3055
+ caption_margin_outside = @theme[%(#{category_caption}_margin_outside)] || @theme.caption_margin_outside
3056
+ caption_margin_inside = @theme[%(#{category_caption}_margin_inside)] || @theme.caption_margin_inside
3005
3057
  container_width = bounds.width
3006
- block_align = opts.delete :block_align
3058
+ indent_by = [0, 0]
3007
3059
  if (align = @theme[%(#{category_caption}_align)] || @theme.caption_align)
3008
- align = align == 'inherit' ? (block_align || @base_align.to_sym) : align.to_sym
3060
+ align = align == 'inherit' ? (block_align || @base_text_align.to_sym) : align.to_sym
3009
3061
  else
3010
- align = @base_align.to_sym
3062
+ align = @base_text_align.to_sym
3011
3063
  end
3012
3064
  if (text_align = @theme[%(#{category_caption}_text_align)] || @theme.caption_text_align)
3013
3065
  text_align = text_align == 'inherit' ? align : text_align.to_sym
3014
3066
  else
3015
3067
  text_align = align
3016
3068
  end
3017
- indent_by = [0, 0]
3018
- block_width = opts.delete :block_width
3019
3069
  if (max_width = opts.delete :max_width) && max_width != 'none'
3020
3070
  if ::String === max_width
3021
3071
  if max_width.start_with? 'fit-content'
@@ -3047,8 +3097,6 @@ module Asciidoctor
3047
3097
  end
3048
3098
  end
3049
3099
  theme_font_cascade [:caption, category_caption] do
3050
- caption_margin_outside = @theme[%(#{category_caption}_margin_outside)] || @theme.caption_margin_outside
3051
- caption_margin_inside = @theme[%(#{category_caption}_margin_inside)] || @theme.caption_margin_inside
3052
3100
  if ((opts.delete :side) || :top) == :top
3053
3101
  margin = { top: caption_margin_outside, bottom: caption_margin_inside }
3054
3102
  else
@@ -3057,8 +3105,13 @@ module Asciidoctor
3057
3105
  unless (inherited = apply_text_decoration [], :caption).empty?
3058
3106
  opts = opts.merge inherited
3059
3107
  end
3108
+ unless scratch? || !(bg_color = @theme[%(#{category_caption}_background_color)] || @theme.caption_background_color)
3109
+ caption_height = height_of_typeset_text string
3110
+ fill_at = [0, cursor + (margin[:top] || 0)]
3111
+ float { bounding_box(fill_at, width: container_width, height: caption_height) { fill_bounds bg_color } }
3112
+ end
3060
3113
  indent(*indent_by) do
3061
- layout_prose string, ({
3114
+ inscribe_prose string, ({
3062
3115
  margin_top: margin[:top],
3063
3116
  margin_bottom: margin[:bottom],
3064
3117
  align: text_align,
@@ -3072,18 +3125,18 @@ module Asciidoctor
3072
3125
  end
3073
3126
 
3074
3127
  # Render the caption for a table and return the height of the rendered content
3075
- def layout_table_caption node, table_alignment = :left, table_width = nil, max_width = nil, side = :top
3076
- layout_caption node, category: :table, side: side, block_align: table_alignment, block_width: table_width, max_width: max_width
3128
+ def inscribe_table_caption node, table_alignment = :left, table_width = nil, max_width = nil, side = :top
3129
+ inscribe_caption node, category: :table, side: side, block_align: table_alignment, block_width: table_width, max_width: max_width
3077
3130
  end
3078
3131
 
3079
- def allocate_toc doc, toc_num_levels, toc_start_cursor, use_title_page
3132
+ def allocate_toc doc, toc_num_levels, toc_start_cursor, title_page_on
3080
3133
  toc_start_page = page_number
3081
3134
  extent = dry_run onto: self do
3082
- layout_toc doc, toc_num_levels, toc_start_page, toc_start_cursor
3083
- margin_bottom @theme.block_margin_bottom unless use_title_page
3135
+ inscribe_toc doc, toc_num_levels, toc_start_page, toc_start_cursor
3136
+ margin_bottom @theme.block_margin_bottom unless title_page_on
3084
3137
  end
3085
3138
  # NOTE: reserve pages for the toc; leaves cursor on page after last page in toc
3086
- if use_title_page
3139
+ if title_page_on
3087
3140
  extent.each_page { start_new_page }
3088
3141
  else
3089
3142
  extent.each_page {|first_page| start_new_page unless first_page }
@@ -3093,14 +3146,14 @@ module Asciidoctor
3093
3146
  end
3094
3147
 
3095
3148
  # NOTE: num_front_matter_pages not used during a dry run
3096
- def layout_toc doc, num_levels, toc_page_number, start_cursor, num_front_matter_pages = 0
3149
+ def inscribe_toc doc, num_levels, toc_page_number, start_cursor, num_front_matter_pages = 0
3097
3150
  go_to_page toc_page_number unless (page_number == toc_page_number) || scratch?
3098
3151
  start_page_number = page_number
3099
3152
  move_cursor_to start_cursor
3100
3153
  unless (toc_title = doc.attr 'toc-title').nil_or_empty?
3101
3154
  theme_font_cascade [[:heading, level: 2], :toc_title] do
3102
- toc_title_align = (@theme.toc_title_align || @theme.heading_h2_align || @theme.heading_align || @base_align).to_sym
3103
- layout_general_heading doc, toc_title, align: toc_title_align, level: 2, outdent: true, role: :toctitle
3155
+ toc_title_align = (@theme.toc_title_text_align || @theme.heading_h2_text_align || @theme.heading_text_align || @base_text_align).to_sym
3156
+ inscribe_general_heading doc, toc_title, align: toc_title_align, level: 2, outdent: true, role: :toctitle
3104
3157
  end
3105
3158
  end
3106
3159
  # QUESTION: should we skip this whole method if num_levels < 0?
@@ -3125,7 +3178,7 @@ module Asciidoctor
3125
3178
  }
3126
3179
  end
3127
3180
  theme_margin :toc, :top
3128
- layout_toc_level doc.sections, num_levels, dot_leader, num_front_matter_pages
3181
+ inscribe_toc_level doc.sections, num_levels, dot_leader, num_front_matter_pages
3129
3182
  end
3130
3183
  # NOTE: range must be calculated relative to toc_page_number; absolute page number in scratch document is arbitrary
3131
3184
  toc_page_numbers = (toc_page_number..(toc_page_number + (page_number - start_page_number)))
@@ -3133,7 +3186,7 @@ module Asciidoctor
3133
3186
  toc_page_numbers
3134
3187
  end
3135
3188
 
3136
- def layout_toc_level sections, num_levels, dot_leader, num_front_matter_pages
3189
+ def inscribe_toc_level sections, num_levels, dot_leader, num_front_matter_pages
3137
3190
  # NOTE: font options aren't always reliable, so store size separately
3138
3191
  toc_font_info = theme_font :toc do
3139
3192
  { font: font, size: @font_size }
@@ -3149,7 +3202,7 @@ module Asciidoctor
3149
3202
  if scratch?
3150
3203
  indent 0, pgnum_label_placeholder_width do
3151
3204
  # NOTE: must wrap title in empty anchor element in case links are styled with different font family / size
3152
- layout_prose sect_title, anchor: true, normalize: false, hanging_indent: hanging_indent, normalize_line_height: true, margin: 0
3205
+ inscribe_prose sect_title, anchor: true, normalize: false, hanging_indent: hanging_indent, normalize_line_height: true, margin: 0
3153
3206
  end
3154
3207
  else
3155
3208
  physical_pgnum = sect.attr 'pdf-page-start'
@@ -3194,7 +3247,7 @@ module Asciidoctor
3194
3247
  end
3195
3248
  end
3196
3249
  indent @theme.toc_indent do
3197
- layout_toc_level sect.sections, num_levels_for_sect, dot_leader, num_front_matter_pages
3250
+ inscribe_toc_level sect.sections, num_levels_for_sect, dot_leader, num_front_matter_pages
3198
3251
  end if num_levels_for_sect > sect.level
3199
3252
  end
3200
3253
  end
@@ -3222,8 +3275,8 @@ module Asciidoctor
3222
3275
  icon_data
3223
3276
  end
3224
3277
 
3225
- # TODO: delegate to layout_page_header and layout_page_footer per page
3226
- def layout_running_content periphery, doc, skip = [1, 1], body_start_page_number = 1
3278
+ # TODO: delegate to inscribe_page_header and inscribe_page_footer per page
3279
+ def inscribe_running_content periphery, doc, skip = [1, 1], body_start_page_number = 1
3227
3280
  skip_pages, skip_pagenums = skip
3228
3281
  # NOTE: find and advance to first non-imported content page to use as model page
3229
3282
  return unless (content_start_page = state.pages[skip_pages..-1].index {|it| !it.imported_page? })
@@ -3790,7 +3843,7 @@ module Asciidoctor
3790
3843
  def theme_fill_and_stroke_block category, extent, opts = {}
3791
3844
  node_with_caption = nil unless (node_with_caption = opts[:caption_node])&.title?
3792
3845
  unless extent
3793
- layout_caption node_with_caption, category: category if node_with_caption
3846
+ inscribe_caption node_with_caption, category: category if node_with_caption
3794
3847
  return
3795
3848
  end
3796
3849
  if (b_width = (opts.key? :border_width) ? opts[:border_width] : @theme[%(#{category}_border_width)])
@@ -3804,7 +3857,7 @@ module Asciidoctor
3804
3857
  bg_color = nil
3805
3858
  end
3806
3859
  unless b_width || bg_color
3807
- layout_caption node_with_caption, category: category if node_with_caption
3860
+ inscribe_caption node_with_caption, category: category if node_with_caption
3808
3861
  return
3809
3862
  end
3810
3863
  if (b_color = @theme[%(#{category}_border_color)]) == 'transparent'
@@ -3822,7 +3875,7 @@ module Asciidoctor
3822
3875
  else # let page background cut into block background; guarantees b_width is set
3823
3876
  b_shift, b_gap_color = (b_width ||= 0.5) * 0.5, @page_bg_color
3824
3877
  end
3825
- layout_caption node_with_caption, category: category if node_with_caption
3878
+ inscribe_caption node_with_caption, category: category if node_with_caption
3826
3879
  extent.from.page += 1 unless extent.from.page == page_number # sanity check
3827
3880
  float do
3828
3881
  extent.each_page do |first_page, last_page|
@@ -4206,7 +4259,7 @@ module Asciidoctor
4206
4259
  (align_role.slice 5, align_role.length).to_sym
4207
4260
  elsif use_theme
4208
4261
  roles.reverse.each do |role|
4209
- if (align = @theme[%(role_#{role}_align)])
4262
+ if (align = @theme[%(role_#{role}_text_align)])
4210
4263
  return align.to_sym
4211
4264
  end
4212
4265
  end
@@ -4542,6 +4595,27 @@ module Asciidoctor
4542
4595
  end
4543
4596
  end
4544
4597
 
4598
+ # Deprecated method names
4599
+ alias layout_footnotes inscribe_footnotes
4600
+ alias layout_title_page inscribe_title_page
4601
+ alias layout_cover_page inscribe_cover_page
4602
+ alias layout_chapter_title inscribe_chapter_title
4603
+ alias layout_part_title inscribe_part_title
4604
+ alias layout_general_heading inscribe_general_heading
4605
+ alias layout_heading inscribe_heading
4606
+ alias layout_prose inscribe_prose
4607
+ alias layout_caption inscribe_caption
4608
+ alias layout_table_caption inscribe_table_caption
4609
+ alias layout_toc inscribe_toc
4610
+ alias layout_toc_level inscribe_toc_level
4611
+ alias layout_running_content inscribe_running_content
4612
+
4613
+ def self.method_added method
4614
+ if (method_name = method.to_s).start_with? 'layout_'
4615
+ alias_method %(inscribe_#{method_name.slice 7, method_name.length}).to_sym, method
4616
+ end
4617
+ end
4618
+
4545
4619
  private
4546
4620
 
4547
4621
  def add_link_to_image uri, image_info, image_opts