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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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