asciidoctor-pdf 2.0.0.beta.1 → 2.0.0.beta.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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +128 -82
- data/README.adoc +6 -119
- data/data/themes/base-theme.yml +1 -1
- data/data/themes/default-for-print-theme.yml +4 -4
- data/data/themes/default-for-print-with-fallback-font-theme.yml +2 -3
- data/data/themes/default-for-print-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/{sans-with-fallback-font-theme.yml → default-sans-theme.yml} +1 -1
- data/data/themes/default-sans-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/default-theme.yml +3 -2
- data/data/themes/default-with-fallback-font-theme.yml +2 -9
- data/data/themes/default-with-font-fallbacks-theme.yml +9 -0
- data/docs/theming-guide.adoc +5 -6051
- data/lib/asciidoctor/pdf/converter.rb +237 -92
- data/lib/asciidoctor/pdf/ext/prawn/document/column_box.rb +16 -0
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +50 -23
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +14 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/indented_paragraph_wrap.rb +39 -0
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +2 -9
- data/lib/asciidoctor/pdf/ext/prawn.rb +2 -0
- data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +7 -2
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +8 -3
@@ -52,6 +52,7 @@ module Asciidoctor
|
|
52
52
|
TextAlignmentRoles = %w(text-justify text-left text-center text-right)
|
53
53
|
TextDecorationStyleTable = { 'underline' => :underline, 'line-through' => :strikethrough }
|
54
54
|
FontKerningTable = { 'normal' => true, 'none' => false }
|
55
|
+
BlockFloatNames = %w(left right)
|
55
56
|
BlockAlignmentNames = %w(left center right)
|
56
57
|
(AlignmentTable = { '<' => :left, '=' => :center, '>' => :right }).default = :left
|
57
58
|
ColumnPositions = [:left, :center, :right]
|
@@ -486,8 +487,9 @@ module Asciidoctor
|
|
486
487
|
theme.code_linenum_font_color ||= '999999'
|
487
488
|
theme.callout_list_margin_top_after_code ||= 0
|
488
489
|
theme.role_unresolved_font_color ||= 'FF0000'
|
489
|
-
theme.index_columns ||= 2
|
490
490
|
theme.footnotes_item_spacing ||= 0
|
491
|
+
theme.index_columns ||= 2
|
492
|
+
theme.index_column_gap ||= theme.base_font_size
|
491
493
|
theme.kbd_separator ||= '+'
|
492
494
|
theme.title_page_authors_delimiter ||= ', '
|
493
495
|
theme.title_page_revision_delimiter ||= ', '
|
@@ -665,24 +667,24 @@ module Asciidoctor
|
|
665
667
|
title, _, subtitle = title.rpartition sep
|
666
668
|
title = %(#{title}\n<em class="subtitle">#{subtitle}</em>)
|
667
669
|
end
|
668
|
-
hlevel = sect.level
|
670
|
+
hlevel = sect.level.next
|
669
671
|
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
670
672
|
chapterlike = !(part = sectname == 'part') && (sectname == 'chapter' || (sect.document.doctype == 'book' && sect.level == 1))
|
671
673
|
hidden = sect.option? 'notitle'
|
672
674
|
hopts = { align: align, level: hlevel, part: part, chapterlike: chapterlike, outdent: !(part || chapterlike) }
|
673
675
|
if part
|
674
676
|
unless @theme.heading_part_break_before == 'auto'
|
675
|
-
|
677
|
+
started_new = true
|
676
678
|
start_new_part sect
|
677
679
|
end
|
678
680
|
elsif chapterlike
|
679
681
|
if @theme.heading_chapter_break_before != 'auto' ||
|
680
682
|
(@theme.heading_part_break_after == 'always' && sect == sect.parent.sections[0])
|
681
|
-
|
683
|
+
started_new = true
|
682
684
|
start_new_chapter sect
|
683
685
|
end
|
684
686
|
end
|
685
|
-
|
687
|
+
arrange_heading sect, title, hopts unless hidden || started_new || at_page_top? || !sect.blocks?
|
686
688
|
# QUESTION: should we store pdf-page-start, pdf-anchor & pdf-destination in internal map?
|
687
689
|
sect.set_attr 'pdf-page-start', (start_pgnum = page_number)
|
688
690
|
# QUESTION: should we just assign the section this generated id?
|
@@ -753,14 +755,17 @@ module Asciidoctor
|
|
753
755
|
end
|
754
756
|
|
755
757
|
def convert_floating_title node
|
756
|
-
|
758
|
+
title = node.title
|
757
759
|
hlevel = node.level.next
|
758
|
-
unless (align =
|
760
|
+
unless (align = resolve_text_align_from_role node.roles)
|
759
761
|
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
760
762
|
end
|
763
|
+
hopts = { align: align, level: hlevel, outdent: (parent = node.parent).context == :section }
|
764
|
+
arrange_heading node, title, hopts unless at_page_top? || node == parent.blocks[-1]
|
765
|
+
add_dest_for_block node if node.id
|
761
766
|
# QUESTION: should we decouple styles from section titles?
|
762
767
|
theme_font :heading, level: hlevel do
|
763
|
-
ink_general_heading node,
|
768
|
+
ink_general_heading node, title, hopts
|
764
769
|
end
|
765
770
|
end
|
766
771
|
|
@@ -778,11 +783,21 @@ module Asciidoctor
|
|
778
783
|
end
|
779
784
|
# FIXME: allow theme to control more first line options
|
780
785
|
if (line1_font_style = @theme.abstract_first_line_font_style&.to_sym) && line1_font_style != font_style
|
781
|
-
|
786
|
+
case line1_font_style
|
787
|
+
when :normal
|
788
|
+
first_line_options = { styles: [] }
|
789
|
+
when :normal_italic
|
790
|
+
first_line_options = { styles: [:italic] }
|
791
|
+
else
|
792
|
+
first_line_options = { styles: [font_style, line1_font_style] }
|
793
|
+
end
|
782
794
|
end
|
783
795
|
if (line1_font_color = @theme.abstract_first_line_font_color)
|
784
796
|
(first_line_options ||= {})[:color] = line1_font_color
|
785
797
|
end
|
798
|
+
if (line1_text_transform = @theme.abstract_first_line_text_transform)
|
799
|
+
(first_line_options ||= {})[:text_transform] = line1_text_transform
|
800
|
+
end
|
786
801
|
prose_opts[:first_line_options] = first_line_options if first_line_options
|
787
802
|
# FIXME: make this cleaner!!
|
788
803
|
if node.blocks?
|
@@ -791,7 +806,7 @@ module Asciidoctor
|
|
791
806
|
if child.context == :paragraph
|
792
807
|
child.document.playback_attributes child.attributes
|
793
808
|
prose_opts[:margin_bottom] = 0 if child == last_block
|
794
|
-
ink_prose child.content, ((align =
|
809
|
+
ink_prose child.content, ((align = resolve_text_align_from_role child.roles) ? (prose_opts.merge align: align) : prose_opts.dup)
|
795
810
|
prose_opts.delete :first_line_options
|
796
811
|
prose_opts.delete :margin_bottom
|
797
812
|
else
|
@@ -800,7 +815,7 @@ module Asciidoctor
|
|
800
815
|
end
|
801
816
|
end
|
802
817
|
elsif node.content_model != :compound && (string = node.content)
|
803
|
-
if (align =
|
818
|
+
if (align = resolve_text_align_from_role node.roles)
|
804
819
|
prose_opts[:align] = align
|
805
820
|
end
|
806
821
|
ink_prose string, (prose_opts.merge margin_bottom: 0)
|
@@ -824,39 +839,39 @@ module Asciidoctor
|
|
824
839
|
|
825
840
|
def convert_paragraph node
|
826
841
|
add_dest_for_block node if node.id
|
842
|
+
|
827
843
|
prose_opts = { margin_bottom: 0, hyphenate: true }
|
828
|
-
if (align =
|
844
|
+
if (align = resolve_text_align_from_role (roles = node.roles), query_theme: true, remove_predefined: true)
|
829
845
|
prose_opts[:align] = align
|
830
|
-
roles -= TextAlignmentRoles
|
831
846
|
end
|
832
|
-
|
847
|
+
role_keys = roles.map {|role| %(role_#{role}).to_sym } unless roles.empty?
|
833
848
|
if (text_indent = @theme.prose_text_indent) > 0 ||
|
834
849
|
((text_indent = @theme.prose_text_indent_inner) > 0 &&
|
835
850
|
(self_idx = (siblings = node.parent.blocks).index node) > 0 && siblings[self_idx - 1].context == :paragraph)
|
836
851
|
prose_opts[:indent_paragraphs] = text_indent
|
837
852
|
end
|
838
|
-
|
839
|
-
# TODO: check if we're within one line of the bottom of the page
|
840
|
-
# and advance to the next page if so (similar to logic for section titles)
|
841
|
-
ink_caption node, labeled: false if node.title?
|
842
|
-
|
843
853
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
844
854
|
prose_opts[:bottom_gutter] = bottom_gutter
|
845
855
|
end
|
846
856
|
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
857
|
+
block_next = next_enclosed_block node
|
858
|
+
|
859
|
+
insert_margin_bottom = proc do
|
860
|
+
if (margin_inner_val = @theme.prose_margin_inner) && block_next&.context == :paragraph
|
861
|
+
margin_bottom margin_inner_val
|
862
|
+
else
|
863
|
+
theme_margin :prose, :bottom, block_next
|
852
864
|
end
|
853
865
|
end
|
854
866
|
|
855
|
-
|
856
|
-
|
857
|
-
margin_bottom margin_inner_val
|
867
|
+
if (float_box = (@float_box ||= nil))
|
868
|
+
ink_paragraph_in_float_box node, float_box, prose_opts, role_keys, block_next, insert_margin_bottom
|
858
869
|
else
|
859
|
-
|
870
|
+
# TODO: check if we're within one line of the bottom of the page
|
871
|
+
# and advance to the next page if so (similar to logic for section titles)
|
872
|
+
ink_caption node, labeled: false if node.title?
|
873
|
+
role_keys ? theme_font_cascade(role_keys) { ink_prose node.content, prose_opts } : (ink_prose node.content, prose_opts)
|
874
|
+
insert_margin_bottom.call
|
860
875
|
end
|
861
876
|
end
|
862
877
|
|
@@ -1122,9 +1137,11 @@ module Asciidoctor
|
|
1122
1137
|
add_dest_for_block node if node.id
|
1123
1138
|
theme_fill_and_stroke_block :sidebar, extent if extent
|
1124
1139
|
pad_box @theme.sidebar_padding, node do
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1140
|
+
tare_first_page_content_stream do
|
1141
|
+
theme_font :sidebar_title do
|
1142
|
+
# QUESTION: should we allow margins of sidebar title to be customized?
|
1143
|
+
ink_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)
|
1144
|
+
end
|
1128
1145
|
end if node.title?
|
1129
1146
|
theme_font :sidebar do
|
1130
1147
|
traverse node
|
@@ -1135,13 +1152,15 @@ module Asciidoctor
|
|
1135
1152
|
end
|
1136
1153
|
|
1137
1154
|
def convert_colist node
|
1138
|
-
|
1155
|
+
unless at_page_top? || (self_idx = (siblings = node.parent.blocks).index node) == 0 || !([:listing, :literal].include? siblings[self_idx - 1].context)
|
1156
|
+
margin_top @theme.callout_list_margin_top_after_code
|
1157
|
+
end
|
1139
1158
|
add_dest_for_block node if node.id
|
1140
1159
|
@list_numerals << 1
|
1141
1160
|
last_item = node.items[-1]
|
1142
1161
|
item_spacing = @theme.callout_list_item_spacing || @theme.list_item_spacing
|
1143
1162
|
item_opts = { margin_bottom: item_spacing, normalize_line_height: true }
|
1144
|
-
if (item_align = (
|
1163
|
+
if (item_align = (resolve_text_align_from_role node.roles) || @theme.list_text_align&.to_sym)
|
1145
1164
|
item_opts[:align] = item_align
|
1146
1165
|
end
|
1147
1166
|
theme_font :callout_list do
|
@@ -1360,7 +1379,7 @@ module Asciidoctor
|
|
1360
1379
|
ink_caption node, category: :list, labeled: false if node.title?
|
1361
1380
|
|
1362
1381
|
opts = {}
|
1363
|
-
if (align =
|
1382
|
+
if (align = resolve_text_align_from_role node.roles)
|
1364
1383
|
opts[:align] = align
|
1365
1384
|
elsif node.style == 'bibliography'
|
1366
1385
|
opts[:align] = :left
|
@@ -1553,9 +1572,13 @@ module Asciidoctor
|
|
1553
1572
|
|
1554
1573
|
return on_image_error :missing, node, target, opts unless image_path
|
1555
1574
|
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1575
|
+
if (float_to = node.attr 'float') && ((BlockFloatNames.include? float_to) ? float_to : (float_to = nil))
|
1576
|
+
alignment = float_to.to_sym
|
1577
|
+
elsif (alignment = node.attr 'align')
|
1578
|
+
alignment = (BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left
|
1579
|
+
else
|
1580
|
+
alignment = (resolve_text_align_from_role node.roles) || @theme.image_align&.to_sym || :left
|
1581
|
+
end
|
1559
1582
|
# TODO: support cover (aka canvas) image layout using "canvas" (or "cover") role
|
1560
1583
|
width = resolve_explicit_width node.attributes, bounds_width: (available_w = bounds.width), support_vw: true, use_fallback: true, constrain_to_bounds: true
|
1561
1584
|
# TODO: add `to_pt page_width` method to ViewportWidth type
|
@@ -1563,6 +1586,7 @@ module Asciidoctor
|
|
1563
1586
|
|
1564
1587
|
caption_end = @theme.image_caption_end&.to_sym || :bottom
|
1565
1588
|
caption_max_width = @theme.image_caption_max_width
|
1589
|
+
caption_max_width = 'fit-content' if float_to && !(caption_max_width&.start_with? 'fit-content')
|
1566
1590
|
# NOTE: if width is not set explicitly and max-width is fit-content, caption height may not be accurate
|
1567
1591
|
caption_h = node.title? ? (ink_caption node, category: :image, end: caption_end, block_align: alignment, block_width: width, max_width: caption_max_width, dry_run: true, force_top_margin: caption_end == :bottom) : 0
|
1568
1592
|
|
@@ -1570,7 +1594,7 @@ module Asciidoctor
|
|
1570
1594
|
pinned = opts[:pinned]
|
1571
1595
|
|
1572
1596
|
begin
|
1573
|
-
rendered_w = nil
|
1597
|
+
rendered_h = rendered_w = nil
|
1574
1598
|
span_page_width_if align_to_page do
|
1575
1599
|
if image_format == 'svg'
|
1576
1600
|
if ::Base64 === image_path
|
@@ -1649,13 +1673,37 @@ module Asciidoctor
|
|
1649
1673
|
end
|
1650
1674
|
end
|
1651
1675
|
ink_caption node, category: :image, end: :bottom, block_align: alignment, block_width: rendered_w, max_width: caption_max_width if caption_end == :bottom && node.title?
|
1652
|
-
|
1676
|
+
if !pinned && (block_next = next_enclosed_block node)
|
1677
|
+
if float_to && (supports_float_wrapping? block_next) && rendered_w < bounds.width
|
1678
|
+
init_float_box node, rendered_w, rendered_h + caption_h, float_to
|
1679
|
+
else
|
1680
|
+
theme_margin :block, :bottom, block_next
|
1681
|
+
end
|
1682
|
+
end
|
1653
1683
|
rescue => e
|
1654
1684
|
raise if ::StopIteration === e
|
1655
1685
|
on_image_error :exception, node, target, (opts.merge message: %(could not embed image: #{image_path}; #{e.message}#{::Prawn::Errors::UnsupportedImageType === e && !(defined? ::GMagick::Image) ? '; install prawn-gmagick gem to add support' : ''}))
|
1656
1686
|
end
|
1657
1687
|
end
|
1658
1688
|
|
1689
|
+
def supports_float_wrapping? node
|
1690
|
+
node.context == :paragraph
|
1691
|
+
end
|
1692
|
+
|
1693
|
+
def init_float_box _node, block_width, block_height, float_to
|
1694
|
+
gap = ::Array === (gap = @theme.image_float_gap) ? gap.dup : [gap, gap]
|
1695
|
+
float_w = block_width + (gap[0] ||= 12)
|
1696
|
+
float_h = block_height + (gap[1] ||= 6)
|
1697
|
+
box_l = bounds.left + (float_to == 'right' ? 0 : float_w)
|
1698
|
+
box_t = cursor + block_height
|
1699
|
+
box_w = bounds.width - float_w
|
1700
|
+
box_r = box_l + box_w
|
1701
|
+
box_h = [box_t, float_h].min
|
1702
|
+
box_b = box_t - box_h
|
1703
|
+
move_cursor_to box_t
|
1704
|
+
@float_box = { page: page_number, top: box_t, right: box_r, bottom: box_b, left: box_l, width: box_w, height: box_h, gap: gap }
|
1705
|
+
end
|
1706
|
+
|
1659
1707
|
def draw_image_border top, w, h, alignment
|
1660
1708
|
if (Array @theme.image_border_width).any? {|it| it&.> 0 } && (@theme.image_border_color || @theme.base_border_color)
|
1661
1709
|
if (@theme.image_border_fit || 'content') == 'auto'
|
@@ -1687,7 +1735,7 @@ module Asciidoctor
|
|
1687
1735
|
theme_font :image_alt do
|
1688
1736
|
alignment = (alignment = node.attr 'align') ?
|
1689
1737
|
((BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left) :
|
1690
|
-
(
|
1738
|
+
(resolve_text_align_from_role node.roles) || (@theme.image_align&.to_sym || :left)
|
1691
1739
|
ink_prose alt_text_template % alt_text_vars, align: alignment, margin: 0, normalize: false, single_line: true
|
1692
1740
|
end
|
1693
1741
|
ink_caption node, category: :image, end: :bottom if node.title?
|
@@ -1742,7 +1790,7 @@ module Asciidoctor
|
|
1742
1790
|
end
|
1743
1791
|
|
1744
1792
|
# QUESTION: can we avoid arranging fragments multiple times (conums & autofit) by eagerly preparing arranger?
|
1745
|
-
def
|
1793
|
+
def convert_code node
|
1746
1794
|
extensions = []
|
1747
1795
|
source_chunks = bg_color_override = font_color_override = adjusted_font_size = nil
|
1748
1796
|
theme_font :code do
|
@@ -1889,17 +1937,31 @@ module Asciidoctor
|
|
1889
1937
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
1890
1938
|
end
|
1891
1939
|
|
1892
|
-
alias convert_listing
|
1893
|
-
alias convert_literal
|
1940
|
+
alias convert_listing convert_code
|
1941
|
+
alias convert_literal convert_code
|
1942
|
+
alias convert_listing_or_literal convert_code
|
1894
1943
|
|
1895
1944
|
def convert_pass node
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1945
|
+
theme_font :code do
|
1946
|
+
typeset_formatted_text [text: (guard_indentation node.content), color: @theme.base_font_color], (calc_line_metrics @base_line_height)
|
1947
|
+
end
|
1948
|
+
theme_margin :block, :bottom, (next_enclosed_block node)
|
1900
1949
|
end
|
1901
1950
|
|
1902
|
-
|
1951
|
+
def convert_stem node
|
1952
|
+
arrange_block node do |extent|
|
1953
|
+
add_dest_for_block node if node.id
|
1954
|
+
tare_first_page_content_stream { theme_fill_and_stroke_block :code, extent, caption_node: node }
|
1955
|
+
pad_box @theme.code_padding, node do
|
1956
|
+
theme_font :code do
|
1957
|
+
typeset_formatted_text [text: (guard_indentation node.content), color: @font_color],
|
1958
|
+
(calc_line_metrics @base_line_height),
|
1959
|
+
bottom_gutter: @bottom_gutters[-1][node]
|
1960
|
+
end
|
1961
|
+
end
|
1962
|
+
end
|
1963
|
+
theme_margin :block, :bottom, (next_enclosed_block node)
|
1964
|
+
end
|
1903
1965
|
|
1904
1966
|
# Extract callout marks from string, indexed by 0-based line number
|
1905
1967
|
# Return an Array with the processed string as the first argument
|
@@ -2353,11 +2415,12 @@ module Asciidoctor
|
|
2353
2415
|
end
|
2354
2416
|
|
2355
2417
|
def convert_thematic_break node
|
2356
|
-
|
2357
|
-
|
2358
|
-
|
2359
|
-
|
2360
|
-
|
2418
|
+
pad_box @theme.thematic_break_padding || [@theme.thematic_break_margin_top, 0] do
|
2419
|
+
stroke_horizontal_rule @theme.thematic_break_border_color,
|
2420
|
+
line_width: @theme.thematic_break_border_width,
|
2421
|
+
line_style: (@theme.thematic_break_border_style&.to_sym || :solid)
|
2422
|
+
end
|
2423
|
+
conceal_page_top { theme_margin :block, :bottom, (next_enclosed_block node) }
|
2361
2424
|
end
|
2362
2425
|
|
2363
2426
|
def convert_toc node, opts = {}
|
@@ -2405,26 +2468,16 @@ module Asciidoctor
|
|
2405
2468
|
def convert_index_section node
|
2406
2469
|
space_needed_for_category = @theme.description_list_term_spacing + (2 * (height_of_typeset_text 'A'))
|
2407
2470
|
pagenum_sequence_style = node.document.attr 'index-pagenum-sequence-style'
|
2408
|
-
column_box [0, cursor], columns: @theme.index_columns, width: bounds.width, reflow_margins: true do
|
2409
|
-
def @bounding_box.move_past_bottom *args # rubocop:disable Lint/NestedMethodDefinition
|
2410
|
-
super(*args)
|
2411
|
-
@document.bounds = @parent = @document.margin_box if @current_column == 0 && @reflow_margins
|
2412
|
-
end
|
2471
|
+
column_box [0, cursor], columns: @theme.index_columns, width: bounds.width, reflow_margins: true, spacer: @theme.index_column_gap do
|
2413
2472
|
@index.categories.each do |category|
|
2414
|
-
|
2415
|
-
bounds.move_past_bottom if space_needed_for_category > y - reference_bounds.absolute_bottom
|
2473
|
+
bounds.move_past_bottom if space_needed_for_category > cursor
|
2416
2474
|
ink_prose category.name,
|
2417
2475
|
align: :left,
|
2418
2476
|
inline_format: false,
|
2419
2477
|
margin_bottom: @theme.description_list_term_spacing,
|
2420
2478
|
style: @theme.description_list_term_font_style&.to_sym
|
2421
2479
|
category.terms.each {|term| convert_index_list_item term, pagenum_sequence_style }
|
2422
|
-
|
2423
|
-
if @theme.prose_margin_bottom > y - reference_bounds.absolute_bottom
|
2424
|
-
bounds.move_past_bottom
|
2425
|
-
else
|
2426
|
-
move_down @theme.prose_margin_bottom
|
2427
|
-
end
|
2480
|
+
@theme.prose_margin_bottom > cursor ? bounds.move_past_bottom : (move_down @theme.prose_margin_bottom)
|
2428
2481
|
end
|
2429
2482
|
end
|
2430
2483
|
nil
|
@@ -2926,23 +2979,31 @@ module Asciidoctor
|
|
2926
2979
|
|
2927
2980
|
alias start_new_part start_new_chapter
|
2928
2981
|
|
2929
|
-
|
2930
|
-
|
2982
|
+
# Position the cursor for where to ink the specified section title or discrete heading node.
|
2983
|
+
#
|
2984
|
+
# This method computes whether there is enough room on the page to prevent the specified node
|
2985
|
+
# from being orphaned. If there is not enough room, the method will advance the cursor to
|
2986
|
+
# the next page. This method is not called if the cursor is already at the top of the page or
|
2987
|
+
# whether this node has no node that follows it in document order.
|
2988
|
+
def arrange_heading node, title, opts
|
2989
|
+
if node.option? 'breakable'
|
2931
2990
|
orphaned = nil
|
2932
2991
|
dry_run single_page: true do
|
2933
2992
|
start_page = page
|
2934
2993
|
theme_font :heading, level: opts[:level] do
|
2935
2994
|
if opts[:part]
|
2936
|
-
ink_part_title
|
2995
|
+
ink_part_title node, title, opts
|
2937
2996
|
elsif opts[:chapterlike]
|
2938
|
-
ink_chapter_title
|
2997
|
+
ink_chapter_title node, title, opts
|
2939
2998
|
else
|
2940
|
-
ink_general_heading
|
2999
|
+
ink_general_heading node, title, opts
|
2941
3000
|
end
|
2942
3001
|
end
|
2943
3002
|
if page == start_page
|
2944
3003
|
page.tare_content_stream
|
2945
|
-
orphaned = stop_if_first_page_empty
|
3004
|
+
orphaned = stop_if_first_page_empty do
|
3005
|
+
node.context == :section ? (traverse node) : (convert (siblings = node.parent.blocks)[(siblings.index node) + 1])
|
3006
|
+
end
|
2946
3007
|
end
|
2947
3008
|
end
|
2948
3009
|
start_new_page if orphaned
|
@@ -2954,7 +3015,10 @@ module Asciidoctor
|
|
2954
3015
|
heading_h = (height_of_typeset_text title) +
|
2955
3016
|
(@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
|
2956
3017
|
(@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom) + h_padding_t + h_padding_b
|
2957
|
-
|
3018
|
+
if (min_height_after = @theme.heading_min_height_after) &&
|
3019
|
+
(node.context == :section ? node.blocks? : node != node.parent.blocks[-1])
|
3020
|
+
heading_h += min_height_after
|
3021
|
+
end
|
2958
3022
|
cursor >= heading_h
|
2959
3023
|
end
|
2960
3024
|
start_new_page unless h_fits
|
@@ -3021,6 +3085,64 @@ module Asciidoctor
|
|
3021
3085
|
end
|
3022
3086
|
end
|
3023
3087
|
|
3088
|
+
# private
|
3089
|
+
def ink_paragraph_in_float_box node, float_box, prose_opts, role_keys, block_next, insert_margin_bottom
|
3090
|
+
@float_box = para_font_descender = para_font_size = end_cursor = nil
|
3091
|
+
if role_keys
|
3092
|
+
line_metrics = theme_font_cascade role_keys do
|
3093
|
+
para_font_descender = font.descender
|
3094
|
+
para_font_size = font_size
|
3095
|
+
calc_line_metrics @base_line_height
|
3096
|
+
end
|
3097
|
+
else
|
3098
|
+
para_font_descender = font.descender
|
3099
|
+
para_font_size = font_size
|
3100
|
+
line_metrics = calc_line_metrics @base_line_height
|
3101
|
+
end
|
3102
|
+
# allocate the space of at least one empty line below block
|
3103
|
+
line_height_length = line_metrics.height + line_metrics.leading + line_metrics.padding_top
|
3104
|
+
start_page_number = float_box[:page]
|
3105
|
+
start_cursor = cursor
|
3106
|
+
block_bottom = (float_box_bottom = float_box[:bottom]) + float_box[:gap][1]
|
3107
|
+
# use :at to incorporate padding top from line metrics
|
3108
|
+
# use :final_gap to incorporate padding bottom from line metrics
|
3109
|
+
# use :draw_text_callback to track end cursor (requires applying :final_gap to result manually)
|
3110
|
+
prose_opts.update \
|
3111
|
+
at: [float_box[:left], start_cursor - line_metrics.padding_top],
|
3112
|
+
width: float_box[:width],
|
3113
|
+
height: [cursor, float_box[:height] - (float_box[:top] - start_cursor) + line_height_length].min,
|
3114
|
+
final_gap: para_font_descender + line_metrics.padding_bottom,
|
3115
|
+
draw_text_callback: (proc do |text, opts|
|
3116
|
+
draw_text! text, opts
|
3117
|
+
end_cursor = opts[:at][1] # does not include :final_gap value
|
3118
|
+
end)
|
3119
|
+
overflow_text = role_keys ?
|
3120
|
+
theme_font_cascade(role_keys) { ink_prose node.content, prose_opts } :
|
3121
|
+
(ink_prose node.content, prose_opts)
|
3122
|
+
move_cursor_to end_cursor -= prose_opts[:final_gap] if end_cursor # ink_prose with :height does not move cursor
|
3123
|
+
if overflow_text.empty?
|
3124
|
+
if block_next && (supports_float_wrapping? block_next)
|
3125
|
+
insert_margin_bottom.call
|
3126
|
+
@float_box = float_box if page_number == start_page_number && cursor > start_cursor - prose_opts[:height]
|
3127
|
+
elsif end_cursor > block_bottom
|
3128
|
+
move_cursor_to block_bottom
|
3129
|
+
theme_margin :block, :bottom, block_next
|
3130
|
+
else
|
3131
|
+
insert_margin_bottom.call
|
3132
|
+
end
|
3133
|
+
else
|
3134
|
+
overflow_prose_opts = { align: prose_opts[:align] || @base_text_align.to_sym }
|
3135
|
+
unless end_cursor
|
3136
|
+
overflow_prose_opts[:indent_paragraphs] = prose_opts[:indent_paragraphs]
|
3137
|
+
move_cursor_to float_box_bottom if start_cursor > float_box_bottom
|
3138
|
+
end
|
3139
|
+
role_keys ?
|
3140
|
+
theme_font_cascade(role_keys) { typeset_formatted_text overflow_text, line_metrics, overflow_prose_opts } :
|
3141
|
+
(typeset_formatted_text overflow_text, line_metrics, overflow_prose_opts)
|
3142
|
+
insert_margin_bottom.call
|
3143
|
+
end
|
3144
|
+
end
|
3145
|
+
|
3024
3146
|
# NOTE: inline_format is true by default
|
3025
3147
|
def ink_prose string, opts = {}
|
3026
3148
|
top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top) || 0
|
@@ -3043,12 +3165,13 @@ module Asciidoctor
|
|
3043
3165
|
text_decoration_width: (opts.delete :text_decoration_width),
|
3044
3166
|
}.compact
|
3045
3167
|
end
|
3046
|
-
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3168
|
+
result = typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3047
3169
|
color: @font_color,
|
3048
3170
|
inline_format: [inline_format_opts],
|
3049
3171
|
align: @base_text_align.to_sym,
|
3050
3172
|
}.merge(opts)
|
3051
3173
|
margin_bottom bot_margin
|
3174
|
+
result
|
3052
3175
|
end
|
3053
3176
|
|
3054
3177
|
def generate_manname_section node
|
@@ -3071,11 +3194,12 @@ module Asciidoctor
|
|
3071
3194
|
# title (i.e., subject.title? returns true).
|
3072
3195
|
def ink_caption subject, opts = {}
|
3073
3196
|
if opts.delete :dry_run
|
3074
|
-
force_top_margin = !at_page_top? if (force_top_margin = opts.delete :force_top_margin).nil?
|
3075
3197
|
return (dry_run keep_together: true, single_page: :enforce do
|
3076
|
-
|
3077
|
-
|
3078
|
-
|
3198
|
+
if opts.delete :force_top_margin
|
3199
|
+
conceal_page_top { ink_caption subject, opts }
|
3200
|
+
else
|
3201
|
+
ink_caption subject, opts
|
3202
|
+
end
|
3079
3203
|
end).single_page_height
|
3080
3204
|
end
|
3081
3205
|
if ::Asciidoctor::AbstractBlock === subject
|
@@ -3103,11 +3227,22 @@ module Asciidoctor
|
|
3103
3227
|
if (max_width = opts.delete :max_width) && max_width != 'none'
|
3104
3228
|
if ::String === max_width
|
3105
3229
|
if max_width.start_with? 'fit-content'
|
3106
|
-
|
3107
|
-
|
3108
|
-
|
3109
|
-
|
3230
|
+
block_width ||= container_width
|
3231
|
+
unless max_width.end_with? 't', '()'
|
3232
|
+
max_width = block_width * (max_width.slice 12, max_width.length - 1).to_f / 100.0
|
3233
|
+
if (caption_width_delta = block_width - max_width) > 0
|
3234
|
+
case align
|
3235
|
+
when :right
|
3236
|
+
indent_by[0] += caption_width_delta
|
3237
|
+
when :center
|
3238
|
+
indent_by[0] += caption_width_delta * 0.5
|
3239
|
+
indent_by[1] += caption_width_delta * 0.5
|
3240
|
+
else # :left, nil
|
3241
|
+
indent_by[1] += caption_width_delta
|
3242
|
+
end
|
3243
|
+
end
|
3110
3244
|
end
|
3245
|
+
max_width = block_width
|
3111
3246
|
elsif max_width.end_with? '%'
|
3112
3247
|
max_width = [max_width.to_f / 100 * bounds.width, bounds.width].min
|
3113
3248
|
block_align = align
|
@@ -3122,11 +3257,12 @@ module Asciidoctor
|
|
3122
3257
|
if (remainder = container_width - max_width) > 0
|
3123
3258
|
case block_align
|
3124
3259
|
when :right
|
3125
|
-
indent_by
|
3260
|
+
indent_by[0] += remainder
|
3126
3261
|
when :center
|
3127
|
-
indent_by
|
3262
|
+
indent_by[0] += remainder * 0.5
|
3263
|
+
indent_by[1] += remainder * 0.5
|
3128
3264
|
else # :left, nil
|
3129
|
-
indent_by
|
3265
|
+
indent_by[1] += remainder
|
3130
3266
|
end
|
3131
3267
|
end
|
3132
3268
|
end
|
@@ -3522,8 +3658,9 @@ module Asciidoctor
|
|
3522
3658
|
content = apply_subs_discretely doc, content, drop_lines_with_unresolved_attributes: true, imagesdir: @themesdir
|
3523
3659
|
content = transform_text content, @text_transform if @text_transform
|
3524
3660
|
end
|
3525
|
-
formatted_text_box (parse_text content,
|
3661
|
+
formatted_text_box (parse_text content, inline_format: [normalize: true]),
|
3526
3662
|
at: [left, bounds.top - trim_styles[:padding][side][0] - trim_styles[:content_offset] + ((Array trim_styles[:valign])[0] == :center ? font.descender * 0.5 : 0)],
|
3663
|
+
color: @font_color,
|
3527
3664
|
width: colwidth,
|
3528
3665
|
height: trim_styles[:prose_content_height][side],
|
3529
3666
|
align: colspec[:align],
|
@@ -4146,9 +4283,10 @@ module Asciidoctor
|
|
4146
4283
|
|
4147
4284
|
# TODO: document me, esp the first line formatting functionality
|
4148
4285
|
def typeset_text string, line_metrics, opts = {}
|
4149
|
-
move_down line_metrics.padding_top
|
4150
4286
|
opts = { leading: line_metrics.leading, final_gap: line_metrics.final_gap }.merge opts
|
4151
4287
|
string = string.gsub CjkLineBreakRx, ZeroWidthSpace if @cjk_line_breaks
|
4288
|
+
return text_box string, opts if opts[:height]
|
4289
|
+
move_down line_metrics.padding_top
|
4152
4290
|
if (hanging_indent = (opts.delete :hanging_indent) || 0) > 0
|
4153
4291
|
indent hanging_indent do
|
4154
4292
|
text string, (opts.merge indent_paragraphs: -hanging_indent)
|
@@ -4312,10 +4450,11 @@ module Asciidoctor
|
|
4312
4450
|
end
|
4313
4451
|
end
|
4314
4452
|
|
4315
|
-
def
|
4453
|
+
def resolve_text_align_from_role roles, query_theme: false, remove_predefined: false
|
4316
4454
|
if (align_role = roles.reverse.find {|r| TextAlignmentRoles.include? r })
|
4455
|
+
roles.replace roles - TextAlignmentRoles if remove_predefined
|
4317
4456
|
(align_role.slice 5, align_role.length).to_sym
|
4318
|
-
elsif
|
4457
|
+
elsif query_theme
|
4319
4458
|
roles.reverse.each do |role|
|
4320
4459
|
if (align = @theme[%(role_#{role}_text_align)])
|
4321
4460
|
return align.to_sym
|
@@ -4325,6 +4464,9 @@ module Asciidoctor
|
|
4325
4464
|
end
|
4326
4465
|
end
|
4327
4466
|
|
4467
|
+
# Deprecated
|
4468
|
+
alias resolve_alignment_from_role resolve_text_align_from_role
|
4469
|
+
|
4328
4470
|
# QUESTION: is this method still necessary?
|
4329
4471
|
def resolve_imagesdir doc
|
4330
4472
|
if (imagesdir = doc.attr 'imagesdir').nil_or_empty? || (imagesdir = imagesdir.chomp '/') == '.'
|
@@ -4669,9 +4811,12 @@ module Asciidoctor
|
|
4669
4811
|
alias layout_running_content ink_running_content
|
4670
4812
|
|
4671
4813
|
# intercepts "class CustomPDFConverter < (Asciidoctor::Converter.for 'pdf')"
|
4672
|
-
def self.method_added
|
4673
|
-
if (method_name =
|
4674
|
-
alias_method %(ink_#{method_name.slice 7, method_name.length}).to_sym,
|
4814
|
+
def self.method_added method_sym
|
4815
|
+
if (method_name = method_sym.to_s).start_with? 'layout_'
|
4816
|
+
alias_method %(ink_#{method_name.slice 7, method_name.length}).to_sym, method_sym
|
4817
|
+
elsif method_name == 'convert_listing_or_literal' || method_name == 'convert_code'
|
4818
|
+
alias_method :convert_listing, method_sym
|
4819
|
+
alias_method :convert_literal, method_sym
|
4675
4820
|
end
|
4676
4821
|
end
|
4677
4822
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Prawn::Document::ColumnBox.prepend (Module.new do
|
4
|
+
def absolute_bottom
|
5
|
+
stretchy? ? @parent.absolute_bottom : super
|
6
|
+
end
|
7
|
+
|
8
|
+
def move_past_bottom *_args
|
9
|
+
initial_page = @document.page
|
10
|
+
super
|
11
|
+
if (page = @document.page) != initial_page && page.margins != initial_page.margins
|
12
|
+
@document.bounds = self.class.new @document, @parent, (margin_box = @document.margin_box).absolute_top_left,
|
13
|
+
columns: @columns, reflow_margins: true, spacer: @spacer, width: margin_box.width
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end)
|